diff options
6 files changed, 596 insertions, 184 deletions
diff --git a/core/java/android/widget/Toolbar.java b/core/java/android/widget/Toolbar.java index 1ce19ce..384e461 100644 --- a/core/java/android/widget/Toolbar.java +++ b/core/java/android/widget/Toolbar.java @@ -1746,6 +1746,17 @@ public class Toolbar extends ViewGroup { } /** + * Accessor to enable LayoutLib to get ActionMenuPresenter directly. + */ + ActionMenuPresenter getOuterActionMenuPresenter() { + return mOuterActionMenuPresenter; + } + + Context getPopupContext() { + return mPopupContext; + } + + /** * Interface responsible for receiving menu item click events if the items themselves * do not have individual item click listeners. */ diff --git a/tools/layoutlib/bridge/src/android/view/WindowCallback.java b/tools/layoutlib/bridge/src/android/view/WindowCallback.java new file mode 100644 index 0000000..78242a8 --- /dev/null +++ b/tools/layoutlib/bridge/src/android/view/WindowCallback.java @@ -0,0 +1,131 @@ +/* + * 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.view.ActionMode.Callback; +import android.view.WindowManager.LayoutParams; +import android.view.accessibility.AccessibilityEvent; + +/** + * An empty implementation of {@link Window.Callback} that always returns null/false. + */ +public class WindowCallback implements Window.Callback { + @Override + public boolean dispatchKeyEvent(KeyEvent event) { + return false; + } + + @Override + public boolean dispatchKeyShortcutEvent(KeyEvent event) { + return false; + } + + @Override + public boolean dispatchTouchEvent(MotionEvent event) { + return false; + } + + @Override + public boolean dispatchTrackballEvent(MotionEvent event) { + return false; + } + + @Override + public boolean dispatchGenericMotionEvent(MotionEvent event) { + return false; + } + + @Override + public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) { + return false; + } + + @Override + public View onCreatePanelView(int featureId) { + return null; + } + + @Override + public boolean onCreatePanelMenu(int featureId, Menu menu) { + return false; + } + + @Override + public boolean onPreparePanel(int featureId, View view, Menu menu) { + return false; + } + + @Override + public boolean onMenuOpened(int featureId, Menu menu) { + return false; + } + + @Override + public boolean onMenuItemSelected(int featureId, MenuItem item) { + return false; + } + + @Override + public void onWindowAttributesChanged(LayoutParams attrs) { + + } + + @Override + public void onContentChanged() { + + } + + @Override + public void onWindowFocusChanged(boolean hasFocus) { + + } + + @Override + public void onAttachedToWindow() { + + } + + @Override + public void onDetachedFromWindow() { + + } + + @Override + public void onPanelClosed(int featureId, Menu menu) { + + } + + @Override + public boolean onSearchRequested() { + return false; + } + + @Override + public ActionMode onWindowStartingActionMode(Callback callback) { + return null; + } + + @Override + public void onActionModeStarted(ActionMode mode) { + + } + + @Override + public void onActionModeFinished(ActionMode mode) { + + } +} diff --git a/tools/layoutlib/bridge/src/android/widget/Toolbar_Accessor.java b/tools/layoutlib/bridge/src/android/widget/Toolbar_Accessor.java new file mode 100644 index 0000000..fdd1779 --- /dev/null +++ b/tools/layoutlib/bridge/src/android/widget/Toolbar_Accessor.java @@ -0,0 +1,32 @@ +/* + * 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.widget; + +import android.content.Context; + +/** + * To access non public members of classes in {@link Toolbar} + */ +public class Toolbar_Accessor { + public static ActionMenuPresenter getActionMenuPresenter(Toolbar toolbar) { + return toolbar.getOuterActionMenuPresenter(); + } + + public static Context getPopupContext(Toolbar toolbar) { + return toolbar.getPopupContext(); + } +} diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/ActionBarLayout.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/ActionBarLayout.java index d95c815..57fd68e 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/ActionBarLayout.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/ActionBarLayout.java @@ -18,36 +18,24 @@ package com.android.layoutlib.bridge.bars; import com.android.annotations.NonNull; import com.android.annotations.Nullable; -import com.android.ide.common.rendering.api.ActionBarCallback; -import com.android.ide.common.rendering.api.ActionBarCallback.HomeButtonStyle; -import com.android.ide.common.rendering.api.RenderResources; import com.android.ide.common.rendering.api.ResourceValue; import com.android.ide.common.rendering.api.SessionParams; import com.android.internal.R; -import com.android.internal.app.WindowDecorActionBar; import com.android.internal.view.menu.MenuBuilder; import com.android.internal.view.menu.MenuItemImpl; -import com.android.internal.widget.ActionBarAccessor; -import com.android.internal.widget.ActionBarContainer; -import com.android.internal.widget.ActionBarView; import com.android.layoutlib.bridge.android.BridgeContext; import com.android.layoutlib.bridge.impl.ResourceHelper; -import com.android.resources.ResourceType; -import android.app.ActionBar; -import android.app.ActionBar.Tab; -import android.app.ActionBar.TabListener; -import android.app.FragmentTransaction; import android.content.Context; import android.content.res.TypedArray; -import android.graphics.drawable.Drawable; import android.util.DisplayMetrics; import android.util.TypedValue; -import android.view.Gravity; import android.view.LayoutInflater; -import android.view.MenuInflater; import android.view.View; +import android.view.View.MeasureSpec; import android.view.ViewGroup; +import android.view.ViewGroup.LayoutParams; +import android.widget.ActionMenuPresenter; import android.widget.FrameLayout; import android.widget.LinearLayout; import android.widget.ListAdapter; @@ -56,172 +44,72 @@ import android.widget.RelativeLayout; import java.util.ArrayList; -/** - * A layout representing the action bar. - */ -public class ActionBarLayout extends LinearLayout { +public class ActionBarLayout { + + private static final String LAYOUT_ATTR_NAME = "windowActionBarFullscreenDecorLayout"; + + // The Action Bar + @NonNull private CustomActionBarWrapper mActionBar; // Store another reference to the context so that we don't have to cast it repeatedly. @NonNull private final BridgeContext mBridgeContext; - @NonNull private final Context mThemedContext; - - @NonNull private final ActionBar mActionBar; - - // Data for Action Bar. - @Nullable private final String mIcon; - @Nullable private final String mTitle; - @Nullable private final String mSubTitle; - private final boolean mSplit; - private final boolean mShowHomeAsUp; - private final int mNavMode; - - // Helper fields. - @NonNull private final MenuBuilder mMenuBuilder; - private final int mPopupMaxWidth; - @NonNull private final RenderResources res; - @Nullable private final ActionBarView mActionBarView; - @Nullable private FrameLayout mContentRoot; - @NonNull private final ActionBarCallback mCallback; + + @NonNull private FrameLayout mContentRoot; // A fake parent for measuring views. @Nullable private ViewGroup mMeasureParent; - public ActionBarLayout(@NonNull BridgeContext context, @NonNull SessionParams params) { - - super(context); - setOrientation(LinearLayout.HORIZONTAL); - setGravity(Gravity.CENTER_VERTICAL); - - // Inflate action bar layout. - LayoutInflater.from(context).inflate(R.layout.screen_action_bar, this, - true /*attachToRoot*/); - mActionBar = new WindowDecorActionBar(this); - - // Set contexts. - mBridgeContext = context; - mThemedContext = mActionBar.getThemedContext(); - - // Set data for action bar. - mCallback = params.getProjectCallback().getActionBarCallback(); - mIcon = params.getAppIcon(); - mTitle = params.getAppLabel(); - // Split Action Bar when the screen size is narrow and the application requests split action - // bar when narrow. - mSplit = context.getResources().getBoolean(R.bool.split_action_bar_is_narrow) && - mCallback.getSplitActionBarWhenNarrow(); - mNavMode = mCallback.getNavigationMode(); - // TODO: Support Navigation Drawer Indicator. - mShowHomeAsUp = mCallback.getHomeButtonStyle() == HomeButtonStyle.SHOW_HOME_AS_UP; - mSubTitle = mCallback.getSubTitle(); - - - // Set helper fields. - mMenuBuilder = new MenuBuilder(mThemedContext); - res = mBridgeContext.getRenderResources(); - mPopupMaxWidth = Math.max(mBridgeContext.getMetrics().widthPixels / 2, - mThemedContext.getResources().getDimensionPixelSize( - R.dimen.config_prefDialogWidth)); - mActionBarView = (ActionBarView) findViewById(R.id.action_bar); - mContentRoot = (FrameLayout) findViewById(android.R.id.content); - - setupActionBar(); - } - /** - * Sets up the action bar by filling the appropriate data. + * Inflate the action bar and attach it to {@code parentView} */ - private void setupActionBar() { - // Add title and sub title. - ResourceValue titleValue = res.findResValue(mTitle, false /*isFramework*/); - if (titleValue != null && titleValue.getValue() != null) { - mActionBar.setTitle(titleValue.getValue()); - } else { - mActionBar.setTitle(mTitle); - } - if (mSubTitle != null) { - mActionBar.setSubtitle(mSubTitle); - } + public ActionBarLayout(@NonNull BridgeContext context, @NonNull SessionParams params, + @NonNull ViewGroup parentView) { - // Add show home as up icon. - if (mShowHomeAsUp) { - mActionBar.setDisplayOptions(0xFF, ActionBar.DISPLAY_HOME_AS_UP); - } + mBridgeContext = context; - // Set the navigation mode. - mActionBar.setNavigationMode(mNavMode); - if (mNavMode == ActionBar.NAVIGATION_MODE_TABS) { - setupTabs(3); + ResourceValue layoutName = context.getRenderResources() + .findItemInTheme(LAYOUT_ATTR_NAME, true); + if (layoutName != null) { + // We may need to resolve the reference obtained. + layoutName = context.getRenderResources().findResValue(layoutName.getValue(), + layoutName.isFramework()); } - - if (mActionBarView != null) { - // If the action bar style doesn't specify an icon, set the icon obtained from the session - // params. - if (!mActionBarView.hasIcon() && mIcon != null) { - Drawable iconDrawable = getDrawable(mIcon, false /*isFramework*/); - if (iconDrawable != null) { - mActionBar.setIcon(iconDrawable); - } + int layoutId = 0; + String error = null; + if (layoutName == null) { + error = "Unable to find action bar layout (" + LAYOUT_ATTR_NAME + + ") in the current theme."; + } else { + layoutId = context.getFrameworkResourceValue(layoutName.getResourceType(), + layoutName.getName(), 0); + if (layoutId == 0) { + error = String.format("Unable to resolve attribute \"%s\" of type \"%s\"", + layoutName.getName(), layoutName.getResourceType()); } - - // Set action bar to be split, if needed. - ActionBarContainer splitView = (ActionBarContainer) findViewById(R.id.split_action_bar); - mActionBarView.setSplitView(splitView); - mActionBarView.setSplitToolbar(mSplit); - - inflateMenus(); } - } - - /** - * Gets the menus to add to the action bar from the callback, resolves them, inflates them and - * adds them to the action bar. - */ - private void inflateMenus() { - if (mActionBarView == null) { - return; - } - final MenuInflater inflater = new MenuInflater(mThemedContext); - for (String name : mCallback.getMenuIdNames()) { - if (mBridgeContext.getRenderResources().getProjectResource(ResourceType.MENU, name) - != null) { - int id = mBridgeContext.getProjectResourceValue(ResourceType.MENU, name, -1); - if (id > -1) { - inflater.inflate(id, mMenuBuilder); - } - } + if (layoutId == 0) { + throw new RuntimeException(error); } - mActionBarView.setMenu(mMenuBuilder, null /*callback*/); - } + // Inflate action bar layout. + View decorContent = LayoutInflater.from(context).inflate(layoutId, parentView, true); - // TODO: Use an adapter, like List View to set up tabs. - private void setupTabs(int num) { - for (int i = 1; i <= num; i++) { - Tab tab = mActionBar.newTab().setText("Tab" + i).setTabListener(new TabListener() { - @Override - public void onTabUnselected(Tab t, FragmentTransaction ft) { - // pass - } - @Override - public void onTabSelected(Tab t, FragmentTransaction ft) { - // pass - } - @Override - public void onTabReselected(Tab t, FragmentTransaction ft) { - // pass - } - }); - mActionBar.addTab(tab); - } - } + mActionBar = CustomActionBarWrapper.getActionBarWrapper(context, params, decorContent); - @Nullable - private Drawable getDrawable(@NonNull String name, boolean isFramework) { - ResourceValue value = res.findResValue(name, isFramework); - value = res.resolveResValue(value); - if (value != null) { - return ResourceHelper.getDrawable(value, mBridgeContext); + FrameLayout contentRoot = (FrameLayout) parentView.findViewById(android.R.id.content); + + // If something went wrong and we were not able to initialize the content root, + // just add a frame layout inside this and return. + if (contentRoot == null) { + contentRoot = new FrameLayout(context); + contentRoot.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, + LayoutParams.MATCH_PARENT)); + parentView.addView(contentRoot); + mContentRoot = contentRoot; + } else { + mContentRoot = contentRoot; + mActionBar.setupActionBar(); + mActionBar.inflateMenus(); } - return null; } /** @@ -229,7 +117,7 @@ public class ActionBarLayout extends LinearLayout { * the content frame which shall serve as the new content root. */ public void createMenuPopup() { - assert mContentRoot != null && findViewById(android.R.id.content) == mContentRoot + assert mContentRoot.getId() == android.R.id.content : "Action Bar Menus have already been created."; if (!isOverflowPopupNeeded()) { @@ -237,7 +125,7 @@ public class ActionBarLayout extends LinearLayout { } // Create a layout to hold the menus and the user's content. - RelativeLayout layout = new RelativeLayout(mThemedContext); + RelativeLayout layout = new RelativeLayout(mActionBar.getPopupContext()); layout.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT)); mContentRoot.addView(layout); @@ -259,13 +147,14 @@ public class ActionBarLayout extends LinearLayout { @NonNull private View createMenuView() { DisplayMetrics metrics = mBridgeContext.getMetrics(); - OverflowMenuAdapter adapter = new OverflowMenuAdapter(mMenuBuilder, mThemedContext); + MenuBuilder menu = mActionBar.getMenuBuilder(); + OverflowMenuAdapter adapter = new OverflowMenuAdapter(menu, mActionBar.getPopupContext()); - LinearLayout layout = new LinearLayout(mThemedContext); + LinearLayout layout = new LinearLayout(mActionBar.getPopupContext()); RelativeLayout.LayoutParams layoutParams = new RelativeLayout.LayoutParams( measureContentWidth(adapter), LayoutParams.WRAP_CONTENT); layoutParams.addRule(RelativeLayout.ALIGN_PARENT_END); - if (mSplit) { + if (mActionBar.isSplit()) { layoutParams.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM); // TODO: Find correct value instead of hardcoded 10dp. layoutParams.bottomMargin = getPixelValue("-10dp", metrics); @@ -273,7 +162,7 @@ public class ActionBarLayout extends LinearLayout { layoutParams.topMargin = getPixelValue("-10dp", metrics); } layout.setLayoutParams(layoutParams); - final TypedArray a = mThemedContext.obtainStyledAttributes(null, + final TypedArray a = mActionBar.getPopupContext().obtainStyledAttributes(null, R.styleable.PopupWindow, R.attr.popupMenuStyle, 0); layout.setBackground(a.getDrawable(R.styleable.PopupWindow_popupBackground)); layout.setDividerDrawable(a.getDrawable(R.attr.actionBarDivider)); @@ -282,20 +171,25 @@ public class ActionBarLayout extends LinearLayout { layout.setDividerPadding(getPixelValue("12dp", metrics)); layout.setShowDividers(LinearLayout.SHOW_DIVIDER_MIDDLE); - ListView listView = new ListView(mThemedContext, null, R.attr.dropDownListViewStyle); + ListView listView = new ListView(mActionBar.getPopupContext(), null, + R.attr.dropDownListViewStyle); listView.setAdapter(adapter); layout.addView(listView); return layout; } private boolean isOverflowPopupNeeded() { - boolean needed = mCallback.isOverflowPopupNeeded(); + boolean needed = mActionBar.isOverflowPopupNeeded(); if (!needed) { return false; } // Copied from android.widget.ActionMenuPresenter.updateMenuView() - ArrayList<MenuItemImpl> menus = mMenuBuilder.getNonActionItems(); - if (ActionBarAccessor.getActionMenuPresenter(mActionBarView).isOverflowReserved() && + ArrayList<MenuItemImpl> menus = mActionBar.getMenuBuilder().getNonActionItems(); + ActionMenuPresenter presenter = mActionBar.getActionMenuPresenter(); + if (presenter == null) { + throw new RuntimeException("Failed to create a Presenter for Action Bar Menus."); + } + if (presenter.isOverflowReserved() && menus != null) { final int count = menus.size(); if (count == 1) { @@ -307,7 +201,7 @@ public class ActionBarLayout extends LinearLayout { return needed; } - @Nullable + @NonNull public FrameLayout getContentRoot() { return mContentRoot; } @@ -319,6 +213,7 @@ public class ActionBarLayout extends LinearLayout { View itemView = null; int itemType = 0; + Context context = mActionBar.getPopupContext(); final int widthMeasureSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED); final int heightMeasureSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED); final int count = adapter.getCount(); @@ -330,15 +225,17 @@ public class ActionBarLayout extends LinearLayout { } if (mMeasureParent == null) { - mMeasureParent = new FrameLayout(mThemedContext); + mMeasureParent = new FrameLayout(context); } itemView = adapter.getView(i, itemView, mMeasureParent); itemView.measure(widthMeasureSpec, heightMeasureSpec); final int itemWidth = itemView.getMeasuredWidth(); - if (itemWidth >= mPopupMaxWidth) { - return mPopupMaxWidth; + int popupMaxWidth = Math.max(mBridgeContext.getMetrics().widthPixels / 2, + context.getResources().getDimensionPixelSize(R.dimen.config_prefDialogWidth)); + if (itemWidth >= popupMaxWidth) { + return popupMaxWidth; } else if (itemWidth > maxWidth) { maxWidth = itemWidth; } diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/CustomActionBarWrapper.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/CustomActionBarWrapper.java new file mode 100644 index 0000000..f3fc397 --- /dev/null +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/CustomActionBarWrapper.java @@ -0,0 +1,344 @@ +/* + * 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.layoutlib.bridge.bars; + +import com.android.annotations.NonNull; +import com.android.annotations.Nullable; +import com.android.ide.common.rendering.api.ActionBarCallback; +import com.android.ide.common.rendering.api.ActionBarCallback.HomeButtonStyle; +import com.android.ide.common.rendering.api.RenderResources; +import com.android.ide.common.rendering.api.ResourceValue; +import com.android.ide.common.rendering.api.SessionParams; +import com.android.internal.R; +import com.android.internal.app.ToolbarActionBar; +import com.android.internal.app.WindowDecorActionBar; +import com.android.internal.view.menu.MenuBuilder; +import com.android.internal.widget.ActionBarAccessor; +import com.android.internal.widget.ActionBarView; +import com.android.internal.widget.DecorToolbar; +import com.android.layoutlib.bridge.android.BridgeContext; +import com.android.layoutlib.bridge.impl.ResourceHelper; +import com.android.resources.ResourceType; + +import android.app.ActionBar; +import android.app.ActionBar.Tab; +import android.app.ActionBar.TabListener; +import android.app.FragmentTransaction; +import android.content.Context; +import android.content.res.Resources; +import android.graphics.drawable.Drawable; +import android.view.MenuInflater; +import android.view.View; +import android.view.ViewGroup; +import android.view.WindowCallback; +import android.widget.ActionMenuPresenter; +import android.widget.Toolbar; +import android.widget.Toolbar_Accessor; + +/** + * A common API to access {@link ToolbarActionBar} and {@link WindowDecorActionBar}. + */ +public abstract class CustomActionBarWrapper { + + @NonNull protected ActionBar mActionBar; + @NonNull protected SessionParams mParams; + @NonNull protected ActionBarCallback mCallback; + @NonNull protected BridgeContext mContext; + + /** + * Returns a wrapper around different implementations of the Action Bar to provide a common API. + * + * @param decorContent the top level view returned by inflating + * ?attr/windowActionBarFullscreenDecorLayout + */ + @NonNull + public static CustomActionBarWrapper getActionBarWrapper(@NonNull BridgeContext context, + @NonNull SessionParams params, @NonNull View decorContent) { + View view = decorContent.findViewById(R.id.action_bar); + if (view instanceof Toolbar) { + return new ToolbarWrapper(context, params, ((Toolbar) view) + ); + } else if (view instanceof ActionBarView) { + return new WindowActionBarWrapper(context, params, decorContent, ((ActionBarView) view) + ); + } else { + throw new IllegalStateException("Can't make an action bar out of " + + view.getClass().getSimpleName()); + } + } + + CustomActionBarWrapper(@NonNull BridgeContext context, @NonNull SessionParams params, + @NonNull ActionBar actionBar) { + mActionBar = actionBar; + mParams = params; + mCallback = params.getProjectCallback().getActionBarCallback(); + mContext = context; + } + + protected void setupActionBar() { + // Do the things that are common to all implementations. + RenderResources res = mContext.getRenderResources(); + + String title = mParams.getAppLabel(); + ResourceValue titleValue = res.findResValue(title, false); + if (titleValue != null && titleValue.getValue() != null) { + mActionBar.setTitle(titleValue.getValue()); + } else { + mActionBar.setTitle(title); + } + + String subTitle = mCallback.getSubTitle(); + if (subTitle != null) { + mActionBar.setSubtitle(subTitle); + } + + // Add show home as up icon. + if (mCallback.getHomeButtonStyle() == HomeButtonStyle.SHOW_HOME_AS_UP) { + mActionBar.setDisplayOptions(0xFF, ActionBar.DISPLAY_HOME_AS_UP); + } + } + + protected boolean isSplit() { + return getDecorToolbar().isSplit(); + } + + protected boolean isOverflowPopupNeeded() { + return mCallback.isOverflowPopupNeeded(); + } + + /** + * Gets the menus to add to the action bar from the callback, resolves them, inflates them and + * adds them to the action bar. + */ + protected void inflateMenus() { + MenuInflater inflater = new MenuInflater(getActionMenuContext()); + MenuBuilder menuBuilder = getMenuBuilder(); + for (String name : mCallback.getMenuIdNames()) { + if (mContext.getRenderResources().getProjectResource(ResourceType.MENU, name) + != null) { + int id = mContext.getProjectResourceValue(ResourceType.MENU, name, -1); + if (id > -1) { + inflater.inflate(id, menuBuilder); + } + } + } + } + + /** + * The context used for the ActionBar and the menus in the ActionBarView. + */ + @NonNull + protected Context getActionMenuContext() { + return mActionBar.getThemedContext(); + } + + /** + * The context used to inflate the popup menu. + */ + @NonNull + abstract Context getPopupContext(); + + /** + * The Menu in which to inflate the user's menus. + */ + @NonNull + abstract MenuBuilder getMenuBuilder(); + + @Nullable + abstract ActionMenuPresenter getActionMenuPresenter(); + + /** + * Framework's wrapper over two ActionBar implementations. + */ + @NonNull + abstract DecorToolbar getDecorToolbar(); + + // ---- The implementations ---- + + /** + * Material theme uses {@link Toolbar} as the action bar. This wrapper provides access to + * Toolbar using a common API. + */ + private static class ToolbarWrapper extends CustomActionBarWrapper { + + @NonNull + private final Toolbar mToolbar; // This is the view. + + ToolbarWrapper(@NonNull BridgeContext context, @NonNull SessionParams params, + @NonNull Toolbar toolbar) { + super(context, params, new ToolbarActionBar(toolbar, "", new WindowCallback()) + ); + mToolbar = toolbar; + } + + @Override + protected void inflateMenus() { + super.inflateMenus(); + // Inflating the menus doesn't initialize the ActionMenuPresenter. Setting a fake menu + // and then setting it back does the trick. + MenuBuilder menu = getMenuBuilder(); + DecorToolbar decorToolbar = getDecorToolbar(); + decorToolbar.setMenu(new MenuBuilder(getActionMenuContext()), null); + decorToolbar.setMenu(menu, null); + } + + @NonNull + @Override + Context getPopupContext() { + return Toolbar_Accessor.getPopupContext(mToolbar); + } + + @NonNull + @Override + MenuBuilder getMenuBuilder() { + return (MenuBuilder) mToolbar.getMenu(); + } + + @Nullable + @Override + ActionMenuPresenter getActionMenuPresenter() { + return Toolbar_Accessor.getActionMenuPresenter(mToolbar); + } + + @NonNull + @Override + DecorToolbar getDecorToolbar() { + return mToolbar.getWrapper(); + } + } + + /** + * Holo theme uses {@link WindowDecorActionBar} as the action bar. This wrapper provides + * access to it using a common API. + */ + private static class WindowActionBarWrapper extends CustomActionBarWrapper{ + + @NonNull + private final WindowDecorActionBar mActionBar; + private final ActionBarView mActionBarView; + private MenuBuilder mMenuBuilder; + + public WindowActionBarWrapper(@NonNull BridgeContext context, @NonNull SessionParams params, + @NonNull View decorContentRoot, @NonNull ActionBarView actionBarView) { + super(context, params, new WindowDecorActionBar(decorContentRoot) + ); + mActionBarView = actionBarView; + mActionBar = ((WindowDecorActionBar) super.mActionBar); + } + + @Override + protected void setupActionBar() { + super.setupActionBar(); + + // Set the navigation mode. + int navMode = mCallback.getNavigationMode(); + mActionBar.setNavigationMode(navMode); + //noinspection deprecation + if (navMode == ActionBar.NAVIGATION_MODE_TABS) { + setupTabs(3); + } + + String icon = mParams.getAppIcon(); + // If the action bar style doesn't specify an icon, set the icon obtained from the + // session params. + if (!mActionBar.hasIcon() && icon != null) { + Drawable iconDrawable = getDrawable(icon, false); + if (iconDrawable != null) { + mActionBar.setIcon(iconDrawable); + } + } + + // Set action bar to be split, if needed. + ViewGroup splitView = (ViewGroup) mActionBarView.findViewById(R.id.split_action_bar); + if (splitView != null) { + mActionBarView.setSplitView(splitView); + Resources res = mContext.getResources(); + boolean split = res.getBoolean(R.bool.split_action_bar_is_narrow) + && mCallback.getSplitActionBarWhenNarrow(); + mActionBarView.setSplitToolbar(split); + } + } + + @Override + protected void inflateMenus() { + super.inflateMenus(); + // The super implementation doesn't set the menu on the view. Set it here. + mActionBarView.setMenu(getMenuBuilder(), null); + } + + @NonNull + @Override + Context getPopupContext() { + return getActionMenuContext(); + } + + @NonNull + @Override + MenuBuilder getMenuBuilder() { + if (mMenuBuilder == null) { + mMenuBuilder = new MenuBuilder(getActionMenuContext()); + } + return mMenuBuilder; + } + + @Nullable + @Override + ActionMenuPresenter getActionMenuPresenter() { + return ActionBarAccessor.getActionMenuPresenter(mActionBarView); + } + + @NonNull + @Override + ActionBarView getDecorToolbar() { + return mActionBarView; + } + + // TODO: Use an adapter, like List View to set up tabs. + @SuppressWarnings("deprecation") // For Tab + private void setupTabs(int num) { + for (int i = 1; i <= num; i++) { + Tab tab = mActionBar.newTab().setText("Tab" + i).setTabListener(new TabListener() { + @Override + public void onTabUnselected(Tab t, FragmentTransaction ft) { + // pass + } + @Override + public void onTabSelected(Tab t, FragmentTransaction ft) { + // pass + } + @Override + public void onTabReselected(Tab t, FragmentTransaction ft) { + // pass + } + }); + mActionBar.addTab(tab); + } + } + + @Nullable + private Drawable getDrawable(@NonNull String name, boolean isFramework) { + RenderResources res = mContext.getRenderResources(); + ResourceValue value = res.findResValue(name, isFramework); + value = res.resolveResValue(value); + if (value != null) { + return ResourceHelper.getDrawable(value, mContext); + } + return null; + } + + } +} diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderSessionImpl.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderSessionImpl.java index b8dce70..a2eed9a 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderSessionImpl.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderSessionImpl.java @@ -353,8 +353,7 @@ public class RenderSessionImpl extends RenderAction<SessionParams> { // if the theme says no title/action bar, then the size will be 0 if (mActionBarSize > 0) { - ActionBarLayout actionBar = createActionBar(context, params); - backgroundLayout.addView(actionBar); + ActionBarLayout actionBar = createActionBar(context, params, backgroundLayout); actionBar.createMenuPopup(); mContentRoot = actionBar.getContentRoot(); } else if (mTitleBarSize > 0) { @@ -1624,11 +1623,9 @@ public class RenderSessionImpl extends RenderAction<SessionParams> { /** * Creates the action bar. Also queries the project callback for missing information. */ - private ActionBarLayout createActionBar(BridgeContext context, SessionParams params) { - ActionBarLayout actionBar = new ActionBarLayout(context, params); - actionBar.setLayoutParams(new LinearLayout.LayoutParams( - LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT)); - return actionBar; + private ActionBarLayout createActionBar(BridgeContext context, SessionParams params, + ViewGroup parentView) { + return new ActionBarLayout(context, params, parentView); } public BufferedImage getImage() { |