mirror of
https://github.com/ouchadam/small-talk.git
synced 2025-03-08 07:48:33 +01:00
adding support for unified push
This commit is contained in:
parent
41da7a2af9
commit
0c35481bda
app/src/main/kotlin/app/dapk/st/graph
domains
features
matrix/services/push/src/main/kotlin/app/dapk/st/matrix/push
@ -92,7 +92,7 @@ internal class AppModule(context: Application, logger: MatrixLogger) {
|
|||||||
private val imageLoaderModule = ImageLoaderModule(context)
|
private val imageLoaderModule = ImageLoaderModule(context)
|
||||||
|
|
||||||
private val matrixModules = MatrixModules(storeModule, trackingModule, workModule, logger, coroutineDispatchers, context.contentResolver)
|
private val matrixModules = MatrixModules(storeModule, trackingModule, workModule, logger, coroutineDispatchers, context.contentResolver)
|
||||||
val domainModules = DomainModules(matrixModules, trackingModule.errorTracker, workModule, storeModule, context)
|
val domainModules = DomainModules(matrixModules, trackingModule.errorTracker, workModule, storeModule, context, coroutineDispatchers)
|
||||||
|
|
||||||
val coreAndroidModule = CoreAndroidModule(intentFactory = object : IntentFactory {
|
val coreAndroidModule = CoreAndroidModule(intentFactory = object : IntentFactory {
|
||||||
override fun notificationOpenApp(context: Context) = PendingIntent.getActivity(
|
override fun notificationOpenApp(context: Context) = PendingIntent.getActivity(
|
||||||
@ -180,6 +180,7 @@ internal class FeatureModules internal constructor(
|
|||||||
val settingsModule by unsafeLazy {
|
val settingsModule by unsafeLazy {
|
||||||
SettingsModule(
|
SettingsModule(
|
||||||
storeModule.value,
|
storeModule.value,
|
||||||
|
pushModule,
|
||||||
matrixModules.crypto,
|
matrixModules.crypto,
|
||||||
matrixModules.sync,
|
matrixModules.sync,
|
||||||
context.contentResolver,
|
context.contentResolver,
|
||||||
@ -424,6 +425,7 @@ internal class DomainModules(
|
|||||||
private val workModule: WorkModule,
|
private val workModule: WorkModule,
|
||||||
private val storeModule: Lazy<StoreModule>,
|
private val storeModule: Lazy<StoreModule>,
|
||||||
private val context: Application,
|
private val context: Application,
|
||||||
|
private val dispatchers: CoroutineDispatchers,
|
||||||
) {
|
) {
|
||||||
|
|
||||||
val pushModule by unsafeLazy {
|
val pushModule by unsafeLazy {
|
||||||
@ -434,7 +436,13 @@ internal class DomainModules(
|
|||||||
matrixModules.sync,
|
matrixModules.sync,
|
||||||
store.roomStore(),
|
store.roomStore(),
|
||||||
)
|
)
|
||||||
PushModule(matrixModules.push, errorTracker, pushHandler, context)
|
PushModule(
|
||||||
|
errorTracker,
|
||||||
|
pushHandler,
|
||||||
|
context,
|
||||||
|
dispatchers,
|
||||||
|
SharedPreferencesDelegate(context.applicationContext, fileName = "dapk-user-preferences", dispatchers)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
val taskRunnerModule by unsafeLazy { TaskRunnerModule(TaskRunnerAdapter(matrixModules.matrix::run, AppTaskRunner(matrixModules.push))) }
|
val taskRunnerModule by unsafeLazy { TaskRunnerModule(TaskRunnerAdapter(matrixModules.matrix::run, AppTaskRunner(matrixModules.push))) }
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,10 @@
|
|||||||
package app.dapk.st.graph
|
package app.dapk.st.graph
|
||||||
|
|
||||||
import app.dapk.st.matrix.push.PushService
|
import app.dapk.st.matrix.push.PushService
|
||||||
|
import app.dapk.st.push.PushTokenPayload
|
||||||
import app.dapk.st.work.TaskRunner
|
import app.dapk.st.work.TaskRunner
|
||||||
import io.ktor.client.plugins.*
|
import io.ktor.client.plugins.*
|
||||||
|
import kotlinx.serialization.json.Json
|
||||||
|
|
||||||
class AppTaskRunner(
|
class AppTaskRunner(
|
||||||
private val pushService: PushService,
|
private val pushService: PushService,
|
||||||
@ -12,7 +14,8 @@ class AppTaskRunner(
|
|||||||
return when (val type = workTask.task.type) {
|
return when (val type = workTask.task.type) {
|
||||||
"push_token" -> {
|
"push_token" -> {
|
||||||
runCatching {
|
runCatching {
|
||||||
pushService.registerPush(workTask.task.jsonPayload)
|
val payload = Json.decodeFromString(PushTokenPayload.serializer(), workTask.task.jsonPayload)
|
||||||
|
pushService.registerPush(payload.token, payload.gatewayUrl)
|
||||||
}.fold(
|
}.fold(
|
||||||
onSuccess = { TaskRunner.TaskResult.Success(workTask.source) },
|
onSuccess = { TaskRunner.TaskResult.Success(workTask.source) },
|
||||||
onFailure = {
|
onFailure = {
|
||||||
@ -25,9 +28,10 @@ class AppTaskRunner(
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
else -> throw IllegalArgumentException("Unknown work type: $type")
|
else -> throw IllegalArgumentException("Unknown work type: $type")
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,10 +1,13 @@
|
|||||||
applyAndroidLibraryModule(project)
|
applyAndroidLibraryModule(project)
|
||||||
|
apply plugin: "org.jetbrains.kotlin.plugin.serialization"
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation project(':core')
|
implementation project(':core')
|
||||||
implementation project(':domains:android:core')
|
implementation project(':domains:android:core')
|
||||||
|
implementation project(':domains:store')
|
||||||
implementation project(':matrix:services:push')
|
implementation project(':matrix:services:push')
|
||||||
implementation platform('com.google.firebase:firebase-bom:29.0.3')
|
implementation platform('com.google.firebase:firebase-bom:29.0.3')
|
||||||
implementation 'com.google.firebase:firebase-messaging'
|
implementation 'com.google.firebase:firebase-messaging'
|
||||||
|
implementation Dependencies.mavenCentral.kotlinSerializationJson
|
||||||
implementation Dependencies.jitPack.unifiedPush
|
implementation Dependencies.jitPack.unifiedPush
|
||||||
}
|
}
|
||||||
|
@ -11,7 +11,7 @@
|
|||||||
</intent-filter>
|
</intent-filter>
|
||||||
</service>
|
</service>
|
||||||
|
|
||||||
<receiver android:exported="true" android:enabled="true" android:name=".unifiedpush.CustomR">
|
<receiver android:exported="true" android:enabled="true" android:name=".unifiedpush.UnifiedPushMessageReceiver">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="org.unifiedpush.android.connector.MESSAGE"/>
|
<action android:name="org.unifiedpush.android.connector.MESSAGE"/>
|
||||||
<action android:name="org.unifiedpush.android.connector.UNREGISTERED"/>
|
<action android:name="org.unifiedpush.android.connector.UNREGISTERED"/>
|
||||||
|
@ -2,8 +2,16 @@ package app.dapk.st.push
|
|||||||
|
|
||||||
import app.dapk.st.matrix.common.EventId
|
import app.dapk.st.matrix.common.EventId
|
||||||
import app.dapk.st.matrix.common.RoomId
|
import app.dapk.st.matrix.common.RoomId
|
||||||
|
import kotlinx.serialization.SerialName
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
|
||||||
interface PushHandler {
|
interface PushHandler {
|
||||||
fun onNewToken(token: String)
|
fun onNewToken(payload: PushTokenPayload)
|
||||||
fun onMessageReceived(eventId: EventId?, roomId: RoomId?)
|
fun onMessageReceived(eventId: EventId?, roomId: RoomId?)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class PushTokenPayload(
|
||||||
|
@SerialName("token") val token: String,
|
||||||
|
@SerialName("gateway_url") val gatewayUrl: String,
|
||||||
|
)
|
@ -1,24 +1,40 @@
|
|||||||
package app.dapk.st.push
|
package app.dapk.st.push
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
|
import app.dapk.st.core.CoroutineDispatchers
|
||||||
import app.dapk.st.core.ProvidableModule
|
import app.dapk.st.core.ProvidableModule
|
||||||
import app.dapk.st.core.extensions.ErrorTracker
|
import app.dapk.st.core.extensions.ErrorTracker
|
||||||
import app.dapk.st.matrix.push.PushService
|
import app.dapk.st.core.extensions.unsafeLazy
|
||||||
import app.dapk.st.push.firebase.RegisterFirebasePushTokenUseCase
|
import app.dapk.st.domain.Preferences
|
||||||
|
import app.dapk.st.domain.push.PushTokenRegistrarPreferences
|
||||||
|
import app.dapk.st.push.firebase.FirebasePushTokenRegistrar
|
||||||
|
import app.dapk.st.push.unifiedpush.UnifiedPushRegistrar
|
||||||
|
|
||||||
class PushModule(
|
class PushModule(
|
||||||
private val pushService: PushService,
|
|
||||||
private val errorTracker: ErrorTracker,
|
private val errorTracker: ErrorTracker,
|
||||||
private val pushHandler: PushHandler,
|
private val pushHandler: PushHandler,
|
||||||
private val context: Context,
|
private val context: Context,
|
||||||
|
private val dispatchers: CoroutineDispatchers,
|
||||||
|
private val preferences: Preferences,
|
||||||
) : ProvidableModule {
|
) : ProvidableModule {
|
||||||
|
|
||||||
fun pushTokenRegistrar(): PushTokenRegistrar = RegisterFirebasePushTokenUseCase(
|
private val registrars by unsafeLazy {
|
||||||
pushService,
|
PushTokenRegistrars(
|
||||||
errorTracker,
|
context,
|
||||||
context,
|
FirebasePushTokenRegistrar(
|
||||||
)
|
errorTracker,
|
||||||
|
context,
|
||||||
|
pushHandler,
|
||||||
|
),
|
||||||
|
UnifiedPushRegistrar(context),
|
||||||
|
PushTokenRegistrarPreferences(preferences)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun pushTokenRegistrars() = registrars
|
||||||
|
|
||||||
|
fun pushTokenRegistrar(): PushTokenRegistrar = pushTokenRegistrars()
|
||||||
fun pushHandler() = pushHandler
|
fun pushHandler() = pushHandler
|
||||||
|
fun dispatcher() = dispatchers
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -3,4 +3,4 @@ package app.dapk.st.push
|
|||||||
interface PushTokenRegistrar {
|
interface PushTokenRegistrar {
|
||||||
suspend fun registerCurrentToken()
|
suspend fun registerCurrentToken()
|
||||||
fun unregister()
|
fun unregister()
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,77 @@
|
|||||||
|
package app.dapk.st.push
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import app.dapk.st.domain.push.PushTokenRegistrarPreferences
|
||||||
|
import app.dapk.st.push.firebase.FirebasePushTokenRegistrar
|
||||||
|
import app.dapk.st.push.unifiedpush.UnifiedPushRegistrar
|
||||||
|
import kotlinx.coroutines.runBlocking
|
||||||
|
import org.unifiedpush.android.connector.UnifiedPush
|
||||||
|
|
||||||
|
private val FIREBASE_OPTION = Registrar("Google - Firebase (FCM)")
|
||||||
|
private val NONE = Registrar("None")
|
||||||
|
|
||||||
|
class PushTokenRegistrars(
|
||||||
|
private val context: Context,
|
||||||
|
private val firebasePushTokenRegistrar: FirebasePushTokenRegistrar,
|
||||||
|
private val unifiedPushRegistrar: UnifiedPushRegistrar,
|
||||||
|
private val pushTokenStore: PushTokenRegistrarPreferences,
|
||||||
|
) : PushTokenRegistrar {
|
||||||
|
|
||||||
|
private var selection: Registrar = runBlocking { pushTokenStore.currentSelection()?.let { Registrar(it) } } ?: FIREBASE_OPTION
|
||||||
|
|
||||||
|
fun options(): List<Registrar> {
|
||||||
|
return listOf(NONE, FIREBASE_OPTION) + UnifiedPush.getDistributors(context).map { Registrar(it) }
|
||||||
|
}
|
||||||
|
|
||||||
|
fun currentSelection() = selection
|
||||||
|
|
||||||
|
suspend fun makeSelection(option: Registrar) {
|
||||||
|
selection = option
|
||||||
|
pushTokenStore.store(option.id)
|
||||||
|
when (option) {
|
||||||
|
NONE -> {
|
||||||
|
firebasePushTokenRegistrar.unregister()
|
||||||
|
unifiedPushRegistrar.unregister()
|
||||||
|
}
|
||||||
|
|
||||||
|
FIREBASE_OPTION -> {
|
||||||
|
unifiedPushRegistrar.unregister()
|
||||||
|
firebasePushTokenRegistrar.registerCurrentToken()
|
||||||
|
}
|
||||||
|
|
||||||
|
else -> {
|
||||||
|
firebasePushTokenRegistrar.unregister()
|
||||||
|
unifiedPushRegistrar.registerSelection(option)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun registerCurrentToken() {
|
||||||
|
when (selection) {
|
||||||
|
FIREBASE_OPTION -> firebasePushTokenRegistrar.registerCurrentToken()
|
||||||
|
NONE -> {
|
||||||
|
// do nothing
|
||||||
|
}
|
||||||
|
|
||||||
|
else -> unifiedPushRegistrar.registerCurrentToken()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun unregister() {
|
||||||
|
when (selection) {
|
||||||
|
FIREBASE_OPTION -> firebasePushTokenRegistrar.unregister()
|
||||||
|
NONE -> {
|
||||||
|
runCatching {
|
||||||
|
firebasePushTokenRegistrar.unregister()
|
||||||
|
unifiedPushRegistrar.unregister()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
else -> unifiedPushRegistrar.unregister()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@JvmInline
|
||||||
|
value class Registrar(val id: String)
|
@ -1,22 +1,34 @@
|
|||||||
package app.dapk.st.push.firebase
|
package app.dapk.st.push.firebase
|
||||||
|
|
||||||
|
import app.dapk.st.core.AppLogTag
|
||||||
import app.dapk.st.core.extensions.unsafeLazy
|
import app.dapk.st.core.extensions.unsafeLazy
|
||||||
|
import app.dapk.st.core.log
|
||||||
import app.dapk.st.core.module
|
import app.dapk.st.core.module
|
||||||
import app.dapk.st.matrix.common.EventId
|
import app.dapk.st.matrix.common.EventId
|
||||||
import app.dapk.st.matrix.common.RoomId
|
import app.dapk.st.matrix.common.RoomId
|
||||||
import app.dapk.st.push.PushModule
|
import app.dapk.st.push.PushModule
|
||||||
|
import app.dapk.st.push.PushTokenPayload
|
||||||
import com.google.firebase.messaging.FirebaseMessagingService
|
import com.google.firebase.messaging.FirebaseMessagingService
|
||||||
import com.google.firebase.messaging.RemoteMessage
|
import com.google.firebase.messaging.RemoteMessage
|
||||||
|
|
||||||
|
private const val SYGNAL_GATEWAY = "https://sygnal.dapk.app/_matrix/push/v1/notify"
|
||||||
|
|
||||||
class FirebasePushService : FirebaseMessagingService() {
|
class FirebasePushService : FirebaseMessagingService() {
|
||||||
|
|
||||||
private val handler by unsafeLazy { module<PushModule>().pushHandler() }
|
private val handler by unsafeLazy { module<PushModule>().pushHandler() }
|
||||||
|
|
||||||
override fun onNewToken(token: String) {
|
override fun onNewToken(token: String) {
|
||||||
handler.onNewToken(token)
|
log(AppLogTag.PUSH, "FCM onNewToken")
|
||||||
|
handler.onNewToken(
|
||||||
|
PushTokenPayload(
|
||||||
|
token = token,
|
||||||
|
gatewayUrl = SYGNAL_GATEWAY,
|
||||||
|
)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onMessageReceived(message: RemoteMessage) {
|
override fun onMessageReceived(message: RemoteMessage) {
|
||||||
|
log(AppLogTag.PUSH, "FCM onMessage")
|
||||||
val eventId = message.data["event_id"]?.let { EventId(it) }
|
val eventId = message.data["event_id"]?.let { EventId(it) }
|
||||||
val roomId = message.data["room_id"]?.let { RoomId(it) }
|
val roomId = message.data["room_id"]?.let { RoomId(it) }
|
||||||
handler.onMessageReceived(eventId, roomId)
|
handler.onMessageReceived(eventId, roomId)
|
||||||
|
61
domains/android/push/src/main/kotlin/app/dapk/st/push/firebase/FirebasePushTokenRegistrar.kt
Normal file
61
domains/android/push/src/main/kotlin/app/dapk/st/push/firebase/FirebasePushTokenRegistrar.kt
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
package app.dapk.st.push.firebase
|
||||||
|
|
||||||
|
import android.content.ComponentName
|
||||||
|
import android.content.Context
|
||||||
|
import android.content.Intent
|
||||||
|
import android.content.pm.PackageManager
|
||||||
|
import app.dapk.st.core.AppLogTag
|
||||||
|
import app.dapk.st.core.extensions.CrashScope
|
||||||
|
import app.dapk.st.core.extensions.ErrorTracker
|
||||||
|
import app.dapk.st.core.log
|
||||||
|
import app.dapk.st.push.PushHandler
|
||||||
|
import app.dapk.st.push.PushTokenPayload
|
||||||
|
import app.dapk.st.push.PushTokenRegistrar
|
||||||
|
import app.dapk.st.push.unifiedpush.UnifiedPushMessageReceiver
|
||||||
|
import com.google.firebase.messaging.FirebaseMessaging
|
||||||
|
|
||||||
|
private const val SYGNAL_GATEWAY = "https://sygnal.dapk.app/_matrix/push/v1/notify"
|
||||||
|
|
||||||
|
class FirebasePushTokenRegistrar(
|
||||||
|
override val errorTracker: ErrorTracker,
|
||||||
|
private val context: Context,
|
||||||
|
private val pushHandler: PushHandler,
|
||||||
|
) : PushTokenRegistrar, CrashScope {
|
||||||
|
|
||||||
|
override suspend fun registerCurrentToken() {
|
||||||
|
log(AppLogTag.PUSH, "FCM - register current token")
|
||||||
|
context.packageManager.setComponentEnabledSetting(
|
||||||
|
ComponentName(context, FirebasePushService::class.java),
|
||||||
|
PackageManager.COMPONENT_ENABLED_STATE_ENABLED,
|
||||||
|
PackageManager.DONT_KILL_APP,
|
||||||
|
)
|
||||||
|
|
||||||
|
kotlin.runCatching {
|
||||||
|
FirebaseMessaging.getInstance().token().also {
|
||||||
|
pushHandler.onNewToken(
|
||||||
|
PushTokenPayload(
|
||||||
|
token = it,
|
||||||
|
gatewayUrl = SYGNAL_GATEWAY,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.trackFailure()
|
||||||
|
.onSuccess {
|
||||||
|
log(AppLogTag.PUSH, "registered new push token")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun unregister() {
|
||||||
|
log(AppLogTag.PUSH, "FCM - unregister")
|
||||||
|
FirebaseMessaging.getInstance().deleteToken()
|
||||||
|
context.stopService(Intent(context, FirebasePushService::class.java))
|
||||||
|
|
||||||
|
context.packageManager.setComponentEnabledSetting(
|
||||||
|
ComponentName(context, FirebasePushService::class.java),
|
||||||
|
PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
|
||||||
|
PackageManager.DONT_KILL_APP,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
36
domains/android/push/src/main/kotlin/app/dapk/st/push/firebase/RegisterFirebasePushTokenUseCase.kt
36
domains/android/push/src/main/kotlin/app/dapk/st/push/firebase/RegisterFirebasePushTokenUseCase.kt
@ -1,36 +0,0 @@
|
|||||||
package app.dapk.st.push.firebase
|
|
||||||
|
|
||||||
import android.content.Context
|
|
||||||
import android.content.Intent
|
|
||||||
import app.dapk.st.core.AppLogTag
|
|
||||||
import app.dapk.st.core.extensions.CrashScope
|
|
||||||
import app.dapk.st.core.extensions.ErrorTracker
|
|
||||||
import app.dapk.st.core.log
|
|
||||||
import app.dapk.st.matrix.push.PushService
|
|
||||||
import app.dapk.st.push.PushTokenRegistrar
|
|
||||||
import com.google.firebase.messaging.FirebaseMessaging
|
|
||||||
|
|
||||||
internal class RegisterFirebasePushTokenUseCase(
|
|
||||||
private val pushService: PushService,
|
|
||||||
override val errorTracker: ErrorTracker,
|
|
||||||
private val context: Context,
|
|
||||||
) : PushTokenRegistrar, CrashScope {
|
|
||||||
|
|
||||||
override suspend fun registerCurrentToken() {
|
|
||||||
kotlin.runCatching {
|
|
||||||
FirebaseMessaging.getInstance().token().also {
|
|
||||||
pushService.registerPush(it)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.trackFailure()
|
|
||||||
.onSuccess {
|
|
||||||
log(AppLogTag.PUSH, "registered new push token")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun unregister() {
|
|
||||||
FirebaseMessaging.getInstance().deleteToken()
|
|
||||||
context.stopService(Intent(context, FirebasePushService::class.java))
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
77
domains/android/push/src/main/kotlin/app/dapk/st/push/unifiedpush/UnifiedPushMessageReceiver.kt
Normal file
77
domains/android/push/src/main/kotlin/app/dapk/st/push/unifiedpush/UnifiedPushMessageReceiver.kt
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
package app.dapk.st.push.unifiedpush
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import app.dapk.st.core.AppLogTag
|
||||||
|
import app.dapk.st.core.log
|
||||||
|
import app.dapk.st.core.module
|
||||||
|
import app.dapk.st.matrix.common.EventId
|
||||||
|
import app.dapk.st.matrix.common.RoomId
|
||||||
|
import app.dapk.st.push.PushModule
|
||||||
|
import app.dapk.st.push.PushTokenPayload
|
||||||
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
import kotlinx.coroutines.SupervisorJob
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import kotlinx.coroutines.withContext
|
||||||
|
import kotlinx.serialization.SerialName
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
import kotlinx.serialization.json.Json
|
||||||
|
import org.unifiedpush.android.connector.MessagingReceiver
|
||||||
|
import java.net.URL
|
||||||
|
|
||||||
|
private val json = Json { ignoreUnknownKeys = true }
|
||||||
|
|
||||||
|
private const val FALLBACK_UNIFIED_PUSH_GATEWAY = "https://matrix.gateway.unifiedpush.org/_matrix/push/v1/notify"
|
||||||
|
|
||||||
|
class UnifiedPushMessageReceiver : MessagingReceiver() {
|
||||||
|
|
||||||
|
private val scope = CoroutineScope(SupervisorJob())
|
||||||
|
|
||||||
|
override fun onMessage(context: Context, message: ByteArray, instance: String) {
|
||||||
|
log(AppLogTag.PUSH, "UnifiedPush onMessage, $message")
|
||||||
|
val module = context.module<PushModule>()
|
||||||
|
val handler = module.pushHandler()
|
||||||
|
scope.launch {
|
||||||
|
withContext(module.dispatcher().io) {
|
||||||
|
val payload = json.decodeFromString(UnifiedPushMessagePayload.serializer(), String(message))
|
||||||
|
handler.onMessageReceived(payload.notification.eventId?.let { EventId(it) }, payload.notification.roomId?.let { RoomId(it) })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onNewEndpoint(context: Context, endpoint: String, instance: String) {
|
||||||
|
log(AppLogTag.PUSH, "UnifiedPush onNewEndpoint $endpoint")
|
||||||
|
val module = context.module<PushModule>()
|
||||||
|
val handler = module.pushHandler()
|
||||||
|
scope.launch {
|
||||||
|
withContext(module.dispatcher().io) {
|
||||||
|
val matrixEndpoint = URL(endpoint).let { URL("${it.protocol}://${it.host}/_matrix/push/v1/notify") }
|
||||||
|
val content = matrixEndpoint.openStream().use { String(it.readBytes()) }
|
||||||
|
val gatewayUrl = when {
|
||||||
|
content.contains("\"gateway\":\"matrix\"") -> matrixEndpoint.toString()
|
||||||
|
else -> FALLBACK_UNIFIED_PUSH_GATEWAY
|
||||||
|
}
|
||||||
|
handler.onNewToken(PushTokenPayload(token = endpoint, gatewayUrl = gatewayUrl))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onRegistrationFailed(context: Context, instance: String) {
|
||||||
|
log(AppLogTag.PUSH, "UnifiedPush onRegistrationFailed")
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onUnregistered(context: Context, instance: String) {
|
||||||
|
log(AppLogTag.PUSH, "UnifiedPush onUnregistered")
|
||||||
|
}
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
private data class UnifiedPushMessagePayload(
|
||||||
|
@SerialName("notification") val notification: Notification,
|
||||||
|
) {
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class Notification(
|
||||||
|
@SerialName("event_id") val eventId: String?,
|
||||||
|
@SerialName("room_id") val roomId: String?,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
47
domains/android/push/src/main/kotlin/app/dapk/st/push/unifiedpush/UnifiedPushRegistrar.kt
Normal file
47
domains/android/push/src/main/kotlin/app/dapk/st/push/unifiedpush/UnifiedPushRegistrar.kt
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
package app.dapk.st.push.unifiedpush
|
||||||
|
|
||||||
|
import android.content.ComponentName
|
||||||
|
import android.content.Context
|
||||||
|
import android.content.pm.PackageManager
|
||||||
|
import app.dapk.st.core.AppLogTag
|
||||||
|
import app.dapk.st.core.log
|
||||||
|
import app.dapk.st.push.PushTokenRegistrar
|
||||||
|
import app.dapk.st.push.Registrar
|
||||||
|
import org.unifiedpush.android.connector.UnifiedPush
|
||||||
|
|
||||||
|
class UnifiedPushRegistrar(
|
||||||
|
private val context: Context,
|
||||||
|
) : PushTokenRegistrar {
|
||||||
|
|
||||||
|
fun registerSelection(registrar: Registrar) {
|
||||||
|
log(AppLogTag.PUSH, "UnifiedPush - register: $registrar")
|
||||||
|
UnifiedPush.saveDistributor(context, registrar.id)
|
||||||
|
registerApp()
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun registerCurrentToken() {
|
||||||
|
log(AppLogTag.PUSH, "UnifiedPush - register current token")
|
||||||
|
if (UnifiedPush.getDistributor(context).isNotEmpty()) {
|
||||||
|
registerApp()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun registerApp() {
|
||||||
|
context.packageManager.setComponentEnabledSetting(
|
||||||
|
ComponentName(context, UnifiedPushMessageReceiver::class.java),
|
||||||
|
PackageManager.COMPONENT_ENABLED_STATE_ENABLED,
|
||||||
|
PackageManager.DONT_KILL_APP,
|
||||||
|
)
|
||||||
|
UnifiedPush.registerApp(context)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun unregister() {
|
||||||
|
UnifiedPush.unregisterApp(context)
|
||||||
|
context.packageManager.setComponentEnabledSetting(
|
||||||
|
ComponentName(context, UnifiedPushMessageReceiver::class.java),
|
||||||
|
PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
|
||||||
|
PackageManager.DONT_KILL_APP,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -7,6 +7,7 @@ import app.dapk.st.core.extensions.unsafeLazy
|
|||||||
import app.dapk.st.domain.eventlog.EventLogPersistence
|
import app.dapk.st.domain.eventlog.EventLogPersistence
|
||||||
import app.dapk.st.domain.localecho.LocalEchoPersistence
|
import app.dapk.st.domain.localecho.LocalEchoPersistence
|
||||||
import app.dapk.st.domain.profile.ProfilePersistence
|
import app.dapk.st.domain.profile.ProfilePersistence
|
||||||
|
import app.dapk.st.domain.push.PushTokenRegistrarPreferences
|
||||||
import app.dapk.st.domain.sync.OverviewPersistence
|
import app.dapk.st.domain.sync.OverviewPersistence
|
||||||
import app.dapk.st.domain.sync.RoomPersistence
|
import app.dapk.st.domain.sync.RoomPersistence
|
||||||
import app.dapk.st.matrix.common.CredentialsStore
|
import app.dapk.st.matrix.common.CredentialsStore
|
||||||
@ -34,6 +35,8 @@ class StoreModule(
|
|||||||
fun filterStore(): FilterStore = FilterPreferences(preferences)
|
fun filterStore(): FilterStore = FilterPreferences(preferences)
|
||||||
val localEchoStore: LocalEchoStore by unsafeLazy { LocalEchoPersistence(errorTracker, database) }
|
val localEchoStore: LocalEchoStore by unsafeLazy { LocalEchoPersistence(errorTracker, database) }
|
||||||
|
|
||||||
|
fun pushStore() = PushTokenRegistrarPreferences(preferences)
|
||||||
|
|
||||||
fun applicationStore() = ApplicationPreferences(preferences)
|
fun applicationStore() = ApplicationPreferences(preferences)
|
||||||
|
|
||||||
fun olmStore() = OlmPersistence(database, credentialsStore())
|
fun olmStore() = OlmPersistence(database, credentialsStore())
|
||||||
|
16
domains/store/src/main/kotlin/app/dapk/st/domain/push/PushTokenRegistrarPreferences.kt
Normal file
16
domains/store/src/main/kotlin/app/dapk/st/domain/push/PushTokenRegistrarPreferences.kt
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
package app.dapk.st.domain.push
|
||||||
|
|
||||||
|
import app.dapk.st.domain.Preferences
|
||||||
|
|
||||||
|
private const val SELECTION_KEY = "push_token_selection"
|
||||||
|
|
||||||
|
class PushTokenRegistrarPreferences(
|
||||||
|
private val preferences: Preferences,
|
||||||
|
) {
|
||||||
|
|
||||||
|
suspend fun currentSelection() = preferences.readString(SELECTION_KEY)
|
||||||
|
|
||||||
|
suspend fun store(registrar: String) {
|
||||||
|
preferences.store(SELECTION_KEY, registrar)
|
||||||
|
}
|
||||||
|
}
|
@ -14,6 +14,7 @@ dependencies {
|
|||||||
|
|
||||||
implementation platform('com.google.firebase:firebase-bom:29.0.3')
|
implementation platform('com.google.firebase:firebase-bom:29.0.3')
|
||||||
implementation 'com.google.firebase:firebase-messaging'
|
implementation 'com.google.firebase:firebase-messaging'
|
||||||
|
implementation Dependencies.mavenCentral.kotlinSerializationJson
|
||||||
|
|
||||||
kotlinTest(it)
|
kotlinTest(it)
|
||||||
|
|
||||||
|
@ -8,9 +8,11 @@ import app.dapk.st.matrix.common.RoomId
|
|||||||
import app.dapk.st.matrix.sync.RoomStore
|
import app.dapk.st.matrix.sync.RoomStore
|
||||||
import app.dapk.st.matrix.sync.SyncService
|
import app.dapk.st.matrix.sync.SyncService
|
||||||
import app.dapk.st.push.PushHandler
|
import app.dapk.st.push.PushHandler
|
||||||
|
import app.dapk.st.push.PushTokenPayload
|
||||||
import app.dapk.st.work.WorkScheduler
|
import app.dapk.st.work.WorkScheduler
|
||||||
import kotlinx.coroutines.*
|
import kotlinx.coroutines.*
|
||||||
import kotlinx.coroutines.flow.*
|
import kotlinx.coroutines.flow.*
|
||||||
|
import kotlinx.serialization.json.Json
|
||||||
|
|
||||||
private var previousJob: Job? = null
|
private var previousJob: Job? = null
|
||||||
|
|
||||||
@ -22,13 +24,13 @@ class MatrixPushHandler(
|
|||||||
private val roomStore: RoomStore,
|
private val roomStore: RoomStore,
|
||||||
) : PushHandler {
|
) : PushHandler {
|
||||||
|
|
||||||
override fun onNewToken(token: String) {
|
override fun onNewToken(payload: PushTokenPayload) {
|
||||||
log(AppLogTag.PUSH, "new push token received")
|
log(AppLogTag.PUSH, "new push token received")
|
||||||
workScheduler.schedule(
|
workScheduler.schedule(
|
||||||
WorkScheduler.WorkTask(
|
WorkScheduler.WorkTask(
|
||||||
type = "push_token",
|
type = "push_token",
|
||||||
jobId = 2,
|
jobId = 2,
|
||||||
jsonPayload = token
|
jsonPayload = Json.encodeToString(PushTokenPayload.serializer(), payload)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -5,6 +5,7 @@ dependencies {
|
|||||||
implementation project(":matrix:services:crypto")
|
implementation project(":matrix:services:crypto")
|
||||||
implementation project(":features:navigator")
|
implementation project(":features:navigator")
|
||||||
implementation project(':domains:store')
|
implementation project(':domains:store')
|
||||||
|
implementation project(':domains:android:push')
|
||||||
implementation project(":domains:android:compose-core")
|
implementation project(":domains:android:compose-core")
|
||||||
implementation project(":domains:android:viewmodel")
|
implementation project(":domains:android:viewmodel")
|
||||||
implementation project(":design-library")
|
implementation project(":design-library")
|
||||||
|
@ -1,13 +1,18 @@
|
|||||||
package app.dapk.st.settings
|
package app.dapk.st.settings
|
||||||
|
|
||||||
import app.dapk.st.core.BuildMeta
|
import app.dapk.st.core.BuildMeta
|
||||||
|
import app.dapk.st.push.PushTokenRegistrars
|
||||||
|
|
||||||
internal class SettingsItemFactory(private val buildMeta: BuildMeta) {
|
internal class SettingsItemFactory(
|
||||||
|
private val buildMeta: BuildMeta,
|
||||||
|
private val pushTokenRegistrars: PushTokenRegistrars,
|
||||||
|
) {
|
||||||
|
|
||||||
fun root() = listOf(
|
fun root() = listOf(
|
||||||
SettingItem.Header("General"),
|
SettingItem.Header("General"),
|
||||||
SettingItem.Text(SettingItem.Id.Encryption, "Encryption"),
|
SettingItem.Text(SettingItem.Id.Encryption, "Encryption"),
|
||||||
SettingItem.Text(SettingItem.Id.EventLog, "Event log"),
|
SettingItem.Text(SettingItem.Id.EventLog, "Event log"),
|
||||||
|
SettingItem.Text(SettingItem.Id.PushProvider, "Push provider", pushTokenRegistrars.currentSelection().id),
|
||||||
SettingItem.Header("Data"),
|
SettingItem.Header("Data"),
|
||||||
SettingItem.Text(SettingItem.Id.ClearCache, "Clear cache"),
|
SettingItem.Text(SettingItem.Id.ClearCache, "Clear cache"),
|
||||||
SettingItem.Header("Account"),
|
SettingItem.Header("Account"),
|
||||||
|
@ -7,10 +7,12 @@ import app.dapk.st.core.ProvidableModule
|
|||||||
import app.dapk.st.domain.StoreModule
|
import app.dapk.st.domain.StoreModule
|
||||||
import app.dapk.st.matrix.crypto.CryptoService
|
import app.dapk.st.matrix.crypto.CryptoService
|
||||||
import app.dapk.st.matrix.sync.SyncService
|
import app.dapk.st.matrix.sync.SyncService
|
||||||
|
import app.dapk.st.push.PushModule
|
||||||
import app.dapk.st.settings.eventlogger.EventLoggerViewModel
|
import app.dapk.st.settings.eventlogger.EventLoggerViewModel
|
||||||
|
|
||||||
class SettingsModule(
|
class SettingsModule(
|
||||||
private val storeModule: StoreModule,
|
private val storeModule: StoreModule,
|
||||||
|
private val pushModule: PushModule,
|
||||||
private val cryptoService: CryptoService,
|
private val cryptoService: CryptoService,
|
||||||
private val syncService: SyncService,
|
private val syncService: SyncService,
|
||||||
private val contentResolver: ContentResolver,
|
private val contentResolver: ContentResolver,
|
||||||
@ -24,7 +26,8 @@ class SettingsModule(
|
|||||||
cryptoService,
|
cryptoService,
|
||||||
syncService,
|
syncService,
|
||||||
UriFilenameResolver(contentResolver, coroutineDispatchers),
|
UriFilenameResolver(contentResolver, coroutineDispatchers),
|
||||||
SettingsItemFactory(buildMeta),
|
SettingsItemFactory(buildMeta, pushModule.pushTokenRegistrars()),
|
||||||
|
pushModule.pushTokenRegistrars(),
|
||||||
)
|
)
|
||||||
|
|
||||||
internal fun eventLogViewModel(): EventLoggerViewModel {
|
internal fun eventLogViewModel(): EventLoggerViewModel {
|
||||||
|
@ -68,6 +68,9 @@ internal fun SettingsScreen(viewModel: SettingsViewModel, onSignOut: () -> Unit,
|
|||||||
item(Page.Routes.encryption) {
|
item(Page.Routes.encryption) {
|
||||||
Encryption(viewModel, it)
|
Encryption(viewModel, it)
|
||||||
}
|
}
|
||||||
|
item(Page.Routes.pushProviders) {
|
||||||
|
PushProviders(viewModel, it)
|
||||||
|
}
|
||||||
item(Page.Routes.importRoomKeys) {
|
item(Page.Routes.importRoomKeys) {
|
||||||
when (it.importProgress) {
|
when (it.importProgress) {
|
||||||
null -> {
|
null -> {
|
||||||
@ -132,6 +135,7 @@ internal fun SettingsScreen(viewModel: SettingsViewModel, onSignOut: () -> Unit,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
is Lce.Content -> {
|
is Lce.Content -> {
|
||||||
Box(Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
|
Box(Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
|
||||||
Column(horizontalAlignment = Alignment.CenterHorizontally) {
|
Column(horizontalAlignment = Alignment.CenterHorizontally) {
|
||||||
@ -142,6 +146,7 @@ internal fun SettingsScreen(viewModel: SettingsViewModel, onSignOut: () -> Unit,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
is Lce.Error -> {
|
is Lce.Error -> {
|
||||||
Box(Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
|
Box(Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
|
||||||
Column(horizontalAlignment = Alignment.CenterHorizontally) {
|
Column(horizontalAlignment = Alignment.CenterHorizontally) {
|
||||||
@ -152,6 +157,7 @@ internal fun SettingsScreen(viewModel: SettingsViewModel, onSignOut: () -> Unit,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
is Lce.Loading -> CenteredLoading()
|
is Lce.Loading -> CenteredLoading()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -176,6 +182,7 @@ private fun RootSettings(page: Page.Root, onClick: (SettingItem) -> Unit) {
|
|||||||
|
|
||||||
SettingsTextRow(item.content, item.subtitle, itemOnClick)
|
SettingsTextRow(item.content, item.subtitle, itemOnClick)
|
||||||
}
|
}
|
||||||
|
|
||||||
is SettingItem.AccessToken -> {
|
is SettingItem.AccessToken -> {
|
||||||
Row(
|
Row(
|
||||||
Modifier
|
Modifier
|
||||||
@ -193,6 +200,7 @@ private fun RootSettings(page: Page.Root, onClick: (SettingItem) -> Unit) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
is SettingItem.Header -> Header(item.label)
|
is SettingItem.Header -> Header(item.label)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -203,6 +211,7 @@ private fun RootSettings(page: Page.Root, onClick: (SettingItem) -> Unit) {
|
|||||||
is Lce.Error -> {
|
is Lce.Error -> {
|
||||||
// TODO
|
// TODO
|
||||||
}
|
}
|
||||||
|
|
||||||
is Lce.Loading -> {
|
is Lce.Loading -> {
|
||||||
// TODO
|
// TODO
|
||||||
}
|
}
|
||||||
@ -216,6 +225,32 @@ private fun Encryption(viewModel: SettingsViewModel, page: Page.Security) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun PushProviders(viewModel: SettingsViewModel, state: Page.PushProviders) {
|
||||||
|
LaunchedEffect(true) {
|
||||||
|
viewModel.fetchPushProviders()
|
||||||
|
}
|
||||||
|
|
||||||
|
when (val lce = state.options) {
|
||||||
|
null -> {}
|
||||||
|
is Lce.Loading -> CenteredLoading()
|
||||||
|
is Lce.Content -> {
|
||||||
|
LazyColumn {
|
||||||
|
items(lce.value) {
|
||||||
|
Row(Modifier.padding(12.dp), verticalAlignment = Alignment.CenterVertically) {
|
||||||
|
RadioButton(selected = it == state.selection, onClick = { viewModel.selectPushProvider(it) })
|
||||||
|
Text(it.id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
is Lce.Error -> TODO()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
private fun SettingsViewModel.ObserveEvents(onSignOut: () -> Unit) {
|
private fun SettingsViewModel.ObserveEvents(onSignOut: () -> Unit) {
|
||||||
val context = LocalContext.current
|
val context = LocalContext.current
|
||||||
@ -228,10 +263,12 @@ private fun SettingsViewModel.ObserveEvents(onSignOut: () -> Unit) {
|
|||||||
clipboard.setPrimaryClip(ClipData.newPlainText("dapk token", it.content))
|
clipboard.setPrimaryClip(ClipData.newPlainText("dapk token", it.content))
|
||||||
Toast.makeText(context, it.message, Toast.LENGTH_SHORT).show()
|
Toast.makeText(context, it.message, Toast.LENGTH_SHORT).show()
|
||||||
}
|
}
|
||||||
|
|
||||||
is SettingsEvent.Toast -> Toast.makeText(context, it.message, Toast.LENGTH_SHORT).show()
|
is SettingsEvent.Toast -> Toast.makeText(context, it.message, Toast.LENGTH_SHORT).show()
|
||||||
OpenEventLog -> {
|
OpenEventLog -> {
|
||||||
context.startActivity(Intent(context, EventLogActivity::class.java))
|
context.startActivity(Intent(context, EventLogActivity::class.java))
|
||||||
}
|
}
|
||||||
|
|
||||||
is OpenUrl -> {
|
is OpenUrl -> {
|
||||||
context.startActivity(Intent(Intent.ACTION_VIEW).apply { data = it.url.toUri() })
|
context.startActivity(Intent(Intent.ACTION_VIEW).apply { data = it.url.toUri() })
|
||||||
}
|
}
|
||||||
|
@ -4,6 +4,7 @@ import android.net.Uri
|
|||||||
import app.dapk.st.core.Lce
|
import app.dapk.st.core.Lce
|
||||||
import app.dapk.st.design.components.Route
|
import app.dapk.st.design.components.Route
|
||||||
import app.dapk.st.design.components.SpiderPage
|
import app.dapk.st.design.components.SpiderPage
|
||||||
|
import app.dapk.st.push.Registrar
|
||||||
|
|
||||||
internal data class SettingsScreenState(
|
internal data class SettingsScreenState(
|
||||||
val page: SpiderPage<out Page>,
|
val page: SpiderPage<out Page>,
|
||||||
@ -17,9 +18,15 @@ internal sealed interface Page {
|
|||||||
val importProgress: Lce<Unit>? = null,
|
val importProgress: Lce<Unit>? = null,
|
||||||
) : Page
|
) : Page
|
||||||
|
|
||||||
|
data class PushProviders(
|
||||||
|
val selection: Registrar? = null,
|
||||||
|
val options: Lce<List<Registrar>>? = Lce.Loading()
|
||||||
|
) : Page
|
||||||
|
|
||||||
object Routes {
|
object Routes {
|
||||||
val root = Route<Root>("Settings")
|
val root = Route<Root>("Settings")
|
||||||
val encryption = Route<Page.Security>("Encryption")
|
val encryption = Route<Page.Security>("Encryption")
|
||||||
|
val pushProviders = Route<Page.PushProviders>("PushProviders")
|
||||||
val importRoomKeys = Route<Page.ImportRoomKey>("ImportRoomKey")
|
val importRoomKeys = Route<Page.ImportRoomKey>("ImportRoomKey")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -42,6 +49,7 @@ internal sealed interface SettingItem {
|
|||||||
AccessToken,
|
AccessToken,
|
||||||
ClearCache,
|
ClearCache,
|
||||||
EventLog,
|
EventLog,
|
||||||
|
PushProvider,
|
||||||
Encryption,
|
Encryption,
|
||||||
PrivacyPolicy,
|
PrivacyPolicy,
|
||||||
Ignored,
|
Ignored,
|
||||||
|
@ -8,6 +8,8 @@ import app.dapk.st.design.components.SpiderPage
|
|||||||
import app.dapk.st.domain.StoreCleaner
|
import app.dapk.st.domain.StoreCleaner
|
||||||
import app.dapk.st.matrix.crypto.CryptoService
|
import app.dapk.st.matrix.crypto.CryptoService
|
||||||
import app.dapk.st.matrix.sync.SyncService
|
import app.dapk.st.matrix.sync.SyncService
|
||||||
|
import app.dapk.st.push.PushTokenRegistrars
|
||||||
|
import app.dapk.st.push.Registrar
|
||||||
import app.dapk.st.settings.SettingItem.Id.*
|
import app.dapk.st.settings.SettingItem.Id.*
|
||||||
import app.dapk.st.settings.SettingsEvent.*
|
import app.dapk.st.settings.SettingsEvent.*
|
||||||
import app.dapk.st.viewmodel.DapkViewModel
|
import app.dapk.st.viewmodel.DapkViewModel
|
||||||
@ -24,6 +26,7 @@ internal class SettingsViewModel(
|
|||||||
private val syncService: SyncService,
|
private val syncService: SyncService,
|
||||||
private val uriFilenameResolver: UriFilenameResolver,
|
private val uriFilenameResolver: UriFilenameResolver,
|
||||||
private val settingsItemFactory: SettingsItemFactory,
|
private val settingsItemFactory: SettingsItemFactory,
|
||||||
|
private val pushTokenRegistrars: PushTokenRegistrars,
|
||||||
factory: MutableStateFactory<SettingsScreenState> = defaultStateFactory(),
|
factory: MutableStateFactory<SettingsScreenState> = defaultStateFactory(),
|
||||||
) : DapkViewModel<SettingsScreenState, SettingsEvent>(
|
) : DapkViewModel<SettingsScreenState, SettingsEvent>(
|
||||||
initialState = SettingsScreenState(SpiderPage(Page.Routes.root, "Settings", null, Page.Root(Lce.Loading()))),
|
initialState = SettingsScreenState(SpiderPage(Page.Routes.root, "Settings", null, Page.Root(Lce.Loading()))),
|
||||||
@ -50,39 +53,70 @@ internal class SettingsViewModel(
|
|||||||
_events.emit(SignedOut)
|
_events.emit(SignedOut)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
AccessToken -> {
|
AccessToken -> {
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
require(item is SettingItem.AccessToken)
|
require(item is SettingItem.AccessToken)
|
||||||
_events.emit(CopyToClipboard("Token copied", item.accessToken))
|
_events.emit(CopyToClipboard("Token copied", item.accessToken))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ClearCache -> {
|
ClearCache -> {
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
cacheCleaner.cleanCache(removeCredentials = false)
|
cacheCleaner.cleanCache(removeCredentials = false)
|
||||||
_events.emit(Toast(message = "Cache deleted"))
|
_events.emit(Toast(message = "Cache deleted"))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
EventLog -> {
|
EventLog -> {
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
_events.emit(OpenEventLog)
|
_events.emit(OpenEventLog)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Encryption -> {
|
Encryption -> {
|
||||||
updateState {
|
updateState {
|
||||||
copy(page = SpiderPage(Page.Routes.encryption, "Encryption", Page.Routes.root, Page.Security))
|
copy(page = SpiderPage(Page.Routes.encryption, "Encryption", Page.Routes.root, Page.Security))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
PrivacyPolicy -> {
|
PrivacyPolicy -> {
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
_events.emit(OpenUrl(PRIVACY_POLICY_URL))
|
_events.emit(OpenUrl(PRIVACY_POLICY_URL))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
PushProvider -> {
|
||||||
|
updateState {
|
||||||
|
copy(page = SpiderPage(Page.Routes.pushProviders, "Push providers", Page.Routes.root, Page.PushProviders()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Ignored -> {
|
Ignored -> {
|
||||||
// do nothing
|
// do nothing
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun fetchPushProviders() {
|
||||||
|
updatePageState<Page.PushProviders> { copy(options = Lce.Loading()) }
|
||||||
|
viewModelScope.launch {
|
||||||
|
updatePageState<Page.PushProviders> {
|
||||||
|
copy(
|
||||||
|
selection = pushTokenRegistrars.currentSelection(),
|
||||||
|
options = Lce.Content(pushTokenRegistrars.options())
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun selectPushProvider(registrar: Registrar) {
|
||||||
|
viewModelScope.launch {
|
||||||
|
pushTokenRegistrars.makeSelection(registrar)
|
||||||
|
fetchPushProviders()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fun importFromFileKeys(file: Uri, passphrase: String) {
|
fun importFromFileKeys(file: Uri, passphrase: String) {
|
||||||
updatePageState<Page.ImportRoomKey> { copy(importProgress = Lce.Loading()) }
|
updatePageState<Page.ImportRoomKey> { copy(importProgress = Lce.Loading()) }
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
|
@ -12,7 +12,7 @@ private val SERVICE_KEY = PushService::class
|
|||||||
|
|
||||||
interface PushService : MatrixService {
|
interface PushService : MatrixService {
|
||||||
|
|
||||||
suspend fun registerPush(token: String)
|
suspend fun registerPush(token: String, gatewayUrl: String)
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
data class PushRequest(
|
data class PushRequest(
|
||||||
|
@ -13,8 +13,8 @@ class DefaultPushService(
|
|||||||
|
|
||||||
private val useCase = RegisterPushUseCase(httpClient, credentialsStore, logger)
|
private val useCase = RegisterPushUseCase(httpClient, credentialsStore, logger)
|
||||||
|
|
||||||
override suspend fun registerPush(token: String) {
|
override suspend fun registerPush(token: String, gatewayUrl: String) {
|
||||||
useCase.registerPushToken(token)
|
useCase.registerPushToken(token, gatewayUrl)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
@ -13,7 +13,7 @@ internal class RegisterPushUseCase(
|
|||||||
private val logger: MatrixLogger,
|
private val logger: MatrixLogger,
|
||||||
) {
|
) {
|
||||||
|
|
||||||
suspend fun registerPushToken(token: String) {
|
suspend fun registerPushToken(token: String, gatewayUrl: String) {
|
||||||
if (credentialsStore.isSignedIn()) {
|
if (credentialsStore.isSignedIn()) {
|
||||||
logger.matrixLog("register push token: $token")
|
logger.matrixLog("register push token: $token")
|
||||||
matrixClient.execute(
|
matrixClient.execute(
|
||||||
@ -29,7 +29,7 @@ internal class RegisterPushUseCase(
|
|||||||
append = false,
|
append = false,
|
||||||
data = PushRequest.Payload(
|
data = PushRequest.Payload(
|
||||||
format = "event_id_only",
|
format = "event_id_only",
|
||||||
url = "https://sygnal.dapk.app/_matrix/push/v1/notify",
|
url = gatewayUrl,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user