From 4c5d33b1fefcbae142efce2a483d674a8de83099 Mon Sep 17 00:00:00 2001 From: Jay Date: Wed, 6 May 2020 03:07:58 -0400 Subject: [PATCH] Added category hopper to bottom of library settings for it coming soon --- .../data/preference/PreferencesHelper.kt | 2 + .../tachiyomi/ui/library/LibraryController.kt | 130 ++++++++++++++++-- .../ui/library/LibraryGestureDetector.kt | 74 ++++++++++ .../kanade/tachiyomi/ui/main/MainActivity.kt | 4 +- app/src/main/res/drawable/round_ripple.xml | 2 +- .../res/layout/library_list_controller.xml | 13 +- .../res/layout/rounded_category_hopper.xml | 62 +++++++++ 7 files changed, 273 insertions(+), 14 deletions(-) create mode 100644 app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryGestureDetector.kt create mode 100644 app/src/main/res/layout/rounded_category_hopper.xml diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/preference/PreferencesHelper.kt b/app/src/main/java/eu/kanade/tachiyomi/data/preference/PreferencesHelper.kt index 8d66ef2c24..3c98faf9fc 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/preference/PreferencesHelper.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/preference/PreferencesHelper.kt @@ -265,6 +265,8 @@ class PreferencesHelper(val context: Context) { fun showAllCategories() = flowPrefs.getBoolean("show_all_categories", true) + fun hopperGravity() = flowPrefs.getInt("hopper_gravity", 1) + // Tutorial preferences fun shownFilterTutorial() = flowPrefs.getBoolean("shown_filter_tutorial", false) diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryController.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryController.kt index 6e2c037d00..fcba9ff437 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryController.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryController.kt @@ -7,6 +7,7 @@ import android.graphics.Color import android.os.Bundle import android.os.Handler import android.util.TypedValue +import android.view.Gravity import android.view.LayoutInflater import android.view.Menu import android.view.MenuInflater @@ -18,6 +19,8 @@ import android.view.inputmethod.InputMethodManager import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.view.ActionMode import androidx.appcompat.widget.SearchView +import androidx.coordinatorlayout.widget.CoordinatorLayout +import androidx.core.view.GestureDetectorCompat import androidx.recyclerview.widget.DefaultItemAnimator import androidx.recyclerview.widget.GridLayoutManager import androidx.recyclerview.widget.ItemTouchHelper @@ -82,6 +85,7 @@ import kotlinx.android.synthetic.main.filter_bottom_sheet.* import kotlinx.android.synthetic.main.library_grid_recycler.* import kotlinx.android.synthetic.main.library_list_controller.* import kotlinx.android.synthetic.main.main_activity.* +import kotlinx.android.synthetic.main.rounded_category_hopper.* import kotlinx.coroutines.delay import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.api.get @@ -90,7 +94,7 @@ import kotlin.math.roundToInt class LibraryController( bundle: Bundle? = null, - private val preferences: PreferencesHelper = Injekt.get() + val preferences: PreferencesHelper = Injekt.get() ) : BaseController(bundle), ActionMode.Callback, ChangeMangaCategoriesDialog.Listener, @@ -108,6 +112,9 @@ class LibraryController( * Position of the active category. */ private var activeCategory: Int = preferences.lastUsedCategory().getOrDefault() + set(value) { + field = value + } private var justStarted = true @@ -164,6 +171,21 @@ class LibraryController( val notAtTop = recycler.canScrollVertically(-1) if (notAtTop != elevate) elevateFunc(notAtTop) val order = getCategoryOrder() + if (!recycler_cover.isClickable) { + category_hopper_frame.translationY += dy + category_hopper_frame.translationY = + category_hopper_frame.translationY.coerceIn(0f, 60f.dpToPx) + up_category.alpha = if (!recycler.canScrollVertically(-1)) { + 0.25f + } else { + 1f + } + down_category.alpha = if (!recycler.canScrollVertically(1)) { + 0.25f + } else { + 1f + } + } if (!filter_bottom_sheet.sheetBehavior.isHidden()) { scrollDistance += abs(dy) if (scrollDistance > scrollDistanceTilHidden) { @@ -180,8 +202,8 @@ class LibraryController( val view = fast_scroller ?: return val height = if (view.childCount > 0) { - view.height - (view.getChildAt(0)?.paddingTop ?: 0) - - (view.getChildAt(view.childCount - 1)?.paddingBottom ?: 0) + view.height - (view.getChildAt(0)?.paddingTop + ?: 0) - (view.getChildAt(view.childCount - 1)?.paddingBottom ?: 0) } else { view.height } @@ -191,9 +213,9 @@ class LibraryController( textAnim?.start() // fastScroll height * indicator position - center text - fastScroll padding - text_view_m.translationY = height * - (index.toFloat() / (adapter.headerItems.size + 1)) - - text_view_m.height / 2 + 16.dpToPx + text_view_m.translationY = + height * (index.toFloat() / (adapter.headerItems.size + 1)) + -text_view_m.height / 2 + 16.dpToPx text_view_m.translationX = 45f.dpToPxEnd text_view_m.alpha = 1f text_view_m.text = headerItem.category.name @@ -213,6 +235,15 @@ class LibraryController( } RecyclerView.SCROLL_STATE_IDLE -> { scrollAnim = fast_scroller?.hide() + val shortAnimationDuration = resources?.getInteger( + android.R.integer.config_shortAnimTime + ) ?: 0 + if (!recycler_cover.isClickable) { + category_hopper_frame.animate().translationY( + if (category_hopper_frame.translationY > 30f.dpToPx) 60f.dpToPx + else 0f + ).setDuration(shortAnimationDuration.toLong()).start() + } } } } @@ -264,9 +295,12 @@ class LibraryController( recycler.adapter = adapter fast_scroller.setupWithRecyclerView(recycler, { position -> val letter = adapter.getSectionText(position) - if (!singleCategory && presenter.showAllCategories && - !adapter.isHeader(adapter.getItem(position)) && - position != adapter.itemCount - 1) null + if (!singleCategory && presenter.showAllCategories && !adapter.isHeader( + adapter.getItem( + position + ) + ) && position != adapter.itemCount - 1 + ) null else if (letter != null) FastScrollItemIndicator.Text(letter) else FastScrollItemIndicator.Icon(R.drawable.ic_star_24dp) }) @@ -336,6 +370,7 @@ class LibraryController( showCategories(false) } category_recycler.onCategoryClicked = { + recycler.itemAnimator = null scrollToHeader(it) showCategories(show = false, scroll = false) } @@ -343,6 +378,60 @@ class LibraryController( preferences.showAllCategories().set(isChecked) presenter.getLibrary() } + + category_hopper_frame.gone() + down_category.setOnClickListener { + val position = getVisibleHeader() ?: return@setOnClickListener + val newOffset = adapter.headerItems.indexOf(position) + 1 + if (newOffset < presenter.categories.size) { + val newOrder = (adapter.headerItems[newOffset] as LibraryHeaderItem).category.order + scrollToHeader(newOrder) + } else { + recycler.scrollToPosition(adapter.itemCount - 1) + } + } + up_category.setOnClickListener { + val position = getVisibleHeader() ?: return@setOnClickListener + val newOffset = adapter.headerItems.indexOf(position) - 1 + if (newOffset > -1) { + val newOrder = (adapter.headerItems[newOffset] as LibraryHeaderItem).category.order + scrollToHeader(newOrder) + } else { + recycler.scrollToPosition(0) + } + } + down_category.setOnLongClickListener { + recycler.scrollToPosition(adapter.itemCount - 1) + true + } + up_category.setOnLongClickListener { + recycler.scrollToPosition(0) + true + } + category_button.setOnClickListener { + showCategories(!recycler_cover.isClickable) + } + + category_button.setOnLongClickListener { + activity?.toolbar?.menu?.performIdentifierAction(R.id.action_search, 0) + true + } + + category_hopper_frame.updateLayoutParams { + anchorGravity = Gravity.TOP or when (preferences.hopperGravity().get()) { + 0 -> Gravity.LEFT + 2 -> Gravity.RIGHT + else -> Gravity.CENTER + } + } + + val gestureDetector = GestureDetectorCompat(activity, LibraryGestureDetector(this)) + listOf(category_hopper_layout, up_category, down_category, category_button).forEach { + it.setOnTouchListener { _, event -> + gestureDetector.onTouchEvent(event) + } + } + scrollViewWith(recycler, swipeRefreshLayout = swipe_refresh, afterInsets = { insets -> category_layout?.updateLayoutParams { topMargin = recycler?.paddingTop ?: 0 @@ -436,6 +525,22 @@ class LibraryController( return null } + private fun getVisibleHeader(): LibraryHeaderItem? { + val position = + (recycler.layoutManager as LinearLayoutManager).findFirstCompletelyVisibleItemPosition() + when (val item = adapter.getItem(position)) { + is LibraryHeaderItem -> return item + is LibraryItem -> return item.header + } + val fPosition = + (recycler.layoutManager as LinearLayoutManager).findFirstVisibleItemPosition() + when (val item = adapter.getItem(fPosition)) { + is LibraryHeaderItem -> return item + is LibraryItem -> return item.header + } + return null + } + private fun getCategoryOrder(): Int? { val position = (recycler.layoutManager as LinearLayoutManager).findFirstCompletelyVisibleItemPosition() @@ -589,6 +694,7 @@ class LibraryController( } } } + category_hopper_frame.visibleIf(!singleCategory) adapter.isLongPressDragEnabled = canDrag() category_recycler.setCategories(presenter.categories) setActiveCategory() @@ -610,6 +716,7 @@ class LibraryController( val full = category_layout.height.toFloat() + recycler.paddingTop val translateY = if (show) full else 0f recycler.animate().translationY(translateY).start() + category_hopper_frame.animate().translationY(translateY).start() if (scroll) { // Smooth scroll the recycler to hide the hidden content blocked by the app bar ValueAnimator.ofInt(recycler.translationY.roundToInt(), translateY.roundToInt()).apply { @@ -661,7 +768,6 @@ class LibraryController( private fun scrollToHeader(pos: Int) { if (!presenter.showAllCategories) { - recycler.itemAnimator = null presenter.switchSection(pos) activeCategory = pos setActiveCategory() @@ -1030,6 +1136,10 @@ class LibraryController( override fun sheetIsExpanded(): Boolean = false override fun handleSheetBack(): Boolean { + if (recycler_cover.isClickable) { + showCategories(false) + return true + } if (filter_bottom_sheet.sheetBehavior.isExpanded()) { filter_bottom_sheet.sheetBehavior?.collapse() return true diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryGestureDetector.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryGestureDetector.kt new file mode 100644 index 0000000000..99bae75fb1 --- /dev/null +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryGestureDetector.kt @@ -0,0 +1,74 @@ +package eu.kanade.tachiyomi.ui.library + +import android.view.GestureDetector +import android.view.Gravity +import android.view.MotionEvent +import androidx.coordinatorlayout.widget.CoordinatorLayout +import eu.kanade.tachiyomi.ui.main.MainActivity +import eu.kanade.tachiyomi.util.view.hide +import eu.kanade.tachiyomi.util.view.updateLayoutParams +import kotlinx.android.synthetic.main.filter_bottom_sheet.* +import kotlinx.android.synthetic.main.library_list_controller.* +import kotlin.math.abs + +class LibraryGestureDetector(private val controller: LibraryController) : GestureDetector +.SimpleOnGestureListener() { + override fun onDown(e: MotionEvent): Boolean { + return false + } + + override fun onFling( + e1: MotionEvent, + e2: MotionEvent, + velocityX: Float, + velocityY: Float + ): Boolean { + var result = false + val diffY = e2.y - e1.y + val diffX = e2.x - e1.x + if (abs(diffX) <= abs(diffY) && + abs(diffY) > MainActivity.SWIPE_THRESHOLD && + abs(velocityY) > MainActivity.SWIPE_VELOCITY_THRESHOLD) { + if (diffY <= 0) { + controller.showSheet() + } else { + controller.filter_bottom_sheet.sheetBehavior?.hide() + } + result = true + } else if (abs(diffX) >= abs(diffY) && + abs(diffX) > MainActivity.SWIPE_THRESHOLD && + abs(velocityX) > MainActivity.SWIPE_VELOCITY_THRESHOLD) { + // val transition = androidx.transition.AutoTransition() + // transition.duration = 150 + + androidx.transition.TransitionManager.beginDelayedTransition( + controller.library_layout + ) + if (diffX <= 0) { + controller.category_hopper_frame.updateLayoutParams { + anchorGravity = + Gravity.TOP or (if (anchorGravity == Gravity.TOP or Gravity.RIGHT) { + controller.preferences.hopperGravity().set(1) + Gravity.CENTER + } else { + controller.preferences.hopperGravity().set(0) + Gravity.LEFT + }) + } + } else { + controller.category_hopper_frame.updateLayoutParams { + anchorGravity = Gravity.TOP or + Gravity.TOP or (if (anchorGravity == Gravity.TOP or Gravity.LEFT) { + controller.preferences.hopperGravity().set(1) + Gravity.CENTER + } else { + controller.preferences.hopperGravity().set(2) + Gravity.RIGHT + }) + } + } + result = true + } + return result + } +} diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/main/MainActivity.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/main/MainActivity.kt index 37adf78ba1..ec32004feb 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/main/MainActivity.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/main/MainActivity.kt @@ -591,8 +591,8 @@ open class MainActivity : BaseActivity(), DownloadServiceListener { companion object { - private const val SWIPE_THRESHOLD = 100 - private const val SWIPE_VELOCITY_THRESHOLD = 100 + const val SWIPE_THRESHOLD = 100 + const val SWIPE_VELOCITY_THRESHOLD = 100 // Shortcut actions const val SHORTCUT_LIBRARY = "eu.kanade.tachiyomi.SHOW_LIBRARY" diff --git a/app/src/main/res/drawable/round_ripple.xml b/app/src/main/res/drawable/round_ripple.xml index d4c17b310f..d3d24007bb 100644 --- a/app/src/main/res/drawable/round_ripple.xml +++ b/app/src/main/res/drawable/round_ripple.xml @@ -1,6 +1,6 @@ + android:color="@color/fullRippleColor"> diff --git a/app/src/main/res/layout/library_list_controller.xml b/app/src/main/res/layout/library_list_controller.xml index bb2048c4b7..e16d5f7bd2 100644 --- a/app/src/main/res/layout/library_list_controller.xml +++ b/app/src/main/res/layout/library_list_controller.xml @@ -37,7 +37,7 @@ app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" - app:layout_constraintTop_toTopOf="parent"/> + app:layout_constraintTop_toTopOf="parent" /> + + + + + diff --git a/app/src/main/res/layout/rounded_category_hopper.xml b/app/src/main/res/layout/rounded_category_hopper.xml new file mode 100644 index 0000000000..9328af26a2 --- /dev/null +++ b/app/src/main/res/layout/rounded_category_hopper.xml @@ -0,0 +1,62 @@ + + + + + + + + + + + +