diff options
author | Xavier Ducrohet <xav@android.com> | 2011-01-27 16:58:52 -0800 |
---|---|---|
committer | Xavier Ducrohet <xav@android.com> | 2011-02-04 10:46:21 -0800 |
commit | bbbb8326020368958a3f1d248878329e9d6b10c0 (patch) | |
tree | 74ec16b151a74dec647b657a68f83f1a178b4183 /tools | |
parent | e8d2b98526e7666b175b4df58b646ed56a572bb0 (diff) | |
download | frameworks_base-bbbb8326020368958a3f1d248878329e9d6b10c0.zip frameworks_base-bbbb8326020368958a3f1d248878329e9d6b10c0.tar.gz frameworks_base-bbbb8326020368958a3f1d248878329e9d6b10c0.tar.bz2 |
LayoutLib: render system/title/action bars.
Also a few generic fixes in the layoutlib itself to support
this.
Change-Id: Ie3f24c9056bd3cc72f39f8a4f2c0861be15bff55
Diffstat (limited to 'tools')
21 files changed, 770 insertions, 201 deletions
diff --git a/tools/layoutlib/bridge/Android.mk b/tools/layoutlib/bridge/Android.mk index 3d4c76a..ca7db8c 100644 --- a/tools/layoutlib/bridge/Android.mk +++ b/tools/layoutlib/bridge/Android.mk @@ -17,6 +17,8 @@ LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) LOCAL_SRC_FILES := $(call all-java-files-under,src) +LOCAL_JAVA_RESOURCE_DIRS := resources + LOCAL_JAVA_LIBRARIES := \ kxml2-2.3.0 \ diff --git a/tools/layoutlib/bridge/resources/bars/action_bar.xml b/tools/layoutlib/bridge/resources/bars/action_bar.xml new file mode 100644 index 0000000..cd99a09 --- /dev/null +++ b/tools/layoutlib/bridge/resources/bars/action_bar.xml @@ -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> diff --git a/tools/layoutlib/bridge/resources/bars/mdpi/ic_sysbar_back_default.png b/tools/layoutlib/bridge/resources/bars/mdpi/ic_sysbar_back_default.png Binary files differnew file mode 100644 index 0000000..4bcd2be --- /dev/null +++ b/tools/layoutlib/bridge/resources/bars/mdpi/ic_sysbar_back_default.png diff --git a/tools/layoutlib/bridge/resources/bars/mdpi/ic_sysbar_home_default.png b/tools/layoutlib/bridge/resources/bars/mdpi/ic_sysbar_home_default.png Binary files differnew file mode 100644 index 0000000..cfeba3e --- /dev/null +++ b/tools/layoutlib/bridge/resources/bars/mdpi/ic_sysbar_home_default.png diff --git a/tools/layoutlib/bridge/resources/bars/mdpi/ic_sysbar_recent_default.png b/tools/layoutlib/bridge/resources/bars/mdpi/ic_sysbar_recent_default.png Binary files differnew file mode 100644 index 0000000..1d97e05 --- /dev/null +++ b/tools/layoutlib/bridge/resources/bars/mdpi/ic_sysbar_recent_default.png diff --git a/tools/layoutlib/bridge/resources/bars/mdpi/stat_sys_wifi_signal_4_fully.png b/tools/layoutlib/bridge/resources/bars/mdpi/stat_sys_wifi_signal_4_fully.png Binary files differnew file mode 100644 index 0000000..c629387 --- /dev/null +++ b/tools/layoutlib/bridge/resources/bars/mdpi/stat_sys_wifi_signal_4_fully.png diff --git a/tools/layoutlib/bridge/resources/bars/phone_system_bar.xml b/tools/layoutlib/bridge/resources/bars/phone_system_bar.xml new file mode 100644 index 0000000..29df909 --- /dev/null +++ b/tools/layoutlib/bridge/resources/bars/phone_system_bar.xml @@ -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> diff --git a/tools/layoutlib/bridge/resources/bars/tablet_system_bar.xml b/tools/layoutlib/bridge/resources/bars/tablet_system_bar.xml new file mode 100644 index 0000000..8a3b87a --- /dev/null +++ b/tools/layoutlib/bridge/resources/bars/tablet_system_bar.xml @@ -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> diff --git a/tools/layoutlib/bridge/resources/bars/title_bar.xml b/tools/layoutlib/bridge/resources/bars/title_bar.xml new file mode 100644 index 0000000..29fcc4b --- /dev/null +++ b/tools/layoutlib/bridge/resources/bars/title_bar.xml @@ -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> diff --git a/tools/layoutlib/bridge/src/android/graphics/BitmapFactory.java b/tools/layoutlib/bridge/src/android/graphics/BitmapFactory.java index 993c305..ee60eb4 100644 --- a/tools/layoutlib/bridge/src/android/graphics/BitmapFactory.java +++ b/tools/layoutlib/bridge/src/android/graphics/BitmapFactory.java @@ -18,6 +18,9 @@ package android.graphics; import com.android.ide.common.rendering.api.LayoutLog; 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 android.content.res.AssetManager; @@ -438,6 +441,8 @@ public class BitmapFactory { return null; } + boolean isNinePatch = is instanceof NinePatchInputStream; + // we need mark/reset to work properly if (!is.markSupported()) { @@ -466,7 +471,29 @@ public class BitmapFactory { if (opts != null) { 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) { return null; } diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java index 93c81d1..65f6bed 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java @@ -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.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.Result; 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 - * {@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. - * @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 */ @Override - public RenderSession createSession(Params params) { + public RenderSession createSession(RenderParams params) { try { Result lastResult = SUCCESS.createResult(); 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 public void clearCaches(Object projectKey) { if (projectKey != null) { diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeRenderSession.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeRenderSession.java index 0c6fa20..765fd99 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeRenderSession.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeRenderSession.java @@ -18,7 +18,7 @@ package com.android.layoutlib.bridge; import com.android.ide.common.rendering.api.IAnimationListener; 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.Result; import com.android.ide.common.rendering.api.ViewInfo; @@ -128,7 +128,7 @@ public class BridgeRenderSession extends RenderSession { boolean isFrameworkAnimation, IAnimationListener listener) { try { Bridge.prepareThread(); - mLastResult = mSession.acquire(Params.DEFAULT_TIMEOUT); + mLastResult = mSession.acquire(RenderParams.DEFAULT_TIMEOUT); if (mLastResult.isSuccess()) { mLastResult = mSession.animate(targetObject, animationName, isFrameworkAnimation, listener); @@ -150,7 +150,7 @@ public class BridgeRenderSession extends RenderSession { try { Bridge.prepareThread(); - mLastResult = mSession.acquire(Params.DEFAULT_TIMEOUT); + mLastResult = mSession.acquire(RenderParams.DEFAULT_TIMEOUT); if (mLastResult.isSuccess()) { mLastResult = mSession.insertChild((ViewGroup) parentView, childXml, index, listener); @@ -176,7 +176,7 @@ public class BridgeRenderSession extends RenderSession { try { Bridge.prepareThread(); - mLastResult = mSession.acquire(Params.DEFAULT_TIMEOUT); + mLastResult = mSession.acquire(RenderParams.DEFAULT_TIMEOUT); if (mLastResult.isSuccess()) { mLastResult = mSession.moveChild((ViewGroup) parentView, (View) childView, index, layoutParams, listener); @@ -197,7 +197,7 @@ public class BridgeRenderSession extends RenderSession { try { Bridge.prepareThread(); - mLastResult = mSession.acquire(Params.DEFAULT_TIMEOUT); + mLastResult = mSession.acquire(RenderParams.DEFAULT_TIMEOUT); if (mLastResult.isSuccess()) { mLastResult = mSession.removeChild((View) childView, listener); } diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeResources.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeResources.java index 5ea0a8d..d31fcc8 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeResources.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeResources.java @@ -22,6 +22,7 @@ import com.android.ide.common.rendering.api.ResourceValue; import com.android.layoutlib.bridge.Bridge; import com.android.layoutlib.bridge.BridgeConstants; import com.android.layoutlib.bridge.impl.ResourceHelper; +import com.android.ninepatch.NinePatch; import com.android.resources.ResourceType; import com.android.util.Pair; @@ -58,6 +59,18 @@ public final class BridgeResources extends Resources { 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 * by methods who get global resources using {@link Resources#getSystem()}. * <p/> @@ -129,7 +142,7 @@ public final class BridgeResources extends Resources { ResourceValue value = getResourceValue(id, mPlatformResourceFlag); 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. @@ -165,44 +178,9 @@ public final class BridgeResources extends Resources { ResourceValue resValue = getResourceValue(id, mPlatformResourceFlag); if (resValue != null) { - 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(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; - } - } + ColorStateList stateList = ResourceHelper.getColorStateList(resValue, mContext); + if (stateList != null) { + return stateList; } } @@ -562,13 +540,19 @@ public final class BridgeResources extends Resources { ResourceValue value = getResourceValue(id, mPlatformResourceFlag); if (value != null) { - String v = value.getValue(); + String path = value.getValue(); - if (v != null) { + if (path != null) { // check this is a file - File f = new File(value.getValue()); + File f = new File(path); if (f.isFile()) { 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); } catch (FileNotFoundException e) { NotFoundException newE = new NotFoundException(); @@ -590,9 +574,17 @@ public final class BridgeResources extends Resources { public InputStream openRawResource(int id, TypedValue value) throws NotFoundException { getValue(id, value, true); - File f = new File(value.string.toString()); + String path = value.string.toString(); + + File f = new File(path); if (f.isFile()) { 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); } catch (FileNotFoundException e) { NotFoundException exception = new NotFoundException(); diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeTypedArray.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeTypedArray.java index cf2c0ff..c226b8b 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeTypedArray.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeTypedArray.java @@ -690,7 +690,7 @@ public final class BridgeTypedArray extends TypedArray { return null; } - return ResourceHelper.getDrawable(value, mContext, mResourceData[index].isFramework()); + return ResourceHelper.getDrawable(value, mContext); } diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/CustomBar.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/CustomBar.java new file mode 100644 index 0000000..f039994 --- /dev/null +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/CustomBar.java @@ -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); + } +} diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/FakeActionBar.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/FakeActionBar.java new file mode 100644 index 0000000..3af4e3a --- /dev/null +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/FakeActionBar.java @@ -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; + } +} diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/PhoneSystemBar.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/PhoneSystemBar.java new file mode 100644 index 0000000..92615dc --- /dev/null +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/PhoneSystemBar.java @@ -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; + } +} diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/TabletSystemBar.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/TabletSystemBar.java new file mode 100644 index 0000000..bc61799 --- /dev/null +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/TabletSystemBar.java @@ -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; + } +} diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/TitleBar.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/TitleBar.java new file mode 100644 index 0000000..d7401d9 --- /dev/null +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/TitleBar.java @@ -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; + } +} diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderSessionImpl.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderSessionImpl.java index c8ad1d6..0aa2e6d 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderSessionImpl.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderSessionImpl.java @@ -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.IProjectCallback; 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.RenderSession; import com.android.ide.common.rendering.api.ResourceValue; 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.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.Result.Status; 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.BridgeWindowSession; 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.ScreenSize; import com.android.util.Pair; +import org.xmlpull.v1.XmlPullParserException; + import android.animation.Animator; import android.animation.AnimatorInflater; import android.animation.LayoutTransition; @@ -105,7 +109,7 @@ public class RenderSessionImpl extends FrameworkResourceIdProvider { */ private static BridgeContext sCurrentContext = null; - private final Params mParams; + private final RenderParams mParams; // scene state private RenderSession mScene; @@ -113,17 +117,18 @@ public class RenderSessionImpl extends FrameworkResourceIdProvider { private BridgeXmlBlockParser mBlockParser; private BridgeInflater mInflater; private ResourceValue mWindowBackground; - private FrameLayout mViewRoot; + private ViewGroup mViewRoot; + private FrameLayout mContentRoot; private Canvas mCanvas; private int mMeasuredScreenWidth = -1; private int mMeasuredScreenHeight = -1; - private boolean mIsAlphaChannelImage = true; + private boolean mIsAlphaChannelImage; + private boolean mWindowIsFloating; private int mStatusBarSize; - private int mTopBarSize; private int mSystemBarSize; - private int mTopOffset; - private int mTotalBarSize; + private int mTitleBarSize; + private int mActionBarSize; // information being returned through the API @@ -146,9 +151,9 @@ public class RenderSessionImpl extends FrameworkResourceIdProvider { * * @see LayoutBridge#createScene(com.android.layoutlib.api.SceneParams) */ - public RenderSessionImpl(Params params) { + public RenderSessionImpl(RenderParams 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. DisplayMetrics metrics = new DisplayMetrics(); - metrics.densityDpi = mParams.getDensity(); - metrics.density = mParams.getDensity() / (float) DisplayMetrics.DENSITY_DEFAULT; + metrics.densityDpi = mParams.getDensity().getDpiValue(); + metrics.density = metrics.densityDpi / (float) DisplayMetrics.DENSITY_DEFAULT; metrics.scaledDensity = metrics.density; metrics.widthPixels = mParams.getScreenWidth(); metrics.heightPixels = mParams.getScreenHeight(); @@ -190,17 +195,16 @@ public class RenderSessionImpl extends FrameworkResourceIdProvider { mIsAlphaChannelImage = getBooleanThemeValue(resources, "windowIsFloating", true /*defaultValue*/); + mWindowIsFloating = getBooleanThemeValue(resources, "windowIsFloating", + true /*defaultValue*/); setUp(); findBackground(resources); findStatusBar(resources, metrics); - findTopBar(resources, metrics); + findActionBar(resources, metrics); findSystemBar(resources, metrics); - mTopOffset = mStatusBarSize + mTopBarSize; - mTotalBarSize = mTopOffset + mSystemBarSize; - // build the inflater and parser. mInflater = new BridgeInflater(mContext, mParams.getProjectCallback()); mContext.setBridgeInflater(mInflater); @@ -353,13 +357,100 @@ public class RenderSessionImpl extends FrameworkResourceIdProvider { 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 // it can instantiate the custom Fragment. Fragment_Delegate.setProjectCallback(mParams.getProjectCallback()); - View view = mInflater.inflate(mBlockParser, mViewRoot); + View view = mInflater.inflate(mBlockParser, mContentRoot); Fragment_Delegate.setProjectCallback(null); @@ -377,9 +468,8 @@ public class RenderSessionImpl extends FrameworkResourceIdProvider { // get the background drawable if (mWindowBackground != null) { - Drawable d = ResourceHelper.getDrawable(mWindowBackground, - mContext, true /* isFramework */); - mViewRoot.setBackgroundDrawable(d); + Drawable d = ResourceHelper.getDrawable(mWindowBackground, mContext); + mContentRoot.setBackgroundDrawable(d); } 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 * the scene, or if {@link #acquire(long)} was not called. * - * @see SceneParams#getRenderingMode() - * @see LayoutScene#render(long) + * @see RenderParams#getRenderingMode() + * @see RenderSession#render(long) */ public Result render(boolean freshRender) { checkLock(); @@ -428,7 +518,7 @@ public class RenderSessionImpl extends FrameworkResourceIdProvider { if (mMeasuredScreenWidth == -1) { newRenderSize = true; mMeasuredScreenWidth = mParams.getScreenWidth(); - mMeasuredScreenHeight = mParams.getScreenHeight() - mTotalBarSize; + mMeasuredScreenHeight = mParams.getScreenHeight(); if (renderingMode != RenderingMode.NORMAL) { // measure the full size needed by the layout. @@ -476,11 +566,11 @@ public class RenderSessionImpl extends FrameworkResourceIdProvider { if (mParams.getImageFactory() != null) { mImage = mParams.getImageFactory().getImage( mMeasuredScreenWidth, - mMeasuredScreenHeight + mTotalBarSize); + mMeasuredScreenHeight); } else { mImage = new BufferedImage( mMeasuredScreenWidth, - mMeasuredScreenHeight + mTotalBarSize, + mMeasuredScreenHeight, BufferedImage.TYPE_INT_ARGB); newImage = true; } @@ -491,46 +581,26 @@ public class RenderSessionImpl extends FrameworkResourceIdProvider { Graphics2D gc = mImage.createGraphics(); gc.setColor(new Color(mParams.getOverrideBgColor(), true)); gc.setComposite(AlphaComposite.Src); - gc.fillRect(0, 0, mMeasuredScreenWidth, - mMeasuredScreenHeight + mTotalBarSize); + gc.fillRect(0, 0, mMeasuredScreenWidth, mMeasuredScreenHeight); gc.dispose(); } // create an Android bitmap around the BufferedImage Bitmap bitmap = Bitmap_Delegate.createBitmap(mImage, - true /*isMutable*/, - Density.getEnum(mParams.getDensity())); + true /*isMutable*/, mParams.getDensity()); // create a Canvas around the Android bitmap mCanvas = new Canvas(bitmap); - mCanvas.setDensity(mParams.getDensity()); - mCanvas.translate(0, mTopOffset); + mCanvas.setDensity(mParams.getDensity().getDpiValue()); } if (freshRender && newImage == false) { Graphics2D gc = mImage.createGraphics(); 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.fillRect(0, mTopOffset, - mMeasuredScreenWidth, mMeasuredScreenHeight + mTopOffset); - - if (mSystemBarSize > 0) { - gc.setColor(new Color(0xFF3C3C3C, true)); - gc.fillRect(0, mMeasuredScreenHeight + mTopOffset, - mMeasuredScreenWidth, mMeasuredScreenHeight + mTotalBarSize); - } + gc.fillRect(0, 0, + mMeasuredScreenWidth, mMeasuredScreenHeight); // done gc.dispose(); @@ -538,7 +608,7 @@ public class RenderSessionImpl extends FrameworkResourceIdProvider { mViewRoot.draw(mCanvas); - mViewInfoList = visitAllChildren((ViewGroup)mViewRoot, mContext, mTopOffset); + mViewInfoList = startVisitingViews(mViewRoot, 0); // success! 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 * 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, 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 * 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, 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 * 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, 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 * 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) { checkLock(); @@ -989,40 +1059,12 @@ public class RenderSessionImpl extends FrameworkResourceIdProvider { 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) { if (isTabletUi() == false) { boolean windowFullscreen = getBooleanThemeValue(resources, "windowFullscreen", false /*defaultValue*/); - if (windowFullscreen == false) { + if (windowFullscreen == false && mWindowIsFloating == false) { // default value 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) { + if (mWindowIsFloating) { + return; } - } - private void findActionBar(RenderResources resources, DisplayMetrics metrics) { boolean windowActionBar = getBooleanThemeValue(resources, "windowActionBar", true /*defaultValue*/); @@ -1062,7 +1095,7 @@ public class RenderSessionImpl extends FrameworkResourceIdProvider { if (windowActionBar) { // default size of the window title bar - mTopBarSize = DEFAULT_TITLE_BAR_HEIGHT; + mActionBarSize = DEFAULT_TITLE_BAR_HEIGHT; // get value from the theme. ResourceValue value = resources.findItemInTheme("actionBarSize"); @@ -1075,44 +1108,43 @@ public class RenderSessionImpl extends FrameworkResourceIdProvider { TypedValue typedValue = ResourceHelper.getValue(value.getValue()); if (typedValue != null) { // compute the pixel value based on the display metrics - mTopBarSize = (int)typedValue.getDimension(metrics); + mActionBarSize = (int)typedValue.getDimension(metrics); } } - } - } - - private void findTitleBar(RenderResources resources, DisplayMetrics metrics) { - boolean windowNoTitle = getBooleanThemeValue(resources, - "windowNoTitle", false /*defaultValue*/); + } 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 == false) { + if (windowNoTitle == false) { - // default size of the window title bar - mTopBarSize = DEFAULT_TITLE_BAR_HEIGHT; + // default size of the window title bar + mTitleBarSize = DEFAULT_TITLE_BAR_HEIGHT; - // get value from the theme. - ResourceValue value = resources.findItemInTheme("windowTitleSize"); + // get value from the theme. + ResourceValue value = resources.findItemInTheme("windowTitleSize"); - // resolve it - value = resources.resolveResValue(value); + // resolve it + value = resources.resolveResValue(value); - if (value != null) { - // get the numerical value, if available - TypedValue typedValue = ResourceHelper.getValue(value.getValue()); - if (typedValue != null) { - // compute the pixel value based on the display metrics - mTopBarSize = (int)typedValue.getDimension(metrics); + if (value != null) { + // get the numerical value, if available + TypedValue typedValue = ResourceHelper.getValue(value.getValue()); + if (typedValue != null) { + // compute the pixel value based on the display metrics + mTitleBarSize = (int)typedValue.getDimension(metrics); + } } } + } } private void findSystemBar(RenderResources resources, DisplayMetrics metrics) { - if (isTabletUi() && getBooleanThemeValue( - resources, "windowIsFloating", true /*defaultValue*/) == false) { + if (isTabletUi() && mWindowIsFloating == false) { // default value - mSystemBarSize = 56; // ?? + mSystemBarSize = 48; // ?? // get the real value 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 * bounds of all the views. * @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) { return null; } ViewInfo result = new ViewInfo(view.getClass().getName(), - context.getViewKey(view), + mContext.getViewKey(view), view.getLeft(), view.getTop() + offset, view.getRight(), view.getBottom() + offset, view, view.getLayoutParams()); if (view instanceof ViewGroup) { ViewGroup group = ((ViewGroup) view); - result.setChildren(visitAllChildren(group, context, 0 /*offset*/)); + result.setChildren(visitAllChildren(group, 0 /*offset*/)); } 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) { return null; } List<ViewInfo> children = new ArrayList<ViewInfo>(); 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; } diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/ResourceHelper.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/ResourceHelper.java index 25bb81c..cea7cf3 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/ResourceHelper.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/ResourceHelper.java @@ -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.LayoutLog; +import com.android.ide.common.rendering.api.RenderResources; import com.android.ide.common.rendering.api.ResourceValue; import com.android.layoutlib.bridge.Bridge; import com.android.layoutlib.bridge.android.BridgeContext; @@ -28,7 +29,9 @@ import com.android.resources.Density; import org.kxml2.io.KXmlParser; import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserException; +import android.content.res.ColorStateList; import android.graphics.Bitmap; import android.graphics.Bitmap_Delegate; import android.graphics.NinePatch_Delegate; @@ -108,19 +111,63 @@ public final class ResourceHelper { 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. * @param value The value that contains a path to a 9 patch, a bitmap or a xml based drawable, * or an hexadecimal color - * @param context - * @param isFramework indicates whether the resource is a framework resources. - * Framework resources are cached, and loaded only once. + * @param context the current context */ - public static Drawable getDrawable(ResourceValue value, BridgeContext context, - boolean isFramework) { + public static Drawable getDrawable(ResourceValue value, BridgeContext context) { Drawable d = null; String stringValue = value.getValue(); + if (RenderResources.REFERENCE_NULL.equals(stringValue)) { + return null; + } String lowerCaseValue = stringValue.toLowerCase(); @@ -129,9 +176,9 @@ public final class ResourceHelper { if (file.isFile()) { // see if we still have both the chunk and the bitmap in the caches NinePatchChunk chunk = Bridge.getCached9Patch(stringValue, - isFramework ? null : context.getProjectKey()); + value.isFramework() ? null : context.getProjectKey()); 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 (chunk == null || bitmap == null) { @@ -143,7 +190,7 @@ public final class ResourceHelper { chunk = ninePatch.getChunk(); Bridge.setCached9Patch(stringValue, chunk, - isFramework ? null : context.getProjectKey()); + value.isFramework() ? null : context.getProjectKey()); } if (bitmap == null) { @@ -158,7 +205,7 @@ public final class ResourceHelper { density); Bridge.setCachedBitmap(stringValue, bitmap, - isFramework ? null : context.getProjectKey()); + value.isFramework() ? null : context.getProjectKey()); } } } catch (MalformedURLException e) { @@ -192,7 +239,7 @@ public final class ResourceHelper { parser.setInput(new FileReader(f)); d = Drawable.createFromXml(context.getResources(), - new BridgeXmlBlockParser(parser, context, isFramework)); + new BridgeXmlBlockParser(parser, context, value.isFramework())); return d; } catch (Exception e) { // 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()) { try { Bitmap bitmap = Bridge.getCachedBitmap(stringValue, - isFramework ? null : context.getProjectKey()); + value.isFramework() ? null : context.getProjectKey()); if (bitmap == null) { Density density = Density.MEDIUM; @@ -223,7 +270,7 @@ public final class ResourceHelper { bitmap = Bitmap_Delegate.createBitmap(bmpFile, false /*isMutable*/, density); Bridge.setCachedBitmap(stringValue, bitmap, - isFramework ? null : context.getProjectKey()); + value.isFramework() ? null : context.getProjectKey()); } return new BitmapDrawable(context.getResources(), bitmap); |