diff options
Diffstat (limited to 'tools/layoutlib/bridge/src')
43 files changed, 1614 insertions, 506 deletions
diff --git a/tools/layoutlib/bridge/src/android/content/res/BridgeResources.java b/tools/layoutlib/bridge/src/android/content/res/BridgeResources.java index 2c2c672..fdb6e75 100644 --- a/tools/layoutlib/bridge/src/android/content/res/BridgeResources.java +++ b/tools/layoutlib/bridge/src/android/content/res/BridgeResources.java @@ -169,7 +169,7 @@ public final class BridgeResources extends Resources { } @Override - public int getColor(int id) throws NotFoundException { + public int getColor(int id, Theme theme) throws NotFoundException { Pair<String, ResourceValue> value = getResourceValue(id, mPlatformResourceFlag); if (value != null) { @@ -192,22 +192,21 @@ public final class BridgeResources extends Resources { } } - // id was not found or not resolved. Throw a NotFoundException. - throwException(id); - - // this is not used since the method above always throws - return 0; + // Suppress possible NPE. getColorStateList will never return null, it will instead + // throw an exception, but intelliJ can't figure that out + //noinspection ConstantConditions + return getColorStateList(id, theme).getDefaultColor(); } @Override - public ColorStateList getColorStateList(int id) throws NotFoundException { + public ColorStateList getColorStateList(int id, Theme theme) throws NotFoundException { Pair<String, ResourceValue> resValue = getResourceValue(id, mPlatformResourceFlag); if (resValue != null) { ColorStateList stateList = ResourceHelper.getColorStateList(resValue.getSecond(), mContext); if (stateList != null) { - return stateList; + return stateList.obtainForTheme(theme); } } diff --git a/tools/layoutlib/bridge/src/android/content/res/BridgeTypedArray.java b/tools/layoutlib/bridge/src/android/content/res/BridgeTypedArray.java index 7d4271b..54a508a 100644 --- a/tools/layoutlib/bridge/src/android/content/res/BridgeTypedArray.java +++ b/tools/layoutlib/bridge/src/android/content/res/BridgeTypedArray.java @@ -320,7 +320,8 @@ public final class BridgeTypedArray extends TypedArray { BridgeXmlBlockParser blockParser = new BridgeXmlBlockParser( parser, mContext, resValue.isFramework()); try { - return ColorStateList.createFromXml(mContext.getResources(), blockParser); + return ColorStateList.createFromXml(mContext.getResources(), blockParser, + mContext.getTheme()); } finally { blockParser.ensurePopped(); } @@ -444,7 +445,7 @@ public final class BridgeTypedArray extends TypedArray { @Override public int getDimensionPixelSize(int index, int defValue) { try { - return getDimension(index); + return getDimension(index, null); } catch (RuntimeException e) { String s = getString(index); @@ -474,12 +475,12 @@ public final class BridgeTypedArray extends TypedArray { @Override public int getLayoutDimension(int index, String name) { try { - // this will throw an exception - return getDimension(index); + // this will throw an exception if not found. + return getDimension(index, name); } catch (RuntimeException e) { if (LayoutInflater_Delegate.sIsInInclude) { - throw new RuntimeException(); + throw new RuntimeException("Layout Dimension '" + name + "' not found."); } Bridge.getLog().warning(LayoutLog.TAG_RESOURCES_FORMAT, @@ -494,9 +495,13 @@ public final class BridgeTypedArray extends TypedArray { return getDimensionPixelSize(index, defValue); } - private int getDimension(int index) { + /** @param name attribute name, used for error reporting. */ + private int getDimension(int index, @Nullable String name) { String s = getString(index); if (s == null) { + if (name != null) { + throw new RuntimeException("Attribute '" + name + "' not found"); + } throw new RuntimeException(); } // Check if the value is a magic constant that doesn't require a unit. @@ -758,6 +763,17 @@ public final class BridgeTypedArray extends TypedArray { return s != null && ResourceHelper.parseFloatAttribute(mNames[index], s, outValue, false); } + @Override + public int getType(int index) { + if (!hasValue(index)) { + return TypedValue.TYPE_NULL; + } + ResourceValue value = mResourceData[index]; + ResourceType resourceType = value.getResourceType(); + return 0; + // TODO: fixme. + } + /** * Determines whether there is an attribute at <var>index</var>. * diff --git a/tools/layoutlib/bridge/src/android/content/res/Resources_Theme_Delegate.java b/tools/layoutlib/bridge/src/android/content/res/Resources_Theme_Delegate.java index 4bd83e9..41d94b7 100644 --- a/tools/layoutlib/bridge/src/android/content/res/Resources_Theme_Delegate.java +++ b/tools/layoutlib/bridge/src/android/content/res/Resources_Theme_Delegate.java @@ -27,6 +27,7 @@ import com.android.tools.layoutlib.annotations.LayoutlibDelegate; import android.content.res.Resources.NotFoundException; import android.content.res.Resources.Theme; +import android.content.res.Resources.ThemeKey; import android.util.AttributeSet; import android.util.TypedValue; @@ -110,22 +111,16 @@ public class Resources_Theme_Delegate { private static boolean setupResources(Theme thisTheme) { // Key is a space-separated list of theme ids applied that have been merged into the // BridgeContext's theme to make thisTheme. - String[] appliedStyles = thisTheme.getKey().split(" "); + final ThemeKey key = thisTheme.getKey(); + final int[] resId = key.mResId; + final boolean[] force = key.mForce; + boolean changed = false; - for (String s : appliedStyles) { - if (s.isEmpty()) { - continue; - } - // See the definition of force parameter in Theme.applyStyle(). - boolean force = false; - if (s.charAt(s.length() - 1) == '!') { - force = true; - s = s.substring(0, s.length() - 1); - } - int styleId = Integer.parseInt(s, 16); - StyleResourceValue style = resolveStyle(styleId); + for (int i = 0, N = key.mCount; i < N; i++) { + StyleResourceValue style = resolveStyle(resId[i]); if (style != null) { - RenderSessionImpl.getCurrentContext().getRenderResources().applyStyle(style, force); + RenderSessionImpl.getCurrentContext().getRenderResources().applyStyle( + style, force[i]); changed = true; } diff --git a/tools/layoutlib/bridge/src/android/graphics/BidiRenderer.java b/tools/layoutlib/bridge/src/android/graphics/BidiRenderer.java index a4a3b7d..21f36ce 100644 --- a/tools/layoutlib/bridge/src/android/graphics/BidiRenderer.java +++ b/tools/layoutlib/bridge/src/android/graphics/BidiRenderer.java @@ -19,6 +19,12 @@ package android.graphics; import com.android.ide.common.rendering.api.LayoutLog; import com.android.layoutlib.bridge.Bridge; +import android.graphics.Paint_Delegate.FontInfo; +import android.icu.lang.UScript; +import android.icu.lang.UScriptRun; +import android.icu.text.Bidi; +import android.icu.text.BidiRun; + import java.awt.Font; import java.awt.Graphics2D; import java.awt.Toolkit; @@ -29,13 +35,6 @@ import java.util.ArrayList; import java.util.LinkedList; import java.util.List; -import com.ibm.icu.lang.UScript; -import com.ibm.icu.lang.UScriptRun; -import com.ibm.icu.text.Bidi; -import com.ibm.icu.text.BidiRun; - -import android.graphics.Paint_Delegate.FontInfo; - /** * Render the text by breaking it into various scripts and using the right font for each script. * Can be used to measure the text without actually drawing it. diff --git a/tools/layoutlib/bridge/src/android/graphics/BitmapShader_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/BitmapShader_Delegate.java index e9b5d6e..af47aeb 100644 --- a/tools/layoutlib/bridge/src/android/graphics/BitmapShader_Delegate.java +++ b/tools/layoutlib/bridge/src/android/graphics/BitmapShader_Delegate.java @@ -23,7 +23,15 @@ import com.android.tools.layoutlib.annotations.LayoutlibDelegate; import android.graphics.Shader.TileMode; +import java.awt.PaintContext; +import java.awt.Rectangle; +import java.awt.RenderingHints; +import java.awt.geom.AffineTransform; +import java.awt.geom.NoninvertibleTransformException; +import java.awt.geom.Rectangle2D; +import java.awt.image.BufferedImage; import java.awt.image.ColorModel; +import java.awt.image.Raster; /** * Delegate implementing the native methods of android.graphics.BitmapShader @@ -67,9 +75,9 @@ public class BitmapShader_Delegate extends Shader_Delegate { // ---- native methods ---- @LayoutlibDelegate - /*package*/ static long nativeCreate(long native_bitmap, int shaderTileModeX, + /*package*/ static long nativeCreate(Bitmap androidBitmap, int shaderTileModeX, int shaderTileModeY) { - Bitmap_Delegate bitmap = Bitmap_Delegate.getDelegate(native_bitmap); + Bitmap_Delegate bitmap = Bitmap_Delegate.getDelegate(androidBitmap); if (bitmap == null) { return 0; } @@ -83,17 +91,17 @@ public class BitmapShader_Delegate extends Shader_Delegate { // ---- Private delegate/helper methods ---- - private BitmapShader_Delegate(java.awt.image.BufferedImage image, + private BitmapShader_Delegate(BufferedImage image, TileMode tileModeX, TileMode tileModeY) { mJavaPaint = new BitmapShaderPaint(image, tileModeX, tileModeY); } private class BitmapShaderPaint implements java.awt.Paint { - private final java.awt.image.BufferedImage mImage; + private final BufferedImage mImage; private final TileMode mTileModeX; private final TileMode mTileModeY; - BitmapShaderPaint(java.awt.image.BufferedImage image, + BitmapShaderPaint(BufferedImage image, TileMode tileModeX, TileMode tileModeY) { mImage = image; mTileModeX = tileModeX; @@ -101,29 +109,24 @@ public class BitmapShader_Delegate extends Shader_Delegate { } @Override - public java.awt.PaintContext createContext( - java.awt.image.ColorModel colorModel, - java.awt.Rectangle deviceBounds, - java.awt.geom.Rectangle2D userBounds, - java.awt.geom.AffineTransform xform, - java.awt.RenderingHints hints) { - - java.awt.geom.AffineTransform canvasMatrix; + public PaintContext createContext(ColorModel colorModel, Rectangle deviceBounds, + Rectangle2D userBounds, AffineTransform xform, RenderingHints hints) { + AffineTransform canvasMatrix; try { canvasMatrix = xform.createInverse(); - } catch (java.awt.geom.NoninvertibleTransformException e) { + } catch (NoninvertibleTransformException e) { Bridge.getLog().fidelityWarning(LayoutLog.TAG_MATRIX_INVERSE, "Unable to inverse matrix in BitmapShader", e, null /*data*/); - canvasMatrix = new java.awt.geom.AffineTransform(); + canvasMatrix = new AffineTransform(); } - java.awt.geom.AffineTransform localMatrix = getLocalMatrix(); + AffineTransform localMatrix = getLocalMatrix(); try { localMatrix = localMatrix.createInverse(); - } catch (java.awt.geom.NoninvertibleTransformException e) { + } catch (NoninvertibleTransformException e) { Bridge.getLog().fidelityWarning(LayoutLog.TAG_MATRIX_INVERSE, "Unable to inverse matrix in BitmapShader", e, null /*data*/); - localMatrix = new java.awt.geom.AffineTransform(); + localMatrix = new AffineTransform(); } if (!colorModel.isCompatibleRaster(mImage.getRaster())) { @@ -134,16 +137,16 @@ public class BitmapShader_Delegate extends Shader_Delegate { return new BitmapShaderContext(canvasMatrix, localMatrix, colorModel); } - private class BitmapShaderContext implements java.awt.PaintContext { + private class BitmapShaderContext implements PaintContext { - private final java.awt.geom.AffineTransform mCanvasMatrix; - private final java.awt.geom.AffineTransform mLocalMatrix; - private final java.awt.image.ColorModel mColorModel; + private final AffineTransform mCanvasMatrix; + private final AffineTransform mLocalMatrix; + private final ColorModel mColorModel; public BitmapShaderContext( - java.awt.geom.AffineTransform canvasMatrix, - java.awt.geom.AffineTransform localMatrix, - java.awt.image.ColorModel colorModel) { + AffineTransform canvasMatrix, + AffineTransform localMatrix, + ColorModel colorModel) { mCanvasMatrix = canvasMatrix; mLocalMatrix = localMatrix; mColorModel = colorModel; @@ -154,13 +157,13 @@ public class BitmapShader_Delegate extends Shader_Delegate { } @Override - public java.awt.image.ColorModel getColorModel() { + public ColorModel getColorModel() { return mColorModel; } @Override - public java.awt.image.Raster getRaster(int x, int y, int w, int h) { - java.awt.image.BufferedImage image = new java.awt.image.BufferedImage( + public Raster getRaster(int x, int y, int w, int h) { + BufferedImage image = new BufferedImage( mColorModel, mColorModel.createCompatibleWritableRaster(w, h), mColorModel.isAlphaPremultiplied(), null); diff --git a/tools/layoutlib/bridge/src/android/graphics/Bitmap_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/Bitmap_Delegate.java index 8d24d38..874bc9d 100644 --- a/tools/layoutlib/bridge/src/android/graphics/Bitmap_Delegate.java +++ b/tools/layoutlib/bridge/src/android/graphics/Bitmap_Delegate.java @@ -16,6 +16,7 @@ package android.graphics; +import com.android.annotations.Nullable; import com.android.ide.common.rendering.api.LayoutLog; import com.android.layoutlib.bridge.Bridge; import com.android.layoutlib.bridge.impl.DelegateManager; @@ -76,19 +77,18 @@ public final class Bitmap_Delegate { // ---- Public Helper methods ---- /** - * Returns the native delegate associated to a given {@link Bitmap_Delegate} object. - */ - public static Bitmap_Delegate getDelegate(Bitmap bitmap) { - return sManager.getDelegate(bitmap.mNativeBitmap); - } - - /** * Returns the native delegate associated to a given an int referencing a {@link Bitmap} object. */ public static Bitmap_Delegate getDelegate(long native_bitmap) { return sManager.getDelegate(native_bitmap); } + @Nullable + public static Bitmap_Delegate getDelegate(@Nullable Bitmap bitmap) { + // refSkPixelRef is a hack to get the native pointer: see #nativeRefPixelRef() + return bitmap == null ? null : getDelegate(bitmap.refSkPixelRef()); + } + /** * Creates and returns a {@link Bitmap} initialized with the given file content. * @@ -187,31 +187,7 @@ public final class Bitmap_Delegate { return createBitmap(delegate, createFlags, density.getDpiValue()); } - /** - * Returns the {@link BufferedImage} used by the delegate of the given {@link Bitmap}. - */ - public static BufferedImage getImage(Bitmap bitmap) { - // get the delegate from the native int. - Bitmap_Delegate delegate = sManager.getDelegate(bitmap.mNativeBitmap); - if (delegate == null) { - return null; - } - - return delegate.mImage; - } - - public static int getBufferedImageType(int nativeBitmapConfig) { - switch (Config.nativeToConfig(nativeBitmapConfig)) { - case ALPHA_8: - return BufferedImage.TYPE_INT_ARGB; - case RGB_565: - return BufferedImage.TYPE_INT_ARGB; - case ARGB_4444: - return BufferedImage.TYPE_INT_ARGB; - case ARGB_8888: - return BufferedImage.TYPE_INT_ARGB; - } - + private static int getBufferedImageType() { return BufferedImage.TYPE_INT_ARGB; } @@ -238,10 +214,6 @@ public final class Bitmap_Delegate { return mHasAlpha && mConfig != Config.RGB_565; } - public boolean hasMipMap() { - // TODO: check if more checks are required as in hasAlpha. - return mHasMipMap; - } /** * Update the generationId. * @@ -256,7 +228,7 @@ public final class Bitmap_Delegate { @LayoutlibDelegate /*package*/ static Bitmap nativeCreate(int[] colors, int offset, int stride, int width, int height, int nativeConfig, boolean isMutable) { - int imageType = getBufferedImageType(nativeConfig); + int imageType = getBufferedImageType(); // create the image BufferedImage image = new BufferedImage(width, height, imageType); @@ -284,7 +256,7 @@ public final class Bitmap_Delegate { int width = srcImage.getWidth(); int height = srcImage.getHeight(); - int imageType = getBufferedImageType(nativeConfig); + int imageType = getBufferedImageType(); // create the image BufferedImage image = new BufferedImage(width, height, imageType); @@ -373,22 +345,16 @@ public final class Bitmap_Delegate { /*package*/ static boolean nativeHasAlpha(long nativeBitmap) { // get the delegate from the native int. Bitmap_Delegate delegate = sManager.getDelegate(nativeBitmap); - if (delegate == null) { - return true; - } + return delegate == null || delegate.mHasAlpha; - return delegate.mHasAlpha; } @LayoutlibDelegate /*package*/ static boolean nativeHasMipMap(long nativeBitmap) { // get the delegate from the native int. Bitmap_Delegate delegate = sManager.getDelegate(nativeBitmap); - if (delegate == null) { - return true; - } + return delegate == null || delegate.mHasMipMap; - return delegate.mHasMipMap; } @LayoutlibDelegate @@ -509,11 +475,6 @@ public final class Bitmap_Delegate { } @LayoutlibDelegate - /*package*/ static void nativePrepareToDraw(long nativeBitmap) { - // nothing to be done here. - } - - @LayoutlibDelegate /*package*/ static boolean nativeIsPremultiplied(long nativeBitmap) { // get the delegate from the native int. Bitmap_Delegate delegate = sManager.getDelegate(nativeBitmap); @@ -599,6 +560,14 @@ public final class Bitmap_Delegate { return Arrays.equals(argb1, argb2); } + // Only used by AssetAtlasService, which we don't care about. + @LayoutlibDelegate + /*package*/ static long nativeRefPixelRef(long nativeBitmap) { + // Hack: This is called by Bitmap.refSkPixelRef() and LayoutLib uses that method to get + // the native pointer from a Bitmap. So, we return nativeBitmap here. + return nativeBitmap; + } + // ---- Private delegate/helper methods ---- private Bitmap_Delegate(BufferedImage image, Config config) { diff --git a/tools/layoutlib/bridge/src/android/graphics/Canvas_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/Canvas_Delegate.java index 4d2d100..47acc42 100644 --- a/tools/layoutlib/bridge/src/android/graphics/Canvas_Delegate.java +++ b/tools/layoutlib/bridge/src/android/graphics/Canvas_Delegate.java @@ -16,6 +16,7 @@ package android.graphics; +import com.android.annotations.Nullable; import com.android.ide.common.rendering.api.LayoutLog; import com.android.layoutlib.bridge.Bridge; import com.android.layoutlib.bridge.impl.DelegateManager; @@ -114,7 +115,11 @@ public final class Canvas_Delegate { } @LayoutlibDelegate - /*package*/ static long initRaster(long nativeBitmapOrZero) { + /*package*/ static long initRaster(@Nullable Bitmap bitmap) { + long nativeBitmapOrZero = 0; + if (bitmap != null) { + nativeBitmapOrZero = bitmap.refSkPixelRef(); + } if (nativeBitmapOrZero > 0) { // get the Bitmap from the int Bitmap_Delegate bitmapDelegate = Bitmap_Delegate.getDelegate(nativeBitmapOrZero); @@ -132,8 +137,7 @@ public final class Canvas_Delegate { } @LayoutlibDelegate - /*package*/ - static void native_setBitmap(long canvas, long bitmap, boolean copyState) { + /*package*/ static void native_setBitmap(long canvas, Bitmap bitmap) { Canvas_Delegate canvasDelegate = sManager.getDelegate(canvas); Bitmap_Delegate bitmapDelegate = Bitmap_Delegate.getDelegate(bitmap); if (canvasDelegate == null || bitmapDelegate==null) { @@ -220,7 +224,8 @@ public final class Canvas_Delegate { } @LayoutlibDelegate - /*package*/ static void native_restore(long nativeCanvas) { + /*package*/ static void native_restore(long nativeCanvas, boolean throwOnUnderflow) { + // FIXME: implement throwOnUnderflow. // get the delegate from the native int. Canvas_Delegate canvasDelegate = sManager.getDelegate(nativeCanvas); if (canvasDelegate == null) { @@ -231,7 +236,9 @@ public final class Canvas_Delegate { } @LayoutlibDelegate - /*package*/ static void native_restoreToCount(long nativeCanvas, int saveCount) { + /*package*/ static void native_restoreToCount(long nativeCanvas, int saveCount, + boolean throwOnUnderflow) { + // FIXME: implement throwOnUnderflow. // get the delegate from the native int. Canvas_Delegate canvasDelegate = sManager.getDelegate(nativeCanvas); if (canvasDelegate == null) { @@ -424,8 +431,7 @@ public final class Canvas_Delegate { canvasDelegate.mDrawFilter = DrawFilter_Delegate.getDelegate(nativeFilter); - if (canvasDelegate.mDrawFilter != null && - canvasDelegate.mDrawFilter.isSupported() == false) { + if (canvasDelegate.mDrawFilter != null && !canvasDelegate.mDrawFilter.isSupported()) { Bridge.getLog().fidelityWarning(LayoutLog.TAG_DRAWFILTER, canvasDelegate.mDrawFilter.getSupportMessage(), null, null /*data*/); } @@ -441,7 +447,7 @@ public final class Canvas_Delegate { } Rectangle rect = canvasDelegate.getSnapshot().getClip().getBounds(); - if (rect != null && rect.isEmpty() == false) { + if (rect != null && !rect.isEmpty()) { bounds.left = rect.x; bounds.top = rect.y; bounds.right = rect.x + rect.width; @@ -717,7 +723,7 @@ public final class Canvas_Delegate { } @LayoutlibDelegate - /*package*/ static void native_drawBitmap(Canvas thisCanvas, long nativeCanvas, long bitmap, + /*package*/ static void native_drawBitmap(Canvas thisCanvas, long nativeCanvas, Bitmap bitmap, float left, float top, long nativePaintOrZero, int canvasDensity, @@ -739,7 +745,7 @@ public final class Canvas_Delegate { } @LayoutlibDelegate - /*package*/ static void native_drawBitmap(Canvas thisCanvas, long nativeCanvas, long bitmap, + /*package*/ static void native_drawBitmap(Canvas thisCanvas, long nativeCanvas, Bitmap bitmap, float srcLeft, float srcTop, float srcRight, float srcBottom, float dstLeft, float dstTop, float dstRight, float dstBottom, long nativePaintOrZero, int screenDensity, int bitmapDensity) { @@ -780,7 +786,7 @@ public final class Canvas_Delegate { } @LayoutlibDelegate - /*package*/ static void nativeDrawBitmapMatrix(long nCanvas, long nBitmap, + /*package*/ static void nativeDrawBitmapMatrix(long nCanvas, Bitmap bitmap, long nMatrix, long nPaint) { // get the delegate from the native int. Canvas_Delegate canvasDelegate = sManager.getDelegate(nCanvas); @@ -792,7 +798,7 @@ public final class Canvas_Delegate { Paint_Delegate paintDelegate = Paint_Delegate.getDelegate(nPaint); // get the delegate from the native int. - Bitmap_Delegate bitmapDelegate = Bitmap_Delegate.getDelegate(nBitmap); + Bitmap_Delegate bitmapDelegate = Bitmap_Delegate.getDelegate(bitmap); if (bitmapDelegate == null) { return; } @@ -821,7 +827,7 @@ public final class Canvas_Delegate { } @LayoutlibDelegate - /*package*/ static void nativeDrawBitmapMesh(long nCanvas, long nBitmap, + /*package*/ static void nativeDrawBitmapMesh(long nCanvas, Bitmap bitmap, int meshWidth, int meshHeight, float[] verts, int vertOffset, int[] colors, int colorOffset, long nPaint) { // FIXME @@ -844,7 +850,7 @@ public final class Canvas_Delegate { @LayoutlibDelegate /*package*/ static void native_drawText(long nativeCanvas, char[] text, int index, int count, float startX, float startY, int flags, long paint, long typeface) { - drawText(nativeCanvas, text, index, count, startX, startY, flags == Canvas.DIRECTION_RTL, + drawText(nativeCanvas, text, index, count, startX, startY, (flags & 1) != 0, paint, typeface); } @@ -1038,8 +1044,7 @@ public final class Canvas_Delegate { } /** - * Restores the {@link GcSnapshot} to <var>saveCount</var> - * @param saveCount the saveCount + * Restores the top {@link GcSnapshot} */ private void restore() { mSnapshot = mSnapshot.restore(); @@ -1102,7 +1107,7 @@ public final class Canvas_Delegate { // before drawing it. if (bitmap.getConfig() == Bitmap.Config.ALPHA_8) { fixAlpha8Bitmap(image); - } else if (bitmap.hasAlpha() == false) { + } else if (!bitmap.hasAlpha()) { // hasAlpha is merely a rendering hint. There can in fact be alpha values // in the bitmap but it should be ignored at drawing time. // There is two ways to do this: @@ -1122,7 +1127,7 @@ public final class Canvas_Delegate { } // if we can't force SRC mode, then create a temp bitmap of TYPE_RGB - if (forceSrcMode[0] == false) { + if (!forceSrcMode[0]) { image = Bitmap_Delegate.createCopy(image, BufferedImage.TYPE_INT_RGB, 0xFF); } } diff --git a/tools/layoutlib/bridge/src/android/graphics/NinePatch_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/NinePatch_Delegate.java index e16dbda..e8d34d0 100644 --- a/tools/layoutlib/bridge/src/android/graphics/NinePatch_Delegate.java +++ b/tools/layoutlib/bridge/src/android/graphics/NinePatch_Delegate.java @@ -90,7 +90,7 @@ public final class NinePatch_Delegate { if (oos != null) { try { oos.close(); - } catch (IOException e) { + } catch (IOException ignored) { } } } @@ -136,7 +136,7 @@ public final class NinePatch_Delegate { if (ois != null) { try { ois.close(); - } catch (IOException e) { + } catch (IOException ignored) { } } } @@ -150,15 +150,12 @@ public final class NinePatch_Delegate { @LayoutlibDelegate /*package*/ static boolean isNinePatchChunk(byte[] chunk) { NinePatchChunk chunkObject = getChunk(chunk); - if (chunkObject != null) { - return true; - } + return chunkObject != null; - return false; } @LayoutlibDelegate - /*package*/ static long validateNinePatchChunk(long bitmap, byte[] chunk) { + /*package*/ static long validateNinePatchChunk(byte[] chunk) { // the default JNI implementation only checks that the byte[] has the same // size as the C struct it represent. Since we cannot do the same check (serialization // will return different size depending on content), we do nothing. @@ -173,7 +170,7 @@ public final class NinePatch_Delegate { } @LayoutlibDelegate - /*package*/ static void nativeDraw(long canvas_instance, RectF loc, long bitmap_instance, + /*package*/ static void nativeDraw(long canvas_instance, RectF loc, Bitmap bitmap_instance, long chunk, long paint_instance_or_null, int destDensity, int srcDensity) { draw(canvas_instance, (int) loc.left, (int) loc.top, (int) loc.right, (int) loc.bottom, @@ -182,7 +179,7 @@ public final class NinePatch_Delegate { } @LayoutlibDelegate - /*package*/ static void nativeDraw(long canvas_instance, Rect loc, long bitmap_instance, + /*package*/ static void nativeDraw(long canvas_instance, Rect loc, Bitmap bitmap_instance, long chunk, long paint_instance_or_null, int destDensity, int srcDensity) { draw(canvas_instance, loc.left, loc.top, loc.right, loc.bottom, @@ -191,7 +188,7 @@ public final class NinePatch_Delegate { } @LayoutlibDelegate - /*package*/ static long nativeGetTransparentRegion(long bitmap, long chunk, Rect location) { + /*package*/ static long nativeGetTransparentRegion(Bitmap bitmap, long chunk, Rect location) { return 0; } @@ -199,7 +196,7 @@ public final class NinePatch_Delegate { private static void draw(long canvas_instance, final int left, final int top, final int right, final int bottom, - long bitmap_instance, long chunk, long paint_instance_or_null, + Bitmap bitmap_instance, long chunk, long paint_instance_or_null, final int destDensity, final int srcDensity) { // get the delegate from the native int. final Bitmap_Delegate bitmap_delegate = Bitmap_Delegate.getDelegate(bitmap_instance); diff --git a/tools/layoutlib/bridge/src/android/graphics/Paint_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/Paint_Delegate.java index 7b07404..3b1e3f9 100644 --- a/tools/layoutlib/bridge/src/android/graphics/Paint_Delegate.java +++ b/tools/layoutlib/bridge/src/android/graphics/Paint_Delegate.java @@ -16,6 +16,8 @@ package android.graphics; +import com.android.annotations.NonNull; +import com.android.annotations.Nullable; import com.android.ide.common.rendering.api.LayoutLog; import com.android.layoutlib.bridge.Bridge; import com.android.layoutlib.bridge.impl.DelegateManager; @@ -83,6 +85,8 @@ public class Paint_Delegate { private float mTextScaleX; private float mTextSkewX; private int mHintingMode = Paint.HINTING_ON; + private int mHyphenEdit; + private float mLetterSpacing; // not used in actual text rendering. // Variant of the font. A paint's variant can only be compact or elegant. private FontVariant mFontVariant = FontVariant.COMPACT; @@ -100,6 +104,7 @@ public class Paint_Delegate { // ---- Public Helper methods ---- + @Nullable public static Paint_Delegate getDelegate(long native_paint) { return sManager.getDelegate(native_paint); } @@ -251,7 +256,7 @@ public class Paint_Delegate { @LayoutlibDelegate /*package*/ static int getFlags(Paint thisPaint) { // get the delegate from the native int. - Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint); + Paint_Delegate delegate = sManager.getDelegate(thisPaint.getNativeInstance()); if (delegate == null) { return 0; } @@ -264,7 +269,7 @@ public class Paint_Delegate { @LayoutlibDelegate /*package*/ static void setFlags(Paint thisPaint, int flags) { // get the delegate from the native int. - Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint); + Paint_Delegate delegate = sManager.getDelegate(thisPaint.getNativeInstance()); if (delegate == null) { return; } @@ -280,7 +285,7 @@ public class Paint_Delegate { @LayoutlibDelegate /*package*/ static int getHinting(Paint thisPaint) { // get the delegate from the native int. - Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint); + Paint_Delegate delegate = sManager.getDelegate(thisPaint.getNativeInstance()); if (delegate == null) { return Paint.HINTING_ON; } @@ -291,7 +296,7 @@ public class Paint_Delegate { @LayoutlibDelegate /*package*/ static void setHinting(Paint thisPaint, int mode) { // get the delegate from the native int. - Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint); + Paint_Delegate delegate = sManager.getDelegate(thisPaint.getNativeInstance()); if (delegate == null) { return; } @@ -337,7 +342,7 @@ public class Paint_Delegate { @LayoutlibDelegate /*package*/ static int getColor(Paint thisPaint) { // get the delegate from the native int. - Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint); + Paint_Delegate delegate = sManager.getDelegate(thisPaint.getNativeInstance()); if (delegate == null) { return 0; } @@ -348,7 +353,7 @@ public class Paint_Delegate { @LayoutlibDelegate /*package*/ static void setColor(Paint thisPaint, int color) { // get the delegate from the native int. - Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint); + Paint_Delegate delegate = sManager.getDelegate(thisPaint.getNativeInstance()); if (delegate == null) { return; } @@ -359,7 +364,7 @@ public class Paint_Delegate { @LayoutlibDelegate /*package*/ static int getAlpha(Paint thisPaint) { // get the delegate from the native int. - Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint); + Paint_Delegate delegate = sManager.getDelegate(thisPaint.getNativeInstance()); if (delegate == null) { return 0; } @@ -370,7 +375,7 @@ public class Paint_Delegate { @LayoutlibDelegate /*package*/ static void setAlpha(Paint thisPaint, int a) { // get the delegate from the native int. - Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint); + Paint_Delegate delegate = sManager.getDelegate(thisPaint.getNativeInstance()); if (delegate == null) { return; } @@ -381,7 +386,7 @@ public class Paint_Delegate { @LayoutlibDelegate /*package*/ static float getStrokeWidth(Paint thisPaint) { // get the delegate from the native int. - Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint); + Paint_Delegate delegate = sManager.getDelegate(thisPaint.getNativeInstance()); if (delegate == null) { return 1.f; } @@ -392,7 +397,7 @@ public class Paint_Delegate { @LayoutlibDelegate /*package*/ static void setStrokeWidth(Paint thisPaint, float width) { // get the delegate from the native int. - Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint); + Paint_Delegate delegate = sManager.getDelegate(thisPaint.getNativeInstance()); if (delegate == null) { return; } @@ -403,7 +408,7 @@ public class Paint_Delegate { @LayoutlibDelegate /*package*/ static float getStrokeMiter(Paint thisPaint) { // get the delegate from the native int. - Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint); + Paint_Delegate delegate = sManager.getDelegate(thisPaint.getNativeInstance()); if (delegate == null) { return 1.f; } @@ -414,7 +419,7 @@ public class Paint_Delegate { @LayoutlibDelegate /*package*/ static void setStrokeMiter(Paint thisPaint, float miter) { // get the delegate from the native int. - Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint); + Paint_Delegate delegate = sManager.getDelegate(thisPaint.getNativeInstance()); if (delegate == null) { return; } @@ -441,14 +446,14 @@ public class Paint_Delegate { @LayoutlibDelegate /*package*/ static boolean isElegantTextHeight(Paint thisPaint) { // get the delegate from the native int. - Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint); + Paint_Delegate delegate = sManager.getDelegate(thisPaint.getNativeInstance()); return delegate != null && delegate.mFontVariant == FontVariant.ELEGANT; } @LayoutlibDelegate /*package*/ static void setElegantTextHeight(Paint thisPaint, boolean elegant) { // get the delegate from the native int. - Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint); + Paint_Delegate delegate = sManager.getDelegate(thisPaint.getNativeInstance()); if (delegate == null) { return; } @@ -459,7 +464,7 @@ public class Paint_Delegate { @LayoutlibDelegate /*package*/ static float getTextSize(Paint thisPaint) { // get the delegate from the native int. - Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint); + Paint_Delegate delegate = sManager.getDelegate(thisPaint.getNativeInstance()); if (delegate == null) { return 1.f; } @@ -470,7 +475,7 @@ public class Paint_Delegate { @LayoutlibDelegate /*package*/ static void setTextSize(Paint thisPaint, float textSize) { // get the delegate from the native int. - Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint); + Paint_Delegate delegate = sManager.getDelegate(thisPaint.getNativeInstance()); if (delegate == null) { return; } @@ -482,7 +487,7 @@ public class Paint_Delegate { @LayoutlibDelegate /*package*/ static float getTextScaleX(Paint thisPaint) { // get the delegate from the native int. - Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint); + Paint_Delegate delegate = sManager.getDelegate(thisPaint.getNativeInstance()); if (delegate == null) { return 1.f; } @@ -493,7 +498,7 @@ public class Paint_Delegate { @LayoutlibDelegate /*package*/ static void setTextScaleX(Paint thisPaint, float scaleX) { // get the delegate from the native int. - Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint); + Paint_Delegate delegate = sManager.getDelegate(thisPaint.getNativeInstance()); if (delegate == null) { return; } @@ -505,7 +510,7 @@ public class Paint_Delegate { @LayoutlibDelegate /*package*/ static float getTextSkewX(Paint thisPaint) { // get the delegate from the native int. - Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint); + Paint_Delegate delegate = sManager.getDelegate(thisPaint.getNativeInstance()); if (delegate == null) { return 1.f; } @@ -516,7 +521,7 @@ public class Paint_Delegate { @LayoutlibDelegate /*package*/ static void setTextSkewX(Paint thisPaint, float skewX) { // get the delegate from the native int. - Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint); + Paint_Delegate delegate = sManager.getDelegate(thisPaint.getNativeInstance()); if (delegate == null) { return; } @@ -528,7 +533,7 @@ public class Paint_Delegate { @LayoutlibDelegate /*package*/ static float ascent(Paint thisPaint) { // get the delegate - Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint); + Paint_Delegate delegate = sManager.getDelegate(thisPaint.getNativeInstance()); if (delegate == null) { return 0; } @@ -545,7 +550,7 @@ public class Paint_Delegate { @LayoutlibDelegate /*package*/ static float descent(Paint thisPaint) { // get the delegate - Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint); + Paint_Delegate delegate = sManager.getDelegate(thisPaint.getNativeInstance()); if (delegate == null) { return 0; } @@ -562,7 +567,7 @@ public class Paint_Delegate { @LayoutlibDelegate /*package*/ static float getFontMetrics(Paint thisPaint, FontMetrics metrics) { // get the delegate - Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint); + Paint_Delegate delegate = sManager.getDelegate(thisPaint.getNativeInstance()); if (delegate == null) { return 0; } @@ -573,7 +578,7 @@ public class Paint_Delegate { @LayoutlibDelegate /*package*/ static int getFontMetricsInt(Paint thisPaint, FontMetricsInt fmi) { // get the delegate - Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint); + Paint_Delegate delegate = sManager.getDelegate(thisPaint.getNativeInstance()); if (delegate == null) { return 0; } @@ -599,7 +604,7 @@ public class Paint_Delegate { /*package*/ static float native_measureText(Paint thisPaint, char[] text, int index, int count, int bidiFlags) { // get the delegate - Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint); + Paint_Delegate delegate = sManager.getDelegate(thisPaint.getNativeInstance()); if (delegate == null) { return 0; } @@ -1088,18 +1093,107 @@ public class Paint_Delegate { @LayoutlibDelegate /*package*/ static float native_getLetterSpacing(long nativePaint) { - // TODO: throw a fidelity warning. - return 0; + Paint_Delegate delegate = sManager.getDelegate(nativePaint); + if (delegate == null) { + return 0; + } + return delegate.mLetterSpacing; } @LayoutlibDelegate /*package*/ static void native_setLetterSpacing(long nativePaint, float letterSpacing) { - // pass. + Bridge.getLog().fidelityWarning(LayoutLog.TAG_TEXT_RENDERING, + "Paint.setLetterSpacing() not supported.", null, null); + Paint_Delegate delegate = sManager.getDelegate(nativePaint); + if (delegate == null) { + return; + } + delegate.mLetterSpacing = letterSpacing; } @LayoutlibDelegate /*package*/ static void native_setFontFeatureSettings(long nativePaint, String settings) { - // pass. + Bridge.getLog().fidelityWarning(LayoutLog.TAG_TEXT_RENDERING, + "Paint.setFontFeatureSettings() not supported.", null, null); + } + + @LayoutlibDelegate + /*package*/ static int native_getHyphenEdit(long nativePaint) { + Paint_Delegate delegate = sManager.getDelegate(nativePaint); + if (delegate == null) { + return 0; + } + return delegate.mHyphenEdit; + } + + @LayoutlibDelegate + /*package*/ static void native_setHyphenEdit(long nativePaint, int hyphen) { + Paint_Delegate delegate = sManager.getDelegate(nativePaint); + if (delegate == null) { + return; + } + delegate.mHyphenEdit = hyphen; + } + + @LayoutlibDelegate + /*package*/ static boolean native_hasGlyph(long nativePaint, long nativeTypeface, int bidiFlags, + String string) { + Paint_Delegate delegate = sManager.getDelegate(nativePaint); + if (delegate == null) { + return false; + } + if (string.length() == 0) { + return false; + } + if (string.length() > 1) { + Bridge.getLog().fidelityWarning(LayoutLog.TAG_TEXT_RENDERING, + "Paint.hasGlyph() is not supported for ligatures.", null, null); + return false; + } + assert nativeTypeface == delegate.mNativeTypeface; + Typeface_Delegate typeface_delegate = Typeface_Delegate.getDelegate(nativeTypeface); + + char c = string.charAt(0); + for (Font font : typeface_delegate.getFonts(delegate.mFontVariant)) { + if (font.canDisplay(c)) { + return true; + } + } + return false; + } + + + @LayoutlibDelegate + /*package*/ static float native_getRunAdvance(long nativePaint, long nativeTypeface, + @NonNull char[] text, int start, int end, int contextStart, int contextEnd, + boolean isRtl, int offset) { + int count = end - start; + float[] advances = new float[count]; + native_getTextRunAdvances(nativePaint, nativeTypeface, text, start, count, + contextStart, contextEnd - contextStart, isRtl, advances, 0); + float sum = 0; + for (int i = 0; i < offset; i++) { + sum += advances[i]; + } + return sum; + } + + @LayoutlibDelegate + /*package*/ static int native_getOffsetForAdvance(long nativePaint, long nativeTypeface, + char[] text, int start, int end, int contextStart, int contextEnd, boolean isRtl, + float advance) { + int count = end - start; + float[] advances = new float[count]; + native_getTextRunAdvances(nativePaint, nativeTypeface, text, start, count, + contextStart, contextEnd - contextStart, isRtl, advances, 0); + float sum = 0; + int i; + for (i = 0; i < count && sum < advance; i++) { + sum += advances[i]; + } + float distanceToI = sum - advance; + float distanceToIMinus1 = advance - (sum - advances[i]); + return distanceToI > distanceToIMinus1 ? i : i - 1; } // ---- Private delegate/helper methods ---- @@ -1137,7 +1231,7 @@ public class Paint_Delegate { } private void reset() { - mFlags = Paint.DEFAULT_PAINT_FLAGS; + mFlags = Paint.HIDDEN_DEFAULT_PAINT_FLAGS; mColor = 0xFF000000; mStyle = Paint.Style.FILL.nativeInt; mCap = Paint.Cap.BUTT.nativeInt; @@ -1232,7 +1326,7 @@ public class Paint_Delegate { private static void setFlag(Paint thisPaint, int flagMask, boolean flagValue) { // get the delegate from the native int. - Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint); + Paint_Delegate delegate = sManager.getDelegate(thisPaint.getNativeInstance()); if (delegate == null) { return; } diff --git a/tools/layoutlib/bridge/src/android/graphics/Shader_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/Shader_Delegate.java index 14e9960..0d491a0 100644 --- a/tools/layoutlib/bridge/src/android/graphics/Shader_Delegate.java +++ b/tools/layoutlib/bridge/src/android/graphics/Shader_Delegate.java @@ -81,14 +81,15 @@ public abstract class Shader_Delegate { } @LayoutlibDelegate - /*package*/ static void nativeSetLocalMatrix(long native_shader, long matrix_instance) { + /*package*/ static long nativeSetLocalMatrix(long native_shader, long matrix_instance) { // get the delegate from the native int. Shader_Delegate shaderDelegate = sManager.getDelegate(native_shader); if (shaderDelegate == null) { - return; + return native_shader; } shaderDelegate.mLocalMatrix = Matrix_Delegate.getDelegate(matrix_instance); + return native_shader; } // ---- Private delegate/helper methods ---- diff --git a/tools/layoutlib/bridge/src/android/text/AndroidBidi_Delegate.java b/tools/layoutlib/bridge/src/android/text/AndroidBidi_Delegate.java index 6247dae..38171dc 100644 --- a/tools/layoutlib/bridge/src/android/text/AndroidBidi_Delegate.java +++ b/tools/layoutlib/bridge/src/android/text/AndroidBidi_Delegate.java @@ -19,8 +19,8 @@ package android.text; import com.android.ide.common.rendering.api.LayoutLog; import com.android.layoutlib.bridge.Bridge; import com.android.tools.layoutlib.annotations.LayoutlibDelegate; -import com.ibm.icu.text.Bidi; +import android.icu.text.Bidi; /** * Delegate used to provide new implementation for the native methods of {@link AndroidBidi} diff --git a/tools/layoutlib/bridge/src/android/text/GreedyLineBreaker.java b/tools/layoutlib/bridge/src/android/text/GreedyLineBreaker.java new file mode 100644 index 0000000..b95cda6 --- /dev/null +++ b/tools/layoutlib/bridge/src/android/text/GreedyLineBreaker.java @@ -0,0 +1,193 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.text; + +import com.android.annotations.NonNull; + +import android.text.Primitive.PrimitiveType; +import android.text.StaticLayout.LineBreaks; + +import java.util.ArrayList; +import java.util.List; + +import static android.text.Primitive.PrimitiveType.PENALTY_INFINITY; + +// Based on the native implementation of GreedyLineBreaker in +// frameworks/base/core/jni/android_text_StaticLayout.cpp revision b808260 +public class GreedyLineBreaker extends LineBreaker { + + public GreedyLineBreaker(@NonNull List<Primitive> primitives, @NonNull LineWidth lineWidth, + @NonNull TabStops tabStops) { + super(primitives, lineWidth, tabStops); + } + + @Override + public void computeBreaks(@NonNull LineBreaks lineBreaks) { + BreakInfo breakInfo = new BreakInfo(); + int lineNum = 0; + float width = 0, printedWidth = 0; + boolean breakFound = false, goodBreakFound = false; + int breakIndex = 0, goodBreakIndex = 0; + float breakWidth = 0, goodBreakWidth = 0; + int firstTabIndex = Integer.MAX_VALUE; + + float maxWidth = mLineWidth.getLineWidth(lineNum); + + int numPrimitives = mPrimitives.size(); + // greedily fit as many characters as possible on each line + // loop over all primitives, and choose the best break point + // (if possible, a break point without splitting a word) + // after going over the maximum length + for (int i = 0; i < numPrimitives; i++) { + Primitive p = mPrimitives.get(i); + + // update the current line width + if (p.type == PrimitiveType.BOX || p.type == PrimitiveType.GLUE) { + width += p.width; + if (p.type == PrimitiveType.BOX) { + printedWidth = width; + } + } else if (p.type == PrimitiveType.VARIABLE) { + width = mTabStops.width(width); + // keep track of first tab character in the region we are examining + // so we can determine whether or not a line contains a tab + firstTabIndex = Math.min(firstTabIndex, i); + } + + // find the best break point for the characters examined so far + if (printedWidth > maxWidth) { + //noinspection StatementWithEmptyBody + if (breakFound || goodBreakFound) { + if (goodBreakFound) { + // a true line break opportunity existed in the characters examined so far, + // so there is no need to split a word + i = goodBreakIndex; // no +1 because of i++ + lineNum++; + maxWidth = mLineWidth.getLineWidth(lineNum); + breakInfo.mBreaksList.add(mPrimitives.get(goodBreakIndex).location); + breakInfo.mWidthsList.add(goodBreakWidth); + breakInfo.mFlagsList.add(firstTabIndex < goodBreakIndex); + firstTabIndex = Integer.MAX_VALUE; + } else { + // must split a word because there is no other option + i = breakIndex; // no +1 because of i++ + lineNum++; + maxWidth = mLineWidth.getLineWidth(lineNum); + breakInfo.mBreaksList.add(mPrimitives.get(breakIndex).location); + breakInfo.mWidthsList.add(breakWidth); + breakInfo.mFlagsList.add(firstTabIndex < breakIndex); + firstTabIndex = Integer.MAX_VALUE; + } + printedWidth = width = 0; + goodBreakFound = breakFound = false; + goodBreakWidth = breakWidth = 0; + continue; + } else { + // no choice, keep going... must make progress by putting at least one + // character on a line, even if part of that character is cut off -- + // there is no other option + } + } + + // update possible break points + if (p.type == PrimitiveType.PENALTY && + p.penalty < PENALTY_INFINITY) { + // this does not handle penalties with width + + // handle forced line break + if (p.penalty == -PENALTY_INFINITY) { + lineNum++; + maxWidth = mLineWidth.getLineWidth(lineNum); + breakInfo.mBreaksList.add(p.location); + breakInfo.mWidthsList.add(printedWidth); + breakInfo.mFlagsList.add(firstTabIndex < i); + firstTabIndex = Integer.MAX_VALUE; + printedWidth = width = 0; + goodBreakFound = breakFound = false; + goodBreakWidth = breakWidth = 0; + continue; + } + if (i > breakIndex && (printedWidth <= maxWidth || !breakFound)) { + breakFound = true; + breakIndex = i; + breakWidth = printedWidth; + } + if (i > goodBreakIndex && printedWidth <= maxWidth) { + goodBreakFound = true; + goodBreakIndex = i; + goodBreakWidth = printedWidth; + } + } else if (p.type == PrimitiveType.WORD_BREAK) { + // only do this if necessary -- we don't want to break words + // when possible, but sometimes it is unavoidable + if (i > breakIndex && (printedWidth <= maxWidth || !breakFound)) { + breakFound = true; + breakIndex = i; + breakWidth = printedWidth; + } + } + } + + if (breakFound || goodBreakFound) { + // output last break if there are more characters to output + if (goodBreakFound) { + breakInfo.mBreaksList.add(mPrimitives.get(goodBreakIndex).location); + breakInfo.mWidthsList.add(goodBreakWidth); + breakInfo.mFlagsList.add(firstTabIndex < goodBreakIndex); + } else { + breakInfo.mBreaksList.add(mPrimitives.get(breakIndex).location); + breakInfo.mWidthsList.add(breakWidth); + breakInfo.mFlagsList.add(firstTabIndex < breakIndex); + } + } + breakInfo.copyTo(lineBreaks); + } + + private static class BreakInfo { + List<Integer> mBreaksList = new ArrayList<Integer>(); + List<Float> mWidthsList = new ArrayList<Float>(); + List<Boolean> mFlagsList = new ArrayList<Boolean>(); + + public void copyTo(LineBreaks lineBreaks) { + if (lineBreaks.breaks.length != mBreaksList.size()) { + lineBreaks.breaks = new int[mBreaksList.size()]; + lineBreaks.widths = new float[mWidthsList.size()]; + lineBreaks.flags = new int[mFlagsList.size()]; + } + + int i = 0; + for (int b : mBreaksList) { + lineBreaks.breaks[i] = b; + i++; + } + i = 0; + for (float b : mWidthsList) { + lineBreaks.widths[i] = b; + i++; + } + i = 0; + for (boolean b : mFlagsList) { + lineBreaks.flags[i] = b ? TAB_MASK : 0; + i++; + } + + mBreaksList = null; + mWidthsList = null; + mFlagsList = null; + } + } +} diff --git a/tools/layoutlib/bridge/src/android/text/Hyphenator_Delegate.java b/tools/layoutlib/bridge/src/android/text/Hyphenator_Delegate.java new file mode 100644 index 0000000..5a59597 --- /dev/null +++ b/tools/layoutlib/bridge/src/android/text/Hyphenator_Delegate.java @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.text; + +import com.android.layoutlib.bridge.impl.DelegateManager; +import com.android.tools.layoutlib.annotations.LayoutlibDelegate; + +import java.io.File; + +/** + * Delegate that overrides implementation for certain methods in {@link android.text.StaticLayout} + * <p/> + * Through the layoutlib_create tool, selected methods of StaticLayout have been replaced + * by calls to methods of the same name in this delegate class. + */ +public class Hyphenator_Delegate { + + private static final DelegateManager<Hyphenator_Delegate> sDelegateManager = new + DelegateManager<Hyphenator_Delegate>(Hyphenator_Delegate.class); + + @LayoutlibDelegate + /*package*/ static File getSystemHyphenatorLocation() { + // FIXME + return null; + } + + /*package*/ static long loadHyphenator(String patternData) { + return sDelegateManager.addNewDelegate(new Hyphenator_Delegate()); + } +} diff --git a/tools/layoutlib/bridge/src/android/text/LineBreaker.java b/tools/layoutlib/bridge/src/android/text/LineBreaker.java new file mode 100644 index 0000000..edeef78 --- /dev/null +++ b/tools/layoutlib/bridge/src/android/text/LineBreaker.java @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.text; + +import android.text.StaticLayout.LineBreaks; +import com.android.annotations.NonNull; + +import java.util.Collections; +import java.util.List; + +// Based on the native implementation of LineBreaker in +// frameworks/base/core/jni/android_text_StaticLayout.cpp revision b808260 +public abstract class LineBreaker { + + protected static final int TAB_MASK = 0x20000000; // keep in sync with StaticLayout + + protected final @NonNull List<Primitive> mPrimitives; + protected final @NonNull LineWidth mLineWidth; + protected final @NonNull TabStops mTabStops; + + public LineBreaker(@NonNull List<Primitive> primitives, @NonNull LineWidth lineWidth, + @NonNull TabStops tabStops) { + mPrimitives = Collections.unmodifiableList(primitives); + mLineWidth = lineWidth; + mTabStops = tabStops; + } + + public abstract void computeBreaks(@NonNull LineBreaks breakInfo); +} diff --git a/tools/layoutlib/bridge/src/android/text/LineWidth.java b/tools/layoutlib/bridge/src/android/text/LineWidth.java new file mode 100644 index 0000000..2ea886d --- /dev/null +++ b/tools/layoutlib/bridge/src/android/text/LineWidth.java @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.text; + +// Based on the native implementation of LineWidth in +// frameworks/base/core/jni/android_text_StaticLayout.cpp revision b808260 +public class LineWidth { + private final float mFirstWidth; + private final int mFirstWidthLineCount; + private float mRestWidth; + + public LineWidth(float firstWidth, int firstWidthLineCount, float restWidth) { + mFirstWidth = firstWidth; + mFirstWidthLineCount = firstWidthLineCount; + mRestWidth = restWidth; + } + + public float getLineWidth(int line) { + return (line < mFirstWidthLineCount) ? mFirstWidth : mRestWidth; + } +} diff --git a/tools/layoutlib/bridge/src/android/text/OptimizingLineBreaker.java b/tools/layoutlib/bridge/src/android/text/OptimizingLineBreaker.java new file mode 100644 index 0000000..cd92581 --- /dev/null +++ b/tools/layoutlib/bridge/src/android/text/OptimizingLineBreaker.java @@ -0,0 +1,262 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.text; + +import com.android.annotations.NonNull; + +import android.text.Primitive.PrimitiveType; +import android.text.StaticLayout.LineBreaks; + +import java.util.ArrayList; +import java.util.List; +import java.util.ListIterator; + +import static android.text.Primitive.PrimitiveType.PENALTY_INFINITY; + + +// Based on the native implementation of OptimizingLineBreaker in +// frameworks/base/core/jni/android_text_StaticLayout.cpp revision b808260 +/** + * A more complex version of line breaking where we try to prevent the right edge from being too + * jagged. + */ +public class OptimizingLineBreaker extends LineBreaker { + + public OptimizingLineBreaker(@NonNull List<Primitive> primitives, @NonNull LineWidth lineWidth, + @NonNull TabStops tabStops) { + super(primitives, lineWidth, tabStops); + } + + @Override + public void computeBreaks(@NonNull LineBreaks breakInfo) { + int numBreaks = mPrimitives.size(); + assert numBreaks > 0; + if (numBreaks == 1) { + // This can be true only if it's an empty paragraph. + Primitive p = mPrimitives.get(0); + assert p.type == PrimitiveType.PENALTY; + breakInfo.breaks = new int[]{0}; + breakInfo.widths = new float[]{p.width}; + breakInfo.flags = new int[]{0}; + return; + } + Node[] opt = new Node[numBreaks]; + opt[0] = new Node(-1, 0, 0, 0, false); + opt[numBreaks - 1] = new Node(-1, 0, 0, 0, false); + + ArrayList<Integer> active = new ArrayList<Integer>(); + active.add(0); + int lastBreak = 0; + for (int i = 0; i < numBreaks; i++) { + Primitive p = mPrimitives.get(i); + if (p.type == PrimitiveType.PENALTY) { + boolean finalBreak = (i + 1 == numBreaks); + Node bestBreak = null; + + for (ListIterator<Integer> it = active.listIterator(); it.hasNext(); + /* incrementing done in loop */) { + int pos = it.next(); + int lines = opt[pos].mPrevCount; + float maxWidth = mLineWidth.getLineWidth(lines); + // we have to compute metrics every time -- + // we can't really pre-compute this stuff and just deal with breaks + // because of the way tab characters work, this makes it computationally + // harder, but this way, we can still optimize while treating tab characters + // correctly + LineMetrics lineMetrics = computeMetrics(pos, i); + if (lineMetrics.mPrintedWidth <= maxWidth) { + float demerits = computeDemerits(maxWidth, lineMetrics.mPrintedWidth, + finalBreak, p.penalty) + opt[pos].mDemerits; + if (bestBreak == null || demerits < bestBreak.mDemerits) { + if (bestBreak == null) { + bestBreak = new Node(pos, opt[pos].mPrevCount + 1, demerits, + lineMetrics.mPrintedWidth, lineMetrics.mHasTabs); + } else { + bestBreak.mPrev = pos; + bestBreak.mPrevCount = opt[pos].mPrevCount + 1; + bestBreak.mDemerits = demerits; + bestBreak.mWidth = lineMetrics.mPrintedWidth; + bestBreak.mHasTabs = lineMetrics.mHasTabs; + } + } + } else { + it.remove(); + } + } + if (p.penalty == -PENALTY_INFINITY) { + active.clear(); + } + if (bestBreak != null) { + opt[i] = bestBreak; + active.add(i); + lastBreak = i; + } + if (active.isEmpty()) { + // we can't give up! + LineMetrics lineMetrics = new LineMetrics(); + int lines = opt[lastBreak].mPrevCount; + float maxWidth = mLineWidth.getLineWidth(lines); + int breakIndex = desperateBreak(lastBreak, numBreaks, maxWidth, lineMetrics); + opt[breakIndex] = new Node(lastBreak, lines + 1, 0 /*doesn't matter*/, + lineMetrics.mWidth, lineMetrics.mHasTabs); + active.add(breakIndex); + lastBreak = breakIndex; + i = breakIndex; // incremented by i++ + } + } + } + + int idx = numBreaks - 1; + int count = opt[idx].mPrevCount; + resize(breakInfo, count); + while (opt[idx].mPrev != -1) { + count--; + assert count >=0; + + breakInfo.breaks[count] = mPrimitives.get(idx).location; + breakInfo.widths[count] = opt[idx].mWidth; + breakInfo.flags [count] = opt[idx].mHasTabs ? TAB_MASK : 0; + idx = opt[idx].mPrev; + } + } + + private static void resize(LineBreaks lineBreaks, int size) { + if (lineBreaks.breaks.length == size) { + return; + } + int[] breaks = new int[size]; + float[] widths = new float[size]; + int[] flags = new int[size]; + + int toCopy = Math.min(size, lineBreaks.breaks.length); + System.arraycopy(lineBreaks.breaks, 0, breaks, 0, toCopy); + System.arraycopy(lineBreaks.widths, 0, widths, 0, toCopy); + System.arraycopy(lineBreaks.flags, 0, flags, 0, toCopy); + + lineBreaks.breaks = breaks; + lineBreaks.widths = widths; + lineBreaks.flags = flags; + } + + @NonNull + private LineMetrics computeMetrics(int start, int end) { + boolean f = false; + float w = 0, pw = 0; + for (int i = start; i < end; i++) { + Primitive p = mPrimitives.get(i); + if (p.type == PrimitiveType.BOX || p.type == PrimitiveType.GLUE) { + w += p.width; + if (p.type == PrimitiveType.BOX) { + pw = w; + } + } else if (p.type == PrimitiveType.VARIABLE) { + w = mTabStops.width(w); + f = true; + } + } + return new LineMetrics(w, pw, f); + } + + private static float computeDemerits(float maxWidth, float width, boolean finalBreak, + float penalty) { + float deviation = finalBreak ? 0 : maxWidth - width; + return (deviation * deviation) + penalty; + } + + /** + * @return the last break position or -1 if failed. + */ + @SuppressWarnings("ConstantConditions") // method too complex to be analyzed. + private int desperateBreak(int start, int limit, float maxWidth, + @NonNull LineMetrics lineMetrics) { + float w = 0, pw = 0; + boolean breakFound = false; + int breakIndex = 0, firstTabIndex = Integer.MAX_VALUE; + for (int i = start; i < limit; i++) { + Primitive p = mPrimitives.get(i); + + if (p.type == PrimitiveType.BOX || p.type == PrimitiveType.GLUE) { + w += p.width; + if (p.type == PrimitiveType.BOX) { + pw = w; + } + } else if (p.type == PrimitiveType.VARIABLE) { + w = mTabStops.width(w); + firstTabIndex = Math.min(firstTabIndex, i); + } + + if (pw > maxWidth && breakFound) { + break; + } + + // must make progress + if (i > start && + (p.type == PrimitiveType.PENALTY || p.type == PrimitiveType.WORD_BREAK)) { + breakFound = true; + breakIndex = i; + } + } + + if (breakFound) { + lineMetrics.mWidth = w; + lineMetrics.mPrintedWidth = pw; + lineMetrics.mHasTabs = (start <= firstTabIndex && firstTabIndex < breakIndex); + return breakIndex; + } else { + return -1; + } + } + + private static class LineMetrics { + /** Actual width of the line. */ + float mWidth; + /** Width of the line minus trailing whitespace. */ + float mPrintedWidth; + boolean mHasTabs; + + public LineMetrics() { + } + + public LineMetrics(float width, float printedWidth, boolean hasTabs) { + mWidth = width; + mPrintedWidth = printedWidth; + mHasTabs = hasTabs; + } + } + + /** + * A struct to store the info about a break. + */ + @SuppressWarnings("SpellCheckingInspection") // For the word struct. + private static class Node { + // -1 for the first node. + int mPrev; + // number of breaks so far. + int mPrevCount; + float mDemerits; + float mWidth; + boolean mHasTabs; + + public Node(int prev, int prevCount, float demerits, float width, boolean hasTabs) { + mPrev = prev; + mPrevCount = prevCount; + mDemerits = demerits; + mWidth = width; + mHasTabs = hasTabs; + } + } +} diff --git a/tools/layoutlib/bridge/src/android/text/Primitive.java b/tools/layoutlib/bridge/src/android/text/Primitive.java new file mode 100644 index 0000000..ce77601 --- /dev/null +++ b/tools/layoutlib/bridge/src/android/text/Primitive.java @@ -0,0 +1,92 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.text; + +import com.android.annotations.NonNull; + +// Based on the native implementation of Primitive in +// frameworks/base/core/jni/android_text_StaticLayout.cpp revision b808260 +public class Primitive { + public final @NonNull PrimitiveType type; + public final int location; + // The following fields don't make sense for all types. + // Box and Glue have width only. + // Penalty has both width and penalty. + // Word_break has penalty only. + public final float width; + public final float penalty; + + /** + * Use {@code PrimitiveType#getNewPrimitive()} + */ + private Primitive(@NonNull PrimitiveType type, int location, float width, float penalty) { + this.type = type; + this.location = location; + this.width = width; + this.penalty = penalty; + } + + public static enum PrimitiveType { + /** + * Something with a constant width that is to be typeset - like a character. + */ + BOX, + /** + * Blank space with fixed width. + */ + GLUE, + /** + * Aesthetic cost indicating how desirable breaking at this point will be. A penalty of + * {@link #PENALTY_INFINITY} means a forced non-break, whereas a penalty of negative + * {@code #PENALTY_INFINITY} means a forced break. + * <p/> + * Currently, it only stores penalty with values 0 or -infinity. + */ + PENALTY, + /** + * For tabs - variable width space. + */ + VARIABLE, + /** + * Possible breakpoints within a word. Think of this as a high cost {@link #PENALTY}. + */ + WORD_BREAK; + + public Primitive getNewPrimitive(int location) { + assert this == VARIABLE; + return new Primitive(this, location, 0f, 0f); + } + + public Primitive getNewPrimitive(int location, float value) { + assert this == BOX || this == GLUE || this == WORD_BREAK; + if (this == BOX || this == GLUE) { + return new Primitive(this, location, value, 0f); + } else { + return new Primitive(this, location, 0f, value); + } + } + + public Primitive getNewPrimitive(int location, float width, float penalty) { + assert this == PENALTY; + return new Primitive(this, location, width, penalty); + } + + // forced non-break, negative infinity is forced break. + public static final float PENALTY_INFINITY = 1e7f; + } +} + diff --git a/tools/layoutlib/bridge/src/android/text/StaticLayout_Delegate.java b/tools/layoutlib/bridge/src/android/text/StaticLayout_Delegate.java index b0d79a8..4226526 100644 --- a/tools/layoutlib/bridge/src/android/text/StaticLayout_Delegate.java +++ b/tools/layoutlib/bridge/src/android/text/StaticLayout_Delegate.java @@ -1,14 +1,22 @@ package android.text; +import com.android.annotations.NonNull; +import com.android.layoutlib.bridge.impl.DelegateManager; import com.android.tools.layoutlib.annotations.LayoutlibDelegate; -import java.text.CharacterIterator; +import android.graphics.BidiRenderer; +import android.graphics.Paint; +import android.graphics.Paint_Delegate; +import android.graphics.RectF; +import android.icu.text.BreakIterator; +import android.icu.util.ULocale; +import android.text.Primitive.PrimitiveType; +import android.text.StaticLayout.LineBreaks; + +import java.util.ArrayList; import java.util.Arrays; -import java.util.Locale; +import java.util.List; -import com.ibm.icu.lang.UCharacter; -import com.ibm.icu.text.BreakIterator; -import com.ibm.icu.util.ULocale; import javax.swing.text.Segment; /** @@ -20,36 +28,206 @@ import javax.swing.text.Segment; */ public class StaticLayout_Delegate { + private static final char CHAR_SPACE = 0x20; + private static final char CHAR_TAB = 0x09; + private static final char CHAR_NEWLINE = 0x0A; + private static final char CHAR_ZWSP = 0x200B; // Zero width space. + + // ---- Builder delegate manager ---- + private static final DelegateManager<Builder> sBuilderManager = + new DelegateManager<Builder>(Builder.class); + + @LayoutlibDelegate + /*package*/ static long nNewBuilder() { + return sBuilderManager.addNewDelegate(new Builder()); + } + + @LayoutlibDelegate + /*package*/ static void nFreeBuilder(long nativeBuilder) { + sBuilderManager.removeJavaReferenceFor(nativeBuilder); + } + + @LayoutlibDelegate + /*package*/ static void nFinishBuilder(long nativeBuilder) { + } + + @LayoutlibDelegate + /*package*/ static long nLoadHyphenator(String patternData) { + return Hyphenator_Delegate.loadHyphenator(patternData); + } + + @LayoutlibDelegate + /*package*/ static void nSetLocale(long nativeBuilder, String locale, long nativeHyphenator) { + Builder builder = sBuilderManager.getDelegate(nativeBuilder); + if (builder != null) { + builder.mLocale = locale; + builder.mNativeHyphenator = nativeHyphenator; + } + } + + @LayoutlibDelegate + /*package*/ static void nSetIndents(long nativeBuilder, int[] indents) { + // TODO. + } + + @LayoutlibDelegate + /*package*/ static void nSetupParagraph(long nativeBuilder, char[] text, int length, + float firstWidth, int firstWidthLineCount, float restWidth, + int[] variableTabStops, int defaultTabStop, int breakStrategy, + int hyphenationFrequency) { + Builder builder = sBuilderManager.getDelegate(nativeBuilder); + if (builder == null) { + return; + } + + builder.mText = text; + builder.mWidths = new float[length]; + builder.mLineWidth = new LineWidth(firstWidth, firstWidthLineCount, restWidth); + builder.mTabStopCalculator = new TabStops(variableTabStops, defaultTabStop); + } + + @LayoutlibDelegate + /*package*/ static float nAddStyleRun(long nativeBuilder, long nativePaint, long nativeTypeface, + int start, int end, boolean isRtl) { + Builder builder = sBuilderManager.getDelegate(nativeBuilder); + + int bidiFlags = isRtl ? Paint.BIDI_FORCE_RTL : Paint.BIDI_FORCE_LTR; + return builder == null ? 0 : + measureText(nativePaint, builder.mText, start, end - start, builder.mWidths, + bidiFlags); + } + + @LayoutlibDelegate + /*package*/ static void nAddMeasuredRun(long nativeBuilder, int start, int end, float[] widths) { + Builder builder = sBuilderManager.getDelegate(nativeBuilder); + if (builder != null) { + System.arraycopy(widths, start, builder.mWidths, start, end - start); + } + } + + @LayoutlibDelegate + /*package*/ static void nAddReplacementRun(long nativeBuilder, int start, int end, float width) { + Builder builder = sBuilderManager.getDelegate(nativeBuilder); + if (builder == null) { + return; + } + builder.mWidths[start] = width; + Arrays.fill(builder.mWidths, start + 1, end, 0.0f); + } + + @LayoutlibDelegate + /*package*/ static void nGetWidths(long nativeBuilder, float[] floatsArray) { + Builder builder = sBuilderManager.getDelegate(nativeBuilder); + if (builder != null) { + System.arraycopy(builder.mWidths, 0, floatsArray, 0, builder.mWidths.length); + } + } + + @LayoutlibDelegate + /*package*/ static int nComputeLineBreaks(long nativeBuilder, + LineBreaks recycle, int[] recycleBreaks, float[] recycleWidths, + int[] recycleFlags, int recycleLength) { + + Builder builder = sBuilderManager.getDelegate(nativeBuilder); + if (builder == null) { + return 0; + } + + // compute all possible breakpoints. + int length = builder.mWidths.length; + BreakIterator it = BreakIterator.getLineInstance(new ULocale(builder.mLocale)); + it.setText(new Segment(builder.mText, 0, length)); + + // average word length in english is 5. So, initialize the possible breaks with a guess. + List<Integer> breaks = new ArrayList<Integer>((int) Math.ceil(length / 5d)); + int loc; + it.first(); + while ((loc = it.next()) != BreakIterator.DONE) { + breaks.add(loc); + } + + List<Primitive> primitives = + computePrimitives(builder.mText, builder.mWidths, length, breaks); + switch (builder.mBreakStrategy) { + case Layout.BREAK_STRATEGY_SIMPLE: + builder.mLineBreaker = new GreedyLineBreaker(primitives, builder.mLineWidth, + builder.mTabStopCalculator); + break; + case Layout.BREAK_STRATEGY_HIGH_QUALITY: + // TODO +// break; + case Layout.BREAK_STRATEGY_BALANCED: + builder.mLineBreaker = new OptimizingLineBreaker(primitives, builder.mLineWidth, + builder.mTabStopCalculator); + break; + default: + throw new AssertionError("Unknown break strategy: " + builder.mBreakStrategy); + } + builder.mLineBreaker.computeBreaks(recycle); + return recycle.breaks.length; + } + /** - * Fills the recycle array with positions that are suitable to break the text at. The array - * must be terminated by '-1'. + * Compute metadata each character - things which help in deciding if it's possible to break + * at a point or not. */ - @LayoutlibDelegate - /*package*/ static int[] nLineBreakOpportunities(String locale, char[] text, int length, - int[] recycle) { - BreakIterator iterator = BreakIterator.getLineInstance(new ULocale(locale)); - Segment segment = new Segment(text, 0, length); - iterator.setText(segment); - if (recycle == null) { - // Because 42 is the answer to everything. - recycle = new int[42]; - } - int breakOpp = iterator.first(); - recycle[0] = breakOpp; - //noinspection ConstantConditions - assert BreakIterator.DONE == -1; - for (int i = 1; breakOpp != BreakIterator.DONE; ++i) { - if (i >= recycle.length) { - recycle = doubleSize(recycle); + @NonNull + private static List<Primitive> computePrimitives(@NonNull char[] text, @NonNull float[] widths, + int length, @NonNull List<Integer> breaks) { + // Initialize the list with a guess of the number of primitives: + // 2 Primitives per non-whitespace char and approx 5 chars per word (i.e. 83% chars) + List<Primitive> primitives = new ArrayList<Primitive>(((int) Math.ceil(length * 1.833))); + int breaksSize = breaks.size(); + int breakIndex = 0; + for (int i = 0; i < length; i++) { + char c = text[i]; + if (c == CHAR_SPACE || c == CHAR_ZWSP) { + primitives.add(PrimitiveType.GLUE.getNewPrimitive(i, widths[i])); + } else if (c == CHAR_TAB) { + primitives.add(PrimitiveType.VARIABLE.getNewPrimitive(i)); + } else if (c != CHAR_NEWLINE) { + while (breakIndex < breaksSize && breaks.get(breakIndex) < i) { + breakIndex++; + } + Primitive p; + if (widths[i] != 0) { + if (breakIndex < breaksSize && breaks.get(breakIndex) == i) { + p = PrimitiveType.PENALTY.getNewPrimitive(i, 0, 0); + } else { + p = PrimitiveType.WORD_BREAK.getNewPrimitive(i, 0); + } + primitives.add(p); + } + + primitives.add(PrimitiveType.BOX.getNewPrimitive(i, widths[i])); } - assert (i < recycle.length); - breakOpp = iterator.next(); - recycle[i] = breakOpp; } - return recycle; + // final break at end of everything + primitives.add( + PrimitiveType.PENALTY.getNewPrimitive(length, 0, -PrimitiveType.PENALTY_INFINITY)); + return primitives; } - private static int[] doubleSize(int[] array) { - return Arrays.copyOf(array, array.length * 2); + private static float measureText(long nativePaint, char []text, int index, int count, + float[] widths, int bidiFlags) { + Paint_Delegate paint = Paint_Delegate.getDelegate(nativePaint); + RectF bounds = new BidiRenderer(null, paint, text) + .renderText(index, index + count, bidiFlags, widths, 0, false); + return bounds.right - bounds.left; + } + + // TODO: Rename to LineBreakerRef and move everything other than LineBreaker to LineBreaker. + /** + * Java representation of the native Builder class. + */ + private static class Builder { + String mLocale; + char[] mText; + float[] mWidths; + LineBreaker mLineBreaker; + long mNativeHyphenator; + int mBreakStrategy; + LineWidth mLineWidth; + TabStops mTabStopCalculator; } } diff --git a/tools/layoutlib/bridge/src/android/text/TabStops.java b/tools/layoutlib/bridge/src/android/text/TabStops.java new file mode 100644 index 0000000..cff6b93 --- /dev/null +++ b/tools/layoutlib/bridge/src/android/text/TabStops.java @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.text; + +import com.android.annotations.Nullable; + +// Based on the native implementation of TabStops in +// frameworks/base/core/jni/android_text_StaticLayout.cpp revision b808260 +public class TabStops { + @Nullable + private int[] mStops; + private final int mTabWidth; + + public TabStops(@Nullable int[] stops, int defaultTabWidth) { + mTabWidth = defaultTabWidth; + mStops = stops; + } + + public float width(float widthSoFar) { + if (mStops != null) { + for (int i : mStops) { + if (i > widthSoFar) { + return i; + } + } + } + // find the next tabStop after widthSoFar. + return (int) ((widthSoFar + mTabWidth) / mTabWidth) * mTabWidth; + } +} diff --git a/tools/layoutlib/bridge/src/android/util/Xml_Delegate.java b/tools/layoutlib/bridge/src/android/util/Xml_Delegate.java index a193330..213e848 100644 --- a/tools/layoutlib/bridge/src/android/util/Xml_Delegate.java +++ b/tools/layoutlib/bridge/src/android/util/Xml_Delegate.java @@ -17,9 +17,9 @@ package android.util; import com.android.layoutlib.bridge.impl.DelegateManager; +import com.android.layoutlib.bridge.impl.ParserFactory; import com.android.tools.layoutlib.annotations.LayoutlibDelegate; -import org.kxml2.io.KXmlParser; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; @@ -37,11 +37,7 @@ public class Xml_Delegate { @LayoutlibDelegate /*package*/ static XmlPullParser newPullParser() { try { - KXmlParser parser = new KXmlParser(); - // The prebuilt kxml2 library with the IDE doesn't support DOCECL. -// parser.setFeature(XmlPullParser.FEATURE_PROCESS_DOCDECL, true); - parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, true); - return parser; + return ParserFactory.instantiateParser(null); } catch (XmlPullParserException e) { throw new AssertionError(); } diff --git a/tools/layoutlib/bridge/src/android/view/BridgeInflater.java b/tools/layoutlib/bridge/src/android/view/BridgeInflater.java index 2e649c3..32ee9e8 100644 --- a/tools/layoutlib/bridge/src/android/view/BridgeInflater.java +++ b/tools/layoutlib/bridge/src/android/view/BridgeInflater.java @@ -53,9 +53,14 @@ public final class BridgeInflater extends LayoutInflater { */ private static final String[] sClassPrefixList = { "android.widget.", - "android.webkit." + "android.webkit.", + "android.app." }; + public static String[] getClassPrefixList() { + return sClassPrefixList; + } + protected BridgeInflater(LayoutInflater original, Context newContext) { super(original, newContext); newContext = getBaseContext(newContext); @@ -127,11 +132,11 @@ public final class BridgeInflater extends LayoutInflater { } @Override - public View createViewFromTag(View parent, String name, AttributeSet attrs, - boolean inheritContext) { + public View createViewFromTag(View parent, String name, Context context, AttributeSet attrs, + boolean ignoreThemeAttrs) { View view; try { - view = super.createViewFromTag(parent, name, attrs, inheritContext); + view = super.createViewFromTag(parent, name, context, attrs, ignoreThemeAttrs); } catch (InflateException e) { // try to load the class from using the custom view loader try { diff --git a/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java b/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java index 5176419..82012c1 100644 --- a/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java +++ b/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java @@ -17,6 +17,7 @@ package android.view; import android.graphics.Point; +import com.android.internal.app.IAssistScreenshotReceiver; import com.android.internal.view.IInputContext; import com.android.internal.view.IInputMethodClient; @@ -215,6 +216,12 @@ public class IWindowManagerImpl implements IWindowManager { } @Override + public void overridePendingAppTransitionClipReveal(int startX, int startY, + int startWidth, int startHeight) throws RemoteException { + // TODO Auto-generated method stub + } + + @Override public void overridePendingAppTransitionThumb(Bitmap srcThumb, int startX, int startY, IRemoteCallback startedCallback, boolean scaleUp) throws RemoteException { // TODO Auto-generated method stub @@ -269,8 +276,15 @@ public class IWindowManagerImpl implements IWindowManager { } @Override - public Bitmap screenshotApplications(IBinder arg0, int displayId, int arg1, - int arg2, boolean arg3) throws RemoteException { + public boolean requestAssistScreenshot(IAssistScreenshotReceiver receiver) + throws RemoteException { + // TODO Auto-generated method stub + return false; + } + + @Override + public Bitmap screenshotApplications(IBinder appToken, int displayId, int maxWidth, + int maxHeight) throws RemoteException { // TODO Auto-generated method stub return null; } @@ -293,7 +307,7 @@ public class IWindowManagerImpl implements IWindowManager { } @Override - public void setAppGroupId(IBinder arg0, int arg1) throws RemoteException { + public void setAppTask(IBinder arg0, int arg1) throws RemoteException { // TODO Auto-generated method stub } @@ -362,6 +376,10 @@ public class IWindowManagerImpl implements IWindowManager { } @Override + public void setForcedDisplayScalingMode(int displayId, int mode) { + } + + @Override public void setInTouchMode(boolean arg0) throws RemoteException { // TODO Auto-generated method stub } diff --git a/tools/layoutlib/bridge/src/android/view/LayoutInflater_Delegate.java b/tools/layoutlib/bridge/src/android/view/LayoutInflater_Delegate.java index 7a73fae..27b406a 100644 --- a/tools/layoutlib/bridge/src/android/view/LayoutInflater_Delegate.java +++ b/tools/layoutlib/bridge/src/android/view/LayoutInflater_Delegate.java @@ -21,9 +21,11 @@ import com.android.tools.layoutlib.annotations.LayoutlibDelegate; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; +import android.content.Context; import android.content.res.TypedArray; import android.content.res.XmlResourceParser; import android.util.AttributeSet; +import android.util.TypedValue; import android.util.Xml; import java.io.IOException; @@ -36,9 +38,13 @@ import java.io.IOException; * */ public class LayoutInflater_Delegate { - private static final String TAG_MERGE = "merge"; + private static final String ATTR_LAYOUT = "layout"; + + private static final int[] ATTRS_THEME = new int[] { + com.android.internal.R.attr.theme }; + public static boolean sIsInInclude = false; /** @@ -49,7 +55,7 @@ public class LayoutInflater_Delegate { */ @LayoutlibDelegate /* package */ static void rInflate(LayoutInflater thisInflater, XmlPullParser parser, - View parent, final AttributeSet attrs, boolean finishInflate, boolean inheritContext) + View parent, Context context, AttributeSet attrs, boolean finishInflate) throws XmlPullParserException, IOException { if (finishInflate == false) { @@ -61,7 +67,7 @@ public class LayoutInflater_Delegate { // ---- START DEFAULT IMPLEMENTATION. - thisInflater.rInflate_Original(parser, parent, attrs, finishInflate, inheritContext); + thisInflater.rInflate_Original(parser, parent, context, attrs, finishInflate); // ---- END DEFAULT IMPLEMENTATION. @@ -74,15 +80,50 @@ public class LayoutInflater_Delegate { } @LayoutlibDelegate - public static void parseInclude(LayoutInflater thisInflater, XmlPullParser parser, View parent, - AttributeSet attrs, boolean inheritContext) throws XmlPullParserException, IOException { - + public static void parseInclude(LayoutInflater thisInflater, XmlPullParser parser, + Context context, View parent, AttributeSet attrs) + throws XmlPullParserException, IOException { int type; if (parent instanceof ViewGroup) { - final int layout = attrs.getAttributeResourceValue(null, "layout", 0); + // Apply a theme wrapper, if requested. This is sort of a weird + // edge case, since developers think the <include> overwrites + // values in the AttributeSet of the included View. So, if the + // included View has a theme attribute, we'll need to ignore it. + final TypedArray ta = context.obtainStyledAttributes(attrs, ATTRS_THEME); + final int themeResId = ta.getResourceId(0, 0); + final boolean hasThemeOverride = themeResId != 0; + if (hasThemeOverride) { + context = new ContextThemeWrapper(context, themeResId); + } + ta.recycle(); + + // If the layout is pointing to a theme attribute, we have to + // massage the value to get a resource identifier out of it. + int layout = attrs.getAttributeResourceValue(null, ATTR_LAYOUT, 0); + if (layout == 0) { + final String value = attrs.getAttributeValue(null, ATTR_LAYOUT); + if (value == null || value.length() <= 0) { + throw new InflateException("You must specify a layout in the" + + " include tag: <include layout=\"@layout/layoutID\" />"); + } + + // Attempt to resolve the "?attr/name" string to an identifier. + layout = context.getResources().getIdentifier(value.substring(1), null, null); + } + + // The layout might be referencing a theme attribute. + // ---- START CHANGES + if (layout != 0) { + final TypedValue tempValue = new TypedValue(); + if (context.getTheme().resolveAttribute(layout, tempValue, true)) { + layout = tempValue.resourceId; + } + } + // ---- END CHANGES + if (layout == 0) { - final String value = attrs.getAttributeValue(null, "layout"); + final String value = attrs.getAttributeValue(null, ATTR_LAYOUT); if (value == null) { throw new InflateException("You must specifiy a layout in the" + " include tag: <include layout=\"@layout/layoutID\" />"); @@ -111,13 +152,20 @@ public class LayoutInflater_Delegate { if (TAG_MERGE.equals(childName)) { // Inflate all children. - thisInflater.rInflate(childParser, parent, childAttrs, false, - inheritContext); + thisInflater.rInflate(childParser, parent, context, childAttrs, false); } else { final View view = thisInflater.createViewFromTag(parent, childName, - childAttrs, inheritContext); + context, childAttrs, hasThemeOverride); final ViewGroup group = (ViewGroup) parent; + final TypedArray a = context.obtainStyledAttributes( + attrs, com.android.internal.R.styleable.Include); + final int id = a.getResourceId( + com.android.internal.R.styleable.Include_id, View.NO_ID); + final int visibility = a.getInt( + com.android.internal.R.styleable.Include_visibility, -1); + a.recycle(); + // We try to load the layout params set in the <include /> tag. If // they don't exist, we will rely on the layout params set in the // included XML file. @@ -133,34 +181,20 @@ public class LayoutInflater_Delegate { // ---- END CHANGES params = group.generateLayoutParams(attrs); - - } catch (RuntimeException e) { - // ---- START CHANGES - sIsInInclude = false; - // ---- END CHANGES - - params = group.generateLayoutParams(childAttrs); + } catch (RuntimeException ignored) { + // Ignore, just fail over to child attrs. } finally { // ---- START CHANGES sIsInInclude = false; // ---- END CHANGES - - if (params != null) { - view.setLayoutParams(params); - } } + if (params == null) { + params = group.generateLayoutParams(childAttrs); + } + view.setLayoutParams(params); // Inflate all children. - thisInflater.rInflate(childParser, view, childAttrs, true, true); - - // Attempt to override the included layout's android:id with the - // one set on the <include /> tag itself. - TypedArray a = thisInflater.mContext.obtainStyledAttributes(attrs, - com.android.internal.R.styleable.View, 0, 0); - int id = a.getResourceId(com.android.internal.R.styleable.View_id, View.NO_ID); - // While we're at it, let's try to override android:visibility. - int visibility = a.getInt(com.android.internal.R.styleable.View_visibility, -1); - a.recycle(); + thisInflater.rInflateChildren(childParser, view, childAttrs, true); if (id != View.NO_ID) { view.setId(id); @@ -188,12 +222,6 @@ public class LayoutInflater_Delegate { throw new InflateException("<include /> can only be used inside of a ViewGroup"); } - final int currentDepth = parser.getDepth(); - while (((type = parser.next()) != XmlPullParser.END_TAG || - parser.getDepth() > currentDepth) && type != XmlPullParser.END_DOCUMENT) { - // Empty - } + LayoutInflater.consumeChildElements(parser); } - - } diff --git a/tools/layoutlib/bridge/src/android/view/ShadowPainter.java b/tools/layoutlib/bridge/src/android/view/ShadowPainter.java index 38846bd..a0db7bf 100644 --- a/tools/layoutlib/bridge/src/android/view/ShadowPainter.java +++ b/tools/layoutlib/bridge/src/android/view/ShadowPainter.java @@ -65,6 +65,9 @@ public class ShadowPainter { @SuppressWarnings({"SuspiciousNameCombination", "UnnecessaryLocalVariable"}) // Imported code public static BufferedImage createDropShadow(BufferedImage source, int shadowSize, float shadowOpacity, int shadowRgb) { + if (shadowSize == 0) { + return source; + } // This code is based on // http://www.jroller.com/gfx/entry/non_rectangular_shadow diff --git a/tools/layoutlib/bridge/src/android/view/ViewGroup_Delegate.java b/tools/layoutlib/bridge/src/android/view/ViewGroup_Delegate.java index e72a0db..51d32e3 100644 --- a/tools/layoutlib/bridge/src/android/view/ViewGroup_Delegate.java +++ b/tools/layoutlib/bridge/src/android/view/ViewGroup_Delegate.java @@ -46,16 +46,12 @@ public class ViewGroup_Delegate { /*package*/ static boolean drawChild(ViewGroup thisVG, Canvas canvas, View child, long drawingTime) { if (child.getZ() > thisVG.getZ()) { + // The background's bounds are set lazily. Make sure they are set correctly so that + // the outline obtained is correct. + child.setBackgroundBounds(); ViewOutlineProvider outlineProvider = child.getOutlineProvider(); - Outline outline = new Outline(); + Outline outline = child.mAttachInfo.mTmpOutline; outlineProvider.getOutline(child, outline); - if (outline.mPath == null && outline.mRect == null) { - // Sometimes, the bounds of the background drawable are not set until View.draw() - // is called. So, we set the bounds manually and try to get the outline again. - child.getBackground().setBounds(0, 0, child.mRight - child.mLeft, - child.mBottom - child.mTop); - outlineProvider.getOutline(child, outline); - } if (outline.mPath != null || (outline.mRect != null && !outline.mRect.isEmpty())) { int restoreTo = transformCanvas(thisVG, canvas, child); drawShadow(thisVG, canvas, child, outline); diff --git a/tools/layoutlib/bridge/src/android/view/View_Delegate.java b/tools/layoutlib/bridge/src/android/view/View_Delegate.java index 8215f7c..408ec54 100644 --- a/tools/layoutlib/bridge/src/android/view/View_Delegate.java +++ b/tools/layoutlib/bridge/src/android/view/View_Delegate.java @@ -16,8 +16,12 @@ package android.view; +import com.android.layoutlib.bridge.android.BridgeContext; import com.android.tools.layoutlib.annotations.LayoutlibDelegate; +import android.content.Context; +import android.os.IBinder; + /** * Delegate used to provide new implementation of a select few methods of {@link View} * @@ -31,4 +35,13 @@ public class View_Delegate { /*package*/ static boolean isInEditMode(View thisView) { return true; } + + @LayoutlibDelegate + /*package*/ static IBinder getWindowToken(View thisView) { + Context baseContext = BridgeContext.getBaseContext(thisView.getContext()); + if (baseContext instanceof BridgeContext) { + return ((BridgeContext) baseContext).getBinder(); + } + return null; + } } diff --git a/tools/layoutlib/bridge/src/android/view/WindowCallback.java b/tools/layoutlib/bridge/src/android/view/WindowCallback.java index 78242a8..d691c8e 100644 --- a/tools/layoutlib/bridge/src/android/view/WindowCallback.java +++ b/tools/layoutlib/bridge/src/android/view/WindowCallback.java @@ -110,6 +110,11 @@ public class WindowCallback implements Window.Callback { } @Override + public boolean onSearchRequested(SearchEvent searchEvent) { + return onSearchRequested(); + } + + @Override public boolean onSearchRequested() { return false; } @@ -120,6 +125,11 @@ public class WindowCallback implements Window.Callback { } @Override + public ActionMode onWindowStartingActionMode(Callback callback, int type) { + return null; + } + + @Override public void onActionModeStarted(ActionMode mode) { } diff --git a/tools/layoutlib/bridge/src/com/android/internal/policy/PolicyManager.java b/tools/layoutlib/bridge/src/com/android/internal/policy/PolicyManager.java deleted file mode 100644 index 6558b6a..0000000 --- a/tools/layoutlib/bridge/src/com/android/internal/policy/PolicyManager.java +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Copyright (C) 2012 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.internal.policy; - -import com.android.ide.common.rendering.api.LayoutLog; -import com.android.layoutlib.bridge.Bridge; -import com.android.layoutlib.bridge.impl.RenderAction; - -import android.content.Context; -import android.view.BridgeInflater; -import android.view.FallbackEventHandler; -import android.view.KeyEvent; -import android.view.LayoutInflater; -import android.view.View; -import android.view.Window; -import android.view.WindowManagerPolicy; - -/** - * Custom implementation of PolicyManager that does nothing to run in LayoutLib. - * - */ -public class PolicyManager { - - public static Window makeNewWindow(Context context) { - // this will likely crash somewhere beyond so we log it. - Bridge.getLog().error(LayoutLog.TAG_UNSUPPORTED, - "Call to PolicyManager.makeNewWindow is not supported", null); - return null; - } - - public static LayoutInflater makeNewLayoutInflater(Context context) { - return new BridgeInflater(context, RenderAction.getCurrentContext().getLayoutlibCallback()); - } - - public static WindowManagerPolicy makeNewWindowManager() { - // this will likely crash somewhere beyond so we log it. - Bridge.getLog().error(LayoutLog.TAG_UNSUPPORTED, - "Call to PolicyManager.makeNewWindowManager is not supported", null); - return null; - } - - public static FallbackEventHandler makeNewFallbackEventHandler(Context context) { - return new FallbackEventHandler() { - @Override - public void setView(View v) { - } - - @Override - public void preDispatchKeyEvent(KeyEvent event) { - } - - @Override - public boolean dispatchKeyEvent(KeyEvent event) { - return false; - } - }; - } -} diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java index c6d60f8..af67a43 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java @@ -16,9 +16,6 @@ package com.android.layoutlib.bridge; -import static com.android.ide.common.rendering.api.Result.Status.ERROR_UNKNOWN; -import static com.android.ide.common.rendering.api.Result.Status.SUCCESS; - import com.android.annotations.NonNull; import com.android.ide.common.rendering.api.Capability; import com.android.ide.common.rendering.api.DrawableParams; @@ -36,13 +33,12 @@ import com.android.resources.ResourceType; import com.android.tools.layoutlib.create.MethodAdapter; import com.android.tools.layoutlib.create.OverrideMethod; import com.android.util.Pair; -import com.ibm.icu.util.ULocale; -import libcore.io.MemoryMappedFile_Delegate; import android.content.res.BridgeAssetManager; import android.graphics.Bitmap; import android.graphics.FontFamily_Delegate; import android.graphics.Typeface_Delegate; +import android.icu.util.ULocale; import android.os.Looper; import android.os.Looper_Accessor; import android.view.View; @@ -60,6 +56,11 @@ import java.util.HashMap; import java.util.Map; import java.util.concurrent.locks.ReentrantLock; +import libcore.io.MemoryMappedFile_Delegate; + +import static com.android.ide.common.rendering.api.Result.Status.ERROR_UNKNOWN; +import static com.android.ide.common.rendering.api.Result.Status.SUCCESS; + /** * Main entry point of the LayoutLib Bridge. * <p/>To use this bridge, simply instantiate an object of type {@link Bridge} and call diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/AndroidLocale.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/AndroidLocale.java index ea5f1ea..e589d9e 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/AndroidLocale.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/AndroidLocale.java @@ -16,9 +16,9 @@ package com.android.layoutlib.bridge.android; -import java.util.Locale; +import android.icu.util.ULocale; -import com.ibm.icu.util.ULocale; +import java.util.Locale; /** * This class provides an alternate implementation for {@code java.util.Locale#toLanguageTag} diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java index 2cbbeba..eded804 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java @@ -16,7 +16,6 @@ package com.android.layoutlib.bridge.android; -import android.os.IBinder; import com.android.annotations.Nullable; import com.android.ide.common.rendering.api.AssetRepository; import com.android.ide.common.rendering.api.ILayoutPullParser; @@ -65,8 +64,12 @@ import android.hardware.display.DisplayManager; import android.net.Uri; import android.os.Bundle; import android.os.Handler; +import android.os.IBinder; +import android.os.IInterface; import android.os.Looper; +import android.os.Parcel; import android.os.PowerManager; +import android.os.RemoteException; import android.os.UserHandle; import android.util.AttributeSet; import android.util.DisplayMetrics; @@ -74,6 +77,7 @@ import android.util.TypedValue; import android.view.BridgeInflater; import android.view.Display; import android.view.DisplayAdjustments; +import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.view.WindowManager; @@ -81,6 +85,7 @@ import android.view.accessibility.AccessibilityManager; import android.view.textservice.TextServicesManager; import java.io.File; +import java.io.FileDescriptor; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; @@ -137,8 +142,10 @@ public final class BridgeContext extends Context { private final Stack<BridgeXmlBlockParser> mParserStack = new Stack<BridgeXmlBlockParser>(); private SharedPreferences mSharedPreferences; + private ClassLoader mClassLoader; + private IBinder mBinder; - /** + /** * @param projectKey An Object identifying the project. This is used for the cache mechanism. * @param metrics the {@link DisplayMetrics}. * @param renderResources the configured resources (both framework and projects) for this @@ -461,7 +468,21 @@ public final class BridgeContext extends Context { @Override public ClassLoader getClassLoader() { - return this.getClass().getClassLoader(); + if (mClassLoader == null) { + mClassLoader = new ClassLoader(getClass().getClassLoader()) { + @Override + protected Class<?> findClass(String name) throws ClassNotFoundException { + for (String prefix : BridgeInflater.getClassPrefixList()) { + if (name.startsWith(prefix)) { + // These are framework classes and should not be loaded from the app. + throw new ClassNotFoundException(name + " not found"); + } + } + return BridgeContext.this.mLayoutlibCallback.findClass(name); + } + }; + } + return mClassLoader; } @Override @@ -499,6 +520,34 @@ public final class BridgeContext extends Context { throw new UnsupportedOperationException("Unsupported Service: " + service); } + @Override + public String getSystemServiceName(Class<?> serviceClass) { + if (serviceClass.equals(LayoutInflater.class)) { + return LAYOUT_INFLATER_SERVICE; + } + + if (serviceClass.equals(TextServicesManager.class)) { + return TEXT_SERVICES_MANAGER_SERVICE; + } + + if (serviceClass.equals(WindowManager.class)) { + return WINDOW_SERVICE; + } + + if (serviceClass.equals(PowerManager.class)) { + return POWER_SERVICE; + } + + if (serviceClass.equals(DisplayManager.class)) { + return DISPLAY_SERVICE; + } + + if (serviceClass.equals(AccessibilityManager.class)) { + return ACCESSIBILITY_SERVICE; + } + + throw new UnsupportedOperationException("Unsupported Service: " + serviceClass); + } @Override public final BridgeTypedArray obtainStyledAttributes(int[] attrs) { @@ -664,44 +713,48 @@ public final class BridgeContext extends Context { } } } else if (defStyleRes != 0) { - boolean isFrameworkRes = true; - Pair<ResourceType, String> value = Bridge.resolveResourceId(defStyleRes); - if (value == null) { - value = mLayoutlibCallback.resolveResourceId(defStyleRes); - isFrameworkRes = false; - } + StyleResourceValue item = mDynamicIdToStyleMap.get(defStyleRes); + if (item != null) { + defStyleValues = item; + } else { + boolean isFrameworkRes = true; + Pair<ResourceType, String> value = Bridge.resolveResourceId(defStyleRes); + if (value == null) { + value = mLayoutlibCallback.resolveResourceId(defStyleRes); + isFrameworkRes = false; + } - if (value != null) { - if ((value.getFirst() == ResourceType.STYLE)) { - // look for the style in all resources: - StyleResourceValue item = mRenderResources.getStyle(value.getSecond(), - isFrameworkRes); - if (item != null) { - if (defaultPropMap != null) { - defaultPropMap.put("style", item.getName()); + if (value != null) { + if ((value.getFirst() == ResourceType.STYLE)) { + // look for the style in all resources: + item = mRenderResources.getStyle(value.getSecond(), isFrameworkRes); + if (item != null) { + if (defaultPropMap != null) { + defaultPropMap.put("style", item.getName()); + } + + defStyleValues = item; + } else { + Bridge.getLog().error(null, + String.format( + "Style with id 0x%x (resolved to '%s') does not exist.", + defStyleRes, value.getSecond()), + null); } - - defStyleValues = item; } else { Bridge.getLog().error(null, String.format( - "Style with id 0x%x (resolved to '%s') does not exist.", - defStyleRes, value.getSecond()), + "Resource id 0x%x is not of type STYLE (instead %s)", + defStyleRes, value.getFirst().toString()), null); } } else { Bridge.getLog().error(null, String.format( - "Resource id 0x%x is not of type STYLE (instead %s)", - defStyleRes, value.getFirst().toString()), + "Failed to find style with id 0x%x in current theme", + defStyleRes), null); } - } else { - Bridge.getLog().error(null, - String.format( - "Failed to find style with id 0x%x in current theme", - defStyleRes), - null); } } @@ -952,6 +1005,61 @@ public final class BridgeContext extends Context { return context; } + public IBinder getBinder() { + if (mBinder == null) { + // create a dummy binder. We only need it be not null. + mBinder = new IBinder() { + @Override + public String getInterfaceDescriptor() throws RemoteException { + return null; + } + + @Override + public boolean pingBinder() { + return false; + } + + @Override + public boolean isBinderAlive() { + return false; + } + + @Override + public IInterface queryLocalInterface(String descriptor) { + return null; + } + + @Override + public void dump(FileDescriptor fd, String[] args) throws RemoteException { + + } + + @Override + public void dumpAsync(FileDescriptor fd, String[] args) throws RemoteException { + + } + + @Override + public boolean transact(int code, Parcel data, Parcel reply, int flags) + throws RemoteException { + return false; + } + + @Override + public void linkToDeath(DeathRecipient recipient, int flags) + throws RemoteException { + + } + + @Override + public boolean unlinkToDeath(DeathRecipient recipient, int flags) { + return false; + } + }; + } + return mBinder; + } + //------------ NOT OVERRIDEN -------------------- @Override @@ -991,6 +1099,12 @@ public final class BridgeContext extends Context { } @Override + public int checkSelfPermission(String arg0) { + // pass + return 0; + } + + @Override public int checkPermission(String arg0, int arg1, int arg2, IBinder arg3) { // pass return 0; @@ -1370,6 +1484,11 @@ public final class BridgeContext extends Context { // pass } + public void sendBroadcastAsUser(Intent intent, UserHandle user, + String receiverPermission, int appOp) { + // pass + } + @Override public void sendOrderedBroadcastAsUser(Intent intent, UserHandle user, String receiverPermission, BroadcastReceiver resultReceiver, Handler scheduler, diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeIInputMethodManager.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeIInputMethodManager.java index c44a57c..8899e53 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeIInputMethodManager.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeIInputMethodManager.java @@ -164,7 +164,8 @@ public class BridgeIInputMethodManager implements IInputMethodManager { } @Override - public void showInputMethodPickerFromClient(IInputMethodClient arg0) throws RemoteException { + public void showInputMethodPickerFromClient(IInputMethodClient arg0, + int arg1) throws RemoteException { // TODO Auto-generated method stub } diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgePowerManager.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgePowerManager.java index 9b755cd..085df85 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgePowerManager.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgePowerManager.java @@ -147,6 +147,11 @@ public class BridgePowerManager implements IPowerManager { } @Override + public boolean isDeviceIdleMode() throws RemoteException { + return false; + } + + @Override public boolean isScreenBrightnessBoosted() throws RemoteException { return false; } diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindow.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindow.java index fb5d44f..771c3c8 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindow.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindow.java @@ -91,7 +91,11 @@ public final class BridgeWindow implements IWindow { } @Override - public void doneAnimating() { + public void onAnimationStarted(int remainingFrameCount) { + } + + @Override + public void onAnimationStopped() { } @Override diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindowSession.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindowSession.java index 25f7078..4289689 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindowSession.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindowSession.java @@ -191,12 +191,6 @@ public final class BridgeWindowSession implements IWindowSession { } @Override - public void setUniverseTransform(IBinder window, float alpha, float offx, float offy, - float dsdx, float dtdx, float dsdy, float dtdy) { - // pass for now. - } - - @Override public IBinder asBinder() { // pass for now. return null; diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/NavigationBar.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/NavigationBar.java index 04aadff..dcf82a3 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/NavigationBar.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/NavigationBar.java @@ -24,6 +24,7 @@ import org.xmlpull.v1.XmlPullParserException; import android.content.Context; import android.content.pm.ApplicationInfo; import android.util.AttributeSet; +import android.util.DisplayMetrics; import android.view.View; import android.widget.LinearLayout; import android.widget.TextView; @@ -32,6 +33,17 @@ public class NavigationBar extends CustomBar { /** Navigation bar background color attribute name. */ private static final String ATTR_COLOR = "navigationBarColor"; + // These correspond to @dimen/navigation_side_padding in the system ui code. + private static final int PADDING_WIDTH_DEFAULT = 36; + private static final int PADDING_WIDTH_SW360 = 40; + private static final int PADDING_WIDTH_SW400 = 50; + // These corresponds to @dimen/navigation_key_width in the system ui code. + private static final int WIDTH_DEFAULT = 36; + private static final int WIDTH_SW360 = 40; + private static final int WIDTH_SW600 = 48; + private static final String LAYOUT_XML = "/bars/navigation_bar.xml"; + private static final String LAYOUT_600DP_XML = "/bars/navigation_bar600dp.xml"; + /** * Constructor to be used when creating the {@link NavigationBar} as a regular control. @@ -45,13 +57,13 @@ public class NavigationBar extends CustomBar { ((BridgeContext) context).getConfiguration().getLayoutDirection() == View.LAYOUT_DIRECTION_RTL, (context.getApplicationInfo().flags & ApplicationInfo.FLAG_SUPPORTS_RTL) != 0, - context.getApplicationInfo().targetSdkVersion); + 0); } public NavigationBar(BridgeContext context, Density density, int orientation, boolean isRtl, boolean rtlEnabled, int simulatedPlatformVersion) throws XmlPullParserException { - super(context, orientation, "/bars/navigation_bar.xml", "navigation_bar.xml", - simulatedPlatformVersion); + super(context, orientation, getShortestWidth(context)>= 600 ? LAYOUT_600DP_XML : LAYOUT_XML, + "navigation_bar.xml", simulatedPlatformVersion); int color = getThemeAttrColor(ATTR_COLOR, true); setBackgroundColor(color == 0 ? 0xFF000000 : color); @@ -61,19 +73,76 @@ public class NavigationBar extends CustomBar { // We do know the order though. // 0 is a spacer. int back = 1; - int recent = 3; + int recent = 5; if (orientation == LinearLayout.VERTICAL || (isRtl && !rtlEnabled)) { // If RTL is enabled, then layoutlib mirrors the layout for us. - back = 3; + back = 5; recent = 1; } //noinspection SpellCheckingInspection - loadIcon(back, "ic_sysbar_back.png", density, isRtl); + loadIcon(back, "ic_sysbar_back.png", density, isRtl); //noinspection SpellCheckingInspection - loadIcon(2, "ic_sysbar_home.png", density, isRtl); + loadIcon(3, "ic_sysbar_home.png", density, isRtl); //noinspection SpellCheckingInspection loadIcon(recent, "ic_sysbar_recent.png", density, isRtl); + setupNavBar(context, orientation); + } + + private void setupNavBar(BridgeContext context, int orientation) { + float sw = getShortestWidth(context); + View leftPadding = getChildAt(0); + View rightPadding = getChildAt(6); + setSize(context, leftPadding, orientation, getSidePadding(sw)); + setSize(context, rightPadding, orientation, getSidePadding(sw)); + int navButtonWidth = getWidth(sw); + for (int i = 1; i < 6; i += 2) { + View navButton = getChildAt(i); + setSize(context, navButton, orientation, navButtonWidth); + } + if (sw >= 600) { + setSize(context, getChildAt(2), orientation, 128); + setSize(context, getChildAt(4), orientation, 128); + } + } + + private static void setSize(BridgeContext context, View view, int orientation, int size) { + size *= context.getMetrics().density; + LayoutParams layoutParams = (LayoutParams) view.getLayoutParams(); + if (orientation == HORIZONTAL) { + layoutParams.width = size; + } else { + layoutParams.height = size; + } + view.setLayoutParams(layoutParams); + } + + private static int getSidePadding(float sw) { + if (sw >= 400) { + return PADDING_WIDTH_SW400; + } + if (sw >= 360) { + return PADDING_WIDTH_SW360; + } + return PADDING_WIDTH_DEFAULT; + } + + private static int getWidth(float sw) { + if (sw >= 600) { + return WIDTH_SW600; + } + if (sw >= 360) { + return WIDTH_SW360; + } + return WIDTH_DEFAULT; + } + + private static float getShortestWidth(BridgeContext context) { + DisplayMetrics metrics = context.getMetrics(); + float sw = metrics.widthPixels < metrics.heightPixels ? + metrics.widthPixels : metrics.heightPixels; + sw /= metrics.density; + return sw; } @Override diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/DelegateManager.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/DelegateManager.java index 261cc98..dbee9ea 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/DelegateManager.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/DelegateManager.java @@ -16,6 +16,7 @@ package com.android.layoutlib.bridge.impl; +import com.android.annotations.Nullable; import com.android.layoutlib.bridge.util.Debug; import com.android.layoutlib.bridge.util.SparseWeakArray; @@ -48,7 +49,7 @@ import java.util.List; * int -> Delegate class link. * * Native methods usually always have the int as parameters. The first thing the delegate method - * will do is call {@link #getDelegate(int)} to get the Java object matching the int. + * will do is call {@link #getDelegate(long)} to get the Java object matching the int. * * Typical native init methods are returning a new int back to the Java class, so * {@link #addNewDelegate(Object)} does the same. @@ -57,7 +58,7 @@ import java.util.List; * the Java object needs to count as a reference (even though it only holds an int), we use the * following mechanism: * - * - {@link #addNewDelegate(Object)} and {@link #removeJavaReferenceFor(int)} adds and removes + * - {@link #addNewDelegate(Object)} and {@link #removeJavaReferenceFor(long)} adds and removes * the delegate to/from a list. This list hold the reference and prevents the GC from reclaiming * the delegate. * @@ -70,12 +71,13 @@ import java.util.List; * @param <T> the delegate class to manage */ public final class DelegateManager<T> { + @SuppressWarnings("FieldCanBeLocal") private final Class<T> mClass; private final SparseWeakArray<T> mDelegates = new SparseWeakArray<T>(); /** list used to store delegates when their main object holds a reference to them. * This is to ensure that the WeakReference in the SparseWeakArray doesn't get GC'ed * @see #addNewDelegate(Object) - * @see #removeJavaReferenceFor(int) + * @see #removeJavaReferenceFor(long) */ private final List<T> mJavaReferences = new ArrayList<T>(); private int mDelegateCounter = 0; @@ -94,6 +96,7 @@ public final class DelegateManager<T> { * @param native_object the native int. * @return the delegate or null if not found. */ + @Nullable public T getDelegate(long native_object) { if (native_object > 0) { T delegate = mDelegates.get(native_object); diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/ParserFactory.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/ParserFactory.java index 803849f..646f960 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/ParserFactory.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/ParserFactory.java @@ -17,7 +17,9 @@ package com.android.layoutlib.bridge.impl; -import org.kxml2.io.KXmlParser; +import com.android.annotations.NonNull; +import com.android.annotations.Nullable; + import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; @@ -35,24 +37,36 @@ import java.io.InputStream; */ public class ParserFactory { + public final static boolean LOG_PARSER = false; + private final static String ENCODING = "UTF-8"; //$NON-NLS-1$ - public final static boolean LOG_PARSER = false; + // Used to get a new XmlPullParser from the client. + @Nullable + private static com.android.ide.common.rendering.api.ParserFactory sParserFactory; - public static XmlPullParser create(File f) + public static void setParserFactory( + @Nullable com.android.ide.common.rendering.api.ParserFactory parserFactory) { + sParserFactory = parserFactory; + } + + @NonNull + public static XmlPullParser create(@NonNull File f) throws XmlPullParserException, FileNotFoundException { InputStream stream = new FileInputStream(f); return create(stream, f.getName(), f.length()); } - public static XmlPullParser create(InputStream stream, String name) + @NonNull + public static XmlPullParser create(@NonNull InputStream stream, @Nullable String name) throws XmlPullParserException { return create(stream, name, -1); } - private static XmlPullParser create(InputStream stream, String name, long size) - throws XmlPullParserException { - KXmlParser parser = instantiateParser(name); + @NonNull + private static XmlPullParser create(@NonNull InputStream stream, @Nullable String name, + long size) throws XmlPullParserException { + XmlPullParser parser = instantiateParser(name); stream = readAndClose(stream, name, size); @@ -60,19 +74,20 @@ public class ParserFactory { return parser; } - private static KXmlParser instantiateParser(String name) throws XmlPullParserException { - KXmlParser parser; - if (name != null) { - parser = new CustomParser(name); - } else { - parser = new KXmlParser(); + @NonNull + public static XmlPullParser instantiateParser(@Nullable String name) + throws XmlPullParserException { + if (sParserFactory == null) { + throw new XmlPullParserException("ParserFactory not initialized."); } + XmlPullParser parser = sParserFactory.createParser(name); parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, true); return parser; } - private static InputStream readAndClose(InputStream stream, String name, long size) - throws XmlPullParserException { + @NonNull + private static InputStream readAndClose(@NonNull InputStream stream, @Nullable String name, + long size) throws XmlPullParserException { // just a sanity check. It's doubtful we'll have such big files! if (size > Integer.MAX_VALUE) { throw new XmlPullParserException("File " + name + " is too big to be parsed"); @@ -121,22 +136,8 @@ public class ParserFactory { } finally { try { bufferedStream.close(); - } catch (IOException e) { + } catch (IOException ignored) { } } } - - private static class CustomParser extends KXmlParser { - private final String mName; - - CustomParser(String name) { - super(); - mName = name; - } - - @Override - public String toString() { - return mName; - } - } } diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderAction.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderAction.java index c708316..f3a0d58 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderAction.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderAction.java @@ -99,6 +99,9 @@ public abstract class RenderAction<T extends RenderParams> extends FrameworkReso return result; } + // setup the ParserFactory + ParserFactory.setParserFactory(mParams.getLayoutlibCallback().getParserFactory()); + HardwareConfig hardwareConfig = mParams.getHardwareConfig(); // setup the display Metrics. @@ -271,7 +274,7 @@ public abstract class RenderAction<T extends RenderParams> extends FrameworkReso mContext.getRenderResources().setFrameworkResourceIdProvider(null); mContext.getRenderResources().setLogger(null); } - + ParserFactory.setParserFactory(null); } public static BridgeContext getCurrentContext() { diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderDrawable.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderDrawable.java index 9e26502..26f9000 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderDrawable.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderDrawable.java @@ -38,6 +38,9 @@ import java.awt.AlphaComposite; import java.awt.Color; import java.awt.Graphics2D; import java.awt.image.BufferedImage; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; import java.util.ArrayList; import java.util.Collections; diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/ResourceHelper.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/ResourceHelper.java index 677c744..a3fde866 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/ResourceHelper.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/ResourceHelper.java @@ -16,6 +16,7 @@ package com.android.layoutlib.bridge.impl; +import com.android.SdkConstants; import com.android.annotations.NonNull; import com.android.ide.common.rendering.api.DensityBasedResourceValue; import com.android.ide.common.rendering.api.LayoutLog; @@ -70,6 +71,10 @@ public final class ResourceHelper { public static int getColor(String value) { if (value != null) { if (!value.startsWith("#")) { + if (value.startsWith(SdkConstants.PREFIX_THEME_REF)) { + throw new NumberFormatException(String.format( + "Attribute '%s' not found. Are you using the right theme?", value)); + } throw new NumberFormatException( String.format("Color value '%s' must start with #", value)); } diff --git a/tools/layoutlib/bridge/src/libcore/icu/DateIntervalFormat_Delegate.java b/tools/layoutlib/bridge/src/libcore/icu/DateIntervalFormat_Delegate.java deleted file mode 100644 index d94c205..0000000 --- a/tools/layoutlib/bridge/src/libcore/icu/DateIntervalFormat_Delegate.java +++ /dev/null @@ -1,74 +0,0 @@ -/* - * Copyright (C) 2013 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package libcore.icu; - -import java.text.FieldPosition; - -import com.android.ide.common.rendering.api.LayoutLog; -import com.android.layoutlib.bridge.Bridge; -import com.android.layoutlib.bridge.impl.DelegateManager; -import com.android.tools.layoutlib.annotations.LayoutlibDelegate; -import com.ibm.icu.text.DateIntervalFormat; -import com.ibm.icu.util.DateInterval; -import com.ibm.icu.util.TimeZone; -import com.ibm.icu.util.ULocale; - -public class DateIntervalFormat_Delegate { - - // ---- delegate manager ---- - private static final DelegateManager<DateIntervalFormat_Delegate> sManager = - new DelegateManager<DateIntervalFormat_Delegate>(DateIntervalFormat_Delegate.class); - - // ---- delegate data ---- - private DateIntervalFormat mFormat; - - - // ---- native methods ---- - - @LayoutlibDelegate - /*package*/static String formatDateInterval(long address, long fromDate, long toDate) { - DateIntervalFormat_Delegate delegate = sManager.getDelegate((int)address); - if (delegate == null) { - Bridge.getLog().error(LayoutLog.TAG_BROKEN, - "Unable for find native DateIntervalFormat", null); - return null; - } - DateInterval interval = new DateInterval(fromDate, toDate); - StringBuffer sb = new StringBuffer(); - FieldPosition pos = new FieldPosition(0); - delegate.mFormat.format(interval, sb, pos); - return sb.toString(); - } - - @LayoutlibDelegate - /*package*/ static long createDateIntervalFormat(String skeleton, String localeName, - String tzName) { - TimeZone prevDefaultTz = TimeZone.getDefault(); - TimeZone.setDefault(TimeZone.getTimeZone(tzName)); - DateIntervalFormat_Delegate newDelegate = new DateIntervalFormat_Delegate(); - newDelegate.mFormat = - DateIntervalFormat.getInstance(skeleton, new ULocale(localeName)); - TimeZone.setDefault(prevDefaultTz); - return sManager.addNewDelegate(newDelegate); - } - - @LayoutlibDelegate - /*package*/ static void destroyDateIntervalFormat(long address) { - sManager.removeJavaReferenceFor((int)address); - } - -} diff --git a/tools/layoutlib/bridge/src/libcore/icu/ICU_Delegate.java b/tools/layoutlib/bridge/src/libcore/icu/ICU_Delegate.java index b8b5fed..9c58010 100644 --- a/tools/layoutlib/bridge/src/libcore/icu/ICU_Delegate.java +++ b/tools/layoutlib/bridge/src/libcore/icu/ICU_Delegate.java @@ -17,9 +17,11 @@ package libcore.icu; import com.android.tools.layoutlib.annotations.LayoutlibDelegate; -import com.ibm.icu.text.DateTimePatternGenerator; -import com.ibm.icu.util.Currency; -import com.ibm.icu.util.ULocale; + +import android.icu.text.DateTimePatternGenerator; +import android.icu.util.Currency; +import android.icu.util.ULocale; +import android.icu.util.VersionInfo; import java.util.Locale; @@ -53,18 +55,19 @@ public class ICU_Delegate { } @LayoutlibDelegate + @SuppressWarnings("deprecation") /*package*/ static String getCldrVersion() { - return "22.1.1"; // TODO: check what the right value should be. + return VersionInfo.ICU_DATA_VERSION.toString(); } @LayoutlibDelegate /*package*/ static String getIcuVersion() { - return "unknown_layoutlib"; + return VersionInfo.ICU_VERSION.toString(); } @LayoutlibDelegate /*package*/ static String getUnicodeVersion() { - return "5.2"; + return VersionInfo.UNICODE_7_0.toString(); } @LayoutlibDelegate @@ -181,8 +184,8 @@ public class ICU_Delegate { /*package*/ static boolean initLocaleDataNative(String locale, LocaleData result) { // Used by Calendar. - result.firstDayOfWeek = Integer.valueOf(1); - result.minimalDaysInFirstWeek = Integer.valueOf(1); + result.firstDayOfWeek = 1; + result.minimalDaysInFirstWeek = 1; // Used by DateFormatSymbols. result.amPm = new String[] { "AM", "PM" }; @@ -252,4 +255,9 @@ public class ICU_Delegate { /*package*/ static String getDefaultLocale() { return ICU.getDefaultLocale(); } + + @LayoutlibDelegate + /*package*/ static String getTZDataVersion() { + return ICU.getTZDataVersion(); + } } |