This source file includes following definitions.
- SuppressLint
- onShow
- onDismiss
- hardwareMenuButtonUp
- handleDragging
- isInSwipeOutRegion
- getShortestDistanceFromEdge
- menuItemAction
- getDistanceFromHardwareMenuButtonSideEdge
- getScreenVisibleRect
- getEdgeSwipeInSlop
package org.chromium.chrome.browser.appmenu;
import android.animation.TimeAnimator;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.content.res.Resources;
import android.graphics.Point;
import android.graphics.Rect;
import android.os.SystemClock;
import android.util.Log;
import android.view.Display;
import android.view.MotionEvent;
import android.view.Surface;
import android.view.View;
import android.view.View.OnTouchListener;
import android.view.ViewConfiguration;
import android.view.ViewParent;
import android.widget.ImageButton;
import android.widget.LinearLayout;
import android.widget.ListPopupWindow;
import android.widget.ListView;
import org.chromium.chrome.R;
import org.chromium.chrome.browser.UmaBridge;
import java.util.ArrayList;
@SuppressLint("NewApi")
class AppMenuDragHelper {
private static final String TAG = "AppMenuDragHelper";
private final Activity mActivity;
private final AppMenu mAppMenu;
private static final int ITEM_ACTION_HIGHLIGHT = 0;
private static final int ITEM_ACTION_PERFORM = 1;
private static final int ITEM_ACTION_CLEAR_HIGHLIGHT_ALL = 2;
private static final float AUTO_SCROLL_AREA_MAX_RATIO = 0.25f;
private static final int EDGE_SWIPE_IN_ADDITIONAL_SLOP_TIME_MS = 500;
private final float mAutoScrollFullVelocity;
private final int mEdgeSwipeInSlop;
private final int mEdgeSwipeInAdditionalSlop;
private final int mEdgeSwipeOutSlop;
private int mScaledTouchSlop;
private long mHardwareMenuButtonUpTime;
private boolean mDragPending;
private final TimeAnimator mDragScrolling = new TimeAnimator();
private float mDragScrollOffset;
private int mDragScrollOffsetRounded;
private volatile float mDragScrollingVelocity;
private volatile float mLastTouchX;
private volatile float mLastTouchY;
private float mTopTouchMovedBound;
private float mBottomTouchMovedBound;
private boolean mIsDownScrollable;
private boolean mIsUpScrollable;
private boolean mIsByHardwareButton;
private int mCurrentScreenRotation = -1;
private final int mItemRowHeight;
private final Rect mScreenVisibleRect = new Rect();
private final int[] mScreenVisiblePoint = new int[2];
private final OnTouchListener mDragScrollTouchEventForwarder = new OnTouchListener() {
@Override
public boolean onTouch(View view, MotionEvent event) {
return handleDragging(event);
}
};
AppMenuDragHelper(Activity activity, AppMenu appMenu, int itemRowHeight) {
mActivity = activity;
mAppMenu = appMenu;
mItemRowHeight = itemRowHeight;
mScaledTouchSlop = ViewConfiguration.get(
mActivity.getApplicationContext()).getScaledTouchSlop();
Resources res = mActivity.getResources();
mAutoScrollFullVelocity = res.getDimensionPixelSize(R.dimen.auto_scroll_full_velocity);
mEdgeSwipeInSlop = res.getDimensionPixelSize(R.dimen.edge_swipe_in_slop);
mEdgeSwipeInAdditionalSlop = res.getDimensionPixelSize(
R.dimen.edge_swipe_in_additional_slop);
mEdgeSwipeOutSlop = res.getDimensionPixelSize(R.dimen.edge_swipe_out_slop);
mDragScrolling.setTimeListener(new TimeAnimator.TimeListener() {
@Override
public void onTimeUpdate(TimeAnimator animation, long totalTime, long deltaTime) {
ListPopupWindow popup = mAppMenu.getPopup();
if (popup == null || popup.getListView() == null) return;
mDragScrollOffset += (deltaTime * 0.001f) * mDragScrollingVelocity;
int diff = Math.round(mDragScrollOffset - mDragScrollOffsetRounded);
mDragScrollOffsetRounded += diff;
popup.getListView().smoothScrollBy(diff, 0);
if (!Float.isNaN(mLastTouchX) && !Float.isNaN(mLastTouchY)) {
int actionToPerform = isInSwipeOutRegion(mLastTouchX, mLastTouchY) ?
ITEM_ACTION_CLEAR_HIGHLIGHT_ALL : ITEM_ACTION_HIGHLIGHT;
menuItemAction(Math.round(mLastTouchX), Math.round(mLastTouchY),
actionToPerform);
}
}
});
}
void onShow(boolean isByHardwareButton, boolean startDragging) {
mCurrentScreenRotation = mActivity.getWindowManager().getDefaultDisplay().getRotation();
mLastTouchX = Float.NaN;
mLastTouchY = Float.NaN;
mDragScrollOffset = 0.0f;
mDragScrollOffsetRounded = 0;
mDragScrollingVelocity = 0.0f;
mIsByHardwareButton = isByHardwareButton;
mDragPending = isByHardwareButton;
mIsDownScrollable = !isByHardwareButton;
mIsUpScrollable = !isByHardwareButton;
mTopTouchMovedBound = Float.POSITIVE_INFINITY;
mBottomTouchMovedBound = Float.NEGATIVE_INFINITY;
mHardwareMenuButtonUpTime = -1;
ListPopupWindow popup = mAppMenu.getPopup();
popup.getListView().setOnTouchListener(mDragScrollTouchEventForwarder);
ViewParent listViewParent = popup.getListView().getParent();
if (listViewParent instanceof View) {
((View) listViewParent).setOnTouchListener(mDragScrollTouchEventForwarder);
} else {
assert false;
}
if (!isByHardwareButton && startDragging) mDragScrolling.start();
}
void onDismiss() {
mDragScrolling.cancel();
}
public void hardwareMenuButtonUp() {
assert mHardwareMenuButtonUpTime == -1;
mHardwareMenuButtonUpTime = SystemClock.uptimeMillis();
}
boolean handleDragging(MotionEvent event) {
if (!mAppMenu.isShowing() || (!mDragPending && !mDragScrolling.isRunning())) return false;
final float rawX = event.getRawX();
final float rawY = event.getRawY();
final int roundedRawX = Math.round(rawX);
final int roundedRawY = Math.round(rawY);
final int eventActionMasked = event.getActionMasked();
final ListView listView = mAppMenu.getPopup().getListView();
mLastTouchX = rawX;
mLastTouchY = rawY;
mTopTouchMovedBound = Math.min(mTopTouchMovedBound, rawY);
mBottomTouchMovedBound = Math.max(mBottomTouchMovedBound, rawY);
if (rawY <= mBottomTouchMovedBound - mScaledTouchSlop) {
mIsUpScrollable = true;
}
if (rawY >= mTopTouchMovedBound + mScaledTouchSlop) {
mIsDownScrollable = true;
}
if (eventActionMasked == MotionEvent.ACTION_CANCEL) {
mAppMenu.dismiss();
return true;
}
if (eventActionMasked == MotionEvent.ACTION_DOWN) {
assert mIsByHardwareButton != mDragScrolling.isStarted();
if (mIsByHardwareButton) {
if (mDragPending && getDistanceFromHardwareMenuButtonSideEdge(rawX, rawY) <
getEdgeSwipeInSlop(event)) {
mDragScrolling.start();
mDragPending = false;
UmaBridge.usingMenu(true, true);
} else {
if (!getScreenVisibleRect(listView).contains(roundedRawX, roundedRawY)) {
mAppMenu.dismiss();
}
mDragPending = false;
UmaBridge.usingMenu(true, false);
return false;
}
}
}
if (!mDragScrolling.isRunning()) return false;
boolean didPerformClick = false;
int itemAction = ITEM_ACTION_CLEAR_HIGHLIGHT_ALL;
if (!isInSwipeOutRegion(rawX, rawY)) {
switch (eventActionMasked) {
case MotionEvent.ACTION_DOWN:
case MotionEvent.ACTION_MOVE:
itemAction = ITEM_ACTION_HIGHLIGHT;
break;
case MotionEvent.ACTION_UP:
itemAction = ITEM_ACTION_PERFORM;
break;
default:
break;
}
}
didPerformClick = menuItemAction(roundedRawX, roundedRawY, itemAction);
if (eventActionMasked == MotionEvent.ACTION_UP && !didPerformClick) {
mAppMenu.dismiss();
} else if (eventActionMasked == MotionEvent.ACTION_MOVE) {
if (listView.getHeight() > 0) {
float autoScrollAreaRatio = Math.min(AUTO_SCROLL_AREA_MAX_RATIO,
mItemRowHeight * 1.2f / listView.getHeight());
float normalizedY =
(rawY - getScreenVisibleRect(listView).top) / listView.getHeight();
if (mIsUpScrollable && normalizedY < autoScrollAreaRatio) {
mDragScrollingVelocity = (normalizedY / autoScrollAreaRatio - 1.0f)
* mAutoScrollFullVelocity;
} else if (mIsDownScrollable && normalizedY > 1.0f - autoScrollAreaRatio) {
mDragScrollingVelocity = ((normalizedY - 1.0f) / autoScrollAreaRatio + 1.0f)
* mAutoScrollFullVelocity;
} else {
mDragScrollingVelocity = 0.0f;
}
}
}
return true;
}
private boolean isInSwipeOutRegion(float rawX, float rawY) {
return getShortestDistanceFromEdge(rawX, rawY) < mEdgeSwipeOutSlop;
}
private float getShortestDistanceFromEdge(float rawX, float rawY) {
Display display = mActivity.getWindowManager().getDefaultDisplay();
Point displaySize = new Point();
display.getSize(displaySize);
float distance = Math.min(
Math.min(rawY, displaySize.y - rawY - 1),
Math.min(rawX, displaySize.x - rawX - 1));
if (distance < 0.0f) {
Log.d(TAG, "Received touch event out of the screen edge boundary. distance = " +
distance);
}
return Math.abs(distance);
}
private boolean menuItemAction(int screenX, int screenY, int action) {
ListView listView = mAppMenu.getPopup().getListView();
ArrayList<View> itemViews = new ArrayList<View>();
for (int i = 0; i < listView.getChildCount(); ++i) {
boolean hasImageButtons = false;
if (listView.getChildAt(i) instanceof LinearLayout) {
LinearLayout layout = (LinearLayout) listView.getChildAt(i);
for (int j = 0; j < layout.getChildCount(); ++j) {
itemViews.add(layout.getChildAt(j));
if (layout.getChildAt(j) instanceof ImageButton) hasImageButtons = true;
}
}
if (!hasImageButtons) itemViews.add(listView.getChildAt(i));
}
boolean didPerformClick = false;
for (int i = 0; i < itemViews.size(); ++i) {
View itemView = itemViews.get(i);
boolean shouldPerform = itemView.isEnabled() && itemView.isShown() &&
getScreenVisibleRect(itemView).contains(screenX, screenY);
switch (action) {
case ITEM_ACTION_HIGHLIGHT:
itemView.setPressed(shouldPerform);
break;
case ITEM_ACTION_PERFORM:
if (shouldPerform) {
itemView.performClick();
didPerformClick = true;
}
break;
case ITEM_ACTION_CLEAR_HIGHLIGHT_ALL:
itemView.setPressed(false);
break;
default:
assert false;
break;
}
}
return didPerformClick;
}
private float getDistanceFromHardwareMenuButtonSideEdge(float rawX, float rawY) {
Display display = mActivity.getWindowManager().getDefaultDisplay();
Point displaySize = new Point();
display.getSize(displaySize);
float distance;
switch (mCurrentScreenRotation) {
case Surface.ROTATION_0:
distance = displaySize.y - rawY - 1;
break;
case Surface.ROTATION_180:
distance = rawY;
break;
case Surface.ROTATION_90:
distance = displaySize.x - rawX - 1;
break;
case Surface.ROTATION_270:
distance = rawX;
break;
default:
distance = 0.0f;
assert false;
break;
}
if (distance < 0.0f) {
Log.d(TAG, "Received touch event out of hardware menu button side edge boundary." +
" distance = " + distance);
}
return Math.abs(distance);
}
private Rect getScreenVisibleRect(View view) {
view.getLocalVisibleRect(mScreenVisibleRect);
view.getLocationOnScreen(mScreenVisiblePoint);
mScreenVisibleRect.offset(mScreenVisiblePoint[0], mScreenVisiblePoint[1]);
return mScreenVisibleRect;
}
private float getEdgeSwipeInSlop(MotionEvent event) {
float edgeSwipeInSlope = mEdgeSwipeInSlop;
if (mHardwareMenuButtonUpTime == -1) {
edgeSwipeInSlope += mEdgeSwipeInAdditionalSlop;
} else {
float additionalEdgeSwipeInSlop = ((mHardwareMenuButtonUpTime - event.getEventTime()
+ EDGE_SWIPE_IN_ADDITIONAL_SLOP_TIME_MS) * 0.001f)
* mEdgeSwipeInAdditionalSlop;
edgeSwipeInSlope += Math.max(0.0f, additionalEdgeSwipeInSlop);
}
return edgeSwipeInSlope;
}
}