From d459c0806be9b5fb90e1145919de8c585542dfcf Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 7 May 2024 13:06:54 +0200 Subject: [PATCH] Wait for UnifiedPush pusher to be registered before returning Result. --- .../libraries/push/impl/DefaultPushService.kt | 8 ++-- .../unifiedpush/RegisterUnifiedPushUseCase.kt | 28 +++++++++++++- .../VectorUnifiedPushMessagingReceiver.kt | 21 +++++++++- .../EndpointRegistrationHandler.kt | 38 +++++++++++++++++++ 4 files changed, 87 insertions(+), 8 deletions(-) create mode 100644 libraries/pushproviders/unifiedpush/src/main/kotlin/io/element/android/libraries/pushproviders/unifiedpush/registration/EndpointRegistrationHandler.kt diff --git a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/DefaultPushService.kt b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/DefaultPushService.kt index 6d0f21b5d9..c504802610 100644 --- a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/DefaultPushService.kt +++ b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/DefaultPushService.kt @@ -67,13 +67,13 @@ class DefaultPushService @Inject constructor( pushProviders.find { it.name == currentPushProviderName }?.unregister(matrixClient) ?.onFailure { Timber.w(it, "Failed to unregister previous push provider") + return Result.failure(it) } } + // Store new value + userPushStore.setPushProviderName(pushProvider.name) + // Then try to register return pushProvider.registerWith(matrixClient, distributor) - .onSuccess { - // Store new value - userPushStore.setPushProviderName(pushProvider.name) - } } override suspend fun testPush(): Boolean { diff --git a/libraries/pushproviders/unifiedpush/src/main/kotlin/io/element/android/libraries/pushproviders/unifiedpush/RegisterUnifiedPushUseCase.kt b/libraries/pushproviders/unifiedpush/src/main/kotlin/io/element/android/libraries/pushproviders/unifiedpush/RegisterUnifiedPushUseCase.kt index d24f1291d8..24f93b88dc 100644 --- a/libraries/pushproviders/unifiedpush/src/main/kotlin/io/element/android/libraries/pushproviders/unifiedpush/RegisterUnifiedPushUseCase.kt +++ b/libraries/pushproviders/unifiedpush/src/main/kotlin/io/element/android/libraries/pushproviders/unifiedpush/RegisterUnifiedPushUseCase.kt @@ -19,17 +19,41 @@ package io.element.android.libraries.pushproviders.unifiedpush import android.content.Context import io.element.android.libraries.di.ApplicationContext import io.element.android.libraries.pushproviders.api.Distributor +import io.element.android.libraries.pushproviders.unifiedpush.registration.EndpointRegistrationHandler +import kotlinx.coroutines.CompletableDeferred +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.flow.filter +import kotlinx.coroutines.flow.first +import kotlinx.coroutines.launch +import kotlinx.coroutines.withTimeout import org.unifiedpush.android.connector.UnifiedPush import javax.inject.Inject +import kotlin.time.Duration.Companion.seconds class RegisterUnifiedPushUseCase @Inject constructor( @ApplicationContext private val context: Context, + private val endpointRegistrationHandler: EndpointRegistrationHandler, + private val coroutineScope: CoroutineScope, ) { - fun execute(distributor: Distributor, clientSecret: String): Result { + suspend fun execute(distributor: Distributor, clientSecret: String): Result { UnifiedPush.saveDistributor(context, distributor.value) + val completable = CompletableDeferred>() + val job = coroutineScope.launch { + val result = endpointRegistrationHandler.state + .filter { it.clientSecret == clientSecret } + .first() + .result + completable.complete(result) + } // This will trigger the callback // VectorUnifiedPushMessagingReceiver.onNewEndpoint UnifiedPush.registerApp(context = context, instance = clientSecret) - return Result.success(Unit) + // Wait for VectorUnifiedPushMessagingReceiver.onNewEndpoint to proceed + return withTimeout(30.seconds) { + completable.await() + } + .onFailure { + job.cancel() + } } } diff --git a/libraries/pushproviders/unifiedpush/src/main/kotlin/io/element/android/libraries/pushproviders/unifiedpush/VectorUnifiedPushMessagingReceiver.kt b/libraries/pushproviders/unifiedpush/src/main/kotlin/io/element/android/libraries/pushproviders/unifiedpush/VectorUnifiedPushMessagingReceiver.kt index e4b9624a1e..c47aabae37 100644 --- a/libraries/pushproviders/unifiedpush/src/main/kotlin/io/element/android/libraries/pushproviders/unifiedpush/VectorUnifiedPushMessagingReceiver.kt +++ b/libraries/pushproviders/unifiedpush/src/main/kotlin/io/element/android/libraries/pushproviders/unifiedpush/VectorUnifiedPushMessagingReceiver.kt @@ -21,6 +21,8 @@ import android.content.Intent import io.element.android.libraries.architecture.bindings import io.element.android.libraries.core.log.logger.LoggerTag import io.element.android.libraries.pushproviders.api.PushHandler +import io.element.android.libraries.pushproviders.unifiedpush.registration.EndpointRegistrationHandler +import io.element.android.libraries.pushproviders.unifiedpush.registration.RegistrationResult import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.SupervisorJob import kotlinx.coroutines.launch @@ -37,6 +39,7 @@ class VectorUnifiedPushMessagingReceiver : MessagingReceiver() { @Inject lateinit var unifiedPushStore: UnifiedPushStore @Inject lateinit var unifiedPushGatewayResolver: UnifiedPushGatewayResolver @Inject lateinit var newGatewayHandler: UnifiedPushNewGatewayHandler + @Inject lateinit var endpointRegistrationHandler: EndpointRegistrationHandler private val coroutineScope = CoroutineScope(SupervisorJob()) @@ -73,14 +76,28 @@ class VectorUnifiedPushMessagingReceiver : MessagingReceiver() { coroutineScope.launch { val gateway = unifiedPushGatewayResolver.getGateway(endpoint) unifiedPushStore.storePushGateway(gateway, instance) - gateway?.let { pushGateway -> - newGatewayHandler.handle(endpoint, pushGateway, instance) + if (gateway == null) { + Timber.tag(loggerTag.value).w("No gateway found for endpoint $endpoint") + endpointRegistrationHandler.registrationDone( + RegistrationResult( + clientSecret = instance, + result = Result.failure(IllegalStateException("No gateway found for endpoint $endpoint")), + ) + ) + } else { + val result = newGatewayHandler.handle(endpoint, gateway, instance) .onFailure { Timber.tag(loggerTag.value).e(it, "Failed to handle new gateway") } .onSuccess { unifiedPushStore.storeUpEndpoint(endpoint, instance) } + endpointRegistrationHandler.registrationDone( + RegistrationResult( + clientSecret = instance, + result = result, + ) + ) } } guardServiceStarter.stop() diff --git a/libraries/pushproviders/unifiedpush/src/main/kotlin/io/element/android/libraries/pushproviders/unifiedpush/registration/EndpointRegistrationHandler.kt b/libraries/pushproviders/unifiedpush/src/main/kotlin/io/element/android/libraries/pushproviders/unifiedpush/registration/EndpointRegistrationHandler.kt new file mode 100644 index 0000000000..504ae51916 --- /dev/null +++ b/libraries/pushproviders/unifiedpush/src/main/kotlin/io/element/android/libraries/pushproviders/unifiedpush/registration/EndpointRegistrationHandler.kt @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2024 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.element.android.libraries.pushproviders.unifiedpush.registration + +import io.element.android.libraries.di.AppScope +import io.element.android.libraries.di.SingleIn +import kotlinx.coroutines.flow.MutableSharedFlow +import kotlinx.coroutines.flow.SharedFlow +import javax.inject.Inject + +data class RegistrationResult( + val clientSecret: String, + val result: Result, +) + +@SingleIn(AppScope::class) +class EndpointRegistrationHandler @Inject constructor() { + private val _state = MutableSharedFlow() + val state: SharedFlow = _state + + suspend fun registrationDone(result: RegistrationResult) { + _state.emit(result) + } +}