forked from GitHub-Mirror/riotX-android
Sync: add progress indicator for sync, need UI inputs.
This commit is contained in:
parent
6323183119
commit
3dd161d65a
@ -19,6 +19,7 @@ package im.vector.matrix.rx
|
|||||||
import im.vector.matrix.android.api.session.Session
|
import im.vector.matrix.android.api.session.Session
|
||||||
import im.vector.matrix.android.api.session.group.model.GroupSummary
|
import im.vector.matrix.android.api.session.group.model.GroupSummary
|
||||||
import im.vector.matrix.android.api.session.room.model.RoomSummary
|
import im.vector.matrix.android.api.session.room.model.RoomSummary
|
||||||
|
import im.vector.matrix.android.api.session.sync.SyncState
|
||||||
import io.reactivex.Observable
|
import io.reactivex.Observable
|
||||||
|
|
||||||
class RxSession(private val session: Session) {
|
class RxSession(private val session: Session) {
|
||||||
@ -31,6 +32,10 @@ class RxSession(private val session: Session) {
|
|||||||
return session.liveGroupSummaries().asObservable()
|
return session.liveGroupSummaries().asObservable()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun liveSyncState(): Observable<SyncState> {
|
||||||
|
return session.syncState().asObservable()
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun Session.rx(): RxSession {
|
fun Session.rx(): RxSession {
|
||||||
|
@ -17,6 +17,7 @@
|
|||||||
package im.vector.matrix.android.api.session
|
package im.vector.matrix.android.api.session
|
||||||
|
|
||||||
import androidx.annotation.MainThread
|
import androidx.annotation.MainThread
|
||||||
|
import androidx.lifecycle.LiveData
|
||||||
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.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
|
||||||
@ -27,6 +28,7 @@ import im.vector.matrix.android.api.session.room.RoomDirectoryService
|
|||||||
import im.vector.matrix.android.api.session.room.RoomService
|
import im.vector.matrix.android.api.session.room.RoomService
|
||||||
import im.vector.matrix.android.api.session.signout.SignOutService
|
import im.vector.matrix.android.api.session.signout.SignOutService
|
||||||
import im.vector.matrix.android.api.session.sync.FilterService
|
import im.vector.matrix.android.api.session.sync.FilterService
|
||||||
|
import im.vector.matrix.android.api.session.sync.SyncState
|
||||||
import im.vector.matrix.android.api.session.user.UserService
|
import im.vector.matrix.android.api.session.user.UserService
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -66,6 +68,12 @@ interface Session :
|
|||||||
@MainThread
|
@MainThread
|
||||||
fun stopSync()
|
fun stopSync()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method allows to listen the sync state.
|
||||||
|
* @return a [LiveData] of [SyncState].
|
||||||
|
*/
|
||||||
|
fun syncState(): LiveData<SyncState>
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This method allow to close a session. It does stop some services.
|
* This method allow to close a session. It does stop some services.
|
||||||
*/
|
*/
|
||||||
@ -102,6 +110,7 @@ interface Session :
|
|||||||
* The access token is not valid anymore
|
* The access token is not valid anymore
|
||||||
*/
|
*/
|
||||||
fun onInvalidToken()
|
fun onInvalidToken()
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
@ -0,0 +1,27 @@
|
|||||||
|
/*
|
||||||
|
*
|
||||||
|
* * 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.sync
|
||||||
|
|
||||||
|
sealed class SyncState {
|
||||||
|
object IDLE : SyncState()
|
||||||
|
data class RUNNING(val catchingUp: Boolean) : SyncState()
|
||||||
|
object PAUSED : SyncState()
|
||||||
|
object KILLING : SyncState()
|
||||||
|
object KILLED : SyncState()
|
||||||
|
}
|
@ -39,6 +39,7 @@ import im.vector.matrix.android.api.session.room.model.roomdirectory.PublicRooms
|
|||||||
import im.vector.matrix.android.api.session.room.model.thirdparty.ThirdPartyProtocol
|
import im.vector.matrix.android.api.session.room.model.thirdparty.ThirdPartyProtocol
|
||||||
import im.vector.matrix.android.api.session.signout.SignOutService
|
import im.vector.matrix.android.api.session.signout.SignOutService
|
||||||
import im.vector.matrix.android.api.session.sync.FilterService
|
import im.vector.matrix.android.api.session.sync.FilterService
|
||||||
|
import im.vector.matrix.android.api.session.sync.SyncState
|
||||||
import im.vector.matrix.android.api.session.user.UserService
|
import im.vector.matrix.android.api.session.user.UserService
|
||||||
import im.vector.matrix.android.api.session.user.model.User
|
import im.vector.matrix.android.api.session.user.model.User
|
||||||
import im.vector.matrix.android.api.util.Cancelable
|
import im.vector.matrix.android.api.util.Cancelable
|
||||||
@ -124,6 +125,10 @@ internal class DefaultSession(override val sessionParams: SessionParams) : Sessi
|
|||||||
isOpen = false
|
isOpen = false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun syncState(): LiveData<SyncState> {
|
||||||
|
return syncThread.liveState()
|
||||||
|
}
|
||||||
|
|
||||||
@MainThread
|
@MainThread
|
||||||
override fun signOut(callback: MatrixCallback<Unit>) {
|
override fun signOut(callback: MatrixCallback<Unit>) {
|
||||||
assert(isOpen)
|
assert(isOpen)
|
||||||
|
@ -16,10 +16,13 @@
|
|||||||
|
|
||||||
package im.vector.matrix.android.internal.session.sync.job
|
package im.vector.matrix.android.internal.session.sync.job
|
||||||
|
|
||||||
|
import androidx.lifecycle.LiveData
|
||||||
|
import androidx.lifecycle.MutableLiveData
|
||||||
import com.squareup.moshi.JsonEncodingException
|
import com.squareup.moshi.JsonEncodingException
|
||||||
import im.vector.matrix.android.api.MatrixCallback
|
import im.vector.matrix.android.api.MatrixCallback
|
||||||
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.api.session.sync.SyncState
|
||||||
import im.vector.matrix.android.api.util.Cancelable
|
import im.vector.matrix.android.api.util.Cancelable
|
||||||
import im.vector.matrix.android.internal.network.NetworkConnectivityChecker
|
import im.vector.matrix.android.internal.network.NetworkConnectivityChecker
|
||||||
import im.vector.matrix.android.internal.session.sync.SyncTask
|
import im.vector.matrix.android.internal.session.sync.SyncTask
|
||||||
@ -42,61 +45,52 @@ internal class SyncThread(private val syncTask: SyncTask,
|
|||||||
private val taskExecutor: TaskExecutor
|
private val taskExecutor: TaskExecutor
|
||||||
) : Thread(), NetworkConnectivityChecker.Listener, BackgroundDetectionObserver.Listener {
|
) : Thread(), NetworkConnectivityChecker.Listener, BackgroundDetectionObserver.Listener {
|
||||||
|
|
||||||
enum class State {
|
private var state: SyncState = SyncState.IDLE
|
||||||
IDLE,
|
private var liveState = MutableLiveData<SyncState>()
|
||||||
RUNNING,
|
|
||||||
PAUSED,
|
|
||||||
KILLING,
|
|
||||||
KILLED
|
|
||||||
}
|
|
||||||
|
|
||||||
private var state: State = State.IDLE
|
|
||||||
private val lock = Object()
|
private val lock = Object()
|
||||||
private var nextBatch = syncTokenStore.getLastToken()
|
private var nextBatch = syncTokenStore.getLastToken()
|
||||||
private var cancelableTask: Cancelable? = null
|
private var cancelableTask: Cancelable? = null
|
||||||
|
|
||||||
fun restart() {
|
init {
|
||||||
synchronized(lock) {
|
updateStateTo(SyncState.IDLE)
|
||||||
if (state != State.PAUSED) {
|
}
|
||||||
return@synchronized
|
|
||||||
}
|
|
||||||
Timber.v("Resume sync...")
|
|
||||||
|
|
||||||
|
fun restart() = synchronized(lock) {
|
||||||
|
if (state is SyncState.PAUSED) {
|
||||||
|
Timber.v("Resume sync...")
|
||||||
// Retrieve the last token, it may have been deleted in case of a clear cache
|
// Retrieve the last token, it may have been deleted in case of a clear cache
|
||||||
nextBatch = syncTokenStore.getLastToken()
|
nextBatch = syncTokenStore.getLastToken()
|
||||||
|
updateStateTo(SyncState.RUNNING(catchingUp = true))
|
||||||
state = State.RUNNING
|
|
||||||
lock.notify()
|
lock.notify()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun pause() {
|
fun pause() = synchronized(lock) {
|
||||||
synchronized(lock) {
|
if (state is SyncState.RUNNING) {
|
||||||
if (state != State.RUNNING) {
|
|
||||||
return@synchronized
|
|
||||||
}
|
|
||||||
Timber.v("Pause sync...")
|
Timber.v("Pause sync...")
|
||||||
state = State.PAUSED
|
updateStateTo(SyncState.PAUSED)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun kill() {
|
fun kill() = synchronized(lock) {
|
||||||
synchronized(lock) {
|
Timber.v("Kill sync...")
|
||||||
Timber.v("Kill sync...")
|
updateStateTo(SyncState.KILLING)
|
||||||
state = State.KILLING
|
cancelableTask?.cancel()
|
||||||
cancelableTask?.cancel()
|
lock.notify()
|
||||||
lock.notify()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun liveState(): LiveData<SyncState> {
|
||||||
|
return liveState
|
||||||
|
}
|
||||||
|
|
||||||
override fun run() {
|
override fun run() {
|
||||||
Timber.v("Start syncing...")
|
Timber.v("Start syncing...")
|
||||||
networkConnectivityChecker.register(this)
|
networkConnectivityChecker.register(this)
|
||||||
backgroundDetectionObserver.register(this)
|
backgroundDetectionObserver.register(this)
|
||||||
state = State.RUNNING
|
updateStateTo(SyncState.RUNNING(catchingUp = true))
|
||||||
while (state != State.KILLING) {
|
|
||||||
if (!networkConnectivityChecker.isConnected() || state == State.PAUSED) {
|
while (state != SyncState.KILLING) {
|
||||||
|
if (!networkConnectivityChecker.isConnected() || state == SyncState.PAUSED) {
|
||||||
Timber.v("Waiting...")
|
Timber.v("Waiting...")
|
||||||
synchronized(lock) {
|
synchronized(lock) {
|
||||||
lock.wait()
|
lock.wait()
|
||||||
@ -133,7 +127,7 @@ internal class SyncThread(private val syncTask: SyncTask,
|
|||||||
if (failure is Failure.ServerError
|
if (failure is Failure.ServerError
|
||||||
&& (failure.error.code == MatrixError.UNKNOWN_TOKEN || failure.error.code == MatrixError.MISSING_TOKEN)) {
|
&& (failure.error.code == MatrixError.UNKNOWN_TOKEN || failure.error.code == MatrixError.MISSING_TOKEN)) {
|
||||||
// No token or invalid token, stop the thread
|
// No token or invalid token, stop the thread
|
||||||
state = State.KILLING
|
updateStateTo(SyncState.KILLING)
|
||||||
}
|
}
|
||||||
|
|
||||||
latch.countDown()
|
latch.countDown()
|
||||||
@ -141,16 +135,23 @@ internal class SyncThread(private val syncTask: SyncTask,
|
|||||||
|
|
||||||
})
|
})
|
||||||
.executeBy(taskExecutor)
|
.executeBy(taskExecutor)
|
||||||
|
|
||||||
latch.await()
|
latch.await()
|
||||||
|
if (state is SyncState.RUNNING) {
|
||||||
|
updateStateTo(SyncState.RUNNING(catchingUp = false))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Timber.v("Sync killed")
|
Timber.v("Sync killed")
|
||||||
state = State.KILLED
|
updateStateTo(SyncState.KILLED)
|
||||||
backgroundDetectionObserver.unregister(this)
|
backgroundDetectionObserver.unregister(this)
|
||||||
networkConnectivityChecker.unregister(this)
|
networkConnectivityChecker.unregister(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun updateStateTo(newState: SyncState) {
|
||||||
|
state = newState
|
||||||
|
liveState.postValue(newState)
|
||||||
|
}
|
||||||
|
|
||||||
override fun onConnect() {
|
override fun onConnect() {
|
||||||
synchronized(lock) {
|
synchronized(lock) {
|
||||||
lock.notify()
|
lock.notify()
|
||||||
|
@ -19,6 +19,7 @@ package im.vector.riotredesign.features.home
|
|||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.os.Parcelable
|
import android.os.Parcelable
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
|
import android.view.View
|
||||||
import androidx.core.view.forEachIndexed
|
import androidx.core.view.forEachIndexed
|
||||||
import androidx.lifecycle.ViewModelProviders
|
import androidx.lifecycle.ViewModelProviders
|
||||||
import com.airbnb.mvrx.args
|
import com.airbnb.mvrx.args
|
||||||
@ -26,6 +27,7 @@ import com.airbnb.mvrx.fragmentViewModel
|
|||||||
import com.airbnb.mvrx.withState
|
import com.airbnb.mvrx.withState
|
||||||
import com.google.android.material.bottomnavigation.BottomNavigationItemView
|
import com.google.android.material.bottomnavigation.BottomNavigationItemView
|
||||||
import com.google.android.material.bottomnavigation.BottomNavigationMenuView
|
import com.google.android.material.bottomnavigation.BottomNavigationMenuView
|
||||||
|
import im.vector.matrix.android.api.session.sync.SyncState
|
||||||
import im.vector.riotredesign.R
|
import im.vector.riotredesign.R
|
||||||
import im.vector.riotredesign.core.platform.ToolbarConfigurable
|
import im.vector.riotredesign.core.platform.ToolbarConfigurable
|
||||||
import im.vector.riotredesign.core.platform.VectorBaseFragment
|
import im.vector.riotredesign.core.platform.VectorBaseFragment
|
||||||
@ -145,6 +147,10 @@ class HomeDetailFragment : VectorBaseFragment() {
|
|||||||
unreadCounterBadgeViews[INDEX_CATCHUP].render(UnreadCounterBadgeView.State(it.notificationCountCatchup, it.notificationHighlightCatchup))
|
unreadCounterBadgeViews[INDEX_CATCHUP].render(UnreadCounterBadgeView.State(it.notificationCountCatchup, it.notificationHighlightCatchup))
|
||||||
unreadCounterBadgeViews[INDEX_PEOPLE].render(UnreadCounterBadgeView.State(it.notificationCountPeople, it.notificationHighlightPeople))
|
unreadCounterBadgeViews[INDEX_PEOPLE].render(UnreadCounterBadgeView.State(it.notificationCountPeople, it.notificationHighlightPeople))
|
||||||
unreadCounterBadgeViews[INDEX_ROOMS].render(UnreadCounterBadgeView.State(it.notificationCountRooms, it.notificationHighlightRooms))
|
unreadCounterBadgeViews[INDEX_ROOMS].render(UnreadCounterBadgeView.State(it.notificationCountRooms, it.notificationHighlightRooms))
|
||||||
|
syncProgressBar.visibility = when (it.syncState) {
|
||||||
|
is SyncState.RUNNING -> if (it.syncState.catchingUp) View.VISIBLE else View.GONE
|
||||||
|
else -> View.GONE
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
@ -18,6 +18,8 @@ package im.vector.riotredesign.features.home
|
|||||||
|
|
||||||
import com.airbnb.mvrx.MvRxViewModelFactory
|
import com.airbnb.mvrx.MvRxViewModelFactory
|
||||||
import com.airbnb.mvrx.ViewModelContext
|
import com.airbnb.mvrx.ViewModelContext
|
||||||
|
import im.vector.matrix.android.api.session.Session
|
||||||
|
import im.vector.matrix.rx.rx
|
||||||
import im.vector.riotredesign.core.platform.VectorViewModel
|
import im.vector.riotredesign.core.platform.VectorViewModel
|
||||||
import org.koin.android.ext.android.get
|
import org.koin.android.ext.android.get
|
||||||
|
|
||||||
@ -25,6 +27,7 @@ import org.koin.android.ext.android.get
|
|||||||
* View model used to update the home bottom bar notification counts
|
* View model used to update the home bottom bar notification counts
|
||||||
*/
|
*/
|
||||||
class HomeDetailViewModel(initialState: HomeDetailViewState,
|
class HomeDetailViewModel(initialState: HomeDetailViewState,
|
||||||
|
private val session: Session,
|
||||||
private val homeRoomListStore: HomeRoomListObservableStore)
|
private val homeRoomListStore: HomeRoomListObservableStore)
|
||||||
: VectorViewModel<HomeDetailViewState>(initialState) {
|
: VectorViewModel<HomeDetailViewState>(initialState) {
|
||||||
|
|
||||||
@ -33,16 +36,29 @@ class HomeDetailViewModel(initialState: HomeDetailViewState,
|
|||||||
@JvmStatic
|
@JvmStatic
|
||||||
override fun create(viewModelContext: ViewModelContext, state: HomeDetailViewState): HomeDetailViewModel? {
|
override fun create(viewModelContext: ViewModelContext, state: HomeDetailViewState): HomeDetailViewModel? {
|
||||||
val homeRoomListStore = viewModelContext.activity.get<HomeRoomListObservableStore>()
|
val homeRoomListStore = viewModelContext.activity.get<HomeRoomListObservableStore>()
|
||||||
return HomeDetailViewModel(state, homeRoomListStore)
|
val session = viewModelContext.activity.get<Session>()
|
||||||
|
return HomeDetailViewModel(state, session, homeRoomListStore)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
init {
|
init {
|
||||||
|
observeSyncState()
|
||||||
observeRoomSummaries()
|
observeRoomSummaries()
|
||||||
}
|
}
|
||||||
|
|
||||||
// PRIVATE METHODS *****************************************************************************
|
// PRIVATE METHODS *****************************************************************************
|
||||||
|
|
||||||
|
private fun observeSyncState() {
|
||||||
|
session.rx()
|
||||||
|
.liveSyncState()
|
||||||
|
.subscribe { syncState ->
|
||||||
|
setState {
|
||||||
|
copy(syncState = syncState)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.disposeOnClear()
|
||||||
|
}
|
||||||
|
|
||||||
private fun observeRoomSummaries() {
|
private fun observeRoomSummaries() {
|
||||||
homeRoomListStore
|
homeRoomListStore
|
||||||
.observe()
|
.observe()
|
||||||
|
@ -17,6 +17,7 @@
|
|||||||
package im.vector.riotredesign.features.home
|
package im.vector.riotredesign.features.home
|
||||||
|
|
||||||
import com.airbnb.mvrx.MvRxState
|
import com.airbnb.mvrx.MvRxState
|
||||||
|
import im.vector.matrix.android.api.session.sync.SyncState
|
||||||
|
|
||||||
data class HomeDetailViewState(
|
data class HomeDetailViewState(
|
||||||
val notificationCountCatchup: Int = 0,
|
val notificationCountCatchup: Int = 0,
|
||||||
@ -24,5 +25,6 @@ data class HomeDetailViewState(
|
|||||||
val notificationCountPeople: Int = 0,
|
val notificationCountPeople: Int = 0,
|
||||||
val notificationHighlightPeople: Boolean = false,
|
val notificationHighlightPeople: Boolean = false,
|
||||||
val notificationCountRooms: Int = 0,
|
val notificationCountRooms: Int = 0,
|
||||||
val notificationHighlightRooms: Boolean = false
|
val notificationHighlightRooms: Boolean = false,
|
||||||
|
val syncState: SyncState = SyncState.IDLE
|
||||||
) : MvRxState
|
) : MvRxState
|
@ -44,13 +44,26 @@
|
|||||||
|
|
||||||
</androidx.appcompat.widget.Toolbar>
|
</androidx.appcompat.widget.Toolbar>
|
||||||
|
|
||||||
|
<ProgressBar
|
||||||
|
android:id="@+id/syncProgressBar"
|
||||||
|
style="@style/Widget.AppCompat.ProgressBar.Horizontal"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:indeterminate="true"
|
||||||
|
android:visibility="gone"
|
||||||
|
android:background="?riotx_header_panel_background"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/groupToolbar"
|
||||||
|
tools:visibility="visible" />
|
||||||
|
|
||||||
<FrameLayout
|
<FrameLayout
|
||||||
android:id="@+id/roomListContainer"
|
android:id="@+id/roomListContainer"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="0dp"
|
android:layout_height="0dp"
|
||||||
android:background="?riotx_header_panel_background"
|
android:background="?riotx_header_panel_background"
|
||||||
app:layout_constraintBottom_toTopOf="@+id/bottomNavigationView"
|
app:layout_constraintBottom_toTopOf="@+id/bottomNavigationView"
|
||||||
app:layout_constraintTop_toBottomOf="@+id/groupToolbar" />
|
app:layout_constraintTop_toBottomOf="@+id/syncProgressBar" />
|
||||||
|
|
||||||
<com.google.android.material.bottomnavigation.BottomNavigationView
|
<com.google.android.material.bottomnavigation.BottomNavigationView
|
||||||
android:id="@+id/bottomNavigationView"
|
android:id="@+id/bottomNavigationView"
|
||||||
|
Loading…
Reference in New Issue
Block a user