mirror of
https://github.com/mihonapp/mihon.git
synced 2024-11-10 04:37:25 +01:00
Remove dead code
This commit is contained in:
parent
fe6aa4358f
commit
4f2a794fba
@ -277,7 +277,7 @@ dependencies {
|
||||
implementation(libs.bundles.conductor)
|
||||
|
||||
// FlowBinding
|
||||
implementation(libs.bundles.flowbinding)
|
||||
implementation(libs.flowbinding.android)
|
||||
|
||||
// Logging
|
||||
implementation(libs.logcat)
|
||||
|
@ -1,216 +0,0 @@
|
||||
package eu.kanade.tachiyomi.ui.base.controller
|
||||
|
||||
import android.app.Activity
|
||||
import android.os.Bundle
|
||||
import android.text.style.CharacterStyle
|
||||
import android.view.Menu
|
||||
import android.view.MenuInflater
|
||||
import android.view.MenuItem
|
||||
import androidx.appcompat.widget.SearchView
|
||||
import androidx.core.text.getSpans
|
||||
import androidx.core.widget.doAfterTextChanged
|
||||
import androidx.viewbinding.ViewBinding
|
||||
import eu.kanade.tachiyomi.R
|
||||
import eu.kanade.tachiyomi.ui.base.presenter.BasePresenter
|
||||
import kotlinx.coroutines.flow.launchIn
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
import reactivecircus.flowbinding.appcompat.QueryTextEvent
|
||||
import reactivecircus.flowbinding.appcompat.queryTextEvents
|
||||
|
||||
/**
|
||||
* Implementation of the NucleusController that has a built-in ViewSearch
|
||||
*/
|
||||
abstract class SearchableNucleusController<VB : ViewBinding, P : BasePresenter<*>>(bundle: Bundle? = null) : NucleusController<VB, P>(bundle) {
|
||||
|
||||
enum class SearchViewState { LOADING, LOADED, COLLAPSING, FOCUSED }
|
||||
|
||||
/**
|
||||
* Used to bypass the initial searchView being set to empty string after an onResume
|
||||
*/
|
||||
private var currentSearchViewState: SearchViewState = SearchViewState.LOADING
|
||||
|
||||
/**
|
||||
* Store the query text that has not been submitted to reassign it after an onResume, UI-only
|
||||
*/
|
||||
protected var nonSubmittedQuery: String = ""
|
||||
|
||||
/**
|
||||
* To be called by classes that extend this subclass in onCreateOptionsMenu
|
||||
*/
|
||||
protected fun createOptionsMenu(
|
||||
menu: Menu,
|
||||
inflater: MenuInflater,
|
||||
menuId: Int,
|
||||
searchItemId: Int,
|
||||
) {
|
||||
inflater.inflate(menuId, menu)
|
||||
|
||||
// Initialize search option.
|
||||
val searchItem = menu.findItem(searchItemId)
|
||||
val searchView = searchItem.actionView as SearchView
|
||||
searchItem.fixExpand(onExpand = { invalidateMenuOnExpand() })
|
||||
searchView.maxWidth = Int.MAX_VALUE
|
||||
|
||||
// Remove formatting from pasted text
|
||||
val searchAutoComplete: SearchView.SearchAutoComplete = searchView.findViewById(
|
||||
R.id.search_src_text,
|
||||
)
|
||||
searchAutoComplete.doAfterTextChanged { editable ->
|
||||
editable?.getSpans<CharacterStyle>()?.forEach { editable.removeSpan(it) }
|
||||
}
|
||||
|
||||
searchView.queryTextEvents()
|
||||
.onEach {
|
||||
val newText = it.queryText.toString()
|
||||
|
||||
if (newText.isNotBlank() or acceptEmptyQuery()) {
|
||||
if (it is QueryTextEvent.QuerySubmitted) {
|
||||
// Abstract function for implementation
|
||||
// Run it first in case the old query data is needed (like BrowseSourceController)
|
||||
onSearchViewQueryTextSubmit(newText)
|
||||
presenter.query = newText
|
||||
nonSubmittedQuery = ""
|
||||
} else if ((it is QueryTextEvent.QueryChanged) && (presenter.query != newText)) {
|
||||
nonSubmittedQuery = newText
|
||||
|
||||
// Abstract function for implementation
|
||||
onSearchViewQueryTextChange(newText)
|
||||
}
|
||||
}
|
||||
// clear the collapsing flag
|
||||
setCurrentSearchViewState(SearchViewState.LOADED, SearchViewState.COLLAPSING)
|
||||
}
|
||||
.launchIn(viewScope)
|
||||
|
||||
val query = presenter.query
|
||||
|
||||
// Restoring a query the user had not submitted
|
||||
if (nonSubmittedQuery.isNotBlank() and (nonSubmittedQuery != query)) {
|
||||
searchItem.expandActionView()
|
||||
searchView.setQuery(nonSubmittedQuery, false)
|
||||
onSearchViewQueryTextChange(nonSubmittedQuery)
|
||||
}
|
||||
|
||||
// Workaround for weird behavior where searchView gets empty text change despite
|
||||
// query being set already, prevents the query from being cleared
|
||||
binding.root.post {
|
||||
setCurrentSearchViewState(SearchViewState.LOADED, SearchViewState.LOADING)
|
||||
}
|
||||
|
||||
searchView.setOnQueryTextFocusChangeListener { _, hasFocus ->
|
||||
if (hasFocus) {
|
||||
setCurrentSearchViewState(SearchViewState.FOCUSED)
|
||||
} else {
|
||||
setCurrentSearchViewState(SearchViewState.LOADED, SearchViewState.FOCUSED)
|
||||
}
|
||||
}
|
||||
|
||||
searchItem.setOnActionExpandListener(
|
||||
object : MenuItem.OnActionExpandListener {
|
||||
override fun onMenuItemActionExpand(item: MenuItem): Boolean {
|
||||
onSearchMenuItemActionExpand(item)
|
||||
return true
|
||||
}
|
||||
|
||||
override fun onMenuItemActionCollapse(item: MenuItem): Boolean {
|
||||
val localSearchView = searchItem.actionView as SearchView
|
||||
|
||||
// if it is blank the flow event won't trigger so we would stay in a COLLAPSING state
|
||||
if (localSearchView.toString().isNotBlank()) {
|
||||
setCurrentSearchViewState(SearchViewState.COLLAPSING)
|
||||
}
|
||||
|
||||
onSearchMenuItemActionCollapse(item)
|
||||
return true
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
override fun onActivityResumed(activity: Activity) {
|
||||
super.onActivityResumed(activity)
|
||||
// Until everything is up and running don't accept empty queries
|
||||
setCurrentSearchViewState(SearchViewState.LOADING)
|
||||
}
|
||||
|
||||
private fun acceptEmptyQuery(): Boolean {
|
||||
return when (currentSearchViewState) {
|
||||
SearchViewState.COLLAPSING, SearchViewState.FOCUSED -> true
|
||||
else -> false
|
||||
}
|
||||
}
|
||||
|
||||
private fun setCurrentSearchViewState(to: SearchViewState, from: SearchViewState? = null) {
|
||||
// When loading ignore all requests other than loaded
|
||||
if ((currentSearchViewState == SearchViewState.LOADING) && (to != SearchViewState.LOADED)) {
|
||||
return
|
||||
}
|
||||
|
||||
// Prevent changing back to an unwanted state when using async flows (ie onFocus event doing
|
||||
// COLLAPSING -> LOADED)
|
||||
if ((from != null) && (currentSearchViewState != from)) {
|
||||
return
|
||||
}
|
||||
|
||||
currentSearchViewState = to
|
||||
}
|
||||
|
||||
/**
|
||||
* Called by the SearchView since since the implementation of these can vary in subclasses
|
||||
* Not abstract as they are optional
|
||||
*/
|
||||
protected open fun onSearchViewQueryTextChange(newText: String?) {
|
||||
}
|
||||
|
||||
protected open fun onSearchViewQueryTextSubmit(query: String?) {
|
||||
}
|
||||
|
||||
protected open fun onSearchMenuItemActionExpand(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
|
||||
* into BaseController) this addresses an issue where the searchView.onTextFocus event is not
|
||||
* triggered
|
||||
*/
|
||||
private fun invalidateMenuOnExpand(): Boolean {
|
||||
return if (expandActionViewFromInteraction) {
|
||||
activity?.invalidateOptionsMenu()
|
||||
setCurrentSearchViewState(SearchViewState.FOCUSED) // we are technically focused here
|
||||
false
|
||||
} else {
|
||||
true
|
||||
}
|
||||
}
|
||||
}
|
@ -7,17 +7,11 @@ import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.MainScope
|
||||
import kotlinx.coroutines.cancel
|
||||
import nucleus.presenter.RxPresenter
|
||||
import rx.Observable
|
||||
|
||||
open class BasePresenter<V> : RxPresenter<V>() {
|
||||
|
||||
var presenterScope: CoroutineScope = MainScope()
|
||||
|
||||
/**
|
||||
* Query from the view where applicable
|
||||
*/
|
||||
var query: String = ""
|
||||
|
||||
override fun onCreate(savedState: Bundle?) {
|
||||
try {
|
||||
super.onCreate(savedState)
|
||||
@ -39,13 +33,4 @@ open class BasePresenter<V> : RxPresenter<V>() {
|
||||
}
|
||||
|
||||
fun <T> Preference<T>.asState() = PreferenceMutableState(this, presenterScope)
|
||||
|
||||
/**
|
||||
* Subscribes an observable with [deliverLatestCache] 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>.subscribeLatestCache(onNext: (V, T) -> Unit, onError: ((V, Throwable) -> Unit) = { _, _ -> }) = compose(deliverLatestCache<T>()).subscribe(split(onNext, onError)).apply { add(this) }
|
||||
}
|
||||
|
@ -898,6 +898,15 @@ class ReaderPresenter(
|
||||
*/
|
||||
private fun <T> Observable<T>.subscribeFirst(onNext: (ReaderActivity, T) -> Unit, onError: ((ReaderActivity, 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
|
||||
* 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>.subscribeLatestCache(onNext: (ReaderActivity, T) -> Unit, onError: ((ReaderActivity, Throwable) -> Unit) = { _, _ -> }) = compose(deliverLatestCache<T>()).subscribe(split(onNext, onError)).apply { add(this) }
|
||||
|
||||
companion object {
|
||||
// Safe theoretical max filename size is 255 bytes and 1 char = 2-4 bytes (UTF-8)
|
||||
private const val MAX_FILE_NAME_BYTES = 250
|
||||
|
@ -1,46 +0,0 @@
|
||||
package eu.kanade.tachiyomi.widget
|
||||
|
||||
import android.content.Context
|
||||
import android.util.AttributeSet
|
||||
import androidx.core.content.withStyledAttributes
|
||||
import androidx.recyclerview.widget.GridLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import kotlin.math.max
|
||||
|
||||
class AutofitRecyclerView @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null) :
|
||||
RecyclerView(context, attrs) {
|
||||
|
||||
private val manager = GridLayoutManager(context, 1)
|
||||
|
||||
private var columnWidth = -1
|
||||
|
||||
var spanCount = 0
|
||||
set(value) {
|
||||
field = value
|
||||
if (value > 0) {
|
||||
manager.spanCount = value
|
||||
}
|
||||
}
|
||||
|
||||
val itemWidth: Int
|
||||
get() = measuredWidth / manager.spanCount
|
||||
|
||||
init {
|
||||
if (attrs != null) {
|
||||
val attrsArray = intArrayOf(android.R.attr.columnWidth)
|
||||
context.withStyledAttributes(attrs, attrsArray) {
|
||||
columnWidth = getDimensionPixelSize(0, -1)
|
||||
}
|
||||
}
|
||||
|
||||
layoutManager = manager
|
||||
}
|
||||
|
||||
override fun onMeasure(widthSpec: Int, heightSpec: Int) {
|
||||
super.onMeasure(widthSpec, heightSpec)
|
||||
if (spanCount == 0 && columnWidth > 0) {
|
||||
val count = max(1, measuredWidth / columnWidth)
|
||||
spanCount = count
|
||||
}
|
||||
}
|
||||
}
|
@ -1,31 +0,0 @@
|
||||
package eu.kanade.tachiyomi.widget
|
||||
|
||||
import android.content.Context
|
||||
import android.util.AttributeSet
|
||||
import android.view.LayoutInflater
|
||||
import android.widget.LinearLayout
|
||||
import androidx.annotation.StringRes
|
||||
import eu.kanade.tachiyomi.databinding.CommonDialogWithCheckboxBinding
|
||||
|
||||
class DialogCheckboxView @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null) :
|
||||
LinearLayout(context, attrs) {
|
||||
|
||||
private val binding: CommonDialogWithCheckboxBinding
|
||||
|
||||
init {
|
||||
binding = CommonDialogWithCheckboxBinding.inflate(LayoutInflater.from(context), this, false)
|
||||
addView(binding.root)
|
||||
}
|
||||
|
||||
fun setDescription(@StringRes id: Int) {
|
||||
binding.description.text = context.getString(id)
|
||||
}
|
||||
|
||||
fun setOptionDescription(@StringRes id: Int) {
|
||||
binding.checkboxOption.text = context.getString(id)
|
||||
}
|
||||
|
||||
fun isChecked(): Boolean {
|
||||
return binding.checkboxOption.isChecked
|
||||
}
|
||||
}
|
@ -1,47 +0,0 @@
|
||||
package eu.kanade.tachiyomi.widget
|
||||
|
||||
import android.content.Context
|
||||
import android.util.AttributeSet
|
||||
import androidx.annotation.StringRes
|
||||
import androidx.compose.material3.LocalContentColor
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.CompositionLocalProvider
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.platform.AbstractComposeView
|
||||
import androidx.core.view.isVisible
|
||||
import eu.kanade.presentation.components.EmptyScreen
|
||||
import eu.kanade.presentation.theme.TachiyomiTheme
|
||||
|
||||
class EmptyView @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null) :
|
||||
AbstractComposeView(context, attrs) {
|
||||
|
||||
var message by mutableStateOf("")
|
||||
|
||||
/**
|
||||
* Hide the information view
|
||||
*/
|
||||
fun hide() {
|
||||
this.isVisible = false
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the information view
|
||||
* @param textResource text of information view
|
||||
*/
|
||||
fun show(@StringRes textResource: Int) {
|
||||
message = context.getString(textResource)
|
||||
this.isVisible = true
|
||||
}
|
||||
|
||||
@Composable
|
||||
override fun Content() {
|
||||
TachiyomiTheme {
|
||||
CompositionLocalProvider(LocalContentColor provides MaterialTheme.colorScheme.onBackground) {
|
||||
EmptyScreen(message = message)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,64 +0,0 @@
|
||||
package eu.kanade.tachiyomi.widget
|
||||
|
||||
import android.content.Context
|
||||
import android.util.AttributeSet
|
||||
import android.view.inputmethod.EditorInfo
|
||||
import androidx.appcompat.widget.SearchView
|
||||
import androidx.core.view.inputmethod.EditorInfoCompat
|
||||
import eu.kanade.domain.base.BasePreferences
|
||||
import eu.kanade.tachiyomi.R
|
||||
import eu.kanade.tachiyomi.util.preference.asHotFlow
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.SupervisorJob
|
||||
import kotlinx.coroutines.cancel
|
||||
import kotlinx.coroutines.flow.launchIn
|
||||
import uy.kohesive.injekt.Injekt
|
||||
import uy.kohesive.injekt.api.get
|
||||
|
||||
/**
|
||||
* A custom [SearchView] that sets [EditorInfoCompat.IME_FLAG_NO_PERSONALIZED_LEARNING] to imeOptions
|
||||
* if [BasePreferences.incognitoMode] is true. Some IMEs may not respect this flag.
|
||||
*/
|
||||
class TachiyomiSearchView @JvmOverloads constructor(
|
||||
context: Context,
|
||||
attrs: AttributeSet? = null,
|
||||
defStyleAttr: Int = R.attr.searchViewStyle,
|
||||
) : SearchView(context, attrs, defStyleAttr) {
|
||||
|
||||
private var scope: CoroutineScope? = null
|
||||
|
||||
override fun onAttachedToWindow() {
|
||||
super.onAttachedToWindow()
|
||||
scope = CoroutineScope(SupervisorJob() + Dispatchers.Main)
|
||||
Injekt.get<BasePreferences>().incognitoMode()
|
||||
.asHotFlow {
|
||||
imeOptions = if (it) {
|
||||
imeOptions or EditorInfoCompat.IME_FLAG_NO_PERSONALIZED_LEARNING
|
||||
} else {
|
||||
imeOptions and EditorInfoCompat.IME_FLAG_NO_PERSONALIZED_LEARNING.inv()
|
||||
}
|
||||
}
|
||||
.launchIn(scope!!)
|
||||
}
|
||||
|
||||
override fun setOnQueryTextListener(listener: OnQueryTextListener?) {
|
||||
super.setOnQueryTextListener(listener)
|
||||
val searchAutoComplete: SearchAutoComplete = findViewById(R.id.search_src_text)
|
||||
searchAutoComplete.setOnEditorActionListener { _, actionID, _ ->
|
||||
if (actionID == EditorInfo.IME_ACTION_SEARCH || actionID == EditorInfo.IME_NULL) {
|
||||
clearFocus()
|
||||
listener?.onQueryTextSubmit(query.toString())
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onDetachedFromWindow() {
|
||||
super.onDetachedFromWindow()
|
||||
scope?.cancel()
|
||||
scope = null
|
||||
}
|
||||
}
|
@ -1,23 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:paddingHorizontal="24dp"
|
||||
android:paddingTop="16dp">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/description"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:textAppearance="?attr/textAppearanceBodyMedium" />
|
||||
|
||||
<CheckBox
|
||||
android:id="@+id/checkbox_option"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="-5dp"
|
||||
android:layout_marginTop="16dp"
|
||||
android:layout_marginEnd="0dp" />
|
||||
|
||||
</LinearLayout>
|
@ -4,7 +4,6 @@ okhttp_version = "5.0.0-alpha.10"
|
||||
nucleus_version = "3.0.0"
|
||||
coil_version = "2.2.2"
|
||||
conductor_version = "3.1.8"
|
||||
flowbinding_version = "1.2.0"
|
||||
shizuku_version = "12.2.0"
|
||||
sqldelight = "1.5.4"
|
||||
leakcanary = "2.10"
|
||||
@ -68,8 +67,7 @@ wheelpicker = "com.github.commandiron:WheelPickerCompose:1.0.11"
|
||||
conductor-core = { module = "com.bluelinelabs:conductor", version.ref = "conductor_version" }
|
||||
conductor-support-preference = { module = "com.github.tachiyomiorg:conductor-support-preference", version.ref = "conductor_version" }
|
||||
|
||||
flowbinding-android = { module = "io.github.reactivecircus.flowbinding:flowbinding-android", version.ref = "flowbinding_version" }
|
||||
flowbinding-appcompat = { module = "io.github.reactivecircus.flowbinding:flowbinding-appcompat", version.ref = "flowbinding_version" }
|
||||
flowbinding-android = "io.github.reactivecircus.flowbinding:flowbinding-android:1.2.0"
|
||||
|
||||
logcat = "com.squareup.logcat:logcat:0.1"
|
||||
|
||||
@ -102,7 +100,6 @@ js-engine = ["quickjs-android"]
|
||||
sqlite = ["sqlitektx", "sqlite-android"]
|
||||
nucleus = ["nucleus-core", "nucleus-supportv7"]
|
||||
coil = ["coil-core", "coil-gif", "coil-compose"]
|
||||
flowbinding = ["flowbinding-android", "flowbinding-appcompat"]
|
||||
conductor = ["conductor-core", "conductor-support-preference"]
|
||||
shizuku = ["shizuku-api", "shizuku-provider"]
|
||||
voyager = ["voyager-navigator", "voyager-transitions"]
|
||||
|
Loading…
Reference in New Issue
Block a user