/* * Copyright (C) 2010 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 static com.android.ide.common.rendering.api.Result.Status.ERROR_ANIM_NOT_FOUND; import static com.android.ide.common.rendering.api.Result.Status.ERROR_INFLATION; import static com.android.ide.common.rendering.api.Result.Status.ERROR_NOT_INFLATED; import static com.android.ide.common.rendering.api.Result.Status.ERROR_UNKNOWN; import static com.android.ide.common.rendering.api.Result.Status.ERROR_VIEWGROUP_NO_CHILDREN; import static com.android.ide.common.rendering.api.Result.Status.SUCCESS; import com.android.ide.common.rendering.api.AdapterBinding; import com.android.ide.common.rendering.api.HardwareConfig; import com.android.ide.common.rendering.api.IAnimationListener; import com.android.ide.common.rendering.api.ILayoutPullParser; import com.android.ide.common.rendering.api.IProjectCallback; import com.android.ide.common.rendering.api.RenderResources; import com.android.ide.common.rendering.api.RenderSession; import com.android.ide.common.rendering.api.ResourceReference; import com.android.ide.common.rendering.api.ResourceValue; 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.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.ActionMenuView; import com.android.internal.view.menu.BridgeMenuItemImpl; import com.android.internal.view.menu.IconMenuItemView; import com.android.internal.view.menu.ListMenuItemView; import com.android.internal.view.menu.MenuItemImpl; import com.android.internal.view.menu.MenuView; import com.android.layoutlib.bridge.Bridge; import com.android.layoutlib.bridge.android.BridgeContext; import com.android.layoutlib.bridge.android.BridgeLayoutParamsMapAttributes; import com.android.layoutlib.bridge.android.BridgeXmlBlockParser; 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.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; import android.animation.LayoutTransition; import android.animation.LayoutTransition.TransitionListener; 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.util.DisplayMetrics; import android.util.TypedValue; import android.view.AttachInfo_Accessor; import android.view.BridgeInflater; import android.view.IWindowManager; import android.view.IWindowManagerImpl; import android.view.Surface; import android.view.View; import android.view.View.MeasureSpec; import android.view.ViewGroup; import android.view.ViewGroup.LayoutParams; import android.view.ViewGroup.MarginLayoutParams; import android.view.ViewParent; import android.view.WindowManagerGlobal_Delegate; import android.view.ViewParent; import android.widget.AbsListView; import android.widget.AbsSpinner; import android.widget.AdapterView; import android.widget.ExpandableListView; import android.widget.FrameLayout; import android.widget.LinearLayout; import android.widget.ListView; import android.widget.QuickContactBadge; import android.widget.TabHost; import android.widget.TabHost.TabSpec; import android.widget.TabWidget; import java.awt.AlphaComposite; import java.awt.Color; import java.awt.Graphics2D; import java.awt.image.BufferedImage; import java.util.ArrayList; import java.util.List; import java.util.Map; /** * Class implementing the render session. *
* A session is a stateful representation of a layout file. It is initialized with data coming * through the {@link Bridge} API to inflate the layout. Further actions and rendering can then * be done on the layout. */ public class RenderSessionImpl extends RenderAction* This must be followed by a call to {@link RenderSessionImpl#init(long)}, * which act as a * call to {@link RenderSessionImpl#acquire(long)} * * @see Bridge#createSession(SessionParams) */ public RenderSessionImpl(SessionParams params) { super(new SessionParams(params)); } /** * Initializes and acquires the scene, creating various Android objects such as context, * inflater, and parser. * * @param timeout the time to wait if another rendering is happening. * * @return whether the scene was prepared * * @see #acquire(long) * @see #release() */ @Override public Result init(long timeout) { Result result = super.init(timeout); if (!result.isSuccess()) { return result; } 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 /*defaultValue*/); mWindowIsFloating = getBooleanThemeValue(resources, "windowIsFloating", true /*defaultValue*/); findBackground(resources); findStatusBar(resources, metrics); findActionBar(resources, metrics); findNavigationBar(resources, metrics); // 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); WindowManagerGlobal_Delegate.setWindowManagerService(iwm); // build the inflater and parser. mInflater = new BridgeInflater(context, params.getProjectCallback()); context.setBridgeInflater(mInflater); mBlockParser = new BridgeXmlBlockParser( params.getLayoutDescription(), context, false /* platformResourceFlag */); return SUCCESS.createResult(); } /** * Inflates the layout. *
* {@link #acquire(long)} must have been called before this. * * @throws IllegalStateException if the current context is different than the one owned by * the scene, or if {@link #init(long)} was not called. */ public Result inflate() { checkLock(); try { 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 { 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); try { NavigationBar navigationBar = createNavigationBar(context, hardwareConfig.getDensity(), isRtl, params.isRtlSupported()); 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()); 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) { ActionBarLayout actionBar = createActionBar(context, params); backgroundLayout.addView(actionBar); actionBar.createMenuPopup(); mContentRoot = actionBar.getContentRoot(); } else if (mTitleBarSize > 0) { try { TitleBar titleBar = createTitleBar(context, hardwareConfig.getDensity(), params.getAppLabel()); 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 (mNavigationBarOrientation == LinearLayout.HORIZONTAL && mNavigationBarSize > 0) { // system bar try { NavigationBar navigationBar = createNavigationBar(context, hardwareConfig.getDensity(), isRtl, params.isRtlSupported()); 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. Fragment_Delegate.setProjectCallback(params.getProjectCallback()); View view = mInflater.inflate(mBlockParser, mContentRoot); // done with the parser, pop it. context.popParser(); Fragment_Delegate.setProjectCallback(null); // set the AttachInfo on the root view. AttachInfo_Accessor.setAttachInfo(mViewRoot); // post-inflate process. For now this supports TabHost/TabWidget postInflateProcess(view, params.getProjectCallback()); // 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); } catch (Throwable e) { // get the real cause of the exception. Throwable t = e; while (t.getCause() != null) { t = t.getCause(); } return ERROR_INFLATION.createResult(t.getMessage(), t); } } /** * Renders the scene. *
* {@link #acquire(long)} must have been called before this.
*
* @param freshRender whether the render is a new one and should erase the existing bitmap (in
* the case where bitmaps are reused). This is typically needed when not playing
* animations.)
*
* @throws IllegalStateException if the current context is different than the one owned by
* the scene, or if {@link #acquire(long)} was not called.
*
* @see SessionParams#getRenderingMode()
* @see RenderSession#render(long)
*/
public Result render(boolean freshRender) {
checkLock();
SessionParams params = getParams();
try {
if (mViewRoot == null) {
return ERROR_NOT_INFLATED.createResult();
}
RenderingMode renderingMode = params.getRenderingMode();
HardwareConfig hardwareConfig = params.getHardwareConfig();
// only do the screen measure when needed.
boolean newRenderSize = false;
if (mMeasuredScreenWidth == -1) {
newRenderSize = true;
mMeasuredScreenWidth = hardwareConfig.getScreenWidth();
mMeasuredScreenHeight = hardwareConfig.getScreenHeight();
if (renderingMode != RenderingMode.NORMAL) {
int widthMeasureSpecMode = renderingMode.isHorizExpand() ?
MeasureSpec.UNSPECIFIED // this lets us know the actual needed size
: MeasureSpec.EXACTLY;
int heightMeasureSpecMode = renderingMode.isVertExpand() ?
MeasureSpec.UNSPECIFIED // this lets us know the actual needed size
: MeasureSpec.EXACTLY;
// We used to compare the measured size of the content to the screen size but
// this does not work anymore due to the 2 following issues:
// - If the content is in a decor (system bar, title/action bar), the root view
// will not resize even with the UNSPECIFIED because of the embedded layout.
// - If there is no decor, but a dialog frame, then the dialog padding prevents
// comparing the size of the content to the screen frame (as it would not
// take into account the dialog padding).
// The solution is to first get the content size in a normal rendering, inside
// the decor or the dialog padding.
// Then measure only the content with UNSPECIFIED to see the size difference
// and apply this to the screen size.
// first measure the full layout, with EXACTLY to get the size of the
// content as it is inside the decor/dialog
@SuppressWarnings("deprecation")
Pair
* {@link #acquire(long)} must have been called before this.
*
* @throws IllegalStateException if the current context is different than the one owned by
* the scene, or if {@link #acquire(long)} was not called.
*
* @see RenderSession#animate(Object, String, boolean, IAnimationListener)
*/
public Result animate(Object targetObject, String animationName,
boolean isFrameworkAnimation, IAnimationListener listener) {
checkLock();
BridgeContext context = getContext();
// find the animation file.
ResourceValue animationResource;
int animationId = 0;
if (isFrameworkAnimation) {
animationResource = context.getRenderResources().getFrameworkResource(
ResourceType.ANIMATOR, animationName);
if (animationResource != null) {
animationId = Bridge.getResourceId(ResourceType.ANIMATOR, animationName);
}
} else {
animationResource = context.getRenderResources().getProjectResource(
ResourceType.ANIMATOR, animationName);
if (animationResource != null) {
animationId = context.getProjectCallback().getResourceId(
ResourceType.ANIMATOR, animationName);
}
}
if (animationResource != null) {
try {
Animator anim = AnimatorInflater.loadAnimator(context, animationId);
if (anim != null) {
anim.setTarget(targetObject);
new PlayAnimationThread(anim, this, animationName, listener).start();
return SUCCESS.createResult();
}
} catch (Exception e) {
// get the real cause of the exception.
Throwable t = e;
while (t.getCause() != null) {
t = t.getCause();
}
return ERROR_UNKNOWN.createResult(t.getMessage(), t);
}
}
return ERROR_ANIM_NOT_FOUND.createResult();
}
/**
* Insert a new child into an existing parent.
*
* {@link #acquire(long)} must have been called before this.
*
* @throws IllegalStateException if the current context is different than the one owned by
* the scene, or if {@link #acquire(long)} was not called.
*
* @see RenderSession#insertChild(Object, ILayoutPullParser, int, IAnimationListener)
*/
public Result insertChild(final ViewGroup parentView, ILayoutPullParser childXml,
final int index, IAnimationListener listener) {
checkLock();
BridgeContext context = getContext();
// create a block parser for the XML
BridgeXmlBlockParser blockParser = new BridgeXmlBlockParser(
childXml, context, false /* platformResourceFlag */);
// inflate the child without adding it to the root since we want to control where it'll
// get added. We do pass the parentView however to ensure that the layoutParams will
// be created correctly.
final View child = mInflater.inflate(blockParser, parentView, false /*attachToRoot*/);
blockParser.ensurePopped();
invalidateRenderingSize();
if (listener != null) {
new AnimationThread(this, "insertChild", listener) {
@Override
public Result preAnimation() {
parentView.setLayoutTransition(new LayoutTransition());
return addView(parentView, child, index);
}
@Override
public void postAnimation() {
parentView.setLayoutTransition(null);
}
}.start();
// always return success since the real status will come through the listener.
return SUCCESS.createResult(child);
}
// add it to the parentView in the correct location
Result result = addView(parentView, child, index);
if (!result.isSuccess()) {
return result;
}
result = render(false /*freshRender*/);
if (result.isSuccess()) {
result = result.getCopyWithData(child);
}
return result;
}
/**
* Adds a given view to a given parent at a given index.
*
* @param parent the parent to receive the view
* @param view the view to add to the parent
* @param index the index where to do the add.
*
* @return a Result with {@link Status#SUCCESS} or
* {@link Status#ERROR_VIEWGROUP_NO_CHILDREN} if the given parent doesn't support
* adding views.
*/
private Result addView(ViewGroup parent, View view, int index) {
try {
parent.addView(view, index);
return SUCCESS.createResult();
} catch (UnsupportedOperationException e) {
// looks like this is a view class that doesn't support children manipulation!
return ERROR_VIEWGROUP_NO_CHILDREN.createResult();
}
}
/**
* Moves a view to a new parent at a given location
*
* {@link #acquire(long)} must have been called before this.
*
* @throws IllegalStateException if the current context is different than the one owned by
* the scene, or if {@link #acquire(long)} was not called.
*
* @see RenderSession#moveChild(Object, Object, int, Map, IAnimationListener)
*/
public Result moveChild(final ViewGroup newParentView, final View childView, final int index,
Map
* {@link #acquire(long)} must have been called before this.
*
* @throws IllegalStateException if the current context is different than the one owned by
* the scene, or if {@link #acquire(long)} was not called.
*
* @see RenderSession#removeChild(Object, IAnimationListener)
*/
public Result removeChild(final View childView, IAnimationListener listener) {
checkLock();
invalidateRenderingSize();
final ViewGroup parent = (ViewGroup) childView.getParent();
if (listener != null) {
new AnimationThread(this, "moveChild", listener) {
@Override
public Result preAnimation() {
parent.setLayoutTransition(new LayoutTransition());
return removeView(parent, childView);
}
@Override
public void postAnimation() {
parent.setLayoutTransition(null);
}
}.start();
// always return success since the real status will come through the listener.
return SUCCESS.createResult();
}
Result result = removeView(parent, childView);
if (!result.isSuccess()) {
return result;
}
return render(false /*freshRender*/);
}
/**
* Removes a given view from its current parent.
*
* @param view the view to remove from its parent
*
* @return a Result with {@link Status#SUCCESS} or
* {@link Status#ERROR_VIEWGROUP_NO_CHILDREN} if the given parent doesn't support
* adding views.
*/
private Result removeView(ViewGroup parent, View view) {
try {
parent.removeView(view);
return SUCCESS.createResult();
} catch (UnsupportedOperationException e) {
// looks like this is a view class that doesn't support children manipulation!
return ERROR_VIEWGROUP_NO_CHILDREN.createResult();
}
}
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 /*defaultValue*/);
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 /*defaultValue*/);
// 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 /*defaultValue*/);
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);
}
}
}
}
/**
* Looks for a attribute in the current theme. The attribute is in the android
* namespace.
*
* @param resources the render resources
* @param name the name of the attribute
* @param defaultValue the default value.
* @return the value of the attribute or the default one if not found.
*/
private boolean getBooleanThemeValue(RenderResources resources,
String name, boolean defaultValue) {
// get the title bar flag from the current theme.
ResourceValue value = resources.findItemInTheme(name, true /*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.
*