Merge pull request #6548 from vector-im/feature/bma/realm_migration_bg

Realm migration bg
This commit is contained in:
Benoit Marty 2022-07-21 14:08:03 +02:00 committed by GitHub
commit fabdc20887
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
28 changed files with 511 additions and 76 deletions

1
changelog.d/6548.feature Normal file
View File

@ -0,0 +1 @@
Move initialization of the Session to a background thread. MainActivity is restoring the session now, instead of VectorApplication. Useful when for instance a long migration of a database is required.

View File

@ -380,6 +380,11 @@
android:exported="false" android:exported="false"
android:foregroundServiceType="location" /> android:foregroundServiceType="location" />
<service
android:name=".features.start.StartAppAndroidService"
android:exported="false"
android:foregroundServiceType="dataSync" />
<service <service
android:name=".features.call.webrtc.ScreenCaptureAndroidService" android:name=".features.call.webrtc.ScreenCaptureAndroidService"
android:exported="false" android:exported="false"

View File

@ -41,8 +41,6 @@ import com.vanniktech.emoji.EmojiManager
import com.vanniktech.emoji.google.GoogleEmojiProvider import com.vanniktech.emoji.google.GoogleEmojiProvider
import dagger.hilt.android.HiltAndroidApp import dagger.hilt.android.HiltAndroidApp
import im.vector.app.core.di.ActiveSessionHolder 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.analytics.VectorAnalytics
import im.vector.app.features.call.webrtc.WebRtcCallManager import im.vector.app.features.call.webrtc.WebRtcCallManager
import im.vector.app.features.configuration.VectorConfiguration import im.vector.app.features.configuration.VectorConfiguration
@ -165,14 +163,6 @@ class VectorApplication :
doNotShowDisclaimerDialog(this) 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 { ProcessLifecycleOwner.get().lifecycle.addObserver(object : DefaultLifecycleObserver {
override fun onResume(owner: LifecycleOwner) { override fun onResume(owner: LifecycleOwner) {
Timber.i("App entered foreground") Timber.i("App entered foreground")
@ -205,14 +195,6 @@ class VectorApplication :
Mapbox.getInstance(this) 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() { private fun enableStrictModeIfNeeded() {
if (BuildConfig.ENABLE_STRICT_MODE_LOGS) { if (BuildConfig.ENABLE_STRICT_MODE_LOGS) {
StrictMode.setThreadPolicy( StrictMode.setThreadPolicy(

View File

@ -0,0 +1,40 @@
/*
* 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 shouldSetActionSession(): Boolean {
return authenticationService.hasAuthenticatedSessions() && !activeSessionHolder.hasActiveSession()
}
fun tryToSetActiveSession(startSync: Boolean) {
if (shouldSetActionSession()) {
val lastAuthenticatedSession = authenticationService.getLastAuthenticatedSession()!!
activeSessionHolder.setActiveSession(lastAuthenticatedSession)
lastAuthenticatedSession.configureAndStart(applicationContext, startSyncing = startSync)
}
}
}

View File

@ -111,6 +111,7 @@ import im.vector.app.features.spaces.manage.SpaceManageSharedViewModel
import im.vector.app.features.spaces.people.SpacePeopleViewModel import im.vector.app.features.spaces.people.SpacePeopleViewModel
import im.vector.app.features.spaces.preview.SpacePreviewViewModel import im.vector.app.features.spaces.preview.SpacePreviewViewModel
import im.vector.app.features.spaces.share.ShareSpaceViewModel import im.vector.app.features.spaces.share.ShareSpaceViewModel
import im.vector.app.features.start.StartAppViewModel
import im.vector.app.features.terms.ReviewTermsViewModel import im.vector.app.features.terms.ReviewTermsViewModel
import im.vector.app.features.usercode.UserCodeSharedViewModel import im.vector.app.features.usercode.UserCodeSharedViewModel
import im.vector.app.features.userdirectory.UserListViewModel import im.vector.app.features.userdirectory.UserListViewModel
@ -483,6 +484,11 @@ interface MavericksViewModelModule {
@MavericksViewModelKey(AnalyticsAccountDataViewModel::class) @MavericksViewModelKey(AnalyticsAccountDataViewModel::class)
fun analyticsAccountDataViewModelFactory(factory: AnalyticsAccountDataViewModel.Factory): MavericksAssistedViewModelFactory<*, *> fun analyticsAccountDataViewModelFactory(factory: AnalyticsAccountDataViewModel.Factory): MavericksAssistedViewModelFactory<*, *>
@Binds
@IntoMap
@MavericksViewModelKey(StartAppViewModel::class)
fun startAppViewModelFactory(factory: StartAppViewModel.Factory): MavericksAssistedViewModelFactory<*, *>
@Binds @Binds
@IntoMap @IntoMap
@MavericksViewModelKey(HomeServerCapabilitiesViewModel::class) @MavericksViewModelKey(HomeServerCapabilitiesViewModel::class)

View File

@ -27,6 +27,7 @@ import androidx.localbroadcastmanager.content.LocalBroadcastManager
import dagger.hilt.android.AndroidEntryPoint import dagger.hilt.android.AndroidEntryPoint
import im.vector.app.BuildConfig import im.vector.app.BuildConfig
import im.vector.app.core.di.ActiveSessionHolder 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.network.WifiDetector
import im.vector.app.core.pushers.model.PushData import im.vector.app.core.pushers.model.PushData
import im.vector.app.core.services.GuardServiceStarter import im.vector.app.core.services.GuardServiceStarter
@ -59,6 +60,7 @@ class VectorMessagingReceiver : MessagingReceiver() {
@Inject lateinit var notificationDrawerManager: NotificationDrawerManager @Inject lateinit var notificationDrawerManager: NotificationDrawerManager
@Inject lateinit var notifiableEventResolver: NotifiableEventResolver @Inject lateinit var notifiableEventResolver: NotifiableEventResolver
@Inject lateinit var pushersManager: PushersManager @Inject lateinit var pushersManager: PushersManager
@Inject lateinit var activeSessionSetter: ActiveSessionSetter
@Inject lateinit var activeSessionHolder: ActiveSessionHolder @Inject lateinit var activeSessionHolder: ActiveSessionHolder
@Inject lateinit var vectorPreferences: VectorPreferences @Inject lateinit var vectorPreferences: VectorPreferences
@Inject lateinit var vectorDataStore: VectorDataStore @Inject lateinit var vectorDataStore: VectorDataStore
@ -177,6 +179,11 @@ class VectorMessagingReceiver : MessagingReceiver() {
} }
val session = activeSessionHolder.getSafeActiveSession() 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) { if (session == null) {
Timber.tag(loggerTag.value).w("## Can't sync from push, no current session") Timber.tag(loggerTag.value).w("## Can't sync from push, no current session")

View File

@ -17,11 +17,15 @@
package im.vector.app.features package im.vector.app.features
import android.app.Activity import android.app.Activity
import android.content.Context
import android.content.Intent import android.content.Intent
import android.os.Bundle import android.os.Bundle
import android.os.Parcelable import android.os.Parcelable
import androidx.core.content.ContextCompat
import androidx.core.view.isVisible
import androidx.lifecycle.Lifecycle import androidx.lifecycle.Lifecycle
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
import com.airbnb.mvrx.viewModel
import com.bumptech.glide.Glide import com.bumptech.glide.Glide
import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.google.android.material.dialog.MaterialAlertDialogBuilder
import dagger.hilt.android.AndroidEntryPoint import dagger.hilt.android.AndroidEntryPoint
@ -44,9 +48,16 @@ import im.vector.app.features.popup.PopupAlertManager
import im.vector.app.features.session.VectorSessionStore import im.vector.app.features.session.VectorSessionStore
import im.vector.app.features.settings.VectorPreferences import im.vector.app.features.settings.VectorPreferences
import im.vector.app.features.signout.hard.SignedOutActivity import im.vector.app.features.signout.hard.SignedOutActivity
import im.vector.app.features.start.StartAppAction
import im.vector.app.features.start.StartAppAndroidService
import im.vector.app.features.start.StartAppViewEvent
import im.vector.app.features.start.StartAppViewModel
import im.vector.app.features.start.StartAppViewState
import im.vector.app.features.themes.ActivityOtherThemes import im.vector.app.features.themes.ActivityOtherThemes
import im.vector.app.features.ui.UiStateRepository import im.vector.app.features.ui.UiStateRepository
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import kotlinx.parcelize.Parcelize import kotlinx.parcelize.Parcelize
@ -73,6 +84,8 @@ class MainActivity : VectorBaseActivity<ActivityMainBinding>(), UnlockedActivity
companion object { companion object {
private const val EXTRA_ARGS = "EXTRA_ARGS" private const val EXTRA_ARGS = "EXTRA_ARGS"
private const val EXTRA_NEXT_INTENT = "EXTRA_NEXT_INTENT"
private const val EXTRA_INIT_SESSION = "EXTRA_INIT_SESSION"
// Special action to clear cache and/or clear credentials // Special action to clear cache and/or clear credentials
fun restartApp(activity: Activity, args: MainActivityArgs) { fun restartApp(activity: Activity, args: MainActivityArgs) {
@ -82,8 +95,22 @@ class MainActivity : VectorBaseActivity<ActivityMainBinding>(), UnlockedActivity
intent.putExtra(EXTRA_ARGS, args) intent.putExtra(EXTRA_ARGS, args)
activity.startActivity(intent) activity.startActivity(intent)
} }
fun getIntentToInitSession(activity: Activity): Intent {
val intent = Intent(activity, MainActivity::class.java)
intent.putExtra(EXTRA_INIT_SESSION, true)
return intent
} }
fun getIntentWithNextIntent(context: Context, nextIntent: Intent): Intent {
val intent = Intent(context, MainActivity::class.java)
intent.putExtra(EXTRA_NEXT_INTENT, nextIntent)
return intent
}
}
private val startAppViewModel: StartAppViewModel by viewModel()
override fun getBinding() = ActivityMainBinding.inflate(layoutInflater) override fun getBinding() = ActivityMainBinding.inflate(layoutInflater)
override fun getOtherThemes() = ActivityOtherThemes.Launcher override fun getOtherThemes() = ActivityOtherThemes.Launcher
@ -103,6 +130,48 @@ class MainActivity : VectorBaseActivity<ActivityMainBinding>(), UnlockedActivity
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
startAppViewModel.onEach {
renderState(it)
}
startAppViewModel.viewEvents.stream()
.onEach(::handleViewEvents)
.launchIn(lifecycleScope)
startAppViewModel.handle(StartAppAction.StartApp)
}
private fun renderState(state: StartAppViewState) {
if (state.mayBeLongToProcess) {
views.status.setText(R.string.updating_your_data)
}
views.status.isVisible = state.mayBeLongToProcess
}
private fun handleViewEvents(event: StartAppViewEvent) {
when (event) {
StartAppViewEvent.StartForegroundService -> handleStartForegroundService()
StartAppViewEvent.AppStarted -> handleAppStarted()
}
}
private fun handleStartForegroundService() {
if (startAppViewModel.shouldStartApp()) {
// Start foreground service, because the operation may take a while
val intent = Intent(this, StartAppAndroidService::class.java)
ContextCompat.startForegroundService(this, intent)
}
}
private fun handleAppStarted() {
if (intent.hasExtra(EXTRA_NEXT_INTENT)) {
// Start the next Activity
val nextIntent = intent.getParcelableExtra<Intent>(EXTRA_NEXT_INTENT)
startIntentAndFinish(nextIntent)
} else if (intent.hasExtra(EXTRA_INIT_SESSION)) {
setResult(RESULT_OK)
finish()
} else {
args = parseArgs() args = parseArgs()
if (args.clearCredentials || args.isUserLoggedOut || args.clearCache) { if (args.clearCredentials || args.isUserLoggedOut || args.clearCache) {
clearNotifications() clearNotifications()
@ -114,6 +183,7 @@ class MainActivity : VectorBaseActivity<ActivityMainBinding>(), UnlockedActivity
startNextActivityAndFinish() startNextActivityAndFinish()
} }
} }
}
private fun clearNotifications() { private fun clearNotifications() {
// Dismiss all notifications // Dismiss all notifications
@ -241,7 +311,7 @@ class MainActivity : VectorBaseActivity<ActivityMainBinding>(), UnlockedActivity
// We have a session. // We have a session.
// Check it can be opened // Check it can be opened
if (sessionHolder.getActiveSession().isOpenable) { if (sessionHolder.getActiveSession().isOpenable) {
HomeActivity.newIntent(this, existingSession = true) HomeActivity.newIntent(this, firstStartMainActivity = false, existingSession = true)
} else { } else {
// The token is still invalid // The token is still invalid
navigator.softLogout(this) navigator.softLogout(this)
@ -253,6 +323,10 @@ class MainActivity : VectorBaseActivity<ActivityMainBinding>(), UnlockedActivity
null null
} }
} }
startIntentAndFinish(intent)
}
private fun startIntentAndFinish(intent: Intent?) {
intent?.let { startActivity(it) } intent?.let { startActivity(it) }
finish() finish()
} }

View File

@ -604,7 +604,7 @@ class VectorCallActivity :
private fun returnToChat() { private fun returnToChat() {
val roomId = withState(callViewModel) { it.roomId } val roomId = withState(callViewModel) { it.roomId }
val args = TimelineArgs(roomId) val args = TimelineArgs(roomId)
val intent = RoomDetailActivity.newIntent(this, args).apply { val intent = RoomDetailActivity.newIntent(this, args, false).apply {
flags = FLAG_ACTIVITY_CLEAR_TOP flags = FLAG_ACTIVITY_CLEAR_TOP
} }
startActivity(intent) startActivity(intent)

View File

@ -611,6 +611,7 @@ class HomeActivity :
companion object { companion object {
fun newIntent( fun newIntent(
context: Context, context: Context,
firstStartMainActivity: Boolean,
clearNotification: Boolean = false, clearNotification: Boolean = false,
authenticationDescription: AuthenticationDescription? = null, authenticationDescription: AuthenticationDescription? = null,
existingSession: Boolean = false, existingSession: Boolean = false,
@ -623,10 +624,16 @@ class HomeActivity :
inviteNotificationRoomId = inviteNotificationRoomId inviteNotificationRoomId = inviteNotificationRoomId
) )
return Intent(context, HomeActivity::class.java) val intent = Intent(context, HomeActivity::class.java)
.apply { .apply {
putExtra(Mavericks.KEY_ARG, args) putExtra(Mavericks.KEY_ARG, args)
} }
return if (firstStartMainActivity) {
MainActivity.getIntentWithNextIntent(context, intent)
} else {
intent
}
} }
} }

View File

@ -35,6 +35,7 @@ import im.vector.app.core.extensions.keepScreenOn
import im.vector.app.core.extensions.replaceFragment import im.vector.app.core.extensions.replaceFragment
import im.vector.app.core.platform.VectorBaseActivity import im.vector.app.core.platform.VectorBaseActivity
import im.vector.app.databinding.ActivityRoomDetailBinding import im.vector.app.databinding.ActivityRoomDetailBinding
import im.vector.app.features.MainActivity
import im.vector.app.features.analytics.plan.MobileScreen import im.vector.app.features.analytics.plan.MobileScreen
import im.vector.app.features.analytics.plan.ViewRoom import im.vector.app.features.analytics.plan.ViewRoom
import im.vector.app.features.home.room.breadcrumbs.BreadcrumbsFragment import im.vector.app.features.home.room.breadcrumbs.BreadcrumbsFragment
@ -191,10 +192,15 @@ class RoomDetailActivity :
const val EXTRA_ROOM_ID = "EXTRA_ROOM_ID" const val EXTRA_ROOM_ID = "EXTRA_ROOM_ID"
const val ACTION_ROOM_DETAILS_FROM_SHORTCUT = "ROOM_DETAILS_FROM_SHORTCUT" const val ACTION_ROOM_DETAILS_FROM_SHORTCUT = "ROOM_DETAILS_FROM_SHORTCUT"
fun newIntent(context: Context, timelineArgs: TimelineArgs): Intent { fun newIntent(context: Context, timelineArgs: TimelineArgs, firstStartMainActivity: Boolean): Intent {
return Intent(context, RoomDetailActivity::class.java).apply { val intent = Intent(context, RoomDetailActivity::class.java).apply {
putExtra(EXTRA_ROOM_DETAIL_ARGS, timelineArgs) putExtra(EXTRA_ROOM_DETAIL_ARGS, timelineArgs)
} }
return if (firstStartMainActivity) {
MainActivity.getIntentWithNextIntent(context, intent)
} else {
intent
}
} }
// Shortcuts can't have intents with parcelables // Shortcuts can't have intents with parcelables

View File

@ -1206,9 +1206,9 @@ class TimelineFragment @Inject constructor(
getRootThreadEventId()?.let { getRootThreadEventId()?.let {
val newRoom = timelineArgs.copy(threadTimelineArgs = null, eventId = it) val newRoom = timelineArgs.copy(threadTimelineArgs = null, eventId = it)
context?.let { con -> context?.let { con ->
val int = RoomDetailActivity.newIntent(con, newRoom) val intent = RoomDetailActivity.newIntent(con, newRoom, false)
int.flags = Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_NEW_TASK intent.flags = Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_NEW_TASK
con.startActivity(int) con.startActivity(intent)
} }
} }
} }

View File

@ -18,18 +18,23 @@ package im.vector.app.features.link
import android.content.Intent import android.content.Intent
import android.net.Uri import android.net.Uri
import android.os.Bundle
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
import com.airbnb.mvrx.viewModel
import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.google.android.material.dialog.MaterialAlertDialogBuilder
import dagger.hilt.android.AndroidEntryPoint import dagger.hilt.android.AndroidEntryPoint
import im.vector.app.R import im.vector.app.R
import im.vector.app.core.di.ActiveSessionHolder import im.vector.app.core.di.ActiveSessionHolder
import im.vector.app.core.error.ErrorFormatter import im.vector.app.core.error.ErrorFormatter
import im.vector.app.core.extensions.registerStartForActivityResult
import im.vector.app.core.platform.VectorBaseActivity import im.vector.app.core.platform.VectorBaseActivity
import im.vector.app.core.utils.toast import im.vector.app.core.utils.toast
import im.vector.app.databinding.ActivityProgressBinding import im.vector.app.databinding.ActivityProgressBinding
import im.vector.app.features.MainActivity
import im.vector.app.features.home.HomeActivity import im.vector.app.features.home.HomeActivity
import im.vector.app.features.login.LoginConfig import im.vector.app.features.login.LoginConfig
import im.vector.app.features.permalink.PermalinkHandler import im.vector.app.features.permalink.PermalinkHandler
import im.vector.app.features.start.StartAppViewModel
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import org.matrix.android.sdk.api.session.permalinks.PermalinkService import org.matrix.android.sdk.api.session.permalinks.PermalinkService
import timber.log.Timber import timber.log.Timber
@ -45,12 +50,33 @@ class LinkHandlerActivity : VectorBaseActivity<ActivityProgressBinding>() {
@Inject lateinit var errorFormatter: ErrorFormatter @Inject lateinit var errorFormatter: ErrorFormatter
@Inject lateinit var permalinkHandler: PermalinkHandler @Inject lateinit var permalinkHandler: PermalinkHandler
private val startAppViewModel: StartAppViewModel by viewModel()
override fun getBinding() = ActivityProgressBinding.inflate(layoutInflater) override fun getBinding() = ActivityProgressBinding.inflate(layoutInflater)
override fun initUiAndData() { override fun initUiAndData() {
handleIntent() handleIntent()
} }
private val launcher = registerStartForActivityResult {
if (it.resultCode == RESULT_OK) {
handleIntent()
} else {
// User has pressed back on the MainActivity, so finish also this one.
finish()
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
if (startAppViewModel.shouldStartApp()) {
launcher.launch(MainActivity.getIntentToInitSession(this))
} else {
handleIntent()
}
}
override fun onNewIntent(intent: Intent?) { override fun onNewIntent(intent: Intent?) {
super.onNewIntent(intent) super.onNewIntent(intent)
handleIntent() handleIntent()

View File

@ -221,7 +221,7 @@ open class LoginActivity : VectorBaseActivity<ActivityLoginBinding>(), UnlockedA
analyticsScreenName = MobileScreen.ScreenName.Register analyticsScreenName = MobileScreen.ScreenName.Register
} }
val authDescription = inferAuthDescription(loginViewState) val authDescription = inferAuthDescription(loginViewState)
val intent = HomeActivity.newIntent(this, authenticationDescription = authDescription) val intent = HomeActivity.newIntent(this, firstStartMainActivity = false, authenticationDescription = authDescription)
startActivity(intent) startActivity(intent)
finish() finish()
return return

View File

@ -173,7 +173,7 @@ class DefaultNavigator @Inject constructor(
} }
val args = TimelineArgs(roomId = roomId, eventId = eventId, isInviteAlreadyAccepted = isInviteAlreadyAccepted) val args = TimelineArgs(roomId = roomId, eventId = eventId, isInviteAlreadyAccepted = isInviteAlreadyAccepted)
val intent = RoomDetailActivity.newIntent(context, args) val intent = RoomDetailActivity.newIntent(context, args, false)
startActivity(context, intent, buildTask) startActivity(context, intent, buildTask)
} }
@ -203,7 +203,7 @@ class DefaultNavigator @Inject constructor(
eventId = null, eventId = null,
openShareSpaceForId = spaceId.takeIf { postSwitchSpaceAction.showShareSheet } openShareSpaceForId = spaceId.takeIf { postSwitchSpaceAction.showShareSheet }
) )
val intent = RoomDetailActivity.newIntent(context, args) val intent = RoomDetailActivity.newIntent(context, args, false)
startActivity(context, intent, false) startActivity(context, intent, false)
} }
} }
@ -290,7 +290,7 @@ class DefaultNavigator @Inject constructor(
override fun openRoomForSharingAndFinish(activity: Activity, roomId: String, sharedData: SharedData) { override fun openRoomForSharingAndFinish(activity: Activity, roomId: String, sharedData: SharedData) {
val args = TimelineArgs(roomId, null, sharedData) val args = TimelineArgs(roomId, null, sharedData)
val intent = RoomDetailActivity.newIntent(activity, args) val intent = RoomDetailActivity.newIntent(activity, args, false)
activity.startActivity(intent) activity.startActivity(intent)
activity.finish() activity.finish()
} }

View File

@ -53,6 +53,7 @@ import im.vector.app.core.resources.StringProvider
import im.vector.app.core.services.CallAndroidService import im.vector.app.core.services.CallAndroidService
import im.vector.app.core.time.Clock import im.vector.app.core.time.Clock
import im.vector.app.core.utils.startNotificationChannelSettingsIntent import im.vector.app.core.utils.startNotificationChannelSettingsIntent
import im.vector.app.features.MainActivity
import im.vector.app.features.call.VectorCallActivity import im.vector.app.features.call.VectorCallActivity
import im.vector.app.features.call.service.CallHeadsUpActionReceiver import im.vector.app.features.call.service.CallHeadsUpActionReceiver
import im.vector.app.features.call.webrtc.WebRtcCall import im.vector.app.features.call.webrtc.WebRtcCall
@ -239,9 +240,10 @@ class NotificationUtils @Inject constructor(
@SuppressLint("NewApi") @SuppressLint("NewApi")
fun buildForegroundServiceNotification(@StringRes subTitleResId: Int, withProgress: Boolean = true): Notification { fun buildForegroundServiceNotification(@StringRes subTitleResId: Int, withProgress: Boolean = true): Notification {
// build the pending intent go to the home screen if this is clicked. // build the pending intent go to the home screen if this is clicked.
val i = HomeActivity.newIntent(context) val i = HomeActivity.newIntent(context, firstStartMainActivity = false)
i.flags = Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_SINGLE_TOP i.flags = Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_SINGLE_TOP
val pi = PendingIntent.getActivity(context, 0, i, PendingIntentCompat.FLAG_IMMUTABLE) val mainIntent = MainActivity.getIntentWithNextIntent(context, i)
val pi = PendingIntent.getActivity(context, 0, mainIntent, PendingIntentCompat.FLAG_IMMUTABLE)
val accentColor = ContextCompat.getColor(context, R.color.notification_accent_color) val accentColor = ContextCompat.getColor(context, R.color.notification_accent_color)
@ -344,7 +346,7 @@ class NotificationUtils @Inject constructor(
) )
val answerCallPendingIntent = TaskStackBuilder.create(context) val answerCallPendingIntent = TaskStackBuilder.create(context)
.addNextIntentWithParentStack(HomeActivity.newIntent(context)) .addNextIntentWithParentStack(HomeActivity.newIntent(context, firstStartMainActivity = false))
.addNextIntent( .addNextIntent(
VectorCallActivity.newIntent( VectorCallActivity.newIntent(
context = context, context = context,
@ -468,7 +470,7 @@ class NotificationUtils @Inject constructor(
) )
val contentPendingIntent = TaskStackBuilder.create(context) val contentPendingIntent = TaskStackBuilder.create(context)
.addNextIntentWithParentStack(HomeActivity.newIntent(context)) .addNextIntentWithParentStack(HomeActivity.newIntent(context, firstStartMainActivity = false))
.addNextIntent(VectorCallActivity.newIntent(context, call, null)) .addNextIntent(VectorCallActivity.newIntent(context, call, null))
.getPendingIntent(clock.epochMillis().toInt(), PendingIntent.FLAG_UPDATE_CURRENT or PendingIntentCompat.FLAG_IMMUTABLE) .getPendingIntent(clock.epochMillis().toInt(), PendingIntent.FLAG_UPDATE_CURRENT or PendingIntentCompat.FLAG_IMMUTABLE)
@ -530,8 +532,8 @@ class NotificationUtils @Inject constructor(
.setCategory(NotificationCompat.CATEGORY_CALL) .setCategory(NotificationCompat.CATEGORY_CALL)
val contentPendingIntent = TaskStackBuilder.create(context) val contentPendingIntent = TaskStackBuilder.create(context)
.addNextIntentWithParentStack(HomeActivity.newIntent(context)) .addNextIntentWithParentStack(HomeActivity.newIntent(context, firstStartMainActivity = false))
.addNextIntent(RoomDetailActivity.newIntent(context, TimelineArgs(callInformation.nativeRoomId))) .addNextIntent(RoomDetailActivity.newIntent(context, TimelineArgs(callInformation.nativeRoomId), true))
.getPendingIntent(clock.epochMillis().toInt(), PendingIntent.FLAG_UPDATE_CURRENT or PendingIntentCompat.FLAG_IMMUTABLE) .getPendingIntent(clock.epochMillis().toInt(), PendingIntent.FLAG_UPDATE_CURRENT or PendingIntentCompat.FLAG_IMMUTABLE)
builder.setContentIntent(contentPendingIntent) builder.setContentIntent(contentPendingIntent)
@ -566,6 +568,19 @@ class NotificationUtils @Inject constructor(
.build() .build()
} }
/**
* Creates a notification that indicates the application is initializing.
*/
fun buildStartAppNotification(): Notification {
return NotificationCompat.Builder(context, LISTENING_FOR_EVENTS_NOTIFICATION_CHANNEL_ID)
.setContentTitle(stringProvider.getString(R.string.updating_your_data))
.setSmallIcon(R.drawable.sync)
.setColor(ThemeUtils.getColor(context, android.R.attr.colorPrimary))
.setCategory(NotificationCompat.CATEGORY_SERVICE)
.setPriority(NotificationCompat.PRIORITY_LOW)
.build()
}
fun buildDownloadFileNotification(uri: Uri, fileName: String, mimeType: String): Notification { fun buildDownloadFileNotification(uri: Uri, fileName: String, mimeType: String): Notification {
return NotificationCompat.Builder(context, SILENT_NOTIFICATION_CHANNEL_ID) return NotificationCompat.Builder(context, SILENT_NOTIFICATION_CHANNEL_ID)
.setGroup(stringProvider.getString(R.string.app_name)) .setGroup(stringProvider.getString(R.string.app_name))
@ -765,7 +780,11 @@ class NotificationUtils @Inject constructor(
joinIntentPendingIntent joinIntentPendingIntent
) )
val contentIntent = HomeActivity.newIntent(context, inviteNotificationRoomId = inviteNotifiableEvent.roomId) val contentIntent = HomeActivity.newIntent(
context,
firstStartMainActivity = true,
inviteNotificationRoomId = inviteNotifiableEvent.roomId
)
contentIntent.flags = Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_SINGLE_TOP contentIntent.flags = Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_SINGLE_TOP
// pending intent get reused by system, this will mess up the extra params, so put unique info to avoid that // pending intent get reused by system, this will mess up the extra params, so put unique info to avoid that
contentIntent.data = createIgnoredUri(inviteNotifiableEvent.eventId) contentIntent.data = createIgnoredUri(inviteNotifiableEvent.eventId)
@ -806,7 +825,7 @@ class NotificationUtils @Inject constructor(
.setColor(accentColor) .setColor(accentColor)
.setAutoCancel(true) .setAutoCancel(true)
.apply { .apply {
val contentIntent = HomeActivity.newIntent(context) val contentIntent = HomeActivity.newIntent(context, firstStartMainActivity = true)
contentIntent.flags = Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_SINGLE_TOP contentIntent.flags = Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_SINGLE_TOP
// pending intent get reused by system, this will mess up the extra params, so put unique info to avoid that // pending intent get reused by system, this will mess up the extra params, so put unique info to avoid that
contentIntent.data = createIgnoredUri(simpleNotifiableEvent.eventId) contentIntent.data = createIgnoredUri(simpleNotifiableEvent.eventId)
@ -828,14 +847,14 @@ class NotificationUtils @Inject constructor(
} }
private fun buildOpenRoomIntent(roomId: String): PendingIntent? { private fun buildOpenRoomIntent(roomId: String): PendingIntent? {
val roomIntentTap = RoomDetailActivity.newIntent(context, TimelineArgs(roomId = roomId, switchToParentSpace = true)) val roomIntentTap = RoomDetailActivity.newIntent(context, TimelineArgs(roomId = roomId, switchToParentSpace = true), true)
roomIntentTap.action = TAP_TO_VIEW_ACTION roomIntentTap.action = TAP_TO_VIEW_ACTION
// pending intent get reused by system, this will mess up the extra params, so put unique info to avoid that // pending intent get reused by system, this will mess up the extra params, so put unique info to avoid that
roomIntentTap.data = createIgnoredUri("openRoom?$roomId") roomIntentTap.data = createIgnoredUri("openRoom?$roomId")
// Recreate the back stack // Recreate the back stack
return TaskStackBuilder.create(context) return TaskStackBuilder.create(context)
.addNextIntentWithParentStack(HomeActivity.newIntent(context)) .addNextIntentWithParentStack(HomeActivity.newIntent(context, firstStartMainActivity = false))
.addNextIntent(roomIntentTap) .addNextIntent(roomIntentTap)
.getPendingIntent( .getPendingIntent(
clock.epochMillis().toInt(), clock.epochMillis().toInt(),
@ -844,13 +863,14 @@ class NotificationUtils @Inject constructor(
} }
private fun buildOpenHomePendingIntentForSummary(): PendingIntent { private fun buildOpenHomePendingIntentForSummary(): PendingIntent {
val intent = HomeActivity.newIntent(context, clearNotification = true) val intent = HomeActivity.newIntent(context, firstStartMainActivity = false, clearNotification = true)
intent.flags = Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_SINGLE_TOP intent.flags = Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_SINGLE_TOP
intent.data = createIgnoredUri("tapSummary") intent.data = createIgnoredUri("tapSummary")
val mainIntent = MainActivity.getIntentWithNextIntent(context, intent)
return PendingIntent.getActivity( return PendingIntent.getActivity(
context, context,
Random.nextInt(1000), Random.nextInt(1000),
intent, mainIntent,
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntentCompat.FLAG_IMMUTABLE PendingIntent.FLAG_UPDATE_CURRENT or PendingIntentCompat.FLAG_IMMUTABLE
) )
} }

View File

@ -302,7 +302,8 @@ class Login2Variant(
private fun terminate() { private fun terminate() {
val intent = HomeActivity.newIntent( val intent = HomeActivity.newIntent(
activity activity,
firstStartMainActivity = false,
) )
activity.startActivity(intent) activity.startActivity(intent)
activity.finish() activity.finish()

View File

@ -482,7 +482,11 @@ class FtueAuthVariant(
private fun navigateToHome() { private fun navigateToHome() {
withState(onboardingViewModel) { withState(onboardingViewModel) {
val intent = HomeActivity.newIntent(activity, authenticationDescription = it.selectedAuthenticationState.description) val intent = HomeActivity.newIntent(
activity,
firstStartMainActivity = false,
authenticationDescription = it.selectedAuthenticationState.description
)
activity.startActivity(intent) activity.startActivity(intent)
activity.finish() activity.finish()
} }

View File

@ -22,7 +22,7 @@ import org.matrix.android.sdk.api.session.room.model.RoomSummary
sealed class IncomingShareAction : VectorViewModelAction { sealed class IncomingShareAction : VectorViewModelAction {
data class SelectRoom(val roomSummary: RoomSummary, val enableMultiSelect: Boolean) : IncomingShareAction() data class SelectRoom(val roomSummary: RoomSummary, val enableMultiSelect: Boolean) : IncomingShareAction()
object ShareToSelectedRooms : IncomingShareAction() object ShareToSelectedRooms : IncomingShareAction()
data class ShareToRoom(val roomSummary: RoomSummary) : IncomingShareAction() data class ShareToRoom(val roomId: String) : IncomingShareAction()
data class ShareMedia(val keepOriginalSize: Boolean) : IncomingShareAction() data class ShareMedia(val keepOriginalSize: Boolean) : IncomingShareAction()
data class FilterWith(val filter: String) : IncomingShareAction() data class FilterWith(val filter: String) : IncomingShareAction()
data class UpdateSharedData(val sharedData: SharedData) : IncomingShareAction() data class UpdateSharedData(val sharedData: SharedData) : IncomingShareAction()

View File

@ -16,21 +16,66 @@
package im.vector.app.features.share package im.vector.app.features.share
import android.content.Intent
import android.os.Bundle
import com.airbnb.mvrx.viewModel
import dagger.hilt.android.AndroidEntryPoint import dagger.hilt.android.AndroidEntryPoint
import im.vector.app.core.di.ActiveSessionHolder
import im.vector.app.core.extensions.addFragment import im.vector.app.core.extensions.addFragment
import im.vector.app.core.extensions.registerStartForActivityResult
import im.vector.app.core.platform.VectorBaseActivity import im.vector.app.core.platform.VectorBaseActivity
import im.vector.app.databinding.ActivitySimpleBinding import im.vector.app.databinding.ActivitySimpleBinding
import im.vector.app.features.MainActivity
import im.vector.app.features.start.StartAppViewModel
import javax.inject.Inject
@AndroidEntryPoint @AndroidEntryPoint
class IncomingShareActivity : VectorBaseActivity<ActivitySimpleBinding>() { class IncomingShareActivity : VectorBaseActivity<ActivitySimpleBinding>() {
private val startAppViewModel: StartAppViewModel by viewModel()
@Inject lateinit var activeSessionHolder: ActiveSessionHolder
private val launcher = registerStartForActivityResult {
if (it.resultCode == RESULT_OK) {
handleAppStarted()
} else {
// User has pressed back on the MainActivity, so finish also this one.
finish()
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
if (startAppViewModel.shouldStartApp()) {
launcher.launch(MainActivity.getIntentToInitSession(this))
} else {
handleAppStarted()
}
}
override fun getBinding() = ActivitySimpleBinding.inflate(layoutInflater) override fun getBinding() = ActivitySimpleBinding.inflate(layoutInflater)
override fun getCoordinatorLayout() = views.coordinatorLayout override fun getCoordinatorLayout() = views.coordinatorLayout
override fun initUiAndData() { private fun handleAppStarted() {
// If we are not logged in, stop the sharing process and open login screen.
// In the future, we might want to relaunch the sharing process after login.
if (!activeSessionHolder.hasActiveSession()) {
startLoginActivity()
} else {
if (isFirstCreation()) { if (isFirstCreation()) {
addFragment(views.simpleFragmentContainer, IncomingShareFragment::class.java) addFragment(views.simpleFragmentContainer, IncomingShareFragment::class.java)
} }
} }
} }
private fun startLoginActivity() {
navigator.openLogin(
context = this,
flags = Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_CLEAR_TASK or Intent.FLAG_ACTIVITY_NEW_TASK
)
finish()
}
}

View File

@ -30,7 +30,6 @@ import com.airbnb.mvrx.fragmentViewModel
import com.airbnb.mvrx.withState import com.airbnb.mvrx.withState
import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.google.android.material.dialog.MaterialAlertDialogBuilder
import im.vector.app.R import im.vector.app.R
import im.vector.app.core.di.ActiveSessionHolder
import im.vector.app.core.extensions.cleanup import im.vector.app.core.extensions.cleanup
import im.vector.app.core.extensions.configureWith import im.vector.app.core.extensions.configureWith
import im.vector.app.core.extensions.registerStartForActivityResult import im.vector.app.core.extensions.registerStartForActivityResult
@ -40,7 +39,6 @@ import im.vector.app.features.analytics.plan.ViewRoom
import im.vector.app.features.attachments.ShareIntentHandler import im.vector.app.features.attachments.ShareIntentHandler
import im.vector.app.features.attachments.preview.AttachmentsPreviewActivity import im.vector.app.features.attachments.preview.AttachmentsPreviewActivity
import im.vector.app.features.attachments.preview.AttachmentsPreviewArgs import im.vector.app.features.attachments.preview.AttachmentsPreviewArgs
import org.matrix.android.sdk.api.session.getRoomSummary
import org.matrix.android.sdk.api.session.room.model.RoomSummary import org.matrix.android.sdk.api.session.room.model.RoomSummary
import javax.inject.Inject import javax.inject.Inject
@ -50,7 +48,6 @@ import javax.inject.Inject
*/ */
class IncomingShareFragment @Inject constructor( class IncomingShareFragment @Inject constructor(
private val incomingShareController: IncomingShareController, private val incomingShareController: IncomingShareController,
private val sessionHolder: ActiveSessionHolder,
private val shareIntentHandler: ShareIntentHandler, private val shareIntentHandler: ShareIntentHandler,
) : ) :
VectorBaseFragment<FragmentIncomingShareBinding>(), VectorBaseFragment<FragmentIncomingShareBinding>(),
@ -63,12 +60,6 @@ class IncomingShareFragment @Inject constructor(
} }
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
// If we are not logged in, stop the sharing process and open login screen.
// In the future, we might want to relaunch the sharing process after login.
if (!sessionHolder.hasActiveSession()) {
startLoginActivity()
return
}
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
setupRecyclerView() setupRecyclerView()
setupToolbar(views.incomingShareToolbar) setupToolbar(views.incomingShareToolbar)
@ -88,7 +79,7 @@ class IncomingShareFragment @Inject constructor(
// Direct share // Direct share
if (intent.hasExtra(Intent.EXTRA_SHORTCUT_ID)) { if (intent.hasExtra(Intent.EXTRA_SHORTCUT_ID)) {
val roomId = intent.getStringExtra(Intent.EXTRA_SHORTCUT_ID)!! val roomId = intent.getStringExtra(Intent.EXTRA_SHORTCUT_ID)!!
sessionHolder.getSafeActiveSession()?.getRoomSummary(roomId)?.let { viewModel.handle(IncomingShareAction.ShareToRoom(it)) } viewModel.handle(IncomingShareAction.ShareToRoom(roomId))
} }
isShareManaged isShareManaged
} }
@ -192,14 +183,6 @@ class IncomingShareFragment @Inject constructor(
.show() .show()
} }
private fun startLoginActivity() {
navigator.openLogin(
context = requireActivity(),
flags = Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_CLEAR_TASK or Intent.FLAG_ACTIVITY_NEW_TASK
)
requireActivity().finish()
}
override fun invalidate() = withState(viewModel) { override fun invalidate() = withState(viewModel) {
views.sendShareButton.isVisible = it.isInMultiSelectionMode views.sendShareButton.isVisible = it.isInMultiSelectionMode
incomingShareController.setData(it) incomingShareController.setData(it)

View File

@ -35,6 +35,7 @@ import org.matrix.android.sdk.api.query.QueryStringValue
import org.matrix.android.sdk.api.session.Session import org.matrix.android.sdk.api.session.Session
import org.matrix.android.sdk.api.session.content.ContentAttachmentData import org.matrix.android.sdk.api.session.content.ContentAttachmentData
import org.matrix.android.sdk.api.session.getRoom import org.matrix.android.sdk.api.session.getRoom
import org.matrix.android.sdk.api.session.getRoomSummary
import org.matrix.android.sdk.api.session.room.model.Membership import org.matrix.android.sdk.api.session.room.model.Membership
import org.matrix.android.sdk.api.session.room.roomSummaryQueryParams import org.matrix.android.sdk.api.session.room.roomSummaryQueryParams
import org.matrix.android.sdk.flow.flow import org.matrix.android.sdk.flow.flow
@ -134,7 +135,8 @@ class IncomingShareViewModel @AssistedInject constructor(
private fun handleShareToRoom(action: IncomingShareAction.ShareToRoom) = withState { state -> private fun handleShareToRoom(action: IncomingShareAction.ShareToRoom) = withState { state ->
val sharedData = state.sharedData ?: return@withState val sharedData = state.sharedData ?: return@withState
_viewEvents.post(IncomingShareViewEvents.ShareToRoom(action.roomSummary, sharedData, showAlert = false)) val roomSummary = session.getRoomSummary(action.roomId) ?: return@withState
_viewEvents.post(IncomingShareViewEvents.ShareToRoom(roomSummary, sharedData, showAlert = false))
} }
private fun handleShareMediaToSelectedRooms(action: IncomingShareAction.ShareMedia) = withState { state -> private fun handleShareMediaToSelectedRooms(action: IncomingShareAction.ShareMedia) = withState { state ->

View File

@ -0,0 +1,23 @@
/*
* 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.start
import im.vector.app.core.platform.VectorViewModelAction
sealed interface StartAppAction : VectorViewModelAction {
object StartApp : StartAppAction
}

View File

@ -0,0 +1,63 @@
/*
* 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.start
import android.content.Intent
import dagger.hilt.android.AndroidEntryPoint
import im.vector.app.core.di.ActiveSessionHolder
import im.vector.app.core.di.NamedGlobalScope
import im.vector.app.core.services.VectorAndroidService
import im.vector.app.features.notifications.NotificationUtils
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import javax.inject.Inject
import kotlin.random.Random
import kotlin.time.Duration.Companion.seconds
/**
* A simple foreground service that let the app (and the SDK) time to initialize.
* Will self stop itself once the active session is set.
*/
@AndroidEntryPoint
class StartAppAndroidService : VectorAndroidService() {
@NamedGlobalScope @Inject lateinit var globalScope: CoroutineScope
@Inject lateinit var notificationUtils: NotificationUtils
@Inject lateinit var activeSessionHolder: ActiveSessionHolder
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
showStickyNotification()
startPollingActiveSession()
return START_STICKY
}
private fun startPollingActiveSession() {
globalScope.launch {
do {
delay(1.seconds.inWholeMilliseconds)
} while (activeSessionHolder.hasActiveSession().not())
myStopSelf()
}
}
private fun showStickyNotification() {
val notificationId = Random.nextInt()
val notification = notificationUtils.buildStartAppNotification()
startForeground(notificationId, notification)
}
}

View File

@ -0,0 +1,31 @@
/*
* 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.start
import im.vector.app.core.platform.VectorViewEvents
sealed interface StartAppViewEvent : VectorViewEvents {
/**
* Will be sent if the process is taking more than 1 second.
*/
object StartForegroundService : StartAppViewEvent
/**
* Will be sent when the current Session has been set.
*/
object AppStarted : StartAppViewEvent
}

View File

@ -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.features.start
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.VectorViewModel
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import kotlin.time.Duration.Companion.seconds
class StartAppViewModel @AssistedInject constructor(
@Assisted val initialState: StartAppViewState,
private val activeSessionSetter: ActiveSessionSetter,
) : VectorViewModel<StartAppViewState, StartAppAction, StartAppViewEvent>(initialState) {
@AssistedFactory
interface Factory : MavericksAssistedViewModelFactory<StartAppViewModel, StartAppViewState> {
override fun create(initialState: StartAppViewState): StartAppViewModel
}
companion object : MavericksViewModelFactory<StartAppViewModel, StartAppViewState> by hiltMavericksViewModelFactory()
fun shouldStartApp(): Boolean {
return activeSessionSetter.shouldSetActionSession()
}
override fun handle(action: StartAppAction) {
when (action) {
StartAppAction.StartApp -> handleStartApp()
}
}
private fun handleStartApp() {
handleLongProcessing()
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(StartAppViewEvent.AppStarted)
}
}
private fun handleLongProcessing() {
viewModelScope.launch(Dispatchers.Default) {
delay(1.seconds.inWholeMilliseconds)
setState { copy(mayBeLongToProcess = true) }
_viewEvents.post(StartAppViewEvent.StartForegroundService)
}
}
}

View File

@ -0,0 +1,23 @@
/*
* 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.start
import com.airbnb.mvrx.MavericksState
data class StartAppViewState(
val mayBeLongToProcess: Boolean = false
) : MavericksState

View File

@ -1,5 +1,4 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?><!-- The background of this Activity is in the theme (so just added as tools here, for preview),
<!-- The background of this Activity is in the theme (so just added as tools here, for preview),
so we just add a ProgressBar here --> so we just add a ProgressBar here -->
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
@ -22,4 +21,20 @@
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" /> app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/status"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/layout_horizontal_margin"
android:layout_marginTop="8dp"
android:layout_marginEnd="@dimen/layout_horizontal_margin"
android:gravity="center"
android:textColor="@color/palette_white"
android:visibility="gone"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/progressBar"
tools:text="@string/updating_your_data"
tools:visibility="visible" />
</androidx.constraintlayout.widget.ConstraintLayout> </androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -1625,6 +1625,7 @@
<string name="error_no_network">No network. Please check your Internet connection.</string> <string name="error_no_network">No network. Please check your Internet connection.</string>
<string name="change_room_directory_network">"Change network"</string> <string name="change_room_directory_network">"Change network"</string>
<string name="please_wait">"Please wait…"</string> <string name="please_wait">"Please wait…"</string>
<string name="updating_your_data">Updating your data…</string>
<!--TODO: delete--> <!--TODO: delete-->
<string name="group_all_communities" tools:ignore="UnusedResources">"All Communities"</string> <string name="group_all_communities" tools:ignore="UnusedResources">"All Communities"</string>