From 4dbca7858cbb49d95135a34e2fc4f84330d75460 Mon Sep 17 00:00:00 2001 From: Maxime NATUREL Date: Tue, 29 Nov 2022 15:07:26 +0100 Subject: [PATCH] Adding new use cases to handle the Unified push registration --- .../im/vector/app/push/fcm/FdroidFcmHelper.kt | 3 +- .../im/vector/app/push/fcm/GoogleFcmHelper.kt | 13 ++-- .../EnsureFcmTokenIsRetrievedUseCase.kt | 44 ++++++++++++ .../im/vector/app/core/pushers/FcmHelper.kt | 3 +- .../pushers/RegisterUnifiedPushUseCase.kt | 70 +++++++++++++++++++ .../app/core/pushers/UnifiedPushHelper.kt | 60 ++++++++++++++-- .../pushers/UnregisterUnifiedPushUseCase.kt | 51 ++++++++++++++ .../vector/app/features/home/HomeActivity.kt | 29 ++------ .../features/home/HomeActivityViewActions.kt | 1 + .../features/home/HomeActivityViewEvents.kt | 3 + .../features/home/HomeActivityViewModel.kt | 53 +++++++------- .../features/home/HomeActivityViewState.kt | 1 - ...leNotificationsForCurrentSessionUseCase.kt | 12 ++-- ...rSettingsNotificationPreferenceFragment.kt | 10 +-- 14 files changed, 271 insertions(+), 82 deletions(-) create mode 100644 vector/src/main/java/im/vector/app/core/pushers/EnsureFcmTokenIsRetrievedUseCase.kt create mode 100644 vector/src/main/java/im/vector/app/core/pushers/RegisterUnifiedPushUseCase.kt create mode 100644 vector/src/main/java/im/vector/app/core/pushers/UnregisterUnifiedPushUseCase.kt diff --git a/vector-app/src/fdroid/java/im/vector/app/push/fcm/FdroidFcmHelper.kt b/vector-app/src/fdroid/java/im/vector/app/push/fcm/FdroidFcmHelper.kt index 5b83769116..44fd92953e 100755 --- a/vector-app/src/fdroid/java/im/vector/app/push/fcm/FdroidFcmHelper.kt +++ b/vector-app/src/fdroid/java/im/vector/app/push/fcm/FdroidFcmHelper.kt @@ -17,7 +17,6 @@ package im.vector.app.push.fcm -import android.app.Activity import android.content.Context import im.vector.app.core.di.ActiveSessionHolder import im.vector.app.core.pushers.FcmHelper @@ -44,7 +43,7 @@ class FdroidFcmHelper @Inject constructor( // No op } - override fun ensureFcmTokenIsRetrieved(activity: Activity, pushersManager: PushersManager, registerPusher: Boolean) { + override fun ensureFcmTokenIsRetrieved(pushersManager: PushersManager, registerPusher: Boolean) { // No op } diff --git a/vector-app/src/gplay/java/im/vector/app/push/fcm/GoogleFcmHelper.kt b/vector-app/src/gplay/java/im/vector/app/push/fcm/GoogleFcmHelper.kt index 7cf90cf874..53e65f88b4 100755 --- a/vector-app/src/gplay/java/im/vector/app/push/fcm/GoogleFcmHelper.kt +++ b/vector-app/src/gplay/java/im/vector/app/push/fcm/GoogleFcmHelper.kt @@ -15,7 +15,6 @@ */ package im.vector.app.push.fcm -import android.app.Activity import android.content.Context import android.content.SharedPreferences import android.widget.Toast @@ -23,6 +22,7 @@ import androidx.core.content.edit import com.google.android.gms.common.ConnectionResult import com.google.android.gms.common.GoogleApiAvailability import com.google.firebase.messaging.FirebaseMessaging +import dagger.hilt.android.qualifiers.ApplicationContext import im.vector.app.R import im.vector.app.core.di.ActiveSessionHolder import im.vector.app.core.di.DefaultPreferences @@ -36,8 +36,8 @@ import javax.inject.Inject * It has an alter ego in the fdroid variant. */ class GoogleFcmHelper @Inject constructor( - @DefaultPreferences - private val sharedPrefs: SharedPreferences, + @ApplicationContext private val context: Context, + @DefaultPreferences private val sharedPrefs: SharedPreferences, ) : FcmHelper { companion object { private const val PREFS_KEY_FCM_TOKEN = "FCM_TOKEN" @@ -56,10 +56,9 @@ class GoogleFcmHelper @Inject constructor( } } - override fun ensureFcmTokenIsRetrieved(activity: Activity, pushersManager: PushersManager, registerPusher: Boolean) { - // if (TextUtils.isEmpty(getFcmToken(activity))) { + override fun ensureFcmTokenIsRetrieved(pushersManager: PushersManager, registerPusher: Boolean) { // 'app should always check the device for a compatible Google Play services APK before accessing Google Play services features' - if (checkPlayServices(activity)) { + if (checkPlayServices(context)) { try { FirebaseMessaging.getInstance().token .addOnSuccessListener { token -> @@ -75,7 +74,7 @@ class GoogleFcmHelper @Inject constructor( Timber.e(e, "## ensureFcmTokenIsRetrieved() : failed") } } else { - Toast.makeText(activity, R.string.no_valid_google_play_services_apk, Toast.LENGTH_SHORT).show() + Toast.makeText(context, R.string.no_valid_google_play_services_apk, Toast.LENGTH_SHORT).show() Timber.e("No valid Google Play Services found. Cannot use FCM.") } } diff --git a/vector/src/main/java/im/vector/app/core/pushers/EnsureFcmTokenIsRetrievedUseCase.kt b/vector/src/main/java/im/vector/app/core/pushers/EnsureFcmTokenIsRetrievedUseCase.kt new file mode 100644 index 0000000000..4a8ff5fb39 --- /dev/null +++ b/vector/src/main/java/im/vector/app/core/pushers/EnsureFcmTokenIsRetrievedUseCase.kt @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2022 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 im.vector.app.core.pushers + +import im.vector.app.core.di.ActiveSessionHolder +import timber.log.Timber +import javax.inject.Inject + +class EnsureFcmTokenIsRetrievedUseCase @Inject constructor( + private val unifiedPushHelper: UnifiedPushHelper, + private val fcmHelper: FcmHelper, + private val activeSessionHolder: ActiveSessionHolder, +) { + + // TODO add unit tests + fun execute(pushersManager: PushersManager, registerPusher: Boolean) { + if (unifiedPushHelper.isEmbeddedDistributor()) { + Timber.d("ensureFcmTokenIsRetrieved") + fcmHelper.ensureFcmTokenIsRetrieved(pushersManager, shouldAddHttpPusher(registerPusher)) + } + } + + private fun shouldAddHttpPusher(registerPusher: Boolean) = if (registerPusher) { + val currentSession = activeSessionHolder.getActiveSession() + val currentPushers = currentSession.pushersService().getPushers() + currentPushers.none { it.deviceId == currentSession.sessionParams.deviceId } + } else { + false + } +} diff --git a/vector/src/main/java/im/vector/app/core/pushers/FcmHelper.kt b/vector/src/main/java/im/vector/app/core/pushers/FcmHelper.kt index 7b2c5e3959..0cc251ce31 100644 --- a/vector/src/main/java/im/vector/app/core/pushers/FcmHelper.kt +++ b/vector/src/main/java/im/vector/app/core/pushers/FcmHelper.kt @@ -39,11 +39,10 @@ interface FcmHelper { /** * onNewToken may not be called on application upgrade, so ensure my shared pref is set. * - * @param activity the first launch Activity. * @param pushersManager the instance to register the pusher on. * @param registerPusher whether the pusher should be registered. */ - fun ensureFcmTokenIsRetrieved(activity: Activity, pushersManager: PushersManager, registerPusher: Boolean) + fun ensureFcmTokenIsRetrieved(pushersManager: PushersManager, registerPusher: Boolean) fun onEnterForeground(activeSessionHolder: ActiveSessionHolder) diff --git a/vector/src/main/java/im/vector/app/core/pushers/RegisterUnifiedPushUseCase.kt b/vector/src/main/java/im/vector/app/core/pushers/RegisterUnifiedPushUseCase.kt new file mode 100644 index 0000000000..7aafa07348 --- /dev/null +++ b/vector/src/main/java/im/vector/app/core/pushers/RegisterUnifiedPushUseCase.kt @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2022 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 im.vector.app.core.pushers + +import android.content.Context +import dagger.hilt.android.qualifiers.ApplicationContext +import im.vector.app.features.VectorFeatures +import org.unifiedpush.android.connector.UnifiedPush +import javax.inject.Inject + +class RegisterUnifiedPushUseCase @Inject constructor( + @ApplicationContext private val context: Context, + private val vectorFeatures: VectorFeatures, +) { + + sealed interface RegisterUnifiedPushResult { + object Success : RegisterUnifiedPushResult + data class NeedToAskUserForDistributor(val distributors: List) : RegisterUnifiedPushResult + } + + // TODO add unit tests + fun execute(distributor: String = ""): RegisterUnifiedPushResult { + if(distributor.isNotEmpty()) { + saveAndRegisterApp(distributor) + return RegisterUnifiedPushResult.Success + } + + if (!vectorFeatures.allowExternalUnifiedPushDistributors()) { + saveAndRegisterApp(context.packageName) + return RegisterUnifiedPushResult.Success + } + + if (UnifiedPush.getDistributor(context).isNotEmpty()) { + registerApp() + return RegisterUnifiedPushResult.Success + } + + val distributors = UnifiedPush.getDistributors(context) + + return if (distributors.size == 1) { + saveAndRegisterApp(distributors.first()) + RegisterUnifiedPushResult.Success + } else { + RegisterUnifiedPushResult.NeedToAskUserForDistributor(distributors) + } + } + + private fun saveAndRegisterApp(distributor: String) { + UnifiedPush.saveDistributor(context, distributor) + registerApp() + } + + private fun registerApp() { + UnifiedPush.registerApp(context) + } +} diff --git a/vector/src/main/java/im/vector/app/core/pushers/UnifiedPushHelper.kt b/vector/src/main/java/im/vector/app/core/pushers/UnifiedPushHelper.kt index aab94eca93..64d2a79494 100644 --- a/vector/src/main/java/im/vector/app/core/pushers/UnifiedPushHelper.kt +++ b/vector/src/main/java/im/vector/app/core/pushers/UnifiedPushHelper.kt @@ -17,6 +17,7 @@ package im.vector.app.core.pushers import android.content.Context +import androidx.annotation.MainThread import androidx.fragment.app.FragmentActivity import androidx.lifecycle.lifecycleScope import com.google.android.material.dialog.MaterialAlertDialogBuilder @@ -28,7 +29,9 @@ import im.vector.app.core.utils.getApplicationLabel import im.vector.app.features.VectorFeatures import im.vector.app.features.settings.BackgroundSyncMode import im.vector.app.features.settings.VectorPreferences +import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext import org.matrix.android.sdk.api.Matrix import org.matrix.android.sdk.api.cache.CacheStrategy import org.matrix.android.sdk.api.util.MatrixJsonParser @@ -49,6 +52,7 @@ class UnifiedPushHelper @Inject constructor( // Called when the home activity starts // or when notifications are enabled + // TODO remove and replace by use case fun register( activity: FragmentActivity, onDoneRunnable: Runnable? = null, @@ -66,10 +70,11 @@ class UnifiedPushHelper @Inject constructor( // The registration is forced in 2 cases : // * in the settings // * in the troubleshoot list (doFix) + // TODO remove and replace by use case fun forceRegister( activity: FragmentActivity, pushersManager: PushersManager, - onDoneRunnable: Runnable? = null + @MainThread onDoneRunnable: Runnable? = null ) { registerInternal( activity, @@ -79,17 +84,21 @@ class UnifiedPushHelper @Inject constructor( ) } + // TODO remove private fun registerInternal( activity: FragmentActivity, force: Boolean = false, pushersManager: PushersManager? = null, onDoneRunnable: Runnable? = null ) { - activity.lifecycleScope.launch { + activity.lifecycleScope.launch(Dispatchers.IO) { + Timber.d("registerInternal force=$force, $activity on thread ${Thread.currentThread()}") if (!vectorFeatures.allowExternalUnifiedPushDistributors()) { UnifiedPush.saveDistributor(context, context.packageName) UnifiedPush.registerApp(context) - onDoneRunnable?.run() + withContext(Dispatchers.Main) { + onDoneRunnable?.run() + } return@launch } if (force) { @@ -99,7 +108,9 @@ class UnifiedPushHelper @Inject constructor( // the !force should not be needed if (!force && UnifiedPush.getDistributor(context).isNotEmpty()) { UnifiedPush.registerApp(context) - onDoneRunnable?.run() + withContext(Dispatchers.Main) { + onDoneRunnable?.run() + } return@launch } @@ -108,7 +119,9 @@ class UnifiedPushHelper @Inject constructor( if (!force && distributors.size == 1) { UnifiedPush.saveDistributor(context, distributors.first()) UnifiedPush.registerApp(context) - onDoneRunnable?.run() + withContext(Dispatchers.Main) { + onDoneRunnable?.run() + } } else { openDistributorDialogInternal( activity = activity, @@ -164,6 +177,43 @@ class UnifiedPushHelper @Inject constructor( .show() } + @MainThread + fun showSelectDistributorDialog( + context: Context, + distributors: List, + onDistributorSelected: (String) -> Unit, + ) { + val internalDistributorName = stringProvider.getString( + if (fcmHelper.isFirebaseAvailable()) { + R.string.unifiedpush_distributor_fcm_fallback + } else { + R.string.unifiedpush_distributor_background_sync + } + ) + + val distributorsName = distributors.map { + if (it == context.packageName) { + internalDistributorName + } else { + context.getApplicationLabel(it) + } + } + + MaterialAlertDialogBuilder(context) + .setTitle(stringProvider.getString(R.string.unifiedpush_getdistributors_dialog_title)) + .setItems(distributorsName.toTypedArray()) { _, which -> + val distributor = distributors[which] + onDistributorSelected(distributor) + } + .setOnCancelListener { + // By default, use internal solution (fcm/background sync) + onDistributorSelected(context.packageName) + } + .setCancelable(true) + .show() + } + + // TODO remove and replace by use case suspend fun unregister(pushersManager: PushersManager? = null) { val mode = BackgroundSyncMode.FDROID_BACKGROUND_SYNC_MODE_FOR_REALTIME vectorPreferences.setFdroidSyncBackgroundMode(mode) diff --git a/vector/src/main/java/im/vector/app/core/pushers/UnregisterUnifiedPushUseCase.kt b/vector/src/main/java/im/vector/app/core/pushers/UnregisterUnifiedPushUseCase.kt new file mode 100644 index 0000000000..d81581679e --- /dev/null +++ b/vector/src/main/java/im/vector/app/core/pushers/UnregisterUnifiedPushUseCase.kt @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2022 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 im.vector.app.core.pushers + +import android.content.Context +import dagger.hilt.android.qualifiers.ApplicationContext +import im.vector.app.features.settings.BackgroundSyncMode +import im.vector.app.features.settings.VectorPreferences +import org.unifiedpush.android.connector.UnifiedPush +import timber.log.Timber +import javax.inject.Inject + +class UnregisterUnifiedPushUseCase @Inject constructor( + @ApplicationContext private val context: Context, + private val pushersManager: PushersManager, + private val vectorPreferences: VectorPreferences, + private val unifiedPushStore: UnifiedPushStore, + private val unifiedPushHelper: UnifiedPushHelper, +) { + + // TODO add unit tests + suspend fun execute() { + val mode = BackgroundSyncMode.FDROID_BACKGROUND_SYNC_MODE_FOR_REALTIME + vectorPreferences.setFdroidSyncBackgroundMode(mode) + try { + unifiedPushHelper.getEndpointOrToken()?.let { + Timber.d("Removing $it") + pushersManager.unregisterPusher(it) + } + } catch (e: Exception) { + Timber.d(e, "Probably unregistering a non existing pusher") + } + unifiedPushStore.storeUpEndpoint(null) + unifiedPushStore.storePushGateway(null) + UnifiedPush.unregisterApp(context) + } +} diff --git a/vector/src/main/java/im/vector/app/features/home/HomeActivity.kt b/vector/src/main/java/im/vector/app/features/home/HomeActivity.kt index 2df94fecad..14157a1de8 100644 --- a/vector/src/main/java/im/vector/app/features/home/HomeActivity.kt +++ b/vector/src/main/java/im/vector/app/features/home/HomeActivity.kt @@ -44,8 +44,6 @@ import im.vector.app.core.extensions.restart import im.vector.app.core.extensions.validateBackPressed import im.vector.app.core.platform.VectorBaseActivity import im.vector.app.core.platform.VectorMenuProvider -import im.vector.app.core.pushers.FcmHelper -import im.vector.app.core.pushers.PushersManager import im.vector.app.core.pushers.UnifiedPushHelper import im.vector.app.core.utils.registerForPermissionsResult import im.vector.app.core.utils.startSharePlainTextIntent @@ -128,7 +126,6 @@ class HomeActivity : private val serverBackupStatusViewModel: ServerBackupStatusViewModel by viewModel() @Inject lateinit var vectorUncaughtExceptionHandler: VectorUncaughtExceptionHandler - @Inject lateinit var pushersManager: PushersManager @Inject lateinit var notificationDrawerManager: NotificationDrawerManager @Inject lateinit var popupAlertManager: PopupAlertManager @Inject lateinit var shortcutsHandler: ShortcutsHandler @@ -137,7 +134,6 @@ class HomeActivity : @Inject lateinit var initSyncStepFormatter: InitSyncStepFormatter @Inject lateinit var spaceStateHandler: SpaceStateHandler @Inject lateinit var unifiedPushHelper: UnifiedPushHelper - @Inject lateinit var fcmHelper: FcmHelper @Inject lateinit var nightlyProxy: NightlyProxy @Inject lateinit var disclaimerDialog: DisclaimerDialog @Inject lateinit var notificationPermissionManager: NotificationPermissionManager @@ -209,16 +205,6 @@ class HomeActivity : isNewAppLayoutEnabled = vectorPreferences.isNewAppLayoutEnabled() analyticsScreenName = MobileScreen.ScreenName.Home supportFragmentManager.registerFragmentLifecycleCallbacks(fragmentLifecycleCallbacks, false) - unifiedPushHelper.register(this) { - if (unifiedPushHelper.isEmbeddedDistributor()) { - fcmHelper.ensureFcmTokenIsRetrieved( - this, - pushersManager, - homeActivityViewModel.shouldAddHttpPusher() - ) - } - } - sharedActionViewModel = viewModelProvider[HomeSharedActionViewModel::class.java] roomListSharedActionViewModel = viewModelProvider[RoomListSharedActionViewModel::class.java] views.drawerLayout.addDrawerListener(drawerListener) @@ -280,6 +266,7 @@ class HomeActivity : HomeActivityViewEvents.ShowReleaseNotes -> handleShowReleaseNotes() HomeActivityViewEvents.NotifyUserForThreadsMigration -> handleNotifyUserForThreadsMigration() is HomeActivityViewEvents.MigrateThreads -> migrateThreadsIfNeeded(it.checkSession) + is HomeActivityViewEvents.AskUserForPushDistributor -> askUserToSelectPushDistributor(it.distributors) } } homeActivityViewModel.onEach { renderState(it) } @@ -292,6 +279,12 @@ class HomeActivity : homeActivityViewModel.handle(HomeActivityViewActions.ViewStarted) } + private fun askUserToSelectPushDistributor(distributors: List) { + unifiedPushHelper.showSelectDistributorDialog(this, distributors) { selection -> + homeActivityViewModel.handle(HomeActivityViewActions.RegisterPushDistributor(selection)) + } + } + private fun handleShowNotificationDialog() { notificationPermissionManager.eventuallyRequestPermission(this, postPermissionLauncher) } @@ -415,14 +408,6 @@ class HomeActivity : } private fun renderState(state: HomeActivityViewState) { - lifecycleScope.launch { - if (state.areNotificationsSilenced) { - unifiedPushHelper.unregister(pushersManager) - } else { - unifiedPushHelper.register(this@HomeActivity) - } - } - when (val status = state.syncRequestState) { is SyncRequestState.InitialSyncProgressing -> { val initSyncStepStr = initSyncStepFormatter.format(status.initialSyncStep) diff --git a/vector/src/main/java/im/vector/app/features/home/HomeActivityViewActions.kt b/vector/src/main/java/im/vector/app/features/home/HomeActivityViewActions.kt index 5f89c89bc9..54392d5f56 100644 --- a/vector/src/main/java/im/vector/app/features/home/HomeActivityViewActions.kt +++ b/vector/src/main/java/im/vector/app/features/home/HomeActivityViewActions.kt @@ -21,4 +21,5 @@ import im.vector.app.core.platform.VectorViewModelAction sealed interface HomeActivityViewActions : VectorViewModelAction { object ViewStarted : HomeActivityViewActions object PushPromptHasBeenReviewed : HomeActivityViewActions + data class RegisterPushDistributor(val distributor: String) : HomeActivityViewActions } diff --git a/vector/src/main/java/im/vector/app/features/home/HomeActivityViewEvents.kt b/vector/src/main/java/im/vector/app/features/home/HomeActivityViewEvents.kt index e548fdb2f3..6fdf441d1d 100644 --- a/vector/src/main/java/im/vector/app/features/home/HomeActivityViewEvents.kt +++ b/vector/src/main/java/im/vector/app/features/home/HomeActivityViewEvents.kt @@ -25,9 +25,11 @@ sealed interface HomeActivityViewEvents : VectorViewEvents { val userItem: MatrixItem.UserItem, val waitForIncomingRequest: Boolean = true, ) : HomeActivityViewEvents + data class CurrentSessionCannotBeVerified( val userItem: MatrixItem.UserItem, ) : HomeActivityViewEvents + data class OnCrossSignedInvalidated(val userItem: MatrixItem.UserItem) : HomeActivityViewEvents object PromptToEnableSessionPush : HomeActivityViewEvents object ShowAnalyticsOptIn : HomeActivityViewEvents @@ -37,4 +39,5 @@ sealed interface HomeActivityViewEvents : VectorViewEvents { data class MigrateThreads(val checkSession: Boolean) : HomeActivityViewEvents object StartRecoverySetupFlow : HomeActivityViewEvents data class ForceVerification(val sendRequest: Boolean) : HomeActivityViewEvents + data class AskUserForPushDistributor(val distributors: List) : HomeActivityViewEvents } diff --git a/vector/src/main/java/im/vector/app/features/home/HomeActivityViewModel.kt b/vector/src/main/java/im/vector/app/features/home/HomeActivityViewModel.kt index 49f2079625..3fd555bbea 100644 --- a/vector/src/main/java/im/vector/app/features/home/HomeActivityViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/home/HomeActivityViewModel.kt @@ -16,7 +16,6 @@ package im.vector.app.features.home -import androidx.lifecycle.asFlow import com.airbnb.mvrx.Mavericks import com.airbnb.mvrx.MavericksViewModelFactory import com.airbnb.mvrx.ViewModelContext @@ -27,7 +26,9 @@ import im.vector.app.core.di.ActiveSessionHolder import im.vector.app.core.di.MavericksAssistedViewModelFactory import im.vector.app.core.di.hiltMavericksViewModelFactory import im.vector.app.core.platform.VectorViewModel -import im.vector.app.features.VectorFeatures +import im.vector.app.core.pushers.EnsureFcmTokenIsRetrievedUseCase +import im.vector.app.core.pushers.PushersManager +import im.vector.app.core.pushers.RegisterUnifiedPushUseCase import im.vector.app.features.analytics.AnalyticsConfig import im.vector.app.features.analytics.AnalyticsTracker import im.vector.app.features.analytics.extensions.toAnalyticsType @@ -48,12 +49,10 @@ import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.delay import kotlinx.coroutines.flow.collect import kotlinx.coroutines.flow.launchIn -import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.onCompletion import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.takeWhile import kotlinx.coroutines.launch -import org.matrix.android.sdk.api.account.LocalNotificationSettingsContent import org.matrix.android.sdk.api.auth.UIABaseAuth import org.matrix.android.sdk.api.auth.UserInteractiveAuthInterceptor import org.matrix.android.sdk.api.auth.UserPasswordAuth @@ -62,11 +61,9 @@ import org.matrix.android.sdk.api.auth.registration.RegistrationFlowResponse import org.matrix.android.sdk.api.auth.registration.nextUncompletedStage import org.matrix.android.sdk.api.extensions.tryOrNull import org.matrix.android.sdk.api.raw.RawService -import org.matrix.android.sdk.api.session.accountdata.UserAccountDataTypes import org.matrix.android.sdk.api.session.crypto.crosssigning.CrossSigningService import org.matrix.android.sdk.api.session.crypto.model.CryptoDeviceInfo import org.matrix.android.sdk.api.session.crypto.model.MXUsersDevicesMap -import org.matrix.android.sdk.api.session.events.model.toModel import org.matrix.android.sdk.api.session.getUserOrDefault import org.matrix.android.sdk.api.session.pushrules.RuleIds import org.matrix.android.sdk.api.session.room.model.Membership @@ -92,8 +89,10 @@ class HomeActivityViewModel @AssistedInject constructor( private val analyticsTracker: AnalyticsTracker, private val analyticsConfig: AnalyticsConfig, private val releaseNotesPreferencesStore: ReleaseNotesPreferencesStore, - private val vectorFeatures: VectorFeatures, private val stopOngoingVoiceBroadcastUseCase: StopOngoingVoiceBroadcastUseCase, + private val pushersManager: PushersManager, + private val registerUnifiedPushUseCase: RegisterUnifiedPushUseCase, + private val ensureFcmTokenIsRetrievedUseCase: EnsureFcmTokenIsRetrievedUseCase, ) : VectorViewModel(initialState) { @AssistedFactory @@ -117,17 +116,32 @@ class HomeActivityViewModel @AssistedInject constructor( private fun initialize() { if (isInitialized) return isInitialized = true + registerUnifiedPush(distributor = "") cleanupFiles() observeInitialSync() checkSessionPushIsOn() observeCrossSigningReset() observeAnalytics() observeReleaseNotes() - observeLocalNotificationsSilenced() initThreadsMigration() viewModelScope.launch { stopOngoingVoiceBroadcastUseCase.execute() } } + private fun registerUnifiedPush(distributor: String) { + viewModelScope.launch { + when (val result = registerUnifiedPushUseCase.execute(distributor = distributor)) { + is RegisterUnifiedPushUseCase.RegisterUnifiedPushResult.NeedToAskUserForDistributor -> { + Timber.d("registerUnifiedPush $distributor need to ask user") + _viewEvents.post(HomeActivityViewEvents.AskUserForPushDistributor(result.distributors)) + } + RegisterUnifiedPushUseCase.RegisterUnifiedPushResult.Success -> { + Timber.d("registerUnifiedPush $distributor success") + ensureFcmTokenIsRetrievedUseCase.execute(pushersManager, registerPusher = vectorPreferences.areNotificationEnabledForDevice()) + } + } + } + } + private fun observeReleaseNotes() = withState { state -> if (vectorPreferences.isNewAppLayoutEnabled()) { // we don't want to show release notes for new users or after relogin @@ -146,26 +160,6 @@ class HomeActivityViewModel @AssistedInject constructor( } } - fun shouldAddHttpPusher() = if (vectorPreferences.areNotificationEnabledForDevice()) { - val currentSession = activeSessionHolder.getActiveSession() - val currentPushers = currentSession.pushersService().getPushers() - currentPushers.none { it.deviceId == currentSession.sessionParams.deviceId } - } else { - false - } - - fun observeLocalNotificationsSilenced() { - val currentSession = activeSessionHolder.getActiveSession() - val deviceId = currentSession.cryptoService().getMyDevice().deviceId - viewModelScope.launch { - currentSession.accountDataService() - .getLiveUserAccountDataEvent(UserAccountDataTypes.TYPE_LOCAL_NOTIFICATION_SETTINGS + deviceId) - .asFlow() - .map { it.getOrNull()?.content?.toModel()?.isSilenced ?: false } - .onEach { setState { copy(areNotificationsSilenced = it) } } - } - } - private fun observeAnalytics() { if (analyticsConfig.isEnabled) { analyticsStore.didAskUserConsentFlow @@ -501,6 +495,9 @@ class HomeActivityViewModel @AssistedInject constructor( HomeActivityViewActions.ViewStarted -> { initialize() } + is HomeActivityViewActions.RegisterPushDistributor -> { + registerUnifiedPush(distributor = action.distributor) + } } } } diff --git a/vector/src/main/java/im/vector/app/features/home/HomeActivityViewState.kt b/vector/src/main/java/im/vector/app/features/home/HomeActivityViewState.kt index 4df2957cbc..f9c1b37ed5 100644 --- a/vector/src/main/java/im/vector/app/features/home/HomeActivityViewState.kt +++ b/vector/src/main/java/im/vector/app/features/home/HomeActivityViewState.kt @@ -23,5 +23,4 @@ import org.matrix.android.sdk.api.session.sync.SyncRequestState data class HomeActivityViewState( val syncRequestState: SyncRequestState = SyncRequestState.Idle, val authenticationDescription: AuthenticationDescription? = null, - val areNotificationsSilenced: Boolean = false, ) : MavericksState diff --git a/vector/src/main/java/im/vector/app/features/settings/notifications/EnableNotificationsForCurrentSessionUseCase.kt b/vector/src/main/java/im/vector/app/features/settings/notifications/EnableNotificationsForCurrentSessionUseCase.kt index 180627a15f..91974787bd 100644 --- a/vector/src/main/java/im/vector/app/features/settings/notifications/EnableNotificationsForCurrentSessionUseCase.kt +++ b/vector/src/main/java/im/vector/app/features/settings/notifications/EnableNotificationsForCurrentSessionUseCase.kt @@ -18,6 +18,7 @@ package im.vector.app.features.settings.notifications import androidx.fragment.app.FragmentActivity import im.vector.app.core.di.ActiveSessionHolder +import im.vector.app.core.pushers.EnsureFcmTokenIsRetrievedUseCase import im.vector.app.core.pushers.FcmHelper import im.vector.app.core.pushers.PushersManager import im.vector.app.core.pushers.UnifiedPushHelper @@ -32,11 +33,12 @@ class EnableNotificationsForCurrentSessionUseCase @Inject constructor( private val activeSessionHolder: ActiveSessionHolder, private val unifiedPushHelper: UnifiedPushHelper, private val pushersManager: PushersManager, - private val fcmHelper: FcmHelper, private val checkIfCanTogglePushNotificationsViaPusherUseCase: CheckIfCanTogglePushNotificationsViaPusherUseCase, private val togglePushNotificationUseCase: TogglePushNotificationUseCase, + private val ensureFcmTokenIsRetrievedUseCase: EnsureFcmTokenIsRetrievedUseCase, ) { + // TODO update unit tests suspend fun execute(fragmentActivity: FragmentActivity) { val pusherForCurrentSession = pushersManager.getPusherForCurrentSession() if (pusherForCurrentSession == null) { @@ -54,13 +56,7 @@ class EnableNotificationsForCurrentSessionUseCase @Inject constructor( suspendCoroutine { continuation -> try { unifiedPushHelper.register(fragmentActivity) { - if (unifiedPushHelper.isEmbeddedDistributor()) { - fcmHelper.ensureFcmTokenIsRetrieved( - fragmentActivity, - pushersManager, - registerPusher = true - ) - } + ensureFcmTokenIsRetrievedUseCase.execute(pushersManager, registerPusher = true) continuation.resume(Unit) } } catch (error: Exception) { diff --git a/vector/src/main/java/im/vector/app/features/settings/notifications/VectorSettingsNotificationPreferenceFragment.kt b/vector/src/main/java/im/vector/app/features/settings/notifications/VectorSettingsNotificationPreferenceFragment.kt index 58f86bc949..f8c1a9ad44 100644 --- a/vector/src/main/java/im/vector/app/features/settings/notifications/VectorSettingsNotificationPreferenceFragment.kt +++ b/vector/src/main/java/im/vector/app/features/settings/notifications/VectorSettingsNotificationPreferenceFragment.kt @@ -37,6 +37,7 @@ import im.vector.app.core.preference.VectorEditTextPreference import im.vector.app.core.preference.VectorPreference import im.vector.app.core.preference.VectorPreferenceCategory import im.vector.app.core.preference.VectorSwitchPreference +import im.vector.app.core.pushers.EnsureFcmTokenIsRetrievedUseCase import im.vector.app.core.pushers.FcmHelper import im.vector.app.core.pushers.PushersManager import im.vector.app.core.pushers.UnifiedPushHelper @@ -82,6 +83,7 @@ class VectorSettingsNotificationPreferenceFragment : @Inject lateinit var notificationPermissionManager: NotificationPermissionManager @Inject lateinit var disableNotificationsForCurrentSessionUseCase: DisableNotificationsForCurrentSessionUseCase @Inject lateinit var enableNotificationsForCurrentSessionUseCase: EnableNotificationsForCurrentSessionUseCase + @Inject lateinit var ensureFcmTokenIsRetrievedUseCase: EnsureFcmTokenIsRetrievedUseCase override var titleRes: Int = R.string.settings_notifications override val preferenceXmlRes = R.xml.vector_settings_notifications @@ -183,13 +185,7 @@ class VectorSettingsNotificationPreferenceFragment : it.summary = unifiedPushHelper.getCurrentDistributorName() it.onPreferenceClickListener = Preference.OnPreferenceClickListener { unifiedPushHelper.forceRegister(requireActivity(), pushersManager) { - if (unifiedPushHelper.isEmbeddedDistributor()) { - fcmHelper.ensureFcmTokenIsRetrieved( - requireActivity(), - pushersManager, - vectorPreferences.areNotificationEnabledForDevice() - ) - } + ensureFcmTokenIsRetrievedUseCase.execute(pushersManager, registerPusher = vectorPreferences.areNotificationEnabledForDevice()) it.summary = unifiedPushHelper.getCurrentDistributorName() session.pushersService().refreshPushers() refreshBackgroundSyncPrefs()