Clean up base classes
Should be able to throw away some of the search controller stuff after Global Search is migrated
This commit is contained in:
parent
7ec822503a
commit
0225711f6f
11 changed files with 42 additions and 137 deletions
|
@ -2,7 +2,6 @@ package eu.kanade.tachiyomi.ui.base.controller
|
||||||
|
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.view.MenuItem
|
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
|
@ -75,60 +74,10 @@ abstract class BaseController<VB : ViewBinding>(bundle: Bundle? = null) : Contro
|
||||||
}
|
}
|
||||||
|
|
||||||
fun setTitle(title: String? = null) {
|
fun setTitle(title: String? = null) {
|
||||||
var parentController = parentController
|
|
||||||
while (parentController != null) {
|
|
||||||
if (parentController is BaseController<*> && parentController.getTitle() != null) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
parentController = parentController.parentController
|
|
||||||
}
|
|
||||||
|
|
||||||
(activity as? AppCompatActivity)?.supportActionBar?.title = title ?: getTitle()
|
(activity as? AppCompatActivity)?.supportActionBar?.title = title ?: getTitle()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun Controller.instance(): String {
|
private fun Controller.instance(): String {
|
||||||
return "${javaClass.simpleName}@${Integer.toHexString(hashCode())}"
|
return "${javaClass.simpleName}@${Integer.toHexString(hashCode())}"
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Workaround for buggy menu item layout after expanding/collapsing an expandable item like a SearchView.
|
|
||||||
* This method should be removed when fixed upstream.
|
|
||||||
* Issue link: https://issuetracker.google.com/issues/37657375
|
|
||||||
*/
|
|
||||||
var expandActionViewFromInteraction = false
|
|
||||||
|
|
||||||
fun MenuItem.fixExpand(onExpand: ((MenuItem) -> Boolean)? = null, onCollapse: ((MenuItem) -> Boolean)? = null) {
|
|
||||||
setOnActionExpandListener(
|
|
||||||
object : MenuItem.OnActionExpandListener {
|
|
||||||
override fun onMenuItemActionExpand(item: MenuItem): Boolean {
|
|
||||||
return onExpand?.invoke(item) ?: true
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onMenuItemActionCollapse(item: MenuItem): Boolean {
|
|
||||||
activity?.invalidateOptionsMenu()
|
|
||||||
|
|
||||||
return onCollapse?.invoke(item) ?: true
|
|
||||||
}
|
|
||||||
},
|
|
||||||
)
|
|
||||||
|
|
||||||
if (expandActionViewFromInteraction) {
|
|
||||||
expandActionViewFromInteraction = false
|
|
||||||
expandActionView()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Workaround for menu items not disappearing when expanding an expandable item like a SearchView.
|
|
||||||
* [expandActionViewFromInteraction] should be set to true in [onOptionsItemSelected] when the expandable item is selected
|
|
||||||
* This method should be called as part of [MenuItem.OnActionExpandListener.onMenuItemActionExpand]
|
|
||||||
*/
|
|
||||||
open fun invalidateMenuOnExpand(): Boolean {
|
|
||||||
return if (expandActionViewFromInteraction) {
|
|
||||||
activity?.invalidateOptionsMenu()
|
|
||||||
false
|
|
||||||
} else {
|
|
||||||
true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,7 +9,7 @@ import nucleus.presenter.Presenter
|
||||||
|
|
||||||
@Suppress("LeakingThis")
|
@Suppress("LeakingThis")
|
||||||
abstract class NucleusController<VB : ViewBinding, P : Presenter<*>>(val bundle: Bundle? = null) :
|
abstract class NucleusController<VB : ViewBinding, P : Presenter<*>>(val bundle: Bundle? = null) :
|
||||||
RxController<VB>(bundle),
|
BaseController<VB>(bundle),
|
||||||
PresenterFactory<P> {
|
PresenterFactory<P> {
|
||||||
|
|
||||||
private val delegate = NucleusConductorDelegate(this)
|
private val delegate = NucleusConductorDelegate(this)
|
||||||
|
|
|
@ -1,31 +0,0 @@
|
||||||
package eu.kanade.tachiyomi.ui.base.controller
|
|
||||||
|
|
||||||
import android.os.Bundle
|
|
||||||
import android.view.View
|
|
||||||
import androidx.annotation.CallSuper
|
|
||||||
import androidx.viewbinding.ViewBinding
|
|
||||||
import rx.Observable
|
|
||||||
import rx.Subscription
|
|
||||||
import rx.subscriptions.CompositeSubscription
|
|
||||||
|
|
||||||
abstract class RxController<VB : ViewBinding>(bundle: Bundle? = null) : BaseController<VB>(bundle) {
|
|
||||||
|
|
||||||
private var untilDestroySubscriptions = CompositeSubscription()
|
|
||||||
|
|
||||||
@CallSuper
|
|
||||||
override fun onViewCreated(view: View) {
|
|
||||||
if (untilDestroySubscriptions.isUnsubscribed) {
|
|
||||||
untilDestroySubscriptions = CompositeSubscription()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@CallSuper
|
|
||||||
override fun onDestroyView(view: View) {
|
|
||||||
super.onDestroyView(view)
|
|
||||||
untilDestroySubscriptions.unsubscribe()
|
|
||||||
}
|
|
||||||
|
|
||||||
fun <T> Observable<T>.subscribeUntilDestroy(onNext: (T) -> Unit): Subscription {
|
|
||||||
return subscribe(onNext).also { untilDestroySubscriptions.add(it) }
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -6,7 +6,6 @@ import android.text.style.CharacterStyle
|
||||||
import android.view.Menu
|
import android.view.Menu
|
||||||
import android.view.MenuInflater
|
import android.view.MenuInflater
|
||||||
import android.view.MenuItem
|
import android.view.MenuItem
|
||||||
import androidx.annotation.StringRes
|
|
||||||
import androidx.appcompat.widget.SearchView
|
import androidx.appcompat.widget.SearchView
|
||||||
import androidx.core.text.getSpans
|
import androidx.core.text.getSpans
|
||||||
import androidx.core.widget.doAfterTextChanged
|
import androidx.core.widget.doAfterTextChanged
|
||||||
|
@ -43,10 +42,7 @@ abstract class SearchableNucleusController<VB : ViewBinding, P : BasePresenter<*
|
||||||
inflater: MenuInflater,
|
inflater: MenuInflater,
|
||||||
menuId: Int,
|
menuId: Int,
|
||||||
searchItemId: Int,
|
searchItemId: Int,
|
||||||
@StringRes queryHint: Int? = null,
|
|
||||||
restoreCurrentQuery: Boolean = true,
|
|
||||||
) {
|
) {
|
||||||
// Inflate menu
|
|
||||||
inflater.inflate(menuId, menu)
|
inflater.inflate(menuId, menu)
|
||||||
|
|
||||||
// Initialize search option.
|
// Initialize search option.
|
||||||
|
@ -93,21 +89,6 @@ abstract class SearchableNucleusController<VB : ViewBinding, P : BasePresenter<*
|
||||||
searchItem.expandActionView()
|
searchItem.expandActionView()
|
||||||
searchView.setQuery(nonSubmittedQuery, false)
|
searchView.setQuery(nonSubmittedQuery, false)
|
||||||
onSearchViewQueryTextChange(nonSubmittedQuery)
|
onSearchViewQueryTextChange(nonSubmittedQuery)
|
||||||
} else {
|
|
||||||
if (queryHint != null) {
|
|
||||||
searchView.queryHint = applicationContext?.getString(queryHint)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (restoreCurrentQuery) {
|
|
||||||
// Restoring a query the user had submitted
|
|
||||||
if (query.isNotBlank()) {
|
|
||||||
searchItem.expandActionView()
|
|
||||||
searchView.setQuery(query, true)
|
|
||||||
searchView.clearFocus()
|
|
||||||
onSearchViewQueryTextChange(query)
|
|
||||||
onSearchViewQueryTextSubmit(query)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Workaround for weird behavior where searchView gets empty text change despite
|
// Workaround for weird behavior where searchView gets empty text change despite
|
||||||
|
@ -190,12 +171,40 @@ abstract class SearchableNucleusController<VB : ViewBinding, P : BasePresenter<*
|
||||||
protected open fun onSearchMenuItemActionCollapse(item: MenuItem?) {
|
protected open fun onSearchMenuItemActionCollapse(item: MenuItem?) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Workaround for buggy menu item layout after expanding/collapsing an expandable item like a SearchView.
|
||||||
|
* This method should be removed when fixed upstream.
|
||||||
|
* Issue link: https://issuetracker.google.com/issues/37657375
|
||||||
|
*/
|
||||||
|
private var expandActionViewFromInteraction = false
|
||||||
|
|
||||||
|
private fun MenuItem.fixExpand(onExpand: ((MenuItem) -> Boolean)? = null, onCollapse: ((MenuItem) -> Boolean)? = null) {
|
||||||
|
setOnActionExpandListener(
|
||||||
|
object : MenuItem.OnActionExpandListener {
|
||||||
|
override fun onMenuItemActionExpand(item: MenuItem): Boolean {
|
||||||
|
return onExpand?.invoke(item) ?: true
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onMenuItemActionCollapse(item: MenuItem): Boolean {
|
||||||
|
activity?.invalidateOptionsMenu()
|
||||||
|
|
||||||
|
return onCollapse?.invoke(item) ?: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
if (expandActionViewFromInteraction) {
|
||||||
|
expandActionViewFromInteraction = false
|
||||||
|
expandActionView()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* During the conversion to SearchableNucleusController (after which I plan to merge its code
|
* During the conversion to SearchableNucleusController (after which I plan to merge its code
|
||||||
* into BaseController) this addresses an issue where the searchView.onTextFocus event is not
|
* into BaseController) this addresses an issue where the searchView.onTextFocus event is not
|
||||||
* triggered
|
* triggered
|
||||||
*/
|
*/
|
||||||
override fun invalidateMenuOnExpand(): Boolean {
|
private fun invalidateMenuOnExpand(): Boolean {
|
||||||
return if (expandActionViewFromInteraction) {
|
return if (expandActionViewFromInteraction) {
|
||||||
activity?.invalidateOptionsMenu()
|
activity?.invalidateOptionsMenu()
|
||||||
setCurrentSearchViewState(SearchViewState.FOCUSED) // we are technically focused here
|
setCurrentSearchViewState(SearchViewState.FOCUSED) // we are technically focused here
|
||||||
|
|
|
@ -40,15 +40,6 @@ open class BasePresenter<V> : RxPresenter<V>() {
|
||||||
|
|
||||||
fun <T> Preference<T>.asState() = PreferenceMutableState(this, presenterScope)
|
fun <T> Preference<T>.asState() = PreferenceMutableState(this, presenterScope)
|
||||||
|
|
||||||
/**
|
|
||||||
* Subscribes an observable with [deliverFirst] and adds it to the presenter's lifecycle
|
|
||||||
* subscription list.
|
|
||||||
*
|
|
||||||
* @param onNext function to execute when the observable emits an item.
|
|
||||||
* @param onError function to execute when the observable throws an error.
|
|
||||||
*/
|
|
||||||
fun <T> Observable<T>.subscribeFirst(onNext: (V, T) -> Unit, onError: ((V, Throwable) -> Unit) = { _, _ -> }) = compose(deliverFirst<T>()).subscribe(split(onNext, onError)).apply { add(this) }
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Subscribes an observable with [deliverLatestCache] and adds it to the presenter's lifecycle
|
* Subscribes an observable with [deliverLatestCache] and adds it to the presenter's lifecycle
|
||||||
* subscription list.
|
* subscription list.
|
||||||
|
|
|
@ -92,8 +92,6 @@ open class GlobalSearchController(
|
||||||
inflater,
|
inflater,
|
||||||
R.menu.global_search,
|
R.menu.global_search,
|
||||||
R.id.action_search,
|
R.id.action_search,
|
||||||
null,
|
|
||||||
false, // the onMenuItemActionExpand will handle this
|
|
||||||
)
|
)
|
||||||
|
|
||||||
optionsMenuSearchItem = menu.findItem(R.id.action_search)
|
optionsMenuSearchItem = menu.findItem(R.id.action_search)
|
||||||
|
|
|
@ -2,7 +2,6 @@ package eu.kanade.tachiyomi.ui.more
|
||||||
|
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import eu.kanade.presentation.more.MoreScreen
|
import eu.kanade.presentation.more.MoreScreen
|
||||||
import eu.kanade.tachiyomi.R
|
|
||||||
import eu.kanade.tachiyomi.ui.base.controller.FullComposeController
|
import eu.kanade.tachiyomi.ui.base.controller.FullComposeController
|
||||||
import eu.kanade.tachiyomi.ui.base.controller.RootController
|
import eu.kanade.tachiyomi.ui.base.controller.RootController
|
||||||
import eu.kanade.tachiyomi.ui.base.controller.pushController
|
import eu.kanade.tachiyomi.ui.base.controller.pushController
|
||||||
|
@ -15,8 +14,6 @@ class MoreController :
|
||||||
FullComposeController<MorePresenter>(),
|
FullComposeController<MorePresenter>(),
|
||||||
RootController {
|
RootController {
|
||||||
|
|
||||||
override fun getTitle() = resources?.getString(R.string.label_more)
|
|
||||||
|
|
||||||
override fun createPresenter() = MorePresenter()
|
override fun createPresenter() = MorePresenter()
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
|
|
|
@ -879,6 +879,15 @@ class ReaderPresenter(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Subscribes an observable with [deliverFirst] and adds it to the presenter's lifecycle
|
||||||
|
* subscription list.
|
||||||
|
*
|
||||||
|
* @param onNext function to execute when the observable emits an item.
|
||||||
|
* @param onError function to execute when the observable throws an error.
|
||||||
|
*/
|
||||||
|
private fun <T> Observable<T>.subscribeFirst(onNext: (ReaderActivity, T) -> Unit, onError: ((ReaderActivity, Throwable) -> Unit) = { _, _ -> }) = compose(deliverFirst<T>()).subscribe(split(onNext, onError)).apply { add(this) }
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
// Safe theoretical max filename size is 255 bytes and 1 char = 2-4 bytes (UTF-8)
|
// Safe theoretical max filename size is 255 bytes and 1 char = 2-4 bytes (UTF-8)
|
||||||
private const val MAX_FILE_NAME_BYTES = 250
|
private const val MAX_FILE_NAME_BYTES = 250
|
||||||
|
|
|
@ -21,7 +21,6 @@ import com.bluelinelabs.conductor.ControllerChangeType
|
||||||
import dev.chrisbanes.insetter.applyInsetter
|
import dev.chrisbanes.insetter.applyInsetter
|
||||||
import eu.kanade.tachiyomi.R
|
import eu.kanade.tachiyomi.R
|
||||||
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
|
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
|
||||||
import eu.kanade.tachiyomi.ui.base.controller.BaseController
|
|
||||||
import eu.kanade.tachiyomi.util.preference.asHotFlow
|
import eu.kanade.tachiyomi.util.preference.asHotFlow
|
||||||
import eu.kanade.tachiyomi.util.system.getResourceColor
|
import eu.kanade.tachiyomi.util.system.getResourceColor
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
@ -114,20 +113,8 @@ abstract class SettingsController : PreferenceController() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
open fun getTitle(): String? {
|
|
||||||
return preferenceScreen?.title?.toString()
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun setTitle() {
|
private fun setTitle() {
|
||||||
var parentController = parentController
|
(activity as? AppCompatActivity)?.supportActionBar?.title = preferenceScreen?.title?.toString()
|
||||||
while (parentController != null) {
|
|
||||||
if (parentController is BaseController<*> && parentController.getTitle() != null) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
parentController = parentController.parentController
|
|
||||||
}
|
|
||||||
|
|
||||||
(activity as? AppCompatActivity)?.supportActionBar?.title = getTitle()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
inline fun <T> Preference.visibleIf(preference: eu.kanade.tachiyomi.core.preference.Preference<T>, crossinline block: (T) -> Boolean) {
|
inline fun <T> Preference.visibleIf(preference: eu.kanade.tachiyomi.core.preference.Preference<T>, crossinline block: (T) -> Boolean) {
|
||||||
|
|
|
@ -43,9 +43,8 @@ object DiskUtil {
|
||||||
/**
|
/**
|
||||||
* Returns the root folders of all the available external storages.
|
* Returns the root folders of all the available external storages.
|
||||||
*/
|
*/
|
||||||
fun getExternalStorages(context: Context): Collection<File> {
|
fun getExternalStorages(context: Context): List<File> {
|
||||||
val directories = mutableSetOf<File>()
|
return ContextCompat.getExternalFilesDirs(context, null)
|
||||||
directories += ContextCompat.getExternalFilesDirs(context, null)
|
|
||||||
.filterNotNull()
|
.filterNotNull()
|
||||||
.mapNotNull {
|
.mapNotNull {
|
||||||
val file = File(it.absolutePath.substringBefore("/Android/"))
|
val file = File(it.absolutePath.substringBefore("/Android/"))
|
||||||
|
@ -56,8 +55,6 @@ object DiskUtil {
|
||||||
null
|
null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return directories
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -27,5 +27,4 @@ class SecurityPreferences(
|
||||||
INCOGNITO(R.string.pref_incognito_mode),
|
INCOGNITO(R.string.pref_incognito_mode),
|
||||||
NEVER(R.string.lock_never),
|
NEVER(R.string.lock_never),
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Reference in a new issue