diff options
-rw-r--r-- | tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/AppCompatActionBar.java | 166 | ||||
-rw-r--r-- | tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/BridgeActionBar.java | 159 | ||||
-rw-r--r-- | tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/FrameworkActionBar.java (renamed from tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/ActionBarLayout.java) | 122 | ||||
-rw-r--r-- | tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/FrameworkActionBarWrapper.java (renamed from tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/CustomActionBarWrapper.java) | 97 | ||||
-rw-r--r-- | tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderSessionImpl.java | 32 |
5 files changed, 452 insertions, 124 deletions
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/AppCompatActionBar.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/AppCompatActionBar.java new file mode 100644 index 0000000..e5023b8 --- /dev/null +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/AppCompatActionBar.java @@ -0,0 +1,166 @@ +/* + * Copyright (C) 2015 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.RenderResources; +import com.android.ide.common.rendering.api.ResourceValue; +import com.android.ide.common.rendering.api.SessionParams; +import com.android.layoutlib.bridge.android.BridgeContext; +import com.android.layoutlib.bridge.impl.ResourceHelper; +import com.android.resources.ResourceType; + +import android.graphics.drawable.Drawable; +import android.view.View; +import android.view.ViewGroup; +import android.widget.FrameLayout; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; + + +/** + * Assumes that the AppCompat library is present in the project's classpath and creates an + * actionbar around it. + */ +public class AppCompatActionBar extends BridgeActionBar { + + private Object mWindowDecorActionBar; + private static final String WINDOW_ACTION_BAR_CLASS = "android.support.v7.internal.app.WindowDecorActionBar"; + private Class<?> mWindowActionBarClass; + + /** + * Inflate the action bar and attach it to {@code parentView} + */ + public AppCompatActionBar(@NonNull BridgeContext context, @NonNull SessionParams params, + @NonNull ViewGroup parentView) { + super(context, params, parentView); + int contentRootId = context.getProjectResourceValue(ResourceType.ID, + "action_bar_activity_content", 0); + View contentView = getDecorContent().findViewById(contentRootId); + if (contentView != null) { + assert contentView instanceof FrameLayout; + setContentRoot(((FrameLayout) contentView)); + } else { + // Something went wrong. Create a new FrameLayout in the enclosing layout. + FrameLayout contentRoot = new FrameLayout(context); + setMatchParent(contentRoot); + mEnclosingLayout.addView(contentRoot); + setContentRoot(contentRoot); + } + try { + Class[] constructorParams = {View.class}; + Object[] constructorArgs = {getDecorContent()}; + mWindowDecorActionBar = params.getProjectCallback().loadView(WINDOW_ACTION_BAR_CLASS, + constructorParams, constructorArgs); + + mWindowActionBarClass = mWindowDecorActionBar == null ? null : + mWindowDecorActionBar.getClass(); + setupActionBar(); + } catch (Exception e) { + e.printStackTrace(); + } + } + + @Override + protected ResourceValue getLayoutResource(BridgeContext context) { + // We always assume that the app has requested the action bar. + return context.getRenderResources().getProjectResource(ResourceType.LAYOUT, + "abc_screen_toolbar"); + } + + @Override + protected void setTitle(CharSequence title) { + if (title != null && mWindowDecorActionBar != null) { + Method setTitle = getMethod(mWindowActionBarClass, "setTitle", CharSequence.class); + invoke(setTitle, mWindowDecorActionBar, title); + } + } + + @Override + protected void setSubtitle(CharSequence subtitle) { + if (subtitle != null && mWindowDecorActionBar != null) { + Method setSubtitle = getMethod(mWindowActionBarClass, "setSubtitle", CharSequence.class); + invoke(setSubtitle, mWindowDecorActionBar, subtitle); + } + } + + @Override + protected void setIcon(String icon) { + // Do this only if the action bar doesn't already have an icon. + if (icon != null && !icon.isEmpty() && mWindowDecorActionBar != null) { + if (((Boolean) invoke(getMethod(mWindowActionBarClass, "hasIcon"), mWindowDecorActionBar) + )) { + Drawable iconDrawable = getDrawable(icon, false); + if (iconDrawable != null) { + Method setIcon = getMethod(mWindowActionBarClass, "setIcon", Drawable.class); + invoke(setIcon, mWindowDecorActionBar, iconDrawable); + } + } + } + } + + @Override + protected void setHomeAsUp(boolean homeAsUp) { + if (mWindowDecorActionBar != null) { + Method setHomeAsUp = getMethod(mWindowActionBarClass, + "setDefaultDisplayHomeAsUpEnabled", boolean.class); + invoke(setHomeAsUp, mWindowDecorActionBar, homeAsUp); + } + } + + @Override + public void createMenuPopup() { + // it's hard to addd menus to appcompat's actionbar, since it'll use a lot of reflection. + // so we skip it for now. + } + + @Nullable + private static Method getMethod(Class<?> owner, String name, Class<?>... parameterTypes) { + try { + return owner == null ? null : owner.getMethod(name, parameterTypes); + } catch (NoSuchMethodException e) { + e.printStackTrace(); + } + return null; + } + + @Nullable + private static Object invoke(Method method, Object owner, Object... args) { + try { + return method == null ? null : method.invoke(owner, args); + } catch (InvocationTargetException e) { + e.printStackTrace(); + } catch (IllegalAccessException e) { + e.printStackTrace(); + } + return null; + } + + // TODO: this is duplicated from FrameworkActionBarWrapper$WindowActionBarWrapper + @Nullable + private Drawable getDrawable(@NonNull String name, boolean isFramework) { + RenderResources res = mBridgeContext.getRenderResources(); + ResourceValue value = res.findResValue(name, isFramework); + value = res.resolveResValue(value); + if (value != null) { + return ResourceHelper.getDrawable(value, mBridgeContext); + } + return null; + } +} diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/BridgeActionBar.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/BridgeActionBar.java new file mode 100644 index 0000000..b29d25f --- /dev/null +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/BridgeActionBar.java @@ -0,0 +1,159 @@ +/* + * Copyright (C) 2015 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.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.layoutlib.bridge.android.BridgeContext; + +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.view.ViewGroup.LayoutParams; +import android.widget.FrameLayout; +import android.widget.RelativeLayout; + +/** + * An abstraction over two implementations of the ActionBar - framework and appcompat. + */ +public abstract class BridgeActionBar { + // Store a reference to the context so that we don't have to cast it repeatedly. + @NonNull protected final BridgeContext mBridgeContext; + @NonNull protected final SessionParams mParams; + // A Layout that contains the inflated action bar. The menu popup is added to this layout. + @NonNull protected final ViewGroup mEnclosingLayout; + + private final View mDecorContent; + private final ActionBarCallback mCallback; + + @NonNull private FrameLayout mContentRoot; + + public BridgeActionBar(@NonNull BridgeContext context, @NonNull SessionParams params, + @NonNull ViewGroup parentView) { + mBridgeContext = context; + mParams = params; + mCallback = params.getProjectCallback().getActionBarCallback(); + ResourceValue layoutName = getLayoutResource(context); + if (layoutName == null) { + throw new RuntimeException("Unable to find the layout for Action Bar."); + } + int layoutId; + if (layoutName.isFramework()) { + layoutId = context.getFrameworkResourceValue(layoutName.getResourceType(), + layoutName.getName(), 0); + } else { + layoutId = context.getProjectResourceValue(layoutName.getResourceType(), + layoutName.getName(), 0); + + } + if (layoutId == 0) { + throw new RuntimeException( + String.format("Unable to resolve attribute \"%1$s\" of type \"%2$s\"", + layoutName.getName(), layoutName.getResourceType())); + } + if (mCallback.isOverflowPopupNeeded()) { + // Create a RelativeLayout around the action bar, to which the overflow popup may be + // added. + mEnclosingLayout = new RelativeLayout(mBridgeContext); + setMatchParent(mEnclosingLayout); + parentView.addView(mEnclosingLayout); + } else { + mEnclosingLayout = parentView; + } + + // Inflate action bar layout. + mDecorContent = LayoutInflater.from(context).inflate(layoutId, mEnclosingLayout, true); + + } + + /** + * Returns the Layout Resource that should be used to inflate the action bar. This layout + * should cover the complete screen, and have a FrameLayout included, where the content will + * be inflated. + */ + protected abstract ResourceValue getLayoutResource(BridgeContext context); + + protected void setContentRoot(FrameLayout contentRoot) { + mContentRoot = contentRoot; + } + + @NonNull + public FrameLayout getContentRoot() { + return mContentRoot; + } + + /** + * Returns the view inflated. This should contain both the ActionBar and the app content in it. + */ + protected View getDecorContent() { + return mDecorContent; + } + + /** Setup things like the title, subtitle, icon etc. */ + protected void setupActionBar() { + setTitle(); + setSutTitle(); + setIcon(); + setHomeAsUp(mCallback.getHomeButtonStyle() == HomeButtonStyle.SHOW_HOME_AS_UP); + } + + protected abstract void setTitle(CharSequence title); + protected abstract void setSubtitle(CharSequence subtitle); + protected abstract void setIcon(String icon); + protected abstract void setHomeAsUp(boolean homeAsUp); + + private void setTitle() { + RenderResources res = mBridgeContext.getRenderResources(); + + String title = mParams.getAppLabel(); + ResourceValue titleValue = res.findResValue(title, false); + if (titleValue != null && titleValue.getValue() != null) { + setTitle(titleValue.getValue()); + } else { + setTitle(title); + } + } + + private void setSutTitle() { + String subTitle = mCallback.getSubTitle(); + if (subTitle != null) { + setSubtitle(subTitle); + } + } + + private void setIcon() { + String appIcon = mParams.getAppIcon(); + if (appIcon != null) { + setIcon(appIcon); + } + } + + public abstract void createMenuPopup(); + + public ActionBarCallback getCallBack() { + return mCallback; + } + + protected static void setMatchParent(View view) { + view.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, + LayoutParams.MATCH_PARENT)); + } +} diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/ActionBarLayout.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/FrameworkActionBar.java index 2ff8d37..a1c9065 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/ActionBarLayout.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/FrameworkActionBar.java @@ -31,7 +31,7 @@ import android.content.Context; import android.content.res.TypedArray; import android.util.DisplayMetrics; import android.util.TypedValue; -import android.view.LayoutInflater; +import android.view.InflateException; import android.view.View; import android.view.View.MeasureSpec; import android.view.ViewGroup; @@ -44,70 +44,30 @@ import android.widget.RelativeLayout; import java.util.ArrayList; -public class ActionBarLayout { +/** + * Creates the ActionBar as done by the framework. + */ +public class FrameworkActionBar extends BridgeActionBar { 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 FrameLayout mContentRoot; + @NonNull private FrameworkActionBarWrapper mActionBar; // A fake parent for measuring views. - @Nullable - private ViewGroup mMeasureParent; - - // A Layout that contains the inflated action bar. The menu popup is added to this layout. - @NonNull - private final RelativeLayout mEnclosingLayout; + @Nullable private ViewGroup mMeasureParent; /** * Inflate the action bar and attach it to {@code parentView} */ - public ActionBarLayout(@NonNull BridgeContext context, @NonNull SessionParams params, + public FrameworkActionBar(@NonNull BridgeContext context, @NonNull SessionParams params, @NonNull ViewGroup parentView) { + super(context, params, parentView); - mBridgeContext = context; - - 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()); - } - 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()); - } - } - if (layoutId == 0) { - throw new RuntimeException(error); - } - // Create a RelativeLayout to hold the action bar. The layout is needed so that we may - // add the menu popup to it. - mEnclosingLayout = new RelativeLayout(mBridgeContext); - setMatchParent(mEnclosingLayout); - parentView.addView(mEnclosingLayout); - - // Inflate action bar layout. - View decorContent = LayoutInflater.from(context).inflate(layoutId, mEnclosingLayout, true); + View decorContent = getDecorContent(); - mActionBar = CustomActionBarWrapper.getActionBarWrapper(context, params, decorContent); + mActionBar = FrameworkActionBarWrapper.getActionBarWrapper(context, getCallBack(), + decorContent); FrameLayout contentRoot = (FrameLayout) mEnclosingLayout.findViewById(android.R.id.content); @@ -117,27 +77,62 @@ public class ActionBarLayout { contentRoot = new FrameLayout(context); setMatchParent(contentRoot); mEnclosingLayout.addView(contentRoot); - mContentRoot = contentRoot; + setContentRoot(contentRoot); } else { - mContentRoot = contentRoot; - mActionBar.setupActionBar(); + setContentRoot(contentRoot); + setupActionBar(); mActionBar.inflateMenus(); } } - private void setMatchParent(View view) { - view.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, - LayoutParams.MATCH_PARENT)); + @Override + protected ResourceValue getLayoutResource(BridgeContext context) { + 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 (layoutName == null) { + throw new InflateException("Unable to find action bar layout (" + LAYOUT_ATTR_NAME + + ") in the current theme."); + } + return layoutName; + } + + @Override + protected void setupActionBar() { + super.setupActionBar(); + mActionBar.setupActionBar(); + } + + @Override + protected void setHomeAsUp(boolean homeAsUp) { + mActionBar.setHomeAsUp(homeAsUp); + } + + @Override + protected void setTitle(CharSequence title) { + mActionBar.setTitle(title); + } + + @Override + protected void setSubtitle(CharSequence subtitle) { + mActionBar.setSubTitle(subtitle); + } + + @Override + protected void setIcon(String icon) { + mActionBar.setIcon(icon); } /** * Creates a Popup and adds it to the content frame. It also adds another {@link FrameLayout} to * the content frame which shall serve as the new content root. */ + @Override public void createMenuPopup() { - assert mEnclosingLayout.getChildCount() == 1 - : "Action Bar Menus have already been created."; - if (!isOverflowPopupNeeded()) { return; } @@ -193,11 +188,6 @@ public class ActionBarLayout { return needed; } - @NonNull - public FrameLayout getContentRoot() { - return mContentRoot; - } - // Copied from com.android.internal.view.menu.MenuPopHelper.measureContentWidth() private int measureContentWidth(@NonNull ListAdapter adapter) { // Menus don't tend to be long, so this is more sane than it looks. diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/CustomActionBarWrapper.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/FrameworkActionBarWrapper.java index 6db722e..44c2cd8 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/CustomActionBarWrapper.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/FrameworkActionBarWrapper.java @@ -19,10 +19,8 @@ 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; @@ -54,10 +52,9 @@ import static com.android.resources.ResourceType.MENU; /** * A common API to access {@link ToolbarActionBar} and {@link WindowDecorActionBar}. */ -public abstract class CustomActionBarWrapper { +public abstract class FrameworkActionBarWrapper { @NonNull protected ActionBar mActionBar; - @NonNull protected SessionParams mParams; @NonNull protected ActionBarCallback mCallback; @NonNull protected BridgeContext mContext; @@ -68,49 +65,48 @@ public abstract class CustomActionBarWrapper { * ?attr/windowActionBarFullscreenDecorLayout */ @NonNull - public static CustomActionBarWrapper getActionBarWrapper(@NonNull BridgeContext context, - @NonNull SessionParams params, @NonNull View decorContent) { + public static FrameworkActionBarWrapper getActionBarWrapper(@NonNull BridgeContext context, + @NonNull ActionBarCallback callback, @NonNull View decorContent) { View view = decorContent.findViewById(R.id.action_bar); if (view instanceof Toolbar) { - return new ToolbarWrapper(context, params, ((Toolbar) view)); + return new ToolbarWrapper(context, callback, (Toolbar) view); } else if (view instanceof ActionBarView) { - return new WindowActionBarWrapper(context, params, decorContent, - ((ActionBarView) view)); + return new WindowActionBarWrapper(context, callback, 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, + FrameworkActionBarWrapper(@NonNull BridgeContext context, ActionBarCallback callback, @NonNull ActionBar actionBar) { mActionBar = actionBar; - mParams = params; - mCallback = params.getProjectCallback().getActionBarCallback(); + mCallback = callback; mContext = context; } + /** A call to setup any custom properties. */ protected void setupActionBar() { - // Do the things that are common to all implementations. - RenderResources res = mContext.getRenderResources(); + // Nothing to do here. + } - String title = mParams.getAppLabel(); - ResourceValue titleValue = res.findResValue(title, false); - if (titleValue != null && titleValue.getValue() != null) { - mActionBar.setTitle(titleValue.getValue()); - } else { - mActionBar.setTitle(title); - } + public void setTitle(CharSequence title) { + mActionBar.setTitle(title); + } - String subTitle = mCallback.getSubTitle(); + public void setSubTitle(CharSequence subTitle) { 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); - } + public void setHomeAsUp(boolean homeAsUp) { + mActionBar.setDisplayHomeAsUpEnabled(homeAsUp); + } + + public void setIcon(String icon) { + // Nothing to do. } protected boolean isSplit() { @@ -186,15 +182,14 @@ public abstract class CustomActionBarWrapper { * 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 { + private static class ToolbarWrapper extends FrameworkActionBarWrapper { @NonNull private final Toolbar mToolbar; // This is the view. - ToolbarWrapper(@NonNull BridgeContext context, @NonNull SessionParams params, + ToolbarWrapper(@NonNull BridgeContext context, @NonNull ActionBarCallback callback, @NonNull Toolbar toolbar) { - super(context, params, new ToolbarActionBar(toolbar, "", new WindowCallback()) - ); + super(context, callback, new ToolbarActionBar(toolbar, "", new WindowCallback())); mToolbar = toolbar; } @@ -248,19 +243,17 @@ public abstract class CustomActionBarWrapper { * 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 { + private static class WindowActionBarWrapper extends FrameworkActionBarWrapper { - @NonNull - private final WindowDecorActionBar mActionBar; - @NonNull - private final ActionBarView mActionBarView; - @NonNull - private final View mDecorContentRoot; + @NonNull private final WindowDecorActionBar mActionBar; + @NonNull private final ActionBarView mActionBarView; + @NonNull private final View mDecorContentRoot; private MenuBuilder mMenuBuilder; - public WindowActionBarWrapper(@NonNull BridgeContext context, @NonNull SessionParams params, - @NonNull View decorContentRoot, @NonNull ActionBarView actionBarView) { - super(context, params, new WindowDecorActionBar(decorContentRoot)); + public WindowActionBarWrapper(@NonNull BridgeContext context, + @NonNull ActionBarCallback callback, @NonNull View decorContentRoot, + @NonNull ActionBarView actionBarView) { + super(context, callback, new WindowDecorActionBar(decorContentRoot)); mActionBarView = actionBarView; mActionBar = ((WindowDecorActionBar) super.mActionBar); mDecorContentRoot = decorContentRoot; @@ -268,7 +261,6 @@ public abstract class CustomActionBarWrapper { @Override protected void setupActionBar() { - super.setupActionBar(); // Set the navigation mode. int navMode = mCallback.getNavigationMode(); @@ -278,16 +270,6 @@ public abstract class CustomActionBarWrapper { 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) mDecorContentRoot.findViewById(R.id.split_action_bar); if (splitView != null) { @@ -300,6 +282,17 @@ public abstract class CustomActionBarWrapper { } @Override + public void setIcon(String icon) { + // Set the icon only if the action bar doesn't specify an icon. + if (!mActionBar.hasIcon() && icon != null) { + Drawable iconDrawable = getDrawable(icon, false); + if (iconDrawable != null) { + mActionBar.setIcon(iconDrawable); + } + } + } + + @Override protected void inflateMenus() { super.inflateMenus(); // The super implementation doesn't set the menu on the view. Set it here. @@ -340,7 +333,7 @@ public abstract class CustomActionBarWrapper { @Override int getMenuPopupMargin() { - return -ActionBarLayout.getPixelValue("10dp", mContext.getMetrics()); + return -FrameworkActionBar.getPixelValue("10dp", mContext.getMetrics()); } // TODO: Use an adapter, like List View to set up tabs. 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 fd976af..84ee914 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 @@ -51,11 +51,13 @@ import com.android.layoutlib.bridge.android.BridgeContext; import com.android.layoutlib.bridge.android.BridgeLayoutParamsMapAttributes; import com.android.layoutlib.bridge.android.BridgeXmlBlockParser; import com.android.layoutlib.bridge.android.SessionParamsFlags; +import com.android.layoutlib.bridge.bars.BridgeActionBar; +import com.android.layoutlib.bridge.bars.AppCompatActionBar; import com.android.layoutlib.bridge.bars.Config; import com.android.layoutlib.bridge.bars.NavigationBar; import com.android.layoutlib.bridge.bars.StatusBar; import com.android.layoutlib.bridge.bars.TitleBar; -import com.android.layoutlib.bridge.bars.ActionBarLayout; +import com.android.layoutlib.bridge.bars.FrameworkActionBar; import com.android.layoutlib.bridge.impl.binding.FakeAdapter; import com.android.layoutlib.bridge.impl.binding.FakeExpandableAdapter; import com.android.resources.Density; @@ -354,7 +356,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); + BridgeActionBar actionBar = createActionBar(context, params, backgroundLayout); actionBar.createMenuPopup(); mContentRoot = actionBar.getContentRoot(); } else if (mTitleBarSize > 0) { @@ -1190,8 +1192,22 @@ public class RenderSessionImpl extends RenderAction<SessionParams> { // android.support.v7.app.ActionBarActivity, and not care about the theme name at all. if (mIsThemeAppCompat == null) { StyleResourceValue defaultTheme = resources.getDefaultTheme(); - StyleResourceValue val = resources.getStyle("Theme.AppCompat", false); - mIsThemeAppCompat = defaultTheme == val || resources.themeIsParentOf(val, defaultTheme); + // We can't simply check for parent using resources.themeIsParentOf() since the + // inheritance structure isn't really what one would expect. The first common parent + // between Theme.AppCompat.Light and Theme.AppCompat is Theme.Material (for v21). + boolean isThemeAppCompat = false; + for (int i = 0; i < 50; i++) { + // for loop ensures that we don't run into cyclic theme inheritance. + if (defaultTheme.getName().startsWith("Theme.AppCompat")) { + isThemeAppCompat = true; + break; + } + defaultTheme = resources.getParent(defaultTheme); + if (defaultTheme == null) { + break; + } + } + mIsThemeAppCompat = isThemeAppCompat; } return mIsThemeAppCompat; } @@ -1647,9 +1663,13 @@ 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, + private BridgeActionBar createActionBar(BridgeContext context, SessionParams params, ViewGroup parentView) { - return new ActionBarLayout(context, params, parentView); + if (mIsThemeAppCompat == Boolean.TRUE) { + return new AppCompatActionBar(context, params, parentView); + } else { + return new FrameworkActionBar(context, params, parentView); + } } public BufferedImage getImage() { |