Improve: Move Away from RxJava and use coroutines (#NoRxGang)

This commit is contained in:
LooKeR 2021-10-31 19:06:55 +05:30
parent 57c3f78021
commit 5e72165d0b
5 changed files with 66 additions and 65 deletions

View File

@ -18,6 +18,9 @@ import com.looker.droidify.service.Connection
import com.looker.droidify.service.SyncService import com.looker.droidify.service.SyncService
import com.looker.droidify.utility.Utils.toInstalledItem import com.looker.droidify.utility.Utils.toInstalledItem
import com.looker.droidify.utility.extension.android.Android import com.looker.droidify.utility.extension.android.Android
import kotlinx.coroutines.MainScope
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.launch
import java.net.InetSocketAddress import java.net.InetSocketAddress
import java.net.Proxy import java.net.Proxy
@ -83,20 +86,22 @@ class MainApplication : Application(), ImageLoaderFactory {
updateProxy() updateProxy()
var lastAutoSync = Preferences[Preferences.Key.AutoSync] var lastAutoSync = Preferences[Preferences.Key.AutoSync]
var lastUpdateUnstable = Preferences[Preferences.Key.UpdateUnstable] var lastUpdateUnstable = Preferences[Preferences.Key.UpdateUnstable]
Preferences.observable.subscribe { MainScope().launch {
if (it == Preferences.Key.ProxyType || it == Preferences.Key.ProxyHost || it == Preferences.Key.ProxyPort) { Preferences.subject.collect {
updateProxy() if (it == Preferences.Key.ProxyType || it == Preferences.Key.ProxyHost || it == Preferences.Key.ProxyPort) {
} else if (it == Preferences.Key.AutoSync) { updateProxy()
val autoSync = Preferences[Preferences.Key.AutoSync] } else if (it == Preferences.Key.AutoSync) {
if (lastAutoSync != autoSync) { val autoSync = Preferences[Preferences.Key.AutoSync]
lastAutoSync = autoSync if (lastAutoSync != autoSync) {
updateSyncJob(true) lastAutoSync = autoSync
} updateSyncJob(true)
} else if (it == Preferences.Key.UpdateUnstable) { }
val updateUnstable = Preferences[Preferences.Key.UpdateUnstable] } else if (it == Preferences.Key.UpdateUnstable) {
if (lastUpdateUnstable != updateUnstable) { val updateUnstable = Preferences[Preferences.Key.UpdateUnstable]
lastUpdateUnstable = updateUnstable if (lastUpdateUnstable != updateUnstable) {
forceSyncAll() lastUpdateUnstable = updateUnstable
forceSyncAll()
}
} }
} }
} }

View File

@ -6,14 +6,18 @@ import android.content.res.Configuration
import com.looker.droidify.R import com.looker.droidify.R
import com.looker.droidify.entity.ProductItem import com.looker.droidify.entity.ProductItem
import com.looker.droidify.utility.extension.android.Android import com.looker.droidify.utility.extension.android.Android
import io.reactivex.rxjava3.core.Observable import kotlinx.coroutines.Dispatchers
import io.reactivex.rxjava3.subjects.PublishSubject import kotlinx.coroutines.MainScope
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.asSharedFlow
import kotlinx.coroutines.launch
import java.net.Proxy import java.net.Proxy
object Preferences { object Preferences {
private lateinit var preferences: SharedPreferences private lateinit var preferences: SharedPreferences
private val subject = PublishSubject.create<Key<*>>() private val _subject = MutableSharedFlow<Key<*>>()
val subject = _subject.asSharedFlow()
private val keys = sequenceOf( private val keys = sequenceOf(
Key.AutoSync, Key.AutoSync,
@ -31,24 +35,24 @@ object Preferences {
fun init(context: Context) { fun init(context: Context) {
preferences = preferences =
context.getSharedPreferences("${context.packageName}_preferences", Context.MODE_PRIVATE) context.getSharedPreferences("${context.packageName}_preferences",
Context.MODE_PRIVATE)
preferences.registerOnSharedPreferenceChangeListener { _, keyString -> preferences.registerOnSharedPreferenceChangeListener { _, keyString ->
keys[keyString]?.let( MainScope().launch(Dispatchers.IO) {
subject::onNext keys[keyString]?.let {
) _subject.emit(it)
}
}
} }
} }
val observable: Observable<Key<*>>
get() = subject
sealed class Value<T> { sealed class Value<T> {
abstract val value: T abstract val value: T
internal abstract fun get( internal abstract fun get(
preferences: SharedPreferences, preferences: SharedPreferences,
key: String, key: String,
defaultValue: Value<T> defaultValue: Value<T>,
): T ): T
internal abstract fun set(preferences: SharedPreferences, key: String, value: T) internal abstract fun set(preferences: SharedPreferences, key: String, value: T)
@ -57,7 +61,7 @@ object Preferences {
override fun get( override fun get(
preferences: SharedPreferences, preferences: SharedPreferences,
key: String, key: String,
defaultValue: Value<Boolean> defaultValue: Value<Boolean>,
): Boolean { ): Boolean {
return preferences.getBoolean(key, defaultValue.value) return preferences.getBoolean(key, defaultValue.value)
} }
@ -71,7 +75,7 @@ object Preferences {
override fun get( override fun get(
preferences: SharedPreferences, preferences: SharedPreferences,
key: String, key: String,
defaultValue: Value<Int> defaultValue: Value<Int>,
): Int { ): Int {
return preferences.getInt(key, defaultValue.value) return preferences.getInt(key, defaultValue.value)
} }
@ -85,7 +89,7 @@ object Preferences {
override fun get( override fun get(
preferences: SharedPreferences, preferences: SharedPreferences,
key: String, key: String,
defaultValue: Value<String> defaultValue: Value<String>,
): String { ): String {
return preferences.getString(key, defaultValue.value) ?: defaultValue.value return preferences.getString(key, defaultValue.value) ?: defaultValue.value
} }
@ -99,7 +103,7 @@ object Preferences {
override fun get( override fun get(
preferences: SharedPreferences, preferences: SharedPreferences,
key: String, key: String,
defaultValue: Value<T> defaultValue: Value<T>,
): T { ): T {
val value = preferences.getString(key, defaultValue.value.valueString) val value = preferences.getString(key, defaultValue.value.valueString)
return defaultValue.value.values.find { it.valueString == value } return defaultValue.value.values.find { it.valueString == value }

View File

@ -15,6 +15,7 @@ import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import androidx.appcompat.app.AlertDialog import androidx.appcompat.app.AlertDialog
import androidx.fragment.app.DialogFragment import androidx.fragment.app.DialogFragment
import androidx.lifecycle.lifecycleScope
import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.looker.droidify.R import com.looker.droidify.R
import com.looker.droidify.database.Database import com.looker.droidify.database.Database
@ -28,7 +29,6 @@ import com.looker.droidify.utility.Utils
import com.looker.droidify.utility.extension.resources.getColorFromAttr import com.looker.droidify.utility.extension.resources.getColorFromAttr
import com.looker.droidify.utility.extension.text.nullIfEmpty import com.looker.droidify.utility.extension.text.nullIfEmpty
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers
import io.reactivex.rxjava3.core.Observable
import io.reactivex.rxjava3.core.Single import io.reactivex.rxjava3.core.Single
import io.reactivex.rxjava3.disposables.Disposable import io.reactivex.rxjava3.disposables.Disposable
import io.reactivex.rxjava3.schedulers.Schedulers import io.reactivex.rxjava3.schedulers.Schedulers
@ -79,7 +79,6 @@ class EditRepositoryFragment() : ScreenFragment() {
private var layout: Layout? = null private var layout: Layout? = null
private val syncConnection = Connection(SyncService::class.java) private val syncConnection = Connection(SyncService::class.java)
private var repositoriesDisposable: Disposable? = null
private var checkDisposable: Disposable? = null private var checkDisposable: Disposable? = null
private var takenAddresses = emptySet<String>() private var takenAddresses = emptySet<String>()
@ -239,18 +238,13 @@ class EditRepositoryFragment() : ScreenFragment() {
} }
} }
repositoriesDisposable = Observable.just(Unit) lifecycleScope.launchWhenCreated {
.concatWith(Database.observable(Database.Subject.Repositories)) val list = Database.RepositoryAdapter.getAll(null)
.observeOn(Schedulers.io()) takenAddresses = list.asSequence().filter { it.id != repositoryId }
.flatMapSingle { RxUtils.querySingle { Database.RepositoryAdapter.getAll(it) } } .flatMap { (it.mirrors + it.address).asSequence() }
.observeOn(AndroidSchedulers.mainThread()) .map { it.withoutKnownPath }.toSet()
.subscribe { it -> invalidateAddress()
takenAddresses = it.asSequence().filter { it.id != repositoryId } }
.flatMap { (it.mirrors + it.address).asSequence() }
.map { it.withoutKnownPath }.toSet()
invalidateAddress()
}
invalidateAddress() invalidateAddress()
invalidateFingerprint() invalidateFingerprint()
invalidateUsernamePassword() invalidateUsernamePassword()
@ -263,8 +257,6 @@ class EditRepositoryFragment() : ScreenFragment() {
layout = null layout = null
syncConnection.unbind(requireContext()) syncConnection.unbind(requireContext())
repositoriesDisposable?.dispose()
repositoriesDisposable = null
checkDisposable?.dispose() checkDisposable?.dispose()
checkDisposable = null checkDisposable = null
} }
@ -453,7 +445,7 @@ class EditRepositoryFragment() : ScreenFragment() {
private fun onSaveRepositoryProceedInvalidate( private fun onSaveRepositoryProceedInvalidate(
address: String, address: String,
fingerprint: String, fingerprint: String,
authentication: String authentication: String,
) { ) {
val binder = syncConnection.binder val binder = syncConnection.binder
if (binder != null) { if (binder != null) {

View File

@ -15,6 +15,7 @@ import androidx.core.net.toUri
import androidx.core.widget.NestedScrollView import androidx.core.widget.NestedScrollView
import androidx.fragment.app.DialogFragment import androidx.fragment.app.DialogFragment
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import androidx.lifecycle.lifecycleScope
import com.google.android.material.circularreveal.CircularRevealFrameLayout import com.google.android.material.circularreveal.CircularRevealFrameLayout
import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.google.android.material.switchmaterial.SwitchMaterial import com.google.android.material.switchmaterial.SwitchMaterial
@ -26,13 +27,12 @@ import com.looker.droidify.content.Preferences
import com.looker.droidify.databinding.PreferenceItemBinding import com.looker.droidify.databinding.PreferenceItemBinding
import com.looker.droidify.utility.extension.resources.* import com.looker.droidify.utility.extension.resources.*
import com.topjohnwu.superuser.Shell import com.topjohnwu.superuser.Shell
import io.reactivex.rxjava3.disposables.Disposable import kotlinx.coroutines.flow.collect
class SettingsFragment : ScreenFragment() { class SettingsFragment : ScreenFragment() {
private lateinit var preferenceBinding: PreferenceItemBinding private lateinit var preferenceBinding: PreferenceItemBinding
private val preferences = mutableMapOf<Preferences.Key<*>, Preference<*>>() private val preferences = mutableMapOf<Preferences.Key<*>, Preference<*>>()
private var disposable: Disposable? = null
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
@ -136,7 +136,9 @@ class SettingsFragment : ScreenFragment() {
) )
} }
disposable = Preferences.observable.subscribe(this::updatePreference) lifecycleScope.launchWhenStarted {
Preferences.subject.collect { updatePreference(it) }
}
updatePreference(null) updatePreference(null)
} }
@ -167,10 +169,7 @@ class SettingsFragment : ScreenFragment() {
override fun onDestroyView() { override fun onDestroyView() {
super.onDestroyView() super.onDestroyView()
preferences.clear() preferences.clear()
disposable?.dispose()
disposable = null
} }
private fun updatePreference(key: Preferences.Key<*>?) { private fun updatePreference(key: Preferences.Key<*>?) {
@ -199,7 +198,7 @@ class SettingsFragment : ScreenFragment() {
private fun LinearLayoutCompat.addCategory( private fun LinearLayoutCompat.addCategory(
title: String, title: String,
callback: LinearLayoutCompat.() -> Unit callback: LinearLayoutCompat.() -> Unit,
) { ) {
val text = MaterialTextView(context) val text = MaterialTextView(context)
text.typeface = TypefaceExtra.medium text.typeface = TypefaceExtra.medium
@ -217,7 +216,7 @@ class SettingsFragment : ScreenFragment() {
private fun <T> LinearLayoutCompat.addPreference( private fun <T> LinearLayoutCompat.addPreference(
key: Preferences.Key<T>, title: String, key: Preferences.Key<T>, title: String,
summaryProvider: () -> String, dialogProvider: ((Context) -> AlertDialog)? summaryProvider: () -> String, dialogProvider: ((Context) -> AlertDialog)?,
): Preference<T> { ): Preference<T> {
val preference = val preference =
Preference(key, this@SettingsFragment, this, title, summaryProvider, dialogProvider) Preference(key, this@SettingsFragment, this, title, summaryProvider, dialogProvider)
@ -228,7 +227,7 @@ class SettingsFragment : ScreenFragment() {
private fun LinearLayoutCompat.addSwitch( private fun LinearLayoutCompat.addSwitch(
key: Preferences.Key<Boolean>, key: Preferences.Key<Boolean>,
title: String, title: String,
summary: String summary: String,
) { ) {
val preference = addPreference(key, title, { summary }, null) val preference = addPreference(key, title, { summary }, null)
preference.check.visibility = View.VISIBLE preference.check.visibility = View.VISIBLE
@ -238,7 +237,7 @@ class SettingsFragment : ScreenFragment() {
private fun <T> LinearLayoutCompat.addEdit( private fun <T> LinearLayoutCompat.addEdit(
key: Preferences.Key<T>, title: String, valueToString: (T) -> String, key: Preferences.Key<T>, title: String, valueToString: (T) -> String,
stringToValue: (String) -> T?, configureEdit: (TextInputEditText) -> Unit stringToValue: (String) -> T?, configureEdit: (TextInputEditText) -> Unit,
) { ) {
addPreference(key, title, { valueToString(Preferences[key]) }) { it -> addPreference(key, title, { valueToString(Preferences[key]) }) { it ->
val scroll = NestedScrollView(it) val scroll = NestedScrollView(it)
@ -279,7 +278,7 @@ class SettingsFragment : ScreenFragment() {
private fun LinearLayoutCompat.addEditInt( private fun LinearLayoutCompat.addEditInt(
key: Preferences.Key<Int>, key: Preferences.Key<Int>,
title: String, title: String,
range: IntRange? range: IntRange?,
) { ) {
addEdit(key, title, { it.toString() }, { it.toIntOrNull() }) { addEdit(key, title, { it.toString() }, { it.toIntOrNull() }) {
it.inputType = InputType.TYPE_CLASS_NUMBER or InputType.TYPE_NUMBER_FLAG_DECIMAL it.inputType = InputType.TYPE_CLASS_NUMBER or InputType.TYPE_NUMBER_FLAG_DECIMAL
@ -295,7 +294,7 @@ class SettingsFragment : ScreenFragment() {
private fun <T : Preferences.Enumeration<T>> LinearLayoutCompat.addEnumeration( private fun <T : Preferences.Enumeration<T>> LinearLayoutCompat.addEnumeration(
key: Preferences.Key<T>, key: Preferences.Key<T>,
title: String, title: String,
valueToString: (T) -> String valueToString: (T) -> String,
) { ) {
addPreference(key, title, { valueToString(Preferences[key]) }) { addPreference(key, title, { valueToString(Preferences[key]) }) {
val values = key.default.value.values val values = key.default.value.values
@ -319,7 +318,7 @@ class SettingsFragment : ScreenFragment() {
parent: ViewGroup, parent: ViewGroup,
titleText: String, titleText: String,
private val summaryProvider: () -> String, private val summaryProvider: () -> String,
private val dialogProvider: ((Context) -> AlertDialog)? private val dialogProvider: ((Context) -> AlertDialog)?,
) { ) {
val view = parent.inflate(R.layout.preference_item) val view = parent.inflate(R.layout.preference_item)
val title = view.findViewById<MaterialTextView>(R.id.title)!! val title = view.findViewById<MaterialTextView>(R.id.title)!!

View File

@ -8,6 +8,7 @@ import android.view.animation.AccelerateInterpolator
import android.view.animation.DecelerateInterpolator import android.view.animation.DecelerateInterpolator
import androidx.appcompat.widget.SearchView import androidx.appcompat.widget.SearchView
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import androidx.lifecycle.lifecycleScope
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import androidx.viewpager2.adapter.FragmentStateAdapter import androidx.viewpager2.adapter.FragmentStateAdapter
@ -32,6 +33,7 @@ import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers
import io.reactivex.rxjava3.core.Observable 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
import kotlinx.coroutines.flow.collect
import kotlin.math.* import kotlin.math.*
class TabsFragment : ScreenFragment() { class TabsFragment : ScreenFragment() {
@ -88,7 +90,6 @@ class TabsFragment : ScreenFragment() {
} }
}) })
private var sortOrderDisposable: Disposable? = null
private var categoriesDisposable: Disposable? = null private var categoriesDisposable: Disposable? = null
private var repositoriesDisposable: Disposable? = null private var repositoriesDisposable: Disposable? = null
private var sectionsAnimator: ValueAnimator? = null private var sectionsAnimator: ValueAnimator? = null
@ -198,9 +199,11 @@ class TabsFragment : ScreenFragment() {
} }
updateOrder() updateOrder()
sortOrderDisposable = Preferences.observable.subscribe { lifecycleScope.launchWhenStarted {
if (it == Preferences.Key.SortOrder) { Preferences.subject.collect {
updateOrder() if (it == Preferences.Key.SortOrder) {
updateOrder()
}
} }
} }
@ -302,8 +305,6 @@ class TabsFragment : ScreenFragment() {
viewPager = null viewPager = null
syncConnection.unbind(requireContext()) syncConnection.unbind(requireContext())
sortOrderDisposable?.dispose()
sortOrderDisposable = null
categoriesDisposable?.dispose() categoriesDisposable?.dispose()
categoriesDisposable = null categoriesDisposable = null
repositoriesDisposable?.dispose() repositoriesDisposable?.dispose()