diff --git a/android/gradle.properties b/android/gradle.properties index 5659e1893..b872d9d12 100644 --- a/android/gradle.properties +++ b/android/gradle.properties @@ -6,7 +6,8 @@ TARGET_SDK_VERSION = 29 COMPILE_SDK_VERSION = 29 DAGGER_VERSION = 2.25.4 -KOTLIN_VERSION = 1.3.61 +KOTLIN_VERSION = 1.4.0 +KX_COROUTINES_VERSION = 1.4.2 SUPPORT_LIBRARY_VERSION = 28.0.0 AUTO_FACTORY_VERSION = 1.0-beta6 BUILD_TOOLS_VERSION = 4.0.0 diff --git a/android/uhabits-android/build.gradle b/android/uhabits-android/build.gradle index c1f7b6659..2ac303436 100644 --- a/android/uhabits-android/build.gradle +++ b/android/uhabits-android/build.gradle @@ -92,6 +92,8 @@ dependencies { implementation "com.google.code.gson:gson:2.8.5" implementation "com.google.code.findbugs:jsr305:3.0.2" implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$KOTLIN_VERSION" + implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:$KX_COROUTINES_VERSION" + implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$KX_COROUTINES_VERSION" implementation "androidx.constraintlayout:constraintlayout:2.0.0-beta4" implementation 'com.google.zxing:core:3.4.1' implementation "io.ktor:ktor-client-core:$KTOR_VERSION" diff --git a/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/list/ListHabitsActivity.kt b/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/list/ListHabitsActivity.kt index 61179d467..b40b71ef0 100644 --- a/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/list/ListHabitsActivity.kt +++ b/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/list/ListHabitsActivity.kt @@ -20,6 +20,7 @@ package org.isoron.uhabits.activities.habits.list import android.os.* +import kotlinx.coroutines.* import org.isoron.uhabits.* import org.isoron.uhabits.activities.* import org.isoron.uhabits.activities.habits.list.views.* @@ -40,6 +41,7 @@ class ListHabitsActivity : HabitsActivity() { lateinit var prefs: Preferences lateinit var midnightTimer: MidnightTimer lateinit var syncManager: SyncManager + private val scope = CoroutineScope(Dispatchers.Main) override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) @@ -61,7 +63,9 @@ class ListHabitsActivity : HabitsActivity() { midnightTimer.onPause() screen.onDettached() adapter.cancelRefresh() - syncManager.onPause() + scope.launch { + syncManager.onPause() + } super.onPause() } @@ -70,7 +74,9 @@ class ListHabitsActivity : HabitsActivity() { screen.onAttached() rootView.postInvalidate() midnightTimer.onResume() - syncManager.onResume() + scope.launch { + syncManager.onResume() + } taskRunner.run { AutoBackup(this@ListHabitsActivity).run() } diff --git a/android/uhabits-android/src/main/java/org/isoron/uhabits/sync/RemoteSyncServer.kt b/android/uhabits-android/src/main/java/org/isoron/uhabits/sync/RemoteSyncServer.kt index bc46d8189..1e0e16781 100644 --- a/android/uhabits-android/src/main/java/org/isoron/uhabits/sync/RemoteSyncServer.kt +++ b/android/uhabits-android/src/main/java/org/isoron/uhabits/sync/RemoteSyncServer.kt @@ -24,6 +24,7 @@ import io.ktor.client.engine.android.* import io.ktor.client.features.* import io.ktor.client.features.json.* import io.ktor.client.request.* +import kotlinx.coroutines.* data class RegisterReponse(val key: String) data class GetDataVersionResponse(val version: Long) @@ -35,16 +36,16 @@ class RemoteSyncServer( } ) : AbstractSyncServer { - override suspend fun register(): String { + override suspend fun register(): String = Dispatchers.IO { try { val response: RegisterReponse = httpClient.post("$baseURL/register") - return response.key + return@IO response.key } catch(e: ServerResponseException) { throw ServiceUnavailable() } } - override suspend fun put(key: String, newData: SyncData) { + override suspend fun put(key: String, newData: SyncData) = Dispatchers.IO { try { val response: String = httpClient.put("$baseURL/db/$key") { header("Content-Type", "application/json") @@ -57,9 +58,10 @@ class RemoteSyncServer( } } - override suspend fun getData(key: String): SyncData { + override suspend fun getData(key: String): SyncData = Dispatchers.IO { try { - return httpClient.get("$baseURL/db/$key") + val data: SyncData = httpClient.get("$baseURL/db/$key") + return@IO data } catch (e: ServerResponseException) { throw ServiceUnavailable() } catch (e: ClientRequestException) { @@ -67,10 +69,10 @@ class RemoteSyncServer( } } - override suspend fun getDataVersion(key: String): Long { + override suspend fun getDataVersion(key: String): Long = Dispatchers.IO { try { val response: GetDataVersionResponse = httpClient.get("$baseURL/db/$key/version") - return response.version + return@IO response.version } catch(e: ServerResponseException) { throw ServiceUnavailable() } catch (e: ClientRequestException) { diff --git a/android/uhabits-android/src/main/java/org/isoron/uhabits/sync/SyncManager.kt b/android/uhabits-android/src/main/java/org/isoron/uhabits/sync/SyncManager.kt index 262c00fe8..e28a642df 100644 --- a/android/uhabits-android/src/main/java/org/isoron/uhabits/sync/SyncManager.kt +++ b/android/uhabits-android/src/main/java/org/isoron/uhabits/sync/SyncManager.kt @@ -51,31 +51,26 @@ class SyncManager @Inject constructor( commandRunner.addListener(this) } - fun sync() { - if(!preferences.isSyncEnabled) { + suspend fun sync() { + if (!preferences.isSyncEnabled) { Log.i("SyncManager", "Device sync is disabled. Skipping sync") return } - taskRunner.execute { - runBlocking { - try { - Log.i("SyncManager", "Starting sync (key: ${preferences.syncKey})") - fetchAndMerge() - upload() - Log.i("SyncManager", "Sync finished") - } catch (e: Exception) { - Log.e("SyncManager", "Unexpected sync exception. Disabling sync", e) - preferences.isSyncEnabled = false - preferences.syncKey = "" - preferences.encryptionKey = "" - } - return@runBlocking - } + try { + Log.i("SyncManager", "Starting sync (key: ${preferences.syncKey})") + fetchAndMerge() + upload() + Log.i("SyncManager", "Sync finished") + } catch (e: Exception) { + Log.e("SyncManager", "Unexpected sync exception. Disabling sync", e) + preferences.isSyncEnabled = false + preferences.syncKey = "" + preferences.encryptionKey = "" } } suspend fun upload() { - if(!dirty) { + if (!dirty) { Log.i("SyncManager", "Database not dirty. Skipping upload.") return } @@ -101,14 +96,19 @@ class SyncManager @Inject constructor( currVersion = data.version + 1 } - fun onResume() { + suspend fun onResume() { sync() } - fun onPause() { + suspend fun onPause() { sync() } - override fun onSyncEnabled() = sync() + + override fun onSyncEnabled() { + CoroutineScope(Dispatchers.Main).launch { + sync() + } + } override fun onCommandExecuted(command: Command?, refreshKey: Long?) { dirty = true