Merge "Add a test to demonstrate parsing of trailing URI parameters."
diff --git a/api/current.xml b/api/current.xml
index e7d5ff4..9fc6e3e 100644
--- a/api/current.xml
+++ b/api/current.xml
@@ -20749,6 +20749,19 @@
visibility="public"
>
</constructor>
+<method name="addOnMenuVisibilityListener"
+ return="void"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="listener" type="android.app.ActionBar.OnMenuVisibilityListener">
+</parameter>
+</method>
<method name="addTab"
return="void"
abstract="true"
@@ -20998,6 +21011,19 @@
visibility="public"
>
</method>
+<method name="removeOnMenuVisibilityListener"
+ return="void"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="listener" type="android.app.ActionBar.OnMenuVisibilityListener">
+</parameter>
+</method>
<method name="removeTab"
return="void"
abstract="true"
@@ -21131,7 +21157,7 @@
>
<parameter name="adapter" type="android.widget.SpinnerAdapter">
</parameter>
-<parameter name="callback" type="android.app.ActionBar.NavigationCallback">
+<parameter name="callback" type="android.app.ActionBar.OnNavigationListener">
</parameter>
</method>
<method name="setDropdownNavigationMode"
@@ -21146,7 +21172,7 @@
>
<parameter name="adapter" type="android.widget.SpinnerAdapter">
</parameter>
-<parameter name="callback" type="android.app.ActionBar.NavigationCallback">
+<parameter name="callback" type="android.app.ActionBar.OnNavigationListener">
</parameter>
<parameter name="defaultSelectedPosition" type="int">
</parameter>
@@ -21163,7 +21189,7 @@
>
<parameter name="adapter" type="android.widget.SpinnerAdapter">
</parameter>
-<parameter name="callback" type="android.app.ActionBar.NavigationCallback">
+<parameter name="callback" type="android.app.ActionBar.OnNavigationListener">
</parameter>
</method>
<method name="setNavigationMode"
@@ -21475,7 +21501,28 @@
>
</field>
</class>
-<interface name="ActionBar.NavigationCallback"
+<interface name="ActionBar.OnMenuVisibilityListener"
+ abstract="true"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<method name="onMenuVisibilityChanged"
+ return="void"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="isVisible" type="boolean">
+</parameter>
+</method>
+</interface>
+<interface name="ActionBar.OnNavigationListener"
abstract="true"
static="true"
final="false"
@@ -249389,7 +249436,7 @@
deprecated="not deprecated"
visibility="public"
>
-<parameter name="t" type="T">
+<parameter name="arg0" type="T">
</parameter>
</method>
</interface>
diff --git a/core/java/android/app/ActionBar.java b/core/java/android/app/ActionBar.java
index 7a6ad0f..2f69520 100644
--- a/core/java/android/app/ActionBar.java
+++ b/core/java/android/app/ActionBar.java
@@ -168,13 +168,13 @@
* @param adapter An adapter that will provide views both to display
* the current navigation selection and populate views
* within the dropdown navigation menu.
- * @param callback A NavigationCallback that will receive events when the user
+ * @param callback A OnNavigationListener that will receive events when the user
* selects a navigation item.
* @deprecated See setListNavigationCallbacks.
*/
@Deprecated
public abstract void setDropdownNavigationMode(SpinnerAdapter adapter,
- NavigationCallback callback);
+ OnNavigationListener callback);
/**
* Set the adapter and navigation callback for list navigation mode.
@@ -182,17 +182,17 @@
* The supplied adapter will provide views for the expanded list as well as
* the currently selected item. (These may be displayed differently.)
*
- * The supplied NavigationCallback will alert the application when the user
+ * The supplied OnNavigationListener will alert the application when the user
* changes the current list selection.
*
* @param adapter An adapter that will provide views both to display
* the current navigation selection and populate views
* within the dropdown navigation menu.
- * @param callback A NavigationCallback that will receive events when the user
+ * @param callback An OnNavigationListener that will receive events when the user
* selects a navigation item.
*/
public abstract void setListNavigationCallbacks(SpinnerAdapter adapter,
- NavigationCallback callback);
+ OnNavigationListener callback);
/**
* Set the action bar into dropdown navigation mode and supply an adapter that will
@@ -201,7 +201,7 @@
* @param adapter An adapter that will provide views both to display the current
* navigation selection and populate views within the dropdown
* navigation menu.
- * @param callback A NavigationCallback that will receive events when the user
+ * @param callback A OnNavigationListener that will receive events when the user
* selects a navigation item.
* @param defaultSelectedPosition Position within the provided adapter that should be
* selected from the outset.
@@ -209,7 +209,7 @@
*/
@Deprecated
public abstract void setDropdownNavigationMode(SpinnerAdapter adapter,
- NavigationCallback callback, int defaultSelectedPosition);
+ OnNavigationListener callback, int defaultSelectedPosition);
/**
* Set the selected navigation item in list or tabbed navigation modes.
@@ -532,9 +532,24 @@
public abstract boolean isShowing();
/**
- * Callback interface for ActionBar navigation events.
+ * Add a listener that will respond to menu visibility change events.
+ *
+ * @param listener The new listener to add
*/
- public interface NavigationCallback {
+ public abstract void addOnMenuVisibilityListener(OnMenuVisibilityListener listener);
+
+ /**
+ * Remove a menu visibility listener. This listener will no longer receive menu
+ * visibility change events.
+ *
+ * @param listener A listener to remove that was previously added
+ */
+ public abstract void removeOnMenuVisibilityListener(OnMenuVisibilityListener listener);
+
+ /**
+ * Listener interface for ActionBar navigation events.
+ */
+ public interface OnNavigationListener {
/**
* This method is called whenever a navigation item in your action bar
* is selected.
@@ -547,6 +562,21 @@
}
/**
+ * Listener for receiving events when action bar menus are shown or hidden.
+ */
+ public interface OnMenuVisibilityListener {
+ /**
+ * Called when an action bar menu is shown or hidden. Applications may want to use
+ * this to tune auto-hiding behavior for the action bar or pause/resume video playback,
+ * gameplay, or other activity within the main content area.
+ *
+ * @param isVisible True if an action bar menu is now visible, false if no action bar
+ * menus are visible.
+ */
+ public void onMenuVisibilityChanged(boolean isVisible);
+ }
+
+ /**
* A tab in the action bar.
*
* <p>Tabs manage the hiding and showing of {@link Fragment}s.
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index d69a179..6b619fb 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -2362,6 +2362,9 @@
* @return The default implementation returns true.
*/
public boolean onMenuOpened(int featureId, Menu menu) {
+ if (featureId == Window.FEATURE_ACTION_BAR) {
+ mActionBar.dispatchMenuVisibilityChanged(true);
+ }
return true;
}
@@ -2392,7 +2395,7 @@
return true;
}
return mFragments.dispatchContextItemSelected(item);
-
+
default:
return false;
}
@@ -2417,6 +2420,10 @@
case Window.FEATURE_CONTEXT_MENU:
onContextMenuClosed(menu);
break;
+
+ case Window.FEATURE_ACTION_BAR:
+ mActionBar.dispatchMenuVisibilityChanged(false);
+ break;
}
}
diff --git a/core/java/android/app/Dialog.java b/core/java/android/app/Dialog.java
index 64a4d7a..f90fc59 100644
--- a/core/java/android/app/Dialog.java
+++ b/core/java/android/app/Dialog.java
@@ -735,6 +735,9 @@
* @see Activity#onMenuOpened(int, Menu)
*/
public boolean onMenuOpened(int featureId, Menu menu) {
+ if (featureId == Window.FEATURE_ACTION_BAR) {
+ mActionBar.dispatchMenuVisibilityChanged(true);
+ }
return true;
}
@@ -749,6 +752,9 @@
* @see Activity#onPanelClosed(int, Menu)
*/
public void onPanelClosed(int featureId, Menu menu) {
+ if (featureId == Window.FEATURE_ACTION_BAR) {
+ mActionBar.dispatchMenuVisibilityChanged(false);
+ }
}
/**
diff --git a/core/java/android/webkit/FindActionModeCallback.java b/core/java/android/webkit/FindActionModeCallback.java
index 27043e0..641604e 100644
--- a/core/java/android/webkit/FindActionModeCallback.java
+++ b/core/java/android/webkit/FindActionModeCallback.java
@@ -43,7 +43,6 @@
private Resources mResources;
private boolean mMatchesFound;
private int mNumberOfMatches;
- private View mTitleBar;
private ActionMode mActionMode;
FindActionModeCallback(Context context) {
@@ -62,8 +61,6 @@
mResources = context.getResources();
}
- void setTitleBar(View v) { mTitleBar = v; }
-
void finish() {
mActionMode.finish();
}
@@ -174,7 +171,6 @@
@Override
public void onDestroyActionMode(ActionMode mode) {
- if (mTitleBar != null) mWebView.setEmbeddedTitleBar(mTitleBar);
mWebView.notifyFindDialogDismissed();
mInput.hideSoftInputFromWindow(mWebView.getWindowToken(), 0);
}
diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java
index b8ccf45..27a4b6d 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -2253,13 +2253,6 @@
* @hide
*/
public void setEmbeddedTitleBar(View v) {
- if (null == v) {
- // If one of our callbacks is holding onto the titlebar to replace
- // it when its ActionMode ends, remove it.
- if (mFindCallback != null) {
- mFindCallback.setTitleBar(null);
- }
- }
if (mTitleBar == v) return;
if (mTitleBar != null) {
removeView(mTitleBar);
@@ -2894,11 +2887,6 @@
setFindIsUp(true);
mFindCallback.setWebView(this);
View titleBar = mTitleBar;
- // We do not want to show the embedded title bar during find or
- // select, but keep track of it so that it can be replaced when the
- // mode is exited.
- setEmbeddedTitleBar(null);
- mFindCallback.setTitleBar(titleBar);
startActionMode(mFindCallback);
if (text == null) {
text = mLastFind;
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index cfdcd32..1d3e4f4 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -8923,66 +8923,14 @@
TextView.this.requestFocus();
return true;
- case DragEvent.ACTION_DRAG_LOCATION: {
+ case DragEvent.ACTION_DRAG_LOCATION:
final int offset = getOffset((int) event.getX(), (int) event.getY());
Selection.setSelection((Spannable)mText, offset);
return true;
- }
- case DragEvent.ACTION_DROP: {
- StringBuilder content = new StringBuilder("");
- ClipData clipData = event.getClipData();
- final int itemCount = clipData.getItemCount();
- for (int i=0; i < itemCount; i++) {
- Item item = clipData.getItem(i);
- content.append(item.coerceToText(TextView.this.mContext));
- }
-
- final int offset = getOffset((int) event.getX(), (int) event.getY());
-
- if (mDragSourcePositions != -1) {
- final int dragSourceStart = extractRangeStartFromLong(mDragSourcePositions);
- final int dragSourceEnd = extractRangeEndFromLong(mDragSourcePositions);
- if (offset >= dragSourceStart && offset < dragSourceEnd) {
- // A drop inside the original selection discards the drop.
- return true;
- }
- }
-
- final int originalLength = mText.length();
- long minMax = prepareSpacesAroundPaste(offset, offset, content);
- int min = extractRangeStartFromLong(minMax);
- int max = extractRangeEndFromLong(minMax);
-
- Selection.setSelection((Spannable) mText, max);
- ((Editable) mText).replace(min, max, content);
-
- if (mDragSourcePositions != -1) {
- int dragSourceStart = extractRangeStartFromLong(mDragSourcePositions);
- int dragSourceEnd = extractRangeEndFromLong(mDragSourcePositions);
- if (max <= dragSourceStart) {
- // Inserting text before selection has shifted positions
- final int shift = mText.length() - originalLength;
- dragSourceStart += shift;
- dragSourceEnd += shift;
- }
-
- // Delete original selection
- ((Editable) mText).delete(dragSourceStart, dragSourceEnd);
-
- // Make sure we do not leave two adjacent spaces.
- if ((dragSourceStart == 0 ||
- Character.isSpaceChar(mTransformed.charAt(dragSourceStart - 1))) &&
- (dragSourceStart == mText.length() ||
- Character.isSpaceChar(mTransformed.charAt(dragSourceStart)))) {
- final int pos = dragSourceStart == mText.length() ?
- dragSourceStart - 1 : dragSourceStart;
- ((Editable) mText).delete(pos, pos + 1);
- }
- }
-
+ case DragEvent.ACTION_DROP:
+ onDrop(event);
return true;
- }
case DragEvent.ACTION_DRAG_ENDED:
mDragSourcePositions = -1;
@@ -8994,6 +8942,59 @@
}
}
+ private void onDrop(DragEvent event) {
+ StringBuilder content = new StringBuilder("");
+ ClipData clipData = event.getClipData();
+ final int itemCount = clipData.getItemCount();
+ for (int i=0; i < itemCount; i++) {
+ Item item = clipData.getItem(i);
+ content.append(item.coerceToText(TextView.this.mContext));
+ }
+
+ final int offset = getOffset((int) event.getX(), (int) event.getY());
+
+ if (mDragSourcePositions != -1) {
+ final int dragSourceStart = extractRangeStartFromLong(mDragSourcePositions);
+ final int dragSourceEnd = extractRangeEndFromLong(mDragSourcePositions);
+ if (offset >= dragSourceStart && offset < dragSourceEnd) {
+ // A drop inside the original selection discards the drop.
+ return;
+ }
+ }
+
+ final int originalLength = mText.length();
+ long minMax = prepareSpacesAroundPaste(offset, offset, content);
+ int min = extractRangeStartFromLong(minMax);
+ int max = extractRangeEndFromLong(minMax);
+
+ Selection.setSelection((Spannable) mText, max);
+ ((Editable) mText).replace(min, max, content);
+
+ if (mDragSourcePositions != -1) {
+ int dragSourceStart = extractRangeStartFromLong(mDragSourcePositions);
+ int dragSourceEnd = extractRangeEndFromLong(mDragSourcePositions);
+ if (max <= dragSourceStart) {
+ // Inserting text before selection has shifted positions
+ final int shift = mText.length() - originalLength;
+ dragSourceStart += shift;
+ dragSourceEnd += shift;
+ }
+
+ // Delete original selection
+ ((Editable) mText).delete(dragSourceStart, dragSourceEnd);
+
+ // Make sure we do not leave two adjacent spaces.
+ if ((dragSourceStart == 0 ||
+ Character.isSpaceChar(mTransformed.charAt(dragSourceStart - 1))) &&
+ (dragSourceStart == mText.length() ||
+ Character.isSpaceChar(mTransformed.charAt(dragSourceStart)))) {
+ final int pos = dragSourceStart == mText.length() ?
+ dragSourceStart - 1 : dragSourceStart;
+ ((Editable) mText).delete(pos, pos + 1);
+ }
+ }
+ }
+
/**
* @return True if this view supports insertion handles.
*/
diff --git a/core/java/com/android/internal/app/ActionBarImpl.java b/core/java/com/android/internal/app/ActionBarImpl.java
index 20402a3..447a062 100644
--- a/core/java/com/android/internal/app/ActionBarImpl.java
+++ b/core/java/com/android/internal/app/ActionBarImpl.java
@@ -70,6 +70,10 @@
private ActionMode mActionMode;
+ private boolean mLastMenuVisibility;
+ private ArrayList<OnMenuVisibilityListener> mMenuVisibilityListeners =
+ new ArrayList<OnMenuVisibilityListener>();
+
private static final int CONTEXT_DISPLAY_NORMAL = 0;
private static final int CONTEXT_DISPLAY_SPLIT = 1;
@@ -120,6 +124,26 @@
CONTEXT_DISPLAY_NORMAL : CONTEXT_DISPLAY_SPLIT;
}
+ public void addOnMenuVisibilityListener(OnMenuVisibilityListener listener) {
+ mMenuVisibilityListeners.add(listener);
+ }
+
+ public void removeOnMenuVisibilityListener(OnMenuVisibilityListener listener) {
+ mMenuVisibilityListeners.remove(listener);
+ }
+
+ public void dispatchMenuVisibilityChanged(boolean isVisible) {
+ if (isVisible == mLastMenuVisibility) {
+ return;
+ }
+ mLastMenuVisibility = isVisible;
+
+ final int count = mMenuVisibilityListeners.size();
+ for (int i = 0; i < count; i++) {
+ mMenuVisibilityListeners.get(i).onMenuVisibilityChanged(isVisible);
+ }
+ }
+
@Override
public void setTitle(int resId) {
setTitle(mContext.getString(resId));
@@ -138,11 +162,11 @@
mActionView.setCallback(null);
}
- public void setDropdownNavigationMode(SpinnerAdapter adapter, NavigationCallback callback) {
+ public void setDropdownNavigationMode(SpinnerAdapter adapter, OnNavigationListener callback) {
setDropdownNavigationMode(adapter, callback, -1);
}
- public void setDropdownNavigationMode(SpinnerAdapter adapter, NavigationCallback callback,
+ public void setDropdownNavigationMode(SpinnerAdapter adapter, OnNavigationListener callback,
int defaultSelectedPosition) {
cleanupTabs();
setDisplayOptions(0, DISPLAY_SHOW_CUSTOM | DISPLAY_SHOW_TITLE);
@@ -516,7 +540,7 @@
public void onMenuModeChange(MenuBuilder menu) {
invalidate();
- mUpperContextView.showOverflowMenu();
+ mUpperContextView.openOverflowMenu();
}
}
@@ -627,7 +651,7 @@
}
@Override
- public void setListNavigationCallbacks(SpinnerAdapter adapter, NavigationCallback callback) {
+ public void setListNavigationCallbacks(SpinnerAdapter adapter, OnNavigationListener callback) {
mActionView.setDropdownAdapter(adapter);
mActionView.setCallback(callback);
}
diff --git a/core/java/com/android/internal/view/StandaloneActionMode.java b/core/java/com/android/internal/view/StandaloneActionMode.java
index b54daba..2d067da 100644
--- a/core/java/com/android/internal/view/StandaloneActionMode.java
+++ b/core/java/com/android/internal/view/StandaloneActionMode.java
@@ -135,6 +135,6 @@
public void onMenuModeChange(MenuBuilder menu) {
invalidate();
- mContextView.showOverflowMenu();
+ mContextView.openOverflowMenu();
}
}
diff --git a/core/java/com/android/internal/view/menu/ActionMenuView.java b/core/java/com/android/internal/view/menu/ActionMenuView.java
index 621defe..84067d0 100644
--- a/core/java/com/android/internal/view/menu/ActionMenuView.java
+++ b/core/java/com/android/internal/view/menu/ActionMenuView.java
@@ -59,6 +59,22 @@
}
};
+ private class OpenOverflowRunnable implements Runnable {
+ private MenuPopupHelper mPopup;
+
+ public OpenOverflowRunnable(MenuPopupHelper popup) {
+ mPopup = popup;
+ }
+
+ public void run() {
+ mOverflowPopup = mPopup;
+ mPopup.show();
+ mPostedOpenRunnable = null;
+ }
+ }
+
+ private OpenOverflowRunnable mPostedOpenRunnable;
+
public ActionMenuView(Context context) {
this(context, null);
}
@@ -100,6 +116,7 @@
@Override
public void onConfigurationChanged(Configuration newConfig) {
+ super.onConfigurationChanged(newConfig);
final int screen = newConfig.screenLayout;
mReserveOverflow = (screen & Configuration.SCREENLAYOUT_SIZE_MASK) ==
Configuration.SCREENLAYOUT_SIZE_XLARGE;
@@ -115,6 +132,14 @@
}
}
+ @Override
+ public void onDetachedFromWindow() {
+ super.onDetachedFromWindow();
+ if (mOverflowPopup != null && mOverflowPopup.isShowing()) {
+ mOverflowPopup.dismiss();
+ }
+ }
+
private int getMaxActionButtons() {
return getResources().getInteger(com.android.internal.R.integer.max_action_buttons);
}
@@ -193,30 +218,34 @@
}
public boolean showOverflowMenu() {
- if (mOverflowButton != null) {
- final MenuPopupHelper popup =
- new MenuPopupHelper(getContext(), mMenu, mOverflowButton, true);
- // Post this for later; we might still need a layout for the anchor to be right.
- post(new Runnable() {
- public void run() {
- popup.show();
- }
- });
- mOverflowPopup = popup;
+ if (mOverflowButton != null && !isOverflowMenuShowing()) {
+ mMenu.getCallback().onMenuModeChange(mMenu);
return true;
}
return false;
}
+ public void openOverflowMenu() {
+ OverflowPopup popup = new OverflowPopup(getContext(), mMenu, mOverflowButton, true);
+ mPostedOpenRunnable = new OpenOverflowRunnable(popup);
+ // Post this for later; we might still need a layout for the anchor to be right.
+ post(mPostedOpenRunnable);
+ }
+
public boolean isOverflowMenuShowing() {
- MenuPopupHelper popup = mOverflowPopup;
- if (popup != null) {
- return popup.isShowing();
- }
- return false;
+ return mOverflowPopup != null && mOverflowPopup.isShowing();
+ }
+
+ public boolean isOverflowMenuOpen() {
+ return mOverflowPopup != null;
}
public boolean hideOverflowMenu() {
+ if (mPostedOpenRunnable != null) {
+ removeCallbacks(mPostedOpenRunnable);
+ return true;
+ }
+
MenuPopupHelper popup = mOverflowPopup;
if (popup != null) {
popup.dismiss();
@@ -274,9 +303,22 @@
return true;
}
- // Change to overflow mode
- mMenu.getCallback().onMenuModeChange(mMenu);
+ showOverflowMenu();
return true;
}
}
+
+ private class OverflowPopup extends MenuPopupHelper {
+ public OverflowPopup(Context context, MenuBuilder menu, View anchorView,
+ boolean overflowOnly) {
+ super(context, menu, anchorView, overflowOnly);
+ }
+
+ @Override
+ public void onDismiss() {
+ super.onDismiss();
+ mMenu.getCallback().onCloseMenu(mMenu, true);
+ mOverflowPopup = null;
+ }
+ }
}
diff --git a/core/java/com/android/internal/view/menu/MenuPopupHelper.java b/core/java/com/android/internal/view/menu/MenuPopupHelper.java
index 1406e4e..2cb78a5 100644
--- a/core/java/com/android/internal/view/menu/MenuPopupHelper.java
+++ b/core/java/com/android/internal/view/menu/MenuPopupHelper.java
@@ -35,7 +35,7 @@
* @hide
*/
public class MenuPopupHelper implements AdapterView.OnItemClickListener, View.OnKeyListener,
- ViewTreeObserver.OnGlobalLayoutListener {
+ ViewTreeObserver.OnGlobalLayoutListener, PopupWindow.OnDismissListener {
private static final String TAG = "MenuPopupHelper";
private Context mContext;
@@ -46,12 +46,6 @@
private boolean mOverflowOnly;
private ViewTreeObserver mTreeObserver;
- private PopupWindow.OnDismissListener mDismissListener = new PopupWindow.OnDismissListener() {
- public void onDismiss() {
- mPopup = null;
- }
- };
-
public MenuPopupHelper(Context context, MenuBuilder menu) {
this(context, menu, null, false);
}
@@ -77,7 +71,7 @@
public void show() {
mPopup = new ListPopupWindow(mContext, null, com.android.internal.R.attr.popupMenuStyle);
mPopup.setOnItemClickListener(this);
- mPopup.setOnDismissListener(mDismissListener);
+ mPopup.setOnDismissListener(this);
final MenuAdapter adapter = mOverflowOnly ?
mMenu.getOverflowMenuAdapter(MenuBuilder.TYPE_POPUP) :
@@ -110,8 +104,12 @@
if (isShowing()) {
mPopup.dismiss();
}
+ }
+
+ public void onDismiss() {
+ mPopup = null;
if (mTreeObserver != null) {
- mTreeObserver.removeGlobalOnLayoutListener(this);
+ mTreeObserver.removeGlobalOnLayoutListener(MenuPopupHelper.this);
mTreeObserver = null;
}
}
diff --git a/core/java/com/android/internal/widget/ActionBarContextView.java b/core/java/com/android/internal/widget/ActionBarContextView.java
index e93c414..cbf12bf 100644
--- a/core/java/com/android/internal/widget/ActionBarContextView.java
+++ b/core/java/com/android/internal/widget/ActionBarContextView.java
@@ -180,6 +180,12 @@
return false;
}
+ public void openOverflowMenu() {
+ if (mMenuView != null) {
+ mMenuView.openOverflowMenu();
+ }
+ }
+
public boolean hideOverflowMenu() {
if (mMenuView != null) {
return mMenuView.hideOverflowMenu();
diff --git a/core/java/com/android/internal/widget/ActionBarView.java b/core/java/com/android/internal/widget/ActionBarView.java
index f931217..07a65fc 100644
--- a/core/java/com/android/internal/widget/ActionBarView.java
+++ b/core/java/com/android/internal/widget/ActionBarView.java
@@ -22,7 +22,7 @@
import com.android.internal.view.menu.MenuBuilder;
import android.app.ActionBar;
-import android.app.ActionBar.NavigationCallback;
+import android.app.ActionBar.OnNavigationListener;
import android.app.Activity;
import android.content.Context;
import android.content.pm.ApplicationInfo;
@@ -114,7 +114,7 @@
private ActionMenuItem mLogoNavItem;
private SpinnerAdapter mSpinnerAdapter;
- private NavigationCallback mCallback;
+ private OnNavigationListener mCallback;
private final AdapterView.OnItemSelectedListener mNavItemSelectedListener =
new AdapterView.OnItemSelectedListener() {
@@ -243,7 +243,7 @@
return null;
}
- public void setCallback(NavigationCallback callback) {
+ public void setCallback(OnNavigationListener callback) {
mCallback = callback;
}
@@ -269,6 +269,12 @@
return false;
}
+ public void openOverflowMenu() {
+ if (mMenuView != null) {
+ mMenuView.openOverflowMenu();
+ }
+ }
+
public void postShowOverflowMenu() {
post(new Runnable() {
public void run() {
@@ -291,6 +297,13 @@
return false;
}
+ public boolean isOverflowMenuOpen() {
+ if (mMenuView != null) {
+ return mMenuView.isOverflowMenuOpen();
+ }
+ return false;
+ }
+
public boolean isOverflowReserved() {
return mMenuView != null && mMenuView.isOverflowReserved();
}
diff --git a/docs/html/guide/topics/ui/actionbar.jd b/docs/html/guide/topics/ui/actionbar.jd
index c3d3482f..44d75c1 100644
--- a/docs/html/guide/topics/ui/actionbar.jd
+++ b/docs/html/guide/topics/ui/actionbar.jd
@@ -455,7 +455,7 @@
<ol>
<li>Create a {@link android.widget.SpinnerAdapter} that provides the
list of selectable items for the list and the layout to use when drawing each item in the list.</li>
- <li>Implement {@link android.app.ActionBar.NavigationCallback} to define the behavior when the
+ <li>Implement {@link android.app.ActionBar.OnNavigationListener} to define the behavior when the
user selects an item from the list.</li>
<li>Turn on navigation mode for the Action Bar with {@link
android.app.ActionBar#setNavigationMode setNavigationMode()}. For example:
@@ -472,17 +472,17 @@
actionBar.setListNavigationCallbacks(mSpinnerAdapter, mNavigationCallback);
</pre>
<p>This method takes your {@link android.widget.SpinnerAdapter} and {@link
-android.app.ActionBar.NavigationCallback}. More about these next.</p>
+android.app.ActionBar.OnNavigationListener}. More about these next.</p>
</li>
</ol>
<p>That's the basic setup. The {@link android.widget.SpinnerAdapter} and {@link
-android.app.ActionBar.NavigationCallback} is where most of the work is done. There are many ways
+android.app.ActionBar.OnNavigationListener} is where most of the work is done. There are many ways
you can implement these to define the functionality for your drop-down navigation. Implementing
various types of {@link android.widget.SpinnerAdapter} is beyond the scope of this
document—you should refer to the class refrence for more information about implementing it or
extending an existing implementation. However, below is a simple example for a {@link
-android.widget.SpinnerAdapter} and {@link android.app.ActionBar.NavigationCallback} to get you
+android.widget.SpinnerAdapter} and {@link android.app.ActionBar.OnNavigationListener} to get you
started.</p>
@@ -520,24 +520,24 @@
</pre>
-<h3 id="NavigationCallback">Example: simple NavigationCallback</h3>
+<h3 id="OnNavigationListener">Example: simple OnNavigationListener</h3>
-<p>Your implementation of {@link android.app.ActionBar.NavigationCallback} is where you handle
+<p>Your implementation of {@link android.app.ActionBar.OnNavigationListener} is where you handle
fragment changes or other modifications to your activity when the user selects an item from the
drop-down list. There's only one callback method to implement: {@link
-android.app.ActionBar.NavigationCallback#onNavigationItemSelected onNavigationItemSelected()}.</p>
+android.app.ActionBar.OnNavigationListener#onNavigationItemSelected onNavigationItemSelected()}.</p>
<p>The {@link
-android.app.ActionBar.NavigationCallback#onNavigationItemSelected onNavigationItemSelected()}
+android.app.ActionBar.OnNavigationListener#onNavigationItemSelected onNavigationItemSelected()}
method receives the position of the item in the list and an item ID provided by the {@link
android.widget.SpinnerAdapter}.</p>
<p>Here's an example that instantiates an anonymous implementation of {@link
-android.app.ActionBar.NavigationCallback}, which inserts a {@link android.app.Fragment} into the
+android.app.ActionBar.OnNavigationListener}, which inserts a {@link android.app.Fragment} into the
layout container identified by {@code R.id.fragment_container}:</p>
<pre>
-mNavigationCallback = new NavigationCallback() {
+mOnNavigationListener = new OnNavigationListener() {
// Get the same strings provided for the drop-down's ArrayAdapter
String[] strings = getResources().getStringArray(R.array.action_list);
@@ -556,7 +556,7 @@
};
</pre>
-<p>This instance of {@link android.app.ActionBar.NavigationCallback} can be given to {@link
+<p>This instance of {@link android.app.ActionBar.OnNavigationListener} can be given to {@link
android.app.ActionBar#setListNavigationCallbacks setListNavigationCallbacks()}, in step 4 from
above.</p>
diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindow.java b/policy/src/com/android/internal/policy/impl/PhoneWindow.java
index cd8a065..138dff7 100644
--- a/policy/src/com/android/internal/policy/impl/PhoneWindow.java
+++ b/policy/src/com/android/internal/policy/impl/PhoneWindow.java
@@ -160,6 +160,8 @@
private ContextMenuBuilder mContextMenu;
private MenuDialogHelper mContextMenuHelper;
+ private ActionButtonSubmenu mActionButtonPopup;
+ private boolean mClosingActionMenu;
private int mVolumeControlStreamType = AudioManager.USE_DEFAULT_STREAM_TYPE;
@@ -542,6 +544,9 @@
if (doCallback) {
callOnPanelClosed(st.featureId, st, null);
}
+ } else if (st.featureId == FEATURE_OPTIONS_PANEL && doCallback &&
+ mActionBar != null) {
+ checkCloseActionMenu(st.menu);
}
st.isPrepared = false;
st.isHandled = false;
@@ -563,6 +568,27 @@
}
}
+ private void checkCloseActionMenu(Menu menu) {
+ if (mClosingActionMenu) {
+ return;
+ }
+
+ boolean closed = false;
+ mClosingActionMenu = true;
+ if (mActionBar.isOverflowMenuOpen() && mActionBar.hideOverflowMenu()) {
+ closed = true;
+ }
+ if (mActionButtonPopup != null) {
+ mActionButtonPopup.dismiss();
+ closed = true;
+ }
+ Callback cb = getCallback();
+ if (cb != null && closed) {
+ cb.onPanelClosed(FEATURE_ACTION_BAR, menu);
+ }
+ mClosingActionMenu = false;
+ }
+
@Override
public final void togglePanel(int featureId, KeyEvent event) {
PanelFeatureState st = getPanelState(featureId, true);
@@ -842,7 +868,12 @@
// The window manager will give us a valid window token
new MenuDialogHelper(subMenu).show(null);
} else {
- new MenuPopupHelper(getContext(), subMenu).show();
+ mActionButtonPopup = new ActionButtonSubmenu(getContext(), subMenu);
+ mActionButtonPopup.show();
+ Callback cb = getCallback();
+ if (cb != null) {
+ cb.onMenuOpened(FEATURE_ACTION_BAR, subMenu);
+ }
}
return true;
@@ -854,16 +885,21 @@
private void reopenMenu(boolean toggleMenuMode) {
if (mActionBar != null) {
+ final Callback cb = getCallback();
if (!mActionBar.isOverflowMenuShowing() || !toggleMenuMode) {
- final Callback cb = getCallback();
if (cb != null) {
final PanelFeatureState st = getPanelState(FEATURE_OPTIONS_PANEL, true);
if (cb.onPreparePanel(FEATURE_OPTIONS_PANEL, st.createdPanelView, st.menu)) {
- mActionBar.showOverflowMenu();
+ cb.onMenuOpened(FEATURE_ACTION_BAR, st.menu);
+ mActionBar.openOverflowMenu();
}
}
} else {
mActionBar.hideOverflowMenu();
+ if (cb != null) {
+ final PanelFeatureState st = getPanelState(FEATURE_OPTIONS_PANEL, true);
+ cb.onPanelClosed(FEATURE_ACTION_BAR, st.menu);
+ }
}
return;
}
@@ -2042,8 +2078,23 @@
if (cb != null && mFeatureId < 0) {
cb.onDetachedFromWindow();
}
+
+ if (mActionButtonPopup != null) {
+ if (mActionButtonPopup.isShowing()) {
+ mActionButtonPopup.dismiss();
+ }
+ mActionButtonPopup = null;
+ }
}
-
+
+ @Override
+ protected void onConfigurationChanged(Configuration newConfig) {
+ if (mActionButtonPopup != null) {
+ mActionButtonPopup.dismiss();
+ post(mActionButtonPopup);
+ }
+ }
+
@Override
public void onCloseSystemDialogs(String reason) {
if (mFeatureId >= 0) {
@@ -2921,4 +2972,29 @@
void sendCloseSystemWindows(String reason) {
PhoneWindowManager.sendCloseSystemWindows(getContext(), reason);
}
+
+ private class ActionButtonSubmenu extends MenuPopupHelper implements Runnable {
+ private SubMenuBuilder mSubMenu;
+
+ public ActionButtonSubmenu(Context context, SubMenuBuilder subMenu) {
+ super(context, subMenu);
+ mSubMenu = subMenu;
+ }
+
+ @Override
+ public void onDismiss() {
+ super.onDismiss();
+ mSubMenu.getCallback().onCloseSubMenu(mSubMenu);
+ mActionButtonPopup = null;
+ }
+
+ @Override
+ public void run() {
+ show();
+ Callback cb = getCallback();
+ if (cb != null) {
+ cb.onMenuOpened(FEATURE_ACTION_BAR, mSubMenu);
+ }
+ }
+ }
}
diff --git a/services/audioflinger/AudioPolicyManagerBase.cpp b/services/audioflinger/AudioPolicyManagerBase.cpp
index e3b5db1..175f613 100644
--- a/services/audioflinger/AudioPolicyManagerBase.cpp
+++ b/services/audioflinger/AudioPolicyManagerBase.cpp
@@ -81,12 +81,6 @@
LOGV("setDeviceConnectionState() BT SCO device, address %s", device_address);
// keep track of SCO device address
mScoDeviceAddress = String8(device_address, MAX_DEVICE_ADDRESS_LEN);
-#ifdef WITH_A2DP
- if (mA2dpOutput != 0 &&
- mPhoneState != AudioSystem::MODE_NORMAL) {
- mpClientInterface->suspendOutput(mA2dpOutput);
- }
-#endif
}
}
break;
@@ -115,12 +109,6 @@
{
if (AudioSystem::isBluetoothScoDevice(device)) {
mScoDeviceAddress = "";
-#ifdef WITH_A2DP
- if (mA2dpOutput != 0 &&
- mPhoneState != AudioSystem::MODE_NORMAL) {
- mpClientInterface->restoreOutput(mA2dpOutput);
- }
-#endif
}
}
} break;
@@ -138,6 +126,7 @@
if (state == AudioSystem::DEVICE_STATE_UNAVAILABLE && AudioSystem::isA2dpDevice(device)) {
closeA2dpOutputs();
}
+ checkA2dpSuspend();
#endif
updateDeviceForStrategy();
setOutputDevice(mHardwareOutput, newDevice);
@@ -280,14 +269,7 @@
newDevice = getNewDevice(mHardwareOutput, false);
#ifdef WITH_A2DP
checkOutputForAllStrategies();
- // suspend A2DP output if a SCO device is present.
- if (mA2dpOutput != 0 && mScoDeviceAddress != "") {
- if (oldState == AudioSystem::MODE_NORMAL) {
- mpClientInterface->suspendOutput(mA2dpOutput);
- } else if (state == AudioSystem::MODE_NORMAL) {
- mpClientInterface->restoreOutput(mA2dpOutput);
- }
- }
+ checkA2dpSuspend();
#endif
updateDeviceForStrategy();
@@ -397,6 +379,7 @@
uint32_t newDevice = getNewDevice(mHardwareOutput, false);
#ifdef WITH_A2DP
checkOutputForAllStrategies();
+ checkA2dpSuspend();
#endif
updateDeviceForStrategy();
setOutputDevice(mHardwareOutput, newDevice);
@@ -1025,8 +1008,10 @@
#ifdef AUDIO_POLICY_TEST
Thread(false),
#endif //AUDIO_POLICY_TEST
- mPhoneState(AudioSystem::MODE_NORMAL), mRingerMode(0), mMusicStopTime(0), mLimitRingtoneVolume(false),
- mLastVoiceVolume(-1.0f), mTotalEffectsCpuLoad(0), mTotalEffectsMemory(0)
+ mPhoneState(AudioSystem::MODE_NORMAL), mRingerMode(0),
+ mMusicStopTime(0), mLimitRingtoneVolume(false), mLastVoiceVolume(-1.0f),
+ mTotalEffectsCpuLoad(0), mTotalEffectsMemory(0),
+ mA2dpSuspended(false)
{
mpClientInterface = clientInterface;
@@ -1322,17 +1307,6 @@
}
AudioOutputDescriptor *hwOutputDesc = mOutputs.valueFor(mHardwareOutput);
- if (mScoDeviceAddress != "") {
- // It is normal to suspend twice if we are both in call,
- // and have the hardware audio output routed to BT SCO
- if (mPhoneState != AudioSystem::MODE_NORMAL) {
- mpClientInterface->suspendOutput(mA2dpOutput);
- }
- if (AudioSystem::isBluetoothScoDevice((AudioSystem::audio_devices)hwOutputDesc->device())) {
- mpClientInterface->suspendOutput(mA2dpOutput);
- }
- }
-
if (!a2dpUsedForSonification()) {
// mute music on A2DP output if a notification or ringtone is playing
uint32_t refCount = hwOutputDesc->strategyRefCount(STRATEGY_SONIFICATION);
@@ -1340,6 +1314,9 @@
setStrategyMute(STRATEGY_MEDIA, true, mA2dpOutput);
}
}
+
+ mA2dpSuspended = false;
+
return NO_ERROR;
}
@@ -1369,6 +1346,7 @@
}
}
mA2dpDeviceAddress = "";
+ mA2dpSuspended = false;
return NO_ERROR;
}
@@ -1467,6 +1445,48 @@
checkOutputForStrategy(STRATEGY_DTMF);
}
+void AudioPolicyManagerBase::checkA2dpSuspend()
+{
+ // suspend A2DP output if:
+ // (NOT already suspended) &&
+ // ((SCO device is connected &&
+ // (forced usage for communication || for record is SCO))) ||
+ // (phone state is ringing || in call)
+ //
+ // restore A2DP output if:
+ // (Already suspended) &&
+ // ((SCO device is NOT connected ||
+ // (forced usage NOT for communication && NOT for record is SCO))) &&
+ // (phone state is NOT ringing && NOT in call)
+ //
+ if (mA2dpOutput == 0) {
+ return;
+ }
+
+ if (mA2dpSuspended) {
+ if (((mScoDeviceAddress == "") ||
+ ((mForceUse[AudioSystem::FOR_COMMUNICATION] != AudioSystem::FORCE_BT_SCO) &&
+ (mForceUse[AudioSystem::FOR_RECORD] != AudioSystem::FORCE_BT_SCO))) &&
+ ((mPhoneState != AudioSystem::MODE_IN_CALL) &&
+ (mPhoneState != AudioSystem::MODE_RINGTONE))) {
+
+ mpClientInterface->restoreOutput(mA2dpOutput);
+ mA2dpSuspended = false;
+ }
+ } else {
+ if (((mScoDeviceAddress != "") &&
+ ((mForceUse[AudioSystem::FOR_COMMUNICATION] == AudioSystem::FORCE_BT_SCO) ||
+ (mForceUse[AudioSystem::FOR_RECORD] == AudioSystem::FORCE_BT_SCO))) ||
+ ((mPhoneState == AudioSystem::MODE_IN_CALL) ||
+ (mPhoneState == AudioSystem::MODE_RINGTONE))) {
+
+ mpClientInterface->suspendOutput(mA2dpOutput);
+ mA2dpSuspended = true;
+ }
+ }
+}
+
+
#endif
uint32_t AudioPolicyManagerBase::getNewDevice(audio_io_handle_t output, bool fromCache)
@@ -1714,14 +1734,7 @@
// wait for the PCM output buffers to empty before proceeding with the rest of the command
usleep(outputDesc->mLatency*2*1000);
}
-#ifdef WITH_A2DP
- // suspend A2DP output if SCO device is selected
- if (AudioSystem::isBluetoothScoDevice((AudioSystem::audio_devices)device)) {
- if (mA2dpOutput != 0) {
- mpClientInterface->suspendOutput(mA2dpOutput);
- }
- }
-#endif
+
// do the routing
AudioParameter param = AudioParameter();
param.addInt(String8(AudioParameter::keyRouting), (int)device);
@@ -1729,15 +1742,6 @@
// update stream volumes according to new device
applyStreamVolumes(output, device, delayMs);
-#ifdef WITH_A2DP
- // if disconnecting SCO device, restore A2DP output
- if (AudioSystem::isBluetoothScoDevice((AudioSystem::audio_devices)prevDevice)) {
- if (mA2dpOutput != 0) {
- LOGV("restore A2DP output");
- mpClientInterface->restoreOutput(mA2dpOutput);
- }
- }
-#endif
// if changing from a combined headset + speaker route, unmute media streams
if (output == mHardwareOutput && AudioSystem::popCount(prevDevice) == 2) {
setStrategyMute(STRATEGY_MEDIA, false, output, delayMs);