Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 77048379 authored by Deepanshu Gupta's avatar Deepanshu Gupta
Browse files

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

The change adds the view cookies for the menus rendered in the action
bar. This enables the IDE to map the menu to the relevant XML Tag in the
menu xml and show the highlighting accordingly.

The change also contains a bugfix where a method wasn't renamed
properly.

Change-Id: Idcfc263a8ebe0a4f25afa3a1eb085fa628fd03ca
(cherry-picked from commit 5ba2f230)
parent 75f435b0
Loading
Loading
Loading
Loading
+31 −5
Original line number Diff line number Diff line
@@ -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)) {
@@ -201,6 +202,29 @@ public class MenuInflater {
        }
    }

    /**
     * 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() {
+10 −3
Original line number Diff line number Diff line
@@ -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
@@ -400,6 +400,13 @@ 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);
    }
+53 −41
Original line number Diff line number Diff line
@@ -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,10 +215,40 @@ 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;
        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);
            }
        }
    }

    public void setIsInMerge(boolean isInMerge) {
        mIsInMerge = isInMerge;
    }

    public void setResourceReference(ResourceReference reference) {
        mResourceReference = reference;
    }

    @Override
    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();
@@ -230,43 +259,26 @@ public final class BridgeInflater extends LayoutInflater {
            // 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.
                // 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 = mIsInMerge ? 2 : 1;
                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 && mIsInMerge) {
                    if (viewKey != null && isInMerge) {
                        viewKey = new MergeCookie(viewKey);
                    }
                }
                    } else if (mResourceReference != null && currentDepth == 1) {
            } 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 = mResourceReference;
                    }
                }

                if (viewKey != null) {
                    bc.addViewKey(view, viewKey);
                }
            }
                viewKey = resourceReference;
            }
        }

    public void setIsInMerge(boolean isInMerge) {
        mIsInMerge = isInMerge;
    }

    public void setResourceReference(ResourceReference reference) {
        mResourceReference = reference;
    }

    @Override
    public LayoutInflater cloneInContext(Context newContext) {
        return new BridgeInflater(this, newContext);
        return viewKey;
    }
}
+75 −0
Original line number Diff line number Diff line
/*
 * 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);
    }

}
+47 −0
Original line number Diff line number Diff line
/*
 * 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;
    }
}
Loading