Add support for Push notifications

This commit is contained in:
sim
2025-08-19 17:30:35 +02:00
parent 6ab0f3222a
commit 9305acc3f1
10 changed files with 165 additions and 29 deletions
@@ -185,6 +185,13 @@
android:foregroundServiceType="mediaPlayback|microphone|camera|remoteMessaging"
/>
<service android:name=".PushService"
android:exported="false">
<intent-filter>
<action android:name="org.unifiedpush.android.connector.PUSH_EVENT"/>
</intent-filter>
</service>
<receiver
android:name=".CallService$CallActionReceiver"
android:enabled="true"
@@ -0,0 +1,82 @@
package chat.simplex.app
import chat.simplex.common.model.CC
import chat.simplex.common.platform.Log
import chat.simplex.common.platform.chatModel
import kotlinx.coroutines.*
import kotlinx.serialization.Serializable
import kotlinx.serialization.json.Json
import org.unifiedpush.android.connector.FailedReason
import org.unifiedpush.android.connector.PushService
import org.unifiedpush.android.connector.data.PushEndpoint
import org.unifiedpush.android.connector.data.PushMessage
class PushService: PushService() {
private val json = Json {
ignoreUnknownKeys = true
}
@Serializable
data class PNMessage(
val verification: String? = null
)
private fun onVerification(code: String) {
CoroutineScope(Dispatchers.Default).launch {
chatModel.controller.sendCmd(
null,
CC.APIVerifySavedNtf(code),
log = true
)
}
}
override fun onMessage(message: PushMessage, instance: String) {
Log.d(TAG, "onMessage")
val pn: PNMessage = json.decodeFromString(String(message.content))
when {
pn.verification != null -> onVerification(pn.verification)
}
// TODO: Start same job than the periodic service ?
// Receiving the push notif is enough to wake the app and fetch msgs
// But it may not be enough when the phone is in doze, or with some
// vendors
}
override fun onNewEndpoint(endpoint: PushEndpoint, instance: String) {
Log.d(TAG, "onNewEndpoint")
endpoint.pubKeySet ?: run {
// Should not happen
Log.w(TAG, "Missing pubKeySet")
return
}
CoroutineScope(Dispatchers.Default).launch {
chatModel.controller.sendCmd(
null,
CC.APIRegisterWebPush(endpoint.url, endpoint.pubKeySet!!.auth, endpoint.pubKeySet!!.pubKey),
log = true
)
}
}
override fun onRegistrationFailed(reason: FailedReason, instance: String) {
Log.d(TAG, "onRegistrationFailed: $reason")
// TODO: notification to inform about failed registration
}
override fun onUnregistered(instance: String) {
Log.d(TAG, "onUnregistered")
// TODO: notification to inform about unregistration
CoroutineScope(Dispatchers.Default).launch {
chatModel.controller.sendCmd(
null,
CC.APIDeleteSavedNtf(),
log = true
)
}
}
companion object {
private const val TAG = "PushService"
}
}
@@ -16,6 +16,7 @@ import androidx.compose.ui.unit.dp
import chat.simplex.common.model.*
import chat.simplex.common.model.ChatController.getUserServers
import chat.simplex.common.platform.Log
import chat.simplex.common.platform.chatModel
import chat.simplex.common.views.helpers.*
import chat.simplex.common.views.usersettings.networkAndServers.showAddServerDialog
import chat.simplex.res.MR
@@ -39,10 +40,11 @@ object PushManager {
* Else alert about missing service
*/
suspend fun initUnifiedPush(context: Context, scope: CoroutineScope, onSuccess: () -> Unit) {
val userServers = getUserServers(null) ?: listOf()
val rh = chatModel.remoteHostId()
val userServers = getUserServers(rh) ?: listOf()
if (!userServers.hasNtfServer()) {
Log.d(TAG, "User doesn't have any NTF server")
showMissingNTFDialog(scope, userServers)
showMissingNTFDialog(scope, rh, userServers)
// After coming back from the server view, users will have to click on "Instant" again
return
}
@@ -64,7 +66,7 @@ object PushManager {
showSelectPushServiceDialog(context, distributors) {
UnifiedPush.saveDistributor(context, it)
register(context)
onSuccess
onSuccess()
}
}
}
@@ -81,13 +83,13 @@ object PushManager {
*/
private fun List<UserOperatorServers>.hasNtfServer(): Boolean {
// TODO: check if ntf server has a VAPID key
return this.any { it.ntfServers.any() }
return this.any { it.ntfServers.any { s -> s.enabled } }
}
/**
* Show a dialog to inform about missing NTF server
*/
private fun showMissingNTFDialog(scope: CoroutineScope, userServers: List<UserOperatorServers>) = AlertManager.shared.showAlert {
private fun showMissingNTFDialog(scope: CoroutineScope, rh: Long?, userServers: List<UserOperatorServers>) = AlertManager.shared.showAlert {
AlertDialog(
onDismissRequest = AlertManager.shared::hideAlert,
title = {
@@ -109,7 +111,7 @@ object PushManager {
confirmButton = {
TextButton(onClick = {
AlertManager.shared.hideAlert()
showAddServerDialog(scope, userServers)
showAddServerDialog(scope, rh, userServers)
}) { Text(stringResource(MR.strings.smp_servers_add)) }
},
// Ignore
@@ -123,10 +125,10 @@ object PushManager {
/**
* Dialog to add a server, manually or with a QR code
*/
private fun showAddServerDialog(scope: CoroutineScope, userServers: List<UserOperatorServers>) {
private fun showAddServerDialog(scope: CoroutineScope, rh: Long?, userServers: List<UserOperatorServers>) {
val userServersState = mutableStateOf(userServers)
val serverErrors = mutableStateOf(listOf<UserServersError>())
showAddServerDialog(scope, userServersState, serverErrors, null)
showAddServerDialog(scope, userServersState, serverErrors, rh)
}
/**