diff --git a/vector/src/debug/java/im/vector/app/features/MainViewModel.kt b/vector/src/debug/java/im/vector/app/features/MainViewModel.kt new file mode 100644 index 0000000000..51fdabebc8 --- /dev/null +++ b/vector/src/debug/java/im/vector/app/features/MainViewModel.kt @@ -0,0 +1,66 @@ +/* + * 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.features + +import com.airbnb.mvrx.MavericksViewModelFactory +import dagger.assisted.Assisted +import dagger.assisted.AssistedFactory +import dagger.assisted.AssistedInject +import im.vector.app.core.di.ActiveSessionSetter +import im.vector.app.core.di.MavericksAssistedViewModelFactory +import im.vector.app.core.di.hiltMavericksViewModelFactory +import im.vector.app.core.platform.VectorDummyViewState +import im.vector.app.core.platform.VectorViewEvents +import im.vector.app.core.platform.VectorViewModel +import im.vector.app.core.platform.VectorViewModelAction +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch + +sealed interface MainViewAction : VectorViewModelAction { + object StartApp : MainViewAction +} + +sealed interface MainViewEvent : VectorViewEvents { + object AppStarted : MainViewEvent +} + +class MainViewModel @AssistedInject constructor( + @Assisted val initialState: VectorDummyViewState, + private val activeSessionSetter: ActiveSessionSetter, +) : VectorViewModel(initialState) { + + @AssistedFactory + interface Factory : MavericksAssistedViewModelFactory { + override fun create(initialState: VectorDummyViewState): MainViewModel + } + + companion object : MavericksViewModelFactory by hiltMavericksViewModelFactory() + + override fun handle(action: MainViewAction) { + when (action) { + MainViewAction.StartApp -> handleStartApp() + } + } + + private fun handleStartApp() { + viewModelScope.launch(Dispatchers.IO) { + // This can take time because of DB migration(s), so do it in a background task. + activeSessionSetter.tryToSetActiveSession(startSync = true) + _viewEvents.post(MainViewEvent.AppStarted) + } + } +} diff --git a/vector/src/main/java/im/vector/app/VectorApplication.kt b/vector/src/main/java/im/vector/app/VectorApplication.kt index 3cb0423ca8..24d9770604 100644 --- a/vector/src/main/java/im/vector/app/VectorApplication.kt +++ b/vector/src/main/java/im/vector/app/VectorApplication.kt @@ -41,8 +41,6 @@ import com.vanniktech.emoji.EmojiManager import com.vanniktech.emoji.google.GoogleEmojiProvider import dagger.hilt.android.HiltAndroidApp import im.vector.app.core.di.ActiveSessionHolder -import im.vector.app.core.extensions.configureAndStart -import im.vector.app.core.extensions.startSyncing import im.vector.app.features.analytics.VectorAnalytics import im.vector.app.features.call.webrtc.WebRtcCallManager import im.vector.app.features.configuration.VectorConfiguration @@ -165,14 +163,6 @@ class VectorApplication : doNotShowDisclaimerDialog(this) } - if (authenticationService.hasAuthenticatedSessions() && !activeSessionHolder.hasActiveSession()) { - val lastAuthenticatedSession = authenticationService.getLastAuthenticatedSession()!! - activeSessionHolder.setActiveSession(lastAuthenticatedSession) - lastAuthenticatedSession.configureAndStart(applicationContext, startSyncing = false) - } - - ProcessLifecycleOwner.get().lifecycle.addObserver(startSyncOnFirstStart) - ProcessLifecycleOwner.get().lifecycle.addObserver(object : DefaultLifecycleObserver { override fun onResume(owner: LifecycleOwner) { Timber.i("App entered foreground") @@ -205,14 +195,6 @@ class VectorApplication : Mapbox.getInstance(this) } - private val startSyncOnFirstStart = object : DefaultLifecycleObserver { - override fun onStart(owner: LifecycleOwner) { - Timber.i("App process started") - authenticationService.getLastAuthenticatedSession()?.startSyncing(appContext) - ProcessLifecycleOwner.get().lifecycle.removeObserver(this) - } - } - private fun enableStrictModeIfNeeded() { if (BuildConfig.ENABLE_STRICT_MODE_LOGS) { StrictMode.setThreadPolicy( diff --git a/vector/src/main/java/im/vector/app/core/di/ActiveSessionSetter.kt b/vector/src/main/java/im/vector/app/core/di/ActiveSessionSetter.kt new file mode 100644 index 0000000000..59c2037cbe --- /dev/null +++ b/vector/src/main/java/im/vector/app/core/di/ActiveSessionSetter.kt @@ -0,0 +1,37 @@ +/* + * Copyright 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.di + +import android.content.Context +import im.vector.app.core.extensions.configureAndStart +import org.matrix.android.sdk.api.auth.AuthenticationService +import javax.inject.Inject + +class ActiveSessionSetter @Inject constructor( + private val activeSessionHolder: ActiveSessionHolder, + private val authenticationService: AuthenticationService, + private val applicationContext: Context, +) { + fun tryToSetActiveSession(startSync: Boolean) { + if (authenticationService.hasAuthenticatedSessions() && !activeSessionHolder.hasActiveSession()) { + val lastAuthenticatedSession = authenticationService.getLastAuthenticatedSession()!! + activeSessionHolder.setActiveSession(lastAuthenticatedSession) + lastAuthenticatedSession.configureAndStart(applicationContext, startSyncing = startSync) + activeSessionHolder.setActiveSession(lastAuthenticatedSession) + } + } +} diff --git a/vector/src/main/java/im/vector/app/core/di/MavericksViewModelModule.kt b/vector/src/main/java/im/vector/app/core/di/MavericksViewModelModule.kt index a3e08036ff..3cd8dc66e9 100644 --- a/vector/src/main/java/im/vector/app/core/di/MavericksViewModelModule.kt +++ b/vector/src/main/java/im/vector/app/core/di/MavericksViewModelModule.kt @@ -20,6 +20,7 @@ import dagger.Binds import dagger.Module import dagger.hilt.InstallIn import dagger.multibindings.IntoMap +import im.vector.app.features.MainViewModel import im.vector.app.features.analytics.accountdata.AnalyticsAccountDataViewModel import im.vector.app.features.analytics.ui.consent.AnalyticsConsentViewModel import im.vector.app.features.auth.ReAuthViewModel @@ -483,6 +484,11 @@ interface MavericksViewModelModule { @MavericksViewModelKey(AnalyticsAccountDataViewModel::class) fun analyticsAccountDataViewModelFactory(factory: AnalyticsAccountDataViewModel.Factory): MavericksAssistedViewModelFactory<*, *> + @Binds + @IntoMap + @MavericksViewModelKey(MainViewModel::class) + fun mainViewModelFactory(factory: MainViewModel.Factory): MavericksAssistedViewModelFactory<*, *> + @Binds @IntoMap @MavericksViewModelKey(HomeServerCapabilitiesViewModel::class) diff --git a/vector/src/main/java/im/vector/app/core/pushers/VectorMessagingReceiver.kt b/vector/src/main/java/im/vector/app/core/pushers/VectorMessagingReceiver.kt index 53a5470ff7..be84dfeaba 100644 --- a/vector/src/main/java/im/vector/app/core/pushers/VectorMessagingReceiver.kt +++ b/vector/src/main/java/im/vector/app/core/pushers/VectorMessagingReceiver.kt @@ -27,6 +27,7 @@ import androidx.localbroadcastmanager.content.LocalBroadcastManager import dagger.hilt.android.AndroidEntryPoint import im.vector.app.BuildConfig import im.vector.app.core.di.ActiveSessionHolder +import im.vector.app.core.di.ActiveSessionSetter import im.vector.app.core.network.WifiDetector import im.vector.app.core.pushers.model.PushData import im.vector.app.core.services.GuardServiceStarter @@ -59,6 +60,7 @@ class VectorMessagingReceiver : MessagingReceiver() { @Inject lateinit var notificationDrawerManager: NotificationDrawerManager @Inject lateinit var notifiableEventResolver: NotifiableEventResolver @Inject lateinit var pushersManager: PushersManager + @Inject lateinit var activeSessionSetter: ActiveSessionSetter @Inject lateinit var activeSessionHolder: ActiveSessionHolder @Inject lateinit var vectorPreferences: VectorPreferences @Inject lateinit var vectorDataStore: VectorDataStore @@ -177,6 +179,11 @@ class VectorMessagingReceiver : MessagingReceiver() { } val session = activeSessionHolder.getSafeActiveSession() + ?: run { + // Active session may not exist yet, if MainActivity has not been launched + activeSessionSetter.tryToSetActiveSession(startSync = false) + activeSessionHolder.getSafeActiveSession() + } if (session == null) { Timber.tag(loggerTag.value).w("## Can't sync from push, no current session") diff --git a/vector/src/main/java/im/vector/app/features/MainActivity.kt b/vector/src/main/java/im/vector/app/features/MainActivity.kt index c2f6f2d778..6bc0a2ca86 100644 --- a/vector/src/main/java/im/vector/app/features/MainActivity.kt +++ b/vector/src/main/java/im/vector/app/features/MainActivity.kt @@ -22,6 +22,7 @@ import android.os.Bundle import android.os.Parcelable import androidx.lifecycle.Lifecycle import androidx.lifecycle.lifecycleScope +import com.airbnb.mvrx.viewModel import com.bumptech.glide.Glide import com.google.android.material.dialog.MaterialAlertDialogBuilder import dagger.hilt.android.AndroidEntryPoint @@ -47,6 +48,8 @@ import im.vector.app.features.signout.hard.SignedOutActivity import im.vector.app.features.themes.ActivityOtherThemes import im.vector.app.features.ui.UiStateRepository import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.flow.launchIn +import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.launch import kotlinx.coroutines.withContext import kotlinx.parcelize.Parcelize @@ -84,6 +87,8 @@ class MainActivity : VectorBaseActivity(), UnlockedActivity } } + private val mainViewModel: MainViewModel by viewModel() + override fun getBinding() = ActivityMainBinding.inflate(layoutInflater) override fun getOtherThemes() = ActivityOtherThemes.Launcher @@ -103,6 +108,21 @@ class MainActivity : VectorBaseActivity(), UnlockedActivity override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) + + mainViewModel.viewEvents.stream() + .onEach(::handleViewEvents) + .launchIn(lifecycleScope) + + mainViewModel.handle(MainViewAction.StartApp) + } + + private fun handleViewEvents(event: MainViewEvent) { + when (event) { + MainViewEvent.AppStarted -> handleAppStarted() + } + } + + private fun handleAppStarted() { args = parseArgs() if (args.clearCredentials || args.isUserLoggedOut || args.clearCache) { clearNotifications()