am 77048379: Add view cookies for action bar menus. [DO NOT MERGE]

* commit '7704837947455909f295b088a3d15a20e107dbd7':
  Add view cookies for action bar menus. [DO NOT MERGE]
This commit is contained in:
Deepanshu Gupta
2014-05-13 01:44:29 +00:00
committed by Android Git Automerger
8 changed files with 292 additions and 53 deletions

View File

@@ -161,6 +161,7 @@ public class MenuInflater {
} else if (tagName.equals(XML_MENU)) {
// A menu start tag denotes a submenu for an item
SubMenu subMenu = menuState.addSubMenuItem();
registerMenu(subMenu, attrs);
// Parse the submenu into returned SubMenu
parseMenu(parser, attrs, subMenu);
@@ -183,9 +184,9 @@ public class MenuInflater {
if (!menuState.hasAddedItem()) {
if (menuState.itemActionProvider != null &&
menuState.itemActionProvider.hasSubMenu()) {
menuState.addSubMenuItem();
registerMenu(menuState.addSubMenuItem(), attrs);
} else {
menuState.addItem();
registerMenu(menuState.addItem(), attrs);
}
}
} else if (tagName.equals(XML_MENU)) {
@@ -200,7 +201,30 @@ public class MenuInflater {
eventType = parser.next();
}
}
/**
* The method is a hook for layoutlib to do its magic.
* Nothing is needed outside of LayoutLib. However, it should not be deleted because it
* appears to do nothing.
*/
private void registerMenu(@SuppressWarnings("unused") MenuItem item,
@SuppressWarnings("unused") AttributeSet set) {
}
/**
* The method is a hook for layoutlib to do its magic.
* Nothing is needed outside of LayoutLib. However, it should not be deleted because it
* appears to do nothing.
*/
private void registerMenu(@SuppressWarnings("unused") SubMenu subMenu,
@SuppressWarnings("unused") AttributeSet set) {
}
// Needed by layoutlib.
/*package*/ Context getContext() {
return mContext;
}
private static class InflatedOnMenuItemClickListener
implements MenuItem.OnMenuItemClickListener {
private static final Class<?>[] PARAM_TYPES = new Class[] { MenuItem.class };
@@ -446,9 +470,11 @@ public class MenuInflater {
}
}
public void addItem() {
public MenuItem addItem() {
itemAdded = true;
setItem(menu.add(groupId, itemId, itemCategoryOrder, itemTitle));
MenuItem item = menu.add(groupId, itemId, itemCategoryOrder, itemTitle);
setItem(item);
return item;
}
public SubMenu addSubMenuItem() {

View File

@@ -386,8 +386,8 @@ public class MenuBuilder implements Menu {
private MenuItem addInternal(int group, int id, int categoryOrder, CharSequence title) {
final int ordering = getOrdering(categoryOrder);
final MenuItemImpl item = new MenuItemImpl(this, group, id, categoryOrder,
ordering, title, mDefaultShowAsAction);
final MenuItemImpl item = createNewMenuItem(group, id, categoryOrder, ordering, title,
mDefaultShowAsAction);
if (mCurrentMenuInfo != null) {
// Pass along the current menu info
@@ -399,7 +399,14 @@ public class MenuBuilder implements Menu {
return item;
}
// Layoutlib overrides this method to return its custom implementation of MenuItemImpl
private MenuItemImpl createNewMenuItem(int group, int id, int categoryOrder, int ordering,
CharSequence title, int defaultShowAsAction) {
return new MenuItemImpl(this, group, id, categoryOrder, ordering, title,
defaultShowAsAction);
}
public MenuItem add(CharSequence title) {
return addInternal(0, 0, 0, title);
}

View File

@@ -32,10 +32,6 @@ import org.xmlpull.v1.XmlPullParser;
import android.content.Context;
import android.util.AttributeSet;
import android.view.InflateException;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import java.io.File;
@@ -154,6 +150,9 @@ public final class BridgeInflater extends LayoutInflater {
@Override
public View inflate(int resource, ViewGroup root) {
Context context = getContext();
if (context instanceof ContextThemeWrapper) {
context = ((ContextThemeWrapper) context).getBaseContext();
}
if (context instanceof BridgeContext) {
BridgeContext bridgeContext = (BridgeContext)context;
@@ -216,43 +215,16 @@ public final class BridgeInflater extends LayoutInflater {
}
private void setupViewInContext(View view, AttributeSet attrs) {
if (getContext() instanceof BridgeContext) {
BridgeContext bc = (BridgeContext) getContext();
if (attrs instanceof BridgeXmlBlockParser) {
BridgeXmlBlockParser parser = (BridgeXmlBlockParser) attrs;
// get the view key
Object viewKey = parser.getViewCookie();
if (viewKey == null) {
int currentDepth = parser.getDepth();
// test whether we are in an included file or in a adapter binding view.
BridgeXmlBlockParser previousParser = bc.getPreviousParser();
if (previousParser != null) {
// looks like we inside an embedded layout.
// only apply the cookie of the calling node (<include>) if we are at the
// top level of the embedded layout. If there is a merge tag, then
// skip it and look for the 2nd level
int testDepth = mIsInMerge ? 2 : 1;
if (currentDepth == testDepth) {
viewKey = previousParser.getViewCookie();
// if we are in a merge, wrap the cookie in a MergeCookie.
if (viewKey != null && mIsInMerge) {
viewKey = new MergeCookie(viewKey);
}
}
} else if (mResourceReference != null && currentDepth == 1) {
// else if there's a resource reference, this means we are in an adapter
// binding case. Set the resource ref as the view cookie only for the top
// level view.
viewKey = mResourceReference;
}
}
if (viewKey != null) {
bc.addViewKey(view, viewKey);
}
Context context = getContext();
if (context instanceof ContextThemeWrapper) {
context = ((ContextThemeWrapper) context).getBaseContext();
}
if (context instanceof BridgeContext) {
BridgeContext bc = (BridgeContext) context;
// get the view key
Object viewKey = getViewKeyFromParser(attrs, bc, mResourceReference, mIsInMerge);
if (viewKey != null) {
bc.addViewKey(view, viewKey);
}
}
}
@@ -269,4 +241,44 @@ public final class BridgeInflater extends LayoutInflater {
public LayoutInflater cloneInContext(Context newContext) {
return new BridgeInflater(this, newContext);
}
/*package*/ static Object getViewKeyFromParser(AttributeSet attrs, BridgeContext bc,
ResourceReference resourceReference, boolean isInMerge) {
if (!(attrs instanceof BridgeXmlBlockParser)) {
return null;
}
BridgeXmlBlockParser parser = ((BridgeXmlBlockParser) attrs);
// get the view key
Object viewKey = parser.getViewCookie();
if (viewKey == null) {
int currentDepth = parser.getDepth();
// test whether we are in an included file or in a adapter binding view.
BridgeXmlBlockParser previousParser = bc.getPreviousParser();
if (previousParser != null) {
// looks like we are inside an embedded layout.
// only apply the cookie of the calling node (<include>) if we are at the
// top level of the embedded layout. If there is a merge tag, then
// skip it and look for the 2nd level
int testDepth = isInMerge ? 2 : 1;
if (currentDepth == testDepth) {
viewKey = previousParser.getViewCookie();
// if we are in a merge, wrap the cookie in a MergeCookie.
if (viewKey != null && isInMerge) {
viewKey = new MergeCookie(viewKey);
}
}
} else if (resourceReference != null && currentDepth == 1) {
// else if there's a resource reference, this means we are in an adapter
// binding case. Set the resource ref as the view cookie only for the top
// level view.
viewKey = resourceReference;
}
}
return viewKey;
}
}

View File

@@ -0,0 +1,75 @@
/*
* Copyright (C) 2014 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 android.content.Context;
import com.android.ide.common.rendering.api.LayoutLog;
import com.android.ide.common.rendering.api.ViewInfo;
import com.android.internal.view.menu.BridgeMenuItemImpl;
import com.android.internal.view.menu.MenuView;
import com.android.layoutlib.bridge.Bridge;
import com.android.layoutlib.bridge.android.BridgeContext;
import com.android.layoutlib.bridge.android.BridgeXmlBlockParser;
import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
import android.util.AttributeSet;
/**
* Delegate used to provide new implementation of a select few methods of {@link MenuInflater}
* <p/>
* Through the layoutlib_create tool, the original methods of MenuInflater have been
* replaced by calls to methods of the same name in this delegate class.
* <p/>
* The main purpose of the class is to get the view key from the menu xml parser and add it to
* the menu item. The view key is used by the IDE to match the individual view elements to the
* corresponding xml tag in the menu/layout file.
* <p/>
* For Menus, the views may be reused and the {@link MenuItem} is a better object to hold the
* view key than the {@link MenuView.ItemView}. At the time of computation of the rest of {@link
* ViewInfo}, we check the corresponding view key in the menu item for the view and add it
*/
public class MenuInflater_Delegate {
@LayoutlibDelegate
/*package*/ static void registerMenu(MenuInflater thisInflater, MenuItem menuItem,
AttributeSet attrs) {
if (menuItem instanceof BridgeMenuItemImpl) {
Context context = thisInflater.getContext();
if (context instanceof ContextThemeWrapper) {
context = ((ContextThemeWrapper) context).getBaseContext();
}
if (context instanceof BridgeContext) {
Object viewKey = BridgeInflater.getViewKeyFromParser(
attrs, ((BridgeContext) context), null, false);
((BridgeMenuItemImpl) menuItem).setViewCookie(viewKey);
return;
}
}
// This means that Bridge did not take over the instantiation of some object properly.
// This is most likely a bug in the LayoutLib code.
Bridge.getLog().warning(LayoutLog.TAG_BROKEN,
"Action Bar Menu rendering may be incorrect.", null);
}
@LayoutlibDelegate
/*package*/ static void registerMenu(MenuInflater thisInflater, SubMenu subMenu,
AttributeSet parser) {
registerMenu(thisInflater, subMenu.getItem(), parser);
}
}

View File

@@ -0,0 +1,47 @@
/*
* Copyright (C) 2014 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;
/**
* An extension of the {@link MenuItemImpl} to store the view cookie also.
*/
public class BridgeMenuItemImpl extends MenuItemImpl {
/**
* An object returned by the IDE that helps mapping each View to the corresponding XML tag in
* the layout. For Menus, we store this cookie here and attach it to the corresponding view
* at the time of rendering.
*/
private Object viewCookie;
/**
* Instantiates this menu item.
*/
BridgeMenuItemImpl(MenuBuilder menu, int group, int id, int categoryOrder, int ordering,
CharSequence title, int showAsAction) {
super(menu, group, id, categoryOrder, ordering, title, showAsAction);
}
public Object getViewCookie() {
return viewCookie;
}
public void setViewCookie(Object viewCookie) {
this.viewCookie = viewCookie;
}
}

View File

@@ -0,0 +1,38 @@
/*
* Copyright (C) 2014 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 com.android.tools.layoutlib.annotations.LayoutlibDelegate;
/**
* Delegate used to provide new implementation of a select few methods of {@link MenuBuilder}
* <p/>
* Through the layoutlib_create tool, the original methods of {@code MenuBuilder} have been
* replaced by calls to methods of the same name in this delegate class.
*/
public class MenuBuilder_Delegate {
/**
* The method overrides the instantiation of the {@link MenuItemImpl} with an instance of
* {@link BridgeMenuItemImpl} so that view cookies may be stored.
*/
@LayoutlibDelegate
/*package*/ static MenuItemImpl createNewMenuItem(MenuBuilder thisMenu, int group, int id,
int categoryOrder, int ordering, CharSequence title, int defaultShowAsAction) {
return new BridgeMenuItemImpl(thisMenu, group, id, categoryOrder, ordering, title,
defaultShowAsAction);
}
}

View File

@@ -28,7 +28,6 @@ import com.android.ide.common.rendering.api.HardwareConfig;
import com.android.ide.common.rendering.api.IAnimationListener;
import com.android.ide.common.rendering.api.ILayoutPullParser;
import com.android.ide.common.rendering.api.IProjectCallback;
import com.android.ide.common.rendering.api.RenderParams;
import com.android.ide.common.rendering.api.RenderResources;
import com.android.ide.common.rendering.api.RenderSession;
import com.android.ide.common.rendering.api.ResourceReference;
@@ -39,6 +38,12 @@ import com.android.ide.common.rendering.api.SessionParams;
import com.android.ide.common.rendering.api.SessionParams.RenderingMode;
import com.android.ide.common.rendering.api.ViewInfo;
import com.android.internal.util.XmlUtils;
import com.android.internal.view.menu.ActionMenuItemView;
import com.android.internal.view.menu.BridgeMenuItemImpl;
import com.android.internal.view.menu.IconMenuItemView;
import com.android.internal.view.menu.ListMenuItemView;
import com.android.internal.view.menu.MenuItemImpl;
import com.android.internal.view.menu.MenuView;
import com.android.layoutlib.bridge.Bridge;
import com.android.layoutlib.bridge.android.BridgeContext;
import com.android.layoutlib.bridge.android.BridgeLayoutParamsMapAttributes;
@@ -574,7 +579,8 @@ public class RenderSessionImpl extends RenderAction<SessionParams> {
mViewRoot.draw(mCanvas);
}
mSystemViewInfoList = visitAllChildren(mViewRoot, 0, params.getExtendedViewInfoMode(), false);
mSystemViewInfoList = visitAllChildren(mViewRoot, 0, params.getExtendedViewInfoMode(),
false);
// success!
return SUCCESS.createResult();
@@ -1469,13 +1475,13 @@ public class RenderSessionImpl extends RenderAction<SessionParams> {
ViewInfo result;
if (isContentFrame) {
result = new ViewInfo(view.getClass().getName(),
getContext().getViewKey(view),
getViewKey(view),
view.getLeft(), view.getTop() + offset, view.getRight(),
view.getBottom() + offset, view, view.getLayoutParams());
} else {
result = new SystemViewInfo(view.getClass().getName(),
getContext().getViewKey(view),
getViewKey(view),
view.getLeft(), view.getTop(), view.getRight(),
view.getBottom(), view, view.getLayoutParams());
}
@@ -1496,6 +1502,32 @@ public class RenderSessionImpl extends RenderAction<SessionParams> {
return result;
}
/**
* The cookie for menu items are stored in menu item and not in the map from View stored in
* BridgeContext.
*/
private Object getViewKey(View view) {
BridgeContext context = getContext();
if (!(view instanceof MenuView.ItemView)) {
return context.getViewKey(view);
}
MenuItemImpl menuItem;
if (view instanceof ActionMenuItemView) {
menuItem = ((ActionMenuItemView) view).getItemData();
} else if (view instanceof ListMenuItemView) {
menuItem = ((ListMenuItemView) view).getItemData();
} else if (view instanceof IconMenuItemView) {
menuItem = ((IconMenuItemView) view).getItemData();
} else {
menuItem = null;
}
if (menuItem instanceof BridgeMenuItemImpl) {
return ((BridgeMenuItemImpl) menuItem).getViewCookie();
}
return null;
}
private void invalidateRenderingSize() {
mMeasuredScreenWidth = mMeasuredScreenHeight = -1;
}

View File

@@ -142,6 +142,8 @@ public final class CreateInfo implements ICreateInfo {
"android.view.ViewRootImpl#isInTouchMode",
"android.view.WindowManagerGlobal#getWindowManagerService",
"android.view.inputmethod.InputMethodManager#getInstance",
"android.view.MenuInflater#registerMenu",
"com.android.internal.view.menu.MenuBuilder#createNewMenuItem",
"com.android.internal.util.XmlUtils#convertValueToInt",
"com.android.internal.textservice.ITextServicesManager$Stub#asInterface",
};