Merge "LayoutLib: render system/title/action bars." into honeycomb

This commit is contained in:
Xavier Ducrohet
2011-02-04 11:42:51 -08:00
committed by Android (Google) Code Review
21 changed files with 771 additions and 202 deletions

View File

@@ -17,6 +17,8 @@ LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS) include $(CLEAR_VARS)
LOCAL_SRC_FILES := $(call all-java-files-under,src) LOCAL_SRC_FILES := $(call all-java-files-under,src)
LOCAL_JAVA_RESOURCE_DIRS := resources
LOCAL_JAVA_LIBRARIES := \ LOCAL_JAVA_LIBRARIES := \
kxml2-2.3.0 \ kxml2-2.3.0 \

View File

@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<merge xmlns:android="http://schemas.android.com/apk/res/android">
<ImageView
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:layout_gravity="center"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"/>
</merge>

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 885 B

View File

@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<merge xmlns:android="http://schemas.android.com/apk/res/android">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text=" "/>
<ImageView
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:layout_gravity="center"/>
</merge>

View File

@@ -0,0 +1,20 @@
<?xml version="1.0" encoding="utf-8"?>
<merge xmlns:android="http://schemas.android.com/apk/res/android">
<ImageView
android:layout_height="wrap_content"
android:layout_width="wrap_content"/>
<ImageView
android:layout_height="wrap_content"
android:layout_width="wrap_content"/>
<ImageView
android:layout_height="wrap_content"
android:layout_width="wrap_content"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"/>
<ImageView
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:layout_gravity="center"/>
</merge>

View File

@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<merge xmlns:android="http://schemas.android.com/apk/res/android">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"/>
</merge>

View File

@@ -18,6 +18,9 @@ package android.graphics;
import com.android.ide.common.rendering.api.LayoutLog; import com.android.ide.common.rendering.api.LayoutLog;
import com.android.layoutlib.bridge.Bridge; import com.android.layoutlib.bridge.Bridge;
import com.android.layoutlib.bridge.android.BridgeResources.NinePatchInputStream;
import com.android.ninepatch.NinePatch;
import com.android.ninepatch.NinePatchChunk;
import com.android.resources.Density; import com.android.resources.Density;
import android.content.res.AssetManager; import android.content.res.AssetManager;
@@ -438,6 +441,8 @@ public class BitmapFactory {
return null; return null;
} }
boolean isNinePatch = is instanceof NinePatchInputStream;
// we need mark/reset to work properly // we need mark/reset to work properly
if (!is.markSupported()) { if (!is.markSupported()) {
@@ -466,7 +471,29 @@ public class BitmapFactory {
if (opts != null) { if (opts != null) {
density = Density.getEnum(opts.inDensity); density = Density.getEnum(opts.inDensity);
} }
bm = Bitmap_Delegate.createBitmap(is, true, density);
if (isNinePatch) {
// load the bitmap as a nine patch
NinePatch ninePatch = NinePatch.load(is, true /*is9Patch*/, false /*convert*/);
// get the bitmap and chunk objects.
bm = Bitmap_Delegate.createBitmap(ninePatch.getImage(), true /*isMutable*/,
density);
NinePatchChunk chunk = ninePatch.getChunk();
// put the chunk in the bitmap
bm.setNinePatchChunk(NinePatch_Delegate.serialize(chunk));
// read the padding
int[] padding = chunk.getPadding();
outPadding.left = padding[0];
outPadding.top = padding[1];
outPadding.right = padding[2];
outPadding.bottom = padding[3];
} else {
// load the bitmap directly.
bm = Bitmap_Delegate.createBitmap(is, true, density);
}
} catch (IOException e) { } catch (IOException e) {
return null; return null;
} }

View File

@@ -21,7 +21,7 @@ import static com.android.ide.common.rendering.api.Result.Status.SUCCESS;
import com.android.ide.common.rendering.api.Capability; import com.android.ide.common.rendering.api.Capability;
import com.android.ide.common.rendering.api.LayoutLog; import com.android.ide.common.rendering.api.LayoutLog;
import com.android.ide.common.rendering.api.Params; import com.android.ide.common.rendering.api.RenderParams;
import com.android.ide.common.rendering.api.RenderSession; import com.android.ide.common.rendering.api.RenderSession;
import com.android.ide.common.rendering.api.Result; import com.android.ide.common.rendering.api.Result;
import com.android.layoutlib.bridge.android.BridgeAssetManager; import com.android.layoutlib.bridge.android.BridgeAssetManager;
@@ -293,15 +293,15 @@ public final class Bridge extends com.android.ide.common.rendering.api.Bridge {
/** /**
* Starts a layout session by inflating and rendering it. The method returns a * Starts a layout session by inflating and rendering it. The method returns a
* {@link ILayoutScene} on which further actions can be taken. * {@link RenderSession} on which further actions can be taken.
* *
* @param params the {@link SceneParams} object with all the information necessary to create * @param params the {@link RenderParams} object with all the information necessary to create
* the scene. * the scene.
* @return a new {@link ILayoutScene} object that contains the result of the layout. * @return a new {@link RenderSession} object that contains the result of the layout.
* @since 5 * @since 5
*/ */
@Override @Override
public RenderSession createSession(Params params) { public RenderSession createSession(RenderParams params) {
try { try {
Result lastResult = SUCCESS.createResult(); Result lastResult = SUCCESS.createResult();
RenderSessionImpl scene = new RenderSessionImpl(params); RenderSessionImpl scene = new RenderSessionImpl(params);
@@ -331,10 +331,6 @@ public final class Bridge extends com.android.ide.common.rendering.api.Bridge {
} }
} }
/*
* (non-Javadoc)
* @see com.android.layoutlib.api.ILayoutLibBridge#clearCaches(java.lang.Object)
*/
@Override @Override
public void clearCaches(Object projectKey) { public void clearCaches(Object projectKey) {
if (projectKey != null) { if (projectKey != null) {

View File

@@ -18,7 +18,7 @@ package com.android.layoutlib.bridge;
import com.android.ide.common.rendering.api.IAnimationListener; import com.android.ide.common.rendering.api.IAnimationListener;
import com.android.ide.common.rendering.api.ILayoutPullParser; import com.android.ide.common.rendering.api.ILayoutPullParser;
import com.android.ide.common.rendering.api.Params; import com.android.ide.common.rendering.api.RenderParams;
import com.android.ide.common.rendering.api.RenderSession; import com.android.ide.common.rendering.api.RenderSession;
import com.android.ide.common.rendering.api.Result; import com.android.ide.common.rendering.api.Result;
import com.android.ide.common.rendering.api.ViewInfo; import com.android.ide.common.rendering.api.ViewInfo;
@@ -128,7 +128,7 @@ public class BridgeRenderSession extends RenderSession {
boolean isFrameworkAnimation, IAnimationListener listener) { boolean isFrameworkAnimation, IAnimationListener listener) {
try { try {
Bridge.prepareThread(); Bridge.prepareThread();
mLastResult = mSession.acquire(Params.DEFAULT_TIMEOUT); mLastResult = mSession.acquire(RenderParams.DEFAULT_TIMEOUT);
if (mLastResult.isSuccess()) { if (mLastResult.isSuccess()) {
mLastResult = mSession.animate(targetObject, animationName, isFrameworkAnimation, mLastResult = mSession.animate(targetObject, animationName, isFrameworkAnimation,
listener); listener);
@@ -150,7 +150,7 @@ public class BridgeRenderSession extends RenderSession {
try { try {
Bridge.prepareThread(); Bridge.prepareThread();
mLastResult = mSession.acquire(Params.DEFAULT_TIMEOUT); mLastResult = mSession.acquire(RenderParams.DEFAULT_TIMEOUT);
if (mLastResult.isSuccess()) { if (mLastResult.isSuccess()) {
mLastResult = mSession.insertChild((ViewGroup) parentView, childXml, index, mLastResult = mSession.insertChild((ViewGroup) parentView, childXml, index,
listener); listener);
@@ -176,7 +176,7 @@ public class BridgeRenderSession extends RenderSession {
try { try {
Bridge.prepareThread(); Bridge.prepareThread();
mLastResult = mSession.acquire(Params.DEFAULT_TIMEOUT); mLastResult = mSession.acquire(RenderParams.DEFAULT_TIMEOUT);
if (mLastResult.isSuccess()) { if (mLastResult.isSuccess()) {
mLastResult = mSession.moveChild((ViewGroup) parentView, (View) childView, index, mLastResult = mSession.moveChild((ViewGroup) parentView, (View) childView, index,
layoutParams, listener); layoutParams, listener);
@@ -197,7 +197,7 @@ public class BridgeRenderSession extends RenderSession {
try { try {
Bridge.prepareThread(); Bridge.prepareThread();
mLastResult = mSession.acquire(Params.DEFAULT_TIMEOUT); mLastResult = mSession.acquire(RenderParams.DEFAULT_TIMEOUT);
if (mLastResult.isSuccess()) { if (mLastResult.isSuccess()) {
mLastResult = mSession.removeChild((View) childView, listener); mLastResult = mSession.removeChild((View) childView, listener);
} }

View File

@@ -22,6 +22,7 @@ import com.android.ide.common.rendering.api.ResourceValue;
import com.android.layoutlib.bridge.Bridge; import com.android.layoutlib.bridge.Bridge;
import com.android.layoutlib.bridge.BridgeConstants; import com.android.layoutlib.bridge.BridgeConstants;
import com.android.layoutlib.bridge.impl.ResourceHelper; import com.android.layoutlib.bridge.impl.ResourceHelper;
import com.android.ninepatch.NinePatch;
import com.android.resources.ResourceType; import com.android.resources.ResourceType;
import com.android.util.Pair; import com.android.util.Pair;
@@ -57,6 +58,18 @@ public final class BridgeResources extends Resources {
private IProjectCallback mProjectCallback; private IProjectCallback mProjectCallback;
private boolean[] mPlatformResourceFlag = new boolean[1]; private boolean[] mPlatformResourceFlag = new boolean[1];
/**
* Simpler wrapper around FileInputStream. This is used when the input stream represent
* not a normal bitmap but a nine patch.
* This is useful when the InputStream is created in a method but used in another that needs
* to know whether this is 9-patch or not, such as BitmapFactory.
*/
public class NinePatchInputStream extends FileInputStream {
public NinePatchInputStream(File file) throws FileNotFoundException {
super(file);
}
}
/** /**
* This initializes the static field {@link Resources#mSystem} which is used * This initializes the static field {@link Resources#mSystem} which is used
* by methods who get global resources using {@link Resources#getSystem()}. * by methods who get global resources using {@link Resources#getSystem()}.
@@ -129,7 +142,7 @@ public final class BridgeResources extends Resources {
ResourceValue value = getResourceValue(id, mPlatformResourceFlag); ResourceValue value = getResourceValue(id, mPlatformResourceFlag);
if (value != null) { if (value != null) {
return ResourceHelper.getDrawable(value, mContext, value.isFramework()); return ResourceHelper.getDrawable(value, mContext);
} }
// id was not found or not resolved. Throw a NotFoundException. // id was not found or not resolved. Throw a NotFoundException.
@@ -165,44 +178,9 @@ public final class BridgeResources extends Resources {
ResourceValue resValue = getResourceValue(id, mPlatformResourceFlag); ResourceValue resValue = getResourceValue(id, mPlatformResourceFlag);
if (resValue != null) { if (resValue != null) {
String value = resValue.getValue(); ColorStateList stateList = ResourceHelper.getColorStateList(resValue, mContext);
if (value != null) { if (stateList != null) {
// first check if the value is a file (xml most likely) return stateList;
File f = new File(value);
if (f.isFile()) {
try {
// let the framework inflate the ColorStateList from the XML file, by
// providing an XmlPullParser
KXmlParser parser = new KXmlParser();
parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, true);
parser.setInput(new FileReader(f));
return ColorStateList.createFromXml(this,
new BridgeXmlBlockParser(parser, mContext, resValue.isFramework()));
} catch (XmlPullParserException e) {
Bridge.getLog().error(LayoutLog.TAG_BROKEN,
"Failed to configure parser for " + value, e, null /*data*/);
// we'll return null below.
} catch (Exception e) {
// this is an error and not warning since the file existence is
// checked before attempting to parse it.
Bridge.getLog().error(LayoutLog.TAG_RESOURCES_READ,
"Failed to parse file " + value, e, null /*data*/);
return null;
}
} else {
// try to load the color state list from an int
try {
int color = ResourceHelper.getColor(value);
return ColorStateList.valueOf(color);
} catch (NumberFormatException e) {
Bridge.getLog().error(LayoutLog.TAG_RESOURCES_FORMAT,
"Failed to convert " + value + " into a ColorStateList", e,
null /*data*/);
return null;
}
}
} }
} }
@@ -562,13 +540,19 @@ public final class BridgeResources extends Resources {
ResourceValue value = getResourceValue(id, mPlatformResourceFlag); ResourceValue value = getResourceValue(id, mPlatformResourceFlag);
if (value != null) { if (value != null) {
String v = value.getValue(); String path = value.getValue();
if (v != null) { if (path != null) {
// check this is a file // check this is a file
File f = new File(value.getValue()); File f = new File(path);
if (f.isFile()) { if (f.isFile()) {
try { try {
// if it's a nine-patch return a custom input stream so that
// other methods (mainly bitmap factory) can detect it's a 9-patch
// and actually load it as a 9-patch instead of a normal bitmap
if (path.toLowerCase().endsWith(NinePatch.EXTENSION_9PATCH)) {
return new NinePatchInputStream(f);
}
return new FileInputStream(f); return new FileInputStream(f);
} catch (FileNotFoundException e) { } catch (FileNotFoundException e) {
NotFoundException newE = new NotFoundException(); NotFoundException newE = new NotFoundException();
@@ -590,9 +574,17 @@ public final class BridgeResources extends Resources {
public InputStream openRawResource(int id, TypedValue value) throws NotFoundException { public InputStream openRawResource(int id, TypedValue value) throws NotFoundException {
getValue(id, value, true); getValue(id, value, true);
File f = new File(value.string.toString()); String path = value.string.toString();
File f = new File(path);
if (f.isFile()) { if (f.isFile()) {
try { try {
// if it's a nine-patch return a custom input stream so that
// other methods (mainly bitmap factory) can detect it's a 9-patch
// and actually load it as a 9-patch instead of a normal bitmap
if (path.toLowerCase().endsWith(NinePatch.EXTENSION_9PATCH)) {
return new NinePatchInputStream(f);
}
return new FileInputStream(f); return new FileInputStream(f);
} catch (FileNotFoundException e) { } catch (FileNotFoundException e) {
NotFoundException exception = new NotFoundException(); NotFoundException exception = new NotFoundException();

View File

@@ -690,7 +690,7 @@ public final class BridgeTypedArray extends TypedArray {
return null; return null;
} }
return ResourceHelper.getDrawable(value, mContext, mResourceData[index].isFramework()); return ResourceHelper.getDrawable(value, mContext);
} }

View File

@@ -0,0 +1,212 @@
/*
* Copyright (C) 2011 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.ide.common.rendering.api.RenderResources;
import com.android.ide.common.rendering.api.ResourceValue;
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.android.BridgeXmlBlockParser;
import com.android.layoutlib.bridge.impl.ResourceHelper;
import com.android.resources.Density;
import org.kxml2.io.KXmlParser;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import android.content.Context;
import android.content.res.ColorStateList;
import android.graphics.Bitmap;
import android.graphics.Bitmap_Delegate;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.util.TypedValue;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
import java.io.IOException;
import java.io.InputStream;
/**
* Base "bar" class for the window decor around the the edited layout.
* This is basically an horizontal layout that loads a given layout on creation (it is read
* through {@link Class#getResourceAsStream(String)}).
*
* The given layout should be a merge layout so that all the children belong to this class directly.
*
* It also provides a few utility methods to configure the content of the layout.
*/
abstract class CustomBar extends LinearLayout {
protected abstract TextView getStyleableTextView();
protected CustomBar(Context context, Density density, String layoutPath)
throws XmlPullParserException {
super(context);
setOrientation(LinearLayout.HORIZONTAL);
setBackgroundColor(0xFF000000);
LayoutInflater inflater = (LayoutInflater) getContext().getSystemService(
Context.LAYOUT_INFLATER_SERVICE);
KXmlParser parser = new KXmlParser();
parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, true);
parser.setInput(
getClass().getResourceAsStream(layoutPath),
"UTF8");
BridgeXmlBlockParser bridgeParser = new BridgeXmlBlockParser(
parser, (BridgeContext) context, false);
inflater.inflate(bridgeParser, this, true);
}
protected void loadIcon(int index, String iconName, Density density) {
View child = getChildAt(index);
if (child instanceof ImageView) {
ImageView imageView = (ImageView) child;
// bitmap url relative to this class
String path = "/bars/" + density.getResourceValue() + "/" + iconName;
// create a bitmap
Bitmap bitmap = Bridge.getCachedBitmap(path, true /*isFramework*/);
if (bitmap == null) {
InputStream stream = getClass().getResourceAsStream(path);
if (stream != null) {
try {
bitmap = Bitmap_Delegate.createBitmap(stream, false /*isMutable*/, density);
Bridge.setCachedBitmap(path, bitmap, true /*isFramework*/);
} catch (IOException e) {
return;
}
}
}
if (bitmap != null) {
BitmapDrawable drawable = new BitmapDrawable(getContext().getResources(), bitmap);
imageView.setBackgroundDrawable(drawable);
}
}
}
protected void loadIcon(int index, String iconReference) {
ResourceValue value = getResourceValue(iconReference);
if (value != null) {
View child = getChildAt(index);
if (child instanceof ImageView) {
ImageView imageView = (ImageView) child;
Drawable drawable = ResourceHelper.getDrawable(
value, (BridgeContext) mContext);
if (drawable != null) {
imageView.setBackgroundDrawable(drawable);
}
}
}
}
protected TextView setText(int index, String stringReference) {
View child = getChildAt(index);
if (child instanceof TextView) {
TextView textView = (TextView) child;
ResourceValue value = getResourceValue(stringReference);
if (value != null) {
textView.setText(value.getValue());
} else {
textView.setText(stringReference);
}
return textView;
}
return null;
}
protected void setStyle(String themeEntryName) {
BridgeContext bridgeContext = (BridgeContext) mContext;
RenderResources res = bridgeContext.getRenderResources();
ResourceValue value = res.findItemInTheme(themeEntryName);
value = res.resolveResValue(value);
if (value instanceof StyleResourceValue == false) {
return;
}
StyleResourceValue style = (StyleResourceValue) value;
// get the background
ResourceValue backgroundValue = res.findItemInStyle(style, "background");
backgroundValue = res.resolveResValue(backgroundValue);
if (backgroundValue != null) {
Drawable d = ResourceHelper.getDrawable(backgroundValue, bridgeContext);
if (d != null) {
setBackgroundDrawable(d);
}
}
TextView textView = getStyleableTextView();
if (textView != null) {
// get the text style
ResourceValue textStyleValue = res.findItemInStyle(style, "titleTextStyle");
textStyleValue = res.resolveResValue(textStyleValue);
if (textStyleValue instanceof StyleResourceValue) {
StyleResourceValue textStyle = (StyleResourceValue) textStyleValue;
ResourceValue textSize = res.findItemInStyle(textStyle, "textSize");
textSize = res.resolveResValue(textSize);
if (textSize != null) {
TypedValue out = new TypedValue();
if (ResourceHelper.stringToFloat(textSize.getValue(), out)) {
textView.setTextSize(
out.getDimension(bridgeContext.getResources().mMetrics));
}
}
ResourceValue textColor = res.findItemInStyle(textStyle, "textColor");
textColor = res.resolveResValue(textColor);
if (textColor != null) {
ColorStateList stateList = ResourceHelper.getColorStateList(
textColor, bridgeContext);
if (stateList != null) {
textView.setTextColor(stateList);
}
}
}
}
}
private ResourceValue getResourceValue(String reference) {
BridgeContext bridgeContext = (BridgeContext) mContext;
RenderResources res = bridgeContext.getRenderResources();
// find the resource
ResourceValue value = res.findResValue(reference, false /*isFramework*/);
// resolve it if needed
return res.resolveResValue(value);
}
}

View File

@@ -0,0 +1,47 @@
/*
* Copyright (C) 2011 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.resources.Density;
import org.xmlpull.v1.XmlPullParserException;
import android.content.Context;
import android.widget.TextView;
public class FakeActionBar extends CustomBar {
private TextView mTextView;
public FakeActionBar(Context context, Density density, String label, String icon)
throws XmlPullParserException {
super(context, density, "/bars/action_bar.xml");
// Cannot access the inside items through id because no R.id values have been
// created for them.
// We do know the order though.
loadIcon(0, icon);
mTextView = setText(1, label);
setStyle("actionBarStyle");
}
@Override
protected TextView getStyleableTextView() {
return mTextView;
}
}

View File

@@ -0,0 +1,42 @@
/*
* Copyright (C) 2011 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.resources.Density;
import org.xmlpull.v1.XmlPullParserException;
import android.content.Context;
import android.widget.TextView;
public class PhoneSystemBar extends CustomBar {
public PhoneSystemBar(Context context, Density density) throws XmlPullParserException {
super(context, density, "/bars/tablet_system_bar.xml");
// Cannot access the inside items through id because no R.id values have been
// created for them.
// We do know the order though.
// 0 is the spacer
loadIcon(1, "stat_sys_wifi_signal_4_fully.png", density);
}
@Override
protected TextView getStyleableTextView() {
return null;
}
}

View File

@@ -0,0 +1,45 @@
/*
* Copyright (C) 2011 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.resources.Density;
import org.xmlpull.v1.XmlPullParserException;
import android.content.Context;
import android.widget.TextView;
public class TabletSystemBar extends CustomBar {
public TabletSystemBar(Context context, Density density) throws XmlPullParserException {
super(context, density, "/bars/tablet_system_bar.xml");
// Cannot access the inside items through id because no R.id values have been
// created for them.
// We do know the order though.
loadIcon(0, "ic_sysbar_back_default.png", density);
loadIcon(1, "ic_sysbar_home_default.png", density);
loadIcon(2, "ic_sysbar_recent_default.png", density);
// 3 is the spacer
loadIcon(4, "stat_sys_wifi_signal_4_fully.png", density);
}
@Override
protected TextView getStyleableTextView() {
return null;
}
}

View File

@@ -0,0 +1,46 @@
/*
* Copyright (C) 2011 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.resources.Density;
import org.xmlpull.v1.XmlPullParserException;
import android.content.Context;
import android.widget.TextView;
public class TitleBar extends CustomBar {
private TextView mTextView;
public TitleBar(Context context, Density density, String label)
throws XmlPullParserException {
super(context, density, "/bars/title_bar.xml");
// Cannot access the inside items through id because no R.id values have been
// created for them.
// We do know the order though.
mTextView = setText(0, label);
setStyle("windowTitleBackgroundStyle");
}
@Override
protected TextView getStyleableTextView() {
return mTextView;
}
}

View File

@@ -29,14 +29,13 @@ import com.android.ide.common.rendering.api.IAnimationListener;
import com.android.ide.common.rendering.api.ILayoutPullParser; import com.android.ide.common.rendering.api.ILayoutPullParser;
import com.android.ide.common.rendering.api.IProjectCallback; import com.android.ide.common.rendering.api.IProjectCallback;
import com.android.ide.common.rendering.api.LayoutLog; import com.android.ide.common.rendering.api.LayoutLog;
import com.android.ide.common.rendering.api.Params; import com.android.ide.common.rendering.api.RenderParams;
import com.android.ide.common.rendering.api.RenderResources; import com.android.ide.common.rendering.api.RenderResources;
import com.android.ide.common.rendering.api.RenderSession; import com.android.ide.common.rendering.api.RenderSession;
import com.android.ide.common.rendering.api.ResourceValue; import com.android.ide.common.rendering.api.ResourceValue;
import com.android.ide.common.rendering.api.Result; import com.android.ide.common.rendering.api.Result;
import com.android.ide.common.rendering.api.StyleResourceValue;
import com.android.ide.common.rendering.api.ViewInfo; import com.android.ide.common.rendering.api.ViewInfo;
import com.android.ide.common.rendering.api.Params.RenderingMode; import com.android.ide.common.rendering.api.RenderParams.RenderingMode;
import com.android.ide.common.rendering.api.RenderResources.FrameworkResourceIdProvider; import com.android.ide.common.rendering.api.RenderResources.FrameworkResourceIdProvider;
import com.android.ide.common.rendering.api.Result.Status; import com.android.ide.common.rendering.api.Result.Status;
import com.android.internal.util.XmlUtils; import com.android.internal.util.XmlUtils;
@@ -47,11 +46,16 @@ import com.android.layoutlib.bridge.android.BridgeLayoutParamsMapAttributes;
import com.android.layoutlib.bridge.android.BridgeWindow; import com.android.layoutlib.bridge.android.BridgeWindow;
import com.android.layoutlib.bridge.android.BridgeWindowSession; import com.android.layoutlib.bridge.android.BridgeWindowSession;
import com.android.layoutlib.bridge.android.BridgeXmlBlockParser; import com.android.layoutlib.bridge.android.BridgeXmlBlockParser;
import com.android.resources.Density; import com.android.layoutlib.bridge.bars.FakeActionBar;
import com.android.layoutlib.bridge.bars.PhoneSystemBar;
import com.android.layoutlib.bridge.bars.TabletSystemBar;
import com.android.layoutlib.bridge.bars.TitleBar;
import com.android.resources.ResourceType; import com.android.resources.ResourceType;
import com.android.resources.ScreenSize; import com.android.resources.ScreenSize;
import com.android.util.Pair; import com.android.util.Pair;
import org.xmlpull.v1.XmlPullParserException;
import android.animation.Animator; import android.animation.Animator;
import android.animation.AnimatorInflater; import android.animation.AnimatorInflater;
import android.animation.LayoutTransition; import android.animation.LayoutTransition;
@@ -105,7 +109,7 @@ public class RenderSessionImpl extends FrameworkResourceIdProvider {
*/ */
private static BridgeContext sCurrentContext = null; private static BridgeContext sCurrentContext = null;
private final Params mParams; private final RenderParams mParams;
// scene state // scene state
private RenderSession mScene; private RenderSession mScene;
@@ -113,17 +117,18 @@ public class RenderSessionImpl extends FrameworkResourceIdProvider {
private BridgeXmlBlockParser mBlockParser; private BridgeXmlBlockParser mBlockParser;
private BridgeInflater mInflater; private BridgeInflater mInflater;
private ResourceValue mWindowBackground; private ResourceValue mWindowBackground;
private FrameLayout mViewRoot; private ViewGroup mViewRoot;
private FrameLayout mContentRoot;
private Canvas mCanvas; private Canvas mCanvas;
private int mMeasuredScreenWidth = -1; private int mMeasuredScreenWidth = -1;
private int mMeasuredScreenHeight = -1; private int mMeasuredScreenHeight = -1;
private boolean mIsAlphaChannelImage = true; private boolean mIsAlphaChannelImage;
private boolean mWindowIsFloating;
private int mStatusBarSize; private int mStatusBarSize;
private int mTopBarSize;
private int mSystemBarSize; private int mSystemBarSize;
private int mTopOffset; private int mTitleBarSize;
private int mTotalBarSize; private int mActionBarSize;
// information being returned through the API // information being returned through the API
@@ -146,9 +151,9 @@ public class RenderSessionImpl extends FrameworkResourceIdProvider {
* *
* @see LayoutBridge#createScene(com.android.layoutlib.api.SceneParams) * @see LayoutBridge#createScene(com.android.layoutlib.api.SceneParams)
*/ */
public RenderSessionImpl(Params params) { public RenderSessionImpl(RenderParams params) {
// copy the params. // copy the params.
mParams = new Params(params); mParams = new RenderParams(params);
} }
/** /**
@@ -172,8 +177,8 @@ public class RenderSessionImpl extends FrameworkResourceIdProvider {
// setup the display Metrics. // setup the display Metrics.
DisplayMetrics metrics = new DisplayMetrics(); DisplayMetrics metrics = new DisplayMetrics();
metrics.densityDpi = mParams.getDensity(); metrics.densityDpi = mParams.getDensity().getDpiValue();
metrics.density = mParams.getDensity() / (float) DisplayMetrics.DENSITY_DEFAULT; metrics.density = metrics.densityDpi / (float) DisplayMetrics.DENSITY_DEFAULT;
metrics.scaledDensity = metrics.density; metrics.scaledDensity = metrics.density;
metrics.widthPixels = mParams.getScreenWidth(); metrics.widthPixels = mParams.getScreenWidth();
metrics.heightPixels = mParams.getScreenHeight(); metrics.heightPixels = mParams.getScreenHeight();
@@ -190,17 +195,16 @@ public class RenderSessionImpl extends FrameworkResourceIdProvider {
mIsAlphaChannelImage = getBooleanThemeValue(resources, mIsAlphaChannelImage = getBooleanThemeValue(resources,
"windowIsFloating", true /*defaultValue*/); "windowIsFloating", true /*defaultValue*/);
mWindowIsFloating = getBooleanThemeValue(resources, "windowIsFloating",
true /*defaultValue*/);
setUp(); setUp();
findBackground(resources); findBackground(resources);
findStatusBar(resources, metrics); findStatusBar(resources, metrics);
findTopBar(resources, metrics); findActionBar(resources, metrics);
findSystemBar(resources, metrics); findSystemBar(resources, metrics);
mTopOffset = mStatusBarSize + mTopBarSize;
mTotalBarSize = mTopOffset + mSystemBarSize;
// build the inflater and parser. // build the inflater and parser.
mInflater = new BridgeInflater(mContext, mParams.getProjectCallback()); mInflater = new BridgeInflater(mContext, mParams.getProjectCallback());
mContext.setBridgeInflater(mInflater); mContext.setBridgeInflater(mInflater);
@@ -353,13 +357,100 @@ public class RenderSessionImpl extends FrameworkResourceIdProvider {
try { try {
mViewRoot = new FrameLayout(mContext); if (mWindowIsFloating || mParams.isForceNoDecor()) {
mViewRoot = mContentRoot = new FrameLayout(mContext);
} else {
/*
* we're creating the following layout
*
+-------------------------------------------------+
| System bar (only in phone UI) |
+-------------------------------------------------+
| Title/Action bar (optional) |
+-------------------------------------------------+
| Content, vertical extending |
| |
+-------------------------------------------------+
| System bar (only in tablet UI) |
+-------------------------------------------------+
*/
LinearLayout topLayout = new LinearLayout(mContext);
mViewRoot = topLayout;
topLayout.setOrientation(LinearLayout.VERTICAL);
if (mStatusBarSize > 0) {
// system bar
try {
PhoneSystemBar systemBar = new PhoneSystemBar(mContext,
mParams.getDensity());
systemBar.setLayoutParams(
new LinearLayout.LayoutParams(
LayoutParams.MATCH_PARENT, mStatusBarSize));
topLayout.addView(systemBar);
} catch (XmlPullParserException e) {
}
}
// if the theme says no title/action bar, then the size will be 0
if (mActionBarSize > 0) {
try {
FakeActionBar actionBar = new FakeActionBar(mContext,
mParams.getDensity(),
mParams.getAppLabel(), mParams.getAppIcon());
actionBar.setLayoutParams(
new LinearLayout.LayoutParams(
LayoutParams.MATCH_PARENT, mActionBarSize));
topLayout.addView(actionBar);
} catch (XmlPullParserException e) {
}
} else if (mTitleBarSize > 0) {
try {
TitleBar titleBar = new TitleBar(mContext,
mParams.getDensity(), mParams.getAppLabel());
titleBar.setLayoutParams(
new LinearLayout.LayoutParams(
LayoutParams.MATCH_PARENT, mTitleBarSize));
topLayout.addView(titleBar);
} catch (XmlPullParserException e) {
}
}
// content frame
mContentRoot = new FrameLayout(mContext);
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(
LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT);
params.weight = 1;
mContentRoot.setLayoutParams(params);
topLayout.addView(mContentRoot);
if (mSystemBarSize > 0) {
// system bar
try {
TabletSystemBar systemBar = new TabletSystemBar(mContext,
mParams.getDensity());
systemBar.setLayoutParams(
new LinearLayout.LayoutParams(
LayoutParams.MATCH_PARENT, mSystemBarSize));
topLayout.addView(systemBar);
} catch (XmlPullParserException e) {
}
}
}
// Sets the project callback (custom view loader) to the fragment delegate so that // Sets the project callback (custom view loader) to the fragment delegate so that
// it can instantiate the custom Fragment. // it can instantiate the custom Fragment.
Fragment_Delegate.setProjectCallback(mParams.getProjectCallback()); Fragment_Delegate.setProjectCallback(mParams.getProjectCallback());
View view = mInflater.inflate(mBlockParser, mViewRoot); View view = mInflater.inflate(mBlockParser, mContentRoot);
Fragment_Delegate.setProjectCallback(null); Fragment_Delegate.setProjectCallback(null);
@@ -377,9 +468,8 @@ public class RenderSessionImpl extends FrameworkResourceIdProvider {
// get the background drawable // get the background drawable
if (mWindowBackground != null) { if (mWindowBackground != null) {
Drawable d = ResourceHelper.getDrawable(mWindowBackground, Drawable d = ResourceHelper.getDrawable(mWindowBackground, mContext);
mContext, true /* isFramework */); mContentRoot.setBackgroundDrawable(d);
mViewRoot.setBackgroundDrawable(d);
} }
return SUCCESS.createResult(); return SUCCESS.createResult();
@@ -408,8 +498,8 @@ public class RenderSessionImpl extends FrameworkResourceIdProvider {
* @throws IllegalStateException if the current context is different than the one owned by * @throws IllegalStateException if the current context is different than the one owned by
* the scene, or if {@link #acquire(long)} was not called. * the scene, or if {@link #acquire(long)} was not called.
* *
* @see SceneParams#getRenderingMode() * @see RenderParams#getRenderingMode()
* @see LayoutScene#render(long) * @see RenderSession#render(long)
*/ */
public Result render(boolean freshRender) { public Result render(boolean freshRender) {
checkLock(); checkLock();
@@ -428,7 +518,7 @@ public class RenderSessionImpl extends FrameworkResourceIdProvider {
if (mMeasuredScreenWidth == -1) { if (mMeasuredScreenWidth == -1) {
newRenderSize = true; newRenderSize = true;
mMeasuredScreenWidth = mParams.getScreenWidth(); mMeasuredScreenWidth = mParams.getScreenWidth();
mMeasuredScreenHeight = mParams.getScreenHeight() - mTotalBarSize; mMeasuredScreenHeight = mParams.getScreenHeight();
if (renderingMode != RenderingMode.NORMAL) { if (renderingMode != RenderingMode.NORMAL) {
// measure the full size needed by the layout. // measure the full size needed by the layout.
@@ -476,11 +566,11 @@ public class RenderSessionImpl extends FrameworkResourceIdProvider {
if (mParams.getImageFactory() != null) { if (mParams.getImageFactory() != null) {
mImage = mParams.getImageFactory().getImage( mImage = mParams.getImageFactory().getImage(
mMeasuredScreenWidth, mMeasuredScreenWidth,
mMeasuredScreenHeight + mTotalBarSize); mMeasuredScreenHeight);
} else { } else {
mImage = new BufferedImage( mImage = new BufferedImage(
mMeasuredScreenWidth, mMeasuredScreenWidth,
mMeasuredScreenHeight + mTotalBarSize, mMeasuredScreenHeight,
BufferedImage.TYPE_INT_ARGB); BufferedImage.TYPE_INT_ARGB);
newImage = true; newImage = true;
} }
@@ -491,46 +581,26 @@ public class RenderSessionImpl extends FrameworkResourceIdProvider {
Graphics2D gc = mImage.createGraphics(); Graphics2D gc = mImage.createGraphics();
gc.setColor(new Color(mParams.getOverrideBgColor(), true)); gc.setColor(new Color(mParams.getOverrideBgColor(), true));
gc.setComposite(AlphaComposite.Src); gc.setComposite(AlphaComposite.Src);
gc.fillRect(0, 0, mMeasuredScreenWidth, gc.fillRect(0, 0, mMeasuredScreenWidth, mMeasuredScreenHeight);
mMeasuredScreenHeight + mTotalBarSize);
gc.dispose(); gc.dispose();
} }
// create an Android bitmap around the BufferedImage // create an Android bitmap around the BufferedImage
Bitmap bitmap = Bitmap_Delegate.createBitmap(mImage, Bitmap bitmap = Bitmap_Delegate.createBitmap(mImage,
true /*isMutable*/, true /*isMutable*/, mParams.getDensity());
Density.getEnum(mParams.getDensity()));
// create a Canvas around the Android bitmap // create a Canvas around the Android bitmap
mCanvas = new Canvas(bitmap); mCanvas = new Canvas(bitmap);
mCanvas.setDensity(mParams.getDensity()); mCanvas.setDensity(mParams.getDensity().getDpiValue());
mCanvas.translate(0, mTopOffset);
} }
if (freshRender && newImage == false) { if (freshRender && newImage == false) {
Graphics2D gc = mImage.createGraphics(); Graphics2D gc = mImage.createGraphics();
gc.setComposite(AlphaComposite.Src); gc.setComposite(AlphaComposite.Src);
if (mStatusBarSize > 0) {
gc.setColor(new Color(0xFF3C3C3C, true));
gc.fillRect(0, 0, mMeasuredScreenWidth, mStatusBarSize);
}
if (mTopBarSize > 0) {
gc.setColor(new Color(0xFF7F7F7F, true));
gc.fillRect(0, mStatusBarSize, mMeasuredScreenWidth, mTopOffset);
}
// erase the rest
gc.setColor(new Color(0x00000000, true)); gc.setColor(new Color(0x00000000, true));
gc.fillRect(0, mTopOffset, gc.fillRect(0, 0,
mMeasuredScreenWidth, mMeasuredScreenHeight + mTopOffset); mMeasuredScreenWidth, mMeasuredScreenHeight);
if (mSystemBarSize > 0) {
gc.setColor(new Color(0xFF3C3C3C, true));
gc.fillRect(0, mMeasuredScreenHeight + mTopOffset,
mMeasuredScreenWidth, mMeasuredScreenHeight + mTotalBarSize);
}
// done // done
gc.dispose(); gc.dispose();
@@ -538,7 +608,7 @@ public class RenderSessionImpl extends FrameworkResourceIdProvider {
mViewRoot.draw(mCanvas); mViewRoot.draw(mCanvas);
mViewInfoList = visitAllChildren((ViewGroup)mViewRoot, mContext, mTopOffset); mViewInfoList = startVisitingViews(mViewRoot, 0);
// success! // success!
return SUCCESS.createResult(); return SUCCESS.createResult();
@@ -561,7 +631,7 @@ public class RenderSessionImpl extends FrameworkResourceIdProvider {
* @throws IllegalStateException if the current context is different than the one owned by * @throws IllegalStateException if the current context is different than the one owned by
* the scene, or if {@link #acquire(long)} was not called. * the scene, or if {@link #acquire(long)} was not called.
* *
* @see LayoutScene#animate(Object, String, boolean, IAnimationListener) * @see RenderSession#animate(Object, String, boolean, IAnimationListener)
*/ */
public Result animate(Object targetObject, String animationName, public Result animate(Object targetObject, String animationName,
boolean isFrameworkAnimation, IAnimationListener listener) { boolean isFrameworkAnimation, IAnimationListener listener) {
@@ -617,7 +687,7 @@ public class RenderSessionImpl extends FrameworkResourceIdProvider {
* @throws IllegalStateException if the current context is different than the one owned by * @throws IllegalStateException if the current context is different than the one owned by
* the scene, or if {@link #acquire(long)} was not called. * the scene, or if {@link #acquire(long)} was not called.
* *
* @see LayoutScene#insertChild(Object, ILayoutPullParser, int, IAnimationListener) * @see RenderSession#insertChild(Object, ILayoutPullParser, int, IAnimationListener)
*/ */
public Result insertChild(final ViewGroup parentView, ILayoutPullParser childXml, public Result insertChild(final ViewGroup parentView, ILayoutPullParser childXml,
final int index, IAnimationListener listener) { final int index, IAnimationListener listener) {
@@ -696,7 +766,7 @@ public class RenderSessionImpl extends FrameworkResourceIdProvider {
* @throws IllegalStateException if the current context is different than the one owned by * @throws IllegalStateException if the current context is different than the one owned by
* the scene, or if {@link #acquire(long)} was not called. * the scene, or if {@link #acquire(long)} was not called.
* *
* @see LayoutScene#moveChild(Object, Object, int, Map, IAnimationListener) * @see RenderSession#moveChild(Object, Object, int, Map, IAnimationListener)
*/ */
public Result moveChild(final ViewGroup newParentView, final View childView, final int index, public Result moveChild(final ViewGroup newParentView, final View childView, final int index,
Map<String, String> layoutParamsMap, final IAnimationListener listener) { Map<String, String> layoutParamsMap, final IAnimationListener listener) {
@@ -892,7 +962,7 @@ public class RenderSessionImpl extends FrameworkResourceIdProvider {
* @throws IllegalStateException if the current context is different than the one owned by * @throws IllegalStateException if the current context is different than the one owned by
* the scene, or if {@link #acquire(long)} was not called. * the scene, or if {@link #acquire(long)} was not called.
* *
* @see LayoutScene#removeChild(Object, IAnimationListener) * @see RenderSession#removeChild(Object, IAnimationListener)
*/ */
public Result removeChild(final View childView, IAnimationListener listener) { public Result removeChild(final View childView, IAnimationListener listener) {
checkLock(); checkLock();
@@ -989,40 +1059,12 @@ public class RenderSessionImpl extends FrameworkResourceIdProvider {
return mParams.getConfigScreenSize() == ScreenSize.XLARGE; return mParams.getConfigScreenSize() == ScreenSize.XLARGE;
} }
private boolean isHCApp() {
RenderResources resources = mContext.getRenderResources();
// the app must say it targets 11+ and the theme name must extend Theme.Holo or
// Theme.Holo.Light (which does not extend Theme.Holo, but Theme.Light)
if (mParams.getTargetSdkVersion() < 11) {
return false;
}
StyleResourceValue currentTheme = resources.getCurrentTheme();
StyleResourceValue holoTheme = resources.getTheme("Theme.Holo", true /*frameworkTheme*/);
if (currentTheme == holoTheme ||
resources.themeIsParentOf(holoTheme, currentTheme)) {
return true;
}
StyleResourceValue holoLightTheme = resources.getTheme("Theme.Holo.Light",
true /*frameworkTheme*/);
if (currentTheme == holoLightTheme ||
resources.themeIsParentOf(holoLightTheme, currentTheme)) {
return true;
}
return false;
}
private void findStatusBar(RenderResources resources, DisplayMetrics metrics) { private void findStatusBar(RenderResources resources, DisplayMetrics metrics) {
if (isTabletUi() == false) { if (isTabletUi() == false) {
boolean windowFullscreen = getBooleanThemeValue(resources, boolean windowFullscreen = getBooleanThemeValue(resources,
"windowFullscreen", false /*defaultValue*/); "windowFullscreen", false /*defaultValue*/);
if (windowFullscreen == false) { if (windowFullscreen == false && mWindowIsFloating == false) {
// default value // default value
mStatusBarSize = DEFAULT_STATUS_BAR_HEIGHT; mStatusBarSize = DEFAULT_STATUS_BAR_HEIGHT;
@@ -1041,20 +1083,11 @@ public class RenderSessionImpl extends FrameworkResourceIdProvider {
} }
} }
private void findTopBar(RenderResources resources, DisplayMetrics metrics) {
boolean windowIsFloating = getBooleanThemeValue(resources,
"windowIsFloating", true /*defaultValue*/);
if (windowIsFloating == false) {
if (isHCApp()) {
findActionBar(resources, metrics);
} else {
findTitleBar(resources, metrics);
}
}
}
private void findActionBar(RenderResources resources, DisplayMetrics metrics) { private void findActionBar(RenderResources resources, DisplayMetrics metrics) {
if (mWindowIsFloating) {
return;
}
boolean windowActionBar = getBooleanThemeValue(resources, boolean windowActionBar = getBooleanThemeValue(resources,
"windowActionBar", true /*defaultValue*/); "windowActionBar", true /*defaultValue*/);
@@ -1062,7 +1095,7 @@ public class RenderSessionImpl extends FrameworkResourceIdProvider {
if (windowActionBar) { if (windowActionBar) {
// default size of the window title bar // default size of the window title bar
mTopBarSize = DEFAULT_TITLE_BAR_HEIGHT; mActionBarSize = DEFAULT_TITLE_BAR_HEIGHT;
// get value from the theme. // get value from the theme.
ResourceValue value = resources.findItemInTheme("actionBarSize"); ResourceValue value = resources.findItemInTheme("actionBarSize");
@@ -1075,44 +1108,43 @@ public class RenderSessionImpl extends FrameworkResourceIdProvider {
TypedValue typedValue = ResourceHelper.getValue(value.getValue()); TypedValue typedValue = ResourceHelper.getValue(value.getValue());
if (typedValue != null) { if (typedValue != null) {
// compute the pixel value based on the display metrics // compute the pixel value based on the display metrics
mTopBarSize = (int)typedValue.getDimension(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*/);
private void findTitleBar(RenderResources resources, DisplayMetrics metrics) { if (windowNoTitle == false) {
boolean windowNoTitle = getBooleanThemeValue(resources,
"windowNoTitle", false /*defaultValue*/);
if (windowNoTitle == false) { // default size of the window title bar
mTitleBarSize = DEFAULT_TITLE_BAR_HEIGHT;
// default size of the window title bar // get value from the theme.
mTopBarSize = DEFAULT_TITLE_BAR_HEIGHT; ResourceValue value = resources.findItemInTheme("windowTitleSize");
// get value from the theme. // resolve it
ResourceValue value = resources.findItemInTheme("windowTitleSize"); value = resources.resolveResValue(value);
// resolve it if (value != null) {
value = resources.resolveResValue(value); // get the numerical value, if available
TypedValue typedValue = ResourceHelper.getValue(value.getValue());
if (value != null) { if (typedValue != null) {
// get the numerical value, if available // compute the pixel value based on the display metrics
TypedValue typedValue = ResourceHelper.getValue(value.getValue()); mTitleBarSize = (int)typedValue.getDimension(metrics);
if (typedValue != null) { }
// compute the pixel value based on the display metrics
mTopBarSize = (int)typedValue.getDimension(metrics);
} }
} }
} }
} }
private void findSystemBar(RenderResources resources, DisplayMetrics metrics) { private void findSystemBar(RenderResources resources, DisplayMetrics metrics) {
if (isTabletUi() && getBooleanThemeValue( if (isTabletUi() && mWindowIsFloating == false) {
resources, "windowIsFloating", true /*defaultValue*/) == false) {
// default value // default value
mSystemBarSize = 56; // ?? mSystemBarSize = 48; // ??
// get the real value // get the real value
ResourceValue value = resources.getFrameworkResource(ResourceType.DIMEN, ResourceValue value = resources.getFrameworkResource(ResourceType.DIMEN,
@@ -1244,40 +1276,71 @@ public class RenderSessionImpl extends FrameworkResourceIdProvider {
} }
} }
private List<ViewInfo> startVisitingViews(View view, int offset) {
if (view == null) {
return null;
}
// adjust the offset to this view.
offset += view.getTop();
if (view == mContentRoot) {
return visitAllChildren(mContentRoot, offset);
}
// otherwise, look for mContentRoot in the children
if (view instanceof ViewGroup) {
ViewGroup group = ((ViewGroup) view);
for (int i = 0; i < group.getChildCount(); i++) {
List<ViewInfo> list = startVisitingViews(group.getChildAt(i), offset);
if (list != null) {
return list;
}
}
}
return null;
}
/** /**
* Visits a View and its children and generate a {@link ViewInfo} containing the * Visits a View and its children and generate a {@link ViewInfo} containing the
* bounds of all the views. * bounds of all the views.
* @param view the root View * @param view the root View
* @param context the context. * @param offset an offset for the view bounds.
*/ */
private ViewInfo visit(View view, BridgeContext context, int offset) { private ViewInfo visit(View view, int offset) {
if (view == null) { if (view == null) {
return null; return null;
} }
ViewInfo result = new ViewInfo(view.getClass().getName(), ViewInfo result = new ViewInfo(view.getClass().getName(),
context.getViewKey(view), mContext.getViewKey(view),
view.getLeft(), view.getTop() + offset, view.getRight(), view.getBottom() + offset, view.getLeft(), view.getTop() + offset, view.getRight(), view.getBottom() + offset,
view, view.getLayoutParams()); view, view.getLayoutParams());
if (view instanceof ViewGroup) { if (view instanceof ViewGroup) {
ViewGroup group = ((ViewGroup) view); ViewGroup group = ((ViewGroup) view);
result.setChildren(visitAllChildren(group, context, 0 /*offset*/)); result.setChildren(visitAllChildren(group, 0 /*offset*/));
} }
return result; return result;
} }
private List<ViewInfo> visitAllChildren(ViewGroup viewGroup, BridgeContext context, /**
int offset) { * Visits all the children of a given ViewGroup generate a list of {@link ViewInfo}
* containing the bounds of all the views.
* @param view the root View
* @param offset an offset for the view bounds.
*/
private List<ViewInfo> visitAllChildren(ViewGroup viewGroup, int offset) {
if (viewGroup == null) { if (viewGroup == null) {
return null; return null;
} }
List<ViewInfo> children = new ArrayList<ViewInfo>(); List<ViewInfo> children = new ArrayList<ViewInfo>();
for (int i = 0; i < viewGroup.getChildCount(); i++) { for (int i = 0; i < viewGroup.getChildCount(); i++) {
children.add(visit(viewGroup.getChildAt(i), context, offset)); children.add(visit(viewGroup.getChildAt(i), offset));
} }
return children; return children;
} }

View File

@@ -18,6 +18,7 @@ package com.android.layoutlib.bridge.impl;
import com.android.ide.common.rendering.api.DensityBasedResourceValue; import com.android.ide.common.rendering.api.DensityBasedResourceValue;
import com.android.ide.common.rendering.api.LayoutLog; 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.ide.common.rendering.api.ResourceValue;
import com.android.layoutlib.bridge.Bridge; import com.android.layoutlib.bridge.Bridge;
import com.android.layoutlib.bridge.android.BridgeContext; import com.android.layoutlib.bridge.android.BridgeContext;
@@ -28,7 +29,9 @@ import com.android.resources.Density;
import org.kxml2.io.KXmlParser; import org.kxml2.io.KXmlParser;
import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import android.content.res.ColorStateList;
import android.graphics.Bitmap; import android.graphics.Bitmap;
import android.graphics.Bitmap_Delegate; import android.graphics.Bitmap_Delegate;
import android.graphics.NinePatch_Delegate; import android.graphics.NinePatch_Delegate;
@@ -108,19 +111,63 @@ public final class ResourceHelper {
throw new NumberFormatException(); throw new NumberFormatException();
} }
public static ColorStateList getColorStateList(ResourceValue resValue, BridgeContext context) {
String value = resValue.getValue();
if (value != null) {
// first check if the value is a file (xml most likely)
File f = new File(value);
if (f.isFile()) {
try {
// let the framework inflate the ColorStateList from the XML file, by
// providing an XmlPullParser
KXmlParser parser = new KXmlParser();
parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, true);
parser.setInput(new FileReader(f));
return ColorStateList.createFromXml(context.getResources(),
new BridgeXmlBlockParser(parser, context, resValue.isFramework()));
} catch (XmlPullParserException e) {
Bridge.getLog().error(LayoutLog.TAG_BROKEN,
"Failed to configure parser for " + value, e, null /*data*/);
// we'll return null below.
} catch (Exception e) {
// this is an error and not warning since the file existence is
// checked before attempting to parse it.
Bridge.getLog().error(LayoutLog.TAG_RESOURCES_READ,
"Failed to parse file " + value, e, null /*data*/);
return null;
}
} else {
// try to load the color state list from an int
try {
int color = ResourceHelper.getColor(value);
return ColorStateList.valueOf(color);
} catch (NumberFormatException e) {
Bridge.getLog().error(LayoutLog.TAG_RESOURCES_FORMAT,
"Failed to convert " + value + " into a ColorStateList", e,
null /*data*/);
return null;
}
}
}
return null;
}
/** /**
* Returns a drawable from the given value. * Returns a drawable from the given value.
* @param value The value that contains a path to a 9 patch, a bitmap or a xml based drawable, * @param value The value that contains a path to a 9 patch, a bitmap or a xml based drawable,
* or an hexadecimal color * or an hexadecimal color
* @param context * @param context the current context
* @param isFramework indicates whether the resource is a framework resources.
* Framework resources are cached, and loaded only once.
*/ */
public static Drawable getDrawable(ResourceValue value, BridgeContext context, public static Drawable getDrawable(ResourceValue value, BridgeContext context) {
boolean isFramework) {
Drawable d = null; Drawable d = null;
String stringValue = value.getValue(); String stringValue = value.getValue();
if (RenderResources.REFERENCE_NULL.equals(stringValue)) {
return null;
}
String lowerCaseValue = stringValue.toLowerCase(); String lowerCaseValue = stringValue.toLowerCase();
@@ -129,9 +176,9 @@ public final class ResourceHelper {
if (file.isFile()) { if (file.isFile()) {
// see if we still have both the chunk and the bitmap in the caches // see if we still have both the chunk and the bitmap in the caches
NinePatchChunk chunk = Bridge.getCached9Patch(stringValue, NinePatchChunk chunk = Bridge.getCached9Patch(stringValue,
isFramework ? null : context.getProjectKey()); value.isFramework() ? null : context.getProjectKey());
Bitmap bitmap = Bridge.getCachedBitmap(stringValue, Bitmap bitmap = Bridge.getCachedBitmap(stringValue,
isFramework ? null : context.getProjectKey()); value.isFramework() ? null : context.getProjectKey());
// if either chunk or bitmap is null, then we reload the 9-patch file. // if either chunk or bitmap is null, then we reload the 9-patch file.
if (chunk == null || bitmap == null) { if (chunk == null || bitmap == null) {
@@ -143,7 +190,7 @@ public final class ResourceHelper {
chunk = ninePatch.getChunk(); chunk = ninePatch.getChunk();
Bridge.setCached9Patch(stringValue, chunk, Bridge.setCached9Patch(stringValue, chunk,
isFramework ? null : context.getProjectKey()); value.isFramework() ? null : context.getProjectKey());
} }
if (bitmap == null) { if (bitmap == null) {
@@ -158,7 +205,7 @@ public final class ResourceHelper {
density); density);
Bridge.setCachedBitmap(stringValue, bitmap, Bridge.setCachedBitmap(stringValue, bitmap,
isFramework ? null : context.getProjectKey()); value.isFramework() ? null : context.getProjectKey());
} }
} }
} catch (MalformedURLException e) { } catch (MalformedURLException e) {
@@ -192,7 +239,7 @@ public final class ResourceHelper {
parser.setInput(new FileReader(f)); parser.setInput(new FileReader(f));
d = Drawable.createFromXml(context.getResources(), d = Drawable.createFromXml(context.getResources(),
new BridgeXmlBlockParser(parser, context, isFramework)); new BridgeXmlBlockParser(parser, context, value.isFramework()));
return d; return d;
} catch (Exception e) { } catch (Exception e) {
// this is an error and not warning since the file existence is checked before // this is an error and not warning since the file existence is checked before
@@ -212,7 +259,7 @@ public final class ResourceHelper {
if (bmpFile.isFile()) { if (bmpFile.isFile()) {
try { try {
Bitmap bitmap = Bridge.getCachedBitmap(stringValue, Bitmap bitmap = Bridge.getCachedBitmap(stringValue,
isFramework ? null : context.getProjectKey()); value.isFramework() ? null : context.getProjectKey());
if (bitmap == null) { if (bitmap == null) {
Density density = Density.MEDIUM; Density density = Density.MEDIUM;
@@ -223,7 +270,7 @@ public final class ResourceHelper {
bitmap = Bitmap_Delegate.createBitmap(bmpFile, false /*isMutable*/, bitmap = Bitmap_Delegate.createBitmap(bmpFile, false /*isMutable*/,
density); density);
Bridge.setCachedBitmap(stringValue, bitmap, Bridge.setCachedBitmap(stringValue, bitmap,
isFramework ? null : context.getProjectKey()); value.isFramework() ? null : context.getProjectKey());
} }
return new BitmapDrawable(context.getResources(), bitmap); return new BitmapDrawable(context.getResources(), bitmap);