Merge pull request #316 from vector-im/feature/initial_sync_progress

Feature/initial sync progress
This commit is contained in:
Valere 2019-07-09 17:58:24 +02:00 committed by GitHub
commit a09850b16c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
18 changed files with 413 additions and 107 deletions

View file

@ -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
)
}

View file

@ -49,14 +49,15 @@ interface Session :
FilterService, FilterService,
FileService, FileService,
PushRuleService, PushRuleService,
PushersService { PushersService,
InitialSyncProgressService {
/** /**
* The params associated to the session * The params associated to the session
*/ */
val sessionParams: SessionParams val sessionParams: SessionParams
val myUserId : String val myUserId: String
get() = sessionParams.credentials.userId get() = sessionParams.credentials.userId

View file

@ -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)
}
}

View file

@ -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

View file

@ -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

View file

@ -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
} }

View file

@ -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)

View file

@ -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)
} }

View file

@ -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)
} }
@ -234,4 +249,4 @@ internal class RoomSyncHandler @Inject constructor(private val monarchy: Monarch
.forEach { roomTagHandler.handle(realm, roomId, it) } .forEach { roomTagHandler.handle(realm, roomId, it) }
} }
} }

View file

@ -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
if (!cryptoManager.isStarted()) { Timber.v("Start handling sync, is InitialSync: $isInitialSync")
Timber.v("Should start cryptoManager") val reporter = initialSyncProgressService.takeIf { isInitialSync }
cryptoManager.start(isInitialSync)
measureTimeMillis {
if (!cryptoManager.isStarted()) {
Timber.v("Should start cryptoManager")
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
Timber.v("Handle toDevice") measureTimeMillis {
if (syncResponse.toDevice != null) { Timber.v("Handle toDevice")
cryptoSyncHandler.handleToDevice(syncResponse.toDevice) reportSubtask(reporter, R.string.initial_sync_start_importing_account_crypto, 100, 0.1f) {
if (syncResponse.toDevice != null) {
cryptoSyncHandler.handleToDevice(syncResponse.toDevice, reporter)
}
}
}.also {
Timber.v("Finish handling toDevice in $it ms")
} }
Timber.v("Handle rooms")
if (syncResponse.rooms != null) { measureTimeMillis {
roomSyncHandler.handle(syncResponse.rooms) Timber.v("Handle rooms")
reportSubtask(reporter, R.string.initial_sync_start_importing_account_rooms, 100, 0.7f) {
if (syncResponse.rooms != null) {
roomSyncHandler.handle(syncResponse.rooms, reporter)
}
}
}.also {
Timber.v("Finish handling rooms in $it ms")
} }
Timber.v("Handle groups")
if (syncResponse.groups != null) {
groupSyncHandler.handle(syncResponse.groups) measureTimeMillis {
reportSubtask(reporter, R.string.initial_sync_start_importing_account_groups, 100, 0.1f) {
Timber.v("Handle groups")
if (syncResponse.groups != null) {
groupSyncHandler.handle(syncResponse.groups, reporter)
}
}
}.also {
Timber.v("Finish handling groups in $it ms")
} }
Timber.v("Handle accoundData")
if (syncResponse.accountData != null) { measureTimeMillis {
userAccountDataSyncHandler.handle(syncResponse.accountData) reportSubtask(reporter, R.string.initial_sync_start_importing_account_data, 100, 0.1f) {
Timber.v("Handle accountData")
if (syncResponse.accountData != null) {
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

View file

@ -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)
} }

View 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>

View file

@ -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?) {

View file

@ -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) {

View file

@ -51,7 +51,7 @@ import javax.inject.Singleton
* BugReporter creates and sends the bug reports. * BugReporter creates and sends the bug reports.
*/ */
@Singleton @Singleton
class BugReporter @Inject constructor(private val activeSessionHolder: ActiveSessionHolder){ class BugReporter @Inject constructor(private val activeSessionHolder: ActiveSessionHolder) {
var inMultiWindowMode = false var inMultiWindowMode = false
companion object { companion object {

View file

@ -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>

View file

@ -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>

View 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>