diff options
Diffstat (limited to 'tools/layoutlib')
83 files changed, 2789 insertions, 694 deletions
diff --git a/tools/layoutlib/.idea/inspectionProfiles/Project_Default.xml b/tools/layoutlib/.idea/inspectionProfiles/Project_Default.xml index 0ac7a44..5bb3e3e 100644 --- a/tools/layoutlib/.idea/inspectionProfiles/Project_Default.xml +++ b/tools/layoutlib/.idea/inspectionProfiles/Project_Default.xml @@ -1,3 +1,4 @@ +<?xml version="1.0" encoding="UTF-8"?> <component name="InspectionProjectProfileManager"> <profile version="1.0" is_locked="false"> <option name="myName" value="Project Default" /> @@ -7,5 +8,6 @@ <option name="CHECK_TRY_CATCH_SECTION" value="true" /> <option name="CHECK_METHOD_BODY" value="true" /> </inspection_tool> + <inspection_tool class="ToArrayCallWithZeroLengthArrayArgument" enabled="false" level="WARNING" enabled_by_default="false" /> </profile> </component>
\ No newline at end of file diff --git a/tools/layoutlib/.idea/libraries/asm_4_0.xml b/tools/layoutlib/.idea/libraries/asm_4_0.xml deleted file mode 100644 index 7df287f..0000000 --- a/tools/layoutlib/.idea/libraries/asm_4_0.xml +++ /dev/null @@ -1,11 +0,0 @@ -<component name="libraryTable"> - <library name="asm-4.0"> - <CLASSES> - <root url="jar://$PROJECT_DIR$/../../../../prebuilts/misc/common/asm/asm-4.0.jar!/" /> - </CLASSES> - <JAVADOC /> - <SOURCES> - <root url="jar://$PROJECT_DIR$/../../../../prebuilts/misc/common/asm/src.zip!/" /> - </SOURCES> - </library> -</component>
\ No newline at end of file diff --git a/tools/layoutlib/.idea/libraries/guava.xml b/tools/layoutlib/.idea/libraries/guava.xml deleted file mode 100644 index eb60719..0000000 --- a/tools/layoutlib/.idea/libraries/guava.xml +++ /dev/null @@ -1,11 +0,0 @@ -<component name="libraryTable"> - <library name="guava"> - <CLASSES> - <root url="jar://$PROJECT_DIR$/../../../../prebuilts/tools/common/m2/repository/com/google/guava/guava/15.0/guava-15.0.jar!/" /> - </CLASSES> - <JAVADOC /> - <SOURCES> - <root url="jar://$PROJECT_DIR$/../../../../prebuilts/tools/common/m2/repository/com/google/guava/guava/15.0/guava-15.0-sources.jar!/" /> - </SOURCES> - </library> -</component>
\ No newline at end of file diff --git a/tools/layoutlib/.idea/libraries/icu4j.xml b/tools/layoutlib/.idea/libraries/icu4j.xml deleted file mode 100644 index dbe0bd7..0000000 --- a/tools/layoutlib/.idea/libraries/icu4j.xml +++ /dev/null @@ -1,11 +0,0 @@ -<component name="libraryTable"> - <library name="icu4j"> - <CLASSES> - <root url="jar://$PROJECT_DIR$/../../../../prebuilts/misc/common/icu4j/icu4j.jar!/" /> - </CLASSES> - <JAVADOC> - <root url="http://icu-project.org/apiref/icu4j50rc/" /> - </JAVADOC> - <SOURCES /> - </library> -</component>
\ No newline at end of file diff --git a/tools/layoutlib/.idea/libraries/kxml2_2_3_0.xml b/tools/layoutlib/.idea/libraries/kxml2_2_3_0.xml deleted file mode 100644 index 2a65050..0000000 --- a/tools/layoutlib/.idea/libraries/kxml2_2_3_0.xml +++ /dev/null @@ -1,11 +0,0 @@ -<component name="libraryTable"> - <library name="kxml2-2.3.0"> - <CLASSES> - <root url="jar://$PROJECT_DIR$/../../../../prebuilts/misc/common/kxml2/kxml2-2.3.0.jar!/" /> - </CLASSES> - <JAVADOC /> - <SOURCES> - <root url="file://$PROJECT_DIR$/../../../../libcore/xml/src/main/java" /> - </SOURCES> - </library> -</component>
\ No newline at end of file diff --git a/tools/layoutlib/.idea/libraries/ninepatch_prebuilt.xml b/tools/layoutlib/.idea/libraries/ninepatch_prebuilt.xml deleted file mode 100644 index f34f7dd..0000000 --- a/tools/layoutlib/.idea/libraries/ninepatch_prebuilt.xml +++ /dev/null @@ -1,11 +0,0 @@ -<component name="libraryTable"> - <library name="ninepatch-prebuilt"> - <CLASSES> - <root url="jar://$PROJECT_DIR$/../../../../prebuilts/misc/common/ninepatch/ninepatch-prebuilt.jar!/" /> - </CLASSES> - <JAVADOC /> - <SOURCES> - <root url="file://$ANDROID_SRC$/tools/base/ninepatch/src/main/java" /> - </SOURCES> - </library> -</component>
\ No newline at end of file diff --git a/tools/layoutlib/.idea/libraries/tools_common_prebuilt.xml b/tools/layoutlib/.idea/libraries/tools_common_prebuilt.xml deleted file mode 100644 index b325ad4..0000000 --- a/tools/layoutlib/.idea/libraries/tools_common_prebuilt.xml +++ /dev/null @@ -1,14 +0,0 @@ -<component name="libraryTable"> - <library name="tools-common-prebuilt"> - <ANNOTATIONS> - <root url="file://$PROJECT_DIR$" /> - </ANNOTATIONS> - <CLASSES> - <root url="jar://$PROJECT_DIR$/../../../../prebuilts/misc/common/tools-common/tools-common-prebuilt.jar!/" /> - </CLASSES> - <JAVADOC /> - <SOURCES> - <root url="file://$ANDROID_SRC$/tools/base/common/src/main/java" /> - </SOURCES> - </library> -</component>
\ No newline at end of file diff --git a/tools/layoutlib/.idea/runConfigurations/Create.xml b/tools/layoutlib/.idea/runConfigurations/Create.xml index ff173e5..58f057a 100644 --- a/tools/layoutlib/.idea/runConfigurations/Create.xml +++ b/tools/layoutlib/.idea/runConfigurations/Create.xml @@ -3,7 +3,7 @@ <extension name="coverage" enabled="false" merge="false" sample_coverage="true" runner="idea" /> <option name="MAIN_CLASS_NAME" value="com.android.tools.layoutlib.create.Main" /> <option name="VM_PARAMETERS" value="" /> - <option name="PROGRAM_PARAMETERS" value="out/host/common/obj/JAVA_LIBRARIES/temp_layoutlib_intermediates/javalib.jar out/target/common/obj/JAVA_LIBRARIES/core-libart_intermediates/classes.jar out/target/common/obj/JAVA_LIBRARIES/framework_intermediates/classes.jar out/target/common/obj/JAVA_LIBRARIES/ext_intermediates/classes.jar out/target/common/obj/JAVA_LIBRARIES/ext_intermediates/javalib.jar" /> + <option name="PROGRAM_PARAMETERS" value="out/host/common/obj/JAVA_LIBRARIES/temp_layoutlib_intermediates/javalib.jar out/target/common/obj/JAVA_LIBRARIES/core-libart_intermediates/classes.jar out/target/common/obj/JAVA_LIBRARIES/framework_intermediates/classes.jar out/target/common/obj/JAVA_LIBRARIES/icu4j-icudata-jarjar_intermediates/classes.jar out/target/common/obj/JAVA_LIBRARIES/icu4j-icutzdata-jarjar_intermediates/classes.jar out/target/common/obj/JAVA_LIBRARIES/ext_intermediates/classes.jar out/target/common/obj/JAVA_LIBRARIES/ext_intermediates/javalib.jar" /> <option name="WORKING_DIRECTORY" value="file://$PROJECT_DIR$/../../../../" /> <option name="ALTERNATIVE_JRE_PATH_ENABLED" value="false" /> <option name="ALTERNATIVE_JRE_PATH" value="" /> diff --git a/tools/layoutlib/Android.mk b/tools/layoutlib/Android.mk index 9300401..61ddb04 100644 --- a/tools/layoutlib/Android.mk +++ b/tools/layoutlib/Android.mk @@ -37,6 +37,10 @@ built_ext_dep := $(call java-lib-deps,ext) built_ext_classes := $(call java-lib-files,ext) built_ext_data := $(call intermediates-dir-for, \ JAVA_LIBRARIES,ext,,COMMON)/javalib.jar +built_icudata_dep := $(call java-lib-deps,icu4j-icudata-jarjar) +built_icudata_data := $(call java-lib-files,icu4j-icudata-jarjar) +built_icutzdata_dep := $(call java-lib-deps,icu4j-icutzdata-jarjar) +built_icutzdata_data := $(call java-lib-files,icu4j-icutzdata-jarjar) built_layoutlib_create_jar := $(call intermediates-dir-for, \ JAVA_LIBRARIES,layoutlib_create,HOST)/javalib.jar @@ -56,6 +60,8 @@ $(LOCAL_BUILT_MODULE): $(built_core_dep) \ $(built_framework_dep) \ $(built_ext_dep) \ $(built_ext_data) \ + $(built_icudata_dep) \ + $(built_icutzdata_dep) \ $(built_layoutlib_create_jar) $(hide) echo "host layoutlib_create: $@" $(hide) mkdir -p $(dir $@) @@ -66,6 +72,8 @@ $(LOCAL_BUILT_MODULE): $(built_core_dep) \ $(built_core_classes) \ $(built_framework_classes) \ $(built_ext_classes) \ + $(built_icudata_data) \ + $(built_icutzdata_data) \ $(built_ext_data) $(hide) ls -l $(built_framework_classes) diff --git a/tools/layoutlib/bridge/Android.mk b/tools/layoutlib/bridge/Android.mk index 80b4d59..0dbdd56 100644 --- a/tools/layoutlib/bridge/Android.mk +++ b/tools/layoutlib/bridge/Android.mk @@ -22,7 +22,6 @@ LOCAL_JAVACFLAGS := -source 6 -target 6 LOCAL_JAVA_LIBRARIES := \ - icu4j \ layoutlib_api-prebuilt \ tools-common-prebuilt diff --git a/tools/layoutlib/bridge/bridge.iml b/tools/layoutlib/bridge/bridge.iml index 0baa5ab..ccc10b3 100644 --- a/tools/layoutlib/bridge/bridge.iml +++ b/tools/layoutlib/bridge/bridge.iml @@ -24,15 +24,57 @@ </content> <orderEntry type="inheritedJdk" /> <orderEntry type="sourceFolder" forTests="false" /> - <orderEntry type="library" name="icu4j" level="project" /> <orderEntry type="library" name="layoutlib_api-prebuilt" level="project" /> - <orderEntry type="library" name="ninepatch-prebuilt" level="project" /> - <orderEntry type="library" name="tools-common-prebuilt" level="project" /> + <orderEntry type="module-library"> + <library name="ninepatch-prebuilt"> + <CLASSES> + <root url="jar://$MODULE_DIR$/../../../../../prebuilts/misc/common/ninepatch/ninepatch-prebuilt.jar!/" /> + </CLASSES> + <JAVADOC /> + <SOURCES> + <root url="file://$ANDROID_SRC$/tools/base/ninepatch/src/main/java" /> + </SOURCES> + </library> + </orderEntry> + <orderEntry type="module-library"> + <library name="tools-common-prebuilt"> + <ANNOTATIONS> + <root url="file://$MODULE_DIR$/.." /> + </ANNOTATIONS> + <CLASSES> + <root url="jar://$MODULE_DIR$/../../../../../prebuilts/misc/common/tools-common/tools-common-prebuilt.jar!/" /> + </CLASSES> + <JAVADOC /> + <SOURCES> + <root url="jar://$MODULE_DIR$/../../../../../prebuilts/misc/common/tools-common/tools-common-prebuilt-sources.jar!/" /> + </SOURCES> + </library> + </orderEntry> <orderEntry type="library" name="framework.jar" level="project" /> - <orderEntry type="library" scope="TEST" name="kxml2-2.3.0" level="project" /> - <orderEntry type="library" scope="TEST" name="guava" level="project" /> <orderEntry type="module-library" scope="TEST"> - <library> + <library name="kxml2-2.3.0"> + <CLASSES> + <root url="jar://$MODULE_DIR$/../../../../../prebuilts/misc/common/kxml2/kxml2-2.3.0.jar!/" /> + </CLASSES> + <JAVADOC /> + <SOURCES> + <root url="file://$MODULE_DIR$/../../../../../libcore/xml/src/main/java" /> + </SOURCES> + </library> + </orderEntry> + <orderEntry type="module-library" scope="TEST"> + <library name="guava"> + <CLASSES> + <root url="jar://$MODULE_DIR$/../../../../../prebuilts/tools/common/m2/repository/com/google/guava/guava/15.0/guava-15.0.jar!/" /> + </CLASSES> + <JAVADOC /> + <SOURCES> + <root url="jar://$MODULE_DIR$/../../../../../prebuilts/tools/common/m2/repository/com/google/guava/guava/15.0/guava-15.0-sources.jar!/" /> + </SOURCES> + </library> + </orderEntry> + <orderEntry type="module-library" scope="TEST"> + <library name="sdk-common"> <CLASSES> <root url="jar://$MODULE_DIR$/../../../../../prebuilts/misc/common/sdk-common/sdk-common.jar!/" /> </CLASSES> diff --git a/tools/layoutlib/bridge/resources/bars/navigation_bar.xml b/tools/layoutlib/bridge/resources/bars/navigation_bar.xml index 79920a1..55bd1d2 100644 --- a/tools/layoutlib/bridge/resources/bars/navigation_bar.xml +++ b/tools/layoutlib/bridge/resources/bars/navigation_bar.xml @@ -1,8 +1,25 @@ <?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2015 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> + <merge xmlns:android="http://schemas.android.com/apk/res/android"> <View android:layout_width="wrap_content" - android:layout_height="wrap_content"/> + android:layout_height="wrap_content" + android:visibility="invisible"/> <ImageView android:layout_height="wrap_content" android:layout_width="wrap_content" @@ -10,20 +27,23 @@ <View android:layout_height="wrap_content" android:layout_width="wrap_content" - android:layout_weight="1"/> + android:layout_weight="1" + android:visibility="invisible"/> <ImageView android:layout_height="wrap_content" android:layout_width="wrap_content" android:scaleType="centerInside"/> <View - android:layout_height="wrap_content" - android:layout_width="wrap_content" - android:layout_weight="1"/> + android:layout_height="wrap_content" + android:layout_width="wrap_content" + android:layout_weight="1" + android:visibility="invisible"/> <ImageView android:layout_height="wrap_content" android:layout_width="wrap_content" android:scaleType="centerInside"/> <View android:layout_width="wrap_content" - android:layout_height="wrap_content"/> + android:layout_height="wrap_content" + android:visibility="invisible"/> </merge> diff --git a/tools/layoutlib/bridge/resources/bars/navigation_bar600dp.xml b/tools/layoutlib/bridge/resources/bars/navigation_bar600dp.xml new file mode 100644 index 0000000..e208a0d --- /dev/null +++ b/tools/layoutlib/bridge/resources/bars/navigation_bar600dp.xml @@ -0,0 +1,49 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2015 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> + +<merge xmlns:android="http://schemas.android.com/apk/res/android"> + <View + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_weight="1" + android:visibility="invisible"/> + <ImageView + android:layout_height="wrap_content" + android:layout_width="wrap_content" + android:scaleType="centerInside"/> + <View + android:layout_height="wrap_content" + android:layout_width="wrap_content" + android:visibility="invisible"/> + <ImageView + android:layout_height="wrap_content" + android:layout_width="wrap_content" + android:scaleType="centerInside"/> + <View + android:layout_height="wrap_content" + android:layout_width="wrap_content" + android:visibility="invisible"/> + <ImageView + android:layout_height="wrap_content" + android:layout_width="wrap_content" + android:scaleType="centerInside"/> + <View + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_weight="1" + android:visibility="invisible"/> +</merge> diff --git a/tools/layoutlib/bridge/src/android/content/res/BridgeResources.java b/tools/layoutlib/bridge/src/android/content/res/BridgeResources.java index e5b47d6..163fbcb 100644 --- a/tools/layoutlib/bridge/src/android/content/res/BridgeResources.java +++ b/tools/layoutlib/bridge/src/android/content/res/BridgeResources.java @@ -174,7 +174,7 @@ public final class BridgeResources extends Resources { } @Override - public int getColor(int id) throws NotFoundException { + public int getColor(int id, Theme theme) throws NotFoundException { Pair<String, ResourceValue> value = getResourceValue(id, mPlatformResourceFlag); if (value != null) { @@ -197,22 +197,21 @@ public final class BridgeResources extends Resources { } } - // id was not found or not resolved. Throw a NotFoundException. - throwException(id); - - // this is not used since the method above always throws - return 0; + // Suppress possible NPE. getColorStateList will never return null, it will instead + // throw an exception, but intelliJ can't figure that out + //noinspection ConstantConditions + return getColorStateList(id, theme).getDefaultColor(); } @Override - public ColorStateList getColorStateList(int id) throws NotFoundException { + public ColorStateList getColorStateList(int id, Theme theme) throws NotFoundException { Pair<String, ResourceValue> resValue = getResourceValue(id, mPlatformResourceFlag); if (resValue != null) { ColorStateList stateList = ResourceHelper.getColorStateList(resValue.getSecond(), mContext); if (stateList != null) { - return stateList; + return stateList.obtainForTheme(theme); } } diff --git a/tools/layoutlib/bridge/src/android/content/res/BridgeTypedArray.java b/tools/layoutlib/bridge/src/android/content/res/BridgeTypedArray.java index b8fd1ca..6a61090 100644 --- a/tools/layoutlib/bridge/src/android/content/res/BridgeTypedArray.java +++ b/tools/layoutlib/bridge/src/android/content/res/BridgeTypedArray.java @@ -334,7 +334,8 @@ public final class BridgeTypedArray extends TypedArray { BridgeXmlBlockParser blockParser = new BridgeXmlBlockParser( parser, mContext, resValue.isFramework()); try { - return ColorStateList.createFromXml(mContext.getResources(), blockParser); + return ColorStateList.createFromXml(mContext.getResources(), blockParser, + mContext.getTheme()); } finally { blockParser.ensurePopped(); } diff --git a/tools/layoutlib/bridge/src/android/content/res/Resources_Theme_Delegate.java b/tools/layoutlib/bridge/src/android/content/res/Resources_Theme_Delegate.java index 5d36b2c..f1e8fc2 100644 --- a/tools/layoutlib/bridge/src/android/content/res/Resources_Theme_Delegate.java +++ b/tools/layoutlib/bridge/src/android/content/res/Resources_Theme_Delegate.java @@ -27,6 +27,7 @@ import com.android.tools.layoutlib.annotations.LayoutlibDelegate; import android.annotation.Nullable; import android.content.res.Resources.NotFoundException; import android.content.res.Resources.Theme; +import android.content.res.Resources.ThemeKey; import android.util.AttributeSet; import android.util.TypedValue; @@ -110,22 +111,16 @@ public class Resources_Theme_Delegate { private static boolean setupResources(Theme thisTheme) { // Key is a space-separated list of theme ids applied that have been merged into the // BridgeContext's theme to make thisTheme. - String[] appliedStyles = thisTheme.getKey().split(" "); + final ThemeKey key = thisTheme.getKey(); + final int[] resId = key.mResId; + final boolean[] force = key.mForce; + boolean changed = false; - for (String s : appliedStyles) { - if (s.isEmpty()) { - continue; - } - // See the definition of force parameter in Theme.applyStyle(). - boolean force = false; - if (s.charAt(s.length() - 1) == '!') { - force = true; - s = s.substring(0, s.length() - 1); - } - int styleId = Integer.parseInt(s, 16); - StyleResourceValue style = resolveStyle(styleId); + for (int i = 0, N = key.mCount; i < N; i++) { + StyleResourceValue style = resolveStyle(resId[i]); if (style != null) { - RenderSessionImpl.getCurrentContext().getRenderResources().applyStyle(style, force); + RenderSessionImpl.getCurrentContext().getRenderResources().applyStyle( + style, force[i]); changed = true; } diff --git a/tools/layoutlib/bridge/src/android/graphics/BidiRenderer.java b/tools/layoutlib/bridge/src/android/graphics/BidiRenderer.java index a4a3b7d..21f36ce 100644 --- a/tools/layoutlib/bridge/src/android/graphics/BidiRenderer.java +++ b/tools/layoutlib/bridge/src/android/graphics/BidiRenderer.java @@ -19,6 +19,12 @@ package android.graphics; import com.android.ide.common.rendering.api.LayoutLog; import com.android.layoutlib.bridge.Bridge; +import android.graphics.Paint_Delegate.FontInfo; +import android.icu.lang.UScript; +import android.icu.lang.UScriptRun; +import android.icu.text.Bidi; +import android.icu.text.BidiRun; + import java.awt.Font; import java.awt.Graphics2D; import java.awt.Toolkit; @@ -29,13 +35,6 @@ import java.util.ArrayList; import java.util.LinkedList; import java.util.List; -import com.ibm.icu.lang.UScript; -import com.ibm.icu.lang.UScriptRun; -import com.ibm.icu.text.Bidi; -import com.ibm.icu.text.BidiRun; - -import android.graphics.Paint_Delegate.FontInfo; - /** * Render the text by breaking it into various scripts and using the right font for each script. * Can be used to measure the text without actually drawing it. diff --git a/tools/layoutlib/bridge/src/android/graphics/BitmapShader_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/BitmapShader_Delegate.java index e9b5d6e..af47aeb 100644 --- a/tools/layoutlib/bridge/src/android/graphics/BitmapShader_Delegate.java +++ b/tools/layoutlib/bridge/src/android/graphics/BitmapShader_Delegate.java @@ -23,7 +23,15 @@ import com.android.tools.layoutlib.annotations.LayoutlibDelegate; import android.graphics.Shader.TileMode; +import java.awt.PaintContext; +import java.awt.Rectangle; +import java.awt.RenderingHints; +import java.awt.geom.AffineTransform; +import java.awt.geom.NoninvertibleTransformException; +import java.awt.geom.Rectangle2D; +import java.awt.image.BufferedImage; import java.awt.image.ColorModel; +import java.awt.image.Raster; /** * Delegate implementing the native methods of android.graphics.BitmapShader @@ -67,9 +75,9 @@ public class BitmapShader_Delegate extends Shader_Delegate { // ---- native methods ---- @LayoutlibDelegate - /*package*/ static long nativeCreate(long native_bitmap, int shaderTileModeX, + /*package*/ static long nativeCreate(Bitmap androidBitmap, int shaderTileModeX, int shaderTileModeY) { - Bitmap_Delegate bitmap = Bitmap_Delegate.getDelegate(native_bitmap); + Bitmap_Delegate bitmap = Bitmap_Delegate.getDelegate(androidBitmap); if (bitmap == null) { return 0; } @@ -83,17 +91,17 @@ public class BitmapShader_Delegate extends Shader_Delegate { // ---- Private delegate/helper methods ---- - private BitmapShader_Delegate(java.awt.image.BufferedImage image, + private BitmapShader_Delegate(BufferedImage image, TileMode tileModeX, TileMode tileModeY) { mJavaPaint = new BitmapShaderPaint(image, tileModeX, tileModeY); } private class BitmapShaderPaint implements java.awt.Paint { - private final java.awt.image.BufferedImage mImage; + private final BufferedImage mImage; private final TileMode mTileModeX; private final TileMode mTileModeY; - BitmapShaderPaint(java.awt.image.BufferedImage image, + BitmapShaderPaint(BufferedImage image, TileMode tileModeX, TileMode tileModeY) { mImage = image; mTileModeX = tileModeX; @@ -101,29 +109,24 @@ public class BitmapShader_Delegate extends Shader_Delegate { } @Override - public java.awt.PaintContext createContext( - java.awt.image.ColorModel colorModel, - java.awt.Rectangle deviceBounds, - java.awt.geom.Rectangle2D userBounds, - java.awt.geom.AffineTransform xform, - java.awt.RenderingHints hints) { - - java.awt.geom.AffineTransform canvasMatrix; + public PaintContext createContext(ColorModel colorModel, Rectangle deviceBounds, + Rectangle2D userBounds, AffineTransform xform, RenderingHints hints) { + AffineTransform canvasMatrix; try { canvasMatrix = xform.createInverse(); - } catch (java.awt.geom.NoninvertibleTransformException e) { + } catch (NoninvertibleTransformException e) { Bridge.getLog().fidelityWarning(LayoutLog.TAG_MATRIX_INVERSE, "Unable to inverse matrix in BitmapShader", e, null /*data*/); - canvasMatrix = new java.awt.geom.AffineTransform(); + canvasMatrix = new AffineTransform(); } - java.awt.geom.AffineTransform localMatrix = getLocalMatrix(); + AffineTransform localMatrix = getLocalMatrix(); try { localMatrix = localMatrix.createInverse(); - } catch (java.awt.geom.NoninvertibleTransformException e) { + } catch (NoninvertibleTransformException e) { Bridge.getLog().fidelityWarning(LayoutLog.TAG_MATRIX_INVERSE, "Unable to inverse matrix in BitmapShader", e, null /*data*/); - localMatrix = new java.awt.geom.AffineTransform(); + localMatrix = new AffineTransform(); } if (!colorModel.isCompatibleRaster(mImage.getRaster())) { @@ -134,16 +137,16 @@ public class BitmapShader_Delegate extends Shader_Delegate { return new BitmapShaderContext(canvasMatrix, localMatrix, colorModel); } - private class BitmapShaderContext implements java.awt.PaintContext { + private class BitmapShaderContext implements PaintContext { - private final java.awt.geom.AffineTransform mCanvasMatrix; - private final java.awt.geom.AffineTransform mLocalMatrix; - private final java.awt.image.ColorModel mColorModel; + private final AffineTransform mCanvasMatrix; + private final AffineTransform mLocalMatrix; + private final ColorModel mColorModel; public BitmapShaderContext( - java.awt.geom.AffineTransform canvasMatrix, - java.awt.geom.AffineTransform localMatrix, - java.awt.image.ColorModel colorModel) { + AffineTransform canvasMatrix, + AffineTransform localMatrix, + ColorModel colorModel) { mCanvasMatrix = canvasMatrix; mLocalMatrix = localMatrix; mColorModel = colorModel; @@ -154,13 +157,13 @@ public class BitmapShader_Delegate extends Shader_Delegate { } @Override - public java.awt.image.ColorModel getColorModel() { + public ColorModel getColorModel() { return mColorModel; } @Override - public java.awt.image.Raster getRaster(int x, int y, int w, int h) { - java.awt.image.BufferedImage image = new java.awt.image.BufferedImage( + public Raster getRaster(int x, int y, int w, int h) { + BufferedImage image = new BufferedImage( mColorModel, mColorModel.createCompatibleWritableRaster(w, h), mColorModel.isAlphaPremultiplied(), null); diff --git a/tools/layoutlib/bridge/src/android/graphics/Bitmap_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/Bitmap_Delegate.java index 57a8bbc..0737682 100644 --- a/tools/layoutlib/bridge/src/android/graphics/Bitmap_Delegate.java +++ b/tools/layoutlib/bridge/src/android/graphics/Bitmap_Delegate.java @@ -77,19 +77,18 @@ public final class Bitmap_Delegate { // ---- Public Helper methods ---- /** - * Returns the native delegate associated to a given {@link Bitmap_Delegate} object. - */ - public static Bitmap_Delegate getDelegate(Bitmap bitmap) { - return sManager.getDelegate(bitmap.mNativeBitmap); - } - - /** * Returns the native delegate associated to a given an int referencing a {@link Bitmap} object. */ public static Bitmap_Delegate getDelegate(long native_bitmap) { return sManager.getDelegate(native_bitmap); } + @Nullable + public static Bitmap_Delegate getDelegate(@Nullable Bitmap bitmap) { + // refSkPixelRef is a hack to get the native pointer: see #nativeRefPixelRef() + return bitmap == null ? null : getDelegate(bitmap.refSkPixelRef()); + } + /** * Creates and returns a {@link Bitmap} initialized with the given file content. * @@ -188,31 +187,7 @@ public final class Bitmap_Delegate { return createBitmap(delegate, createFlags, density.getDpiValue()); } - /** - * Returns the {@link BufferedImage} used by the delegate of the given {@link Bitmap}. - */ - public static BufferedImage getImage(Bitmap bitmap) { - // get the delegate from the native int. - Bitmap_Delegate delegate = sManager.getDelegate(bitmap.mNativeBitmap); - if (delegate == null) { - return null; - } - - return delegate.mImage; - } - - public static int getBufferedImageType(int nativeBitmapConfig) { - switch (Config.nativeToConfig(nativeBitmapConfig)) { - case ALPHA_8: - return BufferedImage.TYPE_INT_ARGB; - case RGB_565: - return BufferedImage.TYPE_INT_ARGB; - case ARGB_4444: - return BufferedImage.TYPE_INT_ARGB; - case ARGB_8888: - return BufferedImage.TYPE_INT_ARGB; - } - + private static int getBufferedImageType() { return BufferedImage.TYPE_INT_ARGB; } @@ -239,10 +214,6 @@ public final class Bitmap_Delegate { return mHasAlpha && mConfig != Config.RGB_565; } - public boolean hasMipMap() { - // TODO: check if more checks are required as in hasAlpha. - return mHasMipMap; - } /** * Update the generationId. * @@ -257,7 +228,7 @@ public final class Bitmap_Delegate { @LayoutlibDelegate /*package*/ static Bitmap nativeCreate(int[] colors, int offset, int stride, int width, int height, int nativeConfig, boolean isMutable) { - int imageType = getBufferedImageType(nativeConfig); + int imageType = getBufferedImageType(); // create the image BufferedImage image = new BufferedImage(width, height, imageType); @@ -285,7 +256,7 @@ public final class Bitmap_Delegate { int width = srcImage.getWidth(); int height = srcImage.getHeight(); - int imageType = getBufferedImageType(nativeConfig); + int imageType = getBufferedImageType(); // create the image BufferedImage image = new BufferedImage(width, height, imageType); @@ -303,6 +274,13 @@ public final class Bitmap_Delegate { } @LayoutlibDelegate + /*package*/ static Bitmap nativeCopyAshmem(long nativeSrcBitmap) { + // Unused method; no implementation provided. + assert false; + return null; + } + + @LayoutlibDelegate /*package*/ static void nativeDestructor(long nativeBitmap) { sManager.removeJavaReferenceFor(nativeBitmap); } @@ -374,22 +352,16 @@ public final class Bitmap_Delegate { /*package*/ static boolean nativeHasAlpha(long nativeBitmap) { // get the delegate from the native int. Bitmap_Delegate delegate = sManager.getDelegate(nativeBitmap); - if (delegate == null) { - return true; - } + return delegate == null || delegate.mHasAlpha; - return delegate.mHasAlpha; } @LayoutlibDelegate /*package*/ static boolean nativeHasMipMap(long nativeBitmap) { // get the delegate from the native int. Bitmap_Delegate delegate = sManager.getDelegate(nativeBitmap); - if (delegate == null) { - return true; - } + return delegate == null || delegate.mHasMipMap; - return delegate.mHasMipMap; } @LayoutlibDelegate @@ -510,11 +482,6 @@ public final class Bitmap_Delegate { } @LayoutlibDelegate - /*package*/ static void nativePrepareToDraw(long nativeBitmap) { - // nothing to be done here. - } - - @LayoutlibDelegate /*package*/ static boolean nativeIsPremultiplied(long nativeBitmap) { // get the delegate from the native int. Bitmap_Delegate delegate = sManager.getDelegate(nativeBitmap); @@ -600,6 +567,14 @@ public final class Bitmap_Delegate { return Arrays.equals(argb1, argb2); } + // Only used by AssetAtlasService, which we don't care about. + @LayoutlibDelegate + /*package*/ static long nativeRefPixelRef(long nativeBitmap) { + // Hack: This is called by Bitmap.refSkPixelRef() and LayoutLib uses that method to get + // the native pointer from a Bitmap. So, we return nativeBitmap here. + return nativeBitmap; + } + // ---- Private delegate/helper methods ---- private Bitmap_Delegate(BufferedImage image, Config config) { diff --git a/tools/layoutlib/bridge/src/android/graphics/Canvas_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/Canvas_Delegate.java index 0b95cf3..f8b3739 100644 --- a/tools/layoutlib/bridge/src/android/graphics/Canvas_Delegate.java +++ b/tools/layoutlib/bridge/src/android/graphics/Canvas_Delegate.java @@ -115,7 +115,11 @@ public final class Canvas_Delegate { } @LayoutlibDelegate - /*package*/ static long initRaster(long nativeBitmapOrZero) { + /*package*/ static long initRaster(@Nullable Bitmap bitmap) { + long nativeBitmapOrZero = 0; + if (bitmap != null) { + nativeBitmapOrZero = bitmap.refSkPixelRef(); + } if (nativeBitmapOrZero > 0) { // get the Bitmap from the int Bitmap_Delegate bitmapDelegate = Bitmap_Delegate.getDelegate(nativeBitmapOrZero); @@ -133,8 +137,7 @@ public final class Canvas_Delegate { } @LayoutlibDelegate - /*package*/ - static void native_setBitmap(long canvas, long bitmap, boolean copyState) { + /*package*/ static void native_setBitmap(long canvas, Bitmap bitmap) { Canvas_Delegate canvasDelegate = sManager.getDelegate(canvas); Bitmap_Delegate bitmapDelegate = Bitmap_Delegate.getDelegate(bitmap); if (canvasDelegate == null || bitmapDelegate==null) { @@ -221,7 +224,8 @@ public final class Canvas_Delegate { } @LayoutlibDelegate - /*package*/ static void native_restore(long nativeCanvas) { + /*package*/ static void native_restore(long nativeCanvas, boolean throwOnUnderflow) { + // FIXME: implement throwOnUnderflow. // get the delegate from the native int. Canvas_Delegate canvasDelegate = sManager.getDelegate(nativeCanvas); if (canvasDelegate == null) { @@ -232,7 +236,9 @@ public final class Canvas_Delegate { } @LayoutlibDelegate - /*package*/ static void native_restoreToCount(long nativeCanvas, int saveCount) { + /*package*/ static void native_restoreToCount(long nativeCanvas, int saveCount, + boolean throwOnUnderflow) { + // FIXME: implement throwOnUnderflow. // get the delegate from the native int. Canvas_Delegate canvasDelegate = sManager.getDelegate(nativeCanvas); if (canvasDelegate == null) { @@ -425,8 +431,7 @@ public final class Canvas_Delegate { canvasDelegate.mDrawFilter = DrawFilter_Delegate.getDelegate(nativeFilter); - if (canvasDelegate.mDrawFilter != null && - canvasDelegate.mDrawFilter.isSupported() == false) { + if (canvasDelegate.mDrawFilter != null && !canvasDelegate.mDrawFilter.isSupported()) { Bridge.getLog().fidelityWarning(LayoutLog.TAG_DRAWFILTER, canvasDelegate.mDrawFilter.getSupportMessage(), null, null /*data*/); } @@ -442,7 +447,7 @@ public final class Canvas_Delegate { } Rectangle rect = canvasDelegate.getSnapshot().getClip().getBounds(); - if (rect != null && rect.isEmpty() == false) { + if (rect != null && !rect.isEmpty()) { bounds.left = rect.x; bounds.top = rect.y; bounds.right = rect.x + rect.width; @@ -718,7 +723,7 @@ public final class Canvas_Delegate { } @LayoutlibDelegate - /*package*/ static void native_drawBitmap(Canvas thisCanvas, long nativeCanvas, long bitmap, + /*package*/ static void native_drawBitmap(Canvas thisCanvas, long nativeCanvas, Bitmap bitmap, float left, float top, long nativePaintOrZero, int canvasDensity, @@ -740,7 +745,7 @@ public final class Canvas_Delegate { } @LayoutlibDelegate - /*package*/ static void native_drawBitmap(Canvas thisCanvas, long nativeCanvas, long bitmap, + /*package*/ static void native_drawBitmap(Canvas thisCanvas, long nativeCanvas, Bitmap bitmap, float srcLeft, float srcTop, float srcRight, float srcBottom, float dstLeft, float dstTop, float dstRight, float dstBottom, long nativePaintOrZero, int screenDensity, int bitmapDensity) { @@ -781,7 +786,7 @@ public final class Canvas_Delegate { } @LayoutlibDelegate - /*package*/ static void nativeDrawBitmapMatrix(long nCanvas, long nBitmap, + /*package*/ static void nativeDrawBitmapMatrix(long nCanvas, Bitmap bitmap, long nMatrix, long nPaint) { // get the delegate from the native int. Canvas_Delegate canvasDelegate = sManager.getDelegate(nCanvas); @@ -793,7 +798,7 @@ public final class Canvas_Delegate { Paint_Delegate paintDelegate = Paint_Delegate.getDelegate(nPaint); // get the delegate from the native int. - Bitmap_Delegate bitmapDelegate = Bitmap_Delegate.getDelegate(nBitmap); + Bitmap_Delegate bitmapDelegate = Bitmap_Delegate.getDelegate(bitmap); if (bitmapDelegate == null) { return; } @@ -822,7 +827,7 @@ public final class Canvas_Delegate { } @LayoutlibDelegate - /*package*/ static void nativeDrawBitmapMesh(long nCanvas, long nBitmap, + /*package*/ static void nativeDrawBitmapMesh(long nCanvas, Bitmap bitmap, int meshWidth, int meshHeight, float[] verts, int vertOffset, int[] colors, int colorOffset, long nPaint) { // FIXME @@ -845,7 +850,7 @@ public final class Canvas_Delegate { @LayoutlibDelegate /*package*/ static void native_drawText(long nativeCanvas, char[] text, int index, int count, float startX, float startY, int flags, long paint, long typeface) { - drawText(nativeCanvas, text, index, count, startX, startY, flags == Canvas.DIRECTION_RTL, + drawText(nativeCanvas, text, index, count, startX, startY, (flags & 1) != 0, paint, typeface); } @@ -1039,8 +1044,7 @@ public final class Canvas_Delegate { } /** - * Restores the {@link GcSnapshot} to <var>saveCount</var> - * @param saveCount the saveCount + * Restores the top {@link GcSnapshot} */ private void restore() { mSnapshot = mSnapshot.restore(); @@ -1103,7 +1107,7 @@ public final class Canvas_Delegate { // before drawing it. if (bitmap.getConfig() == Bitmap.Config.ALPHA_8) { fixAlpha8Bitmap(image); - } else if (bitmap.hasAlpha() == false) { + } else if (!bitmap.hasAlpha()) { // hasAlpha is merely a rendering hint. There can in fact be alpha values // in the bitmap but it should be ignored at drawing time. // There is two ways to do this: @@ -1123,7 +1127,7 @@ public final class Canvas_Delegate { } // if we can't force SRC mode, then create a temp bitmap of TYPE_RGB - if (forceSrcMode[0] == false) { + if (!forceSrcMode[0]) { image = Bitmap_Delegate.createCopy(image, BufferedImage.TYPE_INT_RGB, 0xFF); } } diff --git a/tools/layoutlib/bridge/src/android/graphics/NinePatch_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/NinePatch_Delegate.java index e16dbda..e8d34d0 100644 --- a/tools/layoutlib/bridge/src/android/graphics/NinePatch_Delegate.java +++ b/tools/layoutlib/bridge/src/android/graphics/NinePatch_Delegate.java @@ -90,7 +90,7 @@ public final class NinePatch_Delegate { if (oos != null) { try { oos.close(); - } catch (IOException e) { + } catch (IOException ignored) { } } } @@ -136,7 +136,7 @@ public final class NinePatch_Delegate { if (ois != null) { try { ois.close(); - } catch (IOException e) { + } catch (IOException ignored) { } } } @@ -150,15 +150,12 @@ public final class NinePatch_Delegate { @LayoutlibDelegate /*package*/ static boolean isNinePatchChunk(byte[] chunk) { NinePatchChunk chunkObject = getChunk(chunk); - if (chunkObject != null) { - return true; - } + return chunkObject != null; - return false; } @LayoutlibDelegate - /*package*/ static long validateNinePatchChunk(long bitmap, byte[] chunk) { + /*package*/ static long validateNinePatchChunk(byte[] chunk) { // the default JNI implementation only checks that the byte[] has the same // size as the C struct it represent. Since we cannot do the same check (serialization // will return different size depending on content), we do nothing. @@ -173,7 +170,7 @@ public final class NinePatch_Delegate { } @LayoutlibDelegate - /*package*/ static void nativeDraw(long canvas_instance, RectF loc, long bitmap_instance, + /*package*/ static void nativeDraw(long canvas_instance, RectF loc, Bitmap bitmap_instance, long chunk, long paint_instance_or_null, int destDensity, int srcDensity) { draw(canvas_instance, (int) loc.left, (int) loc.top, (int) loc.right, (int) loc.bottom, @@ -182,7 +179,7 @@ public final class NinePatch_Delegate { } @LayoutlibDelegate - /*package*/ static void nativeDraw(long canvas_instance, Rect loc, long bitmap_instance, + /*package*/ static void nativeDraw(long canvas_instance, Rect loc, Bitmap bitmap_instance, long chunk, long paint_instance_or_null, int destDensity, int srcDensity) { draw(canvas_instance, loc.left, loc.top, loc.right, loc.bottom, @@ -191,7 +188,7 @@ public final class NinePatch_Delegate { } @LayoutlibDelegate - /*package*/ static long nativeGetTransparentRegion(long bitmap, long chunk, Rect location) { + /*package*/ static long nativeGetTransparentRegion(Bitmap bitmap, long chunk, Rect location) { return 0; } @@ -199,7 +196,7 @@ public final class NinePatch_Delegate { private static void draw(long canvas_instance, final int left, final int top, final int right, final int bottom, - long bitmap_instance, long chunk, long paint_instance_or_null, + Bitmap bitmap_instance, long chunk, long paint_instance_or_null, final int destDensity, final int srcDensity) { // get the delegate from the native int. final Bitmap_Delegate bitmap_delegate = Bitmap_Delegate.getDelegate(bitmap_instance); diff --git a/tools/layoutlib/bridge/src/android/graphics/Paint_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/Paint_Delegate.java index 9355474..65b65ec 100644 --- a/tools/layoutlib/bridge/src/android/graphics/Paint_Delegate.java +++ b/tools/layoutlib/bridge/src/android/graphics/Paint_Delegate.java @@ -85,6 +85,8 @@ public class Paint_Delegate { private float mTextScaleX; private float mTextSkewX; private int mHintingMode = Paint.HINTING_ON; + private int mHyphenEdit; + private float mLetterSpacing; // not used in actual text rendering. // Variant of the font. A paint's variant can only be compact or elegant. private FontVariant mFontVariant = FontVariant.COMPACT; @@ -102,6 +104,7 @@ public class Paint_Delegate { // ---- Public Helper methods ---- + @Nullable public static Paint_Delegate getDelegate(long native_paint) { return sManager.getDelegate(native_paint); } @@ -253,7 +256,7 @@ public class Paint_Delegate { @LayoutlibDelegate /*package*/ static int getFlags(Paint thisPaint) { // get the delegate from the native int. - Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint); + Paint_Delegate delegate = sManager.getDelegate(thisPaint.getNativeInstance()); if (delegate == null) { return 0; } @@ -266,7 +269,7 @@ public class Paint_Delegate { @LayoutlibDelegate /*package*/ static void setFlags(Paint thisPaint, int flags) { // get the delegate from the native int. - Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint); + Paint_Delegate delegate = sManager.getDelegate(thisPaint.getNativeInstance()); if (delegate == null) { return; } @@ -282,7 +285,7 @@ public class Paint_Delegate { @LayoutlibDelegate /*package*/ static int getHinting(Paint thisPaint) { // get the delegate from the native int. - Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint); + Paint_Delegate delegate = sManager.getDelegate(thisPaint.getNativeInstance()); if (delegate == null) { return Paint.HINTING_ON; } @@ -293,7 +296,7 @@ public class Paint_Delegate { @LayoutlibDelegate /*package*/ static void setHinting(Paint thisPaint, int mode) { // get the delegate from the native int. - Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint); + Paint_Delegate delegate = sManager.getDelegate(thisPaint.getNativeInstance()); if (delegate == null) { return; } @@ -339,7 +342,7 @@ public class Paint_Delegate { @LayoutlibDelegate /*package*/ static int getColor(Paint thisPaint) { // get the delegate from the native int. - Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint); + Paint_Delegate delegate = sManager.getDelegate(thisPaint.getNativeInstance()); if (delegate == null) { return 0; } @@ -350,7 +353,7 @@ public class Paint_Delegate { @LayoutlibDelegate /*package*/ static void setColor(Paint thisPaint, int color) { // get the delegate from the native int. - Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint); + Paint_Delegate delegate = sManager.getDelegate(thisPaint.getNativeInstance()); if (delegate == null) { return; } @@ -361,7 +364,7 @@ public class Paint_Delegate { @LayoutlibDelegate /*package*/ static int getAlpha(Paint thisPaint) { // get the delegate from the native int. - Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint); + Paint_Delegate delegate = sManager.getDelegate(thisPaint.getNativeInstance()); if (delegate == null) { return 0; } @@ -372,7 +375,7 @@ public class Paint_Delegate { @LayoutlibDelegate /*package*/ static void setAlpha(Paint thisPaint, int a) { // get the delegate from the native int. - Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint); + Paint_Delegate delegate = sManager.getDelegate(thisPaint.getNativeInstance()); if (delegate == null) { return; } @@ -383,7 +386,7 @@ public class Paint_Delegate { @LayoutlibDelegate /*package*/ static float getStrokeWidth(Paint thisPaint) { // get the delegate from the native int. - Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint); + Paint_Delegate delegate = sManager.getDelegate(thisPaint.getNativeInstance()); if (delegate == null) { return 1.f; } @@ -394,7 +397,7 @@ public class Paint_Delegate { @LayoutlibDelegate /*package*/ static void setStrokeWidth(Paint thisPaint, float width) { // get the delegate from the native int. - Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint); + Paint_Delegate delegate = sManager.getDelegate(thisPaint.getNativeInstance()); if (delegate == null) { return; } @@ -405,7 +408,7 @@ public class Paint_Delegate { @LayoutlibDelegate /*package*/ static float getStrokeMiter(Paint thisPaint) { // get the delegate from the native int. - Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint); + Paint_Delegate delegate = sManager.getDelegate(thisPaint.getNativeInstance()); if (delegate == null) { return 1.f; } @@ -416,7 +419,7 @@ public class Paint_Delegate { @LayoutlibDelegate /*package*/ static void setStrokeMiter(Paint thisPaint, float miter) { // get the delegate from the native int. - Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint); + Paint_Delegate delegate = sManager.getDelegate(thisPaint.getNativeInstance()); if (delegate == null) { return; } @@ -443,14 +446,14 @@ public class Paint_Delegate { @LayoutlibDelegate /*package*/ static boolean isElegantTextHeight(Paint thisPaint) { // get the delegate from the native int. - Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint); + Paint_Delegate delegate = sManager.getDelegate(thisPaint.getNativeInstance()); return delegate != null && delegate.mFontVariant == FontVariant.ELEGANT; } @LayoutlibDelegate /*package*/ static void setElegantTextHeight(Paint thisPaint, boolean elegant) { // get the delegate from the native int. - Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint); + Paint_Delegate delegate = sManager.getDelegate(thisPaint.getNativeInstance()); if (delegate == null) { return; } @@ -461,7 +464,7 @@ public class Paint_Delegate { @LayoutlibDelegate /*package*/ static float getTextSize(Paint thisPaint) { // get the delegate from the native int. - Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint); + Paint_Delegate delegate = sManager.getDelegate(thisPaint.getNativeInstance()); if (delegate == null) { return 1.f; } @@ -472,7 +475,7 @@ public class Paint_Delegate { @LayoutlibDelegate /*package*/ static void setTextSize(Paint thisPaint, float textSize) { // get the delegate from the native int. - Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint); + Paint_Delegate delegate = sManager.getDelegate(thisPaint.getNativeInstance()); if (delegate == null) { return; } @@ -484,7 +487,7 @@ public class Paint_Delegate { @LayoutlibDelegate /*package*/ static float getTextScaleX(Paint thisPaint) { // get the delegate from the native int. - Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint); + Paint_Delegate delegate = sManager.getDelegate(thisPaint.getNativeInstance()); if (delegate == null) { return 1.f; } @@ -495,7 +498,7 @@ public class Paint_Delegate { @LayoutlibDelegate /*package*/ static void setTextScaleX(Paint thisPaint, float scaleX) { // get the delegate from the native int. - Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint); + Paint_Delegate delegate = sManager.getDelegate(thisPaint.getNativeInstance()); if (delegate == null) { return; } @@ -507,7 +510,7 @@ public class Paint_Delegate { @LayoutlibDelegate /*package*/ static float getTextSkewX(Paint thisPaint) { // get the delegate from the native int. - Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint); + Paint_Delegate delegate = sManager.getDelegate(thisPaint.getNativeInstance()); if (delegate == null) { return 1.f; } @@ -518,7 +521,7 @@ public class Paint_Delegate { @LayoutlibDelegate /*package*/ static void setTextSkewX(Paint thisPaint, float skewX) { // get the delegate from the native int. - Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint); + Paint_Delegate delegate = sManager.getDelegate(thisPaint.getNativeInstance()); if (delegate == null) { return; } @@ -530,7 +533,7 @@ public class Paint_Delegate { @LayoutlibDelegate /*package*/ static float ascent(Paint thisPaint) { // get the delegate - Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint); + Paint_Delegate delegate = sManager.getDelegate(thisPaint.getNativeInstance()); if (delegate == null) { return 0; } @@ -547,7 +550,7 @@ public class Paint_Delegate { @LayoutlibDelegate /*package*/ static float descent(Paint thisPaint) { // get the delegate - Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint); + Paint_Delegate delegate = sManager.getDelegate(thisPaint.getNativeInstance()); if (delegate == null) { return 0; } @@ -564,7 +567,7 @@ public class Paint_Delegate { @LayoutlibDelegate /*package*/ static float getFontMetrics(Paint thisPaint, FontMetrics metrics) { // get the delegate - Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint); + Paint_Delegate delegate = sManager.getDelegate(thisPaint.getNativeInstance()); if (delegate == null) { return 0; } @@ -575,7 +578,7 @@ public class Paint_Delegate { @LayoutlibDelegate /*package*/ static int getFontMetricsInt(Paint thisPaint, FontMetricsInt fmi) { // get the delegate - Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint); + Paint_Delegate delegate = sManager.getDelegate(thisPaint.getNativeInstance()); if (delegate == null) { return 0; } @@ -601,7 +604,7 @@ public class Paint_Delegate { /*package*/ static float native_measureText(Paint thisPaint, char[] text, int index, int count, int bidiFlags) { // get the delegate - Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint); + Paint_Delegate delegate = sManager.getDelegate(thisPaint.getNativeInstance()); if (delegate == null) { return 0; } @@ -1090,18 +1093,108 @@ public class Paint_Delegate { @LayoutlibDelegate /*package*/ static float native_getLetterSpacing(long nativePaint) { - // TODO: throw a fidelity warning. - return 0; + Paint_Delegate delegate = sManager.getDelegate(nativePaint); + if (delegate == null) { + return 0; + } + return delegate.mLetterSpacing; } @LayoutlibDelegate /*package*/ static void native_setLetterSpacing(long nativePaint, float letterSpacing) { - // pass. + Bridge.getLog().fidelityWarning(LayoutLog.TAG_TEXT_RENDERING, + "Paint.setLetterSpacing() not supported.", null, null); + Paint_Delegate delegate = sManager.getDelegate(nativePaint); + if (delegate == null) { + return; + } + delegate.mLetterSpacing = letterSpacing; } @LayoutlibDelegate /*package*/ static void native_setFontFeatureSettings(long nativePaint, String settings) { - // pass. + Bridge.getLog().fidelityWarning(LayoutLog.TAG_TEXT_RENDERING, + "Paint.setFontFeatureSettings() not supported.", null, null); + } + + @LayoutlibDelegate + /*package*/ static int native_getHyphenEdit(long nativePaint) { + Paint_Delegate delegate = sManager.getDelegate(nativePaint); + if (delegate == null) { + return 0; + } + return delegate.mHyphenEdit; + } + + @LayoutlibDelegate + /*package*/ static void native_setHyphenEdit(long nativePaint, int hyphen) { + Paint_Delegate delegate = sManager.getDelegate(nativePaint); + if (delegate == null) { + return; + } + delegate.mHyphenEdit = hyphen; + } + + @LayoutlibDelegate + /*package*/ static boolean native_hasGlyph(long nativePaint, long nativeTypeface, int bidiFlags, + String string) { + Paint_Delegate delegate = sManager.getDelegate(nativePaint); + if (delegate == null) { + return false; + } + if (string.length() == 0) { + return false; + } + if (string.length() > 1) { + Bridge.getLog().fidelityWarning(LayoutLog.TAG_TEXT_RENDERING, + "Paint.hasGlyph() is not supported for ligatures.", null, null); + return false; + } + assert nativeTypeface == delegate.mNativeTypeface; + Typeface_Delegate typeface_delegate = Typeface_Delegate.getDelegate(nativeTypeface); + + char c = string.charAt(0); + for (Font font : typeface_delegate.getFonts(delegate.mFontVariant)) { + if (font.canDisplay(c)) { + return true; + } + } + return false; + } + + + @LayoutlibDelegate + /*package*/ static float native_getRunAdvance(long nativePaint, long nativeTypeface, + @NonNull char[] text, int start, int end, int contextStart, int contextEnd, + boolean isRtl, int offset) { + int count = end - start; + float[] advances = new float[count]; + native_getTextRunAdvances(nativePaint, nativeTypeface, text, start, count, + contextStart, contextEnd - contextStart, isRtl, advances, 0); + int startOffset = offset - start; // offset from start. + float sum = 0; + for (int i = 0; i < startOffset; i++) { + sum += advances[i]; + } + return sum; + } + + @LayoutlibDelegate + /*package*/ static int native_getOffsetForAdvance(long nativePaint, long nativeTypeface, + char[] text, int start, int end, int contextStart, int contextEnd, boolean isRtl, + float advance) { + int count = end - start; + float[] advances = new float[count]; + native_getTextRunAdvances(nativePaint, nativeTypeface, text, start, count, + contextStart, contextEnd - contextStart, isRtl, advances, 0); + float sum = 0; + int i; + for (i = 0; i < count && sum < advance; i++) { + sum += advances[i]; + } + float distanceToI = sum - advance; + float distanceToIMinus1 = advance - (sum - advances[i]); + return distanceToI > distanceToIMinus1 ? i : i - 1; } // ---- Private delegate/helper methods ---- @@ -1139,7 +1232,7 @@ public class Paint_Delegate { } private void reset() { - mFlags = Paint.DEFAULT_PAINT_FLAGS; + mFlags = Paint.HIDDEN_DEFAULT_PAINT_FLAGS; mColor = 0xFF000000; mStyle = Paint.Style.FILL.nativeInt; mCap = Paint.Cap.BUTT.nativeInt; @@ -1234,7 +1327,7 @@ public class Paint_Delegate { private static void setFlag(Paint thisPaint, int flagMask, boolean flagValue) { // get the delegate from the native int. - Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint); + Paint_Delegate delegate = sManager.getDelegate(thisPaint.getNativeInstance()); if (delegate == null) { return; } diff --git a/tools/layoutlib/bridge/src/android/graphics/Path_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/Path_Delegate.java index 776398f..3c9a062 100644 --- a/tools/layoutlib/bridge/src/android/graphics/Path_Delegate.java +++ b/tools/layoutlib/bridge/src/android/graphics/Path_Delegate.java @@ -21,6 +21,7 @@ import com.android.layoutlib.bridge.Bridge; import com.android.layoutlib.bridge.impl.DelegateManager; import com.android.tools.layoutlib.annotations.LayoutlibDelegate; +import android.annotation.NonNull; import android.graphics.Path.Direction; import android.graphics.Path.FillType; @@ -30,6 +31,7 @@ import java.awt.geom.Arc2D; import java.awt.geom.Area; import java.awt.geom.Ellipse2D; import java.awt.geom.GeneralPath; +import java.awt.geom.Path2D; import java.awt.geom.PathIterator; import java.awt.geom.Point2D; import java.awt.geom.Rectangle2D; @@ -56,7 +58,7 @@ public final class Path_Delegate { // ---- delegate data ---- private FillType mFillType = FillType.WINDING; - private GeneralPath mPath = new GeneralPath(); + private Path2D mPath = new Path2D.Double(); private float mLastX = 0; private float mLastY = 0; @@ -486,8 +488,54 @@ public final class Path_Delegate { @LayoutlibDelegate /*package*/ static float[] native_approximate(long nPath, float error) { - Bridge.getLog().error(LayoutLog.TAG_UNSUPPORTED, "Path.approximate() not supported", null); - return new float[0]; + Bridge.getLog().warning(LayoutLog.TAG_UNSUPPORTED, "Path.approximate() not fully supported", + null); + Path_Delegate pathDelegate = sManager.getDelegate(nPath); + if (pathDelegate == null) { + return null; + } + PathIterator pathIterator = pathDelegate.mPath.getPathIterator(null); + float[] tmp = new float[6]; + float[] coords = new float[6]; + boolean isFirstPoint = true; + while (!pathIterator.isDone()) { + int type = pathIterator.currentSegment(tmp); + switch (type) { + case PathIterator.SEG_MOVETO: + case PathIterator.SEG_LINETO: + store(tmp, coords, 1, isFirstPoint); + break; + case PathIterator.SEG_QUADTO: + store(tmp, coords, 2, isFirstPoint); + break; + case PathIterator.SEG_CUBICTO: + store(tmp, coords, 3, isFirstPoint); + break; + case PathIterator.SEG_CLOSE: + // No points returned. + } + isFirstPoint = false; + pathIterator.next(); + } + if (isFirstPoint) { + // No points found + return new float[0]; + } else { + return coords; + } + } + + private static void store(float[] src, float[] dst, int count, boolean isFirst) { + if (isFirst) { + dst[0] = 0; // fraction + dst[1] = src[0]; // abscissa + dst[2] = src[1]; // ordinate + } + if (count > 1 || !isFirst) { + dst[3] = 1; + dst[4] = src[2 * count - 2]; + dst[5] = src[2 * count - 1]; + } } // ---- Private helper methods ---- @@ -522,6 +570,7 @@ public final class Path_Delegate { throw new IllegalArgumentException(); } + @NonNull private static Direction getDirection(int direction) { for (Direction d : Direction.values()) { if (direction == d.nativeInt) { diff --git a/tools/layoutlib/bridge/src/android/graphics/Shader_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/Shader_Delegate.java index 14e9960..0d491a0 100644 --- a/tools/layoutlib/bridge/src/android/graphics/Shader_Delegate.java +++ b/tools/layoutlib/bridge/src/android/graphics/Shader_Delegate.java @@ -81,14 +81,15 @@ public abstract class Shader_Delegate { } @LayoutlibDelegate - /*package*/ static void nativeSetLocalMatrix(long native_shader, long matrix_instance) { + /*package*/ static long nativeSetLocalMatrix(long native_shader, long matrix_instance) { // get the delegate from the native int. Shader_Delegate shaderDelegate = sManager.getDelegate(native_shader); if (shaderDelegate == null) { - return; + return native_shader; } shaderDelegate.mLocalMatrix = Matrix_Delegate.getDelegate(matrix_instance); + return native_shader; } // ---- Private delegate/helper methods ---- diff --git a/tools/layoutlib/bridge/src/android/text/AndroidBidi_Delegate.java b/tools/layoutlib/bridge/src/android/text/AndroidBidi_Delegate.java index 6247dae..38171dc 100644 --- a/tools/layoutlib/bridge/src/android/text/AndroidBidi_Delegate.java +++ b/tools/layoutlib/bridge/src/android/text/AndroidBidi_Delegate.java @@ -19,8 +19,8 @@ package android.text; import com.android.ide.common.rendering.api.LayoutLog; import com.android.layoutlib.bridge.Bridge; import com.android.tools.layoutlib.annotations.LayoutlibDelegate; -import com.ibm.icu.text.Bidi; +import android.icu.text.Bidi; /** * Delegate used to provide new implementation for the native methods of {@link AndroidBidi} diff --git a/tools/layoutlib/bridge/src/android/text/GreedyLineBreaker.java b/tools/layoutlib/bridge/src/android/text/GreedyLineBreaker.java new file mode 100644 index 0000000..50289e9 --- /dev/null +++ b/tools/layoutlib/bridge/src/android/text/GreedyLineBreaker.java @@ -0,0 +1,192 @@ +/* + * Copyright (C) 2014 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 android.text; + +import android.annotation.NonNull; +import android.text.Primitive.PrimitiveType; +import android.text.StaticLayout.LineBreaks; + +import java.util.ArrayList; +import java.util.List; + +import static android.text.Primitive.PrimitiveType.PENALTY_INFINITY; + +// Based on the native implementation of GreedyLineBreaker in +// frameworks/base/core/jni/android_text_StaticLayout.cpp revision b808260 +public class GreedyLineBreaker extends LineBreaker { + + public GreedyLineBreaker(@NonNull List<Primitive> primitives, @NonNull LineWidth lineWidth, + @NonNull TabStops tabStops) { + super(primitives, lineWidth, tabStops); + } + + @Override + public void computeBreaks(@NonNull LineBreaks lineBreaks) { + BreakInfo breakInfo = new BreakInfo(); + int lineNum = 0; + float width = 0, printedWidth = 0; + boolean breakFound = false, goodBreakFound = false; + int breakIndex = 0, goodBreakIndex = 0; + float breakWidth = 0, goodBreakWidth = 0; + int firstTabIndex = Integer.MAX_VALUE; + + float maxWidth = mLineWidth.getLineWidth(lineNum); + + int numPrimitives = mPrimitives.size(); + // greedily fit as many characters as possible on each line + // loop over all primitives, and choose the best break point + // (if possible, a break point without splitting a word) + // after going over the maximum length + for (int i = 0; i < numPrimitives; i++) { + Primitive p = mPrimitives.get(i); + + // update the current line width + if (p.type == PrimitiveType.BOX || p.type == PrimitiveType.GLUE) { + width += p.width; + if (p.type == PrimitiveType.BOX) { + printedWidth = width; + } + } else if (p.type == PrimitiveType.VARIABLE) { + width = mTabStops.width(width); + // keep track of first tab character in the region we are examining + // so we can determine whether or not a line contains a tab + firstTabIndex = Math.min(firstTabIndex, i); + } + + // find the best break point for the characters examined so far + if (printedWidth > maxWidth) { + //noinspection StatementWithEmptyBody + if (breakFound || goodBreakFound) { + if (goodBreakFound) { + // a true line break opportunity existed in the characters examined so far, + // so there is no need to split a word + i = goodBreakIndex; // no +1 because of i++ + lineNum++; + maxWidth = mLineWidth.getLineWidth(lineNum); + breakInfo.mBreaksList.add(mPrimitives.get(goodBreakIndex).location); + breakInfo.mWidthsList.add(goodBreakWidth); + breakInfo.mFlagsList.add(firstTabIndex < goodBreakIndex); + firstTabIndex = Integer.MAX_VALUE; + } else { + // must split a word because there is no other option + i = breakIndex; // no +1 because of i++ + lineNum++; + maxWidth = mLineWidth.getLineWidth(lineNum); + breakInfo.mBreaksList.add(mPrimitives.get(breakIndex).location); + breakInfo.mWidthsList.add(breakWidth); + breakInfo.mFlagsList.add(firstTabIndex < breakIndex); + firstTabIndex = Integer.MAX_VALUE; + } + printedWidth = width = 0; + goodBreakFound = breakFound = false; + goodBreakWidth = breakWidth = 0; + continue; + } else { + // no choice, keep going... must make progress by putting at least one + // character on a line, even if part of that character is cut off -- + // there is no other option + } + } + + // update possible break points + if (p.type == PrimitiveType.PENALTY && + p.penalty < PENALTY_INFINITY) { + // this does not handle penalties with width + + // handle forced line break + if (p.penalty == -PENALTY_INFINITY) { + lineNum++; + maxWidth = mLineWidth.getLineWidth(lineNum); + breakInfo.mBreaksList.add(p.location); + breakInfo.mWidthsList.add(printedWidth); + breakInfo.mFlagsList.add(firstTabIndex < i); + firstTabIndex = Integer.MAX_VALUE; + printedWidth = width = 0; + goodBreakFound = breakFound = false; + goodBreakWidth = breakWidth = 0; + continue; + } + if (i > breakIndex && (printedWidth <= maxWidth || !breakFound)) { + breakFound = true; + breakIndex = i; + breakWidth = printedWidth; + } + if (i > goodBreakIndex && printedWidth <= maxWidth) { + goodBreakFound = true; + goodBreakIndex = i; + goodBreakWidth = printedWidth; + } + } else if (p.type == PrimitiveType.WORD_BREAK) { + // only do this if necessary -- we don't want to break words + // when possible, but sometimes it is unavoidable + if (i > breakIndex && (printedWidth <= maxWidth || !breakFound)) { + breakFound = true; + breakIndex = i; + breakWidth = printedWidth; + } + } + } + + if (breakFound || goodBreakFound) { + // output last break if there are more characters to output + if (goodBreakFound) { + breakInfo.mBreaksList.add(mPrimitives.get(goodBreakIndex).location); + breakInfo.mWidthsList.add(goodBreakWidth); + breakInfo.mFlagsList.add(firstTabIndex < goodBreakIndex); + } else { + breakInfo.mBreaksList.add(mPrimitives.get(breakIndex).location); + breakInfo.mWidthsList.add(breakWidth); + breakInfo.mFlagsList.add(firstTabIndex < breakIndex); + } + } + breakInfo.copyTo(lineBreaks); + } + + private static class BreakInfo { + List<Integer> mBreaksList = new ArrayList<Integer>(); + List<Float> mWidthsList = new ArrayList<Float>(); + List<Boolean> mFlagsList = new ArrayList<Boolean>(); + + public void copyTo(LineBreaks lineBreaks) { + if (lineBreaks.breaks.length != mBreaksList.size()) { + lineBreaks.breaks = new int[mBreaksList.size()]; + lineBreaks.widths = new float[mWidthsList.size()]; + lineBreaks.flags = new int[mFlagsList.size()]; + } + + int i = 0; + for (int b : mBreaksList) { + lineBreaks.breaks[i] = b; + i++; + } + i = 0; + for (float b : mWidthsList) { + lineBreaks.widths[i] = b; + i++; + } + i = 0; + for (boolean b : mFlagsList) { + lineBreaks.flags[i] = b ? TAB_MASK : 0; + i++; + } + + mBreaksList = null; + mWidthsList = null; + mFlagsList = null; + } + } +} diff --git a/tools/layoutlib/bridge/src/android/text/Hyphenator_Delegate.java b/tools/layoutlib/bridge/src/android/text/Hyphenator_Delegate.java new file mode 100644 index 0000000..5a59597 --- /dev/null +++ b/tools/layoutlib/bridge/src/android/text/Hyphenator_Delegate.java @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.text; + +import com.android.layoutlib.bridge.impl.DelegateManager; +import com.android.tools.layoutlib.annotations.LayoutlibDelegate; + +import java.io.File; + +/** + * Delegate that overrides implementation for certain methods in {@link android.text.StaticLayout} + * <p/> + * Through the layoutlib_create tool, selected methods of StaticLayout have been replaced + * by calls to methods of the same name in this delegate class. + */ +public class Hyphenator_Delegate { + + private static final DelegateManager<Hyphenator_Delegate> sDelegateManager = new + DelegateManager<Hyphenator_Delegate>(Hyphenator_Delegate.class); + + @LayoutlibDelegate + /*package*/ static File getSystemHyphenatorLocation() { + // FIXME + return null; + } + + /*package*/ static long loadHyphenator(String patternData) { + return sDelegateManager.addNewDelegate(new Hyphenator_Delegate()); + } +} diff --git a/tools/layoutlib/bridge/src/android/text/LineBreaker.java b/tools/layoutlib/bridge/src/android/text/LineBreaker.java new file mode 100644 index 0000000..06e9c84 --- /dev/null +++ b/tools/layoutlib/bridge/src/android/text/LineBreaker.java @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2014 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 android.text; + +import android.annotation.NonNull; +import android.text.StaticLayout.LineBreaks; + +import java.util.Collections; +import java.util.List; + +// Based on the native implementation of LineBreaker in +// frameworks/base/core/jni/android_text_StaticLayout.cpp revision b808260 +public abstract class LineBreaker { + + protected static final int TAB_MASK = 0x20000000; // keep in sync with StaticLayout + + protected final @NonNull List<Primitive> mPrimitives; + protected final @NonNull LineWidth mLineWidth; + protected final @NonNull TabStops mTabStops; + + public LineBreaker(@NonNull List<Primitive> primitives, @NonNull LineWidth lineWidth, + @NonNull TabStops tabStops) { + mPrimitives = Collections.unmodifiableList(primitives); + mLineWidth = lineWidth; + mTabStops = tabStops; + } + + public abstract void computeBreaks(@NonNull LineBreaks breakInfo); +} diff --git a/tools/layoutlib/bridge/src/android/text/LineWidth.java b/tools/layoutlib/bridge/src/android/text/LineWidth.java new file mode 100644 index 0000000..2ea886d --- /dev/null +++ b/tools/layoutlib/bridge/src/android/text/LineWidth.java @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2014 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 android.text; + +// Based on the native implementation of LineWidth in +// frameworks/base/core/jni/android_text_StaticLayout.cpp revision b808260 +public class LineWidth { + private final float mFirstWidth; + private final int mFirstWidthLineCount; + private float mRestWidth; + + public LineWidth(float firstWidth, int firstWidthLineCount, float restWidth) { + mFirstWidth = firstWidth; + mFirstWidthLineCount = firstWidthLineCount; + mRestWidth = restWidth; + } + + public float getLineWidth(int line) { + return (line < mFirstWidthLineCount) ? mFirstWidth : mRestWidth; + } +} diff --git a/tools/layoutlib/bridge/src/android/text/OptimizingLineBreaker.java b/tools/layoutlib/bridge/src/android/text/OptimizingLineBreaker.java new file mode 100644 index 0000000..ed8e33a --- /dev/null +++ b/tools/layoutlib/bridge/src/android/text/OptimizingLineBreaker.java @@ -0,0 +1,261 @@ +/* + * Copyright (C) 2014 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 android.text; + +import android.annotation.NonNull; +import android.text.Primitive.PrimitiveType; +import android.text.StaticLayout.LineBreaks; + +import java.util.ArrayList; +import java.util.List; +import java.util.ListIterator; + +import static android.text.Primitive.PrimitiveType.PENALTY_INFINITY; + + +// Based on the native implementation of OptimizingLineBreaker in +// frameworks/base/core/jni/android_text_StaticLayout.cpp revision b808260 +/** + * A more complex version of line breaking where we try to prevent the right edge from being too + * jagged. + */ +public class OptimizingLineBreaker extends LineBreaker { + + public OptimizingLineBreaker(@NonNull List<Primitive> primitives, @NonNull LineWidth lineWidth, + @NonNull TabStops tabStops) { + super(primitives, lineWidth, tabStops); + } + + @Override + public void computeBreaks(@NonNull LineBreaks breakInfo) { + int numBreaks = mPrimitives.size(); + assert numBreaks > 0; + if (numBreaks == 1) { + // This can be true only if it's an empty paragraph. + Primitive p = mPrimitives.get(0); + assert p.type == PrimitiveType.PENALTY; + breakInfo.breaks = new int[]{0}; + breakInfo.widths = new float[]{p.width}; + breakInfo.flags = new int[]{0}; + return; + } + Node[] opt = new Node[numBreaks]; + opt[0] = new Node(-1, 0, 0, 0, false); + opt[numBreaks - 1] = new Node(-1, 0, 0, 0, false); + + ArrayList<Integer> active = new ArrayList<Integer>(); + active.add(0); + int lastBreak = 0; + for (int i = 0; i < numBreaks; i++) { + Primitive p = mPrimitives.get(i); + if (p.type == PrimitiveType.PENALTY) { + boolean finalBreak = (i + 1 == numBreaks); + Node bestBreak = null; + + for (ListIterator<Integer> it = active.listIterator(); it.hasNext(); + /* incrementing done in loop */) { + int pos = it.next(); + int lines = opt[pos].mPrevCount; + float maxWidth = mLineWidth.getLineWidth(lines); + // we have to compute metrics every time -- + // we can't really pre-compute this stuff and just deal with breaks + // because of the way tab characters work, this makes it computationally + // harder, but this way, we can still optimize while treating tab characters + // correctly + LineMetrics lineMetrics = computeMetrics(pos, i); + if (lineMetrics.mPrintedWidth <= maxWidth) { + float demerits = computeDemerits(maxWidth, lineMetrics.mPrintedWidth, + finalBreak, p.penalty) + opt[pos].mDemerits; + if (bestBreak == null || demerits < bestBreak.mDemerits) { + if (bestBreak == null) { + bestBreak = new Node(pos, opt[pos].mPrevCount + 1, demerits, + lineMetrics.mPrintedWidth, lineMetrics.mHasTabs); + } else { + bestBreak.mPrev = pos; + bestBreak.mPrevCount = opt[pos].mPrevCount + 1; + bestBreak.mDemerits = demerits; + bestBreak.mWidth = lineMetrics.mPrintedWidth; + bestBreak.mHasTabs = lineMetrics.mHasTabs; + } + } + } else { + it.remove(); + } + } + if (p.penalty == -PENALTY_INFINITY) { + active.clear(); + } + if (bestBreak != null) { + opt[i] = bestBreak; + active.add(i); + lastBreak = i; + } + if (active.isEmpty()) { + // we can't give up! + LineMetrics lineMetrics = new LineMetrics(); + int lines = opt[lastBreak].mPrevCount; + float maxWidth = mLineWidth.getLineWidth(lines); + int breakIndex = desperateBreak(lastBreak, numBreaks, maxWidth, lineMetrics); + opt[breakIndex] = new Node(lastBreak, lines + 1, 0 /*doesn't matter*/, + lineMetrics.mWidth, lineMetrics.mHasTabs); + active.add(breakIndex); + lastBreak = breakIndex; + i = breakIndex; // incremented by i++ + } + } + } + + int idx = numBreaks - 1; + int count = opt[idx].mPrevCount; + resize(breakInfo, count); + while (opt[idx].mPrev != -1) { + count--; + assert count >=0; + + breakInfo.breaks[count] = mPrimitives.get(idx).location; + breakInfo.widths[count] = opt[idx].mWidth; + breakInfo.flags [count] = opt[idx].mHasTabs ? TAB_MASK : 0; + idx = opt[idx].mPrev; + } + } + + private static void resize(LineBreaks lineBreaks, int size) { + if (lineBreaks.breaks.length == size) { + return; + } + int[] breaks = new int[size]; + float[] widths = new float[size]; + int[] flags = new int[size]; + + int toCopy = Math.min(size, lineBreaks.breaks.length); + System.arraycopy(lineBreaks.breaks, 0, breaks, 0, toCopy); + System.arraycopy(lineBreaks.widths, 0, widths, 0, toCopy); + System.arraycopy(lineBreaks.flags, 0, flags, 0, toCopy); + + lineBreaks.breaks = breaks; + lineBreaks.widths = widths; + lineBreaks.flags = flags; + } + + @NonNull + private LineMetrics computeMetrics(int start, int end) { + boolean f = false; + float w = 0, pw = 0; + for (int i = start; i < end; i++) { + Primitive p = mPrimitives.get(i); + if (p.type == PrimitiveType.BOX || p.type == PrimitiveType.GLUE) { + w += p.width; + if (p.type == PrimitiveType.BOX) { + pw = w; + } + } else if (p.type == PrimitiveType.VARIABLE) { + w = mTabStops.width(w); + f = true; + } + } + return new LineMetrics(w, pw, f); + } + + private static float computeDemerits(float maxWidth, float width, boolean finalBreak, + float penalty) { + float deviation = finalBreak ? 0 : maxWidth - width; + return (deviation * deviation) + penalty; + } + + /** + * @return the last break position or -1 if failed. + */ + @SuppressWarnings("ConstantConditions") // method too complex to be analyzed. + private int desperateBreak(int start, int limit, float maxWidth, + @NonNull LineMetrics lineMetrics) { + float w = 0, pw = 0; + boolean breakFound = false; + int breakIndex = 0, firstTabIndex = Integer.MAX_VALUE; + for (int i = start; i < limit; i++) { + Primitive p = mPrimitives.get(i); + + if (p.type == PrimitiveType.BOX || p.type == PrimitiveType.GLUE) { + w += p.width; + if (p.type == PrimitiveType.BOX) { + pw = w; + } + } else if (p.type == PrimitiveType.VARIABLE) { + w = mTabStops.width(w); + firstTabIndex = Math.min(firstTabIndex, i); + } + + if (pw > maxWidth && breakFound) { + break; + } + + // must make progress + if (i > start && + (p.type == PrimitiveType.PENALTY || p.type == PrimitiveType.WORD_BREAK)) { + breakFound = true; + breakIndex = i; + } + } + + if (breakFound) { + lineMetrics.mWidth = w; + lineMetrics.mPrintedWidth = pw; + lineMetrics.mHasTabs = (start <= firstTabIndex && firstTabIndex < breakIndex); + return breakIndex; + } else { + return -1; + } + } + + private static class LineMetrics { + /** Actual width of the line. */ + float mWidth; + /** Width of the line minus trailing whitespace. */ + float mPrintedWidth; + boolean mHasTabs; + + public LineMetrics() { + } + + public LineMetrics(float width, float printedWidth, boolean hasTabs) { + mWidth = width; + mPrintedWidth = printedWidth; + mHasTabs = hasTabs; + } + } + + /** + * A struct to store the info about a break. + */ + @SuppressWarnings("SpellCheckingInspection") // For the word struct. + private static class Node { + // -1 for the first node. + int mPrev; + // number of breaks so far. + int mPrevCount; + float mDemerits; + float mWidth; + boolean mHasTabs; + + public Node(int prev, int prevCount, float demerits, float width, boolean hasTabs) { + mPrev = prev; + mPrevCount = prevCount; + mDemerits = demerits; + mWidth = width; + mHasTabs = hasTabs; + } + } +} diff --git a/tools/layoutlib/bridge/src/android/text/Primitive.java b/tools/layoutlib/bridge/src/android/text/Primitive.java new file mode 100644 index 0000000..37ed072 --- /dev/null +++ b/tools/layoutlib/bridge/src/android/text/Primitive.java @@ -0,0 +1,92 @@ +/* + * Copyright (C) 2014 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 android.text; + +import android.annotation.NonNull; + +// Based on the native implementation of Primitive in +// frameworks/base/core/jni/android_text_StaticLayout.cpp revision b808260 +public class Primitive { + public final @NonNull PrimitiveType type; + public final int location; + // The following fields don't make sense for all types. + // Box and Glue have width only. + // Penalty has both width and penalty. + // Word_break has penalty only. + public final float width; + public final float penalty; + + /** + * Use {@code PrimitiveType#getNewPrimitive()} + */ + private Primitive(@NonNull PrimitiveType type, int location, float width, float penalty) { + this.type = type; + this.location = location; + this.width = width; + this.penalty = penalty; + } + + public static enum PrimitiveType { + /** + * Something with a constant width that is to be typeset - like a character. + */ + BOX, + /** + * Blank space with fixed width. + */ + GLUE, + /** + * Aesthetic cost indicating how desirable breaking at this point will be. A penalty of + * {@link #PENALTY_INFINITY} means a forced non-break, whereas a penalty of negative + * {@code #PENALTY_INFINITY} means a forced break. + * <p/> + * Currently, it only stores penalty with values 0 or -infinity. + */ + PENALTY, + /** + * For tabs - variable width space. + */ + VARIABLE, + /** + * Possible breakpoints within a word. Think of this as a high cost {@link #PENALTY}. + */ + WORD_BREAK; + + public Primitive getNewPrimitive(int location) { + assert this == VARIABLE; + return new Primitive(this, location, 0f, 0f); + } + + public Primitive getNewPrimitive(int location, float value) { + assert this == BOX || this == GLUE || this == WORD_BREAK; + if (this == BOX || this == GLUE) { + return new Primitive(this, location, value, 0f); + } else { + return new Primitive(this, location, 0f, value); + } + } + + public Primitive getNewPrimitive(int location, float width, float penalty) { + assert this == PENALTY; + return new Primitive(this, location, width, penalty); + } + + // forced non-break, negative infinity is forced break. + public static final float PENALTY_INFINITY = 1e7f; + } +} + diff --git a/tools/layoutlib/bridge/src/android/text/StaticLayout_Delegate.java b/tools/layoutlib/bridge/src/android/text/StaticLayout_Delegate.java index b0d79a8..1b0ba51 100644 --- a/tools/layoutlib/bridge/src/android/text/StaticLayout_Delegate.java +++ b/tools/layoutlib/bridge/src/android/text/StaticLayout_Delegate.java @@ -1,14 +1,22 @@ package android.text; +import com.android.layoutlib.bridge.impl.DelegateManager; import com.android.tools.layoutlib.annotations.LayoutlibDelegate; -import java.text.CharacterIterator; +import android.annotation.NonNull; +import android.graphics.BidiRenderer; +import android.graphics.Paint; +import android.graphics.Paint_Delegate; +import android.graphics.RectF; +import android.icu.text.BreakIterator; +import android.icu.util.ULocale; +import android.text.Primitive.PrimitiveType; +import android.text.StaticLayout.LineBreaks; + +import java.util.ArrayList; import java.util.Arrays; -import java.util.Locale; +import java.util.List; -import com.ibm.icu.lang.UCharacter; -import com.ibm.icu.text.BreakIterator; -import com.ibm.icu.util.ULocale; import javax.swing.text.Segment; /** @@ -20,36 +28,206 @@ import javax.swing.text.Segment; */ public class StaticLayout_Delegate { + private static final char CHAR_SPACE = 0x20; + private static final char CHAR_TAB = 0x09; + private static final char CHAR_NEWLINE = 0x0A; + private static final char CHAR_ZWSP = 0x200B; // Zero width space. + + // ---- Builder delegate manager ---- + private static final DelegateManager<Builder> sBuilderManager = + new DelegateManager<Builder>(Builder.class); + + @LayoutlibDelegate + /*package*/ static long nNewBuilder() { + return sBuilderManager.addNewDelegate(new Builder()); + } + + @LayoutlibDelegate + /*package*/ static void nFreeBuilder(long nativeBuilder) { + sBuilderManager.removeJavaReferenceFor(nativeBuilder); + } + + @LayoutlibDelegate + /*package*/ static void nFinishBuilder(long nativeBuilder) { + } + + @LayoutlibDelegate + /*package*/ static long nLoadHyphenator(String patternData) { + return Hyphenator_Delegate.loadHyphenator(patternData); + } + + @LayoutlibDelegate + /*package*/ static void nSetLocale(long nativeBuilder, String locale, long nativeHyphenator) { + Builder builder = sBuilderManager.getDelegate(nativeBuilder); + if (builder != null) { + builder.mLocale = locale; + builder.mNativeHyphenator = nativeHyphenator; + } + } + + @LayoutlibDelegate + /*package*/ static void nSetIndents(long nativeBuilder, int[] indents) { + // TODO. + } + + @LayoutlibDelegate + /*package*/ static void nSetupParagraph(long nativeBuilder, char[] text, int length, + float firstWidth, int firstWidthLineCount, float restWidth, + int[] variableTabStops, int defaultTabStop, int breakStrategy, + int hyphenationFrequency) { + Builder builder = sBuilderManager.getDelegate(nativeBuilder); + if (builder == null) { + return; + } + + builder.mText = text; + builder.mWidths = new float[length]; + builder.mLineWidth = new LineWidth(firstWidth, firstWidthLineCount, restWidth); + builder.mTabStopCalculator = new TabStops(variableTabStops, defaultTabStop); + } + + @LayoutlibDelegate + /*package*/ static float nAddStyleRun(long nativeBuilder, long nativePaint, long nativeTypeface, + int start, int end, boolean isRtl) { + Builder builder = sBuilderManager.getDelegate(nativeBuilder); + + int bidiFlags = isRtl ? Paint.BIDI_FORCE_RTL : Paint.BIDI_FORCE_LTR; + return builder == null ? 0 : + measureText(nativePaint, builder.mText, start, end - start, builder.mWidths, + bidiFlags); + } + + @LayoutlibDelegate + /*package*/ static void nAddMeasuredRun(long nativeBuilder, int start, int end, float[] widths) { + Builder builder = sBuilderManager.getDelegate(nativeBuilder); + if (builder != null) { + System.arraycopy(widths, start, builder.mWidths, start, end - start); + } + } + + @LayoutlibDelegate + /*package*/ static void nAddReplacementRun(long nativeBuilder, int start, int end, float width) { + Builder builder = sBuilderManager.getDelegate(nativeBuilder); + if (builder == null) { + return; + } + builder.mWidths[start] = width; + Arrays.fill(builder.mWidths, start + 1, end, 0.0f); + } + + @LayoutlibDelegate + /*package*/ static void nGetWidths(long nativeBuilder, float[] floatsArray) { + Builder builder = sBuilderManager.getDelegate(nativeBuilder); + if (builder != null) { + System.arraycopy(builder.mWidths, 0, floatsArray, 0, builder.mWidths.length); + } + } + + @LayoutlibDelegate + /*package*/ static int nComputeLineBreaks(long nativeBuilder, + LineBreaks recycle, int[] recycleBreaks, float[] recycleWidths, + int[] recycleFlags, int recycleLength) { + + Builder builder = sBuilderManager.getDelegate(nativeBuilder); + if (builder == null) { + return 0; + } + + // compute all possible breakpoints. + int length = builder.mWidths.length; + BreakIterator it = BreakIterator.getLineInstance(new ULocale(builder.mLocale)); + it.setText(new Segment(builder.mText, 0, length)); + + // average word length in english is 5. So, initialize the possible breaks with a guess. + List<Integer> breaks = new ArrayList<Integer>((int) Math.ceil(length / 5d)); + int loc; + it.first(); + while ((loc = it.next()) != BreakIterator.DONE) { + breaks.add(loc); + } + + List<Primitive> primitives = + computePrimitives(builder.mText, builder.mWidths, length, breaks); + switch (builder.mBreakStrategy) { + case Layout.BREAK_STRATEGY_SIMPLE: + builder.mLineBreaker = new GreedyLineBreaker(primitives, builder.mLineWidth, + builder.mTabStopCalculator); + break; + case Layout.BREAK_STRATEGY_HIGH_QUALITY: + // TODO +// break; + case Layout.BREAK_STRATEGY_BALANCED: + builder.mLineBreaker = new OptimizingLineBreaker(primitives, builder.mLineWidth, + builder.mTabStopCalculator); + break; + default: + throw new AssertionError("Unknown break strategy: " + builder.mBreakStrategy); + } + builder.mLineBreaker.computeBreaks(recycle); + return recycle.breaks.length; + } + /** - * Fills the recycle array with positions that are suitable to break the text at. The array - * must be terminated by '-1'. + * Compute metadata each character - things which help in deciding if it's possible to break + * at a point or not. */ - @LayoutlibDelegate - /*package*/ static int[] nLineBreakOpportunities(String locale, char[] text, int length, - int[] recycle) { - BreakIterator iterator = BreakIterator.getLineInstance(new ULocale(locale)); - Segment segment = new Segment(text, 0, length); - iterator.setText(segment); - if (recycle == null) { - // Because 42 is the answer to everything. - recycle = new int[42]; - } - int breakOpp = iterator.first(); - recycle[0] = breakOpp; - //noinspection ConstantConditions - assert BreakIterator.DONE == -1; - for (int i = 1; breakOpp != BreakIterator.DONE; ++i) { - if (i >= recycle.length) { - recycle = doubleSize(recycle); + @NonNull + private static List<Primitive> computePrimitives(@NonNull char[] text, @NonNull float[] widths, + int length, @NonNull List<Integer> breaks) { + // Initialize the list with a guess of the number of primitives: + // 2 Primitives per non-whitespace char and approx 5 chars per word (i.e. 83% chars) + List<Primitive> primitives = new ArrayList<Primitive>(((int) Math.ceil(length * 1.833))); + int breaksSize = breaks.size(); + int breakIndex = 0; + for (int i = 0; i < length; i++) { + char c = text[i]; + if (c == CHAR_SPACE || c == CHAR_ZWSP) { + primitives.add(PrimitiveType.GLUE.getNewPrimitive(i, widths[i])); + } else if (c == CHAR_TAB) { + primitives.add(PrimitiveType.VARIABLE.getNewPrimitive(i)); + } else if (c != CHAR_NEWLINE) { + while (breakIndex < breaksSize && breaks.get(breakIndex) < i) { + breakIndex++; + } + Primitive p; + if (widths[i] != 0) { + if (breakIndex < breaksSize && breaks.get(breakIndex) == i) { + p = PrimitiveType.PENALTY.getNewPrimitive(i, 0, 0); + } else { + p = PrimitiveType.WORD_BREAK.getNewPrimitive(i, 0); + } + primitives.add(p); + } + + primitives.add(PrimitiveType.BOX.getNewPrimitive(i, widths[i])); } - assert (i < recycle.length); - breakOpp = iterator.next(); - recycle[i] = breakOpp; } - return recycle; + // final break at end of everything + primitives.add( + PrimitiveType.PENALTY.getNewPrimitive(length, 0, -PrimitiveType.PENALTY_INFINITY)); + return primitives; } - private static int[] doubleSize(int[] array) { - return Arrays.copyOf(array, array.length * 2); + private static float measureText(long nativePaint, char []text, int index, int count, + float[] widths, int bidiFlags) { + Paint_Delegate paint = Paint_Delegate.getDelegate(nativePaint); + RectF bounds = new BidiRenderer(null, paint, text) + .renderText(index, index + count, bidiFlags, widths, 0, false); + return bounds.right - bounds.left; + } + + // TODO: Rename to LineBreakerRef and move everything other than LineBreaker to LineBreaker. + /** + * Java representation of the native Builder class. + */ + private static class Builder { + String mLocale; + char[] mText; + float[] mWidths; + LineBreaker mLineBreaker; + long mNativeHyphenator; + int mBreakStrategy; + LineWidth mLineWidth; + TabStops mTabStopCalculator; } } diff --git a/tools/layoutlib/bridge/src/android/text/TabStops.java b/tools/layoutlib/bridge/src/android/text/TabStops.java new file mode 100644 index 0000000..6c2f1e1 --- /dev/null +++ b/tools/layoutlib/bridge/src/android/text/TabStops.java @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2014 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 android.text; + +import android.annotation.Nullable; + +// Based on the native implementation of TabStops in +// frameworks/base/core/jni/android_text_StaticLayout.cpp revision b808260 +public class TabStops { + @Nullable + private int[] mStops; + private final int mTabWidth; + + public TabStops(@Nullable int[] stops, int defaultTabWidth) { + mTabWidth = defaultTabWidth; + mStops = stops; + } + + public float width(float widthSoFar) { + if (mStops != null) { + for (int i : mStops) { + if (i > widthSoFar) { + return i; + } + } + } + // find the next tabStop after widthSoFar. + return (int) ((widthSoFar + mTabWidth) / mTabWidth) * mTabWidth; + } +} diff --git a/tools/layoutlib/bridge/src/android/view/BridgeInflater.java b/tools/layoutlib/bridge/src/android/view/BridgeInflater.java index da37fe0..1e33e3a 100644 --- a/tools/layoutlib/bridge/src/android/view/BridgeInflater.java +++ b/tools/layoutlib/bridge/src/android/view/BridgeInflater.java @@ -134,11 +134,11 @@ public final class BridgeInflater extends LayoutInflater { } @Override - public View createViewFromTag(View parent, String name, AttributeSet attrs, - boolean inheritContext) { + public View createViewFromTag(View parent, String name, Context context, AttributeSet attrs, + boolean ignoreThemeAttrs) { View view; try { - view = super.createViewFromTag(parent, name, attrs, inheritContext); + view = super.createViewFromTag(parent, name, context, attrs, ignoreThemeAttrs); } catch (InflateException e) { // try to load the class from using the custom view loader try { diff --git a/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java b/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java index 5176419..82012c1 100644 --- a/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java +++ b/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java @@ -17,6 +17,7 @@ package android.view; import android.graphics.Point; +import com.android.internal.app.IAssistScreenshotReceiver; import com.android.internal.view.IInputContext; import com.android.internal.view.IInputMethodClient; @@ -215,6 +216,12 @@ public class IWindowManagerImpl implements IWindowManager { } @Override + public void overridePendingAppTransitionClipReveal(int startX, int startY, + int startWidth, int startHeight) throws RemoteException { + // TODO Auto-generated method stub + } + + @Override public void overridePendingAppTransitionThumb(Bitmap srcThumb, int startX, int startY, IRemoteCallback startedCallback, boolean scaleUp) throws RemoteException { // TODO Auto-generated method stub @@ -269,8 +276,15 @@ public class IWindowManagerImpl implements IWindowManager { } @Override - public Bitmap screenshotApplications(IBinder arg0, int displayId, int arg1, - int arg2, boolean arg3) throws RemoteException { + public boolean requestAssistScreenshot(IAssistScreenshotReceiver receiver) + throws RemoteException { + // TODO Auto-generated method stub + return false; + } + + @Override + public Bitmap screenshotApplications(IBinder appToken, int displayId, int maxWidth, + int maxHeight) throws RemoteException { // TODO Auto-generated method stub return null; } @@ -293,7 +307,7 @@ public class IWindowManagerImpl implements IWindowManager { } @Override - public void setAppGroupId(IBinder arg0, int arg1) throws RemoteException { + public void setAppTask(IBinder arg0, int arg1) throws RemoteException { // TODO Auto-generated method stub } @@ -362,6 +376,10 @@ public class IWindowManagerImpl implements IWindowManager { } @Override + public void setForcedDisplayScalingMode(int displayId, int mode) { + } + + @Override public void setInTouchMode(boolean arg0) throws RemoteException { // TODO Auto-generated method stub } diff --git a/tools/layoutlib/bridge/src/android/view/LayoutInflater_Delegate.java b/tools/layoutlib/bridge/src/android/view/LayoutInflater_Delegate.java index 579f274..27b406a 100644 --- a/tools/layoutlib/bridge/src/android/view/LayoutInflater_Delegate.java +++ b/tools/layoutlib/bridge/src/android/view/LayoutInflater_Delegate.java @@ -21,9 +21,11 @@ import com.android.tools.layoutlib.annotations.LayoutlibDelegate; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; +import android.content.Context; import android.content.res.TypedArray; import android.content.res.XmlResourceParser; import android.util.AttributeSet; +import android.util.TypedValue; import android.util.Xml; import java.io.IOException; @@ -36,9 +38,13 @@ import java.io.IOException; * */ public class LayoutInflater_Delegate { - private static final String TAG_MERGE = "merge"; + private static final String ATTR_LAYOUT = "layout"; + + private static final int[] ATTRS_THEME = new int[] { + com.android.internal.R.attr.theme }; + public static boolean sIsInInclude = false; /** @@ -49,7 +55,7 @@ public class LayoutInflater_Delegate { */ @LayoutlibDelegate /* package */ static void rInflate(LayoutInflater thisInflater, XmlPullParser parser, - View parent, final AttributeSet attrs, boolean finishInflate, boolean inheritContext) + View parent, Context context, AttributeSet attrs, boolean finishInflate) throws XmlPullParserException, IOException { if (finishInflate == false) { @@ -61,7 +67,7 @@ public class LayoutInflater_Delegate { // ---- START DEFAULT IMPLEMENTATION. - thisInflater.rInflate_Original(parser, parent, attrs, finishInflate, inheritContext); + thisInflater.rInflate_Original(parser, parent, context, attrs, finishInflate); // ---- END DEFAULT IMPLEMENTATION. @@ -74,15 +80,50 @@ public class LayoutInflater_Delegate { } @LayoutlibDelegate - public static void parseInclude(LayoutInflater thisInflater, XmlPullParser parser, View parent, - AttributeSet attrs, boolean inheritContext) throws XmlPullParserException, IOException { - + public static void parseInclude(LayoutInflater thisInflater, XmlPullParser parser, + Context context, View parent, AttributeSet attrs) + throws XmlPullParserException, IOException { int type; if (parent instanceof ViewGroup) { - final int layout = attrs.getAttributeResourceValue(null, "layout", 0); + // Apply a theme wrapper, if requested. This is sort of a weird + // edge case, since developers think the <include> overwrites + // values in the AttributeSet of the included View. So, if the + // included View has a theme attribute, we'll need to ignore it. + final TypedArray ta = context.obtainStyledAttributes(attrs, ATTRS_THEME); + final int themeResId = ta.getResourceId(0, 0); + final boolean hasThemeOverride = themeResId != 0; + if (hasThemeOverride) { + context = new ContextThemeWrapper(context, themeResId); + } + ta.recycle(); + + // If the layout is pointing to a theme attribute, we have to + // massage the value to get a resource identifier out of it. + int layout = attrs.getAttributeResourceValue(null, ATTR_LAYOUT, 0); if (layout == 0) { - final String value = attrs.getAttributeValue(null, "layout"); + final String value = attrs.getAttributeValue(null, ATTR_LAYOUT); + if (value == null || value.length() <= 0) { + throw new InflateException("You must specify a layout in the" + + " include tag: <include layout=\"@layout/layoutID\" />"); + } + + // Attempt to resolve the "?attr/name" string to an identifier. + layout = context.getResources().getIdentifier(value.substring(1), null, null); + } + + // The layout might be referencing a theme attribute. + // ---- START CHANGES + if (layout != 0) { + final TypedValue tempValue = new TypedValue(); + if (context.getTheme().resolveAttribute(layout, tempValue, true)) { + layout = tempValue.resourceId; + } + } + // ---- END CHANGES + + if (layout == 0) { + final String value = attrs.getAttributeValue(null, ATTR_LAYOUT); if (value == null) { throw new InflateException("You must specifiy a layout in the" + " include tag: <include layout=\"@layout/layoutID\" />"); @@ -111,13 +152,20 @@ public class LayoutInflater_Delegate { if (TAG_MERGE.equals(childName)) { // Inflate all children. - thisInflater.rInflate(childParser, parent, childAttrs, false, - inheritContext); + thisInflater.rInflate(childParser, parent, context, childAttrs, false); } else { final View view = thisInflater.createViewFromTag(parent, childName, - childAttrs, inheritContext); + context, childAttrs, hasThemeOverride); final ViewGroup group = (ViewGroup) parent; + final TypedArray a = context.obtainStyledAttributes( + attrs, com.android.internal.R.styleable.Include); + final int id = a.getResourceId( + com.android.internal.R.styleable.Include_id, View.NO_ID); + final int visibility = a.getInt( + com.android.internal.R.styleable.Include_visibility, -1); + a.recycle(); + // We try to load the layout params set in the <include /> tag. If // they don't exist, we will rely on the layout params set in the // included XML file. @@ -133,30 +181,20 @@ public class LayoutInflater_Delegate { // ---- END CHANGES params = group.generateLayoutParams(attrs); - } catch (RuntimeException ignored) { // Ignore, just fail over to child attrs. } finally { // ---- START CHANGES sIsInInclude = false; // ---- END CHANGES - - if (params != null) { - view.setLayoutParams(params); - } } + if (params == null) { + params = group.generateLayoutParams(childAttrs); + } + view.setLayoutParams(params); // Inflate all children. - thisInflater.rInflate(childParser, view, childAttrs, true, true); - - // Attempt to override the included layout's android:id with the - // one set on the <include /> tag itself. - TypedArray a = thisInflater.mContext.obtainStyledAttributes(attrs, - com.android.internal.R.styleable.View, 0, 0); - int id = a.getResourceId(com.android.internal.R.styleable.View_id, View.NO_ID); - // While we're at it, let's try to override android:visibility. - int visibility = a.getInt(com.android.internal.R.styleable.View_visibility, -1); - a.recycle(); + thisInflater.rInflateChildren(childParser, view, childAttrs, true); if (id != View.NO_ID) { view.setId(id); @@ -184,12 +222,6 @@ public class LayoutInflater_Delegate { throw new InflateException("<include /> can only be used inside of a ViewGroup"); } - final int currentDepth = parser.getDepth(); - while (((type = parser.next()) != XmlPullParser.END_TAG || - parser.getDepth() > currentDepth) && type != XmlPullParser.END_DOCUMENT) { - // Empty - } + LayoutInflater.consumeChildElements(parser); } - - } diff --git a/tools/layoutlib/bridge/src/android/view/ViewGroup_Delegate.java b/tools/layoutlib/bridge/src/android/view/ViewGroup_Delegate.java index e72a0db..51d32e3 100644 --- a/tools/layoutlib/bridge/src/android/view/ViewGroup_Delegate.java +++ b/tools/layoutlib/bridge/src/android/view/ViewGroup_Delegate.java @@ -46,16 +46,12 @@ public class ViewGroup_Delegate { /*package*/ static boolean drawChild(ViewGroup thisVG, Canvas canvas, View child, long drawingTime) { if (child.getZ() > thisVG.getZ()) { + // The background's bounds are set lazily. Make sure they are set correctly so that + // the outline obtained is correct. + child.setBackgroundBounds(); ViewOutlineProvider outlineProvider = child.getOutlineProvider(); - Outline outline = new Outline(); + Outline outline = child.mAttachInfo.mTmpOutline; outlineProvider.getOutline(child, outline); - if (outline.mPath == null && outline.mRect == null) { - // Sometimes, the bounds of the background drawable are not set until View.draw() - // is called. So, we set the bounds manually and try to get the outline again. - child.getBackground().setBounds(0, 0, child.mRight - child.mLeft, - child.mBottom - child.mTop); - outlineProvider.getOutline(child, outline); - } if (outline.mPath != null || (outline.mRect != null && !outline.mRect.isEmpty())) { int restoreTo = transformCanvas(thisVG, canvas, child); drawShadow(thisVG, canvas, child, outline); diff --git a/tools/layoutlib/bridge/src/android/view/View_Delegate.java b/tools/layoutlib/bridge/src/android/view/View_Delegate.java index 8215f7c..408ec54 100644 --- a/tools/layoutlib/bridge/src/android/view/View_Delegate.java +++ b/tools/layoutlib/bridge/src/android/view/View_Delegate.java @@ -16,8 +16,12 @@ package android.view; +import com.android.layoutlib.bridge.android.BridgeContext; import com.android.tools.layoutlib.annotations.LayoutlibDelegate; +import android.content.Context; +import android.os.IBinder; + /** * Delegate used to provide new implementation of a select few methods of {@link View} * @@ -31,4 +35,13 @@ public class View_Delegate { /*package*/ static boolean isInEditMode(View thisView) { return true; } + + @LayoutlibDelegate + /*package*/ static IBinder getWindowToken(View thisView) { + Context baseContext = BridgeContext.getBaseContext(thisView.getContext()); + if (baseContext instanceof BridgeContext) { + return ((BridgeContext) baseContext).getBinder(); + } + return null; + } } diff --git a/tools/layoutlib/bridge/src/android/view/WindowCallback.java b/tools/layoutlib/bridge/src/android/view/WindowCallback.java index 78242a8..d691c8e 100644 --- a/tools/layoutlib/bridge/src/android/view/WindowCallback.java +++ b/tools/layoutlib/bridge/src/android/view/WindowCallback.java @@ -110,6 +110,11 @@ public class WindowCallback implements Window.Callback { } @Override + public boolean onSearchRequested(SearchEvent searchEvent) { + return onSearchRequested(); + } + + @Override public boolean onSearchRequested() { return false; } @@ -120,6 +125,11 @@ public class WindowCallback implements Window.Callback { } @Override + public ActionMode onWindowStartingActionMode(Callback callback, int type) { + return null; + } + + @Override public void onActionModeStarted(ActionMode mode) { } diff --git a/tools/layoutlib/bridge/src/android/widget/SimpleMonthView_Delegate.java b/tools/layoutlib/bridge/src/android/widget/SimpleMonthView_Delegate.java new file mode 100644 index 0000000..8e41e51 --- /dev/null +++ b/tools/layoutlib/bridge/src/android/widget/SimpleMonthView_Delegate.java @@ -0,0 +1,99 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.widget; + +import com.android.tools.layoutlib.annotations.LayoutlibDelegate; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.icu.text.SimpleDateFormat; +import android.text.format.DateFormat; + +import java.util.Calendar; +import java.util.Locale; + +/** + * Delegate that provides implementation for some methods in {@link SimpleMonthView}. + * <p/> + * Through the layoutlib_create tool, selected methods of SimpleMonthView have been replaced by + * calls to methods of the same name in this delegate class. + * <p/> + * The main purpose of this class is to use {@link android.icu.text.SimpleDateFormat} instead of + * {@link java.text.SimpleDateFormat}. + */ +public class SimpleMonthView_Delegate { + + private static final String DEFAULT_TITLE_FORMAT = "MMMMy"; + private static final String DAY_OF_WEEK_FORMAT = "EEEEE"; + + // Maintain a cache of the last view used, so that the formatters can be reused. + @Nullable private static SimpleMonthView sLastView; + @Nullable private static SimpleMonthView_Delegate sLastDelegate; + + private SimpleDateFormat mTitleFormatter; + private SimpleDateFormat mDayOfWeekFormatter; + + private Locale locale; + + @LayoutlibDelegate + /*package*/ static CharSequence getTitle(SimpleMonthView view) { + if (view.mTitle == null) { + SimpleMonthView_Delegate delegate = getDelegate(view); + if (delegate.mTitleFormatter == null) { + delegate.mTitleFormatter = new SimpleDateFormat(DateFormat.getBestDateTimePattern( + getLocale(delegate, view), DEFAULT_TITLE_FORMAT)); + } + view.mTitle = delegate.mTitleFormatter.format(view.mCalendar.getTime()); + } + return view.mTitle; + } + + @LayoutlibDelegate + /*package*/ static String getDayOfWeekLabel(SimpleMonthView view, int dayOfWeek) { + view.mDayOfWeekLabelCalendar.set(Calendar.DAY_OF_WEEK, dayOfWeek); + SimpleMonthView_Delegate delegate = getDelegate(view); + if (delegate.mDayOfWeekFormatter == null) { + delegate.mDayOfWeekFormatter = + new SimpleDateFormat(DAY_OF_WEEK_FORMAT, getLocale(delegate, view)); + } + return delegate.mDayOfWeekFormatter.format(view.mDayOfWeekLabelCalendar.getTime()); + } + + private static Locale getLocale(SimpleMonthView_Delegate delegate, SimpleMonthView view) { + if (delegate.locale == null) { + delegate.locale = view.getContext().getResources().getConfiguration().locale; + } + return delegate.locale; + } + + @NonNull + private static SimpleMonthView_Delegate getDelegate(SimpleMonthView view) { + if (view == sLastView) { + assert sLastDelegate != null; + return sLastDelegate; + } else { + sLastView = view; + sLastDelegate = new SimpleMonthView_Delegate(); + return sLastDelegate; + } + } + + public static void clearCache() { + sLastView = null; + sLastDelegate = null; + } +} diff --git a/tools/layoutlib/bridge/src/com/android/internal/policy/PolicyManager.java b/tools/layoutlib/bridge/src/com/android/internal/policy/PolicyManager.java deleted file mode 100644 index 6558b6a..0000000 --- a/tools/layoutlib/bridge/src/com/android/internal/policy/PolicyManager.java +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Copyright (C) 2012 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.internal.policy; - -import com.android.ide.common.rendering.api.LayoutLog; -import com.android.layoutlib.bridge.Bridge; -import com.android.layoutlib.bridge.impl.RenderAction; - -import android.content.Context; -import android.view.BridgeInflater; -import android.view.FallbackEventHandler; -import android.view.KeyEvent; -import android.view.LayoutInflater; -import android.view.View; -import android.view.Window; -import android.view.WindowManagerPolicy; - -/** - * Custom implementation of PolicyManager that does nothing to run in LayoutLib. - * - */ -public class PolicyManager { - - public static Window makeNewWindow(Context context) { - // this will likely crash somewhere beyond so we log it. - Bridge.getLog().error(LayoutLog.TAG_UNSUPPORTED, - "Call to PolicyManager.makeNewWindow is not supported", null); - return null; - } - - public static LayoutInflater makeNewLayoutInflater(Context context) { - return new BridgeInflater(context, RenderAction.getCurrentContext().getLayoutlibCallback()); - } - - public static WindowManagerPolicy makeNewWindowManager() { - // this will likely crash somewhere beyond so we log it. - Bridge.getLog().error(LayoutLog.TAG_UNSUPPORTED, - "Call to PolicyManager.makeNewWindowManager is not supported", null); - return null; - } - - public static FallbackEventHandler makeNewFallbackEventHandler(Context context) { - return new FallbackEventHandler() { - @Override - public void setView(View v) { - } - - @Override - public void preDispatchKeyEvent(KeyEvent event) { - } - - @Override - public boolean dispatchKeyEvent(KeyEvent event) { - return false; - } - }; - } -} 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 0fede75..48ca7d8 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java @@ -16,9 +16,6 @@ package com.android.layoutlib.bridge; -import static com.android.ide.common.rendering.api.Result.Status.ERROR_UNKNOWN; -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.DrawableParams; import com.android.ide.common.rendering.api.Features; @@ -35,14 +32,13 @@ import com.android.resources.ResourceType; import com.android.tools.layoutlib.create.MethodAdapter; import com.android.tools.layoutlib.create.OverrideMethod; import com.android.util.Pair; -import com.ibm.icu.util.ULocale; -import libcore.io.MemoryMappedFile_Delegate; import android.annotation.NonNull; import android.content.res.BridgeAssetManager; import android.graphics.Bitmap; import android.graphics.FontFamily_Delegate; import android.graphics.Typeface_Delegate; +import android.icu.util.ULocale; import android.os.Looper; import android.os.Looper_Accessor; import android.view.View; @@ -61,6 +57,11 @@ import java.util.HashMap; import java.util.Map; import java.util.concurrent.locks.ReentrantLock; +import libcore.io.MemoryMappedFile_Delegate; + +import static com.android.ide.common.rendering.api.Result.Status.ERROR_UNKNOWN; +import static com.android.ide.common.rendering.api.Result.Status.SUCCESS; + /** * Main entry point of the LayoutLib Bridge. * <p/>To use this bridge, simply instantiate an object of type {@link Bridge} and call diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/MockView.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/MockView.java index 4a9f718..44a9aad 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/MockView.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/MockView.java @@ -30,6 +30,10 @@ import android.widget.TextView; */ public class MockView extends TextView { + public MockView(Context context, AttributeSet attrs) { + this(context, attrs, 0); + } + public MockView(Context context, AttributeSet attrs, int defStyle) { this(context, attrs, defStyle, 0); } diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/AndroidLocale.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/AndroidLocale.java index ea5f1ea..faaf105 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/AndroidLocale.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/AndroidLocale.java @@ -16,9 +16,11 @@ package com.android.layoutlib.bridge.android; -import java.util.Locale; +import com.android.layoutlib.bridge.impl.RenderAction; + +import android.icu.util.ULocale; -import com.ibm.icu.util.ULocale; +import java.util.Locale; /** * This class provides an alternate implementation for {@code java.util.Locale#toLanguageTag} @@ -56,4 +58,15 @@ public class AndroidLocale { public static String getScript(Locale locale) { return ULocale.forLocale(locale).getScript(); } + + public static Locale getDefault() { + BridgeContext context = RenderAction.getCurrentContext(); + if (context != null) { + Locale locale = context.getConfiguration().locale; + if (locale != null) { + return locale; + } + } + return Locale.getDefault(); + } } diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java index 9555e9e..689e359 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java @@ -68,6 +68,7 @@ import android.os.Build.VERSION_CODES; import android.os.Bundle; import android.os.Handler; import android.os.IBinder; +import android.os.IInterface; import android.os.Looper; import android.os.Parcel; import android.os.PowerManager; @@ -79,6 +80,7 @@ import android.util.TypedValue; import android.view.BridgeInflater; import android.view.Display; import android.view.DisplayAdjustments; +import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.view.WindowManager; @@ -86,6 +88,7 @@ import android.view.accessibility.AccessibilityManager; import android.view.textservice.TextServicesManager; import java.io.File; +import java.io.FileDescriptor; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; @@ -146,6 +149,8 @@ public final class BridgeContext extends Context { private final Stack<BridgeXmlBlockParser> mParserStack = new Stack<BridgeXmlBlockParser>(); private SharedPreferences mSharedPreferences; private ClassLoader mClassLoader; + private IBinder mBinder; + private PackageManager mPackageManager; /** @@ -559,6 +564,34 @@ public final class BridgeContext extends Context { throw new UnsupportedOperationException("Unsupported Service: " + service); } + @Override + public String getSystemServiceName(Class<?> serviceClass) { + if (serviceClass.equals(LayoutInflater.class)) { + return LAYOUT_INFLATER_SERVICE; + } + + if (serviceClass.equals(TextServicesManager.class)) { + return TEXT_SERVICES_MANAGER_SERVICE; + } + + if (serviceClass.equals(WindowManager.class)) { + return WINDOW_SERVICE; + } + + if (serviceClass.equals(PowerManager.class)) { + return POWER_SERVICE; + } + + if (serviceClass.equals(DisplayManager.class)) { + return DISPLAY_SERVICE; + } + + if (serviceClass.equals(AccessibilityManager.class)) { + return ACCESSIBILITY_SERVICE; + } + + throw new UnsupportedOperationException("Unsupported Service: " + serviceClass); + } @Override public final BridgeTypedArray obtainStyledAttributes(int[] attrs) { @@ -880,6 +913,14 @@ public final class BridgeContext extends Context { return mApplicationInfo.packageName; } + @Override + public PackageManager getPackageManager() { + if (mPackageManager == null) { + mPackageManager = new BridgePackageManager(); + } + return mPackageManager; + } + // ------------- private new methods /** @@ -1039,6 +1080,61 @@ public final class BridgeContext extends Context { return context; } + public IBinder getBinder() { + if (mBinder == null) { + // create a dummy binder. We only need it be not null. + mBinder = new IBinder() { + @Override + public String getInterfaceDescriptor() throws RemoteException { + return null; + } + + @Override + public boolean pingBinder() { + return false; + } + + @Override + public boolean isBinderAlive() { + return false; + } + + @Override + public IInterface queryLocalInterface(String descriptor) { + return null; + } + + @Override + public void dump(FileDescriptor fd, String[] args) throws RemoteException { + + } + + @Override + public void dumpAsync(FileDescriptor fd, String[] args) throws RemoteException { + + } + + @Override + public boolean transact(int code, Parcel data, Parcel reply, int flags) + throws RemoteException { + return false; + } + + @Override + public void linkToDeath(DeathRecipient recipient, int flags) + throws RemoteException { + + } + + @Override + public boolean unlinkToDeath(DeathRecipient recipient, int flags) { + return false; + } + }; + } + return mBinder; + } + //------------ NOT OVERRIDEN -------------------- @Override @@ -1078,6 +1174,12 @@ public final class BridgeContext extends Context { } @Override + public int checkSelfPermission(String arg0) { + // pass + return 0; + } + + @Override public int checkPermission(String arg0, int arg1, int arg2, IBinder arg3) { // pass return 0; @@ -1281,12 +1383,6 @@ public final class BridgeContext extends Context { } @Override - public PackageManager getPackageManager() { - // pass - return null; - } - - @Override public String getBasePackageName() { // pass return null; @@ -1421,6 +1517,18 @@ public final class BridgeContext extends Context { } @Override + public void sendBroadcastMultiplePermissions(Intent intent, String[] receiverPermissions) { + // pass + + } + + @Override + public void sendBroadcast(Intent arg0, String arg1, Bundle arg2) { + // pass + + } + + @Override public void sendBroadcast(Intent intent, String receiverPermission, int appOp) { // pass } @@ -1440,6 +1548,14 @@ public final class BridgeContext extends Context { } @Override + public void sendOrderedBroadcast(Intent arg0, String arg1, + Bundle arg7, BroadcastReceiver arg2, Handler arg3, int arg4, String arg5, + Bundle arg6) { + // pass + + } + + @Override public void sendOrderedBroadcast(Intent intent, String receiverPermission, int appOp, BroadcastReceiver resultReceiver, Handler scheduler, int initialCode, String initialData, Bundle initialExtras) { @@ -1457,6 +1573,11 @@ public final class BridgeContext extends Context { // pass } + public void sendBroadcastAsUser(Intent intent, UserHandle user, + String receiverPermission, int appOp) { + // pass + } + @Override public void sendOrderedBroadcastAsUser(Intent intent, UserHandle user, String receiverPermission, BroadcastReceiver resultReceiver, Handler scheduler, @@ -1473,6 +1594,14 @@ public final class BridgeContext extends Context { } @Override + public void sendOrderedBroadcastAsUser(Intent intent, UserHandle user, + String receiverPermission, int appOp, Bundle options, BroadcastReceiver resultReceiver, + Handler scheduler, + int initialCode, String initialData, Bundle initialExtras) { + // pass + } + + @Override public void sendStickyBroadcast(Intent arg0) { // pass diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeIInputMethodManager.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeIInputMethodManager.java index c44a57c..8899e53 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeIInputMethodManager.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeIInputMethodManager.java @@ -164,7 +164,8 @@ public class BridgeIInputMethodManager implements IInputMethodManager { } @Override - public void showInputMethodPickerFromClient(IInputMethodClient arg0) throws RemoteException { + public void showInputMethodPickerFromClient(IInputMethodClient arg0, + int arg1) throws RemoteException { // TODO Auto-generated method stub } diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgePackageManager.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgePackageManager.java new file mode 100644 index 0000000..f04654e --- /dev/null +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgePackageManager.java @@ -0,0 +1,778 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.layoutlib.bridge.android; + +import android.app.PackageInstallObserver; +import android.content.ComponentName; +import android.content.Intent; +import android.content.IntentFilter; +import android.content.IntentSender; +import android.content.pm.ActivityInfo; +import android.content.pm.ApplicationInfo; +import android.content.pm.ContainerEncryptionParams; +import android.content.pm.FeatureInfo; +import android.content.pm.IPackageDataObserver; +import android.content.pm.IPackageDeleteObserver; +import android.content.pm.IPackageInstallObserver; +import android.content.pm.IPackageStatsObserver; +import android.content.pm.InstrumentationInfo; +import android.content.pm.IntentFilterVerificationInfo; +import android.content.pm.KeySet; +import android.content.pm.ManifestDigest; +import android.content.pm.PackageInfo; +import android.content.pm.PackageInstaller; +import android.content.pm.PackageItemInfo; +import android.content.pm.PackageManager; +import android.content.pm.PermissionGroupInfo; +import android.content.pm.PermissionInfo; +import android.content.pm.ProviderInfo; +import android.content.pm.ResolveInfo; +import android.content.pm.ServiceInfo; +import android.content.pm.VerificationParams; +import android.content.pm.VerifierDeviceIdentity; +import android.content.res.Resources; +import android.content.res.XmlResourceParser; +import android.graphics.Rect; +import android.graphics.drawable.Drawable; +import android.net.Uri; +import android.os.Handler; +import android.os.UserHandle; +import android.os.storage.VolumeInfo; +import java.util.List; + +/** + * An implementation of {@link PackageManager} that does nothing. + */ +@SuppressWarnings("deprecation") +public class BridgePackageManager extends PackageManager { + @Override + public PackageInfo getPackageInfo(String packageName, int flags) throws NameNotFoundException { + return null; + } + + @Override + public String[] currentToCanonicalPackageNames(String[] names) { + return new String[0]; + } + + @Override + public String[] canonicalToCurrentPackageNames(String[] names) { + return new String[0]; + } + + @Override + public Intent getLaunchIntentForPackage(String packageName) { + return null; + } + + @Override + public Intent getLeanbackLaunchIntentForPackage(String packageName) { + return null; + } + + @Override + public int[] getPackageGids(String packageName) throws NameNotFoundException { + return new int[0]; + } + + @Override + public int getPackageUid(String packageName, int userHandle) throws NameNotFoundException { + return 0; + } + + @Override + public PermissionInfo getPermissionInfo(String name, int flags) throws NameNotFoundException { + return null; + } + + @Override + public List<PermissionInfo> queryPermissionsByGroup(String group, int flags) + throws NameNotFoundException { + return null; + } + + @Override + public PermissionGroupInfo getPermissionGroupInfo(String name, int flags) + throws NameNotFoundException { + return null; + } + + @Override + public List<PermissionGroupInfo> getAllPermissionGroups(int flags) { + return null; + } + + @Override + public ApplicationInfo getApplicationInfo(String packageName, int flags) + throws NameNotFoundException { + return null; + } + + @Override + public ActivityInfo getActivityInfo(ComponentName component, int flags) + throws NameNotFoundException { + return null; + } + + @Override + public ActivityInfo getReceiverInfo(ComponentName component, int flags) + throws NameNotFoundException { + return null; + } + + @Override + public ServiceInfo getServiceInfo(ComponentName component, int flags) + throws NameNotFoundException { + return null; + } + + @Override + public ProviderInfo getProviderInfo(ComponentName component, int flags) + throws NameNotFoundException { + return null; + } + + @Override + public List<PackageInfo> getInstalledPackages(int flags) { + return null; + } + + @Override + public List<PackageInfo> getPackagesHoldingPermissions(String[] permissions, int flags) { + return null; + } + + @Override + public List<PackageInfo> getInstalledPackages(int flags, int userId) { + return null; + } + + @Override + public int checkPermission(String permName, String pkgName) { + return 0; + } + + @Override + public boolean isPermissionRevokedByPolicy(String permName, String pkgName) { + return false; + } + + @Override + public String getPermissionControllerPackageName() { + return null; + } + + @Override + public boolean addPermission(PermissionInfo info) { + return false; + } + + @Override + public boolean addPermissionAsync(PermissionInfo info) { + return false; + } + + @Override + public void removePermission(String name) { + } + + @Override + public void grantRuntimePermission(String packageName, String permissionName, UserHandle user) { + } + + @Override + public void revokeRuntimePermission(String packageName, String permissionName, + UserHandle user) { + } + + @Override + public int getPermissionFlags(String permissionName, String packageName, UserHandle user) { + return 0; + } + + @Override + public void updatePermissionFlags(String permissionName, String packageName, int flagMask, + int flagValues, UserHandle user) { + } + + @Override + public boolean shouldShowRequestPermissionRationale(String permission) { + return false; + } + + @Override + public int checkSignatures(String pkg1, String pkg2) { + return 0; + } + + @Override + public int checkSignatures(int uid1, int uid2) { + return 0; + } + + @Override + public String[] getPackagesForUid(int uid) { + return new String[0]; + } + + @Override + public String getNameForUid(int uid) { + return null; + } + + @Override + public int getUidForSharedUser(String sharedUserName) throws NameNotFoundException { + return 0; + } + + @Override + public List<ApplicationInfo> getInstalledApplications(int flags) { + return null; + } + + @Override + public String[] getSystemSharedLibraryNames() { + return new String[0]; + } + + @Override + public FeatureInfo[] getSystemAvailableFeatures() { + return new FeatureInfo[0]; + } + + @Override + public boolean hasSystemFeature(String name) { + return false; + } + + @Override + public ResolveInfo resolveActivity(Intent intent, int flags) { + return null; + } + + @Override + public ResolveInfo resolveActivityAsUser(Intent intent, int flags, int userId) { + return null; + } + + @Override + public List<ResolveInfo> queryIntentActivities(Intent intent, int flags) { + return null; + } + + @Override + public List<ResolveInfo> queryIntentActivitiesAsUser(Intent intent, int flags, int userId) { + return null; + } + + @Override + public List<ResolveInfo> queryIntentActivityOptions(ComponentName caller, Intent[] specifics, + Intent intent, int flags) { + return null; + } + + @Override + public List<ResolveInfo> queryBroadcastReceivers(Intent intent, int flags) { + return null; + } + + @Override + public List<ResolveInfo> queryBroadcastReceivers(Intent intent, int flags, int userId) { + return null; + } + + @Override + public ResolveInfo resolveService(Intent intent, int flags) { + return null; + } + + @Override + public List<ResolveInfo> queryIntentServices(Intent intent, int flags) { + return null; + } + + @Override + public List<ResolveInfo> queryIntentServicesAsUser(Intent intent, int flags, int userId) { + return null; + } + + @Override + public List<ResolveInfo> queryIntentContentProvidersAsUser(Intent intent, int flags, + int userId) { + return null; + } + + @Override + public List<ResolveInfo> queryIntentContentProviders(Intent intent, int flags) { + return null; + } + + @Override + public ProviderInfo resolveContentProvider(String name, int flags) { + return null; + } + + @Override + public ProviderInfo resolveContentProviderAsUser(String name, int flags, int userId) { + return null; + } + + @Override + public List<ProviderInfo> queryContentProviders(String processName, int uid, int flags) { + return null; + } + + @Override + public InstrumentationInfo getInstrumentationInfo(ComponentName className, int flags) + throws NameNotFoundException { + return null; + } + + @Override + public List<InstrumentationInfo> queryInstrumentation(String targetPackage, int flags) { + return null; + } + + @Override + public Drawable getDrawable(String packageName, int resid, ApplicationInfo appInfo) { + return null; + } + + @Override + public Drawable getActivityIcon(ComponentName activityName) throws NameNotFoundException { + return null; + } + + @Override + public Drawable getActivityIcon(Intent intent) throws NameNotFoundException { + return null; + } + + @Override + public Drawable getActivityBanner(ComponentName activityName) throws NameNotFoundException { + return null; + } + + @Override + public Drawable getActivityBanner(Intent intent) throws NameNotFoundException { + return null; + } + + @Override + public Drawable getDefaultActivityIcon() { + return null; + } + + @Override + public Drawable getApplicationIcon(ApplicationInfo info) { + return null; + } + + @Override + public Drawable getApplicationIcon(String packageName) throws NameNotFoundException { + return null; + } + + @Override + public Drawable getApplicationBanner(ApplicationInfo info) { + return null; + } + + @Override + public Drawable getApplicationBanner(String packageName) throws NameNotFoundException { + return null; + } + + @Override + public Drawable getActivityLogo(ComponentName activityName) throws NameNotFoundException { + return null; + } + + @Override + public Drawable getActivityLogo(Intent intent) throws NameNotFoundException { + return null; + } + + @Override + public Drawable getApplicationLogo(ApplicationInfo info) { + return null; + } + + @Override + public Drawable getApplicationLogo(String packageName) throws NameNotFoundException { + return null; + } + + @Override + public Drawable getUserBadgedIcon(Drawable icon, UserHandle user) { + return null; + } + + @Override + public Drawable getUserBadgedDrawableForDensity(Drawable drawable, UserHandle user, + Rect badgeLocation, int badgeDensity) { + return null; + } + + @Override + public Drawable getUserBadgeForDensity(UserHandle user, int density) { + return null; + } + + @Override + public CharSequence getUserBadgedLabel(CharSequence label, UserHandle user) { + return null; + } + + @Override + public CharSequence getText(String packageName, int resid, ApplicationInfo appInfo) { + return null; + } + + @Override + public XmlResourceParser getXml(String packageName, int resid, ApplicationInfo appInfo) { + return null; + } + + @Override + public CharSequence getApplicationLabel(ApplicationInfo info) { + return null; + } + + @Override + public Resources getResourcesForActivity(ComponentName activityName) + throws NameNotFoundException { + return null; + } + + @Override + public Resources getResourcesForApplication(ApplicationInfo app) throws NameNotFoundException { + return null; + } + + @Override + public Resources getResourcesForApplication(String appPackageName) + throws NameNotFoundException { + return null; + } + + @Override + public Resources getResourcesForApplicationAsUser(String appPackageName, int userId) + throws NameNotFoundException { + return null; + } + + @Override + public void installPackage(Uri packageURI, IPackageInstallObserver observer, int flags, + String installerPackageName) { + } + + @Override + public void installPackageWithVerification(Uri packageURI, IPackageInstallObserver observer, + int flags, String installerPackageName, Uri verificationURI, + ManifestDigest manifestDigest, ContainerEncryptionParams encryptionParams) { + } + + @Override + public void installPackageWithVerificationAndEncryption(Uri packageURI, + IPackageInstallObserver observer, int flags, String installerPackageName, + VerificationParams verificationParams, ContainerEncryptionParams encryptionParams) { + } + + @Override + public void installPackage(Uri packageURI, PackageInstallObserver observer, int flags, + String installerPackageName) { + } + + @Override + public void installPackageWithVerification(Uri packageURI, PackageInstallObserver observer, + int flags, String installerPackageName, Uri verificationURI, + ManifestDigest manifestDigest, ContainerEncryptionParams encryptionParams) { + } + + @Override + public void installPackageWithVerificationAndEncryption(Uri packageURI, + PackageInstallObserver observer, int flags, String installerPackageName, + VerificationParams verificationParams, ContainerEncryptionParams encryptionParams) { + } + + @Override + public int installExistingPackage(String packageName) throws NameNotFoundException { + return 0; + } + + @Override + public void verifyPendingInstall(int id, int verificationCode) { + } + + @Override + public void extendVerificationTimeout(int id, int verificationCodeAtTimeout, + long millisecondsToDelay) { + } + + @Override + public void verifyIntentFilter(int verificationId, int verificationCode, + List<String> outFailedDomains) { + } + + @Override + public int getIntentVerificationStatus(String packageName, int userId) { + return 0; + } + + @Override + public boolean updateIntentVerificationStatus(String packageName, int status, int userId) { + return false; + } + + @Override + public List<IntentFilterVerificationInfo> getIntentFilterVerifications(String packageName) { + return null; + } + + @Override + public List<IntentFilter> getAllIntentFilters(String packageName) { + return null; + } + + @Override + public String getDefaultBrowserPackageName(int userId) { + return null; + } + + @Override + public boolean setDefaultBrowserPackageName(String packageName, int userId) { + return false; + } + + @Override + public void setInstallerPackageName(String targetPackage, String installerPackageName) { + } + + @Override + public void deletePackage(String packageName, IPackageDeleteObserver observer, int flags) { + } + + @Override + public String getInstallerPackageName(String packageName) { + return null; + } + + @Override + public void clearApplicationUserData(String packageName, IPackageDataObserver observer) { + } + + @Override + public void deleteApplicationCacheFiles(String packageName, IPackageDataObserver observer) { + } + + @Override + public void freeStorageAndNotify(String volumeUuid, long freeStorageSize, + IPackageDataObserver observer) { + } + + @Override + public void freeStorage(String volumeUuid, long freeStorageSize, IntentSender pi) { + } + + @Override + public void getPackageSizeInfo(String packageName, int userHandle, + IPackageStatsObserver observer) { + } + + @Override + public void addPackageToPreferred(String packageName) { + } + + @Override + public void removePackageFromPreferred(String packageName) { + } + + @Override + public List<PackageInfo> getPreferredPackages(int flags) { + return null; + } + + @Override + public void addPreferredActivity(IntentFilter filter, int match, ComponentName[] set, + ComponentName activity) { + } + + @Override + public void replacePreferredActivity(IntentFilter filter, int match, ComponentName[] set, + ComponentName activity) { + } + + @Override + public void clearPackagePreferredActivities(String packageName) { + } + + @Override + public int getPreferredActivities(List<IntentFilter> outFilters, + List<ComponentName> outActivities, String packageName) { + return 0; + } + + @Override + public ComponentName getHomeActivities(List<ResolveInfo> outActivities) { + return null; + } + + @Override + public void setComponentEnabledSetting(ComponentName componentName, int newState, int flags) { + } + + @Override + public int getComponentEnabledSetting(ComponentName componentName) { + return 0; + } + + @Override + public void setApplicationEnabledSetting(String packageName, int newState, int flags) { + } + + @Override + public int getApplicationEnabledSetting(String packageName) { + return 0; + } + + @Override + public boolean setApplicationHiddenSettingAsUser(String packageName, boolean hidden, + UserHandle userHandle) { + return false; + } + + @Override + public boolean getApplicationHiddenSettingAsUser(String packageName, UserHandle userHandle) { + return false; + } + + @Override + public boolean isSafeMode() { + return false; + } + + @Override + public void addOnPermissionsChangeListener(OnPermissionsChangedListener listener) { + } + + @Override + public void removeOnPermissionsChangeListener(OnPermissionsChangedListener listener) { + } + + @Override + public KeySet getKeySetByAlias(String packageName, String alias) { + return null; + } + + @Override + public KeySet getSigningKeySet(String packageName) { + return null; + } + + @Override + public boolean isSignedBy(String packageName, KeySet ks) { + return false; + } + + @Override + public boolean isSignedByExactly(String packageName, KeySet ks) { + return false; + } + + @Override + public int getMoveStatus(int moveId) { + return 0; + } + + @Override + public void registerMoveCallback(MoveCallback callback, Handler handler) { + } + + @Override + public void unregisterMoveCallback(MoveCallback callback) { + } + + @Override + public int movePackage(String packageName, VolumeInfo vol) { + return 0; + } + + @Override + public VolumeInfo getPackageCurrentVolume(ApplicationInfo app) { + return null; + } + + @Override + public List<VolumeInfo> getPackageCandidateVolumes(ApplicationInfo app) { + return null; + } + + @Override + public int movePrimaryStorage(VolumeInfo vol) { + return 0; + } + + @Override + public VolumeInfo getPrimaryStorageCurrentVolume() { + return null; + } + + @Override + public List<VolumeInfo> getPrimaryStorageCandidateVolumes() { + return null; + } + + @Override + public VerifierDeviceIdentity getVerifierDeviceIdentity() { + return null; + } + + @Override + public boolean isUpgrade() { + return false; + } + + @Override + public PackageInstaller getPackageInstaller() { + return null; + } + + @Override + public void addCrossProfileIntentFilter(IntentFilter filter, int sourceUserId, int targetUserId, + int flags) { + } + + @Override + public void clearCrossProfileIntentFilters(int sourceUserId) { + } + + @Override + public Drawable loadItemIcon(PackageItemInfo itemInfo, ApplicationInfo appInfo) { + return null; + } + + @Override + public Drawable loadUnbadgedItemIcon(PackageItemInfo itemInfo, ApplicationInfo appInfo) { + return null; + } + + @Override + public boolean isPackageAvailable(String packageName) { + return false; + } +} diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgePowerManager.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgePowerManager.java index 9b755cd..895f9c9 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgePowerManager.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgePowerManager.java @@ -137,7 +137,7 @@ public class BridgePowerManager implements IPowerManager { } @Override - public void wakeUp(long time) throws RemoteException { + public void wakeUp(long time, String reason, String opPackageName) throws RemoteException { // pass for now. } @@ -147,6 +147,11 @@ public class BridgePowerManager implements IPowerManager { } @Override + public boolean isDeviceIdleMode() throws RemoteException { + return false; + } + + @Override public boolean isScreenBrightnessBoosted() throws RemoteException { return false; } diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindow.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindow.java index fb5d44f..771c3c8 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindow.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindow.java @@ -91,7 +91,11 @@ public final class BridgeWindow implements IWindow { } @Override - public void doneAnimating() { + public void onAnimationStarted(int remainingFrameCount) { + } + + @Override + public void onAnimationStopped() { } @Override diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindowSession.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindowSession.java index a7de1e6..bea1f86 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindowSession.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindowSession.java @@ -191,12 +191,6 @@ public final class BridgeWindowSession implements IWindowSession { } @Override - public void setUniverseTransform(IBinder window, float alpha, float offx, float offy, - float dsdx, float dtdx, float dsdy, float dtdy) { - // pass for now. - } - - @Override public IBinder asBinder() { // pass for now. return null; diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/AppCompatActionBar.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/AppCompatActionBar.java index 7f2d3b4..868c6d3 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/AppCompatActionBar.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/AppCompatActionBar.java @@ -151,7 +151,7 @@ public class AppCompatActionBar extends BridgeActionBar { @Override public void createMenuPopup() { - // it's hard to addd menus to appcompat's actionbar, since it'll use a lot of reflection. + // it's hard to add menus to appcompat's actionbar, since it'll use a lot of reflection. // so we skip it for now. } diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/Config.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/Config.java index dc89d0c..09937bc 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/Config.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/Config.java @@ -74,8 +74,8 @@ public class Config { } public static String getTime(int platformVersion) { - if (isGreaterOrEqual(platformVersion, LOLLIPOP_MR1)) { - return "5:10"; + if (isGreaterOrEqual(platformVersion, M)) { + return "6:00"; } if (platformVersion < GINGERBREAD) { return "2:20"; @@ -95,6 +95,9 @@ public class Config { if (platformVersion < LOLLIPOP_MR1) { return "5:00"; } + if (platformVersion < M) { + return "5:10"; + } // Should never happen. return "4:04"; } 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 index 567002e..b76ec17 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/CustomBar.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/CustomBar.java @@ -33,7 +33,6 @@ import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; import android.annotation.NonNull; -import android.annotation.Nullable; import android.content.res.ColorStateList; import android.graphics.Bitmap; import android.graphics.Bitmap_Delegate; @@ -228,18 +227,16 @@ abstract class CustomBar extends LinearLayout { * Find the background color for this bar from the theme attributes. Only relevant to StatusBar * and NavigationBar. * <p/> - * Returns null if not found. + * Returns 0 if not found. * * @param colorAttrName the attribute name for the background color * @param translucentAttrName the attribute name for the translucency property of the bar. * * @throws NumberFormatException if color resolved to an invalid string. */ - @Nullable - protected Integer getBarColor(@NonNull String colorAttrName, - @NonNull String translucentAttrName) { + protected int getBarColor(@NonNull String colorAttrName, @NonNull String translucentAttrName) { if (!Config.isGreaterOrEqual(mSimulatedPlatformVersion, LOLLIPOP)) { - return null; + return 0; } RenderResources renderResources = getContext().getRenderResources(); // First check if the bar is translucent. @@ -254,11 +251,10 @@ abstract class CustomBar extends LinearLayout { if (transparent) { return getColor(renderResources, colorAttrName); } - return null; + return 0; } - @Nullable - private static Integer getColor(RenderResources renderResources, String attr) { + private static int getColor(RenderResources renderResources, String attr) { // From ?attr/foo to @color/bar. This is most likely an ItemResourceValue. ResourceValue resource = renderResources.findItemInTheme(attr, true); // Form @color/bar to the #AARRGGBB @@ -279,7 +275,7 @@ abstract class CustomBar extends LinearLayout { } } } - return null; + return 0; } private ResourceValue getResourceValue(String reference) { diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/NavigationBar.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/NavigationBar.java index 4666850..9c89bfe 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/NavigationBar.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/NavigationBar.java @@ -41,6 +41,9 @@ public class NavigationBar extends CustomBar { private static final int WIDTH_DEFAULT = 36; private static final int WIDTH_SW360 = 40; private static final int WIDTH_SW600 = 48; + private static final String LAYOUT_XML = "/bars/navigation_bar.xml"; + private static final String LAYOUT_600DP_XML = "/bars/navigation_bar600dp.xml"; + /** * Constructor to be used when creating the {@link NavigationBar} as a regular control. @@ -59,11 +62,11 @@ public class NavigationBar extends CustomBar { public NavigationBar(BridgeContext context, Density density, int orientation, boolean isRtl, boolean rtlEnabled, int simulatedPlatformVersion) { - super(context, orientation, "/bars/navigation_bar.xml", "navigation_bar.xml", - simulatedPlatformVersion); + super(context, orientation, getShortestWidth(context)>= 600 ? LAYOUT_600DP_XML : LAYOUT_XML, + "navigation_bar.xml", simulatedPlatformVersion); - Integer color = getBarColor(ATTR_COLOR, ATTR_TRANSLUCENT); - setBackgroundColor(color == null ? 0xFF000000 : color); + int color = getBarColor(ATTR_COLOR, ATTR_TRANSLUCENT); + setBackgroundColor(color == 0 ? 0xFF000000 : color); // Cannot access the inside items through id because no R.id values have been // created for them. @@ -87,13 +90,19 @@ public class NavigationBar extends CustomBar { } private void setupNavBar(BridgeContext context, int orientation) { + float sw = getShortestWidth(context); View leftPadding = getChildAt(0); View rightPadding = getChildAt(6); - setSize(context, leftPadding, orientation, getSidePadding(context)); - setSize(context, rightPadding, orientation, getSidePadding(context)); + setSize(context, leftPadding, orientation, getSidePadding(sw)); + setSize(context, rightPadding, orientation, getSidePadding(sw)); + int navButtonWidth = getWidth(sw); for (int i = 1; i < 6; i += 2) { View navButton = getChildAt(i); - setSize(context, navButton, orientation, getWidth(context)); + setSize(context, navButton, orientation, navButtonWidth); + } + if (sw >= 600) { + setSize(context, getChildAt(2), orientation, 128); + setSize(context, getChildAt(4), orientation, 128); } } @@ -108,11 +117,7 @@ public class NavigationBar extends CustomBar { view.setLayoutParams(layoutParams); } - private static int getSidePadding(BridgeContext context) { - DisplayMetrics metrics = context.getMetrics(); - float sw = metrics.widthPixels > metrics.heightPixels - ? metrics.heightPixels : metrics.widthPixels; - sw /= metrics.density; + private static int getSidePadding(float sw) { if (sw >= 400) { return PADDING_WIDTH_SW400; } @@ -122,11 +127,7 @@ public class NavigationBar extends CustomBar { return PADDING_WIDTH_DEFAULT; } - private static int getWidth(BridgeContext context) { - DisplayMetrics metrics = context.getMetrics(); - float sw = metrics.widthPixels > metrics.heightPixels - ? metrics.heightPixels : metrics.widthPixels; - sw /= metrics.density; + private static int getWidth(float sw) { if (sw >= 600) { return WIDTH_SW600; } @@ -136,6 +137,14 @@ public class NavigationBar extends CustomBar { return WIDTH_DEFAULT; } + private static float getShortestWidth(BridgeContext context) { + DisplayMetrics metrics = context.getMetrics(); + float sw = metrics.widthPixels < metrics.heightPixels ? + metrics.widthPixels : metrics.heightPixels; + sw /= metrics.density; + return sw; + } + @Override protected TextView getStyleableTextView() { return null; diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/StatusBar.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/StatusBar.java index 95a5a58..2dc7c65 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/StatusBar.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/StatusBar.java @@ -71,9 +71,8 @@ public class StatusBar extends CustomBar { // FIXME: use FILL_H? setGravity(Gravity.START | Gravity.TOP | Gravity.RIGHT); - Integer color = getBarColor(ATTR_COLOR, ATTR_TRANSLUCENT); - setBackgroundColor( - color == null ? Config.getStatusBarColor(simulatedPlatformVersion) : color); + int color = getBarColor(ATTR_COLOR, ATTR_TRANSLUCENT); + setBackgroundColor(color == 0 ? Config.getStatusBarColor(simulatedPlatformVersion) : color); // Cannot access the inside items through id because no R.id values have been // created for them. diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/DelegateManager.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/DelegateManager.java index 7f2a7ae..baf2e2e 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/DelegateManager.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/DelegateManager.java @@ -49,7 +49,7 @@ import java.util.List; * int -> Delegate class link. * * Native methods usually always have the int as parameters. The first thing the delegate method - * will do is call {@link #getDelegate(int)} to get the Java object matching the int. + * will do is call {@link #getDelegate(long)} to get the Java object matching the int. * * Typical native init methods are returning a new int back to the Java class, so * {@link #addNewDelegate(Object)} does the same. @@ -58,7 +58,7 @@ import java.util.List; * the Java object needs to count as a reference (even though it only holds an int), we use the * following mechanism: * - * - {@link #addNewDelegate(Object)} and {@link #removeJavaReferenceFor(int)} adds and removes + * - {@link #addNewDelegate(Object)} and {@link #removeJavaReferenceFor(long)} adds and removes * the delegate to/from a list. This list hold the reference and prevents the GC from reclaiming * the delegate. * @@ -71,12 +71,13 @@ import java.util.List; * @param <T> the delegate class to manage */ public final class DelegateManager<T> { + @SuppressWarnings("FieldCanBeLocal") private final Class<T> mClass; private final SparseWeakArray<T> mDelegates = new SparseWeakArray<T>(); /** list used to store delegates when their main object holds a reference to them. * This is to ensure that the WeakReference in the SparseWeakArray doesn't get GC'ed * @see #addNewDelegate(Object) - * @see #removeJavaReferenceFor(int) + * @see #removeJavaReferenceFor(long) */ private final List<T> mJavaReferences = new ArrayList<T>(); private int mDelegateCounter = 0; @@ -95,13 +96,14 @@ public final class DelegateManager<T> { * @param native_object the native int. * @return the delegate or null if not found. */ - public T getDelegate(long native_object) { + @Nullable + public synchronized T getDelegate(long native_object) { if (native_object > 0) { - T delegate = mDelegates.get(native_object); + T delegate = mDelegates.get(native_object); if (Debug.DEBUG) { if (delegate == null) { - System.out.println("Unknown " + mClass.getSimpleName() + " with int " + + System.err.println("Unknown " + mClass.getSimpleName() + " with int " + native_object); } } @@ -117,14 +119,18 @@ public final class DelegateManager<T> { * @param newDelegate the delegate to add * @return a unique native int to identify the delegate */ - public long addNewDelegate(T newDelegate) { + public synchronized long addNewDelegate(T newDelegate) { long native_object = ++mDelegateCounter; + mDelegates.put(native_object, newDelegate); assert !mJavaReferences.contains(newDelegate); mJavaReferences.add(newDelegate); if (Debug.DEBUG) { - System.out.println("New " + mClass.getSimpleName() + " with int " + native_object); + System.out.println( + "New " + mClass.getSimpleName() + " " + + "with int " + + native_object); } return native_object; @@ -134,7 +140,7 @@ public final class DelegateManager<T> { * Removes the main reference on the given delegate. * @param native_object the native integer representing the delegate. */ - public void removeJavaReferenceFor(long native_object) { + public synchronized void removeJavaReferenceFor(long native_object) { T delegate = getDelegate(native_object); if (Debug.DEBUG) { diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/Layout.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/Layout.java index 9228d87..cbd0415 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/Layout.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/Layout.java @@ -330,9 +330,12 @@ class Layout extends RelativeLayout { mWindowIsFloating = getBooleanThemeValue(mResources, ATTR_WINDOW_FLOATING, true, true); findBackground(); - findStatusBar(); - findActionBar(); - findNavBar(); + + if (!mParams.isForceNoDecor()) { + findStatusBar(); + findActionBar(); + findNavBar(); + } } private void findBackground() { diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderAction.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderAction.java index de77d57..92b39e3 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderAction.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderAction.java @@ -27,6 +27,7 @@ import com.android.layoutlib.bridge.android.BridgeContext; import com.android.resources.Density; import com.android.resources.ResourceType; import com.android.resources.ScreenOrientation; +import com.android.resources.ScreenRound; import com.android.resources.ScreenSize; import android.content.res.Configuration; @@ -35,7 +36,9 @@ import android.util.DisplayMetrics; import android.view.ViewConfiguration_Accessor; import android.view.inputmethod.InputMethodManager; import android.view.inputmethod.InputMethodManager_Accessor; +import android.widget.SimpleMonthView_Delegate; +import java.util.Locale; import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.ReentrantLock; @@ -275,6 +278,7 @@ public abstract class RenderAction<T extends RenderParams> extends FrameworkReso mContext.getRenderResources().setLogger(null); } ParserFactory.setParserFactory(null); + SimpleMonthView_Delegate.clearCache(); } public static BridgeContext getCurrentContext() { @@ -378,6 +382,27 @@ public abstract class RenderAction<T extends RenderParams> extends FrameworkReso config.orientation = Configuration.ORIENTATION_UNDEFINED; } + try { + ScreenRound roundness = hardwareConfig.getScreenRoundness(); + if (roundness != null) { + switch (roundness) { + case ROUND: + config.screenLayout |= Configuration.SCREENLAYOUT_ROUND_YES; + break; + case NOTROUND: + config.screenLayout |= Configuration.SCREENLAYOUT_ROUND_NO; + } + } else { + config.screenLayout |= Configuration.SCREENLAYOUT_ROUND_UNDEFINED; + } + } catch (NoSuchMethodError ignored) { + // getScreenRoundness was added in later stages of API 15. So, it's not present on some + // preview releases of API 15. + // TODO: Remove the try catch around Oct 2015. + } + String locale = getParams().getLocale(); + if (locale != null && !locale.isEmpty()) config.locale = new Locale(locale); + // TODO: fill in more config info. return config; diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderDrawable.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderDrawable.java index 9e26502..26f9000 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderDrawable.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderDrawable.java @@ -38,6 +38,9 @@ import java.awt.AlphaComposite; import java.awt.Color; import java.awt.Graphics2D; import java.awt.image.BufferedImage; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; import java.util.ArrayList; import java.util.Collections; 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 24e1ce7..ac7c409 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 @@ -421,7 +421,8 @@ public class RenderSessionImpl extends RenderAction<SessionParams> { gc.setComposite(AlphaComposite.Src); gc.setColor(new Color(0x00000000, true)); - gc.fillRect(0, 0, mMeasuredScreenWidth, mMeasuredScreenHeight); + gc.fillRect(0, 0, + mMeasuredScreenWidth, mMeasuredScreenHeight); // done gc.dispose(); diff --git a/tools/layoutlib/bridge/src/libcore/icu/DateIntervalFormat_Delegate.java b/tools/layoutlib/bridge/src/libcore/icu/DateIntervalFormat_Delegate.java deleted file mode 100644 index d94c205..0000000 --- a/tools/layoutlib/bridge/src/libcore/icu/DateIntervalFormat_Delegate.java +++ /dev/null @@ -1,74 +0,0 @@ -/* - * Copyright (C) 2013 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 libcore.icu; - -import java.text.FieldPosition; - -import com.android.ide.common.rendering.api.LayoutLog; -import com.android.layoutlib.bridge.Bridge; -import com.android.layoutlib.bridge.impl.DelegateManager; -import com.android.tools.layoutlib.annotations.LayoutlibDelegate; -import com.ibm.icu.text.DateIntervalFormat; -import com.ibm.icu.util.DateInterval; -import com.ibm.icu.util.TimeZone; -import com.ibm.icu.util.ULocale; - -public class DateIntervalFormat_Delegate { - - // ---- delegate manager ---- - private static final DelegateManager<DateIntervalFormat_Delegate> sManager = - new DelegateManager<DateIntervalFormat_Delegate>(DateIntervalFormat_Delegate.class); - - // ---- delegate data ---- - private DateIntervalFormat mFormat; - - - // ---- native methods ---- - - @LayoutlibDelegate - /*package*/static String formatDateInterval(long address, long fromDate, long toDate) { - DateIntervalFormat_Delegate delegate = sManager.getDelegate((int)address); - if (delegate == null) { - Bridge.getLog().error(LayoutLog.TAG_BROKEN, - "Unable for find native DateIntervalFormat", null); - return null; - } - DateInterval interval = new DateInterval(fromDate, toDate); - StringBuffer sb = new StringBuffer(); - FieldPosition pos = new FieldPosition(0); - delegate.mFormat.format(interval, sb, pos); - return sb.toString(); - } - - @LayoutlibDelegate - /*package*/ static long createDateIntervalFormat(String skeleton, String localeName, - String tzName) { - TimeZone prevDefaultTz = TimeZone.getDefault(); - TimeZone.setDefault(TimeZone.getTimeZone(tzName)); - DateIntervalFormat_Delegate newDelegate = new DateIntervalFormat_Delegate(); - newDelegate.mFormat = - DateIntervalFormat.getInstance(skeleton, new ULocale(localeName)); - TimeZone.setDefault(prevDefaultTz); - return sManager.addNewDelegate(newDelegate); - } - - @LayoutlibDelegate - /*package*/ static void destroyDateIntervalFormat(long address) { - sManager.removeJavaReferenceFor((int)address); - } - -} diff --git a/tools/layoutlib/bridge/src/libcore/icu/ICU_Delegate.java b/tools/layoutlib/bridge/src/libcore/icu/ICU_Delegate.java index b8b5fed..9c58010 100644 --- a/tools/layoutlib/bridge/src/libcore/icu/ICU_Delegate.java +++ b/tools/layoutlib/bridge/src/libcore/icu/ICU_Delegate.java @@ -17,9 +17,11 @@ package libcore.icu; import com.android.tools.layoutlib.annotations.LayoutlibDelegate; -import com.ibm.icu.text.DateTimePatternGenerator; -import com.ibm.icu.util.Currency; -import com.ibm.icu.util.ULocale; + +import android.icu.text.DateTimePatternGenerator; +import android.icu.util.Currency; +import android.icu.util.ULocale; +import android.icu.util.VersionInfo; import java.util.Locale; @@ -53,18 +55,19 @@ public class ICU_Delegate { } @LayoutlibDelegate + @SuppressWarnings("deprecation") /*package*/ static String getCldrVersion() { - return "22.1.1"; // TODO: check what the right value should be. + return VersionInfo.ICU_DATA_VERSION.toString(); } @LayoutlibDelegate /*package*/ static String getIcuVersion() { - return "unknown_layoutlib"; + return VersionInfo.ICU_VERSION.toString(); } @LayoutlibDelegate /*package*/ static String getUnicodeVersion() { - return "5.2"; + return VersionInfo.UNICODE_7_0.toString(); } @LayoutlibDelegate @@ -181,8 +184,8 @@ public class ICU_Delegate { /*package*/ static boolean initLocaleDataNative(String locale, LocaleData result) { // Used by Calendar. - result.firstDayOfWeek = Integer.valueOf(1); - result.minimalDaysInFirstWeek = Integer.valueOf(1); + result.firstDayOfWeek = 1; + result.minimalDaysInFirstWeek = 1; // Used by DateFormatSymbols. result.amPm = new String[] { "AM", "PM" }; @@ -252,4 +255,9 @@ public class ICU_Delegate { /*package*/ static String getDefaultLocale() { return ICU.getDefaultLocale(); } + + @LayoutlibDelegate + /*package*/ static String getTZDataVersion() { + return ICU.getTZDataVersion(); + } } diff --git a/tools/layoutlib/bridge/tests/Android.mk b/tools/layoutlib/bridge/tests/Android.mk index 11390c3..5eef24a 100644 --- a/tools/layoutlib/bridge/tests/Android.mk +++ b/tools/layoutlib/bridge/tests/Android.mk @@ -26,7 +26,6 @@ LOCAL_MODULE_TAGS := optional LOCAL_JAVA_LIBRARIES := layoutlib \ kxml2-2.3.0 \ - icu4j \ layoutlib_api-prebuilt \ tools-common-prebuilt \ sdk-common \ diff --git a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R$menu.class b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R$menu.class Binary files differindex 8b32ba6..aecbff6 100644 --- a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R$menu.class +++ b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R$menu.class diff --git a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R$string.class b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R$string.class Binary files differindex 1c84476..fc3f236 100644 --- a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R$string.class +++ b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R$string.class diff --git a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R$style.class b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R$style.class Binary files differindex 672782f..83ad35b 100644 --- a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R$style.class +++ b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R$style.class diff --git a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/golden/activity.png b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/golden/activity.png Binary files differindex 1a96c47..9bf302a 100644 --- a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/golden/activity.png +++ b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/golden/activity.png diff --git a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/golden/allwidgets.png b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/golden/allwidgets.png Binary files differindex e7d7967..2b86bfb 100644 --- a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/golden/allwidgets.png +++ b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/golden/allwidgets.png diff --git a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/golden/array_check.png b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/golden/array_check.png Binary files differindex d8ad0b9..336f9d8 100644 --- a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/golden/array_check.png +++ b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/golden/array_check.png diff --git a/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/TestDelegates.java b/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/TestDelegates.java index 8b362ec..d8937f4 100644 --- a/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/TestDelegates.java +++ b/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/TestDelegates.java @@ -192,12 +192,12 @@ public class TestDelegates extends TestCase { StringBuilder sb = new StringBuilder(method.getName() + "("); for (int j = 0; j < parameters.length; j++) { Class<?> theClass = parameters[j]; - sb.append(theClass.getName()); int dimensions = 0; while (theClass.isArray()) { dimensions++; theClass = theClass.getComponentType(); } + sb.append(theClass.getName()); for (int i = 0; i < dimensions; i++) { sb.append("[]"); } diff --git a/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/intensive/Main.java b/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/intensive/Main.java index 5562ec5..9ebeebd 100644 --- a/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/intensive/Main.java +++ b/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/intensive/Main.java @@ -40,6 +40,7 @@ import org.junit.BeforeClass; import org.junit.Test; import android.annotation.NonNull; +import android.annotation.Nullable; import java.io.File; import java.io.FileFilter; @@ -356,6 +357,7 @@ public class Main { // TODO: Set up action bar handler properly to test menu rendering. // Create session params. RenderSession session = sBridge.createSession(params); + if (!session.getResult().isSuccess()) { getLogger().error(session.getResult().getException(), session.getResult().getErrorMessage()); @@ -426,13 +428,14 @@ public class Main { } @Override - public void fidelityWarning(String tag, String message, Throwable throwable, - Object data) { + public void fidelityWarning(@Nullable String tag, String message, + Throwable throwable, Object data) { + System.out.println("FidelityWarning " + tag + ": " + message); if (throwable != null) { throwable.printStackTrace(); } - failWithMsg(message); + failWithMsg(message == null ? "" : message); } @Override @@ -458,11 +461,11 @@ public class Main { if (sLogger == null) { sLogger = new ILogger() { @Override - public void error(Throwable t, String msgFormat, Object... args) { + public void error(Throwable t, @Nullable String msgFormat, Object... args) { if (t != null) { t.printStackTrace(); } - failWithMsg(msgFormat, args); + failWithMsg(msgFormat == null ? "" : msgFormat, args); } @Override diff --git a/tools/layoutlib/create/create.iml b/tools/layoutlib/create/create.iml index b7e8eb3..9b18e73 100644 --- a/tools/layoutlib/create/create.iml +++ b/tools/layoutlib/create/create.iml @@ -11,8 +11,17 @@ </content> <orderEntry type="inheritedJdk" /> <orderEntry type="sourceFolder" forTests="false" /> - <orderEntry type="library" name="asm-4.0" level="project" /> + <orderEntry type="module-library"> + <library name="asm-4.0"> + <CLASSES> + <root url="jar://$MODULE_DIR$/../../../../../prebuilts/misc/common/asm/asm-4.0.jar!/" /> + </CLASSES> + <JAVADOC /> + <SOURCES> + <root url="jar://$MODULE_DIR$/../../../../../prebuilts/misc/common/asm/src.zip!/" /> + </SOURCES> + </library> + </orderEntry> <orderEntry type="library" scope="TEST" name="JUnit4" level="application" /> </component> -</module> - +</module>
\ No newline at end of file diff --git a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/AsmAnalyzer.java b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/AsmAnalyzer.java index d37a93e..c8b2b84 100644 --- a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/AsmAnalyzer.java +++ b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/AsmAnalyzer.java @@ -112,8 +112,6 @@ public class AsmAnalyzer { mGen.setDeps(deps); mGen.setCopyFiles(filesFound); mGen.setRewriteMethodCallClasses(mReplaceMethodCallClasses); - // hack hack hack - mGen.mSimpleMonthViewReader = zipClasses.get(AsmGenerator.SIMPLE_MONTH_VIEW); } } diff --git a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/AsmGenerator.java b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/AsmGenerator.java index 2bfceb4..8f0ad01 100644 --- a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/AsmGenerator.java +++ b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/AsmGenerator.java @@ -77,9 +77,8 @@ public class AsmGenerator { /** Methods to inject. FQCN of class in which method should be injected => runnable that does * the injection. */ private final Map<String, ICreateInfo.InjectMethodRunnable> mInjectedMethodsMap; - // A hack to do some modifications to simple month view. - static final String SIMPLE_MONTH_VIEW = "android/widget/SimpleMonthView"; - ClassReader mSimpleMonthViewReader; + /** A map { FQCN => set { field names } } which should be promoted to public visibility */ + private final Map<String, Set<String>> mPromotedFields; /** * Creates a new generator that can generate the output JAR with the stubbed classes. @@ -112,20 +111,8 @@ public class AsmGenerator { // Create the map/set of methods to change to delegates mDelegateMethods = new HashMap<String, Set<String>>(); - for (String signature : createInfo.getDelegateMethods()) { - int pos = signature.indexOf('#'); - if (pos <= 0 || pos >= signature.length() - 1) { - continue; - } - String className = binaryToInternalClassName(signature.substring(0, pos)); - String methodName = signature.substring(pos + 1); - Set<String> methods = mDelegateMethods.get(className); - if (methods == null) { - methods = new HashSet<String>(); - mDelegateMethods.put(className, methods); - } - methods.add(methodName); - } + addToMap(createInfo.getDelegateMethods(), mDelegateMethods); + for (String className : createInfo.getDelegateClassNatives()) { className = binaryToInternalClassName(className); Set<String> methods = mDelegateMethods.get(className); @@ -190,10 +177,34 @@ public class AsmGenerator { returnTypes.add(binaryToInternalClassName(className)); } + mPromotedFields = new HashMap<String, Set<String>>(); + addToMap(createInfo.getPromotedFields(), mPromotedFields); + mInjectedMethodsMap = createInfo.getInjectedMethodsMap(); } /** + * For each value in the array, split the value on '#' and add the parts to the map as key + * and value. + */ + private void addToMap(String[] entries, Map<String, Set<String>> map) { + for (String entry : entries) { + int pos = entry.indexOf('#'); + if (pos <= 0 || pos >= entry.length() - 1) { + return; + } + String className = binaryToInternalClassName(entry.substring(0, pos)); + String methodOrFieldName = entry.substring(pos + 1); + Set<String> set = map.get(className); + if (set == null) { + set = new HashSet<String>(); + map.put(className, set); + } + set.add(methodOrFieldName); + } + } + + /** * Returns the list of classes that have not been renamed yet. * <p/> * The names are "internal class names" rather than FQCN, i.e. they use "/" instead "." @@ -207,7 +218,7 @@ public class AsmGenerator { * Utility that returns the internal ASM class name from a fully qualified binary class * name. E.g. it returns android/view/View from android.view.View. */ - static String binaryToInternalClassName(String className) { + String binaryToInternalClassName(String className) { if (className == null) { return null; } else { @@ -383,10 +394,10 @@ public class AsmGenerator { } } - if (SIMPLE_MONTH_VIEW.equals(className)) { - cv = new SimpleMonthViewAdapter(cv); + Set<String> promoteFields = mPromotedFields.get(className); + if (promoteFields != null && !promoteFields.isEmpty()) { + cv = new PromoteFieldClassAdapter(cv, promoteFields); } - cr.accept(cv, 0); return cw.toByteArray(); } diff --git a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java index 33fa679..484240f 100644 --- a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java +++ b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java @@ -120,6 +120,11 @@ public final class CreateInfo implements ICreateInfo { } @Override + public String[] getPromotedFields() { + return PROMOTED_FIELDS; + } + + @Override public Map<String, InjectMethodRunnable> getInjectedMethodsMap() { return INJECTED_METHODS; } @@ -167,12 +172,14 @@ public final class CreateInfo implements ICreateInfo { "android.os.HandlerThread#run", "android.preference.Preference#getView", "android.text.format.DateFormat#is24HourFormat", + "android.text.Hyphenator#getSystemHyphenatorLocation", "android.util.Xml#newPullParser", "android.view.Choreographer#getRefreshRate", "android.view.Display#updateDisplayInfoLocked", "android.view.Display#getWindowManager", "android.view.LayoutInflater#rInflate", "android.view.LayoutInflater#parseInclude", + "android.view.View#getWindowToken", "android.view.View#isInEditMode", "android.view.ViewRootImpl#isInTouchMode", "android.view.WindowManagerGlobal#getWindowManagerService", @@ -183,6 +190,8 @@ public final class CreateInfo implements ICreateInfo { "android.view.RenderNode#nSetElevation", "android.view.RenderNode#nGetElevation", "android.view.ViewGroup#drawChild", + "android.widget.SimpleMonthView#getTitle", + "android.widget.SimpleMonthView#getDayOfWeekLabel", "android.widget.TimePickerClockDelegate#getAmOrPmKeyCode", "com.android.internal.view.menu.MenuBuilder#createNewMenuItem", "com.android.internal.util.XmlUtils#convertValueToInt", @@ -242,7 +251,6 @@ public final class CreateInfo implements ICreateInfo { "android.text.AndroidBidi", "android.text.StaticLayout", "android.view.Display", - "libcore.icu.DateIntervalFormat", "libcore.icu.ICU", }; @@ -265,7 +273,6 @@ public final class CreateInfo implements ICreateInfo { "android.view.SurfaceView", "android.view._Original_SurfaceView", "android.view.accessibility.AccessibilityManager", "android.view.accessibility._Original_AccessibilityManager", "android.webkit.WebView", "android.webkit._Original_WebView", - "com.android.internal.policy.PolicyManager", "com.android.internal.policy._Original_PolicyManager", }; /** @@ -289,6 +296,12 @@ public final class CreateInfo implements ICreateInfo { "org.kxml2.io.KXmlParser" }; + private final static String[] PROMOTED_FIELDS = new String[] { + "android.widget.SimpleMonthView#mTitle", + "android.widget.SimpleMonthView#mCalendar", + "android.widget.SimpleMonthView#mDayOfWeekLabelCalendar" + }; + /** * List of classes for which the methods returning them should be deleted. * The array contains a list of null terminated section starting with the name of the class diff --git a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/ICreateInfo.java b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/ICreateInfo.java index 54b1fe6..6c62423 100644 --- a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/ICreateInfo.java +++ b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/ICreateInfo.java @@ -78,6 +78,13 @@ public interface ICreateInfo { Set<String> getExcludedClasses(); /** + * Returns a list of fields which should be promoted to public visibility. The array values + * are in the form of the binary FQCN of the class containing the field and the field name + * separated by a '#'. + */ + String[] getPromotedFields(); + + /** * Returns a map from binary FQCN className to {@link InjectMethodRunnable} which will be * called to inject methods into a class. * Can be empty but must not be null. diff --git a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/Main.java b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/Main.java index 97716fd..383168f 100644 --- a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/Main.java +++ b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/Main.java @@ -116,12 +116,15 @@ public class Main { "android.app.DatePickerDialog", // b.android.com/28318 "android.app.TimePickerDialog", // b.android.com/61515 "com.android.internal.view.menu.ActionMenu", + "android.icu.**", // needed by LayoutLib "android.annotation.NonNull", // annotations "android.annotation.Nullable", // annotations + "com.android.internal.transition.EpicenterTranslateClipReveal", }, excludeClasses, new String[] { "com/android/i18n/phonenumbers/data/*", + "android/icu/impl/data/**" }); aa.analyze(); agen.generate(); diff --git a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/PromoteFieldClassAdapter.java b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/PromoteFieldClassAdapter.java new file mode 100644 index 0000000..e4b70da --- /dev/null +++ b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/PromoteFieldClassAdapter.java @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.tools.layoutlib.create; + +import org.objectweb.asm.ClassVisitor; +import org.objectweb.asm.FieldVisitor; + +import java.util.Set; + +import static org.objectweb.asm.Opcodes.ACC_PRIVATE; +import static org.objectweb.asm.Opcodes.ACC_PROTECTED; +import static org.objectweb.asm.Opcodes.ACC_PUBLIC; +import static org.objectweb.asm.Opcodes.ASM4; + +/** + * Promotes given fields to public visibility. + */ +public class PromoteFieldClassAdapter extends ClassVisitor { + + private final Set<String> mFieldNames; + private static final int ACC_NOT_PUBLIC = ~(ACC_PRIVATE | ACC_PROTECTED); + + public PromoteFieldClassAdapter(ClassVisitor cv, Set<String> fieldNames) { + super(ASM4, cv); + mFieldNames = fieldNames; + } + + @Override + public FieldVisitor visitField(int access, String name, String desc, String signature, + Object value) { + if (mFieldNames.contains(name)) { + if ((access & ACC_PUBLIC) == 0) { + access = (access & ACC_NOT_PUBLIC) | ACC_PUBLIC; + } + } + return super.visitField(access, name, desc, signature, value); + } +} diff --git a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/ReplaceMethodCallsAdapter.java b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/ReplaceMethodCallsAdapter.java index 4369148..0b85c48 100644 --- a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/ReplaceMethodCallsAdapter.java +++ b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/ReplaceMethodCallsAdapter.java @@ -93,18 +93,22 @@ public class ReplaceMethodCallsAdapter extends ClassVisitor { } }); - // Case 3: java.util.Locale.adjustLanguageCode() or java.util.Locale.forLanguageTag() + // Case 3: java.util.Locale.adjustLanguageCode() or java.util.Locale.forLanguageTag() or + // java.util.Locale.getDefault() METHOD_REPLACERS.add(new MethodReplacer() { private final String STRING_TO_STRING = Type.getMethodDescriptor(STRING, STRING); private final String STRING_TO_LOCALE = Type.getMethodDescriptor( Type.getType(Locale.class), STRING); + private final String VOID_TO_LOCALE = + Type.getMethodDescriptor(Type.getType(Locale.class)); @Override public boolean isNeeded(String owner, String name, String desc, String sourceClass) { return JAVA_LOCALE_CLASS.equals(owner) && ("adjustLanguageCode".equals(name) && desc.equals(STRING_TO_STRING) || - "forLanguageTag".equals(name) && desc.equals(STRING_TO_LOCALE)); + "forLanguageTag".equals(name) && desc.equals(STRING_TO_LOCALE) || + "getDefault".equals(name) && desc.equals(VOID_TO_LOCALE)); } @Override diff --git a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/SimpleMonthViewAdapter.java b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/SimpleMonthViewAdapter.java deleted file mode 100644 index 49fd456..0000000 --- a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/SimpleMonthViewAdapter.java +++ /dev/null @@ -1,90 +0,0 @@ -/* - * Copyright (C) 2015 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.tools.layoutlib.create; - -import org.objectweb.asm.ClassVisitor; -import org.objectweb.asm.FieldVisitor; -import org.objectweb.asm.MethodVisitor; -import org.objectweb.asm.Opcodes; -import org.objectweb.asm.Type; - -import java.text.SimpleDateFormat; - -import static com.android.tools.layoutlib.create.AsmGenerator.binaryToInternalClassName; - -/** - * A very ugly hack to transform all references to {@link java.text.SimpleDateFormat} in {@code - * android.widget.SimpleMonthView} to {@code com.ibm.icu.text.SimpleDateFormat}. - */ -public class SimpleMonthViewAdapter extends ClassVisitor { - - private static final String JAVA_SDF_DESC = Type.getDescriptor(SimpleDateFormat.class); - private static final String JAVA_SDF_INTERNAL_NAME = - binaryToInternalClassName(SimpleDateFormat.class.getName()); - private static final String ICU_SDF_INTERNAL_NAME = "com/ibm/icu/text/SimpleDateFormat"; - - public SimpleMonthViewAdapter(ClassVisitor cv) { - super(Opcodes.ASM4, cv); - } - - @Override - public FieldVisitor visitField(int access, String name, String desc, String signature, - Object value) { - if (JAVA_SDF_DESC.equals(desc)) { - desc = "L" + ICU_SDF_INTERNAL_NAME + ";"; - } - return super.visitField(access, name, desc, signature, value); - } - - @Override - public MethodVisitor visitMethod(int access, String name, String desc, String signature, - String[] exceptions) { - return new MyMethodVisitor(super.visitMethod(access, name, desc, signature, exceptions)); - } - - private static class MyMethodVisitor extends MethodVisitor { - public MyMethodVisitor(MethodVisitor mv) { - super(Opcodes.ASM4, mv); - } - - @Override - public void visitTypeInsn(int opcode, String type) { - if (JAVA_SDF_INTERNAL_NAME.equals(type) && opcode == Opcodes.NEW) { - type = ICU_SDF_INTERNAL_NAME; - } - super.visitTypeInsn(opcode, type); - } - - @Override - public void visitMethodInsn(int opcode, String owner, String name, String desc) { - if (JAVA_SDF_INTERNAL_NAME.equals(owner)) { - owner = ICU_SDF_INTERNAL_NAME; - } - super.visitMethodInsn(opcode, owner, name, desc); - } - - @Override - public void visitFieldInsn(int opcode, String owner, String name, String desc) { - if (JAVA_SDF_DESC.equals(desc) && - (opcode == Opcodes.PUTFIELD || opcode == Opcodes.GETFIELD) && - name.equals("mDayFormatter")) { - desc = "L" + ICU_SDF_INTERNAL_NAME + ";"; - } - super.visitFieldInsn(opcode, owner, name, desc); - } - } -} diff --git a/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/AsmGeneratorTest.java b/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/AsmGeneratorTest.java index 2c21470..8a2235b 100644 --- a/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/AsmGeneratorTest.java +++ b/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/AsmGeneratorTest.java @@ -138,6 +138,11 @@ public class AsmGeneratorTest { } @Override + public String[] getPromotedFields() { + return new String[0]; + } + + @Override public Map<String, InjectMethodRunnable> getInjectedMethodsMap() { return new HashMap<String, InjectMethodRunnable>(0); } @@ -213,6 +218,11 @@ public class AsmGeneratorTest { } @Override + public String[] getPromotedFields() { + return new String[0]; + } + + @Override public Map<String, InjectMethodRunnable> getInjectedMethodsMap() { return new HashMap<String, InjectMethodRunnable>(0); } @@ -296,6 +306,11 @@ public class AsmGeneratorTest { } @Override + public String[] getPromotedFields() { + return new String[0]; + } + + @Override public Map<String, InjectMethodRunnable> getInjectedMethodsMap() { return new HashMap<String, InjectMethodRunnable>(0); } @@ -374,6 +389,11 @@ public class AsmGeneratorTest { } @Override + public String[] getPromotedFields() { + return new String[0]; + } + + @Override public Map<String, InjectMethodRunnable> getInjectedMethodsMap() { HashMap<String, InjectMethodRunnable> map = new HashMap<String, InjectMethodRunnable>(1); diff --git a/tools/layoutlib/rename_font/build_font.py b/tools/layoutlib/rename_font/build_font.py index c747d92..9713a4c 100755 --- a/tools/layoutlib/rename_font/build_font.py +++ b/tools/layoutlib/rename_font/build_font.py @@ -209,15 +209,18 @@ def ends_in_regular(string): def get_version(string): - # The string must begin with 'Version n.nn ' - # to extract n.nn, we return the second entry in the split strings. string = string.strip() - if not string.startswith('Version '): - raise InvalidFontException('mal-formed font version') - return sanitize(string.split()[1]) + # The spec says that the version string should start with "Version ". But not + # all fonts do. So, we return the complete string if it doesn't start with + # the prefix, else we return the rest of the string after sanitizing it. + prefix = 'Version ' + if string.startswith(prefix): + string = string[len(prefix):] + return sanitize(string) def sanitize(string): + """ Remove non-standard chars. """ return re.sub(r'[^\w-]+', '', string) if __name__ == '__main__': diff --git a/tools/layoutlib/rename_font/build_font_single.py b/tools/layoutlib/rename_font/build_font_single.py index 5f7dad9..4245cdc 100755 --- a/tools/layoutlib/rename_font/build_font_single.py +++ b/tools/layoutlib/rename_font/build_font_single.py @@ -193,15 +193,18 @@ def ends_in_regular(string): def get_version(string): - # The string must begin with 'Version n.nn ' - # to extract n.nn, we return the second entry in the split strings. string = string.strip() - if not string.startswith('Version '): - raise InvalidFontException('mal-formed font version') - return sanitize(string.split()[1]) + # The spec says that the version string should start with "Version ". But not + # all fonts do. So, we return the complete string if it doesn't start with + # the prefix, else we return the rest of the string after sanitizing it. + prefix = 'Version ' + if string.startswith(prefix): + string = string[len(prefix):] + return sanitize(string) def sanitize(string): + """ Remove non-standard chars. """ return re.sub(r'[^\w-]+', '', string) if __name__ == '__main__': |