Upstream version 10.38.208.0
[platform/framework/web/crosswalk.git] / src / chrome / android / java / src / org / chromium / chrome / browser / appmenu / AppMenu.java
index 254795b..3f3ac68 100644 (file)
@@ -4,14 +4,19 @@
 
 package org.chromium.chrome.browser.appmenu;
 
+import android.animation.Animator;
+import android.animation.AnimatorSet;
 import android.content.Context;
 import android.content.res.Resources;
 import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
+import android.view.KeyEvent;
 import android.view.LayoutInflater;
 import android.view.Menu;
 import android.view.MenuItem;
 import android.view.Surface;
 import android.view.View;
+import android.view.View.OnKeyListener;
 import android.view.ViewGroup;
 import android.widget.AdapterView;
 import android.widget.AdapterView.OnItemClickListener;
@@ -20,6 +25,7 @@ import android.widget.ListPopupWindow;
 import android.widget.PopupWindow;
 import android.widget.PopupWindow.OnDismissListener;
 
+import org.chromium.base.SysUtils;
 import org.chromium.chrome.R;
 
 import java.util.ArrayList;
@@ -31,15 +37,17 @@ import java.util.List;
  *   - Only visible MenuItems are shown.
  *   - Disabled items are grayed out.
  */
-public class AppMenu implements OnItemClickListener {
+public class AppMenu implements OnItemClickListener, OnKeyListener {
+    /** Whether or not to show the software menu button in the menu. */
+    private static final boolean SHOW_SW_MENU_BUTTON = true;
+
     private static final float LAST_ITEM_SHOW_FRACTION = 0.5f;
-    private static final int DIVIDER_HEIGHT_DP = 1;
 
     private final Menu mMenu;
     private final int mItemRowHeight;
     private final int mItemDividerHeight;
     private final int mVerticalFadeDistance;
-    private final int mAdditionalVerticalOffset;
+    private final int mNegativeSoftwareVerticalOffset;
     private ListPopupWindow mPopup;
     private AppMenuAdapter mAdapter;
     private AppMenuHandler mHandler;
@@ -50,10 +58,12 @@ public class AppMenu implements OnItemClickListener {
      * Creates and sets up the App Menu.
      * @param menu Original menu created by the framework.
      * @param itemRowHeight Desired height for each app menu row.
+     * @param itemDividerHeight Desired height for the divider between app menu items.
      * @param handler AppMenuHandler receives callbacks from AppMenu.
      * @param res Resources object used to get dimensions and style attributes.
      */
-    AppMenu(Menu menu, int itemRowHeight, AppMenuHandler handler, Resources res) {
+    AppMenu(Menu menu, int itemRowHeight, int itemDividerHeight, AppMenuHandler handler,
+            Resources res) {
         mMenu = menu;
 
         mItemRowHeight = itemRowHeight;
@@ -61,10 +71,11 @@ public class AppMenu implements OnItemClickListener {
 
         mHandler = handler;
 
-        final float dpToPx = res.getDisplayMetrics().density;
-        mItemDividerHeight = Math.round(DIVIDER_HEIGHT_DP * dpToPx);
+        mItemDividerHeight = itemDividerHeight;
+        assert mItemDividerHeight >= 0;
 
-        mAdditionalVerticalOffset = res.getDimensionPixelSize(R.dimen.menu_vertical_offset);
+        mNegativeSoftwareVerticalOffset =
+                res.getDimensionPixelSize(R.dimen.menu_negative_software_vertical_offset);
         mVerticalFadeDistance = res.getDimensionPixelSize(R.dimen.menu_vertical_fade_distance);
     }
 
@@ -78,9 +89,10 @@ public class AppMenu implements OnItemClickListener {
      *                            button)
      * @param screenRotation      Current device screen rotation.
      * @param visibleDisplayFrame The display area rect in which AppMenu is supposed to fit in.
+     * @param screenHeight        Current device screen height.
      */
     void show(Context context, View anchorView, boolean isByHardwareButton, int screenRotation,
-            Rect visibleDisplayFrame) {
+            Rect visibleDisplayFrame, int screenHeight) {
         mPopup = new ListPopupWindow(context, null, android.R.attr.popupMenuStyle);
         mPopup.setModal(true);
         mPopup.setAnchorView(anchorView);
@@ -94,7 +106,32 @@ public class AppMenu implements OnItemClickListener {
                 mHandler.onMenuVisibilityChanged(false);
             }
         });
-        mPopup.setWidth(context.getResources().getDimensionPixelSize(R.dimen.menu_width));
+
+        // Some OEMs don't actually let us change the background... but they still return the
+        // padding of the new background, which breaks the menu height.  If we still have a
+        // drawable here even though our style says @null we should use this padding instead...
+        Drawable originalBgDrawable = mPopup.getBackground();
+
+        // Need to explicitly set the background here.  Relying on it being set in the style caused
+        // an incorrectly drawn background.
+        if (isByHardwareButton) {
+            mPopup.setBackgroundDrawable(context.getResources().getDrawable(R.drawable.menu_bg));
+        } else {
+            mPopup.setBackgroundDrawable(
+                    context.getResources().getDrawable(R.drawable.edge_menu_bg));
+            mPopup.setAnimationStyle(R.style.OverflowMenuAnim);
+        }
+
+        // Turn off window animations for low end devices.
+        if (SysUtils.isLowEndDevice()) mPopup.setAnimationStyle(0);
+
+        Rect bgPadding = new Rect();
+        mPopup.getBackground().getPadding(bgPadding);
+
+        int popupWidth = context.getResources().getDimensionPixelSize(R.dimen.menu_width) +
+                bgPadding.left + bgPadding.right;
+
+        mPopup.setWidth(popupWidth);
 
         mCurrentScreenRotation = screenRotation;
         mIsByHardwareButton = isByHardwareButton;
@@ -109,17 +146,28 @@ public class AppMenu implements OnItemClickListener {
             }
         }
 
+        Rect sizingPadding = new Rect(bgPadding);
+        if (isByHardwareButton && originalBgDrawable != null) {
+            Rect originalPadding = new Rect();
+            originalBgDrawable.getPadding(originalPadding);
+            sizingPadding.top = originalPadding.top;
+            sizingPadding.bottom = originalPadding.bottom;
+        }
+
+        boolean showMenuButton = !mIsByHardwareButton;
+        if (!SHOW_SW_MENU_BUTTON) showMenuButton = false;
         // A List adapter for visible items in the Menu. The first row is added as a header to the
         // list view.
-        mAdapter = new AppMenuAdapter(this, menuItems, LayoutInflater.from(context));
+        mAdapter = new AppMenuAdapter(
+                this, menuItems, LayoutInflater.from(context), showMenuButton);
         mPopup.setAdapter(mAdapter);
 
-        setMenuHeight(menuItems.size(), visibleDisplayFrame);
-        setPopupOffset(mPopup, mCurrentScreenRotation, visibleDisplayFrame);
+        setMenuHeight(menuItems.size(), visibleDisplayFrame, screenHeight, sizingPadding);
+        setPopupOffset(mPopup, mCurrentScreenRotation, visibleDisplayFrame, sizingPadding);
         mPopup.setOnItemClickListener(this);
         mPopup.show();
-        mPopup.getListView().setDividerHeight(mItemDividerHeight);
         mPopup.getListView().setItemsCanFocus(true);
+        mPopup.getListView().setOnKeyListener(this);
 
         mHandler.onMenuVisibilityChanged(true);
 
@@ -127,13 +175,25 @@ public class AppMenu implements OnItemClickListener {
             mPopup.getListView().setVerticalFadingEdgeEnabled(true);
             mPopup.getListView().setFadingEdgeLength(mVerticalFadeDistance);
         }
+
+        // Don't animate the menu items for low end devices.
+        if (!SysUtils.isLowEndDevice()) {
+            mPopup.getListView().addOnLayoutChangeListener(new View.OnLayoutChangeListener() {
+                @Override
+                public void onLayoutChange(View v, int left, int top, int right, int bottom,
+                        int oldLeft, int oldTop, int oldRight, int oldBottom) {
+                    mPopup.getListView().removeOnLayoutChangeListener(this);
+                    runMenuItemEnterAnimations();
+                }
+            });
+        }
     }
 
-    private void setPopupOffset(ListPopupWindow popup, int screenRotation, Rect appRect) {
-        Rect paddingRect = new Rect();
-        popup.getBackground().getPadding(paddingRect);
+    private void setPopupOffset(
+            ListPopupWindow popup, int screenRotation, Rect appRect, Rect padding) {
         int[] anchorLocation = new int[2];
         popup.getAnchorView().getLocationInWindow(anchorLocation);
+        int anchorHeight = popup.getAnchorView().getHeight();
 
         // If we have a hardware menu button, locate the app menu closer to the estimated
         // hardware menu button location.
@@ -154,13 +214,13 @@ public class AppMenu implements OnItemClickListener {
                     break;
             }
             popup.setHorizontalOffset(horizontalOffset);
-            // The menu is displayed above the anchored view, so shift the menu up by the top
+            // The menu is displayed above the anchored view, so shift the menu up by the bottom
             // padding of the background.
-            popup.setVerticalOffset(mAdditionalVerticalOffset - paddingRect.bottom);
+            popup.setVerticalOffset(-padding.bottom);
         } else {
-            // The menu is displayed below the anchored view, so shift the menu up by the top
-            // padding of the background.
-            popup.setVerticalOffset(mAdditionalVerticalOffset - paddingRect.top);
+            // The menu is displayed over and below the anchored view, so shift the menu up by the
+            // height of the anchor view.
+            popup.setVerticalOffset(-mNegativeSoftwareVerticalOffset - anchorHeight);
         }
     }
 
@@ -180,12 +240,34 @@ public class AppMenu implements OnItemClickListener {
         onItemClick(mAdapter.getItem(position));
     }
 
+    @Override
+    public boolean onKey(View v, int keyCode, KeyEvent event) {
+        if (mPopup == null || mPopup.getListView() == null) return false;
+
+        if (event.getKeyCode() == KeyEvent.KEYCODE_MENU) {
+            if (event.getAction() == KeyEvent.ACTION_DOWN && event.getRepeatCount() == 0) {
+                event.startTracking();
+                v.getKeyDispatcherState().startTracking(event, this);
+                return true;
+            } else if (event.getAction() == KeyEvent.ACTION_UP) {
+                v.getKeyDispatcherState().handleUpEvent(event);
+                if (event.isTracking() && !event.isCanceled()) {
+                    dismiss();
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+
     /**
      * Dismisses the app menu and cancels the drag-to-scroll if it is taking place.
      */
     void dismiss() {
         mHandler.appMenuDismissed();
-        if (isShowing()) mPopup.dismiss();
+        if (isShowing()) {
+            mPopup.dismiss();
+        }
     }
 
     /**
@@ -205,19 +287,24 @@ public class AppMenu implements OnItemClickListener {
         return mPopup;
     }
 
-    private void setMenuHeight(int numMenuItems, Rect appDimensions) {
+    private void setMenuHeight(
+            int numMenuItems, Rect appDimensions, int screenHeight, Rect padding) {
         assert mPopup.getAnchorView() != null;
         View anchorView = mPopup.getAnchorView();
         int[] anchorViewLocation = new int[2];
         anchorView.getLocationOnScreen(anchorViewLocation);
         anchorViewLocation[1] -= appDimensions.top;
+        int anchorViewImpactHeight = mIsByHardwareButton ? anchorView.getHeight() : 0;
 
+        // Set appDimensions.height() for abnormal anchorViewLocation.
+        if (anchorViewLocation[1] > screenHeight) {
+            anchorViewLocation[1] = appDimensions.height();
+        }
         int availableScreenSpace = Math.max(anchorViewLocation[1],
-                appDimensions.height() - anchorViewLocation[1] - anchorView.getHeight());
+                appDimensions.height() - anchorViewLocation[1] - anchorViewImpactHeight);
 
-        Rect padding = new Rect();
-        mPopup.getBackground().getPadding(padding);
-        availableScreenSpace -= mIsByHardwareButton ? padding.top : padding.bottom;
+        availableScreenSpace -= padding.bottom;
+        if (mIsByHardwareButton) availableScreenSpace -= padding.top;
 
         int numCanFit = availableScreenSpace / (mItemRowHeight + mItemDividerHeight);
 
@@ -237,4 +324,24 @@ public class AppMenu implements OnItemClickListener {
             mPopup.setHeight(ViewGroup.LayoutParams.WRAP_CONTENT);
         }
     }
-}
\ No newline at end of file
+
+    private void runMenuItemEnterAnimations() {
+        AnimatorSet animation = new AnimatorSet();
+        AnimatorSet.Builder builder = null;
+
+        ViewGroup list = mPopup.getListView();
+        for (int i = 0; i < list.getChildCount(); i++) {
+            View view = list.getChildAt(i);
+            Object animatorObject = view.getTag(R.id.menu_item_enter_anim_id);
+            if (animatorObject != null) {
+                if (builder == null) {
+                    builder = animation.play((Animator) animatorObject);
+                } else {
+                    builder.with((Animator) animatorObject);
+                }
+            }
+        }
+
+        animation.start();
+    }
+}