mirror of
https://github.com/null2264/yokai.git
synced 2025-06-21 10:44:42 +00:00
Added horizontal gesture to change categories while "show all categories" is disabled
Also shifting category hopper arrows to be left and right instead of up and down in single category mode Closes #1280
This commit is contained in:
parent
6b1725fd3b
commit
0ef33f7ed2
6 changed files with 182 additions and 7 deletions
|
@ -0,0 +1,99 @@
|
|||
package eu.kanade.tachiyomi.ui.library
|
||||
|
||||
import android.graphics.Rect
|
||||
import android.view.GestureDetector
|
||||
import android.view.MotionEvent
|
||||
import android.view.View
|
||||
import eu.kanade.tachiyomi.util.system.isLTR
|
||||
import eu.kanade.tachiyomi.util.view.activityBinding
|
||||
import kotlin.math.abs
|
||||
import kotlin.math.pow
|
||||
import kotlin.math.sign
|
||||
|
||||
class LibraryCategoryGestureDetector(private val controller: LibraryController?) : GestureDetector
|
||||
.SimpleOnGestureListener() {
|
||||
var locked = false
|
||||
var cancelled = false
|
||||
private val poa = 1.7f
|
||||
|
||||
override fun onDown(e: MotionEvent): Boolean {
|
||||
locked = false
|
||||
controller ?: return false
|
||||
val startingOnLibraryView = listOf<View?>(
|
||||
controller.activityBinding?.bottomNav,
|
||||
controller.binding.filterBottomSheet.root,
|
||||
controller.binding.categoryHopperFrame,
|
||||
controller.activityBinding?.appBar,
|
||||
).none {
|
||||
it ?: return false
|
||||
val viewRect = Rect()
|
||||
it.getGlobalVisibleRect(viewRect)
|
||||
viewRect.contains(e.x.toInt(), e.y.toInt())
|
||||
}
|
||||
cancelled = !startingOnLibraryView
|
||||
return startingOnLibraryView
|
||||
}
|
||||
|
||||
override fun onScroll(
|
||||
e1: MotionEvent,
|
||||
e2: MotionEvent,
|
||||
distanceX: Float,
|
||||
distanceY: Float,
|
||||
): Boolean {
|
||||
val controller = controller ?: return false
|
||||
val distance = e1.rawX - e2.rawX
|
||||
val totalDistanceY = e1.rawY - e2.rawY
|
||||
controller.binding.libraryGridRecycler.recycler.translationX =
|
||||
if (!cancelled) abs(distance / 50).pow(poa) * -sign(distance / 50) else 0f
|
||||
if (!locked && abs(distance) > 50 && !cancelled) {
|
||||
val ev2 = MotionEvent.obtain(e1)
|
||||
ev2.action = MotionEvent.ACTION_CANCEL
|
||||
controller.binding.swipeRefresh.dispatchTouchEvent(ev2)
|
||||
ev2.recycle()
|
||||
locked = true
|
||||
} else if (abs(totalDistanceY) > 50 && !locked) {
|
||||
cancelled = true
|
||||
return false
|
||||
}
|
||||
return super.onScroll(e1, e2, distanceX, distanceY)
|
||||
}
|
||||
|
||||
override fun onFling(
|
||||
e1: MotionEvent,
|
||||
e2: MotionEvent,
|
||||
velocityX: Float,
|
||||
velocityY: Float,
|
||||
): Boolean {
|
||||
locked = false
|
||||
if (cancelled) {
|
||||
cancelled = false
|
||||
return false
|
||||
}
|
||||
cancelled = false
|
||||
val controller = controller ?: return false
|
||||
var result = false
|
||||
val diffY = e2.y - e1.y
|
||||
val diffX = e2.x - e1.x
|
||||
val recycler = controller.binding.libraryGridRecycler.recycler
|
||||
var moved = false
|
||||
if (abs(diffX) >= abs(diffY) &&
|
||||
abs(diffX) > SWIPE_THRESHOLD * 5 &&
|
||||
abs(velocityX) > SWIPE_VELOCITY_THRESHOLD
|
||||
) {
|
||||
moved = controller.jumpToNextCategory((diffX >= 0).xor(controller.binding.root.resources.isLTR))
|
||||
result = true
|
||||
}
|
||||
if (!result || !moved) {
|
||||
val animator = controller.binding.libraryGridRecycler.recycler.animate().setDuration(150L)
|
||||
animator.translationX(0f)
|
||||
animator.withEndAction { recycler.translationX = 0f }
|
||||
animator.start()
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
private companion object {
|
||||
const val SWIPE_THRESHOLD = 50
|
||||
const val SWIPE_VELOCITY_THRESHOLD = 100
|
||||
}
|
||||
}
|
|
@ -87,6 +87,7 @@ import eu.kanade.tachiyomi.ui.reader.ReaderActivity
|
|||
import eu.kanade.tachiyomi.ui.source.globalsearch.GlobalSearchController
|
||||
import eu.kanade.tachiyomi.util.isLocal
|
||||
import eu.kanade.tachiyomi.util.moveCategories
|
||||
import eu.kanade.tachiyomi.util.system.contextCompatDrawable
|
||||
import eu.kanade.tachiyomi.util.system.dpToPx
|
||||
import eu.kanade.tachiyomi.util.system.getResourceColor
|
||||
import eu.kanade.tachiyomi.util.system.ignoredSystemInsets
|
||||
|
@ -162,6 +163,8 @@ class LibraryController(
|
|||
|
||||
var singleCategory: Boolean = false
|
||||
private set
|
||||
var hopperAnimation: ValueAnimator? = null
|
||||
var catGestureDetector: GestureDetectorCompat? = null
|
||||
|
||||
/**
|
||||
* Library search query.
|
||||
|
@ -292,8 +295,7 @@ class LibraryController(
|
|||
if (!binding.fastScroller.isFastScrolling) {
|
||||
updateSmallerViewsTopMargins()
|
||||
}
|
||||
binding.roundedCategoryHopper.upCategory.alpha = if (isAtTop()) 0.25f else 1f
|
||||
binding.roundedCategoryHopper.downCategory.alpha = if (isAtBottom()) 0.25f else 1f
|
||||
updateHopperAlpha()
|
||||
}
|
||||
if (!binding.filterBottomSheet.filterBottomSheet.sheetBehavior.isHidden()) {
|
||||
scrollDistance += abs(dy)
|
||||
|
@ -332,6 +334,11 @@ class LibraryController(
|
|||
}
|
||||
}
|
||||
|
||||
fun updateHopperAlpha() {
|
||||
binding.roundedCategoryHopper.upCategory.alpha = if (isAtTop()) 0.25f else 1f
|
||||
binding.roundedCategoryHopper.downCategory.alpha = if (isAtBottom()) 0.25f else 1f
|
||||
}
|
||||
|
||||
private fun removeStaggeredObserver() {
|
||||
if (staggeredObserver != null) {
|
||||
binding.libraryGridRecycler.recycler.viewTreeObserver.removeOnGlobalLayoutListener(
|
||||
|
@ -384,6 +391,7 @@ class LibraryController(
|
|||
closerToHopperBottom
|
||||
}
|
||||
val end = if (closerToEdge) maxHopperOffset else 0f
|
||||
hopperAnimation?.cancel()
|
||||
val alphaAnimation = ValueAnimator.ofFloat(hopperOffset, end)
|
||||
alphaAnimation.addUpdateListener { valueAnimator ->
|
||||
hopperOffset = valueAnimator.animatedValue as Float
|
||||
|
@ -394,6 +402,7 @@ class LibraryController(
|
|||
updateHopperY()
|
||||
}
|
||||
alphaAnimation.duration = shortAnimationDuration.toLong()
|
||||
hopperAnimation = alphaAnimation
|
||||
alphaAnimation.start()
|
||||
}
|
||||
}
|
||||
|
@ -713,6 +722,7 @@ class LibraryController(
|
|||
true
|
||||
}.show()
|
||||
}
|
||||
catGestureDetector = GestureDetectorCompat(binding.root.context, LibraryCategoryGestureDetector(this))
|
||||
|
||||
binding.roundedCategoryHopper.categoryButton.setOnLongClickListener {
|
||||
when (preferences.hopperLongPressAction().get()) {
|
||||
|
@ -765,6 +775,20 @@ class LibraryController(
|
|||
}
|
||||
}
|
||||
|
||||
fun handleGeneralEvent(event: MotionEvent) {
|
||||
if (presenter.showAllCategories) return
|
||||
if (event.action == MotionEvent.ACTION_UP || event.action == MotionEvent.ACTION_CANCEL) {
|
||||
val result = catGestureDetector?.onTouchEvent(event) ?: false
|
||||
if (!result && binding.libraryGridRecycler.recycler.translationX != 0f) {
|
||||
binding.libraryGridRecycler.recycler.animate().setDuration(150L)
|
||||
.translationX(0f)
|
||||
.start()
|
||||
}
|
||||
} else {
|
||||
catGestureDetector?.onTouchEvent(event)
|
||||
}
|
||||
}
|
||||
|
||||
fun updateHopperY(windowInsets: WindowInsetsCompat? = null) {
|
||||
val view = view ?: return
|
||||
val insets = windowInsets ?: view.rootWindowInsetsCompat
|
||||
|
@ -802,24 +826,26 @@ class LibraryController(
|
|||
binding.jumperCategoryText.isVisible = !hide
|
||||
}
|
||||
|
||||
private fun jumpToNextCategory(next: Boolean) {
|
||||
val category = getVisibleHeader() ?: return
|
||||
fun jumpToNextCategory(next: Boolean): Boolean {
|
||||
val category = getVisibleHeader() ?: return false
|
||||
if (presenter.showAllCategories) {
|
||||
if (!next) {
|
||||
val fPosition = binding.libraryGridRecycler.recycler.findFirstVisibleItemPosition()
|
||||
if (fPosition > adapter.currentItems.indexOf(category)) {
|
||||
scrollToHeader(category.category.order)
|
||||
return
|
||||
return true
|
||||
}
|
||||
}
|
||||
val newOffset = adapter.headerItems.indexOf(category) + (if (next) 1 else -1)
|
||||
if (if (!next) newOffset > -1 else newOffset < adapter.headerItems.size) {
|
||||
return if (if (!next) newOffset > -1 else newOffset < adapter.headerItems.size) {
|
||||
val newCategory = (adapter.headerItems[newOffset] as LibraryHeaderItem).category
|
||||
val newOrder = newCategory.order
|
||||
scrollToHeader(newOrder)
|
||||
showCategoryText(newCategory.name)
|
||||
true
|
||||
} else {
|
||||
binding.libraryGridRecycler.recycler.scrollToPosition(if (next) adapter.itemCount - 1 else 0)
|
||||
true
|
||||
}
|
||||
} else {
|
||||
val newOffset =
|
||||
|
@ -835,8 +861,13 @@ class LibraryController(
|
|||
val newOrder = newCategory.order
|
||||
scrollToHeader(newOrder)
|
||||
showCategoryText(newCategory.name)
|
||||
hopperAnimation?.cancel()
|
||||
hopperOffset = 0f
|
||||
updateHopperY()
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
private fun getHeader(firstCompletelyVisible: Boolean = false): LibraryHeaderItem? {
|
||||
|
@ -1036,6 +1067,15 @@ class LibraryController(
|
|||
)
|
||||
}
|
||||
adapter.setItems(mangaMap)
|
||||
if (binding.libraryGridRecycler.recycler.translationX != 0f) {
|
||||
val time = binding.root.resources.getInteger(
|
||||
android.R.integer.config_shortAnimTime,
|
||||
).toLong()
|
||||
viewScope.launchUI {
|
||||
delay(time / 2)
|
||||
binding.libraryGridRecycler.recycler.translationX = 0f
|
||||
}
|
||||
}
|
||||
singleCategory = presenter.categories.size <= 1
|
||||
binding.progress.isVisible = false
|
||||
if (!freshStart) {
|
||||
|
@ -1122,6 +1162,27 @@ class LibraryController(
|
|||
setSubtitle()
|
||||
showMiniBar()
|
||||
}
|
||||
updateHopperAlpha()
|
||||
val isSingleCategory = !presenter.showAllCategories || presenter.forceShowAllCategories
|
||||
val context = binding.roundedCategoryHopper.root.context
|
||||
binding.roundedCategoryHopper.upCategory.setImageDrawable(
|
||||
context.contextCompatDrawable(
|
||||
if (isSingleCategory) {
|
||||
R.drawable.ic_arrow_start_24dp
|
||||
} else {
|
||||
R.drawable.ic_expand_less_24dp
|
||||
},
|
||||
),
|
||||
)
|
||||
binding.roundedCategoryHopper.downCategory.setImageDrawable(
|
||||
context.contextCompatDrawable(
|
||||
if (isSingleCategory) {
|
||||
R.drawable.ic_arrow_end_24dp
|
||||
} else {
|
||||
R.drawable.ic_expand_more_24dp
|
||||
},
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
private fun showSlideAnimation() {
|
||||
|
@ -1188,6 +1249,7 @@ class LibraryController(
|
|||
activityBinding?.appBar?.updateAppBarAfterY(binding.libraryGridRecycler.recycler)
|
||||
binding.swipeRefresh.isEnabled = !show
|
||||
setSubtitle()
|
||||
binding.categoryRecycler.isInvisible = !show
|
||||
if (show) {
|
||||
binding.categoryRecycler.post {
|
||||
binding.categoryRecycler.scrollToCategory(activeCategory)
|
||||
|
|
|
@ -1112,7 +1112,10 @@ open class MainActivity : BaseActivity<MainActivityBinding>(), DownloadServiceLi
|
|||
}
|
||||
|
||||
override fun dispatchTouchEvent(ev: MotionEvent?): Boolean {
|
||||
ev?.let { gestureDetector?.onTouchEvent(it) }
|
||||
ev?.let {
|
||||
gestureDetector?.onTouchEvent(it)
|
||||
(router.backstack.lastOrNull()?.controller as? LibraryController)?.handleGeneralEvent(it)
|
||||
}
|
||||
if (ev?.action == MotionEvent.ACTION_DOWN) {
|
||||
if (snackBar != null && snackBar!!.isShown) {
|
||||
val sRect = Rect()
|
||||
|
|
5
app/src/main/res/drawable/ic_arrow_end_24dp.xml
Normal file
5
app/src/main/res/drawable/ic_arrow_end_24dp.xml
Normal file
|
@ -0,0 +1,5 @@
|
|||
<vector android:autoMirrored="true" android:height="24dp"
|
||||
android:tint="#000000" android:viewportHeight="24"
|
||||
android:viewportWidth="24" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<path android:fillColor="@android:color/white" android:pathData="M8.59,16.59L13.17,12 8.59,7.41 10,6l6,6 -6,6 -1.41,-1.41z"/>
|
||||
</vector>
|
5
app/src/main/res/drawable/ic_arrow_start_24dp.xml
Normal file
5
app/src/main/res/drawable/ic_arrow_start_24dp.xml
Normal file
|
@ -0,0 +1,5 @@
|
|||
<vector android:autoMirrored="true" android:height="24dp"
|
||||
android:tint="#000000" android:viewportHeight="24"
|
||||
android:viewportWidth="24" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<path android:fillColor="@android:color/white" android:pathData="M15.41,16.59L10.83,12l4.58,-4.59L14,6l-6,6 6,6 1.41,-1.41z"/>
|
||||
</vector>
|
|
@ -28,6 +28,7 @@
|
|||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="?actionBarSize"
|
||||
android:clipToPadding="false"
|
||||
android:visibility="invisible"
|
||||
android:paddingBottom="4dp"
|
||||
android:scrollbars="vertical" />
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue