diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/source/browse/BrowseSourceController.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/source/browse/BrowseSourceController.kt index b56183937c..134cfb6823 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/source/browse/BrowseSourceController.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/source/browse/BrowseSourceController.kt @@ -289,7 +289,7 @@ open class BrowseSourceController(bundle: Bundle) : } private fun showFilters() { - val sheet = SourceSearchSheet(activity!!) + val sheet = SourceFilterSheet(activity!!) sheet.setFilters(presenter.filterItems) presenter.filtersChanged = false val oldFilters = mutableListOf() diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/source/browse/SourceFilterSheet.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/source/browse/SourceFilterSheet.kt new file mode 100644 index 0000000000..711777c0ac --- /dev/null +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/source/browse/SourceFilterSheet.kt @@ -0,0 +1,150 @@ +package eu.kanade.tachiyomi.ui.source.browse + +import android.app.Activity +import android.os.Bundle +import android.view.View +import android.view.ViewGroup +import android.view.ViewTreeObserver.OnGlobalLayoutListener +import androidx.constraintlayout.widget.ConstraintLayout +import androidx.recyclerview.widget.RecyclerView +import com.google.android.material.bottomsheet.BottomSheetBehavior +import com.google.android.material.bottomsheet.BottomSheetDialog +import eu.davidea.flexibleadapter.FlexibleAdapter +import eu.davidea.flexibleadapter.items.IFlexible +import eu.kanade.tachiyomi.R +import eu.kanade.tachiyomi.databinding.SourceFilterSheetBinding +import eu.kanade.tachiyomi.util.system.dpToPx +import eu.kanade.tachiyomi.util.view.collapse +import eu.kanade.tachiyomi.util.view.setEdgeToEdge +import eu.kanade.tachiyomi.util.view.updateLayoutParams +import eu.kanade.tachiyomi.util.view.updatePaddingRelative + +class SourceFilterSheet(val activity: Activity) : + BottomSheetDialog(activity, R.style.BottomSheetDialogTheme) { + + private val sheetBehavior: BottomSheetBehavior<*> + + private var filterChanged = true + + val adapter: FlexibleAdapter> = FlexibleAdapter>(null) + .setDisplayHeadersAtStartUp(true) + + var onSearchClicked = {} + + var onResetClicked = {} + + private val binding = SourceFilterSheetBinding.inflate(activity.layoutInflater) + init { + setContentView(binding.root) + binding.searchBtn.setOnClickListener { dismiss() } + binding.resetBtn.setOnClickListener { onResetClicked() } + + sheetBehavior = BottomSheetBehavior.from(binding.root.parent as ViewGroup) + sheetBehavior.peekHeight = 450.dpToPx + sheetBehavior.collapse() + setEdgeToEdge(activity, binding.root) + + binding.titleLayout.viewTreeObserver.addOnGlobalLayoutListener(object : OnGlobalLayoutListener { + override fun onGlobalLayout() { + binding.cardView.updateLayoutParams { + val fullHeight = activity.window.decorView.height + val insets = activity.window.decorView.rootWindowInsets + matchConstraintMaxHeight = + fullHeight - (insets?.systemWindowInsetTop ?: 0) - + binding.titleLayout.height - 75.dpToPx + } + if (binding.titleLayout.height > 0) { + binding.titleLayout.viewTreeObserver.removeOnGlobalLayoutListener(this) + } + } + }) + + (binding.root.parent.parent as? View)?.viewTreeObserver?.addOnGlobalLayoutListener(object : OnGlobalLayoutListener { + override fun onGlobalLayout() { + updateBottomButtons() + if (sheetBehavior.state != BottomSheetBehavior.STATE_COLLAPSED) { + (binding.root.parent.parent as? View)?.viewTreeObserver?.removeOnGlobalLayoutListener(this) + } + } + }) + + setOnShowListener { + updateBottomButtons() + } + + binding.filtersRecycler.layoutManager = androidx.recyclerview.widget.LinearLayoutManager(context) + binding.filtersRecycler.clipToPadding = false + binding.filtersRecycler.adapter = adapter + binding.filtersRecycler.setHasFixedSize(true) + + sheetBehavior.addBottomSheetCallback( + object : BottomSheetBehavior.BottomSheetCallback() { + override fun onSlide(bottomSheet: View, progress: Float) { + updateBottomButtons() + } + + override fun onStateChanged(p0: View, state: Int) { + updateBottomButtons() + } + } + ) + + binding.filtersRecycler.addOnScrollListener(object : RecyclerView.OnScrollListener() { + override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) { + super.onScrollStateChanged(recyclerView, newState) + if (newState == RecyclerView.SCROLL_STATE_IDLE) { + sheetBehavior.isDraggable = true + } + } + + override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) { + super.onScrolled(recyclerView, dx, dy) + if (recyclerView.canScrollVertically(-1)) { + sheetBehavior.isDraggable = false + } + } + }) + } + + override fun onStart() { + super.onStart() + sheetBehavior.collapse() + updateBottomButtons() + binding.root.post { + updateBottomButtons() + } + } + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + val attrsArray = intArrayOf(android.R.attr.actionBarSize) + val array = context.obtainStyledAttributes(attrsArray) + val headerHeight = array.getDimensionPixelSize(0, 0) + binding.titleLayout.updatePaddingRelative( + bottom = activity.window.decorView.rootWindowInsets.systemWindowInsetBottom + ) + + binding.titleLayout.updateLayoutParams { + height = headerHeight + binding.titleLayout.paddingBottom + } + array.recycle() + } + + private fun updateBottomButtons() { + val bottomSheet = binding.root.parent as View + val bottomSheetVisibleHeight = -bottomSheet.top + (activity.window.decorView.height - bottomSheet.height) + + binding.titleLayout.translationY = bottomSheetVisibleHeight.toFloat() + } + + override fun dismiss() { + super.dismiss() + if (filterChanged) { + onSearchClicked() + } + } + + fun setFilters(items: List>) { + adapter.updateDataSet(items) + } +} diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/source/browse/SourceSearchSheet.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/source/browse/SourceSearchSheet.kt deleted file mode 100644 index feddee5110..0000000000 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/source/browse/SourceSearchSheet.kt +++ /dev/null @@ -1,130 +0,0 @@ -package eu.kanade.tachiyomi.ui.source.browse - -import android.animation.ObjectAnimator -import android.animation.ValueAnimator -import android.app.Activity -import android.view.ActionMode -import android.view.View -import android.view.ViewGroup -import androidx.recyclerview.widget.RecyclerView -import com.google.android.material.bottomsheet.BottomSheetBehavior -import com.google.android.material.bottomsheet.BottomSheetDialog -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.databinding.SourceFilterSheetBinding -import eu.kanade.tachiyomi.util.system.dpToPx -import eu.kanade.tachiyomi.util.view.RecyclerWindowInsetsListener -import eu.kanade.tachiyomi.util.view.expand -import eu.kanade.tachiyomi.util.view.setEdgeToEdge -import uy.kohesive.injekt.injectLazy - -class SourceSearchSheet(activity: Activity) : - BottomSheetDialog(activity, R.style.BottomSheetDialogTheme) { - - /** - * Preferences helper. - */ - private val preferences by injectLazy() - - private var sheetBehavior: BottomSheetBehavior<*> - - private var elevationAnimator: ValueAnimator? = null - - var filterChanged = true - - var isNotElevated = false - - val adapter: FlexibleAdapter> = FlexibleAdapter>(null) - .setDisplayHeadersAtStartUp(true) - - var onSearchClicked = {} - - var onResetClicked = {} - - private val binding = SourceFilterSheetBinding.inflate(activity.layoutInflater) - init { - setContentView(binding.root) - binding.toolbarTitle.text = context.getString(R.string.search_filters) - binding.searchBtn.setOnClickListener { dismiss() } - binding.resetBtn.setOnClickListener { onResetClicked() } - - sheetBehavior = BottomSheetBehavior.from(binding.root.parent as ViewGroup) - sheetBehavior.skipCollapsed = true - sheetBehavior.expand() - setEdgeToEdge( - activity, - binding.root, - 50.dpToPx - ) - - binding.recycler.layoutManager = androidx.recyclerview.widget.LinearLayoutManager(context) - binding.recycler.clipToPadding = false - binding.recycler.adapter = adapter - binding.recycler.setHasFixedSize(true) - binding.recycler.setOnApplyWindowInsetsListener(RecyclerWindowInsetsListener) - - // the spinner in the recycler can break the sheet's layout on change - // this is to reset it back - binding.sourceFilterSheet.post { - (binding.sourceFilterSheet.parent as? View)?.fitsSystemWindows = false - binding.sourceFilterSheet.viewTreeObserver.addOnDrawListener { - (binding.sourceFilterSheet.parent as? View)?.fitsSystemWindows = false - } - } - - sheetBehavior.addBottomSheetCallback( - object : BottomSheetBehavior.BottomSheetCallback() { - override fun onSlide(bottomSheet: View, progress: Float) {} - - override fun onStateChanged(p0: View, state: Int) { - if (state == BottomSheetBehavior.STATE_EXPANDED) { - sheetBehavior.skipCollapsed = true - } - } - } - ) - - binding.recycler.addOnScrollListener( - object : RecyclerView.OnScrollListener() { - override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) { - super.onScrolled(recyclerView, dx, dy) - val atTop = !binding.recycler.canScrollVertically(-1) - if (atTop != isNotElevated) { - elevationAnimator?.cancel() - isNotElevated = atTop - elevationAnimator?.cancel() - elevationAnimator = ObjectAnimator.ofFloat( - binding.titleLayout, - "elevation", - binding.titleLayout.elevation, - if (atTop) 0f else 10f.dpToPx - ) - elevationAnimator?.duration = 100 - elevationAnimator?.start() - } - } - } - ) - } - - override fun onWindowStartingActionMode( - callback: ActionMode.Callback?, - type: Int - ): ActionMode? { - (binding.sourceFilterSheet.parent as View).fitsSystemWindows = false - return super.onWindowStartingActionMode(callback, type) - } - - override fun dismiss() { - super.dismiss() - if (filterChanged) { - onSearchClicked() - } - } - - fun setFilters(items: List>) { - adapter.updateDataSet(items) - } -} diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/source/filter/GroupItem.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/source/filter/GroupItem.kt index 67f49f0553..f0c6d368a3 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/source/filter/GroupItem.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/source/filter/GroupItem.kt @@ -36,9 +36,9 @@ class GroupItem(val filter: Filter.Group<*>) : AbstractExpandableHeaderItem) : AbstractFlexibleItem() { @@ -24,21 +21,13 @@ open class SelectItem(val filter: Filter.Select<*>) : AbstractFlexibleItem>, holder: Holder, position: Int, payloads: MutableList?) { - holder.text.text = filter.name + ": " + holder.spinnerView.title = filter.name + ": " - val spinner = holder.spinner - spinner.prompt = filter.name - spinner.adapter = ArrayAdapter( - holder.itemView.context, - android.R.layout.simple_spinner_item, - filter.values - ).apply { - setDropDownViewResource(R.layout.common_spinner_item) - } - spinner.onItemSelectedListener = IgnoreFirstSpinnerListener { pos -> + holder.spinnerView.setEntries(filter.values.map { it.toString() }) + holder.spinnerView.setSelection(filter.state) + holder.spinnerView.onItemSelectedListener = { pos -> filter.state = pos } - spinner.setSelection(filter.state) } override fun equals(other: Any?): Boolean { @@ -52,8 +41,6 @@ open class SelectItem(val filter: Filter.Select<*>) : AbstractFlexibleItem>) : FlexibleViewHolder(view, adapter) { - - val text: TextView = itemView.findViewById(R.id.nav_view_item_text) - val spinner: Spinner = itemView.findViewById(R.id.nav_view_item) + val spinnerView: MaterialSpinnerView = itemView.findViewById(R.id.nav_view_item) } } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/source/filter/SortGroup.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/source/filter/SortGroup.kt index 558f66d541..3f0d0f5626 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/source/filter/SortGroup.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/source/filter/SortGroup.kt @@ -33,9 +33,9 @@ class SortGroup(val filter: Filter.Sort) : AbstractExpandableHeaderItem) { + this.entries = entries popup = makeSettingsPopup() setOnTouchListener(popup?.dragToOpenListener) setOnClickListener { popup?.show() } - - a.recycle() } fun setSelection(selection: Int) { diff --git a/app/src/main/java/eu/kanade/tachiyomi/widget/SimpleNavigationView.kt b/app/src/main/java/eu/kanade/tachiyomi/widget/SimpleNavigationView.kt index 64c07b5688..797cf75b8f 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/widget/SimpleNavigationView.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/widget/SimpleNavigationView.kt @@ -9,7 +9,6 @@ import android.widget.CheckBox import android.widget.CheckedTextView import android.widget.EditText import android.widget.RadioButton -import android.widget.Spinner import android.widget.TextView import androidx.appcompat.widget.TintTypedArray import androidx.core.view.ViewCompat @@ -154,8 +153,7 @@ open class SimpleNavigationView @JvmOverloads constructor( class SpinnerHolder(parent: ViewGroup, listener: OnClickListener? = null) : ClickableHolder(parent.inflate(TR.layout.navigation_view_spinner), listener) { - val text: TextView = itemView.findViewById(TR.id.nav_view_item_text) - val spinner: Spinner = itemView.findViewById(TR.id.nav_view_item) + val spinnerView: MaterialSpinnerView = itemView.findViewById(TR.id.nav_view_item) } class EditTextHolder(parent: ViewGroup) : diff --git a/app/src/main/res/layout/navigation_view_group.xml b/app/src/main/res/layout/navigation_view_group.xml index 91fb1ee088..5281b7513a 100644 --- a/app/src/main/res/layout/navigation_view_group.xml +++ b/app/src/main/res/layout/navigation_view_group.xml @@ -1,30 +1,33 @@ - + android:paddingEnd="?attr/listPreferredItemPaddingEnd"> + android:textAllCaps="true" + android:textSize="15sp" + android:textAppearance="@style/TextAppearance.MaterialComponents.Headline6" + android:textColor="?android:attr/textColorPrimary" + tools:text="Header" /> + android:layout_height="wrap_content" + tools:src="@drawable/ic_expand_more_24dp" + app:tint="?android:attr/textColorPrimary" /> diff --git a/app/src/main/res/layout/navigation_view_spinner.xml b/app/src/main/res/layout/navigation_view_spinner.xml index 067fbf012b..8f1dd7587a 100644 --- a/app/src/main/res/layout/navigation_view_spinner.xml +++ b/app/src/main/res/layout/navigation_view_spinner.xml @@ -1,6 +1,5 @@ - - - - + tools:entries="@array/viewers_selector" + tools:title="Filter: "/> - + diff --git a/app/src/main/res/layout/source_filter_sheet.xml b/app/src/main/res/layout/source_filter_sheet.xml index f6a41c7269..79ea6c3ef3 100644 --- a/app/src/main/res/layout/source_filter_sheet.xml +++ b/app/src/main/res/layout/source_filter_sheet.xml @@ -1,19 +1,40 @@ - - + + + + - - + app:layout_constraintHorizontal_chainStyle="spread" + app:layout_constraintStart_toEndOf="@id/reset_btn" + app:layout_constraintTop_toTopOf="parent" + app:layout_constraintWidth_min="150dp" /> + + - + diff --git a/app/src/main/res/values/attrs.xml b/app/src/main/res/values/attrs.xml index 5b7868f3a8..5be15778ff 100644 --- a/app/src/main/res/values/attrs.xml +++ b/app/src/main/res/values/attrs.xml @@ -19,7 +19,7 @@ - +