Reduce stutter when entering Browse screen (#6435)

* More coil

* ExtensionController: Drop first text change event

* Browse-Source: Remove unnecessary load

* ExtensionPresenter: Increase debounce timeout

To avoid heavy list reload during first enter animation
This commit is contained in:
Ivan Iskandar 2022-01-09 00:55:22 +07:00 committed by GitHub
parent b8f7653fb2
commit 78a261f5d3
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 28 additions and 21 deletions

View file

@ -1,5 +1,6 @@
package eu.kanade.tachiyomi.extension.model package eu.kanade.tachiyomi.extension.model
import android.graphics.drawable.Drawable
import eu.kanade.tachiyomi.source.Source import eu.kanade.tachiyomi.source.Source
sealed class Extension { sealed class Extension {
@ -20,6 +21,7 @@ sealed class Extension {
override val isNsfw: Boolean, override val isNsfw: Boolean,
val pkgFactory: String?, val pkgFactory: String?,
val sources: List<Source>, val sources: List<Source>,
val icon: Drawable?,
val hasUpdate: Boolean = false, val hasUpdate: Boolean = false,
val isObsolete: Boolean = false, val isObsolete: Boolean = false,
val isUnofficial: Boolean = false val isUnofficial: Boolean = false

View file

@ -13,6 +13,7 @@ import eu.kanade.tachiyomi.source.CatalogueSource
import eu.kanade.tachiyomi.source.Source import eu.kanade.tachiyomi.source.Source
import eu.kanade.tachiyomi.source.SourceFactory import eu.kanade.tachiyomi.source.SourceFactory
import eu.kanade.tachiyomi.util.lang.Hash import eu.kanade.tachiyomi.util.lang.Hash
import eu.kanade.tachiyomi.util.system.getApplicationIcon
import eu.kanade.tachiyomi.util.system.logcat import eu.kanade.tachiyomi.util.system.logcat
import kotlinx.coroutines.async import kotlinx.coroutines.async
import kotlinx.coroutines.runBlocking import kotlinx.coroutines.runBlocking
@ -181,7 +182,8 @@ internal object ExtensionLoader {
isNsfw, isNsfw,
sources = sources, sources = sources,
pkgFactory = appInfo.metaData.getString(METADATA_SOURCE_FACTORY), pkgFactory = appInfo.metaData.getString(METADATA_SOURCE_FACTORY),
isUnofficial = signatureHash != officialSignature isUnofficial = signatureHash != officialSignature,
icon = context.getApplicationIcon(pkgName)
) )
return LoadResult.Success(extension) return LoadResult.Success(extension)
} }

View file

@ -20,6 +20,7 @@ import eu.kanade.tachiyomi.ui.base.controller.NucleusController
import eu.kanade.tachiyomi.ui.base.controller.withFadeTransaction import eu.kanade.tachiyomi.ui.base.controller.withFadeTransaction
import eu.kanade.tachiyomi.ui.browse.BrowseController import eu.kanade.tachiyomi.ui.browse.BrowseController
import eu.kanade.tachiyomi.ui.browse.extension.details.ExtensionDetailsController import eu.kanade.tachiyomi.ui.browse.extension.details.ExtensionDetailsController
import kotlinx.coroutines.flow.drop
import kotlinx.coroutines.flow.filter import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.onEach
@ -143,6 +144,7 @@ open class ExtensionController :
} }
searchView.queryTextChanges() searchView.queryTextChanges()
.drop(1) // Drop first event after subscribed
.filter { router.backstack.lastOrNull()?.controller == this } .filter { router.backstack.lastOrNull()?.controller == this }
.onEach { .onEach {
query = it.toString() query = it.toString()

View file

@ -42,8 +42,8 @@ class ExtensionHolder(view: View, val adapter: ExtensionAdapter) :
binding.icon.clear() binding.icon.clear()
if (extension is Extension.Available) { if (extension is Extension.Available) {
binding.icon.load(extension.iconUrl) binding.icon.load(extension.iconUrl)
} else { } else if (extension is Extension.Installed) {
extension.getApplicationIcon(itemView.context)?.let { binding.icon.setImageDrawable(it) } binding.icon.load(extension.icon)
} }
bindButtons(item) bindButtons(item)
} }

View file

@ -47,7 +47,7 @@ open class ExtensionPresenter(
.startWith(emptyList<Extension.Available>()) .startWith(emptyList<Extension.Available>())
return Observable.combineLatest(installedObservable, untrustedObservable, availableObservable) { installed, untrusted, available -> Triple(installed, untrusted, available) } return Observable.combineLatest(installedObservable, untrustedObservable, availableObservable) { installed, untrusted, available -> Triple(installed, untrusted, available) }
.debounce(100, TimeUnit.MILLISECONDS) .debounce(500, TimeUnit.MILLISECONDS)
.map(::toItems) .map(::toItems)
.observeOn(AndroidSchedulers.mainThread()) .observeOn(AndroidSchedulers.mainThread())
.subscribeLatestCache({ view, _ -> view.setExtensions(extensions) }) .subscribeLatestCache({ view, _ -> view.setExtensions(extensions) })

View file

@ -2,6 +2,7 @@ package eu.kanade.tachiyomi.ui.browse.migration.sources
import android.view.View import android.view.View
import androidx.core.view.isVisible import androidx.core.view.isVisible
import coil.load
import eu.davidea.viewholders.FlexibleViewHolder import eu.davidea.viewholders.FlexibleViewHolder
import eu.kanade.tachiyomi.databinding.SourceMainControllerItemBinding import eu.kanade.tachiyomi.databinding.SourceMainControllerItemBinding
import eu.kanade.tachiyomi.source.icon import eu.kanade.tachiyomi.source.icon
@ -20,7 +21,7 @@ class SourceHolder(view: View, val adapter: SourceAdapter) :
binding.subtitle.text = LocaleHelper.getDisplayName(source.lang) binding.subtitle.text = LocaleHelper.getDisplayName(source.lang)
itemView.post { itemView.post {
binding.image.setImageDrawable(source.icon()) binding.image.load(source.icon())
} }
} }
} }

View file

@ -92,6 +92,7 @@ class SourceController :
// Update list on extension changes (e.g. new installation) // Update list on extension changes (e.g. new installation)
(parentController as BrowseController).extensionListUpdateRelay (parentController as BrowseController).extensionListUpdateRelay
.skip(1) // Skip first update when ExtensionController created
.subscribeUntilDestroy { .subscribeUntilDestroy {
presenter.updateSources() presenter.updateSources()
} }

View file

@ -2,6 +2,7 @@ package eu.kanade.tachiyomi.ui.browse.source
import android.view.View import android.view.View
import androidx.core.view.isVisible import androidx.core.view.isVisible
import coil.load
import eu.davidea.viewholders.FlexibleViewHolder import eu.davidea.viewholders.FlexibleViewHolder
import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.databinding.SourceMainControllerItemBinding import eu.kanade.tachiyomi.databinding.SourceMainControllerItemBinding
@ -10,7 +11,7 @@ import eu.kanade.tachiyomi.source.icon
import eu.kanade.tachiyomi.util.system.LocaleHelper import eu.kanade.tachiyomi.util.system.LocaleHelper
import eu.kanade.tachiyomi.util.view.setVectorCompat import eu.kanade.tachiyomi.util.view.setVectorCompat
class SourceHolder(private val view: View, val adapter: SourceAdapter) : class SourceHolder(view: View, val adapter: SourceAdapter) :
FlexibleViewHolder(view, adapter) { FlexibleViewHolder(view, adapter) {
private val binding = SourceMainControllerItemBinding.bind(view) private val binding = SourceMainControllerItemBinding.bind(view)
@ -33,12 +34,10 @@ class SourceHolder(private val view: View, val adapter: SourceAdapter) :
binding.subtitle.text = LocaleHelper.getDisplayName(source.lang) binding.subtitle.text = LocaleHelper.getDisplayName(source.lang)
// Set source icon // Set source icon
itemView.post {
val icon = source.icon() val icon = source.icon()
when { when {
icon != null -> binding.image.setImageDrawable(icon) icon != null -> binding.image.load(icon)
item.source.id == LocalSource.ID -> binding.image.setImageResource(R.mipmap.ic_local_source) item.source.id == LocalSource.ID -> binding.image.load(R.mipmap.ic_local_source)
}
} }
binding.sourceLatest.isVisible = source.supportsLatest binding.sourceLatest.isVisible = source.supportsLatest

View file

@ -1,6 +1,5 @@
package eu.kanade.tachiyomi.ui.browse.source package eu.kanade.tachiyomi.ui.browse.source
import android.os.Bundle
import eu.kanade.tachiyomi.data.preference.PreferencesHelper import eu.kanade.tachiyomi.data.preference.PreferencesHelper
import eu.kanade.tachiyomi.source.CatalogueSource import eu.kanade.tachiyomi.source.CatalogueSource
import eu.kanade.tachiyomi.source.LocalSource import eu.kanade.tachiyomi.source.LocalSource
@ -37,14 +36,6 @@ class SourcePresenter(
*/ */
private var sourceSubscription: Subscription? = null private var sourceSubscription: Subscription? = null
override fun onCreate(savedState: Bundle?) {
super.onCreate(savedState)
// Load enabled and last used sources
loadSources()
loadLastUsedSource()
}
/** /**
* Unsubscribe and create a new subscription to fetch enabled sources. * Unsubscribe and create a new subscription to fetch enabled sources.
*/ */

View file

@ -14,6 +14,7 @@ import android.content.pm.PackageManager
import android.content.res.Configuration import android.content.res.Configuration
import android.content.res.Resources import android.content.res.Resources
import android.graphics.Color import android.graphics.Color
import android.graphics.drawable.Drawable
import android.net.ConnectivityManager import android.net.ConnectivityManager
import android.net.NetworkCapabilities import android.net.NetworkCapabilities
import android.net.Uri import android.net.Uri
@ -418,3 +419,11 @@ fun Context.isPackageInstalled(packageName: String): Boolean {
false false
} }
} }
fun Context.getApplicationIcon(pkgName: String): Drawable? {
return try {
packageManager.getApplicationIcon(pkgName)
} catch (e: PackageManager.NameNotFoundException) {
null
}
}