Add support for custom action views in ActionBar menus

Change-Id: Ic7850bc00fcfe95d805dd6a1b15cb5d413942475
diff --git a/api/current.xml b/api/current.xml
index a3a56b4..b560d98 100644
--- a/api/current.xml
+++ b/api/current.xml
@@ -2176,6 +2176,17 @@
  visibility="public"
 >
 </field>
+<field name="actionLayout"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16843580"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="actionModeBackground"
  type="int"
  transient="false"
@@ -2220,6 +2231,17 @@
  visibility="public"
 >
 </field>
+<field name="actionViewClass"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16843581"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="activityCloseEnterAnimation"
  type="int"
  transient="false"
@@ -193228,6 +193250,17 @@
  deprecated="not deprecated"
  visibility="public"
 >
+<method name="getActionView"
+ return="android.view.View"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
 <method name="getAlphabeticShortcut"
  return="char"
  abstract="true"
@@ -193404,6 +193437,19 @@
  visibility="public"
 >
 </method>
+<method name="setActionView"
+ return="android.view.MenuItem"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="view" type="android.view.View">
+</parameter>
+</method>
 <method name="setAlphabeticShortcut"
  return="android.view.MenuItem"
  abstract="true"
diff --git a/core/java/android/view/MenuInflater.java b/core/java/android/view/MenuInflater.java
index d0985d9..7d5dcd8 100644
--- a/core/java/android/view/MenuInflater.java
+++ b/core/java/android/view/MenuInflater.java
@@ -16,8 +16,7 @@
 
 package android.view;
 
-import java.io.IOException;
-import java.lang.reflect.Method;
+import com.android.internal.view.menu.MenuItemImpl;
 
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
@@ -29,7 +28,9 @@
 import android.util.AttributeSet;
 import android.util.Xml;
 
-import com.android.internal.view.menu.MenuItemImpl;
+import java.io.IOException;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Method;
 
 /**
  * This class is used to instantiate menu XML files into Menu objects.
@@ -52,6 +53,8 @@
 
     private static final int NO_ID = 0;
     
+    private static final Class<?>[] ACTION_VIEW_CONSTRUCTOR_SIGNATURE = new Class[]{Context.class};
+
     private Context mContext;
     
     /**
@@ -249,6 +252,9 @@
          * - -1: Safe sentinel for "no value".
          */
         private int itemShowAsAction;
+
+        private int itemActionViewLayout;
+        private String itemActionViewClassName;
         
         private String itemListenerMethodName;
         
@@ -325,6 +331,8 @@
             itemEnabled = a.getBoolean(com.android.internal.R.styleable.MenuItem_enabled, groupEnabled);
             itemShowAsAction = a.getInt(com.android.internal.R.styleable.MenuItem_showAsAction, -1);
             itemListenerMethodName = a.getString(com.android.internal.R.styleable.MenuItem_onClick);
+            itemActionViewLayout = a.getResourceId(com.android.internal.R.styleable.MenuItem_actionLayout, 0);
+            itemActionViewClassName = a.getString(com.android.internal.R.styleable.MenuItem_actionViewClass);
             
             a.recycle();
             
@@ -368,6 +376,19 @@
                     impl.setExclusiveCheckable(true);
                 }
             }
+
+            if (itemActionViewClassName != null) {
+                try {
+                    final Class<?> clazz = Class.forName(itemActionViewClassName);
+                    Constructor<?> c = clazz.getConstructor(ACTION_VIEW_CONSTRUCTOR_SIGNATURE);
+                    item.setActionView((View) c.newInstance(mContext));
+                } catch (Exception e) {
+                    throw new InflateException(e);
+                }
+            } else if (itemActionViewLayout > 0) {
+                final LayoutInflater inflater = LayoutInflater.from(mContext);
+                item.setActionView(inflater.inflate(itemActionViewLayout, null));
+            }
         }
         
         public void addItem() {
diff --git a/core/java/android/view/MenuItem.java b/core/java/android/view/MenuItem.java
index 2604cf9..8b9d659 100644
--- a/core/java/android/view/MenuItem.java
+++ b/core/java/android/view/MenuItem.java
@@ -405,6 +405,29 @@
      * {@link #SHOW_AS_ACTION_NEVER}. SHOW_AS_ACTION_NEVER is the default.
      * 
      * @see android.app.ActionBar
+     * @see #setActionView(View)
      */
     public void setShowAsAction(int actionEnum);
+
+    /**
+     * Set an action view for this menu item. An action view will be displayed in place
+     * of an automatically generated menu item element in the UI when this item is shown
+     * as an action within a parent.
+     *
+     * @param view View to use for presenting this item to the user.
+     * @return This Item so additional setters can be called.
+     *
+     * @see #setShowAsAction(int)
+     */
+    public MenuItem setActionView(View view);
+
+    /**
+     * Returns the currently set action view for this menu item.
+     *
+     * @return This item's action view
+     *
+     * @see #setActionView(View)
+     * @see #setShowAsAction(int)
+     */
+    public View getActionView();
 }
\ No newline at end of file
diff --git a/core/java/com/android/internal/view/menu/ActionMenuItem.java b/core/java/com/android/internal/view/menu/ActionMenuItem.java
index 035875a..d2851a9 100644
--- a/core/java/com/android/internal/view/menu/ActionMenuItem.java
+++ b/core/java/com/android/internal/view/menu/ActionMenuItem.java
@@ -19,9 +19,10 @@
 import android.content.Context;
 import android.content.Intent;
 import android.graphics.drawable.Drawable;
+import android.view.ContextMenu.ContextMenuInfo;
 import android.view.MenuItem;
 import android.view.SubMenu;
-import android.view.ContextMenu.ContextMenuInfo;
+import android.view.View;
 
 /**
  * @hide
@@ -222,4 +223,12 @@
     public void setShowAsAction(int show) {
         // Do nothing. ActionMenuItems always show as action buttons.
     }
+
+    public MenuItem setActionView(View actionView) {
+        throw new UnsupportedOperationException();
+    }
+
+    public View getActionView() {
+        return null;
+    }
 }
diff --git a/core/java/com/android/internal/view/menu/ActionMenuView.java b/core/java/com/android/internal/view/menu/ActionMenuView.java
index c4b6214..20939ab 100644
--- a/core/java/com/android/internal/view/menu/ActionMenuView.java
+++ b/core/java/com/android/internal/view/menu/ActionMenuView.java
@@ -21,6 +21,7 @@
 import android.content.res.TypedArray;
 import android.util.AttributeSet;
 import android.view.Gravity;
+import android.view.View;
 import android.view.ViewGroup;
 import android.widget.ImageButton;
 import android.widget.LinearLayout;
@@ -158,8 +159,13 @@
         
         for (int i = 0; i < itemCount; i++) {
             final MenuItemImpl itemData = itemsToShow.get(i);
-            addItemView((ActionMenuItemView) itemData.getItemView(MenuBuilder.TYPE_ACTION_BUTTON,
-                    this));
+            final View actionView = itemData.getActionView();
+            if (actionView != null) {
+                addView(actionView);
+            } else {
+                addItemView((ActionMenuItemView) itemData.getItemView(
+                        MenuBuilder.TYPE_ACTION_BUTTON, this));
+            }
         }
 
         if (reserveOverflow) {
diff --git a/core/java/com/android/internal/view/menu/MenuItemImpl.java b/core/java/com/android/internal/view/menu/MenuItemImpl.java
index 07a2a94..42f9771 100644
--- a/core/java/com/android/internal/view/menu/MenuItemImpl.java
+++ b/core/java/com/android/internal/view/menu/MenuItemImpl.java
@@ -78,6 +78,8 @@
 
     private int mShowAsAction = SHOW_AS_ACTION_NEVER;
 
+    private View mActionView;
+
     /** Used for the icon resource ID if this item does not have an icon */
     static final int NO_ICON = 0;
 
@@ -666,4 +668,13 @@
         mShowAsAction = actionEnum;
         mMenu.onItemActionRequestChanged(this);
     }
+
+    public MenuItem setActionView(View view) {
+        mActionView = view;
+        return this;
+    }
+
+    public View getActionView() {
+        return mActionView;
+    }
 }
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 3bee089..9363db5 100755
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -3502,6 +3502,16 @@
             <enum name="always" value="2" />
         </attr>
 
+        <!-- An optional layout to be used as an action view.
+             See {@link android.view.MenuItem#setActionView(android.view.View)}
+             for more info. -->
+        <attr name="actionLayout" format="reference" />
+
+        <!-- The name of an optional View class to instantiate and use as an
+             action view. See {@link android.view.MenuItem#setActionView(android.view.View)}
+             for more info. -->
+        <attr name="actionViewClass" format="string" />
+
     </declare-styleable>
 
     <!-- **************************************************************** -->
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 0b32362..86e5579 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -1344,6 +1344,8 @@
   <public type="attr" name="titleTextStyle" />
   <public type="attr" name="subtitleTextStyle" />
   <public type="attr" name="iconifiedByDefault" />
+  <public type="attr" name="actionLayout" />
+  <public type="attr" name="actionViewClass" />
 
   <public type="anim" name="animator_fade_in" />
   <public type="anim" name="animator_fade_out" />