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:
arkon 2022-09-18 17:22:54 -04:00
parent 7ec822503a
commit 0225711f6f
11 changed files with 42 additions and 137 deletions

View file

@ -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
}
}
} }

View file

@ -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)

View file

@ -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) }
}
}

View file

@ -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

View file

@ -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.

View file

@ -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)

View file

@ -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

View file

@ -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

View file

@ -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) {

View file

@ -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
} }
/** /**

View file

@ -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),
} }
} }