ActionBar added to framework, integrated with Activity and styles.
Added onClick attribute support to menus in MenuInflater.
Change-Id: I739771b4f249d87a0d8b15969f3d526b099067a1
diff --git a/core/java/android/app/ActionBar.java b/core/java/android/app/ActionBar.java
new file mode 100644
index 0000000..e28d3d3
--- /dev/null
+++ b/core/java/android/app/ActionBar.java
@@ -0,0 +1,253 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app;
+
+import android.graphics.drawable.Drawable;
+import android.view.ActionBarView;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.view.View;
+
+/**
+ * This is the public interface to the contextual ActionBar.
+ * The ActionBar acts as a replacement for the title bar in Activities.
+ * It provides facilities for creating toolbar actions as well as
+ * methods of navigating around an application.
+ */
+public abstract class ActionBar {
+ /**
+ * Normal/standard navigation mode. Consists of either a logo or icon
+ * and title text with an optional subtitle. Clicking any of these elements
+ * will dispatch onActionItemSelected to the registered Callback with
+ * a MenuItem with item ID android.R.id.home.
+ */
+ public static final int NAVIGATION_MODE_NORMAL = 0;
+
+ /**
+ * Dropdown list navigation mode. Instead of static title text this mode
+ * presents a dropdown menu for navigation within the activity.
+ */
+ public static final int NAVIGATION_MODE_DROPDOWN_LIST = 1;
+
+ /**
+ * Tab navigation mode. Instead of static title text this mode
+ * presents a series of tabs for navigation within the activity.
+ */
+ public static final int NAVIGATION_MODE_TABS = 2;
+
+ /**
+ * Custom navigation mode. This navigation mode is set implicitly whenever
+ * a custom navigation view is set. See {@link #setCustomNavigationView(View)}.
+ */
+ public static final int NAVIGATION_MODE_CUSTOM = 3;
+
+ /**
+ * Use logo instead of icon if available. This flag will cause appropriate
+ * navigation modes to use a wider logo in place of the standard icon.
+ */
+ public static final int DISPLAY_USE_LOGO = 0x1;
+
+ /**
+ * Hide 'home' elements in this action bar, leaving more space for other
+ * navigation elements. This includes logo and icon.
+ */
+ public static final int DISPLAY_HIDE_HOME = 0x2;
+
+ /**
+ * Set the callback that the ActionBar will use to handle events
+ * and populate menus.
+ * @param callback Callback to use
+ */
+ public abstract void setCallback(Callback callback);
+
+ /**
+ * Set a custom navigation view.
+ *
+ * Custom navigation views appear between the application icon and
+ * any action buttons and may use any space available there. Common
+ * use cases for custom navigation views might include an address bar
+ * for a browser or other navigation mechanisms that do not translate
+ * well to provided navigation modes.
+ *
+ * Setting a non-null custom navigation view will also set the
+ * navigation mode to NAVMODE_CUSTOM.
+ *
+ * @param view Custom navigation view to place in the ActionBar.
+ */
+ public abstract void setCustomNavigationView(View view);
+
+ /**
+ * Set the ActionBar's title.
+ *
+ * This is set automatically to the name of your Activity,
+ * but may be changed here.
+ *
+ * @param title Title text
+ */
+ public abstract void setTitle(CharSequence title);
+
+ /**
+ * Set the ActionBar's subtitle.
+ *
+ * The subtitle is usually displayed as a second line of text
+ * under the title. Good for extended descriptions of activity state.
+ *
+ * @param subtitle Subtitle text.
+ */
+ public abstract void setSubtitle(CharSequence subtitle);
+
+ /**
+ * Set the navigation mode.
+ *
+ * @param mode One of {@link #NAVIGATION_MODE_NORMAL}, {@link #NAVIGATION_MODE_DROPDOWN_LIST},
+ * {@link #NAVIGATION_MODE_TABS}, or {@link #NAVIGATION_MODE_CUSTOM}.
+ */
+ public abstract void setNavigationMode(int mode);
+
+ /**
+ * Set display options.
+ *
+ * @param options A combination of the bits defined by the DISPLAY_ constants
+ * defined in ActionBar.
+ */
+ public abstract void setDisplayOptions(int options);
+
+ /**
+ * Set the ActionBar's background.
+ *
+ * @param d Background drawable
+ */
+ public abstract void setBackgroundDrawable(Drawable d);
+
+ /**
+ * Set a drawable to use as a divider between sections of the ActionBar.
+ *
+ * @param d Divider drawable
+ */
+ public abstract void setDividerDrawable(Drawable d);
+
+ /**
+ * @return The current custom navigation view.
+ */
+ public abstract View getCustomNavigationView();
+
+ /**
+ * @return The current ActionBar title.
+ */
+ public abstract CharSequence getTitle();
+
+ /**
+ * @return The current ActionBar subtitle.
+ */
+ public abstract CharSequence getSubtitle();
+
+ /**
+ * @return The current navigation mode.
+ */
+ public abstract int getNavigationMode();
+
+ /**
+ * @return The current set of display options.
+ */
+ public abstract int getDisplayOptions();
+
+ /**
+ * Request an update of the items in the action menu.
+ * This will result in a call to Callback.onUpdateActionMenu(Menu)
+ * and the ActionBar will update based on any changes made there.
+ */
+ public abstract void updateActionMenu();
+
+ /**
+ * Callback interface for ActionBar events.
+ */
+ public interface Callback {
+ /**
+ * Initialize the always-visible contents of the action bar.
+ * You should place your menu items into <var>menu</var>.
+ *
+ * <p>This is only called once, the first time the action bar is displayed.
+ *
+ * @param menu The action menu in which to place your items.
+ * @return You must return true for actions to be displayed;
+ * if you return false they will not be shown.
+ *
+ * @see #onActionItemSelected(MenuItem)
+ */
+ public boolean onCreateActionMenu(Menu menu);
+
+ /**
+ * Update the action bar. This is called in response to {@link #updateActionMenu()}
+ * calls, which may be application-initiated or the result of changing fragment state.
+ *
+ * @return true if the action bar should update based on altered menu contents,
+ * false if no changes are necessary.
+ */
+ public boolean onUpdateActionMenu(Menu menu);
+
+ /**
+ * This hook is called whenever an item in your action bar is selected.
+ * The default implementation simply returns false to have the normal
+ * processing happen (sending a message to its handler). You can use this
+ * method for any items for which you would like to do processing without
+ * those other facilities.
+ *
+ * @param item The action bar item that was selected.
+ * @return boolean Return false to allow normal menu processing to proceed,
+ * true to consume it here.
+ */
+ public boolean onActionItemSelected(MenuItem item);
+
+ /*
+ * In progress
+ */
+ public boolean onCreateContextMode(int modeId, Menu menu);
+ public boolean onPrepareContextMode(int modeId, Menu menu);
+ public boolean onContextItemSelected(int modeId, MenuItem item);
+ }
+
+ /**
+ * Simple stub implementations of ActionBar.Callback methods.
+ * Extend this if you only need a subset of Callback functionality.
+ */
+ public static class SimpleCallback implements Callback {
+ public boolean onCreateActionMenu(Menu menu) {
+ return false;
+ }
+
+ public boolean onUpdateActionMenu(Menu menu) {
+ return false;
+ }
+
+ public boolean onActionItemSelected(MenuItem item) {
+ return false;
+ }
+
+ public boolean onCreateContextMode(int modeId, Menu menu) {
+ return false;
+ }
+
+ public boolean onPrepareContextMode(int modeId, Menu menu) {
+ return false;
+ }
+
+ public boolean onContextItemSelected(int modeId, MenuItem item) {
+ return false;
+ }
+ }
+
+}
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index aa3cb69..21c6d2a 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -16,14 +16,16 @@
package android.app;
-import com.android.internal.policy.PolicyManager;
+import java.lang.reflect.Constructor;
+import java.util.ArrayList;
+import java.util.HashMap;
import android.content.ComponentCallbacks;
import android.content.ComponentName;
import android.content.ContentResolver;
import android.content.Context;
-import android.content.Intent;
import android.content.IIntentSender;
+import android.content.Intent;
import android.content.IntentSender;
import android.content.SharedPreferences;
import android.content.pm.ActivityInfo;
@@ -50,6 +52,7 @@
import android.util.EventLog;
import android.util.Log;
import android.util.SparseArray;
+import android.view.ActionBarView;
import android.view.ContextMenu;
import android.view.ContextThemeWrapper;
import android.view.InflateException;
@@ -69,10 +72,10 @@
import android.view.ViewGroup.LayoutParams;
import android.view.accessibility.AccessibilityEvent;
import android.widget.AdapterView;
+import android.widget.LinearLayout;
-import java.lang.reflect.Constructor;
-import java.util.ArrayList;
-import java.util.HashMap;
+import com.android.internal.app.SplitActionBar;
+import com.android.internal.policy.PolicyManager;
/**
* An activity is a single, focused thing that the user can do. Almost all
@@ -650,6 +653,7 @@
/*package*/ boolean mWindowAdded = false;
/*package*/ boolean mVisibleFromServer = false;
/*package*/ boolean mVisibleFromClient = true;
+ /*package*/ ActionBar mActionBar = null;
private CharSequence mTitle;
private int mTitleColor = 0;
@@ -1793,7 +1797,19 @@
public View findViewById(int id) {
return getWindow().findViewById(id);
}
-
+
+ /**
+ * Retrieve a reference to this activity's ActionBar.
+ *
+ * <p><em>Note:</em> The ActionBar is initialized when a content view
+ * is set. This function will return null if called before {@link #setContentView}
+ * or {@link #addContentView}.
+ * @return The Activity's ActionBar, or null if it does not have one.
+ */
+ public ActionBar getActionBar() {
+ return mActionBar;
+ }
+
/**
* Finds a fragment that was identified by the given id either when inflated
* from XML or as the container ID when added in a transaction. This only
@@ -1805,6 +1821,27 @@
}
/**
+ * Creates a new ActionBar, locates the inflated ActionBarView,
+ * initializes the ActionBar with the view, and sets mActionBar.
+ */
+ private void initActionBar() {
+ if (!getWindow().hasFeature(Window.FEATURE_ACTION_BAR)) {
+ return;
+ }
+
+ ActionBarView view = (ActionBarView) findViewById(com.android.internal.R.id.action_bar);
+ if (view != null) {
+ LinearLayout splitView =
+ (LinearLayout) findViewById(com.android.internal.R.id.context_action_bar);
+ if (splitView != null) {
+ mActionBar = new SplitActionBar(view, splitView);
+ }
+ } else {
+ Log.e(TAG, "Could not create action bar; view not found in window decor.");
+ }
+ }
+
+ /**
* Set the activity content from a layout resource. The resource will be
* inflated, adding all top-level views to the activity.
*
@@ -1812,6 +1849,7 @@
*/
public void setContentView(int layoutResID) {
getWindow().setContentView(layoutResID);
+ initActionBar();
}
/**
@@ -1823,6 +1861,7 @@
*/
public void setContentView(View view) {
getWindow().setContentView(view);
+ initActionBar();
}
/**
@@ -1835,6 +1874,7 @@
*/
public void setContentView(View view, ViewGroup.LayoutParams params) {
getWindow().setContentView(view, params);
+ initActionBar();
}
/**
@@ -1846,6 +1886,7 @@
*/
public void addContentView(View view, ViewGroup.LayoutParams params) {
getWindow().addContentView(view, params);
+ initActionBar();
}
/**
diff --git a/core/java/android/view/ActionBarView.java b/core/java/android/view/ActionBarView.java
new file mode 100644
index 0000000..941ef21
--- /dev/null
+++ b/core/java/android/view/ActionBarView.java
@@ -0,0 +1,625 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view;
+
+import java.util.ArrayList;
+
+import android.app.ActionBar;
+import android.app.Activity;
+import android.app.ActionBar.Callback;
+import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.content.res.TypedArray;
+import android.graphics.PorterDuff;
+import android.graphics.PorterDuffColorFilter;
+import android.graphics.drawable.Drawable;
+import android.util.AttributeSet;
+import android.util.DisplayMetrics;
+import android.util.SparseArray;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+import com.android.internal.R;
+import com.android.internal.view.menu.ActionMenu;
+import com.android.internal.view.menu.ActionMenuItem;
+
+/**
+ * @hide
+ */
+public class ActionBarView extends ViewGroup {
+ private static final String TAG = "ActionBarView";
+
+ // TODO: This must be defined in the default theme
+ private static final int CONTENT_HEIGHT_DIP = 50;
+ private static final int CONTENT_PADDING_DIP = 3;
+ private static final int CONTENT_SPACING_DIP = 6;
+ private static final int CONTENT_ACTION_SPACING_DIP = 12;
+
+ /**
+ * Display options applied by default
+ */
+ public static final int DISPLAY_DEFAULT = 0;
+
+ /**
+ * Display options that require re-layout as opposed to a simple invalidate
+ */
+ private static final int DISPLAY_RELAYOUT_MASK = ActionBar.DISPLAY_USE_LOGO;
+
+ private final int mContentHeight;
+
+ private int mNavigationMode;
+ private int mDisplayOptions;
+ private int mSpacing;
+ private int mActionSpacing;
+ private CharSequence mTitle;
+ private CharSequence mSubtitle;
+ private Drawable mIcon;
+ private Drawable mLogo;
+ private Drawable mDivider;
+
+ private ImageView mIconView;
+ private ImageView mLogoView;
+ private TextView mTitleView;
+ private TextView mSubtitleView;
+ private View mNavigationView;
+
+ private boolean mShowMenu;
+
+ private ActionMenuItem mLogoNavItem;
+ private ActionMenu mNavMenu;
+ private ActionMenu mActionMenu;
+ private ActionMenu mOptionsMenu;
+
+ private SparseArray<ActionMenu> mContextMenus;
+
+ private Callback mCallback;
+
+ private final ArrayList<ActionView> mActions = new ArrayList<ActionView>();
+ private final OnClickListener mActionClickHandler = new OnClickListener() {
+ public void onClick(View v) {
+ ActionView av = (ActionView) v;
+ ActionMenuItem item = (ActionMenuItem) av.menuItem;
+
+ if (!mCallback.onActionItemSelected(item)) {
+ item.invoke();
+ }
+ }
+ };
+
+ private OnClickListener mHomeClickListener = null;
+
+ public ActionBarView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+
+ final DisplayMetrics metrics = context.getResources().getDisplayMetrics();
+ mContentHeight = (int) (CONTENT_HEIGHT_DIP * metrics.density + 0.5f);
+
+ TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.ActionBar);
+
+ final int colorFilter = a.getColor(R.styleable.ActionBar_colorFilter, 0);
+
+ if (colorFilter != 0) {
+ final Drawable d = getBackground();
+ d.setDither(true);
+ d.setColorFilter(new PorterDuffColorFilter(colorFilter, PorterDuff.Mode.OVERLAY));
+ }
+
+ ApplicationInfo info = context.getApplicationInfo();
+ PackageManager pm = context.getPackageManager();
+ mNavigationMode = a.getInt(R.styleable.ActionBar_navigationMode, ActionBar.NAVIGATION_MODE_NORMAL);
+ mTitle = a.getText(R.styleable.ActionBar_title);
+ mSubtitle = a.getText(R.styleable.ActionBar_subtitle);
+ mDisplayOptions = a.getInt(R.styleable.ActionBar_displayOptions, DISPLAY_DEFAULT);
+
+ mLogo = a.getDrawable(R.styleable.ActionBar_logo);
+ if (mLogo == null) {
+ mLogo = info.loadLogo(pm);
+ }
+ mIcon = a.getDrawable(R.styleable.ActionBar_icon);
+ if (mIcon == null) {
+ mIcon = info.loadIcon(pm);
+ }
+ mDivider = a.getDrawable(R.styleable.ActionBar_divider);
+
+ Drawable background = a.getDrawable(R.styleable.ActionBar_background);
+ if (background != null) {
+ setBackgroundDrawable(background);
+ }
+
+ final int customNavId = a.getResourceId(R.styleable.ActionBar_customNavigationLayout, 0);
+ if (customNavId != 0) {
+ LayoutInflater inflater = LayoutInflater.from(context);
+ mNavigationView = (View) inflater.inflate(customNavId, null);
+ mNavigationMode = ActionBar.NAVIGATION_MODE_CUSTOM;
+ }
+
+ a.recycle();
+
+ // TODO: Set this in the theme
+ int padding = (int) (CONTENT_PADDING_DIP * metrics.density + 0.5f);
+ setPadding(padding, padding, padding, padding);
+
+ mSpacing = (int) (CONTENT_SPACING_DIP * metrics.density + 0.5f);
+ mActionSpacing = (int) (CONTENT_ACTION_SPACING_DIP * metrics.density + 0.5f);
+
+ if (mLogo != null || mIcon != null || mTitle != null) {
+ mLogoNavItem = new ActionMenuItem(context, 0, android.R.id.home, 0, 0, mTitle);
+ mHomeClickListener = new OnClickListener() {
+ public void onClick(View v) {
+ if (mCallback != null) {
+ mCallback.onActionItemSelected(mLogoNavItem);
+ }
+ }
+ };
+ }
+
+ mContextMenus = new SparseArray<ActionMenu>();
+ }
+
+ private boolean initOptionsMenu() {
+ final Context context = getContext();
+ if (!(context instanceof Activity)) {
+ return false;
+ }
+
+ final Activity activity = (Activity) context;
+ ActionMenu optionsMenu = new ActionMenu(context);
+ if (activity.onCreateOptionsMenu(optionsMenu)) {
+ mOptionsMenu = optionsMenu;
+ return true;
+ }
+
+ return false;
+ }
+
+ public void setCallback(Callback callback) {
+ final Context context = getContext();
+ mCallback = callback;
+
+ ActionMenu actionMenu = new ActionMenu(context);
+ if (callback.onCreateActionMenu(actionMenu)) {
+ mActionMenu = actionMenu;
+ performUpdateActionMenu();
+ }
+ }
+
+ public void setCustomNavigationView(View view) {
+ mNavigationView = view;
+ if (view != null) {
+ setNavigationMode(ActionBar.NAVIGATION_MODE_CUSTOM);
+ }
+ requestLayout();
+ }
+
+ public void setDividerDrawable(Drawable d) {
+ mDivider = d;
+ }
+
+ public CharSequence getTitle() {
+ return mTitle;
+ }
+
+ public void setTitle(CharSequence title) {
+ mTitle = title;
+ if (mTitleView != null) {
+ mTitleView.setText(title);
+ }
+ if (mLogoNavItem != null) {
+ mLogoNavItem.setTitle(title);
+ }
+ }
+
+ public CharSequence getSubtitle() {
+ return mSubtitle;
+ }
+
+ public void setSubtitle(CharSequence subtitle) {
+ mSubtitle = subtitle;
+ if (mSubtitleView != null) {
+ mSubtitleView.setText(subtitle);
+ }
+ }
+
+ public void setDisplayOptions(int options) {
+ final int flagsChanged = options & mDisplayOptions;
+ mDisplayOptions = options;
+ if ((flagsChanged & DISPLAY_RELAYOUT_MASK) != 0) {
+ requestLayout();
+ } else {
+ invalidate();
+ }
+ }
+
+ public void setNavigationMode(int mode) {
+ if (mode != mNavigationMode) {
+ mNavigationMode = mode;
+ requestLayout();
+ }
+ }
+
+ public View getCustomNavigationView() {
+ return mNavigationView;
+ }
+
+ public int getNavigationMode() {
+ return mNavigationMode;
+ }
+
+ public int getDisplayOptions() {
+ return mDisplayOptions;
+ }
+
+ private ActionView findActionViewForItem(MenuItem item) {
+ final ArrayList<ActionView> actions = mActions;
+ final int actionCount = actions.size();
+ for (int i = 0; i < actionCount; i++) {
+ ActionView av = actions.get(i);
+ if (av.menuItem.equals(item)) {
+ return av;
+ }
+ }
+ return null;
+ }
+
+ public void setContextMode(int mode) {
+ Callback callback = mCallback;
+ if (callback == null) {
+ throw new IllegalStateException(
+ "Attempted to set ActionBar context mode with no callback");
+ }
+
+ ActionMenu menu = mContextMenus.get(mode);
+ if (menu == null) {
+ // Initialize the new mode
+ menu = new ActionMenu(getContext());
+
+ if (!callback.onCreateContextMode(mode, menu)) {
+ throw new IllegalArgumentException(
+ "ActionBar callback does not know how to create context mode " + mode);
+ }
+ mContextMenus.put(mode, menu);
+ }
+
+ if (callback.onPrepareContextMode(mode, menu)) {
+ // TODO Set mode, animate, etc.
+ }
+ }
+
+ public void exitContextMode() {
+ // TODO Turn off context mode; go back to normal.
+ }
+
+ public void updateActionMenu() {
+ final ActionMenu menu = mActionMenu;
+ if (menu == null || mCallback == null || !mCallback.onUpdateActionMenu(menu)) {
+ return;
+ }
+ performUpdateActionMenu();
+ }
+
+ private void performUpdateActionMenu() {
+ final ActionMenu menu = mActionMenu;
+ if (menu == null) {
+ return;
+ }
+ final Context context = getContext();
+
+ int childCount = getChildCount();
+ int childIndex = 0;
+ while (childIndex < childCount) {
+ View v = getChildAt(childIndex);
+ if (v instanceof ActionView) {
+ detachViewFromParent(childIndex);
+ childCount--;
+ } else {
+ childIndex++;
+ }
+ }
+
+ ArrayList<ActionView> detachedViews = new ArrayList<ActionView>(mActions);
+ final int itemCount = menu.size();
+ for (int i = 0; i < itemCount; i++) {
+ final MenuItem item = menu.getItem(i);
+
+ boolean newView = false;
+ ActionView actionView = findActionViewForItem(item);
+ if (actionView == null) {
+ actionView = new ActionView(context);
+ newView = true;
+ }
+ actionView.actionId = item.getItemId();
+ actionView.menuItem = item;
+ actionView.actionLabel = item.getTitle();
+ actionView.setAdjustViewBounds(true);
+ actionView.setImageDrawable(item.getIcon());
+ actionView.setOnClickListener(mActionClickHandler);
+
+ LayoutParams layoutParams = new LayoutParams(LayoutParams.WRAP_CONTENT,
+ LayoutParams.MATCH_PARENT, LayoutParams.ITEM_TYPE_ACTION);
+ actionView.setLayoutParams(layoutParams);
+
+ if (newView) {
+ addView(actionView);
+ mActions.add(actionView);
+ } else {
+ attachViewToParent(actionView, -1, layoutParams);
+ detachedViews.remove(actionView);
+ actionView.invalidate();
+ }
+ }
+
+ final int detachedCount = detachedViews.size();
+ for (int i = 0; i < detachedCount; i++) {
+ removeDetachedView(detachedViews.get(i), false);
+ }
+
+ requestLayout();
+ }
+
+ public void addAction(int id, Drawable icon, CharSequence label, OnActionListener listener) {
+ ActionView actionView = new ActionView(getContext());
+ actionView.actionId = id;
+ actionView.actionLabel = label;
+ actionView.actionListener = listener;
+ actionView.setAdjustViewBounds(true);
+ actionView.setImageDrawable(icon);
+ actionView.setOnClickListener(mActionClickHandler);
+
+ actionView.setLayoutParams(new LayoutParams(LayoutParams.WRAP_CONTENT,
+ LayoutParams.MATCH_PARENT, LayoutParams.ITEM_TYPE_ACTION));
+
+ addView(actionView);
+ mActions.add(actionView);
+
+ requestLayout();
+ }
+
+ @Override
+ protected void onFinishInflate() {
+ super.onFinishInflate();
+
+ if ((mDisplayOptions & ActionBar.DISPLAY_HIDE_HOME) == 0) {
+ if (mLogo != null && (mDisplayOptions & ActionBar.DISPLAY_USE_LOGO) != 0) {
+ mLogoView = new ImageView(getContext());
+ mLogoView.setAdjustViewBounds(true);
+ mLogoView.setLayoutParams(new LayoutParams(LayoutParams.WRAP_CONTENT,
+ LayoutParams.MATCH_PARENT, LayoutParams.ITEM_TYPE_ICON));
+ mLogoView.setImageDrawable(mLogo);
+ mLogoView.setClickable(true);
+ mLogoView.setOnClickListener(mHomeClickListener);
+ addView(mLogoView);
+ } else if (mIcon != null) {
+ mIconView = new ImageView(getContext());
+ mIconView.setAdjustViewBounds(true);
+ mIconView.setLayoutParams(new LayoutParams(LayoutParams.WRAP_CONTENT,
+ LayoutParams.MATCH_PARENT, LayoutParams.ITEM_TYPE_ICON));
+ mIconView.setImageDrawable(mIcon);
+ mIconView.setClickable(true);
+ mIconView.setOnClickListener(mHomeClickListener);
+ addView(mIconView);
+ }
+ }
+
+ switch (mNavigationMode) {
+ case ActionBar.NAVIGATION_MODE_NORMAL:
+ if (mLogoView == null) {
+ LayoutInflater inflater = LayoutInflater.from(getContext());
+ mTitleView = (TextView) inflater.inflate(R.layout.action_bar_title_item, null);
+ mTitleView.setLayoutParams(new LayoutParams(LayoutParams.WRAP_CONTENT,
+ LayoutParams.WRAP_CONTENT, LayoutParams.ITEM_TYPE_TITLE));
+ if (mTitle != null) {
+ mTitleView.setText(mTitle);
+ }
+ mTitleView.setClickable(true);
+ mTitleView.setOnClickListener(mHomeClickListener);
+ addView(mTitleView);
+ }
+ break;
+
+ case ActionBar.NAVIGATION_MODE_DROPDOWN_LIST:
+ throw new UnsupportedOperationException(
+ "Dropdown list navigation isn't supported yet!");
+
+ case ActionBar.NAVIGATION_MODE_TABS:
+ throw new UnsupportedOperationException(
+ "Tab navigation isn't supported yet!");
+
+ case ActionBar.NAVIGATION_MODE_CUSTOM:
+ if (mNavigationView != null) {
+ mNavigationView.setLayoutParams(new LayoutParams(LayoutParams.WRAP_CONTENT,
+ LayoutParams.WRAP_CONTENT, LayoutParams.ITEM_TYPE_CUSTOM_NAV));
+ addView(mNavigationView);
+ }
+ break;
+ }
+ }
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ int widthMode = MeasureSpec.getMode(widthMeasureSpec);
+ if (widthMode != MeasureSpec.EXACTLY) {
+ throw new IllegalStateException(getClass().getSimpleName() + " can only be used " +
+ "with android:layout_width=\"match_parent\" (or fill_parent)");
+ }
+
+ int heightMode = MeasureSpec.getMode(heightMeasureSpec);
+ if (heightMode != MeasureSpec.AT_MOST) {
+ throw new IllegalStateException(getClass().getSimpleName() + " can only be used " +
+ "with android:layout_height=\"wrap_content\"");
+ }
+
+ int contentWidth = MeasureSpec.getSize(widthMeasureSpec);
+
+ int availableWidth = contentWidth - getPaddingLeft() - getPaddingRight();
+ int childSpecHeight = MeasureSpec.makeMeasureSpec(mContentHeight - getPaddingTop() -
+ getPaddingBottom(), MeasureSpec.AT_MOST);
+
+ if (mLogoView != null) {
+ availableWidth = measureChildView(mLogoView, availableWidth, childSpecHeight, mSpacing);
+ }
+ if (mIconView != null) {
+ availableWidth = measureChildView(mIconView, availableWidth, childSpecHeight, mSpacing);
+ }
+
+ final ArrayList<ActionView> actions = mActions;
+ final int actionCount = actions.size();
+ for (int i = 0; i < actionCount; i++) {
+ ActionView action = actions.get(i);
+ availableWidth = measureChildView(action, availableWidth,
+ childSpecHeight, mActionSpacing);
+ }
+
+ switch (mNavigationMode) {
+ case ActionBar.NAVIGATION_MODE_NORMAL:
+ if (mTitleView != null) {
+ availableWidth = measureChildView(mTitleView, availableWidth,
+ childSpecHeight, mSpacing);
+ }
+ break;
+ case ActionBar.NAVIGATION_MODE_CUSTOM:
+ if (mNavigationView != null) {
+ availableWidth = measureChildView(mNavigationView, availableWidth,
+ childSpecHeight, mSpacing);
+ }
+ break;
+ }
+
+ setMeasuredDimension(contentWidth, mContentHeight);
+ }
+
+ private int measureChildView(View child, int availableWidth, int childSpecHeight, int spacing) {
+ measureChild(child,
+ MeasureSpec.makeMeasureSpec(availableWidth, MeasureSpec.AT_MOST),
+ childSpecHeight);
+
+ availableWidth -= child.getMeasuredWidth();
+ availableWidth -= spacing;
+
+ return availableWidth;
+ }
+
+ @Override
+ protected void onLayout(boolean changed, int l, int t, int r, int b) {
+ int x = getPaddingLeft();
+ final int y = getPaddingTop();
+ final int contentHeight = b - t - getPaddingTop() - getPaddingBottom();
+
+ if (mLogoView != null) {
+ x += positionChild(mLogoView, x, y, contentHeight) + mSpacing;
+ }
+ if (mIconView != null) {
+ x += positionChild(mIconView, x, y, contentHeight) + mSpacing;
+ }
+
+ switch (mNavigationMode) {
+ case ActionBar.NAVIGATION_MODE_NORMAL:
+ if (mTitleView != null) {
+ x += positionChild(mTitleView, x, y, contentHeight) + mSpacing;
+ }
+ break;
+
+ case ActionBar.NAVIGATION_MODE_CUSTOM:
+ if (mNavigationView != null) {
+ x += positionChild(mNavigationView, x, y, contentHeight) + mSpacing;
+ }
+ break;
+ }
+
+ x = r - l - getPaddingRight();
+
+ final int count = mActions.size();
+ for (int i = count - 1; i >= 0; i--) {
+ ActionView action = mActions.get(i);
+ x -= (positionChildInverse(action, x, y, contentHeight) + mActionSpacing);
+ }
+ }
+
+ private int positionChild(View child, int x, int y, int contentHeight) {
+ int childWidth = child.getMeasuredWidth();
+ int childHeight = child.getMeasuredHeight();
+ int childTop = y + (contentHeight - childHeight) / 2;
+
+ child.layout(x, childTop, x + childWidth, childTop + childHeight);
+
+ return childWidth;
+ }
+
+ private int positionChildInverse(View child, int x, int y, int contentHeight) {
+ int childWidth = child.getMeasuredWidth();
+ int childHeight = child.getMeasuredHeight();
+ int childTop = y + (contentHeight - childHeight) / 2;
+
+ child.layout(x - childWidth, childTop, x, childTop + childHeight);
+
+ return childWidth;
+ }
+
+ @Override
+ public ViewGroup.LayoutParams generateLayoutParams(AttributeSet attrs) {
+ return new ViewGroup.LayoutParams(getContext(), attrs);
+ }
+
+ @Override
+ protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
+ return p != null && p instanceof LayoutParams;
+ }
+
+ private static class LayoutParams extends ViewGroup.LayoutParams {
+ static final int ITEM_TYPE_UNKNOWN = -1;
+ static final int ITEM_TYPE_ICON = 0;
+ static final int ITEM_TYPE_TITLE = 1;
+ static final int ITEM_TYPE_CUSTOM_NAV = 2;
+ static final int ITEM_TYPE_ACTION = 3;
+ static final int ITEM_TYPE_MORE = 4;
+
+ int type = ITEM_TYPE_UNKNOWN;
+
+ public LayoutParams(Context c, AttributeSet attrs) {
+ super(c, attrs);
+ }
+
+ public LayoutParams(int width, int height) {
+ super(width, height);
+ }
+
+ public LayoutParams(int width, int height, int type) {
+ this(width, height);
+ this.type = type;
+ }
+
+
+ public LayoutParams(ViewGroup.LayoutParams source) {
+ super(source);
+ }
+ }
+
+ public interface OnActionListener {
+ void onAction(int id);
+ }
+
+ private static class ActionView extends ImageView {
+ int actionId;
+ CharSequence actionLabel;
+ OnActionListener actionListener;
+ MenuItem menuItem;
+
+ public ActionView(Context context) {
+ super(context);
+ }
+ }
+}
diff --git a/core/java/android/view/MenuInflater.java b/core/java/android/view/MenuInflater.java
index 46c805c..a37d83d 100644
--- a/core/java/android/view/MenuInflater.java
+++ b/core/java/android/view/MenuInflater.java
@@ -16,9 +16,8 @@
package android.view;
-import com.android.internal.view.menu.MenuItemImpl;
-
import java.io.IOException;
+import java.lang.reflect.Method;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
@@ -30,6 +29,8 @@
import android.util.AttributeSet;
import android.util.Xml;
+import com.android.internal.view.menu.MenuItemImpl;
+
/**
* This class is used to instantiate menu XML files into Menu objects.
* <p>
@@ -166,6 +167,41 @@
}
}
+ private static class InflatedOnMenuItemClickListener
+ implements MenuItem.OnMenuItemClickListener {
+ private static final Class[] PARAM_TYPES = new Class[] { MenuItem.class };
+
+ private Context mContext;
+ private Method mMethod;
+
+ public InflatedOnMenuItemClickListener(Context context, String methodName) {
+ mContext = context;
+ Class c = context.getClass();
+ try {
+ mMethod = c.getMethod(methodName, PARAM_TYPES);
+ } catch (Exception e) {
+ InflateException ex = new InflateException(
+ "Couldn't resolve menu item onClick handler " + methodName +
+ " in class " + c.getName());
+ ex.initCause(e);
+ throw ex;
+ }
+ }
+
+ public boolean onMenuItemClick(MenuItem item) {
+ try {
+ if (mMethod.getReturnType() == Boolean.TYPE) {
+ return (Boolean) mMethod.invoke(mContext, item);
+ } else {
+ mMethod.invoke(mContext, item);
+ return true;
+ }
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+ }
+
/**
* State for the current menu.
* <p>
@@ -205,6 +241,8 @@
private boolean itemVisible;
private boolean itemEnabled;
+ private String itemListenerMethodName;
+
private static final int defaultGroupId = NO_ID;
private static final int defaultItemId = NO_ID;
private static final int defaultItemCategory = 0;
@@ -276,6 +314,7 @@
itemChecked = a.getBoolean(com.android.internal.R.styleable.MenuItem_checked, defaultItemChecked);
itemVisible = a.getBoolean(com.android.internal.R.styleable.MenuItem_visible, groupVisible);
itemEnabled = a.getBoolean(com.android.internal.R.styleable.MenuItem_enabled, groupEnabled);
+ itemListenerMethodName = a.getString(com.android.internal.R.styleable.MenuItem_onClick);
a.recycle();
@@ -299,8 +338,13 @@
.setIcon(itemIconResId)
.setAlphabeticShortcut(itemAlphabeticShortcut)
.setNumericShortcut(itemNumericShortcut);
+
+ if (itemListenerMethodName != null) {
+ item.setOnMenuItemClickListener(
+ new InflatedOnMenuItemClickListener(mContext, itemListenerMethodName));
+ }
- if (itemCheckable >= 2) {
+ if (itemCheckable >= 2 && item instanceof MenuItemImpl) {
((MenuItemImpl) item).setExclusiveCheckable(true);
}
}
diff --git a/core/java/android/view/Window.java b/core/java/android/view/Window.java
index 7dd5085..e1ff4e8 100644
--- a/core/java/android/view/Window.java
+++ b/core/java/android/view/Window.java
@@ -61,6 +61,13 @@
@hide
*/
public static final int FEATURE_OPENGL = 8;
+ /**
+ * Flag for enabling the Action Bar.
+ * This is enabled by default for some devices. The Action Bar
+ * replaces the title bar and provides an alternate location
+ * for an on-screen menu button on some devices.
+ */
+ public static final int FEATURE_ACTION_BAR = 9;
/** Flag for setting the progress bar's visibility to VISIBLE */
public static final int PROGRESS_VISIBILITY_ON = -1;
/** Flag for setting the progress bar's visibility to GONE */
@@ -981,6 +988,16 @@
{
return mFeatures;
}
+
+ /**
+ * Query for the availability of a certain feature.
+ *
+ * @param feature The feature ID to check
+ * @return true if the feature is enabled, false otherwise.
+ */
+ public boolean hasFeature(int feature) {
+ return (getFeatures() & (1 << feature)) != 0;
+ }
/**
* Return the feature bits that are being implemented by this Window.
diff --git a/core/java/com/android/internal/app/SplitActionBar.java b/core/java/com/android/internal/app/SplitActionBar.java
new file mode 100644
index 0000000..4e19e04
--- /dev/null
+++ b/core/java/com/android/internal/app/SplitActionBar.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.app;
+
+import android.app.ActionBar;
+import android.graphics.drawable.Drawable;
+import android.view.ActionBarView;
+import android.view.View;
+import android.widget.LinearLayout;
+
+/**
+ * SplitActionBar is the ActionBar implementation used
+ * by small-screen devices. It expects to split contextual
+ * modes across both the ActionBarView at the top of the screen
+ * and a horizontal LinearLayout at the bottom which is normally
+ * hidden.
+ */
+public class SplitActionBar extends ActionBar {
+ private ActionBarView mActionView;
+ private LinearLayout mContextView;
+
+ public SplitActionBar(ActionBarView view, LinearLayout contextView) {
+ mActionView = view;
+ mContextView = contextView;
+ }
+
+ public void setCallback(Callback callback) {
+ mActionView.setCallback(callback);
+ }
+
+ public void setCustomNavigationView(View view) {
+ mActionView.setCustomNavigationView(view);
+ }
+
+ public void setTitle(CharSequence title) {
+ mActionView.setTitle(title);
+ }
+
+ public void setSubtitle(CharSequence subtitle) {
+ mActionView.setSubtitle(subtitle);
+ }
+
+ public void setNavigationMode(int mode) {
+ mActionView.setNavigationMode(mode);
+ }
+
+ public void setDisplayOptions(int options) {
+ mActionView.setDisplayOptions(options);
+ }
+
+ public void setBackgroundDrawable(Drawable d) {
+ mActionView.setBackgroundDrawable(d);
+ }
+
+ public void setDividerDrawable(Drawable d) {
+ mActionView.setDividerDrawable(d);
+ }
+
+ public View getCustomNavigationView() {
+ return mActionView.getCustomNavigationView();
+ }
+
+ public CharSequence getTitle() {
+ return mActionView.getTitle();
+ }
+
+ public CharSequence getSubtitle() {
+ return mActionView.getSubtitle();
+ }
+
+ public int getNavigationMode() {
+ return mActionView.getNavigationMode();
+ }
+
+ public int getDisplayOptions() {
+ return mActionView.getDisplayOptions();
+ }
+
+ public void updateActionMenu() {
+ mActionView.updateActionMenu();
+ }
+
+}
diff --git a/core/java/com/android/internal/view/menu/ActionMenu.java b/core/java/com/android/internal/view/menu/ActionMenu.java
new file mode 100644
index 0000000..3d44ebc
--- /dev/null
+++ b/core/java/com/android/internal/view/menu/ActionMenu.java
@@ -0,0 +1,263 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.view.menu;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.view.KeyEvent;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.view.SubMenu;
+
+/**
+ * @hide
+ */
+public class ActionMenu implements Menu {
+ private Context mContext;
+
+ private boolean mIsQwerty;
+
+ private ArrayList<ActionMenuItem> mItems;
+
+ public ActionMenu(Context context) {
+ mContext = context;
+ mItems = new ArrayList<ActionMenuItem>();
+ }
+
+ public Context getContext() {
+ return mContext;
+ }
+
+ public MenuItem add(CharSequence title) {
+ return add(0, 0, 0, title);
+ }
+
+ public MenuItem add(int titleRes) {
+ return add(0, 0, 0, titleRes);
+ }
+
+ public MenuItem add(int groupId, int itemId, int order, int titleRes) {
+ return add(groupId, itemId, order, mContext.getResources().getString(titleRes));
+ }
+
+ public MenuItem add(int groupId, int itemId, int order, CharSequence title) {
+ ActionMenuItem item = new ActionMenuItem(getContext(),
+ groupId, itemId, 0, order, title);
+ mItems.add(order, item);
+ return item;
+ }
+
+ public int addIntentOptions(int groupId, int itemId, int order,
+ ComponentName caller, Intent[] specifics, Intent intent, int flags,
+ MenuItem[] outSpecificItems) {
+ PackageManager pm = mContext.getPackageManager();
+ final List<ResolveInfo> lri =
+ pm.queryIntentActivityOptions(caller, specifics, intent, 0);
+ final int N = lri != null ? lri.size() : 0;
+
+ if ((flags & FLAG_APPEND_TO_GROUP) == 0) {
+ removeGroup(groupId);
+ }
+
+ for (int i=0; i<N; i++) {
+ final ResolveInfo ri = lri.get(i);
+ Intent rintent = new Intent(
+ ri.specificIndex < 0 ? intent : specifics[ri.specificIndex]);
+ rintent.setComponent(new ComponentName(
+ ri.activityInfo.applicationInfo.packageName,
+ ri.activityInfo.name));
+ final MenuItem item = add(groupId, itemId, order, ri.loadLabel(pm))
+ .setIcon(ri.loadIcon(pm))
+ .setIntent(rintent);
+ if (outSpecificItems != null && ri.specificIndex >= 0) {
+ outSpecificItems[ri.specificIndex] = item;
+ }
+ }
+
+ return N;
+ }
+
+ public SubMenu addSubMenu(CharSequence title) {
+ // TODO Implement submenus
+ return null;
+ }
+
+ public SubMenu addSubMenu(int titleRes) {
+ // TODO Implement submenus
+ return null;
+ }
+
+ public SubMenu addSubMenu(int groupId, int itemId, int order,
+ CharSequence title) {
+ // TODO Implement submenus
+ return null;
+ }
+
+ public SubMenu addSubMenu(int groupId, int itemId, int order, int titleRes) {
+ // TODO Implement submenus
+ return null;
+ }
+
+ public void clear() {
+ mItems.clear();
+ }
+
+ public void close() {
+ }
+
+ private int findItemIndex(int id) {
+ final ArrayList<ActionMenuItem> items = mItems;
+ final int itemCount = items.size();
+ for (int i = 0; i < itemCount; i++) {
+ if (items.get(i).getItemId() == id) {
+ return i;
+ }
+ }
+
+ return -1;
+ }
+
+ public MenuItem findItem(int id) {
+ return mItems.get(findItemIndex(id));
+ }
+
+ public MenuItem getItem(int index) {
+ return mItems.get(index);
+ }
+
+ public boolean hasVisibleItems() {
+ final ArrayList<ActionMenuItem> items = mItems;
+ final int itemCount = items.size();
+
+ for (int i = 0; i < itemCount; i++) {
+ if (items.get(i).isVisible()) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ private ActionMenuItem findItemWithShortcut(int keyCode, KeyEvent event) {
+ // TODO Make this smarter.
+ final boolean qwerty = mIsQwerty;
+ final ArrayList<ActionMenuItem> items = mItems;
+ final int itemCount = items.size();
+
+ for (int i = 0; i < itemCount; i++) {
+ ActionMenuItem item = items.get(i);
+ final char shortcut = qwerty ? item.getAlphabeticShortcut() :
+ item.getNumericShortcut();
+ if (keyCode == shortcut) {
+ return item;
+ }
+ }
+ return null;
+ }
+
+ public boolean isShortcutKey(int keyCode, KeyEvent event) {
+ return findItemWithShortcut(keyCode, event) != null;
+ }
+
+ public boolean performIdentifierAction(int id, int flags) {
+ final int index = findItemIndex(id);
+ if (index < 0) {
+ return false;
+ }
+
+ return mItems.get(index).invoke();
+ }
+
+ public boolean performShortcut(int keyCode, KeyEvent event, int flags) {
+ ActionMenuItem item = findItemWithShortcut(keyCode, event);
+ if (item == null) {
+ return false;
+ }
+
+ return item.invoke();
+ }
+
+ public void removeGroup(int groupId) {
+ final ArrayList<ActionMenuItem> items = mItems;
+ int itemCount = items.size();
+ int i = 0;
+ while (i < itemCount) {
+ if (items.get(i).getGroupId() == groupId) {
+ items.remove(i);
+ itemCount--;
+ } else {
+ i++;
+ }
+ }
+ }
+
+ public void removeItem(int id) {
+ mItems.remove(findItemIndex(id));
+ }
+
+ public void setGroupCheckable(int group, boolean checkable,
+ boolean exclusive) {
+ final ArrayList<ActionMenuItem> items = mItems;
+ final int itemCount = items.size();
+
+ for (int i = 0; i < itemCount; i++) {
+ ActionMenuItem item = items.get(i);
+ if (item.getGroupId() == group) {
+ item.setCheckable(checkable);
+ item.setExclusiveCheckable(exclusive);
+ }
+ }
+ }
+
+ public void setGroupEnabled(int group, boolean enabled) {
+ final ArrayList<ActionMenuItem> items = mItems;
+ final int itemCount = items.size();
+
+ for (int i = 0; i < itemCount; i++) {
+ ActionMenuItem item = items.get(i);
+ if (item.getGroupId() == group) {
+ item.setEnabled(enabled);
+ }
+ }
+ }
+
+ public void setGroupVisible(int group, boolean visible) {
+ final ArrayList<ActionMenuItem> items = mItems;
+ final int itemCount = items.size();
+
+ for (int i = 0; i < itemCount; i++) {
+ ActionMenuItem item = items.get(i);
+ if (item.getGroupId() == group) {
+ item.setVisible(visible);
+ }
+ }
+ }
+
+ public void setQwertyMode(boolean isQwerty) {
+ mIsQwerty = isQwerty;
+ }
+
+ public int size() {
+ return mItems.size();
+ }
+}
diff --git a/core/java/com/android/internal/view/menu/ActionMenuItem.java b/core/java/com/android/internal/view/menu/ActionMenuItem.java
new file mode 100644
index 0000000..47d5fb9
--- /dev/null
+++ b/core/java/com/android/internal/view/menu/ActionMenuItem.java
@@ -0,0 +1,221 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.view.menu;
+
+import android.content.Context;
+import android.content.Intent;
+import android.graphics.drawable.Drawable;
+import android.view.MenuItem;
+import android.view.SubMenu;
+import android.view.ContextMenu.ContextMenuInfo;
+
+/**
+ * @hide
+ */
+public class ActionMenuItem implements MenuItem {
+ private final int mId;
+ private final int mGroup;
+ private final int mCategoryOrder;
+ private final int mOrdering;
+
+ private CharSequence mTitle;
+ private CharSequence mTitleCondensed;
+ private Intent mIntent;
+ private char mShortcutNumericChar;
+ private char mShortcutAlphabeticChar;
+
+ private Drawable mIconDrawable;
+ private int mIconResId = NO_ICON;
+
+ private Context mContext;
+
+ private MenuItem.OnMenuItemClickListener mClickListener;
+
+ private static final int NO_ICON = 0;
+
+ private int mFlags = ENABLED;
+ private static final int CHECKABLE = 0x00000001;
+ private static final int CHECKED = 0x00000002;
+ private static final int EXCLUSIVE = 0x00000004;
+ private static final int HIDDEN = 0x00000008;
+ private static final int ENABLED = 0x00000010;
+
+ public ActionMenuItem(Context context, int group, int id, int categoryOrder, int ordering,
+ CharSequence title) {
+ mContext = context;
+ mId = id;
+ mGroup = group;
+ mCategoryOrder = categoryOrder;
+ mOrdering = ordering;
+ mTitle = title;
+ }
+
+ public char getAlphabeticShortcut() {
+ return mShortcutAlphabeticChar;
+ }
+
+ public int getGroupId() {
+ return mGroup;
+ }
+
+ public Drawable getIcon() {
+ return mIconDrawable;
+ }
+
+ public Intent getIntent() {
+ return mIntent;
+ }
+
+ public int getItemId() {
+ return mId;
+ }
+
+ public ContextMenuInfo getMenuInfo() {
+ return null;
+ }
+
+ public char getNumericShortcut() {
+ return mShortcutNumericChar;
+ }
+
+ public int getOrder() {
+ return mOrdering;
+ }
+
+ public SubMenu getSubMenu() {
+ return null;
+ }
+
+ public CharSequence getTitle() {
+ return mTitle;
+ }
+
+ public CharSequence getTitleCondensed() {
+ return mTitleCondensed;
+ }
+
+ public boolean hasSubMenu() {
+ return false;
+ }
+
+ public boolean isCheckable() {
+ return (mFlags & CHECKABLE) != 0;
+ }
+
+ public boolean isChecked() {
+ return (mFlags & CHECKED) != 0;
+ }
+
+ public boolean isEnabled() {
+ return (mFlags & ENABLED) != 0;
+ }
+
+ public boolean isVisible() {
+ return (mFlags & HIDDEN) == 0;
+ }
+
+ public MenuItem setAlphabeticShortcut(char alphaChar) {
+ mShortcutAlphabeticChar = alphaChar;
+ return this;
+ }
+
+ public MenuItem setCheckable(boolean checkable) {
+ mFlags = (mFlags & ~CHECKABLE) | (checkable ? CHECKABLE : 0);
+ return this;
+ }
+
+ public ActionMenuItem setExclusiveCheckable(boolean exclusive) {
+ mFlags = (mFlags & ~EXCLUSIVE) | (exclusive ? EXCLUSIVE : 0);
+ return this;
+ }
+
+ public MenuItem setChecked(boolean checked) {
+ mFlags = (mFlags & ~CHECKED) | (checked ? CHECKED : 0);
+ return this;
+ }
+
+ public MenuItem setEnabled(boolean enabled) {
+ mFlags = (mFlags & ~ENABLED) | (enabled ? ENABLED : 0);
+ return this;
+ }
+
+ public MenuItem setIcon(Drawable icon) {
+ mIconDrawable = icon;
+ mIconResId = NO_ICON;
+ return this;
+ }
+
+ public MenuItem setIcon(int iconRes) {
+ mIconResId = iconRes;
+ mIconDrawable = mContext.getResources().getDrawable(iconRes);
+ return this;
+ }
+
+ public MenuItem setIntent(Intent intent) {
+ mIntent = intent;
+ return this;
+ }
+
+ public MenuItem setNumericShortcut(char numericChar) {
+ mShortcutNumericChar = numericChar;
+ return this;
+ }
+
+ public MenuItem setOnMenuItemClickListener(OnMenuItemClickListener menuItemClickListener) {
+ mClickListener = menuItemClickListener;
+ return this;
+ }
+
+ public MenuItem setShortcut(char numericChar, char alphaChar) {
+ mShortcutNumericChar = numericChar;
+ mShortcutAlphabeticChar = alphaChar;
+ return this;
+ }
+
+ public MenuItem setTitle(CharSequence title) {
+ mTitle = title;
+ return this;
+ }
+
+ public MenuItem setTitle(int title) {
+ mTitle = mContext.getResources().getString(title);
+ return this;
+ }
+
+ public MenuItem setTitleCondensed(CharSequence title) {
+ mTitleCondensed = title;
+ return this;
+ }
+
+ public MenuItem setVisible(boolean visible) {
+ mFlags = (mFlags & HIDDEN) | (visible ? 0 : HIDDEN);
+ return this;
+ }
+
+ public boolean invoke() {
+ if (mClickListener != null && mClickListener.onMenuItemClick(this)) {
+ return true;
+ }
+
+ if (mIntent != null) {
+ mContext.startActivity(mIntent);
+ return true;
+ }
+
+ return false;
+ }
+}
diff --git a/core/res/res/drawable/action_bar_background.xml b/core/res/res/drawable/action_bar_background.xml
new file mode 100644
index 0000000..3929d7f
--- /dev/null
+++ b/core/res/res/drawable/action_bar_background.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<shape xmlns:android="http://schemas.android.com/apk/res/android">
+ <gradient
+ android:startColor="#ffd1d2d4"
+ android:endColor="#ff85878a"
+ android:angle="270" />
+</shape>
diff --git a/core/res/res/drawable/action_bar_divider.xml b/core/res/res/drawable/action_bar_divider.xml
new file mode 100644
index 0000000..414309f
--- /dev/null
+++ b/core/res/res/drawable/action_bar_divider.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<shape xmlns:android="http://schemas.android.com/apk/res/android">
+ <gradient
+ android:startColor="#ffe1e2e4"
+ android:endColor="#ff95979a"
+ android:angle="270" />
+</shape>
diff --git a/core/res/res/layout/action_bar_title_item.xml b/core/res/res/layout/action_bar_title_item.xml
new file mode 100644
index 0000000..d7f7c13
--- /dev/null
+++ b/core/res/res/layout/action_bar_title_item.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<TextView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+
+ android:singleLine="true"
+ android:ellipsize="end"
+ android:textAppearance="?android:attr/textAppearanceMediumInverse" />
diff --git a/core/res/res/layout/screen_action_bar.xml b/core/res/res/layout/screen_action_bar.xml
new file mode 100644
index 0000000..01a4b42
--- /dev/null
+++ b/core/res/res/layout/screen_action_bar.xml
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<!--
+This is an optimized layout for a screen with the Action Bar enabled.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical"
+ android:fitsSystemWindows="true">
+ <ActionBarView android:id="@+id/action_bar"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ style="?android:attr/windowActionBarStyle" />
+ <FrameLayout android:id="@android:id/content"
+ android:layout_width="match_parent"
+ android:layout_height="0dip"
+ android:layout_weight="1"
+ android:foregroundGravity="fill_horizontal|top"
+ android:foreground="?android:attr/windowContentOverlay" />
+ <LinearLayout android:id="@+id/context_action_bar"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ style="?android:attr/windowActionBarStyle"
+ android:visibility="gone" />
+</LinearLayout>
+
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 1449b02..e7b83c7 100755
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -244,6 +244,13 @@
{@link android.R.styleable#WindowAnimation}. -->
<attr name="windowAnimationStyle" format="reference" />
+ <!-- Flag indicating whether this window should have an Action Bar
+ in place of the usual title bar. -->
+ <attr name="windowActionBar" format="boolean" />
+
+ <!-- Reference to a style for the Action Bar -->
+ <attr name="windowActionBarStyle" format="reference" />
+
<!-- Defines the default soft input state that this window would
like when it is displayed. -->
<attr name="windowSoftInputMode">
@@ -948,6 +955,8 @@
<attr name="textColor" />
<attr name="backgroundDimEnabled" />
<attr name="backgroundDimAmount" />
+ <attr name="windowActionBar" />
+ <attr name="windowActionBarStyle" />
</declare-styleable>
<!-- The set of attributes that describe a AlertDialog's theme. -->
@@ -3243,6 +3252,10 @@
<!-- Whether the item is enabled. -->
<attr name="enabled" />
+ <!-- Name of a method on the Context used to inflate the menu that will be
+ called when the item is clicked. -->
+ <attr name="onClick" />
+
</declare-styleable>
<!-- **************************************************************** -->
@@ -3762,4 +3775,42 @@
<attr name="withClass" format="string" />
</declare-styleable>
+ <!-- Attributes used to style the Action Bar. -->
+ <declare-styleable name="ActionBar">
+ <!-- The type of navigation to use. -->
+ <attr name="navigationMode">
+ <!-- Normal static title text -->
+ <enum name="normal" value="0" />
+ <!-- The action bar will use a drop-down selection in place of title text. -->
+ <enum name="dropdownList" value="1" />
+ <!-- The action bar will use a series of horizontal tabs in place of title text. -->
+ <enum name="tabBar" value="2" />
+ </attr>
+ <!-- Options affecting how the action bar is displayed. -->
+ <attr name="displayOptions">
+ <flag name="useLogo" value="1" />
+ <flag name="hideHome" value="2" />
+ </attr>
+ <!-- Specifies the color used to style the action bar. -->
+ <attr name="colorFilter" format="color" />
+ <!-- Specifies title text used for navigationMode="normal" -->
+ <attr name="title" />
+ <!-- Specifies subtitle text used for navigationMode="normal" -->
+ <attr name="subtitle" format="string" />
+ <!-- Specifies a style to use for title text. -->
+ <attr name="titleTextStyle" format="reference" />
+ <!-- Specifies a style to use for subtitle text. -->
+ <attr name="subtitleTextStyle" format="reference" />
+ <!-- Specifies the drawable used for the application icon. -->
+ <attr name="icon" />
+ <!-- Specifies the drawable used for the application logo. -->
+ <attr name="logo" />
+ <!-- Specifies the drawable used for item dividers. -->
+ <attr name="divider" />
+ <!-- Specifies a background drawable for the action bar. -->
+ <attr name="background" />
+ <!-- Specifies a layout for custom navigation. Overrides navigationMode. -->
+ <attr name="customNavigationLayout" format="reference" />
+ </declare-styleable>
+
</resources>
diff --git a/core/res/res/values/ids.xml b/core/res/res/values/ids.xml
index 8b6af71..e607fad5 100644
--- a/core/res/res/values/ids.xml
+++ b/core/res/res/values/ids.xml
@@ -68,4 +68,5 @@
<item type="id" name="accountPreferences" />
<item type="id" name="smallIcon" />
<item type="id" name="custom" />
+ <item type="id" name="home" />
</resources>
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index fe27174..9055970 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -1275,5 +1275,15 @@
<public type="attr" name="withExpression" />
<public type="attr" name="withClass" />
<public type="attr" name="allContactsName" />
+ <public type="attr" name="windowActionBar" />
+ <public type="attr" name="windowActionBarStyle" />
+ <public type="attr" name="navigationMode" />
+ <public type="attr" name="displayOptions" />
+ <public type="attr" name="subtitle" />
+ <public type="attr" name="customNavigationLayout" />
+
+ <public type="id" name="home" />
+
+ <public type="style" name="Theme.WithActionBar" />
</resources>
diff --git a/core/res/res/values/styles.xml b/core/res/res/values/styles.xml
index f9b0667..73c3444 100644
--- a/core/res/res/values/styles.xml
+++ b/core/res/res/values/styles.xml
@@ -840,4 +840,10 @@
<item name="android:paddingBottom">1dip</item>
<item name="android:background">@android:drawable/bottom_bar</item>
</style>
+
+ <style name="ActionBar">
+ <item name="android:background">@android:drawable/action_bar_background</item>
+ <item name="android:displayOptions">useLogo</item>
+ <item name="android:divider">@android:drawable/action_bar_divider</item>
+ </style>
</resources>
diff --git a/core/res/res/values/themes.xml b/core/res/res/values/themes.xml
index d585d9e..32c5f47 100644
--- a/core/res/res/values/themes.xml
+++ b/core/res/res/values/themes.xml
@@ -115,6 +115,8 @@
<item name="windowTitleBackgroundStyle">@android:style/WindowTitleBackground</item>
<item name="android:windowAnimationStyle">@android:style/Animation.Activity</item>
<item name="android:windowSoftInputMode">stateUnspecified|adjustUnspecified</item>
+ <item name="windowActionBar">false</item>
+ <item name="windowActionBarStyle">@android:style/ActionBar</item>
<!-- Dialog attributes -->
<item name="alertDialogStyle">@android:style/AlertDialog</item>
@@ -521,5 +523,9 @@
<item name="android:windowAnimationStyle">@android:style/Animation.RecentApplications</item>
<item name="android:textColor">@android:color/secondary_text_nofocus</item>
</style>
+
+ <style name="Theme.WithActionBar">
+ <item name="android:windowActionBar">true</item>
+ </style>
</resources>