Clean up base classes

Should be able to throw away some of the search controller stuff after Global Search is migrated

(cherry picked from commit 0225711f6f91417af0ae41d42f81c70c86a572ab)
This commit is contained in:
arkon 2022-09-18 17:22:54 -04:00 committed by Jobobby04
parent 966bd31d61
commit 00bb74f330
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

@ -98,8 +98,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
@ -18,8 +17,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

@ -1106,6 +1106,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

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