mirror of
https://github.com/ankidroid/Anki-Android.git
synced 2024-09-20 12:02:16 +02:00
Fix snackbar swipe to dismiss behavior
While you can can dismiss snackbars by flinging, due to a bug in the library, they stay in place while you do so, and only ever disappear to the right, which might not be the direction of the fling. This makes snackbars move along with the finger.
This commit is contained in:
parent
f0e6ee7ac9
commit
3223b526cf
@ -12,6 +12,7 @@ import android.widget.Toast
|
||||
import androidx.annotation.StringRes
|
||||
import androidx.core.content.ContextCompat
|
||||
import com.google.android.material.snackbar.Snackbar
|
||||
import com.ichi2.anki.snackbar.fixSwipeDismissBehavior
|
||||
import com.ichi2.async.CollectionTask.SaveCollection
|
||||
import com.ichi2.async.TaskListener
|
||||
import com.ichi2.async.TaskManager
|
||||
@ -122,6 +123,7 @@ object UIUtils {
|
||||
@JvmStatic
|
||||
fun getSnackbar(activity: Activity?, mainText: String?, length: Int, actionTextResource: Int, listener: View.OnClickListener?, root: View, callback: Snackbar.Callback?): Snackbar {
|
||||
val sb = Snackbar.make(root, mainText!!, length)
|
||||
sb.fixSwipeDismissBehavior()
|
||||
if (listener != null) {
|
||||
sb.setAction(actionTextResource, listener)
|
||||
}
|
||||
@ -143,6 +145,7 @@ object UIUtils {
|
||||
@JvmStatic
|
||||
fun getDismissibleSnackbar(activity: Activity?, mainText: String, length: Int, dismissTextResource: Int, root: View): Snackbar {
|
||||
val sb = Snackbar.make(root, mainText, length)
|
||||
sb.fixSwipeDismissBehavior()
|
||||
sb.setAction(dismissTextResource) {
|
||||
sb.dismiss()
|
||||
}
|
||||
|
@ -0,0 +1,84 @@
|
||||
/*
|
||||
* This program is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License as published by the Free Software
|
||||
* Foundation; either version 3 of the License, or (at your option) any later
|
||||
* version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
|
||||
* PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.ichi2.anki.snackbar
|
||||
|
||||
import android.view.MotionEvent
|
||||
import android.view.View
|
||||
import androidx.coordinatorlayout.widget.CoordinatorLayout
|
||||
import androidx.customview.widget.ViewDragHelper
|
||||
import com.google.android.material.behavior.SwipeDismissBehavior
|
||||
import com.google.android.material.snackbar.Snackbar
|
||||
|
||||
/**
|
||||
* This exists to help Snackbars actually move on the screen when you try to swipe them away.
|
||||
* With the default SwipeDismissBehavior, while you can can dismiss them by flinging,
|
||||
* they stay in place while you do so, and only ever disappear to the right,
|
||||
* not into the direction of the fling. The library provides the functionality, it's just broken.
|
||||
*
|
||||
* The issue is, when we get a move event, ViewDragHelper will capture the view for dragging,
|
||||
* and ask parent to stop intercepting further touches. This propagates to CoordinatorLayout,
|
||||
* which then resets touch behavior for its children--including us.
|
||||
*
|
||||
* The sequence of unfortunate events:
|
||||
* * [onInterceptTouchEvent]
|
||||
* * [ViewDragHelper.shouldInterceptTouchEvent]
|
||||
* * [ViewDragHelper.tryCaptureViewForDrag]
|
||||
* * [ViewDragHelper.captureChildView]
|
||||
* * [ViewDragHelper.Callback.onViewCaptured]
|
||||
* * [CoordinatorLayout.requestDisallowInterceptTouchEvent]
|
||||
* * [CoordinatorLayout.resetTouchBehaviors]
|
||||
* * [onTouchEvent]
|
||||
*
|
||||
* This fix solves the issue in a very simple way, by ignoring calls to [onTouchEvent]
|
||||
* during the call to [onInterceptTouchEvent].
|
||||
*/
|
||||
class SwipeDismissBehaviorFix<V : View> : SwipeDismissBehavior<V>() {
|
||||
private var ignoreCallsToOnTouchEvent = false
|
||||
|
||||
override fun onInterceptTouchEvent(parent: CoordinatorLayout, child: V, event: MotionEvent): Boolean {
|
||||
ignoreCallsToOnTouchEvent = true
|
||||
return super.onInterceptTouchEvent(parent, child, event).also {
|
||||
ignoreCallsToOnTouchEvent = false
|
||||
}
|
||||
}
|
||||
|
||||
override fun onTouchEvent(parent: CoordinatorLayout, child: V, event: MotionEvent): Boolean {
|
||||
if (ignoreCallsToOnTouchEvent) return false
|
||||
return super.onTouchEvent(parent, child, event)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This does three things:
|
||||
* * Changes the default behavior to the fixed one;
|
||||
* * Copies the listener from the default behavior. When dragging or settling,
|
||||
* this listener pauses the timer that removes the snackbar,
|
||||
* so it does not disappear from under your finger;
|
||||
* * Allows swiping the snackbar to the left, as well as to the right.
|
||||
*/
|
||||
fun Snackbar.fixSwipeDismissBehavior() {
|
||||
addCallback(object : Snackbar.Callback() {
|
||||
override fun onShown(snackbar: Snackbar) {
|
||||
super.onShown(snackbar)
|
||||
val params = snackbar.view.layoutParams
|
||||
if (params is CoordinatorLayout.LayoutParams) {
|
||||
params.behavior = SwipeDismissBehaviorFix<View>().apply {
|
||||
listener = (params.behavior as? SwipeDismissBehavior)?.listener
|
||||
setSwipeDirection(SwipeDismissBehavior.SWIPE_DIRECTION_ANY)
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
Loading…
Reference in New Issue
Block a user