Merge "Remove references to SwipeHelper from NotificationMenuRow."
This commit is contained in:
committed by
Android (Google) Code Review
commit
b981efa912
@@ -38,11 +38,12 @@ import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin.MenuItem
|
||||
public interface NotificationMenuRowPlugin extends Plugin {
|
||||
|
||||
public static final String ACTION = "com.android.systemui.action.PLUGIN_NOTIFICATION_MENU_ROW";
|
||||
public static final int VERSION = 4;
|
||||
public static final int VERSION = 5;
|
||||
|
||||
@ProvidesInterface(version = OnMenuEventListener.VERSION)
|
||||
public interface OnMenuEventListener {
|
||||
public static final int VERSION = 1;
|
||||
|
||||
public void onMenuClicked(View row, int x, int y, MenuItem menu);
|
||||
|
||||
public void onMenuReset(View row);
|
||||
@@ -53,6 +54,7 @@ public interface NotificationMenuRowPlugin extends Plugin {
|
||||
@ProvidesInterface(version = MenuItem.VERSION)
|
||||
public interface MenuItem {
|
||||
public static final int VERSION = 1;
|
||||
|
||||
public View getMenuView();
|
||||
|
||||
public View getGutsView();
|
||||
@@ -84,34 +86,136 @@ public interface NotificationMenuRowPlugin extends Plugin {
|
||||
|
||||
public void setMenuClickListener(OnMenuEventListener listener);
|
||||
|
||||
public void setSwipeActionHelper(NotificationSwipeActionHelper listener);
|
||||
|
||||
public void setAppName(String appName);
|
||||
|
||||
public void createMenu(ViewGroup parent, StatusBarNotification sbn);
|
||||
|
||||
public View getMenuView();
|
||||
|
||||
public boolean isMenuVisible();
|
||||
|
||||
public void resetMenu();
|
||||
|
||||
public void onTranslationUpdate(float translation);
|
||||
public View getMenuView();
|
||||
|
||||
public void onHeightUpdate();
|
||||
/**
|
||||
* Get the target position that a notification row should be snapped open to in order to reveal
|
||||
* the menu. This is generally determined by the number of icons in the notification menu and the
|
||||
* size of each icon. This method accounts for whether the menu appears on the left or ride side
|
||||
* of the parent notification row.
|
||||
*
|
||||
|
||||
public void onNotificationUpdated(StatusBarNotification sbn);
|
||||
* @return an int representing the x-offset in pixels that the notification should snap open to.
|
||||
* Positive values imply that the notification should be offset to the right to reveal the menu,
|
||||
* and negative alues imply that the notification should be offset to the right.
|
||||
*/
|
||||
public int getMenuSnapTarget();
|
||||
|
||||
public boolean onTouchEvent(View view, MotionEvent ev, float velocity);
|
||||
/**
|
||||
* Determines whether or not the menu should be shown in response to user input.
|
||||
* @return true if the menu should be shown, false otherwise.
|
||||
*/
|
||||
public boolean shouldShowMenu();
|
||||
|
||||
/**
|
||||
* Determines whether the menu is currently visible.
|
||||
* @return true if the menu is visible, false otherwise.
|
||||
*/
|
||||
public boolean isMenuVisible();
|
||||
|
||||
/**
|
||||
* Determines whether a given movement is towards or away from the current location of the menu.
|
||||
* @param movement
|
||||
* @return true if the movement is towards the menu, false otherwise.
|
||||
*/
|
||||
public boolean isTowardsMenu(float movement);
|
||||
|
||||
/**
|
||||
* Determines whether the menu should snap closed instead of dismissing the
|
||||
* parent notification, as a function of its current state.
|
||||
*
|
||||
* @return true if the menu should snap closed, false otherwise.
|
||||
*/
|
||||
public boolean shouldSnapBack();
|
||||
|
||||
/**
|
||||
* Determines whether the menu was previously snapped open to the same side that it is currently
|
||||
* being shown on.
|
||||
* @return true if the menu is snapped open to the same side on which it currently appears,
|
||||
* false otherwise.
|
||||
*/
|
||||
public boolean isSnappedAndOnSameSide();
|
||||
|
||||
/**
|
||||
* Determines whether the notification the menu is attached to is able to be dismissed.
|
||||
* @return true if the menu's parent notification is dismissable, false otherwise.
|
||||
*/
|
||||
public boolean canBeDismissed();
|
||||
|
||||
/**
|
||||
* Determines whether the menu should remain open given its current state, or snap closed.
|
||||
* @return true if the menu should remain open, false otherwise.
|
||||
*/
|
||||
public boolean isWithinSnapMenuThreshold();
|
||||
|
||||
/**
|
||||
* Determines whether the menu has been swiped far enough to snap open.
|
||||
* @return true if the menu has been swiped far enough to open, false otherwise.
|
||||
*/
|
||||
public boolean isSwipedEnoughToShowMenu();
|
||||
|
||||
public default boolean onInterceptTouchEvent(View view, MotionEvent ev) {
|
||||
return false;
|
||||
}
|
||||
|
||||
public default boolean useDefaultMenuItems() {
|
||||
public default boolean shouldUseDefaultMenuItems() {
|
||||
return false;
|
||||
}
|
||||
|
||||
public default void onConfigurationChanged() {
|
||||
}
|
||||
/**
|
||||
* Callback used to signal the menu that its parent's translation has changed.
|
||||
* @param translation The new x-translation of the menu as a position (not an offset).
|
||||
*/
|
||||
public void onParentTranslationUpdate(float translation);
|
||||
|
||||
/**
|
||||
* Callback used to signal the menu that its parent's height has changed.
|
||||
*/
|
||||
public void onParentHeightUpdate();
|
||||
|
||||
/**
|
||||
* Callback used to signal the menu that its parent notification has been updated.
|
||||
* @param sbn
|
||||
*/
|
||||
public void onNotificationUpdated(StatusBarNotification sbn);
|
||||
|
||||
/**
|
||||
* Callback used to signal the menu that a user is moving the parent notification.
|
||||
* @param delta The change in the parent notification's position.
|
||||
*/
|
||||
public void onTouchMove(float delta);
|
||||
|
||||
/**
|
||||
* Callback used to signal the menu that a user has begun touching its parent notification.
|
||||
*/
|
||||
public void onTouchStart();
|
||||
|
||||
/**
|
||||
* Callback used to signal the menu that a user has finished touching its parent notification.
|
||||
*/
|
||||
public void onTouchEnd();
|
||||
|
||||
/**
|
||||
* Callback used to signal the menu that it has been snapped closed.
|
||||
*/
|
||||
public void onSnapClosed();
|
||||
|
||||
/**
|
||||
* Callback used to signal the menu that it has been snapped open.
|
||||
*/
|
||||
public void onSnapOpen();
|
||||
|
||||
/**
|
||||
* Callback used to signal the menu that its parent notification has been dismissed.
|
||||
*/
|
||||
public void onDismiss();
|
||||
|
||||
public default void onConfigurationChanged() { }
|
||||
|
||||
}
|
||||
|
||||
@@ -39,7 +39,7 @@ public interface NotificationSwipeActionHelper {
|
||||
/**
|
||||
* Call this to snap a notification to provided {@code targetLeft}.
|
||||
*/
|
||||
public void snap(View animView, float velocity, float targetLeft);
|
||||
public void snapOpen(View animView, int targetLeft, float velocity);
|
||||
|
||||
/**
|
||||
* Call this to snooze a notification based on the provided {@link SnoozeOption}.
|
||||
|
||||
@@ -1037,7 +1037,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
|
||||
removeView(mMenuRow.getMenuView());
|
||||
}
|
||||
mMenuRow = plugin;
|
||||
if (mMenuRow.useDefaultMenuItems()) {
|
||||
if (mMenuRow.shouldUseDefaultMenuItems()) {
|
||||
ArrayList<MenuItem> items = new ArrayList<>();
|
||||
items.add(NotificationMenuRow.createInfoItem(mContext));
|
||||
items.add(NotificationMenuRow.createSnoozeItem(mContext));
|
||||
@@ -1787,7 +1787,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
|
||||
getEntry().expandedIcon.setScrollX((int) -translationX);
|
||||
}
|
||||
if (mMenuRow.getMenuView() != null) {
|
||||
mMenuRow.onTranslationUpdate(translationX);
|
||||
mMenuRow.onParentTranslationUpdate(translationX);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2292,7 +2292,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
|
||||
notifyHeightChanged(true /* needsAnimation */);
|
||||
}
|
||||
if (mMenuRow.getMenuView() != null) {
|
||||
mMenuRow.onHeightUpdate();
|
||||
mMenuRow.onParentHeightUpdate();
|
||||
}
|
||||
updateContentShiftHeight();
|
||||
if (mLayoutListener != null) {
|
||||
@@ -2543,7 +2543,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
|
||||
mGuts.setActualHeight(height);
|
||||
}
|
||||
if (mMenuRow.getMenuView() != null) {
|
||||
mMenuRow.onHeightUpdate();
|
||||
mMenuRow.onParentHeightUpdate();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -16,14 +16,13 @@
|
||||
|
||||
package com.android.systemui.statusbar.notification.row;
|
||||
|
||||
import static com.android.systemui.SwipeHelper.SWIPED_FAR_ENOUGH_SIZE_FRACTION;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
import static com.android.systemui.SwipeHelper.SWIPED_FAR_ENOUGH_SIZE_FRACTION;
|
||||
|
||||
import com.android.systemui.Interpolators;
|
||||
import com.android.systemui.R;
|
||||
import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
|
||||
import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper;
|
||||
import com.android.systemui.statusbar.AlphaOptimizedImageView;
|
||||
import com.android.systemui.statusbar.notification.row.NotificationGuts.GutsContent;
|
||||
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
|
||||
@@ -38,25 +37,21 @@ import android.content.res.Resources;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.util.Log;
|
||||
import android.service.notification.StatusBarNotification;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.FrameLayout;
|
||||
import android.widget.FrameLayout.LayoutParams;
|
||||
|
||||
import com.android.internal.annotations.VisibleForTesting;
|
||||
|
||||
public class NotificationMenuRow implements NotificationMenuRowPlugin, View.OnClickListener,
|
||||
ExpandableNotificationRow.LayoutListener {
|
||||
|
||||
private static final boolean DEBUG = false;
|
||||
private static final String TAG = "swipe";
|
||||
|
||||
private static final int ICON_ALPHA_ANIM_DURATION = 200;
|
||||
private static final long SHOW_MENU_DELAY = 60;
|
||||
private static final long SWIPE_MENU_TIMING = 200;
|
||||
|
||||
// Notification must be swiped at least this fraction of a single menu item to show menu
|
||||
private static final float SWIPED_FAR_ENOUGH_MENU_FRACTION = 0.25f;
|
||||
private static final float SWIPED_FAR_ENOUGH_MENU_UNCLEARABLE_FRACTION = 0.15f;
|
||||
@@ -65,6 +60,9 @@ public class NotificationMenuRow implements NotificationMenuRowPlugin, View.OnCl
|
||||
// menu item to snap back to menu (else it will cover the menu or it'll be dismissed)
|
||||
private static final float SWIPED_BACK_ENOUGH_TO_COVER_FRACTION = 0.2f;
|
||||
|
||||
private static final int ICON_ALPHA_ANIM_DURATION = 200;
|
||||
private static final long SHOW_MENU_DELAY = 60;
|
||||
|
||||
private ExpandableNotificationRow mParent;
|
||||
|
||||
private Context mContext;
|
||||
@@ -89,22 +87,20 @@ public class NotificationMenuRow implements NotificationMenuRowPlugin, View.OnCl
|
||||
private int[] mIconLocation = new int[2];
|
||||
private int[] mParentLocation = new int[2];
|
||||
|
||||
private float mHorizSpaceForIcon = -1;
|
||||
private int mHorizSpaceForIcon = -1;
|
||||
private int mVertSpaceForIcons = -1;
|
||||
private int mIconPadding = -1;
|
||||
private int mSidePadding;
|
||||
|
||||
private float mAlpha = 0f;
|
||||
private float mPrevX;
|
||||
|
||||
private CheckForDrag mCheckForDrag;
|
||||
private Handler mHandler;
|
||||
|
||||
private boolean mMenuSnappedTo;
|
||||
private boolean mMenuSnapped;
|
||||
private boolean mMenuSnappedOnLeft;
|
||||
private boolean mShouldShowMenu;
|
||||
|
||||
private NotificationSwipeActionHelper mSwipeHelper;
|
||||
private boolean mIsUserTouching;
|
||||
|
||||
public NotificationMenuRow(Context context) {
|
||||
@@ -134,9 +130,34 @@ public class NotificationMenuRow implements NotificationMenuRowPlugin, View.OnCl
|
||||
return mSnoozeItem;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSwipeActionHelper(NotificationSwipeActionHelper helper) {
|
||||
mSwipeHelper = helper;
|
||||
@VisibleForTesting
|
||||
protected ExpandableNotificationRow getParent() {
|
||||
return mParent;
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
protected boolean isMenuOnLeft() {
|
||||
return mOnLeft;
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
protected boolean isMenuSnappedOnLeft() {
|
||||
return mMenuSnappedOnLeft;
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
protected boolean isMenuSnapped() {
|
||||
return mMenuSnapped;
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
protected boolean isDismissing() {
|
||||
return mDismissing;
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
protected boolean isSnapping() {
|
||||
return mSnapping;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -155,16 +176,36 @@ public class NotificationMenuRow implements NotificationMenuRowPlugin, View.OnCl
|
||||
return mAlpha > 0;
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
protected boolean isUserTouching() {
|
||||
return mIsUserTouching;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shouldShowMenu() {
|
||||
return mShouldShowMenu;
|
||||
}
|
||||
|
||||
@Override
|
||||
public View getMenuView() {
|
||||
return mMenuContainer;
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
protected float getTranslation() {
|
||||
return mTranslation;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resetMenu() {
|
||||
resetState(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTouchEnd() {
|
||||
mIsUserTouching = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNotificationUpdated(StatusBarNotification sbn) {
|
||||
if (mMenuContainer == null) {
|
||||
@@ -222,9 +263,7 @@ public class NotificationMenuRow implements NotificationMenuRowPlugin, View.OnCl
|
||||
mIconsPlaced = false;
|
||||
setMenuLocation();
|
||||
if (!mIsUserTouching) {
|
||||
// If the # of items showing changed we need to update the snap position
|
||||
showMenu(mParent, mOnLeft ? getSpaceForMenu() : -getSpaceForMenu(),
|
||||
0 /* velocity */);
|
||||
onSnapOpen();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -236,7 +275,7 @@ public class NotificationMenuRow implements NotificationMenuRowPlugin, View.OnCl
|
||||
mAnimating = false;
|
||||
mSnapping = false;
|
||||
mDismissing = false;
|
||||
mMenuSnappedTo = false;
|
||||
mMenuSnapped = false;
|
||||
setMenuLocation();
|
||||
if (mMenuListener != null && notify) {
|
||||
mMenuListener.onMenuReset(mParent);
|
||||
@@ -244,185 +283,102 @@ public class NotificationMenuRow implements NotificationMenuRowPlugin, View.OnCl
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onTouchEvent(View view, MotionEvent ev, float velocity) {
|
||||
final int action = ev.getActionMasked();
|
||||
switch (action) {
|
||||
case MotionEvent.ACTION_DOWN:
|
||||
mSnapping = false;
|
||||
if (mFadeAnimator != null) {
|
||||
mFadeAnimator.cancel();
|
||||
}
|
||||
mHandler.removeCallbacks(mCheckForDrag);
|
||||
public void onTouchMove(float delta) {
|
||||
mSnapping = false;
|
||||
|
||||
if (!isTowardsMenu(delta) && isMenuLocationChange()) {
|
||||
// Don't consider it "snapped" if location has changed.
|
||||
mMenuSnapped = false;
|
||||
|
||||
// Changed directions, make sure we check to fade in icon again.
|
||||
if (!mHandler.hasCallbacks(mCheckForDrag)) {
|
||||
// No check scheduled, set null to schedule a new one.
|
||||
mCheckForDrag = null;
|
||||
mPrevX = ev.getRawX();
|
||||
mIsUserTouching = true;
|
||||
break;
|
||||
|
||||
case MotionEvent.ACTION_MOVE:
|
||||
mSnapping = false;
|
||||
float diffX = ev.getRawX() - mPrevX;
|
||||
mPrevX = ev.getRawX();
|
||||
if (!isTowardsMenu(diffX) && isMenuLocationChange()) {
|
||||
// Don't consider it "snapped" if location has changed.
|
||||
mMenuSnappedTo = false;
|
||||
|
||||
// Changed directions, make sure we check to fade in icon again.
|
||||
if (!mHandler.hasCallbacks(mCheckForDrag)) {
|
||||
// No check scheduled, set null to schedule a new one.
|
||||
mCheckForDrag = null;
|
||||
} else {
|
||||
// Check scheduled, reset alpha and update location; check will fade it in
|
||||
setMenuAlpha(0f);
|
||||
setMenuLocation();
|
||||
}
|
||||
}
|
||||
if (mShouldShowMenu
|
||||
&& !NotificationStackScrollLayout.isPinnedHeadsUp(view)
|
||||
&& !mParent.areGutsExposed()
|
||||
&& !mParent.isDark()
|
||||
&& (mCheckForDrag == null || !mHandler.hasCallbacks(mCheckForDrag))) {
|
||||
// Only show the menu if we're not a heads up view and guts aren't exposed.
|
||||
mCheckForDrag = new CheckForDrag();
|
||||
mHandler.postDelayed(mCheckForDrag, SHOW_MENU_DELAY);
|
||||
}
|
||||
break;
|
||||
|
||||
case MotionEvent.ACTION_UP:
|
||||
mIsUserTouching = false;
|
||||
return handleUpEvent(ev, view, velocity);
|
||||
case MotionEvent.ACTION_CANCEL:
|
||||
mIsUserTouching = false;
|
||||
cancelDrag();
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean handleUpEvent(MotionEvent ev, View animView, float velocity) {
|
||||
// If the menu should not be shown, then there is no need to check if the a swipe
|
||||
// should result in a snapping to the menu. As a result, just check if the swipe
|
||||
// was enough to dismiss the notification.
|
||||
if (!mShouldShowMenu) {
|
||||
if (mSwipeHelper.isDismissGesture(ev)) {
|
||||
dismiss(animView, velocity);
|
||||
} else {
|
||||
snapBack(animView, velocity);
|
||||
// Check scheduled, reset alpha and update location; check will fade it in
|
||||
setMenuAlpha(0f);
|
||||
setMenuLocation();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
final boolean gestureTowardsMenu = isTowardsMenu(velocity);
|
||||
final boolean gestureFastEnough =
|
||||
mSwipeHelper.getMinDismissVelocity() <= Math.abs(velocity);
|
||||
final boolean gestureFarEnough =
|
||||
mSwipeHelper.swipedFarEnough(mTranslation, mParent.getWidth());
|
||||
final double timeForGesture = ev.getEventTime() - ev.getDownTime();
|
||||
final boolean showMenuForSlowOnGoing = !mParent.canViewBeDismissed()
|
||||
&& timeForGesture >= SWIPE_MENU_TIMING;
|
||||
final float menuSnapTarget = mOnLeft ? getSpaceForMenu() : -getSpaceForMenu();
|
||||
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "mTranslation= " + mTranslation
|
||||
+ " mAlpha= " + mAlpha
|
||||
+ " velocity= " + velocity
|
||||
+ " mMenuSnappedTo= " + mMenuSnappedTo
|
||||
+ " mMenuSnappedOnLeft= " + mMenuSnappedOnLeft
|
||||
+ " mOnLeft= " + mOnLeft
|
||||
+ " minDismissVel= " + mSwipeHelper.getMinDismissVelocity()
|
||||
+ " isDismissGesture= " + mSwipeHelper.isDismissGesture(ev)
|
||||
+ " gestureTowardsMenu= " + gestureTowardsMenu
|
||||
+ " gestureFastEnough= " + gestureFastEnough
|
||||
+ " gestureFarEnough= " + gestureFarEnough);
|
||||
if (mShouldShowMenu
|
||||
&& !NotificationStackScrollLayout.isPinnedHeadsUp(getParent())
|
||||
&& !mParent.areGutsExposed()
|
||||
&& !mParent.isDark()
|
||||
&& (mCheckForDrag == null || !mHandler.hasCallbacks(mCheckForDrag))) {
|
||||
// Only show the menu if we're not a heads up view and guts aren't exposed.
|
||||
mCheckForDrag = new CheckForDrag();
|
||||
mHandler.postDelayed(mCheckForDrag, SHOW_MENU_DELAY);
|
||||
}
|
||||
|
||||
if (mMenuSnappedTo && isMenuVisible() && mMenuSnappedOnLeft == mOnLeft) {
|
||||
// Menu was snapped to previously and we're on the same side, figure out if
|
||||
// we should stick to the menu, snap back into place, or dismiss
|
||||
final float maximumSwipeDistance = mHorizSpaceForIcon
|
||||
* SWIPED_BACK_ENOUGH_TO_COVER_FRACTION;
|
||||
final float targetLeft = getSpaceForMenu() - maximumSwipeDistance;
|
||||
final float targetRight = mParent.getWidth() * SWIPED_FAR_ENOUGH_SIZE_FRACTION;
|
||||
boolean withinSnapMenuThreshold = mOnLeft
|
||||
? mTranslation > targetLeft && mTranslation < targetRight
|
||||
: mTranslation < -targetLeft && mTranslation > -targetRight;
|
||||
boolean shouldSnapTo = mOnLeft ? mTranslation < targetLeft : mTranslation > -targetLeft;
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, " withinSnapMenuThreshold= " + withinSnapMenuThreshold
|
||||
+ " shouldSnapTo= " + shouldSnapTo
|
||||
+ " targetLeft= " + targetLeft
|
||||
+ " targetRight= " + targetRight);
|
||||
}
|
||||
if (withinSnapMenuThreshold && !mSwipeHelper.isDismissGesture(ev)) {
|
||||
// Haven't moved enough to unsnap from the menu
|
||||
showMenu(animView, menuSnapTarget, velocity);
|
||||
} else if (mSwipeHelper.isDismissGesture(ev) && !shouldSnapTo) {
|
||||
// Only dismiss if we're not moving towards the menu
|
||||
dismiss(animView, velocity);
|
||||
} else {
|
||||
snapBack(animView, velocity);
|
||||
}
|
||||
} else if (!mSwipeHelper.isFalseGesture(ev)
|
||||
&& (swipedEnoughToShowMenu() && (!gestureFastEnough || showMenuForSlowOnGoing))
|
||||
|| (gestureTowardsMenu && !mSwipeHelper.isDismissGesture(ev))) {
|
||||
// Menu has not been snapped to previously and this is menu revealing gesture
|
||||
showMenu(animView, menuSnapTarget, velocity);
|
||||
} else if (mSwipeHelper.isDismissGesture(ev) && !gestureTowardsMenu) {
|
||||
dismiss(animView, velocity);
|
||||
} else {
|
||||
snapBack(animView, velocity);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private void showMenu(View animView, float targetLeft, float velocity) {
|
||||
mMenuSnappedTo = true;
|
||||
mMenuSnappedOnLeft = mOnLeft;
|
||||
mMenuListener.onMenuShown(animView);
|
||||
mSwipeHelper.snap(animView, targetLeft, velocity);
|
||||
@VisibleForTesting
|
||||
protected void beginDrag() {
|
||||
mSnapping = false;
|
||||
if (mFadeAnimator != null) {
|
||||
mFadeAnimator.cancel();
|
||||
}
|
||||
mHandler.removeCallbacks(mCheckForDrag);
|
||||
mCheckForDrag = null;
|
||||
mIsUserTouching = true;
|
||||
}
|
||||
|
||||
private void snapBack(View animView, float velocity) {
|
||||
@Override
|
||||
public void onTouchStart() {
|
||||
beginDrag();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSnapOpen() {
|
||||
mMenuSnapped = true;
|
||||
mMenuSnappedOnLeft = isMenuOnLeft();
|
||||
if (mMenuListener != null) {
|
||||
mMenuListener.onMenuShown(getParent());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSnapClosed() {
|
||||
cancelDrag();
|
||||
mMenuSnappedTo = false;
|
||||
mMenuSnapped = false;
|
||||
mSnapping = true;
|
||||
mSwipeHelper.snap(animView, 0 /* leftTarget */, velocity);
|
||||
}
|
||||
|
||||
private void dismiss(View animView, float velocity) {
|
||||
@Override
|
||||
public void onDismiss() {
|
||||
cancelDrag();
|
||||
mMenuSnappedTo = false;
|
||||
mMenuSnapped = false;
|
||||
mDismissing = true;
|
||||
mSwipeHelper.dismiss(animView, velocity);
|
||||
}
|
||||
|
||||
private void cancelDrag() {
|
||||
@VisibleForTesting
|
||||
protected void cancelDrag() {
|
||||
if (mFadeAnimator != null) {
|
||||
mFadeAnimator.cancel();
|
||||
}
|
||||
mHandler.removeCallbacks(mCheckForDrag);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return whether the notification has been translated enough to show the menu and not enough
|
||||
* to be dismissed.
|
||||
*/
|
||||
private boolean swipedEnoughToShowMenu() {
|
||||
final float multiplier = mParent.canViewBeDismissed()
|
||||
@VisibleForTesting
|
||||
protected float getMinimumSwipeDistance() {
|
||||
final float multiplier = getParent().canViewBeDismissed()
|
||||
? SWIPED_FAR_ENOUGH_MENU_FRACTION
|
||||
: SWIPED_FAR_ENOUGH_MENU_UNCLEARABLE_FRACTION;
|
||||
final float minimumSwipeDistance = mHorizSpaceForIcon * multiplier;
|
||||
return !mSwipeHelper.swipedFarEnough(0, 0) && isMenuVisible()
|
||||
&& (mOnLeft ? mTranslation > minimumSwipeDistance
|
||||
: mTranslation < -minimumSwipeDistance);
|
||||
return mHorizSpaceForIcon * multiplier;
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
protected float getMaximumSwipeDistance() {
|
||||
return mHorizSpaceForIcon * SWIPED_BACK_ENOUGH_TO_COVER_FRACTION;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the gesture is towards the menu location or not.
|
||||
*/
|
||||
private boolean isTowardsMenu(float movement) {
|
||||
@Override
|
||||
public boolean isTowardsMenu(float movement) {
|
||||
return isMenuVisible()
|
||||
&& ((mOnLeft && movement <= 0)
|
||||
|| (!mOnLeft && movement >= 0));
|
||||
&& ((isMenuOnLeft() && movement <= 0)
|
||||
|| (!isMenuOnLeft() && movement >= 0));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -445,7 +401,7 @@ public class NotificationMenuRow implements NotificationMenuRowPlugin, View.OnCl
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onHeightUpdate() {
|
||||
public void onParentHeightUpdate() {
|
||||
if (mParent == null || mMenuItems.size() == 0 || mMenuContainer == null) {
|
||||
return;
|
||||
}
|
||||
@@ -460,7 +416,7 @@ public class NotificationMenuRow implements NotificationMenuRowPlugin, View.OnCl
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTranslationUpdate(float translation) {
|
||||
public void onParentTranslationUpdate(float translation) {
|
||||
mTranslation = translation;
|
||||
if (mAnimating || !mMenuFadedIn) {
|
||||
// Don't adjust when animating, or if the menu hasn't been shown yet.
|
||||
@@ -492,13 +448,15 @@ public class NotificationMenuRow implements NotificationMenuRowPlugin, View.OnCl
|
||||
final int x = mIconLocation[0] - mParentLocation[0] + centerX;
|
||||
final int y = mIconLocation[1] - mParentLocation[1] + centerY;
|
||||
final int index = mMenuContainer.indexOfChild(v);
|
||||
mMenuListener.onMenuClicked(mParent, x, y, mMenuItems.get(index));
|
||||
if (mMenuListener != null) {
|
||||
mMenuListener.onMenuClicked(mParent, x, y, mMenuItems.get(index));
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isMenuLocationChange() {
|
||||
boolean onLeft = mTranslation > mIconPadding;
|
||||
boolean onRight = mTranslation < -mIconPadding;
|
||||
if ((mOnLeft && onRight) || (!mOnLeft && onLeft)) {
|
||||
if ((isMenuOnLeft() && onRight) || (!isMenuOnLeft() && onLeft)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
@@ -506,7 +464,7 @@ public class NotificationMenuRow implements NotificationMenuRowPlugin, View.OnCl
|
||||
|
||||
private void setMenuLocation() {
|
||||
boolean showOnLeft = mTranslation > 0;
|
||||
if ((mIconsPlaced && showOnLeft == mOnLeft) || mSnapping || mMenuContainer == null
|
||||
if ((mIconsPlaced && showOnLeft == isMenuOnLeft()) || isSnapping() || mMenuContainer == null
|
||||
|| !mMenuContainer.isAttachedToWindow()) {
|
||||
// Do nothing
|
||||
return;
|
||||
@@ -522,7 +480,8 @@ public class NotificationMenuRow implements NotificationMenuRowPlugin, View.OnCl
|
||||
mIconsPlaced = true;
|
||||
}
|
||||
|
||||
private void setMenuAlpha(float alpha) {
|
||||
@VisibleForTesting
|
||||
protected void setMenuAlpha(float alpha) {
|
||||
mAlpha = alpha;
|
||||
if (mMenuContainer == null) {
|
||||
return;
|
||||
@@ -542,7 +501,8 @@ public class NotificationMenuRow implements NotificationMenuRowPlugin, View.OnCl
|
||||
/**
|
||||
* Returns the horizontal space in pixels required to display the menu.
|
||||
*/
|
||||
private float getSpaceForMenu() {
|
||||
@VisibleForTesting
|
||||
protected int getSpaceForMenu() {
|
||||
return mHorizSpaceForIcon * mMenuContainer.getChildCount();
|
||||
}
|
||||
|
||||
@@ -646,12 +606,71 @@ public class NotificationMenuRow implements NotificationMenuRowPlugin, View.OnCl
|
||||
parent.addView(menuView);
|
||||
menuView.setOnClickListener(this);
|
||||
FrameLayout.LayoutParams lp = (LayoutParams) menuView.getLayoutParams();
|
||||
lp.width = (int) mHorizSpaceForIcon;
|
||||
lp.height = (int) mHorizSpaceForIcon;
|
||||
lp.width = mHorizSpaceForIcon;
|
||||
lp.height = mHorizSpaceForIcon;
|
||||
menuView.setLayoutParams(lp);
|
||||
}
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
/**
|
||||
* Determine the minimum offset below which the menu should snap back closed.
|
||||
*/
|
||||
protected float getSnapBackThreshold() {
|
||||
return getSpaceForMenu() - getMaximumSwipeDistance();
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine the maximum offset above which the parent notification should be dismissed.
|
||||
* @return
|
||||
*/
|
||||
@VisibleForTesting
|
||||
protected float getDismissThreshold() {
|
||||
return getParent().getWidth() * SWIPED_FAR_ENOUGH_SIZE_FRACTION;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isWithinSnapMenuThreshold() {
|
||||
float translation = getTranslation();
|
||||
float snapBackThreshold = getSnapBackThreshold();
|
||||
float targetRight = getDismissThreshold();
|
||||
return isMenuOnLeft()
|
||||
? translation > snapBackThreshold && translation < targetRight
|
||||
: translation < -snapBackThreshold && translation > -targetRight;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSwipedEnoughToShowMenu() {
|
||||
final float minimumSwipeDistance = getMinimumSwipeDistance();
|
||||
final float translation = getTranslation();
|
||||
return isMenuVisible() && (isMenuOnLeft() ?
|
||||
translation > minimumSwipeDistance
|
||||
: translation < -minimumSwipeDistance);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMenuSnapTarget() {
|
||||
return isMenuOnLeft() ? getSpaceForMenu() : -getSpaceForMenu();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shouldSnapBack() {
|
||||
float translation = getTranslation();
|
||||
float targetLeft = getSnapBackThreshold();
|
||||
return isMenuOnLeft() ? translation < targetLeft : translation > -targetLeft;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSnappedAndOnSameSide() {
|
||||
return isMenuSnapped() && isMenuVisible()
|
||||
&& isMenuSnappedOnLeft() == isMenuOnLeft();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canBeDismissed() {
|
||||
return getParent().canViewBeDismissed();
|
||||
}
|
||||
|
||||
public static class NotificationMenuItem implements MenuItem {
|
||||
View mMenuView;
|
||||
GutsContent mGutsContent;
|
||||
|
||||
@@ -26,7 +26,6 @@ import android.animation.PropertyValuesHolder;
|
||||
import android.animation.TimeAnimator;
|
||||
import android.animation.ValueAnimator;
|
||||
import android.animation.ValueAnimator.AnimatorUpdateListener;
|
||||
import android.annotation.FloatRange;
|
||||
import android.annotation.Nullable;
|
||||
import android.app.WallpaperManager;
|
||||
import android.content.Context;
|
||||
@@ -5315,13 +5314,15 @@ public class NotificationStackScrollLayout extends ViewGroup
|
||||
void flingTopOverscroll(float velocity, boolean open);
|
||||
}
|
||||
|
||||
@ShadeViewRefactor(RefactorComponent.INPUT)
|
||||
private class NotificationSwipeHelper extends SwipeHelper
|
||||
@ShadeViewRefactor(RefactorComponent.INPUT)
|
||||
private class NotificationSwipeHelper extends SwipeHelper
|
||||
implements NotificationSwipeActionHelper {
|
||||
private static final long COVER_MENU_DELAY = 4000;
|
||||
private Runnable mFalsingCheck;
|
||||
private Handler mHandler;
|
||||
|
||||
private static final long SWIPE_MENU_TIMING = 200;
|
||||
|
||||
public NotificationSwipeHelper(int swipeDirection, Callback callback, Context context) {
|
||||
super(swipeDirection, callback, context);
|
||||
mHandler = new Handler();
|
||||
@@ -5337,7 +5338,7 @@ public class NotificationStackScrollLayout extends ViewGroup
|
||||
public void onDownUpdate(View currView, MotionEvent ev) {
|
||||
mTranslatingParentView = currView;
|
||||
if (mCurrMenuRow != null) {
|
||||
mCurrMenuRow.onTouchEvent(currView, ev, 0 /* velocity */);
|
||||
mCurrMenuRow.onTouchStart();
|
||||
}
|
||||
mCurrMenuRow = null;
|
||||
mHandler.removeCallbacks(mFalsingCheck);
|
||||
@@ -5350,18 +5351,21 @@ public class NotificationStackScrollLayout extends ViewGroup
|
||||
|
||||
if (row.getEntry().hasFinishedInitialization()) {
|
||||
mCurrMenuRow = row.createMenu();
|
||||
mCurrMenuRow.setSwipeActionHelper(NotificationSwipeHelper.this);
|
||||
mCurrMenuRow.setMenuClickListener(NotificationStackScrollLayout.this);
|
||||
mCurrMenuRow.onTouchEvent(currView, ev, 0 /* velocity */);
|
||||
mCurrMenuRow.onTouchStart();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private boolean swipedEnoughToShowMenu(NotificationMenuRowPlugin menuRow) {
|
||||
return !swipedFarEnough() && menuRow.isSwipedEnoughToShowMenu();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onMoveUpdate(View view, MotionEvent ev, float translation, float delta) {
|
||||
mHandler.removeCallbacks(mFalsingCheck);
|
||||
if (mCurrMenuRow != null) {
|
||||
mCurrMenuRow.onTouchEvent(view, ev, 0 /* velocity */);
|
||||
mCurrMenuRow.onTouchMove(delta);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5369,11 +5373,91 @@ public class NotificationStackScrollLayout extends ViewGroup
|
||||
public boolean handleUpEvent(MotionEvent ev, View animView, float velocity,
|
||||
float translation) {
|
||||
if (mCurrMenuRow != null) {
|
||||
return mCurrMenuRow.onTouchEvent(animView, ev, velocity);
|
||||
mCurrMenuRow.onTouchEnd();
|
||||
handleMenuRowSwipe(ev, animView, velocity, mCurrMenuRow);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean swipedFarEnough(float translation, float viewSize) {
|
||||
return swipedFarEnough();
|
||||
}
|
||||
|
||||
private void handleMenuRowSwipe(MotionEvent ev, View animView, float velocity,
|
||||
NotificationMenuRowPlugin menuRow) {
|
||||
if (!menuRow.shouldShowMenu()) {
|
||||
// If the menu should not be shown, then there is no need to check if the a swipe
|
||||
// should result in a snapping to the menu. As a result, just check if the swipe
|
||||
// was enough to dismiss the notification.
|
||||
if (isDismissGesture(ev)) {
|
||||
dismiss(animView, velocity);
|
||||
} else {
|
||||
snapBack(animView, velocity);
|
||||
menuRow.onSnapClosed();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (menuRow.isSnappedAndOnSameSide()) {
|
||||
// Menu was snapped to previously and we're on the same side
|
||||
handleSwipeFromSnap(ev, animView, velocity, menuRow);
|
||||
} else {
|
||||
// Menu has not been snapped, or was snapped previously but is now on
|
||||
// the opposite side.
|
||||
handleSwipeFromNonSnap(ev, animView, velocity, menuRow);
|
||||
}
|
||||
}
|
||||
|
||||
private void handleSwipeFromNonSnap(MotionEvent ev, View animView, float velocity,
|
||||
NotificationMenuRowPlugin menuRow) {
|
||||
boolean isDismissGesture = isDismissGesture(ev);
|
||||
final boolean gestureTowardsMenu = menuRow.isTowardsMenu(velocity);
|
||||
final boolean gestureFastEnough =
|
||||
mSwipeHelper.getMinDismissVelocity() <= Math.abs(velocity);
|
||||
|
||||
final double timeForGesture = ev.getEventTime() - ev.getDownTime();
|
||||
final boolean showMenuForSlowOnGoing = !menuRow.canBeDismissed()
|
||||
&& timeForGesture >= SWIPE_MENU_TIMING;
|
||||
|
||||
if (!isFalseGesture(ev)
|
||||
&& (swipedEnoughToShowMenu(menuRow)
|
||||
&& (!gestureFastEnough || showMenuForSlowOnGoing))
|
||||
|| (gestureTowardsMenu && !isDismissGesture)) {
|
||||
// Menu has not been snapped to previously and this is menu revealing gesture
|
||||
snapOpen(animView, menuRow.getMenuSnapTarget(), velocity);
|
||||
menuRow.onSnapOpen();
|
||||
} else if (isDismissGesture(ev) && !gestureTowardsMenu) {
|
||||
dismiss(animView, velocity);
|
||||
menuRow.onDismiss();
|
||||
} else {
|
||||
snapBack(animView, velocity);
|
||||
menuRow.onSnapClosed();
|
||||
}
|
||||
}
|
||||
|
||||
private void handleSwipeFromSnap(MotionEvent ev, View animView, float velocity,
|
||||
NotificationMenuRowPlugin menuRow) {
|
||||
boolean isDismissGesture = isDismissGesture(ev);
|
||||
|
||||
final boolean withinSnapMenuThreshold =
|
||||
menuRow.isWithinSnapMenuThreshold();
|
||||
|
||||
if (withinSnapMenuThreshold && !isDismissGesture) {
|
||||
// Haven't moved enough to unsnap from the menu
|
||||
menuRow.onSnapOpen();
|
||||
snapOpen(animView, menuRow.getMenuSnapTarget(), velocity);
|
||||
} else if (isDismissGesture && !menuRow.shouldSnapBack()) {
|
||||
// Only dismiss if we're not moving towards the menu
|
||||
dismiss(animView, velocity);
|
||||
menuRow.onDismiss();
|
||||
} else {
|
||||
snapBack(animView, velocity);
|
||||
menuRow.onSnapClosed();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dismissChild(final View view, float velocity,
|
||||
boolean useAccelerateInterpolator) {
|
||||
@@ -5403,10 +5487,6 @@ public class NotificationStackScrollLayout extends ViewGroup
|
||||
mStatusBar.setNotificationSnoozed(sbn, snoozeOption);
|
||||
}
|
||||
|
||||
public boolean isFalseGesture(MotionEvent ev) {
|
||||
return super.isFalseGesture(ev);
|
||||
}
|
||||
|
||||
private void handleMenuCoveredOrDismissed() {
|
||||
if (mMenuExposedView != null && mMenuExposedView == mTranslatingParentView) {
|
||||
mMenuExposedView = null;
|
||||
@@ -5440,13 +5520,12 @@ public class NotificationStackScrollLayout extends ViewGroup
|
||||
}
|
||||
|
||||
@Override
|
||||
public void snap(View animView, float targetLeft, float velocity) {
|
||||
public void snapOpen(View animView, int targetLeft, float velocity) {
|
||||
snapChild(animView, targetLeft, velocity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean swipedFarEnough(float translation, float viewSize) {
|
||||
return swipedFarEnough();
|
||||
private void snapBack(View animView, float velocity) {
|
||||
snapChild(animView, 0, velocity);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -15,10 +15,15 @@
|
||||
package com.android.systemui.statusbar.notification.row;
|
||||
|
||||
import static junit.framework.Assert.assertEquals;
|
||||
import static junit.framework.Assert.assertFalse;
|
||||
import static junit.framework.Assert.assertTrue;
|
||||
|
||||
import static org.mockito.Mockito.doReturn;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
import static org.mockito.Mockito.doNothing;
|
||||
|
||||
import android.app.Notification;
|
||||
import android.service.notification.StatusBarNotification;
|
||||
@@ -27,7 +32,6 @@ import android.testing.AndroidTestingRunner;
|
||||
import android.testing.TestableLooper;
|
||||
import android.testing.TestableLooper.RunWithLooper;
|
||||
import android.testing.ViewUtils;
|
||||
import android.testing.ViewUtils;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
|
||||
@@ -36,6 +40,7 @@ import com.android.systemui.utils.leaks.LeakCheckedTest;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mockito;
|
||||
|
||||
@RunWith(AndroidTestingRunner.class)
|
||||
@RunWithLooper(setAsMainLooper = true)
|
||||
@@ -72,6 +77,7 @@ public class NotificationMenuRowTest extends LeakCheckedTest {
|
||||
row.resetMenu();
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testNoAppOpsInSlowSwipe() {
|
||||
NotificationMenuRow row = new NotificationMenuRow(mContext);
|
||||
@@ -86,4 +92,255 @@ public class NotificationMenuRowTest extends LeakCheckedTest {
|
||||
// one for snooze and one for noti blocking
|
||||
assertEquals(2, container.getChildCount());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIsSnappedAndOnSameSide() {
|
||||
NotificationMenuRow row = Mockito.spy(new NotificationMenuRow((mContext)));
|
||||
|
||||
when(row.isMenuVisible()).thenReturn(true);
|
||||
when(row.isMenuSnapped()).thenReturn(true);
|
||||
when(row.isMenuOnLeft()).thenReturn(true);
|
||||
when(row.isMenuSnappedOnLeft()).thenReturn(true);
|
||||
|
||||
assertTrue("Showing on left and on left", row.isSnappedAndOnSameSide());
|
||||
|
||||
|
||||
when(row.isMenuOnLeft()).thenReturn(false);
|
||||
when(row.isMenuSnappedOnLeft()).thenReturn(false);
|
||||
assertTrue("Snapped to right and on right", row.isSnappedAndOnSameSide());
|
||||
|
||||
when(row.isMenuOnLeft()).thenReturn(true);
|
||||
when(row.isMenuSnapped()).thenReturn(false);
|
||||
assertFalse("Snapped to right and on left", row.isSnappedAndOnSameSide());
|
||||
|
||||
when(row.isMenuOnLeft()).thenReturn(true);
|
||||
when(row.isMenuSnappedOnLeft()).thenReturn(true);
|
||||
when(row.isMenuVisible()).thenReturn(false);
|
||||
assertFalse("Snapped to left and on left, but menu not visible",
|
||||
row.isSnappedAndOnSameSide());
|
||||
|
||||
when(row.isMenuVisible()).thenReturn(true);
|
||||
when(row.isMenuSnapped()).thenReturn(false);
|
||||
assertFalse("Snapped to left and on left, but not actually snapped to",
|
||||
row.isSnappedAndOnSameSide());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetMenuSnapTarget() {
|
||||
NotificationMenuRow row = Mockito.spy(new NotificationMenuRow((mContext)));
|
||||
when(row.isMenuOnLeft()).thenReturn(true);
|
||||
doReturn(30).when(row).getSpaceForMenu();
|
||||
|
||||
assertEquals("When on left, snap target is space for menu",
|
||||
30, row.getMenuSnapTarget());
|
||||
|
||||
when(row.isMenuOnLeft()).thenReturn(false);
|
||||
assertEquals("When on right, snap target is negative space for menu",
|
||||
-30, row.getMenuSnapTarget());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIsSwipedEnoughToShowMenu() {
|
||||
NotificationMenuRow row = Mockito.spy(new NotificationMenuRow((mContext)));
|
||||
when(row.isMenuVisible()).thenReturn(true);
|
||||
when(row.isMenuOnLeft()).thenReturn(true);
|
||||
doReturn(40f).when(row).getMinimumSwipeDistance();
|
||||
|
||||
when(row.getTranslation()).thenReturn(30f);
|
||||
assertFalse("on left, translation is less than min", row.isSwipedEnoughToShowMenu());
|
||||
|
||||
when(row.getTranslation()).thenReturn(50f);
|
||||
assertTrue("on left, translation is greater than min", row.isSwipedEnoughToShowMenu());
|
||||
|
||||
when(row.isMenuOnLeft()).thenReturn(false);
|
||||
when(row.getTranslation()).thenReturn(-30f);
|
||||
assertFalse("on right, translation is greater than -min", row.isSwipedEnoughToShowMenu());
|
||||
|
||||
when(row.getTranslation()).thenReturn(-50f);
|
||||
assertTrue("on right, translation is less than -min", row.isSwipedEnoughToShowMenu());
|
||||
|
||||
when(row.isMenuVisible()).thenReturn(false);
|
||||
when(row.getTranslation()).thenReturn(30f);
|
||||
assertFalse("on left, translation greater than min, but not visible",
|
||||
row.isSwipedEnoughToShowMenu());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIsWithinSnapMenuThreshold() {
|
||||
NotificationMenuRow row = Mockito.spy(new NotificationMenuRow((mContext)));
|
||||
doReturn(30f).when(row).getSnapBackThreshold();
|
||||
doReturn(50f).when(row).getDismissThreshold();
|
||||
|
||||
when(row.isMenuOnLeft()).thenReturn(true);
|
||||
when(row.getTranslation()).thenReturn(40f);
|
||||
assertTrue("When on left, translation is between min and max",
|
||||
row.isWithinSnapMenuThreshold());
|
||||
|
||||
when(row.getTranslation()).thenReturn(20f);
|
||||
assertFalse("When on left, translation is less than min",
|
||||
row.isWithinSnapMenuThreshold());
|
||||
|
||||
when(row.getTranslation()).thenReturn(60f);
|
||||
assertFalse("When on left, translation is greater than max",
|
||||
row.isWithinSnapMenuThreshold());
|
||||
|
||||
when(row.isMenuOnLeft()).thenReturn(false);
|
||||
when(row.getTranslation()).thenReturn(-40f);
|
||||
assertTrue("When on right, translation is between -min and -max",
|
||||
row.isWithinSnapMenuThreshold());
|
||||
|
||||
when(row.getTranslation()).thenReturn(-20f);
|
||||
assertFalse("When on right, translation is greater than -min",
|
||||
row.isWithinSnapMenuThreshold());
|
||||
|
||||
when(row.getTranslation()).thenReturn(-60f);
|
||||
assertFalse("When on right, translation is less than -max",
|
||||
row.isWithinSnapMenuThreshold());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testShouldSnapBack() {
|
||||
NotificationMenuRow row = Mockito.spy(new NotificationMenuRow((mContext)));
|
||||
doReturn(40f).when(row).getSnapBackThreshold();
|
||||
when(row.isMenuVisible()).thenReturn(false);
|
||||
when(row.isMenuOnLeft()).thenReturn(true);
|
||||
|
||||
when(row.getTranslation()).thenReturn(50f);
|
||||
assertFalse("On left, translation greater than minimum target", row.shouldSnapBack());
|
||||
|
||||
when(row.getTranslation()).thenReturn(30f);
|
||||
assertTrue("On left, translation less than minimum target", row.shouldSnapBack());
|
||||
|
||||
when(row.isMenuOnLeft()).thenReturn(false);
|
||||
when(row.getTranslation()).thenReturn(-50f);
|
||||
assertFalse("On right, translation less than minimum target", row.shouldSnapBack());
|
||||
|
||||
when(row.getTranslation()).thenReturn(-30f);
|
||||
assertTrue("On right, translation greater than minimum target", row.shouldSnapBack());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCanBeDismissed() {
|
||||
NotificationMenuRow row = Mockito.spy(new NotificationMenuRow((mContext)));
|
||||
ExpandableNotificationRow parent = mock(ExpandableNotificationRow.class);
|
||||
|
||||
when(row.getParent()).thenReturn(parent);
|
||||
when(parent.canViewBeDismissed()).thenReturn(true);
|
||||
|
||||
assertTrue("Row can be dismissed if parent can be dismissed", row.canBeDismissed());
|
||||
|
||||
when(parent.canViewBeDismissed()).thenReturn(false);
|
||||
assertFalse("Row cannot be dismissed if parent cannot be dismissed",
|
||||
row.canBeDismissed());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIsTowardsMenu() {
|
||||
NotificationMenuRow row = Mockito.spy(new NotificationMenuRow((mContext)));
|
||||
when(row.isMenuVisible()).thenReturn(true);
|
||||
when(row.isMenuOnLeft()).thenReturn(true);
|
||||
|
||||
assertTrue("menu on left, movement is negative", row.isTowardsMenu(-30f));
|
||||
assertFalse("menu on left, movement is positive", row.isTowardsMenu(30f));
|
||||
assertTrue("menu on left, movement is 0", row.isTowardsMenu(0f));
|
||||
|
||||
when(row.isMenuOnLeft()).thenReturn(false);
|
||||
assertTrue("menu on right, movement is positive", row.isTowardsMenu(30f));
|
||||
assertFalse("menu on right, movement is negative", row.isTowardsMenu(-30f));
|
||||
assertTrue("menu on right, movement is 0", row.isTowardsMenu(0f));
|
||||
|
||||
when(row.isMenuVisible()).thenReturn(false);
|
||||
assertFalse("menu on left, movement is negative, but menu not visible",
|
||||
row.isTowardsMenu(-30f));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onSnapBack() {
|
||||
NotificationMenuRow row = Mockito.spy(new NotificationMenuRow((mContext)));
|
||||
NotificationMenuRowPlugin.OnMenuEventListener listener = mock(NotificationMenuRowPlugin
|
||||
.OnMenuEventListener.class);
|
||||
row.setMenuClickListener(listener);
|
||||
ExpandableNotificationRow parent = mock(ExpandableNotificationRow.class);
|
||||
when(row.getParent()).thenReturn(parent);
|
||||
doNothing().when(row).cancelDrag();
|
||||
|
||||
row.onSnapOpen();
|
||||
|
||||
assertTrue("before onSnapClosed, row is snapped to", row.isMenuSnapped());
|
||||
assertFalse("before onSnapClosed, row is not snapping", row.isSnapping());
|
||||
|
||||
row.onSnapClosed();
|
||||
|
||||
assertFalse("after onSnapClosed, row is not snapped to", row.isMenuSnapped());
|
||||
assertTrue("after onSnapClosed, row is snapping", row.isSnapping());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOnSnap() {
|
||||
NotificationMenuRow row = Mockito.spy(new NotificationMenuRow((mContext)));
|
||||
when(row.isMenuOnLeft()).thenReturn(true);
|
||||
NotificationMenuRowPlugin.OnMenuEventListener listener = mock(NotificationMenuRowPlugin
|
||||
.OnMenuEventListener.class);
|
||||
row.setMenuClickListener(listener);
|
||||
ExpandableNotificationRow parent = mock(ExpandableNotificationRow.class);
|
||||
when(row.getParent()).thenReturn(parent);
|
||||
|
||||
assertFalse("before onSnapOpen, row is not snapped to", row.isMenuSnapped());
|
||||
assertFalse("before onSnapOpen, row is not snapped on left", row.isMenuSnappedOnLeft());
|
||||
|
||||
row.onSnapOpen();
|
||||
|
||||
assertTrue("after onSnapOpen, row is snapped to", row.isMenuSnapped());
|
||||
assertTrue("after onSnapOpen, row is snapped on left", row.isMenuSnapped());
|
||||
verify(listener, times(1)).onMenuShown(parent);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOnDismiss() {
|
||||
NotificationMenuRow row = Mockito.spy(new NotificationMenuRow((mContext)));
|
||||
doNothing().when(row).cancelDrag();
|
||||
row.onSnapOpen();
|
||||
|
||||
assertFalse("before onDismiss, row is not dismissing", row.isDismissing());
|
||||
assertTrue("before onDismiss, row is showing", row.isMenuSnapped());
|
||||
|
||||
row.onDismiss();
|
||||
|
||||
verify(row, times(1)).cancelDrag();
|
||||
assertTrue("after onDismiss, row is dismissing", row.isDismissing());
|
||||
assertFalse("after onDismiss, row is not showing", row.isMenuSnapped());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOnDown() {
|
||||
NotificationMenuRow row = Mockito.spy(new NotificationMenuRow((mContext)));
|
||||
doNothing().when(row).beginDrag();
|
||||
|
||||
row.onTouchStart();
|
||||
|
||||
verify(row, times(1)).beginDrag();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOnUp() {
|
||||
NotificationMenuRow row = Mockito.spy(new NotificationMenuRow((mContext)));
|
||||
row.onTouchStart();
|
||||
|
||||
assertTrue("before onTouchEnd, isUserTouching is true", row.isUserTouching());
|
||||
|
||||
row.onTouchEnd();
|
||||
|
||||
assertFalse("after onTouchEnd, isUserTouching is false", row.isUserTouching());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIsMenuVisible() {
|
||||
NotificationMenuRow row = Mockito.spy(new NotificationMenuRow((mContext)));
|
||||
row.setMenuAlpha(0);
|
||||
|
||||
assertFalse("when alpha is 0, menu is not visible", row.isMenuVisible());
|
||||
|
||||
row.setMenuAlpha(0.5f);
|
||||
assertTrue("when alpha is .5, menu is visible", row.isMenuVisible());
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user