Merge "Fix bugs around restoring submenu state after activity teardown/restore"
This commit is contained in:
@@ -20,7 +20,8 @@ import com.android.internal.view.menu.ActionMenuView.ActionMenuChildView;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.Resources;
|
||||
import android.util.Log;
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
import android.util.SparseBooleanArray;
|
||||
import android.view.MenuItem;
|
||||
import android.view.SoundEffectConstants;
|
||||
@@ -60,6 +61,9 @@ public class ActionMenuPresenter extends BaseMenuPresenter {
|
||||
|
||||
private OpenOverflowRunnable mPostedOpenRunnable;
|
||||
|
||||
final PopupPresenterCallback mPopupPresenterCallback = new PopupPresenterCallback();
|
||||
int mOpenSubMenuId;
|
||||
|
||||
public ActionMenuPresenter() {
|
||||
super(com.android.internal.R.layout.action_menu_layout,
|
||||
com.android.internal.R.layout.action_menu_item_layout);
|
||||
@@ -196,8 +200,12 @@ public class ActionMenuPresenter extends BaseMenuPresenter {
|
||||
topSubMenu = (SubMenuBuilder) topSubMenu.getParentMenu();
|
||||
}
|
||||
View anchor = findViewForItem(topSubMenu.getItem());
|
||||
if (anchor == null) return false;
|
||||
if (anchor == null) {
|
||||
if (mOverflowButton == null) return false;
|
||||
anchor = mOverflowButton;
|
||||
}
|
||||
|
||||
mOpenSubMenuId = subMenu.getItem().getItemId();
|
||||
mActionButtonPopup = new ActionButtonSubmenu(mContext, subMenu);
|
||||
mActionButtonPopup.setAnchorView(anchor);
|
||||
mActionButtonPopup.show();
|
||||
@@ -426,6 +434,57 @@ public class ActionMenuPresenter extends BaseMenuPresenter {
|
||||
super.onCloseMenu(menu, allMenusAreClosing);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Parcelable onSaveInstanceState() {
|
||||
SavedState state = new SavedState();
|
||||
state.openSubMenuId = mOpenSubMenuId;
|
||||
return state;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRestoreInstanceState(Parcelable state) {
|
||||
SavedState saved = (SavedState) state;
|
||||
if (saved.openSubMenuId > 0) {
|
||||
MenuItem item = mMenu.findItem(saved.openSubMenuId);
|
||||
if (item != null) {
|
||||
SubMenuBuilder subMenu = (SubMenuBuilder) item.getSubMenu();
|
||||
onSubMenuSelected(subMenu);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static class SavedState implements Parcelable {
|
||||
public int openSubMenuId;
|
||||
|
||||
SavedState() {
|
||||
}
|
||||
|
||||
SavedState(Parcel in) {
|
||||
openSubMenuId = in.readInt();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int describeContents() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeToParcel(Parcel dest, int flags) {
|
||||
dest.writeInt(openSubMenuId);
|
||||
}
|
||||
|
||||
public static final Parcelable.Creator<SavedState> CREATOR
|
||||
= new Parcelable.Creator<SavedState>() {
|
||||
public SavedState createFromParcel(Parcel in) {
|
||||
return new SavedState(in);
|
||||
}
|
||||
|
||||
public SavedState[] newArray(int size) {
|
||||
return new SavedState[size];
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private class OverflowMenuButton extends ImageButton implements ActionMenuChildView {
|
||||
public OverflowMenuButton(Context context) {
|
||||
super(context, null, com.android.internal.R.attr.actionOverflowButtonStyle);
|
||||
@@ -460,6 +519,7 @@ public class ActionMenuPresenter extends BaseMenuPresenter {
|
||||
public OverflowPopup(Context context, MenuBuilder menu, View anchorView,
|
||||
boolean overflowOnly) {
|
||||
super(context, menu, anchorView, overflowOnly);
|
||||
setCallback(mPopupPresenterCallback);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -482,6 +542,8 @@ public class ActionMenuPresenter extends BaseMenuPresenter {
|
||||
// Give a reasonable anchor to nested submenus.
|
||||
setAnchorView(mOverflowButton == null ? (View) mMenuView : mOverflowButton);
|
||||
}
|
||||
|
||||
setCallback(mPopupPresenterCallback);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -489,6 +551,20 @@ public class ActionMenuPresenter extends BaseMenuPresenter {
|
||||
super.onDismiss();
|
||||
mSubMenu.close();
|
||||
mActionButtonPopup = null;
|
||||
mOpenSubMenuId = 0;
|
||||
}
|
||||
}
|
||||
|
||||
private class PopupPresenterCallback implements MenuPresenter.Callback {
|
||||
|
||||
@Override
|
||||
public boolean onOpenSubMenu(MenuBuilder subMenu) {
|
||||
mOpenSubMenuId = ((SubMenuBuilder) subMenu).getItem().getItemId();
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCloseMenu(MenuBuilder menu, boolean allMenusAreClosing) {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -39,6 +39,8 @@ public abstract class BaseMenuPresenter implements MenuPresenter {
|
||||
|
||||
protected MenuView mMenuView;
|
||||
|
||||
private int mId;
|
||||
|
||||
/**
|
||||
* Construct a new BaseMenuPresenter.
|
||||
*
|
||||
@@ -200,4 +202,12 @@ public abstract class BaseMenuPresenter implements MenuPresenter {
|
||||
public boolean collapseItemActionView(MenuBuilder menu, MenuItemImpl item) {
|
||||
return false;
|
||||
}
|
||||
|
||||
public int getId() {
|
||||
return mId;
|
||||
}
|
||||
|
||||
public void setId(int id) {
|
||||
mId = id;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,6 +23,7 @@ import android.os.Parcelable;
|
||||
import android.util.SparseArray;
|
||||
import android.view.ContextThemeWrapper;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
@@ -35,7 +36,12 @@ public class IconMenuPresenter extends BaseMenuPresenter {
|
||||
private IconMenuItemView mMoreView;
|
||||
private int mMaxItems = -1;
|
||||
|
||||
int mOpenSubMenuId;
|
||||
SubMenuPresenterCallback mSubMenuPresenterCallback = new SubMenuPresenterCallback();
|
||||
MenuDialogHelper mOpenSubMenu;
|
||||
|
||||
private static final String VIEWS_TAG = "android:menu:icon";
|
||||
private static final String OPEN_SUBMENU_KEY = "android:menu:icon:submenu";
|
||||
|
||||
public IconMenuPresenter() {
|
||||
super(com.android.internal.R.layout.icon_menu_layout,
|
||||
@@ -86,7 +92,11 @@ public class IconMenuPresenter extends BaseMenuPresenter {
|
||||
if (!subMenu.hasVisibleItems()) return false;
|
||||
|
||||
// The window manager will give us a token.
|
||||
new MenuDialogHelper(subMenu).show(null);
|
||||
MenuDialogHelper helper = new MenuDialogHelper(subMenu);
|
||||
helper.setPresenterCallback(mSubMenuPresenterCallback);
|
||||
helper.show(null);
|
||||
mOpenSubMenu = helper;
|
||||
mOpenSubMenuId = subMenu.getItem().getItemId();
|
||||
super.onSubMenuSelected(subMenu);
|
||||
return true;
|
||||
}
|
||||
@@ -137,5 +147,47 @@ public class IconMenuPresenter extends BaseMenuPresenter {
|
||||
if (viewStates != null) {
|
||||
((View) mMenuView).restoreHierarchyState(viewStates);
|
||||
}
|
||||
int subMenuId = inState.getInt(OPEN_SUBMENU_KEY, 0);
|
||||
if (subMenuId > 0 && mMenu != null) {
|
||||
MenuItem item = mMenu.findItem(subMenuId);
|
||||
if (item != null) {
|
||||
onSubMenuSelected((SubMenuBuilder) item.getSubMenu());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Parcelable onSaveInstanceState() {
|
||||
if (mMenuView == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
Bundle state = new Bundle();
|
||||
saveHierarchyState(state);
|
||||
if (mOpenSubMenuId > 0) {
|
||||
state.putInt(OPEN_SUBMENU_KEY, mOpenSubMenuId);
|
||||
}
|
||||
return state;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRestoreInstanceState(Parcelable state) {
|
||||
restoreHierarchyState((Bundle) state);
|
||||
}
|
||||
|
||||
class SubMenuPresenterCallback implements MenuPresenter.Callback {
|
||||
@Override
|
||||
public void onCloseMenu(MenuBuilder menu, boolean allMenusAreClosing) {
|
||||
mOpenSubMenuId = 0;
|
||||
mOpenSubMenu.dismiss();
|
||||
mOpenSubMenu = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onOpenSubMenu(MenuBuilder subMenu) {
|
||||
mOpenSubMenuId = ((SubMenuBuilder) subMenu).getItem().getItemId();
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -47,6 +47,8 @@ public class ListMenuPresenter implements MenuPresenter, AdapterView.OnItemClick
|
||||
private Callback mCallback;
|
||||
private MenuAdapter mAdapter;
|
||||
|
||||
private int mId;
|
||||
|
||||
public static final String VIEWS_TAG = "android:menu:list";
|
||||
|
||||
/**
|
||||
@@ -182,6 +184,31 @@ public class ListMenuPresenter implements MenuPresenter, AdapterView.OnItemClick
|
||||
}
|
||||
}
|
||||
|
||||
public void setId(int id) {
|
||||
mId = id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getId() {
|
||||
return mId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Parcelable onSaveInstanceState() {
|
||||
if (mMenuView == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
Bundle state = new Bundle();
|
||||
saveHierarchyState(state);
|
||||
return state;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRestoreInstanceState(Parcelable state) {
|
||||
restoreHierarchyState((Bundle) state);
|
||||
}
|
||||
|
||||
private class MenuAdapter extends BaseAdapter {
|
||||
public int getCount() {
|
||||
ArrayList<MenuItemImpl> items = mMenu.getNonActionItems();
|
||||
|
||||
@@ -25,8 +25,8 @@ import android.content.pm.ResolveInfo;
|
||||
import android.content.res.Configuration;
|
||||
import android.content.res.Resources;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.os.Bundle;
|
||||
import android.os.Parcelable;
|
||||
import android.util.Log;
|
||||
import android.util.SparseArray;
|
||||
import android.view.ContextMenu.ContextMenuInfo;
|
||||
import android.view.KeyCharacterMap;
|
||||
@@ -38,7 +38,6 @@ import android.view.View;
|
||||
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
|
||||
@@ -49,6 +48,8 @@ import java.util.concurrent.CopyOnWriteArrayList;
|
||||
public class MenuBuilder implements Menu {
|
||||
private static final String LOGTAG = "MenuBuilder";
|
||||
|
||||
private static final String PRESENTER_KEY = "android:menu:presenters";
|
||||
|
||||
private static final int[] sCategoryToOrder = new int[] {
|
||||
1, /* No category */
|
||||
4, /* CONTAINER */
|
||||
@@ -254,6 +255,58 @@ public class MenuBuilder implements Menu {
|
||||
return result;
|
||||
}
|
||||
|
||||
private void dispatchSaveInstanceState(Bundle outState) {
|
||||
if (mPresenters.isEmpty()) return;
|
||||
|
||||
SparseArray<Parcelable> presenterStates = new SparseArray<Parcelable>();
|
||||
|
||||
for (WeakReference<MenuPresenter> ref : mPresenters) {
|
||||
final MenuPresenter presenter = ref.get();
|
||||
if (presenter == null) {
|
||||
mPresenters.remove(ref);
|
||||
} else {
|
||||
final int id = presenter.getId();
|
||||
if (id > 0) {
|
||||
final Parcelable state = presenter.onSaveInstanceState();
|
||||
if (state != null) {
|
||||
presenterStates.put(id, state);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
outState.putSparseParcelableArray(PRESENTER_KEY, presenterStates);
|
||||
}
|
||||
|
||||
private void dispatchRestoreInstanceState(Bundle state) {
|
||||
SparseArray<Parcelable> presenterStates = state.getSparseParcelableArray(PRESENTER_KEY);
|
||||
|
||||
if (presenterStates == null || mPresenters.isEmpty()) return;
|
||||
|
||||
for (WeakReference<MenuPresenter> ref : mPresenters) {
|
||||
final MenuPresenter presenter = ref.get();
|
||||
if (presenter == null) {
|
||||
mPresenters.remove(ref);
|
||||
} else {
|
||||
final int id = presenter.getId();
|
||||
if (id > 0) {
|
||||
Parcelable parcel = presenterStates.get(id);
|
||||
if (parcel != null) {
|
||||
presenter.onRestoreInstanceState(parcel);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void savePresenterStates(Bundle outState) {
|
||||
dispatchSaveInstanceState(outState);
|
||||
}
|
||||
|
||||
public void restorePresenterStates(Bundle state) {
|
||||
dispatchRestoreInstanceState(state);
|
||||
}
|
||||
|
||||
public void setCallback(Callback cb) {
|
||||
mCallback = cb;
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@ package com.android.internal.view.menu;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.Resources;
|
||||
import android.os.Parcelable;
|
||||
import android.util.DisplayMetrics;
|
||||
import android.view.KeyEvent;
|
||||
import android.view.LayoutInflater;
|
||||
@@ -296,4 +297,18 @@ public class MenuPopupHelper implements AdapterView.OnItemClickListener, View.On
|
||||
return convertView;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getId() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Parcelable onSaveInstanceState() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRestoreInstanceState(Parcelable state) {
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
package com.android.internal.view.menu;
|
||||
|
||||
import android.content.Context;
|
||||
import android.os.Parcelable;
|
||||
import android.view.Menu;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
@@ -125,4 +126,24 @@ public interface MenuPresenter {
|
||||
* @return true if this presenter collapsed the action view, false otherwise.
|
||||
*/
|
||||
public boolean collapseItemActionView(MenuBuilder menu, MenuItemImpl item);
|
||||
|
||||
/**
|
||||
* Returns an ID for determining how to save/restore instance state.
|
||||
* @return a valid ID value.
|
||||
*/
|
||||
public int getId();
|
||||
|
||||
/**
|
||||
* Returns a Parcelable describing the current state of the presenter.
|
||||
* It will be passed to the {@link #onRestoreInstanceState(Parcelable)}
|
||||
* method of the presenter sharing the same ID later.
|
||||
* @return The saved instance state
|
||||
*/
|
||||
public Parcelable onSaveInstanceState();
|
||||
|
||||
/**
|
||||
* Supplies the previously saved instance state to be restored.
|
||||
* @param state The previously saved instance state
|
||||
*/
|
||||
public void onRestoreInstanceState(Parcelable state);
|
||||
}
|
||||
|
||||
@@ -337,6 +337,7 @@ public class ActionBarView extends AbsActionBarView {
|
||||
if (mActionMenuPresenter == null) {
|
||||
mActionMenuPresenter = new ActionMenuPresenter();
|
||||
mActionMenuPresenter.setCallback(cb);
|
||||
mActionMenuPresenter.setId(com.android.internal.R.id.action_menu_presenter);
|
||||
mExpandedMenuPresenter = new ExpandedActionViewMenuPresenter();
|
||||
}
|
||||
|
||||
@@ -1301,5 +1302,19 @@ public class ActionBarView extends AbsActionBarView {
|
||||
item.setActionViewExpanded(false);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getId() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Parcelable onSaveInstanceState() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRestoreInstanceState(Parcelable state) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -74,4 +74,9 @@
|
||||
<item type="id" name="rowTypeId" />
|
||||
<item type="id" name="up" />
|
||||
<item type="id" name="action_menu_divider" />
|
||||
<item type="id" name="icon_menu_presenter" />
|
||||
<item type="id" name="list_menu_presenter" />
|
||||
<item type="id" name="action_menu_presenter" />
|
||||
<item type="id" name="overflow_menu_presenter" />
|
||||
<item type="id" name="popup_submenu_presenter" />
|
||||
</resources>
|
||||
|
||||
@@ -1555,9 +1555,12 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback {
|
||||
// We restore the panel if it was last open; we skip it if it
|
||||
// now is open, to avoid a race condition if the user immediately
|
||||
// opens it when we are resuming.
|
||||
if ((st != null) && !st.isOpen && st.wasLastOpen) {
|
||||
st.isInExpandedMode = st.wasLastExpanded;
|
||||
openPanel(st, null);
|
||||
if (st != null) {
|
||||
st.applyFrozenState();
|
||||
if (!st.isOpen && st.wasLastOpen) {
|
||||
st.isInExpandedMode = st.wasLastExpanded;
|
||||
openPanel(st, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2235,6 +2238,11 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback {
|
||||
}
|
||||
mActionModePopup = null;
|
||||
}
|
||||
|
||||
PanelFeatureState st = getPanelState(FEATURE_OPTIONS_PANEL, false);
|
||||
if (st != null && st.menu != null) {
|
||||
st.menu.close();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -3046,46 +3054,33 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback {
|
||||
|
||||
getIconMenuView(cb); // Need this initialized to know where our offset goes
|
||||
|
||||
boolean init = false;
|
||||
if (expandedMenuPresenter == null) {
|
||||
expandedMenuPresenter = new ListMenuPresenter(
|
||||
com.android.internal.R.layout.list_menu_item_layout,
|
||||
com.android.internal.R.style.Theme_ExpandedMenu);
|
||||
expandedMenuPresenter.setCallback(cb);
|
||||
expandedMenuPresenter.setId(com.android.internal.R.id.list_menu_presenter);
|
||||
menu.addMenuPresenter(expandedMenuPresenter);
|
||||
init = true;
|
||||
}
|
||||
|
||||
expandedMenuPresenter.setItemIndexOffset(iconMenuPresenter.getNumActualItemsShown());
|
||||
MenuView result = expandedMenuPresenter.getMenuView(decorView);
|
||||
|
||||
if (init && frozenMenuState != null) {
|
||||
expandedMenuPresenter.restoreHierarchyState(frozenMenuState);
|
||||
// Once we initialize the expanded menu we're done with the frozen state
|
||||
// since we will have also restored any icon menu state.
|
||||
frozenMenuState = null;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
MenuView getIconMenuView(MenuPresenter.Callback cb) {
|
||||
if (menu == null) return null;
|
||||
|
||||
boolean init = false;
|
||||
if (iconMenuPresenter == null) {
|
||||
iconMenuPresenter = new IconMenuPresenter();
|
||||
iconMenuPresenter.setCallback(cb);
|
||||
iconMenuPresenter.setId(com.android.internal.R.id.icon_menu_presenter);
|
||||
menu.addMenuPresenter(iconMenuPresenter);
|
||||
init = true;
|
||||
}
|
||||
|
||||
MenuView result = iconMenuPresenter.getMenuView(decorView);
|
||||
|
||||
if (init && frozenMenuState != null) {
|
||||
iconMenuPresenter.restoreHierarchyState(frozenMenuState);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -3097,12 +3092,7 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback {
|
||||
|
||||
if (menu != null) {
|
||||
savedState.menuState = new Bundle();
|
||||
if (iconMenuPresenter != null) {
|
||||
iconMenuPresenter.saveHierarchyState(savedState.menuState);
|
||||
}
|
||||
if (expandedMenuPresenter != null) {
|
||||
expandedMenuPresenter.saveHierarchyState(savedState.menuState);
|
||||
}
|
||||
menu.savePresenterStates(savedState.menuState);
|
||||
}
|
||||
|
||||
return savedState;
|
||||
@@ -3127,6 +3117,13 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback {
|
||||
decorView = null;
|
||||
}
|
||||
|
||||
void applyFrozenState() {
|
||||
if (menu != null && frozenMenuState != null) {
|
||||
menu.restorePresenterStates(frozenMenuState);
|
||||
frozenMenuState = null;
|
||||
}
|
||||
}
|
||||
|
||||
private static class SavedState implements Parcelable {
|
||||
int featureId;
|
||||
boolean isOpen;
|
||||
|
||||
Reference in New Issue
Block a user