am f5ba5356: am 49fcfdd4: Merge changes I9c294329,Ie4db5d28 into mnc-dev

* commit 'f5ba535684939328e719051f8403b35bd1e0e2fc':
  LayoutLib: translucent sys ui bars
  Move the layout code out of RenderSessionImpl.
This commit is contained in:
Deepanshu Gupta
2015-07-24 03:11:34 +00:00
committed by Android Git Automerger
12 changed files with 581 additions and 509 deletions

View File

@@ -594,9 +594,13 @@ public final class Bridge extends com.android.ide.common.rendering.api.Bridge {
/**
* Returns the integer id of a framework resource, from a given resource type and resource name.
* <p/>
* If no resource is found, it creates a dynamic id for the resource.
*
* @param type the type of the resource
* @param name the name of the resource.
* @return an {@link Integer} containing the resource id, or null if no resource were found.
*
* @return an {@link Integer} containing the resource id.
*/
@NonNull
public static Integer getResourceId(ResourceType type, String name) {

View File

@@ -31,7 +31,6 @@ import android.graphics.drawable.Drawable;
import android.view.ContextThemeWrapper;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;
import java.lang.reflect.InvocationTargetException;
@@ -51,9 +50,8 @@ public class AppCompatActionBar extends BridgeActionBar {
/**
* 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);
public AppCompatActionBar(@NonNull BridgeContext context, @NonNull SessionParams params) {
super(context, params);
int contentRootId = context.getProjectResourceValue(ResourceType.ID,
"action_bar_activity_content", 0);
View contentView = getDecorContent().findViewById(contentRootId);
@@ -64,7 +62,9 @@ public class AppCompatActionBar extends BridgeActionBar {
// Something went wrong. Create a new FrameLayout in the enclosing layout.
FrameLayout contentRoot = new FrameLayout(context);
setMatchParent(contentRoot);
mEnclosingLayout.addView(contentRoot);
if (mEnclosingLayout != null) {
mEnclosingLayout.addView(contentRoot);
}
setContentRoot(contentRoot);
}
try {

View File

@@ -24,6 +24,7 @@ import com.android.ide.common.rendering.api.SessionParams;
import com.android.layoutlib.bridge.android.BridgeContext;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
@@ -39,7 +40,7 @@ public abstract class BridgeActionBar {
@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;
@Nullable protected final ViewGroup mEnclosingLayout;
private final View mDecorContent;
private final ActionBarCallback mCallback;
@@ -47,8 +48,7 @@ public abstract class BridgeActionBar {
@SuppressWarnings("NullableProblems") // Should be initialized by subclasses.
@NonNull private FrameLayout mContentRoot;
public BridgeActionBar(@NonNull BridgeContext context, @NonNull SessionParams params,
@NonNull ViewGroup parentView) {
public BridgeActionBar(@NonNull BridgeContext context, @NonNull SessionParams params) {
mBridgeContext = context;
mParams = params;
mCallback = params.getLayoutlibCallback().getActionBarCallback();
@@ -75,14 +75,13 @@ public abstract class BridgeActionBar {
// added.
mEnclosingLayout = new RelativeLayout(mBridgeContext);
setMatchParent(mEnclosingLayout);
parentView.addView(mEnclosingLayout);
} else {
mEnclosingLayout = parentView;
mEnclosingLayout = null;
}
// Inflate action bar layout.
mDecorContent = getInflater(context).inflate(layoutId, mEnclosingLayout, true);
mDecorContent =
getInflater(context).inflate(layoutId, mEnclosingLayout, mEnclosingLayout != null);
}
/**
@@ -153,6 +152,13 @@ public abstract class BridgeActionBar {
public abstract void createMenuPopup();
/**
* The root view that represents the action bar and possibly the content included in it.
*/
public View getRootView() {
return mEnclosingLayout == null ? mDecorContent : mEnclosingLayout;
}
public ActionBarCallback getCallBack() {
return mCallback;
}

View File

@@ -32,7 +32,6 @@ import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import android.annotation.NonNull;
import android.content.Context;
import android.content.res.ColorStateList;
import android.graphics.Bitmap;
import android.graphics.Bitmap_Delegate;
@@ -68,7 +67,7 @@ abstract class CustomBar extends LinearLayout {
protected abstract TextView getStyleableTextView();
protected CustomBar(BridgeContext context, int orientation, String layoutPath,
String name, int simulatedPlatformVersion) throws XmlPullParserException {
String name, int simulatedPlatformVersion) {
super(context);
mSimulatedPlatformVersion = simulatedPlatformVersion;
setOrientation(orientation);
@@ -78,14 +77,18 @@ abstract class CustomBar extends LinearLayout {
setGravity(Gravity.CENTER_HORIZONTAL);
}
LayoutInflater inflater = (LayoutInflater) getContext().getSystemService(
Context.LAYOUT_INFLATER_SERVICE);
LayoutInflater inflater = LayoutInflater.from(mContext);
XmlPullParser parser = ParserFactory.create(getClass().getResourceAsStream(layoutPath),
name);
XmlPullParser parser;
try {
parser = ParserFactory.create(getClass().getResourceAsStream(layoutPath), name);
} catch (XmlPullParserException e) {
// Should not happen as the resource is bundled with the jar, and ParserFactory should
// have been initialized.
throw new AssertionError(e);
}
BridgeXmlBlockParser bridgeParser = new BridgeXmlBlockParser(
parser, (BridgeContext) context, false /*platformFile*/);
BridgeXmlBlockParser bridgeParser = new BridgeXmlBlockParser(parser, context, false);
try {
inflater.inflate(bridgeParser, this, true);
@@ -154,7 +157,7 @@ abstract class CustomBar extends LinearLayout {
protected void setStyle(String themeEntryName) {
BridgeContext bridgeContext = (BridgeContext) mContext;
BridgeContext bridgeContext = getContext();
RenderResources res = bridgeContext.getRenderResources();
ResourceValue value = res.findItemInTheme(themeEntryName, true /*isFrameworkAttr*/);
@@ -214,27 +217,47 @@ abstract class CustomBar extends LinearLayout {
}
}
@Override
public BridgeContext getContext() {
return (BridgeContext) mContext;
}
/**
* Given a theme attribute name, get the color referenced by it. The theme attribute may be
* used in a layout like "?attr/foo".
* Find the background color for this bar from the theme attributes. Only relevant to StatusBar
* and NavigationBar.
* <p/>
* Returns 0 if not found.
*
* @param colorAttrName the attribute name for the background color
* @param translucentAttrName the attribute name for the translucency property of the bar.
*
* @throws NumberFormatException if color resolved to an invalid string.
*/
protected int getThemeAttrColor(@NonNull String attrName, boolean isFramework) {
protected int getBarColor(@NonNull String colorAttrName, @NonNull String translucentAttrName) {
if (!Config.isGreaterOrEqual(mSimulatedPlatformVersion, LOLLIPOP)) {
return 0;
}
assert mContext instanceof BridgeContext;
BridgeContext context = ((BridgeContext) mContext);
RenderResources renderResources = context.getRenderResources();
// From ?attr/foo to @color/bar. This is most likely an ItemResourceValue.
ResourceValue resource = renderResources.findItemInTheme(attrName, isFramework);
if (resource != null) {
// Form @color/bar to the #AARRGGBB
resource = renderResources.resolveResValue(resource);
RenderResources renderResources = getContext().getRenderResources();
// First check if the bar is translucent.
boolean translucent = ResourceHelper.getBooleanThemeValue(renderResources,
translucentAttrName, true, false);
if (translucent) {
// Keep in sync with R.color.system_bar_background_semi_transparent from system ui.
return 0x66000000; // 40% black.
}
boolean transparent = ResourceHelper.getBooleanThemeValue(renderResources,
"windowDrawsSystemBarBackgrounds", true, false);
if (transparent) {
return getColor(renderResources, colorAttrName);
}
return 0;
}
private static int getColor(RenderResources renderResources, String attr) {
// From ?attr/foo to @color/bar. This is most likely an ItemResourceValue.
ResourceValue resource = renderResources.findItemInTheme(attr, true);
// Form @color/bar to the #AARRGGBB
resource = renderResources.resolveResValue(resource);
if (resource != null && ResourceType.COLOR.equals(resource.getResourceType())) {
return ResourceHelper.getColor(resource.getValue());
}
@@ -242,8 +265,7 @@ abstract class CustomBar extends LinearLayout {
}
private ResourceValue getResourceValue(String reference) {
BridgeContext bridgeContext = (BridgeContext) mContext;
RenderResources res = bridgeContext.getRenderResources();
RenderResources res = getContext().getRenderResources();
// find the resource
ResourceValue value = res.findResValue(reference, false);

View File

@@ -60,23 +60,24 @@ public class FrameworkActionBar extends BridgeActionBar {
/**
* Inflate the action bar and attach it to {@code parentView}
*/
public FrameworkActionBar(@NonNull BridgeContext context, @NonNull SessionParams params,
@NonNull ViewGroup parentView) {
super(context, params, parentView);
public FrameworkActionBar(@NonNull BridgeContext context, @NonNull SessionParams params) {
super(context, params);
View decorContent = getDecorContent();
mActionBar = FrameworkActionBarWrapper.getActionBarWrapper(context, getCallBack(),
decorContent);
FrameLayout contentRoot = (FrameLayout) mEnclosingLayout.findViewById(android.R.id.content);
FrameLayout contentRoot = (FrameLayout) decorContent.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);
setMatchParent(contentRoot);
mEnclosingLayout.addView(contentRoot);
if (mEnclosingLayout != null) {
mEnclosingLayout.addView(contentRoot);
}
setContentRoot(contentRoot);
} else {
setContentRoot(contentRoot);
@@ -162,6 +163,7 @@ public class FrameworkActionBar extends BridgeActionBar {
listView.setDivider(a.getDrawable(R.attr.actionBarDivider));
a.recycle();
listView.setElevation(mActionBar.getMenuPopupElevation());
assert mEnclosingLayout != null : "Unable to find view to attach ActionMenuPopup.";
mEnclosingLayout.addView(listView);
}

View File

@@ -79,7 +79,7 @@ public abstract class FrameworkActionBarWrapper {
}
}
FrameworkActionBarWrapper(@NonNull BridgeContext context, ActionBarCallback callback,
FrameworkActionBarWrapper(@NonNull BridgeContext context, @NonNull ActionBarCallback callback,
@NonNull ActionBar actionBar) {
mActionBar = actionBar;
mCallback = callback;

View File

@@ -19,8 +19,6 @@ package com.android.layoutlib.bridge.bars;
import com.android.layoutlib.bridge.android.BridgeContext;
import com.android.resources.Density;
import org.xmlpull.v1.XmlPullParserException;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.util.AttributeSet;
@@ -33,6 +31,8 @@ public class NavigationBar extends CustomBar {
/** Navigation bar background color attribute name. */
private static final String ATTR_COLOR = "navigationBarColor";
/** Attribute for translucency property. */
public static final String ATTR_TRANSLUCENT = "windowTranslucentNavigation";
// These correspond to @dimen/navigation_side_padding in the system ui code.
private static final int PADDING_WIDTH_DEFAULT = 36;
private static final int PADDING_WIDTH_SW360 = 40;
@@ -49,8 +49,8 @@ public class NavigationBar extends CustomBar {
* Constructor to be used when creating the {@link NavigationBar} as a regular control.
* This is currently used by the theme editor.
*/
public NavigationBar(Context context, AttributeSet attrs)
throws XmlPullParserException {
@SuppressWarnings("unused")
public NavigationBar(Context context, AttributeSet attrs) {
this((BridgeContext) context,
Density.getEnum(((BridgeContext) context).getMetrics().densityDpi),
LinearLayout.HORIZONTAL, // In this mode, it doesn't need to be render vertically
@@ -61,11 +61,11 @@ public class NavigationBar extends CustomBar {
}
public NavigationBar(BridgeContext context, Density density, int orientation, boolean isRtl,
boolean rtlEnabled, int simulatedPlatformVersion) throws XmlPullParserException {
boolean rtlEnabled, int simulatedPlatformVersion) {
super(context, orientation, getShortestWidth(context)>= 600 ? LAYOUT_600DP_XML : LAYOUT_XML,
"navigation_bar.xml", simulatedPlatformVersion);
int color = getThemeAttrColor(ATTR_COLOR, true);
int color = getBarColor(ATTR_COLOR, ATTR_TRANSLUCENT);
setBackgroundColor(color == 0 ? 0xFF000000 : color);
// Cannot access the inside items through id because no R.id values have been

View File

@@ -26,6 +26,7 @@ import com.android.resources.Density;
import org.xmlpull.v1.XmlPullParserException;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.view.Gravity;
@@ -42,22 +43,26 @@ public class StatusBar extends CustomBar {
private final int mSimulatedPlatformVersion;
/** Status bar background color attribute name. */
private static final String ATTR_COLOR = "statusBarColor";
/** Attribute for translucency property. */
public static final String ATTR_TRANSLUCENT = "windowTranslucentStatus";
/**
* Constructor to be used when creating the {@link StatusBar} as a regular control. This
* is currently used by the theme editor.
*/
public StatusBar(Context context, AttributeSet attrs) throws XmlPullParserException {
@SuppressWarnings("UnusedParameters")
public StatusBar(Context context, AttributeSet attrs) {
this((BridgeContext) context,
Density.getEnum(((BridgeContext) context).getMetrics().densityDpi),
LinearLayout.HORIZONTAL, // In this mode, it doesn't need to be render vertically
((BridgeContext) context).getConfiguration().getLayoutDirection() ==
View.LAYOUT_DIRECTION_RTL,
(context.getApplicationInfo().flags & ApplicationInfo.FLAG_SUPPORTS_RTL) != 0,
context.getApplicationInfo().targetSdkVersion);
}
public StatusBar(BridgeContext context, Density density, int direction, boolean RtlEnabled,
int simulatedPlatformVersion) throws XmlPullParserException {
@SuppressWarnings("UnusedParameters")
public StatusBar(BridgeContext context, Density density, boolean isRtl, boolean rtlEnabled,
int simulatedPlatformVersion) {
// FIXME: if direction is RTL but it's not enabled in application manifest, mirror this bar.
super(context, LinearLayout.HORIZONTAL, "/bars/status_bar.xml", "status_bar.xml",
simulatedPlatformVersion);
@@ -66,7 +71,7 @@ public class StatusBar extends CustomBar {
// FIXME: use FILL_H?
setGravity(Gravity.START | Gravity.TOP | Gravity.RIGHT);
int color = getThemeAttrColor(ATTR_COLOR, true);
int color = getBarColor(ATTR_COLOR, ATTR_TRANSLUCENT);
setBackgroundColor(color == 0 ? Config.getStatusBarColor(simulatedPlatformVersion) : color);
// Cannot access the inside items through id because no R.id values have been

View File

@@ -27,8 +27,7 @@ public class TitleBar extends CustomBar {
private TextView mTextView;
public TitleBar(BridgeContext context, String label, int simulatedPlatformVersion)
throws XmlPullParserException {
public TitleBar(BridgeContext context, String label, int simulatedPlatformVersion) {
super(context, LinearLayout.HORIZONTAL, "/bars/title_bar.xml", "title_bar.xml",
simulatedPlatformVersion);

View File

@@ -0,0 +1,459 @@
/*
* 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.impl;
import com.android.ide.common.rendering.api.HardwareConfig;
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.ide.common.rendering.api.StyleResourceValue;
import com.android.layoutlib.bridge.Bridge;
import com.android.layoutlib.bridge.android.BridgeContext;
import com.android.layoutlib.bridge.bars.AppCompatActionBar;
import com.android.layoutlib.bridge.bars.BridgeActionBar;
import com.android.layoutlib.bridge.bars.Config;
import com.android.layoutlib.bridge.bars.FrameworkActionBar;
import com.android.layoutlib.bridge.bars.NavigationBar;
import com.android.layoutlib.bridge.bars.StatusBar;
import com.android.layoutlib.bridge.bars.TitleBar;
import com.android.resources.Density;
import com.android.resources.ResourceType;
import com.android.resources.ScreenOrientation;
import android.annotation.NonNull;
import android.graphics.drawable.Drawable;
import android.util.DisplayMetrics;
import android.util.TypedValue;
import android.view.View;
import android.widget.FrameLayout;
import android.widget.LinearLayout;
import android.widget.RelativeLayout;
import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
import static android.widget.LinearLayout.VERTICAL;
/**
* The Layout used to create the system decor.
*
* The layout inflated will contain a content frame where the user's layout can be inflated.
* <pre>
* +-------------------------------------------------+---+
* | Status bar | N |
* +-------------------------------------------------+ a |
* | Title/Action bar (optional) | v |
* +-------------------------------------------------+ |
* | Content, vertical extending | b |
* | | a |
* | | r |
* +-------------------------------------------------+---+
* </pre>
* or
* <pre>
* +-------------------------------------+
* | Status bar |
* +-------------------------------------+
* | Title/Action bar (optional) |
* +-------------------------------------+
* | Content, vertical extending |
* | |
* | |
* +-------------------------------------+
* | Nav bar |
* +-------------------------------------+
* </pre>
*
*/
class Layout extends RelativeLayout {
// Theme attributes used for configuring appearance of the system decor.
private static final String ATTR_WINDOW_FLOATING = "windowIsFloating";
private static final String ATTR_WINDOW_BACKGROUND = "windowBackground";
private static final String ATTR_WINDOW_FULL_SCREEN = "windowFullScreen";
private static final String ATTR_NAV_BAR_HEIGHT = "navigation_bar_height";
private static final String ATTR_NAV_BAR_WIDTH = "navigation_bar_width";
private static final String ATTR_STATUS_BAR_HEIGHT = "status_bar_height";
private static final String ATTR_WINDOW_ACTION_BAR = "windowActionBar";
private static final String ATTR_ACTION_BAR_SIZE = "actionBarSize";
private static final String ATTR_WINDOW_NO_TITLE = "windowNoTitle";
private static final String ATTR_WINDOW_TITLE_SIZE = "windowTitleSize";
private static final String ATTR_WINDOW_TRANSLUCENT_STATUS = StatusBar.ATTR_TRANSLUCENT;
private static final String ATTR_WINDOW_TRANSLUCENT_NAV = NavigationBar.ATTR_TRANSLUCENT;
private static final String PREFIX_THEME_APPCOMPAT = "Theme.AppCompat";
// Default sizes
private static final int DEFAULT_STATUS_BAR_HEIGHT = 25;
private static final int DEFAULT_TITLE_BAR_HEIGHT = 25;
private static final int DEFAULT_NAV_BAR_SIZE = 48;
// Ids assigned to components created. This is so that we can refer to other components in
// layout params.
private static final String ID_NAV_BAR = "navBar";
private static final String ID_STATUS_BAR = "statusBar";
private static final String ID_TITLE_BAR = "titleBar";
// Prefix used with the above ids in order to make them unique in framework namespace.
private static final String ID_PREFIX = "android_layoutlib_";
/**
* Temporarily store the builder so that it doesn't have to be passed to all methods used
* during inflation.
*/
private Builder mBuilder;
/**
* This holds user's layout.
*/
private FrameLayout mContentRoot;
public Layout(@NonNull Builder builder) {
super(builder.mContext);
mBuilder = builder;
if (builder.mWindowBackground != null) {
Drawable d = ResourceHelper.getDrawable(builder.mWindowBackground, builder.mContext);
setBackground(d);
}
int simulatedPlatformVersion = getParams().getSimulatedPlatformVersion();
HardwareConfig hwConfig = getParams().getHardwareConfig();
Density density = hwConfig.getDensity();
boolean isRtl = Bridge.isLocaleRtl(getParams().getLocale());
NavigationBar navBar = null;
if (mBuilder.hasNavBar()) {
navBar = createNavBar(getContext(), density, isRtl, getParams().isRtlSupported(),
simulatedPlatformVersion);
}
StatusBar statusBar = null;
if (builder.mStatusBarSize > 0) {
statusBar = createStatusBar(getContext(), density, isRtl, getParams().isRtlSupported(),
simulatedPlatformVersion);
}
View actionBar = null;
TitleBar titleBar = null;
if (builder.mActionBarSize > 0) {
BridgeActionBar bar = createActionBar(getContext(), getParams());
mContentRoot = bar.getContentRoot();
actionBar = bar.getRootView();
} else if (mBuilder.mTitleBarSize > 0) {
titleBar = createTitleBar(getContext(), getParams().getAppLabel(),
simulatedPlatformVersion);
}
addViews(titleBar, mContentRoot == null ? (mContentRoot = createContentFrame()) : actionBar,
statusBar, navBar);
// Done with the builder. Don't hold a reference to it.
mBuilder = null;
}
@NonNull
private FrameLayout createContentFrame() {
FrameLayout contentRoot = new FrameLayout(getContext());
LayoutParams params = createLayoutParams(MATCH_PARENT, MATCH_PARENT);
int rule = mBuilder.isNavBarVertical() ? START_OF : ABOVE;
if (mBuilder.solidBars()) {
params.addRule(rule, getId(ID_NAV_BAR));
}
int below = -1;
if (mBuilder.mActionBarSize <= 0 && mBuilder.mTitleBarSize > 0) {
below = getId(ID_TITLE_BAR);
} else if (mBuilder.solidBars()) {
below = getId(ID_STATUS_BAR);
}
if (below != -1) {
params.addRule(BELOW, below);
}
contentRoot.setLayoutParams(params);
return contentRoot;
}
@NonNull
private LayoutParams createLayoutParams(int width, int height) {
DisplayMetrics metrics = getContext().getResources().getDisplayMetrics();
if (width > 0) {
width = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, width, metrics);
}
if (height > 0) {
height = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, height, metrics);
}
return new LayoutParams(width, height);
}
@NonNull
public FrameLayout getContentRoot() {
return mContentRoot;
}
@NonNull
private SessionParams getParams() {
return mBuilder.mParams;
}
@NonNull
@Override
public BridgeContext getContext(){
return (BridgeContext) super.getContext();
}
/**
* @param isRtl whether the current locale is an RTL locale.
* @param isRtlSupported whether the applications supports RTL (i.e. has supportsRtl=true
* in the manifest and targetSdkVersion >= 17.
*/
@NonNull
private StatusBar createStatusBar(BridgeContext context, Density density, boolean isRtl,
boolean isRtlSupported, int simulatedPlatformVersion) {
StatusBar statusBar =
new StatusBar(context, density, isRtl, isRtlSupported, simulatedPlatformVersion);
LayoutParams params = createLayoutParams(MATCH_PARENT, mBuilder.mStatusBarSize);
if (mBuilder.isNavBarVertical()) {
params.addRule(START_OF, getId(ID_NAV_BAR));
}
statusBar.setLayoutParams(params);
statusBar.setId(getId(ID_STATUS_BAR));
return statusBar;
}
private BridgeActionBar createActionBar(@NonNull BridgeContext context,
@NonNull SessionParams params) {
BridgeActionBar actionBar;
if (mBuilder.isThemeAppCompat()) {
actionBar = new AppCompatActionBar(context, params);
} else {
actionBar = new FrameworkActionBar(context, params);
}
LayoutParams layoutParams = createLayoutParams(MATCH_PARENT, MATCH_PARENT);
int rule = mBuilder.isNavBarVertical() ? START_OF : ABOVE;
if (mBuilder.solidBars()) {
layoutParams.addRule(rule, getId(ID_NAV_BAR));
}
if (mBuilder.solidBars()) {
layoutParams.addRule(BELOW, getId(ID_STATUS_BAR));
}
actionBar.getRootView().setLayoutParams(layoutParams);
actionBar.createMenuPopup();
return actionBar;
}
@NonNull
private TitleBar createTitleBar(BridgeContext context, String title,
int simulatedPlatformVersion) {
TitleBar titleBar = new TitleBar(context, title, simulatedPlatformVersion);
LayoutParams params = createLayoutParams(MATCH_PARENT, mBuilder.mTitleBarSize);
if (mBuilder.solidBars()) {
params.addRule(BELOW, getId(ID_STATUS_BAR));
}
if (mBuilder.isNavBarVertical() && mBuilder.solidBars()) {
params.addRule(START_OF, getId(ID_NAV_BAR));
}
titleBar.setLayoutParams(params);
titleBar.setId(getId(ID_TITLE_BAR));
return titleBar;
}
/**
* @param isRtl whether the current locale is an RTL locale.
* @param isRtlSupported whether the applications supports RTL (i.e. has supportsRtl=true
* in the manifest and targetSdkVersion >= 17.
*/
@NonNull
private NavigationBar createNavBar(BridgeContext context, Density density, boolean isRtl,
boolean isRtlSupported, int simulatedPlatformVersion) {
int orientation = mBuilder.mNavBarOrientation;
int size = mBuilder.mNavBarSize;
NavigationBar navBar = new NavigationBar(context, density, orientation, isRtl,
isRtlSupported, simulatedPlatformVersion);
boolean isVertical = mBuilder.isNavBarVertical();
int w = isVertical ? size : MATCH_PARENT;
int h = isVertical ? MATCH_PARENT : size;
LayoutParams params = createLayoutParams(w, h);
params.addRule(isVertical ? ALIGN_PARENT_END : ALIGN_PARENT_BOTTOM);
navBar.setLayoutParams(params);
navBar.setId(getId(ID_NAV_BAR));
return navBar;
}
private void addViews(@NonNull View... views) {
for (View view : views) {
if (view != null) {
addView(view);
}
}
}
private int getId(String name) {
return Bridge.getResourceId(ResourceType.ID, ID_PREFIX + name);
}
/**
* A helper class to help initialize the Layout.
*/
static class Builder {
@NonNull
private final SessionParams mParams;
@NonNull
private final BridgeContext mContext;
private final RenderResources mResources;
private final boolean mWindowIsFloating;
private ResourceValue mWindowBackground;
private int mStatusBarSize;
private int mNavBarSize;
private int mNavBarOrientation;
private int mActionBarSize;
private int mTitleBarSize;
private boolean mTranslucentStatus;
private boolean mTranslucentNav;
private Boolean mIsThemeAppCompat;
public Builder(@NonNull SessionParams params, @NonNull BridgeContext context) {
mParams = params;
mContext = context;
mResources = mParams.getResources();
mWindowIsFloating = ResourceHelper.getBooleanThemeValue(mResources, ATTR_WINDOW_FLOATING, true, true);
findBackground();
findStatusBar();
findActionBar();
findNavBar();
}
public boolean isNavBarVertical() {
return mNavBarOrientation == VERTICAL;
}
private void findBackground() {
if (!mParams.isBgColorOverridden()) {
mWindowBackground = mResources.findItemInTheme(ATTR_WINDOW_BACKGROUND, true);
mWindowBackground = mResources.resolveResValue(mWindowBackground);
}
}
private void findStatusBar() {
boolean windowFullScreen =
ResourceHelper.getBooleanThemeValue(mResources, ATTR_WINDOW_FULL_SCREEN, true, false);
if (!windowFullScreen && !mWindowIsFloating) {
mStatusBarSize =
getDimension(ATTR_STATUS_BAR_HEIGHT, true, DEFAULT_STATUS_BAR_HEIGHT);
mTranslucentStatus = ResourceHelper.getBooleanThemeValue(mResources,
ATTR_WINDOW_TRANSLUCENT_STATUS, true, false);
}
}
private void findActionBar() {
if (mWindowIsFloating) {
return;
}
// Check if an actionbar is needed
boolean windowActionBar = ResourceHelper.getBooleanThemeValue(mResources, ATTR_WINDOW_ACTION_BAR,
!isThemeAppCompat(), true);
if (windowActionBar) {
mActionBarSize = getDimension(ATTR_ACTION_BAR_SIZE, true, DEFAULT_TITLE_BAR_HEIGHT);
} else {
// Maybe the gingerbread era title bar is needed
boolean windowNoTitle =
ResourceHelper.getBooleanThemeValue(mResources, ATTR_WINDOW_NO_TITLE, true, false);
if (!windowNoTitle) {
mTitleBarSize =
getDimension(ATTR_WINDOW_TITLE_SIZE, true, DEFAULT_TITLE_BAR_HEIGHT);
}
}
}
private void findNavBar() {
if (hasSoftwareButtons() && !mWindowIsFloating) {
// get orientation
HardwareConfig hwConfig = mParams.getHardwareConfig();
boolean barOnBottom = true;
if (hwConfig.getOrientation() == ScreenOrientation.LANDSCAPE) {
int shortSize = hwConfig.getScreenHeight();
int shortSizeDp = shortSize * DisplayMetrics.DENSITY_DEFAULT /
hwConfig.getDensity().getDpiValue();
// 0-599dp: "phone" UI with bar on the side
// 600+dp: "tablet" UI with bar on the bottom
barOnBottom = shortSizeDp >= 600;
}
mNavBarOrientation = barOnBottom ? LinearLayout.HORIZONTAL : VERTICAL;
mNavBarSize = getDimension(barOnBottom ? ATTR_NAV_BAR_HEIGHT : ATTR_NAV_BAR_WIDTH,
true, DEFAULT_NAV_BAR_SIZE);
mTranslucentNav = ResourceHelper.getBooleanThemeValue(mResources,
ATTR_WINDOW_TRANSLUCENT_NAV, true, false);
}
}
private int getDimension(String attr, boolean isFramework, int defaultValue) {
ResourceValue value = mResources.findItemInTheme(attr, isFramework);
value = mResources.resolveResValue(value);
if (value != null) {
TypedValue typedValue = ResourceHelper.getValue(attr, value.getValue(), true);
if (typedValue != null) {
return (int) typedValue.getDimension(mContext.getMetrics());
}
}
return defaultValue;
}
private boolean hasSoftwareButtons() {
return mParams.getHardwareConfig().hasSoftwareButtons();
}
private boolean isThemeAppCompat() {
// If a cached value exists, return it.
if (mIsThemeAppCompat != null) {
return mIsThemeAppCompat;
}
// Ideally, we should check if the corresponding activity extends
// android.support.v7.app.ActionBarActivity, and not care about the theme name at all.
StyleResourceValue defaultTheme = mResources.getDefaultTheme();
// 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++) {
if (defaultTheme == null) {
break;
}
// for loop ensures that we don't run into cyclic theme inheritance.
if (defaultTheme.getName().startsWith(PREFIX_THEME_APPCOMPAT)) {
isThemeAppCompat = true;
break;
}
defaultTheme = mResources.getParent(defaultTheme);
}
mIsThemeAppCompat = isThemeAppCompat;
return isThemeAppCompat;
}
/**
* Return if both status bar and nav bar are solid (content doesn't overlap with these
* bars).
*/
private boolean solidBars() {
return hasNavBar() && !mTranslucentNav && !mTranslucentStatus && mStatusBarSize > 0;
}
private boolean hasNavBar() {
return Config.showOnScreenNavBar(mParams.getSimulatedPlatformVersion()) &&
hasSoftwareButtons() && mNavBarSize > 0;
}
}
}

View File

@@ -29,10 +29,8 @@ import com.android.ide.common.rendering.api.Result;
import com.android.ide.common.rendering.api.Result.Status;
import com.android.ide.common.rendering.api.SessionParams;
import com.android.ide.common.rendering.api.SessionParams.RenderingMode;
import com.android.ide.common.rendering.api.StyleResourceValue;
import com.android.ide.common.rendering.api.ViewInfo;
import com.android.ide.common.rendering.api.ViewType;
import com.android.internal.util.XmlUtils;
import com.android.internal.view.menu.ActionMenuItemView;
import com.android.internal.view.menu.BridgeMenuItemImpl;
import com.android.internal.view.menu.IconMenuItemView;
@@ -45,22 +43,11 @@ import com.android.layoutlib.bridge.android.BridgeLayoutParamsMapAttributes;
import com.android.layoutlib.bridge.android.BridgeXmlBlockParser;
import com.android.layoutlib.bridge.android.RenderParamsFlags;
import com.android.layoutlib.bridge.android.support.DesignLibUtil;
import com.android.layoutlib.bridge.bars.AppCompatActionBar;
import com.android.layoutlib.bridge.bars.BridgeActionBar;
import com.android.layoutlib.bridge.bars.Config;
import com.android.layoutlib.bridge.bars.FrameworkActionBar;
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.impl.binding.FakeAdapter;
import com.android.layoutlib.bridge.impl.binding.FakeExpandableAdapter;
import com.android.resources.Density;
import com.android.resources.ResourceType;
import com.android.resources.ScreenOrientation;
import com.android.util.Pair;
import org.xmlpull.v1.XmlPullParserException;
import android.animation.AnimationThread;
import android.animation.Animator;
import android.animation.AnimatorInflater;
@@ -72,10 +59,7 @@ import android.app.Fragment_Delegate;
import android.graphics.Bitmap;
import android.graphics.Bitmap_Delegate;
import android.graphics.Canvas;
import android.graphics.drawable.Drawable;
import android.preference.Preference_Delegate;
import android.util.DisplayMetrics;
import android.util.TypedValue;
import android.view.AttachInfo_Accessor;
import android.view.BridgeInflater;
import android.view.IWindowManager;
@@ -126,33 +110,22 @@ import static com.android.layoutlib.bridge.util.ReflectionUtils.isInstanceOf;
*/
public class RenderSessionImpl extends RenderAction<SessionParams> {
private static final int DEFAULT_TITLE_BAR_HEIGHT = 25;
private static final int DEFAULT_STATUS_BAR_HEIGHT = 25;
// scene state
private RenderSession mScene;
private BridgeXmlBlockParser mBlockParser;
private BridgeInflater mInflater;
private ResourceValue mWindowBackground;
private ViewGroup mViewRoot;
private FrameLayout mContentRoot;
private Canvas mCanvas;
private int mMeasuredScreenWidth = -1;
private int mMeasuredScreenHeight = -1;
private boolean mIsAlphaChannelImage;
private boolean mWindowIsFloating;
private Boolean mIsThemeAppCompat;
private int mStatusBarSize;
private int mNavigationBarSize;
private int mNavigationBarOrientation = LinearLayout.HORIZONTAL;
private int mTitleBarSize;
private int mActionBarSize;
// information being returned through the API
private BufferedImage mImage;
private List<ViewInfo> mViewInfoList;
private List<ViewInfo> mSystemViewInfoList;
private Layout.Builder mLayoutBuilder;
private static final class PostInflateException extends Exception {
private static final long serialVersionUID = 1L;
@@ -196,34 +169,24 @@ public class RenderSessionImpl extends RenderAction<SessionParams> {
SessionParams params = getParams();
BridgeContext context = getContext();
RenderResources resources = getParams().getResources();
DisplayMetrics metrics = getContext().getMetrics();
// use default of true in case it's not found to use alpha by default
mIsAlphaChannelImage = getBooleanThemeValue(resources, "windowIsFloating", true, true);
// FIXME: Find out why both variables are taking the same value.
mWindowIsFloating = getBooleanThemeValue(resources, "windowIsFloating", true, true);
mIsAlphaChannelImage = ResourceHelper.getBooleanThemeValue(params.getResources(),
"windowIsFloating", true, true);
findBackground(resources);
findStatusBar(resources, metrics);
findActionBar(resources, metrics);
findNavigationBar(resources, metrics);
mLayoutBuilder = new Layout.Builder(params, context);
// FIXME: find those out, and possibly add them to the render params
boolean hasNavigationBar = true;
//noinspection ConstantConditions
IWindowManager iwm = new IWindowManagerImpl(getContext().getConfiguration(),
metrics, Surface.ROTATION_0,
hasNavigationBar);
context.getMetrics(), Surface.ROTATION_0, hasNavigationBar);
WindowManagerGlobal_Delegate.setWindowManagerService(iwm);
// build the inflater and parser.
mInflater = new BridgeInflater(context, params.getLayoutlibCallback());
context.setBridgeInflater(mInflater);
mBlockParser = new BridgeXmlBlockParser(
params.getLayoutDescription(), context, false /* platformResourceFlag */);
mBlockParser = new BridgeXmlBlockParser(params.getLayoutDescription(), context, false);
return SUCCESS.createResult();
}
@@ -240,164 +203,11 @@ public class RenderSessionImpl extends RenderAction<SessionParams> {
checkLock();
try {
mViewRoot = new Layout(mLayoutBuilder);
mLayoutBuilder = null; // Done with the builder.
mContentRoot = ((Layout) mViewRoot).getContentRoot();
SessionParams params = getParams();
HardwareConfig hardwareConfig = params.getHardwareConfig();
BridgeContext context = getContext();
boolean isRtl = Bridge.isLocaleRtl(params.getLocale());
int layoutDirection = isRtl ? View.LAYOUT_DIRECTION_RTL : View.LAYOUT_DIRECTION_LTR;
// the view group that receives the window background.
ViewGroup backgroundView;
if (mWindowIsFloating || params.isForceNoDecor()) {
backgroundView = mViewRoot = mContentRoot = new FrameLayout(context);
mViewRoot.setLayoutDirection(layoutDirection);
} else {
int simulatedPlatformVersion = params.getSimulatedPlatformVersion();
if (hasSoftwareButtons() && mNavigationBarOrientation == LinearLayout.VERTICAL) {
/*
* This is a special case where the navigation bar is on the right.
+-------------------------------------------------+---+
| Status bar (always) | |
+-------------------------------------------------+ |
| (Layout with background drawable) | |
| +---------------------------------------------+ | |
| | Title/Action bar (optional) | | |
| +---------------------------------------------+ | |
| | Content, vertical extending | | |
| | | | |
| +---------------------------------------------+ | |
+-------------------------------------------------+---+
So we create a horizontal layout, with the nav bar on the right,
and the left part is the normal layout below without the nav bar at
the bottom
*/
LinearLayout topLayout = new LinearLayout(context);
topLayout.setLayoutDirection(layoutDirection);
mViewRoot = topLayout;
topLayout.setOrientation(LinearLayout.HORIZONTAL);
if (Config.showOnScreenNavBar(simulatedPlatformVersion)) {
try {
NavigationBar navigationBar = createNavigationBar(context,
hardwareConfig.getDensity(), isRtl, params.isRtlSupported(),
simulatedPlatformVersion);
topLayout.addView(navigationBar);
} catch (XmlPullParserException ignored) {
}
}
}
/*
* we're creating the following layout
*
+-------------------------------------------------+
| Status bar (always) |
+-------------------------------------------------+
| (Layout with background drawable) |
| +---------------------------------------------+ |
| | Title/Action bar (optional) | |
| +---------------------------------------------+ |
| | Content, vertical extending | |
| | | |
| +---------------------------------------------+ |
+-------------------------------------------------+
| Navigation bar for soft buttons, maybe see above|
+-------------------------------------------------+
*/
LinearLayout topLayout = new LinearLayout(context);
topLayout.setOrientation(LinearLayout.VERTICAL);
topLayout.setLayoutDirection(layoutDirection);
// if we don't already have a view root this is it
if (mViewRoot == null) {
mViewRoot = topLayout;
} else {
int topLayoutWidth =
params.getHardwareConfig().getScreenWidth() - mNavigationBarSize;
LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(
topLayoutWidth, LayoutParams.MATCH_PARENT);
topLayout.setLayoutParams(layoutParams);
// this is the case of soft buttons + vertical bar.
// this top layout is the first layout in the horizontal layout. see above)
if (isRtl && params.isRtlSupported()) {
// If RTL is enabled, layoutlib will mirror the layouts. So, add the
// topLayout to the right of Navigation Bar and layoutlib will draw it
// to the left.
mViewRoot.addView(topLayout);
} else {
// Add the top layout to the left of the Navigation Bar.
mViewRoot.addView(topLayout, 0);
}
}
if (mStatusBarSize > 0) {
// system bar
try {
StatusBar statusBar = createStatusBar(context, hardwareConfig.getDensity(),
layoutDirection, params.isRtlSupported(),
simulatedPlatformVersion);
topLayout.addView(statusBar);
} catch (XmlPullParserException ignored) {
}
}
LinearLayout backgroundLayout = new LinearLayout(context);
backgroundView = backgroundLayout;
backgroundLayout.setOrientation(LinearLayout.VERTICAL);
LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(
LayoutParams.MATCH_PARENT, 0);
layoutParams.weight = 1;
backgroundLayout.setLayoutParams(layoutParams);
topLayout.addView(backgroundLayout);
// if the theme says no title/action bar, then the size will be 0
if (mActionBarSize > 0) {
BridgeActionBar actionBar = createActionBar(context, params, backgroundLayout);
actionBar.createMenuPopup();
mContentRoot = actionBar.getContentRoot();
} else if (mTitleBarSize > 0) {
try {
TitleBar titleBar = createTitleBar(context,
params.getAppLabel(),
simulatedPlatformVersion);
backgroundLayout.addView(titleBar);
} catch (XmlPullParserException ignored) {
}
}
// content frame
if (mContentRoot == null) {
mContentRoot = new FrameLayout(context);
layoutParams = new LinearLayout.LayoutParams(
LayoutParams.MATCH_PARENT, 0);
layoutParams.weight = 1;
mContentRoot.setLayoutParams(layoutParams);
backgroundLayout.addView(mContentRoot);
}
if (Config.showOnScreenNavBar(simulatedPlatformVersion) &&
mNavigationBarOrientation == LinearLayout.HORIZONTAL &&
mNavigationBarSize > 0) {
// system bar
try {
NavigationBar navigationBar = createNavigationBar(context,
hardwareConfig.getDensity(), isRtl, params.isRtlSupported(),
simulatedPlatformVersion);
topLayout.addView(navigationBar);
} catch (XmlPullParserException ignored) {
}
}
}
// Sets the project callback (custom view loader) to the fragment delegate so that
// it can instantiate the custom Fragment.
@@ -408,7 +218,7 @@ public class RenderSessionImpl extends RenderAction<SessionParams> {
View view;
if (isPreference) {
view = Preference_Delegate.inflatePreference(getContext(), mBlockParser,
mContentRoot);
mContentRoot);
} else {
view = mInflater.inflate(mBlockParser, mContentRoot);
}
@@ -427,12 +237,6 @@ public class RenderSessionImpl extends RenderAction<SessionParams> {
setActiveToolbar(view, context, params);
// get the background drawable
if (mWindowBackground != null) {
Drawable d = ResourceHelper.getDrawable(mWindowBackground, context);
backgroundView.setBackground(d);
}
return SUCCESS.createResult();
} catch (PostInflateException e) {
return ERROR_INFLATION.createResult(e.getMessage(), e);
@@ -1073,198 +877,6 @@ public class RenderSessionImpl extends RenderAction<SessionParams> {
}
}
private void findBackground(RenderResources resources) {
if (!getParams().isBgColorOverridden()) {
mWindowBackground = resources.findItemInTheme("windowBackground",
true /*isFrameworkAttr*/);
if (mWindowBackground != null) {
mWindowBackground = resources.resolveResValue(mWindowBackground);
}
}
}
private boolean hasSoftwareButtons() {
return getParams().getHardwareConfig().hasSoftwareButtons();
}
private void findStatusBar(RenderResources resources, DisplayMetrics metrics) {
boolean windowFullscreen = getBooleanThemeValue(resources,
"windowFullscreen", false, true);
if (!windowFullscreen && !mWindowIsFloating) {
// default value
mStatusBarSize = DEFAULT_STATUS_BAR_HEIGHT;
// get the real value
ResourceValue value = resources.getFrameworkResource(ResourceType.DIMEN,
"status_bar_height");
if (value != null) {
TypedValue typedValue = ResourceHelper.getValue("status_bar_height",
value.getValue(), true /*requireUnit*/);
if (typedValue != null) {
// compute the pixel value based on the display metrics
mStatusBarSize = (int)typedValue.getDimension(metrics);
}
}
}
}
private void findActionBar(RenderResources resources, DisplayMetrics metrics) {
if (mWindowIsFloating) {
return;
}
boolean windowActionBar = getBooleanThemeValue(resources,
"windowActionBar", true, !isThemeAppCompat(resources));
// if there's a value and it's false (default is true)
if (windowActionBar) {
// default size of the window title bar
mActionBarSize = DEFAULT_TITLE_BAR_HEIGHT;
// get value from the theme.
ResourceValue value = resources.findItemInTheme("actionBarSize",
true /*isFrameworkAttr*/);
// resolve it
value = resources.resolveResValue(value);
if (value != null) {
// get the numerical value, if available
TypedValue typedValue = ResourceHelper.getValue("actionBarSize", value.getValue(),
true /*requireUnit*/);
if (typedValue != null) {
// compute the pixel value based on the display metrics
mActionBarSize = (int)typedValue.getDimension(metrics);
}
}
} else {
// action bar overrides title bar so only look for this one if action bar is hidden
boolean windowNoTitle = getBooleanThemeValue(resources, "windowNoTitle", false, true);
if (!windowNoTitle) {
// default size of the window title bar
mTitleBarSize = DEFAULT_TITLE_BAR_HEIGHT;
// get value from the theme.
ResourceValue value = resources.findItemInTheme("windowTitleSize",
true /*isFrameworkAttr*/);
// resolve it
value = resources.resolveResValue(value);
if (value != null) {
// get the numerical value, if available
TypedValue typedValue = ResourceHelper.getValue("windowTitleSize",
value.getValue(), true /*requireUnit*/);
if (typedValue != null) {
// compute the pixel value based on the display metrics
mTitleBarSize = (int)typedValue.getDimension(metrics);
}
}
}
}
}
private void findNavigationBar(RenderResources resources, DisplayMetrics metrics) {
if (hasSoftwareButtons() && !mWindowIsFloating) {
// default value
mNavigationBarSize = 48; // ??
HardwareConfig hardwareConfig = getParams().getHardwareConfig();
boolean barOnBottom = true;
if (hardwareConfig.getOrientation() == ScreenOrientation.LANDSCAPE) {
// compute the dp of the screen.
int shortSize = hardwareConfig.getScreenHeight();
// compute in dp
int shortSizeDp = shortSize * DisplayMetrics.DENSITY_DEFAULT /
hardwareConfig.getDensity().getDpiValue();
// 0-599dp: "phone" UI with bar on the side
// 600+dp: "tablet" UI with bar on the bottom
barOnBottom = shortSizeDp >= 600;
}
if (barOnBottom) {
mNavigationBarOrientation = LinearLayout.HORIZONTAL;
} else {
mNavigationBarOrientation = LinearLayout.VERTICAL;
}
// get the real value
ResourceValue value = resources.getFrameworkResource(ResourceType.DIMEN,
barOnBottom ? "navigation_bar_height" : "navigation_bar_width");
if (value != null) {
TypedValue typedValue = ResourceHelper.getValue("navigation_bar_height",
value.getValue(), true /*requireUnit*/);
if (typedValue != null) {
// compute the pixel value based on the display metrics
mNavigationBarSize = (int)typedValue.getDimension(metrics);
}
}
}
}
private boolean isThemeAppCompat(RenderResources resources) {
// Ideally, we should check if the corresponding activity extends
// android.support.v7.app.ActionBarActivity, and not care about the theme name at all.
if (mIsThemeAppCompat == null) {
StyleResourceValue defaultTheme = resources.getDefaultTheme();
// 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++) {
if (defaultTheme == null) {
break;
}
// 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);
}
mIsThemeAppCompat = isThemeAppCompat;
}
return mIsThemeAppCompat;
}
/**
* Looks for an attribute in the current theme.
*
* @param resources the render resources
* @param name the name of the attribute
* @param defaultValue the default value.
* @param isFrameworkAttr if the attribute is in android namespace
* @return the value of the attribute or the default one if not found.
*/
private boolean getBooleanThemeValue(RenderResources resources,
String name, boolean defaultValue, boolean isFrameworkAttr) {
ResourceValue value = resources.findItemInTheme(name, isFrameworkAttr);
// because it may reference something else, we resolve it.
value = resources.resolveResValue(value);
// if there's no value, return the default.
if (value == null || value.getValue() == null) {
return defaultValue;
}
return XmlUtils.convertValueToBoolean(value.getValue(), defaultValue);
}
/**
* Post process on a view hierarchy that was just inflated.
* <p/>
@@ -1737,63 +1349,6 @@ public class RenderSessionImpl extends RenderAction<SessionParams> {
mMeasuredScreenWidth = mMeasuredScreenHeight = -1;
}
/**
* Creates the status bar with wifi and battery icons.
*/
private StatusBar createStatusBar(BridgeContext context, Density density, int direction,
boolean isRtlSupported, int platformVersion) throws XmlPullParserException {
StatusBar statusBar = new StatusBar(context, density,
direction, isRtlSupported, platformVersion);
statusBar.setLayoutParams(
new LinearLayout.LayoutParams(
LayoutParams.MATCH_PARENT, mStatusBarSize));
return statusBar;
}
/**
* Creates the navigation bar with back, home and recent buttons.
*
* @param isRtl true if the current locale is right-to-left
* @param isRtlSupported true is the project manifest declares that the application
* is RTL aware.
*/
private NavigationBar createNavigationBar(BridgeContext context, Density density,
boolean isRtl, boolean isRtlSupported, int simulatedPlatformVersion)
throws XmlPullParserException {
NavigationBar navigationBar = new NavigationBar(context,
density, mNavigationBarOrientation, isRtl,
isRtlSupported, simulatedPlatformVersion);
if (mNavigationBarOrientation == LinearLayout.VERTICAL) {
navigationBar.setLayoutParams(new LinearLayout.LayoutParams(mNavigationBarSize,
LayoutParams.MATCH_PARENT));
} else {
navigationBar.setLayoutParams(new LinearLayout.LayoutParams(LayoutParams.MATCH_PARENT,
mNavigationBarSize));
}
return navigationBar;
}
private TitleBar createTitleBar(BridgeContext context, String title,
int simulatedPlatformVersion)
throws XmlPullParserException {
TitleBar titleBar = new TitleBar(context, title, simulatedPlatformVersion);
titleBar.setLayoutParams(
new LinearLayout.LayoutParams(LayoutParams.MATCH_PARENT, mTitleBarSize));
return titleBar;
}
/**
* Creates the action bar. Also queries the project callback for missing information.
*/
private BridgeActionBar createActionBar(BridgeContext context, SessionParams params,
ViewGroup parentView) {
if (mIsThemeAppCompat == Boolean.TRUE) {
return new AppCompatActionBar(context, params, parentView);
} else {
return new FrameworkActionBar(context, params, parentView);
}
}
public BufferedImage getImage() {
return mImage;
}

View File

@@ -21,6 +21,7 @@ import com.android.ide.common.rendering.api.DensityBasedResourceValue;
import com.android.ide.common.rendering.api.LayoutLog;
import com.android.ide.common.rendering.api.RenderResources;
import com.android.ide.common.rendering.api.ResourceValue;
import com.android.internal.util.XmlUtils;
import com.android.layoutlib.bridge.Bridge;
import com.android.layoutlib.bridge.android.BridgeContext;
import com.android.layoutlib.bridge.android.BridgeXmlBlockParser;
@@ -327,6 +328,25 @@ public final class ResourceHelper {
return null;
}
/**
* Looks for an attribute in the current theme.
*
* @param resources the render resources
* @param name the name of the attribute
* @param defaultValue the default value.
* @param isFrameworkAttr if the attribute is in android namespace
* @return the value of the attribute or the default one if not found.
*/
public static boolean getBooleanThemeValue(@NonNull RenderResources resources, String name,
boolean isFrameworkAttr, boolean defaultValue) {
ResourceValue value = resources.findItemInTheme(name, isFrameworkAttr);
value = resources.resolveResValue(value);
if (value == null) {
return defaultValue;
}
return XmlUtils.convertValueToBoolean(value.getValue(), defaultValue);
}
// ------- TypedValue stuff
// This is taken from //device/libs/utils/ResourceTypes.cpp