| Index: chrome/android/java/src/org/chromium/chrome/browser/appmenu/AppMenu.java
|
| diff --git a/chrome/android/java/src/org/chromium/chrome/browser/appmenu/AppMenu.java b/chrome/android/java/src/org/chromium/chrome/browser/appmenu/AppMenu.java
|
| index e8fa56dbcaea0c5100706dc73328f51bf034b7d3..40c7bc41a22d37824778c08a7339bd53e858fc44 100644
|
| --- a/chrome/android/java/src/org/chromium/chrome/browser/appmenu/AppMenu.java
|
| +++ b/chrome/android/java/src/org/chromium/chrome/browser/appmenu/AppMenu.java
|
| @@ -4,47 +4,32 @@
|
|
|
| package org.chromium.chrome.browser.appmenu;
|
|
|
| -import android.animation.TimeAnimator;
|
| import android.app.Activity;
|
| import android.content.Context;
|
| import android.content.res.Resources;
|
| -import android.graphics.Point;
|
| import android.graphics.Rect;
|
| -import android.graphics.drawable.Drawable;
|
| -import android.os.SystemClock;
|
| -import android.util.AttributeSet;
|
| -import android.util.Log;
|
| -import android.view.Display;
|
| import android.view.KeyEvent;
|
| import android.view.LayoutInflater;
|
| import android.view.Menu;
|
| import android.view.MenuItem;
|
| -import android.view.MotionEvent;
|
| import android.view.Surface;
|
| import android.view.View;
|
| import android.view.View.OnClickListener;
|
| import android.view.View.OnKeyListener;
|
| -import android.view.View.OnTouchListener;
|
| -import android.view.ViewConfiguration;
|
| import android.view.ViewGroup;
|
| -import android.view.ViewParent;
|
| import android.widget.AdapterView;
|
| -import android.widget.BaseAdapter;
|
| import android.widget.HeaderViewListAdapter;
|
| import android.widget.ImageButton;
|
| -import android.widget.ImageView;
|
| import android.widget.ListPopupWindow;
|
| import android.widget.ListView;
|
| import android.widget.ListView.FixedViewInfo;
|
| import android.widget.PopupWindow;
|
| import android.widget.PopupWindow.OnDismissListener;
|
| -import android.widget.TextView;
|
|
|
| import com.google.common.annotations.VisibleForTesting;
|
|
|
| import org.chromium.chrome.R;
|
| import org.chromium.chrome.browser.BookmarksBridge;
|
| -import org.chromium.chrome.browser.UmaBridge;
|
| import org.chromium.chrome.browser.util.KeyNavigationUtil;
|
|
|
| import java.util.ArrayList;
|
| @@ -57,19 +42,9 @@ import java.util.List;
|
| * - Disabled items are grayed out.
|
| */
|
| public class AppMenu implements AdapterView.OnItemClickListener, OnKeyListener {
|
| - private static final String TAG = "AppMenu";
|
| -
|
| private static final float LAST_ITEM_SHOW_FRACTION = 0.5f;
|
| private static final int DIVIDER_HEIGHT_DP = 1;
|
|
|
| - private static final float AUTO_SCROLL_AREA_MAX_RATIO = 0.25f;
|
| - private static final int EDGE_SWIPE_IN_ADDITIONAL_SLOP_TIME_MS = 500;
|
| -
|
| - // Internally used action constants for dragging.
|
| - 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 final Menu mMenu;
|
| private final Activity mActivity;
|
| private final int mItemRowHeight;
|
| @@ -79,46 +54,11 @@ public class AppMenu implements AdapterView.OnItemClickListener, OnKeyListener {
|
| private ListPopupWindow mPopup;
|
| private LayoutInflater mInflater;
|
| private boolean mShowIconRow;
|
| - private MenuAdapter mAdapter;
|
| - private ImageButton mBookmarkButton;
|
| + private AppMenuAdapter mAdapter;
|
| private View mIconRowView;
|
| private AppMenuHandler mHandler;
|
| -
|
| - // Dragging related variables, i.e., menu showing initiated by touch down and drag to navigate.
|
| - private final float mAutoScrollFullVelocity;
|
| - private final int mEdgeSwipeInSlop;
|
| - private final int mEdgeSwipeInAdditionalSlop;
|
| - private final int mEdgeSwipeOutSlop;
|
| - private int mScaledTouchSlop;
|
| - private long mHardwareMenuButtonUpTime;
|
| - private boolean mIsByHardwareButton;
|
| - 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 int mCurrentScreenRotation = -1;
|
| - private float mTopTouchMovedBound;
|
| - private float mBottomTouchMovedBound;
|
| - private boolean mIsDownScrollable;
|
| - private boolean mIsUpScrollable;
|
| -
|
| - // Sub-UI-controls, backward, forward, bookmark and listView, are getting a touch event first
|
| - // if the app menu is initiated by hardware menu button. For those cases, we need to
|
| - // conditionally forward the touch event to our drag scrolling method.
|
| - private final OnTouchListener mDragScrollTouchEventForwarder = new OnTouchListener() {
|
| - @Override
|
| - public boolean onTouch(View view, MotionEvent event) {
|
| - return AppMenu.this.handleDragging(event);
|
| - }
|
| - };
|
| -
|
| - // These are used in a function locally, but defined here to avoid heap allocation on every
|
| - // touch event.
|
| - private final Rect mScreenVisibleRect = new Rect();
|
| - private final int[] mScreenVisiblePoint = new int[2];
|
| + private boolean mIsByHardwareButton;
|
|
|
| /**
|
| * Creates and sets up the App Menu.
|
| @@ -129,8 +69,7 @@ public class AppMenu implements AdapterView.OnItemClickListener, OnKeyListener {
|
| AppMenu(Activity activity, Menu menu, int itemRowHeight, AppMenuHandler handler) {
|
| mActivity = activity;
|
| mMenu = menu;
|
| - mScaledTouchSlop =
|
| - ViewConfiguration.get(mActivity.getApplicationContext()).getScaledTouchSlop();
|
| +
|
| mItemRowHeight = itemRowHeight;
|
| assert mItemRowHeight > 0;
|
|
|
| @@ -142,50 +81,6 @@ public class AppMenu implements AdapterView.OnItemClickListener, OnKeyListener {
|
|
|
| mAdditionalVerticalOffset = res.getDimensionPixelSize(R.dimen.menu_vertical_offset);
|
| mVerticalFadeDistance = res.getDimensionPixelSize(R.dimen.menu_vertical_fade_distance);
|
| - 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);
|
| - // If user is dragging and the popup ListView is too big to display at once,
|
| - // mDragScrolling animator scrolls mPopup.getListView() automatically depending on
|
| - // the user's touch position.
|
| - mDragScrolling.setTimeListener(new TimeAnimator.TimeListener() {
|
| - @Override
|
| - public void onTimeUpdate(TimeAnimator animation, long totalTime, long deltaTime) {
|
| - if (mPopup == null || mPopup.getListView() == null) return;
|
| -
|
| - // We keep both mDragScrollOffset and mDragScrollOffsetRounded because
|
| - // the actual scrolling is by the rounded value but at the same time we also
|
| - // want to keep the precise scroll value in float.
|
| - mDragScrollOffset += (deltaTime * 0.001f) * mDragScrollingVelocity;
|
| - int diff = Math.round(mDragScrollOffset - mDragScrollOffsetRounded);
|
| - mDragScrollOffsetRounded += diff;
|
| - mPopup.getListView().smoothScrollBy(diff, 0);
|
| -
|
| - // Force touch move event to highlight items correctly for the scrolled position.
|
| - 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);
|
| - }
|
| - }
|
| - });
|
| - }
|
| -
|
| - private void updateBookmarkButton() {
|
| - final MenuItem bookmarkMenuItem = mMenu.findItem(R.id.bookmark_this_page_id);
|
| - if (mBookmarkButton == null || bookmarkMenuItem == null) return;
|
| - if (bookmarkMenuItem.isEnabled()) {
|
| - mBookmarkButton.setImageResource(R.drawable.star);
|
| - mBookmarkButton.setContentDescription(mBookmarkButton.getContext().getString(
|
| - R.string.accessibility_menu_bookmark));
|
| - } else {
|
| - mBookmarkButton.setImageResource(R.drawable.star_lit);
|
| - mBookmarkButton.setContentDescription(mBookmarkButton.getContext().getString(
|
| - R.string.accessibility_menu_edit_bookmark));
|
| - }
|
| }
|
|
|
| /**
|
| @@ -197,13 +92,8 @@ public class AppMenu implements AdapterView.OnItemClickListener, OnKeyListener {
|
| * @param showIconRow Whether or not the icon row should be shown,
|
| * @param isByHardwareButton Whether or not hardware button triggered it. (oppose to software
|
| * button)
|
| - * @param startDragging Whether dragging is started. For example, if the app menu
|
| - * is showed by tapping on a button, this should be false. If it is
|
| - * showed by start dragging down on the menu button, this should be
|
| - * true. Note that if isByHardwareButton is true, this is ignored.
|
| */
|
| - void show(Context context, View anchorView, boolean showIconRow,
|
| - boolean isByHardwareButton, boolean startDragging) {
|
| + void show(Context context, View anchorView, boolean showIconRow, boolean isByHardwareButton) {
|
| mPopup = new ListPopupWindow(context, null, android.R.attr.popupMenuStyle);
|
| mInflater = LayoutInflater.from(context);
|
| mPopup.setModal(true);
|
| @@ -215,7 +105,7 @@ public class AppMenu implements AdapterView.OnItemClickListener, OnKeyListener {
|
| if (mPopup.getAnchorView() instanceof ImageButton) {
|
| ((ImageButton) mPopup.getAnchorView()).setSelected(false);
|
| }
|
| - mHandler.onMenuVisibilityChanged(false, ListView.INVALID_POSITION);
|
| + mHandler.onMenuVisibilityChanged(false);
|
| }
|
| });
|
| mPopup.setWidth(context.getResources().getDimensionPixelSize(R.dimen.menu_width));
|
| @@ -236,11 +126,11 @@ public class AppMenu implements AdapterView.OnItemClickListener, OnKeyListener {
|
|
|
| // A List adapter for visible items in the Menu. The first row is added as a header to the
|
| // list view.
|
| - mAdapter = new MenuAdapter(menuItems, mInflater);
|
| + mAdapter = new AppMenuAdapter(menuItems, mInflater);
|
| if (mShowIconRow) {
|
| mIconRowView = mInflater.inflate(R.layout.menu_icon_row, null);
|
| // Add click handlers for the header icons.
|
| - setIconRowEventHandlers(mIconRowView, mActivity);
|
| + setIconRowEventHandlers(mIconRowView);
|
| updateBookmarkButton();
|
|
|
| // Icon row goes into header of List view.
|
| @@ -264,7 +154,7 @@ public class AppMenu implements AdapterView.OnItemClickListener, OnKeyListener {
|
| mPopup.show();
|
| mPopup.getListView().setDividerHeight(mItemDividerHeight);
|
|
|
| - mHandler.onMenuVisibilityChanged(true, getCurrentFocusedPosition());
|
| + mHandler.onMenuVisibilityChanged(true);
|
|
|
| if (mVerticalFadeDistance > 0) {
|
| mPopup.getListView().setVerticalFadingEdgeEnabled(true);
|
| @@ -272,35 +162,6 @@ public class AppMenu implements AdapterView.OnItemClickListener, OnKeyListener {
|
| }
|
|
|
| mPopup.getListView().setOnKeyListener(this);
|
| -
|
| - // Initiate drag related variables and listeners.
|
| - mLastTouchX = Float.NaN;
|
| - mLastTouchY = Float.NaN;
|
| - mDragScrollOffset = 0.0f;
|
| - mDragScrollOffsetRounded = 0;
|
| - mDragScrollingVelocity = 0.0f;
|
| -
|
| - mDragPending = isByHardwareButton;
|
| - mIsDownScrollable = !isByHardwareButton;
|
| - mIsUpScrollable = !isByHardwareButton;
|
| -
|
| - mTopTouchMovedBound = Float.POSITIVE_INFINITY;
|
| - mBottomTouchMovedBound = Float.NEGATIVE_INFINITY;
|
| - mHardwareMenuButtonUpTime = -1;
|
| -
|
| - // Handles dragging related logic.
|
| - mPopup.getListView().setOnTouchListener(mDragScrollTouchEventForwarder);
|
| -
|
| - // We assume that the parent of popup ListView is an instance of View. Otherwise, dragging
|
| - // from a hardware menu button won't work.
|
| - ViewParent listViewParent = mPopup.getListView().getParent();
|
| - if (listViewParent instanceof View) {
|
| - ((View) listViewParent).setOnTouchListener(mDragScrollTouchEventForwarder);
|
| - } else {
|
| - assert false;
|
| - }
|
| -
|
| - if (!isByHardwareButton && startDragging) mDragScrolling.start();
|
| }
|
|
|
| private void setPopupOffset(ListPopupWindow popup, int screenRotation, Rect appRect) {
|
| @@ -355,15 +216,12 @@ public class AppMenu implements AdapterView.OnItemClickListener, OnKeyListener {
|
| mPopup.clearListSelection();
|
| requestFocusToEnabledIconRowButton();
|
| }
|
| -
|
| - mHandler.onKeyboardFocusChanged(getCurrentFocusedPosition());
|
| return handled;
|
| } else if (KeyNavigationUtil.isGoDown(event)) {
|
| boolean handled = mPopup.onKeyDown(keyCode, event);
|
| if (listView.getSelectedItemPosition() == ListView.INVALID_POSITION) {
|
| listView.setSelection(listView.getCount() - 1);
|
| }
|
| - mHandler.onKeyboardFocusChanged(getCurrentFocusedPosition());
|
| return handled;
|
| } else if (KeyNavigationUtil.isEnter(event)) {
|
| int position = getCurrentFocusedPosition();
|
| @@ -372,8 +230,7 @@ public class AppMenu implements AdapterView.OnItemClickListener, OnKeyListener {
|
| int adjustedPosition = mShowIconRow ? position - 1 : position;
|
| MenuItem clickedItem = mAdapter.getItem(adjustedPosition);
|
| dismiss();
|
| - mActivity.onOptionsItemSelected(clickedItem);
|
| - mHandler.onKeyboardActivatedItem(position);
|
| + mHandler.onOptionsItemSelected(clickedItem);
|
| return true;
|
| } else if (event.getKeyCode() == KeyEvent.KEYCODE_MENU) {
|
| if (event.getAction() == KeyEvent.ACTION_DOWN && event.getRepeatCount() == 0) {
|
| @@ -393,285 +250,160 @@ public class AppMenu implements AdapterView.OnItemClickListener, OnKeyListener {
|
| return false;
|
| }
|
|
|
| - /**
|
| - * @return Visible rect in screen coordinates for the given View.
|
| - */
|
| - private Rect getScreenVisibleRect(View view) {
|
| - view.getLocalVisibleRect(mScreenVisibleRect);
|
| - view.getLocationOnScreen(mScreenVisiblePoint);
|
| - mScreenVisibleRect.offset(mScreenVisiblePoint[0], mScreenVisiblePoint[1]);
|
| - return mScreenVisibleRect;
|
| + @Override
|
| + public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
|
| + // Account for header. MenuAdapter does not know about header,
|
| + // but the 'position' includes the header.
|
| + int adjustedPosition = mShowIconRow ? position - 1 : position;
|
| + MenuItem clickedItem = mAdapter.getItem(adjustedPosition);
|
| + if (clickedItem.isEnabled()) {
|
| + dismiss();
|
| + mHandler.onOptionsItemSelected(clickedItem);
|
| + }
|
| }
|
|
|
| /**
|
| - * This is a hint for adjusting edgeSwipeInSlop. For example. If the touch event started
|
| - * immediately after hardware menu button up, then we use larger edgeSwipeInSlop because it
|
| - * implies user is swiping in fast.
|
| + * Dismisses the app menu and cancels the drag-to-scroll if it is taking place.
|
| */
|
| - public void hardwareMenuButtonUp() {
|
| - // There should be only one time hardware menu button up.
|
| - assert mHardwareMenuButtonUpTime == -1;
|
| - mHardwareMenuButtonUpTime = SystemClock.uptimeMillis();
|
| + void dismiss() {
|
| + mHandler.appMenudismiss();
|
| + if (isShowing()) mPopup.dismiss();
|
| }
|
|
|
| /**
|
| - * @return The shortest distance from the screen edges for the given position rawX, rawY
|
| - * in screen coordinates.
|
| + * @return Whether the app menu is currently showing.
|
| */
|
| - 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);
|
| + boolean isShowing() {
|
| + if (mPopup == null) {
|
| + return false;
|
| }
|
| - return Math.abs(distance);
|
| + return mPopup.isShowing();
|
| }
|
|
|
| - /**
|
| - * @return The distance from the screen edge that is likely where the hardware menu button is
|
| - * located at. We assume the hardware menu button is at the bottom in the default,
|
| - * ROTATION_0, rotation. Note that there is a bug filed for Android API to request
|
| - * hardware menu button position b/10007237.
|
| - */
|
| - 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);
|
| + @VisibleForTesting
|
| + int getCount() {
|
| + if (mPopup == null || mPopup.getListView() == null) return 0;
|
| + return mPopup.getListView().getCount();
|
| }
|
|
|
| - /**
|
| - * @return Whether or not the position should be considered swiping-out, if ACTION_UP happens
|
| - * at the position.
|
| - */
|
| - private boolean isInSwipeOutRegion(float rawX, float rawY) {
|
| - return getShortestDistanceFromEdge(rawX, rawY) < mEdgeSwipeOutSlop;
|
| + ListPopupWindow getPopup() {
|
| + return mPopup;
|
| }
|
|
|
| - /**
|
| - * Computes Edge-swipe-in-slop and returns it.
|
| - *
|
| - * When user swipes in from a hardware menu button, because the swiping-in touch event doesn't
|
| - * necessarily start form the exact edge, we should also consider slightly more inside touch
|
| - * event as swiping-in. This value, Edge-swipe-in-slop, is the threshold distance from the
|
| - * edge that separates swiping-in and normal touch.
|
| - *
|
| - * @param event Touch event that eventually made this call.
|
| - * @return Edge-swipe-in-slop.
|
| - */
|
| - private float getEdgeSwipeInSlop(MotionEvent event) {
|
| - float edgeSwipeInSlope = mEdgeSwipeInSlop;
|
| - if (mHardwareMenuButtonUpTime == -1) {
|
| - // Hardware menu hasn't even had UP event yet. That means, user is swiping in really
|
| - // really fast. So use large edgeSwipeInSlope.
|
| - edgeSwipeInSlope += mEdgeSwipeInAdditionalSlop;
|
| - } else {
|
| - // If it's right after we had hardware menu button UP event, use large edgeSwipeInSlop,
|
| - // Otherwise, use small edgeSwipeInSlop.
|
| - float additionalEdgeSwipeInSlop = ((mHardwareMenuButtonUpTime - event.getEventTime()
|
| - + EDGE_SWIPE_IN_ADDITIONAL_SLOP_TIME_MS) * 0.001f)
|
| - * mEdgeSwipeInAdditionalSlop;
|
| - edgeSwipeInSlope += Math.max(0.0f, additionalEdgeSwipeInSlop);
|
| - }
|
| - return edgeSwipeInSlope;
|
| + boolean isShowingIconRow() {
|
| + return mShowIconRow;
|
| }
|
|
|
| - /**
|
| - * Gets all the touch events and updates dragging related logic. Note that if this app menu
|
| - * is initiated by software UI control, then the control should set onTouchListener and forward
|
| - * all the events to this method because the initial UI control that processed ACTION_DOWN will
|
| - * continue to get all the subsequent events.
|
| - *
|
| - * @param event Touch event to be processed.
|
| - * @return Whether the event is handled.
|
| - */
|
| - boolean handleDragging(MotionEvent event) {
|
| - if (!isShowing() || (!mDragPending && !mDragScrolling.isRunning())) return false;
|
| -
|
| - // We will only use the screen space coordinate (rawX, rawY) to reduce confusion.
|
| - // This code works across many different controls, so using local coordinates will be
|
| - // a disaster.
|
| -
|
| - 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 = mPopup.getListView();
|
| -
|
| - mLastTouchX = rawX;
|
| - mLastTouchY = rawY;
|
| -
|
| - // Because (hardware) menu button can be right or left side of the screen, if we just
|
| - // trigger auto scrolling based on Y inside the listView, it might be scrolled
|
| - // unintentionally. Therefore, we will require touch position to move up or down a certain
|
| - // amount of distance to trigger auto scrolling up or down.
|
| - mTopTouchMovedBound = Math.min(mTopTouchMovedBound, rawY);
|
| - mBottomTouchMovedBound = Math.max(mBottomTouchMovedBound, rawY);
|
| - if (rawY <= mBottomTouchMovedBound - mScaledTouchSlop) {
|
| - mIsUpScrollable = true;
|
| - }
|
| - if (rawY >= mTopTouchMovedBound + mScaledTouchSlop) {
|
| - mIsDownScrollable = true;
|
| - }
|
| + View getIconRowView() {
|
| + return mIconRowView;
|
| + }
|
|
|
| - if (eventActionMasked == MotionEvent.ACTION_CANCEL) {
|
| - dismiss();
|
| - return true;
|
| - }
|
| + int getItemRowHeight() {
|
| + return mItemRowHeight;
|
| + }
|
|
|
| - 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)) {
|
| - dismiss();
|
| - }
|
| - mDragPending = false;
|
| - UmaBridge.usingMenu(true, false);
|
| - return false;
|
| - }
|
| - }
|
| + private void updateBookmarkButton() {
|
| + final MenuItem bookmarkMenuItem = mMenu.findItem(R.id.bookmark_this_page_id);
|
| + ImageButton bookmarkButton =
|
| + (ImageButton) mIconRowView.findViewById(R.id.menu_item_bookmark);
|
| + if (bookmarkMenuItem.isEnabled()) {
|
| + bookmarkButton.setImageResource(R.drawable.star);
|
| + bookmarkButton.setContentDescription(bookmarkButton.getContext().getString(
|
| + R.string.accessibility_menu_bookmark));
|
| + } else {
|
| + bookmarkButton.setImageResource(R.drawable.star_lit);
|
| + bookmarkButton.setContentDescription(bookmarkButton.getContext().getString(
|
| + R.string.accessibility_menu_edit_bookmark));
|
| }
|
| + }
|
|
|
| - // After this line, drag scrolling is happening.
|
| - if (!mDragScrolling.isRunning()) return false;
|
| + @VisibleForTesting
|
| + int getCurrentFocusedPosition() {
|
| + if (mPopup == null || mPopup.getListView() == null) return ListView.INVALID_POSITION;
|
| + ListView listView = mPopup.getListView();
|
| + int position = listView.getSelectedItemPosition();
|
|
|
| - 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;
|
| + // Check if any of the icon row icons are focused.
|
| + if (mShowIconRow) {
|
| + if (mIconRowView.findViewById(R.id.menu_item_back).isFocused() ||
|
| + mIconRowView.findViewById(R.id.menu_item_forward).isFocused() ||
|
| + mIconRowView.findViewById(R.id.menu_item_bookmark).isFocused()) {
|
| + return 0;
|
| }
|
| }
|
| - didPerformClick = menuItemAction(roundedRawX, roundedRawY, itemAction);
|
| + return position;
|
| + }
|
|
|
| - if (eventActionMasked == MotionEvent.ACTION_UP && !didPerformClick) {
|
| - dismiss();
|
| - } else if (eventActionMasked == MotionEvent.ACTION_MOVE) {
|
| - // Auto scrolling on the top or the bottom of the listView.
|
| - 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) {
|
| - // Top
|
| - mDragScrollingVelocity = (normalizedY / autoScrollAreaRatio - 1.0f)
|
| - * mAutoScrollFullVelocity;
|
| - } else if (mIsDownScrollable && normalizedY > 1.0f - autoScrollAreaRatio) {
|
| - // Bottom
|
| - mDragScrollingVelocity = ((normalizedY - 1.0f) / autoScrollAreaRatio + 1.0f)
|
| - * mAutoScrollFullVelocity;
|
| - } else {
|
| - // Middle or not scrollable.
|
| - mDragScrollingVelocity = 0.0f;
|
| - }
|
| + @VisibleForTesting
|
| + int getCurrentFocusedItemId() {
|
| + int position = getCurrentFocusedPosition();
|
| + if (position == ListView.INVALID_POSITION) return -1;
|
| + if (mShowIconRow && position == 0) {
|
| + if (mIconRowView.findViewById(R.id.menu_item_back).isFocused()) {
|
| + return R.id.back_menu_id;
|
| + } else if (mIconRowView.findViewById(R.id.menu_item_forward).isFocused()) {
|
| + return R.id.forward_menu_id;
|
| + } else if (mIconRowView.findViewById(R.id.menu_item_bookmark).isFocused()) {
|
| + return R.id.bookmark_this_page_id;
|
| }
|
| }
|
| -
|
| - return true;
|
| + int adjustedPosition = mShowIconRow ? position - 1 : position;
|
| + return mAdapter.getItem(adjustedPosition).getItemId();
|
| }
|
|
|
| /**
|
| - * Performs the specified action on the menu item specified by the screen coordinate position.
|
| - * @param screenX X in screen space coordinate.
|
| - * @param screenY Y in screen space coordinate.
|
| - * @param action Action type to perform, it should be one of ITEM_ACTION_* constants.
|
| - * @return true whether or not a menu item is performed (executed).
|
| + * Adds click handlers for items in the icon row.
|
| + * Also disable/enable the view based on the menu item.
|
| + * We assume that we have Back, Forward and Bookmark-star icons in this view.
|
| */
|
| - private boolean menuItemAction(int screenX, int screenY, int action) {
|
| - ListView listView = mPopup.getListView();
|
| -
|
| - ArrayList<View> itemViews = new ArrayList<View>();
|
| - for (int i = 0; i < listView.getChildCount(); ++i) {
|
| - itemViews.add(listView.getChildAt(i));
|
| - }
|
| -
|
| - if (mIconRowView != null && mShowIconRow) {
|
| - itemViews.add(mIconRowView.findViewById(R.id.menu_item_back));
|
| - itemViews.add(mIconRowView.findViewById(R.id.menu_item_forward));
|
| - itemViews.add(mIconRowView.findViewById(R.id.menu_item_bookmark));
|
| - }
|
| + private void setIconRowEventHandlers(View iconRowView) {
|
| + OnKeyListener keyListener = new OnKeyListener() {
|
| + @Override
|
| + public boolean onKey(View v, int keyCode, KeyEvent event) {
|
| + if (isShowing() && v.isFocused()) {
|
| + if (keyCode == KeyEvent.KEYCODE_MENU) {
|
| + v.setSelected(false);
|
| + dismiss();
|
| + return true;
|
| + } else if (KeyNavigationUtil.isGoUp(event)) {
|
| + // Catch attempts to move out of bounds.
|
| + return true;
|
| + } else if (KeyNavigationUtil.isGoDown(event)) {
|
| + // Requesting focus on the mPopup.getListView().getChildAt(0) does not work.
|
| + // Requesting the focus on the ListView focuses the first non-header item.
|
| + mPopup.getListView().requestFocus();
|
| + return true;
|
| + }
|
| + }
|
| + return false;
|
| + }
|
| + };
|
|
|
| - boolean didPerformClick = false;
|
| - for (int i = 0; i < itemViews.size(); ++i) {
|
| - View itemView = itemViews.get(i);
|
| + MenuItem menuItem = mMenu.findItem(R.id.back_menu_id);
|
| + setIconRowItemEventHandlers(menuItem, iconRowView.findViewById(R.id.menu_item_back),
|
| + keyListener, menuItem.isEnabled());
|
|
|
| - // Skip the icon row that belongs to the listView because that doesn't really
|
| - // exist as an item.
|
| - int listViewPositionIndex = listView.getFirstVisiblePosition() + i;
|
| - if (mShowIconRow && listViewPositionIndex == 0) continue;
|
| + menuItem = mMenu.findItem(R.id.forward_menu_id);
|
| + setIconRowItemEventHandlers(menuItem, iconRowView.findViewById(R.id.menu_item_forward),
|
| + keyListener, menuItem.isEnabled());
|
|
|
| - boolean shouldPerform = itemView.isEnabled() && itemView.isShown() &&
|
| - getScreenVisibleRect(itemView).contains(screenX, screenY);
|
| + menuItem = mMenu.findItem(R.id.bookmark_this_page_id);
|
| + setIconRowItemEventHandlers(menuItem, iconRowView.findViewById(R.id.menu_item_bookmark),
|
| + keyListener, BookmarksBridge.isEditBookmarksEnabled());
|
| + }
|
|
|
| - switch (action) {
|
| - case ITEM_ACTION_HIGHLIGHT:
|
| - itemView.setPressed(shouldPerform);
|
| - break;
|
| - case ITEM_ACTION_PERFORM:
|
| - if (shouldPerform) {
|
| - if (itemView.getParent() == listView) {
|
| - listView.performItemClick(itemView, listViewPositionIndex, 0);
|
| - } else {
|
| - itemView.performClick();
|
| - }
|
| - didPerformClick = true;
|
| - }
|
| - break;
|
| - case ITEM_ACTION_CLEAR_HIGHLIGHT_ALL:
|
| - itemView.setPressed(false);
|
| - break;
|
| - default:
|
| - assert false;
|
| - break;
|
| + private void setIconRowItemEventHandlers(final MenuItem menuItem, View iconView,
|
| + OnKeyListener keyListener, boolean enabled) {
|
| + iconView.setEnabled(enabled);
|
| + iconView.setFocusable(enabled);
|
| + iconView.setOnKeyListener(keyListener);
|
| + iconView.setOnClickListener(new OnClickListener() {
|
| + @Override
|
| + public void onClick(View v) {
|
| + dismiss();
|
| + mHandler.onOptionsItemSelected(menuItem);
|
| }
|
| - }
|
| - return didPerformClick;
|
| + });
|
| }
|
|
|
| /**
|
| @@ -738,268 +470,4 @@ public class AppMenu implements AdapterView.OnItemClickListener, OnKeyListener {
|
| headerInfoList.add(viewInfo);
|
| return headerInfoList;
|
| }
|
| -
|
| - /**
|
| - * Adds click handlers for items in the icon row.
|
| - * Also disable/enable the view based on the menu item.
|
| - * We assume that we have Back, Forward and Bookmark-star icons in this view.
|
| - */
|
| - private void setIconRowEventHandlers(View iconRowView, final Activity activity) {
|
| - final MenuItem backMenuItem = mMenu.findItem(R.id.back_menu_id);
|
| - final MenuItem forwardMenuItem = mMenu.findItem(R.id.forward_menu_id);
|
| - final MenuItem bookmarkMenuItem = mMenu.findItem(R.id.bookmark_this_page_id);
|
| -
|
| - View.OnFocusChangeListener focusListener = new View.OnFocusChangeListener() {
|
| - @Override
|
| - public void onFocusChange(View v, boolean hasFocus) {
|
| - mHandler.onKeyboardFocusChanged(getCurrentFocusedPosition());
|
| - }
|
| - };
|
| -
|
| - OnKeyListener keyListener = new OnKeyListener() {
|
| - @Override
|
| - public boolean onKey(View v, int keyCode, KeyEvent event) {
|
| - if (isShowing() && v.isFocused()) {
|
| - if (keyCode == KeyEvent.KEYCODE_MENU) {
|
| - v.setSelected(false);
|
| - dismiss();
|
| - return true;
|
| - } else if (KeyNavigationUtil.isGoUp(event)) {
|
| - // Catch attempts to move out of bounds.
|
| - mHandler.onKeyboardFocusChanged(getCurrentFocusedPosition());
|
| - return true;
|
| - } else if (KeyNavigationUtil.isGoDown(event)) {
|
| - // Requesting focus on the mPopup.getListView().getChildAt(0) does not work.
|
| - // Requesting the focus on the ListView focuses the first non-header item.
|
| - mPopup.getListView().requestFocus();
|
| - mHandler.onKeyboardFocusChanged(getCurrentFocusedPosition());
|
| - return true;
|
| - } else if (KeyNavigationUtil.isEnter(event)) {
|
| - mHandler.onKeyboardActivatedItem(getCurrentFocusedPosition());
|
| - }
|
| - }
|
| - return false;
|
| - }
|
| - };
|
| -
|
| - View backIcon = iconRowView.findViewById(R.id.menu_item_back);
|
| - backIcon.setEnabled(backMenuItem.isEnabled());
|
| - backIcon.setFocusable(backMenuItem.isEnabled());
|
| - backIcon.setOnKeyListener(keyListener);
|
| - backIcon.setOnFocusChangeListener(focusListener);
|
| - backIcon.setOnTouchListener(mDragScrollTouchEventForwarder);
|
| - backIcon.setOnClickListener(new OnClickListener() {
|
| - @Override
|
| - public void onClick(View v) {
|
| - dismiss();
|
| - activity.onOptionsItemSelected(backMenuItem);
|
| - }
|
| - });
|
| -
|
| - View forwardIcon = iconRowView.findViewById(R.id.menu_item_forward);
|
| - forwardIcon.setEnabled(forwardMenuItem.isEnabled());
|
| - forwardIcon.setFocusable(forwardMenuItem.isEnabled());
|
| - forwardIcon.setOnKeyListener(keyListener);
|
| - forwardIcon.setOnFocusChangeListener(focusListener);
|
| - forwardIcon.setOnTouchListener(mDragScrollTouchEventForwarder);
|
| - forwardIcon.setOnClickListener(new OnClickListener() {
|
| - @Override
|
| - public void onClick(View v) {
|
| - dismiss();
|
| - activity.onOptionsItemSelected(forwardMenuItem);
|
| - }
|
| - });
|
| -
|
| - // The bookmark button is assumed to be always enabled and focusable when navigating the
|
| - // menu using a keyboard.
|
| - mBookmarkButton = (ImageButton) iconRowView.findViewById(R.id.menu_item_bookmark);
|
| - mBookmarkButton.setEnabled(BookmarksBridge.isEditBookmarksEnabled());
|
| - mBookmarkButton.setOnKeyListener(keyListener);
|
| - mBookmarkButton.setOnFocusChangeListener(focusListener);
|
| - mBookmarkButton.setOnTouchListener(mDragScrollTouchEventForwarder);
|
| - mBookmarkButton.setOnClickListener(new OnClickListener() {
|
| - @Override
|
| - public void onClick(View v) {
|
| - dismiss();
|
| - activity.onOptionsItemSelected(bookmarkMenuItem);
|
| - }
|
| - });
|
| - }
|
| -
|
| - /**
|
| - * Dismisses the app menu and cancels the drag-to-scroll if it is taking place.
|
| - */
|
| - void dismiss() {
|
| - mDragScrolling.cancel();
|
| - if (isShowing()) {
|
| - mPopup.dismiss();
|
| - }
|
| - }
|
| -
|
| - /**
|
| - * @return Whether the app menu is currently showing.
|
| - */
|
| - public boolean isShowing() {
|
| - if (mPopup == null) {
|
| - return false;
|
| - }
|
| - return mPopup.isShowing();
|
| - }
|
| -
|
| - private int getCurrentFocusedPosition() {
|
| - if (mPopup == null || mPopup.getListView() == null) return ListView.INVALID_POSITION;
|
| - ListView listView = mPopup.getListView();
|
| - int position = listView.getSelectedItemPosition();
|
| -
|
| - // Check if any of the icon row icons are focused.
|
| - if (mShowIconRow) {
|
| - if (mIconRowView.findViewById(R.id.menu_item_back).isFocused() ||
|
| - mIconRowView.findViewById(R.id.menu_item_forward).isFocused() ||
|
| - mBookmarkButton.isFocused()) {
|
| - return 0;
|
| - }
|
| - }
|
| - return position;
|
| - }
|
| -
|
| - @Override
|
| - public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
|
| - // Account for header. MenuAdapter does not know about header,
|
| - // but the 'position' includes the header.
|
| - int adjustedPosition = mShowIconRow ? position - 1 : position;
|
| - MenuItem clickedItem = mAdapter.getItem(adjustedPosition);
|
| - if (clickedItem.isEnabled()) {
|
| - dismiss();
|
| - mActivity.onOptionsItemSelected(clickedItem);
|
| - }
|
| - }
|
| -
|
| - @VisibleForTesting
|
| - int getCount() {
|
| - if (mPopup == null || mPopup.getListView() == null) return 0;
|
| - return mPopup.getListView().getCount();
|
| - }
|
| -
|
| - /**
|
| - * ListAdapter to customize the view of items in the list.
|
| - */
|
| - private static class MenuAdapter extends BaseAdapter {
|
| - private static final int VIEW_TYPE_MENUITEM = 0;
|
| - private static final int VIEW_TYPE_COUNT = 1;
|
| -
|
| - private final LayoutInflater mInflater;
|
| - private final List<MenuItem> mMenuItems;
|
| - private final int mNumMenuItems;
|
| -
|
| - public MenuAdapter(List<MenuItem> menuItems, LayoutInflater inflater) {
|
| - mMenuItems = menuItems;
|
| - mInflater = inflater;
|
| - mNumMenuItems = menuItems.size();
|
| - }
|
| -
|
| - @Override
|
| - public int getCount() {
|
| - return mNumMenuItems;
|
| - }
|
| -
|
| - @Override
|
| - public int getViewTypeCount() {
|
| - return VIEW_TYPE_COUNT;
|
| - }
|
| -
|
| - @Override
|
| - public int getItemViewType(int position) {
|
| - return VIEW_TYPE_MENUITEM;
|
| - }
|
| -
|
| - @Override
|
| - public long getItemId(int position) {
|
| - return getItem(position).getItemId();
|
| - }
|
| -
|
| - @Override
|
| - public MenuItem getItem(int position) {
|
| - if (position == ListView.INVALID_POSITION) return null;
|
| - assert position >= 0;
|
| - assert position < mMenuItems.size();
|
| - return mMenuItems.get(position);
|
| - }
|
| -
|
| - @Override
|
| - public View getView(int position, View convertView, ViewGroup parent) {
|
| - View rowView = convertView;
|
| - // A ViewHolder keeps references to children views to avoid unneccessary calls
|
| - // to findViewById() on each row.
|
| - ViewHolder holder = null;
|
| -
|
| - // When convertView is not null, we can reuse it directly, there is no need
|
| - // to reinflate it.
|
| - if (rowView == null) {
|
| - holder = new ViewHolder();
|
| - rowView = mInflater.inflate(R.layout.menu_item, null);
|
| - holder.text = (TextView) rowView.findViewById(R.id.menu_item_text);
|
| - holder.image = (MenuItemIcon) rowView.findViewById(R.id.menu_item_icon);
|
| - rowView.setTag(holder);
|
| - } else {
|
| - holder = (ViewHolder) convertView.getTag();
|
| - }
|
| - MenuItem item = getItem(position);
|
| -
|
| - // Set up the icon.
|
| - Drawable icon = item.getIcon();
|
| - holder.image.setImageDrawable(icon);
|
| - holder.image.setVisibility(icon == null ? View.GONE : View.VISIBLE);
|
| - holder.image.setChecked(item.isChecked());
|
| -
|
| - holder.text.setText(item.getTitle());
|
| - boolean isEnabled = item.isEnabled();
|
| - // Set the text color (using a color state list).
|
| - holder.text.setEnabled(isEnabled);
|
| - // This will ensure that the item is not highlighted when selected.
|
| - rowView.setEnabled(isEnabled);
|
| - return rowView;
|
| - }
|
| -
|
| - static class ViewHolder {
|
| - TextView text;
|
| - MenuItemIcon image;
|
| - }
|
| - }
|
| -
|
| - /**
|
| - * A menu icon that supports the checkable state.
|
| - */
|
| - static class MenuItemIcon extends ImageView {
|
| - private static final int[] CHECKED_STATE_SET = new int[] {android.R.attr.state_checked};
|
| - private boolean mCheckedState;
|
| -
|
| - public MenuItemIcon(Context context, AttributeSet attrs) {
|
| - super(context, attrs);
|
| - }
|
| -
|
| - /**
|
| - * Sets whether the item is checked and refreshes the View if necessary.
|
| - */
|
| - protected void setChecked(boolean state) {
|
| - if (state == mCheckedState) return;
|
| - mCheckedState = state;
|
| - refreshDrawableState();
|
| - }
|
| -
|
| - @Override
|
| - public void setPressed(boolean state) {
|
| - // We don't want to highlight the checkbox icon since the parent item is already
|
| - // highlighted.
|
| - return;
|
| - }
|
| -
|
| - @Override
|
| - public int[] onCreateDrawableState(int extraSpace) {
|
| - final int[] drawableState = super.onCreateDrawableState(extraSpace + 1);
|
| - if (mCheckedState) {
|
| - mergeDrawableStates(drawableState, CHECKED_STATE_SET);
|
| - }
|
| - return drawableState;
|
| - }
|
| - }
|
| -}
|
| +}
|
|
|