Merge pull request #473 from vector-im/feature/sync_room

Feature/sync room
This commit is contained in:
Benoit Marty 2019-08-08 16:15:26 +02:00 committed by GitHub
commit 5a9d88e791
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 83 additions and 23 deletions

View File

@ -12,9 +12,10 @@ Improvements:
- Enable proper cancellation of suspending functions (including db transaction)
- Enhances network connectivity checks in SDK
- Add "View Edit History" item in the message bottom sheet (#401)
- Cancel sync request on pause and timeout to 0 after pause (#404)

Other changes:
-
- Show sync progress also in room detail screen (#403)

Bugfix:
- Edited message: link confusion when (edited) appears in body (#398)

View File

@ -30,6 +30,7 @@ import im.vector.matrix.android.internal.task.TaskExecutor
import im.vector.matrix.android.internal.task.TaskThread
import im.vector.matrix.android.internal.task.configureWith
import im.vector.matrix.android.internal.util.BackgroundDetectionObserver
import kotlinx.coroutines.CancellationException
import timber.log.Timber
import java.net.SocketTimeoutException
import java.util.concurrent.CountDownLatch
@ -70,6 +71,8 @@ internal class SyncThread @Inject constructor(private val syncTask: SyncTask,
if (state is SyncState.RUNNING) {
Timber.v("Pause sync...")
updateStateTo(SyncState.PAUSED)
cancelableTask?.cancel()
lock.notify()
}
}

@ -90,18 +93,25 @@ internal class SyncThread @Inject constructor(private val syncTask: SyncTask,
backgroundDetectionObserver.register(this)

while (state != SyncState.KILLING) {
Timber.v("Entering loop, state: $state")

if (!networkConnectivityChecker.isConnected() || state == SyncState.PAUSED) {
Timber.v("Sync is Paused. Waiting...")
Timber.v("No network or sync is Paused. Waiting...")
synchronized(lock) {
lock.wait()
}
Timber.v("...unlocked")
} else {
if (state !is SyncState.RUNNING) {
updateStateTo(SyncState.RUNNING(afterPause = true))
}
Timber.v("[$this] Execute sync request with timeout $DEFAULT_LONG_POOL_TIMEOUT")

// No timeout after a pause
val timeout = state.let { if (it is SyncState.RUNNING && it.afterPause) 0 else DEFAULT_LONG_POOL_TIMEOUT }

Timber.v("Execute sync request with timeout $timeout")
val latch = CountDownLatch(1)
val params = SyncTask.Params(DEFAULT_LONG_POOL_TIMEOUT)
val params = SyncTask.Params(timeout)

cancelableTask = syncTask.configureWith(params) {
this.callbackThread = TaskThread.SYNC
@ -109,29 +119,31 @@ internal class SyncThread @Inject constructor(private val syncTask: SyncTask,
this.callback = object : MatrixCallback<Unit> {

override fun onSuccess(data: Unit) {
Timber.v("onSuccess")
latch.countDown()
}

override fun onFailure(failure: Throwable) {
if (failure is Failure.NetworkConnection
&& failure.cause is SocketTimeoutException) {
if (failure is Failure.NetworkConnection && failure.cause is SocketTimeoutException) {
// Timeout are not critical
Timber.v("Timeout")
} else {
Timber.e(failure)
}

if (failure !is Failure.NetworkConnection
|| failure.cause is JsonEncodingException) {
// Wait 10s before retrying
sleep(RETRY_WAIT_TIME_MS)
}

if (failure is Failure.ServerError
} else if (failure is Failure.Unknown && failure.throwable is CancellationException) {
Timber.v("Cancelled")
} else if (failure is Failure.ServerError
&& (failure.error.code == MatrixError.UNKNOWN_TOKEN || failure.error.code == MatrixError.MISSING_TOKEN)) {
// No token or invalid token, stop the thread
Timber.w(failure)
updateStateTo(SyncState.KILLING)
} else {
Timber.e(failure)

if (failure !is Failure.NetworkConnection || failure.cause is JsonEncodingException) {
// Wait 10s before retrying
Timber.v("Wait 10s")
sleep(RETRY_WAIT_TIME_MS)
}
}

latch.countDown()
}
}
@ -139,8 +151,10 @@ internal class SyncThread @Inject constructor(private val syncTask: SyncTask,
.executeBy(taskExecutor)

latch.await()
if (state is SyncState.RUNNING) {
updateStateTo(SyncState.RUNNING(afterPause = false))
state.let {
if (it is SyncState.RUNNING && it.afterPause) {
updateStateTo(SyncState.RUNNING(afterPause = false))
}
}

Timber.v("...Continue")

View File

@ -65,6 +65,7 @@ import im.vector.matrix.android.api.session.room.send.SendState
import im.vector.matrix.android.api.session.room.timeline.TimelineEvent
import im.vector.matrix.android.api.session.room.timeline.getLastMessageContent
import im.vector.matrix.android.api.session.room.timeline.getTextEditableContent
import im.vector.matrix.android.api.session.sync.SyncState
import im.vector.matrix.android.api.session.user.model.User
import im.vector.riotx.R
import im.vector.riotx.core.di.ScreenComponent
@ -247,6 +248,14 @@ class RoomDetailFragment :
is SendMode.REPLY -> enterSpecialMode(mode.timelineEvent, R.drawable.ic_reply, false)
}
}

roomDetailViewModel.selectSubscribe(RoomDetailViewState::syncState) { syncState ->
syncProgressBar.visibility = when (syncState) {
is SyncState.RUNNING -> if (syncState.afterPause) View.VISIBLE else View.GONE
else -> View.GONE
}
syncProgressBarWrap.visibility = syncProgressBar.visibility
}
}

private fun setupNotificationView() {

View File

@ -103,6 +103,7 @@ class RoomDetailViewModel @AssistedInject constructor(@Assisted initialState: Ro
}

init {
observeSyncState()
observeRoomSummary()
observeEventDisplayedActions()
observeSummaryState()
@ -631,6 +632,17 @@ class RoomDetailViewModel @AssistedInject constructor(@Assisted initialState: Ro
.disposeOnClear()
}

private fun observeSyncState() {
session.rx()
.liveSyncState()
.subscribe { syncState ->
setState {
copy(syncState = syncState)
}
}
.disposeOnClear()
}

private fun observeRoomSummary() {
room.rx().liveRoomSummary()
.execute { async ->

View File

@ -21,9 +21,9 @@ import com.airbnb.mvrx.MvRxState
import com.airbnb.mvrx.Uninitialized
import im.vector.matrix.android.api.session.events.model.Event
import im.vector.matrix.android.api.session.room.model.RoomSummary
import im.vector.matrix.android.api.session.room.model.tombstone.RoomTombstoneContent
import im.vector.matrix.android.api.session.room.timeline.Timeline
import im.vector.matrix.android.api.session.room.timeline.TimelineEvent
import im.vector.matrix.android.api.session.sync.SyncState
import im.vector.matrix.android.api.session.user.model.User

/**
@ -50,7 +50,8 @@ data class RoomDetailViewState(
val sendMode: SendMode = SendMode.REGULAR,
val isEncrypted: Boolean = false,
val tombstoneEvent: Event? = null,
val tombstoneEventHandling: Async<String> = Uninitialized
val tombstoneEventHandling: Async<String> = Uninitialized,
val syncState: SyncState = SyncState.IDLE
) : MvRxState {

constructor(args: RoomDetailArgs) : this(roomId = args.roomId, eventId = args.eventId)

View File

@ -11,8 +11,8 @@
style="@style/VectorToolbarStyle"
android:layout_width="0dp"
android:layout_height="?actionBarSize"
android:transitionName="toolbar"
android:elevation="4dp"
android:transitionName="toolbar"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
@ -71,6 +71,29 @@

</androidx.appcompat.widget.Toolbar>

<!-- Trick to remove surrounding padding (clip frome wrapping frame) -->
<FrameLayout
android:id="@+id/syncProgressBarWrap"
android:layout_width="match_parent"
android:layout_height="3dp"
android:visibility="gone"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/roomToolbar"
tools:visibility="visible">

<ProgressBar
android:id="@+id/syncProgressBar"
style="@style/Widget.AppCompat.ProgressBar.Horizontal"
android:layout_width="match_parent"
android:layout_height="14dp"
android:layout_gravity="center"
android:background="?riotx_header_panel_background"
android:indeterminate="true"
android:visibility="gone"
tools:visibility="visible" />
</FrameLayout>

<com.airbnb.epoxy.EpoxyRecyclerView
android:id="@+id/recyclerView"
android:layout_width="0dp"
@ -78,7 +101,7 @@
app:layout_constraintBottom_toTopOf="@+id/recyclerViewBarrier"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/roomToolbar"
app:layout_constraintTop_toBottomOf="@id/syncProgressBarWrap"
tools:listitem="@layout/item_timeline_event_base" />

<androidx.constraintlayout.widget.Barrier