From: uazo Date: Mon, 18 Aug 2025 13:46:14 +0000 Subject: Enable snackbar swipe Allows swipe of snackbar notifications License: GPL-2.0-or-later - https://spdx.org/licenses/GPL-2.0-or-later.html --- .../ui/messages/snackbar/SnackbarView.java | 91 ++++++++++++++++--- 1 file changed, 77 insertions(+), 14 deletions(-) diff --git a/chrome/browser/ui/messages/android/java/src/org/chromium/chrome/browser/ui/messages/snackbar/SnackbarView.java b/chrome/browser/ui/messages/android/java/src/org/chromium/chrome/browser/ui/messages/snackbar/SnackbarView.java --- a/chrome/browser/ui/messages/android/java/src/org/chromium/chrome/browser/ui/messages/snackbar/SnackbarView.java +++ b/chrome/browser/ui/messages/android/java/src/org/chromium/chrome/browser/ui/messages/snackbar/SnackbarView.java @@ -9,14 +9,17 @@ import android.animation.AnimatorListenerAdapter; import android.animation.AnimatorSet; import android.animation.ObjectAnimator; import android.app.Activity; +import android.content.Context; import android.graphics.Rect; import android.graphics.drawable.Drawable; import android.graphics.drawable.GradientDrawable; import android.util.Pair; +import android.view.MotionEvent; import android.view.Gravity; import android.view.LayoutInflater; import android.view.SurfaceView; import android.view.View; +import android.view.View.OnClickListener; import android.view.View.OnLayoutChangeListener; import android.view.ViewGroup; import android.widget.FrameLayout; @@ -36,6 +39,8 @@ import org.chromium.chrome.browser.ui.messages.snackbar.SnackbarSwipeHandler.Del import org.chromium.chrome.ui.messages.R; import org.chromium.components.browser_ui.styles.SemanticColorUtils; import org.chromium.components.browser_ui.widget.gesture.SwipeGestureListener; +import org.chromium.components.browser_ui.widget.gesture.SwipeGestureListener.ScrollDirection; +import org.chromium.components.browser_ui.widget.gesture.SwipeGestureListener.SwipeHandler; import org.chromium.components.browser_ui.widget.text.TemplatePreservingTextView; import org.chromium.ui.base.WindowAndroid; import org.chromium.ui.insets.InsetObserver; @@ -48,7 +53,7 @@ import org.chromium.ui.interpolators.Interpolators; // TODO (jianli): Change this class and its methods back to package protected after the offline // indicator experiment is done. @NullMarked -public class SnackbarView implements InsetObserver.WindowInsetObserver { +public class SnackbarView implements InsetObserver.WindowInsetObserver, View.OnTouchListener, SwipeHandler { private static final int MAX_LINES = 5; private static final int DEFAULT_LINES = 2; @@ -96,6 +101,16 @@ public class SnackbarView implements InsetObserver.WindowInsetObserver { } }; + private final SwipeGestureListenerImpl mSwipeGestureListener; + private final OnClickListener mDismissListener; + private final float mHorizontalTranslationPx; + + private class SwipeGestureListenerImpl extends SwipeGestureListener { + public SwipeGestureListenerImpl(Context context, SwipeHandler handler) { + super(context, handler); + } + } + /** * Creates an instance of the {@link SnackbarView}. * @@ -151,21 +166,15 @@ public class SnackbarView implements InsetObserver.WindowInsetObserver { adjustViewPosition(); } }); - SwipeGestureListener swipeGestureListener = - new SwipeGestureListener(activity, mSnackbarSwipeHandler); - + mSwipeGestureListener = + new SwipeGestureListenerImpl(mContainerView.getContext(), this); // Make sure clicks are not consumed by content beneath the container view. mContainerView.setClickable(true); - mContainerView.setOnClickListener((event) -> manager.resetSnackbarTimeout()); - mContainerView.setOnTouchListener( - (view, event) -> { - if (swipeGestureListener.onTouchEvent(event)) return true; - // Disable touch inputs during animation. - if (mIsAnimating) return true; - mContainerView.performClick(); - return true; - }); - + if (!snackbar.isTypePersistent()) { + mContainerView.setOnTouchListener(this); + } + mDismissListener = dismissListener; + mHorizontalTranslationPx = mContainerView.getResources().getDisplayMetrics().widthPixels / 3; mSnackbarView = mContainerView.findViewById(R.id.snackbar); mAnimationDuration = mContainerView.getResources().getInteger(android.R.integer.config_mediumAnimTime); @@ -311,6 +320,60 @@ public class SnackbarView implements InsetObserver.WindowInsetObserver { return mContainerView.isShown(); } + @Override + public boolean onTouch(View v, MotionEvent event) { + return mSwipeGestureListener.onTouchEvent(event); + } + + private boolean mSwipeStarted; + private float mCurrentSwipePosition; + + @Override + public void onSwipeStarted(@ScrollDirection int direction, MotionEvent ev) { + mContainerView.setTranslationX(0); + mSwipeStarted = true; + } + + @Override + public void onSwipeUpdated(MotionEvent current, float tx, float ty, float dx, float dy) { + if (!mSwipeStarted) return; + mCurrentSwipePosition = Math.abs(tx); + mContainerView.setTranslationX(tx); + } + + @Override + public void onSwipeFinished() { + if (!mSwipeStarted) return; + mSwipeStarted = false; + if (mCurrentSwipePosition >= mHorizontalTranslationPx) { + mDismissListener.onClick(mDismissButtonView); + return; + } + mContainerView.setTranslationX(0); + } + + @Override + public void onFling( + @ScrollDirection int direction, + MotionEvent current, + float tx, + float ty, + float vx, + float vy) { + if (!mSwipeStarted) return; + mSwipeStarted = false; + if (mCurrentSwipePosition >= mHorizontalTranslationPx) { + mDismissListener.onClick(mDismissButtonView); + return; + } + mContainerView.setTranslationX(0); + } + + @Override + public boolean isSwipeEnabled(@ScrollDirection int direction) { + return direction == ScrollDirection.LEFT || direction == ScrollDirection.RIGHT; + } + void bringToFront() { mContainerView.bringToFront(); } --