Improve: Use IO Thread to show notification (Inconsistent Notification)

Improve: List Animation Disabled by default
Add: Coroutine and Lifecycle Dependencies
This commit is contained in:
LooKeR 2021-11-12 20:54:18 +05:30
parent 06f6d2b56c
commit bc44c9de15
3 changed files with 55 additions and 49 deletions

View File

@ -129,12 +129,16 @@ dependencies {
// Backend // Backend
implementation 'io.coil-kt:coil:1.4.0' implementation 'io.coil-kt:coil:1.4.0'
implementation 'androidx.core:core-ktx:1.7.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 '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' implementation 'androidx.room:room-ktx:2.3.0'
kapt 'androidx.room:room-compiler:2.3.0' kapt 'androidx.room:room-compiler:2.3.0'
} }

View File

@ -131,7 +131,7 @@ object Preferences {
Key<Boolean>("incompatible_versions", Value.BooleanValue(false)) Key<Boolean>("incompatible_versions", Value.BooleanValue(false))
object ListAnimation : object ListAnimation :
Key<Boolean>("list_animation", Value.BooleanValue(true)) Key<Boolean>("list_animation", Value.BooleanValue(false))
object ProxyHost : Key<String>("proxy_host", Value.StringValue("localhost")) object ProxyHost : Key<String>("proxy_host", Value.StringValue("localhost"))
object ProxyPort : Key<Int>("proxy_port", Value.IntValue(9050)) object ProxyPort : Key<Int>("proxy_port", Value.IntValue(9050))

View File

@ -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.resources.getColorFromAttr
import com.looker.droidify.utility.extension.text.formatSize import com.looker.droidify.utility.extension.text.formatSize
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers
import io.reactivex.rxjava3.core.Observable
import io.reactivex.rxjava3.disposables.Disposable import io.reactivex.rxjava3.disposables.Disposable
import io.reactivex.rxjava3.schedulers.Schedulers import io.reactivex.rxjava3.schedulers.Schedulers.io
import io.reactivex.rxjava3.subjects.PublishSubject 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.lang.ref.WeakReference
import java.util.concurrent.TimeUnit
import kotlin.math.roundToInt import kotlin.math.roundToInt
class SyncService : ConnectionService<SyncService.Binder>() { class SyncService : ConnectionService<SyncService.Binder>() {
private val scope = CoroutineScope(Dispatchers.Default)
companion object { companion object {
private const val ACTION_CANCEL = "${BuildConfig.APPLICATION_ID}.intent.action.CANCEL" private const val ACTION_CANCEL = "${BuildConfig.APPLICATION_ID}.intent.action.CANCEL"
private val stateSubject = PublishSubject.create<State>() private val mutableStateSubject = MutableSharedFlow<State>()
private val finishSubject = PublishSubject.create<Unit>() private val mutableFinishSubject = MutableSharedFlow<Unit>()
private val stateSubject = mutableStateSubject.asSharedFlow()
private val finishSubject = mutableFinishSubject.asSharedFlow()
} }
private sealed class State { private sealed class State {
data class Connecting(val name: String) : State() data class Connecting(val name: String) : State()
data class Syncing( data class Syncing(
val name: String, val stage: RepositoryUpdater.Stage, val name: String, val stage: RepositoryUpdater.Stage,
val read: Long, val total: Long? val read: Long, val total: Long?,
) : State() ) : State()
object Finishing : State() object Finishing : State()
@ -58,7 +65,7 @@ class SyncService : ConnectionService<SyncService.Binder>() {
private class Task(val repositoryId: Long, val manual: Boolean) private class Task(val repositoryId: Long, val manual: Boolean)
private data class CurrentTask( private data class CurrentTask(
val task: Task?, val disposable: Disposable, 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 } private enum class Started { NO, AUTO, MANUAL }
@ -72,7 +79,7 @@ class SyncService : ConnectionService<SyncService.Binder>() {
enum class SyncRequest { AUTO, MANUAL, FORCE } enum class SyncRequest { AUTO, MANUAL, FORCE }
inner class Binder : android.os.Binder() { inner class Binder : android.os.Binder() {
val finish: Observable<Unit> val finish: SharedFlow<Unit>
get() = finishSubject get() = finishSubject
private fun sync(ids: List<Long>, request: SyncRequest) { private fun sync(ids: List<Long>, request: SyncRequest) {
@ -152,8 +159,6 @@ class SyncService : ConnectionService<SyncService.Binder>() {
private val binder = Binder() private val binder = Binder()
override fun onBind(intent: Intent): Binder = binder override fun onBind(intent: Intent): Binder = binder
private var stateDisposable: Disposable? = null
override fun onCreate() { override fun onCreate() {
super.onCreate() super.onCreate()
@ -170,17 +175,17 @@ class SyncService : ConnectionService<SyncService.Binder>() {
) )
.let(notificationManager::createNotificationChannel) .let(notificationManager::createNotificationChannel)
} }
scope.launch(Dispatchers.Default) {
stateDisposable = stateSubject stateSubject.collect {
.sample(500L, TimeUnit.MILLISECONDS, AndroidSchedulers.mainThread()) publishForegroundState(false,
.subscribe { publishForegroundState(false, it) } it)
}
}
} }
override fun onDestroy() { override fun onDestroy() {
super.onDestroy() super.onDestroy()
scope.cancel()
stateDisposable?.dispose()
stateDisposable = null
cancelTasks { true } cancelTasks { true }
cancelCurrentTask { true } cancelCurrentTask { true }
} }
@ -349,18 +354,20 @@ class SyncService : ConnectionService<SyncService.Binder>() {
lateinit var disposable: Disposable lateinit var disposable: Disposable
disposable = RepositoryUpdater disposable = RepositoryUpdater
.update(this, repository, unstable) { stage, progress, total -> .update(this, repository, unstable) { stage, progress, total ->
if (!disposable.isDisposed) { scope.launch(Dispatchers.Default) {
stateSubject.onNext( if (!disposable.isDisposed) {
State.Syncing( mutableStateSubject.emit(
repository.name, State.Syncing(
stage, repository.name,
progress, stage,
total progress,
total
)
) )
) }
} }
} }
.observeOn(AndroidSchedulers.mainThread()) .observeOn(io())
.subscribe { result, throwable -> .subscribe { result, throwable ->
currentTask = null currentTask = null
throwable?.printStackTrace() throwable?.printStackTrace()
@ -391,7 +398,7 @@ class SyncService : ConnectionService<SyncService.Binder>() {
.toList() .toList()
} }
} }
.subscribeOn(Schedulers.io()) .subscribeOn(io())
.observeOn(AndroidSchedulers.mainThread()) .observeOn(AndroidSchedulers.mainThread())
.subscribe { result, throwable -> .subscribe { result, throwable ->
throwable?.printStackTrace() throwable?.printStackTrace()
@ -404,7 +411,7 @@ class SyncService : ConnectionService<SyncService.Binder>() {
} }
currentTask = CurrentTask(null, disposable, true, State.Finishing) currentTask = CurrentTask(null, disposable, true, State.Finishing)
} else { } else {
finishSubject.onNext(Unit) scope.launch(Dispatchers.Main) { mutableFinishSubject.emit(Unit) }
val needStop = started == Started.MANUAL val needStop = started == Started.MANUAL
started = Started.NO started = Started.NO
if (needStop) { if (needStop) {
@ -472,23 +479,20 @@ class SyncService : ConnectionService<SyncService.Binder>() {
class Job : JobService() { class Job : JobService() {
private var syncParams: JobParameters? = null private var syncParams: JobParameters? = null
private var syncDisposable: Disposable? = null
private val syncConnection = private val syncConnection =
Connection(SyncService::class.java, onBind = { connection, binder -> Connection(SyncService::class.java, onBind = { connection, binder ->
syncDisposable = binder.finish.subscribe { MainScope().launch {
val params = syncParams binder.finish.collect {
if (params != null) { val params = syncParams
syncParams = null if (params != null) {
syncDisposable?.dispose() syncParams = null
syncDisposable = null connection.unbind(this@Job)
connection.unbind(this) jobFinished(params, false)
jobFinished(params, false) }
} }
binder.sync(SyncRequest.AUTO)
} }
binder.sync(SyncRequest.AUTO)
}, onUnbind = { _, binder -> }, onUnbind = { _, binder ->
syncDisposable?.dispose()
syncDisposable = null
binder.cancelAuto() binder.cancelAuto()
val params = syncParams val params = syncParams
if (params != null) { if (params != null) {
@ -505,8 +509,6 @@ class SyncService : ConnectionService<SyncService.Binder>() {
override fun onStopJob(params: JobParameters): Boolean { override fun onStopJob(params: JobParameters): Boolean {
syncParams = null syncParams = null
syncDisposable?.dispose()
syncDisposable = null
val reschedule = syncConnection.binder?.cancelAuto() == true val reschedule = syncConnection.binder?.cancelAuto() == true
syncConnection.unbind(this) syncConnection.unbind(this)
return reschedule return reschedule