Using selector for tab icons

This commit is contained in:
Jay 2020-03-15 22:21:14 -07:00
parent 7087e4ce9f
commit cc28ff7a77
13 changed files with 48 additions and 421 deletions

View File

@ -178,6 +178,10 @@ class CatalogueController : NucleusController<CataloguePresenter>(),
}
fun showExtensions() {
ext_bottom_sheet.sheetBehavior?.state = BottomSheetBehavior.STATE_EXPANDED
}
fun toggleExtensions() {
if (ext_bottom_sheet.sheetBehavior?.state != BottomSheetBehavior.STATE_COLLAPSED) {
ext_bottom_sheet.sheetBehavior?.state = BottomSheetBehavior.STATE_COLLAPSED

View File

@ -19,8 +19,11 @@ import uy.kohesive.injekt.api.get
import java.util.concurrent.TimeUnit
import kotlin.coroutines.CoroutineContext
typealias ExtensionTuple
= Triple<List<Extension.Installed>, List<Extension.Untrusted>, List<Extension.Available>>
/**
* Presenter of [ExtensionController].
* Presenter of [ExtensionBottomSheet].
*/
open class ExtensionBottomPresenter(
private val bottomSheet: ExtensionBottomSheet,

View File

@ -1,224 +0,0 @@
package eu.kanade.tachiyomi.ui.extension
import android.view.LayoutInflater
import android.view.Menu
import android.view.MenuInflater
import android.view.MenuItem
import android.view.View
import android.view.ViewGroup
import androidx.appcompat.widget.SearchView
import com.bluelinelabs.conductor.ControllerChangeHandler
import com.bluelinelabs.conductor.ControllerChangeType
import com.bluelinelabs.conductor.RouterTransaction
import com.bluelinelabs.conductor.changehandler.FadeChangeHandler
import com.jakewharton.rxbinding.support.v4.widget.refreshes
import com.jakewharton.rxbinding.support.v7.widget.queryTextChanges
import eu.davidea.flexibleadapter.FlexibleAdapter
import eu.davidea.flexibleadapter.items.IFlexible
import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
import eu.kanade.tachiyomi.data.preference.getOrDefault
import eu.kanade.tachiyomi.extension.ExtensionUpdateJob
import eu.kanade.tachiyomi.extension.model.Extension
import eu.kanade.tachiyomi.ui.base.controller.NucleusController
import eu.kanade.tachiyomi.ui.base.controller.withFadeTransaction
import eu.kanade.tachiyomi.util.view.RecyclerWindowInsetsListener
import eu.kanade.tachiyomi.util.view.scrollViewWith
import kotlinx.android.synthetic.main.extension_controller.*
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
/**
* Controller to manage the catalogues available in the app.
*/
open class ExtensionController : NucleusController<ExtensionPresenter>(),
ExtensionAdapter.OnButtonClickListener,
FlexibleAdapter.OnItemClickListener,
FlexibleAdapter.OnItemLongClickListener,
ExtensionTrustDialog.Listener {
/**
* Adapter containing the list of manga from the catalogue.
*/
private var adapter: FlexibleAdapter<IFlexible<*>>? = null
private var extensions: List<ExtensionItem> = emptyList()
private var query = ""
init {
setHasOptionsMenu(true)
}
override fun getTitle(): String? {
return applicationContext?.getString(R.string.label_extensions)
}
override fun createPresenter(): ExtensionPresenter {
return ExtensionPresenter()
}
override fun inflateView(inflater: LayoutInflater, container: ViewGroup): View {
return inflater.inflate(R.layout.extension_controller, container, false)
}
override fun onViewCreated(view: View) {
super.onViewCreated(view)
//view.applyWindowInsetsForController()
ext_swipe_refresh.isRefreshing = true
ext_swipe_refresh.refreshes().subscribeUntilDestroy {
presenter.findAvailableExtensions()
}
// Initialize adapter, scroll listener and recycler views
adapter = ExtensionAdapter(this)
// Create recycler and set adapter.
ext_recycler.layoutManager = androidx.recyclerview.widget.LinearLayoutManager(view.context)
ext_recycler.adapter = adapter
ext_recycler.addItemDecoration(ExtensionDividerItemDecoration(view.context))
ext_recycler.setOnApplyWindowInsetsListener(RecyclerWindowInsetsListener)
scrollViewWith(ext_recycler, true, ext_swipe_refresh)
}
override fun onDestroyView(view: View) {
adapter = null
super.onDestroyView(view)
}
override fun onOptionsItemSelected(item: MenuItem): Boolean {
when (item.itemId) {
R.id.action_search -> expandActionViewFromInteraction = true
R.id.action_filter -> {
router.pushController((RouterTransaction.with(SettingsExtensionsController()))
.popChangeHandler(SettingsExtensionsFadeChangeHandler())
.pushChangeHandler(FadeChangeHandler()))
}
R.id.action_auto_check -> {
item.isChecked = !item.isChecked
val preferences:PreferencesHelper = Injekt.get()
preferences.automaticExtUpdates().set(item.isChecked)
if (item.isChecked)
ExtensionUpdateJob.setupTask()
else
ExtensionUpdateJob.cancelTask()
}
else -> return super.onOptionsItemSelected(item)
}
return true
}
override fun onChangeStarted(handler: ControllerChangeHandler, type: ControllerChangeType) {
super.onChangeStarted(handler, type)
if (!type.isPush && handler is SettingsExtensionsFadeChangeHandler) {
presenter.findAvailableExtensions()
}
}
override fun onButtonClick(position: Int) {
val extension = (adapter?.getItem(position) as? ExtensionItem)?.extension ?: return
when (extension) {
is Extension.Installed -> {
if (!extension.hasUpdate) {
openDetails(extension)
} else {
presenter.updateExtension(extension)
}
}
is Extension.Available -> {
presenter.installExtension(extension)
}
is Extension.Untrusted -> {
openTrustDialog(extension)
}
}
}
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
inflater.inflate(R.menu.extension_main, menu)
val searchItem = menu.findItem(R.id.action_search)
val searchView = searchItem.actionView as SearchView
searchView.maxWidth = Int.MAX_VALUE
if (query.isNotEmpty()) {
searchItem.expandActionView()
searchView.setQuery(query, true)
searchView.clearFocus()
}
searchView.queryTextChanges()
.filter { router.backstack.lastOrNull()?.controller() == this }
.subscribeUntilDestroy {
query = it.toString()
drawExtensions()
}
// Fixes problem with the overflow icon showing up in lieu of search
searchItem.fixExpand(onExpand = { invalidateMenuOnExpand() })
val autoItem = menu.findItem(R.id.action_auto_check)
val preferences:PreferencesHelper = Injekt.get()
autoItem.isChecked = preferences.automaticExtUpdates().getOrDefault()
}
override fun onItemClick(view: View?, position: Int): Boolean {
val extension = (adapter?.getItem(position) as? ExtensionItem)?.extension ?: return false
if (extension is Extension.Installed) {
openDetails(extension)
} else if (extension is Extension.Untrusted) {
openTrustDialog(extension)
}
return false
}
override fun onItemLongClick(position: Int) {
val extension = (adapter?.getItem(position) as? ExtensionItem)?.extension ?: return
if (extension is Extension.Installed || extension is Extension.Untrusted) {
uninstallExtension(extension.pkgName)
}
}
private fun openDetails(extension: Extension.Installed) {
val controller = ExtensionDetailsController(extension.pkgName)
router.pushController(controller.withFadeTransaction())
}
private fun openTrustDialog(extension: Extension.Untrusted) {
ExtensionTrustDialog(this, extension.signatureHash, extension.pkgName)
.showDialog(router)
}
fun setExtensions(extensions: List<ExtensionItem>) {
ext_swipe_refresh?.isRefreshing = false
this.extensions = extensions
drawExtensions()
}
fun drawExtensions() {
if (!query.isBlank()) {
adapter?.updateDataSet(
extensions.filter {
it.extension.name.contains(query, ignoreCase = true)
})
} else {
adapter?.updateDataSet(extensions)
}
}
fun downloadUpdate(item: ExtensionItem) {
adapter?.updateItem(item, item.installStep)
}
override fun trustSignature(signatureHash: String) {
presenter.trustSignature(signatureHash)
}
override fun uninstallExtension(pkgName: String) {
presenter.uninstallExtension(pkgName)
}
class SettingsExtensionsFadeChangeHandler : FadeChangeHandler()
}

View File

@ -1,148 +0,0 @@
package eu.kanade.tachiyomi.ui.extension
import android.app.Application
import android.os.Bundle
import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
import eu.kanade.tachiyomi.data.preference.getOrDefault
import eu.kanade.tachiyomi.extension.ExtensionManager
import eu.kanade.tachiyomi.extension.model.Extension
import eu.kanade.tachiyomi.extension.model.InstallStep
import eu.kanade.tachiyomi.ui.base.presenter.BasePresenter
import eu.kanade.tachiyomi.util.system.LocaleHelper
import rx.Observable
import rx.Subscription
import rx.android.schedulers.AndroidSchedulers
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
import java.util.concurrent.TimeUnit
typealias ExtensionTuple
= Triple<List<Extension.Installed>, List<Extension.Untrusted>, List<Extension.Available>>
/**
* Presenter of [ExtensionController].
*/
open class ExtensionPresenter(
private val extensionManager: ExtensionManager = Injekt.get(),
private val preferences: PreferencesHelper = Injekt.get()
) : BasePresenter<ExtensionController>() {
private var extensions = emptyList<ExtensionItem>()
private var currentDownloads = hashMapOf<String, InstallStep>()
override fun onCreate(savedState: Bundle?) {
super.onCreate(savedState)
extensionManager.findAvailableExtensions()
bindToExtensionsObservable()
}
private fun bindToExtensionsObservable(): Subscription {
val installedObservable = extensionManager.getInstalledExtensionsObservable()
val untrustedObservable = extensionManager.getUntrustedExtensionsObservable()
val availableObservable = extensionManager.getAvailableExtensionsObservable()
.startWith(emptyList<Extension.Available>())
return Observable.combineLatest(installedObservable, untrustedObservable, availableObservable)
{ installed, untrusted, available -> Triple(installed, untrusted, available) }
.debounce(100, TimeUnit.MILLISECONDS)
.map(::toItems)
.observeOn(AndroidSchedulers.mainThread())
.subscribeLatestCache({ view, _ -> view.setExtensions(extensions) })
}
@Synchronized
private fun toItems(tuple: ExtensionTuple): List<ExtensionItem> {
val context = Injekt.get<Application>()
val activeLangs = preferences.enabledLanguages().getOrDefault()
val (installed, untrusted, available) = tuple
val items = mutableListOf<ExtensionItem>()
val installedSorted = installed.sortedWith(compareBy({ !it.hasUpdate }, { !it.isObsolete }, { it.pkgName }))
val untrustedSorted = untrusted.sortedBy { it.pkgName }
val availableSorted = available
// Filter out already installed extensions and disabled languages
.filter { avail -> installed.none { it.pkgName == avail.pkgName }
&& untrusted.none { it.pkgName == avail.pkgName }
&& (avail.lang in activeLangs || avail.lang == "all")}
.sortedBy { it.pkgName }
if (installedSorted.isNotEmpty() || untrustedSorted.isNotEmpty()) {
val header = ExtensionGroupItem(context.getString(R.string.ext_installed), installedSorted.size + untrustedSorted.size)
items += installedSorted.map { extension ->
ExtensionItem(extension, header, currentDownloads[extension.pkgName])
}
items += untrustedSorted.map { extension ->
ExtensionItem(extension, header)
}
}
if (availableSorted.isNotEmpty()) {
val availableGroupedByLang = availableSorted
.groupBy { LocaleHelper.getDisplayName(it.lang, context) }
.toSortedMap()
availableGroupedByLang
.forEach {
val header = ExtensionGroupItem(it.key, it.value.size)
items += it.value.map { extension ->
ExtensionItem(extension, header, currentDownloads[extension.pkgName])
}
}
}
this.extensions = items
return items
}
@Synchronized
private fun updateInstallStep(extension: Extension, state: InstallStep): ExtensionItem? {
val extensions = extensions.toMutableList()
val position = extensions.indexOfFirst { it.extension.pkgName == extension.pkgName }
return if (position != -1) {
val item = extensions[position].copy(installStep = state)
extensions[position] = item
this.extensions = extensions
item
} else {
null
}
}
fun installExtension(extension: Extension.Available) {
extensionManager.installExtension(extension).subscribeToInstallUpdate(extension)
}
fun updateExtension(extension: Extension.Installed) {
extensionManager.updateExtension(extension).subscribeToInstallUpdate(extension)
}
private fun Observable<InstallStep>.subscribeToInstallUpdate(extension: Extension) {
this.doOnNext { currentDownloads[extension.pkgName] = it }
.doOnUnsubscribe { currentDownloads.remove(extension.pkgName) }
.map { state -> updateInstallStep(extension, state) }
.subscribeWithView({ view, item ->
if (item != null) {
view.downloadUpdate(item)
}
})
}
fun uninstallExtension(pkgName: String) {
extensionManager.uninstallExtension(pkgName)
}
fun findAvailableExtensions() {
extensionManager.findAvailableExtensions()
}
fun trustSignature(signatureHash: String) {
extensionManager.trustSignature(signatureHash)
}
}

View File

@ -300,7 +300,7 @@ class LibraryListController(bundle: Bundle? = null) : LibraryController(bundle),
})
recycler.setHasFixedSize(true)
recycler.adapter = adapter
//adapter.fastScroller = fast_scroller
adapter.fastScroller = fast_scroller
recycler.addOnScrollListener(scrollListener)
val tv = TypedValue()

View File

@ -50,7 +50,6 @@ import eu.kanade.tachiyomi.ui.base.controller.withFadeTransaction
import eu.kanade.tachiyomi.ui.catalogue.CatalogueController
import eu.kanade.tachiyomi.ui.catalogue.global_search.CatalogueSearchController
import eu.kanade.tachiyomi.ui.download.DownloadController
import eu.kanade.tachiyomi.ui.extension.ExtensionController
import eu.kanade.tachiyomi.ui.library.LibraryController
import eu.kanade.tachiyomi.ui.library.LibraryListController
import eu.kanade.tachiyomi.ui.manga.MangaDetailsController
@ -164,7 +163,6 @@ open class MainActivity : BaseActivity(), DownloadServiceListener {
setRoot(RecentlyReadController(), id)
}
R.id.nav_catalogues -> setRoot(CatalogueController(), id)
//R.id.nav_settings -> setRoot(SettingsMainController(), id)
}
}
else if (currentRoot.tag()?.toIntOrNull() == id) {
@ -175,6 +173,7 @@ open class MainActivity : BaseActivity(), DownloadServiceListener {
if (!showRecents) setRoot(RecentChaptersController(), id)
else setRoot(RecentlyReadController(), id)
preferences.showRecentUpdates().set(!showRecents)
updateRecentsIcon()
}
R.id.nav_library -> {
val controller =
@ -189,13 +188,11 @@ open class MainActivity : BaseActivity(), DownloadServiceListener {
}
}
}
updateIcons(id)
true
}
val container: ViewGroup = findViewById(R.id.controller_container)
val content: ViewGroup = findViewById(R.id.main_content)
//val dwawerContainer: ViewGroup = findViewById(R.id.drawer_container)
DownloadService.addListener(this)
content.systemUiVisibility = View.SYSTEM_UI_FLAG_LAYOUT_STABLE or
View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN or
@ -203,9 +200,7 @@ open class MainActivity : BaseActivity(), DownloadServiceListener {
container.systemUiVisibility = View.SYSTEM_UI_FLAG_LAYOUT_STABLE or
View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN or
View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
/*dwawerContainer.systemUiVisibility = View.SYSTEM_UI_FLAG_LAYOUT_STABLE or
View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN or
View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION*/
updateRecentsIcon()
content.viewTreeObserver.addOnGlobalLayoutListener {
val heightDiff: Int = content.rootView.height - content.height
if (heightDiff > 200 &&
@ -368,31 +363,11 @@ open class MainActivity : BaseActivity(), DownloadServiceListener {
setExtensionsBadge()
}
override fun onRestoreInstanceState(savedInstanceState: Bundle) {
super.onRestoreInstanceState(savedInstanceState)
updateIcons(bottom_nav.selectedItemId)
}
fun updateIcons(id: Int) {
bottom_nav.menu.findItem(R.id.nav_library).icon = AppCompatResources.getDrawable(
this, if (id == R.id.nav_library) R.drawable.library_24dp
else R.drawable.library_outline_24dp
)
bottom_nav.menu.findItem(R.id.nav_recents).icon = AppCompatResources.getDrawable(
this, if (id == R.id.nav_recents) {
if (preferences.showRecentUpdates().getOrDefault()) R.drawable.recent_updates_24dp
else R.drawable.recent_read_24dp
} else {
if (preferences.showRecentUpdates()
.getOrDefault()
) R.drawable.recent_updates_outline_24dp
else R.drawable.recent_read_outline_24dp
}
)
bottom_nav.menu.findItem(R.id.nav_catalogues).icon = AppCompatResources.getDrawable(
this, if (id == R.id.nav_catalogues) R.drawable.browse_24dp
else R.drawable.browse_outline_24dp
)
fun updateRecentsIcon() {
bottom_nav.menu.findItem(R.id.nav_recents).icon =
AppCompatResources.getDrawable(this,
if (preferences.showRecentUpdates().getOrDefault()) R.drawable.recent_updates_selector_24dp
else R.drawable.recent_read_selector_24dp)
}
override fun startSupportActionMode(callback: androidx.appcompat.view.ActionMode.Callback): androidx.appcompat.view.ActionMode? {
@ -466,21 +441,18 @@ open class MainActivity : BaseActivity(), DownloadServiceListener {
SHORTCUT_RECENTLY_UPDATED, SHORTCUT_RECENTLY_READ -> {
preferences.showRecentUpdates().set(intent.action == SHORTCUT_RECENTLY_UPDATED)
bottom_nav.selectedItemId = R.id.nav_recents
updateRecentsIcon()
}
SHORTCUT_CATALOGUES -> bottom_nav.selectedItemId = R.id.nav_catalogues
SHORTCUT_EXTENSIONS -> {
if (router.backstack.none { it.controller() is ExtensionController }) {
if (router.backstack.isEmpty()) {
bottom_nav.selectedItemId = R.id.nav_library
router.pushController(
RouterTransaction.with(ExtensionController()).pushChangeHandler(
SimpleSwapChangeHandler()
).popChangeHandler(FadeChangeHandler())
)
} else {
router.pushController(ExtensionController().withFadeTransaction())
bottom_nav.selectedItemId = R.id.nav_catalogues
bottom_nav.post {
val controller =
router.backstack.firstOrNull()?.controller() as? CatalogueController
controller?.showExtensions()
}
}
}
}
SHORTCUT_MANGA -> {
val extras = intent.extras ?: return false

View File

@ -385,7 +385,7 @@ class RecentChaptersController : NucleusController<RecentChaptersPresenter>(),
router.setRoot(
RecentlyReadController().withFadeTransaction().tag(R.id.nav_recents.toString()))
Injekt.get<PreferencesHelper>().showRecentUpdates().set(false)
(activity as? MainActivity)?.updateIcons(R.id.nav_recents)
(activity as? MainActivity)?.updateRecentsIcon()
}
}
return super.onOptionsItemSelected(item)

View File

@ -239,7 +239,7 @@ class RecentlyReadController(bundle: Bundle? = null) : BaseController(bundle),
router.setRoot(
RecentChaptersController().withFadeTransaction().tag(R.id.nav_recents.toString()))
Injekt.get<PreferencesHelper>().showRecentUpdates().set(true)
(activity as? MainActivity)?.updateIcons(R.id.nav_recents)
(activity as? MainActivity)?.updateRecentsIcon()
}
}
return super.onOptionsItemSelected(item)

View File

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@drawable/browse_24dp" android:state_checked="true" />
<item android:drawable="@drawable/browse_outline_24dp" android:state_checked="false" />
</selector>

View File

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@drawable/library_24dp" android:state_checked="true" />
<item android:drawable="@drawable/library_outline_24dp" android:state_checked="false" />
</selector>

View File

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@drawable/recent_read_24dp" android:state_checked="true" />
<item android:drawable="@drawable/recent_read_outline_24dp" android:state_checked="false" />
</selector>

View File

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@drawable/recent_updates_24dp" android:state_checked="true" />
<item android:drawable="@drawable/recent_updates_outline_24dp" android:state_checked="false" />
</selector>

View File

@ -5,15 +5,15 @@
<item
android:checked="true"
android:id="@+id/nav_library"
android:icon="@drawable/library_outline_24dp"
android:icon="@drawable/library_selector_24dp"
android:title="@string/label_library" />
<item
android:id="@+id/nav_recents"
android:icon="@drawable/recent_updates_outline_24dp"
android:icon="@drawable/recent_updates_selector_24dp"
android:title="@string/short_recents" />
<item
android:id="@+id/nav_catalogues"
android:icon="@drawable/browse_outline_24dp"
android:icon="@drawable/browse_selector_24dp"
android:title="@string/short_catalogues" />
</group>
</menu>