Merge pull request #6548 from vector-im/feature/bma/realm_migration_bg
Realm migration bg
This commit is contained in:
commit
fabdc20887
1
changelog.d/6548.feature
Normal file
1
changelog.d/6548.feature
Normal 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.
|
@ -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"
|
||||||
|
@ -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(
|
||||||
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -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)
|
||||||
|
@ -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")
|
||||||
|
@ -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,15 +130,58 @@ class MainActivity : VectorBaseActivity<ActivityMainBinding>(), UnlockedActivity
|
|||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
args = parseArgs()
|
|
||||||
if (args.clearCredentials || args.isUserLoggedOut || args.clearCache) {
|
startAppViewModel.onEach {
|
||||||
clearNotifications()
|
renderState(it)
|
||||||
}
|
}
|
||||||
// Handle some wanted cleanup
|
startAppViewModel.viewEvents.stream()
|
||||||
if (args.clearCache || args.clearCredentials) {
|
.onEach(::handleViewEvents)
|
||||||
doCleanUp()
|
.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 {
|
} else {
|
||||||
startNextActivityAndFinish()
|
args = parseArgs()
|
||||||
|
if (args.clearCredentials || args.isUserLoggedOut || args.clearCache) {
|
||||||
|
clearNotifications()
|
||||||
|
}
|
||||||
|
// Handle some wanted cleanup
|
||||||
|
if (args.clearCache || args.clearCredentials) {
|
||||||
|
doCleanUp()
|
||||||
|
} else {
|
||||||
|
startNextActivityAndFinish()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -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()
|
||||||
}
|
}
|
||||||
|
@ -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)
|
||||||
|
@ -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
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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()
|
||||||
|
@ -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
|
||||||
|
@ -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()
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -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()
|
||||||
|
@ -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()
|
||||||
}
|
}
|
||||||
|
@ -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()
|
||||||
|
@ -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 (isFirstCreation()) {
|
// If we are not logged in, stop the sharing process and open login screen.
|
||||||
addFragment(views.simpleFragmentContainer, IncomingShareFragment::class.java)
|
// In the future, we might want to relaunch the sharing process after login.
|
||||||
|
if (!activeSessionHolder.hasActiveSession()) {
|
||||||
|
startLoginActivity()
|
||||||
|
} else {
|
||||||
|
if (isFirstCreation()) {
|
||||||
|
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()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -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)
|
||||||
|
@ -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 ->
|
||||||
|
@ -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
|
||||||
|
}
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
@ -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
|
||||||
|
}
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -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
|
@ -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>
|
||||||
|
@ -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>
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user