Split push module into several modules: Firebase, UnifiedPush, store
This commit is contained in:
parent
9ac46aed7c
commit
7333995630
53 changed files with 768 additions and 276 deletions
|
|
@ -0,0 +1,47 @@
|
|||
<?xml version="1.0" encoding="utf-8"?><!--
|
||||
~ Copyright (c) 2023 New Vector Ltd
|
||||
~
|
||||
~ Licensed under the Apache License, Version 2.0 (the "License");
|
||||
~ you may not use this file except in compliance with the License.
|
||||
~ You may obtain a copy of the License at
|
||||
~
|
||||
~ http://www.apache.org/licenses/LICENSE-2.0
|
||||
~
|
||||
~ Unless required by applicable law or agreed to in writing, software
|
||||
~ distributed under the License is distributed on an "AS IS" BASIS,
|
||||
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
~ See the License for the specific language governing permissions and
|
||||
~ limitations under the License.
|
||||
-->
|
||||
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools">
|
||||
<application>
|
||||
<receiver
|
||||
android:name=".VectorUnifiedPushMessagingReceiver"
|
||||
android:enabled="true"
|
||||
android:exported="true"
|
||||
tools:ignore="ExportedReceiver">
|
||||
<intent-filter>
|
||||
<action android:name="org.unifiedpush.android.connector.MESSAGE" />
|
||||
<action android:name="org.unifiedpush.android.connector.UNREGISTERED" />
|
||||
<action android:name="org.unifiedpush.android.connector.NEW_ENDPOINT" />
|
||||
<action android:name="org.unifiedpush.android.connector.REGISTRATION_FAILED" />
|
||||
<action android:name="org.unifiedpush.android.connector.REGISTRATION_REFUSED" />
|
||||
</intent-filter>
|
||||
</receiver>
|
||||
<receiver
|
||||
android:name=".KeepInternalDistributor"
|
||||
android:enabled="true"
|
||||
android:exported="false">
|
||||
<intent-filter>
|
||||
<!--
|
||||
This action is checked to track installed and uninstalled distributors.
|
||||
We declare it to keep the background sync as an internal
|
||||
unifiedpush distributor.
|
||||
-->
|
||||
<action android:name="org.unifiedpush.android.distributor.REGISTER" />
|
||||
</intent-filter>
|
||||
</receiver>
|
||||
</application>
|
||||
</manifest>
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
/*
|
||||
* Copyright (c) 2023 New Vector Ltd
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package io.element.android.libraries.push.providers.unifiedpush
|
||||
|
||||
import com.squareup.anvil.annotations.ContributesBinding
|
||||
import io.element.android.libraries.di.AppScope
|
||||
import javax.inject.Inject
|
||||
|
||||
interface GuardServiceStarter {
|
||||
fun start() {}
|
||||
fun stop() {}
|
||||
}
|
||||
|
||||
@ContributesBinding(AppScope::class)
|
||||
class NoopGuardServiceStarter @Inject constructor() : GuardServiceStarter
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
/*
|
||||
* Copyright (c) 2023 New Vector Ltd
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package io.element.android.libraries.push.providers.unifiedpush
|
||||
|
||||
import android.content.BroadcastReceiver
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
|
||||
/**
|
||||
* UnifiedPush lib tracks an action to check installed and uninstalled distributors.
|
||||
* We declare it to keep the background sync as an internal unifiedpush distributor.
|
||||
* This class is used to declare this action.
|
||||
*/
|
||||
class KeepInternalDistributor : BroadcastReceiver() {
|
||||
override fun onReceive(context: Context, intent: Intent) {}
|
||||
}
|
||||
|
|
@ -0,0 +1,67 @@
|
|||
/*
|
||||
* Copyright (c) 2023 New Vector Ltd
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package io.element.android.libraries.push.providers.unifiedpush
|
||||
|
||||
import io.element.android.libraries.matrix.api.core.asEventId
|
||||
import io.element.android.libraries.matrix.api.core.asRoomId
|
||||
import io.element.android.libraries.push.providers.api.PushData
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
/**
|
||||
* In this case, the format is:
|
||||
* <pre>
|
||||
* {
|
||||
* "notification":{
|
||||
* "event_id":"$anEventId",
|
||||
* "room_id":"!aRoomId",
|
||||
* "counts":{
|
||||
* "unread":1
|
||||
* },
|
||||
* "prio":"high"
|
||||
* }
|
||||
* }
|
||||
* </pre>
|
||||
* .
|
||||
*/
|
||||
@Serializable
|
||||
data class PushDataUnifiedPush(
|
||||
val notification: PushDataUnifiedPushNotification? = null
|
||||
)
|
||||
|
||||
@Serializable
|
||||
data class PushDataUnifiedPushNotification(
|
||||
@SerialName("event_id") val eventId: String? = null,
|
||||
@SerialName("room_id") val roomId: String? = null,
|
||||
@SerialName("counts") var counts: PushDataUnifiedPushCounts? = null,
|
||||
)
|
||||
|
||||
@Serializable
|
||||
data class PushDataUnifiedPushCounts(
|
||||
@SerialName("unread") val unread: Int? = null
|
||||
)
|
||||
|
||||
fun PushDataUnifiedPush.toPushData(): PushData? {
|
||||
val safeEventId = notification?.eventId?.asEventId() ?: return null
|
||||
val safeRoomId = notification.roomId?.asRoomId() ?: return null
|
||||
return PushData(
|
||||
eventId = safeEventId,
|
||||
roomId = safeRoomId,
|
||||
unread = notification.counts?.unread,
|
||||
clientSecret = null // TODO EAx check how client secret will be sent through UnifiedPush
|
||||
)
|
||||
}
|
||||
|
|
@ -0,0 +1,62 @@
|
|||
/*
|
||||
* Copyright (c) 2023 New Vector Ltd
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package io.element.android.libraries.push.providers.unifiedpush
|
||||
|
||||
import android.content.Context
|
||||
import io.element.android.libraries.di.ApplicationContext
|
||||
import org.unifiedpush.android.connector.UnifiedPush
|
||||
import javax.inject.Inject
|
||||
|
||||
class RegisterUnifiedPushUseCase @Inject constructor(
|
||||
@ApplicationContext private val context: Context,
|
||||
) {
|
||||
|
||||
sealed interface RegisterUnifiedPushResult {
|
||||
object Success : RegisterUnifiedPushResult
|
||||
object NeedToAskUserForDistributor : RegisterUnifiedPushResult
|
||||
}
|
||||
|
||||
fun execute(distributor: String = ""): RegisterUnifiedPushResult {
|
||||
if (distributor.isNotEmpty()) {
|
||||
saveAndRegisterApp(distributor)
|
||||
return RegisterUnifiedPushResult.Success
|
||||
}
|
||||
|
||||
if (UnifiedPush.getDistributor(context).isNotEmpty()) {
|
||||
registerApp()
|
||||
return RegisterUnifiedPushResult.Success
|
||||
}
|
||||
|
||||
val distributors = UnifiedPush.getDistributors(context)
|
||||
|
||||
return if (distributors.size == 1) {
|
||||
saveAndRegisterApp(distributors.first())
|
||||
RegisterUnifiedPushResult.Success
|
||||
} else {
|
||||
RegisterUnifiedPushResult.NeedToAskUserForDistributor
|
||||
}
|
||||
}
|
||||
|
||||
private fun saveAndRegisterApp(distributor: String) {
|
||||
UnifiedPush.saveDistributor(context, distributor)
|
||||
registerApp()
|
||||
}
|
||||
|
||||
private fun registerApp() {
|
||||
UnifiedPush.registerApp(context)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
/*
|
||||
* Copyright (c) 2023 New Vector Ltd
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package io.element.android.libraries.push.providers.unifiedpush
|
||||
|
||||
object UnifiedPushConfig {
|
||||
/**
|
||||
* It is the push gateway for UnifiedPush.
|
||||
* Note: default_push_gateway_http_url should have path '/_matrix/push/v1/notify'
|
||||
*/
|
||||
const val default_push_gateway_http_url: String = "https://matrix.gateway.unifiedpush.org/_matrix/push/v1/notify"
|
||||
|
||||
const val internalName = "NOTIFICATION_METHOD_UNIFIEDPUSH"
|
||||
}
|
||||
|
|
@ -0,0 +1,185 @@
|
|||
/*
|
||||
* Copyright (c) 2023 New Vector Ltd
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package io.element.android.libraries.push.providers.unifiedpush
|
||||
|
||||
import android.content.Context
|
||||
import io.element.android.libraries.di.ApplicationContext
|
||||
import io.element.android.services.toolbox.api.strings.StringProvider
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
import org.unifiedpush.android.connector.UnifiedPush
|
||||
import timber.log.Timber
|
||||
import java.net.URL
|
||||
import javax.inject.Inject
|
||||
|
||||
class UnifiedPushHelper @Inject constructor(
|
||||
@ApplicationContext private val context: Context,
|
||||
private val unifiedPushStore: UnifiedPushStore,
|
||||
private val stringProvider: StringProvider,
|
||||
) {
|
||||
|
||||
/* TODO EAx
|
||||
@MainThread
|
||||
fun showSelectDistributorDialog(
|
||||
context: Context,
|
||||
onDistributorSelected: (String) -> Unit,
|
||||
) {
|
||||
val internalDistributorName = stringProvider.getString(
|
||||
if (fcmHelper.isFirebaseAvailable()) {
|
||||
R.string.push_distributor_firebase_android
|
||||
} else {
|
||||
R.string.push_distributor_background_sync_android
|
||||
}
|
||||
)
|
||||
|
||||
val distributors = UnifiedPush.getDistributors(context)
|
||||
val distributorsName = distributors.map {
|
||||
if (it == context.packageName) {
|
||||
internalDistributorName
|
||||
} else {
|
||||
context.getApplicationLabel(it)
|
||||
}
|
||||
}
|
||||
|
||||
MaterialAlertDialogBuilder(context)
|
||||
.setTitle(stringProvider.getString(R.string.push_choose_distributor_dialog_title_android))
|
||||
.setItems(distributorsName.toTypedArray()) { _, which ->
|
||||
val distributor = distributors[which]
|
||||
onDistributorSelected(distributor)
|
||||
}
|
||||
.setOnCancelListener {
|
||||
// we do not want to change the distributor on behalf of the user
|
||||
if (UnifiedPush.getDistributor(context).isEmpty()) {
|
||||
// By default, use internal solution (fcm/background sync)
|
||||
onDistributorSelected(context.packageName)
|
||||
}
|
||||
}
|
||||
.setCancelable(true)
|
||||
.show()
|
||||
}
|
||||
|
||||
*/
|
||||
|
||||
@Serializable
|
||||
internal data class DiscoveryResponse(
|
||||
@SerialName("unifiedpush") val unifiedpush: DiscoveryUnifiedPush = DiscoveryUnifiedPush()
|
||||
)
|
||||
|
||||
@Serializable
|
||||
internal data class DiscoveryUnifiedPush(
|
||||
@SerialName("gateway") val gateway: String = ""
|
||||
)
|
||||
|
||||
suspend fun storeCustomOrDefaultGateway(
|
||||
endpoint: String,
|
||||
onDoneRunnable: Runnable? = null
|
||||
) {
|
||||
// if we use the embedded distributor,
|
||||
// register app_id type upfcm on sygnal
|
||||
// the pushkey if FCM key
|
||||
/*
|
||||
if (UnifiedPush.getDistributor(context) == context.packageName) {
|
||||
unifiedPushStore.storePushGateway(PushConfig.pusher_http_url)
|
||||
onDoneRunnable?.run()
|
||||
return
|
||||
}
|
||||
|
||||
*/
|
||||
/* TODO EAx UnifiedPush
|
||||
// else, unifiedpush, and pushkey is an endpoint
|
||||
val gateway = PushConfig.default_push_gateway_http_url
|
||||
val parsed = URL(endpoint)
|
||||
val custom = "${parsed.protocol}://${parsed.host}/_matrix/push/v1/notify"
|
||||
Timber.i("Testing $custom")
|
||||
try {
|
||||
val response = matrix.rawService().getUrl(custom, CacheStrategy.NoCache)
|
||||
tryOrNull { Json.decodeFromString<DiscoveryResponse>(response) }
|
||||
?.let { discoveryResponse ->
|
||||
if (discoveryResponse.unifiedpush.gateway == "matrix") {
|
||||
Timber.d("Using custom gateway")
|
||||
unifiedPushStore.storePushGateway(custom)
|
||||
onDoneRunnable?.run()
|
||||
return
|
||||
}
|
||||
}
|
||||
} catch (e: Throwable) {
|
||||
Timber.d(e, "Cannot try custom gateway")
|
||||
}
|
||||
unifiedPushStore.storePushGateway(gateway)
|
||||
onDoneRunnable?.run()
|
||||
|
||||
*/
|
||||
}
|
||||
|
||||
fun getExternalDistributors(): List<String> {
|
||||
return UnifiedPush.getDistributors(context)
|
||||
.filterNot { it == context.packageName }
|
||||
}
|
||||
|
||||
fun getCurrentDistributorName(): String {
|
||||
TODO()
|
||||
/*
|
||||
return when {
|
||||
isEmbeddedDistributor() -> stringProvider.getString(R.string.push_distributor_firebase_android)
|
||||
isBackgroundSync() -> stringProvider.getString(R.string.push_distributor_background_sync_android)
|
||||
else -> context.getApplicationLabel(UnifiedPush.getDistributor(context))
|
||||
}
|
||||
|
||||
*/
|
||||
}
|
||||
|
||||
fun isEmbeddedDistributor(): Boolean {
|
||||
TODO()
|
||||
//return isInternalDistributor() && fcmHelper.isFirebaseAvailable()
|
||||
}
|
||||
|
||||
fun isBackgroundSync(): Boolean {
|
||||
TODO()
|
||||
//return isInternalDistributor() && !fcmHelper.isFirebaseAvailable()
|
||||
}
|
||||
|
||||
private fun isInternalDistributor(): Boolean {
|
||||
return UnifiedPush.getDistributor(context).isEmpty() ||
|
||||
UnifiedPush.getDistributor(context) == context.packageName
|
||||
}
|
||||
|
||||
fun getPrivacyFriendlyUpEndpoint(): String? {
|
||||
val endpoint = getEndpointOrToken()
|
||||
if (endpoint.isNullOrEmpty()) return null
|
||||
if (isEmbeddedDistributor()) {
|
||||
return endpoint
|
||||
}
|
||||
return try {
|
||||
val parsed = URL(endpoint)
|
||||
"${parsed.protocol}://${parsed.host}/***"
|
||||
} catch (e: Exception) {
|
||||
Timber.e(e, "Error parsing unifiedpush endpoint")
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
fun getEndpointOrToken(): String? {
|
||||
// TODO
|
||||
return if (isEmbeddedDistributor()) "" // fcmHelper.getFcmToken()
|
||||
else unifiedPushStore.getEndpoint()
|
||||
}
|
||||
|
||||
fun getPushGateway(): String? {
|
||||
return if (isEmbeddedDistributor()) "" // PushConfig.pusher_http_url
|
||||
else unifiedPushStore.getPushGateway()
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
/*
|
||||
* Copyright (c) 2023 New Vector Ltd
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package io.element.android.libraries.push.providers.unifiedpush
|
||||
|
||||
import io.element.android.libraries.core.data.tryOrNull
|
||||
import io.element.android.libraries.push.providers.api.PushData
|
||||
import kotlinx.serialization.decodeFromString
|
||||
import kotlinx.serialization.json.Json
|
||||
import javax.inject.Inject
|
||||
|
||||
class UnifiedPushParser @Inject constructor() {
|
||||
private val json by lazy { Json { ignoreUnknownKeys = true } }
|
||||
|
||||
fun parse(message: ByteArray): PushData? {
|
||||
return tryOrNull { json.decodeFromString<PushDataUnifiedPush>(String(message)) }?.toPushData()
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
/*
|
||||
* Copyright (c) 2023 New Vector Ltd
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package io.element.android.libraries.push.providers.unifiedpush
|
||||
|
||||
import io.element.android.libraries.matrix.api.MatrixClient
|
||||
import io.element.android.libraries.push.providers.api.PushProvider
|
||||
import javax.inject.Inject
|
||||
|
||||
class UnifiedPushProvider @Inject constructor(): PushProvider {
|
||||
override fun getDistributorNames(): List<String> {
|
||||
TODO("Not yet implemented")
|
||||
}
|
||||
|
||||
override suspend fun registerWith(matrixClient: MatrixClient, distributorName: String) {
|
||||
TODO("Not yet implemented")
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,77 @@
|
|||
/*
|
||||
* Copyright (c) 2023 New Vector Ltd
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package io.element.android.libraries.push.providers.unifiedpush
|
||||
|
||||
import android.content.Context
|
||||
import android.content.SharedPreferences
|
||||
import androidx.core.content.edit
|
||||
import io.element.android.libraries.di.ApplicationContext
|
||||
import io.element.android.libraries.di.DefaultPreferences
|
||||
import javax.inject.Inject
|
||||
|
||||
/**
|
||||
* TODO EAx Store in BDD (for multisession).
|
||||
*/
|
||||
class UnifiedPushStore @Inject constructor(
|
||||
@ApplicationContext val context: Context,
|
||||
@DefaultPreferences private val defaultPrefs: SharedPreferences,
|
||||
) {
|
||||
/**
|
||||
* Retrieves the UnifiedPush Endpoint.
|
||||
*
|
||||
* @return the UnifiedPush Endpoint or null if not received
|
||||
*/
|
||||
fun getEndpoint(): String? {
|
||||
return defaultPrefs.getString(PREFS_ENDPOINT_OR_TOKEN, null)
|
||||
}
|
||||
|
||||
/**
|
||||
* Store UnifiedPush Endpoint to the SharedPrefs.
|
||||
*
|
||||
* @param endpoint the endpoint to store
|
||||
*/
|
||||
fun storeUpEndpoint(endpoint: String?) {
|
||||
defaultPrefs.edit {
|
||||
putString(PREFS_ENDPOINT_OR_TOKEN, endpoint)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the Push Gateway.
|
||||
*
|
||||
* @return the Push Gateway or null if not defined
|
||||
*/
|
||||
fun getPushGateway(): String? {
|
||||
return defaultPrefs.getString(PREFS_PUSH_GATEWAY, null)
|
||||
}
|
||||
|
||||
/**
|
||||
* Store Push Gateway to the SharedPrefs.
|
||||
*
|
||||
* @param gateway the push gateway to store
|
||||
*/
|
||||
fun storePushGateway(gateway: String?) {
|
||||
defaultPrefs.edit {
|
||||
putString(PREFS_PUSH_GATEWAY, gateway)
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val PREFS_ENDPOINT_OR_TOKEN = "UP_ENDPOINT_OR_TOKEN"
|
||||
private const val PREFS_PUSH_GATEWAY = "PUSH_GATEWAY"
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,43 @@
|
|||
/*
|
||||
* Copyright (c) 2023 New Vector Ltd
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package io.element.android.libraries.push.providers.unifiedpush
|
||||
|
||||
/*
|
||||
class UnregisterUnifiedPushUseCase @Inject constructor(
|
||||
@ApplicationContext private val context: Context,
|
||||
private val pushDataStore: PushDataStore,
|
||||
private val unifiedPushStore: UnifiedPushStore,
|
||||
private val unifiedPushHelper: UnifiedPushHelper,
|
||||
) {
|
||||
|
||||
suspend fun execute(pushersManager: PushersManager?) {
|
||||
val mode = BackgroundSyncMode.FDROID_BACKGROUND_SYNC_MODE_FOR_REALTIME
|
||||
pushDataStore.setFdroidSyncBackgroundMode(mode)
|
||||
try {
|
||||
unifiedPushHelper.getEndpointOrToken()?.let {
|
||||
Timber.d("Removing $it")
|
||||
pushersManager?.unregisterPusher(it)
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
Timber.d(e, "Probably unregistering a non existing pusher")
|
||||
}
|
||||
unifiedPushStore.storeUpEndpoint(null)
|
||||
unifiedPushStore.storePushGateway(null)
|
||||
UnifiedPush.unregisterApp(context)
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
|
@ -0,0 +1,122 @@
|
|||
/*
|
||||
* Copyright (c) 2023 New Vector Ltd
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package io.element.android.libraries.push.providers.unifiedpush
|
||||
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import io.element.android.libraries.architecture.bindings
|
||||
import io.element.android.libraries.core.log.logger.LoggerTag
|
||||
import io.element.android.libraries.push.providers.api.PushHandler
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.SupervisorJob
|
||||
import kotlinx.coroutines.launch
|
||||
import org.unifiedpush.android.connector.MessagingReceiver
|
||||
import timber.log.Timber
|
||||
import javax.inject.Inject
|
||||
|
||||
private val loggerTag = LoggerTag("VectorUnifiedPushMessagingReceiver")
|
||||
|
||||
class VectorUnifiedPushMessagingReceiver : MessagingReceiver() {
|
||||
@Inject lateinit var pushParser: UnifiedPushParser
|
||||
// @Inject lateinit var pushDataStore: PushDataStore
|
||||
@Inject lateinit var pushHandler: PushHandler
|
||||
@Inject lateinit var guardServiceStarter: GuardServiceStarter
|
||||
// @Inject lateinit var unifiedPushStore: UnifiedPushStore
|
||||
// @Inject lateinit var unifiedPushHelper: UnifiedPushHelper
|
||||
|
||||
private val coroutineScope = CoroutineScope(SupervisorJob())
|
||||
|
||||
override fun onReceive(context: Context, intent: Intent) {
|
||||
super.onReceive(context, intent)
|
||||
// Inject
|
||||
context.applicationContext.bindings<VectorUnifiedPushMessagingReceiverBindings>().inject(this)
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when message is received.
|
||||
*
|
||||
* @param context the Android context
|
||||
* @param message the message
|
||||
* @param instance connection, for multi-account
|
||||
*/
|
||||
override fun onMessage(context: Context, message: ByteArray, instance: String) {
|
||||
Timber.tag(loggerTag.value).d("New message")
|
||||
coroutineScope.launch {
|
||||
val pushData = pushParser.parse(message)
|
||||
if (pushData == null) {
|
||||
Timber.tag(loggerTag.value).w("Invalid data received from UnifiedPush")
|
||||
} else {
|
||||
pushHandler.handle(pushData)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onNewEndpoint(context: Context, endpoint: String, instance: String) {
|
||||
TODO()
|
||||
/*
|
||||
Timber.tag(loggerTag.value).i("onNewEndpoint: adding $endpoint")
|
||||
if (pushDataStore.areNotificationEnabledForDevice() /* TODO EAx && activeSessionHolder.hasActiveSession() */) {
|
||||
// If the endpoint has changed
|
||||
// or the gateway has changed
|
||||
if (unifiedPushHelper.getEndpointOrToken() != endpoint) {
|
||||
unifiedPushStore.storeUpEndpoint(endpoint)
|
||||
coroutineScope.launch {
|
||||
unifiedPushHelper.storeCustomOrDefaultGateway(endpoint) {
|
||||
unifiedPushHelper.getPushGateway()?.let {
|
||||
coroutineScope.launch {
|
||||
pushersManager.onNewUnifiedPushEndpoint(endpoint, it)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Timber.tag(loggerTag.value).i("onNewEndpoint: skipped")
|
||||
}
|
||||
}
|
||||
val mode = BackgroundSyncMode.FDROID_BACKGROUND_SYNC_MODE_DISABLED
|
||||
pushDataStore.setFdroidSyncBackgroundMode(mode)
|
||||
guardServiceStarter.stop()
|
||||
*/
|
||||
}
|
||||
|
||||
override fun onRegistrationFailed(context: Context, instance: String) {
|
||||
TODO()
|
||||
/*
|
||||
Toast.makeText(context, "Push service registration failed", Toast.LENGTH_SHORT).show()
|
||||
val mode = BackgroundSyncMode.FDROID_BACKGROUND_SYNC_MODE_FOR_REALTIME
|
||||
pushDataStore.setFdroidSyncBackgroundMode(mode)
|
||||
guardServiceStarter.start()
|
||||
*/
|
||||
}
|
||||
|
||||
override fun onUnregistered(context: Context, instance: String) {
|
||||
TODO()
|
||||
/*
|
||||
Timber.tag(loggerTag.value).d("Unifiedpush: Unregistered")
|
||||
val mode = BackgroundSyncMode.FDROID_BACKGROUND_SYNC_MODE_FOR_REALTIME
|
||||
pushDataStore.setFdroidSyncBackgroundMode(mode)
|
||||
guardServiceStarter.start()
|
||||
runBlocking {
|
||||
try {
|
||||
pushersManager.unregisterPusher(unifiedPushHelper.getEndpointOrToken().orEmpty())
|
||||
} catch (e: Exception) {
|
||||
Timber.tag(loggerTag.value).d("Probably unregistering a non existing pusher")
|
||||
}
|
||||
}
|
||||
*/
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
/*
|
||||
* Copyright (c) 2023 New Vector Ltd
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package io.element.android.libraries.push.providers.unifiedpush
|
||||
|
||||
import com.squareup.anvil.annotations.ContributesTo
|
||||
import io.element.android.libraries.di.AppScope
|
||||
|
||||
@ContributesTo(AppScope::class)
|
||||
interface VectorUnifiedPushMessagingReceiverBindings {
|
||||
fun inject(receiver: VectorUnifiedPushMessagingReceiver)
|
||||
}
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
/*
|
||||
* Copyright (c) 2023 New Vector Ltd
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package io.element.android.libraries.push.providers.unifiedpush.di
|
||||
|
||||
import com.squareup.anvil.annotations.ContributesTo
|
||||
import dagger.Binds
|
||||
import dagger.Module
|
||||
import dagger.multibindings.IntoSet
|
||||
import io.element.android.libraries.di.AppScope
|
||||
import io.element.android.libraries.push.providers.api.PushProvider
|
||||
import io.element.android.libraries.push.providers.unifiedpush.UnifiedPushProvider
|
||||
|
||||
@Module
|
||||
@ContributesTo(AppScope::class)
|
||||
interface UnifiedPushModule {
|
||||
@Binds
|
||||
@IntoSet
|
||||
fun bind(pushProvider: UnifiedPushProvider): PushProvider
|
||||
}
|
||||
|
|
@ -0,0 +1,84 @@
|
|||
/*
|
||||
* Copyright (c) 2023 New Vector Ltd
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package io.element.android.libraries.push.providers.unifiedpush
|
||||
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import io.element.android.libraries.matrix.test.AN_EVENT_ID
|
||||
import io.element.android.libraries.matrix.test.A_ROOM_ID
|
||||
import io.element.android.libraries.push.providers.api.PushData
|
||||
import org.junit.Test
|
||||
|
||||
class UnifiedPushParserTest {
|
||||
private val validData = PushData(
|
||||
eventId = AN_EVENT_ID,
|
||||
roomId = A_ROOM_ID,
|
||||
unread = 1,
|
||||
// TODO handle client secret here.
|
||||
clientSecret = null
|
||||
)
|
||||
|
||||
@Test
|
||||
fun `test edge cases UnifiedPush`() {
|
||||
val pushParser = UnifiedPushParser()
|
||||
// Empty string
|
||||
assertThat(pushParser.parse("".toByteArray())).isNull()
|
||||
// Empty Json
|
||||
assertThat(pushParser.parse("{}".toByteArray())).isNull()
|
||||
// Bad Json
|
||||
assertThat(pushParser.parse("ABC".toByteArray())).isNull()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `test UnifiedPush format`() {
|
||||
val pushParser = UnifiedPushParser()
|
||||
assertThat(pushParser.parse(UNIFIED_PUSH_DATA.toByteArray())).isEqualTo(validData)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `test empty roomId`() {
|
||||
val pushParser = UnifiedPushParser()
|
||||
assertThat(pushParser.parse(UNIFIED_PUSH_DATA.replace(A_ROOM_ID.value, "").toByteArray())).isNull()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `test invalid roomId`() {
|
||||
val pushParser = UnifiedPushParser()
|
||||
assertThat(pushParser.parse(UNIFIED_PUSH_DATA.mutate(A_ROOM_ID.value, "aRoomId:domain"))).isNull()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `test empty eventId`() {
|
||||
val pushParser = UnifiedPushParser()
|
||||
assertThat(pushParser.parse(UNIFIED_PUSH_DATA.mutate(AN_EVENT_ID.value, ""))).isNull()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `test invalid eventId`() {
|
||||
val pushParser = UnifiedPushParser()
|
||||
assertThat(pushParser.parse(UNIFIED_PUSH_DATA.mutate(AN_EVENT_ID.value, "anEventId"))).isNull()
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val UNIFIED_PUSH_DATA =
|
||||
"{\"notification\":{\"event_id\":\"${AN_EVENT_ID.value}\",\"room_id\":\"${A_ROOM_ID.value}\",\"counts\":{\"unread\":1},\"prio\":\"high\"}}"
|
||||
// TODO Check client secret format?
|
||||
}
|
||||
}
|
||||
|
||||
private fun String.mutate(oldValue: String, newValue: String): ByteArray {
|
||||
return replace(oldValue, newValue).toByteArray()
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue