Add hidden API for whether permission review mode is on. am: 7b89a7b1f7 am: 7c230c6f3c
am: dd91db5515
Change-Id: I936449f717e3c2919622181c9068ec8a48b452ad
diff --git a/.gitignore b/.gitignore
index eb52b64..819103d 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,4 @@
bin
/.idea/workspace.xml
/out
+/bridge/out
diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml
index 3681f2a..74fa549 100644
--- a/.idea/inspectionProfiles/Project_Default.xml
+++ b/.idea/inspectionProfiles/Project_Default.xml
@@ -11,7 +11,6 @@
<option name="loggerClassName" value="org.apache.log4j.Logger,org.slf4j.LoggerFactory,org.apache.commons.logging.LogFactory,java.util.logging.Logger" />
<option name="loggerFactoryMethodName" value="getLogger,getLogger,getLog,getLogger" />
</inspection_tool>
- <inspection_tool class="ToArrayCallWithZeroLengthArrayArgument" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="WeakerAccess" enabled="true" level="WARNING" enabled_by_default="true">
<option name="SUGGEST_PACKAGE_LOCAL_FOR_MEMBERS" value="false" />
<option name="SUGGEST_PACKAGE_LOCAL_FOR_TOP_CLASSES" value="false" />
diff --git a/.idea/libraries/junit.xml b/.idea/libraries/junit.xml
index c889f5f..ba46ebf 100644
--- a/.idea/libraries/junit.xml
+++ b/.idea/libraries/junit.xml
@@ -1,7 +1,7 @@
<component name="libraryTable">
<library name="junit">
<CLASSES>
- <root url="jar://$PROJECT_DIR$/../../../../out/host/common/obj/JAVA_LIBRARIES/junit_intermediates/javalib.jar!/" />
+ <root url="jar://$PROJECT_DIR$/../../../../out/host/common/obj/JAVA_LIBRARIES/junit-host_intermediates/javalib.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES>
diff --git a/.idea/modules.xml b/.idea/modules.xml
index 9bdc381..6ffc1cc 100644
--- a/.idea/modules.xml
+++ b/.idea/modules.xml
@@ -4,6 +4,7 @@
<modules>
<module fileurl="file://$PROJECT_DIR$/bridge/bridge.iml" filepath="$PROJECT_DIR$/bridge/bridge.iml" />
<module fileurl="file://$PROJECT_DIR$/create/create.iml" filepath="$PROJECT_DIR$/create/create.iml" />
+ <module fileurl="file://$PROJECT_DIR$/legacy/legacy.iml" filepath="$PROJECT_DIR$/legacy/legacy.iml" />
<module fileurl="file://$PROJECT_DIR$/studio-custom-widgets/studio-android-widgets.iml" filepath="$PROJECT_DIR$/studio-custom-widgets/studio-android-widgets.iml" />
</modules>
</component>
diff --git a/bridge/bridge.iml b/bridge/bridge.iml
index 57d08cb..fbaed52 100644
--- a/bridge/bridge.iml
+++ b/bridge/bridge.iml
@@ -7,6 +7,7 @@
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/tests/res" type="java-test-resource" />
<sourceFolder url="file://$MODULE_DIR$/tests/src" isTestSource="true" />
+ <sourceFolder url="file://$MODULE_DIR$/tests/res/testApp/MyApplication/src/main/myapplication.widgets" isTestSource="true" />
<excludeFolder url="file://$MODULE_DIR$/.settings" />
<excludeFolder url="file://$MODULE_DIR$/bin" />
<excludeFolder url="file://$MODULE_DIR$/tests/res/testApp/MyApplication/.gradle" />
diff --git a/bridge/src/android/animation/AnimationThread.java b/bridge/src/android/animation/AnimationThread.java
index b10ec9f..ce2aec7 100644
--- a/bridge/src/android/animation/AnimationThread.java
+++ b/bridge/src/android/animation/AnimationThread.java
@@ -25,7 +25,6 @@
import android.os.Handler;
import android.os.Handler_Delegate;
-import android.os.Handler_Delegate.IHandlerCallback;
import android.os.Message;
import java.util.PriorityQueue;
diff --git a/bridge/src/android/content/res/AssetManager_Delegate.java b/bridge/src/android/content/res/AssetManager_Delegate.java
index e0d3b8c..b4d5288 100644
--- a/bridge/src/android/content/res/AssetManager_Delegate.java
+++ b/bridge/src/android/content/res/AssetManager_Delegate.java
@@ -18,6 +18,8 @@
import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
+import android.util.SparseArray;
+
/**
* Delegate used to provide implementation of a select few native methods of {@link AssetManager}
* <p/>
@@ -38,4 +40,8 @@
Resources_Theme_Delegate.getDelegateManager().removeJavaReferenceFor(theme);
}
+ @LayoutlibDelegate
+ /*package*/ static SparseArray<String> getAssignedPackageIdentifiers(AssetManager manager) {
+ return new SparseArray<>();
+ }
}
diff --git a/bridge/src/android/content/res/BridgeTypedArray.java b/bridge/src/android/content/res/BridgeTypedArray.java
index d0e431a..35cf903 100644
--- a/bridge/src/android/content/res/BridgeTypedArray.java
+++ b/bridge/src/android/content/res/BridgeTypedArray.java
@@ -80,7 +80,7 @@
public BridgeTypedArray(Resources resources, BridgeContext context, int len,
boolean platformFile) {
- super(resources, null, null, 0);
+ super(resources);
mBridgeResources = resources;
mContext = context;
mPlatformFile = platformFile;
@@ -584,6 +584,7 @@
if (value == null) {
return defValue;
}
+ value = value.trim();
// if the value is just an integer, return it.
try {
@@ -595,6 +596,11 @@
// pass
}
+ if (value.startsWith("#")) {
+ // this looks like a color, do not try to parse it
+ return defValue;
+ }
+
// Handle the @id/<name>, @+id/<name> and @android:id/<name>
// We need to return the exact value that was compiled (from the various R classes),
// as these values can be reused internally with calls to findViewById().
@@ -632,7 +638,15 @@
}
}
- // not a direct id valid reference? resolve it
+ // not a direct id valid reference. First check if it's an enum (this is a corner case
+ // for attributes that have a reference|enum type), then fallback to resolve
+ // as an ID without prefix.
+ Integer enumValue = resolveEnumAttribute(index);
+ if (enumValue != null) {
+ return enumValue;
+ }
+
+ // Ok, not an enum, resolve as an ID
Integer idValue;
if (resValue.isFramework()) {
diff --git a/bridge/src/android/content/res/Resources_Delegate.java b/bridge/src/android/content/res/Resources_Delegate.java
index ea320c7..e0f8e1c 100644
--- a/bridge/src/android/content/res/Resources_Delegate.java
+++ b/bridge/src/android/content/res/Resources_Delegate.java
@@ -21,6 +21,7 @@
import com.android.ide.common.rendering.api.DensityBasedResourceValue;
import com.android.ide.common.rendering.api.LayoutLog;
import com.android.ide.common.rendering.api.LayoutlibCallback;
+import com.android.ide.common.rendering.api.PluralsResourceValue;
import com.android.ide.common.rendering.api.RenderResources;
import com.android.ide.common.rendering.api.ResourceValue;
import com.android.layoutlib.bridge.Bridge;
@@ -43,8 +44,10 @@
import android.content.res.Resources.NotFoundException;
import android.content.res.Resources.Theme;
import android.graphics.drawable.Drawable;
+import android.icu.text.PluralRules;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
+import android.util.LruCache;
import android.util.TypedValue;
import android.view.ViewGroup.LayoutParams;
@@ -58,6 +61,9 @@
public class Resources_Delegate {
private static boolean[] mPlatformResourceFlag = new boolean[1];
+ // TODO: This cache is cleared every time a render session is disposed. Look into making this
+ // more long lived.
+ private static LruCache<String, Drawable.ConstantState> sDrawableCache = new LruCache<>(50);
public static Resources initSystem(BridgeContext context,
AssetManager assets,
@@ -75,6 +81,7 @@
* would prevent us from unloading the library.
*/
public static void disposeSystem() {
+ sDrawableCache.evictAll();
Resources.mSystem.mContext = null;
Resources.mSystem.mLayoutlibCallback = null;
Resources.mSystem = null;
@@ -137,9 +144,23 @@
@LayoutlibDelegate
static Drawable getDrawable(Resources resources, int id, Theme theme) {
Pair<String, ResourceValue> value = getResourceValue(resources, id, mPlatformResourceFlag);
-
if (value != null) {
- return ResourceHelper.getDrawable(value.getSecond(), resources.mContext, theme);
+ String key = value.getSecond().getValue();
+
+ Drawable.ConstantState constantState = key != null ? sDrawableCache.get(key) : null;
+ Drawable drawable;
+ if (constantState != null) {
+ drawable = constantState.newDrawable(resources, theme);
+ } else {
+ drawable =
+ ResourceHelper.getDrawable(value.getSecond(), resources.mContext, theme);
+
+ if (key != null) {
+ sDrawableCache.put(key, drawable.getConstantState());
+ }
+ }
+
+ return drawable;
}
// id was not found or not resolved. Throw a NotFoundException.
@@ -386,9 +407,6 @@
rv = resources.mContext.getRenderResources().resolveResValue(rv);
if (rv != null) {
return rv.getValue();
- } else {
- Bridge.getLog().error(LayoutLog.TAG_RESOURCES_RESOLVE,
- "Unable to resolve resource " + ref, null);
}
}
// Not a reference.
@@ -719,6 +737,48 @@
}
@LayoutlibDelegate
+ static String getQuantityString(Resources resources, int id, int quantity) throws
+ NotFoundException {
+ Pair<String, ResourceValue> value = getResourceValue(resources, id, mPlatformResourceFlag);
+
+ if (value != null) {
+ if (value.getSecond() instanceof PluralsResourceValue) {
+ PluralsResourceValue pluralsResourceValue = (PluralsResourceValue) value.getSecond();
+ PluralRules pluralRules = PluralRules.forLocale(resources.getConfiguration().getLocales()
+ .get(0));
+ String strValue = pluralsResourceValue.getValue(pluralRules.select(quantity));
+ if (strValue == null) {
+ strValue = pluralsResourceValue.getValue(PluralRules.KEYWORD_OTHER);
+ }
+
+ return strValue;
+ }
+ else {
+ return value.getSecond().getValue();
+ }
+ }
+
+ // id was not found or not resolved. Throw a NotFoundException.
+ throwException(resources, id);
+
+ // this is not used since the method above always throws
+ return null;
+ }
+
+ @LayoutlibDelegate
+ static String getQuantityString(Resources resources, int id, int quantity, Object... formatArgs)
+ throws NotFoundException {
+ String raw = getQuantityString(resources, id, quantity);
+ return String.format(resources.getConfiguration().getLocales().get(0), raw, formatArgs);
+ }
+
+ @LayoutlibDelegate
+ static CharSequence getQuantityText(Resources resources, int id, int quantity) throws
+ NotFoundException {
+ return getQuantityString(resources, id, quantity);
+ }
+
+ @LayoutlibDelegate
static void getValue(Resources resources, int id, TypedValue outValue, boolean resolveRefs)
throws NotFoundException {
Pair<String, ResourceValue> value = getResourceValue(resources, id, mPlatformResourceFlag);
diff --git a/bridge/src/android/graphics/BaseCanvas_Delegate.java b/bridge/src/android/graphics/BaseCanvas_Delegate.java
new file mode 100644
index 0000000..df85806
--- /dev/null
+++ b/bridge/src/android/graphics/BaseCanvas_Delegate.java
@@ -0,0 +1,727 @@
+/*
+ * Copyright (C) 2016 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.graphics;
+
+import com.android.ide.common.rendering.api.LayoutLog;
+import com.android.layoutlib.bridge.Bridge;
+import com.android.layoutlib.bridge.impl.DelegateManager;
+import com.android.layoutlib.bridge.impl.GcSnapshot;
+import com.android.layoutlib.bridge.impl.PorterDuffUtility;
+import com.android.ninepatch.NinePatchChunk;
+import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
+
+import android.text.TextUtils;
+
+import java.awt.*;
+import java.awt.geom.AffineTransform;
+import java.awt.geom.Arc2D;
+import java.awt.geom.Rectangle2D;
+import java.awt.image.BufferedImage;
+
+public class BaseCanvas_Delegate {
+ // ---- delegate manager ----
+ protected static DelegateManager<BaseCanvas_Delegate> sManager =
+ new DelegateManager<>(BaseCanvas_Delegate.class);
+
+ // ---- delegate helper data ----
+ private final static boolean[] sBoolOut = new boolean[1];
+
+
+ // ---- delegate data ----
+ protected Bitmap_Delegate mBitmap;
+ protected GcSnapshot mSnapshot;
+
+ // ---- Public Helper methods ----
+
+ protected BaseCanvas_Delegate(Bitmap_Delegate bitmap) {
+ mSnapshot = GcSnapshot.createDefaultSnapshot(mBitmap = bitmap);
+ }
+
+ protected BaseCanvas_Delegate() {
+ mSnapshot = GcSnapshot.createDefaultSnapshot(null /*image*/);
+ }
+
+ /**
+ * Disposes of the {@link Graphics2D} stack.
+ */
+ protected void dispose() {
+ mSnapshot.dispose();
+ }
+
+ /**
+ * Returns the current {@link Graphics2D} used to draw.
+ */
+ public GcSnapshot getSnapshot() {
+ return mSnapshot;
+ }
+
+ // ---- native methods ----
+
+ @LayoutlibDelegate
+ /*package*/ static void nDrawBitmap(long nativeCanvas, Bitmap bitmap, float left, float top,
+ long nativePaintOrZero, int canvasDensity, int screenDensity, int bitmapDensity) {
+ // get the delegate from the native int.
+ Bitmap_Delegate bitmapDelegate = Bitmap_Delegate.getDelegate(bitmap);
+ if (bitmapDelegate == null) {
+ return;
+ }
+
+ BufferedImage image = bitmapDelegate.getImage();
+ float right = left + image.getWidth();
+ float bottom = top + image.getHeight();
+
+ drawBitmap(nativeCanvas, bitmapDelegate, nativePaintOrZero,
+ 0, 0, image.getWidth(), image.getHeight(),
+ (int)left, (int)top, (int)right, (int)bottom);
+ }
+
+ @LayoutlibDelegate
+ /*package*/ static void nDrawBitmap(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) {
+ // get the delegate from the native int.
+ Bitmap_Delegate bitmapDelegate = Bitmap_Delegate.getDelegate(bitmap);
+ if (bitmapDelegate == null) {
+ return;
+ }
+
+ drawBitmap(nativeCanvas, bitmapDelegate, nativePaintOrZero, (int) srcLeft, (int) srcTop,
+ (int) srcRight, (int) srcBottom, (int) dstLeft, (int) dstTop, (int) dstRight,
+ (int) dstBottom);
+ }
+
+ @LayoutlibDelegate
+ /*package*/ static void nDrawBitmap(long nativeCanvas, int[] colors, int offset, int stride,
+ final float x, final float y, int width, int height, boolean hasAlpha,
+ long nativePaintOrZero) {
+ // create a temp BufferedImage containing the content.
+ final BufferedImage image = new BufferedImage(width, height,
+ hasAlpha ? BufferedImage.TYPE_INT_ARGB : BufferedImage.TYPE_INT_RGB);
+ image.setRGB(0, 0, width, height, colors, offset, stride);
+
+ draw(nativeCanvas, nativePaintOrZero, true /*compositeOnly*/, false /*forceSrcMode*/,
+ (graphics, paint) -> {
+ if (paint != null && paint.isFilterBitmap()) {
+ graphics.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
+ RenderingHints.VALUE_INTERPOLATION_BILINEAR);
+ }
+
+ graphics.drawImage(image, (int) x, (int) y, null);
+ });
+ }
+
+ @LayoutlibDelegate
+ /*package*/ static void nDrawColor(long nativeCanvas, final int color, final int mode) {
+ // get the delegate from the native int.
+ BaseCanvas_Delegate canvasDelegate = sManager.getDelegate(nativeCanvas);
+ if (canvasDelegate == null) {
+ return;
+ }
+
+ final int w = canvasDelegate.mBitmap.getImage().getWidth();
+ final int h = canvasDelegate.mBitmap.getImage().getHeight();
+ draw(nativeCanvas, (graphics, paint) -> {
+ // reset its transform just in case
+ graphics.setTransform(new AffineTransform());
+
+ // set the color
+ graphics.setColor(new java.awt.Color(color, true /*alpha*/));
+
+ Composite composite = PorterDuffUtility.getComposite(
+ PorterDuffUtility.getPorterDuffMode(mode), 0xFF);
+ if (composite != null) {
+ graphics.setComposite(composite);
+ }
+
+ graphics.fillRect(0, 0, w, h);
+ });
+ }
+
+ @LayoutlibDelegate
+ /*package*/ static void nDrawPaint(long nativeCanvas, long paint) {
+ // FIXME
+ Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
+ "Canvas.drawPaint is not supported.", null, null /*data*/);
+ }
+
+ @LayoutlibDelegate
+ /*package*/ static void nDrawPoint(long nativeCanvas, float x, float y,
+ long nativePaint) {
+ // FIXME
+ Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
+ "Canvas.drawPoint is not supported.", null, null /*data*/);
+ }
+
+ @LayoutlibDelegate
+ /*package*/ static void nDrawPoints(long nativeCanvas, float[] pts, int offset, int count,
+ long nativePaint) {
+ // FIXME
+ Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
+ "Canvas.drawPoint is not supported.", null, null /*data*/);
+ }
+
+ @LayoutlibDelegate
+ /*package*/ static void nDrawLine(long nativeCanvas,
+ final float startX, final float startY, final float stopX, final float stopY,
+ long paint) {
+ draw(nativeCanvas, paint, false /*compositeOnly*/, false /*forceSrcMode*/,
+ (graphics, paintDelegate) -> graphics.drawLine((int)startX, (int)startY, (int)stopX, (int)stopY));
+ }
+
+ @LayoutlibDelegate
+ /*package*/ static void nDrawLines(long nativeCanvas,
+ final float[] pts, final int offset, final int count,
+ long nativePaint) {
+ draw(nativeCanvas, nativePaint, false /*compositeOnly*/,
+ false /*forceSrcMode*/, (graphics, paintDelegate) -> {
+ for (int i = 0; i < count; i += 4) {
+ graphics.drawLine((int) pts[i + offset], (int) pts[i + offset + 1],
+ (int) pts[i + offset + 2], (int) pts[i + offset + 3]);
+ }
+ });
+ }
+
+ @LayoutlibDelegate
+ /*package*/ static void nDrawRect(long nativeCanvas,
+ final float left, final float top, final float right, final float bottom, long paint) {
+
+ draw(nativeCanvas, paint, false /*compositeOnly*/, false /*forceSrcMode*/,
+ (graphics, paintDelegate) -> {
+ int style = paintDelegate.getStyle();
+
+ // draw
+ if (style == Paint.Style.FILL.nativeInt ||
+ style == Paint.Style.FILL_AND_STROKE.nativeInt) {
+ graphics.fillRect((int)left, (int)top,
+ (int)(right-left), (int)(bottom-top));
+ }
+
+ if (style == Paint.Style.STROKE.nativeInt ||
+ style == Paint.Style.FILL_AND_STROKE.nativeInt) {
+ graphics.drawRect((int)left, (int)top,
+ (int)(right-left), (int)(bottom-top));
+ }
+ });
+ }
+
+ @LayoutlibDelegate
+ /*package*/ static void nDrawOval(long nativeCanvas, final float left,
+ final float top, final float right, final float bottom, long paint) {
+ if (right > left && bottom > top) {
+ draw(nativeCanvas, paint, false /*compositeOnly*/, false /*forceSrcMode*/,
+ (graphics, paintDelegate) -> {
+ int style = paintDelegate.getStyle();
+
+ // draw
+ if (style == Paint.Style.FILL.nativeInt ||
+ style == Paint.Style.FILL_AND_STROKE.nativeInt) {
+ graphics.fillOval((int)left, (int)top,
+ (int)(right - left), (int)(bottom - top));
+ }
+
+ if (style == Paint.Style.STROKE.nativeInt ||
+ style == Paint.Style.FILL_AND_STROKE.nativeInt) {
+ graphics.drawOval((int)left, (int)top,
+ (int)(right - left), (int)(bottom - top));
+ }
+ });
+ }
+ }
+
+ @LayoutlibDelegate
+ /*package*/ static void nDrawCircle(long nativeCanvas,
+ float cx, float cy, float radius, long paint) {
+ nDrawOval(nativeCanvas,
+ cx - radius, cy - radius, cx + radius, cy + radius,
+ paint);
+ }
+
+ @LayoutlibDelegate
+ /*package*/ static void nDrawArc(long nativeCanvas,
+ final float left, final float top, final float right, final float bottom,
+ final float startAngle, final float sweep,
+ final boolean useCenter, long paint) {
+ if (right > left && bottom > top) {
+ draw(nativeCanvas, paint, false /*compositeOnly*/, false /*forceSrcMode*/,
+ (graphics, paintDelegate) -> {
+ int style = paintDelegate.getStyle();
+
+ Arc2D.Float arc = new Arc2D.Float(
+ left, top, right - left, bottom - top,
+ -startAngle, -sweep,
+ useCenter ? Arc2D.PIE : Arc2D.OPEN);
+
+ // draw
+ if (style == Paint.Style.FILL.nativeInt ||
+ style == Paint.Style.FILL_AND_STROKE.nativeInt) {
+ graphics.fill(arc);
+ }
+
+ if (style == Paint.Style.STROKE.nativeInt ||
+ style == Paint.Style.FILL_AND_STROKE.nativeInt) {
+ graphics.draw(arc);
+ }
+ });
+ }
+ }
+
+ @LayoutlibDelegate
+ /*package*/ static void nDrawRoundRect(long nativeCanvas,
+ final float left, final float top, final float right, final float bottom,
+ final float rx, final float ry, long paint) {
+ draw(nativeCanvas, paint, false /*compositeOnly*/, false /*forceSrcMode*/,
+ (graphics, paintDelegate) -> {
+ int style = paintDelegate.getStyle();
+
+ // draw
+ if (style == Paint.Style.FILL.nativeInt ||
+ style == Paint.Style.FILL_AND_STROKE.nativeInt) {
+ graphics.fillRoundRect(
+ (int)left, (int)top,
+ (int)(right - left), (int)(bottom - top),
+ 2 * (int)rx, 2 * (int)ry);
+ }
+
+ if (style == Paint.Style.STROKE.nativeInt ||
+ style == Paint.Style.FILL_AND_STROKE.nativeInt) {
+ graphics.drawRoundRect(
+ (int)left, (int)top,
+ (int)(right - left), (int)(bottom - top),
+ 2 * (int)rx, 2 * (int)ry);
+ }
+ });
+ }
+
+ @LayoutlibDelegate
+ public static void nDrawPath(long nativeCanvas, long path, long paint) {
+ final Path_Delegate pathDelegate = Path_Delegate.getDelegate(path);
+ if (pathDelegate == null) {
+ return;
+ }
+
+ draw(nativeCanvas, paint, false /*compositeOnly*/, false /*forceSrcMode*/,
+ (graphics, paintDelegate) -> {
+ Shape shape = pathDelegate.getJavaShape();
+ Rectangle2D bounds = shape.getBounds2D();
+ if (bounds.isEmpty()) {
+ // Apple JRE 1.6 doesn't like drawing empty shapes.
+ // http://b.android.com/178278
+
+ if (pathDelegate.isEmpty()) {
+ // This means that the path doesn't have any lines or curves so
+ // nothing to draw.
+ return;
+ }
+
+ // The stroke width is not consider for the size of the bounds so,
+ // for example, a horizontal line, would be considered as an empty
+ // rectangle.
+ // If the strokeWidth is not 0, we use it to consider the size of the
+ // path as well.
+ float strokeWidth = paintDelegate.getStrokeWidth();
+ if (strokeWidth <= 0.0f) {
+ return;
+ }
+ bounds.setRect(bounds.getX(), bounds.getY(),
+ Math.max(strokeWidth, bounds.getWidth()),
+ Math.max(strokeWidth, bounds.getHeight()));
+ }
+
+ int style = paintDelegate.getStyle();
+
+ if (style == Paint.Style.FILL.nativeInt ||
+ style == Paint.Style.FILL_AND_STROKE.nativeInt) {
+ graphics.fill(shape);
+ }
+
+ if (style == Paint.Style.STROKE.nativeInt ||
+ style == Paint.Style.FILL_AND_STROKE.nativeInt) {
+ graphics.draw(shape);
+ }
+ });
+ }
+
+ @LayoutlibDelegate
+ /*package*/ static void nDrawRegion(long nativeCanvas, long nativeRegion,
+ long nativePaint) {
+ // FIXME
+ Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
+ "Some canvas paths may not be drawn", null, null);
+ }
+
+ @LayoutlibDelegate
+ /*package*/ static void nDrawNinePatch(long nativeCanvas, long nativeBitmap, long ninePatch,
+ final float dstLeft, final float dstTop, final float dstRight, final float dstBottom,
+ long nativePaintOrZero, final int screenDensity, final int bitmapDensity) {
+
+ // get the delegate from the native int.
+ final Bitmap_Delegate bitmapDelegate = Bitmap_Delegate.getDelegate(nativeBitmap);
+ if (bitmapDelegate == null) {
+ return;
+ }
+
+ byte[] c = NinePatch_Delegate.getChunk(ninePatch);
+ if (c == null) {
+ // not a 9-patch?
+ BufferedImage image = bitmapDelegate.getImage();
+ drawBitmap(nativeCanvas, bitmapDelegate, nativePaintOrZero, 0, 0, image.getWidth(),
+ image.getHeight(), (int) dstLeft, (int) dstTop, (int) dstRight,
+ (int) dstBottom);
+ return;
+ }
+
+ final NinePatchChunk chunkObject = NinePatch_Delegate.getChunk(c);
+ assert chunkObject != null;
+ if (chunkObject == null) {
+ return;
+ }
+
+ Canvas_Delegate canvasDelegate = Canvas_Delegate.getDelegate(nativeCanvas);
+ if (canvasDelegate == null) {
+ return;
+ }
+
+ // this one can be null
+ Paint_Delegate paintDelegate = Paint_Delegate.getDelegate(nativePaintOrZero);
+
+ canvasDelegate.getSnapshot().draw(new GcSnapshot.Drawable() {
+ @Override
+ public void draw(Graphics2D graphics, Paint_Delegate paint) {
+ chunkObject.draw(bitmapDelegate.getImage(), graphics, (int) dstLeft, (int) dstTop,
+ (int) (dstRight - dstLeft), (int) (dstBottom - dstTop), screenDensity,
+ bitmapDensity);
+ }
+ }, paintDelegate, true, false);
+
+ }
+
+ @LayoutlibDelegate
+ /*package*/ static void nDrawBitmapMatrix(long nCanvas, Bitmap bitmap,
+ long nMatrix, long nPaint) {
+ // get the delegate from the native int.
+ BaseCanvas_Delegate canvasDelegate = sManager.getDelegate(nCanvas);
+ if (canvasDelegate == null) {
+ return;
+ }
+
+ // get the delegate from the native int, which can be null
+ Paint_Delegate paintDelegate = Paint_Delegate.getDelegate(nPaint);
+
+ // get the delegate from the native int.
+ Bitmap_Delegate bitmapDelegate = Bitmap_Delegate.getDelegate(bitmap);
+ if (bitmapDelegate == null) {
+ return;
+ }
+
+ final BufferedImage image = getImageToDraw(bitmapDelegate, paintDelegate, sBoolOut);
+
+ Matrix_Delegate matrixDelegate = Matrix_Delegate.getDelegate(nMatrix);
+ if (matrixDelegate == null) {
+ return;
+ }
+
+ final AffineTransform mtx = matrixDelegate.getAffineTransform();
+
+ canvasDelegate.getSnapshot().draw((graphics, paint) -> {
+ if (paint != null && paint.isFilterBitmap()) {
+ graphics.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
+ RenderingHints.VALUE_INTERPOLATION_BILINEAR);
+ }
+
+ //FIXME add support for canvas, screen and bitmap densities.
+ graphics.drawImage(image, mtx, null);
+ }, paintDelegate, true /*compositeOnly*/, false /*forceSrcMode*/);
+ }
+
+ @LayoutlibDelegate
+ /*package*/ static void nDrawBitmapMesh(long nCanvas, Bitmap bitmap,
+ int meshWidth, int meshHeight, float[] verts, int vertOffset, int[] colors,
+ int colorOffset, long nPaint) {
+ // FIXME
+ Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
+ "Canvas.drawBitmapMesh is not supported.", null, null /*data*/);
+ }
+
+ @LayoutlibDelegate
+ /*package*/ static void nDrawVertices(long nCanvas, int mode, int n,
+ float[] verts, int vertOffset,
+ float[] texs, int texOffset,
+ int[] colors, int colorOffset,
+ short[] indices, int indexOffset,
+ int indexCount, long nPaint) {
+ // FIXME
+ Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
+ "Canvas.drawVertices is not supported.", null, null /*data*/);
+ }
+
+ @LayoutlibDelegate
+ /*package*/ static void nDrawText(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 & 1) != 0,
+ paint, typeface);
+ }
+
+ @LayoutlibDelegate
+ /*package*/ static void nDrawText(long nativeCanvas, String text,
+ int start, int end, float x, float y, final int flags, long paint,
+ long typeface) {
+ int count = end - start;
+ char[] buffer = TemporaryBuffer.obtain(count);
+ TextUtils.getChars(text, start, end, buffer, 0);
+
+ nDrawText(nativeCanvas, buffer, 0, count, x, y, flags, paint, typeface);
+ }
+
+ @LayoutlibDelegate
+ /*package*/ static void nDrawTextRun(long nativeCanvas, String text,
+ int start, int end, int contextStart, int contextEnd,
+ float x, float y, boolean isRtl, long paint, long typeface) {
+ int count = end - start;
+ char[] buffer = TemporaryBuffer.obtain(count);
+ TextUtils.getChars(text, start, end, buffer, 0);
+
+ drawText(nativeCanvas, buffer, 0, count, x, y, isRtl, paint, typeface);
+ }
+
+ @LayoutlibDelegate
+ /*package*/ static void nDrawTextRun(long nativeCanvas, char[] text,
+ int start, int count, int contextStart, int contextCount,
+ float x, float y, boolean isRtl, long paint, long typeface) {
+ drawText(nativeCanvas, text, start, count, x, y, isRtl, paint, typeface);
+ }
+
+ @LayoutlibDelegate
+ /*package*/ static void nDrawTextOnPath(long nativeCanvas,
+ char[] text, int index,
+ int count, long path,
+ float hOffset,
+ float vOffset, int bidiFlags,
+ long paint, long typeface) {
+ // FIXME
+ Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
+ "Canvas.drawTextOnPath is not supported.", null, null /*data*/);
+ }
+
+ @LayoutlibDelegate
+ /*package*/ static void nDrawTextOnPath(long nativeCanvas,
+ String text, long path,
+ float hOffset,
+ float vOffset,
+ int bidiFlags, long paint,
+ long typeface) {
+ // FIXME
+ Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
+ "Canvas.drawTextOnPath is not supported.", null, null /*data*/);
+ }
+
+ // ---- Private delegate/helper methods ----
+
+ /**
+ * Executes a {@link GcSnapshot.Drawable} with a given canvas and paint.
+ * <p>Note that the drawable may actually be executed several times if there are
+ * layers involved (see {@link #saveLayer(RectF, Paint_Delegate, int)}.
+ */
+ private static void draw(long nCanvas, long nPaint, boolean compositeOnly, boolean forceSrcMode,
+ GcSnapshot.Drawable drawable) {
+ // get the delegate from the native int.
+ BaseCanvas_Delegate canvasDelegate = sManager.getDelegate(nCanvas);
+ if (canvasDelegate == null) {
+ return;
+ }
+
+ // get the paint which can be null if nPaint is 0;
+ Paint_Delegate paintDelegate = Paint_Delegate.getDelegate(nPaint);
+
+ canvasDelegate.getSnapshot().draw(drawable, paintDelegate, compositeOnly, forceSrcMode);
+ }
+
+ /**
+ * Executes a {@link GcSnapshot.Drawable} with a given canvas. No paint object will be provided
+ * to {@link GcSnapshot.Drawable#draw(Graphics2D, Paint_Delegate)}.
+ * <p>Note that the drawable may actually be executed several times if there are
+ * layers involved (see {@link #saveLayer(RectF, Paint_Delegate, int)}.
+ */
+ private static void draw(long nCanvas, GcSnapshot.Drawable drawable) {
+ // get the delegate from the native int.
+ BaseCanvas_Delegate canvasDelegate = sManager.getDelegate(nCanvas);
+ if (canvasDelegate == null) {
+ return;
+ }
+
+ canvasDelegate.mSnapshot.draw(drawable);
+ }
+
+ private static void drawText(long nativeCanvas, final char[] text, final int index,
+ final int count, final float startX, final float startY, final boolean isRtl,
+ long paint, final long typeface) {
+
+ draw(nativeCanvas, paint, false /*compositeOnly*/, false /*forceSrcMode*/,
+ (graphics, paintDelegate) -> {
+ // WARNING: the logic in this method is similar to Paint_Delegate.measureText.
+ // Any change to this method should be reflected in Paint.measureText
+
+ // assert that the typeface passed is actually the one stored in paint.
+ assert (typeface == paintDelegate.mNativeTypeface);
+
+ // Paint.TextAlign indicates how the text is positioned relative to X.
+ // LEFT is the default and there's nothing to do.
+ float x = startX;
+ int limit = index + count;
+ if (paintDelegate.getTextAlign() != Paint.Align.LEFT.nativeInt) {
+ RectF bounds =
+ paintDelegate.measureText(text, index, count, null, 0, isRtl);
+ float m = bounds.right - bounds.left;
+ if (paintDelegate.getTextAlign() == Paint.Align.CENTER.nativeInt) {
+ x -= m / 2;
+ } else if (paintDelegate.getTextAlign() == Paint.Align.RIGHT.nativeInt) {
+ x -= m;
+ }
+ }
+
+ new BidiRenderer(graphics, paintDelegate, text).setRenderLocation(x,
+ startY).renderText(index, limit, isRtl, null, 0, true);
+ });
+ }
+
+ private static void drawBitmap(long nativeCanvas, Bitmap_Delegate bitmap,
+ long nativePaintOrZero, final int sleft, final int stop, final int sright,
+ final int sbottom, final int dleft, final int dtop, final int dright,
+ final int dbottom) {
+ // get the delegate from the native int.
+ BaseCanvas_Delegate canvasDelegate = sManager.getDelegate(nativeCanvas);
+ if (canvasDelegate == null) {
+ return;
+ }
+
+ // get the paint, which could be null if the int is 0
+ Paint_Delegate paintDelegate = Paint_Delegate.getDelegate(nativePaintOrZero);
+
+ final BufferedImage image = getImageToDraw(bitmap, paintDelegate, sBoolOut);
+
+ draw(nativeCanvas, nativePaintOrZero, true /*compositeOnly*/, sBoolOut[0],
+ (graphics, paint) -> {
+ if (paint != null && paint.isFilterBitmap()) {
+ graphics.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
+ RenderingHints.VALUE_INTERPOLATION_BILINEAR);
+ }
+
+ //FIXME add support for canvas, screen and bitmap densities.
+ graphics.drawImage(image, dleft, dtop, dright, dbottom, sleft, stop, sright,
+ sbottom, null);
+ });
+ }
+
+ /**
+ * Returns a BufferedImage ready for drawing, based on the bitmap and paint delegate.
+ * The image returns, through a 1-size boolean array, whether the drawing code should
+ * use a SRC composite no matter what the paint says.
+ *
+ * @param bitmap the bitmap
+ * @param paint the paint that will be used to draw
+ * @param forceSrcMode whether the composite will have to be SRC
+ * @return the image to draw
+ */
+ private static BufferedImage getImageToDraw(Bitmap_Delegate bitmap, Paint_Delegate paint,
+ boolean[] forceSrcMode) {
+ BufferedImage image = bitmap.getImage();
+ forceSrcMode[0] = false;
+
+ // if the bitmap config is alpha_8, then we erase all color value from it
+ // before drawing it.
+ if (bitmap.getConfig() == Bitmap.Config.ALPHA_8) {
+ fixAlpha8Bitmap(image);
+ } 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:
+ // - override the composite to be SRC. This can only be used if the composite
+ // was going to be SRC or SRC_OVER in the first place
+ // - Create a different bitmap to draw in which all the alpha channel values is set
+ // to 0xFF.
+ if (paint != null) {
+ PorterDuff.Mode mode = PorterDuff.intToMode(paint.getPorterDuffMode());
+
+ forceSrcMode[0] = mode == PorterDuff.Mode.SRC_OVER || mode == PorterDuff.Mode.SRC;
+ }
+
+ // if we can't force SRC mode, then create a temp bitmap of TYPE_RGB
+ if (!forceSrcMode[0]) {
+ image = Bitmap_Delegate.createCopy(image, BufferedImage.TYPE_INT_RGB, 0xFF);
+ }
+ }
+
+ return image;
+ }
+
+ private static void fixAlpha8Bitmap(final BufferedImage image) {
+ int w = image.getWidth();
+ int h = image.getHeight();
+ int[] argb = new int[w * h];
+ image.getRGB(0, 0, image.getWidth(), image.getHeight(), argb, 0, image.getWidth());
+
+ final int length = argb.length;
+ for (int i = 0 ; i < length; i++) {
+ argb[i] &= 0xFF000000;
+ }
+ image.setRGB(0, 0, w, h, argb, 0, w);
+ }
+
+ protected int save(int saveFlags) {
+ // get the current save count
+ int count = mSnapshot.size();
+
+ mSnapshot = mSnapshot.save(saveFlags);
+
+ // return the old save count
+ return count;
+ }
+
+ protected int saveLayerAlpha(RectF rect, int alpha, int saveFlags) {
+ Paint_Delegate paint = new Paint_Delegate();
+ paint.setAlpha(alpha);
+ return saveLayer(rect, paint, saveFlags);
+ }
+
+ protected int saveLayer(RectF rect, Paint_Delegate paint, int saveFlags) {
+ // get the current save count
+ int count = mSnapshot.size();
+
+ mSnapshot = mSnapshot.saveLayer(rect, paint, saveFlags);
+
+ // return the old save count
+ return count;
+ }
+
+ /**
+ * Restores the {@link GcSnapshot} to <var>saveCount</var>
+ * @param saveCount the saveCount
+ */
+ protected void restoreTo(int saveCount) {
+ mSnapshot = mSnapshot.restoreTo(saveCount);
+ }
+
+ /**
+ * Restores the top {@link GcSnapshot}
+ */
+ protected void restore() {
+ mSnapshot = mSnapshot.restore();
+ }
+
+ protected boolean clipRect(float left, float top, float right, float bottom, int regionOp) {
+ return mSnapshot.clipRect(left, top, right, bottom, regionOp);
+ }
+}
diff --git a/bridge/src/android/graphics/BidiRenderer.java b/bridge/src/android/graphics/BidiRenderer.java
index 21f36ce..c6827a3 100644
--- a/bridge/src/android/graphics/BidiRenderer.java
+++ b/bridge/src/android/graphics/BidiRenderer.java
@@ -30,6 +30,7 @@
import java.awt.Toolkit;
import java.awt.font.FontRenderContext;
import java.awt.font.GlyphVector;
+import java.awt.geom.AffineTransform;
import java.awt.geom.Rectangle2D;
import java.util.ArrayList;
import java.util.LinkedList;
@@ -41,6 +42,7 @@
*/
@SuppressWarnings("deprecation")
public class BidiRenderer {
+ private static String JAVA_VENDOR = System.getProperty("java.vendor");
private static class ScriptRun {
int start;
@@ -221,9 +223,16 @@
frc = mGraphics.getFontRenderContext();
} else {
frc = Toolkit.getDefaultToolkit().getFontMetrics(font).getFontRenderContext();
+
// Metrics obtained this way don't have anti-aliasing set. So,
// we create a new FontRenderContext with anti-aliasing set.
- frc = new FontRenderContext(font.getTransform(), mPaint.isAntiAliased(), frc.usesFractionalMetrics());
+ AffineTransform transform = font.getTransform();
+ if (mPaint.isAntiAliased() &&
+ // Workaround for http://b.android.com/211659
+ (transform.getScaleX() <= 9.9 ||
+ !"JetBrains s.r.o".equals(JAVA_VENDOR))) {
+ frc = new FontRenderContext(transform, true, frc.usesFractionalMetrics());
+ }
}
GlyphVector gv = font.layoutGlyphVector(frc, mText, start, limit, flag);
int ng = gv.getNumGlyphs();
diff --git a/bridge/src/android/graphics/Bitmap_Delegate.java b/bridge/src/android/graphics/Bitmap_Delegate.java
index f1da3a2..9d9d71f 100644
--- a/bridge/src/android/graphics/Bitmap_Delegate.java
+++ b/bridge/src/android/graphics/Bitmap_Delegate.java
@@ -62,7 +62,7 @@
public enum BitmapCreateFlags {
- PREMULTIPLIED, MUTABLE
+ NONE, PREMULTIPLIED, MUTABLE
}
// ---- delegate manager ----
@@ -92,8 +92,7 @@
@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());
+ return bitmap == null ? null : getDelegate(bitmap.getNativeInstance());
}
/**
@@ -327,7 +326,7 @@
@LayoutlibDelegate
/*package*/ static void nativeReconfigure(long nativeBitmap, int width, int height,
- int config, int allocSize, boolean isPremultiplied) {
+ int config, boolean isPremultiplied) {
Bridge.getLog().error(LayoutLog.TAG_UNSUPPORTED,
"Bitmap.reconfigure() is not supported", null /*data*/);
}
@@ -601,12 +600,40 @@
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;
+ /*package*/ static int nativeGetAllocationByteCount(long nativeBitmap) {
+ // get the delegate from the native int.
+ Bitmap_Delegate delegate = sManager.getDelegate(nativeBitmap);
+ if (delegate == null) {
+ return 0;
+ }
+ return nativeRowBytes(nativeBitmap) * delegate.mImage.getHeight();
+
+ }
+
+ @LayoutlibDelegate
+ /*package*/ static void nativePrepareToDraw(long nativeBitmap) {
+ // do nothing as Bitmap_Delegate does not have caches
+ }
+
+ @LayoutlibDelegate
+ /*package*/ static Bitmap nativeCopyPreserveInternalConfig(long nativeBitmap) {
+ Bitmap_Delegate srcBmpDelegate = sManager.getDelegate(nativeBitmap);
+ if (srcBmpDelegate == null) {
+ return null;
+ }
+
+ BufferedImage srcImage = srcBmpDelegate.getImage();
+
+ // create the image
+ BufferedImage image = new BufferedImage(srcImage.getColorModel(), srcImage.copyData(null),
+ srcImage.isAlphaPremultiplied(), null);
+
+ // create a delegate with the content of the stream.
+ Bitmap_Delegate delegate = new Bitmap_Delegate(image, srcBmpDelegate.getConfig());
+
+ return createBitmap(delegate, EnumSet.of(BitmapCreateFlags.NONE),
+ Bitmap.getDefaultDensity());
}
// ---- Private delegate/helper methods ----
@@ -627,7 +654,7 @@
boolean isPremultiplied = createFlags.contains(BitmapCreateFlags.PREMULTIPLIED);
// and create/return a new Bitmap with it
- return new Bitmap(nativeInt, null /* buffer */, width, height, density, isMutable,
+ return new Bitmap(nativeInt, width, height, density, isMutable,
isPremultiplied, null /*ninePatchChunk*/, null /* layoutBounds */);
}
diff --git a/bridge/src/android/graphics/Canvas_Delegate.java b/bridge/src/android/graphics/Canvas_Delegate.java
index fa880f0..c599e9d 100644
--- a/bridge/src/android/graphics/Canvas_Delegate.java
+++ b/bridge/src/android/graphics/Canvas_Delegate.java
@@ -20,24 +20,14 @@
import com.android.layoutlib.bridge.Bridge;
import com.android.layoutlib.bridge.impl.DelegateManager;
import com.android.layoutlib.bridge.impl.GcSnapshot;
-import com.android.layoutlib.bridge.impl.PorterDuffUtility;
-import com.android.ninepatch.NinePatchChunk;
import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
import android.annotation.Nullable;
import android.graphics.Bitmap.Config;
-import android.text.TextUtils;
-import java.awt.Color;
-import java.awt.Composite;
import java.awt.Graphics2D;
import java.awt.Rectangle;
-import java.awt.RenderingHints;
-import java.awt.Shape;
import java.awt.geom.AffineTransform;
-import java.awt.geom.Arc2D;
-import java.awt.geom.Rectangle2D;
-import java.awt.image.BufferedImage;
import libcore.util.NativeAllocationRegistry_Delegate;
@@ -55,40 +45,27 @@
* @see DelegateManager
*
*/
-public final class Canvas_Delegate {
+public final class Canvas_Delegate extends BaseCanvas_Delegate {
// ---- delegate manager ----
- private static final DelegateManager<Canvas_Delegate> sManager =
- new DelegateManager<Canvas_Delegate>(Canvas_Delegate.class);
private static long sFinalizer = -1;
-
- // ---- delegate helper data ----
-
- private final static boolean[] sBoolOut = new boolean[1];
-
-
- // ---- delegate data ----
- private Bitmap_Delegate mBitmap;
- private GcSnapshot mSnapshot;
-
private DrawFilter_Delegate mDrawFilter = null;
-
// ---- Public Helper methods ----
/**
* Returns the native delegate associated to a given {@link Canvas} object.
*/
public static Canvas_Delegate getDelegate(Canvas canvas) {
- return sManager.getDelegate(canvas.getNativeCanvasWrapper());
+ return (Canvas_Delegate) sManager.getDelegate(canvas.getNativeCanvasWrapper());
}
/**
* Returns the native delegate associated to a given an int referencing a {@link Canvas} object.
*/
public static Canvas_Delegate getDelegate(long native_canvas) {
- return sManager.getDelegate(native_canvas);
+ return (Canvas_Delegate) sManager.getDelegate(native_canvas);
}
/**
@@ -110,20 +87,20 @@
// ---- native methods ----
@LayoutlibDelegate
- /*package*/ static void freeCaches() {
+ /*package*/ static void nFreeCaches() {
// nothing to be done here.
}
@LayoutlibDelegate
- /*package*/ static void freeTextLayoutCaches() {
+ /*package*/ static void nFreeTextLayoutCaches() {
// nothing to be done here yet.
}
@LayoutlibDelegate
- /*package*/ static long initRaster(@Nullable Bitmap bitmap) {
+ /*package*/ static long nInitRaster(@Nullable Bitmap bitmap) {
long nativeBitmapOrZero = 0;
if (bitmap != null) {
- nativeBitmapOrZero = bitmap.refSkPixelRef();
+ nativeBitmapOrZero = bitmap.getNativeInstance();
}
if (nativeBitmapOrZero > 0) {
// get the Bitmap from the int
@@ -142,8 +119,8 @@
}
@LayoutlibDelegate
- public static void native_setBitmap(long canvas, Bitmap bitmap) {
- Canvas_Delegate canvasDelegate = sManager.getDelegate(canvas);
+ public static void nSetBitmap(long canvas, Bitmap bitmap) {
+ Canvas_Delegate canvasDelegate = Canvas_Delegate.getDelegate(canvas);
Bitmap_Delegate bitmapDelegate = Bitmap_Delegate.getDelegate(bitmap);
if (canvasDelegate == null || bitmapDelegate==null) {
return;
@@ -153,9 +130,9 @@
}
@LayoutlibDelegate
- public static boolean native_isOpaque(long nativeCanvas) {
+ public static boolean nIsOpaque(long nativeCanvas) {
// get the delegate from the native int.
- Canvas_Delegate canvasDelegate = sManager.getDelegate(nativeCanvas);
+ Canvas_Delegate canvasDelegate = Canvas_Delegate.getDelegate(nativeCanvas);
if (canvasDelegate == null) {
return false;
}
@@ -164,12 +141,12 @@
}
@LayoutlibDelegate
- public static void native_setHighContrastText(long nativeCanvas, boolean highContrastText){}
+ public static void nSetHighContrastText(long nativeCanvas, boolean highContrastText){}
@LayoutlibDelegate
- public static int native_getWidth(long nativeCanvas) {
+ public static int nGetWidth(long nativeCanvas) {
// get the delegate from the native int.
- Canvas_Delegate canvasDelegate = sManager.getDelegate(nativeCanvas);
+ Canvas_Delegate canvasDelegate = Canvas_Delegate.getDelegate(nativeCanvas);
if (canvasDelegate == null) {
return 0;
}
@@ -178,9 +155,9 @@
}
@LayoutlibDelegate
- public static int native_getHeight(long nativeCanvas) {
+ public static int nGetHeight(long nativeCanvas) {
// get the delegate from the native int.
- Canvas_Delegate canvasDelegate = sManager.getDelegate(nativeCanvas);
+ Canvas_Delegate canvasDelegate = Canvas_Delegate.getDelegate(nativeCanvas);
if (canvasDelegate == null) {
return 0;
}
@@ -189,9 +166,9 @@
}
@LayoutlibDelegate
- public static int native_save(long nativeCanvas, int saveFlags) {
+ public static int nSave(long nativeCanvas, int saveFlags) {
// get the delegate from the native int.
- Canvas_Delegate canvasDelegate = sManager.getDelegate(nativeCanvas);
+ Canvas_Delegate canvasDelegate = Canvas_Delegate.getDelegate(nativeCanvas);
if (canvasDelegate == null) {
return 0;
}
@@ -200,11 +177,11 @@
}
@LayoutlibDelegate
- public static int native_saveLayer(long nativeCanvas, float l,
+ public static int nSaveLayer(long nativeCanvas, float l,
float t, float r, float b,
long paint, int layerFlags) {
// get the delegate from the native int.
- Canvas_Delegate canvasDelegate = sManager.getDelegate(nativeCanvas);
+ Canvas_Delegate canvasDelegate = Canvas_Delegate.getDelegate(nativeCanvas);
if (canvasDelegate == null) {
return 0;
}
@@ -219,11 +196,11 @@
}
@LayoutlibDelegate
- public static int native_saveLayerAlpha(long nativeCanvas, float l,
+ public static int nSaveLayerAlpha(long nativeCanvas, float l,
float t, float r, float b,
int alpha, int layerFlags) {
// get the delegate from the native int.
- Canvas_Delegate canvasDelegate = sManager.getDelegate(nativeCanvas);
+ Canvas_Delegate canvasDelegate = Canvas_Delegate.getDelegate(nativeCanvas);
if (canvasDelegate == null) {
return 0;
}
@@ -232,10 +209,10 @@
}
@LayoutlibDelegate
- public static void native_restore(long nativeCanvas, boolean throwOnUnderflow) {
+ public static void nRestore(long nativeCanvas, boolean throwOnUnderflow) {
// FIXME: implement throwOnUnderflow.
// get the delegate from the native int.
- Canvas_Delegate canvasDelegate = sManager.getDelegate(nativeCanvas);
+ Canvas_Delegate canvasDelegate = Canvas_Delegate.getDelegate(nativeCanvas);
if (canvasDelegate == null) {
return;
}
@@ -244,11 +221,11 @@
}
@LayoutlibDelegate
- public static void native_restoreToCount(long nativeCanvas, int saveCount,
+ public static void nRestoreToCount(long nativeCanvas, int saveCount,
boolean throwOnUnderflow) {
// FIXME: implement throwOnUnderflow.
// get the delegate from the native int.
- Canvas_Delegate canvasDelegate = sManager.getDelegate(nativeCanvas);
+ Canvas_Delegate canvasDelegate = Canvas_Delegate.getDelegate(nativeCanvas);
if (canvasDelegate == null) {
return;
}
@@ -257,9 +234,9 @@
}
@LayoutlibDelegate
- public static int native_getSaveCount(long nativeCanvas) {
+ public static int nGetSaveCount(long nativeCanvas) {
// get the delegate from the native int.
- Canvas_Delegate canvasDelegate = sManager.getDelegate(nativeCanvas);
+ Canvas_Delegate canvasDelegate = Canvas_Delegate.getDelegate(nativeCanvas);
if (canvasDelegate == null) {
return 0;
}
@@ -268,9 +245,9 @@
}
@LayoutlibDelegate
- public static void native_translate(long nativeCanvas, float dx, float dy) {
+ public static void nTranslate(long nativeCanvas, float dx, float dy) {
// get the delegate from the native int.
- Canvas_Delegate canvasDelegate = sManager.getDelegate(nativeCanvas);
+ Canvas_Delegate canvasDelegate = Canvas_Delegate.getDelegate(nativeCanvas);
if (canvasDelegate == null) {
return;
}
@@ -279,9 +256,9 @@
}
@LayoutlibDelegate
- public static void native_scale(long nativeCanvas, float sx, float sy) {
+ public static void nScale(long nativeCanvas, float sx, float sy) {
// get the delegate from the native int.
- Canvas_Delegate canvasDelegate = sManager.getDelegate(nativeCanvas);
+ Canvas_Delegate canvasDelegate = Canvas_Delegate.getDelegate(nativeCanvas);
if (canvasDelegate == null) {
return;
}
@@ -290,9 +267,9 @@
}
@LayoutlibDelegate
- public static void native_rotate(long nativeCanvas, float degrees) {
+ public static void nRotate(long nativeCanvas, float degrees) {
// get the delegate from the native int.
- Canvas_Delegate canvasDelegate = sManager.getDelegate(nativeCanvas);
+ Canvas_Delegate canvasDelegate = Canvas_Delegate.getDelegate(nativeCanvas);
if (canvasDelegate == null) {
return;
}
@@ -301,9 +278,9 @@
}
@LayoutlibDelegate
- public static void native_skew(long nativeCanvas, float kx, float ky) {
+ public static void nSkew(long nativeCanvas, float kx, float ky) {
// get the delegate from the native int.
- Canvas_Delegate canvasDelegate = sManager.getDelegate(nativeCanvas);
+ Canvas_Delegate canvasDelegate = Canvas_Delegate.getDelegate(nativeCanvas);
if (canvasDelegate == null) {
return;
}
@@ -325,9 +302,9 @@
}
@LayoutlibDelegate
- public static void native_concat(long nCanvas, long nMatrix) {
+ public static void nConcat(long nCanvas, long nMatrix) {
// get the delegate from the native int.
- Canvas_Delegate canvasDelegate = sManager.getDelegate(nCanvas);
+ Canvas_Delegate canvasDelegate = Canvas_Delegate.getDelegate(nCanvas);
if (canvasDelegate == null) {
return;
}
@@ -353,9 +330,9 @@
}
@LayoutlibDelegate
- public static void native_setMatrix(long nCanvas, long nMatrix) {
+ public static void nSetMatrix(long nCanvas, long nMatrix) {
// get the delegate from the native int.
- Canvas_Delegate canvasDelegate = sManager.getDelegate(nCanvas);
+ Canvas_Delegate canvasDelegate = Canvas_Delegate.getDelegate(nCanvas);
if (canvasDelegate == null) {
return;
}
@@ -383,12 +360,12 @@
}
@LayoutlibDelegate
- public static boolean native_clipRect(long nCanvas,
+ public static boolean nClipRect(long nCanvas,
float left, float top,
float right, float bottom,
int regionOp) {
// get the delegate from the native int.
- Canvas_Delegate canvasDelegate = sManager.getDelegate(nCanvas);
+ Canvas_Delegate canvasDelegate = Canvas_Delegate.getDelegate(nCanvas);
if (canvasDelegate == null) {
return false;
}
@@ -397,10 +374,10 @@
}
@LayoutlibDelegate
- public static boolean native_clipPath(long nativeCanvas,
+ public static boolean nClipPath(long nativeCanvas,
long nativePath,
int regionOp) {
- Canvas_Delegate canvasDelegate = sManager.getDelegate(nativeCanvas);
+ Canvas_Delegate canvasDelegate = Canvas_Delegate.getDelegate(nativeCanvas);
if (canvasDelegate == null) {
return true;
}
@@ -414,25 +391,8 @@
}
@LayoutlibDelegate
- public static boolean native_clipRegion(long nativeCanvas,
- long nativeRegion,
- int regionOp) {
- Canvas_Delegate canvasDelegate = sManager.getDelegate(nativeCanvas);
- if (canvasDelegate == null) {
- return true;
- }
-
- Region_Delegate region = Region_Delegate.getDelegate(nativeRegion);
- if (region == null) {
- return true;
- }
-
- return canvasDelegate.mSnapshot.clip(region.getJavaArea(), regionOp);
- }
-
- @LayoutlibDelegate
- public static void nativeSetDrawFilter(long nativeCanvas, long nativeFilter) {
- Canvas_Delegate canvasDelegate = sManager.getDelegate(nativeCanvas);
+ public static void nSetDrawFilter(long nativeCanvas, long nativeFilter) {
+ Canvas_Delegate canvasDelegate = Canvas_Delegate.getDelegate(nativeCanvas);
if (canvasDelegate == null) {
return;
}
@@ -446,10 +406,10 @@
}
@LayoutlibDelegate
- public static boolean native_getClipBounds(long nativeCanvas,
+ public static boolean nGetClipBounds(long nativeCanvas,
Rect bounds) {
// get the delegate from the native int.
- Canvas_Delegate canvasDelegate = sManager.getDelegate(nativeCanvas);
+ Canvas_Delegate canvasDelegate = Canvas_Delegate.getDelegate(nativeCanvas);
if (canvasDelegate == null) {
return false;
}
@@ -467,9 +427,9 @@
}
@LayoutlibDelegate
- public static void native_getCTM(long canvas, long matrix) {
+ public static void nGetCTM(long canvas, long matrix) {
// get the delegate from the native int.
- Canvas_Delegate canvasDelegate = sManager.getDelegate(canvas);
+ Canvas_Delegate canvasDelegate = Canvas_Delegate.getDelegate(canvas);
if (canvasDelegate == null) {
return;
}
@@ -484,13 +444,13 @@
}
@LayoutlibDelegate
- public static boolean native_quickReject(long nativeCanvas, long path) {
+ public static boolean nQuickReject(long nativeCanvas, long path) {
// FIXME properly implement quickReject
return false;
}
@LayoutlibDelegate
- public static boolean native_quickReject(long nativeCanvas,
+ public static boolean nQuickReject(long nativeCanvas,
float left, float top,
float right, float bottom) {
// FIXME properly implement quickReject
@@ -498,509 +458,11 @@
}
@LayoutlibDelegate
- public static void native_drawColor(long nativeCanvas, final int color, final int mode) {
- // get the delegate from the native int.
- Canvas_Delegate canvasDelegate = sManager.getDelegate(nativeCanvas);
- if (canvasDelegate == null) {
- return;
- }
-
- final int w = canvasDelegate.mBitmap.getImage().getWidth();
- final int h = canvasDelegate.mBitmap.getImage().getHeight();
- draw(nativeCanvas, new GcSnapshot.Drawable() {
-
- @Override
- public void draw(Graphics2D graphics, Paint_Delegate paint) {
- // reset its transform just in case
- graphics.setTransform(new AffineTransform());
-
- // set the color
- graphics.setColor(new Color(color, true /*alpha*/));
-
- Composite composite = PorterDuffUtility.getComposite(
- PorterDuffUtility.getPorterDuffMode(mode), 0xFF);
- if (composite != null) {
- graphics.setComposite(composite);
- }
-
- graphics.fillRect(0, 0, w, h);
- }
- });
- }
-
- @LayoutlibDelegate
- public static void native_drawPaint(long nativeCanvas, long paint) {
- // FIXME
- Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
- "Canvas.drawPaint is not supported.", null, null /*data*/);
- }
-
- @LayoutlibDelegate
- public static void native_drawPoint(long nativeCanvas, float x, float y,
- long nativePaint) {
- // FIXME
- Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
- "Canvas.drawPoint is not supported.", null, null /*data*/);
- }
-
- @LayoutlibDelegate
- public static void native_drawPoints(long nativeCanvas, float[] pts, int offset, int count,
- long nativePaint) {
- // FIXME
- Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
- "Canvas.drawPoint is not supported.", null, null /*data*/);
- }
-
- @LayoutlibDelegate
- public static void native_drawLine(long nativeCanvas,
- final float startX, final float startY, final float stopX, final float stopY,
- long paint) {
- draw(nativeCanvas, paint, false /*compositeOnly*/, false /*forceSrcMode*/,
- new GcSnapshot.Drawable() {
- @Override
- public void draw(Graphics2D graphics, Paint_Delegate paintDelegate) {
- graphics.drawLine((int)startX, (int)startY, (int)stopX, (int)stopY);
- }
- });
- }
-
- @LayoutlibDelegate
- public static void native_drawLines(long nativeCanvas,
- final float[] pts, final int offset, final int count,
- long nativePaint) {
- draw(nativeCanvas, nativePaint, false /*compositeOnly*/,
- false /*forceSrcMode*/, new GcSnapshot.Drawable() {
- @Override
- public void draw(Graphics2D graphics, Paint_Delegate paintDelegate) {
- for (int i = 0; i < count; i += 4) {
- graphics.drawLine((int) pts[i + offset], (int) pts[i + offset + 1],
- (int) pts[i + offset + 2], (int) pts[i + offset + 3]);
- }
- }
- });
- }
-
- @LayoutlibDelegate
- public static void native_drawRect(long nativeCanvas,
- final float left, final float top, final float right, final float bottom, long paint) {
-
- draw(nativeCanvas, paint, false /*compositeOnly*/, false /*forceSrcMode*/,
- new GcSnapshot.Drawable() {
- @Override
- public void draw(Graphics2D graphics, Paint_Delegate paintDelegate) {
- int style = paintDelegate.getStyle();
-
- // draw
- if (style == Paint.Style.FILL.nativeInt ||
- style == Paint.Style.FILL_AND_STROKE.nativeInt) {
- graphics.fillRect((int)left, (int)top,
- (int)(right-left), (int)(bottom-top));
- }
-
- if (style == Paint.Style.STROKE.nativeInt ||
- style == Paint.Style.FILL_AND_STROKE.nativeInt) {
- graphics.drawRect((int)left, (int)top,
- (int)(right-left), (int)(bottom-top));
- }
- }
- });
- }
-
- @LayoutlibDelegate
- public static void native_drawOval(long nativeCanvas, final float left,
- final float top, final float right, final float bottom, long paint) {
- if (right > left && bottom > top) {
- draw(nativeCanvas, paint, false /*compositeOnly*/, false /*forceSrcMode*/,
- new GcSnapshot.Drawable() {
- @Override
- public void draw(Graphics2D graphics, Paint_Delegate paintDelegate) {
- int style = paintDelegate.getStyle();
-
- // draw
- if (style == Paint.Style.FILL.nativeInt ||
- style == Paint.Style.FILL_AND_STROKE.nativeInt) {
- graphics.fillOval((int)left, (int)top,
- (int)(right - left), (int)(bottom - top));
- }
-
- if (style == Paint.Style.STROKE.nativeInt ||
- style == Paint.Style.FILL_AND_STROKE.nativeInt) {
- graphics.drawOval((int)left, (int)top,
- (int)(right - left), (int)(bottom - top));
- }
- }
- });
- }
- }
-
- @LayoutlibDelegate
- public static void native_drawCircle(long nativeCanvas,
- float cx, float cy, float radius, long paint) {
- native_drawOval(nativeCanvas,
- cx - radius, cy - radius, cx + radius, cy + radius,
- paint);
- }
-
- @LayoutlibDelegate
- public static void native_drawArc(long nativeCanvas,
- final float left, final float top, final float right, final float bottom,
- final float startAngle, final float sweep,
- final boolean useCenter, long paint) {
- if (right > left && bottom > top) {
- draw(nativeCanvas, paint, false /*compositeOnly*/, false /*forceSrcMode*/,
- new GcSnapshot.Drawable() {
- @Override
- public void draw(Graphics2D graphics, Paint_Delegate paintDelegate) {
- int style = paintDelegate.getStyle();
-
- Arc2D.Float arc = new Arc2D.Float(
- left, top, right - left, bottom - top,
- -startAngle, -sweep,
- useCenter ? Arc2D.PIE : Arc2D.OPEN);
-
- // draw
- if (style == Paint.Style.FILL.nativeInt ||
- style == Paint.Style.FILL_AND_STROKE.nativeInt) {
- graphics.fill(arc);
- }
-
- if (style == Paint.Style.STROKE.nativeInt ||
- style == Paint.Style.FILL_AND_STROKE.nativeInt) {
- graphics.draw(arc);
- }
- }
- });
- }
- }
-
- @LayoutlibDelegate
- public static void native_drawRoundRect(long nativeCanvas,
- final float left, final float top, final float right, final float bottom,
- final float rx, final float ry, long paint) {
- draw(nativeCanvas, paint, false /*compositeOnly*/, false /*forceSrcMode*/,
- new GcSnapshot.Drawable() {
- @Override
- public void draw(Graphics2D graphics, Paint_Delegate paintDelegate) {
- int style = paintDelegate.getStyle();
-
- // draw
- if (style == Paint.Style.FILL.nativeInt ||
- style == Paint.Style.FILL_AND_STROKE.nativeInt) {
- graphics.fillRoundRect(
- (int)left, (int)top,
- (int)(right - left), (int)(bottom - top),
- 2 * (int)rx, 2 * (int)ry);
- }
-
- if (style == Paint.Style.STROKE.nativeInt ||
- style == Paint.Style.FILL_AND_STROKE.nativeInt) {
- graphics.drawRoundRect(
- (int)left, (int)top,
- (int)(right - left), (int)(bottom - top),
- 2 * (int)rx, 2 * (int)ry);
- }
- }
- });
- }
-
- @LayoutlibDelegate
- public static void native_drawPath(long nativeCanvas, long path, long paint) {
- final Path_Delegate pathDelegate = Path_Delegate.getDelegate(path);
- if (pathDelegate == null) {
- return;
- }
-
- draw(nativeCanvas, paint, false /*compositeOnly*/, false /*forceSrcMode*/,
- new GcSnapshot.Drawable() {
- @Override
- public void draw(Graphics2D graphics, Paint_Delegate paintDelegate) {
- Shape shape = pathDelegate.getJavaShape();
- Rectangle2D bounds = shape.getBounds2D();
- if (bounds.isEmpty()) {
- // Apple JRE 1.6 doesn't like drawing empty shapes.
- // http://b.android.com/178278
-
- if (pathDelegate.isEmpty()) {
- // This means that the path doesn't have any lines or curves so
- // nothing to draw.
- return;
- }
-
- // The stroke width is not consider for the size of the bounds so,
- // for example, a horizontal line, would be considered as an empty
- // rectangle.
- // If the strokeWidth is not 0, we use it to consider the size of the
- // path as well.
- float strokeWidth = paintDelegate.getStrokeWidth();
- if (strokeWidth <= 0.0f) {
- return;
- }
- bounds.setRect(bounds.getX(), bounds.getY(),
- Math.max(strokeWidth, bounds.getWidth()),
- Math.max(strokeWidth, bounds.getHeight()));
- }
-
- int style = paintDelegate.getStyle();
-
- if (style == Paint.Style.FILL.nativeInt ||
- style == Paint.Style.FILL_AND_STROKE.nativeInt) {
- graphics.fill(shape);
- }
-
- if (style == Paint.Style.STROKE.nativeInt ||
- style == Paint.Style.FILL_AND_STROKE.nativeInt) {
- graphics.draw(shape);
- }
- }
- });
- }
-
- @LayoutlibDelegate
- public static void native_drawRegion(long nativeCanvas, long nativeRegion,
- long nativePaint) {
- // FIXME
- Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
- "Some canvas paths may not be drawn", null, null);
- }
-
- @LayoutlibDelegate
- public static void native_drawNinePatch(Canvas thisCanvas, long nativeCanvas,
- long nativeBitmap, long ninePatch, final float dstLeft, final float dstTop,
- final float dstRight, final float dstBottom, long nativePaintOrZero,
- final int screenDensity, final int bitmapDensity) {
-
- // get the delegate from the native int.
- final Bitmap_Delegate bitmapDelegate = Bitmap_Delegate.getDelegate(nativeBitmap);
- if (bitmapDelegate == null) {
- return;
- }
-
- byte[] c = NinePatch_Delegate.getChunk(ninePatch);
- if (c == null) {
- // not a 9-patch?
- BufferedImage image = bitmapDelegate.getImage();
- drawBitmap(nativeCanvas, bitmapDelegate, nativePaintOrZero, 0, 0, image.getWidth(),
- image.getHeight(), (int) dstLeft, (int) dstTop, (int) dstRight,
- (int) dstBottom);
- return;
- }
-
- final NinePatchChunk chunkObject = NinePatch_Delegate.getChunk(c);
- assert chunkObject != null;
- if (chunkObject == null) {
- return;
- }
-
- Canvas_Delegate canvasDelegate = Canvas_Delegate.getDelegate(nativeCanvas);
- if (canvasDelegate == null) {
- return;
- }
-
- // this one can be null
- Paint_Delegate paintDelegate = Paint_Delegate.getDelegate(nativePaintOrZero);
-
- canvasDelegate.getSnapshot().draw(new GcSnapshot.Drawable() {
- @Override
- public void draw(Graphics2D graphics, Paint_Delegate paint) {
- chunkObject.draw(bitmapDelegate.getImage(), graphics, (int) dstLeft, (int) dstTop,
- (int) (dstRight - dstLeft), (int) (dstBottom - dstTop), screenDensity,
- bitmapDensity);
- }
- }, paintDelegate, true, false);
-
- }
-
- @LayoutlibDelegate
- public static void native_drawBitmap(Canvas thisCanvas, long nativeCanvas, Bitmap bitmap,
- float left, float top,
- long nativePaintOrZero,
- int canvasDensity,
- int screenDensity,
- int bitmapDensity) {
- // get the delegate from the native int.
- Bitmap_Delegate bitmapDelegate = Bitmap_Delegate.getDelegate(bitmap);
- if (bitmapDelegate == null) {
- return;
- }
-
- BufferedImage image = bitmapDelegate.getImage();
- float right = left + image.getWidth();
- float bottom = top + image.getHeight();
-
- drawBitmap(nativeCanvas, bitmapDelegate, nativePaintOrZero,
- 0, 0, image.getWidth(), image.getHeight(),
- (int)left, (int)top, (int)right, (int)bottom);
- }
-
- @LayoutlibDelegate
- public 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) {
- // get the delegate from the native int.
- Bitmap_Delegate bitmapDelegate = Bitmap_Delegate.getDelegate(bitmap);
- if (bitmapDelegate == null) {
- return;
- }
-
- drawBitmap(nativeCanvas, bitmapDelegate, nativePaintOrZero,
- (int)srcLeft, (int)srcTop, (int)srcRight, (int)srcBottom,
- (int)dstLeft, (int)dstTop, (int)dstRight, (int)dstBottom);
- }
-
- @LayoutlibDelegate
- public static void native_drawBitmap(long nativeCanvas, int[] colors,
- int offset, int stride, final float x,
- final float y, int width, int height,
- boolean hasAlpha,
- long nativePaintOrZero) {
- // create a temp BufferedImage containing the content.
- final BufferedImage image = new BufferedImage(width, height,
- hasAlpha ? BufferedImage.TYPE_INT_ARGB : BufferedImage.TYPE_INT_RGB);
- image.setRGB(0, 0, width, height, colors, offset, stride);
-
- draw(nativeCanvas, nativePaintOrZero, true /*compositeOnly*/, false /*forceSrcMode*/,
- new GcSnapshot.Drawable() {
- @Override
- public void draw(Graphics2D graphics, Paint_Delegate paint) {
- if (paint != null && paint.isFilterBitmap()) {
- graphics.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
- RenderingHints.VALUE_INTERPOLATION_BILINEAR);
- }
-
- graphics.drawImage(image, (int) x, (int) y, null);
- }
- });
- }
-
- @LayoutlibDelegate
- public static void nativeDrawBitmapMatrix(long nCanvas, Bitmap bitmap,
- long nMatrix, long nPaint) {
- // get the delegate from the native int.
- Canvas_Delegate canvasDelegate = sManager.getDelegate(nCanvas);
- if (canvasDelegate == null) {
- return;
- }
-
- // get the delegate from the native int, which can be null
- Paint_Delegate paintDelegate = Paint_Delegate.getDelegate(nPaint);
-
- // get the delegate from the native int.
- Bitmap_Delegate bitmapDelegate = Bitmap_Delegate.getDelegate(bitmap);
- if (bitmapDelegate == null) {
- return;
- }
-
- final BufferedImage image = getImageToDraw(bitmapDelegate, paintDelegate, sBoolOut);
-
- Matrix_Delegate matrixDelegate = Matrix_Delegate.getDelegate(nMatrix);
- if (matrixDelegate == null) {
- return;
- }
-
- final AffineTransform mtx = matrixDelegate.getAffineTransform();
-
- canvasDelegate.getSnapshot().draw(new GcSnapshot.Drawable() {
- @Override
- public void draw(Graphics2D graphics, Paint_Delegate paint) {
- if (paint != null && paint.isFilterBitmap()) {
- graphics.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
- RenderingHints.VALUE_INTERPOLATION_BILINEAR);
- }
-
- //FIXME add support for canvas, screen and bitmap densities.
- graphics.drawImage(image, mtx, null);
- }
- }, paintDelegate, true /*compositeOnly*/, false /*forceSrcMode*/);
- }
-
- @LayoutlibDelegate
- public static void nativeDrawBitmapMesh(long nCanvas, Bitmap bitmap,
- int meshWidth, int meshHeight, float[] verts, int vertOffset, int[] colors,
- int colorOffset, long nPaint) {
- // FIXME
- Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
- "Canvas.drawBitmapMesh is not supported.", null, null /*data*/);
- }
-
- @LayoutlibDelegate
- public static void nativeDrawVertices(long nCanvas, int mode, int n,
- float[] verts, int vertOffset,
- float[] texs, int texOffset,
- int[] colors, int colorOffset,
- short[] indices, int indexOffset,
- int indexCount, long nPaint) {
- // FIXME
- Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
- "Canvas.drawVertices is not supported.", null, null /*data*/);
- }
-
- @LayoutlibDelegate
- public 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 & 1) != 0,
- paint, typeface);
- }
-
- @LayoutlibDelegate
- public static void native_drawText(long nativeCanvas, String text,
- int start, int end, float x, float y, final int flags, long paint,
- long typeface) {
- int count = end - start;
- char[] buffer = TemporaryBuffer.obtain(count);
- TextUtils.getChars(text, start, end, buffer, 0);
-
- native_drawText(nativeCanvas, buffer, 0, count, x, y, flags, paint, typeface);
- }
-
- @LayoutlibDelegate
- public static void native_drawTextRun(long nativeCanvas, String text,
- int start, int end, int contextStart, int contextEnd,
- float x, float y, boolean isRtl, long paint, long typeface) {
- int count = end - start;
- char[] buffer = TemporaryBuffer.obtain(count);
- TextUtils.getChars(text, start, end, buffer, 0);
-
- drawText(nativeCanvas, buffer, 0, count, x, y, isRtl, paint, typeface);
- }
-
- @LayoutlibDelegate
- public static void native_drawTextRun(long nativeCanvas, char[] text,
- int start, int count, int contextStart, int contextCount,
- float x, float y, boolean isRtl, long paint, long typeface) {
- drawText(nativeCanvas, text, start, count, x, y, isRtl, paint, typeface);
- }
-
- @LayoutlibDelegate
- public static void native_drawTextOnPath(long nativeCanvas,
- char[] text, int index,
- int count, long path,
- float hOffset,
- float vOffset, int bidiFlags,
- long paint, long typeface) {
- // FIXME
- Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
- "Canvas.drawTextOnPath is not supported.", null, null /*data*/);
- }
-
- @LayoutlibDelegate
- public static void native_drawTextOnPath(long nativeCanvas,
- String text, long path,
- float hOffset,
- float vOffset,
- int bidiFlags, long paint,
- long typeface) {
- // FIXME
- Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
- "Canvas.drawTextOnPath is not supported.", null, null /*data*/);
- }
-
- @LayoutlibDelegate
- /*package*/ static long getNativeFinalizer() {
+ /*package*/ static long nGetNativeFinalizer() {
synchronized (Canvas_Delegate.class) {
if (sFinalizer == -1) {
sFinalizer = NativeAllocationRegistry_Delegate.createFinalizer(nativePtr -> {
- Canvas_Delegate delegate = sManager.getDelegate(nativePtr);
+ Canvas_Delegate delegate = Canvas_Delegate.getDelegate(nativePtr);
if (delegate != null) {
delegate.dispose();
}
@@ -1011,230 +473,12 @@
return sFinalizer;
}
- // ---- Private delegate/helper methods ----
-
- /**
- * Executes a {@link GcSnapshot.Drawable} with a given canvas and paint.
- * <p>Note that the drawable may actually be executed several times if there are
- * layers involved (see {@link #saveLayer(RectF, Paint_Delegate, int)}.
- */
- private static void draw(long nCanvas, long nPaint, boolean compositeOnly, boolean forceSrcMode,
- GcSnapshot.Drawable drawable) {
- // get the delegate from the native int.
- Canvas_Delegate canvasDelegate = sManager.getDelegate(nCanvas);
- if (canvasDelegate == null) {
- return;
- }
-
- // get the paint which can be null if nPaint is 0;
- Paint_Delegate paintDelegate = Paint_Delegate.getDelegate(nPaint);
-
- canvasDelegate.getSnapshot().draw(drawable, paintDelegate, compositeOnly, forceSrcMode);
- }
-
- /**
- * Executes a {@link GcSnapshot.Drawable} with a given canvas. No paint object will be provided
- * to {@link GcSnapshot.Drawable#draw(Graphics2D, Paint_Delegate)}.
- * <p>Note that the drawable may actually be executed several times if there are
- * layers involved (see {@link #saveLayer(RectF, Paint_Delegate, int)}.
- */
- private static void draw(long nCanvas, GcSnapshot.Drawable drawable) {
- // get the delegate from the native int.
- Canvas_Delegate canvasDelegate = sManager.getDelegate(nCanvas);
- if (canvasDelegate == null) {
- return;
- }
-
- canvasDelegate.mSnapshot.draw(drawable);
- }
-
- private static void drawText(long nativeCanvas, final char[] text, final int index,
- final int count, final float startX, final float startY, final boolean isRtl,
- long paint, final long typeface) {
-
- draw(nativeCanvas, paint, false /*compositeOnly*/, false /*forceSrcMode*/,
- new GcSnapshot.Drawable() {
- @Override
- public void draw(Graphics2D graphics, Paint_Delegate paintDelegate) {
- // WARNING: the logic in this method is similar to Paint_Delegate.measureText.
- // Any change to this method should be reflected in Paint.measureText
-
- // assert that the typeface passed is actually the one stored in paint.
- assert (typeface == paintDelegate.mNativeTypeface);
-
- // Paint.TextAlign indicates how the text is positioned relative to X.
- // LEFT is the default and there's nothing to do.
- float x = startX;
- int limit = index + count;
- if (paintDelegate.getTextAlign() != Paint.Align.LEFT.nativeInt) {
- RectF bounds = paintDelegate.measureText(text, index, count, null, 0,
- isRtl);
- float m = bounds.right - bounds.left;
- if (paintDelegate.getTextAlign() == Paint.Align.CENTER.nativeInt) {
- x -= m / 2;
- } else if (paintDelegate.getTextAlign() == Paint.Align.RIGHT.nativeInt) {
- x -= m;
- }
- }
-
- new BidiRenderer(graphics, paintDelegate, text).setRenderLocation(x, startY)
- .renderText(index, limit, isRtl, null, 0, true);
- }
- });
- }
-
private Canvas_Delegate(Bitmap_Delegate bitmap) {
- mSnapshot = GcSnapshot.createDefaultSnapshot(mBitmap = bitmap);
+ super(bitmap);
}
private Canvas_Delegate() {
- mSnapshot = GcSnapshot.createDefaultSnapshot(null /*image*/);
- }
-
- /**
- * Disposes of the {@link Graphics2D} stack.
- */
- private void dispose() {
- mSnapshot.dispose();
- }
-
- private int save(int saveFlags) {
- // get the current save count
- int count = mSnapshot.size();
-
- mSnapshot = mSnapshot.save(saveFlags);
-
- // return the old save count
- return count;
- }
-
- private int saveLayerAlpha(RectF rect, int alpha, int saveFlags) {
- Paint_Delegate paint = new Paint_Delegate();
- paint.setAlpha(alpha);
- return saveLayer(rect, paint, saveFlags);
- }
-
- private int saveLayer(RectF rect, Paint_Delegate paint, int saveFlags) {
- // get the current save count
- int count = mSnapshot.size();
-
- mSnapshot = mSnapshot.saveLayer(rect, paint, saveFlags);
-
- // return the old save count
- return count;
- }
-
- /**
- * Restores the {@link GcSnapshot} to <var>saveCount</var>
- * @param saveCount the saveCount
- */
- private void restoreTo(int saveCount) {
- mSnapshot = mSnapshot.restoreTo(saveCount);
- }
-
- /**
- * Restores the top {@link GcSnapshot}
- */
- private void restore() {
- mSnapshot = mSnapshot.restore();
- }
-
- private boolean clipRect(float left, float top, float right, float bottom, int regionOp) {
- return mSnapshot.clipRect(left, top, right, bottom, regionOp);
- }
-
- private static void drawBitmap(
- long nativeCanvas,
- Bitmap_Delegate bitmap,
- long nativePaintOrZero,
- final int sleft, final int stop, final int sright, final int sbottom,
- final int dleft, final int dtop, final int dright, final int dbottom) {
- // get the delegate from the native int.
- Canvas_Delegate canvasDelegate = sManager.getDelegate(nativeCanvas);
- if (canvasDelegate == null) {
- return;
- }
-
- // get the paint, which could be null if the int is 0
- Paint_Delegate paintDelegate = Paint_Delegate.getDelegate(nativePaintOrZero);
-
- final BufferedImage image = getImageToDraw(bitmap, paintDelegate, sBoolOut);
-
- draw(nativeCanvas, nativePaintOrZero, true /*compositeOnly*/, sBoolOut[0],
- new GcSnapshot.Drawable() {
- @Override
- public void draw(Graphics2D graphics, Paint_Delegate paint) {
- if (paint != null && paint.isFilterBitmap()) {
- graphics.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
- RenderingHints.VALUE_INTERPOLATION_BILINEAR);
- }
-
- //FIXME add support for canvas, screen and bitmap densities.
- graphics.drawImage(image, dleft, dtop, dright, dbottom,
- sleft, stop, sright, sbottom, null);
- }
- });
- }
-
-
- /**
- * Returns a BufferedImage ready for drawing, based on the bitmap and paint delegate.
- * The image returns, through a 1-size boolean array, whether the drawing code should
- * use a SRC composite no matter what the paint says.
- *
- * @param bitmap the bitmap
- * @param paint the paint that will be used to draw
- * @param forceSrcMode whether the composite will have to be SRC
- * @return the image to draw
- */
- private static BufferedImage getImageToDraw(Bitmap_Delegate bitmap, Paint_Delegate paint,
- boolean[] forceSrcMode) {
- BufferedImage image = bitmap.getImage();
- forceSrcMode[0] = false;
-
- // if the bitmap config is alpha_8, then we erase all color value from it
- // before drawing it.
- if (bitmap.getConfig() == Bitmap.Config.ALPHA_8) {
- fixAlpha8Bitmap(image);
- } 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:
- // - override the composite to be SRC. This can only be used if the composite
- // was going to be SRC or SRC_OVER in the first place
- // - Create a different bitmap to draw in which all the alpha channel values is set
- // to 0xFF.
- if (paint != null) {
- Xfermode_Delegate xfermodeDelegate = paint.getXfermode();
- if (xfermodeDelegate instanceof PorterDuffXfermode_Delegate) {
- PorterDuff.Mode mode =
- ((PorterDuffXfermode_Delegate)xfermodeDelegate).getMode();
-
- forceSrcMode[0] = mode == PorterDuff.Mode.SRC_OVER ||
- mode == PorterDuff.Mode.SRC;
- }
- }
-
- // if we can't force SRC mode, then create a temp bitmap of TYPE_RGB
- if (!forceSrcMode[0]) {
- image = Bitmap_Delegate.createCopy(image, BufferedImage.TYPE_INT_RGB, 0xFF);
- }
- }
-
- return image;
- }
-
- private static void fixAlpha8Bitmap(final BufferedImage image) {
- int w = image.getWidth();
- int h = image.getHeight();
- int[] argb = new int[w * h];
- image.getRGB(0, 0, image.getWidth(), image.getHeight(), argb, 0, image.getWidth());
-
- final int length = argb.length;
- for (int i = 0 ; i < length; i++) {
- argb[i] &= 0xFF000000;
- }
- image.setRGB(0, 0, w, h, argb, 0, w);
+ super();
}
}
diff --git a/bridge/src/android/graphics/ComposeShader_Delegate.java b/bridge/src/android/graphics/ComposeShader_Delegate.java
index 59ddcc6..a459734 100644
--- a/bridge/src/android/graphics/ComposeShader_Delegate.java
+++ b/bridge/src/android/graphics/ComposeShader_Delegate.java
@@ -63,16 +63,8 @@
// ---- native methods ----
@LayoutlibDelegate
- /*package*/ static long nativeCreate1(long native_shaderA, long native_shaderB,
- long native_mode) {
- // FIXME not supported yet.
- ComposeShader_Delegate newDelegate = new ComposeShader_Delegate();
- return sManager.addNewDelegate(newDelegate);
- }
-
- @LayoutlibDelegate
- /*package*/ static long nativeCreate2(long native_shaderA, long native_shaderB,
- int porterDuffMode) {
+ /*package*/ static long nativeCreate(long native_shaderA, long native_shaderB,
+ int native_mode) {
// FIXME not supported yet.
ComposeShader_Delegate newDelegate = new ComposeShader_Delegate();
return sManager.addNewDelegate(newDelegate);
diff --git a/bridge/src/android/graphics/FontFamily_Delegate.java b/bridge/src/android/graphics/FontFamily_Delegate.java
index 50efc7f..147ed99 100644
--- a/bridge/src/android/graphics/FontFamily_Delegate.java
+++ b/bridge/src/android/graphics/FontFamily_Delegate.java
@@ -16,6 +16,7 @@
package android.graphics;
+import android.text.FontConfig;
import com.android.ide.common.rendering.api.AssetRepository;
import com.android.ide.common.rendering.api.LayoutLog;
import com.android.layoutlib.bridge.Bridge;
@@ -248,14 +249,17 @@
// ---- delegate methods ----
@LayoutlibDelegate
/*package*/ static boolean addFont(FontFamily thisFontFamily, String path, int ttcIndex) {
- final FontFamily_Delegate delegate = getDelegate(thisFontFamily.mNativePtr);
+ if (thisFontFamily.mBuilderPtr == 0) {
+ throw new IllegalStateException("Unable to call addFont after freezing.");
+ }
+ final FontFamily_Delegate delegate = getDelegate(thisFontFamily.mBuilderPtr);
return delegate != null && delegate.addFont(path, ttcIndex);
}
// ---- native methods ----
@LayoutlibDelegate
- /*package*/ static long nCreateFamily(String lang, int variant) {
+ /*package*/ static long nInitBuilder(String lang, int variant) {
// TODO: support lang. This is required for japanese locale.
FontFamily_Delegate delegate = new FontFamily_Delegate();
// variant can be 0, 1 or 2.
@@ -270,6 +274,11 @@
}
@LayoutlibDelegate
+ /*package*/ static long nCreateFamily(long builderPtr) {
+ return builderPtr;
+ }
+
+ @LayoutlibDelegate
/*package*/ static void nUnrefFamily(long nativePtr) {
// Removing the java reference for the object doesn't mean that it's freed for garbage
// collection. Typeface_Delegate may still hold a reference for it.
@@ -277,22 +286,22 @@
}
@LayoutlibDelegate
- /*package*/ static boolean nAddFont(long nativeFamily, ByteBuffer font, int ttcIndex) {
+ /*package*/ static boolean nAddFont(long builderPtr, ByteBuffer font, int ttcIndex) {
assert false : "The only client of this method has been overriden.";
return false;
}
@LayoutlibDelegate
- /*package*/ static boolean nAddFontWeightStyle(long nativeFamily, ByteBuffer font,
- int ttcIndex, List<FontListParser.Axis> listOfAxis,
+ /*package*/ static boolean nAddFontWeightStyle(long builderPtr, ByteBuffer font,
+ int ttcIndex, List<FontConfig.Axis> listOfAxis,
int weight, boolean isItalic) {
assert false : "The only client of this method has been overriden.";
return false;
}
- static boolean addFont(long nativeFamily, final String path, final int weight,
+ static boolean addFont(long builderPtr, final String path, final int weight,
final boolean isItalic) {
- final FontFamily_Delegate delegate = getDelegate(nativeFamily);
+ final FontFamily_Delegate delegate = getDelegate(builderPtr);
if (delegate != null) {
if (sFontLocation == null) {
delegate.mPostInitRunnables.add(() -> delegate.addFont(path, weight, isItalic));
@@ -304,8 +313,8 @@
}
@LayoutlibDelegate
- /*package*/ static boolean nAddFontFromAsset(long nativeFamily, AssetManager mgr, String path) {
- FontFamily_Delegate ffd = sManager.getDelegate(nativeFamily);
+ /*package*/ static boolean nAddFontFromAsset(long builderPtr, AssetManager mgr, String path) {
+ FontFamily_Delegate ffd = sManager.getDelegate(builderPtr);
if (ffd == null) {
return false;
}
diff --git a/bridge/src/android/graphics/LayerRasterizer_Delegate.java b/bridge/src/android/graphics/LayerRasterizer_Delegate.java
deleted file mode 100644
index 10cc572..0000000
--- a/bridge/src/android/graphics/LayerRasterizer_Delegate.java
+++ /dev/null
@@ -1,69 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.graphics;
-
-import com.android.layoutlib.bridge.impl.DelegateManager;
-import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
-
-/**
- * Delegate implementing the native methods of android.graphics.LayerRasterizer
- *
- * Through the layoutlib_create tool, the original native methods of LayerRasterizer have
- * been replaced by calls to methods of the same name in this delegate class.
- *
- * This class behaves like the original native implementation, but in Java, keeping previously
- * native data into its own objects and mapping them to int that are sent back and forth between
- * it and the original LayerRasterizer class.
- *
- * Because this extends {@link Rasterizer_Delegate}, there's no need to use a
- * {@link DelegateManager}, as all the Shader classes will be added to the manager
- * owned by {@link Rasterizer_Delegate}.
- *
- * @see Rasterizer_Delegate
- *
- */
-public class LayerRasterizer_Delegate extends Rasterizer_Delegate {
-
- // ---- delegate data ----
-
- // ---- Public Helper methods ----
-
- @Override
- public boolean isSupported() {
- return false;
- }
-
- @Override
- public String getSupportMessage() {
- return "Layer Rasterizers are not supported.";
- }
-
- // ---- native methods ----
-
- @LayoutlibDelegate
- /*package*/ static long nativeConstructor() {
- LayerRasterizer_Delegate newDelegate = new LayerRasterizer_Delegate();
- return sManager.addNewDelegate(newDelegate);
- }
-
- @LayoutlibDelegate
- /*package*/ static void nativeAddLayer(long native_layer, long native_paint, float dx, float dy) {
-
- }
-
- // ---- Private delegate/helper methods ----
-}
diff --git a/bridge/src/android/graphics/Matrix_Delegate.java b/bridge/src/android/graphics/Matrix_Delegate.java
index a503e50..354f919 100644
--- a/bridge/src/android/graphics/Matrix_Delegate.java
+++ b/bridge/src/android/graphics/Matrix_Delegate.java
@@ -27,6 +27,8 @@
import java.awt.geom.AffineTransform;
import java.awt.geom.NoninvertibleTransformException;
+import libcore.util.NativeAllocationRegistry_Delegate;
+
/**
* Delegate implementing the native methods of android.graphics.Matrix
*
@@ -47,6 +49,7 @@
// ---- delegate manager ----
private static final DelegateManager<Matrix_Delegate> sManager =
new DelegateManager<Matrix_Delegate>(Matrix_Delegate.class);
+ private static long sFinalizer = -1;
// ---- delegate data ----
private float mValues[] = new float[MATRIX_SIZE];
@@ -174,7 +177,7 @@
// ---- native methods ----
@LayoutlibDelegate
- /*package*/ static long native_create(long native_src_or_zero) {
+ /*package*/ static long nCreate(long native_src_or_zero) {
// create the delegate
Matrix_Delegate newDelegate = new Matrix_Delegate();
@@ -193,7 +196,7 @@
}
@LayoutlibDelegate
- /*package*/ static boolean native_isIdentity(long native_object) {
+ /*package*/ static boolean nIsIdentity(long native_object) {
Matrix_Delegate d = sManager.getDelegate(native_object);
if (d == null) {
return false;
@@ -203,7 +206,7 @@
}
@LayoutlibDelegate
- /*package*/ static boolean native_isAffine(long native_object) {
+ /*package*/ static boolean nIsAffine(long native_object) {
Matrix_Delegate d = sManager.getDelegate(native_object);
if (d == null) {
return true;
@@ -213,7 +216,7 @@
}
@LayoutlibDelegate
- /*package*/ static boolean native_rectStaysRect(long native_object) {
+ /*package*/ static boolean nRectStaysRect(long native_object) {
Matrix_Delegate d = sManager.getDelegate(native_object);
if (d == null) {
return true;
@@ -223,7 +226,7 @@
}
@LayoutlibDelegate
- /*package*/ static void native_reset(long native_object) {
+ /*package*/ static void nReset(long native_object) {
Matrix_Delegate d = sManager.getDelegate(native_object);
if (d == null) {
return;
@@ -233,7 +236,7 @@
}
@LayoutlibDelegate
- /*package*/ static void native_set(long native_object, long other) {
+ /*package*/ static void nSet(long native_object, long other) {
Matrix_Delegate d = sManager.getDelegate(native_object);
if (d == null) {
return;
@@ -248,7 +251,7 @@
}
@LayoutlibDelegate
- /*package*/ static void native_setTranslate(long native_object, float dx, float dy) {
+ /*package*/ static void nSetTranslate(long native_object, float dx, float dy) {
Matrix_Delegate d = sManager.getDelegate(native_object);
if (d == null) {
return;
@@ -258,7 +261,7 @@
}
@LayoutlibDelegate
- /*package*/ static void native_setScale(long native_object, float sx, float sy,
+ /*package*/ static void nSetScale(long native_object, float sx, float sy,
float px, float py) {
Matrix_Delegate d = sManager.getDelegate(native_object);
if (d == null) {
@@ -269,7 +272,7 @@
}
@LayoutlibDelegate
- /*package*/ static void native_setScale(long native_object, float sx, float sy) {
+ /*package*/ static void nSetScale(long native_object, float sx, float sy) {
Matrix_Delegate d = sManager.getDelegate(native_object);
if (d == null) {
return;
@@ -287,7 +290,7 @@
}
@LayoutlibDelegate
- /*package*/ static void native_setRotate(long native_object, float degrees, float px, float py) {
+ /*package*/ static void nSetRotate(long native_object, float degrees, float px, float py) {
Matrix_Delegate d = sManager.getDelegate(native_object);
if (d == null) {
return;
@@ -297,7 +300,7 @@
}
@LayoutlibDelegate
- /*package*/ static void native_setRotate(long native_object, float degrees) {
+ /*package*/ static void nSetRotate(long native_object, float degrees) {
Matrix_Delegate d = sManager.getDelegate(native_object);
if (d == null) {
return;
@@ -307,7 +310,7 @@
}
@LayoutlibDelegate
- /*package*/ static void native_setSinCos(long native_object, float sinValue, float cosValue,
+ /*package*/ static void nSetSinCos(long native_object, float sinValue, float cosValue,
float px, float py) {
Matrix_Delegate d = sManager.getDelegate(native_object);
if (d == null) {
@@ -326,7 +329,7 @@
}
@LayoutlibDelegate
- /*package*/ static void native_setSinCos(long native_object, float sinValue, float cosValue) {
+ /*package*/ static void nSetSinCos(long native_object, float sinValue, float cosValue) {
Matrix_Delegate d = sManager.getDelegate(native_object);
if (d == null) {
return;
@@ -336,7 +339,7 @@
}
@LayoutlibDelegate
- /*package*/ static void native_setSkew(long native_object, float kx, float ky,
+ /*package*/ static void nSetSkew(long native_object, float kx, float ky,
float px, float py) {
Matrix_Delegate d = sManager.getDelegate(native_object);
if (d == null) {
@@ -347,7 +350,7 @@
}
@LayoutlibDelegate
- /*package*/ static void native_setSkew(long native_object, float kx, float ky) {
+ /*package*/ static void nSetSkew(long native_object, float kx, float ky) {
Matrix_Delegate d = sManager.getDelegate(native_object);
if (d == null) {
return;
@@ -365,12 +368,12 @@
}
@LayoutlibDelegate
- /*package*/ static void native_setConcat(long native_object, long a, long b) {
+ /*package*/ static void nSetConcat(long native_object, long a, long b) {
if (a == native_object) {
- native_preConcat(native_object, b);
+ nPreConcat(native_object, b);
return;
} else if (b == native_object) {
- native_postConcat(native_object, a);
+ nPostConcat(native_object, a);
return;
}
@@ -383,7 +386,7 @@
}
@LayoutlibDelegate
- /*package*/ static void native_preTranslate(long native_object, float dx, float dy) {
+ /*package*/ static void nPreTranslate(long native_object, float dx, float dy) {
Matrix_Delegate d = sManager.getDelegate(native_object);
if (d != null) {
d.preTransform(getTranslate(dx, dy));
@@ -391,7 +394,7 @@
}
@LayoutlibDelegate
- /*package*/ static void native_preScale(long native_object, float sx, float sy,
+ /*package*/ static void nPreScale(long native_object, float sx, float sy,
float px, float py) {
Matrix_Delegate d = sManager.getDelegate(native_object);
if (d != null) {
@@ -400,7 +403,7 @@
}
@LayoutlibDelegate
- /*package*/ static void native_preScale(long native_object, float sx, float sy) {
+ /*package*/ static void nPreScale(long native_object, float sx, float sy) {
Matrix_Delegate d = sManager.getDelegate(native_object);
if (d != null) {
d.preTransform(getScale(sx, sy));
@@ -408,7 +411,7 @@
}
@LayoutlibDelegate
- /*package*/ static void native_preRotate(long native_object, float degrees,
+ /*package*/ static void nPreRotate(long native_object, float degrees,
float px, float py) {
Matrix_Delegate d = sManager.getDelegate(native_object);
if (d != null) {
@@ -417,7 +420,7 @@
}
@LayoutlibDelegate
- /*package*/ static void native_preRotate(long native_object, float degrees) {
+ /*package*/ static void nPreRotate(long native_object, float degrees) {
Matrix_Delegate d = sManager.getDelegate(native_object);
if (d != null) {
@@ -430,7 +433,7 @@
}
@LayoutlibDelegate
- /*package*/ static void native_preSkew(long native_object, float kx, float ky,
+ /*package*/ static void nPreSkew(long native_object, float kx, float ky,
float px, float py) {
Matrix_Delegate d = sManager.getDelegate(native_object);
if (d != null) {
@@ -439,7 +442,7 @@
}
@LayoutlibDelegate
- /*package*/ static void native_preSkew(long native_object, float kx, float ky) {
+ /*package*/ static void nPreSkew(long native_object, float kx, float ky) {
Matrix_Delegate d = sManager.getDelegate(native_object);
if (d != null) {
d.preTransform(getSkew(kx, ky));
@@ -447,7 +450,7 @@
}
@LayoutlibDelegate
- /*package*/ static void native_preConcat(long native_object, long other_matrix) {
+ /*package*/ static void nPreConcat(long native_object, long other_matrix) {
Matrix_Delegate d = sManager.getDelegate(native_object);
Matrix_Delegate other = sManager.getDelegate(other_matrix);
if (d != null && other != null) {
@@ -456,7 +459,7 @@
}
@LayoutlibDelegate
- /*package*/ static void native_postTranslate(long native_object, float dx, float dy) {
+ /*package*/ static void nPostTranslate(long native_object, float dx, float dy) {
Matrix_Delegate d = sManager.getDelegate(native_object);
if (d != null) {
d.postTransform(getTranslate(dx, dy));
@@ -464,7 +467,7 @@
}
@LayoutlibDelegate
- /*package*/ static void native_postScale(long native_object, float sx, float sy,
+ /*package*/ static void nPostScale(long native_object, float sx, float sy,
float px, float py) {
Matrix_Delegate d = sManager.getDelegate(native_object);
if (d != null) {
@@ -473,7 +476,7 @@
}
@LayoutlibDelegate
- /*package*/ static void native_postScale(long native_object, float sx, float sy) {
+ /*package*/ static void nPostScale(long native_object, float sx, float sy) {
Matrix_Delegate d = sManager.getDelegate(native_object);
if (d != null) {
d.postTransform(getScale(sx, sy));
@@ -481,7 +484,7 @@
}
@LayoutlibDelegate
- /*package*/ static void native_postRotate(long native_object, float degrees,
+ /*package*/ static void nPostRotate(long native_object, float degrees,
float px, float py) {
Matrix_Delegate d = sManager.getDelegate(native_object);
if (d != null) {
@@ -490,7 +493,7 @@
}
@LayoutlibDelegate
- /*package*/ static void native_postRotate(long native_object, float degrees) {
+ /*package*/ static void nPostRotate(long native_object, float degrees) {
Matrix_Delegate d = sManager.getDelegate(native_object);
if (d != null) {
d.postTransform(getRotate(degrees));
@@ -498,7 +501,7 @@
}
@LayoutlibDelegate
- /*package*/ static void native_postSkew(long native_object, float kx, float ky,
+ /*package*/ static void nPostSkew(long native_object, float kx, float ky,
float px, float py) {
Matrix_Delegate d = sManager.getDelegate(native_object);
if (d != null) {
@@ -507,7 +510,7 @@
}
@LayoutlibDelegate
- /*package*/ static void native_postSkew(long native_object, float kx, float ky) {
+ /*package*/ static void nPostSkew(long native_object, float kx, float ky) {
Matrix_Delegate d = sManager.getDelegate(native_object);
if (d != null) {
d.postTransform(getSkew(kx, ky));
@@ -515,7 +518,7 @@
}
@LayoutlibDelegate
- /*package*/ static void native_postConcat(long native_object, long other_matrix) {
+ /*package*/ static void nPostConcat(long native_object, long other_matrix) {
Matrix_Delegate d = sManager.getDelegate(native_object);
Matrix_Delegate other = sManager.getDelegate(other_matrix);
if (d != null && other != null) {
@@ -524,7 +527,7 @@
}
@LayoutlibDelegate
- /*package*/ static boolean native_setRectToRect(long native_object, RectF src,
+ /*package*/ static boolean nSetRectToRect(long native_object, RectF src,
RectF dst, int stf) {
Matrix_Delegate d = sManager.getDelegate(native_object);
if (d == null) {
@@ -589,7 +592,7 @@
}
@LayoutlibDelegate
- /*package*/ static boolean native_setPolyToPoly(long native_object, float[] src, int srcIndex,
+ /*package*/ static boolean nSetPolyToPoly(long native_object, float[] src, int srcIndex,
float[] dst, int dstIndex, int pointCount) {
// FIXME
Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
@@ -599,7 +602,7 @@
}
@LayoutlibDelegate
- /*package*/ static boolean native_invert(long native_object, long inverse) {
+ /*package*/ static boolean nInvert(long native_object, long inverse) {
Matrix_Delegate d = sManager.getDelegate(native_object);
if (d == null) {
return false;
@@ -627,7 +630,7 @@
}
@LayoutlibDelegate
- /*package*/ static void native_mapPoints(long native_object, float[] dst, int dstIndex,
+ /*package*/ static void nMapPoints(long native_object, float[] dst, int dstIndex,
float[] src, int srcIndex, int ptCount, boolean isPts) {
Matrix_Delegate d = sManager.getDelegate(native_object);
if (d == null) {
@@ -642,7 +645,7 @@
}
@LayoutlibDelegate
- /*package*/ static boolean native_mapRect(long native_object, RectF dst, RectF src) {
+ /*package*/ static boolean nMapRect(long native_object, RectF dst, RectF src) {
Matrix_Delegate d = sManager.getDelegate(native_object);
if (d == null) {
return false;
@@ -652,7 +655,7 @@
}
@LayoutlibDelegate
- /*package*/ static float native_mapRadius(long native_object, float radius) {
+ /*package*/ static float nMapRadius(long native_object, float radius) {
Matrix_Delegate d = sManager.getDelegate(native_object);
if (d == null) {
return 0.f;
@@ -667,7 +670,7 @@
}
@LayoutlibDelegate
- /*package*/ static void native_getValues(long native_object, float[] values) {
+ /*package*/ static void nGetValues(long native_object, float[] values) {
Matrix_Delegate d = sManager.getDelegate(native_object);
if (d == null) {
return;
@@ -677,7 +680,7 @@
}
@LayoutlibDelegate
- /*package*/ static void native_setValues(long native_object, float[] values) {
+ /*package*/ static void nSetValues(long native_object, float[] values) {
Matrix_Delegate d = sManager.getDelegate(native_object);
if (d == null) {
return;
@@ -687,7 +690,7 @@
}
@LayoutlibDelegate
- /*package*/ static boolean native_equals(long native_a, long native_b) {
+ /*package*/ static boolean nEquals(long native_a, long native_b) {
Matrix_Delegate a = sManager.getDelegate(native_a);
if (a == null) {
return false;
@@ -708,8 +711,13 @@
}
@LayoutlibDelegate
- /*package*/ static void finalizer(long native_instance) {
- sManager.removeJavaReferenceFor(native_instance);
+ /*package*/ static long nGetNativeFinalizer() {
+ synchronized (Matrix_Delegate.class) {
+ if (sFinalizer == -1) {
+ sFinalizer = NativeAllocationRegistry_Delegate.createFinalizer(sManager::removeJavaReferenceFor);
+ }
+ }
+ return sFinalizer;
}
// ---- Private helper methods ----
diff --git a/bridge/src/android/graphics/Paint_Delegate.java b/bridge/src/android/graphics/Paint_Delegate.java
index 33296e1..9b8fa99 100644
--- a/bridge/src/android/graphics/Paint_Delegate.java
+++ b/bridge/src/android/graphics/Paint_Delegate.java
@@ -90,15 +90,15 @@
private int mHintingMode = Paint.HINTING_ON;
private int mHyphenEdit;
private float mLetterSpacing; // not used in actual text rendering.
+ private float mWordSpacing; // 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;
- private Xfermode_Delegate mXfermode;
+ private int mPorterDuffMode = Xfermode.DEFAULT;
private ColorFilter_Delegate mColorFilter;
private Shader_Delegate mShader;
private PathEffect_Delegate mPathEffect;
private MaskFilter_Delegate mMaskFilter;
- private Rasterizer_Delegate mRasterizer;
private Locale mLocale = Locale.getDefault();
@@ -206,12 +206,10 @@
}
/**
- * Returns the {@link Xfermode} delegate or null if none have been set
- *
- * @return the delegate or null.
+ * Returns the {@link PorterDuff.Mode} as an int
*/
- public Xfermode_Delegate getXfermode() {
- return mXfermode;
+ public int getPorterDuffMode() {
+ return mPorterDuffMode;
}
/**
@@ -249,19 +247,10 @@
return mMaskFilter;
}
- /**
- * Returns the {@link Rasterizer} delegate or null if none have been set
- *
- * @return the delegate or null.
- */
- public Rasterizer_Delegate getRasterizer() {
- return mRasterizer;
- }
-
// ---- native methods ----
@LayoutlibDelegate
- /*package*/ static int nGetFlags(Paint thisPaint, long nativePaint) {
+ /*package*/ static int nGetFlags(long nativePaint) {
// get the delegate from the native int.
Paint_Delegate delegate = sManager.getDelegate(nativePaint);
if (delegate == null) {
@@ -274,7 +263,7 @@
@LayoutlibDelegate
- /*package*/ static void nSetFlags(Paint thisPaint, long nativePaint, int flags) {
+ /*package*/ static void nSetFlags(long nativePaint, int flags) {
// get the delegate from the native int.
Paint_Delegate delegate = sManager.getDelegate(nativePaint);
if (delegate == null) {
@@ -285,12 +274,12 @@
}
@LayoutlibDelegate
- /*package*/ static void nSetFilterBitmap(Paint thisPaint, long nativePaint, boolean filter) {
+ /*package*/ static void nSetFilterBitmap(long nativePaint, boolean filter) {
setFlag(nativePaint, Paint.FILTER_BITMAP_FLAG, filter);
}
@LayoutlibDelegate
- /*package*/ static int nGetHinting(Paint thisPaint, long nativePaint) {
+ /*package*/ static int nGetHinting(long nativePaint) {
// get the delegate from the native int.
Paint_Delegate delegate = sManager.getDelegate(nativePaint);
if (delegate == null) {
@@ -301,7 +290,7 @@
}
@LayoutlibDelegate
- /*package*/ static void nSetHinting(Paint thisPaint, long nativePaint, int mode) {
+ /*package*/ static void nSetHinting(long nativePaint, int mode) {
// get the delegate from the native int.
Paint_Delegate delegate = sManager.getDelegate(nativePaint);
if (delegate == null) {
@@ -312,46 +301,46 @@
}
@LayoutlibDelegate
- /*package*/ static void nSetAntiAlias(Paint thisPaint, long nativePaint, boolean aa) {
+ /*package*/ static void nSetAntiAlias(long nativePaint, boolean aa) {
setFlag(nativePaint, Paint.ANTI_ALIAS_FLAG, aa);
}
@LayoutlibDelegate
- /*package*/ static void nSetSubpixelText(Paint thisPaint, long nativePaint,
+ /*package*/ static void nSetSubpixelText(long nativePaint,
boolean subpixelText) {
setFlag(nativePaint, Paint.SUBPIXEL_TEXT_FLAG, subpixelText);
}
@LayoutlibDelegate
- /*package*/ static void nSetUnderlineText(Paint thisPaint, long nativePaint,
+ /*package*/ static void nSetUnderlineText(long nativePaint,
boolean underlineText) {
setFlag(nativePaint, Paint.UNDERLINE_TEXT_FLAG, underlineText);
}
@LayoutlibDelegate
- /*package*/ static void nSetStrikeThruText(Paint thisPaint, long nativePaint,
+ /*package*/ static void nSetStrikeThruText(long nativePaint,
boolean strikeThruText) {
setFlag(nativePaint, Paint.STRIKE_THRU_TEXT_FLAG, strikeThruText);
}
@LayoutlibDelegate
- /*package*/ static void nSetFakeBoldText(Paint thisPaint, long nativePaint,
+ /*package*/ static void nSetFakeBoldText(long nativePaint,
boolean fakeBoldText) {
setFlag(nativePaint, Paint.FAKE_BOLD_TEXT_FLAG, fakeBoldText);
}
@LayoutlibDelegate
- /*package*/ static void nSetDither(Paint thisPaint, long nativePaint, boolean dither) {
+ /*package*/ static void nSetDither(long nativePaint, boolean dither) {
setFlag(nativePaint, Paint.DITHER_FLAG, dither);
}
@LayoutlibDelegate
- /*package*/ static void nSetLinearText(Paint thisPaint, long nativePaint, boolean linearText) {
+ /*package*/ static void nSetLinearText(long nativePaint, boolean linearText) {
setFlag(nativePaint, Paint.LINEAR_TEXT_FLAG, linearText);
}
@LayoutlibDelegate
- /*package*/ static int nGetColor(Paint thisPaint, long nativePaint) {
+ /*package*/ static int nGetColor(long nativePaint) {
// get the delegate from the native int.
Paint_Delegate delegate = sManager.getDelegate(nativePaint);
if (delegate == null) {
@@ -362,7 +351,7 @@
}
@LayoutlibDelegate
- /*package*/ static void nSetColor(Paint thisPaint, long nativePaint, int color) {
+ /*package*/ static void nSetColor(long nativePaint, int color) {
// get the delegate from the native int.
Paint_Delegate delegate = sManager.getDelegate(nativePaint);
if (delegate == null) {
@@ -373,7 +362,7 @@
}
@LayoutlibDelegate
- /*package*/ static int nGetAlpha(Paint thisPaint, long nativePaint) {
+ /*package*/ static int nGetAlpha(long nativePaint) {
// get the delegate from the native int.
Paint_Delegate delegate = sManager.getDelegate(nativePaint);
if (delegate == null) {
@@ -384,7 +373,7 @@
}
@LayoutlibDelegate
- /*package*/ static void nSetAlpha(Paint thisPaint, long nativePaint, int a) {
+ /*package*/ static void nSetAlpha(long nativePaint, int a) {
// get the delegate from the native int.
Paint_Delegate delegate = sManager.getDelegate(nativePaint);
if (delegate == null) {
@@ -395,7 +384,7 @@
}
@LayoutlibDelegate
- /*package*/ static float nGetStrokeWidth(Paint thisPaint, long nativePaint) {
+ /*package*/ static float nGetStrokeWidth(long nativePaint) {
// get the delegate from the native int.
Paint_Delegate delegate = sManager.getDelegate(nativePaint);
if (delegate == null) {
@@ -406,7 +395,7 @@
}
@LayoutlibDelegate
- /*package*/ static void nSetStrokeWidth(Paint thisPaint, long nativePaint, float width) {
+ /*package*/ static void nSetStrokeWidth(long nativePaint, float width) {
// get the delegate from the native int.
Paint_Delegate delegate = sManager.getDelegate(nativePaint);
if (delegate == null) {
@@ -417,7 +406,7 @@
}
@LayoutlibDelegate
- /*package*/ static float nGetStrokeMiter(Paint thisPaint, long nativePaint) {
+ /*package*/ static float nGetStrokeMiter(long nativePaint) {
// get the delegate from the native int.
Paint_Delegate delegate = sManager.getDelegate(nativePaint);
if (delegate == null) {
@@ -428,7 +417,7 @@
}
@LayoutlibDelegate
- /*package*/ static void nSetStrokeMiter(Paint thisPaint, long nativePaint, float miter) {
+ /*package*/ static void nSetStrokeMiter(long nativePaint, float miter) {
// get the delegate from the native int.
Paint_Delegate delegate = sManager.getDelegate(nativePaint);
if (delegate == null) {
@@ -455,14 +444,14 @@
}
@LayoutlibDelegate
- /*package*/ static boolean nIsElegantTextHeight(Paint thisPaint, long nativePaint) {
+ /*package*/ static boolean nIsElegantTextHeight(long nativePaint) {
// get the delegate from the native int.
Paint_Delegate delegate = sManager.getDelegate(nativePaint);
return delegate != null && delegate.mFontVariant == FontVariant.ELEGANT;
}
@LayoutlibDelegate
- /*package*/ static void nSetElegantTextHeight(Paint thisPaint, long nativePaint,
+ /*package*/ static void nSetElegantTextHeight(long nativePaint,
boolean elegant) {
// get the delegate from the native int.
Paint_Delegate delegate = sManager.getDelegate(nativePaint);
@@ -474,7 +463,7 @@
}
@LayoutlibDelegate
- /*package*/ static float nGetTextSize(Paint thisPaint, long nativePaint) {
+ /*package*/ static float nGetTextSize(long nativePaint) {
// get the delegate from the native int.
Paint_Delegate delegate = sManager.getDelegate(nativePaint);
if (delegate == null) {
@@ -485,7 +474,7 @@
}
@LayoutlibDelegate
- /*package*/ static void nSetTextSize(Paint thisPaint, long nativePaint, float textSize) {
+ /*package*/ static void nSetTextSize(long nativePaint, float textSize) {
// get the delegate from the native int.
Paint_Delegate delegate = sManager.getDelegate(nativePaint);
if (delegate == null) {
@@ -499,7 +488,7 @@
}
@LayoutlibDelegate
- /*package*/ static float nGetTextScaleX(Paint thisPaint, long nativePaint) {
+ /*package*/ static float nGetTextScaleX(long nativePaint) {
// get the delegate from the native int.
Paint_Delegate delegate = sManager.getDelegate(nativePaint);
if (delegate == null) {
@@ -510,7 +499,7 @@
}
@LayoutlibDelegate
- /*package*/ static void nSetTextScaleX(Paint thisPaint, long nativePaint, float scaleX) {
+ /*package*/ static void nSetTextScaleX(long nativePaint, float scaleX) {
// get the delegate from the native int.
Paint_Delegate delegate = sManager.getDelegate(nativePaint);
if (delegate == null) {
@@ -524,7 +513,7 @@
}
@LayoutlibDelegate
- /*package*/ static float nGetTextSkewX(Paint thisPaint, long nativePaint) {
+ /*package*/ static float nGetTextSkewX(long nativePaint) {
// get the delegate from the native int.
Paint_Delegate delegate = sManager.getDelegate(nativePaint);
if (delegate == null) {
@@ -535,7 +524,7 @@
}
@LayoutlibDelegate
- /*package*/ static void nSetTextSkewX(Paint thisPaint, long nativePaint, float skewX) {
+ /*package*/ static void nSetTextSkewX(long nativePaint, float skewX) {
// get the delegate from the native int.
Paint_Delegate delegate = sManager.getDelegate(nativePaint);
if (delegate == null) {
@@ -549,7 +538,7 @@
}
@LayoutlibDelegate
- /*package*/ static float nAscent(Paint thisPaint, long nativePaint, long nativeTypeface) {
+ /*package*/ static float nAscent(long nativePaint, long nativeTypeface) {
// get the delegate
Paint_Delegate delegate = sManager.getDelegate(nativePaint);
if (delegate == null) {
@@ -566,7 +555,7 @@
}
@LayoutlibDelegate
- /*package*/ static float nDescent(Paint thisPaint, long nativePaint, long nativeTypeface) {
+ /*package*/ static float nDescent(long nativePaint, long nativeTypeface) {
// get the delegate
Paint_Delegate delegate = sManager.getDelegate(nativePaint);
if (delegate == null) {
@@ -583,7 +572,7 @@
}
@LayoutlibDelegate
- /*package*/ static float nGetFontMetrics(Paint thisPaint, long nativePaint, long nativeTypeface,
+ /*package*/ static float nGetFontMetrics(long nativePaint, long nativeTypeface,
FontMetrics metrics) {
// get the delegate
Paint_Delegate delegate = sManager.getDelegate(nativePaint);
@@ -595,7 +584,7 @@
}
@LayoutlibDelegate
- /*package*/ static int nGetFontMetricsInt(Paint thisPaint, long nativePaint,
+ /*package*/ static int nGetFontMetricsInt(long nativePaint,
long nativeTypeface, FontMetricsInt fmi) {
// get the delegate
Paint_Delegate delegate = sManager.getDelegate(nativePaint);
@@ -841,16 +830,12 @@
}
@LayoutlibDelegate
- /*package*/ static long nSetXfermode(long native_object, long xfermode) {
- // get the delegate from the native int.
+ /*package*/ static void nSetXfermode(long native_object, int xfermode) {
Paint_Delegate delegate = sManager.getDelegate(native_object);
if (delegate == null) {
- return xfermode;
+ return;
}
-
- delegate.mXfermode = Xfermode_Delegate.getDelegate(xfermode);
-
- return xfermode;
+ delegate.mPorterDuffMode = xfermode;
}
@LayoutlibDelegate
@@ -903,25 +888,6 @@
}
@LayoutlibDelegate
- /*package*/ static long nSetRasterizer(long native_object, long rasterizer) {
- // get the delegate from the native int.
- Paint_Delegate delegate = sManager.getDelegate(native_object);
- if (delegate == null) {
- return rasterizer;
- }
-
- delegate.mRasterizer = Rasterizer_Delegate.getDelegate(rasterizer);
-
- // since none of those are supported, display a fidelity warning right away
- if (delegate.mRasterizer != null && !delegate.mRasterizer.isSupported()) {
- Bridge.getLog().fidelityWarning(LayoutLog.TAG_RASTERIZER,
- delegate.mRasterizer.getSupportMessage(), null, null /*data*/);
- }
-
- return rasterizer;
- }
-
- @LayoutlibDelegate
/*package*/ static int nGetTextAlign(long native_object) {
// get the delegate from the native int.
Paint_Delegate delegate = sManager.getDelegate(native_object);
@@ -998,7 +964,7 @@
}
@LayoutlibDelegate
- /*package*/ static int nGetTextRunCursor(Paint thisPaint, long native_object, char[] text,
+ /*package*/ static int nGetTextRunCursor(Paint paint, long native_object, char[] text,
int contextStart, int contextLength, int flags, int offset, int cursorOpt) {
// FIXME
Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
@@ -1007,7 +973,7 @@
}
@LayoutlibDelegate
- /*package*/ static int nGetTextRunCursor(Paint thisPaint, long native_object, String text,
+ /*package*/ static int nGetTextRunCursor(Paint paint, long native_object, String text,
int contextStart, int contextEnd, int flags, int offset, int cursorOpt) {
// FIXME
Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
@@ -1086,6 +1052,26 @@
}
@LayoutlibDelegate
+ /*package*/ static float nGetWordSpacing(long nativePaint) {
+ Paint_Delegate delegate = sManager.getDelegate(nativePaint);
+ if (delegate == null) {
+ return 0;
+ }
+ return delegate.mWordSpacing;
+ }
+
+ @LayoutlibDelegate
+ /*package*/ static void nSetWordSpacing(long nativePaint, float wordSpacing) {
+ Bridge.getLog().fidelityWarning(LayoutLog.TAG_TEXT_RENDERING,
+ "Paint.setWordSpacing() not supported.", null, null);
+ Paint_Delegate delegate = sManager.getDelegate(nativePaint);
+ if (delegate == null) {
+ return;
+ }
+ delegate.mWordSpacing = wordSpacing;
+ }
+
+ @LayoutlibDelegate
/*package*/ static void nSetFontFeatureSettings(long nativePaint, String settings) {
Bridge.getLog().fidelityWarning(LayoutLog.TAG_TEXT_RENDERING,
"Paint.setFontFeatureSettings() not supported.", null, null);
@@ -1215,12 +1201,11 @@
mStrokeWidth = paint.mStrokeWidth;
mStrokeMiter = paint.mStrokeMiter;
- mXfermode = paint.mXfermode;
+ mPorterDuffMode = paint.mPorterDuffMode;
mColorFilter = paint.mColorFilter;
mShader = paint.mShader;
mPathEffect = paint.mPathEffect;
mMaskFilter = paint.mMaskFilter;
- mRasterizer = paint.mRasterizer;
mHintingMode = paint.mHintingMode;
if (needsFontUpdate) {
@@ -1242,12 +1227,11 @@
mTextSize = 20.f;
mTextScaleX = 1.f;
mTextSkewX = 0.f;
- mXfermode = null;
+ mPorterDuffMode = Xfermode.DEFAULT;
mColorFilter = null;
mShader = null;
mPathEffect = null;
mMaskFilter = null;
- mRasterizer = null;
updateFontObject();
mHintingMode = Paint.HINTING_ON;
}
diff --git a/bridge/src/android/graphics/PathMeasure_Delegate.java b/bridge/src/android/graphics/PathMeasure_Delegate.java
index fc9b4f7..7f707c9 100644
--- a/bridge/src/android/graphics/PathMeasure_Delegate.java
+++ b/bridge/src/android/graphics/PathMeasure_Delegate.java
@@ -56,8 +56,8 @@
if (native_path != 0) {
if (forceClosed) {
// Copy the path and call close
- native_path = Path_Delegate.init2(native_path);
- Path_Delegate.native_close(native_path);
+ native_path = Path_Delegate.nInit(native_path);
+ Path_Delegate.nClose(native_path);
}
Path_Delegate pathDelegate = Path_Delegate.getDelegate(native_path);
@@ -108,8 +108,8 @@
if (native_path != 0) {
if (forceClosed) {
// Copy the path and call close
- native_path = Path_Delegate.init2(native_path);
- Path_Delegate.native_close(native_path);
+ native_path = Path_Delegate.nInit(native_path);
+ Path_Delegate.nClose(native_path);
}
Path_Delegate pathDelegate = Path_Delegate.getDelegate(native_path);
@@ -184,28 +184,28 @@
if (type != PathIterator.SEG_MOVETO) {
float[] lastPoint = new float[2];
iterator.getCurrentSegmentEnd(lastPoint);
- Path_Delegate.native_moveTo(native_dst_path, lastPoint[0], lastPoint[1]);
+ Path_Delegate.nMoveTo(native_dst_path, lastPoint[0], lastPoint[1]);
}
}
isZeroLength = isZeroLength && iterator.getCurrentSegmentLength() > 0;
switch (type) {
case PathIterator.SEG_MOVETO:
- Path_Delegate.native_moveTo(native_dst_path, points[0], points[1]);
+ Path_Delegate.nMoveTo(native_dst_path, points[0], points[1]);
break;
case PathIterator.SEG_LINETO:
- Path_Delegate.native_lineTo(native_dst_path, points[0], points[1]);
+ Path_Delegate.nLineTo(native_dst_path, points[0], points[1]);
break;
case PathIterator.SEG_CLOSE:
- Path_Delegate.native_close(native_dst_path);
+ Path_Delegate.nClose(native_dst_path);
break;
case PathIterator.SEG_CUBICTO:
- Path_Delegate.native_cubicTo(native_dst_path, points[0], points[1],
+ Path_Delegate.nCubicTo(native_dst_path, points[0], points[1],
points[2], points[3],
points[4], points[5]);
break;
case PathIterator.SEG_QUADTO:
- Path_Delegate.native_quadTo(native_dst_path, points[0], points[1],
+ Path_Delegate.nQuadTo(native_dst_path, points[0], points[1],
points[2],
points[3]);
break;
diff --git a/bridge/src/android/graphics/Path_Delegate.java b/bridge/src/android/graphics/Path_Delegate.java
index 265ebd1..579fce0 100644
--- a/bridge/src/android/graphics/Path_Delegate.java
+++ b/bridge/src/android/graphics/Path_Delegate.java
@@ -86,6 +86,8 @@
public void reset() {
mPath.reset();
+ mLastX = 0;
+ mLastY = 0;
}
public void setPathIterator(PathIterator iterator) {
@@ -96,7 +98,7 @@
// ---- native methods ----
@LayoutlibDelegate
- /*package*/ static long init1() {
+ /*package*/ static long nInit() {
// create the delegate
Path_Delegate newDelegate = new Path_Delegate();
@@ -104,7 +106,7 @@
}
@LayoutlibDelegate
- /*package*/ static long init2(long nPath) {
+ /*package*/ static long nInit(long nPath) {
// create the delegate
Path_Delegate newDelegate = new Path_Delegate();
@@ -118,30 +120,30 @@
}
@LayoutlibDelegate
- /*package*/ static void native_reset(long nPath) {
+ /*package*/ static void nReset(long nPath) {
Path_Delegate pathDelegate = sManager.getDelegate(nPath);
if (pathDelegate == null) {
return;
}
- pathDelegate.mPath.reset();
+ pathDelegate.reset();
}
@LayoutlibDelegate
- /*package*/ static void native_rewind(long nPath) {
+ /*package*/ static void nRewind(long nPath) {
// call out to reset since there's nothing to optimize in
// terms of data structs.
- native_reset(nPath);
+ nReset(nPath);
}
@LayoutlibDelegate
- /*package*/ static void native_set(long native_dst, long native_src) {
+ /*package*/ static void nSet(long native_dst, long nSrc) {
Path_Delegate pathDstDelegate = sManager.getDelegate(native_dst);
if (pathDstDelegate == null) {
return;
}
- Path_Delegate pathSrcDelegate = sManager.getDelegate(native_src);
+ Path_Delegate pathSrcDelegate = sManager.getDelegate(nSrc);
if (pathSrcDelegate == null) {
return;
}
@@ -150,14 +152,14 @@
}
@LayoutlibDelegate
- /*package*/ static boolean native_isConvex(long nPath) {
+ /*package*/ static boolean nIsConvex(long nPath) {
Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
"Path.isConvex is not supported.", null, null);
return true;
}
@LayoutlibDelegate
- /*package*/ static int native_getFillType(long nPath) {
+ /*package*/ static int nGetFillType(long nPath) {
Path_Delegate pathDelegate = sManager.getDelegate(nPath);
if (pathDelegate == null) {
return 0;
@@ -167,7 +169,7 @@
}
@LayoutlibDelegate
- public static void native_setFillType(long nPath, int ft) {
+ public static void nSetFillType(long nPath, int ft) {
Path_Delegate pathDelegate = sManager.getDelegate(nPath);
if (pathDelegate == null) {
return;
@@ -177,14 +179,14 @@
}
@LayoutlibDelegate
- /*package*/ static boolean native_isEmpty(long nPath) {
+ /*package*/ static boolean nIsEmpty(long nPath) {
Path_Delegate pathDelegate = sManager.getDelegate(nPath);
return pathDelegate == null || pathDelegate.isEmpty();
}
@LayoutlibDelegate
- /*package*/ static boolean native_isRect(long nPath, RectF rect) {
+ /*package*/ static boolean nIsRect(long nPath, RectF rect) {
Path_Delegate pathDelegate = sManager.getDelegate(nPath);
if (pathDelegate == null) {
return false;
@@ -204,7 +206,7 @@
}
@LayoutlibDelegate
- /*package*/ static void native_computeBounds(long nPath, RectF bounds) {
+ /*package*/ static void nComputeBounds(long nPath, RectF bounds) {
Path_Delegate pathDelegate = sManager.getDelegate(nPath);
if (pathDelegate == null) {
return;
@@ -214,13 +216,13 @@
}
@LayoutlibDelegate
- /*package*/ static void native_incReserve(long nPath, int extraPtCount) {
+ /*package*/ static void nIncReserve(long nPath, int extraPtCount) {
// since we use a java2D path, there's no way to pre-allocate new points,
// so we do nothing.
}
@LayoutlibDelegate
- /*package*/ static void native_moveTo(long nPath, float x, float y) {
+ /*package*/ static void nMoveTo(long nPath, float x, float y) {
Path_Delegate pathDelegate = sManager.getDelegate(nPath);
if (pathDelegate == null) {
return;
@@ -230,7 +232,7 @@
}
@LayoutlibDelegate
- /*package*/ static void native_rMoveTo(long nPath, float dx, float dy) {
+ /*package*/ static void nRMoveTo(long nPath, float dx, float dy) {
Path_Delegate pathDelegate = sManager.getDelegate(nPath);
if (pathDelegate == null) {
return;
@@ -240,7 +242,7 @@
}
@LayoutlibDelegate
- /*package*/ static void native_lineTo(long nPath, float x, float y) {
+ /*package*/ static void nLineTo(long nPath, float x, float y) {
Path_Delegate pathDelegate = sManager.getDelegate(nPath);
if (pathDelegate == null) {
return;
@@ -250,7 +252,7 @@
}
@LayoutlibDelegate
- /*package*/ static void native_rLineTo(long nPath, float dx, float dy) {
+ /*package*/ static void nRLineTo(long nPath, float dx, float dy) {
Path_Delegate pathDelegate = sManager.getDelegate(nPath);
if (pathDelegate == null) {
return;
@@ -260,7 +262,7 @@
}
@LayoutlibDelegate
- /*package*/ static void native_quadTo(long nPath, float x1, float y1, float x2, float y2) {
+ /*package*/ static void nQuadTo(long nPath, float x1, float y1, float x2, float y2) {
Path_Delegate pathDelegate = sManager.getDelegate(nPath);
if (pathDelegate == null) {
return;
@@ -270,7 +272,7 @@
}
@LayoutlibDelegate
- /*package*/ static void native_rQuadTo(long nPath, float dx1, float dy1, float dx2, float dy2) {
+ /*package*/ static void nRQuadTo(long nPath, float dx1, float dy1, float dx2, float dy2) {
Path_Delegate pathDelegate = sManager.getDelegate(nPath);
if (pathDelegate == null) {
return;
@@ -280,7 +282,7 @@
}
@LayoutlibDelegate
- /*package*/ static void native_cubicTo(long nPath, float x1, float y1,
+ /*package*/ static void nCubicTo(long nPath, float x1, float y1,
float x2, float y2, float x3, float y3) {
Path_Delegate pathDelegate = sManager.getDelegate(nPath);
if (pathDelegate == null) {
@@ -291,7 +293,7 @@
}
@LayoutlibDelegate
- /*package*/ static void native_rCubicTo(long nPath, float x1, float y1,
+ /*package*/ static void nRCubicTo(long nPath, float x1, float y1,
float x2, float y2, float x3, float y3) {
Path_Delegate pathDelegate = sManager.getDelegate(nPath);
if (pathDelegate == null) {
@@ -302,7 +304,7 @@
}
@LayoutlibDelegate
- /*package*/ static void native_arcTo(long nPath, float left, float top, float right,
+ /*package*/ static void nArcTo(long nPath, float left, float top, float right,
float bottom,
float startAngle, float sweepAngle, boolean forceMoveTo) {
Path_Delegate pathDelegate = sManager.getDelegate(nPath);
@@ -314,7 +316,7 @@
}
@LayoutlibDelegate
- /*package*/ static void native_close(long nPath) {
+ /*package*/ static void nClose(long nPath) {
Path_Delegate pathDelegate = sManager.getDelegate(nPath);
if (pathDelegate == null) {
return;
@@ -324,7 +326,7 @@
}
@LayoutlibDelegate
- /*package*/ static void native_addRect(long nPath,
+ /*package*/ static void nAddRect(long nPath,
float left, float top, float right, float bottom, int dir) {
Path_Delegate pathDelegate = sManager.getDelegate(nPath);
if (pathDelegate == null) {
@@ -335,7 +337,7 @@
}
@LayoutlibDelegate
- /*package*/ static void native_addOval(long nPath, float left, float top, float right,
+ /*package*/ static void nAddOval(long nPath, float left, float top, float right,
float bottom, int dir) {
Path_Delegate pathDelegate = sManager.getDelegate(nPath);
if (pathDelegate == null) {
@@ -347,7 +349,7 @@
}
@LayoutlibDelegate
- /*package*/ static void native_addCircle(long nPath, float x, float y, float radius, int dir) {
+ /*package*/ static void nAddCircle(long nPath, float x, float y, float radius, int dir) {
Path_Delegate pathDelegate = sManager.getDelegate(nPath);
if (pathDelegate == null) {
return;
@@ -359,7 +361,7 @@
}
@LayoutlibDelegate
- /*package*/ static void native_addArc(long nPath, float left, float top, float right,
+ /*package*/ static void nAddArc(long nPath, float left, float top, float right,
float bottom, float startAngle, float sweepAngle) {
Path_Delegate pathDelegate = sManager.getDelegate(nPath);
if (pathDelegate == null) {
@@ -373,7 +375,7 @@
}
@LayoutlibDelegate
- /*package*/ static void native_addRoundRect(long nPath, float left, float top, float right,
+ /*package*/ static void nAddRoundRect(long nPath, float left, float top, float right,
float bottom, float rx, float ry, int dir) {
Path_Delegate pathDelegate = sManager.getDelegate(nPath);
@@ -386,7 +388,7 @@
}
@LayoutlibDelegate
- /*package*/ static void native_addRoundRect(long nPath, float left, float top, float right,
+ /*package*/ static void nAddRoundRect(long nPath, float left, float top, float right,
float bottom, float[] radii, int dir) {
Path_Delegate pathDelegate = sManager.getDelegate(nPath);
@@ -403,17 +405,17 @@
}
@LayoutlibDelegate
- /*package*/ static void native_addPath(long nPath, long src, float dx, float dy) {
+ /*package*/ static void nAddPath(long nPath, long src, float dx, float dy) {
addPath(nPath, src, AffineTransform.getTranslateInstance(dx, dy));
}
@LayoutlibDelegate
- /*package*/ static void native_addPath(long nPath, long src) {
+ /*package*/ static void nAddPath(long nPath, long src) {
addPath(nPath, src, null /*transform*/);
}
@LayoutlibDelegate
- /*package*/ static void native_addPath(long nPath, long src, long matrix) {
+ /*package*/ static void nAddPath(long nPath, long src, long matrix) {
Matrix_Delegate matrixDelegate = Matrix_Delegate.getDelegate(matrix);
if (matrixDelegate == null) {
return;
@@ -423,7 +425,7 @@
}
@LayoutlibDelegate
- /*package*/ static void native_offset(long nPath, float dx, float dy) {
+ /*package*/ static void nOffset(long nPath, float dx, float dy) {
Path_Delegate pathDelegate = sManager.getDelegate(nPath);
if (pathDelegate == null) {
return;
@@ -433,7 +435,7 @@
}
@LayoutlibDelegate
- /*package*/ static void native_setLastPoint(long nPath, float dx, float dy) {
+ /*package*/ static void nSetLastPoint(long nPath, float dx, float dy) {
Path_Delegate pathDelegate = sManager.getDelegate(nPath);
if (pathDelegate == null) {
return;
@@ -444,7 +446,7 @@
}
@LayoutlibDelegate
- /*package*/ static void native_transform(long nPath, long matrix,
+ /*package*/ static void nTransform(long nPath, long matrix,
long dst_path) {
Path_Delegate pathDelegate = sManager.getDelegate(nPath);
if (pathDelegate == null) {
@@ -463,23 +465,23 @@
}
@LayoutlibDelegate
- /*package*/ static void native_transform(long nPath, long matrix) {
- native_transform(nPath, matrix, 0);
+ /*package*/ static void nTransform(long nPath, long matrix) {
+ nTransform(nPath, matrix, 0);
}
@LayoutlibDelegate
- /*package*/ static boolean native_op(long nPath1, long nPath2, int op, long result) {
+ /*package*/ static boolean nOp(long nPath1, long nPath2, int op, long result) {
Bridge.getLog().error(LayoutLog.TAG_UNSUPPORTED, "Path.op() not supported", null);
return false;
}
@LayoutlibDelegate
- /*package*/ static void finalizer(long nPath) {
+ /*package*/ static void nFinalize(long nPath) {
sManager.removeJavaReferenceFor(nPath);
}
@LayoutlibDelegate
- /*package*/ static float[] native_approximate(long nPath, float error) {
+ /*package*/ static float[] nApproximate(long nPath, float error) {
Path_Delegate pathDelegate = sManager.getDelegate(nPath);
if (pathDelegate == null) {
return null;
diff --git a/bridge/src/android/graphics/PorterDuffXfermode_Delegate.java b/bridge/src/android/graphics/PorterDuffXfermode_Delegate.java
deleted file mode 100644
index 8825f84..0000000
--- a/bridge/src/android/graphics/PorterDuffXfermode_Delegate.java
+++ /dev/null
@@ -1,87 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.graphics;
-
-import com.android.layoutlib.bridge.impl.DelegateManager;
-import com.android.layoutlib.bridge.impl.PorterDuffUtility;
-import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
-
-import android.graphics.PorterDuff.Mode;
-
-import java.awt.Composite;
-
-import static com.android.layoutlib.bridge.impl.PorterDuffUtility.getPorterDuffMode;
-
-/**
- * Delegate implementing the native methods of android.graphics.PorterDuffXfermode
- *
- * Through the layoutlib_create tool, the original native methods of PorterDuffXfermode have been
- * replaced by calls to methods of the same name in this delegate class.
- *
- * This class behaves like the original native implementation, but in Java, keeping previously
- * native data into its own objects and mapping them to int that are sent back and forth between
- * it and the original PorterDuffXfermode class.
- *
- * Because this extends {@link Xfermode_Delegate}, there's no need to use a
- * {@link DelegateManager}, as all the PathEffect classes will be added to the manager owned by
- * {@link Xfermode_Delegate}.
- *
- */
-public class PorterDuffXfermode_Delegate extends Xfermode_Delegate {
-
- // ---- delegate data ----
-
- private final Mode mMode;
-
- // ---- Public Helper methods ----
-
- public Mode getMode() {
- return mMode;
- }
-
- @Override
- public Composite getComposite(int alpha) {
- return PorterDuffUtility.getComposite(mMode, alpha);
- }
-
- @Override
- public boolean isSupported() {
- return true;
- }
-
- @Override
- public String getSupportMessage() {
- // no message since isSupported returns true;
- return null;
- }
-
-
- // ---- native methods ----
-
- @LayoutlibDelegate
- /*package*/ static long nativeCreateXfermode(int mode) {
- PorterDuffXfermode_Delegate newDelegate = new PorterDuffXfermode_Delegate(mode);
- return sManager.addNewDelegate(newDelegate);
- }
-
- // ---- Private delegate/helper methods ----
-
- private PorterDuffXfermode_Delegate(int mode) {
- mMode = getPorterDuffMode(mode);
- }
-
-}
diff --git a/bridge/src/android/graphics/Rasterizer_Delegate.java b/bridge/src/android/graphics/Rasterizer_Delegate.java
deleted file mode 100644
index a742840..0000000
--- a/bridge/src/android/graphics/Rasterizer_Delegate.java
+++ /dev/null
@@ -1,64 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.graphics;
-
-import com.android.layoutlib.bridge.impl.DelegateManager;
-import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
-
-/**
- * Delegate implementing the native methods of android.graphics.Rasterizer
- *
- * Through the layoutlib_create tool, the original native methods of Rasterizer have been replaced
- * by calls to methods of the same name in this delegate class.
- *
- * This class behaves like the original native implementation, but in Java, keeping previously
- * native data into its own objects and mapping them to int that are sent back and forth between
- * it and the original Rasterizer class.
- *
- * This also serve as a base class for all Rasterizer delegate classes.
- *
- * @see DelegateManager
- *
- */
-public abstract class Rasterizer_Delegate {
-
- // ---- delegate manager ----
- protected static final DelegateManager<Rasterizer_Delegate> sManager =
- new DelegateManager<Rasterizer_Delegate>(Rasterizer_Delegate.class);
-
- // ---- delegate helper data ----
-
- // ---- delegate data ----
-
- // ---- Public Helper methods ----
-
- public static Rasterizer_Delegate getDelegate(long nativeShader) {
- return sManager.getDelegate(nativeShader);
- }
-
- public abstract boolean isSupported();
- public abstract String getSupportMessage();
-
- // ---- native methods ----
-
- @LayoutlibDelegate
- /*package*/ static void finalizer(long native_instance) {
- sManager.removeJavaReferenceFor(native_instance);
- }
-
- // ---- Private delegate/helper methods ----
-}
diff --git a/bridge/src/android/graphics/Typeface_Delegate.java b/bridge/src/android/graphics/Typeface_Delegate.java
index 5cd34f6..f6c463f 100644
--- a/bridge/src/android/graphics/Typeface_Delegate.java
+++ b/bridge/src/android/graphics/Typeface_Delegate.java
@@ -16,6 +16,7 @@
package android.graphics;
+import android.text.FontConfig;
import com.android.layoutlib.bridge.impl.DelegateManager;
import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
@@ -208,13 +209,14 @@
}
@LayoutlibDelegate
- /*package*/ static FontFamily makeFamilyFromParsed(FontListParser.Family family,
+ /*package*/ static FontFamily makeFamilyFromParsed(FontConfig.Family family,
Map<String, ByteBuffer> bufferForPath) {
- FontFamily fontFamily = new FontFamily(family.lang, family.variant);
- for (FontListParser.Font font : family.fonts) {
- FontFamily_Delegate.addFont(fontFamily.mNativePtr, font.fontName, font.weight,
- font.isItalic);
+ FontFamily fontFamily = new FontFamily(family.getLanguage(), family.getVariant());
+ for (FontConfig.Font font : family.getFonts()) {
+ FontFamily_Delegate.addFont(fontFamily.mBuilderPtr, font.getFontName(),
+ font.getWeight(), font.isItalic());
}
+ fontFamily.freeze();
return fontFamily;
}
diff --git a/bridge/src/android/graphics/Xfermode_Delegate.java b/bridge/src/android/graphics/Xfermode_Delegate.java
deleted file mode 100644
index 94a6d76..0000000
--- a/bridge/src/android/graphics/Xfermode_Delegate.java
+++ /dev/null
@@ -1,69 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.graphics;
-
-import com.android.layoutlib.bridge.impl.DelegateManager;
-import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
-
-import java.awt.Composite;
-
-/**
- * Delegate implementing the native methods of android.graphics.Xfermode
- *
- * Through the layoutlib_create tool, the original native methods of Xfermode have been replaced
- * by calls to methods of the same name in this delegate class.
- *
- * This class behaves like the original native implementation, but in Java, keeping previously
- * native data into its own objects and mapping them to int that are sent back and forth between
- * it and the original Xfermode class.
- *
- * This also serve as a base class for all Xfermode delegate classes.
- *
- * @see DelegateManager
- *
- */
-public abstract class Xfermode_Delegate {
-
- // ---- delegate manager ----
- protected static final DelegateManager<Xfermode_Delegate> sManager =
- new DelegateManager<Xfermode_Delegate>(Xfermode_Delegate.class);
-
- // ---- delegate helper data ----
-
- // ---- delegate data ----
-
- // ---- Public Helper methods ----
-
- public static Xfermode_Delegate getDelegate(long native_instance) {
- return sManager.getDelegate(native_instance);
- }
-
- public abstract Composite getComposite(int alpha);
- public abstract boolean isSupported();
- public abstract String getSupportMessage();
-
-
- // ---- native methods ----
-
- @LayoutlibDelegate
- /*package*/ static void finalizer(long native_instance) {
- sManager.removeJavaReferenceFor(native_instance);
- }
-
- // ---- Private delegate/helper methods ----
-
-}
diff --git a/bridge/src/android/graphics/drawable/AnimatedVectorDrawable_Delegate.java b/bridge/src/android/graphics/drawable/AnimatedVectorDrawable_Delegate.java
index 200fe3b..ad2c564 100644
--- a/bridge/src/android/graphics/drawable/AnimatedVectorDrawable_Delegate.java
+++ b/bridge/src/android/graphics/drawable/AnimatedVectorDrawable_Delegate.java
@@ -58,8 +58,13 @@
}
@LayoutlibDelegate
+ /*package*/ static void nSetVectorDrawableTarget(long animatorPtr, long vectorDrawablePtr) {
+ // TODO: implement
+ }
+ @LayoutlibDelegate
/*package*/ static void nAddAnimator(long setPtr, long propertyValuesHolder,
- long nativeInterpolator, long startDelay, long duration, int repeatCount) {
+ long nativeInterpolator, long startDelay, long duration, int repeatCount,
+ int repeatMode) {
PropertySetter holder = sHolders.getDelegate(propertyValuesHolder);
if (holder == null || holder.getValues() == null) {
return;
@@ -72,6 +77,7 @@
animator.setStartDelay(startDelay);
animator.setDuration(duration);
animator.setRepeatCount(repeatCount);
+ animator.setRepeatMode(repeatMode);
animator.setTarget(holder);
animator.setPropertyName(holder.getValues().getPropertyName());
@@ -137,6 +143,14 @@
}
@LayoutlibDelegate
+ /*package*/ static void nSetPropertyHolderData(long nativePtr, int[] data, int length) {
+ PropertySetter setter = sHolders.getDelegate(nativePtr);
+ assert setter != null;
+
+ setter.setValues(data);
+ }
+
+ @LayoutlibDelegate
/*package*/ static void nStart(long animatorSetPtr, VectorDrawableAnimatorRT set, int id) {
AnimatorSetHolder animatorSet = sAnimatorSets.getDelegate(animatorSetPtr);
assert animatorSet != null;
diff --git a/bridge/src/android/graphics/drawable/AnimatedVectorDrawable_VectorDrawableAnimatorRT_Delegate.java b/bridge/src/android/graphics/drawable/AnimatedVectorDrawable_VectorDrawableAnimatorRT_Delegate.java
index 3d78931..fc848d9 100644
--- a/bridge/src/android/graphics/drawable/AnimatedVectorDrawable_VectorDrawableAnimatorRT_Delegate.java
+++ b/bridge/src/android/graphics/drawable/AnimatedVectorDrawable_VectorDrawableAnimatorRT_Delegate.java
@@ -18,6 +18,7 @@
import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
+import android.graphics.Canvas;
import android.graphics.drawable.AnimatedVectorDrawable.VectorDrawableAnimatorRT;
public class AnimatedVectorDrawable_VectorDrawableAnimatorRT_Delegate {
@@ -25,4 +26,9 @@
/*package*/ static boolean useLastSeenTarget(VectorDrawableAnimatorRT thisDrawableAnimator) {
return true;
}
+
+ @LayoutlibDelegate
+ /*package*/ static void onDraw(VectorDrawableAnimatorRT thisDrawableAnimator, Canvas canvas) {
+ // Do not attempt to record as we are not using a DisplayListCanvas
+ }
}
diff --git a/bridge/src/android/graphics/drawable/VectorDrawable_Delegate.java b/bridge/src/android/graphics/drawable/VectorDrawable_Delegate.java
index 49f8691..430607a 100644
--- a/bridge/src/android/graphics/drawable/VectorDrawable_Delegate.java
+++ b/bridge/src/android/graphics/drawable/VectorDrawable_Delegate.java
@@ -23,6 +23,7 @@
import android.content.res.Resources;
import android.content.res.Resources.Theme;
import android.content.res.TypedArray;
+import android.graphics.BaseCanvas_Delegate;
import android.graphics.Canvas_Delegate;
import android.graphics.Color;
import android.graphics.Matrix;
@@ -70,6 +71,13 @@
private static final DelegateManager<VNativeObject> sPathManager =
new DelegateManager<>(VNativeObject.class);
+ private static long addNativeObject(VNativeObject object) {
+ long ptr = sPathManager.addNewDelegate(object);
+ object.setNativePtr(ptr);
+
+ return ptr;
+ }
+
/**
* Obtains styled attributes from the theme, if available, or unstyled resources if the theme is
* null.
@@ -91,8 +99,14 @@
@LayoutlibDelegate
static long nCreateTree(long rootGroupPtr) {
- VGroup_Delegate rootGroup = VNativeObject.getDelegate(rootGroupPtr);
- return sPathManager.addNewDelegate(new VPathRenderer_Delegate(rootGroup));
+ return addNativeObject(new VPathRenderer_Delegate(rootGroupPtr));
+ }
+
+ @LayoutlibDelegate
+ static long nCreateTreeFromCopy(long rendererToCopyPtr, long rootGroupPtr) {
+ VPathRenderer_Delegate rendererToCopy = VNativeObject.getDelegate(rendererToCopyPtr);
+ return addNativeObject(new VPathRenderer_Delegate(rendererToCopy,
+ rootGroupPtr));
}
@LayoutlibDelegate
@@ -128,12 +142,12 @@
long colorFilterPtr, Rect bounds, boolean needsMirroring, boolean canReuseCache) {
VPathRenderer_Delegate nativePathRenderer = VNativeObject.getDelegate(rendererPtr);
- Canvas_Delegate.native_save(canvasWrapperPtr, MATRIX_SAVE_FLAG | CLIP_SAVE_FLAG);
- Canvas_Delegate.native_translate(canvasWrapperPtr, bounds.left, bounds.top);
+ Canvas_Delegate.nSave(canvasWrapperPtr, MATRIX_SAVE_FLAG | CLIP_SAVE_FLAG);
+ Canvas_Delegate.nTranslate(canvasWrapperPtr, bounds.left, bounds.top);
if (needsMirroring) {
- Canvas_Delegate.native_translate(canvasWrapperPtr, bounds.width(), 0);
- Canvas_Delegate.native_scale(canvasWrapperPtr, -1.0f, 1.0f);
+ Canvas_Delegate.nTranslate(canvasWrapperPtr, bounds.width(), 0);
+ Canvas_Delegate.nScale(canvasWrapperPtr, -1.0f, 1.0f);
}
// At this point, canvas has been translated to the right position.
@@ -142,21 +156,20 @@
bounds.offsetTo(0, 0);
nativePathRenderer.draw(canvasWrapperPtr, colorFilterPtr, bounds.width(), bounds.height());
- Canvas_Delegate.native_restore(canvasWrapperPtr, true);
+ Canvas_Delegate.nRestore(canvasWrapperPtr, true);
return bounds.width() * bounds.height();
}
@LayoutlibDelegate
static long nCreateFullPath() {
- return sPathManager.addNewDelegate(new VFullPath_Delegate());
+ return addNativeObject(new VFullPath_Delegate());
}
@LayoutlibDelegate
static long nCreateFullPath(long nativeFullPathPtr) {
VFullPath_Delegate original = VNativeObject.getDelegate(nativeFullPathPtr);
-
- return sPathManager.addNewDelegate(new VFullPath_Delegate(original));
+ return addNativeObject(new VFullPath_Delegate(original));
}
@LayoutlibDelegate
@@ -222,25 +235,24 @@
@LayoutlibDelegate
static long nCreateClipPath() {
- return sPathManager.addNewDelegate(new VClipPath_Delegate());
+ return addNativeObject(new VClipPath_Delegate());
}
@LayoutlibDelegate
static long nCreateClipPath(long clipPathPtr) {
VClipPath_Delegate original = VNativeObject.getDelegate(clipPathPtr);
- return sPathManager.addNewDelegate(new VClipPath_Delegate(original));
+ return addNativeObject(new VClipPath_Delegate(original));
}
@LayoutlibDelegate
static long nCreateGroup() {
- return sPathManager.addNewDelegate(new VGroup_Delegate());
+ return addNativeObject(new VGroup_Delegate());
}
@LayoutlibDelegate
static long nCreateGroup(long groupPtr) {
VGroup_Delegate original = VNativeObject.getDelegate(groupPtr);
- return sPathManager.addNewDelegate(
- new VGroup_Delegate(original, new ArrayMap<String, Object>()));
+ return addNativeObject(new VGroup_Delegate(original, new ArrayMap<>()));
}
@LayoutlibDelegate
@@ -493,7 +505,9 @@
* not need it
* </ol>
*/
- interface VNativeObject {
+ abstract static class VNativeObject {
+ long mNativePtr = 0;
+
@NonNull
static <T> T getDelegate(long nativePtr) {
//noinspection unchecked
@@ -503,7 +517,17 @@
return vNativeObject;
}
- void setName(String name);
+ abstract void setName(String name);
+
+ void setNativePtr(long nativePtr) {
+ mNativePtr = nativePtr;
+ }
+
+ /**
+ * Method to explicitly dispose native objects
+ */
+ void dispose() {
+ }
}
private static class VClipPath_Delegate extends VPath_Delegate {
@@ -773,7 +797,7 @@
}
}
- static class VGroup_Delegate implements VNativeObject {
+ static class VGroup_Delegate extends VNativeObject {
// This constants need to be kept in sync with their definitions in VectorDrawable.Group
private static final int ROTATE_INDEX = 0;
private static final int PIVOT_X_INDEX = 1;
@@ -959,9 +983,28 @@
public void setName(String name) {
mGroupName = name;
}
+
+ @Override
+ protected void dispose() {
+ mChildren.stream().filter(child -> child instanceof VNativeObject).forEach(child
+ -> {
+ VNativeObject nativeObject = (VNativeObject) child;
+ if (nativeObject.mNativePtr != 0) {
+ sPathManager.removeJavaReferenceFor(nativeObject.mNativePtr);
+ nativeObject.mNativePtr = 0;
+ }
+ nativeObject.dispose();
+ });
+ mChildren.clear();
+ }
+
+ @Override
+ protected void finalize() throws Throwable {
+ super.finalize();
+ }
}
- public static class VPath_Delegate implements VNativeObject {
+ public static class VPath_Delegate extends VNativeObject {
protected PathParser_Delegate.PathDataNode[] mNodes = null;
String mPathName;
int mChangingConfigurations;
@@ -973,7 +1016,7 @@
public VPath_Delegate(VPath_Delegate copy) {
mPathName = copy.mPathName;
mChangingConfigurations = copy.mChangingConfigurations;
- mNodes = PathParser_Delegate.deepCopyNodes(copy.mNodes);
+ mNodes = copy.mNodes != null ? PathParser_Delegate.deepCopyNodes(copy.mNodes) : null;
}
public void toPath(Path path) {
@@ -1001,9 +1044,14 @@
PathParser_Delegate.updateNodes(mNodes, nodes);
}
}
+
+ @Override
+ void dispose() {
+ mNodes = null;
+ }
}
- static class VPathRenderer_Delegate implements VNativeObject {
+ static class VPathRenderer_Delegate extends VNativeObject {
/* Right now the internal data structure is organized as a tree.
* Each node can be a group node, or a path.
* A group node can have groups or paths as children, but a path node has
@@ -1021,7 +1069,7 @@
private final Path mPath;
private final Path mRenderPath;
private final Matrix mFinalPathMatrix = new Matrix();
- private final VGroup_Delegate mRootGroup;
+ private final long mRootGroupPtr;
private float mViewportWidth = 0;
private float mViewportHeight = 0;
private float mRootAlpha = 1.0f;
@@ -1029,12 +1077,20 @@
private Paint mFillPaint;
private PathMeasure mPathMeasure;
- private VPathRenderer_Delegate(VGroup_Delegate rootGroup) {
- mRootGroup = rootGroup;
+ private VPathRenderer_Delegate(long rootGroupPtr) {
+ mRootGroupPtr = rootGroupPtr;
mPath = new Path();
mRenderPath = new Path();
}
+ private VPathRenderer_Delegate(VPathRenderer_Delegate rendererToCopy,
+ long rootGroupPtr) {
+ this(rootGroupPtr);
+ mViewportWidth = rendererToCopy.mViewportWidth;
+ mViewportHeight = rendererToCopy.mViewportHeight;
+ mRootAlpha = rendererToCopy.mRootAlpha;
+ }
+
private float getRootAlpha() {
return mRootAlpha;
}
@@ -1053,7 +1109,7 @@
currentGroup.mStackedMatrix.preConcat(currentGroup.mLocalMatrix);
// Save the current clip information, which is local to this group.
- Canvas_Delegate.native_save(canvasPtr, MATRIX_SAVE_FLAG | CLIP_SAVE_FLAG);
+ Canvas_Delegate.nSave(canvasPtr, MATRIX_SAVE_FLAG | CLIP_SAVE_FLAG);
// Draw the group tree in the same order as the XML file.
for (int i = 0; i < currentGroup.mChildren.size(); i++) {
Object child = currentGroup.mChildren.get(i);
@@ -1066,12 +1122,12 @@
drawPath(currentGroup, childPath, canvasPtr, w, h, filterPtr);
}
}
- Canvas_Delegate.native_restore(canvasPtr, true);
+ Canvas_Delegate.nRestore(canvasPtr, true);
}
public void draw(long canvasPtr, long filterPtr, int w, int h) {
// Traverse the tree in pre-order to draw.
- drawGroupTree(mRootGroup, Matrix.IDENTITY_MATRIX, canvasPtr, w, h, filterPtr);
+ drawGroupTree(VNativeObject.getDelegate(mRootGroupPtr), Matrix.IDENTITY_MATRIX, canvasPtr, w, h, filterPtr);
}
private void drawPath(VGroup_Delegate VGroup, VPath_Delegate VPath, long canvasPtr,
@@ -1098,7 +1154,7 @@
if (VPath.isClipPath()) {
mRenderPath.addPath(path, mFinalPathMatrix);
- Canvas_Delegate.native_clipPath(canvasPtr, mRenderPath.mNativePath, Op
+ Canvas_Delegate.nClipPath(canvasPtr, mRenderPath.mNativePath, Op
.INTERSECT.nativeInt);
} else {
VFullPath_Delegate fullPath = (VFullPath_Delegate) VPath;
@@ -1133,15 +1189,16 @@
}
final Paint fillPaint = mFillPaint;
- fillPaint.setColor(applyAlpha(fullPath.mFillColor, fullPath.mFillAlpha));
+ fillPaint.setColor(applyAlpha(applyAlpha(fullPath.mFillColor, fullPath
+ .mFillAlpha), getRootAlpha()));
Paint_Delegate fillPaintDelegate = Paint_Delegate.getDelegate(fillPaint
.getNativeInstance());
// mFillPaint can not be null at this point so we will have a delegate
assert fillPaintDelegate != null;
fillPaintDelegate.setColorFilter(filterPtr);
fillPaintDelegate.setShader(fullPath.mFillGradient);
- Path_Delegate.native_setFillType(mRenderPath.mNativePath, fullPath.mFillType);
- Canvas_Delegate.native_drawPath(canvasPtr, mRenderPath.mNativePath, fillPaint
+ Path_Delegate.nSetFillType(mRenderPath.mNativePath, fullPath.mFillType);
+ BaseCanvas_Delegate.nDrawPath(canvasPtr, mRenderPath.mNativePath, fillPaint
.getNativeInstance());
}
@@ -1162,7 +1219,8 @@
}
strokePaint.setStrokeMiter(fullPath.mStrokeMiterlimit);
- strokePaint.setColor(applyAlpha(fullPath.mStrokeColor, fullPath.mStrokeAlpha));
+ strokePaint.setColor(applyAlpha(applyAlpha(fullPath.mStrokeColor, fullPath
+ .mStrokeAlpha), getRootAlpha()));
Paint_Delegate strokePaintDelegate = Paint_Delegate.getDelegate(strokePaint
.getNativeInstance());
// mStrokePaint can not be null at this point so we will have a delegate
@@ -1171,7 +1229,7 @@
final float finalStrokeScale = minScale * matrixScale;
strokePaint.setStrokeWidth(fullPath.mStrokeWidth * finalStrokeScale);
strokePaintDelegate.setShader(fullPath.mStrokeGradient);
- Canvas_Delegate.native_drawPath(canvasPtr, mRenderPath.mNativePath, strokePaint
+ BaseCanvas_Delegate.nDrawPath(canvasPtr, mRenderPath.mNativePath, strokePaint
.getNativeInstance());
}
}
@@ -1209,5 +1267,17 @@
@Override
public void setName(String name) {
}
+
+ @Override
+ protected void finalize() throws Throwable {
+ // The mRootGroupPtr is not explicitly freed by anything in the VectorDrawable so we
+ // need to free it here.
+ VNativeObject nativeObject = sPathManager.getDelegate(mRootGroupPtr);
+ sPathManager.removeJavaReferenceFor(mRootGroupPtr);
+ assert nativeObject != null;
+ nativeObject.dispose();
+
+ super.finalize();
+ }
}
}
diff --git a/bridge/src/android/os/ServiceManager.java b/bridge/src/android/os/ServiceManager.java
index 549074d..34c7845 100644
--- a/bridge/src/android/os/ServiceManager.java
+++ b/bridge/src/android/os/ServiceManager.java
@@ -31,6 +31,13 @@
}
/**
+ * Is not supposed to return null, but that is fine for layoutlib.
+ */
+ public static IBinder getServiceOrThrow(String name) throws ServiceNotFoundException {
+ throw new ServiceNotFoundException(name);
+ }
+
+ /**
* Place a new @a service called @a name into the service
* manager.
*
@@ -71,4 +78,18 @@
public static void initServiceCache(Map<String, IBinder> cache) {
// pass
}
+
+ /**
+ * Exception thrown when no service published for given name. This might be
+ * thrown early during boot before certain services have published
+ * themselves.
+ *
+ * @hide
+ */
+ public static class ServiceNotFoundException extends Exception {
+ // identical to the original implementation
+ public ServiceNotFoundException(String name) {
+ super("No service published for: " + name);
+ }
+ }
}
diff --git a/bridge/src/android/os/SystemProperties_Delegate.java b/bridge/src/android/os/SystemProperties_Delegate.java
index af0c456..d299add 100644
--- a/bridge/src/android/os/SystemProperties_Delegate.java
+++ b/bridge/src/android/os/SystemProperties_Delegate.java
@@ -102,4 +102,9 @@
/*package*/ static void native_add_change_callback() {
// pass.
}
+
+ @LayoutlibDelegate
+ /*package*/ static void native_report_sysprop_change() {
+ // pass.
+ }
}
diff --git a/bridge/src/android/preference/BridgePreferenceInflater.java b/bridge/src/android/preference/BridgePreferenceInflater.java
index 4f00b5d..aa393a9 100644
--- a/bridge/src/android/preference/BridgePreferenceInflater.java
+++ b/bridge/src/android/preference/BridgePreferenceInflater.java
@@ -21,6 +21,7 @@
import android.content.Context;
import android.util.AttributeSet;
+import android.view.InflateException;
public class BridgePreferenceInflater extends PreferenceInflater {
@@ -42,7 +43,15 @@
viewKey = ((BridgeXmlBlockParser) attrs).getViewCookie();
}
- Preference preference = super.onCreateItem(name, attrs);
+ Preference preference = null;
+ try {
+ preference = super.onCreateItem(name, attrs);
+ } catch (ClassNotFoundException | InflateException exception) {
+ // name is probably not a valid preference type
+ if ("SwitchPreferenceCompat".equals(name)) {
+ preference = super.onCreateItem("SwitchPreference", attrs);
+ }
+ }
if (viewKey != null && bc != null) {
bc.addCookie(preference, viewKey);
diff --git a/bridge/src/android/view/AttachInfo_Accessor.java b/bridge/src/android/view/AttachInfo_Accessor.java
index 94f3f54..4445a22 100644
--- a/bridge/src/android/view/AttachInfo_Accessor.java
+++ b/bridge/src/android/view/AttachInfo_Accessor.java
@@ -34,7 +34,7 @@
Display display = wm.getDefaultDisplay();
ViewRootImpl root = new ViewRootImpl(context, display);
AttachInfo info = new AttachInfo(new BridgeWindowSession(), new BridgeWindow(),
- display, root, new Handler(), null);
+ display, root, new Handler(), null, context);
info.mHasWindowFocus = true;
info.mWindowVisibility = View.VISIBLE;
info.mInTouchMode = false; // this is so that we can display selections.
@@ -51,4 +51,8 @@
view.dispatchDetachedFromWindow();
}
}
+
+ public static ViewRootImpl getRootView(View view) {
+ return view.mAttachInfo != null ? view.mAttachInfo.mViewRootImpl : null;
+ }
}
diff --git a/bridge/src/android/view/BridgeInflater.java b/bridge/src/android/view/BridgeInflater.java
index bdddfd8..b19cb58 100644
--- a/bridge/src/android/view/BridgeInflater.java
+++ b/bridge/src/android/view/BridgeInflater.java
@@ -39,11 +39,28 @@
import android.content.Context;
import android.content.res.TypedArray;
import android.util.AttributeSet;
+import android.widget.NumberPicker;
import java.io.File;
+import java.util.Arrays;
+import java.util.Collections;
import java.util.HashMap;
+import java.util.HashSet;
import java.util.Map;
+import java.util.Set;
+import static com.android.SdkConstants.AUTO_COMPLETE_TEXT_VIEW;
+import static com.android.SdkConstants.BUTTON;
+import static com.android.SdkConstants.CHECKED_TEXT_VIEW;
+import static com.android.SdkConstants.CHECK_BOX;
+import static com.android.SdkConstants.EDIT_TEXT;
+import static com.android.SdkConstants.IMAGE_BUTTON;
+import static com.android.SdkConstants.IMAGE_VIEW;
+import static com.android.SdkConstants.MULTI_AUTO_COMPLETE_TEXT_VIEW;
+import static com.android.SdkConstants.RADIO_BUTTON;
+import static com.android.SdkConstants.SEEK_BAR;
+import static com.android.SdkConstants.SPINNER;
+import static com.android.SdkConstants.TEXT_VIEW;
import static com.android.layoutlib.bridge.android.BridgeContext.getBaseContext;
/**
@@ -52,6 +69,21 @@
public final class BridgeInflater extends LayoutInflater {
private final LayoutlibCallback mLayoutlibCallback;
+ /**
+ * If true, the inflater will try to replace the framework widgets with the AppCompat versions.
+ * Ideally, this should be based on the activity being an AppCompat activity but since that is
+ * not trivial to check from layoutlib, we currently base the decision on the current theme
+ * being an AppCompat theme.
+ */
+ private boolean mLoadAppCompatViews;
+ /**
+ * This set contains the framework views that have an AppCompat version but failed to load.
+ * This might happen because not all widgets are contained in all versions of the support
+ * library.
+ * This will help us to avoid trying to load the AppCompat version multiple times if it
+ * doesn't exist.
+ */
+ private Set<String> mFailedAppCompatViews = new HashSet<>();
private boolean mIsInMerge = false;
private ResourceReference mResourceReference;
private Map<View, String> mOpenDrawerLayouts;
@@ -59,6 +91,15 @@
// Keep in sync with the same value in LayoutInflater.
private static final int[] ATTRS_THEME = new int[] {com.android.internal.R.attr.theme };
+ private static final String APPCOMPAT_WIDGET_PREFIX = "android.support.v7.widget.AppCompat";
+ /** List of platform widgets that have an AppCompat version */
+ private static final Set<String> APPCOMPAT_VIEWS = Collections.unmodifiableSet(
+ new HashSet<>(
+ Arrays.asList(TEXT_VIEW, IMAGE_VIEW, BUTTON, EDIT_TEXT, SPINNER,
+ IMAGE_BUTTON, CHECK_BOX, RADIO_BUTTON, CHECKED_TEXT_VIEW,
+ AUTO_COMPLETE_TEXT_VIEW, MULTI_AUTO_COMPLETE_TEXT_VIEW, "RatingBar",
+ SEEK_BAR)));
+
/**
* List of class prefixes which are tried first by default.
* <p/>
@@ -74,13 +115,15 @@
return sClassPrefixList;
}
- protected BridgeInflater(LayoutInflater original, Context newContext) {
+ private BridgeInflater(LayoutInflater original, Context newContext) {
super(original, newContext);
newContext = getBaseContext(newContext);
if (newContext instanceof BridgeContext) {
mLayoutlibCallback = ((BridgeContext) newContext).getLayoutlibCallback();
+ mLoadAppCompatViews = ((BridgeContext) newContext).isAppCompatTheme();
} else {
mLayoutlibCallback = null;
+ mLoadAppCompatViews = false;
}
}
@@ -90,10 +133,11 @@
* @param context The Android application context.
* @param layoutlibCallback the {@link LayoutlibCallback} object.
*/
- public BridgeInflater(Context context, LayoutlibCallback layoutlibCallback) {
+ public BridgeInflater(BridgeContext context, LayoutlibCallback layoutlibCallback) {
super(context);
mLayoutlibCallback = layoutlibCallback;
mConstructorArgs[0] = context;
+ mLoadAppCompatViews = context.isAppCompatTheme();
}
@Override
@@ -101,26 +145,39 @@
View view = null;
try {
- // First try to find a class using the default Android prefixes
- for (String prefix : sClassPrefixList) {
- try {
- view = createView(name, prefix, attrs);
- if (view != null) {
- break;
- }
- } catch (ClassNotFoundException e) {
- // Ignore. We'll try again using the base class below.
+ if (mLoadAppCompatViews
+ && APPCOMPAT_VIEWS.contains(name)
+ && !mFailedAppCompatViews.contains(name)) {
+ // We are using an AppCompat theme so try to load the appcompat views
+ view = loadCustomView(APPCOMPAT_WIDGET_PREFIX + name, attrs, true);
+
+ if (view == null) {
+ mFailedAppCompatViews.add(name); // Do not try this one anymore
}
}
- // Next try using the parent loader. This will most likely only work for
- // fully-qualified class names.
- try {
- if (view == null) {
- view = super.onCreateView(name, attrs);
+ if (view == null) {
+ // First try to find a class using the default Android prefixes
+ for (String prefix : sClassPrefixList) {
+ try {
+ view = createView(name, prefix, attrs);
+ if (view != null) {
+ break;
+ }
+ } catch (ClassNotFoundException e) {
+ // Ignore. We'll try again using the base class below.
+ }
}
- } catch (ClassNotFoundException e) {
- // Ignore. We'll try again using the custom view loader below.
+
+ // Next try using the parent loader. This will most likely only work for
+ // fully-qualified class names.
+ try {
+ if (view == null) {
+ view = super.onCreateView(name, attrs);
+ }
+ } catch (ClassNotFoundException e) {
+ // Ignore. We'll try again using the custom view loader below.
+ }
}
// Finally try again using the custom view loader
@@ -144,9 +201,26 @@
@Override
public View createViewFromTag(View parent, String name, Context context, AttributeSet attrs,
boolean ignoreThemeAttr) {
- View view;
+ View view = null;
+ if (name.equals("view")) {
+ // This is usually done by the superclass but this allows us catching the error and
+ // reporting something useful.
+ name = attrs.getAttributeValue(null, "class");
+
+ if (name == null) {
+ Bridge.getLog().error(LayoutLog.TAG_BROKEN, "Unable to inflate view tag without " +
+ "class attribute", null);
+ // We weren't able to resolve the view so we just pass a mock View to be able to
+ // continue rendering.
+ view = new MockView(context, attrs);
+ ((MockView) view).setText("view");
+ }
+ }
+
try {
- view = super.createViewFromTag(parent, name, context, attrs, ignoreThemeAttr);
+ if (view == null) {
+ view = super.createViewFromTag(parent, name, context, attrs, ignoreThemeAttr);
+ }
} catch (InflateException e) {
// Creation of ContextThemeWrapper code is same as in the super method.
// Apply a theme wrapper, if allowed and one is specified.
@@ -235,17 +309,29 @@
return null;
}
- private View loadCustomView(String name, AttributeSet attrs) throws Exception {
+ /**
+ * Instantiates the given view name and returns the instance. If the view doesn't exist, a
+ * MockView or null might be returned.
+ * @param name the custom view name
+ * @param attrs the {@link AttributeSet} to be passed to the view constructor
+ * @param silent if true, errors while loading the view won't be reported and, if the view
+ * doesn't exist, null will be returned.
+ */
+ private View loadCustomView(String name, AttributeSet attrs, boolean silent) throws Exception {
if (mLayoutlibCallback != null) {
// first get the classname in case it's not the node name
if (name.equals("view")) {
name = attrs.getAttributeValue(null, "class");
+ if (name == null) {
+ return null;
+ }
}
mConstructorArgs[1] = attrs;
- Object customView = mLayoutlibCallback.loadView(name, mConstructorSignature,
- mConstructorArgs);
+ Object customView = silent ?
+ mLayoutlibCallback.loadClass(name, mConstructorSignature, mConstructorArgs)
+ : mLayoutlibCallback.loadView(name, mConstructorSignature, mConstructorArgs);
if (customView instanceof View) {
return (View)customView;
@@ -255,6 +341,10 @@
return null;
}
+ private View loadCustomView(String name, AttributeSet attrs) throws Exception {
+ return loadCustomView(name, attrs, false);
+ }
+
private void setupViewInContext(View view, AttributeSet attrs) {
Context context = getContext();
context = getBaseContext(context);
@@ -300,6 +390,17 @@
getDrawerLayoutMap().put(view, attrVal);
}
}
+ else if (view instanceof NumberPicker) {
+ NumberPicker numberPicker = (NumberPicker) view;
+ String minValue = attrs.getAttributeValue(BridgeConstants.NS_TOOLS_URI, "minValue");
+ if (minValue != null) {
+ numberPicker.setMinValue(Integer.parseInt(minValue));
+ }
+ String maxValue = attrs.getAttributeValue(BridgeConstants.NS_TOOLS_URI, "maxValue");
+ if (maxValue != null) {
+ numberPicker.setMaxValue(Integer.parseInt(maxValue));
+ }
+ }
}
}
diff --git a/bridge/src/android/view/IWindowManagerImpl.java b/bridge/src/android/view/IWindowManagerImpl.java
index 0c3231b..56898f1 100644
--- a/bridge/src/android/view/IWindowManagerImpl.java
+++ b/bridge/src/android/view/IWindowManagerImpl.java
@@ -16,25 +16,23 @@
package android.view;
-import android.graphics.Point;
-import android.graphics.Rect;
-import com.android.internal.app.IAssistScreenshotReceiver;
-import com.android.internal.os.IResultReceiver;
-import com.android.internal.policy.IShortcutService;
-import com.android.internal.view.IInputContext;
-import com.android.internal.view.IInputMethodClient;
-
-import android.content.res.CompatibilityInfo;
import android.content.res.Configuration;
import android.graphics.Bitmap;
+import android.graphics.Point;
+import android.graphics.Rect;
import android.os.Bundle;
import android.os.IBinder;
import android.os.IRemoteCallback;
+import android.os.ParcelFileDescriptor;
import android.os.RemoteException;
import android.util.DisplayMetrics;
-import android.view.AppTransitionAnimationSpec;
-import java.lang.Override;
+import com.android.internal.app.IAssistScreenshotReceiver;
+import com.android.internal.os.IResultReceiver;
+import com.android.internal.policy.IKeyguardDismissCallback;
+import com.android.internal.policy.IShortcutService;
+import com.android.internal.view.IInputContext;
+import com.android.internal.view.IInputMethodClient;
/**
* Basic implementation of {@link IWindowManager} so that {@link Display} (and
@@ -76,16 +74,7 @@
// ---- unused implementation of IWindowManager ----
@Override
- public void addAppToken(int arg0, IApplicationToken arg1, int arg2, int arg3, int arg4,
- boolean arg5, boolean arg6, int arg7, int arg8, boolean arg9, boolean arg10,
- Rect arg11, Configuration arg12, int arg13, boolean arg14, boolean arg15, int arg16,
- int arg17)
- throws RemoteException {
- // TODO Auto-generated method stub
- }
-
- @Override
- public void addWindowToken(IBinder arg0, int arg1) throws RemoteException {
+ public void addWindowToken(IBinder arg0, int arg1, int arg2) throws RemoteException {
// TODO Auto-generated method stub
}
@@ -159,12 +148,6 @@
}
@Override
- public int getAppOrientation(IApplicationToken arg0) throws RemoteException {
- // TODO Auto-generated method stub
- return 0;
- }
-
- @Override
public int getPendingAppTransition() throws RemoteException {
// TODO Auto-generated method stub
return 0;
@@ -258,12 +241,6 @@
}
@Override
- public void pauseKeyDispatching(IBinder arg0) throws RemoteException {
- // TODO Auto-generated method stub
-
- }
-
- @Override
public void prepareAppTransition(int arg0, boolean arg1) throws RemoteException {
// TODO Auto-generated method stub
@@ -276,19 +253,7 @@
}
@Override
- public void removeAppToken(IBinder arg0) throws RemoteException {
- // TODO Auto-generated method stub
-
- }
-
- @Override
- public void removeWindowToken(IBinder arg0) throws RemoteException {
- // TODO Auto-generated method stub
-
- }
-
- @Override
- public void resumeKeyDispatching(IBinder arg0) throws RemoteException {
+ public void removeWindowToken(IBinder arg0, int arg1) throws RemoteException {
// TODO Auto-generated method stub
}
@@ -301,13 +266,6 @@
}
@Override
- public Bitmap screenshotApplications(IBinder appToken, int displayId, int maxWidth,
- int maxHeight, float frameScale) throws RemoteException {
- // TODO Auto-generated method stub
- return null;
- }
-
- @Override
public void setAnimationScale(int arg0, float arg1) throws RemoteException {
// TODO Auto-generated method stub
@@ -325,43 +283,6 @@
}
@Override
- public void setAppTask(IBinder arg0, int arg1, int arg2, Rect arg3, Configuration arg4,
- int arg5, boolean arg6)
- throws RemoteException {
- // TODO Auto-generated method stub
- }
-
- @Override
- public void setAppOrientation(IApplicationToken arg0, int arg1) throws RemoteException {
- // TODO Auto-generated method stub
- }
-
- @Override
- public boolean setAppStartingWindow(IBinder arg0, String arg1, int arg2, CompatibilityInfo arg3,
- CharSequence arg4, int arg5, int arg6, int arg7, int arg8, IBinder arg9, boolean arg10)
- throws RemoteException {
- // TODO Auto-generated method stub
- return false;
- }
-
- @Override
- public void setAppVisibility(IBinder arg0, boolean arg1) throws RemoteException {
- // TODO Auto-generated method stub
-
- }
-
- @Override
- public void notifyAppResumed(IBinder token, boolean wasStopped, boolean allowSavedSurface)
- throws RemoteException {
- // TODO Auto-generated method stub
- }
-
- @Override
- public void notifyAppStopped(IBinder token) throws RemoteException {
- // TODO Auto-generated method stub
- }
-
- @Override
public void setEventDispatching(boolean arg0) throws RemoteException {
// TODO Auto-generated method stub
}
@@ -412,13 +333,8 @@
}
@Override
- public int[] setNewConfiguration(Configuration arg0) throws RemoteException {
- // TODO Auto-generated method stub
- return null;
- }
-
- @Override
- public Rect getBoundsForNewConfiguration(int stackId) throws RemoteException {
+ public int[] setNewDisplayOverrideConfiguration(Configuration arg0, int displayId)
+ throws RemoteException {
// TODO Auto-generated method stub
return null;
}
@@ -444,11 +360,6 @@
}
@Override
- public void startAppFreezingScreen(IBinder arg0, int arg1) throws RemoteException {
- // TODO Auto-generated method stub
- }
-
- @Override
public boolean startViewServer(int arg0) throws RemoteException {
// TODO Auto-generated method stub
return false;
@@ -470,11 +381,6 @@
}
@Override
- public void stopAppFreezingScreen(IBinder arg0, boolean arg1) throws RemoteException {
- // TODO Auto-generated method stub
- }
-
- @Override
public boolean stopViewServer() throws RemoteException {
// TODO Auto-generated method stub
return false;
@@ -486,7 +392,7 @@
}
@Override
- public Configuration updateOrientationFromAppTokens(Configuration arg0, IBinder arg1)
+ public Configuration updateOrientationFromAppTokens(Configuration arg0, IBinder arg1, int arg2)
throws RemoteException {
// TODO Auto-generated method stub
return null;
@@ -514,11 +420,11 @@
}
@Override
- public void dismissKeyguard() {
+ public void dismissKeyguard(IKeyguardDismissCallback callback) throws RemoteException {
}
@Override
- public void keyguardGoingAway(int flags) throws RemoteException {
+ public void setSwitchingUser(boolean switching) throws RemoteException {
}
@Override
@@ -565,14 +471,6 @@
}
@Override
- public void cancelTaskWindowTransition(int taskId) {
- }
-
- @Override
- public void cancelTaskThumbnailTransition(int taskId) {
- }
-
- @Override
public void endProlongedAnimations() {
}
@@ -581,6 +479,20 @@
}
@Override
+ public void registerPinnedStackListener(int displayId, IPinnedStackListener listener) throws RemoteException {
+ }
+
+ @Override
+ public Rect getPictureInPictureDefaultBounds(int displayId) {
+ return null;
+ }
+
+ @Override
+ public Rect getPictureInPictureMovementBounds(int displayId) {
+ return null;
+ }
+
+ @Override
public void setResizeDimLayer(boolean visible, int targetStackId, float alpha)
throws RemoteException {
}
@@ -595,7 +507,7 @@
}
@Override
- public void getStableInsets(Rect outInsets) throws RemoteException {
+ public void getStableInsets(int displayId, Rect outInsets) throws RemoteException {
}
@Override
@@ -603,13 +515,24 @@
throws RemoteException {}
@Override
- public void createWallpaperInputConsumer(InputChannel inputChannel) throws RemoteException {}
+ public void createInputConsumer(String name, InputChannel inputChannel)
+ throws RemoteException {}
@Override
- public void removeWallpaperInputConsumer() throws RemoteException {}
+ public boolean destroyInputConsumer(String name) throws RemoteException {
+ return false;
+ }
@Override
public Bitmap screenshotWallpaper() throws RemoteException {
return null;
}
+
+ @Override
+ public void enableSurfaceTrace(ParcelFileDescriptor fd) throws RemoteException {
+ }
+
+ @Override
+ public void disableSurfaceTrace() throws RemoteException {
+ }
}
diff --git a/bridge/src/android/view/PointerIcon_Delegate.java b/bridge/src/android/view/PointerIcon_Delegate.java
new file mode 100644
index 0000000..4a5ea9b
--- /dev/null
+++ b/bridge/src/android/view/PointerIcon_Delegate.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2016 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.view;
+
+import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
+
+import android.content.Context;
+import android.content.res.Resources;
+
+public class PointerIcon_Delegate {
+
+ @LayoutlibDelegate
+ /*package*/ static void loadResource(PointerIcon icon, Context context, Resources resources,
+ int resourceId) {
+ // HACK: This bypasses the problem of having an enum resolved as a resourceId.
+ // PointerIcon would not be displayed by layoutlib anyway, so we always return the null
+ // icon.
+ }
+}
diff --git a/bridge/src/android/view/RectShadowPainter.java b/bridge/src/android/view/RectShadowPainter.java
index ea9a255..fad35d2 100644
--- a/bridge/src/android/view/RectShadowPainter.java
+++ b/bridge/src/android/view/RectShadowPainter.java
@@ -16,6 +16,7 @@
package android.view;
+import com.android.layoutlib.bridge.impl.GcSnapshot;
import com.android.layoutlib.bridge.impl.ResourceHelper;
import android.graphics.Canvas;
@@ -32,6 +33,8 @@
import android.graphics.Region.Op;
import android.graphics.Shader.TileMode;
+import java.awt.Rectangle;
+
/**
* Paints shadow for rounded rectangles. Inspiration from CardView. Couldn't use that directly,
* since it modifies the size of the content, that we can't do.
@@ -54,12 +57,18 @@
if (saved == -1) {
return;
}
+
+ float radius = viewOutline.getRadius();
+ if (radius <= 0) {
+ // We can not paint a shadow with radius 0
+ return;
+ }
+
try {
Paint cornerPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.DITHER_FLAG);
cornerPaint.setStyle(Style.FILL);
Paint edgePaint = new Paint(cornerPaint);
edgePaint.setAntiAlias(false);
- float radius = viewOutline.getRadius();
float outerArcRadius = radius + shadowSize;
int[] colors = {START_COLOR, START_COLOR, END_COLOR};
cornerPaint.setShader(new RadialGradient(0, 0, outerArcRadius, colors,
@@ -121,9 +130,16 @@
int saved = canvas.save();
// Usually canvas has been translated to the top left corner of the view when this is
// called. So, setting a clip rect at 0,0 will clip the top left part of the shadow.
- // Thus, we just expand in each direction by width and height of the canvas.
- canvas.clipRect(-canvas.getWidth(), -canvas.getHeight(), canvas.getWidth(),
- canvas.getHeight(), Op.REPLACE);
+ // Thus, we just expand in each direction by width and height of the canvas, while staying
+ // inside the original drawing region.
+ GcSnapshot snapshot = Canvas_Delegate.getDelegate(canvas).getSnapshot();
+ Rectangle originalClip = snapshot.getOriginalClip();
+ if (originalClip != null) {
+ canvas.clipRect(originalClip.x, originalClip.y, originalClip.x + originalClip.width,
+ originalClip.y + originalClip.height, Op.REPLACE);
+ canvas.clipRect(-canvas.getWidth(), -canvas.getHeight(), canvas.getWidth(),
+ canvas.getHeight(), Op.INTERSECT);
+ }
canvas.translate(0, shadowSize / 2f);
return saved;
}
diff --git a/bridge/src/android/view/RenderNode_Delegate.java b/bridge/src/android/view/RenderNode_Delegate.java
index 24f7887..a801cb0 100644
--- a/bridge/src/android/view/RenderNode_Delegate.java
+++ b/bridge/src/android/view/RenderNode_Delegate.java
@@ -21,6 +21,8 @@
import android.graphics.Matrix;
+import libcore.util.NativeAllocationRegistry_Delegate;
+
/**
* Delegate implementing the native methods of {@link RenderNode}
* <p/>
@@ -35,7 +37,7 @@
// ---- delegate manager ----
private static final DelegateManager<RenderNode_Delegate> sManager =
new DelegateManager<RenderNode_Delegate>(RenderNode_Delegate.class);
-
+ private static long sFinalizer = -1;
private float mLift;
private float mTranslationX;
@@ -62,8 +64,13 @@
}
@LayoutlibDelegate
- /*package*/ static void nDestroyRenderNode(long renderNode) {
- sManager.removeJavaReferenceFor(renderNode);
+ /*package*/ static long nGetNativeFinalizer() {
+ synchronized (RenderNode_Delegate.class) {
+ if (sFinalizer == -1) {
+ sFinalizer = NativeAllocationRegistry_Delegate.createFinalizer(sManager::removeJavaReferenceFor);
+ }
+ }
+ return sFinalizer;
}
@LayoutlibDelegate
diff --git a/bridge/src/android/view/SurfaceView.java b/bridge/src/android/view/SurfaceView.java
index 1e7dfbe..ebb2af4 100644
--- a/bridge/src/android/view/SurfaceView.java
+++ b/bridge/src/android/view/SurfaceView.java
@@ -21,6 +21,7 @@
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Rect;
+import android.graphics.Region;
import android.util.AttributeSet;
/**
@@ -49,6 +50,19 @@
super(context, attrs, defStyleAttr, defStyleRes);
}
+ public boolean gatherTransparentRegion(Region region) {
+ return false;
+ }
+
+ public void setZOrderMediaOverlay(boolean isMediaOverlay) {
+ }
+
+ public void setZOrderOnTop(boolean onTop) {
+ }
+
+ public void setSecure(boolean isSecure) {
+ }
+
public SurfaceHolder getHolder() {
return mSurfaceHolder;
}
diff --git a/bridge/src/android/view/ViewRootImpl_Accessor.java b/bridge/src/android/view/ViewRootImpl_Accessor.java
new file mode 100644
index 0000000..0e15b97
--- /dev/null
+++ b/bridge/src/android/view/ViewRootImpl_Accessor.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2016 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.view;
+
+/**
+ * Accessor to allow layoutlib to call {@link ViewRootImpl#dispatchApplyInsets} directly.
+ */
+public class ViewRootImpl_Accessor {
+ public static void dispatchApplyInsets(ViewRootImpl viewRoot, View host) {
+ viewRoot.dispatchApplyInsets(host);
+ }
+}
diff --git a/bridge/src/android/view/accessibility/AccessibilityManager.java b/bridge/src/android/view/accessibility/AccessibilityManager.java
index edb5eff..3ce7cab 100644
--- a/bridge/src/android/view/accessibility/AccessibilityManager.java
+++ b/bridge/src/android/view/accessibility/AccessibilityManager.java
@@ -92,6 +92,9 @@
new IAccessibilityManagerClient.Stub() {
public void setState(int state) {
}
+
+ public void notifyServicesStateChanged() {
+ }
};
/**
diff --git a/bridge/src/android/view/textservice/TextServicesManager.java b/bridge/src/android/view/textservice/TextServicesManager.java
new file mode 100644
index 0000000..06874bd
--- /dev/null
+++ b/bridge/src/android/view/textservice/TextServicesManager.java
@@ -0,0 +1,342 @@
+/*
+ * Copyright (C) 2016 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.view.textservice;
+
+import com.android.internal.textservice.ISpellCheckerSessionListener;
+import com.android.internal.textservice.ITextServicesManager;
+import com.android.internal.textservice.ITextServicesSessionListener;
+
+import android.content.Context;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.util.Log;
+import android.view.textservice.SpellCheckerSession.SpellCheckerSessionListener;
+
+import java.util.Locale;
+
+/**
+ * System API to the overall text services, which arbitrates interaction between applications
+ * and text services. You can retrieve an instance of this interface with
+ * {@link Context#getSystemService(String) Context.getSystemService()}.
+ *
+ * The user can change the current text services in Settings. And also applications can specify
+ * the target text services.
+ *
+ * <h3>Architecture Overview</h3>
+ *
+ * <p>There are three primary parties involved in the text services
+ * framework (TSF) architecture:</p>
+ *
+ * <ul>
+ * <li> The <strong>text services manager</strong> as expressed by this class
+ * is the central point of the system that manages interaction between all
+ * other parts. It is expressed as the client-side API here which exists
+ * in each application context and communicates with a global system service
+ * that manages the interaction across all processes.
+ * <li> A <strong>text service</strong> implements a particular
+ * interaction model allowing the client application to retrieve information of text.
+ * The system binds to the current text service that is in use, causing it to be created and run.
+ * <li> Multiple <strong>client applications</strong> arbitrate with the text service
+ * manager for connections to text services.
+ * </ul>
+ *
+ * <h3>Text services sessions</h3>
+ * <ul>
+ * <li>The <strong>spell checker session</strong> is one of the text services.
+ * {@link android.view.textservice.SpellCheckerSession}</li>
+ * </ul>
+ *
+ */
+public final class TextServicesManager {
+ private static final String TAG = TextServicesManager.class.getSimpleName();
+ private static final boolean DBG = false;
+
+ private static TextServicesManager sInstance;
+
+ private final ITextServicesManager mService;
+
+ private TextServicesManager() {
+ mService = new FakeTextServicesManager();
+ }
+
+ /**
+ * Retrieve the global TextServicesManager instance, creating it if it doesn't already exist.
+ * @hide
+ */
+ public static TextServicesManager getInstance() {
+ synchronized (TextServicesManager.class) {
+ if (sInstance == null) {
+ sInstance = new TextServicesManager();
+ }
+ return sInstance;
+ }
+ }
+
+ /**
+ * Returns the language component of a given locale string.
+ */
+ private static String parseLanguageFromLocaleString(String locale) {
+ final int idx = locale.indexOf('_');
+ if (idx < 0) {
+ return locale;
+ } else {
+ return locale.substring(0, idx);
+ }
+ }
+
+ /**
+ * Get a spell checker session for the specified spell checker
+ * @param locale the locale for the spell checker. If {@code locale} is null and
+ * referToSpellCheckerLanguageSettings is true, the locale specified in Settings will be
+ * returned. If {@code locale} is not null and referToSpellCheckerLanguageSettings is true,
+ * the locale specified in Settings will be returned only when it is same as {@code locale}.
+ * Exceptionally, when referToSpellCheckerLanguageSettings is true and {@code locale} is
+ * only language (e.g. "en"), the specified locale in Settings (e.g. "en_US") will be
+ * selected.
+ * @param listener a spell checker session lister for getting results from a spell checker.
+ * @param referToSpellCheckerLanguageSettings if true, the session for one of enabled
+ * languages in settings will be returned.
+ * @return the spell checker session of the spell checker
+ */
+ public SpellCheckerSession newSpellCheckerSession(Bundle bundle, Locale locale,
+ SpellCheckerSessionListener listener, boolean referToSpellCheckerLanguageSettings) {
+ if (listener == null) {
+ throw new NullPointerException();
+ }
+ if (!referToSpellCheckerLanguageSettings && locale == null) {
+ throw new IllegalArgumentException("Locale should not be null if you don't refer"
+ + " settings.");
+ }
+
+ if (referToSpellCheckerLanguageSettings && !isSpellCheckerEnabled()) {
+ return null;
+ }
+
+ final SpellCheckerInfo sci;
+ try {
+ sci = mService.getCurrentSpellChecker(null);
+ } catch (RemoteException e) {
+ return null;
+ }
+ if (sci == null) {
+ return null;
+ }
+ SpellCheckerSubtype subtypeInUse = null;
+ if (referToSpellCheckerLanguageSettings) {
+ subtypeInUse = getCurrentSpellCheckerSubtype(true);
+ if (subtypeInUse == null) {
+ return null;
+ }
+ if (locale != null) {
+ final String subtypeLocale = subtypeInUse.getLocale();
+ final String subtypeLanguage = parseLanguageFromLocaleString(subtypeLocale);
+ if (subtypeLanguage.length() < 2 || !locale.getLanguage().equals(subtypeLanguage)) {
+ return null;
+ }
+ }
+ } else {
+ final String localeStr = locale.toString();
+ for (int i = 0; i < sci.getSubtypeCount(); ++i) {
+ final SpellCheckerSubtype subtype = sci.getSubtypeAt(i);
+ final String tempSubtypeLocale = subtype.getLocale();
+ final String tempSubtypeLanguage = parseLanguageFromLocaleString(tempSubtypeLocale);
+ if (tempSubtypeLocale.equals(localeStr)) {
+ subtypeInUse = subtype;
+ break;
+ } else if (tempSubtypeLanguage.length() >= 2 &&
+ locale.getLanguage().equals(tempSubtypeLanguage)) {
+ subtypeInUse = subtype;
+ }
+ }
+ }
+ if (subtypeInUse == null) {
+ return null;
+ }
+ final SpellCheckerSession session = new SpellCheckerSession(
+ sci, mService, listener, subtypeInUse);
+ try {
+ mService.getSpellCheckerService(sci.getId(), subtypeInUse.getLocale(),
+ session.getTextServicesSessionListener(),
+ session.getSpellCheckerSessionListener(), bundle);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ return session;
+ }
+
+ /**
+ * @hide
+ */
+ public SpellCheckerInfo[] getEnabledSpellCheckers() {
+ try {
+ final SpellCheckerInfo[] retval = mService.getEnabledSpellCheckers();
+ if (DBG) {
+ Log.d(TAG, "getEnabledSpellCheckers: " + (retval != null ? retval.length : "null"));
+ }
+ return retval;
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * @hide
+ */
+ public SpellCheckerInfo getCurrentSpellChecker() {
+ try {
+ // Passing null as a locale for ICS
+ return mService.getCurrentSpellChecker(null);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * @hide
+ */
+ public void setCurrentSpellChecker(SpellCheckerInfo sci) {
+ try {
+ if (sci == null) {
+ throw new NullPointerException("SpellCheckerInfo is null.");
+ }
+ mService.setCurrentSpellChecker(null, sci.getId());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * @hide
+ */
+ public SpellCheckerSubtype getCurrentSpellCheckerSubtype(
+ boolean allowImplicitlySelectedSubtype) {
+ try {
+ // Passing null as a locale until we support multiple enabled spell checker subtypes.
+ return mService.getCurrentSpellCheckerSubtype(null, allowImplicitlySelectedSubtype);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * @hide
+ */
+ public void setSpellCheckerSubtype(SpellCheckerSubtype subtype) {
+ try {
+ final int hashCode;
+ if (subtype == null) {
+ hashCode = 0;
+ } else {
+ hashCode = subtype.hashCode();
+ }
+ mService.setCurrentSpellCheckerSubtype(null, hashCode);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * @hide
+ */
+ public void setSpellCheckerEnabled(boolean enabled) {
+ try {
+ mService.setSpellCheckerEnabled(enabled);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * @hide
+ */
+ public boolean isSpellCheckerEnabled() {
+ try {
+ return mService.isSpellCheckerEnabled();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ private static class FakeTextServicesManager implements ITextServicesManager {
+
+ @Override
+ public void finishSpellCheckerService(ISpellCheckerSessionListener arg0)
+ throws RemoteException {
+ // TODO Auto-generated method stub
+
+ }
+
+ @Override
+ public SpellCheckerInfo getCurrentSpellChecker(String arg0) throws RemoteException {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ public SpellCheckerSubtype getCurrentSpellCheckerSubtype(String arg0, boolean arg1)
+ throws RemoteException {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ public SpellCheckerInfo[] getEnabledSpellCheckers() throws RemoteException {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ public void getSpellCheckerService(String arg0, String arg1,
+ ITextServicesSessionListener arg2, ISpellCheckerSessionListener arg3, Bundle arg4)
+ throws RemoteException {
+ // TODO Auto-generated method stub
+
+ }
+
+ @Override
+ public boolean isSpellCheckerEnabled() throws RemoteException {
+ // TODO Auto-generated method stub
+ return false;
+ }
+
+ @Override
+ public void setCurrentSpellChecker(String arg0, String arg1) throws RemoteException {
+ // TODO Auto-generated method stub
+
+ }
+
+ @Override
+ public void setCurrentSpellCheckerSubtype(String arg0, int arg1) throws RemoteException {
+ // TODO Auto-generated method stub
+
+ }
+
+ @Override
+ public void setSpellCheckerEnabled(boolean arg0) throws RemoteException {
+ // TODO Auto-generated method stub
+
+ }
+
+ @Override
+ public IBinder asBinder() {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ }
+}
\ No newline at end of file
diff --git a/bridge/src/com/android/internal/textservice/ITextServicesManager_Stub_Delegate.java b/bridge/src/com/android/internal/textservice/ITextServicesManager_Stub_Delegate.java
deleted file mode 100644
index 3017292..0000000
--- a/bridge/src/com/android/internal/textservice/ITextServicesManager_Stub_Delegate.java
+++ /dev/null
@@ -1,111 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.internal.textservice;
-
-import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
-
-import android.os.Bundle;
-import android.os.IBinder;
-import android.os.RemoteException;
-import android.view.textservice.SpellCheckerInfo;
-import android.view.textservice.SpellCheckerSubtype;
-
-
-/**
- * Delegate used to provide new implementation of a select few methods of
- * {@link ITextServicesManager$Stub}
- *
- * Through the layoutlib_create tool, the original methods of Stub have been replaced
- * by calls to methods of the same name in this delegate class.
- *
- */
-public class ITextServicesManager_Stub_Delegate {
-
- @LayoutlibDelegate
- public static ITextServicesManager asInterface(IBinder obj) {
- // ignore the obj and return a fake interface implementation
- return new FakeTextServicesManager();
- }
-
- private static class FakeTextServicesManager implements ITextServicesManager {
-
- @Override
- public void finishSpellCheckerService(ISpellCheckerSessionListener arg0)
- throws RemoteException {
- // TODO Auto-generated method stub
-
- }
-
- @Override
- public SpellCheckerInfo getCurrentSpellChecker(String arg0) throws RemoteException {
- // TODO Auto-generated method stub
- return null;
- }
-
- @Override
- public SpellCheckerSubtype getCurrentSpellCheckerSubtype(String arg0, boolean arg1)
- throws RemoteException {
- // TODO Auto-generated method stub
- return null;
- }
-
- @Override
- public SpellCheckerInfo[] getEnabledSpellCheckers() throws RemoteException {
- // TODO Auto-generated method stub
- return null;
- }
-
- @Override
- public void getSpellCheckerService(String arg0, String arg1,
- ITextServicesSessionListener arg2, ISpellCheckerSessionListener arg3, Bundle arg4)
- throws RemoteException {
- // TODO Auto-generated method stub
-
- }
-
- @Override
- public boolean isSpellCheckerEnabled() throws RemoteException {
- // TODO Auto-generated method stub
- return false;
- }
-
- @Override
- public void setCurrentSpellChecker(String arg0, String arg1) throws RemoteException {
- // TODO Auto-generated method stub
-
- }
-
- @Override
- public void setCurrentSpellCheckerSubtype(String arg0, int arg1) throws RemoteException {
- // TODO Auto-generated method stub
-
- }
-
- @Override
- public void setSpellCheckerEnabled(boolean arg0) throws RemoteException {
- // TODO Auto-generated method stub
-
- }
-
- @Override
- public IBinder asBinder() {
- // TODO Auto-generated method stub
- return null;
- }
-
- }
- }
diff --git a/bridge/src/com/android/internal/view/animation/NativeInterpolatorFactoryHelper_Delegate.java b/bridge/src/com/android/internal/view/animation/NativeInterpolatorFactoryHelper_Delegate.java
index ffce1a0..da1ab27 100644
--- a/bridge/src/com/android/internal/view/animation/NativeInterpolatorFactoryHelper_Delegate.java
+++ b/bridge/src/com/android/internal/view/animation/NativeInterpolatorFactoryHelper_Delegate.java
@@ -19,6 +19,7 @@
import com.android.layoutlib.bridge.impl.DelegateManager;
import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
+import android.graphics.Path;
import android.util.MathUtils;
import android.view.animation.AccelerateDecelerateInterpolator;
import android.view.animation.AccelerateInterpolator;
@@ -31,6 +32,7 @@
import android.view.animation.Interpolator;
import android.view.animation.LinearInterpolator;
import android.view.animation.OvershootInterpolator;
+import android.view.animation.PathInterpolator;
/**
* Delegate used to provide new implementation of a select few methods of {@link
@@ -93,6 +95,16 @@
return sManager.addNewDelegate(new OvershootInterpolator(tension));
}
+ @LayoutlibDelegate
+ /*package*/ static long createPathInterpolator(float[] x, float[] y) {
+ Path path = new Path();
+ path.moveTo(x[0], y[0]);
+ for (int i = 1; i < x.length; i++) {
+ path.lineTo(x[i], y[i]);
+ }
+ return sManager.addNewDelegate(new PathInterpolator(path));
+ }
+
private static class LutInterpolator extends BaseInterpolator {
private final float[] mValues;
private final int mSize;
diff --git a/bridge/src/com/android/layoutlib/bridge/BridgeRenderSession.java b/bridge/src/com/android/layoutlib/bridge/BridgeRenderSession.java
index 3ac1889..77b131f 100644
--- a/bridge/src/com/android/layoutlib/bridge/BridgeRenderSession.java
+++ b/bridge/src/com/android/layoutlib/bridge/BridgeRenderSession.java
@@ -76,6 +76,23 @@
}
@Override
+ public Result measure(long timeout) {
+ try {
+ Bridge.prepareThread();
+ mLastResult = mSession.acquire(timeout);
+ if (mLastResult.isSuccess()) {
+ mSession.invalidateRenderingSize();
+ mLastResult = mSession.measure();
+ }
+ } finally {
+ mSession.release();
+ Bridge.cleanupThread();
+ }
+
+ return mLastResult;
+ }
+
+ @Override
public Result render(long timeout, boolean forceMeasure) {
try {
Bridge.prepareThread();
diff --git a/bridge/src/com/android/layoutlib/bridge/android/BridgeContentProvider.java b/bridge/src/com/android/layoutlib/bridge/android/BridgeContentProvider.java
index e4cbb2f..c827f17 100644
--- a/bridge/src/com/android/layoutlib/bridge/android/BridgeContentProvider.java
+++ b/bridge/src/com/android/layoutlib/bridge/android/BridgeContentProvider.java
@@ -97,8 +97,8 @@
}
@Override
- public Cursor query(String callingPackage, Uri arg0, String[] arg1, String arg2, String[] arg3,
- String arg4, ICancellationSignal arg5) throws RemoteException {
+ public Cursor query(String callingPackage, Uri arg0, String[] arg1,
+ Bundle arg3, ICancellationSignal arg4) throws RemoteException {
// TODO Auto-generated method stub
return null;
}
@@ -145,4 +145,10 @@
public Uri uncanonicalize(String callingPkg, Uri uri) throws RemoteException {
return null;
}
+
+ @Override
+ public boolean refresh(String callingPkg, Uri url, Bundle args,
+ ICancellationSignal cancellationSignal) throws RemoteException {
+ return false;
+ }
}
diff --git a/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java b/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java
index 616cb57..dff4f69 100644
--- a/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java
+++ b/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java
@@ -40,6 +40,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.app.Notification;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.ContentResolver;
@@ -66,7 +67,6 @@
import android.graphics.drawable.Drawable;
import android.hardware.display.DisplayManager;
import android.net.Uri;
-import android.os.Build.VERSION_CODES;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
@@ -76,6 +76,7 @@
import android.os.PowerManager;
import android.os.RemoteException;
import android.os.ResultReceiver;
+import android.os.ShellCallback;
import android.os.UserHandle;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
@@ -103,6 +104,7 @@
import java.util.List;
import java.util.Map;
+import static android.os.Build.VERSION_CODES.JELLY_BEAN_MR1;
import static com.android.layoutlib.bridge.android.RenderParamsFlags.FLAG_KEY_APPLICATION_PACKAGE;
/**
@@ -110,6 +112,29 @@
*/
@SuppressWarnings("deprecation") // For use of Pair.
public final class BridgeContext extends Context {
+ private static final String PREFIX_THEME_APPCOMPAT = "Theme.AppCompat";
+
+ private static final Map<String, ResourceValue> FRAMEWORK_PATCHED_VALUES = new HashMap<>(2);
+ private static final Map<String, ResourceValue> FRAMEWORK_REPLACE_VALUES = new HashMap<>(3);
+
+ static {
+ FRAMEWORK_PATCHED_VALUES.put("animateFirstView", new ResourceValue(
+ ResourceType.BOOL, "animateFirstView", "false", false));
+ FRAMEWORK_PATCHED_VALUES.put("animateLayoutChanges",
+ new ResourceValue(ResourceType.BOOL, "animateLayoutChanges", "false", false));
+
+
+ FRAMEWORK_REPLACE_VALUES.put("textEditSuggestionItemLayout",
+ new ResourceValue(ResourceType.LAYOUT, "textEditSuggestionItemLayout",
+ "text_edit_suggestion_item", true));
+ FRAMEWORK_REPLACE_VALUES.put("textEditSuggestionContainerLayout",
+ new ResourceValue(ResourceType.LAYOUT, "textEditSuggestionContainerLayout",
+ "text_edit_suggestion_container", true));
+ FRAMEWORK_REPLACE_VALUES.put("textEditSuggestionHighlightStyle",
+ new ResourceValue(ResourceType.STYLE, "textEditSuggestionHighlightStyle",
+ "TextAppearance.Holo.SuggestionHighlight", true));
+
+ }
/** The map adds cookies to each view so that IDE can link xml tags to views. */
private final HashMap<View, Object> mViewKeyMap = new HashMap<>();
@@ -153,6 +178,7 @@
private ClassLoader mClassLoader;
private IBinder mBinder;
private PackageManager mPackageManager;
+ private Boolean mIsThemeAppCompat;
/**
* Some applications that target both pre API 17 and post API 17, set the newer attrs to
@@ -309,7 +335,7 @@
* Returns the current parser at the top the of the stack.
* @return a parser or null.
*/
- public BridgeXmlBlockParser getCurrentParser() {
+ private BridgeXmlBlockParser getCurrentParser() {
return mParserStack.peek();
}
@@ -371,7 +397,9 @@
return true;
}
- return false;
+ // If the value is not a valid reference, fallback to pass the value as a string.
+ outValue.string = value.getValue();
+ return true;
}
@@ -401,7 +429,8 @@
}
public Pair<View, Boolean> inflateView(ResourceReference resource, ViewGroup parent,
- boolean attachToRoot, boolean skipCallbackParser) {
+ @SuppressWarnings("SameParameterValue") boolean attachToRoot,
+ boolean skipCallbackParser) {
boolean isPlatformLayout = resource.isFramework();
if (!isPlatformLayout && !skipCallbackParser) {
@@ -479,6 +508,36 @@
return Pair.of(null, Boolean.FALSE);
}
+ /**
+ * Returns whether the current selected theme is based on AppCompat
+ */
+ public boolean isAppCompatTheme() {
+ // If a cached value exists, return it.
+ if (mIsThemeAppCompat != null) {
+ return mIsThemeAppCompat;
+ }
+ // Ideally, we should check if the corresponding activity extends
+ // android.support.v7.app.ActionBarActivity, and not care about the theme name at all.
+ StyleResourceValue defaultTheme = mRenderResources.getDefaultTheme();
+ // We can't simply check for parent using resources.themeIsParentOf() since the
+ // inheritance structure isn't really what one would expect. The first common parent
+ // between Theme.AppCompat.Light and Theme.AppCompat is Theme.Material (for v21).
+ boolean isThemeAppCompat = false;
+ for (int i = 0; i < 50; i++) {
+ if (defaultTheme == null) {
+ break;
+ }
+ // for loop ensures that we don't run into cyclic theme inheritance.
+ if (defaultTheme.getName().startsWith(PREFIX_THEME_APPCOMPAT)) {
+ isThemeAppCompat = true;
+ break;
+ }
+ defaultTheme = mRenderResources.getParent(defaultTheme);
+ }
+ mIsThemeAppCompat = isThemeAppCompat;
+ return isThemeAppCompat;
+ }
+
@SuppressWarnings("deprecation")
private ILayoutPullParser getParser(ResourceReference resource) {
ILayoutPullParser parser;
@@ -676,11 +735,7 @@
Object key = parser.getViewCookie();
if (key != null) {
- defaultPropMap = mDefaultPropMaps.get(key);
- if (defaultPropMap == null) {
- defaultPropMap = new PropertiesMap();
- mDefaultPropMaps.put(key, defaultPropMap);
- }
+ defaultPropMap = mDefaultPropMaps.computeIfAbsent(key, k -> new PropertiesMap());
}
} else if (set instanceof BridgeLayoutParamsMapAttributes) {
@@ -831,60 +886,85 @@
}
}
- // if there's no direct value for this attribute in the XML, we look for default
- // values in the widget defStyle, and then in the theme.
- if (value == null) {
- ResourceValue resValue = null;
-
+ // Calculate the default value from the Theme in two cases:
+ // - If defaultPropMap is not null, get the default value to add it to the list
+ // of default values of properties.
+ // - If value is null, it means that the attribute is not directly set as an
+ // attribute in the XML so try to get the default value.
+ ResourceValue defaultValue = null;
+ if (defaultPropMap != null || value == null) {
// look for the value in the custom style first (and its parent if needed)
if (customStyleValues != null) {
- resValue = mRenderResources.findItemInStyle(customStyleValues,
- attrName, frameworkAttr);
+ defaultValue = mRenderResources.findItemInStyle(customStyleValues, attrName,
+ frameworkAttr);
}
// then look for the value in the default Style (and its parent if needed)
- if (resValue == null && defStyleValues != null) {
- resValue = mRenderResources.findItemInStyle(defStyleValues,
- attrName, frameworkAttr);
+ if (defaultValue == null && defStyleValues != null) {
+ defaultValue = mRenderResources.findItemInStyle(defStyleValues, attrName,
+ frameworkAttr);
}
// if the item is not present in the defStyle, we look in the main theme (and
// its parent themes)
- if (resValue == null) {
- resValue = mRenderResources.findItemInTheme(attrName, frameworkAttr);
+ if (defaultValue == null) {
+ defaultValue = mRenderResources.findItemInTheme(attrName, frameworkAttr);
}
// if we found a value, we make sure this doesn't reference another value.
// So we resolve it.
- if (resValue != null) {
- String preResolve = resValue.getValue();
- resValue = mRenderResources.resolveResValue(resValue);
+ if (defaultValue != null) {
+ String preResolve = defaultValue.getValue();
+ defaultValue = mRenderResources.resolveResValue(defaultValue);
if (defaultPropMap != null) {
defaultPropMap.put(
frameworkAttr ? SdkConstants.PREFIX_ANDROID + attrName :
- attrName,
- new Property(preResolve, resValue.getValue()));
+ attrName, new Property(preResolve, defaultValue.getValue()));
}
+ }
+ }
+ // Done calculating the defaultValue
+ // if there's no direct value for this attribute in the XML, we look for default
+ // values in the widget defStyle, and then in the theme.
+ if (value == null) {
+ if (frameworkAttr) {
+ // For some framework values, layoutlib patches the actual value in the
+ // theme when it helps to improve the final preview. In most cases
+ // we just disable animations.
+ ResourceValue patchedValue = FRAMEWORK_PATCHED_VALUES.get(attrName);
+ if (patchedValue != null) {
+ defaultValue = patchedValue;
+ }
+ }
+
+ // if we found a value, we make sure this doesn't reference another value.
+ // So we resolve it.
+ if (defaultValue != null) {
// If the value is a reference to another theme attribute that doesn't
// exist, we should log a warning and omit it.
- String val = resValue.getValue();
+ String val = defaultValue.getValue();
if (val != null && val.startsWith(SdkConstants.PREFIX_THEME_REF)) {
- if (!attrName.equals(RTL_ATTRS.get(val)) ||
- getApplicationInfo().targetSdkVersion <
- VERSION_CODES.JELLY_BEAN_MR1) {
+ // Because we always use the latest framework code, some resources might
+ // fail to resolve when using old themes (they haven't been backported).
+ // Since this is an artifact caused by us using always the latest
+ // code, we check for some of those values and replace them here.
+ defaultValue = FRAMEWORK_REPLACE_VALUES.get(attrName);
+
+ if (defaultValue == null &&
+ (getApplicationInfo().targetSdkVersion < JELLY_BEAN_MR1 ||
+ !attrName.equals(RTL_ATTRS.get(val)))) {
// Only log a warning if the referenced value isn't one of the RTL
// attributes, or the app targets old API.
Bridge.getLog().warning(LayoutLog.TAG_RESOURCES_RESOLVE_THEME_ATTR,
String.format("Failed to find '%s' in current theme.", val),
val);
}
- resValue = null;
}
}
- ta.bridgeSetValue(index, attrName, frameworkAttr, resValue);
+ ta.bridgeSetValue(index, attrName, frameworkAttr, defaultValue);
} else {
// there is a value in the XML, but we need to resolve it in case it's
// referencing another resource or a theme value.
@@ -1138,7 +1218,7 @@
@Override
public void shellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err,
- String[] args, ResultReceiver resultReceiver) {
+ String[] args, ShellCallback shellCallback, ResultReceiver resultReceiver) {
}
};
}
@@ -1245,6 +1325,12 @@
}
@Override
+ public Context createContextForSplit(String splitName) {
+ // pass
+ return null;
+ }
+
+ @Override
public String[] databaseList() {
// pass
return null;
@@ -1615,6 +1701,12 @@
// pass
}
+ @Override
+ public void sendBroadcastAsUser(Intent intent, UserHandle user,
+ String receiverPermission, Bundle options) {
+ // pass
+ }
+
public void sendBroadcastAsUser(Intent intent, UserHandle user,
String receiverPermission, int appOp) {
// pass
@@ -1735,6 +1827,13 @@
}
@Override
+ public ComponentName startServiceInForeground(Intent service,
+ int id, Notification notification) {
+ // pass
+ return null;
+ }
+
+ @Override
public boolean stopService(Intent arg0) {
// pass
return false;
@@ -1747,6 +1846,13 @@
}
@Override
+ public ComponentName startServiceInForegroundAsUser(Intent service,
+ int id, Notification notification, UserHandle user) {
+ // pass
+ return null;
+ }
+
+ @Override
public boolean stopServiceAsUser(Intent arg0, UserHandle arg1) {
// pass
return false;
@@ -1893,7 +1999,7 @@
Map<List<StyleResourceValue>,
Map<Integer, Pair<BridgeTypedArray, PropertiesMap>>>> mCache;
- public TypedArrayCache() {
+ private TypedArrayCache() {
mCache = new IdentityHashMap<>();
}
@@ -1914,17 +2020,9 @@
public void put(int[] attrs, List<StyleResourceValue> themes, int resId,
Pair<BridgeTypedArray, PropertiesMap> value) {
Map<List<StyleResourceValue>, Map<Integer, Pair<BridgeTypedArray, PropertiesMap>>>
- cacheFromThemes = mCache.get(attrs);
- if (cacheFromThemes == null) {
- cacheFromThemes = new HashMap<>();
- mCache.put(attrs, cacheFromThemes);
- }
+ cacheFromThemes = mCache.computeIfAbsent(attrs, k -> new HashMap<>());
Map<Integer, Pair<BridgeTypedArray, PropertiesMap>> cacheFromResId =
- cacheFromThemes.get(themes);
- if (cacheFromResId == null) {
- cacheFromResId = new HashMap<>();
- cacheFromThemes.put(themes, cacheFromResId);
- }
+ cacheFromThemes.computeIfAbsent(themes, k -> new HashMap<>());
cacheFromResId.put(resId, value);
}
diff --git a/bridge/src/com/android/layoutlib/bridge/android/BridgePackageManager.java b/bridge/src/com/android/layoutlib/bridge/android/BridgePackageManager.java
index 8dbbd07..de04c43 100644
--- a/bridge/src/com/android/layoutlib/bridge/android/BridgePackageManager.java
+++ b/bridge/src/com/android/layoutlib/bridge/android/BridgePackageManager.java
@@ -42,14 +42,15 @@
import android.content.pm.ProviderInfo;
import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
+import android.content.pm.SharedLibraryInfo;
import android.content.pm.VerifierDeviceIdentity;
+import android.content.pm.VersionedPackage;
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.RemoteException;
import android.os.UserHandle;
import android.os.storage.VolumeInfo;
import java.util.List;
@@ -71,6 +72,23 @@
}
@Override
+ public PackageInfo getPackageInfo(VersionedPackage versionedPackage,
+ @PackageInfoFlags int flags) throws NameNotFoundException {
+ return null;
+ }
+
+ @Override
+ public List<SharedLibraryInfo> getSharedLibraries(@InstallFlags int flags) {
+ return null;
+ }
+
+ @Override
+ public List<SharedLibraryInfo> getSharedLibrariesAsUser(@InstallFlags int flags,
+ int userId) {
+ return null;
+ }
+
+ @Override
public String[] currentToCanonicalPackageNames(String[] names) {
return new String[0];
}
@@ -277,6 +295,11 @@
}
@Override
+ public List<ApplicationInfo> getInstalledApplicationsAsUser(int flags, int userId) {
+ return null;
+ }
+
+ @Override
public List<EphemeralApplicationInfo> getEphemeralApplications() {
return null;
}
@@ -495,12 +518,6 @@
}
@Override
- public Drawable getManagedUserBadgedDrawable(Drawable drawable, Rect badgeLocation,
- int badgeDensity) {
- return null;
- }
-
- @Override
public Drawable getUserBadgedIcon(Drawable icon, UserHandle user) {
return null;
}
@@ -790,6 +807,10 @@
}
@Override
+ public void setApplicationCategoryHint(String packageName, int categoryHint) {
+ }
+
+ @Override
public int getMoveStatus(int moveId) {
return 0;
}
@@ -870,4 +891,14 @@
public boolean isPackageAvailable(String packageName) {
return false;
}
+
+ @Override
+ public int getInstallReason(String packageName, UserHandle user) {
+ return INSTALL_REASON_UNKNOWN;
+ }
+
+ @Override
+ public boolean canRequestPackageInstalls() {
+ return false;
+ }
}
diff --git a/bridge/src/com/android/layoutlib/bridge/android/BridgeWindow.java b/bridge/src/com/android/layoutlib/bridge/android/BridgeWindow.java
index a83f100..a51ad2e 100644
--- a/bridge/src/com/android/layoutlib/bridge/android/BridgeWindow.java
+++ b/bridge/src/com/android/layoutlib/bridge/android/BridgeWindow.java
@@ -108,6 +108,10 @@
}
@Override
+ public void dispatchPointerCaptureChanged(boolean hasCapture) {
+ }
+
+ @Override
public IBinder asBinder() {
// pass for now.
return null;
diff --git a/bridge/src/com/android/layoutlib/bridge/android/support/RecyclerViewUtil.java b/bridge/src/com/android/layoutlib/bridge/android/support/RecyclerViewUtil.java
index d432120..ab27819 100644
--- a/bridge/src/com/android/layoutlib/bridge/android/support/RecyclerViewUtil.java
+++ b/bridge/src/com/android/layoutlib/bridge/android/support/RecyclerViewUtil.java
@@ -21,6 +21,7 @@
import com.android.layoutlib.bridge.Bridge;
import com.android.layoutlib.bridge.android.BridgeContext;
import com.android.layoutlib.bridge.android.RenderParamsFlags;
+import com.android.layoutlib.bridge.util.ReflectionUtils;
import com.android.layoutlib.bridge.util.ReflectionUtils.ReflectionException;
import android.annotation.NonNull;
@@ -116,7 +117,7 @@
private static void setProperty(@NonNull Object object, @NonNull String propertyClassName,
@NonNull Object propertyValue, @NonNull String propertySetter)
throws ReflectionException {
- Class<?> propertyClass = getClassInstance(propertyValue, propertyClassName);
+ Class<?> propertyClass = ReflectionUtils.getClassInstance(propertyValue, propertyClassName);
setProperty(object, propertyClass, propertyValue, propertySetter);
}
@@ -126,22 +127,4 @@
invoke(getMethod(object.getClass(), propertySetter, propertyClass), object, propertyValue);
}
- /**
- * Looks through the class hierarchy of {@code object} at runtime and returns the class matching
- * the name {@code className}.
- * <p/>
- * This is used when we cannot use Class.forName() since the class we want was loaded from a
- * different ClassLoader.
- */
- @NonNull
- private static Class<?> getClassInstance(@NonNull Object object, @NonNull String className) {
- Class<?> superClass = object.getClass();
- while (superClass != null) {
- if (className.equals(superClass.getName())) {
- return superClass;
- }
- superClass = superClass.getSuperclass();
- }
- throw new RuntimeException("invalid object/classname combination.");
- }
}
diff --git a/bridge/src/com/android/layoutlib/bridge/android/support/SupportPreferencesUtil.java b/bridge/src/com/android/layoutlib/bridge/android/support/SupportPreferencesUtil.java
new file mode 100644
index 0000000..6ad9efc
--- /dev/null
+++ b/bridge/src/com/android/layoutlib/bridge/android/support/SupportPreferencesUtil.java
@@ -0,0 +1,282 @@
+/*
+ * Copyright (C) 2016 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.support;
+
+import com.android.ide.common.rendering.api.LayoutlibCallback;
+import com.android.ide.common.rendering.api.RenderResources;
+import com.android.ide.common.rendering.api.ResourceValue;
+import com.android.ide.common.rendering.api.StyleResourceValue;
+import com.android.layoutlib.bridge.android.BridgeContext;
+import com.android.layoutlib.bridge.android.BridgeXmlBlockParser;
+import com.android.layoutlib.bridge.util.ReflectionUtils.ReflectionException;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.Context;
+import android.view.ContextThemeWrapper;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.LinearLayout;
+import android.widget.LinearLayout.LayoutParams;
+import android.widget.ScrollView;
+
+import java.io.IOException;
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+
+import static com.android.layoutlib.bridge.util.ReflectionUtils.getAccessibleMethod;
+import static com.android.layoutlib.bridge.util.ReflectionUtils.getClassInstance;
+import static com.android.layoutlib.bridge.util.ReflectionUtils.getMethod;
+import static com.android.layoutlib.bridge.util.ReflectionUtils.invoke;
+
+/**
+ * Class with utility methods to instantiate Preferences provided by the support library.
+ * This class uses reflection to access the support preference objects so it heavily depends on
+ * the API being stable.
+ */
+public class SupportPreferencesUtil {
+ private static final String PREFERENCE_PKG = "android.support.v7.preference";
+ private static final String PREFERENCE_MANAGER = PREFERENCE_PKG + ".PreferenceManager";
+ private static final String PREFERENCE_GROUP = PREFERENCE_PKG + ".PreferenceGroup";
+ private static final String PREFERENCE_GROUP_ADAPTER =
+ PREFERENCE_PKG + ".PreferenceGroupAdapter";
+ private static final String PREFERENCE_INFLATER = PREFERENCE_PKG + ".PreferenceInflater";
+
+ private SupportPreferencesUtil() {
+ }
+
+ @NonNull
+ private static Object instantiateClass(@NonNull LayoutlibCallback callback,
+ @NonNull String className, @Nullable Class[] constructorSignature,
+ @Nullable Object[] constructorArgs) throws ReflectionException {
+ try {
+ Object instance = callback.loadClass(className, constructorSignature, constructorArgs);
+ if (instance == null) {
+ throw new ClassNotFoundException(className + " class not found");
+ }
+ return instance;
+ } catch (ClassNotFoundException e) {
+ throw new ReflectionException(e);
+ }
+ }
+
+ @NonNull
+ private static Object createPreferenceGroupAdapter(@NonNull LayoutlibCallback callback,
+ @NonNull Object preferenceScreen) throws ReflectionException {
+ Class<?> preferenceGroupClass = getClassInstance(preferenceScreen, PREFERENCE_GROUP);
+
+ return instantiateClass(callback, PREFERENCE_GROUP_ADAPTER,
+ new Class[]{preferenceGroupClass}, new Object[]{preferenceScreen});
+ }
+
+ @NonNull
+ private static Object createInflatedPreference(@NonNull LayoutlibCallback callback,
+ @NonNull Context context, @NonNull XmlPullParser parser, @NonNull Object preferenceScreen,
+ @NonNull Object preferenceManager) throws ReflectionException {
+ Class<?> preferenceGroupClass = getClassInstance(preferenceScreen, PREFERENCE_GROUP);
+ Object preferenceInflater = instantiateClass(callback, PREFERENCE_INFLATER,
+ new Class[]{Context.class, preferenceManager.getClass()},
+ new Object[]{context, preferenceManager});
+ Object inflatedPreference =
+ invoke(getAccessibleMethod(preferenceInflater.getClass(), "inflate",
+ XmlPullParser.class, preferenceGroupClass), preferenceInflater, parser,
+ null);
+
+ if (inflatedPreference == null) {
+ throw new ReflectionException("inflate method returned null");
+ }
+
+ return inflatedPreference;
+ }
+
+ /**
+ * Returns a themed wrapper context of {@link BridgeContext} with the theme specified in
+ * ?attr/preferenceTheme applied to it.
+ */
+ @Nullable
+ private static Context getThemedContext(@NonNull BridgeContext bridgeContext) {
+ RenderResources resources = bridgeContext.getRenderResources();
+ ResourceValue preferenceTheme = resources.findItemInTheme("preferenceTheme", false);
+
+ if (preferenceTheme != null) {
+ // resolve it, if needed.
+ preferenceTheme = resources.resolveResValue(preferenceTheme);
+ }
+ if (preferenceTheme instanceof StyleResourceValue) {
+ int styleId = bridgeContext.getDynamicIdByStyle(((StyleResourceValue) preferenceTheme));
+ if (styleId != 0) {
+ return new ContextThemeWrapper(bridgeContext, styleId);
+ }
+ }
+
+ return null;
+ }
+
+ /**
+ * Returns a {@link LinearLayout} containing all the UI widgets representing the preferences
+ * passed in the group adapter.
+ */
+ @Nullable
+ private static LinearLayout setUpPreferencesListView(@NonNull BridgeContext bridgeContext,
+ @NonNull Context themedContext, @NonNull ArrayList<Object> viewCookie,
+ @NonNull Object preferenceGroupAdapter) throws ReflectionException {
+ // Setup the LinearLayout that will contain the preferences
+ LinearLayout listView = new LinearLayout(themedContext);
+ listView.setOrientation(LinearLayout.VERTICAL);
+ listView.setLayoutParams(
+ new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
+
+ if (!viewCookie.isEmpty()) {
+ bridgeContext.addViewKey(listView, viewCookie.get(0));
+ }
+
+ // Get all the preferences and add them to the LinearLayout
+ Integer preferencesCount =
+ (Integer) invoke(getMethod(preferenceGroupAdapter.getClass(), "getItemCount"),
+ preferenceGroupAdapter);
+ if (preferencesCount == null) {
+ return listView;
+ }
+
+ Method getItemId = getMethod(preferenceGroupAdapter.getClass(), "getItemId", int.class);
+ Method getItemViewType =
+ getMethod(preferenceGroupAdapter.getClass(), "getItemViewType", int.class);
+ Method onCreateViewHolder =
+ getMethod(preferenceGroupAdapter.getClass(), "onCreateViewHolder", ViewGroup.class,
+ int.class);
+ for (int i = 0; i < preferencesCount; i++) {
+ Long id = (Long) invoke(getItemId, preferenceGroupAdapter, i);
+ if (id == null) {
+ continue;
+ }
+
+ // Get the type of the preference layout and bind it to a newly created view holder
+ Integer type = (Integer) invoke(getItemViewType, preferenceGroupAdapter, i);
+ Object viewHolder =
+ invoke(onCreateViewHolder, preferenceGroupAdapter, listView, type);
+ if (viewHolder == null) {
+ continue;
+ }
+ invoke(getMethod(preferenceGroupAdapter.getClass(), "onBindViewHolder",
+ viewHolder.getClass(), int.class), preferenceGroupAdapter, viewHolder, i);
+
+ try {
+ // Get the view from the view holder and add it to our layout
+ View itemView =
+ (View) viewHolder.getClass().getField("itemView").get(viewHolder);
+
+ int arrayPosition = id.intValue() - 1; // IDs are 1 based
+ if (arrayPosition >= 0 && arrayPosition < viewCookie.size()) {
+ bridgeContext.addViewKey(itemView, viewCookie.get(arrayPosition));
+ }
+ listView.addView(itemView);
+ } catch (IllegalAccessException | NoSuchFieldException ignored) {
+ }
+ }
+
+ return listView;
+ }
+
+ /**
+ * Inflates a preferences layout using the support library. If the support library is not
+ * available, this method will return null without advancing the parsers.
+ */
+ @Nullable
+ public static View inflatePreference(@NonNull BridgeContext bridgeContext,
+ @NonNull XmlPullParser parser, @Nullable ViewGroup root) {
+ try {
+ LayoutlibCallback callback = bridgeContext.getLayoutlibCallback();
+
+ Context context = getThemedContext(bridgeContext);
+ if (context == null) {
+ // Probably we couldn't find the "preferenceTheme" in the theme
+ return null;
+ }
+
+ // Create PreferenceManager
+ Object preferenceManager =
+ instantiateClass(callback, PREFERENCE_MANAGER, new Class[]{Context.class},
+ new Object[]{context});
+
+ // From this moment on, we can assume that we found the support library and that
+ // nothing should fail
+
+ // Create PreferenceScreen
+ Object preferenceScreen =
+ invoke(getMethod(preferenceManager.getClass(), "createPreferenceScreen",
+ Context.class), preferenceManager, context);
+ if (preferenceScreen == null) {
+ return null;
+ }
+
+ // Setup a parser that stores the list of cookies in the same order as the preferences
+ // are inflated. That way we can later reconstruct the list using the preference id
+ // since they are sequential and start in 1.
+ ArrayList<Object> viewCookie = new ArrayList<>();
+ if (parser instanceof BridgeXmlBlockParser) {
+ // Setup a parser that stores the XmlTag
+ parser = new BridgeXmlBlockParser(parser, null, false) {
+ @Override
+ public Object getViewCookie() {
+ return ((BridgeXmlBlockParser) getParser()).getViewCookie();
+ }
+
+ @Override
+ public int next() throws XmlPullParserException, IOException {
+ int ev = super.next();
+ if (ev == XmlPullParser.START_TAG) {
+ viewCookie.add(this.getViewCookie());
+ }
+
+ return ev;
+ }
+ };
+ }
+
+ // Create the PreferenceInflater
+ Object inflatedPreference =
+ createInflatedPreference(callback, context, parser, preferenceScreen,
+ preferenceManager);
+
+ // Setup the RecyclerView (set adapter and layout manager)
+ Object preferenceGroupAdapter =
+ createPreferenceGroupAdapter(callback, inflatedPreference);
+
+ // Instead of just setting the group adapter as adapter for a RecyclerView, we manually
+ // get all the items and add them to a LinearLayout. This allows us to set the view
+ // cookies so the preferences are correctly linked to their XML.
+ LinearLayout listView = setUpPreferencesListView(bridgeContext, context, viewCookie,
+ preferenceGroupAdapter);
+
+ ScrollView scrollView = new ScrollView(context);
+ scrollView.setLayoutParams(
+ new ViewGroup.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT));
+ scrollView.addView(listView);
+
+ if (root != null) {
+ root.addView(scrollView);
+ }
+
+ return scrollView;
+ } catch (ReflectionException e) {
+ return null;
+ }
+ }
+}
diff --git a/bridge/src/com/android/layoutlib/bridge/android/view/WindowManagerImpl.java b/bridge/src/com/android/layoutlib/bridge/android/view/WindowManagerImpl.java
index 3031701..d7d16cf 100644
--- a/bridge/src/com/android/layoutlib/bridge/android/view/WindowManagerImpl.java
+++ b/bridge/src/com/android/layoutlib/bridge/android/view/WindowManagerImpl.java
@@ -17,6 +17,7 @@
import android.util.DisplayMetrics;
import android.view.Display;
+import android.view.Display.Mode;
import android.view.DisplayAdjustments;
import android.view.DisplayInfo;
import android.view.View;
@@ -33,6 +34,9 @@
DisplayInfo info = new DisplayInfo();
info.logicalHeight = mMetrics.heightPixels;
info.logicalWidth = mMetrics.widthPixels;
+ info.supportedModes = new Mode[] {
+ new Mode(0, mMetrics.widthPixels, mMetrics.heightPixels, 60f)
+ };
mDisplay = new Display(null, Display.DEFAULT_DISPLAY, info,
DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS);
}
diff --git a/bridge/src/com/android/layoutlib/bridge/bars/Config.java b/bridge/src/com/android/layoutlib/bridge/bars/Config.java
index 09937bc..5386b17 100644
--- a/bridge/src/com/android/layoutlib/bridge/bars/Config.java
+++ b/bridge/src/com/android/layoutlib/bridge/bars/Config.java
@@ -74,8 +74,8 @@
}
public static String getTime(int platformVersion) {
- if (isGreaterOrEqual(platformVersion, M)) {
- return "6:00";
+ if (isGreaterOrEqual(platformVersion, N)) {
+ return "7:00";
}
if (platformVersion < GINGERBREAD) {
return "2:20";
@@ -98,6 +98,9 @@
if (platformVersion < M) {
return "5:10";
}
+ if (platformVersion < N) {
+ return "6:00";
+ }
// Should never happen.
return "4:04";
}
diff --git a/bridge/src/com/android/layoutlib/bridge/impl/DelegateManager.java b/bridge/src/com/android/layoutlib/bridge/impl/DelegateManager.java
index c59b1a6..11da445 100644
--- a/bridge/src/com/android/layoutlib/bridge/impl/DelegateManager.java
+++ b/bridge/src/com/android/layoutlib/bridge/impl/DelegateManager.java
@@ -24,8 +24,8 @@
import java.io.PrintStream;
import java.lang.ref.WeakReference;
-import java.util.ArrayList;
-import java.util.List;
+import java.util.HashSet;
+import java.util.Set;
import java.util.concurrent.atomic.AtomicLong;
/**
@@ -33,7 +33,7 @@
*
* This is used in conjunction with layoublib_create: certain Android java classes are mere
* wrappers around a heavily native based implementation, and we need a way to run these classes
- * in our Eclipse rendering framework without bringing all the native code from the Android
+ * in our Android Studio rendering framework without bringing all the native code from the Android
* platform.
*
* Thus we instruct layoutlib_create to modify the bytecode of these classes to replace their
@@ -61,7 +61,7 @@
* following mechanism:
*
* - {@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 to/from a set. This set holds the reference and prevents the GC from reclaiming
* the delegate.
*
* - {@link #addNewDelegate(Object)} also adds the delegate to a {@link SparseArray} that holds a
@@ -76,12 +76,12 @@
@SuppressWarnings("FieldCanBeLocal")
private final Class<T> mClass;
private static final SparseWeakArray<Object> sDelegates = new SparseWeakArray<>();
- /** list used to store delegates when their main object holds a reference to them.
+ /** Set 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(long)
*/
- private static final List<Object> sJavaReferences = new ArrayList<>();
+ private static final Set<Object> sJavaReferences = new HashSet<>();
private static final AtomicLong sDelegateCounter = new AtomicLong(1);
public DelegateManager(Class<T> theClass) {
@@ -129,7 +129,7 @@
long native_object = sDelegateCounter.getAndIncrement();
synchronized (DelegateManager.class) {
sDelegates.put(native_object, newDelegate);
- assert !sJavaReferences.contains(newDelegate);
+ // Only for development: assert !sJavaReferences.contains(newDelegate);
sJavaReferences.add(newDelegate);
}
@@ -165,5 +165,6 @@
int idx = sDelegates.indexOfValue(reference);
out.printf("[%d] %s\n", sDelegates.keyAt(idx), reference.getClass().getSimpleName());
}
+ out.printf("\nTotal number of objects: %d\n", sJavaReferences.size());
}
}
diff --git a/bridge/src/com/android/layoutlib/bridge/impl/GcSnapshot.java b/bridge/src/com/android/layoutlib/bridge/impl/GcSnapshot.java
index a39eb4d..7526e09 100644
--- a/bridge/src/com/android/layoutlib/bridge/impl/GcSnapshot.java
+++ b/bridge/src/com/android/layoutlib/bridge/impl/GcSnapshot.java
@@ -24,12 +24,13 @@
import android.graphics.ColorFilter_Delegate;
import android.graphics.Paint;
import android.graphics.Paint_Delegate;
+import android.graphics.PorterDuff;
+import android.graphics.PorterDuff.Mode;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.Region;
import android.graphics.Region_Delegate;
import android.graphics.Shader_Delegate;
-import android.graphics.Xfermode_Delegate;
import java.awt.AlphaComposite;
import java.awt.Color;
@@ -40,6 +41,7 @@
import java.awt.Shape;
import java.awt.geom.AffineTransform;
import java.awt.geom.Area;
+import java.awt.geom.NoninvertibleTransformException;
import java.awt.geom.Rectangle2D;
import java.awt.image.BufferedImage;
import java.util.ArrayList;
@@ -620,7 +622,8 @@
int y = 0;
int width;
int height;
- Rectangle clipBounds = originalGraphics.getClipBounds();
+ Rectangle clipBounds = originalGraphics.getClip() != null ? originalGraphics
+ .getClipBounds() : null;
if (clipBounds != null) {
if (clipBounds.width == 0 || clipBounds.height == 0) {
// Clip is 0 so no need to paint anything.
@@ -825,28 +828,9 @@
g.setComposite(AlphaComposite.getInstance(forceMode, (float) alpha / 255.f));
return;
}
- Xfermode_Delegate xfermodeDelegate = paint.getXfermode();
- if (xfermodeDelegate != null) {
- if (xfermodeDelegate.isSupported()) {
- Composite composite = xfermodeDelegate.getComposite(alpha);
- assert composite != null;
- if (composite != null) {
- g.setComposite(composite);
- return;
- }
- } else {
- Bridge.getLog().fidelityWarning(LayoutLog.TAG_XFERMODE,
- xfermodeDelegate.getSupportMessage(),
- null /*throwable*/, null /*data*/);
- }
- }
- // if there was no custom xfermode, but we have alpha (due to a shader and a non
- // opaque alpha channel in the paint color), then we create an AlphaComposite anyway
- // that will handle the alpha.
- if (alpha != 0xFF) {
- g.setComposite(AlphaComposite.getInstance(
- AlphaComposite.SRC_OVER, (float) alpha / 255.f));
- }
+ Mode mode = PorterDuff.intToMode(paint.getPorterDuffMode());
+ Composite composite = PorterDuffUtility.getComposite(mode, alpha);
+ g.setComposite(composite);
}
private void mapRect(AffineTransform matrix, RectF dst, RectF src) {
@@ -869,4 +853,33 @@
dst.bottom = Math.max(Math.max(corners[1], corners[3]), Math.max(corners[5], corners[7]));
}
+ /**
+ * Returns the clip of the oldest snapshot of the stack, appropriately translated to be
+ * expressed in the coordinate system of the latest snapshot.
+ */
+ public Rectangle getOriginalClip() {
+ GcSnapshot originalSnapshot = this;
+ while (originalSnapshot.mPrevious != null) {
+ originalSnapshot = originalSnapshot.mPrevious;
+ }
+ if (originalSnapshot.mLayers.isEmpty()) {
+ return null;
+ }
+ Graphics2D graphics2D = originalSnapshot.mLayers.get(0).getGraphics();
+ Rectangle bounds = graphics2D.getClipBounds();
+ if (bounds == null) {
+ return null;
+ }
+ try {
+ AffineTransform originalTransform =
+ ((Graphics2D) graphics2D.create()).getTransform().createInverse();
+ AffineTransform latestTransform = getTransform().createInverse();
+ bounds.x += latestTransform.getTranslateX() - originalTransform.getTranslateX();
+ bounds.y += latestTransform.getTranslateY() - originalTransform.getTranslateY();
+ } catch (NoninvertibleTransformException e) {
+ Bridge.getLog().warning(null, "Non invertible transformation", null);
+ }
+ return bounds;
+ }
+
}
diff --git a/bridge/src/com/android/layoutlib/bridge/impl/Layout.java b/bridge/src/com/android/layoutlib/bridge/impl/Layout.java
index 1afd90d..2fe3ed5 100644
--- a/bridge/src/com/android/layoutlib/bridge/impl/Layout.java
+++ b/bridge/src/com/android/layoutlib/bridge/impl/Layout.java
@@ -20,7 +20,6 @@
import com.android.ide.common.rendering.api.RenderResources;
import com.android.ide.common.rendering.api.ResourceValue;
import com.android.ide.common.rendering.api.SessionParams;
-import com.android.ide.common.rendering.api.StyleResourceValue;
import com.android.layoutlib.bridge.Bridge;
import com.android.layoutlib.bridge.android.BridgeContext;
import com.android.layoutlib.bridge.android.RenderParamsFlags;
@@ -39,7 +38,10 @@
import android.graphics.drawable.Drawable;
import android.util.DisplayMetrics;
import android.util.TypedValue;
+import android.view.AttachInfo_Accessor;
import android.view.View;
+import android.view.ViewRootImpl;
+import android.view.ViewRootImpl_Accessor;
import android.widget.FrameLayout;
import android.widget.LinearLayout;
import android.widget.RelativeLayout;
@@ -94,7 +96,6 @@
private static final String ATTR_WINDOW_TITLE_SIZE = "windowTitleSize";
private static final String ATTR_WINDOW_TRANSLUCENT_STATUS = StatusBar.ATTR_TRANSLUCENT;
private static final String ATTR_WINDOW_TRANSLUCENT_NAV = NavigationBar.ATTR_TRANSLUCENT;
- private static final String PREFIX_THEME_APPCOMPAT = "Theme.AppCompat";
// Default sizes
private static final int DEFAULT_STATUS_BAR_HEIGHT = 25;
@@ -168,13 +169,13 @@
FrameLayout contentRoot = new FrameLayout(getContext());
LayoutParams params = createLayoutParams(MATCH_PARENT, MATCH_PARENT);
int rule = mBuilder.isNavBarVertical() ? START_OF : ABOVE;
- if (mBuilder.hasNavBar() && mBuilder.solidBars()) {
+ if (mBuilder.hasSolidNavBar()) {
params.addRule(rule, getId(ID_NAV_BAR));
}
int below = -1;
if (mBuilder.mActionBarSize <= 0 && mBuilder.mTitleBarSize > 0) {
below = getId(ID_TITLE_BAR);
- } else if (mBuilder.hasStatusBar() && mBuilder.solidBars()) {
+ } else if (mBuilder.hasSolidStatusBar()) {
below = getId(ID_STATUS_BAR);
}
if (below != -1) {
@@ -236,17 +237,17 @@
boolean isMenu = "menu".equals(params.getFlag(RenderParamsFlags.FLAG_KEY_ROOT_TAG));
BridgeActionBar actionBar;
- if (mBuilder.isThemeAppCompat() && !isMenu) {
+ if (context.isAppCompatTheme() && !isMenu) {
actionBar = new AppCompatActionBar(context, params);
} else {
actionBar = new FrameworkActionBar(context, params);
}
LayoutParams layoutParams = createLayoutParams(MATCH_PARENT, MATCH_PARENT);
int rule = mBuilder.isNavBarVertical() ? START_OF : ABOVE;
- if (mBuilder.hasNavBar() && mBuilder.solidBars()) {
+ if (mBuilder.hasSolidNavBar()) {
layoutParams.addRule(rule, getId(ID_NAV_BAR));
}
- if (mBuilder.hasStatusBar() && mBuilder.solidBars()) {
+ if (mBuilder.hasSolidStatusBar()) {
layoutParams.addRule(BELOW, getId(ID_STATUS_BAR));
}
actionBar.getRootView().setLayoutParams(layoutParams);
@@ -259,10 +260,10 @@
int simulatedPlatformVersion) {
TitleBar titleBar = new TitleBar(context, title, simulatedPlatformVersion);
LayoutParams params = createLayoutParams(MATCH_PARENT, mBuilder.mTitleBarSize);
- if (mBuilder.hasStatusBar() && mBuilder.solidBars()) {
+ if (mBuilder.hasSolidStatusBar()) {
params.addRule(BELOW, getId(ID_STATUS_BAR));
}
- if (mBuilder.isNavBarVertical() && mBuilder.solidBars()) {
+ if (mBuilder.isNavBarVertical() && mBuilder.hasSolidNavBar()) {
params.addRule(START_OF, getId(ID_NAV_BAR));
}
titleBar.setLayoutParams(params);
@@ -304,6 +305,17 @@
return Bridge.getResourceId(ResourceType.ID, ID_PREFIX + name);
}
+ @Override
+ public void requestFitSystemWindows() {
+ // The framework call would usually bubble up to ViewRootImpl but, in layoutlib, Layout will
+ // act as view root for most purposes. That way, we can also save going through the Handler
+ // to dispatch the new applied insets.
+ ViewRootImpl root = AttachInfo_Accessor.getRootView(this);
+ if (root != null) {
+ ViewRootImpl_Accessor.dispatchApplyInsets(root, this);
+ }
+ }
+
/**
* A helper class to help initialize the Layout.
*/
@@ -324,8 +336,6 @@
private boolean mTranslucentStatus;
private boolean mTranslucentNav;
- private Boolean mIsThemeAppCompat;
-
public Builder(@NonNull SessionParams params, @NonNull BridgeContext context) {
mParams = params;
mContext = context;
@@ -364,8 +374,10 @@
return;
}
// Check if an actionbar is needed
- boolean windowActionBar = getBooleanThemeValue(mResources, ATTR_WINDOW_ACTION_BAR,
- !isThemeAppCompat(), true);
+ boolean isMenu = "menu".equals(mParams.getFlag(RenderParamsFlags.FLAG_KEY_ROOT_TAG));
+ boolean windowActionBar = isMenu ||
+ getBooleanThemeValue(mResources, ATTR_WINDOW_ACTION_BAR,
+ !mContext.isAppCompatTheme(), true);
if (windowActionBar) {
mActionBarSize = getDimension(ATTR_ACTION_BAR_SIZE, true, DEFAULT_TITLE_BAR_HEIGHT);
} else {
@@ -420,39 +432,18 @@
return mParams.getHardwareConfig().hasSoftwareButtons();
}
- private boolean isThemeAppCompat() {
- // If a cached value exists, return it.
- if (mIsThemeAppCompat != null) {
- return mIsThemeAppCompat;
- }
- // Ideally, we should check if the corresponding activity extends
- // android.support.v7.app.ActionBarActivity, and not care about the theme name at all.
- StyleResourceValue defaultTheme = mResources.getDefaultTheme();
- // We can't simply check for parent using resources.themeIsParentOf() since the
- // inheritance structure isn't really what one would expect. The first common parent
- // between Theme.AppCompat.Light and Theme.AppCompat is Theme.Material (for v21).
- boolean isThemeAppCompat = false;
- for (int i = 0; i < 50; i++) {
- if (defaultTheme == null) {
- break;
- }
- // for loop ensures that we don't run into cyclic theme inheritance.
- if (defaultTheme.getName().startsWith(PREFIX_THEME_APPCOMPAT)) {
- isThemeAppCompat = true;
- break;
- }
- defaultTheme = mResources.getParent(defaultTheme);
- }
- mIsThemeAppCompat = isThemeAppCompat;
- return isThemeAppCompat;
+ /**
+ * Return true if the nav bar is present and not translucent
+ */
+ private boolean hasSolidNavBar() {
+ return hasNavBar() && !mTranslucentNav;
}
/**
- * Return true if the status bar or nav bar are present, they are not translucent (i.e
- * content doesn't overlap with them).
+ * Return true if the status bar is present and not translucent
*/
- private boolean solidBars() {
- return !(hasNavBar() && mTranslucentNav) && !(hasStatusBar() && mTranslucentStatus);
+ private boolean hasSolidStatusBar() {
+ return hasStatusBar() && !mTranslucentStatus;
}
private boolean hasNavBar() {
diff --git a/bridge/src/com/android/layoutlib/bridge/impl/ParserFactory.java b/bridge/src/com/android/layoutlib/bridge/impl/ParserFactory.java
index e273b2c..1ae9cb6 100644
--- a/bridge/src/com/android/layoutlib/bridge/impl/ParserFactory.java
+++ b/bridge/src/com/android/layoutlib/bridge/impl/ParserFactory.java
@@ -39,8 +39,6 @@
public final static boolean LOG_PARSER = false;
- private final static String ENCODING = "UTF-8"; //$NON-NLS-1$
-
// Used to get a new XmlPullParser from the client.
@Nullable
private static com.android.ide.common.rendering.api.ParserFactory sParserFactory;
@@ -74,7 +72,7 @@
stream = readAndClose(stream, name, size);
- parser.setInput(stream, ENCODING);
+ parser.setInput(stream, null);
if (isLayout) {
try {
return new LayoutParserWrapper(parser).peekTillLayoutStart();
diff --git a/bridge/src/com/android/layoutlib/bridge/impl/PorterDuffUtility.java b/bridge/src/com/android/layoutlib/bridge/impl/PorterDuffUtility.java
index 80d7c68..70e2eb1 100644
--- a/bridge/src/com/android/layoutlib/bridge/impl/PorterDuffUtility.java
+++ b/bridge/src/com/android/layoutlib/bridge/impl/PorterDuffUtility.java
@@ -24,14 +24,12 @@
import android.graphics.PorterDuff;
import android.graphics.PorterDuff.Mode;
import android.graphics.PorterDuffColorFilter_Delegate;
-import android.graphics.PorterDuffXfermode_Delegate;
import java.awt.AlphaComposite;
import java.awt.Composite;
/**
- * Provides various utility methods for {@link PorterDuffColorFilter_Delegate} and {@link
- * PorterDuffXfermode_Delegate}.
+ * Provides various utility methods for {@link PorterDuffColorFilter_Delegate}.
*/
public final class PorterDuffUtility {
diff --git a/bridge/src/com/android/layoutlib/bridge/impl/RenderSessionImpl.java b/bridge/src/com/android/layoutlib/bridge/impl/RenderSessionImpl.java
index a8077cc..85fe2a4 100644
--- a/bridge/src/com/android/layoutlib/bridge/impl/RenderSessionImpl.java
+++ b/bridge/src/com/android/layoutlib/bridge/impl/RenderSessionImpl.java
@@ -20,6 +20,7 @@
import com.android.ide.common.rendering.api.HardwareConfig;
import com.android.ide.common.rendering.api.IAnimationListener;
import com.android.ide.common.rendering.api.ILayoutPullParser;
+import com.android.ide.common.rendering.api.LayoutLog;
import com.android.ide.common.rendering.api.LayoutlibCallback;
import com.android.ide.common.rendering.api.RenderResources;
import com.android.ide.common.rendering.api.RenderSession;
@@ -44,6 +45,7 @@
import com.android.layoutlib.bridge.android.RenderParamsFlags;
import com.android.layoutlib.bridge.android.graphics.NopCanvas;
import com.android.layoutlib.bridge.android.support.DesignLibUtil;
+import com.android.layoutlib.bridge.android.support.SupportPreferencesUtil;
import com.android.layoutlib.bridge.impl.binding.FakeAdapter;
import com.android.layoutlib.bridge.impl.binding.FakeExpandableAdapter;
import com.android.resources.ResourceType;
@@ -206,7 +208,7 @@
/**
* Measures the the current layout if needed (see {@link #invalidateRenderingSize}).
*/
- private void measure(@NonNull SessionParams params) {
+ private void measureLayout(@NonNull SessionParams params) {
// only do the screen measure when needed.
if (mMeasuredScreenWidth != -1) {
return;
@@ -303,6 +305,20 @@
SessionParams params = getParams();
BridgeContext context = getContext();
+ if (Bridge.isLocaleRtl(params.getLocale())) {
+ if (!params.isRtlSupported()) {
+ Bridge.getLog().warning(LayoutLog.TAG_RTL_NOT_ENABLED,
+ "You are using a right-to-left " +
+ "(RTL) locale but RTL is not enabled", null);
+ } else if (params.getSimulatedPlatformVersion() < 17) {
+ // This will render ok because we are using the latest layoutlib but at least
+ // warn the user that this might fail in a real device.
+ Bridge.getLog().warning(LayoutLog.TAG_RTL_NOT_SUPPORTED, "You are using a " +
+ "right-to-left " +
+ "(RTL) locale but RTL is not supported for API level < 17", null);
+ }
+ }
+
// Sets the project callback (custom view loader) to the fragment delegate so that
// it can instantiate the custom Fragment.
Fragment_Delegate.setLayoutlibCallback(params.getLayoutlibCallback());
@@ -311,8 +327,14 @@
boolean isPreference = "PreferenceScreen".equals(rootTag);
View view;
if (isPreference) {
- view = Preference_Delegate.inflatePreference(getContext(), mBlockParser,
+ // First try to use the support library inflater. If something fails, fallback
+ // to the system preference inflater.
+ view = SupportPreferencesUtil.inflatePreference(getContext(), mBlockParser,
mContentRoot);
+ if (view == null) {
+ view = Preference_Delegate.inflatePreference(getContext(), mBlockParser,
+ mContentRoot);
+ }
} else {
view = mInflater.inflate(mBlockParser, mContentRoot);
}
@@ -331,12 +353,13 @@
setActiveToolbar(view, context, params);
- measure(params);
+ measureLayout(params);
measureView(mViewRoot, null /*measuredView*/,
mMeasuredScreenWidth, MeasureSpec.EXACTLY,
mMeasuredScreenHeight, MeasureSpec.EXACTLY);
mViewRoot.layout(0, 0, mMeasuredScreenWidth, mMeasuredScreenHeight);
- mSystemViewInfoList = visitAllChildren(mViewRoot, 0, params.getExtendedViewInfoMode(),
+ mSystemViewInfoList =
+ visitAllChildren(mViewRoot, 0, 0, params.getExtendedViewInfoMode(),
false);
return SUCCESS.createResult();
@@ -362,13 +385,10 @@
}
/**
- * Renders the given view hierarchy to the passed canvas and returns the result of the render
- * operation.
- * @param canvas an optional canvas to render the views to. If null, only the measure and
- * layout steps will be executed.
+ * Runs a layout pass for the given view root
*/
- private static Result render(@NonNull BridgeContext context, @NonNull ViewGroup viewRoot,
- @Nullable Canvas canvas, int width, int height) {
+ private static void doLayout(@NonNull BridgeContext context, @NonNull ViewGroup viewRoot,
+ int width, int height) {
// measure again with the size we need
// This must always be done before the call to layout
measureView(viewRoot, null /*measuredView*/,
@@ -378,7 +398,16 @@
// now do the layout.
viewRoot.layout(0, 0, width, height);
handleScrolling(context, viewRoot);
+ }
+ /**
+ * Renders the given view hierarchy to the passed canvas and returns the result of the render
+ * operation.
+ * @param canvas an optional canvas to render the views to. If null, only the measure and
+ * layout steps will be executed.
+ */
+ private static Result renderAndBuildResult(@NonNull BridgeContext context, @NonNull ViewGroup viewRoot,
+ @Nullable Canvas canvas, int width, int height) {
if (canvas == null) {
return SUCCESS.createResult();
}
@@ -405,6 +434,40 @@
* @see RenderSession#render(long)
*/
public Result render(boolean freshRender) {
+ return renderAndBuildResult(freshRender, false);
+ }
+
+ /**
+ * Measures the layout
+ * <p>
+ * {@link #acquire(long)} must have been called before this.
+ *
+ * @throws IllegalStateException if the current context is different than the one owned by
+ * the scene, or if {@link #acquire(long)} was not called.
+ *
+ * @see SessionParams#getRenderingMode()
+ * @see RenderSession#render(long)
+ */
+ public Result measure() {
+ return renderAndBuildResult(false, true);
+ }
+
+ /**
+ * Renders the scene.
+ * <p>
+ * {@link #acquire(long)} must have been called before this.
+ *
+ * @param freshRender whether the render is a new one and should erase the existing bitmap (in
+ * the case where bitmaps are reused). This is typically needed when not playing
+ * animations.)
+ *
+ * @throws IllegalStateException if the current context is different than the one owned by
+ * the scene, or if {@link #acquire(long)} was not called.
+ *
+ * @see SessionParams#getRenderingMode()
+ * @see RenderSession#render(long)
+ */
+ private Result renderAndBuildResult(boolean freshRender, boolean onlyMeasure) {
checkLock();
SessionParams params = getParams();
@@ -414,14 +477,15 @@
return ERROR_NOT_INFLATED.createResult();
}
- measure(params);
+ measureLayout(params);
HardwareConfig hardwareConfig = params.getHardwareConfig();
Result renderResult = SUCCESS.createResult();
- if (params.isLayoutOnly()) {
+ if (onlyMeasure) {
// delete the canvas and image to reset them on the next full rendering
mImage = null;
mCanvas = null;
+ doLayout(getContext(), mViewRoot, mMeasuredScreenWidth, mMeasuredScreenHeight);
} else {
// draw the views
// create the BufferedImage into which the layout will be rendered.
@@ -482,11 +546,12 @@
gc.dispose();
}
+ doLayout(getContext(), mViewRoot, mMeasuredScreenWidth, mMeasuredScreenHeight);
if (mElapsedFrameTimeNanos >= 0) {
long initialTime = System_Delegate.nanoTime();
if (!mFirstFrameExecuted) {
// We need to run an initial draw call to initialize the animations
- render(getContext(), mViewRoot, NOP_CANVAS, mMeasuredScreenWidth, mMeasuredScreenHeight);
+ renderAndBuildResult(getContext(), mViewRoot, NOP_CANVAS, mMeasuredScreenWidth, mMeasuredScreenHeight);
// The first frame will initialize the animations
Choreographer_Delegate.doFrame(initialTime);
@@ -495,11 +560,12 @@
// Second frame will move the animations
Choreographer_Delegate.doFrame(initialTime + mElapsedFrameTimeNanos);
}
- renderResult = render(getContext(), mViewRoot, mCanvas, mMeasuredScreenWidth,
+ renderResult = renderAndBuildResult(getContext(), mViewRoot, mCanvas, mMeasuredScreenWidth,
mMeasuredScreenHeight);
}
- mSystemViewInfoList = visitAllChildren(mViewRoot, 0, params.getExtendedViewInfoMode(),
+ mSystemViewInfoList =
+ visitAllChildren(mViewRoot, 0, 0, params.getExtendedViewInfoMode(),
false);
// success!
@@ -1220,20 +1286,22 @@
* bounds of all the views.
*
* @param view the root View
- * @param offset an offset for the view bounds.
+ * @param hOffset horizontal offset for the view bounds.
+ * @param vOffset vertical offset for the view bounds.
* @param setExtendedInfo whether to set the extended view info in the {@link ViewInfo} object.
* @param isContentFrame {@code true} if the {@code ViewInfo} to be created is part of the
* content frame.
*
* @return {@code ViewInfo} containing the bounds of the view and it children otherwise.
*/
- private ViewInfo visit(View view, int offset, boolean setExtendedInfo,
+ private ViewInfo visit(View view, int hOffset, int vOffset, boolean setExtendedInfo,
boolean isContentFrame) {
- ViewInfo result = createViewInfo(view, offset, setExtendedInfo, isContentFrame);
+ ViewInfo result = createViewInfo(view, hOffset, vOffset, setExtendedInfo, isContentFrame);
if (view instanceof ViewGroup) {
ViewGroup group = ((ViewGroup) view);
- result.setChildren(visitAllChildren(group, isContentFrame ? 0 : offset,
+ result.setChildren(visitAllChildren(group, isContentFrame ? 0 : hOffset,
+ isContentFrame ? 0 : vOffset,
setExtendedInfo, isContentFrame));
}
return result;
@@ -1245,20 +1313,22 @@
* the children of the {@code mContentRoot}.
*
* @param viewGroup the root View
- * @param offset an offset from the top for the content view frame.
+ * @param hOffset horizontal offset from the top for the content view frame.
+ * @param vOffset vertical offset from the top for the content view frame.
* @param setExtendedInfo whether to set the extended view info in the {@link ViewInfo} object.
* @param isContentFrame {@code true} if the {@code ViewInfo} to be created is part of the
* content frame. {@code false} if the {@code ViewInfo} to be created is
* part of the system decor.
*/
- private List<ViewInfo> visitAllChildren(ViewGroup viewGroup, int offset,
+ private List<ViewInfo> visitAllChildren(ViewGroup viewGroup, int hOffset, int vOffset,
boolean setExtendedInfo, boolean isContentFrame) {
if (viewGroup == null) {
return null;
}
if (!isContentFrame) {
- offset += viewGroup.getTop();
+ vOffset += viewGroup.getTop();
+ hOffset += viewGroup.getLeft();
}
int childCount = viewGroup.getChildCount();
@@ -1266,7 +1336,8 @@
List<ViewInfo> childrenWithoutOffset = new ArrayList<ViewInfo>(childCount);
List<ViewInfo> childrenWithOffset = new ArrayList<ViewInfo>(childCount);
for (int i = 0; i < childCount; i++) {
- ViewInfo[] childViewInfo = visitContentRoot(viewGroup.getChildAt(i), offset,
+ ViewInfo[] childViewInfo =
+ visitContentRoot(viewGroup.getChildAt(i), hOffset, vOffset,
setExtendedInfo);
childrenWithoutOffset.add(childViewInfo[0]);
childrenWithOffset.add(childViewInfo[1]);
@@ -1276,7 +1347,7 @@
} else {
List<ViewInfo> children = new ArrayList<ViewInfo>(childCount);
for (int i = 0; i < childCount; i++) {
- children.add(visit(viewGroup.getChildAt(i), offset, setExtendedInfo,
+ children.add(visit(viewGroup.getChildAt(i), hOffset, vOffset, setExtendedInfo,
isContentFrame));
}
return children;
@@ -1295,16 +1366,18 @@
* index 1 is with the offset.
*/
@NonNull
- private ViewInfo[] visitContentRoot(View view, int offset, boolean setExtendedInfo) {
+ private ViewInfo[] visitContentRoot(View view, int hOffset, int vOffset,
+ boolean setExtendedInfo) {
ViewInfo[] result = new ViewInfo[2];
if (view == null) {
return result;
}
- result[0] = createViewInfo(view, 0, setExtendedInfo, true);
- result[1] = createViewInfo(view, offset, setExtendedInfo, true);
+ result[0] = createViewInfo(view, 0, 0, setExtendedInfo, true);
+ result[1] = createViewInfo(view, hOffset, vOffset, setExtendedInfo, true);
if (view instanceof ViewGroup) {
- List<ViewInfo> children = visitAllChildren((ViewGroup) view, 0, setExtendedInfo, true);
+ List<ViewInfo> children =
+ visitAllChildren((ViewGroup) view, 0, 0, setExtendedInfo, true);
result[0].setChildren(children);
result[1].setChildren(children);
}
@@ -1315,9 +1388,12 @@
* Creates a {@link ViewInfo} for the view. The {@code ViewInfo} corresponding to the children
* of the {@code view} are not created. Consequently, the children of {@code ViewInfo} is not
* set.
- * @param offset an offset for the view bounds. Used only if view is part of the content frame.
+ * @param hOffset horizontal offset for the view bounds. Used only if view is part of the
+ * content frame.
+ * @param vOffset vertial an offset for the view bounds. Used only if view is part of the
+ * content frame.
*/
- private ViewInfo createViewInfo(View view, int offset, boolean setExtendedInfo,
+ private ViewInfo createViewInfo(View view, int hOffset, int vOffset, boolean setExtendedInfo,
boolean isContentFrame) {
if (view == null) {
return null;
@@ -1333,9 +1409,9 @@
// The view is part of the layout added by the user. Hence,
// the ViewCookie may be obtained only through the Context.
result = new ViewInfo(view.getClass().getName(),
- getContext().getViewKey(view),
- -scrollX + view.getLeft(), -scrollY + view.getTop() + offset,
- -scrollX + view.getRight(), -scrollY + view.getBottom() + offset,
+ getContext().getViewKey(view), -scrollX + view.getLeft() + hOffset,
+ -scrollY + view.getTop() + vOffset, -scrollX + view.getRight() + hOffset,
+ -scrollY + view.getBottom() + vOffset,
view, view.getLayoutParams());
} else {
// We are part of the system decor.
diff --git a/bridge/src/com/android/layoutlib/bridge/impl/ResourceHelper.java b/bridge/src/com/android/layoutlib/bridge/impl/ResourceHelper.java
index a21de56..c197e40 100644
--- a/bridge/src/com/android/layoutlib/bridge/impl/ResourceHelper.java
+++ b/bridge/src/com/android/layoutlib/bridge/impl/ResourceHelper.java
@@ -77,6 +77,7 @@
*/
public static int getColor(String value) {
if (value != null) {
+ value = value.trim();
if (!value.startsWith("#")) {
if (value.startsWith(SdkConstants.PREFIX_THEME_REF)) {
throw new NumberFormatException(String.format(
diff --git a/bridge/src/com/android/layoutlib/bridge/util/ReflectionUtils.java b/bridge/src/com/android/layoutlib/bridge/util/ReflectionUtils.java
index 7ce27b6..040191e 100644
--- a/bridge/src/com/android/layoutlib/bridge/util/ReflectionUtils.java
+++ b/bridge/src/com/android/layoutlib/bridge/util/ReflectionUtils.java
@@ -37,6 +37,15 @@
}
}
+ @NonNull
+ public static Method getAccessibleMethod(@NonNull Class<?> clazz, @NonNull String name,
+ @Nullable Class<?>... params) throws ReflectionException {
+ Method method = getMethod(clazz, name, params);
+ method.setAccessible(true);
+
+ return method;
+ }
+
@Nullable
public static Object invoke(@NonNull Method method, @Nullable Object object,
@Nullable Object... args) throws ReflectionException {
@@ -74,6 +83,25 @@
}
/**
+ * Looks through the class hierarchy of {@code object} at runtime and returns the class matching
+ * the name {@code className}.
+ * <p>
+ * This is used when we cannot use Class.forName() since the class we want was loaded from a
+ * different ClassLoader.
+ */
+ @NonNull
+ public static Class<?> getClassInstance(@NonNull Object object, @NonNull String className) {
+ Class<?> superClass = object.getClass();
+ while (superClass != null) {
+ if (className.equals(superClass.getName())) {
+ return superClass;
+ }
+ superClass = superClass.getSuperclass();
+ }
+ throw new RuntimeException("invalid object/classname combination.");
+ }
+
+ /**
* Wraps all reflection related exceptions. Created since ReflectiveOperationException was
* introduced in 1.7 and we are still on 1.6
*/
diff --git a/bridge/src/libcore/util/NativeAllocationRegistry_Delegate.java b/bridge/src/libcore/util/NativeAllocationRegistry_Delegate.java
index 6246ec1..04fabc2 100644
--- a/bridge/src/libcore/util/NativeAllocationRegistry_Delegate.java
+++ b/bridge/src/libcore/util/NativeAllocationRegistry_Delegate.java
@@ -52,9 +52,15 @@
@LayoutlibDelegate
/*package*/ static void applyFreeFunction(long freeFunction, long nativePtr) {
- NativeAllocationRegistry_Delegate delegate = sManager.getDelegate(freeFunction);
- if (delegate != null) {
- delegate.mFinalizer.free(nativePtr);
+ // This method MIGHT run in the context of the finalizer thread. If the delegate method
+ // crashes, it could bring down the VM. That's why we catch all the exceptions and ignore
+ // them.
+ try {
+ NativeAllocationRegistry_Delegate delegate = sManager.getDelegate(freeFunction);
+ if (delegate != null) {
+ delegate.mFinalizer.free(nativePtr);
+ }
+ } catch (Throwable ignore) {
}
}
diff --git a/bridge/tests/Android.mk b/bridge/tests/Android.mk
index 4656476..33d55de 100644
--- a/bridge/tests/Android.mk
+++ b/bridge/tests/Android.mk
@@ -17,7 +17,9 @@
include $(CLEAR_VARS)
# Only compile source java files in this lib.
-LOCAL_SRC_FILES := $(call all-java-files-under, src)
+LOCAL_SRC_FILES := \
+ $(call all-java-files-under, src) \
+ $(call all-java-files-under, res/testApp/MyApplication/src/main/myapplication.widgets)
LOCAL_JAVA_RESOURCE_DIRS := res
LOCAL_MODULE := layoutlib-tests
@@ -28,7 +30,8 @@
layoutlib_api-prebuilt \
tools-common-prebuilt \
sdk-common \
- junit-host
+ junit-host \
+ guavalib
include $(BUILD_HOST_JAVA_LIBRARY)
diff --git a/bridge/tests/res/empty.xml b/bridge/tests/res/empty.xml
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/bridge/tests/res/empty.xml
diff --git a/bridge/tests/res/testApp/MyApplication/build.gradle b/bridge/tests/res/testApp/MyApplication/build.gradle
index 4561e1b..4781660 100644
--- a/bridge/tests/res/testApp/MyApplication/build.gradle
+++ b/bridge/tests/res/testApp/MyApplication/build.gradle
@@ -19,12 +19,12 @@
apply plugin: 'com.android.application'
android {
- compileSdkVersion 22
- buildToolsVersion '21.1.2'
+ compileSdkVersion 25
+ buildToolsVersion '25.0.0'
defaultConfig {
applicationId 'com.android.layoutlib.test.myapplication'
minSdkVersion 21
- targetSdkVersion 22
+ targetSdkVersion 25
versionCode 1
versionName '1.0'
}
@@ -34,6 +34,9 @@
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
+ lintOptions {
+ abortOnError false
+ }
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_6
targetCompatibility JavaVersion.VERSION_1_6
diff --git a/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/ArraysCheckWidget.class b/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/ArraysCheckWidget.class
index e0373cb..f73528a 100644
--- a/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/ArraysCheckWidget.class
+++ b/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/ArraysCheckWidget.class
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/CustomCalendar.class b/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/CustomCalendar.class
deleted file mode 100644
index c363055..0000000
--- a/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/CustomCalendar.class
+++ /dev/null
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/CustomDate.class b/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/CustomDate.class
deleted file mode 100644
index edda3de..0000000
--- a/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/CustomDate.class
+++ /dev/null
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/MyActivity.class b/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/MyActivity.class
index ec42017..5bb04fc 100644
--- a/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/MyActivity.class
+++ b/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/MyActivity.class
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R$color.class b/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R$color.class
new file mode 100644
index 0000000..ff699d1
--- /dev/null
+++ b/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R$color.class
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R$dimen.class b/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R$dimen.class
index 0e208f2..a3931b8 100644
--- a/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R$dimen.class
+++ b/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R$dimen.class
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R$drawable.class b/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R$drawable.class
index 2b77af3..e293677 100644
--- a/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R$drawable.class
+++ b/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R$drawable.class
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R$id.class b/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R$id.class
index fd01b44..d6268bf 100644
--- a/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R$id.class
+++ b/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R$id.class
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R$integer.class b/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R$integer.class
index 91cf5b6..08b98fb 100644
--- a/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R$integer.class
+++ b/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R$integer.class
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R$layout.class b/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R$layout.class
index 6c351da..f9be1ca 100644
--- a/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R$layout.class
+++ b/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R$layout.class
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R$menu.class b/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R$menu.class
index aecbff6..6874b49 100644
--- a/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R$menu.class
+++ b/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R$menu.class
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R$string.class b/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R$string.class
index fc3f236..a4205a8 100644
--- a/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R$string.class
+++ b/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R$string.class
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R$style.class b/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R$style.class
index 83ad35b..4fb3b61 100644
--- a/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R$style.class
+++ b/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R$style.class
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R.class b/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R.class
index 6d7c719..dba67fd 100644
--- a/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R.class
+++ b/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R.class
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden/activity.png b/bridge/tests/res/testApp/MyApplication/golden/activity.png
index 9bf302a..199ea60 100644
--- a/bridge/tests/res/testApp/MyApplication/golden/activity.png
+++ b/bridge/tests/res/testApp/MyApplication/golden/activity.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden/allwidgets.png b/bridge/tests/res/testApp/MyApplication/golden/allwidgets.png
index 0e788e0..89ff5db 100644
--- a/bridge/tests/res/testApp/MyApplication/golden/allwidgets.png
+++ b/bridge/tests/res/testApp/MyApplication/golden/allwidgets.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden/allwidgets_tab.png b/bridge/tests/res/testApp/MyApplication/golden/allwidgets_tab.png
index bad296b..1f4405d 100644
--- a/bridge/tests/res/testApp/MyApplication/golden/allwidgets_tab.png
+++ b/bridge/tests/res/testApp/MyApplication/golden/allwidgets_tab.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden/animated_vector.png b/bridge/tests/res/testApp/MyApplication/golden/animated_vector.png
index 9f26627..26aed6a 100644
--- a/bridge/tests/res/testApp/MyApplication/golden/animated_vector.png
+++ b/bridge/tests/res/testApp/MyApplication/golden/animated_vector.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden/animated_vector_1.png b/bridge/tests/res/testApp/MyApplication/golden/animated_vector_1.png
index 89009be..aaf1514 100644
--- a/bridge/tests/res/testApp/MyApplication/golden/animated_vector_1.png
+++ b/bridge/tests/res/testApp/MyApplication/golden/animated_vector_1.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden/array_check.png b/bridge/tests/res/testApp/MyApplication/golden/array_check.png
index 336f9d8..c3bd708 100644
--- a/bridge/tests/res/testApp/MyApplication/golden/array_check.png
+++ b/bridge/tests/res/testApp/MyApplication/golden/array_check.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden/four_corners.png b/bridge/tests/res/testApp/MyApplication/golden/four_corners.png
new file mode 100644
index 0000000..868cd51
--- /dev/null
+++ b/bridge/tests/res/testApp/MyApplication/golden/four_corners.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden/four_corners_translucent.png b/bridge/tests/res/testApp/MyApplication/golden/four_corners_translucent.png
new file mode 100644
index 0000000..601f711
--- /dev/null
+++ b/bridge/tests/res/testApp/MyApplication/golden/four_corners_translucent.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden/four_corners_translucent_land.png b/bridge/tests/res/testApp/MyApplication/golden/four_corners_translucent_land.png
new file mode 100644
index 0000000..0b8f1a9
--- /dev/null
+++ b/bridge/tests/res/testApp/MyApplication/golden/four_corners_translucent_land.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden/simple_activity-old-theme.png b/bridge/tests/res/testApp/MyApplication/golden/simple_activity-old-theme.png
new file mode 100644
index 0000000..eb431b0
--- /dev/null
+++ b/bridge/tests/res/testApp/MyApplication/golden/simple_activity-old-theme.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden/simple_activity.png b/bridge/tests/res/testApp/MyApplication/golden/simple_activity.png
new file mode 100644
index 0000000..4e448c8
--- /dev/null
+++ b/bridge/tests/res/testApp/MyApplication/golden/simple_activity.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden/simple_activity_noactionbar.png b/bridge/tests/res/testApp/MyApplication/golden/simple_activity_noactionbar.png
new file mode 100644
index 0000000..b756719
--- /dev/null
+++ b/bridge/tests/res/testApp/MyApplication/golden/simple_activity_noactionbar.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden/vector_drawable.png b/bridge/tests/res/testApp/MyApplication/golden/vector_drawable.png
index 55d6a20..466eca8 100644
--- a/bridge/tests/res/testApp/MyApplication/golden/vector_drawable.png
+++ b/bridge/tests/res/testApp/MyApplication/golden/vector_drawable.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden/vector_drawable_91383.png b/bridge/tests/res/testApp/MyApplication/golden/vector_drawable_91383.png
new file mode 100644
index 0000000..940fe5b
--- /dev/null
+++ b/bridge/tests/res/testApp/MyApplication/golden/vector_drawable_91383.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/src/main/java/com/android/layoutlib/test/myapplication/ArraysCheckWidget.java b/bridge/tests/res/testApp/MyApplication/src/main/java/com/android/layoutlib/test/myapplication/ArraysCheckWidget.java
index 41d75de..e7f22bf 100644
--- a/bridge/tests/res/testApp/MyApplication/src/main/java/com/android/layoutlib/test/myapplication/ArraysCheckWidget.java
+++ b/bridge/tests/res/testApp/MyApplication/src/main/java/com/android/layoutlib/test/myapplication/ArraysCheckWidget.java
@@ -1,3 +1,19 @@
+/*
+ * Copyright (C) 2016 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.test.myapplication;
import android.content.Context;
diff --git a/bridge/tests/res/testApp/MyApplication/src/main/java/com/android/layoutlib/test/myapplication/CustomCalendar.java b/bridge/tests/res/testApp/MyApplication/src/main/java/com/android/layoutlib/test/myapplication/CustomCalendar.java
deleted file mode 100644
index 80bbaf1..0000000
--- a/bridge/tests/res/testApp/MyApplication/src/main/java/com/android/layoutlib/test/myapplication/CustomCalendar.java
+++ /dev/null
@@ -1,31 +0,0 @@
-package com.android.layoutlib.test.myapplication;
-
-import android.content.Context;
-import android.util.AttributeSet;
-import android.widget.CalendarView;
-
-public class CustomCalendar extends CalendarView {
- public CustomCalendar(Context context) {
- super(context);
- init();
- }
-
- public CustomCalendar(Context context, AttributeSet attrs) {
- super(context, attrs);
- init();
- }
-
- public CustomCalendar(Context context, AttributeSet attrs, int defStyleAttr) {
- super(context, attrs, defStyleAttr);
- init();
- }
-
- public CustomCalendar(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
- super(context, attrs, defStyleAttr, defStyleRes);
- init();
- }
-
- private void init() {
- setDate(871703200000L, false, true);
- }
-}
diff --git a/bridge/tests/res/testApp/MyApplication/src/main/java/com/android/layoutlib/test/myapplication/CustomDate.java b/bridge/tests/res/testApp/MyApplication/src/main/java/com/android/layoutlib/test/myapplication/CustomDate.java
deleted file mode 100644
index cb750f4..0000000
--- a/bridge/tests/res/testApp/MyApplication/src/main/java/com/android/layoutlib/test/myapplication/CustomDate.java
+++ /dev/null
@@ -1,31 +0,0 @@
-package com.android.layoutlib.test.myapplication;
-
-import android.content.Context;
-import android.util.AttributeSet;
-import android.widget.DatePicker;
-
-public class CustomDate extends DatePicker {
- public CustomDate(Context context) {
- super(context);
- init();
- }
-
- public CustomDate(Context context, AttributeSet attrs) {
- super(context, attrs);
- init();
- }
-
- public CustomDate(Context context, AttributeSet attrs, int defStyleAttr) {
- super(context, attrs, defStyleAttr);
- init();
- }
-
- public CustomDate(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
- super(context, attrs, defStyleAttr, defStyleRes);
- init();
- }
-
- private void init() {
- init(2015, 0, 20, null);
- }
-}
diff --git a/bridge/tests/res/testApp/MyApplication/src/main/myapplication.widgets/CustomCalendar.java b/bridge/tests/res/testApp/MyApplication/src/main/myapplication.widgets/CustomCalendar.java
new file mode 100644
index 0000000..3b819e5
--- /dev/null
+++ b/bridge/tests/res/testApp/MyApplication/src/main/myapplication.widgets/CustomCalendar.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2016 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.test.myapplication.widgets;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.widget.CalendarView;
+
+public class CustomCalendar extends CalendarView {
+ public CustomCalendar(Context context) {
+ super(context);
+ init();
+ }
+
+ public CustomCalendar(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ init();
+ }
+
+ public CustomCalendar(Context context, AttributeSet attrs, int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+ init();
+ }
+
+ public CustomCalendar(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
+ super(context, attrs, defStyleAttr, defStyleRes);
+ init();
+ }
+
+ private void init() {
+ setDate(871732800000L, false, true);
+ }
+}
diff --git a/bridge/tests/res/testApp/MyApplication/src/main/myapplication.widgets/CustomDate.java b/bridge/tests/res/testApp/MyApplication/src/main/myapplication.widgets/CustomDate.java
new file mode 100644
index 0000000..f3877f1
--- /dev/null
+++ b/bridge/tests/res/testApp/MyApplication/src/main/myapplication.widgets/CustomDate.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2016 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.test.myapplication.widgets;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.widget.DatePicker;
+
+public class CustomDate extends DatePicker {
+ public CustomDate(Context context) {
+ super(context);
+ init();
+ }
+
+ public CustomDate(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ init();
+ }
+
+ public CustomDate(Context context, AttributeSet attrs, int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+ init();
+ }
+
+ public CustomDate(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
+ super(context, attrs, defStyleAttr, defStyleRes);
+ init();
+ }
+
+ private void init() {
+ init(2015, 0, 20, null);
+ }
+}
diff --git a/bridge/tests/res/testApp/MyApplication/src/main/myapplication.widgets/InsetsWidget.java b/bridge/tests/res/testApp/MyApplication/src/main/myapplication.widgets/InsetsWidget.java
new file mode 100644
index 0000000..36e5c26
--- /dev/null
+++ b/bridge/tests/res/testApp/MyApplication/src/main/myapplication.widgets/InsetsWidget.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2016 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.test.myapplication.widgets;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.view.WindowInsets;
+import android.widget.TextView;
+
+public class InsetsWidget extends TextView {
+ public static boolean sApplyInsetsCalled = false;
+
+ public InsetsWidget(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ @Override
+ protected void onAttachedToWindow() {
+ super.onAttachedToWindow();
+
+ requestApplyInsets();
+ }
+
+ @Override
+ public WindowInsets onApplyWindowInsets(WindowInsets insets) {
+ sApplyInsetsCalled = true;
+ return super.onApplyWindowInsets(insets);
+ }
+}
diff --git a/bridge/tests/res/testApp/MyApplication/src/main/myapplication.widgets/package-info.java b/bridge/tests/res/testApp/MyApplication/src/main/myapplication.widgets/package-info.java
new file mode 100644
index 0000000..58ad46d
--- /dev/null
+++ b/bridge/tests/res/testApp/MyApplication/src/main/myapplication.widgets/package-info.java
@@ -0,0 +1,6 @@
+/**
+ * This package contains custom widgets used to set a specific time for the DayTimePicker during
+ * testing.
+ * The classes here are both used from the Android project and from the Bridge test project.
+ */
+package com.android.layoutlib.test.myapplication.widgets;
\ No newline at end of file
diff --git a/bridge/tests/res/testApp/MyApplication/src/main/res/drawable/android.xml b/bridge/tests/res/testApp/MyApplication/src/main/res/drawable/android.xml
new file mode 100644
index 0000000..42e3beb
--- /dev/null
+++ b/bridge/tests/res/testApp/MyApplication/src/main/res/drawable/android.xml
@@ -0,0 +1,65 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2016 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.
+ -->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:viewportWidth="1102"
+ android:viewportHeight="642"
+ android:width="1102px"
+ android:height="642px">
+
+ <group
+ android:translateX="-126.347"
+ android:translateY="6.7928655e-4">
+
+
+ <path
+ android:fillColor="#94c147"
+ android:pathData="
+ m 552.777,147.57
+ c -14.147,0 -25.622,11.652 -25.622,26.02
+ v 101.68
+ c 0,14.372 11.475,26.019 25.622,26.019 14.147,0 25.61,-11.646 25.61,-26.019
+ V 173.59
+ c 0.001,-14.368 -11.462,-26.02 -25.61,-26.02
+ z
+
+ m -309.011,0
+ c -14.155,0 -25.618,11.652 -25.618,26.02
+ v 101.68
+ c 0,14.372 11.462,26.019 25.618,26.019 14.153,0 25.623,-11.646 25.623,-26.019
+ V 173.59
+ c -0.008,-14.368 -11.475,-26.02 -25.623,-26.02
+ z" />
+
+
+ <path
+ android:fillColor="#94c147"
+ android:pathData="m 284.799,148.364 v 185.768 c 0,11.035 8.948,19.98 19.983,19.98 h 22.815 v 56.567 c 0,14.37 11.47,26.016 25.623,26.016 14.148,0 25.623,-11.646 25.623,-26.016 v -56.567 h 39.878 v 56.567 c 0,14.37 11.463,26.016 25.61,26.016 14.147,0 25.622,-11.646 25.622,-26.016 v -56.567 h 22.828 c 11.022,0 19.971,-8.953 19.971,-19.98 V 148.364 H 284.799 l 0,0 z" />
+
+ <group
+ android:name="head"
+ android:pivotX="400"
+ android:pivotY="131.105">
+
+ <path
+ android:fillColor="#94c147"
+ android:pathData="m 452.302,52.105 21.057,-30.572 c 1.245,-1.819 0.939,-4.199 -0.695,-5.329 -1.637,-1.123 -3.968,-0.568 -5.225,1.251 l -21.875,31.75 c -14.404,-5.682 -30.418,-8.844 -47.293,-8.844 -16.87,0 -32.893,3.162 -47.297,8.844 l -21.875,-31.75 c -1.257,-1.819 -3.589,-2.375 -5.225,-1.251 -1.636,1.124 -1.946,3.509 -0.696,5.329 l 21.057,30.572 c -33.464,15.57 -56.951,45.166 -59.941,79.706 H 512.25 C 509.259,97.271 485.772,67.676 452.302,52.105 z M 350.187,100.28 c -6.965,0 -12.617,-5.646 -12.617,-12.616 0,-6.958 5.647,-12.61 12.617,-12.61 6.97,0 12.603,5.652 12.603,12.61 0,6.965 -5.64,12.616 -12.603,12.616 z m 97.744,0 c -6.97,0 -12.609,-5.646 -12.609,-12.616 0,-6.958 5.64,-12.61 12.609,-12.61 6.971,0 12.61,5.652 12.61,12.61 0,6.965 -5.64,12.616 -12.61,12.616 z" />
+ </group>
+
+ </group>
+
+</vector>
\ No newline at end of file
diff --git a/bridge/tests/res/testApp/MyApplication/src/main/res/drawable/headset.xml b/bridge/tests/res/testApp/MyApplication/src/main/res/drawable/headset.xml
new file mode 100644
index 0000000..897c411
--- /dev/null
+++ b/bridge/tests/res/testApp/MyApplication/src/main/res/drawable/headset.xml
@@ -0,0 +1,25 @@
+<!--
+ ~ Copyright (C) 2016 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.
+ -->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="150dp"
+ android:height="150dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="m12,1c-4.97,0 -9,4.03 -9,9v7c0,1.66 1.34,3 3,3h3v-8H5v-2c0,-3.87 3.13,-7 7,-7s7,3.13 7,7v2h-4v8h4v1h-7v2h6c1.66,0 3,-1.34 3,-3V10c0,-4.97 -4.03,-9 -9,-9z"/>
+</vector>
\ No newline at end of file
diff --git a/bridge/tests/res/testApp/MyApplication/src/main/res/drawable/multi_path.xml b/bridge/tests/res/testApp/MyApplication/src/main/res/drawable/multi_path.xml
index 32e6e73..0998b25 100644
--- a/bridge/tests/res/testApp/MyApplication/src/main/res/drawable/multi_path.xml
+++ b/bridge/tests/res/testApp/MyApplication/src/main/res/drawable/multi_path.xml
@@ -4,7 +4,8 @@
android:height="76dp"
android:width="76dp"
android:viewportHeight="48"
- android:viewportWidth="48">
+ android:viewportWidth="48"
+ android:alpha="0.6">
<group
android:name="root"
@@ -79,7 +80,7 @@
android:fillType="nonZero"
android:strokeWidth="1"
android:strokeColor="#AABBCC"
- android:fillColor="#AAEFCC"
+ android:fillColor="#40AAEFCC"
android:pathData="M0,-40 l0, 10 l10, 0 l0, -10 l-10,0 m5,0 l0, 10 l10, 0 l0, -10 l-10,0"
/>
</group>
diff --git a/bridge/tests/res/testApp/MyApplication/src/main/res/layout/allwidgets.xml b/bridge/tests/res/testApp/MyApplication/src/main/res/layout/allwidgets.xml
index adb58a3..05a3665 100644
--- a/bridge/tests/res/testApp/MyApplication/src/main/res/layout/allwidgets.xml
+++ b/bridge/tests/res/testApp/MyApplication/src/main/res/layout/allwidgets.xml
@@ -28,7 +28,8 @@
android:layout_alignParentStart="true"
android:layout_below="@id/frameLayout"
android:text="Large Text"
- android:textAppearance="?android:attr/textAppearanceLarge" />
+ android:textAppearance="?android:attr/textAppearanceLarge"
+ android:pointerIcon="hand" />
<TextView
android:id="@id/textView3"
diff --git a/bridge/tests/res/testApp/MyApplication/src/main/res/layout/array_check.xml b/bridge/tests/res/testApp/MyApplication/src/main/res/layout/array_check.xml
index 50646ab..e9aa9e1 100644
--- a/bridge/tests/res/testApp/MyApplication/src/main/res/layout/array_check.xml
+++ b/bridge/tests/res/testApp/MyApplication/src/main/res/layout/array_check.xml
@@ -1,5 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
-<com.android.layoutlib.test.myapplication.ArraysCheckWidget xmlns:android="http://schemas.android.com/apk/res/android"
+<com.android.layoutlib.test.myapplication.ArraysCheckWidget
+ xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="match_parent">
diff --git a/bridge/tests/res/testApp/MyApplication/src/main/res/layout/four_corners.xml b/bridge/tests/res/testApp/MyApplication/src/main/res/layout/four_corners.xml
new file mode 100644
index 0000000..c42284a
--- /dev/null
+++ b/bridge/tests/res/testApp/MyApplication/src/main/res/layout/four_corners.xml
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentBottom="true"
+ android:layout_centerHorizontal="true"
+ android:textSize="40sp"
+ android:text="Bottom Text" />
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentTop="true"
+ android:layout_centerHorizontal="true"
+ android:textSize="40sp"
+ android:text="Top text" />
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentStart="true"
+ android:layout_centerVertical="true"
+ android:textSize="40sp"
+ android:text="Start text" />
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentEnd="true"
+ android:layout_centerVertical="true"
+ android:textSize="40sp"
+ android:text="End text" />
+</RelativeLayout>
\ No newline at end of file
diff --git a/bridge/tests/res/testApp/MyApplication/src/main/res/layout/insets.xml b/bridge/tests/res/testApp/MyApplication/src/main/res/layout/insets.xml
new file mode 100644
index 0000000..ff06d79
--- /dev/null
+++ b/bridge/tests/res/testApp/MyApplication/src/main/res/layout/insets.xml
@@ -0,0 +1,12 @@
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:padding="16dp"
+ android:orientation="horizontal"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content">
+
+ <com.android.layoutlib.test.myapplication.widgets.InsetsWidget
+ android:text="Hello world"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:id="@+id/text1"/>
+</LinearLayout>
diff --git a/bridge/tests/res/testApp/MyApplication/src/main/res/layout/layout.xml b/bridge/tests/res/testApp/MyApplication/src/main/res/layout/layout.xml
index c1f663e..ff14ce0 100644
--- a/bridge/tests/res/testApp/MyApplication/src/main/res/layout/layout.xml
+++ b/bridge/tests/res/testApp/MyApplication/src/main/res/layout/layout.xml
@@ -15,10 +15,10 @@
android:checked="true"
android:layout_gravity="center"
/>
- <com.android.layoutlib.test.myapplication.CustomDate
+ <com.android.layoutlib.test.myapplication.widgets.CustomDate
android:layout_width="100dp"
android:layout_height="wrap_content"/>
- <com.android.layoutlib.test.myapplication.CustomCalendar
+ <com.android.layoutlib.test.myapplication.widgets.CustomCalendar
android:layout_width="200dp"
android:layout_gravity="center_horizontal"
android:layout_height="200dp"/>
diff --git a/bridge/tests/res/testApp/MyApplication/src/main/res/layout/simple_activity.xml b/bridge/tests/res/testApp/MyApplication/src/main/res/layout/simple_activity.xml
new file mode 100644
index 0000000..14b93f3
--- /dev/null
+++ b/bridge/tests/res/testApp/MyApplication/src/main/res/layout/simple_activity.xml
@@ -0,0 +1,32 @@
+<!--
+ ~ Copyright (C) 2016 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.
+ -->
+
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:paddingLeft="@dimen/activity_horizontal_margin"
+ android:paddingRight="@dimen/activity_horizontal_margin"
+ android:paddingTop="@dimen/activity_vertical_margin"
+ android:paddingBottom="@dimen/activity_vertical_margin">
+
+ <TextView
+ android:text="@string/hello_world"
+ android:layout_width="wrap_content"
+ android:layout_height="200dp"
+ android:background="#FF0000"
+ android:id="@+id/text1"/>
+
+</RelativeLayout>
\ No newline at end of file
diff --git a/bridge/tests/res/testApp/MyApplication/src/main/res/layout/vector_drawable_android.xml b/bridge/tests/res/testApp/MyApplication/src/main/res/layout/vector_drawable_android.xml
new file mode 100644
index 0000000..3b01ea0
--- /dev/null
+++ b/bridge/tests/res/testApp/MyApplication/src/main/res/layout/vector_drawable_android.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+ ~ Copyright (C) 2016 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.
+ -->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:padding="16dp"
+ android:orientation="vertical"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent">
+ <ImageView
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:src="@drawable/android"/>
+ <ImageView
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:src="@drawable/headset"/>
+
+</LinearLayout>
+
diff --git a/bridge/tests/res/testApp/MyApplication/src/main/res/values/arrays.xml b/bridge/tests/res/testApp/MyApplication/src/main/res/values/arrays.xml
index f6e14d2..5f58d39 100644
--- a/bridge/tests/res/testApp/MyApplication/src/main/res/values/arrays.xml
+++ b/bridge/tests/res/testApp/MyApplication/src/main/res/values/arrays.xml
@@ -17,6 +17,7 @@
<!-- theme ref in android NS. value = @string/candidates_style = <u>candidates</u> -->
<item>?android:attr/candidatesTextStyleSpans</item>
<item>@android:string/unknownName</item> <!-- value = Unknown -->
+ <item>?EC</item>
</string-array>
<!-- resources that the above array can refer to -->
diff --git a/bridge/tests/res/testApp/MyApplication/src/main/res/values/styles.xml b/bridge/tests/res/testApp/MyApplication/src/main/res/values/styles.xml
index 88c9cbc..debe33b 100644
--- a/bridge/tests/res/testApp/MyApplication/src/main/res/values/styles.xml
+++ b/bridge/tests/res/testApp/MyApplication/src/main/res/values/styles.xml
@@ -3,7 +3,6 @@
<!-- Base application theme. -->
<style name="AppTheme" parent="android:Theme.Material.Light.DarkActionBar">
<item name="myattr">@integer/ten</item>
- <!-- Customize your theme here. -->
</style>
</resources>
diff --git a/bridge/tests/src/com/android/layoutlib/bridge/intensive/Main.java b/bridge/tests/src/com/android/layoutlib/bridge/intensive/Main.java
index 8f570ae..ded52a7 100644
--- a/bridge/tests/src/com/android/layoutlib/bridge/intensive/Main.java
+++ b/bridge/tests/src/com/android/layoutlib/bridge/intensive/Main.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2014 The Android Open Source Project
+ * Copyright (C) 2016 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.
@@ -16,672 +16,23 @@
package com.android.layoutlib.bridge.intensive;
-import com.android.ide.common.rendering.api.LayoutLog;
-import com.android.ide.common.rendering.api.RenderSession;
-import com.android.ide.common.rendering.api.Result;
-import com.android.ide.common.rendering.api.SessionParams;
-import com.android.ide.common.rendering.api.SessionParams.RenderingMode;
-import com.android.ide.common.rendering.api.ViewInfo;
-import com.android.ide.common.resources.FrameworkResources;
-import com.android.ide.common.resources.ResourceItem;
-import com.android.ide.common.resources.ResourceRepository;
-import com.android.ide.common.resources.ResourceResolver;
-import com.android.ide.common.resources.configuration.FolderConfiguration;
-import com.android.io.FolderWrapper;
-import com.android.layoutlib.bridge.Bridge;
-import com.android.layoutlib.bridge.android.BridgeContext;
-import com.android.layoutlib.bridge.android.RenderParamsFlags;
-import com.android.layoutlib.bridge.impl.DelegateManager;
-import com.android.layoutlib.bridge.impl.RenderAction;
-import com.android.layoutlib.bridge.intensive.setup.ConfigGenerator;
-import com.android.layoutlib.bridge.intensive.setup.LayoutLibTestCallback;
-import com.android.layoutlib.bridge.intensive.setup.LayoutPullParser;
-import com.android.resources.Density;
-import com.android.resources.Navigation;
-import com.android.resources.ResourceType;
-import com.android.utils.ILogger;
+import com.android.layoutlib.bridge.TestDelegates;
+import com.android.layoutlib.bridge.android.BridgeXmlBlockParserTest;
+import com.android.layoutlib.bridge.impl.LayoutParserWrapperTest;
-import org.junit.AfterClass;
-import org.junit.Before;
-import org.junit.BeforeClass;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.rules.TestWatcher;
-import org.junit.runner.Description;
+import org.junit.runner.RunWith;
+import org.junit.runners.Suite;
+import org.junit.runners.Suite.SuiteClasses;
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.content.res.AssetManager;
-import android.content.res.Configuration;
-import android.content.res.Resources;
-import android.util.DisplayMetrics;
-
-import java.io.File;
-import java.io.IOException;
-import java.lang.ref.WeakReference;
-import java.net.URL;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.concurrent.TimeUnit;
-
-import com.google.android.collect.Lists;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.fail;
+import android.graphics.Matrix_DelegateTest;
/**
- * This is a set of tests that loads all the framework resources and a project checked in this
- * test's resources. The main dependencies
- * are:
- * 1. Fonts directory.
- * 2. Framework Resources.
- * 3. App resources.
- * 4. build.prop file
- *
- * These are configured by two variables set in the system properties.
- *
- * 1. platform.dir: This is the directory for the current platform in the built SDK
- * (.../sdk/platforms/android-<version>).
- *
- * The fonts are platform.dir/data/fonts.
- * The Framework resources are platform.dir/data/res.
- * build.prop is at platform.dir/build.prop.
- *
- * 2. test_res.dir: This is the directory for the resources of the test. If not specified, this
- * falls back to getClass().getProtectionDomain().getCodeSource().getLocation()
- *
- * The app resources are at: test_res.dir/testApp/MyApplication/app/src/main/res
+ * Suite used by the layoutlib build system
*/
+@RunWith(Suite.class)
+@SuiteClasses({
+ RenderTests.class, LayoutParserWrapperTest.class, BridgeXmlBlockParserTest.class,
+ Matrix_DelegateTest.class, TestDelegates.class
+})
public class Main {
-
- private static final String PLATFORM_DIR_PROPERTY = "platform.dir";
- private static final String RESOURCE_DIR_PROPERTY = "test_res.dir";
-
- private static final String PLATFORM_DIR;
- private static final String TEST_RES_DIR;
- /** Location of the app to test inside {@link #TEST_RES_DIR}*/
- private static final String APP_TEST_DIR = "/testApp/MyApplication";
- /** Location of the app's res dir inside {@link #TEST_RES_DIR}*/
- private static final String APP_TEST_RES = APP_TEST_DIR + "/src/main/res";
-
- private static LayoutLog sLayoutLibLog;
- private static FrameworkResources sFrameworkRepo;
- private static ResourceRepository sProjectResources;
- private static ILogger sLogger;
- private static Bridge sBridge;
-
- /** List of log messages generated by a render call. It can be used to find specific errors */
- private static ArrayList<String> sRenderMessages = Lists.newArrayList();
-
- @Rule
- public static TestWatcher sRenderMessageWatcher = new TestWatcher() {
- @Override
- protected void succeeded(Description description) {
- // We only check error messages if the rest of the test case was successful.
- if (!sRenderMessages.isEmpty()) {
- fail(description.getMethodName() + " render error message: " + sRenderMessages.get
- (0));
- }
- }
- };
-
- static {
- // Test that System Properties are properly set.
- PLATFORM_DIR = getPlatformDir();
- if (PLATFORM_DIR == null) {
- fail(String.format("System Property %1$s not properly set. The value is %2$s",
- PLATFORM_DIR_PROPERTY, System.getProperty(PLATFORM_DIR_PROPERTY)));
- }
-
- TEST_RES_DIR = getTestResDir();
- if (TEST_RES_DIR == null) {
- fail(String.format("System property %1$s.dir not properly set. The value is %2$s",
- RESOURCE_DIR_PROPERTY, System.getProperty(RESOURCE_DIR_PROPERTY)));
- }
- }
-
- private static String getPlatformDir() {
- String platformDir = System.getProperty(PLATFORM_DIR_PROPERTY);
- if (platformDir != null && !platformDir.isEmpty() && new File(platformDir).isDirectory()) {
- return platformDir;
- }
- // System Property not set. Try to find the directory in the build directory.
- String androidHostOut = System.getenv("ANDROID_HOST_OUT");
- if (androidHostOut != null) {
- platformDir = getPlatformDirFromHostOut(new File(androidHostOut));
- if (platformDir != null) {
- return platformDir;
- }
- }
- String workingDirString = System.getProperty("user.dir");
- File workingDir = new File(workingDirString);
- // Test if workingDir is android checkout root.
- platformDir = getPlatformDirFromRoot(workingDir);
- if (platformDir != null) {
- return platformDir;
- }
-
- // Test if workingDir is platform/frameworks/base/tools/layoutlib/bridge.
- File currentDir = workingDir;
- if (currentDir.getName().equalsIgnoreCase("bridge")) {
- currentDir = currentDir.getParentFile();
- }
- // Test if currentDir is platform/frameworks/base/tools/layoutlib. That is, root should be
- // workingDir/../../../../ (4 levels up)
- for (int i = 0; i < 4; i++) {
- if (currentDir != null) {
- currentDir = currentDir.getParentFile();
- }
- }
- return currentDir == null ? null : getPlatformDirFromRoot(currentDir);
- }
-
- private static String getPlatformDirFromRoot(File root) {
- if (!root.isDirectory()) {
- return null;
- }
- File out = new File(root, "out");
- if (!out.isDirectory()) {
- return null;
- }
- File host = new File(out, "host");
- if (!host.isDirectory()) {
- return null;
- }
- File[] hosts = host.listFiles(path -> path.isDirectory() &&
- (path.getName().startsWith("linux-") || path.getName().startsWith("darwin-")));
- for (File hostOut : hosts) {
- String platformDir = getPlatformDirFromHostOut(hostOut);
- if (platformDir != null) {
- return platformDir;
- }
- }
- return null;
- }
-
- private static String getPlatformDirFromHostOut(File out) {
- if (!out.isDirectory()) {
- return null;
- }
- File sdkDir = new File(out, "sdk");
- if (!sdkDir.isDirectory()) {
- return null;
- }
- File[] sdkDirs = sdkDir.listFiles(path -> {
- // We need to search for $TARGET_PRODUCT (usually, sdk_phone_armv7)
- return path.isDirectory() && path.getName().startsWith("sdk");
- });
- for (File dir : sdkDirs) {
- String platformDir = getPlatformDirFromHostOutSdkSdk(dir);
- if (platformDir != null) {
- return platformDir;
- }
- }
- return null;
- }
-
- private static String getPlatformDirFromHostOutSdkSdk(File sdkDir) {
- File[] possibleSdks = sdkDir.listFiles(
- path -> path.isDirectory() && path.getName().contains("android-sdk"));
- for (File possibleSdk : possibleSdks) {
- File platformsDir = new File(possibleSdk, "platforms");
- File[] platforms = platformsDir.listFiles(
- path -> path.isDirectory() && path.getName().startsWith("android-"));
- if (platforms == null || platforms.length == 0) {
- continue;
- }
- Arrays.sort(platforms, (o1, o2) -> {
- final int MAX_VALUE = 1000;
- String suffix1 = o1.getName().substring("android-".length());
- String suffix2 = o2.getName().substring("android-".length());
- int suff1, suff2;
- try {
- suff1 = Integer.parseInt(suffix1);
- } catch (NumberFormatException e) {
- suff1 = MAX_VALUE;
- }
- try {
- suff2 = Integer.parseInt(suffix2);
- } catch (NumberFormatException e) {
- suff2 = MAX_VALUE;
- }
- if (suff1 != MAX_VALUE || suff2 != MAX_VALUE) {
- return suff2 - suff1;
- }
- return suffix2.compareTo(suffix1);
- });
- return platforms[0].getAbsolutePath();
- }
- return null;
- }
-
- private static String getTestResDir() {
- String resourceDir = System.getProperty(RESOURCE_DIR_PROPERTY);
- if (resourceDir != null && !resourceDir.isEmpty() && new File(resourceDir).isDirectory()) {
- return resourceDir;
- }
- // TEST_RES_DIR not explicitly set. Fallback to the class's source location.
- try {
- URL location = Main.class.getProtectionDomain().getCodeSource().getLocation();
- return new File(location.getPath()).exists() ? location.getPath() : null;
- } catch (NullPointerException e) {
- // Prevent a lot of null checks by just catching the exception.
- return null;
- }
- }
-
- /**
- * Initialize the bridge and the resource maps.
- */
- @BeforeClass
- public static void setUp() {
- File data_dir = new File(PLATFORM_DIR, "data");
- File res = new File(data_dir, "res");
- sFrameworkRepo = new FrameworkResources(new FolderWrapper(res));
- sFrameworkRepo.loadResources();
- sFrameworkRepo.loadPublicResources(getLogger());
-
- sProjectResources =
- new ResourceRepository(new FolderWrapper(TEST_RES_DIR + APP_TEST_RES), false) {
- @NonNull
- @Override
- protected ResourceItem createResourceItem(@NonNull String name) {
- return new ResourceItem(name);
- }
- };
- sProjectResources.loadResources();
-
- File fontLocation = new File(data_dir, "fonts");
- File buildProp = new File(PLATFORM_DIR, "build.prop");
- File attrs = new File(res, "values" + File.separator + "attrs.xml");
- sBridge = new Bridge();
- sBridge.init(ConfigGenerator.loadProperties(buildProp), fontLocation,
- ConfigGenerator.getEnumMap(attrs), getLayoutLog());
- }
-
- @Before
- public void beforeTestCase() {
- sRenderMessages.clear();
- }
-
- /** Test activity.xml */
- @Test
- public void testActivity() throws ClassNotFoundException {
- renderAndVerify("activity.xml", "activity.png");
- }
-
- /** Test allwidgets.xml */
- @Test
- public void testAllWidgets() throws ClassNotFoundException {
- renderAndVerify("allwidgets.xml", "allwidgets.png");
-
- // We expect fidelity warnings for Path.isConvex. Fail for anything else.
- sRenderMessages.removeIf(message -> message.equals("Path.isConvex is not supported."));
- }
-
- @Test
- public void testArrayCheck() throws ClassNotFoundException {
- renderAndVerify("array_check.xml", "array_check.png");
- }
-
- @Test
- public void testAllWidgetsTablet() throws ClassNotFoundException {
- renderAndVerify("allwidgets.xml", "allwidgets_tab.png", ConfigGenerator.NEXUS_7_2012);
-
- // We expect fidelity warnings for Path.isConvex. Fail for anything else.
- sRenderMessages.removeIf(message -> message.equals("Path.isConvex is not supported."));
- }
-
- private static void gc() {
- // See RuntimeUtil#gc in jlibs (http://jlibs.in/)
- Object obj = new Object();
- WeakReference ref = new WeakReference<Object>(obj);
- obj = null;
- while(ref.get() != null) {
- System.gc();
- }
- }
-
- @AfterClass
- public static void tearDown() {
- sLayoutLibLog = null;
- sFrameworkRepo = null;
- sProjectResources = null;
- sLogger = null;
- sBridge = null;
-
- gc();
-
- System.out.println("Objects still linked from the DelegateManager:");
- DelegateManager.dump(System.out);
- }
-
- /** Test expand_layout.xml */
- @Test
- public void testExpand() throws ClassNotFoundException {
- // Create the layout pull parser.
- LayoutPullParser parser = createLayoutPullParser("expand_vert_layout.xml");
- // Create LayoutLibCallback.
- LayoutLibTestCallback layoutLibCallback = new LayoutLibTestCallback(getLogger());
- layoutLibCallback.initResources();
-
- ConfigGenerator customConfigGenerator = new ConfigGenerator()
- .setScreenWidth(300)
- .setScreenHeight(20)
- .setDensity(Density.XHIGH)
- .setNavigation(Navigation.NONAV);
-
- SessionParams params = getSessionParams(parser, customConfigGenerator,
- layoutLibCallback, "Theme.Material.Light.NoActionBar.Fullscreen", false,
- RenderingMode.V_SCROLL, 22);
-
- renderAndVerify(params, "expand_vert_layout.png");
-
- customConfigGenerator = new ConfigGenerator()
- .setScreenWidth(20)
- .setScreenHeight(300)
- .setDensity(Density.XHIGH)
- .setNavigation(Navigation.NONAV);
- parser = createLayoutPullParser("expand_horz_layout.xml");
- params = getSessionParams(parser, customConfigGenerator,
- layoutLibCallback, "Theme.Material.Light.NoActionBar.Fullscreen", false,
- RenderingMode.H_SCROLL, 22);
-
- renderAndVerify(params, "expand_horz_layout.png");
- }
-
- /** Test indeterminate_progressbar.xml */
- @Test
- public void testVectorAnimation() throws ClassNotFoundException {
- // Create the layout pull parser.
- LayoutPullParser parser = createLayoutPullParser("indeterminate_progressbar.xml");
- // Create LayoutLibCallback.
- LayoutLibTestCallback layoutLibCallback = new LayoutLibTestCallback(getLogger());
- layoutLibCallback.initResources();
-
- SessionParams params = getSessionParams(parser, ConfigGenerator.NEXUS_5,
- layoutLibCallback, "Theme.Material.NoActionBar.Fullscreen", false,
- RenderingMode.V_SCROLL, 22);
-
- renderAndVerify(params, "animated_vector.png", TimeUnit.SECONDS.toNanos(2));
-
- parser = createLayoutPullParser("indeterminate_progressbar.xml");
- params = getSessionParams(parser, ConfigGenerator.NEXUS_5,
- layoutLibCallback, "Theme.Material.NoActionBar.Fullscreen", false,
- RenderingMode.V_SCROLL, 22);
- renderAndVerify(params, "animated_vector_1.png", TimeUnit.SECONDS.toNanos(3));
- }
-
- /**
- * Test a vector drawable that uses trimStart and trimEnd. It also tests all the primitives
- * for vector drawables (lines, moves and cubic and quadratic curves).
- */
- @Test
- public void testVectorDrawable() throws ClassNotFoundException {
- // Create the layout pull parser.
- LayoutPullParser parser = createLayoutPullParser("vector_drawable.xml");
- // Create LayoutLibCallback.
- LayoutLibTestCallback layoutLibCallback = new LayoutLibTestCallback(getLogger());
- layoutLibCallback.initResources();
-
- SessionParams params = getSessionParams(parser, ConfigGenerator.NEXUS_5,
- layoutLibCallback, "Theme.Material.NoActionBar.Fullscreen", false,
- RenderingMode.V_SCROLL, 22);
-
- renderAndVerify(params, "vector_drawable.png", TimeUnit.SECONDS.toNanos(2));
- }
-
- /** Test activity.xml */
- @Test
- public void testScrolling() throws ClassNotFoundException {
- // Create the layout pull parser.
- LayoutPullParser parser = createLayoutPullParser("scrolled.xml");
- // Create LayoutLibCallback.
- LayoutLibTestCallback layoutLibCallback = new LayoutLibTestCallback(getLogger());
- layoutLibCallback.initResources();
-
- SessionParams params = getSessionParams(parser, ConfigGenerator.NEXUS_5,
- layoutLibCallback, "Theme.Material.NoActionBar.Fullscreen", false,
- RenderingMode.V_SCROLL, 22);
- params.setForceNoDecor();
- params.setExtendedViewInfoMode(true);
-
- RenderResult result = renderAndVerify(params, "scrolled.png");
- assertNotNull(result);
- assertTrue(result.getResult().isSuccess());
-
- ViewInfo rootLayout = result.getRootViews().get(0);
- // Check the first box in the main LinearLayout
- assertEquals(-90, rootLayout.getChildren().get(0).getTop());
- assertEquals(-30, rootLayout.getChildren().get(0).getLeft());
- assertEquals(90, rootLayout.getChildren().get(0).getBottom());
- assertEquals(150, rootLayout.getChildren().get(0).getRight());
-
- // Check the first box within the nested LinearLayout
- assertEquals(-450, rootLayout.getChildren().get(5).getChildren().get(0).getTop());
- assertEquals(90, rootLayout.getChildren().get(5).getChildren().get(0).getLeft());
- assertEquals(-270, rootLayout.getChildren().get(5).getChildren().get(0).getBottom());
- assertEquals(690, rootLayout.getChildren().get(5).getChildren().get(0).getRight());
- }
-
- @Test
- public void testGetResourceNameVariants() throws Exception {
- // Setup
- SessionParams params = createSessionParams("", ConfigGenerator.NEXUS_4);
- AssetManager assetManager = AssetManager.getSystem();
- DisplayMetrics metrics = new DisplayMetrics();
- Configuration configuration = RenderAction.getConfiguration(params);
- Resources resources = new Resources(assetManager, metrics, configuration);
- resources.mLayoutlibCallback = params.getLayoutlibCallback();
- resources.mContext =
- new BridgeContext(params.getProjectKey(), metrics, params.getResources(),
- params.getAssets(), params.getLayoutlibCallback(), configuration,
- params.getTargetSdkVersion(), params.isRtlSupported());
- // Test
- assertEquals("android:style/ButtonBar",
- resources.getResourceName(android.R.style.ButtonBar));
- assertEquals("android", resources.getResourcePackageName(android.R.style.ButtonBar));
- assertEquals("ButtonBar", resources.getResourceEntryName(android.R.style.ButtonBar));
- assertEquals("style", resources.getResourceTypeName(android.R.style.ButtonBar));
- int id = resources.mLayoutlibCallback.getResourceId(ResourceType.STRING, "app_name");
- assertEquals("com.android.layoutlib.test.myapplication:string/app_name",
- resources.getResourceName(id));
- assertEquals("com.android.layoutlib.test.myapplication",
- resources.getResourcePackageName(id));
- assertEquals("string", resources.getResourceTypeName(id));
- assertEquals("app_name", resources.getResourceEntryName(id));
- }
-
- @NonNull
- private LayoutPullParser createLayoutPullParser(String layoutPath) {
- return new LayoutPullParser(APP_TEST_RES + "/layout/" + layoutPath);
- }
-
- /**
- * Create a new rendering session and test that rendering the given layout doesn't throw any
- * exceptions and matches the provided image.
- * <p>
- * If frameTimeNanos is >= 0 a frame will be executed during the rendering. The time indicates
- * how far in the future is.
- */
- @Nullable
- private RenderResult renderAndVerify(SessionParams params, String goldenFileName, long frameTimeNanos)
- throws ClassNotFoundException {
- // TODO: Set up action bar handler properly to test menu rendering.
- // Create session params.
- RenderSession session = sBridge.createSession(params);
-
- if (frameTimeNanos != -1) {
- session.setElapsedFrameTimeNanos(frameTimeNanos);
- }
-
- if (!session.getResult().isSuccess()) {
- getLogger().error(session.getResult().getException(),
- session.getResult().getErrorMessage());
- }
- // Render the session with a timeout of 50s.
- Result renderResult = session.render(50000);
- if (!renderResult.isSuccess()) {
- getLogger().error(session.getResult().getException(),
- session.getResult().getErrorMessage());
- }
- try {
- String goldenImagePath = APP_TEST_DIR + "/golden/" + goldenFileName;
- ImageUtils.requireSimilar(goldenImagePath, session.getImage());
-
- return RenderResult.getFromSession(session);
- } catch (IOException e) {
- getLogger().error(e, e.getMessage());
- } finally {
- session.dispose();
- }
-
- return null;
- }
-
- /**
- * Create a new rendering session and test that rendering the given layout doesn't throw any
- * exceptions and matches the provided image.
- */
- @Nullable
- private RenderResult renderAndVerify(SessionParams params, String goldenFileName)
- throws ClassNotFoundException {
- return renderAndVerify(params, goldenFileName, -1);
- }
-
- /**
- * Create a new rendering session and test that rendering the given layout on nexus 5
- * doesn't throw any exceptions and matches the provided image.
- */
- @Nullable
- private RenderResult renderAndVerify(String layoutFileName, String goldenFileName)
- throws ClassNotFoundException {
- return renderAndVerify(layoutFileName, goldenFileName, ConfigGenerator.NEXUS_5);
- }
-
- /**
- * Create a new rendering session and test that rendering the given layout on given device
- * doesn't throw any exceptions and matches the provided image.
- */
- @Nullable
- private RenderResult renderAndVerify(String layoutFileName, String goldenFileName,
- ConfigGenerator deviceConfig)
- throws ClassNotFoundException {
- SessionParams params = createSessionParams(layoutFileName, deviceConfig);
- return renderAndVerify(params, goldenFileName);
- }
-
- private SessionParams createSessionParams(String layoutFileName, ConfigGenerator deviceConfig)
- throws ClassNotFoundException {
- // Create the layout pull parser.
- LayoutPullParser parser = createLayoutPullParser(layoutFileName);
- // Create LayoutLibCallback.
- LayoutLibTestCallback layoutLibCallback = new LayoutLibTestCallback(getLogger());
- layoutLibCallback.initResources();
- // TODO: Set up action bar handler properly to test menu rendering.
- // Create session params.
- return getSessionParams(parser, deviceConfig,
- layoutLibCallback, "AppTheme", true, RenderingMode.NORMAL, 22);
- }
-
- /**
- * Uses Theme.Material and Target sdk version as 22.
- */
- private SessionParams getSessionParams(LayoutPullParser layoutParser,
- ConfigGenerator configGenerator, LayoutLibTestCallback layoutLibCallback,
- String themeName, boolean isProjectTheme, RenderingMode renderingMode, int targetSdk) {
- FolderConfiguration config = configGenerator.getFolderConfig();
- ResourceResolver resourceResolver =
- ResourceResolver.create(sProjectResources.getConfiguredResources(config),
- sFrameworkRepo.getConfiguredResources(config),
- themeName, isProjectTheme);
-
- SessionParams sessionParams = new SessionParams(
- layoutParser,
- renderingMode,
- null /*used for caching*/,
- configGenerator.getHardwareConfig(),
- resourceResolver,
- layoutLibCallback,
- 0,
- targetSdk,
- getLayoutLog());
- sessionParams.setFlag(RenderParamsFlags.FLAG_DO_NOT_RENDER_ON_CREATE, true);
- return sessionParams;
- }
-
- private static LayoutLog getLayoutLog() {
- if (sLayoutLibLog == null) {
- sLayoutLibLog = new LayoutLog() {
- @Override
- public void warning(String tag, String message, Object data) {
- System.out.println("Warning " + tag + ": " + message);
- failWithMsg(message);
- }
-
- @Override
- 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 == null ? "" : message);
- }
-
- @Override
- public void error(String tag, String message, Object data) {
- System.out.println("Error " + tag + ": " + message);
- failWithMsg(message);
- }
-
- @Override
- public void error(String tag, String message, Throwable throwable, Object data) {
- System.out.println("Error " + tag + ": " + message);
- if (throwable != null) {
- throwable.printStackTrace();
- }
- failWithMsg(message);
- }
- };
- }
- return sLayoutLibLog;
- }
-
- private static ILogger getLogger() {
- if (sLogger == null) {
- sLogger = new ILogger() {
- @Override
- public void error(Throwable t, @Nullable String msgFormat, Object... args) {
- if (t != null) {
- t.printStackTrace();
- }
- failWithMsg(msgFormat == null ? "" : msgFormat, args);
- }
-
- @Override
- public void warning(@NonNull String msgFormat, Object... args) {
- failWithMsg(msgFormat, args);
- }
-
- @Override
- public void info(@NonNull String msgFormat, Object... args) {
- // pass.
- }
-
- @Override
- public void verbose(@NonNull String msgFormat, Object... args) {
- // pass.
- }
- };
- }
- return sLogger;
- }
-
- private static void failWithMsg(@NonNull String msgFormat, Object... args) {
- sRenderMessages.add(args == null ? msgFormat : String.format(msgFormat, args));
- }
}
diff --git a/bridge/tests/src/com/android/layoutlib/bridge/intensive/PerformanceTests.java b/bridge/tests/src/com/android/layoutlib/bridge/intensive/PerformanceTests.java
new file mode 100644
index 0000000..c90c26a
--- /dev/null
+++ b/bridge/tests/src/com/android/layoutlib/bridge/intensive/PerformanceTests.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2016 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.intensive;
+
+import com.android.ide.common.rendering.api.SessionParams;
+import com.android.layoutlib.bridge.intensive.setup.ConfigGenerator;
+import com.android.layoutlib.bridge.intensive.util.perf.PerformanceRunner;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import android.annotation.NonNull;
+
+/**
+ * Set of render tests
+ */
+@RunWith(PerformanceRunner.class)
+public class PerformanceTests extends RenderTestBase {
+
+ @Before
+ public void setUp() {
+ ignoreAllLogging();
+ }
+
+
+ private void render(@NonNull String layoutFileName) throws ClassNotFoundException {
+ SessionParams params = createSessionParams(layoutFileName, ConfigGenerator.NEXUS_5);
+ render(params, 250);
+ }
+
+ @Test
+ public void testActivity() throws ClassNotFoundException {
+ render("activity.xml");
+ }
+
+ @Test
+ public void testAllWidgets() throws ClassNotFoundException {
+ render("allwidgets.xml");
+ }
+}
diff --git a/bridge/tests/src/com/android/layoutlib/bridge/intensive/RenderResult.java b/bridge/tests/src/com/android/layoutlib/bridge/intensive/RenderResult.java
index 17b20f7..087478f 100644
--- a/bridge/tests/src/com/android/layoutlib/bridge/intensive/RenderResult.java
+++ b/bridge/tests/src/com/android/layoutlib/bridge/intensive/RenderResult.java
@@ -23,6 +23,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
+import java.awt.image.BufferedImage;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
@@ -31,19 +32,22 @@
private final List<ViewInfo> mRootViews;
private final List<ViewInfo> mSystemViews;
private final Result mRenderResult;
+ private BufferedImage mImage;
private RenderResult(@Nullable Result result, @Nullable List<ViewInfo> systemViewInfoList,
- @Nullable List<ViewInfo> rootViewInfoList) {
+ @Nullable List<ViewInfo> rootViewInfoList, @Nullable BufferedImage image) {
mSystemViews = systemViewInfoList == null ? Collections.emptyList() : systemViewInfoList;
mRootViews = rootViewInfoList == null ? Collections.emptyList() : rootViewInfoList;
mRenderResult = result;
+ mImage = image;
}
@NonNull
static RenderResult getFromSession(@NonNull RenderSession session) {
return new RenderResult(session.getResult(),
new ArrayList<>(session.getSystemRootViews()),
- new ArrayList<>(session.getRootViews()));
+ new ArrayList<>(session.getRootViews()),
+ session.getImage());
}
@Nullable
@@ -60,4 +64,9 @@
public List<ViewInfo> getSystemViews() {
return mSystemViews;
}
+
+ @Nullable
+ public BufferedImage getImage() {
+ return mImage;
+ }
}
diff --git a/bridge/tests/src/com/android/layoutlib/bridge/intensive/RenderTestBase.java b/bridge/tests/src/com/android/layoutlib/bridge/intensive/RenderTestBase.java
new file mode 100644
index 0000000..3e5f9e0
--- /dev/null
+++ b/bridge/tests/src/com/android/layoutlib/bridge/intensive/RenderTestBase.java
@@ -0,0 +1,542 @@
+/*
+ * Copyright (C) 2016 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.intensive;
+
+import com.android.ide.common.rendering.api.LayoutLog;
+import com.android.ide.common.rendering.api.RenderSession;
+import com.android.ide.common.rendering.api.Result;
+import com.android.ide.common.rendering.api.SessionParams;
+import com.android.ide.common.rendering.api.SessionParams.RenderingMode;
+import com.android.ide.common.resources.FrameworkResources;
+import com.android.ide.common.resources.ResourceItem;
+import com.android.ide.common.resources.ResourceRepository;
+import com.android.ide.common.resources.ResourceResolver;
+import com.android.ide.common.resources.configuration.FolderConfiguration;
+import com.android.io.FolderWrapper;
+import com.android.layoutlib.bridge.Bridge;
+import com.android.layoutlib.bridge.android.RenderParamsFlags;
+import com.android.layoutlib.bridge.impl.DelegateManager;
+import com.android.layoutlib.bridge.intensive.setup.ConfigGenerator;
+import com.android.layoutlib.bridge.intensive.setup.LayoutLibTestCallback;
+import com.android.layoutlib.bridge.intensive.setup.LayoutPullParser;
+import com.android.layoutlib.bridge.intensive.util.ImageUtils;
+import com.android.layoutlib.bridge.intensive.util.ModuleClassLoader;
+import com.android.layoutlib.bridge.intensive.util.TestUtils;
+import com.android.tools.layoutlib.java.System_Delegate;
+import com.android.utils.ILogger;
+
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Rule;
+import org.junit.rules.TestWatcher;
+import org.junit.runner.Description;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+
+import java.io.File;
+import java.io.IOException;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.concurrent.TimeUnit;
+
+import com.google.android.collect.Lists;
+
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.fail;
+
+/**
+ * Base class for render tests. The render tests load all the framework resources and a project
+ * checked in this test's resources. The main dependencies
+ * are:
+ * 1. Fonts directory.
+ * 2. Framework Resources.
+ * 3. App resources.
+ * 4. build.prop file
+ * <p>
+ * These are configured by two variables set in the system properties.
+ * <p>
+ * 1. platform.dir: This is the directory for the current platform in the built SDK
+ * (.../sdk/platforms/android-<version>).
+ * <p>
+ * The fonts are platform.dir/data/fonts.
+ * The Framework resources are platform.dir/data/res.
+ * build.prop is at platform.dir/build.prop.
+ * <p>
+ * 2. test_res.dir: This is the directory for the resources of the test. If not specified, this
+ * falls back to getClass().getProtectionDomain().getCodeSource().getLocation()
+ * <p>
+ * The app resources are at: test_res.dir/testApp/MyApplication/app/src/main/res
+ */
+public class RenderTestBase {
+
+ private static final String PLATFORM_DIR_PROPERTY = "platform.dir";
+ private static final String RESOURCE_DIR_PROPERTY = "test_res.dir";
+
+ private static final String PLATFORM_DIR;
+ private static final String TEST_RES_DIR;
+ /** Location of the app to test inside {@link #TEST_RES_DIR} */
+ private static final String APP_TEST_DIR = "testApp/MyApplication";
+ /** Location of the app's res dir inside {@link #TEST_RES_DIR} */
+ private static final String APP_TEST_RES = APP_TEST_DIR + "/src/main/res";
+ private static final String APP_CLASSES_LOCATION =
+ APP_TEST_DIR + "/build/intermediates/classes/debug/";
+ protected static Bridge sBridge;
+ /** List of log messages generated by a render call. It can be used to find specific errors */
+ protected static ArrayList<String> sRenderMessages = Lists.newArrayList();
+ private static LayoutLog sLayoutLibLog;
+ private static FrameworkResources sFrameworkRepo;
+ private static ResourceRepository sProjectResources;
+ private static ILogger sLogger;
+
+ static {
+ // Test that System Properties are properly set.
+ PLATFORM_DIR = getPlatformDir();
+ if (PLATFORM_DIR == null) {
+ fail(String.format("System Property %1$s not properly set. The value is %2$s",
+ PLATFORM_DIR_PROPERTY, System.getProperty(PLATFORM_DIR_PROPERTY)));
+ }
+
+ TEST_RES_DIR = getTestResDir();
+ if (TEST_RES_DIR == null) {
+ fail(String.format("System property %1$s.dir not properly set. The value is %2$s",
+ RESOURCE_DIR_PROPERTY, System.getProperty(RESOURCE_DIR_PROPERTY)));
+ }
+ }
+
+ @Rule
+ public TestWatcher sRenderMessageWatcher = new TestWatcher() {
+ @Override
+ protected void succeeded(Description description) {
+ // We only check error messages if the rest of the test case was successful.
+ if (!sRenderMessages.isEmpty()) {
+ fail(description.getMethodName() + " render error message: " +
+ sRenderMessages.get(0));
+ }
+ }
+ };
+ // Default class loader with access to the app classes
+ protected ClassLoader mDefaultClassLoader =
+ new ModuleClassLoader(APP_CLASSES_LOCATION, getClass().getClassLoader());
+
+ private static String getPlatformDir() {
+ String platformDir = System.getProperty(PLATFORM_DIR_PROPERTY);
+ if (platformDir != null && !platformDir.isEmpty() && new File(platformDir).isDirectory()) {
+ return platformDir;
+ }
+ // System Property not set. Try to find the directory in the build directory.
+ String androidHostOut = System.getenv("ANDROID_HOST_OUT");
+ if (androidHostOut != null) {
+ platformDir = getPlatformDirFromHostOut(new File(androidHostOut));
+ if (platformDir != null) {
+ return platformDir;
+ }
+ }
+ String workingDirString = System.getProperty("user.dir");
+ File workingDir = new File(workingDirString);
+ // Test if workingDir is android checkout root.
+ platformDir = getPlatformDirFromRoot(workingDir);
+ if (platformDir != null) {
+ return platformDir;
+ }
+
+ // Test if workingDir is platform/frameworks/base/tools/layoutlib/bridge.
+ File currentDir = workingDir;
+ if (currentDir.getName().equalsIgnoreCase("bridge")) {
+ currentDir = currentDir.getParentFile();
+ }
+ // Test if currentDir is platform/frameworks/base/tools/layoutlib. That is, root should be
+ // workingDir/../../../../ (4 levels up)
+ for (int i = 0; i < 4; i++) {
+ if (currentDir != null) {
+ currentDir = currentDir.getParentFile();
+ }
+ }
+ return currentDir == null ? null : getPlatformDirFromRoot(currentDir);
+ }
+
+ private static String getPlatformDirFromRoot(File root) {
+ if (!root.isDirectory()) {
+ return null;
+ }
+ File out = new File(root, "out");
+ if (!out.isDirectory()) {
+ return null;
+ }
+ File host = new File(out, "host");
+ if (!host.isDirectory()) {
+ return null;
+ }
+ File[] hosts = host.listFiles(path -> path.isDirectory() &&
+ (path.getName().startsWith("linux-") || path.getName().startsWith("darwin-")));
+ assert hosts != null;
+ for (File hostOut : hosts) {
+ String platformDir = getPlatformDirFromHostOut(hostOut);
+ if (platformDir != null) {
+ return platformDir;
+ }
+ }
+ return null;
+ }
+
+ private static String getPlatformDirFromHostOut(File out) {
+ if (!out.isDirectory()) {
+ return null;
+ }
+ File sdkDir = new File(out, "sdk");
+ if (!sdkDir.isDirectory()) {
+ return null;
+ }
+ File[] sdkDirs = sdkDir.listFiles(path -> {
+ // We need to search for $TARGET_PRODUCT (usually, sdk_phone_armv7)
+ return path.isDirectory() && path.getName().startsWith("sdk");
+ });
+ assert sdkDirs != null;
+ for (File dir : sdkDirs) {
+ String platformDir = getPlatformDirFromHostOutSdkSdk(dir);
+ if (platformDir != null) {
+ return platformDir;
+ }
+ }
+ return null;
+ }
+
+ private static String getPlatformDirFromHostOutSdkSdk(File sdkDir) {
+ File[] possibleSdks = sdkDir.listFiles(
+ path -> path.isDirectory() && path.getName().contains("android-sdk"));
+ assert possibleSdks != null;
+ for (File possibleSdk : possibleSdks) {
+ File platformsDir = new File(possibleSdk, "platforms");
+ File[] platforms = platformsDir.listFiles(
+ path -> path.isDirectory() && path.getName().startsWith("android-"));
+ if (platforms == null || platforms.length == 0) {
+ continue;
+ }
+ Arrays.sort(platforms, (o1, o2) -> {
+ final int MAX_VALUE = 1000;
+ String suffix1 = o1.getName().substring("android-".length());
+ String suffix2 = o2.getName().substring("android-".length());
+ int suff1, suff2;
+ try {
+ suff1 = Integer.parseInt(suffix1);
+ } catch (NumberFormatException e) {
+ suff1 = MAX_VALUE;
+ }
+ try {
+ suff2 = Integer.parseInt(suffix2);
+ } catch (NumberFormatException e) {
+ suff2 = MAX_VALUE;
+ }
+ if (suff1 != MAX_VALUE || suff2 != MAX_VALUE) {
+ return suff2 - suff1;
+ }
+ return suffix2.compareTo(suffix1);
+ });
+ return platforms[0].getAbsolutePath();
+ }
+ return null;
+ }
+
+ private static String getTestResDir() {
+ String resourceDir = System.getProperty(RESOURCE_DIR_PROPERTY);
+ if (resourceDir != null && !resourceDir.isEmpty() && new File(resourceDir).isDirectory()) {
+ return resourceDir;
+ }
+ // TEST_RES_DIR not explicitly set. Fallback to the class's source location.
+ try {
+ URL location = RenderTestBase.class.getProtectionDomain().getCodeSource().getLocation();
+ return new File(location.getPath()).exists() ? location.getPath() : null;
+ } catch (NullPointerException e) {
+ // Prevent a lot of null checks by just catching the exception.
+ return null;
+ }
+ }
+
+ /**
+ * Initialize the bridge and the resource maps.
+ */
+ @BeforeClass
+ public static void beforeClass() {
+ File data_dir = new File(PLATFORM_DIR, "data");
+ File res = new File(data_dir, "res");
+ sFrameworkRepo = new FrameworkResources(new FolderWrapper(res));
+ sFrameworkRepo.loadResources();
+ sFrameworkRepo.loadPublicResources(getLogger());
+
+ sProjectResources =
+ new ResourceRepository(new FolderWrapper(TEST_RES_DIR + "/" + APP_TEST_RES),
+ false) {
+ @NonNull
+ @Override
+ protected ResourceItem createResourceItem(@NonNull String name) {
+ return new ResourceItem(name);
+ }
+ };
+ sProjectResources.loadResources();
+
+ File fontLocation = new File(data_dir, "fonts");
+ File buildProp = new File(PLATFORM_DIR, "build.prop");
+ File attrs = new File(res, "values" + File.separator + "attrs.xml");
+ sBridge = new Bridge();
+ sBridge.init(ConfigGenerator.loadProperties(buildProp), fontLocation,
+ ConfigGenerator.getEnumMap(attrs), getLayoutLog());
+ Bridge.getLock().lock();
+ try {
+ Bridge.setLog(getLayoutLog());
+ } finally {
+ Bridge.getLock().unlock();
+ }
+ }
+
+ @AfterClass
+ public static void tearDown() {
+ sLayoutLibLog = null;
+ sFrameworkRepo = null;
+ sProjectResources = null;
+ sLogger = null;
+ sBridge = null;
+
+ TestUtils.gc();
+
+ System.out.println("Objects still linked from the DelegateManager:");
+ DelegateManager.dump(System.out);
+ }
+
+ @NonNull
+ protected static RenderResult render(SessionParams params, long frameTimeNanos) {
+ // TODO: Set up action bar handler properly to test menu rendering.
+ // Create session params.
+ System_Delegate.setBootTimeNanos(TimeUnit.MILLISECONDS.toNanos(871732800000L));
+ System_Delegate.setNanosTime(TimeUnit.MILLISECONDS.toNanos(871732800000L));
+ RenderSession session = sBridge.createSession(params);
+
+ try {
+
+ if (frameTimeNanos != -1) {
+ session.setElapsedFrameTimeNanos(frameTimeNanos);
+ }
+
+ if (!session.getResult().isSuccess()) {
+ getLogger().error(session.getResult().getException(),
+ session.getResult().getErrorMessage());
+ }
+ // Render the session with a timeout of 50s.
+ Result renderResult = session.render(50000);
+ if (!renderResult.isSuccess()) {
+ getLogger().error(session.getResult().getException(),
+ session.getResult().getErrorMessage());
+ }
+
+ return RenderResult.getFromSession(session);
+ } finally {
+ session.dispose();
+ }
+ }
+
+ /**
+ * Create a new rendering session and test that rendering the given layout doesn't throw any
+ * exceptions and matches the provided image.
+ * <p>
+ * If frameTimeNanos is >= 0 a frame will be executed during the rendering. The time indicates
+ * how far in the future is.
+ */
+ @Nullable
+ protected static RenderResult renderAndVerify(SessionParams params, String goldenFileName,
+ long frameTimeNanos) throws ClassNotFoundException {
+ RenderResult result = RenderTestBase.render(params, frameTimeNanos);
+ try {
+ String goldenImagePath = APP_TEST_DIR + "/golden/" + goldenFileName;
+ assertNotNull(result.getImage());
+ ImageUtils.requireSimilar(goldenImagePath, result.getImage());
+ } catch (IOException e) {
+ getLogger().error(e, e.getMessage());
+ }
+
+ return result;
+ }
+
+ /**
+ * Create a new rendering session and test that rendering the given layout doesn't throw any
+ * exceptions and matches the provided image.
+ */
+ @Nullable
+ protected static RenderResult renderAndVerify(SessionParams params, String goldenFileName)
+ throws ClassNotFoundException {
+ return RenderTestBase.renderAndVerify(params, goldenFileName, -1);
+ }
+
+ private static LayoutLog getLayoutLog() {
+ if (sLayoutLibLog == null) {
+ sLayoutLibLog = new LayoutLog() {
+ @Override
+ public void warning(String tag, String message, Object data) {
+ System.out.println("Warning " + tag + ": " + message);
+ failWithMsg(message);
+ }
+
+ @Override
+ 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 == null ? "" : message);
+ }
+
+ @Override
+ public void error(String tag, String message, Object data) {
+ System.out.println("Error " + tag + ": " + message);
+ failWithMsg(message);
+ }
+
+ @Override
+ public void error(String tag, String message, Throwable throwable, Object data) {
+ System.out.println("Error " + tag + ": " + message);
+ if (throwable != null) {
+ throwable.printStackTrace();
+ }
+ failWithMsg(message);
+ }
+ };
+ }
+ return sLayoutLibLog;
+ }
+
+ protected static void ignoreAllLogging() {
+ sLayoutLibLog = new LayoutLog();
+ sLogger = new ILogger() {
+ @Override
+ public void error(Throwable t, String msgFormat, Object... args) {
+ }
+
+ @Override
+ public void warning(String msgFormat, Object... args) {
+ }
+
+ @Override
+ public void info(String msgFormat, Object... args) {
+ }
+
+ @Override
+ public void verbose(String msgFormat, Object... args) {
+ }
+ };
+ }
+
+ protected static ILogger getLogger() {
+ if (sLogger == null) {
+ sLogger = new ILogger() {
+ @Override
+ public void error(Throwable t, @Nullable String msgFormat, Object... args) {
+ if (t != null) {
+ t.printStackTrace();
+ }
+ failWithMsg(msgFormat == null ? "" : msgFormat, args);
+ }
+
+ @Override
+ public void warning(@NonNull String msgFormat, Object... args) {
+ failWithMsg(msgFormat, args);
+ }
+
+ @Override
+ public void info(@NonNull String msgFormat, Object... args) {
+ // pass.
+ }
+
+ @Override
+ public void verbose(@NonNull String msgFormat, Object... args) {
+ // pass.
+ }
+ };
+ }
+ return sLogger;
+ }
+
+ private static void failWithMsg(@NonNull String msgFormat, Object... args) {
+ sRenderMessages.add(args == null ? msgFormat : String.format(msgFormat, args));
+ }
+
+ @Before
+ public void beforeTestCase() {
+ sRenderMessages.clear();
+ }
+
+ @NonNull
+ protected LayoutPullParser createLayoutPullParser(String layoutPath) {
+ return new LayoutPullParser(APP_TEST_RES + "/layout/" + layoutPath);
+ }
+
+ /**
+ * Create a new rendering session and test that rendering the given layout on nexus 5
+ * doesn't throw any exceptions and matches the provided image.
+ */
+ @Nullable
+ protected RenderResult renderAndVerify(String layoutFileName, String goldenFileName)
+ throws ClassNotFoundException {
+ return renderAndVerify(layoutFileName, goldenFileName, ConfigGenerator.NEXUS_5);
+ }
+
+ /**
+ * Create a new rendering session and test that rendering the given layout on given device
+ * doesn't throw any exceptions and matches the provided image.
+ */
+ @Nullable
+ protected RenderResult renderAndVerify(String layoutFileName, String goldenFileName,
+ ConfigGenerator deviceConfig) throws ClassNotFoundException {
+ SessionParams params = createSessionParams(layoutFileName, deviceConfig);
+ return renderAndVerify(params, goldenFileName);
+ }
+
+ protected SessionParams createSessionParams(String layoutFileName, ConfigGenerator deviceConfig)
+ throws ClassNotFoundException {
+ // Create the layout pull parser.
+ LayoutPullParser parser = createLayoutPullParser(layoutFileName);
+ // Create LayoutLibCallback.
+ LayoutLibTestCallback layoutLibCallback =
+ new LayoutLibTestCallback(getLogger(), mDefaultClassLoader);
+ layoutLibCallback.initResources();
+ // TODO: Set up action bar handler properly to test menu rendering.
+ // Create session params.
+ return getSessionParams(parser, deviceConfig, layoutLibCallback, "AppTheme", true,
+ RenderingMode.NORMAL, 22);
+ }
+
+ /**
+ * Uses Theme.Material and Target sdk version as 22.
+ */
+ protected SessionParams getSessionParams(LayoutPullParser layoutParser,
+ ConfigGenerator configGenerator, LayoutLibTestCallback layoutLibCallback,
+ String themeName, boolean isProjectTheme, RenderingMode renderingMode,
+ @SuppressWarnings("SameParameterValue") int targetSdk) {
+ FolderConfiguration config = configGenerator.getFolderConfig();
+ ResourceResolver resourceResolver =
+ ResourceResolver.create(sProjectResources.getConfiguredResources(config),
+ sFrameworkRepo.getConfiguredResources(config), themeName, isProjectTheme);
+
+ SessionParams sessionParams =
+ new SessionParams(layoutParser, renderingMode, null /*used for caching*/,
+ configGenerator.getHardwareConfig(), resourceResolver, layoutLibCallback, 0,
+ targetSdk, getLayoutLog());
+ sessionParams.setFlag(RenderParamsFlags.FLAG_DO_NOT_RENDER_ON_CREATE, true);
+ return sessionParams;
+ }
+}
diff --git a/bridge/tests/src/com/android/layoutlib/bridge/intensive/RenderTests.java b/bridge/tests/src/com/android/layoutlib/bridge/intensive/RenderTests.java
new file mode 100644
index 0000000..73e51ec
--- /dev/null
+++ b/bridge/tests/src/com/android/layoutlib/bridge/intensive/RenderTests.java
@@ -0,0 +1,387 @@
+/*
+ * 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 com.android.layoutlib.bridge.intensive;
+
+import com.android.ide.common.rendering.api.RenderSession;
+import com.android.ide.common.rendering.api.SessionParams;
+import com.android.ide.common.rendering.api.SessionParams.RenderingMode;
+import com.android.ide.common.rendering.api.ViewInfo;
+import com.android.layoutlib.bridge.android.BridgeContext;
+import com.android.layoutlib.bridge.android.RenderParamsFlags;
+import com.android.layoutlib.bridge.impl.RenderAction;
+import com.android.layoutlib.bridge.intensive.setup.ConfigGenerator;
+import com.android.layoutlib.bridge.intensive.setup.LayoutLibTestCallback;
+import com.android.layoutlib.bridge.intensive.setup.LayoutPullParser;
+import com.android.resources.Density;
+import com.android.resources.Navigation;
+import com.android.resources.ResourceType;
+
+import org.junit.Test;
+
+import android.content.res.AssetManager;
+import android.content.res.Configuration;
+import android.content.res.Resources;
+import android.util.DisplayMetrics;
+
+import java.lang.reflect.Field;
+import java.util.concurrent.TimeUnit;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * Set of render tests
+ */
+public class RenderTests extends RenderTestBase {
+
+ @Test
+ public void testActivity() throws ClassNotFoundException {
+ renderAndVerify("activity.xml", "activity.png");
+ }
+
+ @Test
+ public void testActivityOnOldTheme() throws ClassNotFoundException {
+ LayoutLibTestCallback layoutLibCallback =
+ new LayoutLibTestCallback(getLogger(), mDefaultClassLoader);
+ layoutLibCallback.initResources();
+
+ LayoutPullParser parser = createLayoutPullParser("simple_activity.xml");
+ SessionParams params = getSessionParams(parser, ConfigGenerator.NEXUS_5,
+ layoutLibCallback, "Theme.NoTitleBar", false,
+ RenderingMode.NORMAL, 22);
+
+ renderAndVerify(params, "simple_activity-old-theme.png");
+ }
+
+ @Test
+ public void testTranslucentBars() throws ClassNotFoundException {
+ LayoutLibTestCallback layoutLibCallback =
+ new LayoutLibTestCallback(getLogger(), mDefaultClassLoader);
+ layoutLibCallback.initResources();
+
+ LayoutPullParser parser = createLayoutPullParser("four_corners.xml");
+ SessionParams params = getSessionParams(parser, ConfigGenerator.NEXUS_5,
+ layoutLibCallback, "Theme.Material.Light.NoActionBar.TranslucentDecor", false,
+ RenderingMode.NORMAL, 22);
+ renderAndVerify(params, "four_corners_translucent.png");
+
+ parser = createLayoutPullParser("four_corners.xml");
+ params = getSessionParams(parser, ConfigGenerator.NEXUS_5_LAND,
+ layoutLibCallback, "Theme.Material.Light.NoActionBar.TranslucentDecor", false,
+ RenderingMode.NORMAL, 22);
+ renderAndVerify(params, "four_corners_translucent_land.png");
+
+ parser = createLayoutPullParser("four_corners.xml");
+ params = getSessionParams(parser, ConfigGenerator.NEXUS_5,
+ layoutLibCallback, "Theme.Material.Light.NoActionBar", false,
+ RenderingMode.NORMAL, 22);
+ renderAndVerify(params, "four_corners.png");
+ }
+
+ @Test
+ public void testAllWidgets() throws ClassNotFoundException {
+ renderAndVerify("allwidgets.xml", "allwidgets.png");
+
+ // We expect fidelity warnings for Path.isConvex. Fail for anything else.
+ sRenderMessages.removeIf(message -> message.equals("Path.isConvex is not supported."));
+ }
+
+ @Test
+ public void testArrayCheck() throws ClassNotFoundException {
+ renderAndVerify("array_check.xml", "array_check.png");
+ }
+
+ @Test
+ public void testAllWidgetsTablet() throws ClassNotFoundException {
+ renderAndVerify("allwidgets.xml", "allwidgets_tab.png", ConfigGenerator.NEXUS_7_2012);
+
+ // We expect fidelity warnings for Path.isConvex. Fail for anything else.
+ sRenderMessages.removeIf(message -> message.equals("Path.isConvex is not supported."));
+ }
+
+ @Test
+ public void testActivityActionBar() throws ClassNotFoundException {
+ LayoutPullParser parser = createLayoutPullParser("simple_activity.xml");
+ LayoutLibTestCallback layoutLibCallback =
+ new LayoutLibTestCallback(getLogger(), mDefaultClassLoader);
+ layoutLibCallback.initResources();
+
+ SessionParams params = getSessionParams(parser, ConfigGenerator.NEXUS_5,
+ layoutLibCallback, "Theme.Material.Light.NoActionBar", false,
+ RenderingMode.V_SCROLL, 22);
+
+ renderAndVerify(params, "simple_activity_noactionbar.png");
+
+ parser = createLayoutPullParser("simple_activity.xml");
+ params = getSessionParams(parser, ConfigGenerator.NEXUS_5,
+ layoutLibCallback, "Theme.Material.Light", false,
+ RenderingMode.V_SCROLL, 22);
+
+ renderAndVerify(params, "simple_activity.png");
+
+ // This also tests that a theme with "NoActionBar" DOES HAVE an action bar when we are
+ // displaying menus.
+ parser = createLayoutPullParser("simple_activity.xml");
+ params = getSessionParams(parser, ConfigGenerator.NEXUS_5,
+ layoutLibCallback, "Theme.Material.Light.NoActionBar", false,
+ RenderingMode.V_SCROLL, 22);
+ params.setFlag(RenderParamsFlags.FLAG_KEY_ROOT_TAG, "menu");
+ renderAndVerify(params, "simple_activity.png");
+ }
+
+ @Test
+ public void testOnApplyInsetsCall()
+ throws ClassNotFoundException, NoSuchFieldException, IllegalAccessException {
+ // We get the widget via reflection to avoid IntelliJ complaining about the class being
+ // located in the wrong package. (From the Bridge tests point of view, it is)
+ Class insetsWidgetClass = Class.forName("com.android.layoutlib.test.myapplication.widgets" +
+ ".InsetsWidget");
+ Field field = insetsWidgetClass.getDeclaredField("sApplyInsetsCalled");
+ assertFalse((Boolean)field.get(null));
+
+ LayoutPullParser parser = createLayoutPullParser("insets.xml");
+ LayoutLibTestCallback layoutLibCallback =
+ new LayoutLibTestCallback(getLogger(), mDefaultClassLoader);
+ layoutLibCallback.initResources();
+ SessionParams params = getSessionParams(parser, ConfigGenerator.NEXUS_5,
+ layoutLibCallback, "Theme.Material.Light.NoActionBar", false,
+ RenderingMode.NORMAL, 22);
+
+ render(params, -1);
+
+ assertTrue((Boolean)field.get(null));
+ field.set(null, false);
+ }
+
+ /** Test expand_layout.xml */
+ @Test
+ public void testExpand() throws ClassNotFoundException {
+ // Create the layout pull parser.
+ LayoutPullParser parser = createLayoutPullParser("expand_vert_layout.xml");
+ // Create LayoutLibCallback.
+ LayoutLibTestCallback layoutLibCallback =
+ new LayoutLibTestCallback(getLogger(), mDefaultClassLoader);
+ layoutLibCallback.initResources();
+
+ ConfigGenerator customConfigGenerator = new ConfigGenerator()
+ .setScreenWidth(300)
+ .setScreenHeight(20)
+ .setDensity(Density.XHIGH)
+ .setNavigation(Navigation.NONAV);
+
+ SessionParams params = getSessionParams(parser, customConfigGenerator,
+ layoutLibCallback, "Theme.Material.Light.NoActionBar.Fullscreen", false,
+ RenderingMode.V_SCROLL, 22);
+
+ renderAndVerify(params, "expand_vert_layout.png");
+
+ customConfigGenerator = new ConfigGenerator()
+ .setScreenWidth(20)
+ .setScreenHeight(300)
+ .setDensity(Density.XHIGH)
+ .setNavigation(Navigation.NONAV);
+ parser = createLayoutPullParser("expand_horz_layout.xml");
+ params = getSessionParams(parser, customConfigGenerator,
+ layoutLibCallback, "Theme.Material.Light.NoActionBar.Fullscreen", false,
+ RenderingMode.H_SCROLL, 22);
+
+ renderAndVerify(params, "expand_horz_layout.png");
+ }
+
+ /** Test indeterminate_progressbar.xml */
+ @Test
+ public void testVectorAnimation() throws ClassNotFoundException {
+ // Create the layout pull parser.
+ LayoutPullParser parser = createLayoutPullParser("indeterminate_progressbar.xml");
+ // Create LayoutLibCallback.
+ LayoutLibTestCallback layoutLibCallback =
+ new LayoutLibTestCallback(getLogger(), mDefaultClassLoader);
+ layoutLibCallback.initResources();
+
+ SessionParams params = getSessionParams(parser, ConfigGenerator.NEXUS_5,
+ layoutLibCallback, "Theme.Material.NoActionBar.Fullscreen", false,
+ RenderingMode.V_SCROLL, 22);
+
+ renderAndVerify(params, "animated_vector.png", TimeUnit.SECONDS.toNanos(2));
+
+ parser = createLayoutPullParser("indeterminate_progressbar.xml");
+ params = getSessionParams(parser, ConfigGenerator.NEXUS_5,
+ layoutLibCallback, "Theme.Material.NoActionBar.Fullscreen", false,
+ RenderingMode.V_SCROLL, 22);
+ renderAndVerify(params, "animated_vector_1.png", TimeUnit.SECONDS.toNanos(3));
+ }
+
+ /**
+ * Test a vector drawable that uses trimStart and trimEnd. It also tests all the primitives
+ * for vector drawables (lines, moves and cubic and quadratic curves).
+ */
+ @Test
+ public void testVectorDrawable() throws ClassNotFoundException {
+ // Create the layout pull parser.
+ LayoutPullParser parser = createLayoutPullParser("vector_drawable.xml");
+ // Create LayoutLibCallback.
+ LayoutLibTestCallback layoutLibCallback =
+ new LayoutLibTestCallback(getLogger(), mDefaultClassLoader);
+ layoutLibCallback.initResources();
+
+ SessionParams params = getSessionParams(parser, ConfigGenerator.NEXUS_5,
+ layoutLibCallback, "Theme.Material.NoActionBar.Fullscreen", false,
+ RenderingMode.V_SCROLL, 22);
+
+ renderAndVerify(params, "vector_drawable.png", TimeUnit.SECONDS.toNanos(2));
+ }
+
+ /**
+ * Regression test for http://b.android.com/91383 and http://b.android.com/203797
+ */
+ @Test
+ public void testVectorDrawable91383() throws ClassNotFoundException {
+ // Create the layout pull parser.
+ LayoutPullParser parser = createLayoutPullParser("vector_drawable_android.xml");
+ // Create LayoutLibCallback.
+ LayoutLibTestCallback layoutLibCallback =
+ new LayoutLibTestCallback(getLogger(), mDefaultClassLoader);
+ layoutLibCallback.initResources();
+
+ SessionParams params = getSessionParams(parser, ConfigGenerator.NEXUS_5,
+ layoutLibCallback, "Theme.Material.NoActionBar.Fullscreen", false,
+ RenderingMode.V_SCROLL, 22);
+
+ renderAndVerify(params, "vector_drawable_91383.png", TimeUnit.SECONDS.toNanos(2));
+ }
+
+ /** Test activity.xml */
+ @Test
+ public void testScrollingAndMeasure() throws ClassNotFoundException {
+ // Create the layout pull parser.
+ LayoutPullParser parser = createLayoutPullParser("scrolled.xml");
+ // Create LayoutLibCallback.
+ LayoutLibTestCallback layoutLibCallback =
+ new LayoutLibTestCallback(getLogger(), mDefaultClassLoader);
+ layoutLibCallback.initResources();
+
+ SessionParams params = getSessionParams(parser, ConfigGenerator.NEXUS_5,
+ layoutLibCallback, "Theme.Material.NoActionBar.Fullscreen", false,
+ RenderingMode.V_SCROLL, 22);
+ params.setForceNoDecor();
+ params.setExtendedViewInfoMode(true);
+
+ // Do an only-measure pass
+ RenderSession session = sBridge.createSession(params);
+ session.measure();
+ RenderResult result = RenderResult.getFromSession(session);
+ assertNotNull(result);
+ assertNotNull(result.getResult());
+ assertTrue(result.getResult().isSuccess());
+
+ ViewInfo rootLayout = result.getRootViews().get(0);
+ // Check the first box in the main LinearLayout
+ assertEquals(-90, rootLayout.getChildren().get(0).getTop());
+ assertEquals(-30, rootLayout.getChildren().get(0).getLeft());
+ assertEquals(90, rootLayout.getChildren().get(0).getBottom());
+ assertEquals(150, rootLayout.getChildren().get(0).getRight());
+
+ // Check the first box within the nested LinearLayout
+ assertEquals(-450, rootLayout.getChildren().get(5).getChildren().get(0).getTop());
+ assertEquals(90, rootLayout.getChildren().get(5).getChildren().get(0).getLeft());
+ assertEquals(-270, rootLayout.getChildren().get(5).getChildren().get(0).getBottom());
+ assertEquals(690, rootLayout.getChildren().get(5).getChildren().get(0).getRight());
+
+ // Do a full render pass
+ parser = createLayoutPullParser("scrolled.xml");
+
+ params = getSessionParams(parser, ConfigGenerator.NEXUS_5,
+ layoutLibCallback, "Theme.Material.NoActionBar.Fullscreen", false,
+ RenderingMode.V_SCROLL, 22);
+ params.setForceNoDecor();
+ params.setExtendedViewInfoMode(true);
+
+ result = renderAndVerify(params, "scrolled.png");
+ assertNotNull(result);
+ assertNotNull(result.getResult());
+ assertTrue(result.getResult().isSuccess());
+ }
+
+ @Test
+ public void testGetResourceNameVariants() throws Exception {
+ // Setup
+ // Create the layout pull parser for our resources (empty.xml can not be part of the test
+ // app as it won't compile).
+ LayoutPullParser parser = new LayoutPullParser("/empty.xml");
+ // Create LayoutLibCallback.
+ LayoutLibTestCallback layoutLibCallback =
+ new LayoutLibTestCallback(getLogger(), mDefaultClassLoader);
+ layoutLibCallback.initResources();
+ SessionParams params = getSessionParams(parser, ConfigGenerator.NEXUS_4,
+ layoutLibCallback, "AppTheme", true, RenderingMode.NORMAL, 22);
+ AssetManager assetManager = AssetManager.getSystem();
+ DisplayMetrics metrics = new DisplayMetrics();
+ Configuration configuration = RenderAction.getConfiguration(params);
+ Resources resources = new Resources(assetManager, metrics, configuration);
+ resources.mLayoutlibCallback = params.getLayoutlibCallback();
+ resources.mContext =
+ new BridgeContext(params.getProjectKey(), metrics, params.getResources(),
+ params.getAssets(), params.getLayoutlibCallback(), configuration,
+ params.getTargetSdkVersion(), params.isRtlSupported());
+ // Test
+ assertEquals("android:style/ButtonBar",
+ resources.getResourceName(android.R.style.ButtonBar));
+ assertEquals("android", resources.getResourcePackageName(android.R.style.ButtonBar));
+ assertEquals("ButtonBar", resources.getResourceEntryName(android.R.style.ButtonBar));
+ assertEquals("style", resources.getResourceTypeName(android.R.style.ButtonBar));
+ int id = resources.mLayoutlibCallback.getResourceId(ResourceType.STRING, "app_name");
+ assertEquals("com.android.layoutlib.test.myapplication:string/app_name",
+ resources.getResourceName(id));
+ assertEquals("com.android.layoutlib.test.myapplication",
+ resources.getResourcePackageName(id));
+ assertEquals("string", resources.getResourceTypeName(id));
+ assertEquals("app_name", resources.getResourceEntryName(id));
+ }
+
+ @Test
+ public void testStringEscaping() throws Exception {
+ // Setup
+ // Create the layout pull parser for our resources (empty.xml can not be part of the test
+ // app as it won't compile).
+ LayoutPullParser parser = new LayoutPullParser("/empty.xml");
+ // Create LayoutLibCallback.
+ LayoutLibTestCallback layoutLibCallback =
+ new LayoutLibTestCallback(RenderTestBase.getLogger(), mDefaultClassLoader);
+ layoutLibCallback.initResources();
+ SessionParams params = getSessionParams(parser, ConfigGenerator.NEXUS_4,
+ layoutLibCallback, "AppTheme", true, RenderingMode.NORMAL, 22);
+ AssetManager assetManager = AssetManager.getSystem();
+ DisplayMetrics metrics = new DisplayMetrics();
+ Configuration configuration = RenderAction.getConfiguration(params);
+ Resources resources = new Resources(assetManager, metrics, configuration);
+ resources.mLayoutlibCallback = params.getLayoutlibCallback();
+ resources.mContext =
+ new BridgeContext(params.getProjectKey(), metrics, params.getResources(),
+ params.getAssets(), params.getLayoutlibCallback(), configuration,
+ params.getTargetSdkVersion(), params.isRtlSupported());
+
+ int id = resources.mLayoutlibCallback.getResourceId(ResourceType.ARRAY, "string_array");
+ String[] strings = resources.getStringArray(id);
+ assertArrayEquals(
+ new String[]{"mystring", "Hello world!", "candidates", "Unknown", "?EC"},
+ strings);
+ assertTrue(sRenderMessages.isEmpty());
+ }
+}
diff --git a/bridge/tests/src/com/android/layoutlib/bridge/intensive/setup/LayoutLibTestCallback.java b/bridge/tests/src/com/android/layoutlib/bridge/intensive/setup/LayoutLibTestCallback.java
index 96ae523..bae2dc0 100644
--- a/bridge/tests/src/com/android/layoutlib/bridge/intensive/setup/LayoutLibTestCallback.java
+++ b/bridge/tests/src/com/android/layoutlib/bridge/intensive/setup/LayoutLibTestCallback.java
@@ -42,6 +42,9 @@
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.net.URLClassLoader;
import java.util.Map;
import com.google.android.collect.Maps;
@@ -59,10 +62,11 @@
private final Map<ResourceType, Map<String, Integer>> mResources = Maps.newHashMap();
private final ILogger mLog;
private final ActionBarCallback mActionBarCallback = new ActionBarCallback();
- private final ClassLoader mModuleClassLoader = new ModuleClassLoader(PROJECT_CLASSES_LOCATION);
+ private final ClassLoader mModuleClassLoader;
- public LayoutLibTestCallback(ILogger logger) {
+ public LayoutLibTestCallback(ILogger logger, ClassLoader classLoader) {
mLog = logger;
+ mModuleClassLoader = classLoader;
}
public void initResources() throws ClassNotFoundException {
diff --git a/bridge/tests/src/com/android/layoutlib/bridge/intensive/setup/LayoutPullParser.java b/bridge/tests/src/com/android/layoutlib/bridge/intensive/setup/LayoutPullParser.java
index 1110494..bc8083f 100644
--- a/bridge/tests/src/com/android/layoutlib/bridge/intensive/setup/LayoutPullParser.java
+++ b/bridge/tests/src/com/android/layoutlib/bridge/intensive/setup/LayoutPullParser.java
@@ -42,9 +42,11 @@
* @param layoutPath Must start with '/' and be relative to test resources.
*/
public LayoutPullParser(String layoutPath) {
- assert layoutPath.startsWith("/");
+ if (layoutPath.startsWith("/")) {
+ layoutPath = layoutPath.substring(1);
+ }
try {
- init(getClass().getResourceAsStream(layoutPath));
+ init(getClass().getClassLoader().getResourceAsStream(layoutPath));
} catch (XmlPullParserException e) {
throw new IOError(e);
}
diff --git a/bridge/tests/src/com/android/layoutlib/bridge/intensive/setup/ModuleClassLoader.java b/bridge/tests/src/com/android/layoutlib/bridge/intensive/setup/ModuleClassLoader.java
deleted file mode 100644
index 110f4c8..0000000
--- a/bridge/tests/src/com/android/layoutlib/bridge/intensive/setup/ModuleClassLoader.java
+++ /dev/null
@@ -1,67 +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.layoutlib.bridge.intensive.setup;
-
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.util.Map;
-
-import com.google.android.collect.Maps;
-
-/**
- * The ClassLoader to load the project's classes.
- */
-public class ModuleClassLoader extends ClassLoader {
-
- private final Map<String, Class<?>> mClasses = Maps.newHashMap();
- private final String mClassLocation;
-
- public ModuleClassLoader(String classLocation) {
- mClassLocation = classLocation;
- }
-
- @Override
- protected Class<?> findClass(String name) throws ClassNotFoundException {
- Class<?> aClass = mClasses.get(name);
- if (aClass != null) {
- return aClass;
- }
- String pathName = mClassLocation.concat(name.replace('.', '/')).concat(".class");
- InputStream classInputStream = getClass().getResourceAsStream(pathName);
- if (classInputStream == null) {
- throw new ClassNotFoundException("Unable to find class " + name + " at " + pathName);
- }
- byte[] data;
- try {
- ByteArrayOutputStream buffer = new ByteArrayOutputStream();
- int nRead;
- data = new byte[16384]; // 16k
- while ((nRead = classInputStream.read(data, 0, data.length)) != -1) {
- buffer.write(data, 0, nRead);
- }
- buffer.flush();
- data = buffer.toByteArray();
- } catch (IOException e) {
- // Wrap the exception with ClassNotFoundException so that caller can deal with it.
- throw new ClassNotFoundException("Unable to load class " + name, e);
- }
- aClass = defineClass(name, data, 0, data.length);
- mClasses.put(name, aClass);
- return aClass;
- }
-}
diff --git a/bridge/tests/src/com/android/layoutlib/bridge/intensive/ImageUtils.java b/bridge/tests/src/com/android/layoutlib/bridge/intensive/util/ImageUtils.java
similarity index 95%
rename from bridge/tests/src/com/android/layoutlib/bridge/intensive/ImageUtils.java
rename to bridge/tests/src/com/android/layoutlib/bridge/intensive/util/ImageUtils.java
index 8f9fa8a..18c6629 100644
--- a/bridge/tests/src/com/android/layoutlib/bridge/intensive/ImageUtils.java
+++ b/bridge/tests/src/com/android/layoutlib/bridge/intensive/util/ImageUtils.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2014 The Android Open Source Project
+ * Copyright (C) 2016 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.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.layoutlib.bridge.intensive;
+package com.android.layoutlib.bridge.intensive.util;
import android.annotation.NonNull;
@@ -64,7 +64,7 @@
double scale = THUMBNAIL_SIZE / (double)maxDimension;
BufferedImage thumbnail = scale(image, scale, scale);
- InputStream is = ImageUtils.class.getResourceAsStream(relativePath);
+ InputStream is = ImageUtils.class.getClassLoader().getResourceAsStream(relativePath);
if (is == null) {
String message = "Unable to load golden thumbnail: " + relativePath + "\n";
message = saveImageAndAppendMessage(thumbnail, message, relativePath);
@@ -179,7 +179,7 @@
g.drawString("Actual", 2 * imageWidth + 10, 20);
}
- File output = new File(getTempDir(), "delta-" + imageName);
+ File output = new File(getFailureDir(), "delta-" + imageName);
if (output.exists()) {
boolean deleted = output.delete();
assertTrue(deleted);
@@ -302,15 +302,16 @@
}
/**
- * Temp directory where to write the thumbnails and deltas.
+ * Directory where to write the thumbnails and deltas.
*/
@NonNull
- private static File getTempDir() {
- if (System.getProperty("os.name").equals("Mac OS X")) {
- return new File("/tmp"); //$NON-NLS-1$
- }
+ private static File getFailureDir() {
+ String workingDirString = System.getProperty("user.dir");
+ File failureDir = new File(workingDirString, "out/failures");
- return new File(System.getProperty("java.io.tmpdir")); //$NON-NLS-1$
+ //noinspection ResultOfMethodCallIgnored
+ failureDir.mkdirs();
+ return failureDir; //$NON-NLS-1$
}
/**
@@ -319,7 +320,7 @@
@NonNull
private static String saveImageAndAppendMessage(@NonNull BufferedImage image,
@NonNull String initialMessage, @NonNull String relativePath) throws IOException {
- File output = new File(getTempDir(), getName(relativePath));
+ File output = new File(getFailureDir(), getName(relativePath));
if (output.exists()) {
boolean deleted = output.delete();
assertTrue(deleted);
diff --git a/bridge/tests/src/com/android/layoutlib/bridge/intensive/util/ModuleClassLoader.java b/bridge/tests/src/com/android/layoutlib/bridge/intensive/util/ModuleClassLoader.java
new file mode 100644
index 0000000..da360f3
--- /dev/null
+++ b/bridge/tests/src/com/android/layoutlib/bridge/intensive/util/ModuleClassLoader.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2016 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.intensive.util;
+
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+
+import libcore.io.Streams;
+
+/**
+ * Module class loader that loads classes from the test project.
+ */
+public class ModuleClassLoader extends ClassLoader {
+ private final Map<String, Class<?>> mClasses = new HashMap<>();
+ private String myModuleRoot;
+
+ /**
+ * @param moduleRoot The path to the module root
+ * @param parent The parent class loader
+ */
+ public ModuleClassLoader(String moduleRoot, ClassLoader parent) {
+ super(parent);
+ myModuleRoot = moduleRoot + (moduleRoot.endsWith("/") ? "" : "/");
+ }
+
+ @Override
+ protected Class<?> findClass(String name) throws ClassNotFoundException {
+ try {
+ return super.findClass(name);
+ } catch (ClassNotFoundException ignored) {
+ }
+
+ Class<?> clazz = mClasses.get(name);
+ if (clazz == null) {
+ String path = name.replace('.', '/').concat(".class");
+ try {
+ byte[] b = Streams.readFully(getResourceAsStream(myModuleRoot + path));
+ clazz = defineClass(name, b, 0, b.length);
+ mClasses.put(name, clazz);
+ } catch (IOException ignore) {
+ throw new ClassNotFoundException(name + " not found");
+ }
+ }
+
+ return clazz;
+ }
+}
diff --git a/bridge/tests/src/com/android/layoutlib/bridge/intensive/util/TestUtils.java b/bridge/tests/src/com/android/layoutlib/bridge/intensive/util/TestUtils.java
new file mode 100644
index 0000000..1df8e79
--- /dev/null
+++ b/bridge/tests/src/com/android/layoutlib/bridge/intensive/util/TestUtils.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2016 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.intensive.util;
+
+import java.lang.ref.WeakReference;
+
+public class TestUtils {
+ public static void gc() {
+ // See RuntimeUtil#gc in jlibs (http://jlibs.in/)
+ Object obj = new Object();
+ WeakReference ref = new WeakReference<>(obj);
+ //noinspection UnusedAssignment
+ obj = null;
+ while (ref.get() != null) {
+ System.gc();
+ System.runFinalization();
+ }
+
+ System.gc();
+ System.runFinalization();
+ }
+}
diff --git a/bridge/tests/src/com/android/layoutlib/bridge/intensive/util/perf/LongStatsCollector.java b/bridge/tests/src/com/android/layoutlib/bridge/intensive/util/perf/LongStatsCollector.java
new file mode 100644
index 0000000..ee98b4b
--- /dev/null
+++ b/bridge/tests/src/com/android/layoutlib/bridge/intensive/util/perf/LongStatsCollector.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2016 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.intensive.util.perf;
+
+import android.annotation.NonNull;
+import android.util.LongArray;
+
+import java.util.Arrays;
+import java.util.function.LongConsumer;
+
+/**
+ * Class that collect a series of longs and produces the median, min and max values.
+ */
+public class LongStatsCollector implements LongConsumer {
+ private final LongArray mAllValues;
+ private long mMin = Long.MAX_VALUE;
+ private long mMax = Long.MIN_VALUE;
+ public LongStatsCollector(int estimatedRuns) {
+ mAllValues = new LongArray(estimatedRuns);
+ }
+
+ public int size() {
+ return mAllValues.size();
+ }
+
+ @NonNull
+ public Stats getStats() {
+ if (mAllValues.size() == 0) {
+ throw new IndexOutOfBoundsException("No data");
+ }
+
+ double median;
+ int size = mAllValues.size();
+ long[] buffer = new long[size];
+ for (int i = 0; i < size; i++) {
+ buffer[i] = mAllValues.get(i);
+ }
+
+ Arrays.sort(buffer);
+
+ int midPoint = size / 2;
+ median = (size % 2 == 0) ? (buffer[midPoint - 1] + buffer[midPoint]) / 2 : buffer[midPoint];
+
+ return new Stats(mAllValues.size(), mMin, mMax, median);
+ }
+
+ @Override
+ public void accept(long value) {
+ mMin = Math.min(mMin, value);
+ mMax = Math.max(mMax, value);
+ mAllValues.add(value);
+ }
+
+ public static class Stats {
+ private final int mSamples;
+ private final long mMin;
+ private final long mMax;
+ private final double mMedian;
+
+ private Stats(int samples, long min, long max, double median) {
+ mSamples = samples;
+ mMin = min;
+ mMax = max;
+ mMedian = median;
+ }
+
+ public int getSampleCount() {
+ return mSamples;
+ }
+
+ public long getMin() {
+ return mMin;
+ }
+
+ public long getMax() {
+ return mMax;
+ }
+
+ public double getMedian() {
+ return mMedian;
+ }
+ }
+}
diff --git a/bridge/tests/src/com/android/layoutlib/bridge/intensive/util/perf/PerformanceRunner.java b/bridge/tests/src/com/android/layoutlib/bridge/intensive/util/perf/PerformanceRunner.java
new file mode 100644
index 0000000..7225a10
--- /dev/null
+++ b/bridge/tests/src/com/android/layoutlib/bridge/intensive/util/perf/PerformanceRunner.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2016 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.intensive.util.perf;
+
+import org.junit.runner.Runner;
+import org.junit.runner.notification.RunNotifier;
+import org.junit.runners.BlockJUnit4ClassRunner;
+import org.junit.runners.model.FrameworkMethod;
+import org.junit.runners.model.InitializationError;
+import org.junit.runners.model.Statement;
+
+import java.lang.annotation.Inherited;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * JUnit {@link Runner} that times the test execution and produces some stats.
+ */
+public class PerformanceRunner extends BlockJUnit4ClassRunner {
+ private static final int DEFAULT_WARMUP_ITERATIONS = 50;
+ private static final int DEFAULT_RUNS = 100;
+
+ private final int mWarmUpIterations;
+ private final int mRuns;
+
+ public PerformanceRunner(Class<?> testClass) throws InitializationError {
+ super(testClass);
+
+ Configuration classConfig = testClass.getAnnotation(Configuration.class);
+ mWarmUpIterations = classConfig != null && classConfig.warmUpIterations() != -1 ?
+ classConfig.warmUpIterations() :
+ DEFAULT_WARMUP_ITERATIONS;
+ mRuns = classConfig != null && classConfig.runs() != -1 ?
+ classConfig.runs() :
+ DEFAULT_RUNS;
+ }
+
+ @Override
+ protected Statement methodInvoker(FrameworkMethod method, Object test) {
+ int warmUpIterations;
+ int runs;
+
+ Configuration methodConfig = method.getAnnotation(Configuration.class);
+ warmUpIterations = methodConfig != null && methodConfig.warmUpIterations() != -1 ?
+ methodConfig.warmUpIterations() :
+ mWarmUpIterations;
+ runs = methodConfig != null && methodConfig.runs() != -1 ?
+ methodConfig.runs() :
+ mRuns;
+ return new TimedStatement(super.methodInvoker(method, test), warmUpIterations, runs,
+ (result) -> System.out.println(result.toString()));
+ }
+
+ @Override
+ public void run(RunNotifier notifier) {
+ super.run(notifier);
+ }
+
+ @Retention(RetentionPolicy.RUNTIME)
+ @Inherited
+ public @interface Configuration {
+ int warmUpIterations() default -1;
+
+ int runs() default -1;
+ }
+}
diff --git a/bridge/tests/src/com/android/layoutlib/bridge/intensive/util/perf/TimedStatement.java b/bridge/tests/src/com/android/layoutlib/bridge/intensive/util/perf/TimedStatement.java
new file mode 100644
index 0000000..77a2b0e
--- /dev/null
+++ b/bridge/tests/src/com/android/layoutlib/bridge/intensive/util/perf/TimedStatement.java
@@ -0,0 +1,178 @@
+/*
+ * Copyright (C) 2016 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.intensive.util.perf;
+
+import com.android.layoutlib.bridge.intensive.util.TestUtils;
+
+import org.junit.runners.model.Statement;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.Random;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.function.Consumer;
+
+import com.google.common.hash.HashCode;
+import com.google.common.hash.HashFunction;
+import com.google.common.hash.Hashing;
+
+/**
+ * JUnit {@link Statement} used to measure some statistics about the test method.
+ */
+public class TimedStatement extends Statement {
+ private static final int CALIBRATION_WARMUP_ITERATIONS = 50;
+ private static final int CALIBRATION_RUNS = 100;
+
+ private static boolean sIsCalibrated;
+ private static double sCalibrated;
+
+ private final Statement mStatement;
+ private final int mWarmUpIterations;
+ private final int mRuns;
+ private final Runtime mRuntime = Runtime.getRuntime();
+ private final Consumer<TimedStatementResult> mCallback;
+
+ TimedStatement(Statement statement, int warmUpIterations, int runs,
+ Consumer<TimedStatementResult> finishedCallback) {
+ mStatement = statement;
+ mWarmUpIterations = warmUpIterations;
+ mRuns = runs;
+ mCallback = finishedCallback;
+ }
+
+ /**
+ * The calibrate method tries to do some work that involves IO, memory allocations and some
+ * operations on the randomly generated data to calibrate the speed of the machine with
+ * something that resembles the execution of a test case.
+ */
+ private static void calibrateMethod() throws IOException {
+ File tmpFile = File.createTempFile("test", "file");
+ Random rnd = new Random();
+ HashFunction hashFunction = Hashing.sha512();
+ for (int i = 0; i < 5 + rnd.nextInt(5); i++) {
+ FileOutputStream stream = new FileOutputStream(tmpFile);
+ int bytes = 30000 + rnd.nextInt(60000);
+ byte[] buffer = new byte[bytes];
+
+ rnd.nextBytes(buffer);
+ byte acc = 0;
+ for (int j = 0; j < bytes; j++) {
+ acc += buffer[i];
+ }
+ buffer[0] = acc;
+ stream.write(buffer);
+ System.gc();
+ stream.close();
+ FileInputStream input = new FileInputStream(tmpFile);
+ byte[] readBuffer = new byte[bytes];
+ //noinspection ResultOfMethodCallIgnored
+ input.read(readBuffer);
+ buffer = readBuffer;
+ HashCode code1 = hashFunction.hashBytes(buffer);
+ Arrays.sort(buffer);
+ HashCode code2 = hashFunction.hashBytes(buffer);
+ input.close();
+
+ FileOutputStream hashStream = new FileOutputStream(tmpFile);
+ hashStream.write(code1.asBytes());
+ hashStream.write(code2.asBytes());
+ hashStream.close();
+ }
+ }
+
+ /**
+ * Runs the calibration process and sets the calibration measure in {@link #sCalibrated}
+ */
+ private static void doCalibration() throws IOException {
+ System.out.println("Calibrating ...");
+ TestUtils.gc();
+ for (int i = 0; i < CALIBRATION_WARMUP_ITERATIONS; i++) {
+ calibrateMethod();
+ }
+
+ LongStatsCollector stats = new LongStatsCollector(CALIBRATION_RUNS);
+ for (int i = 0; i < CALIBRATION_RUNS; i++) {
+ TestUtils.gc();
+ long start = System.currentTimeMillis();
+ calibrateMethod();
+ stats.accept(System.currentTimeMillis() - start);
+ }
+
+ sCalibrated = stats.getStats().getMedian();
+ sIsCalibrated = true;
+ System.out.printf(" DONE %fms\n", sCalibrated);
+ }
+
+ private long getUsedMemory() {
+ return mRuntime.totalMemory() - mRuntime.freeMemory();
+ }
+
+
+ @Override
+ public void evaluate() throws Throwable {
+ if (!sIsCalibrated) {
+ doCalibration();
+ }
+
+ for (int i = 0; i < mWarmUpIterations; i++) {
+ mStatement.evaluate();
+ }
+
+ LongStatsCollector timeStats = new LongStatsCollector(mRuns);
+ LongStatsCollector memoryUseStats = new LongStatsCollector(mRuns);
+ AtomicBoolean collectSamples = new AtomicBoolean(false);
+
+ ScheduledExecutorService executorService = Executors.newScheduledThreadPool(1);
+ TestUtils.gc();
+ executorService.scheduleAtFixedRate(() -> {
+ if (!collectSamples.get()) {
+ return;
+ }
+ memoryUseStats.accept(getUsedMemory());
+ }, 0, 200, TimeUnit.MILLISECONDS);
+
+ try {
+ for (int i = 0; i < mRuns; i++) {
+ TestUtils.gc();
+ collectSamples.set(true);
+ long startTimeMs = System.currentTimeMillis();
+ mStatement.evaluate();
+ long stopTimeMs = System.currentTimeMillis();
+ collectSamples.set(true);
+ timeStats.accept(stopTimeMs - startTimeMs);
+
+ }
+ } finally {
+ executorService.shutdownNow();
+ }
+
+ TimedStatementResult result = new TimedStatementResult(
+ mWarmUpIterations,
+ mRuns,
+ sCalibrated,
+ timeStats.getStats(),
+ memoryUseStats.getStats());
+ mCallback.accept(result);
+ }
+
+}
diff --git a/bridge/tests/src/com/android/layoutlib/bridge/intensive/util/perf/TimedStatementResult.java b/bridge/tests/src/com/android/layoutlib/bridge/intensive/util/perf/TimedStatementResult.java
new file mode 100644
index 0000000..59f90d2
--- /dev/null
+++ b/bridge/tests/src/com/android/layoutlib/bridge/intensive/util/perf/TimedStatementResult.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2016 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.intensive.util.perf;
+
+import com.android.layoutlib.bridge.intensive.util.perf.LongStatsCollector.Stats;
+
+import java.text.DecimalFormat;
+
+/**
+ * Result value of a {@link TimedStatement}
+ */
+public class TimedStatementResult {
+ private static final DecimalFormat UNITS_FORMAT = new DecimalFormat("#.##");
+
+ private final int mWarmUpIterations;
+ private final int mRuns;
+ private final double mCalibrationTimeMs;
+ private final Stats mTimeStats;
+ private final Stats mMemoryStats;
+
+ TimedStatementResult(int warmUpIterations, int runs,
+ double calibrationTimeMs,
+ Stats timeStats,
+ Stats memoryStats) {
+ mWarmUpIterations = warmUpIterations;
+ mRuns = runs;
+ mCalibrationTimeMs = calibrationTimeMs;
+ mTimeStats = timeStats;
+ mMemoryStats = memoryStats;
+ }
+
+ @Override
+ public String toString() {
+ return String.format(
+ "Warm up %d. Runs %d\n" + "Time: %s ms (min: %s, max %s)\n" +
+ "Calibration Time: %f ms\n" +
+ "Calibrated Time: %s units (min: %s, max %s)\n" +
+ "Sampled %d times\n" +
+ " Memory used: %d bytes (max %d)\n\n",
+ mWarmUpIterations, mRuns,
+ mTimeStats.getMedian(), mTimeStats.getMin(), mTimeStats.getMax(),
+ mCalibrationTimeMs,
+ UNITS_FORMAT.format((mTimeStats.getMedian() / mCalibrationTimeMs) * 100000),
+ UNITS_FORMAT.format((mTimeStats.getMin() / mCalibrationTimeMs) * 100000),
+ UNITS_FORMAT.format((mTimeStats.getMax() / mCalibrationTimeMs) * 100000),
+ mMemoryStats.getSampleCount(),
+ (long)mMemoryStats.getMedian() - mMemoryStats.getMin(),
+ mMemoryStats.getMax() - mMemoryStats.getMin());
+ }
+}
diff --git a/create/src/com/android/tools/layoutlib/create/AsmGenerator.java b/create/src/com/android/tools/layoutlib/create/AsmGenerator.java
index 3b37612..a2f8372 100644
--- a/create/src/com/android/tools/layoutlib/create/AsmGenerator.java
+++ b/create/src/com/android/tools/layoutlib/create/AsmGenerator.java
@@ -26,6 +26,7 @@
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.ListIterator;
@@ -48,8 +49,6 @@
private final String mOsDestJar;
/** List of classes to inject in the final JAR from _this_ archive. */
private final Class<?>[] mInjectClasses;
- /** The set of methods to stub out. */
- private final Set<String> mStubMethods;
/** All classes to output as-is, except if they have native methods. */
private Map<String, ClassReader> mKeep;
/** All dependencies that must be completely stubbed. */
@@ -107,7 +106,6 @@
}
}
mInjectClasses = injectedClasses.toArray(new Class<?>[0]);
- mStubMethods = new HashSet<>(Arrays.asList(createInfo.getOverriddenMethods()));
// Create the map/set of methods to change to delegates
mDelegateMethods = new HashMap<>();
@@ -384,7 +382,7 @@
if (mInjectedMethodsMap.keySet().contains(binaryNewName)) {
cv = new InjectMethodsAdapter(cv, mInjectedMethodsMap.get(binaryNewName));
}
- cv = new TransformClassAdapter(mLog, mStubMethods, mDeleteReturns.get(className),
+ cv = new TransformClassAdapter(mLog, Collections.emptySet(), mDeleteReturns.get(className),
newName, cv, stubNativesOnly);
Set<String> delegateMethods = mDelegateMethods.get(className);
diff --git a/create/src/com/android/tools/layoutlib/create/CreateInfo.java b/create/src/com/android/tools/layoutlib/create/CreateInfo.java
index 061bed7..741eb27 100644
--- a/create/src/com/android/tools/layoutlib/create/CreateInfo.java
+++ b/create/src/com/android/tools/layoutlib/create/CreateInfo.java
@@ -64,18 +64,6 @@
}
/**
- * Returns The list of methods to stub out. Each entry must be in the form
- * "package.package.OuterClass$InnerClass#MethodName".
- * The list can be empty but must not be null.
- * <p/>
- * This usage is deprecated. Please use method 'delegates' instead.
- */
- @Override
- public String[] getOverriddenMethods() {
- return OVERRIDDEN_METHODS;
- }
-
- /**
* Returns the list of classes to rename, must be an even list: the binary FQCN
* of class to replace followed by the new FQCN.
* The list can be empty but must not be null.
@@ -166,6 +154,8 @@
"android.content.res.Resources#getIntArray",
"android.content.res.Resources#getInteger",
"android.content.res.Resources#getLayout",
+ "android.content.res.Resources#getQuantityString",
+ "android.content.res.Resources#getQuantityText",
"android.content.res.Resources#getResourceEntryName",
"android.content.res.Resources#getResourceName",
"android.content.res.Resources#getResourcePackageName",
@@ -186,11 +176,13 @@
"android.content.res.Resources$Theme#resolveAttributes",
"android.content.res.AssetManager#newTheme",
"android.content.res.AssetManager#deleteTheme",
+ "android.content.res.AssetManager#getAssignedPackageIdentifiers",
"android.content.res.TypedArray#getValueAt",
"android.content.res.TypedArray#obtain",
"android.graphics.BitmapFactory#finishDecode",
"android.graphics.BitmapFactory#setDensityFromOptions",
"android.graphics.drawable.AnimatedVectorDrawable$VectorDrawableAnimatorRT#useLastSeenTarget",
+ "android.graphics.drawable.AnimatedVectorDrawable$VectorDrawableAnimatorRT#onDraw",
"android.graphics.drawable.GradientDrawable#buildRing",
"android.graphics.FontFamily#addFont",
"android.graphics.Typeface#getSystemFontConfigLocation",
@@ -217,7 +209,7 @@
"android.view.MenuInflater#registerMenu",
"android.view.RenderNode#getMatrix",
"android.view.RenderNode#nCreate",
- "android.view.RenderNode#nDestroyRenderNode",
+ "android.view.RenderNode#nGetNativeFinalizer",
"android.view.RenderNode#nSetElevation",
"android.view.RenderNode#nGetElevation",
"android.view.RenderNode#nSetTranslationX",
@@ -242,10 +234,10 @@
"android.view.RenderNode#nSetScaleY",
"android.view.RenderNode#nGetScaleY",
"android.view.RenderNode#nIsPivotExplicitlySet",
+ "android.view.PointerIcon#loadResource",
"android.view.ViewGroup#drawChild",
"com.android.internal.view.menu.MenuBuilder#createNewMenuItem",
"com.android.internal.util.XmlUtils#convertValueToInt",
- "com.android.internal.textservice.ITextServicesManager$Stub#asInterface",
"dalvik.system.VMRuntime#newUnpaddedArray",
"libcore.io.MemoryMappedFile#mmapRO",
"libcore.io.MemoryMappedFile#close",
@@ -258,6 +250,7 @@
*/
public final static String[] DELEGATE_CLASS_NATIVES = new String[] {
"android.animation.PropertyValuesHolder",
+ "android.graphics.BaseCanvas",
"android.graphics.Bitmap",
"android.graphics.BitmapFactory",
"android.graphics.BitmapShader",
@@ -273,7 +266,6 @@
"android.graphics.DrawFilter",
"android.graphics.EmbossMaskFilter",
"android.graphics.FontFamily",
- "android.graphics.LayerRasterizer",
"android.graphics.LightingColorFilter",
"android.graphics.LinearGradient",
"android.graphics.MaskFilter",
@@ -286,15 +278,12 @@
"android.graphics.PathEffect",
"android.graphics.PathMeasure",
"android.graphics.PorterDuffColorFilter",
- "android.graphics.PorterDuffXfermode",
"android.graphics.RadialGradient",
- "android.graphics.Rasterizer",
"android.graphics.Region",
"android.graphics.Shader",
"android.graphics.SumPathEffect",
"android.graphics.SweepGradient",
"android.graphics.Typeface",
- "android.graphics.Xfermode",
"android.graphics.drawable.AnimatedVectorDrawable",
"android.graphics.drawable.VectorDrawable",
"android.os.SystemClock",
@@ -309,20 +298,13 @@
};
/**
- * The list of methods to stub out. Each entry must be in the form
- * "package.package.OuterClass$InnerClass#MethodName".
- * This usage is deprecated. Please use method 'delegates' instead.
- */
- private final static String[] OVERRIDDEN_METHODS = new String[] {
- };
-
- /**
* The list of classes to rename, must be an even list: the binary FQCN
* of class to replace followed by the new FQCN.
*/
private final static String[] RENAMED_CLASSES =
new String[] {
"android.os.ServiceManager", "android.os._Original_ServiceManager",
+ "android.view.textservice.TextServicesManager", "android.view.textservice._Original_TextServicesManager",
"android.util.LruCache", "android.util._Original_LruCache",
"android.view.SurfaceView", "android.view._Original_SurfaceView",
"android.view.accessibility.AccessibilityManager", "android.view.accessibility._Original_AccessibilityManager",
@@ -357,7 +339,8 @@
*/
private final static String[] PROMOTED_FIELDS = new String[] {
"android.graphics.drawable.VectorDrawable#mVectorState",
- "android.view.Choreographer#mLastFrameTimeNanos"
+ "android.view.Choreographer#mLastFrameTimeNanos",
+ "android.graphics.FontFamily#mBuilderPtr"
};
/**
diff --git a/create/src/com/android/tools/layoutlib/create/ICreateInfo.java b/create/src/com/android/tools/layoutlib/create/ICreateInfo.java
index 6c62423..535a9a8 100644
--- a/create/src/com/android/tools/layoutlib/create/ICreateInfo.java
+++ b/create/src/com/android/tools/layoutlib/create/ICreateInfo.java
@@ -45,13 +45,6 @@
String[] getDelegateClassNatives();
/**
- * Returns The list of methods to stub out. Each entry must be in the form
- * "package.package.OuterClass$InnerClass#MethodName".
- * The list can be empty but must not be null.
- */
- String[] getOverriddenMethods();
-
- /**
* Returns the list of classes to rename, must be an even list: the binary FQCN
* of class to replace followed by the new FQCN.
* The list can be empty but must not be null.
diff --git a/create/src/com/android/tools/layoutlib/create/Main.java b/create/src/com/android/tools/layoutlib/create/Main.java
index 9bb91e5..4b6cf43 100644
--- a/create/src/com/android/tools/layoutlib/create/Main.java
+++ b/create/src/com/android/tools/layoutlib/create/Main.java
@@ -124,6 +124,7 @@
"android.annotation.NonNull", // annotations
"android.annotation.Nullable", // annotations
"com.android.internal.transition.EpicenterTranslateClipReveal",
+ "com.android.internal.graphics.drawable.AnimationScaleListDrawable",
},
excludeClasses,
new String[] {
diff --git a/create/tests/com/android/tools/layoutlib/create/AsmGeneratorTest.java b/create/tests/com/android/tools/layoutlib/create/AsmGeneratorTest.java
index c4dd7ee..0560d8a 100644
--- a/create/tests/com/android/tools/layoutlib/create/AsmGeneratorTest.java
+++ b/create/tests/com/android/tools/layoutlib/create/AsmGeneratorTest.java
@@ -107,12 +107,6 @@
}
@Override
- public String[] getOverriddenMethods() {
- // methods to force override
- return EMPTY_STRING_ARRAY;
- }
-
- @Override
public String[] getRenamedClasses() {
// classes to rename (so that we can replace them)
return new String[] {
@@ -187,12 +181,6 @@
}
@Override
- public String[] getOverriddenMethods() {
- // methods to force override
- return EMPTY_STRING_ARRAY;
- }
-
- @Override
public String[] getRenamedClasses() {
// classes to rename (so that we can replace them)
return EMPTY_STRING_ARRAY;
@@ -274,12 +262,6 @@
}
@Override
- public String[] getOverriddenMethods() {
- // methods to force override
- return EMPTY_STRING_ARRAY;
- }
-
- @Override
public String[] getRenamedClasses() {
// classes to rename (so that we can replace them)
return EMPTY_STRING_ARRAY;
@@ -360,12 +342,6 @@
}
@Override
- public String[] getOverriddenMethods() {
- // methods to force override
- return EMPTY_STRING_ARRAY;
- }
-
- @Override
public String[] getRenamedClasses() {
// classes to rename (so that we can replace them)
return EMPTY_STRING_ARRAY;
diff --git a/legacy/Android.mk b/legacy/Android.mk
new file mode 100644
index 0000000..5855f89
--- /dev/null
+++ b/legacy/Android.mk
@@ -0,0 +1,30 @@
+#
+# Copyright (C) 2008 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.
+#
+LOCAL_PATH := $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := $(call all-java-files-under,src)
+
+LOCAL_JAVA_LIBRARIES := \
+ layoutlib_api-prebuilt
+
+LOCAL_MODULE := layoutlib-legacy
+
+include $(BUILD_HOST_JAVA_LIBRARY)
+
+# Build all sub-directories
+include $(call all-makefiles-under,$(LOCAL_PATH))
+
diff --git a/legacy/legacy.iml b/legacy/legacy.iml
new file mode 100644
index 0000000..a167a75
--- /dev/null
+++ b/legacy/legacy.iml
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<module type="JAVA_MODULE" version="4">
+ <component name="NewModuleRootManager" inherit-compiler-output="true">
+ <exclude-output />
+ <content url="file://$MODULE_DIR$">
+ <sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
+ </content>
+ <orderEntry type="inheritedJdk" />
+ <orderEntry type="sourceFolder" forTests="false" />
+ <orderEntry type="library" name="layoutlib_api-prebuilt" level="project" />
+ </component>
+</module>
\ No newline at end of file
diff --git a/legacy/src/com/android/layoutlib/bridge/Bridge.java b/legacy/src/com/android/layoutlib/bridge/Bridge.java
new file mode 100644
index 0000000..0cfc181
--- /dev/null
+++ b/legacy/src/com/android/layoutlib/bridge/Bridge.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2016 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;import com.android.ide.common.rendering.api.RenderSession;
+import com.android.ide.common.rendering.api.Result;
+import com.android.ide.common.rendering.api.Result.Status;
+import com.android.ide.common.rendering.api.SessionParams;
+
+import java.awt.Graphics2D;
+import java.awt.image.BufferedImage;
+
+/**
+ * Legacy Bridge used in the SDK version of layoutlib
+ */
+public final class Bridge extends com.android.ide.common.rendering.api.Bridge {
+ private static final String SDK_NOT_SUPPORTED = "The SDK layoutlib version is not supported";
+ private static final Result NOT_SUPPORTED_RESULT =
+ Status.NOT_IMPLEMENTED.createResult(SDK_NOT_SUPPORTED);
+ private static BufferedImage sImage;
+
+ private static class BridgeRenderSession extends RenderSession {
+
+ @Override
+ public synchronized BufferedImage getImage() {
+ if (sImage == null) {
+ sImage = new BufferedImage(500, 500, BufferedImage.TYPE_INT_ARGB);
+ Graphics2D g = sImage.createGraphics();
+ g.clearRect(0, 0, 500, 500);
+ g.drawString(SDK_NOT_SUPPORTED, 20, 20);
+ g.dispose();
+ }
+
+ return sImage;
+ }
+
+ @Override
+ public Result render(long timeout, boolean forceMeasure) {
+ return NOT_SUPPORTED_RESULT;
+ }
+
+ @Override
+ public Result measure(long timeout) {
+ return NOT_SUPPORTED_RESULT;
+ }
+
+ @Override
+ public Result getResult() {
+ return NOT_SUPPORTED_RESULT;
+ }
+ }
+
+
+ @Override
+ public RenderSession createSession(SessionParams params) {
+ return new BridgeRenderSession();
+ }
+
+ @Override
+ public int getApiLevel() {
+ return 0;
+ }
+}