From bc44c9de1581228fdd559d545783dbe8c984f81d Mon Sep 17 00:00:00 2001 From: LooKeR Date: Fri, 12 Nov 2021 20:54:18 +0530 Subject: [PATCH] Improve: Use IO Thread to show notification (Inconsistent Notification) Improve: List Animation Disabled by default Add: Coroutine and Lifecycle Dependencies --- build.gradle | 14 +-- .../looker/droidify/content/Preferences.kt | 2 +- .../looker/droidify/service/SyncService.kt | 88 ++++++++++--------- 3 files changed, 55 insertions(+), 49 deletions(-) diff --git a/build.gradle b/build.gradle index 6ba0aa86..4f3b3aa3 100644 --- a/build.gradle +++ b/build.gradle @@ -129,12 +129,16 @@ dependencies { // Backend implementation 'io.coil-kt:coil:1.4.0' implementation 'androidx.core:core-ktx:1.7.0' - implementation 'io.reactivex.rxjava3:rxjava:3.1.1' - implementation 'io.reactivex.rxjava3:rxandroid:3.0.0' - implementation 'com.fasterxml.jackson.core:jackson-core:2.13.0' - implementation 'com.squareup.okhttp3:okhttp:4.9.2' - implementation 'com.github.topjohnwu.libsu:core:3.1.2' implementation 'androidx.room:room-runtime:2.3.0' + implementation 'io.reactivex.rxjava3:rxjava:3.1.1' + implementation 'com.squareup.okhttp3:okhttp:4.9.2' + implementation 'io.reactivex.rxjava3:rxandroid:3.0.0' + implementation 'com.github.topjohnwu.libsu:core:3.1.2' + implementation 'com.fasterxml.jackson.core:jackson-core:2.13.0' + implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.4.0' + implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.5.2' + implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.5.2' + implementation 'androidx.room:room-ktx:2.3.0' kapt 'androidx.room:room-compiler:2.3.0' } diff --git a/src/main/kotlin/com/looker/droidify/content/Preferences.kt b/src/main/kotlin/com/looker/droidify/content/Preferences.kt index 260a7cd7..8a077506 100644 --- a/src/main/kotlin/com/looker/droidify/content/Preferences.kt +++ b/src/main/kotlin/com/looker/droidify/content/Preferences.kt @@ -131,7 +131,7 @@ object Preferences { Key("incompatible_versions", Value.BooleanValue(false)) object ListAnimation : - Key("list_animation", Value.BooleanValue(true)) + Key("list_animation", Value.BooleanValue(false)) object ProxyHost : Key("proxy_host", Value.StringValue("localhost")) object ProxyPort : Key("proxy_port", Value.IntValue(9050)) diff --git a/src/main/kotlin/com/looker/droidify/service/SyncService.kt b/src/main/kotlin/com/looker/droidify/service/SyncService.kt index 4d997851..34ee5af1 100644 --- a/src/main/kotlin/com/looker/droidify/service/SyncService.kt +++ b/src/main/kotlin/com/looker/droidify/service/SyncService.kt @@ -29,27 +29,34 @@ import com.looker.droidify.utility.extension.android.notificationManager import com.looker.droidify.utility.extension.resources.getColorFromAttr import com.looker.droidify.utility.extension.text.formatSize import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers -import io.reactivex.rxjava3.core.Observable import io.reactivex.rxjava3.disposables.Disposable -import io.reactivex.rxjava3.schedulers.Schedulers -import io.reactivex.rxjava3.subjects.PublishSubject +import io.reactivex.rxjava3.schedulers.Schedulers.io +import kotlinx.coroutines.* +import kotlinx.coroutines.flow.MutableSharedFlow +import kotlinx.coroutines.flow.SharedFlow +import kotlinx.coroutines.flow.asSharedFlow +import kotlinx.coroutines.flow.collect import java.lang.ref.WeakReference -import java.util.concurrent.TimeUnit import kotlin.math.roundToInt class SyncService : ConnectionService() { + private val scope = CoroutineScope(Dispatchers.Default) + companion object { private const val ACTION_CANCEL = "${BuildConfig.APPLICATION_ID}.intent.action.CANCEL" - private val stateSubject = PublishSubject.create() - private val finishSubject = PublishSubject.create() + private val mutableStateSubject = MutableSharedFlow() + private val mutableFinishSubject = MutableSharedFlow() + + private val stateSubject = mutableStateSubject.asSharedFlow() + private val finishSubject = mutableFinishSubject.asSharedFlow() } private sealed class State { data class Connecting(val name: String) : State() data class Syncing( val name: String, val stage: RepositoryUpdater.Stage, - val read: Long, val total: Long? + val read: Long, val total: Long?, ) : State() object Finishing : State() @@ -58,7 +65,7 @@ class SyncService : ConnectionService() { private class Task(val repositoryId: Long, val manual: Boolean) private data class CurrentTask( val task: Task?, val disposable: Disposable, - val hasUpdates: Boolean, val lastState: State + val hasUpdates: Boolean, val lastState: State, ) private enum class Started { NO, AUTO, MANUAL } @@ -72,7 +79,7 @@ class SyncService : ConnectionService() { enum class SyncRequest { AUTO, MANUAL, FORCE } inner class Binder : android.os.Binder() { - val finish: Observable + val finish: SharedFlow get() = finishSubject private fun sync(ids: List, request: SyncRequest) { @@ -152,8 +159,6 @@ class SyncService : ConnectionService() { private val binder = Binder() override fun onBind(intent: Intent): Binder = binder - private var stateDisposable: Disposable? = null - override fun onCreate() { super.onCreate() @@ -170,17 +175,17 @@ class SyncService : ConnectionService() { ) .let(notificationManager::createNotificationChannel) } - - stateDisposable = stateSubject - .sample(500L, TimeUnit.MILLISECONDS, AndroidSchedulers.mainThread()) - .subscribe { publishForegroundState(false, it) } + scope.launch(Dispatchers.Default) { + stateSubject.collect { + publishForegroundState(false, + it) + } + } } override fun onDestroy() { super.onDestroy() - - stateDisposable?.dispose() - stateDisposable = null + scope.cancel() cancelTasks { true } cancelCurrentTask { true } } @@ -349,18 +354,20 @@ class SyncService : ConnectionService() { lateinit var disposable: Disposable disposable = RepositoryUpdater .update(this, repository, unstable) { stage, progress, total -> - if (!disposable.isDisposed) { - stateSubject.onNext( - State.Syncing( - repository.name, - stage, - progress, - total + scope.launch(Dispatchers.Default) { + if (!disposable.isDisposed) { + mutableStateSubject.emit( + State.Syncing( + repository.name, + stage, + progress, + total + ) ) - ) + } } } - .observeOn(AndroidSchedulers.mainThread()) + .observeOn(io()) .subscribe { result, throwable -> currentTask = null throwable?.printStackTrace() @@ -391,7 +398,7 @@ class SyncService : ConnectionService() { .toList() } } - .subscribeOn(Schedulers.io()) + .subscribeOn(io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe { result, throwable -> throwable?.printStackTrace() @@ -404,7 +411,7 @@ class SyncService : ConnectionService() { } currentTask = CurrentTask(null, disposable, true, State.Finishing) } else { - finishSubject.onNext(Unit) + scope.launch(Dispatchers.Main) { mutableFinishSubject.emit(Unit) } val needStop = started == Started.MANUAL started = Started.NO if (needStop) { @@ -472,23 +479,20 @@ class SyncService : ConnectionService() { class Job : JobService() { private var syncParams: JobParameters? = null - private var syncDisposable: Disposable? = null private val syncConnection = Connection(SyncService::class.java, onBind = { connection, binder -> - syncDisposable = binder.finish.subscribe { - val params = syncParams - if (params != null) { - syncParams = null - syncDisposable?.dispose() - syncDisposable = null - connection.unbind(this) - jobFinished(params, false) + MainScope().launch { + binder.finish.collect { + val params = syncParams + if (params != null) { + syncParams = null + connection.unbind(this@Job) + jobFinished(params, false) + } } + binder.sync(SyncRequest.AUTO) } - binder.sync(SyncRequest.AUTO) }, onUnbind = { _, binder -> - syncDisposable?.dispose() - syncDisposable = null binder.cancelAuto() val params = syncParams if (params != null) { @@ -505,8 +509,6 @@ class SyncService : ConnectionService() { override fun onStopJob(params: JobParameters): Boolean { syncParams = null - syncDisposable?.dispose() - syncDisposable = null val reschedule = syncConnection.binder?.cancelAuto() == true syncConnection.unbind(this) return reschedule