mirror of
https://github.com/element-hq/element-android
synced 2024-11-28 13:38:49 +03:00
Merge pull request #316 from vector-im/feature/initial_sync_progress
Feature/initial sync progress
This commit is contained in:
commit
a09850b16c
18 changed files with 413 additions and 107 deletions
|
@ -0,0 +1,29 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2019 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.matrix.android.api.session
|
||||||
|
|
||||||
|
import androidx.annotation.StringRes
|
||||||
|
import androidx.lifecycle.LiveData
|
||||||
|
|
||||||
|
interface InitialSyncProgressService {
|
||||||
|
|
||||||
|
fun getLiveStatus() : LiveData<Status?>
|
||||||
|
|
||||||
|
data class Status(
|
||||||
|
@StringRes val statusText: Int?,
|
||||||
|
val percentProgress: Int = 0
|
||||||
|
)
|
||||||
|
}
|
|
@ -49,7 +49,8 @@ interface Session :
|
||||||
FilterService,
|
FilterService,
|
||||||
FileService,
|
FileService,
|
||||||
PushRuleService,
|
PushRuleService,
|
||||||
PushersService {
|
PushersService,
|
||||||
|
InitialSyncProgressService {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The params associated to the session
|
* The params associated to the session
|
||||||
|
|
|
@ -0,0 +1,128 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2019 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.matrix.android.internal.session
|
||||||
|
|
||||||
|
import androidx.lifecycle.LiveData
|
||||||
|
import androidx.lifecycle.MutableLiveData
|
||||||
|
import im.vector.matrix.android.api.session.InitialSyncProgressService
|
||||||
|
import timber.log.Timber
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
|
||||||
|
@SessionScope
|
||||||
|
class DefaultInitialSyncProgressService @Inject constructor() : InitialSyncProgressService {
|
||||||
|
|
||||||
|
var status = MutableLiveData<InitialSyncProgressService.Status>()
|
||||||
|
|
||||||
|
var rootTask: TaskInfo? = null
|
||||||
|
|
||||||
|
override fun getLiveStatus(): LiveData<InitialSyncProgressService.Status?> {
|
||||||
|
return status
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fun startTask(nameRes: Int, totalProgress: Int, parentWeight: Float = 1f) {
|
||||||
|
if (rootTask == null) {
|
||||||
|
rootTask = TaskInfo(nameRes, totalProgress)
|
||||||
|
} else {
|
||||||
|
val currentLeaf = rootTask!!.leaf()
|
||||||
|
val newTask = TaskInfo(nameRes, totalProgress)
|
||||||
|
newTask.parent = currentLeaf
|
||||||
|
newTask.offset = currentLeaf.currentProgress
|
||||||
|
currentLeaf.child = newTask
|
||||||
|
newTask.parentWeight = parentWeight
|
||||||
|
}
|
||||||
|
reportProgress(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun reportProgress(progress: Int) {
|
||||||
|
rootTask?.leaf()?.incrementProgress(progress)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun endTask(nameRes: Int) {
|
||||||
|
val endedTask = rootTask?.leaf()
|
||||||
|
if (endedTask?.nameRes == nameRes) {
|
||||||
|
//close it
|
||||||
|
val parent = endedTask.parent
|
||||||
|
parent?.child = null
|
||||||
|
parent?.incrementProgress(endedTask.offset + (endedTask.totalProgress * endedTask.parentWeight).toInt())
|
||||||
|
}
|
||||||
|
if (endedTask?.parent == null) {
|
||||||
|
status.postValue(null)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun endAll() {
|
||||||
|
rootTask = null
|
||||||
|
status.postValue(null)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
inner class TaskInfo(var nameRes: Int,
|
||||||
|
var totalProgress: Int) {
|
||||||
|
var parent: TaskInfo? = null
|
||||||
|
var child: TaskInfo? = null
|
||||||
|
var parentWeight: Float = 1f
|
||||||
|
var currentProgress: Int = 0
|
||||||
|
var offset: Int = 0
|
||||||
|
|
||||||
|
fun leaf(): TaskInfo {
|
||||||
|
var last = this
|
||||||
|
while (last.child != null) {
|
||||||
|
last = last.child!!
|
||||||
|
}
|
||||||
|
return last
|
||||||
|
}
|
||||||
|
|
||||||
|
fun incrementProgress(progress: Int) {
|
||||||
|
currentProgress = progress
|
||||||
|
// val newProgress = Math.min(currentProgress + progress, totalProgress)
|
||||||
|
parent?.let {
|
||||||
|
val parentProgress = (currentProgress * parentWeight).toInt()
|
||||||
|
it.incrementProgress(offset + parentProgress)
|
||||||
|
}
|
||||||
|
if (parent == null) {
|
||||||
|
Timber.e("--- ${leaf().nameRes}: ${currentProgress}")
|
||||||
|
status.postValue(
|
||||||
|
InitialSyncProgressService.Status(leaf().nameRes, currentProgress)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
inline fun <T> reportSubtask(reporter: DefaultInitialSyncProgressService?, nameRes: Int, totalProgress: Int, parentWeight: Float = 1f, block: () -> T): T {
|
||||||
|
reporter?.startTask(nameRes, totalProgress, parentWeight)
|
||||||
|
return block().also {
|
||||||
|
reporter?.endTask(nameRes)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
inline fun <K, V, R> Map<out K, V>.mapWithProgress(reporter: DefaultInitialSyncProgressService?, taskId: Int, weight: Float, transform: (Map.Entry<K, V>) -> R): List<R> {
|
||||||
|
val total = count()
|
||||||
|
var current = 0
|
||||||
|
reporter?.startTask(taskId, 100, weight)
|
||||||
|
return this.map {
|
||||||
|
reporter?.reportProgress((current / total.toFloat() * 100).toInt())
|
||||||
|
current++
|
||||||
|
transform.invoke(it)
|
||||||
|
}.also {
|
||||||
|
reporter?.endTask(taskId)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -24,6 +24,7 @@ import androidx.work.WorkManager
|
||||||
import im.vector.matrix.android.api.MatrixCallback
|
import im.vector.matrix.android.api.MatrixCallback
|
||||||
import im.vector.matrix.android.api.auth.data.SessionParams
|
import im.vector.matrix.android.api.auth.data.SessionParams
|
||||||
import im.vector.matrix.android.api.pushrules.PushRuleService
|
import im.vector.matrix.android.api.pushrules.PushRuleService
|
||||||
|
import im.vector.matrix.android.api.session.InitialSyncProgressService
|
||||||
import im.vector.matrix.android.api.session.Session
|
import im.vector.matrix.android.api.session.Session
|
||||||
import im.vector.matrix.android.api.session.cache.CacheService
|
import im.vector.matrix.android.api.session.cache.CacheService
|
||||||
import im.vector.matrix.android.api.session.content.ContentUploadStateTracker
|
import im.vector.matrix.android.api.session.content.ContentUploadStateTracker
|
||||||
|
@ -64,7 +65,8 @@ internal class DefaultSession @Inject constructor(override val sessionParams: Se
|
||||||
private val fileService: FileService,
|
private val fileService: FileService,
|
||||||
private val syncThread: SyncThread,
|
private val syncThread: SyncThread,
|
||||||
private val contentUrlResolver: ContentUrlResolver,
|
private val contentUrlResolver: ContentUrlResolver,
|
||||||
private val contentUploadProgressTracker: ContentUploadStateTracker)
|
private val contentUploadProgressTracker: ContentUploadStateTracker,
|
||||||
|
private val initialSyncProgressService: InitialSyncProgressService)
|
||||||
: Session,
|
: Session,
|
||||||
RoomService by roomService,
|
RoomService by roomService,
|
||||||
RoomDirectoryService by roomDirectoryService,
|
RoomDirectoryService by roomDirectoryService,
|
||||||
|
@ -74,9 +76,10 @@ internal class DefaultSession @Inject constructor(override val sessionParams: Se
|
||||||
CacheService by cacheService,
|
CacheService by cacheService,
|
||||||
SignOutService by signOutService,
|
SignOutService by signOutService,
|
||||||
FilterService by filterService,
|
FilterService by filterService,
|
||||||
FileService by fileService,
|
|
||||||
PushRuleService by pushRuleService,
|
PushRuleService by pushRuleService,
|
||||||
PushersService by pushersService {
|
PushersService by pushersService,
|
||||||
|
FileService by fileService,
|
||||||
|
InitialSyncProgressService by initialSyncProgressService {
|
||||||
|
|
||||||
private var isOpen = false
|
private var isOpen = false
|
||||||
|
|
||||||
|
|
|
@ -19,6 +19,7 @@ package im.vector.matrix.android.internal.session
|
||||||
import dagger.BindsInstance
|
import dagger.BindsInstance
|
||||||
import dagger.Component
|
import dagger.Component
|
||||||
import im.vector.matrix.android.api.auth.data.SessionParams
|
import im.vector.matrix.android.api.auth.data.SessionParams
|
||||||
|
import im.vector.matrix.android.api.session.InitialSyncProgressService
|
||||||
import im.vector.matrix.android.api.session.Session
|
import im.vector.matrix.android.api.session.Session
|
||||||
import im.vector.matrix.android.internal.crypto.CryptoModule
|
import im.vector.matrix.android.internal.crypto.CryptoModule
|
||||||
import im.vector.matrix.android.internal.di.MatrixComponent
|
import im.vector.matrix.android.internal.di.MatrixComponent
|
||||||
|
|
|
@ -25,6 +25,7 @@ import dagger.multibindings.IntoSet
|
||||||
import im.vector.matrix.android.api.auth.data.Credentials
|
import im.vector.matrix.android.api.auth.data.Credentials
|
||||||
import im.vector.matrix.android.api.auth.data.HomeServerConnectionConfig
|
import im.vector.matrix.android.api.auth.data.HomeServerConnectionConfig
|
||||||
import im.vector.matrix.android.api.auth.data.SessionParams
|
import im.vector.matrix.android.api.auth.data.SessionParams
|
||||||
|
import im.vector.matrix.android.api.session.InitialSyncProgressService
|
||||||
import im.vector.matrix.android.api.session.Session
|
import im.vector.matrix.android.api.session.Session
|
||||||
import im.vector.matrix.android.internal.database.LiveEntityObserver
|
import im.vector.matrix.android.internal.database.LiveEntityObserver
|
||||||
import im.vector.matrix.android.internal.database.configureEncryption
|
import im.vector.matrix.android.internal.database.configureEncryption
|
||||||
|
@ -132,4 +133,7 @@ internal abstract class SessionModule {
|
||||||
@IntoSet
|
@IntoSet
|
||||||
abstract fun bindUserEntityUpdater(groupSummaryUpdater: UserEntityUpdater): LiveEntityObserver
|
abstract fun bindUserEntityUpdater(groupSummaryUpdater: UserEntityUpdater): LiveEntityObserver
|
||||||
|
|
||||||
|
@Binds
|
||||||
|
abstract fun bindInitialSyncProgressService(initialSyncProgressService: DefaultInitialSyncProgressService): InitialSyncProgressService
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,6 +26,8 @@ import im.vector.matrix.android.internal.crypto.CryptoManager
|
||||||
import im.vector.matrix.android.internal.crypto.MXEventDecryptionResult
|
import im.vector.matrix.android.internal.crypto.MXEventDecryptionResult
|
||||||
import im.vector.matrix.android.internal.crypto.algorithms.olm.OlmDecryptionResult
|
import im.vector.matrix.android.internal.crypto.algorithms.olm.OlmDecryptionResult
|
||||||
import im.vector.matrix.android.internal.crypto.verification.DefaultSasVerificationService
|
import im.vector.matrix.android.internal.crypto.verification.DefaultSasVerificationService
|
||||||
|
import im.vector.matrix.android.internal.session.DefaultInitialSyncProgressService
|
||||||
|
import im.vector.matrix.android.internal.session.SessionScope
|
||||||
import im.vector.matrix.android.internal.session.sync.model.SyncResponse
|
import im.vector.matrix.android.internal.session.sync.model.SyncResponse
|
||||||
import im.vector.matrix.android.internal.session.sync.model.ToDeviceSyncResponse
|
import im.vector.matrix.android.internal.session.sync.model.ToDeviceSyncResponse
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
|
@ -35,8 +37,10 @@ import javax.inject.Inject
|
||||||
internal class CryptoSyncHandler @Inject constructor(private val cryptoManager: CryptoManager,
|
internal class CryptoSyncHandler @Inject constructor(private val cryptoManager: CryptoManager,
|
||||||
private val sasVerificationService: DefaultSasVerificationService) {
|
private val sasVerificationService: DefaultSasVerificationService) {
|
||||||
|
|
||||||
fun handleToDevice(toDevice: ToDeviceSyncResponse) {
|
fun handleToDevice(toDevice: ToDeviceSyncResponse, initialSyncProgressService: DefaultInitialSyncProgressService? = null) {
|
||||||
toDevice.events?.forEach { event ->
|
val total = toDevice.events?.size ?: 0
|
||||||
|
toDevice.events?.forEachIndexed { index, event ->
|
||||||
|
initialSyncProgressService?.reportProgress(((index / total.toFloat()) * 100).toInt())
|
||||||
// Decrypt event if necessary
|
// Decrypt event if necessary
|
||||||
decryptEvent(event, null)
|
decryptEvent(event, null)
|
||||||
if (TextUtils.equals(event.getClearType(), EventType.MESSAGE)
|
if (TextUtils.equals(event.getClearType(), EventType.MESSAGE)
|
||||||
|
|
|
@ -17,10 +17,12 @@
|
||||||
package im.vector.matrix.android.internal.session.sync
|
package im.vector.matrix.android.internal.session.sync
|
||||||
|
|
||||||
import com.zhuinden.monarchy.Monarchy
|
import com.zhuinden.monarchy.Monarchy
|
||||||
|
import im.vector.matrix.android.R
|
||||||
import im.vector.matrix.android.api.session.room.model.Membership
|
import im.vector.matrix.android.api.session.room.model.Membership
|
||||||
import im.vector.matrix.android.internal.database.model.GroupEntity
|
import im.vector.matrix.android.internal.database.model.GroupEntity
|
||||||
import im.vector.matrix.android.internal.database.query.where
|
import im.vector.matrix.android.internal.database.query.where
|
||||||
import im.vector.matrix.android.internal.session.SessionScope
|
import im.vector.matrix.android.internal.session.DefaultInitialSyncProgressService
|
||||||
|
import im.vector.matrix.android.internal.session.mapWithProgress
|
||||||
import im.vector.matrix.android.internal.session.sync.model.GroupsSyncResponse
|
import im.vector.matrix.android.internal.session.sync.model.GroupsSyncResponse
|
||||||
import im.vector.matrix.android.internal.session.sync.model.InvitedGroupSync
|
import im.vector.matrix.android.internal.session.sync.model.InvitedGroupSync
|
||||||
import io.realm.Realm
|
import io.realm.Realm
|
||||||
|
@ -34,21 +36,33 @@ internal class GroupSyncHandler @Inject constructor(private val monarchy: Monarc
|
||||||
data class LEFT(val data: Map<String, Any>) : HandlingStrategy()
|
data class LEFT(val data: Map<String, Any>) : HandlingStrategy()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun handle(roomsSyncResponse: GroupsSyncResponse) {
|
fun handle(roomsSyncResponse: GroupsSyncResponse, reporter: DefaultInitialSyncProgressService? = null) {
|
||||||
monarchy.runTransactionSync { realm ->
|
monarchy.runTransactionSync { realm ->
|
||||||
handleGroupSync(realm, GroupSyncHandler.HandlingStrategy.JOINED(roomsSyncResponse.join))
|
handleGroupSync(realm, HandlingStrategy.JOINED(roomsSyncResponse.join), reporter)
|
||||||
handleGroupSync(realm, GroupSyncHandler.HandlingStrategy.INVITED(roomsSyncResponse.invite))
|
handleGroupSync(realm, HandlingStrategy.INVITED(roomsSyncResponse.invite), reporter)
|
||||||
handleGroupSync(realm, GroupSyncHandler.HandlingStrategy.LEFT(roomsSyncResponse.leave))
|
handleGroupSync(realm, HandlingStrategy.LEFT(roomsSyncResponse.leave), reporter)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// PRIVATE METHODS *****************************************************************************
|
// PRIVATE METHODS *****************************************************************************
|
||||||
|
|
||||||
private fun handleGroupSync(realm: Realm, handlingStrategy: HandlingStrategy) {
|
private fun handleGroupSync(realm: Realm, handlingStrategy: HandlingStrategy, reporter: DefaultInitialSyncProgressService?) {
|
||||||
val groups = when (handlingStrategy) {
|
val groups = when (handlingStrategy) {
|
||||||
is HandlingStrategy.JOINED -> handlingStrategy.data.map { handleJoinedGroup(realm, it.key) }
|
is HandlingStrategy.JOINED ->
|
||||||
is HandlingStrategy.INVITED -> handlingStrategy.data.map { handleInvitedGroup(realm, it.key) }
|
handlingStrategy.data.mapWithProgress(reporter, R.string.initial_sync_start_importing_account_groups, 0.6f) {
|
||||||
is HandlingStrategy.LEFT -> handlingStrategy.data.map { handleLeftGroup(realm, it.key) }
|
handleJoinedGroup(realm, it.key)
|
||||||
|
}
|
||||||
|
|
||||||
|
is HandlingStrategy.INVITED ->
|
||||||
|
handlingStrategy.data.mapWithProgress(reporter, R.string.initial_sync_start_importing_account_groups, 0.3f) {
|
||||||
|
handleInvitedGroup(realm, it.key)
|
||||||
|
}
|
||||||
|
|
||||||
|
is HandlingStrategy.LEFT ->
|
||||||
|
handlingStrategy.data.mapWithProgress(reporter, R.string.initial_sync_start_importing_account_groups, 0.1f) {
|
||||||
|
handleLeftGroup(realm, it.key)
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
realm.insertOrUpdate(groups)
|
realm.insertOrUpdate(groups)
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
package im.vector.matrix.android.internal.session.sync
|
package im.vector.matrix.android.internal.session.sync
|
||||||
|
|
||||||
import com.zhuinden.monarchy.Monarchy
|
import com.zhuinden.monarchy.Monarchy
|
||||||
|
import im.vector.matrix.android.R
|
||||||
import im.vector.matrix.android.api.session.events.model.Event
|
import im.vector.matrix.android.api.session.events.model.Event
|
||||||
import im.vector.matrix.android.api.session.events.model.EventType
|
import im.vector.matrix.android.api.session.events.model.EventType
|
||||||
import im.vector.matrix.android.api.session.events.model.toModel
|
import im.vector.matrix.android.api.session.events.model.toModel
|
||||||
|
@ -32,6 +33,8 @@ import im.vector.matrix.android.internal.database.model.RoomEntity
|
||||||
import im.vector.matrix.android.internal.database.query.find
|
import im.vector.matrix.android.internal.database.query.find
|
||||||
import im.vector.matrix.android.internal.database.query.findLastLiveChunkFromRoom
|
import im.vector.matrix.android.internal.database.query.findLastLiveChunkFromRoom
|
||||||
import im.vector.matrix.android.internal.database.query.where
|
import im.vector.matrix.android.internal.database.query.where
|
||||||
|
import im.vector.matrix.android.internal.session.DefaultInitialSyncProgressService
|
||||||
|
import im.vector.matrix.android.internal.session.mapWithProgress
|
||||||
import im.vector.matrix.android.internal.session.notification.DefaultPushRuleService
|
import im.vector.matrix.android.internal.session.notification.DefaultPushRuleService
|
||||||
import im.vector.matrix.android.internal.session.notification.ProcessEventForPushTask
|
import im.vector.matrix.android.internal.session.notification.ProcessEventForPushTask
|
||||||
import im.vector.matrix.android.internal.session.room.RoomSummaryUpdater
|
import im.vector.matrix.android.internal.session.room.RoomSummaryUpdater
|
||||||
|
@ -60,11 +63,11 @@ internal class RoomSyncHandler @Inject constructor(private val monarchy: Monarch
|
||||||
data class LEFT(val data: Map<String, RoomSync>) : HandlingStrategy()
|
data class LEFT(val data: Map<String, RoomSync>) : HandlingStrategy()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun handle(roomsSyncResponse: RoomsSyncResponse) {
|
fun handle(roomsSyncResponse: RoomsSyncResponse, reporter: DefaultInitialSyncProgressService? = null) {
|
||||||
monarchy.runTransactionSync { realm ->
|
monarchy.runTransactionSync { realm ->
|
||||||
handleRoomSync(realm, HandlingStrategy.JOINED(roomsSyncResponse.join))
|
handleRoomSync(realm, HandlingStrategy.JOINED(roomsSyncResponse.join), reporter)
|
||||||
handleRoomSync(realm, HandlingStrategy.INVITED(roomsSyncResponse.invite))
|
handleRoomSync(realm, HandlingStrategy.INVITED(roomsSyncResponse.invite), reporter)
|
||||||
handleRoomSync(realm, HandlingStrategy.LEFT(roomsSyncResponse.leave))
|
handleRoomSync(realm, HandlingStrategy.LEFT(roomsSyncResponse.leave), reporter)
|
||||||
}
|
}
|
||||||
|
|
||||||
//handle event for bing rule checks
|
//handle event for bing rule checks
|
||||||
|
@ -87,11 +90,23 @@ internal class RoomSyncHandler @Inject constructor(private val monarchy: Monarch
|
||||||
|
|
||||||
// PRIVATE METHODS *****************************************************************************
|
// PRIVATE METHODS *****************************************************************************
|
||||||
|
|
||||||
private fun handleRoomSync(realm: Realm, handlingStrategy: HandlingStrategy) {
|
private fun handleRoomSync(realm: Realm, handlingStrategy: HandlingStrategy, reporter: DefaultInitialSyncProgressService?) {
|
||||||
|
|
||||||
val rooms = when (handlingStrategy) {
|
val rooms = when (handlingStrategy) {
|
||||||
is HandlingStrategy.JOINED -> handlingStrategy.data.map { handleJoinedRoom(realm, it.key, it.value) }
|
is HandlingStrategy.JOINED ->
|
||||||
is HandlingStrategy.INVITED -> handlingStrategy.data.map { handleInvitedRoom(realm, it.key, it.value) }
|
handlingStrategy.data.mapWithProgress(reporter, R.string.initial_sync_start_importing_account_joined_rooms, 0.6f) {
|
||||||
is HandlingStrategy.LEFT -> handlingStrategy.data.map { handleLeftRoom(realm, it.key, it.value) }
|
handleJoinedRoom(realm, it.key, it.value)
|
||||||
|
}
|
||||||
|
is HandlingStrategy.INVITED ->
|
||||||
|
handlingStrategy.data.mapWithProgress(reporter, R.string.initial_sync_start_importing_account_invited_rooms, 0.4f) {
|
||||||
|
handleInvitedRoom(realm, it.key, it.value)
|
||||||
|
}
|
||||||
|
|
||||||
|
is HandlingStrategy.LEFT -> {
|
||||||
|
handlingStrategy.data.mapWithProgress(reporter, R.string.initial_sync_start_importing_account_left_rooms, 0.2f) {
|
||||||
|
handleLeftRoom(realm, it.key, it.value)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
realm.insertOrUpdate(rooms)
|
realm.insertOrUpdate(rooms)
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,7 +17,10 @@
|
||||||
package im.vector.matrix.android.internal.session.sync
|
package im.vector.matrix.android.internal.session.sync
|
||||||
|
|
||||||
import arrow.core.Try
|
import arrow.core.Try
|
||||||
|
import im.vector.matrix.android.R
|
||||||
import im.vector.matrix.android.internal.crypto.CryptoManager
|
import im.vector.matrix.android.internal.crypto.CryptoManager
|
||||||
|
import im.vector.matrix.android.internal.session.DefaultInitialSyncProgressService
|
||||||
|
import im.vector.matrix.android.internal.session.reportSubtask
|
||||||
import im.vector.matrix.android.internal.session.sync.model.SyncResponse
|
import im.vector.matrix.android.internal.session.sync.model.SyncResponse
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
@ -27,37 +30,75 @@ internal class SyncResponseHandler @Inject constructor(private val roomSyncHandl
|
||||||
private val userAccountDataSyncHandler: UserAccountDataSyncHandler,
|
private val userAccountDataSyncHandler: UserAccountDataSyncHandler,
|
||||||
private val groupSyncHandler: GroupSyncHandler,
|
private val groupSyncHandler: GroupSyncHandler,
|
||||||
private val cryptoSyncHandler: CryptoSyncHandler,
|
private val cryptoSyncHandler: CryptoSyncHandler,
|
||||||
private val cryptoManager: CryptoManager) {
|
private val cryptoManager: CryptoManager,
|
||||||
|
private val initialSyncProgressService: DefaultInitialSyncProgressService) {
|
||||||
|
|
||||||
fun handleResponse(syncResponse: SyncResponse, fromToken: String?, isCatchingUp: Boolean): Try<SyncResponse> {
|
fun handleResponse(syncResponse: SyncResponse, fromToken: String?, isCatchingUp: Boolean): Try<SyncResponse> {
|
||||||
return Try {
|
return Try {
|
||||||
Timber.v("Start handling sync")
|
|
||||||
val isInitialSync = fromToken == null
|
val isInitialSync = fromToken == null
|
||||||
|
Timber.v("Start handling sync, is InitialSync: $isInitialSync")
|
||||||
|
val reporter = initialSyncProgressService.takeIf { isInitialSync }
|
||||||
|
|
||||||
|
measureTimeMillis {
|
||||||
if (!cryptoManager.isStarted()) {
|
if (!cryptoManager.isStarted()) {
|
||||||
Timber.v("Should start cryptoManager")
|
Timber.v("Should start cryptoManager")
|
||||||
cryptoManager.start(isInitialSync)
|
cryptoManager.start(isInitialSync)
|
||||||
}
|
}
|
||||||
|
}.also {
|
||||||
|
Timber.v("Finish handling start cryptoManager in $it ms")
|
||||||
|
}
|
||||||
val measure = measureTimeMillis {
|
val measure = measureTimeMillis {
|
||||||
// Handle the to device events before the room ones
|
// Handle the to device events before the room ones
|
||||||
// to ensure to decrypt them properly
|
// to ensure to decrypt them properly
|
||||||
|
measureTimeMillis {
|
||||||
Timber.v("Handle toDevice")
|
Timber.v("Handle toDevice")
|
||||||
|
reportSubtask(reporter, R.string.initial_sync_start_importing_account_crypto, 100, 0.1f) {
|
||||||
if (syncResponse.toDevice != null) {
|
if (syncResponse.toDevice != null) {
|
||||||
cryptoSyncHandler.handleToDevice(syncResponse.toDevice)
|
cryptoSyncHandler.handleToDevice(syncResponse.toDevice, reporter)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}.also {
|
||||||
|
Timber.v("Finish handling toDevice in $it ms")
|
||||||
|
}
|
||||||
|
|
||||||
|
measureTimeMillis {
|
||||||
Timber.v("Handle rooms")
|
Timber.v("Handle rooms")
|
||||||
|
|
||||||
|
reportSubtask(reporter, R.string.initial_sync_start_importing_account_rooms, 100, 0.7f) {
|
||||||
if (syncResponse.rooms != null) {
|
if (syncResponse.rooms != null) {
|
||||||
roomSyncHandler.handle(syncResponse.rooms)
|
roomSyncHandler.handle(syncResponse.rooms, reporter)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}.also {
|
||||||
|
Timber.v("Finish handling rooms in $it ms")
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
measureTimeMillis {
|
||||||
|
reportSubtask(reporter, R.string.initial_sync_start_importing_account_groups, 100, 0.1f) {
|
||||||
Timber.v("Handle groups")
|
Timber.v("Handle groups")
|
||||||
if (syncResponse.groups != null) {
|
if (syncResponse.groups != null) {
|
||||||
groupSyncHandler.handle(syncResponse.groups)
|
groupSyncHandler.handle(syncResponse.groups, reporter)
|
||||||
}
|
}
|
||||||
Timber.v("Handle accoundData")
|
}
|
||||||
|
}.also {
|
||||||
|
Timber.v("Finish handling groups in $it ms")
|
||||||
|
}
|
||||||
|
|
||||||
|
measureTimeMillis {
|
||||||
|
reportSubtask(reporter, R.string.initial_sync_start_importing_account_data, 100, 0.1f) {
|
||||||
|
Timber.v("Handle accountData")
|
||||||
if (syncResponse.accountData != null) {
|
if (syncResponse.accountData != null) {
|
||||||
userAccountDataSyncHandler.handle(syncResponse.accountData)
|
userAccountDataSyncHandler.handle(syncResponse.accountData)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}.also {
|
||||||
|
Timber.v("Finish handling accountData in $it ms")
|
||||||
|
}
|
||||||
|
|
||||||
Timber.v("On sync completed")
|
Timber.v("On sync completed")
|
||||||
cryptoSyncHandler.onSyncCompleted(syncResponse)
|
cryptoSyncHandler.onSyncCompleted(syncResponse)
|
||||||
|
|
||||||
}
|
}
|
||||||
Timber.v("Finish handling sync in $measure ms")
|
Timber.v("Finish handling sync in $measure ms")
|
||||||
syncResponse
|
syncResponse
|
||||||
|
|
|
@ -19,14 +19,18 @@ package im.vector.matrix.android.internal.session.sync
|
||||||
import arrow.core.Try
|
import arrow.core.Try
|
||||||
import arrow.core.failure
|
import arrow.core.failure
|
||||||
import arrow.core.recoverWith
|
import arrow.core.recoverWith
|
||||||
|
import com.zhuinden.monarchy.Monarchy
|
||||||
|
import im.vector.matrix.android.R
|
||||||
import im.vector.matrix.android.api.auth.data.Credentials
|
import im.vector.matrix.android.api.auth.data.Credentials
|
||||||
import im.vector.matrix.android.api.failure.Failure
|
import im.vector.matrix.android.api.failure.Failure
|
||||||
import im.vector.matrix.android.api.failure.MatrixError
|
import im.vector.matrix.android.api.failure.MatrixError
|
||||||
import im.vector.matrix.android.internal.auth.SessionParamsStore
|
import im.vector.matrix.android.internal.auth.SessionParamsStore
|
||||||
import im.vector.matrix.android.internal.network.executeRequest
|
import im.vector.matrix.android.internal.network.executeRequest
|
||||||
|
import im.vector.matrix.android.internal.session.DefaultInitialSyncProgressService
|
||||||
import im.vector.matrix.android.internal.session.filter.FilterRepository
|
import im.vector.matrix.android.internal.session.filter.FilterRepository
|
||||||
import im.vector.matrix.android.internal.session.sync.model.SyncResponse
|
import im.vector.matrix.android.internal.session.sync.model.SyncResponse
|
||||||
import im.vector.matrix.android.internal.task.Task
|
import im.vector.matrix.android.internal.task.Task
|
||||||
|
import im.vector.matrix.android.internal.util.tryTransactionAsync
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
internal interface SyncTask : Task<SyncTask.Params, Unit> {
|
internal interface SyncTask : Task<SyncTask.Params, Unit> {
|
||||||
|
@ -40,7 +44,9 @@ internal class DefaultSyncTask @Inject constructor(private val syncAPI: SyncAPI,
|
||||||
private val filterRepository: FilterRepository,
|
private val filterRepository: FilterRepository,
|
||||||
private val syncResponseHandler: SyncResponseHandler,
|
private val syncResponseHandler: SyncResponseHandler,
|
||||||
private val sessionParamsStore: SessionParamsStore,
|
private val sessionParamsStore: SessionParamsStore,
|
||||||
private val syncTokenStore: SyncTokenStore
|
private val initialSyncProgressService: DefaultInitialSyncProgressService,
|
||||||
|
private val syncTokenStore: SyncTokenStore,
|
||||||
|
private val monarchy: Monarchy
|
||||||
) : SyncTask {
|
) : SyncTask {
|
||||||
|
|
||||||
|
|
||||||
|
@ -55,6 +61,11 @@ internal class DefaultSyncTask @Inject constructor(private val syncAPI: SyncAPI,
|
||||||
requestParams["timeout"] = timeout.toString()
|
requestParams["timeout"] = timeout.toString()
|
||||||
requestParams["filter"] = filterRepository.getFilter()
|
requestParams["filter"] = filterRepository.getFilter()
|
||||||
|
|
||||||
|
val isInitialSync = token == null
|
||||||
|
if (isInitialSync) {
|
||||||
|
initialSyncProgressService.endAll()
|
||||||
|
initialSyncProgressService.startTask(R.string.initial_sync_start_importing_account, 100)
|
||||||
|
}
|
||||||
return executeRequest<SyncResponse> {
|
return executeRequest<SyncResponse> {
|
||||||
apiCall = syncAPI.sync(requestParams)
|
apiCall = syncAPI.sync(requestParams)
|
||||||
}.recoverWith { throwable ->
|
}.recoverWith { throwable ->
|
||||||
|
@ -67,7 +78,13 @@ internal class DefaultSyncTask @Inject constructor(private val syncAPI: SyncAPI,
|
||||||
// Transmit the throwable
|
// Transmit the throwable
|
||||||
throwable.failure()
|
throwable.failure()
|
||||||
}.flatMap { syncResponse ->
|
}.flatMap { syncResponse ->
|
||||||
syncResponseHandler.handleResponse(syncResponse, token, false)
|
syncResponseHandler.handleResponse(syncResponse, token, false).also {
|
||||||
|
if (isInitialSync) {
|
||||||
|
monarchy.tryTransactionAsync {
|
||||||
|
initialSyncProgressService.endAll()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}.map {
|
}.map {
|
||||||
syncTokenStore.saveToken(it.nextBatch)
|
syncTokenStore.saveToken(it.nextBatch)
|
||||||
}
|
}
|
||||||
|
|
10
matrix-sdk-android/src/main/res/values/strings_RiotX.xml
Normal file
10
matrix-sdk-android/src/main/res/values/strings_RiotX.xml
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
<resources>
|
||||||
|
<string name="initial_sync_start_importing_account">Initial Sync:\nImporting account…</string>
|
||||||
|
<string name="initial_sync_start_importing_account_crypto">Initial Sync:\nImporting crypto</string>
|
||||||
|
<string name="initial_sync_start_importing_account_rooms">Initial Sync:\nImporting Rooms</string>
|
||||||
|
<string name="initial_sync_start_importing_account_joined_rooms">Initial Sync:\nImporting Joined Rooms</string>
|
||||||
|
<string name="initial_sync_start_importing_account_invited_rooms">Initial Sync:\nImporting Invited Rooms</string>
|
||||||
|
<string name="initial_sync_start_importing_account_left_rooms">Initial Sync:\nImporting Left Rooms</string>
|
||||||
|
<string name="initial_sync_start_importing_account_groups">Initial Sync:\nImporting Communities</string>
|
||||||
|
<string name="initial_sync_start_importing_account_data">Initial Sync:\nImporting Account Data</string>
|
||||||
|
</resources>
|
|
@ -24,6 +24,7 @@ import android.view.MenuItem
|
||||||
import androidx.appcompat.app.AlertDialog
|
import androidx.appcompat.app.AlertDialog
|
||||||
import androidx.appcompat.widget.Toolbar
|
import androidx.appcompat.widget.Toolbar
|
||||||
import androidx.core.view.GravityCompat
|
import androidx.core.view.GravityCompat
|
||||||
|
import androidx.core.view.isVisible
|
||||||
import androidx.drawerlayout.widget.DrawerLayout
|
import androidx.drawerlayout.widget.DrawerLayout
|
||||||
import androidx.fragment.app.FragmentManager
|
import androidx.fragment.app.FragmentManager
|
||||||
import androidx.lifecycle.Observer
|
import androidx.lifecycle.Observer
|
||||||
|
@ -45,6 +46,8 @@ import im.vector.riotx.features.rageshake.VectorUncaughtExceptionHandler
|
||||||
import im.vector.riotx.features.workers.signout.SignOutViewModel
|
import im.vector.riotx.features.workers.signout.SignOutViewModel
|
||||||
import im.vector.riotx.push.fcm.FcmHelper
|
import im.vector.riotx.push.fcm.FcmHelper
|
||||||
import kotlinx.android.synthetic.main.activity_home.*
|
import kotlinx.android.synthetic.main.activity_home.*
|
||||||
|
import kotlinx.android.synthetic.main.merge_overlay_waiting_view.*
|
||||||
|
import timber.log.Timber
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
|
||||||
|
@ -114,6 +117,28 @@ class HomeActivity : VectorBaseActivity(), ToolbarConfigurable {
|
||||||
notificationDrawerManager.clearAllEvents()
|
notificationDrawerManager.clearAllEvents()
|
||||||
intent.removeExtra(EXTRA_CLEAR_EXISTING_NOTIFICATION)
|
intent.removeExtra(EXTRA_CLEAR_EXISTING_NOTIFICATION)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
activeSessionHolder.getSafeActiveSession()?.getLiveStatus()?.observe(this, Observer { sprogress ->
|
||||||
|
Timber.e("${sprogress?.statusText?.let { getString(it) }} ${sprogress?.percentProgress}")
|
||||||
|
if (sprogress == null) {
|
||||||
|
waiting_view.isVisible = false
|
||||||
|
} else {
|
||||||
|
waiting_view.setOnClickListener {
|
||||||
|
//block interactions
|
||||||
|
}
|
||||||
|
waiting_view_status_horizontal_progress.apply {
|
||||||
|
isIndeterminate = false
|
||||||
|
max = 100
|
||||||
|
progress = sprogress.percentProgress
|
||||||
|
isVisible = true
|
||||||
|
}
|
||||||
|
waiting_view_status_text.apply {
|
||||||
|
text = sprogress.statusText?.let { getString(it) }
|
||||||
|
isVisible = true
|
||||||
|
}
|
||||||
|
waiting_view.isVisible = true
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onNewIntent(intent: Intent?) {
|
override fun onNewIntent(intent: Intent?) {
|
||||||
|
|
|
@ -184,7 +184,7 @@ class NotificationDrawerManager @Inject constructor(private val context: Context
|
||||||
|
|
||||||
val session = activeSessionHolder.getActiveSession()
|
val session = activeSessionHolder.getActiveSession()
|
||||||
val user = session.getUser(session.sessionParams.credentials.userId)
|
val user = session.getUser(session.sessionParams.credentials.userId)
|
||||||
val myUserDisplayName = user?.displayName ?: ""
|
val myUserDisplayName = user?.displayName ?: session.sessionParams.credentials.userId
|
||||||
val myUserAvatarUrl = session.contentUrlResolver().resolveThumbnail(user?.avatarUrl, avatarSize, avatarSize, ContentUrlResolver.ThumbnailMethod.SCALE)
|
val myUserAvatarUrl = session.contentUrlResolver().resolveThumbnail(user?.avatarUrl, avatarSize, avatarSize, ContentUrlResolver.ThumbnailMethod.SCALE)
|
||||||
synchronized(eventList) {
|
synchronized(eventList) {
|
||||||
|
|
||||||
|
|
|
@ -22,66 +22,6 @@
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
app:layout_constraintTop_toBottomOf="@id/toolbar" />
|
app:layout_constraintTop_toBottomOf="@id/toolbar" />
|
||||||
|
|
||||||
<androidx.constraintlayout.widget.ConstraintLayout
|
<include layout="@layout/merge_overlay_waiting_view"/>
|
||||||
android:id="@+id/waiting_view"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
android:background="?vctr_waiting_background_color"
|
|
||||||
android:visibility="gone"
|
|
||||||
tools:visibility="visible">
|
|
||||||
|
|
||||||
<LinearLayout
|
|
||||||
android:id="@+id/waiting_view_content"
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_margin="@dimen/layout_horizontal_margin"
|
|
||||||
android:background="?attr/colorBackgroundFloating"
|
|
||||||
android:orientation="vertical"
|
|
||||||
android:padding="@dimen/layout_horizontal_margin"
|
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
|
||||||
app:layout_constraintWidth_percent="@dimen/dialog_width_ratio">
|
|
||||||
|
|
||||||
<LinearLayout
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:orientation="horizontal">
|
|
||||||
|
|
||||||
<ProgressBar
|
|
||||||
android:id="@+id/waiting_view_status_circular_progress"
|
|
||||||
android:layout_width="40dp"
|
|
||||||
android:layout_height="40dp"
|
|
||||||
android:layout_gravity="center_vertical"
|
|
||||||
android:layout_marginEnd="6dp"
|
|
||||||
android:layout_marginRight="6dp" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/waiting_view_status_text"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_gravity="center_vertical"
|
|
||||||
android:textColor="?riotx_text_secondary"
|
|
||||||
android:visibility="gone"
|
|
||||||
tools:text="Waiting status..."
|
|
||||||
tools:visibility="visible" />
|
|
||||||
|
|
||||||
</LinearLayout>
|
|
||||||
|
|
||||||
<ProgressBar
|
|
||||||
android:id="@+id/waiting_view_status_horizontal_progress"
|
|
||||||
style="?android:attr/progressBarStyleHorizontal"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="20dp"
|
|
||||||
android:layout_marginTop="10dp"
|
|
||||||
android:visibility="gone"
|
|
||||||
tools:max="100"
|
|
||||||
tools:progress="30"
|
|
||||||
tools:visibility="visible" />
|
|
||||||
|
|
||||||
</LinearLayout>
|
|
||||||
|
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
|
||||||
|
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
|
|
|
@ -15,6 +15,8 @@
|
||||||
android:id="@+id/homeDetailFragmentContainer"
|
android:id="@+id/homeDetailFragmentContainer"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent" />
|
android:layout_height="match_parent" />
|
||||||
|
|
||||||
|
<include layout="@layout/merge_overlay_waiting_view" />
|
||||||
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
||||||
|
|
||||||
|
|
||||||
|
@ -24,4 +26,5 @@
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:layout_gravity="start" />
|
android:layout_gravity="start" />
|
||||||
|
|
||||||
|
|
||||||
</androidx.drawerlayout.widget.DrawerLayout>
|
</androidx.drawerlayout.widget.DrawerLayout>
|
71
vector/src/main/res/layout/merge_overlay_waiting_view.xml
Normal file
71
vector/src/main/res/layout/merge_overlay_waiting_view.xml
Normal file
|
@ -0,0 +1,71 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<merge xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<androidx.constraintlayout.widget.ConstraintLayout
|
||||||
|
android:id="@+id/waiting_view"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:background="?vctr_waiting_background_color"
|
||||||
|
android:visibility="gone"
|
||||||
|
tools:visibility="visible">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/waiting_view_content"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_margin="@dimen/layout_horizontal_margin"
|
||||||
|
android:background="?attr/colorBackgroundFloating"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:padding="@dimen/layout_horizontal_margin"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
|
app:layout_constraintWidth_percent="@dimen/dialog_width_ratio">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="horizontal">
|
||||||
|
|
||||||
|
<ProgressBar
|
||||||
|
android:id="@+id/waiting_view_status_circular_progress"
|
||||||
|
android:layout_width="40dp"
|
||||||
|
android:layout_height="40dp"
|
||||||
|
android:layout_gravity="center_vertical"
|
||||||
|
android:layout_marginEnd="6dp"
|
||||||
|
android:layout_marginRight="6dp" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/waiting_view_status_text"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="center_vertical"
|
||||||
|
android:textColor="?riotx_text_secondary"
|
||||||
|
android:visibility="gone"
|
||||||
|
tools:text="Waiting status..."
|
||||||
|
tools:visibility="visible" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<ProgressBar
|
||||||
|
android:id="@+id/waiting_view_status_horizontal_progress"
|
||||||
|
style="?android:attr/progressBarStyleHorizontal"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="20dp"
|
||||||
|
android:layout_marginTop="10dp"
|
||||||
|
android:visibility="gone"
|
||||||
|
tools:max="100"
|
||||||
|
tools:progress="30"
|
||||||
|
tools:visibility="visible" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
|
|
||||||
|
</merge>
|
Loading…
Reference in a new issue