/* * 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.annotation.StringRes 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 { private var status = MutableLiveData() private var rootTask: TaskInfo? = null override fun getInitialSyncProgressStatus(): LiveData { return status } fun startTask(@StringRes nameRes: Int, totalProgress: Int, parentWeight: Float = 1f) { // Create a rootTask, or add a child to the leaf if (rootTask == null) { rootTask = TaskInfo(nameRes, totalProgress) } else { val currentLeaf = rootTask!!.leaf() val newTask = TaskInfo(nameRes, totalProgress, currentLeaf, parentWeight) currentLeaf.child = newTask } reportProgress(0) } fun reportProgress(progress: Int) { rootTask?.leaf()?.setProgress(progress) } fun endTask(nameRes: Int) { val endedTask = rootTask?.leaf() if (endedTask?.nameRes == nameRes) { //close it val parent = endedTask.parent parent?.child = null parent?.setProgress(endedTask.offset + (endedTask.totalProgress * endedTask.parentWeight).toInt()) } if (endedTask?.parent == null) { status.postValue(null) } } fun endAll() { rootTask = null status.postValue(null) } private inner class TaskInfo(@StringRes var nameRes: Int, var totalProgress: Int, var parent: TaskInfo? = null, var parentWeight: Float = 1f, var offset: Int = parent?.currentProgress ?: 0) { var child: TaskInfo? = null var currentProgress: Int = 0 /** * Get the further child */ fun leaf(): TaskInfo { var last = this while (last.child != null) { last = last.child!! } return last } /** * Set progress of the parent if any (which will post value), or post the value */ fun setProgress(progress: Int) { currentProgress = progress // val newProgress = Math.min(currentProgress + progress, totalProgress) parent?.let { val parentProgress = (currentProgress * parentWeight).toInt() it.setProgress(offset + parentProgress) } ?: run { Timber.e("--- ${leaf().nameRes}: $currentProgress") status.postValue( InitialSyncProgressService.Status(leaf().nameRes, currentProgress) ) } } } } inline fun reportSubtask(reporter: DefaultInitialSyncProgressService?, @StringRes nameRes: Int, totalProgress: Int, parentWeight: Float = 1f, block: () -> T): T { reporter?.startTask(nameRes, totalProgress, parentWeight) return block().also { reporter?.endTask(nameRes) } } inline fun Map.mapWithProgress(reporter: DefaultInitialSyncProgressService?, taskId: Int, weight: Float, transform: (Map.Entry) -> R): List { val total = count().toFloat() var current = 0 reporter?.startTask(taskId, 100, weight) return map { reporter?.reportProgress((current / total * 100).toInt()) current++ transform.invoke(it) }.also { reporter?.endTask(taskId) } }