From 8e613d03e326adbfa36d72e38cdff021ac806b87 Mon Sep 17 00:00:00 2001 From: arkon Date: Thu, 7 Jan 2021 19:16:26 -0500 Subject: [PATCH] Address coroutine scope leaks in custom views --- .../tachiyomi/ui/browse/source/filter/TextItem.kt | 15 ++++++--------- .../kanade/tachiyomi/ui/library/LibraryAdapter.kt | 2 +- .../tachiyomi/ui/library/LibraryCategoryView.kt | 8 +++++++- .../tachiyomi/widget/DialogCustomDownloadView.kt | 14 ++++---------- .../kanade/tachiyomi/widget/SimpleTextWatcher.kt | 15 +++++++++++++++ 5 files changed, 33 insertions(+), 21 deletions(-) create mode 100644 app/src/main/java/eu/kanade/tachiyomi/widget/SimpleTextWatcher.kt diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/filter/TextItem.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/filter/TextItem.kt index 149114c1d..f55ca009b 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/filter/TextItem.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/filter/TextItem.kt @@ -10,15 +10,10 @@ import eu.davidea.flexibleadapter.items.IFlexible import eu.davidea.viewholders.FlexibleViewHolder import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.source.model.Filter -import kotlinx.coroutines.MainScope -import kotlinx.coroutines.flow.launchIn -import kotlinx.coroutines.flow.onEach -import reactivecircus.flowbinding.android.widget.textChanges +import eu.kanade.tachiyomi.widget.SimpleTextWatcher open class TextItem(val filter: Filter.Text) : AbstractFlexibleItem() { - private val scope = MainScope() - override fun getLayoutRes(): Int { return R.layout.navigation_view_text } @@ -30,9 +25,11 @@ open class TextItem(val filter: Filter.Text) : AbstractFlexibleItem>, holder: Holder, position: Int, payloads: List?) { holder.wrapper.hint = filter.name holder.edit.setText(filter.state) - holder.edit.textChanges() - .onEach { filter.state = it.toString() } - .launchIn(scope) + holder.edit.addTextChangedListener(object : SimpleTextWatcher() { + override fun onTextChanged(text: CharSequence, start: Int, before: Int, count: Int) { + filter.state = text.toString() + } + }) } override fun equals(other: Any?): Boolean { diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryAdapter.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryAdapter.kt index 152a3649c..98c0c8162 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryAdapter.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryAdapter.kt @@ -97,7 +97,7 @@ class LibraryAdapter(private val controller: LibraryController) : RecyclerViewPa fun onDestroy() { for (view in boundViews) { if (view is LibraryCategoryView) { - view.unsubscribe() + view.onDestroy() } } } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryCategoryView.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryCategoryView.kt index e33db2c15..0ac2a8ac2 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryCategoryView.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryCategoryView.kt @@ -20,6 +20,7 @@ import eu.kanade.tachiyomi.util.system.toast import eu.kanade.tachiyomi.util.view.inflate import eu.kanade.tachiyomi.widget.AutofitRecyclerView import kotlinx.coroutines.MainScope +import kotlinx.coroutines.cancel import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach import reactivecircus.flowbinding.recyclerview.scrollStateChanges @@ -155,7 +156,12 @@ class LibraryCategoryView @JvmOverloads constructor(context: Context, attrs: Att unsubscribe() } - fun unsubscribe() { + fun onDestroy() { + unsubscribe() + scope.cancel() + } + + private fun unsubscribe() { subscriptions.clear() } diff --git a/app/src/main/java/eu/kanade/tachiyomi/widget/DialogCustomDownloadView.kt b/app/src/main/java/eu/kanade/tachiyomi/widget/DialogCustomDownloadView.kt index 88cc34a64..44c5f23f0 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/widget/DialogCustomDownloadView.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/widget/DialogCustomDownloadView.kt @@ -7,10 +7,6 @@ import android.view.LayoutInflater import android.view.View import android.widget.LinearLayout import eu.kanade.tachiyomi.databinding.DownloadCustomAmountBinding -import kotlinx.coroutines.MainScope -import kotlinx.coroutines.flow.launchIn -import kotlinx.coroutines.flow.onEach -import reactivecircus.flowbinding.android.widget.textChanges import timber.log.Timber /** @@ -35,8 +31,6 @@ class DialogCustomDownloadView @JvmOverloads constructor(context: Context, attrs */ private var max = 0 - private val scope = MainScope() - private val binding: DownloadCustomAmountBinding init { @@ -71,16 +65,16 @@ class DialogCustomDownloadView @JvmOverloads constructor(context: Context, attrs } // When user inputs custom number set amount equal to input. - binding.myNumber.textChanges() - .onEach { + binding.myNumber.addTextChangedListener(object : SimpleTextWatcher() { + override fun onTextChanged(text: CharSequence, start: Int, before: Int, count: Int) { try { - amount = getAmount(Integer.parseInt(it.toString())) + amount = getAmount(text.toString().toInt()) } catch (error: NumberFormatException) { // Catch NumberFormatException to prevent parse exception when input is empty. Timber.e(error) } } - .launchIn(scope) + }) } /** diff --git a/app/src/main/java/eu/kanade/tachiyomi/widget/SimpleTextWatcher.kt b/app/src/main/java/eu/kanade/tachiyomi/widget/SimpleTextWatcher.kt new file mode 100644 index 000000000..06870c239 --- /dev/null +++ b/app/src/main/java/eu/kanade/tachiyomi/widget/SimpleTextWatcher.kt @@ -0,0 +1,15 @@ +package eu.kanade.tachiyomi.widget + +import android.text.Editable +import android.text.TextWatcher + +open class SimpleTextWatcher : TextWatcher { + override fun beforeTextChanged(text: CharSequence, start: Int, count: Int, after: Int) { + } + + override fun onTextChanged(text: CharSequence, start: Int, before: Int, count: Int) { + } + + override fun afterTextChanged(text: Editable) { + } +}