mirror of
https://github.com/simplex-chat/simplex-chat.git
synced 2026-09-09 22:36:18 +00:00
Add support for Push notifications
This commit is contained in:
@@ -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)
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user