am 3d5535cf: Merge "Limited AppCompat support" into lmp-dev automerge: 7789447

automerge: 580937f

* commit '580937fc45bc5ef97e45f1eaf67945a07c5ce41f':
  Limited AppCompat support
This commit is contained in:
Deepanshu Gupta
2015-01-31 03:59:37 +00:00
committed by android-build-merger
5 changed files with 452 additions and 124 deletions

View File

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

View File

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

View File

@ -31,7 +31,7 @@ import android.content.Context;
import android.content.res.TypedArray; import android.content.res.TypedArray;
import android.util.DisplayMetrics; import android.util.DisplayMetrics;
import android.util.TypedValue; import android.util.TypedValue;
import android.view.LayoutInflater; import android.view.InflateException;
import android.view.View; import android.view.View;
import android.view.View.MeasureSpec; import android.view.View.MeasureSpec;
import android.view.ViewGroup; import android.view.ViewGroup;
@ -44,70 +44,30 @@ import android.widget.RelativeLayout;
import java.util.ArrayList; 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"; private static final String LAYOUT_ATTR_NAME = "windowActionBarFullscreenDecorLayout";
// The Action Bar // The Action Bar
@NonNull @NonNull private FrameworkActionBarWrapper mActionBar;
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;
// A fake parent for measuring views. // A fake parent for measuring views.
@Nullable @Nullable private ViewGroup mMeasureParent;
private ViewGroup mMeasureParent;
// A Layout that contains the inflated action bar. The menu popup is added to this layout.
@NonNull
private final RelativeLayout mEnclosingLayout;
/** /**
* Inflate the action bar and attach it to {@code parentView} * 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) { @NonNull ViewGroup parentView) {
super(context, params, parentView);
mBridgeContext = context; View decorContent = getDecorContent();
ResourceValue layoutName = context.getRenderResources() mActionBar = FrameworkActionBarWrapper.getActionBarWrapper(context, getCallBack(),
.findItemInTheme(LAYOUT_ATTR_NAME, true); decorContent);
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);
mActionBar = CustomActionBarWrapper.getActionBarWrapper(context, params, decorContent);
FrameLayout contentRoot = (FrameLayout) mEnclosingLayout.findViewById(android.R.id.content); FrameLayout contentRoot = (FrameLayout) mEnclosingLayout.findViewById(android.R.id.content);
@ -117,27 +77,62 @@ public class ActionBarLayout {
contentRoot = new FrameLayout(context); contentRoot = new FrameLayout(context);
setMatchParent(contentRoot); setMatchParent(contentRoot);
mEnclosingLayout.addView(contentRoot); mEnclosingLayout.addView(contentRoot);
mContentRoot = contentRoot; setContentRoot(contentRoot);
} else { } else {
mContentRoot = contentRoot; setContentRoot(contentRoot);
mActionBar.setupActionBar(); setupActionBar();
mActionBar.inflateMenus(); mActionBar.inflateMenus();
} }
} }
private void setMatchParent(View view) { @Override
view.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, protected ResourceValue getLayoutResource(BridgeContext context) {
LayoutParams.MATCH_PARENT)); 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 * 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. * the content frame which shall serve as the new content root.
*/ */
@Override
public void createMenuPopup() { public void createMenuPopup() {
assert mEnclosingLayout.getChildCount() == 1
: "Action Bar Menus have already been created.";
if (!isOverflowPopupNeeded()) { if (!isOverflowPopupNeeded()) {
return; return;
} }
@ -193,11 +188,6 @@ public class ActionBarLayout {
return needed; return needed;
} }
@NonNull
public FrameLayout getContentRoot() {
return mContentRoot;
}
// Copied from com.android.internal.view.menu.MenuPopHelper.measureContentWidth() // Copied from com.android.internal.view.menu.MenuPopHelper.measureContentWidth()
private int measureContentWidth(@NonNull ListAdapter adapter) { private int measureContentWidth(@NonNull ListAdapter adapter) {
// Menus don't tend to be long, so this is more sane than it looks. // Menus don't tend to be long, so this is more sane than it looks.

View File

@ -19,10 +19,8 @@ package com.android.layoutlib.bridge.bars;
import com.android.annotations.NonNull; import com.android.annotations.NonNull;
import com.android.annotations.Nullable; import com.android.annotations.Nullable;
import com.android.ide.common.rendering.api.ActionBarCallback; 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.RenderResources;
import com.android.ide.common.rendering.api.ResourceValue; 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.R;
import com.android.internal.app.ToolbarActionBar; import com.android.internal.app.ToolbarActionBar;
import com.android.internal.app.WindowDecorActionBar; 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}. * A common API to access {@link ToolbarActionBar} and {@link WindowDecorActionBar}.
*/ */
public abstract class CustomActionBarWrapper { public abstract class FrameworkActionBarWrapper {
@NonNull protected ActionBar mActionBar; @NonNull protected ActionBar mActionBar;
@NonNull protected SessionParams mParams;
@NonNull protected ActionBarCallback mCallback; @NonNull protected ActionBarCallback mCallback;
@NonNull protected BridgeContext mContext; @NonNull protected BridgeContext mContext;
@ -68,49 +65,48 @@ public abstract class CustomActionBarWrapper {
* ?attr/windowActionBarFullscreenDecorLayout * ?attr/windowActionBarFullscreenDecorLayout
*/ */
@NonNull @NonNull
public static CustomActionBarWrapper getActionBarWrapper(@NonNull BridgeContext context, public static FrameworkActionBarWrapper getActionBarWrapper(@NonNull BridgeContext context,
@NonNull SessionParams params, @NonNull View decorContent) { @NonNull ActionBarCallback callback, @NonNull View decorContent) {
View view = decorContent.findViewById(R.id.action_bar); View view = decorContent.findViewById(R.id.action_bar);
if (view instanceof Toolbar) { if (view instanceof Toolbar) {
return new ToolbarWrapper(context, params, ((Toolbar) view)); return new ToolbarWrapper(context, callback, (Toolbar) view);
} else if (view instanceof ActionBarView) { } else if (view instanceof ActionBarView) {
return new WindowActionBarWrapper(context, params, decorContent, return new WindowActionBarWrapper(context, callback, decorContent,
((ActionBarView) view)); (ActionBarView) view);
} else { } else {
throw new IllegalStateException("Can't make an action bar out of " + throw new IllegalStateException("Can't make an action bar out of " +
view.getClass().getSimpleName()); view.getClass().getSimpleName());
} }
} }
CustomActionBarWrapper(@NonNull BridgeContext context, @NonNull SessionParams params, FrameworkActionBarWrapper(@NonNull BridgeContext context, ActionBarCallback callback,
@NonNull ActionBar actionBar) { @NonNull ActionBar actionBar) {
mActionBar = actionBar; mActionBar = actionBar;
mParams = params; mCallback = callback;
mCallback = params.getProjectCallback().getActionBarCallback();
mContext = context; mContext = context;
} }
/** A call to setup any custom properties. */
protected void setupActionBar() { protected void setupActionBar() {
// Do the things that are common to all implementations. // Nothing to do here.
RenderResources res = mContext.getRenderResources(); }
String title = mParams.getAppLabel(); public void setTitle(CharSequence title) {
ResourceValue titleValue = res.findResValue(title, false); mActionBar.setTitle(title);
if (titleValue != null && titleValue.getValue() != null) { }
mActionBar.setTitle(titleValue.getValue());
} else {
mActionBar.setTitle(title);
}
String subTitle = mCallback.getSubTitle(); public void setSubTitle(CharSequence subTitle) {
if (subTitle != null) { if (subTitle != null) {
mActionBar.setSubtitle(subTitle); mActionBar.setSubtitle(subTitle);
} }
}
// Add show home as up icon. public void setHomeAsUp(boolean homeAsUp) {
if (mCallback.getHomeButtonStyle() == HomeButtonStyle.SHOW_HOME_AS_UP) { mActionBar.setDisplayHomeAsUpEnabled(homeAsUp);
mActionBar.setDisplayOptions(0xFF, ActionBar.DISPLAY_HOME_AS_UP); }
}
public void setIcon(String icon) {
// Nothing to do.
} }
protected boolean isSplit() { 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 * Material theme uses {@link Toolbar} as the action bar. This wrapper provides access to
* Toolbar using a common API. * Toolbar using a common API.
*/ */
private static class ToolbarWrapper extends CustomActionBarWrapper { private static class ToolbarWrapper extends FrameworkActionBarWrapper {
@NonNull @NonNull
private final Toolbar mToolbar; // This is the view. 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) { @NonNull Toolbar toolbar) {
super(context, params, new ToolbarActionBar(toolbar, "", new WindowCallback()) super(context, callback, new ToolbarActionBar(toolbar, "", new WindowCallback()));
);
mToolbar = toolbar; mToolbar = toolbar;
} }
@ -248,19 +243,17 @@ public abstract class CustomActionBarWrapper {
* Holo theme uses {@link WindowDecorActionBar} as the action bar. This wrapper provides * Holo theme uses {@link WindowDecorActionBar} as the action bar. This wrapper provides
* access to it using a common API. * access to it using a common API.
*/ */
private static class WindowActionBarWrapper extends CustomActionBarWrapper { private static class WindowActionBarWrapper extends FrameworkActionBarWrapper {
@NonNull @NonNull private final WindowDecorActionBar mActionBar;
private final WindowDecorActionBar mActionBar; @NonNull private final ActionBarView mActionBarView;
@NonNull @NonNull private final View mDecorContentRoot;
private final ActionBarView mActionBarView;
@NonNull
private final View mDecorContentRoot;
private MenuBuilder mMenuBuilder; private MenuBuilder mMenuBuilder;
public WindowActionBarWrapper(@NonNull BridgeContext context, @NonNull SessionParams params, public WindowActionBarWrapper(@NonNull BridgeContext context,
@NonNull View decorContentRoot, @NonNull ActionBarView actionBarView) { @NonNull ActionBarCallback callback, @NonNull View decorContentRoot,
super(context, params, new WindowDecorActionBar(decorContentRoot)); @NonNull ActionBarView actionBarView) {
super(context, callback, new WindowDecorActionBar(decorContentRoot));
mActionBarView = actionBarView; mActionBarView = actionBarView;
mActionBar = ((WindowDecorActionBar) super.mActionBar); mActionBar = ((WindowDecorActionBar) super.mActionBar);
mDecorContentRoot = decorContentRoot; mDecorContentRoot = decorContentRoot;
@ -268,7 +261,6 @@ public abstract class CustomActionBarWrapper {
@Override @Override
protected void setupActionBar() { protected void setupActionBar() {
super.setupActionBar();
// Set the navigation mode. // Set the navigation mode.
int navMode = mCallback.getNavigationMode(); int navMode = mCallback.getNavigationMode();
@ -278,16 +270,6 @@ public abstract class CustomActionBarWrapper {
setupTabs(3); 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. // Set action bar to be split, if needed.
ViewGroup splitView = (ViewGroup) mDecorContentRoot.findViewById(R.id.split_action_bar); ViewGroup splitView = (ViewGroup) mDecorContentRoot.findViewById(R.id.split_action_bar);
if (splitView != null) { if (splitView != null) {
@ -299,6 +281,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 @Override
protected void inflateMenus() { protected void inflateMenus() {
super.inflateMenus(); super.inflateMenus();
@ -340,7 +333,7 @@ public abstract class CustomActionBarWrapper {
@Override @Override
int getMenuPopupMargin() { 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. // TODO: Use an adapter, like List View to set up tabs.

View File

@ -51,11 +51,13 @@ import com.android.layoutlib.bridge.android.BridgeContext;
import com.android.layoutlib.bridge.android.BridgeLayoutParamsMapAttributes; import com.android.layoutlib.bridge.android.BridgeLayoutParamsMapAttributes;
import com.android.layoutlib.bridge.android.BridgeXmlBlockParser; import com.android.layoutlib.bridge.android.BridgeXmlBlockParser;
import com.android.layoutlib.bridge.android.SessionParamsFlags; 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.Config;
import com.android.layoutlib.bridge.bars.NavigationBar; import com.android.layoutlib.bridge.bars.NavigationBar;
import com.android.layoutlib.bridge.bars.StatusBar; import com.android.layoutlib.bridge.bars.StatusBar;
import com.android.layoutlib.bridge.bars.TitleBar; 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.FakeAdapter;
import com.android.layoutlib.bridge.impl.binding.FakeExpandableAdapter; import com.android.layoutlib.bridge.impl.binding.FakeExpandableAdapter;
import com.android.resources.Density; 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 the theme says no title/action bar, then the size will be 0
if (mActionBarSize > 0) { if (mActionBarSize > 0) {
ActionBarLayout actionBar = createActionBar(context, params, backgroundLayout); BridgeActionBar actionBar = createActionBar(context, params, backgroundLayout);
actionBar.createMenuPopup(); actionBar.createMenuPopup();
mContentRoot = actionBar.getContentRoot(); mContentRoot = actionBar.getContentRoot();
} else if (mTitleBarSize > 0) { } 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. // android.support.v7.app.ActionBarActivity, and not care about the theme name at all.
if (mIsThemeAppCompat == null) { if (mIsThemeAppCompat == null) {
StyleResourceValue defaultTheme = resources.getDefaultTheme(); StyleResourceValue defaultTheme = resources.getDefaultTheme();
StyleResourceValue val = resources.getStyle("Theme.AppCompat", false); // We can't simply check for parent using resources.themeIsParentOf() since the
mIsThemeAppCompat = defaultTheme == val || resources.themeIsParentOf(val, defaultTheme); // 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; return mIsThemeAppCompat;
} }
@ -1647,9 +1663,13 @@ public class RenderSessionImpl extends RenderAction<SessionParams> {
/** /**
* Creates the action bar. Also queries the project callback for missing information. * 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) { 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() { public BufferedImage getImage() {