From 44eb838610e8c035c4c1d9eb32b8b60bbbae5490 Mon Sep 17 00:00:00 2001 From: ganfra <francois.ganard@gmail.com> Date: Wed, 17 Oct 2018 13:59:21 +0200 Subject: [PATCH] Manage sync in an infinite thread --- .idea/caches/build_file_checksums.ser | Bin 651 -> 651 bytes .idea/dictionaries/ganfra.xml | 2 + .../features/home/HomeActivity.kt | 40 ++----- app/src/main/res/layout/activity_home.xml | 24 ++-- matrix-sdk-android/build.gradle | 7 +- .../src/main/AndroidManifest.xml | 1 + .../im/vector/matrix/android/api/Matrix.kt | 8 +- .../matrix/android/api/session/Session.kt | 4 +- .../vector/matrix/android/api/util/Logger.kt | 57 ---------- .../internal/database/mapper/EventMapper.kt | 2 +- .../android/internal/di/NetworkModule.kt | 5 + .../internal/events/sync/RoomSyncHandler.kt | 1 + .../internal/events/sync/StateEvent.kt | 18 --- .../internal/events/sync/SyncModule.kt | 9 +- .../sync/{Synchronizer.kt => SyncRequest.kt} | 16 ++- .../internal/events/sync/job/SyncThread.kt | 105 ++++++++++++++++++ .../android/internal/legacy/MXSession.java | 2 +- .../internal/legacy/sync/EventsThread.java | 6 +- .../network/NetworkConnectivityChecker.kt | 44 ++++++++ .../internal/session/DefaultSession.kt | 20 ++-- 20 files changed, 221 insertions(+), 150 deletions(-) delete mode 100644 matrix-sdk-android/src/main/java/im/vector/matrix/android/api/util/Logger.kt delete mode 100644 matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/events/sync/StateEvent.kt rename matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/events/sync/{Synchronizer.kt => SyncRequest.kt} (78%) create mode 100644 matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/events/sync/job/SyncThread.kt create mode 100644 matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/network/NetworkConnectivityChecker.kt diff --git a/.idea/caches/build_file_checksums.ser b/.idea/caches/build_file_checksums.ser index 3d334265dec011b5a457deca9220c0aa8634a839..f1d03afc68338383cfc12e31de7f1918461bef67 100644 GIT binary patch delta 36 ucmV+<0NekI1&alcm;`}Qt_iW6Edda3$riX)ldWJg4nkNN*^r)-*#WZofev>7 delta 36 ucmV+<0NekI1&alcm;`+J{R^?2EddZ5hgPFM8Y$3-=d6;?9cdwx*#WW=Ko5lg diff --git a/.idea/dictionaries/ganfra.xml b/.idea/dictionaries/ganfra.xml index a9343c033e..806a104826 100644 --- a/.idea/dictionaries/ganfra.xml +++ b/.idea/dictionaries/ganfra.xml @@ -1,7 +1,9 @@ <component name="ProjectDictionaryState"> <dictionary name="ganfra"> <words> + <w>connectable</w> <w>coroutine</w> + <w>merlins</w> <w>moshi</w> <w>synchronizer</w> </words> diff --git a/app/src/main/java/im/vector/riotredesign/features/home/HomeActivity.kt b/app/src/main/java/im/vector/riotredesign/features/home/HomeActivity.kt index 56d391fe38..07f3be3e5f 100644 --- a/app/src/main/java/im/vector/riotredesign/features/home/HomeActivity.kt +++ b/app/src/main/java/im/vector/riotredesign/features/home/HomeActivity.kt @@ -3,53 +3,31 @@ package im.vector.riotredesign.features.home import android.content.Context import android.content.Intent import android.os.Bundle -import android.view.View import im.vector.matrix.android.api.Matrix -import im.vector.matrix.android.api.MatrixCallback -import im.vector.matrix.android.api.failure.Failure import im.vector.matrix.android.internal.database.model.RoomSummaryEntity -import im.vector.matrix.android.internal.events.sync.data.SyncResponse import im.vector.riotredesign.R import im.vector.riotredesign.core.platform.RiotActivity import kotlinx.android.synthetic.main.activity_home.* import org.koin.android.ext.android.inject import timber.log.Timber + class HomeActivity : RiotActivity() { private val matrix by inject<Matrix>() - private val synchronizer = matrix.currentSession?.synchronizer() - private val realmHolder = matrix.currentSession?.realmHolder() + private val currentSession = matrix.currentSession!! + private val realmHolder = currentSession.realmHolder() + private val syncThread = currentSession.syncThread() override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_home) - synchronizeButton.setOnClickListener { synchronize() } - } - - private fun synchronize() { - synchronizeButton.visibility = View.GONE - loadingView.visibility = View.VISIBLE - synchronizer?.synchronize(object : MatrixCallback<SyncResponse> { - override fun onSuccess(data: SyncResponse) { - synchronizeButton.visibility = View.VISIBLE - loadingView.visibility = View.GONE - Timber.v("Sync successful") - } - - override fun onFailure(failure: Failure) { - synchronizeButton.visibility = View.VISIBLE - loadingView.visibility = View.GONE - Timber.e("Sync has failed : %s", failure.toString()) - } - }) - if (realmHolder != null) { - val results = realmHolder.instance.where(RoomSummaryEntity::class.java).findAll() - results.addChangeListener { summaries -> - Timber.v("Summaries updated") - } + val results = realmHolder.instance.where(RoomSummaryEntity::class.java).findAll() + results.addChangeListener { summaries -> + Timber.v("Summaries updated") } - + startSyncButton.setOnClickListener { syncThread.restart() } + stopSyncButton.setOnClickListener { syncThread.pause() } } companion object { diff --git a/app/src/main/res/layout/activity_home.xml b/app/src/main/res/layout/activity_home.xml index 3078248ee8..e5007dce7d 100644 --- a/app/src/main/res/layout/activity_home.xml +++ b/app/src/main/res/layout/activity_home.xml @@ -7,34 +7,34 @@ android:orientation="vertical" tools:context=".features.login.LoginActivity"> - <ProgressBar - android:id="@+id/loadingView" - android:layout_width="wrap_content" - android:layout_height="wrap_content" + <Button + android:id="@+id/startSyncButton" + android:layout_width="0dp" + android:layout_height="0dp" android:layout_marginBottom="8dp" android:layout_marginEnd="8dp" android:layout_marginStart="8dp" android:layout_marginTop="8dp" - android:visibility="gone" - app:layout_constraintBottom_toBottomOf="parent" + android:text="Start sync" + app:layout_constraintBottom_toTopOf="@+id/stopSyncButton" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" /> - <Button - android:id="@+id/synchronizeButton" - android:layout_width="wrap_content" - android:layout_height="wrap_content" + android:id="@+id/stopSyncButton" + android:layout_width="0dp" + android:layout_height="0dp" android:layout_marginBottom="8dp" android:layout_marginEnd="8dp" android:layout_marginStart="8dp" android:layout_marginTop="8dp" - android:text="Synchronize" + android:text="stop sync" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintHorizontal_bias="0.5" app:layout_constraintStart_toStartOf="parent" - app:layout_constraintTop_toTopOf="parent" /> + app:layout_constraintTop_toBottomOf="@+id/startSyncButton" /> </android.support.constraint.ConstraintLayout> \ No newline at end of file diff --git a/matrix-sdk-android/build.gradle b/matrix-sdk-android/build.gradle index 905877d00c..7e1402bad1 100644 --- a/matrix-sdk-android/build.gradle +++ b/matrix-sdk-android/build.gradle @@ -45,6 +45,7 @@ dependencies { def support_version = '28.0.0' def moshi_version = '1.7.0' def lifecycle_version = "1.1.1" + def work_version = "1.0.0-alpha10" implementation fileTree(dir: 'libs', include: ['*.aar']) implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" @@ -55,7 +56,6 @@ dependencies { implementation "android.arch.lifecycle:extensions:$lifecycle_version" kapt "android.arch.lifecycle:compiler:$lifecycle_version" - // Network implementation 'com.squareup.retrofit2:retrofit:2.4.0' implementation 'com.squareup.retrofit2:converter-moshi:2.4.0' @@ -64,6 +64,7 @@ dependencies { implementation 'com.squareup.okhttp3:okhttp:3.10.0' implementation 'com.squareup.okhttp3:logging-interceptor:3.10.0' implementation 'com.squareup.okio:okio:1.15.0' + implementation 'com.novoda:merlin:1.1.6' implementation 'com.google.code.gson:gson:2.8.5' implementation "com.squareup.moshi:moshi-adapters:$moshi_version" @@ -72,6 +73,10 @@ dependencies { // Paging implementation "android.arch.paging:runtime:1.0.1" + // Worker + implementation "android.arch.work:work-runtime-ktx:$work_version" + implementation 'com.evernote:android-job:1.2.6' + implementation "io.arrow-kt:arrow-core:$arrow_version" // DI diff --git a/matrix-sdk-android/src/main/AndroidManifest.xml b/matrix-sdk-android/src/main/AndroidManifest.xml index 8139d75a37..44b519b0b0 100644 --- a/matrix-sdk-android/src/main/AndroidManifest.xml +++ b/matrix-sdk-android/src/main/AndroidManifest.xml @@ -1,5 +1,6 @@ <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="im.vector.matrix.android" > + <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> <uses-permission android:name="android.permission.VIBRATE" /> </manifest> diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/Matrix.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/Matrix.kt index 7af58b9b57..83852976ef 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/Matrix.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/Matrix.kt @@ -1,7 +1,7 @@ package im.vector.matrix.android.api import android.content.Context -import im.vector.matrix.android.BuildConfig +import com.evernote.android.job.JobManager import im.vector.matrix.android.api.auth.Authenticator import im.vector.matrix.android.api.session.Session import im.vector.matrix.android.internal.auth.AuthModule @@ -11,8 +11,6 @@ import io.realm.Realm import org.koin.standalone.KoinComponent import org.koin.standalone.StandAloneContext.loadKoinModules import org.koin.standalone.inject -import timber.log.Timber -import timber.log.Timber.DebugTree class Matrix(matrixOptions: MatrixOptions) : KoinComponent { @@ -23,13 +21,11 @@ class Matrix(matrixOptions: MatrixOptions) : KoinComponent { init { Realm.init(matrixOptions.context) + JobManager.create(matrixOptions.context) val matrixModule = MatrixModule(matrixOptions) val networkModule = NetworkModule() val authModule = AuthModule() loadKoinModules(listOf(matrixModule, networkModule, authModule)) - if (BuildConfig.DEBUG) { - Timber.plant(DebugTree()) - } } fun authenticator(): Authenticator { diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/Session.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/Session.kt index d1fa6bb900..cacf786dd8 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/Session.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/Session.kt @@ -2,14 +2,14 @@ package im.vector.matrix.android.api.session import android.support.annotation.MainThread import im.vector.matrix.android.internal.database.SessionRealmHolder -import im.vector.matrix.android.internal.events.sync.Synchronizer +import im.vector.matrix.android.internal.events.sync.job.SyncThread interface Session { @MainThread fun open() - fun synchronizer(): Synchronizer + fun syncThread(): SyncThread // Visible for testing request directly. Will be deleted fun realmHolder(): SessionRealmHolder diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/util/Logger.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/util/Logger.kt deleted file mode 100644 index 2c78e9f502..0000000000 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/util/Logger.kt +++ /dev/null @@ -1,57 +0,0 @@ -package im.vector.matrix.android.api.util - -interface Logger { - /** Log a verbose message with optional format args. */ - fun v(message: String, vararg args: Any) - - /** Log a verbose exception and a message with optional format args. */ - fun v(t: Throwable, message: String, vararg args: Any) - - /** Log a verbose exception. */ - fun v(t: Throwable) - - /** Log a debug message with optional format args. */ - fun d(message: String, vararg args: Any) - - /** Log a debug exception and a message with optional format args. */ - fun d(t: Throwable, message: String, vararg args: Any) - - /** Log a debug exception. */ - fun d(t: Throwable) - - /** Log an info message with optional format args. */ - fun i(message: String, vararg args: Any) - - /** Log an info exception and a message with optional format args. */ - fun i(t: Throwable, message: String, vararg args: Any) - - /** Log an info exception. */ - fun i(t: Throwable) - - /** Log a warning message with optional format args. */ - fun w(message: String, vararg args: Any) - - /** Log a warning exception and a message with optional format args. */ - fun w(t: Throwable, message: String, vararg args: Any) - - /** Log a warning exception. */ - fun w(t: Throwable) - - /** Log an error message with optional format args. */ - fun e(message: String, vararg args: Any) - - /** Log an error exception and a message with optional format args. */ - fun e(t: Throwable, message: String, vararg args: Any) - - /** Log an error exception. */ - fun e(t: Throwable) - - /** Log an assert message with optional format args. */ - fun wtf(message: String, vararg args: Any) - - /** Log an assert exception and a message with optional format args. */ - fun wtf(t: Throwable, message: String, vararg args: Any) - - /** Log an assert exception. */ - fun wtf(t: Throwable) -} \ No newline at end of file diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/mapper/EventMapper.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/mapper/EventMapper.kt index 9ccc93074a..05b25dbbf8 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/mapper/EventMapper.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/mapper/EventMapper.kt @@ -15,7 +15,7 @@ object EventMapper { internal fun map(event: Event): EventEntity { val eventEntity = EventEntity() - eventEntity.eventId = event.eventId!! + eventEntity.eventId = event.eventId ?: "" eventEntity.content = adapter.toJson(event.content) eventEntity.prevContent = adapter.toJson(event.prevContent) eventEntity.stateKey = event.stateKey diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/di/NetworkModule.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/di/NetworkModule.kt index 9504b7b072..7300242d2b 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/di/NetworkModule.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/di/NetworkModule.kt @@ -2,6 +2,7 @@ package im.vector.matrix.android.internal.di import com.jakewharton.retrofit2.adapter.kotlin.coroutines.CoroutineCallAdapterFactory import im.vector.matrix.android.internal.network.AccessTokenInterceptor +import im.vector.matrix.android.internal.network.NetworkConnectivityChecker import okhttp3.OkHttpClient import okhttp3.logging.HttpLoggingInterceptor import org.koin.dsl.context.ModuleDefinition @@ -49,6 +50,10 @@ class NetworkModule : Module { CoroutineCallAdapterFactory() as CallAdapter.Factory } + single { + NetworkConnectivityChecker(get()) + } + factory { Retrofit.Builder() .client(get()) diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/events/sync/RoomSyncHandler.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/events/sync/RoomSyncHandler.kt index a96c73d32e..070247fd80 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/events/sync/RoomSyncHandler.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/events/sync/RoomSyncHandler.kt @@ -104,6 +104,7 @@ class RoomSyncHandler(private val realmConfiguration: RealmConfiguration) { chunkEntity.nextToken = nextToken chunkEntity.isLimited = isLimited + eventList.forEach { event -> val eventEntity = event.asEntity().let { realm.copyToRealmOrUpdate(it) diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/events/sync/StateEvent.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/events/sync/StateEvent.kt deleted file mode 100644 index 602f6dcd13..0000000000 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/events/sync/StateEvent.kt +++ /dev/null @@ -1,18 +0,0 @@ -package im.vector.matrix.android.internal.events.sync - -import com.squareup.moshi.Json -import com.squareup.moshi.JsonClass - -@JsonClass(generateAdapter = true) -data class StateEvent( - @Json(name = "name") val name: String? = null, - @Json(name = "topic") val topic: String? = null, - @Json(name = "join_rule") val joinRule: String? = null, - @Json(name = "guest_access") val guestAccess: String? = null, - @Json(name = "alias") val canonicalAlias: String? = null, - @Json(name = "aliases") val aliases: List<String>? = null, - @Json(name = "algorithm") val algorithm: String? = null, - @Json(name = "history_visibility") val historyVisibility: String? = null, - @Json(name = "url") val url: String? = null, - @Json(name = "groups") val groups: List<String>? = null -) \ No newline at end of file diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/events/sync/SyncModule.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/events/sync/SyncModule.kt index cea92d7fe4..3c545172f7 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/events/sync/SyncModule.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/events/sync/SyncModule.kt @@ -1,11 +1,13 @@ package im.vector.matrix.android.internal.events.sync +import im.vector.matrix.android.internal.events.sync.job.SyncThread import im.vector.matrix.android.internal.session.DefaultSession import org.koin.dsl.context.ModuleDefinition import org.koin.dsl.module.Module import org.koin.dsl.module.module import retrofit2.Retrofit + class SyncModule : Module { override fun invoke(): ModuleDefinition = module(override = true) { @@ -24,8 +26,13 @@ class SyncModule : Module { } scope(DefaultSession.SCOPE) { - Synchronizer(get(), get(), get()) + SyncRequest(get(), get(), get()) } + scope(DefaultSession.SCOPE) { + SyncThread(get(), get()) + } + + }.invoke() } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/events/sync/Synchronizer.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/events/sync/SyncRequest.kt similarity index 78% rename from matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/events/sync/Synchronizer.kt rename to matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/events/sync/SyncRequest.kt index c834837002..69a034d8dd 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/events/sync/Synchronizer.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/events/sync/SyncRequest.kt @@ -16,28 +16,27 @@ import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.launch import kotlinx.coroutines.withContext -class Synchronizer(private val syncAPI: SyncAPI, - private val coroutineDispatchers: MatrixCoroutineDispatchers, - private val syncResponseHandler: SyncResponseHandler) { +class SyncRequest(private val syncAPI: SyncAPI, + private val coroutineDispatchers: MatrixCoroutineDispatchers, + private val syncResponseHandler: SyncResponseHandler) { - private var token: String? = null - fun synchronize(callback: MatrixCallback<SyncResponse>): Cancelable { + fun execute(token: String?, callback: MatrixCallback<SyncResponse>): Cancelable { val job = GlobalScope.launch(coroutineDispatchers.main) { - val syncOrFailure = synchronize() + val syncOrFailure = execute(token) syncOrFailure.bimap({ callback.onFailure(it) }, { callback.onSuccess(it) }) } return CancelableCoroutine(job) } - private suspend fun synchronize() = withContext(coroutineDispatchers.io) { + private suspend fun execute(token: String?) = withContext(coroutineDispatchers.io) { val params = HashMap<String, String>() val filterBody = FilterBody() FilterUtil.enableLazyLoading(filterBody, true) var timeout = 0 if (token != null) { params["since"] = token as String - timeout = 30 + timeout = 30000 } params["timeout"] = timeout.toString() params["filter"] = filterBody.toJSONString() @@ -46,7 +45,6 @@ class Synchronizer(private val syncAPI: SyncAPI, }.leftIfNull { Failure.Unknown(RuntimeException("Sync response shouln't be null")) }.flatMap { - token = it?.nextBatch try { syncResponseHandler.handleResponse(it, null, false) Either.right(it) diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/events/sync/job/SyncThread.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/events/sync/job/SyncThread.kt new file mode 100644 index 0000000000..623517beac --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/events/sync/job/SyncThread.kt @@ -0,0 +1,105 @@ +package im.vector.matrix.android.internal.events.sync.job + +import im.vector.matrix.android.api.MatrixCallback +import im.vector.matrix.android.api.failure.Failure +import im.vector.matrix.android.api.util.Cancelable +import im.vector.matrix.android.internal.events.sync.SyncRequest +import im.vector.matrix.android.internal.events.sync.data.SyncResponse +import im.vector.matrix.android.internal.network.NetworkConnectivityChecker +import timber.log.Timber +import java.util.concurrent.CountDownLatch + +private const val RETRY_WAIT_TIME_MS = 10_000L + +class SyncThread(private val syncRequest: SyncRequest, + private val networkConnectivityChecker: NetworkConnectivityChecker +) : Thread(), NetworkConnectivityChecker.Listener { + + enum class State { + IDLE, + RUNNING, + PAUSED, + KILLING, + KILLED + } + + private var state: State = State.IDLE + private val lock = Object() + private var nextBatch: String? = null + private var cancelableRequest: Cancelable? = null + + fun restart() { + synchronized(lock) { + if (state != State.PAUSED) { + return@synchronized + } + Timber.v("Unpause sync...") + state = State.RUNNING + lock.notify() + } + } + + fun pause() { + synchronized(lock) { + if (state != State.RUNNING) { + return@synchronized + } + Timber.v("Pause sync...") + state = State.PAUSED + } + } + + fun kill() { + synchronized(lock) { + Timber.v("Kill sync...") + state = State.KILLING + cancelableRequest?.cancel() + lock.notify() + } + } + + + override fun run() { + Timber.v("Start syncing...") + state = State.RUNNING + networkConnectivityChecker.register(this) + while (state != State.KILLING) { + if (!networkConnectivityChecker.isConnected() || state == State.PAUSED) { + Timber.v("Waiting...") + synchronized(lock) { + lock.wait() + } + } else { + Timber.v("Execute sync request...") + val latch = CountDownLatch(1) + cancelableRequest = syncRequest.execute(nextBatch, object : MatrixCallback<SyncResponse> { + override fun onSuccess(data: SyncResponse) { + nextBatch = data.nextBatch + latch.countDown() + } + + override fun onFailure(failure: Failure) { + if (failure !is Failure.NetworkConnection) { + // Wait 10s before retrying + sleep(RETRY_WAIT_TIME_MS) + } + latch.countDown() + } + }) + latch.await() + } + } + Timber.v("Sync killed") + state = State.KILLED + networkConnectivityChecker.unregister(this) + } + + override fun onConnect() { + synchronized(lock) { + lock.notify() + } + } + +} + + diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/legacy/MXSession.java b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/legacy/MXSession.java index ac0be7bb4a..9a8ceab6d9 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/legacy/MXSession.java +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/legacy/MXSession.java @@ -1075,7 +1075,7 @@ public class MXSession { } if (null != mEventsThread) { - Log.d(LOG_TAG, "## resumeEventStream() : unpause"); + Log.d(LOG_TAG, "## resumeEventStream() : pickUp"); mEventsThread.unpause(); } else { Log.e(LOG_TAG, "resumeEventStream : mEventsThread is null"); diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/legacy/sync/EventsThread.java b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/legacy/sync/EventsThread.java index 9c580b14a0..dfef023ad4 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/legacy/sync/EventsThread.java +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/legacy/sync/EventsThread.java @@ -228,7 +228,7 @@ public class EventsThread extends Thread { } /** - * Pause the thread. It will resume where it left off when unpause()d. + * Pause the thread. It will resume where it left off when pickUp()d. */ public void pause() { Log.d(LOG_TAG, "pause()"); @@ -264,10 +264,10 @@ public class EventsThread extends Thread { * Unpause the thread if it had previously been paused. If not, this does nothing. */ public void unpause() { - Log.d(LOG_TAG, "## unpause() : thread state " + getState()); + Log.d(LOG_TAG, "## pickUp() : thread state " + getState()); if (State.WAITING == getState()) { - Log.d(LOG_TAG, "## unpause() : the thread was paused so resume it."); + Log.d(LOG_TAG, "## pickUp() : the thread was paused so resume it."); mPaused = false; synchronized (mSyncObject) { diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/network/NetworkConnectivityChecker.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/network/NetworkConnectivityChecker.kt new file mode 100644 index 0000000000..588cc05dfa --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/network/NetworkConnectivityChecker.kt @@ -0,0 +1,44 @@ +package im.vector.matrix.android.internal.network + +import android.content.Context +import com.novoda.merlin.Merlin +import com.novoda.merlin.MerlinsBeard +import com.novoda.merlin.registerable.connection.Connectable + +class NetworkConnectivityChecker(context: Context) { + + private val merlin = Merlin.Builder().withConnectableCallbacks().build(context) + private val merlinsBeard = MerlinsBeard.from(context) + + private val listeners = ArrayList<Listener>() + + fun register(listener: Listener) { + if (listeners.isEmpty()) { + merlin.bind() + } + listeners.add(listener) + val connectable = Connectable { + if (listeners.contains(listener)) { + listener.onConnect() + } + } + merlin.registerConnectable(connectable) + } + + fun unregister(listener: Listener) { + if (listeners.remove(listener) && listeners.isEmpty()) { + merlin.unbind() + } + } + + fun isConnected(): Boolean { + return merlinsBeard.isConnected + } + + interface Listener { + fun onConnect() + } + + +} + diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/DefaultSession.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/DefaultSession.kt index 23e0bf5db6..820ec2b092 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/DefaultSession.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/DefaultSession.kt @@ -7,13 +7,14 @@ import im.vector.matrix.android.internal.auth.data.SessionParams import im.vector.matrix.android.internal.database.SessionRealmHolder import im.vector.matrix.android.internal.di.SessionModule import im.vector.matrix.android.internal.events.sync.SyncModule -import im.vector.matrix.android.internal.events.sync.Synchronizer +import im.vector.matrix.android.internal.events.sync.job.SyncThread import org.koin.core.scope.Scope import org.koin.standalone.KoinComponent import org.koin.standalone.StandAloneContext import org.koin.standalone.getKoin import org.koin.standalone.inject + class DefaultSession(private val sessionParams: SessionParams) : Session, KoinComponent { companion object { @@ -23,9 +24,8 @@ class DefaultSession(private val sessionParams: SessionParams) : Session, KoinCo private lateinit var scope: Scope private val realmInstanceHolder by inject<SessionRealmHolder>() - private val synchronizer by inject<Synchronizer>() private val roomSummaryObserver by inject<RoomSummaryObserver>() - + private val syncThread by inject<SyncThread>() private var isOpen = false @MainThread @@ -39,11 +39,7 @@ class DefaultSession(private val sessionParams: SessionParams) : Session, KoinCo scope = getKoin().getOrCreateScope(SCOPE) realmInstanceHolder.open() roomSummaryObserver.start() - } - - override fun synchronizer(): Synchronizer { - assert(isOpen) - return synchronizer + syncThread.start() } override fun realmHolder(): SessionRealmHolder { @@ -51,16 +47,24 @@ class DefaultSession(private val sessionParams: SessionParams) : Session, KoinCo return realmInstanceHolder } + override fun syncThread(): SyncThread { + assert(isOpen) + return syncThread + } + @MainThread override fun close() { checkIsMainThread() assert(isOpen) + syncThread.kill() roomSummaryObserver.dispose() realmInstanceHolder.close() scope.close() isOpen = false } + // Private methods ***************************************************************************** + private fun checkIsMainThread() { if (Looper.myLooper() != Looper.getMainLooper()) { throw IllegalStateException("Should be called on main thread")