From bbbb8326020368958a3f1d248878329e9d6b10c0 Mon Sep 17 00:00:00 2001 From: Xavier Ducrohet Date: Thu, 27 Jan 2011 16:58:52 -0800 Subject: [PATCH] LayoutLib: render system/title/action bars. Also a few generic fixes in the layoutlib itself to support this. Change-Id: Ie3f24c9056bd3cc72f39f8a4f2c0861be15bff55 --- tools/layoutlib/bridge/Android.mk | 2 + .../bridge/resources/bars/action_bar.xml | 11 + .../bars/mdpi/ic_sysbar_back_default.png | Bin 0 -> 3900 bytes .../bars/mdpi/ic_sysbar_home_default.png | Bin 0 -> 3991 bytes .../bars/mdpi/ic_sysbar_recent_default.png | Bin 0 -> 3140 bytes .../mdpi/stat_sys_wifi_signal_4_fully.png | Bin 0 -> 885 bytes .../resources/bars/phone_system_bar.xml | 12 + .../resources/bars/tablet_system_bar.xml | 20 ++ .../bridge/resources/bars/title_bar.xml | 7 + .../src/android/graphics/BitmapFactory.java | 29 +- .../com/android/layoutlib/bridge/Bridge.java | 14 +- .../layoutlib/bridge/BridgeRenderSession.java | 10 +- .../bridge/android/BridgeResources.java | 78 ++--- .../bridge/android/BridgeTypedArray.java | 2 +- .../layoutlib/bridge/bars/CustomBar.java | 212 ++++++++++++ .../layoutlib/bridge/bars/FakeActionBar.java | 47 +++ .../layoutlib/bridge/bars/PhoneSystemBar.java | 42 +++ .../bridge/bars/TabletSystemBar.java | 45 +++ .../layoutlib/bridge/bars/TitleBar.java | 46 +++ .../bridge/impl/RenderSessionImpl.java | 325 +++++++++++------- .../layoutlib/bridge/impl/ResourceHelper.java | 71 +++- 21 files changed, 771 insertions(+), 202 deletions(-) create mode 100644 tools/layoutlib/bridge/resources/bars/action_bar.xml create mode 100644 tools/layoutlib/bridge/resources/bars/mdpi/ic_sysbar_back_default.png create mode 100644 tools/layoutlib/bridge/resources/bars/mdpi/ic_sysbar_home_default.png create mode 100644 tools/layoutlib/bridge/resources/bars/mdpi/ic_sysbar_recent_default.png create mode 100644 tools/layoutlib/bridge/resources/bars/mdpi/stat_sys_wifi_signal_4_fully.png create mode 100644 tools/layoutlib/bridge/resources/bars/phone_system_bar.xml create mode 100644 tools/layoutlib/bridge/resources/bars/tablet_system_bar.xml create mode 100644 tools/layoutlib/bridge/resources/bars/title_bar.xml create mode 100644 tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/CustomBar.java create mode 100644 tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/FakeActionBar.java create mode 100644 tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/PhoneSystemBar.java create mode 100644 tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/TabletSystemBar.java create mode 100644 tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/TitleBar.java diff --git a/tools/layoutlib/bridge/Android.mk b/tools/layoutlib/bridge/Android.mk index 3d4c76a36c605..ca7db8ccd63e4 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 0000000000000..cd99a096d07cb --- /dev/null +++ b/tools/layoutlib/bridge/resources/bars/action_bar.xml @@ -0,0 +1,11 @@ + + + + + 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 new file mode 100644 index 0000000000000000000000000000000000000000..4bcd2be5cb86e63b65732615d00ac64b43a9f7c9 GIT binary patch literal 3900 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 literal 0 HcmV?d00001 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 new file mode 100644 index 0000000000000000000000000000000000000000..1d97e0581c59f1293067b9d6600e84006668843e GIT binary patch literal 3140 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 literal 0 HcmV?d00001 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 0000000000000..29df90906fcf5 --- /dev/null +++ b/tools/layoutlib/bridge/resources/bars/phone_system_bar.xml @@ -0,0 +1,12 @@ + + + + + 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 0000000000000..8a3b87ae48c04 --- /dev/null +++ b/tools/layoutlib/bridge/resources/bars/tablet_system_bar.xml @@ -0,0 +1,20 @@ + + + + + + + + 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 0000000000000..29fcc4b40b7f9 --- /dev/null +++ b/tools/layoutlib/bridge/resources/bars/title_bar.xml @@ -0,0 +1,7 @@ + + + + diff --git a/tools/layoutlib/bridge/src/android/graphics/BitmapFactory.java b/tools/layoutlib/bridge/src/android/graphics/BitmapFactory.java index 993c30543e324..ee60eb4a002d1 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 93c81d18013d4..65f6bedeb8189 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 0c6fa20fc9772..765fd990483eb 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 5ea0a8df10f93..d31fcc8422d66 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; @@ -57,6 +58,18 @@ public final class BridgeResources extends Resources { private IProjectCallback mProjectCallback; 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()}. @@ -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 cf2c0ff8947f9..c226b8b54f91c 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 0000000000000..f03999405592d --- /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 0000000000000..3af4e3ada18b3 --- /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 0000000000000..92615dc260d55 --- /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 0000000000000..bc61799b3bd27 --- /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 0000000000000..d7401d9601281 --- /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 c8ad1d6c968ae..0aa2e6d4ea561 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 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; + } + 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); } } - } - } + } 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) { - boolean windowNoTitle = getBooleanThemeValue(resources, - "windowNoTitle", false /*defaultValue*/); + if (windowNoTitle == false) { - if (windowNoTitle == false) { + // default size of the window title bar + mTitleBarSize = DEFAULT_TITLE_BAR_HEIGHT; - // default size of the window title bar - mTopBarSize = 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 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 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 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 visitAllChildren(ViewGroup viewGroup, int offset) { if (viewGroup == null) { return null; } List children = new ArrayList(); 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 25bb81c437350..cea7cf360ce2a 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);