Merge "Fix bugs around restoring submenu state after activity teardown/restore"

This commit is contained in:
Adam Powell
2011-07-12 11:36:43 -07:00
committed by Android (Google) Code Review
10 changed files with 300 additions and 29 deletions

View File

@@ -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) {
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}
}

View File

@@ -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();

View File

@@ -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;
}

View File

@@ -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) {
}
}

View File

@@ -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);
}

View File

@@ -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) {
}
}
}

View File

@@ -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>

View File

@@ -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;