mirror of
https://github.com/SchildiChat/SchildiChat-android.git
synced 2025-03-18 12:18:48 +03:00
Merge pull request #4228 from vector-im/feature/adm/suspending_add_pusher
Improved /settings/notifications push toggle error handling
This commit is contained in:
commit
36d2f8e46b
22 changed files with 610 additions and 195 deletions
2
changelog.d/4106.bugfix
Normal file
2
changelog.d/4106.bugfix
Normal file
|
@ -0,0 +1,2 @@
|
|||
Fixes push notification emails list not refreshing the first time seeing the notifications page.
|
||||
Also improves the error handling in the email notification toggling by using synchronous flows instead of the WorkManager
|
|
@ -130,6 +130,7 @@ dependencies {
|
|||
|
||||
// Database
|
||||
implementation 'com.github.Zhuinden:realm-monarchy:0.7.1'
|
||||
|
||||
kapt 'dk.ilios:realmfieldnameshelper:2.0.0'
|
||||
|
||||
// Work
|
||||
|
@ -165,6 +166,8 @@ dependencies {
|
|||
implementation libs.jetbrains.coroutinesAndroid
|
||||
// Plant Timber tree for test
|
||||
testImplementation 'net.lachlanmckee:timber-junit-rule:1.0.1'
|
||||
// Transitively required for mocking realm as monarchy doesn't expose Rx
|
||||
testImplementation libs.rx.rxKotlin
|
||||
|
||||
kaptAndroidTest libs.dagger.daggerCompiler
|
||||
androidTestImplementation libs.androidx.testCore
|
||||
|
|
|
@ -29,38 +29,19 @@ interface PushersService {
|
|||
* Add a new HTTP pusher.
|
||||
* Ref: https://matrix.org/docs/spec/client_server/latest#post-matrix-client-r0-pushers-set
|
||||
*
|
||||
* @param pushkey This is a unique identifier for this pusher. The value you should use for
|
||||
* this is the routing or destination address information for the notification,
|
||||
* for example, the APNS token for APNS or the Registration ID for GCM. If your
|
||||
* notification client has no such concept, use any unique identifier. Max length, 512 chars.
|
||||
* @param appId the application id
|
||||
* This is a reverse-DNS style identifier for the application. It is recommended
|
||||
* that this end with the platform, such that different platform versions get
|
||||
* different app identifiers. Max length, 64 chars.
|
||||
* @param profileTag This string determines which set of device specific rules this pusher executes.
|
||||
* @param lang The preferred language for receiving notifications (e.g. "en" or "en-US").
|
||||
* @param appDisplayName A human readable string that will allow the user to identify what application owns this pusher.
|
||||
* @param deviceDisplayName A human readable string that will allow the user to identify what device owns this pusher.
|
||||
* @param url The URL to use to send notifications to. MUST be an HTTPS URL with a path of /_matrix/push/v1/notify.
|
||||
* @param append If true, the homeserver should add another pusher with the given pushkey and App ID in addition
|
||||
* to any others with different user IDs. Otherwise, the homeserver must remove any other pushers
|
||||
* with the same App ID and pushkey for different users.
|
||||
* @param withEventIdOnly true to limit the push content to only id and not message content
|
||||
* Ref: https://matrix.org/docs/spec/push_gateway/r0.1.1#homeserver-behaviour
|
||||
*
|
||||
* @return A work request uuid. Can be used to listen to the status
|
||||
* (LiveData<WorkInfo> status = workManager.getWorkInfoByIdLiveData(<UUID>))
|
||||
* @throws [InvalidParameterException] if a parameter is not correct
|
||||
*/
|
||||
fun addHttpPusher(pushkey: String,
|
||||
appId: String,
|
||||
profileTag: String,
|
||||
lang: String,
|
||||
appDisplayName: String,
|
||||
deviceDisplayName: String,
|
||||
url: String,
|
||||
append: Boolean,
|
||||
withEventIdOnly: Boolean): UUID
|
||||
suspend fun addHttpPusher(httpPusher: HttpPusher)
|
||||
|
||||
/**
|
||||
* Enqueues a new HTTP pusher via the WorkManager API.
|
||||
* Ref: https://matrix.org/docs/spec/client_server/latest#post-matrix-client-r0-pushers-set
|
||||
*
|
||||
* @return A work request uuid. Can be used to listen to the status
|
||||
* (LiveData<WorkInfo> status = workManager.getWorkInfoByIdLiveData(<UUID>))
|
||||
* @throws [InvalidParameterException] if a parameter is not correct
|
||||
*/
|
||||
fun enqueueAddHttpPusher(httpPusher: HttpPusher): UUID
|
||||
|
||||
/**
|
||||
* Add a new Email pusher.
|
||||
|
@ -75,16 +56,14 @@ interface PushersService {
|
|||
* to any others with different user IDs. Otherwise, the homeserver must remove any other pushers
|
||||
* with the same App ID and pushkey for different users. Typically We always want to append for
|
||||
* email pushers since we don't want to stop other accounts notifying to the same email address.
|
||||
* @return A work request uuid. Can be used to listen to the status
|
||||
* (LiveData<WorkInfo> status = workManager.getWorkInfoByIdLiveData(<UUID>))
|
||||
* @throws [InvalidParameterException] if a parameter is not correct
|
||||
*/
|
||||
fun addEmailPusher(email: String,
|
||||
lang: String,
|
||||
emailBranding: String,
|
||||
appDisplayName: String,
|
||||
deviceDisplayName: String,
|
||||
append: Boolean = true): UUID
|
||||
suspend fun addEmailPusher(email: String,
|
||||
lang: String,
|
||||
emailBranding: String,
|
||||
appDisplayName: String,
|
||||
deviceDisplayName: String,
|
||||
append: Boolean = true)
|
||||
|
||||
/**
|
||||
* Directly ask the push gateway to send a push to this device
|
||||
|
@ -128,4 +107,61 @@ interface PushersService {
|
|||
* Get the current pushers
|
||||
*/
|
||||
fun getPushers(): List<Pusher>
|
||||
|
||||
data class HttpPusher(
|
||||
|
||||
/**
|
||||
* This is a unique identifier for this pusher. The value you should use for
|
||||
* this is the routing or destination address information for the notification,
|
||||
* for example, the APNS token for APNS or the Registration ID for GCM. If your
|
||||
* notification client has no such concept, use any unique identifier. Max length, 512 chars.
|
||||
*/
|
||||
val pushkey: String,
|
||||
|
||||
/**
|
||||
* The application id
|
||||
* This is a reverse-DNS style identifier for the application. It is recommended
|
||||
* that this end with the platform, such that different platform versions get
|
||||
* different app identifiers. Max length, 64 chars.
|
||||
*/
|
||||
val appId: String,
|
||||
|
||||
/**
|
||||
* This string determines which set of device specific rules this pusher executes.
|
||||
*/
|
||||
val profileTag: String,
|
||||
|
||||
/**
|
||||
* The preferred language for receiving notifications (e.g. "en" or "en-US").
|
||||
*/
|
||||
val lang: String,
|
||||
|
||||
/**
|
||||
* A human readable string that will allow the user to identify what application owns this pusher.
|
||||
*/
|
||||
val appDisplayName: String,
|
||||
|
||||
/**
|
||||
* A human readable string that will allow the user to identify what device owns this pusher.
|
||||
*/
|
||||
val deviceDisplayName: String,
|
||||
|
||||
/**
|
||||
* The URL to use to send notifications to. MUST be an HTTPS URL with a path of /_matrix/push/v1/notify.
|
||||
*/
|
||||
val url: String,
|
||||
|
||||
/**
|
||||
* If true, the homeserver should add another pusher with the given pushkey and App ID in addition
|
||||
* to any others with different user IDs. Otherwise, the homeserver must remove any other pushers
|
||||
* with the same App ID and pushkey for different users.
|
||||
*/
|
||||
val append: Boolean,
|
||||
|
||||
/**
|
||||
* true to limit the push content to only id and not message content
|
||||
* Ref: https://matrix.org/docs/spec/push_gateway/r0.1.1#homeserver-behaviour
|
||||
*/
|
||||
val withEventIdOnly: Boolean
|
||||
)
|
||||
}
|
||||
|
|
|
@ -18,6 +18,7 @@ package org.matrix.android.sdk.internal.extensions
|
|||
|
||||
import androidx.lifecycle.LifecycleOwner
|
||||
import androidx.lifecycle.LiveData
|
||||
import androidx.lifecycle.MediatorLiveData
|
||||
import androidx.lifecycle.Observer
|
||||
|
||||
inline fun <T> LiveData<T>.observeK(owner: LifecycleOwner, crossinline observer: (T?) -> Unit) {
|
||||
|
@ -27,3 +28,25 @@ inline fun <T> LiveData<T>.observeK(owner: LifecycleOwner, crossinline observer:
|
|||
inline fun <T> LiveData<T>.observeNotNull(owner: LifecycleOwner, crossinline observer: (T) -> Unit) {
|
||||
this.observe(owner, Observer { it?.run(observer) })
|
||||
}
|
||||
|
||||
fun <T1, T2, R> combineLatest(source1: LiveData<T1>, source2: LiveData<T2>, mapper: (T1, T2) -> R): LiveData<R> {
|
||||
val combined = MediatorLiveData<R>()
|
||||
var source1Result: T1? = null
|
||||
var source2Result: T2? = null
|
||||
|
||||
fun notify() {
|
||||
if (source1Result != null && source2Result != null) {
|
||||
combined.value = mapper(source1Result!!, source2Result!!)
|
||||
}
|
||||
}
|
||||
|
||||
combined.addSource(source1) {
|
||||
source1Result = it
|
||||
notify()
|
||||
}
|
||||
combined.addSource(source2) {
|
||||
source2Result = it
|
||||
notify()
|
||||
}
|
||||
return combined
|
||||
}
|
||||
|
|
|
@ -0,0 +1,36 @@
|
|||
/*
|
||||
* Copyright 2021 The Matrix.org Foundation C.I.C.
|
||||
*
|
||||
* 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 org.matrix.android.sdk.internal.network
|
||||
import org.matrix.android.sdk.internal.network.executeRequest as internalExecuteRequest
|
||||
|
||||
internal interface RequestExecutor {
|
||||
suspend fun <DATA> executeRequest(globalErrorReceiver: GlobalErrorReceiver?,
|
||||
canRetry: Boolean = false,
|
||||
maxDelayBeforeRetry: Long = 32_000L,
|
||||
maxRetriesCount: Int = 4,
|
||||
requestBlock: suspend () -> DATA): DATA
|
||||
}
|
||||
|
||||
internal object DefaultRequestExecutor : RequestExecutor {
|
||||
override suspend fun <DATA> executeRequest(globalErrorReceiver: GlobalErrorReceiver?,
|
||||
canRetry: Boolean,
|
||||
maxDelayBeforeRetry: Long,
|
||||
maxRetriesCount: Int,
|
||||
requestBlock: suspend () -> DATA): DATA {
|
||||
return internalExecuteRequest(globalErrorReceiver, canRetry, maxDelayBeforeRetry, maxRetriesCount, requestBlock)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
/*
|
||||
* Copyright 2021 The Matrix.org Foundation C.I.C.
|
||||
*
|
||||
* 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 org.matrix.android.sdk.internal.network
|
||||
|
||||
import dagger.Module
|
||||
import dagger.Provides
|
||||
|
||||
@Module
|
||||
internal object RequestModule {
|
||||
|
||||
@Provides
|
||||
fun providesRequestExecutor(): RequestExecutor {
|
||||
return DefaultRequestExecutor
|
||||
}
|
||||
}
|
|
@ -30,6 +30,7 @@ import org.matrix.android.sdk.internal.crypto.verification.SendVerificationMessa
|
|||
import org.matrix.android.sdk.internal.di.MatrixComponent
|
||||
import org.matrix.android.sdk.internal.federation.FederationModule
|
||||
import org.matrix.android.sdk.internal.network.NetworkConnectivityChecker
|
||||
import org.matrix.android.sdk.internal.network.RequestModule
|
||||
import org.matrix.android.sdk.internal.session.account.AccountModule
|
||||
import org.matrix.android.sdk.internal.session.cache.CacheModule
|
||||
import org.matrix.android.sdk.internal.session.call.CallModule
|
||||
|
@ -94,7 +95,8 @@ import org.matrix.android.sdk.internal.util.system.SystemModule
|
|||
CallModule::class,
|
||||
SearchModule::class,
|
||||
ThirdPartyModule::class,
|
||||
SpaceModule::class
|
||||
SpaceModule::class,
|
||||
RequestModule::class
|
||||
]
|
||||
)
|
||||
@SessionScope
|
||||
|
|
|
@ -0,0 +1,78 @@
|
|||
/*
|
||||
* Copyright (c) 2021 The Matrix.org Foundation C.I.C.
|
||||
*
|
||||
* 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 org.matrix.android.sdk.internal.session.pushers
|
||||
|
||||
import com.zhuinden.monarchy.Monarchy
|
||||
import org.matrix.android.sdk.api.session.pushers.PusherState
|
||||
import org.matrix.android.sdk.internal.database.mapper.toEntity
|
||||
import org.matrix.android.sdk.internal.database.model.PusherEntity
|
||||
import org.matrix.android.sdk.internal.database.query.where
|
||||
import org.matrix.android.sdk.internal.di.SessionDatabase
|
||||
import org.matrix.android.sdk.internal.network.GlobalErrorReceiver
|
||||
import org.matrix.android.sdk.internal.network.RequestExecutor
|
||||
import org.matrix.android.sdk.internal.task.Task
|
||||
import org.matrix.android.sdk.internal.util.awaitTransaction
|
||||
import javax.inject.Inject
|
||||
|
||||
internal interface AddPusherTask : Task<AddPusherTask.Params, Unit> {
|
||||
data class Params(val pusher: JsonPusher)
|
||||
}
|
||||
|
||||
internal class DefaultAddPusherTask @Inject constructor(
|
||||
private val pushersAPI: PushersAPI,
|
||||
@SessionDatabase private val monarchy: Monarchy,
|
||||
private val requestExecutor: RequestExecutor,
|
||||
private val globalErrorReceiver: GlobalErrorReceiver
|
||||
) : AddPusherTask {
|
||||
override suspend fun execute(params: AddPusherTask.Params) {
|
||||
val pusher = params.pusher
|
||||
try {
|
||||
setPusher(pusher)
|
||||
} catch (error: Throwable) {
|
||||
monarchy.awaitTransaction { realm ->
|
||||
PusherEntity.where(realm, pusher.pushKey).findFirst()?.let {
|
||||
it.state = PusherState.FAILED_TO_REGISTER
|
||||
}
|
||||
}
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun setPusher(pusher: JsonPusher) {
|
||||
requestExecutor.executeRequest(globalErrorReceiver) {
|
||||
pushersAPI.setPusher(pusher)
|
||||
}
|
||||
monarchy.awaitTransaction { realm ->
|
||||
val echo = PusherEntity.where(realm, pusher.pushKey).findFirst()
|
||||
if (echo == null) {
|
||||
pusher.toEntity().also {
|
||||
it.state = PusherState.REGISTERED
|
||||
realm.insertOrUpdate(it)
|
||||
}
|
||||
} else {
|
||||
echo.appDisplayName = pusher.appDisplayName
|
||||
echo.appId = pusher.appId
|
||||
echo.kind = pusher.kind
|
||||
echo.lang = pusher.lang
|
||||
echo.profileTag = pusher.profileTag
|
||||
echo.data?.format = pusher.data?.format
|
||||
echo.data?.url = pusher.data?.url
|
||||
echo.state = PusherState.REGISTERED
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -18,17 +18,8 @@ package org.matrix.android.sdk.internal.session.pushers
|
|||
import android.content.Context
|
||||
import androidx.work.WorkerParameters
|
||||
import com.squareup.moshi.JsonClass
|
||||
import com.zhuinden.monarchy.Monarchy
|
||||
import org.matrix.android.sdk.api.failure.Failure
|
||||
import org.matrix.android.sdk.api.session.pushers.PusherState
|
||||
import org.matrix.android.sdk.internal.database.mapper.toEntity
|
||||
import org.matrix.android.sdk.internal.database.model.PusherEntity
|
||||
import org.matrix.android.sdk.internal.database.query.where
|
||||
import org.matrix.android.sdk.internal.di.SessionDatabase
|
||||
import org.matrix.android.sdk.internal.network.GlobalErrorReceiver
|
||||
import org.matrix.android.sdk.internal.network.executeRequest
|
||||
import org.matrix.android.sdk.internal.session.SessionComponent
|
||||
import org.matrix.android.sdk.internal.util.awaitTransaction
|
||||
import org.matrix.android.sdk.internal.worker.SessionSafeCoroutineWorker
|
||||
import org.matrix.android.sdk.internal.worker.SessionWorkerParams
|
||||
import javax.inject.Inject
|
||||
|
@ -43,9 +34,7 @@ internal class AddPusherWorker(context: Context, params: WorkerParameters) :
|
|||
override val lastFailureMessage: String? = null
|
||||
) : SessionWorkerParams
|
||||
|
||||
@Inject lateinit var pushersAPI: PushersAPI
|
||||
@Inject @SessionDatabase lateinit var monarchy: Monarchy
|
||||
@Inject lateinit var globalErrorReceiver: GlobalErrorReceiver
|
||||
@Inject lateinit var addPusherTask: AddPusherTask
|
||||
|
||||
override fun injectWith(injector: SessionComponent) {
|
||||
injector.inject(this)
|
||||
|
@ -58,20 +47,12 @@ internal class AddPusherWorker(context: Context, params: WorkerParameters) :
|
|||
return Result.failure()
|
||||
}
|
||||
return try {
|
||||
setPusher(pusher)
|
||||
addPusherTask.execute(AddPusherTask.Params(pusher))
|
||||
Result.success()
|
||||
} catch (exception: Throwable) {
|
||||
when (exception) {
|
||||
is Failure.NetworkConnection -> Result.retry()
|
||||
else -> {
|
||||
monarchy.awaitTransaction { realm ->
|
||||
PusherEntity.where(realm, pusher.pushKey).findFirst()?.let {
|
||||
// update it
|
||||
it.state = PusherState.FAILED_TO_REGISTER
|
||||
}
|
||||
}
|
||||
Result.failure()
|
||||
}
|
||||
else -> Result.failure()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -79,29 +60,4 @@ internal class AddPusherWorker(context: Context, params: WorkerParameters) :
|
|||
override fun buildErrorParams(params: Params, message: String): Params {
|
||||
return params.copy(lastFailureMessage = params.lastFailureMessage ?: message)
|
||||
}
|
||||
|
||||
private suspend fun setPusher(pusher: JsonPusher) {
|
||||
executeRequest(globalErrorReceiver) {
|
||||
pushersAPI.setPusher(pusher)
|
||||
}
|
||||
monarchy.awaitTransaction { realm ->
|
||||
val echo = PusherEntity.where(realm, pusher.pushKey).findFirst()
|
||||
if (echo != null) {
|
||||
// update it
|
||||
echo.appDisplayName = pusher.appDisplayName
|
||||
echo.appId = pusher.appId
|
||||
echo.kind = pusher.kind
|
||||
echo.lang = pusher.lang
|
||||
echo.profileTag = pusher.profileTag
|
||||
echo.data?.format = pusher.data?.format
|
||||
echo.data?.url = pusher.data?.url
|
||||
echo.state = PusherState.REGISTERED
|
||||
} else {
|
||||
pusher.toEntity().also {
|
||||
it.state = PusherState.REGISTERED
|
||||
realm.insertOrUpdate(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -30,7 +30,6 @@ import org.matrix.android.sdk.internal.session.pushers.gateway.PushGatewayNotify
|
|||
import org.matrix.android.sdk.internal.task.TaskExecutor
|
||||
import org.matrix.android.sdk.internal.task.configureWith
|
||||
import org.matrix.android.sdk.internal.worker.WorkerParamsFactory
|
||||
import java.security.InvalidParameterException
|
||||
import java.util.UUID
|
||||
import java.util.concurrent.TimeUnit
|
||||
import javax.inject.Inject
|
||||
|
@ -41,6 +40,7 @@ internal class DefaultPushersService @Inject constructor(
|
|||
@SessionId private val sessionId: String,
|
||||
private val getPusherTask: GetPushersTask,
|
||||
private val pushGatewayNotifyTask: PushGatewayNotifyTask,
|
||||
private val addPusherTask: AddPusherTask,
|
||||
private val removePusherTask: RemovePusherTask,
|
||||
private val taskExecutor: TaskExecutor
|
||||
) : PushersService {
|
||||
|
@ -58,51 +58,48 @@ internal class DefaultPushersService @Inject constructor(
|
|||
.executeBy(taskExecutor)
|
||||
}
|
||||
|
||||
override fun addHttpPusher(pushkey: String,
|
||||
appId: String,
|
||||
profileTag: String,
|
||||
lang: String,
|
||||
appDisplayName: String,
|
||||
deviceDisplayName: String,
|
||||
url: String,
|
||||
append: Boolean,
|
||||
withEventIdOnly: Boolean
|
||||
) = addPusher(
|
||||
JsonPusher(
|
||||
pushKey = pushkey,
|
||||
kind = Pusher.KIND_HTTP,
|
||||
appId = appId,
|
||||
profileTag = profileTag,
|
||||
lang = lang,
|
||||
appDisplayName = appDisplayName,
|
||||
deviceDisplayName = deviceDisplayName,
|
||||
data = JsonPusherData(url, EVENT_ID_ONLY.takeIf { withEventIdOnly }),
|
||||
append = append
|
||||
)
|
||||
override fun enqueueAddHttpPusher(httpPusher: PushersService.HttpPusher): UUID {
|
||||
return enqueueAddPusher(httpPusher.toJsonPusher())
|
||||
}
|
||||
|
||||
override suspend fun addHttpPusher(httpPusher: PushersService.HttpPusher) {
|
||||
addPusherTask.execute(AddPusherTask.Params(httpPusher.toJsonPusher()))
|
||||
}
|
||||
|
||||
private fun PushersService.HttpPusher.toJsonPusher() = JsonPusher(
|
||||
pushKey = pushkey,
|
||||
kind = "http",
|
||||
appId = appId,
|
||||
profileTag = profileTag,
|
||||
lang = lang,
|
||||
appDisplayName = appDisplayName,
|
||||
deviceDisplayName = deviceDisplayName,
|
||||
data = JsonPusherData(url, EVENT_ID_ONLY.takeIf { withEventIdOnly }),
|
||||
append = append
|
||||
)
|
||||
|
||||
override fun addEmailPusher(email: String,
|
||||
lang: String,
|
||||
emailBranding: String,
|
||||
appDisplayName: String,
|
||||
deviceDisplayName: String,
|
||||
append: Boolean
|
||||
) = addPusher(
|
||||
JsonPusher(
|
||||
pushKey = email,
|
||||
kind = Pusher.KIND_EMAIL,
|
||||
appId = Pusher.APP_ID_EMAIL,
|
||||
profileTag = "",
|
||||
lang = lang,
|
||||
appDisplayName = appDisplayName,
|
||||
deviceDisplayName = deviceDisplayName,
|
||||
data = JsonPusherData(brand = emailBranding),
|
||||
append = append
|
||||
)
|
||||
)
|
||||
override suspend fun addEmailPusher(email: String,
|
||||
lang: String,
|
||||
emailBranding: String,
|
||||
appDisplayName: String,
|
||||
deviceDisplayName: String,
|
||||
append: Boolean) {
|
||||
addPusherTask.execute(
|
||||
AddPusherTask.Params(JsonPusher(
|
||||
pushKey = email,
|
||||
kind = Pusher.KIND_EMAIL,
|
||||
appId = Pusher.APP_ID_EMAIL,
|
||||
profileTag = "",
|
||||
lang = lang,
|
||||
appDisplayName = appDisplayName,
|
||||
deviceDisplayName = deviceDisplayName,
|
||||
data = JsonPusherData(brand = emailBranding),
|
||||
append = append
|
||||
))
|
||||
)
|
||||
}
|
||||
|
||||
private fun addPusher(pusher: JsonPusher): UUID {
|
||||
pusher.validateParameters()
|
||||
private fun enqueueAddPusher(pusher: JsonPusher): UUID {
|
||||
val params = AddPusherWorker.Params(sessionId, pusher)
|
||||
val request = workManagerProvider.matrixOneTimeWorkRequestBuilder<AddPusherWorker>()
|
||||
.setConstraints(WorkManagerProvider.workConstraints)
|
||||
|
@ -113,13 +110,6 @@ internal class DefaultPushersService @Inject constructor(
|
|||
return request.id
|
||||
}
|
||||
|
||||
private fun JsonPusher.validateParameters() {
|
||||
// Do some parameter checks. It's ok to throw Exception, to inform developer of the problem
|
||||
if (pushKey.length > 512) throw InvalidParameterException("pushkey should not exceed 512 chars")
|
||||
if (appId.length > 64) throw InvalidParameterException("appId should not exceed 64 chars")
|
||||
data?.url?.let { url -> if ("/_matrix/push/v1/notify" !in url) throw InvalidParameterException("url should contain '/_matrix/push/v1/notify'") }
|
||||
}
|
||||
|
||||
override suspend fun removePusher(pusher: Pusher) {
|
||||
removePusher(pusher.pushKey, pusher.appId)
|
||||
}
|
||||
|
|
|
@ -18,6 +18,7 @@ package org.matrix.android.sdk.internal.session.pushers
|
|||
import com.squareup.moshi.Json
|
||||
import com.squareup.moshi.JsonClass
|
||||
import org.matrix.android.sdk.internal.di.SerializeNulls
|
||||
import java.security.InvalidParameterException
|
||||
|
||||
/**
|
||||
* Example:
|
||||
|
@ -112,4 +113,11 @@ internal data class JsonPusher(
|
|||
*/
|
||||
@Json(name = "append")
|
||||
val append: Boolean? = false
|
||||
)
|
||||
) {
|
||||
init {
|
||||
// Do some parameter checks. It's ok to throw Exception, to inform developer of the problem
|
||||
if (pushKey.length > 512) throw InvalidParameterException("pushkey should not exceed 512 chars")
|
||||
if (appId.length > 64) throw InvalidParameterException("appId should not exceed 64 chars")
|
||||
data?.url?.let { url -> if ("/_matrix/push/v1/notify" !in url) throw InvalidParameterException("url should contain '/_matrix/push/v1/notify'") }
|
||||
}
|
||||
}
|
||||
|
|
|
@ -65,6 +65,9 @@ internal abstract class PushersModule {
|
|||
@Binds
|
||||
abstract fun bindSavePushRulesTask(task: DefaultSavePushRulesTask): SavePushRulesTask
|
||||
|
||||
@Binds
|
||||
abstract fun bindAddPusherTask(task: DefaultAddPusherTask): AddPusherTask
|
||||
|
||||
@Binds
|
||||
abstract fun bindRemovePusherTask(task: DefaultRemovePusherTask): RemovePusherTask
|
||||
|
||||
|
|
|
@ -0,0 +1,103 @@
|
|||
/*
|
||||
* Copyright (c) 2021 The Matrix.org Foundation C.I.C.
|
||||
*
|
||||
* 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 org.matrix.android.sdk.internal.session.pushers
|
||||
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import org.amshove.kluent.internal.assertFailsWith
|
||||
import org.amshove.kluent.shouldBeEqualTo
|
||||
import org.junit.Test
|
||||
import org.matrix.android.sdk.api.session.pushers.PusherState
|
||||
import org.matrix.android.sdk.internal.database.model.PusherEntity
|
||||
import org.matrix.android.sdk.test.fakes.FakeGlobalErrorReceiver
|
||||
import org.matrix.android.sdk.test.fakes.FakeMonarchy
|
||||
import org.matrix.android.sdk.test.fakes.FakePushersAPI
|
||||
import org.matrix.android.sdk.test.fakes.FakeRequestExecutor
|
||||
import java.net.SocketException
|
||||
|
||||
private val A_JSON_PUSHER = JsonPusher(
|
||||
pushKey = "push-key",
|
||||
kind = "http",
|
||||
appId = "m.email",
|
||||
appDisplayName = "Element",
|
||||
deviceDisplayName = null,
|
||||
profileTag = "",
|
||||
lang = "en-GB",
|
||||
data = JsonPusherData(brand = "Element")
|
||||
)
|
||||
|
||||
class DefaultAddPusherTaskTest {
|
||||
|
||||
private val pushersAPI = FakePushersAPI()
|
||||
private val monarchy = FakeMonarchy()
|
||||
|
||||
private val addPusherTask = DefaultAddPusherTask(
|
||||
pushersAPI = pushersAPI,
|
||||
monarchy = monarchy.instance,
|
||||
requestExecutor = FakeRequestExecutor(),
|
||||
globalErrorReceiver = FakeGlobalErrorReceiver()
|
||||
)
|
||||
|
||||
@Test
|
||||
fun `given no persisted pusher when adding Pusher then updates api and inserts result with Registered state`() {
|
||||
monarchy.givenWhereReturns<PusherEntity>(result = null)
|
||||
|
||||
runBlocking { addPusherTask.execute(AddPusherTask.Params(A_JSON_PUSHER)) }
|
||||
|
||||
pushersAPI.verifySetPusher(A_JSON_PUSHER)
|
||||
monarchy.verifyInsertOrUpdate<PusherEntity> {
|
||||
withArg { actual ->
|
||||
actual.state shouldBeEqualTo PusherState.REGISTERED
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `given a persisted pusher when adding Pusher then updates api and mutates persisted result with Registered state`() {
|
||||
val realmResult = PusherEntity(appDisplayName = null)
|
||||
monarchy.givenWhereReturns(result = realmResult)
|
||||
|
||||
runBlocking { addPusherTask.execute(AddPusherTask.Params(A_JSON_PUSHER)) }
|
||||
|
||||
pushersAPI.verifySetPusher(A_JSON_PUSHER)
|
||||
|
||||
realmResult.appDisplayName shouldBeEqualTo A_JSON_PUSHER.appDisplayName
|
||||
realmResult.state shouldBeEqualTo PusherState.REGISTERED
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `given a persisted push entity and SetPush API fails when adding Pusher then mutates persisted result with Failed registration state and rethrows error`() {
|
||||
val realmResult = PusherEntity()
|
||||
monarchy.givenWhereReturns(result = realmResult)
|
||||
pushersAPI.givenSetPusherErrors(SocketException())
|
||||
|
||||
assertFailsWith<SocketException> {
|
||||
runBlocking { addPusherTask.execute(AddPusherTask.Params(A_JSON_PUSHER)) }
|
||||
}
|
||||
|
||||
realmResult.state shouldBeEqualTo PusherState.FAILED_TO_REGISTER
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `given no persisted push entity and SetPush API fails when adding Pusher then rethrows error`() {
|
||||
monarchy.givenWhereReturns<PusherEntity>(result = null)
|
||||
pushersAPI.givenSetPusherErrors(SocketException())
|
||||
|
||||
assertFailsWith<SocketException> {
|
||||
runBlocking { addPusherTask.execute(AddPusherTask.Params(A_JSON_PUSHER)) }
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
/*
|
||||
* Copyright (c) 2021 The Matrix.org Foundation C.I.C.
|
||||
*
|
||||
* 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 org.matrix.android.sdk.test.fakes
|
||||
|
||||
import org.matrix.android.sdk.api.failure.GlobalError
|
||||
import org.matrix.android.sdk.internal.network.GlobalErrorReceiver
|
||||
|
||||
internal class FakeGlobalErrorReceiver : GlobalErrorReceiver {
|
||||
override fun handleGlobalError(globalError: GlobalError) {
|
||||
// do nothing
|
||||
}
|
||||
}
|
|
@ -0,0 +1,55 @@
|
|||
/*
|
||||
* Copyright (c) 2021 The Matrix.org Foundation C.I.C.
|
||||
*
|
||||
* 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 org.matrix.android.sdk.test.fakes
|
||||
|
||||
import com.zhuinden.monarchy.Monarchy
|
||||
import io.mockk.MockKVerificationScope
|
||||
import io.mockk.coEvery
|
||||
import io.mockk.every
|
||||
import io.mockk.mockk
|
||||
import io.mockk.mockkStatic
|
||||
import io.mockk.verify
|
||||
import io.realm.Realm
|
||||
import io.realm.RealmModel
|
||||
import io.realm.RealmQuery
|
||||
import io.realm.kotlin.where
|
||||
import org.matrix.android.sdk.internal.util.awaitTransaction
|
||||
|
||||
internal class FakeMonarchy {
|
||||
|
||||
val instance = mockk<Monarchy>()
|
||||
private val realm = mockk<Realm>(relaxed = true)
|
||||
|
||||
init {
|
||||
mockkStatic("org.matrix.android.sdk.internal.util.MonarchyKt")
|
||||
coEvery {
|
||||
instance.awaitTransaction(any<suspend (Realm) -> Any>())
|
||||
} coAnswers {
|
||||
secondArg<suspend (Realm) -> Any>().invoke(realm)
|
||||
}
|
||||
}
|
||||
|
||||
inline fun <reified T : RealmModel> givenWhereReturns(result: T?) {
|
||||
val queryResult = mockk<RealmQuery<T>>(relaxed = true)
|
||||
every { queryResult.findFirst() } returns result
|
||||
every { realm.where<T>() } returns queryResult
|
||||
}
|
||||
|
||||
inline fun <reified T : RealmModel> verifyInsertOrUpdate(crossinline verification: MockKVerificationScope.() -> T) {
|
||||
verify { realm.insertOrUpdate(verification()) }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,45 @@
|
|||
/*
|
||||
* Copyright (c) 2021 The Matrix.org Foundation C.I.C.
|
||||
*
|
||||
* 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 org.matrix.android.sdk.test.fakes
|
||||
|
||||
import org.amshove.kluent.shouldBeEqualTo
|
||||
import org.matrix.android.sdk.internal.session.pushers.GetPushersResponse
|
||||
import org.matrix.android.sdk.internal.session.pushers.JsonPusher
|
||||
import org.matrix.android.sdk.internal.session.pushers.PushersAPI
|
||||
|
||||
internal class FakePushersAPI : PushersAPI {
|
||||
|
||||
private var setRequestPayload: JsonPusher? = null
|
||||
private var error: Throwable? = null
|
||||
|
||||
override suspend fun getPushers(): GetPushersResponse {
|
||||
TODO("Not yet implemented")
|
||||
}
|
||||
|
||||
override suspend fun setPusher(jsonPusher: JsonPusher) {
|
||||
error?.let { throw it }
|
||||
setRequestPayload = jsonPusher
|
||||
}
|
||||
|
||||
fun verifySetPusher(payload: JsonPusher) {
|
||||
this.setRequestPayload shouldBeEqualTo payload
|
||||
}
|
||||
|
||||
fun givenSetPusherErrors(error: Throwable) {
|
||||
this.error = error
|
||||
}
|
||||
}
|
|
@ -0,0 +1,31 @@
|
|||
/*
|
||||
* Copyright (c) 2021 The Matrix.org Foundation C.I.C.
|
||||
*
|
||||
* 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 org.matrix.android.sdk.test.fakes
|
||||
|
||||
import org.matrix.android.sdk.internal.network.GlobalErrorReceiver
|
||||
import org.matrix.android.sdk.internal.network.RequestExecutor
|
||||
|
||||
internal class FakeRequestExecutor : RequestExecutor {
|
||||
|
||||
override suspend fun <DATA> executeRequest(globalErrorReceiver: GlobalErrorReceiver?,
|
||||
canRetry: Boolean,
|
||||
maxDelayBeforeRetry: Long,
|
||||
maxRetriesCount: Int,
|
||||
requestBlock: suspend () -> DATA): DATA {
|
||||
return requestBlock()
|
||||
}
|
||||
}
|
|
@ -57,7 +57,7 @@ class TestTokenRegistration @Inject constructor(private val context: AppCompatAc
|
|||
stringProvider.getString(R.string.sas_error_unknown))
|
||||
quickFix = object : TroubleshootQuickFix(R.string.settings_troubleshoot_test_token_registration_quick_fix) {
|
||||
override fun doFix() {
|
||||
val workId = pushersManager.registerPusherWithFcmKey(fcmToken)
|
||||
val workId = pushersManager.enqueueRegisterPusherWithFcmKey(fcmToken)
|
||||
WorkManager.getInstance(context).getWorkInfoByIdLiveData(workId).observe(context, Observer { workInfo ->
|
||||
if (workInfo != null) {
|
||||
if (workInfo.state == WorkInfo.State.SUCCEEDED) {
|
||||
|
|
|
@ -135,7 +135,7 @@ class VectorFirebaseMessagingService : FirebaseMessagingService() {
|
|||
Timber.tag(loggerTag.value).i("onNewToken: FCM Token has been updated")
|
||||
FcmHelper.storeFcmToken(this, refreshedToken)
|
||||
if (vectorPreferences.areNotificationEnabledForDevice() && activeSessionHolder.hasActiveSession()) {
|
||||
pusherManager.registerPusherWithFcmKey(refreshedToken)
|
||||
pusherManager.enqueueRegisterPusherWithFcmKey(refreshedToken)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -75,7 +75,7 @@ object FcmHelper {
|
|||
.addOnSuccessListener { token ->
|
||||
storeFcmToken(activity, token)
|
||||
if (registerPusher) {
|
||||
pushersManager.registerPusherWithFcmKey(token)
|
||||
pushersManager.enqueueRegisterPusherWithFcmKey(token)
|
||||
}
|
||||
}
|
||||
.addOnFailureListener { e ->
|
||||
|
|
|
@ -21,6 +21,7 @@ import im.vector.app.core.di.ActiveSessionHolder
|
|||
import im.vector.app.core.resources.AppNameProvider
|
||||
import im.vector.app.core.resources.LocaleProvider
|
||||
import im.vector.app.core.resources.StringProvider
|
||||
import org.matrix.android.sdk.api.session.pushers.PushersService
|
||||
import java.util.UUID
|
||||
import javax.inject.Inject
|
||||
import kotlin.math.abs
|
||||
|
@ -44,24 +45,29 @@ class PushersManager @Inject constructor(
|
|||
)
|
||||
}
|
||||
|
||||
fun registerPusherWithFcmKey(pushKey: String): UUID {
|
||||
fun enqueueRegisterPusherWithFcmKey(pushKey: String): UUID {
|
||||
val currentSession = activeSessionHolder.getActiveSession()
|
||||
val profileTag = DEFAULT_PUSHER_FILE_TAG + "_" + abs(currentSession.myUserId.hashCode())
|
||||
|
||||
return currentSession.addHttpPusher(
|
||||
pushKey,
|
||||
stringProvider.getString(R.string.pusher_app_id),
|
||||
profileTag,
|
||||
localeProvider.current().language,
|
||||
appNameProvider.getAppName(),
|
||||
currentSession.sessionParams.deviceId ?: "MOBILE",
|
||||
stringProvider.getString(R.string.pusher_http_url),
|
||||
append = false,
|
||||
withEventIdOnly = true
|
||||
)
|
||||
return currentSession.enqueueAddHttpPusher(createHttpPusher(pushKey))
|
||||
}
|
||||
|
||||
fun registerEmailForPush(email: String) {
|
||||
suspend fun registerPusherWithFcmKey(pushKey: String) {
|
||||
val currentSession = activeSessionHolder.getActiveSession()
|
||||
currentSession.addHttpPusher(createHttpPusher(pushKey))
|
||||
}
|
||||
|
||||
private fun createHttpPusher(pushKey: String) = PushersService.HttpPusher(
|
||||
pushKey,
|
||||
stringProvider.getString(R.string.pusher_app_id),
|
||||
profileTag = DEFAULT_PUSHER_FILE_TAG + "_" + abs(activeSessionHolder.getActiveSession().myUserId.hashCode()),
|
||||
localeProvider.current().language,
|
||||
appNameProvider.getAppName(),
|
||||
activeSessionHolder.getActiveSession().sessionParams.deviceId ?: "MOBILE",
|
||||
stringProvider.getString(R.string.pusher_http_url),
|
||||
append = false,
|
||||
withEventIdOnly = true
|
||||
)
|
||||
|
||||
suspend fun registerEmailForPush(email: String) {
|
||||
val currentSession = activeSessionHolder.getActiveSession()
|
||||
val appName = appNameProvider.getAppName()
|
||||
currentSession.addEmailPusher(
|
||||
|
|
|
@ -54,6 +54,7 @@ import org.matrix.android.sdk.api.pushrules.RuleKind
|
|||
import org.matrix.android.sdk.api.session.Session
|
||||
import org.matrix.android.sdk.api.session.identity.ThreePid
|
||||
import org.matrix.android.sdk.api.session.pushers.Pusher
|
||||
import org.matrix.android.sdk.internal.extensions.combineLatest
|
||||
import javax.inject.Inject
|
||||
|
||||
// Referenced in vector_settings_preferences_root.xml
|
||||
|
@ -85,6 +86,21 @@ class VectorSettingsNotificationPreferenceFragment @Inject constructor(
|
|||
(pref as SwitchPreference).isChecked = areNotifEnabledAtAccountLevel
|
||||
}
|
||||
|
||||
findPreference<SwitchPreference>(VectorPreferences.SETTINGS_ENABLE_THIS_DEVICE_PREFERENCE_KEY)?.let {
|
||||
it.setTransactionalSwitchChangeListener(lifecycleScope) { isChecked ->
|
||||
if (isChecked) {
|
||||
FcmHelper.getFcmToken(requireContext())?.let {
|
||||
pushManager.registerPusherWithFcmKey(it)
|
||||
}
|
||||
} else {
|
||||
FcmHelper.getFcmToken(requireContext())?.let {
|
||||
pushManager.unregisterPusher(it)
|
||||
session.refreshPushers()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
findPreference<VectorPreference>(VectorPreferences.SETTINGS_FDROID_BACKGROUND_SYNC_MODE)?.let {
|
||||
it.onPreferenceClickListener = Preference.OnPreferenceClickListener {
|
||||
val initialMode = vectorPreferences.getFdroidSyncBackgroundMode()
|
||||
|
@ -324,46 +340,16 @@ class VectorSettingsNotificationPreferenceFragment @Inject constructor(
|
|||
|
||||
override fun onPreferenceTreeClick(preference: Preference?): Boolean {
|
||||
return when (preference?.key) {
|
||||
VectorPreferences.SETTINGS_ENABLE_THIS_DEVICE_PREFERENCE_KEY -> {
|
||||
updateEnabledForDevice(preference)
|
||||
true
|
||||
}
|
||||
VectorPreferences.SETTINGS_ENABLE_ALL_NOTIF_PREFERENCE_KEY -> {
|
||||
VectorPreferences.SETTINGS_ENABLE_ALL_NOTIF_PREFERENCE_KEY -> {
|
||||
updateEnabledForAccount(preference)
|
||||
true
|
||||
}
|
||||
else -> {
|
||||
else -> {
|
||||
return super.onPreferenceTreeClick(preference)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun updateEnabledForDevice(preference: Preference?) {
|
||||
val switchPref = preference as SwitchPreference
|
||||
if (switchPref.isChecked) {
|
||||
FcmHelper.getFcmToken(requireContext())?.let {
|
||||
pushManager.registerPusherWithFcmKey(it)
|
||||
}
|
||||
} else {
|
||||
FcmHelper.getFcmToken(requireContext())?.let {
|
||||
lifecycleScope.launch {
|
||||
runCatching { pushManager.unregisterPusher(it) }
|
||||
.fold(
|
||||
{ session.refreshPushers() },
|
||||
{
|
||||
if (!isAdded) {
|
||||
return@fold
|
||||
}
|
||||
// revert the check box
|
||||
switchPref.isChecked = !switchPref.isChecked
|
||||
Toast.makeText(activity, R.string.unknown_error, Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun updateEnabledForAccount(preference: Preference?) {
|
||||
val pushRuleService = session
|
||||
val switchPref = preference as SwitchPreference
|
||||
|
@ -421,12 +407,9 @@ private fun Session.getEmailsWithPushInformation(): List<Pair<ThreePid.Email, Bo
|
|||
}
|
||||
|
||||
private fun Session.getEmailsWithPushInformationLive(): LiveData<List<Pair<ThreePid.Email, Boolean>>> {
|
||||
return getThreePidsLive(refreshData = false)
|
||||
.distinctUntilChanged()
|
||||
.map { threePids ->
|
||||
val emailPushers = getPushers().filter { it.kind == Pusher.KIND_EMAIL }
|
||||
threePids
|
||||
.filterIsInstance<ThreePid.Email>()
|
||||
.map { it to emailPushers.any { pusher -> pusher.pushKey == it.email } }
|
||||
}
|
||||
val emailThreePids = getThreePidsLive(refreshData = true).map { it.filterIsInstance<ThreePid.Email>() }
|
||||
val emailPushers = getPushersLive().map { it.filter { pusher -> pusher.kind == Pusher.KIND_EMAIL } }
|
||||
return combineLatest(emailThreePids, emailPushers) { emailThreePidsResult, emailPushersResult ->
|
||||
emailThreePidsResult.map { it to emailPushersResult.any { pusher -> pusher.pushKey == it.email } }
|
||||
}.distinctUntilChanged()
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue