diff options
Diffstat (limited to 'tools/layoutlib/bridge')
19 files changed, 981 insertions, 591 deletions
diff --git a/tools/layoutlib/bridge/src/android/graphics/Bitmap.java b/tools/layoutlib/bridge/src/android/graphics/Bitmap.java index 7dde634..ff1b295 100644 --- a/tools/layoutlib/bridge/src/android/graphics/Bitmap.java +++ b/tools/layoutlib/bridge/src/android/graphics/Bitmap.java @@ -28,13 +28,13 @@ public final class Bitmap extends _Original_Bitmap { private BufferedImage mImage; public Bitmap(File input) throws IOException { - super(1, true, null); + super(1, true, null, -1); mImage = ImageIO.read(input); } Bitmap(BufferedImage image) { - super(1, true, null); + super(1, true, null, -1); mImage = image; } diff --git a/tools/layoutlib/bridge/src/android/graphics/Canvas.java b/tools/layoutlib/bridge/src/android/graphics/Canvas.java index 3fa1d1d..4986c77 100644 --- a/tools/layoutlib/bridge/src/android/graphics/Canvas.java +++ b/tools/layoutlib/bridge/src/android/graphics/Canvas.java @@ -26,6 +26,7 @@ import android.graphics.RectF; import android.graphics.Region; import android.graphics.Xfermode; import android.graphics.Paint.Align; +import android.graphics.Paint.FontInfo; import android.graphics.Paint.Style; import android.graphics.Region.Op; @@ -37,6 +38,7 @@ import java.awt.Rectangle; import java.awt.RenderingHints; import java.awt.geom.AffineTransform; import java.awt.image.BufferedImage; +import java.util.List; import java.util.Stack; import javax.microedition.khronos.opengles.GL; @@ -620,19 +622,21 @@ public class Canvas extends _Original_Canvas { */ @Override public void drawText(char[] text, int index, int count, float x, float y, Paint paint) { + // WARNING: the logic in this method is similar to Paint.measureText. + // Any change to this method should be reflected in Paint.measureText Graphics2D g = getGraphics2d(); g = (Graphics2D)g.create(); g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); - g.setFont(paint.getFont()); - - // set the color. because this only handles RGB we have to handle the alpha separately + // set the color. because this only handles RGB, the alpha channel is handled + // as a composite. g.setColor(new Color(paint.getColor())); int alpha = paint.getAlpha(); float falpha = alpha / 255.f; g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, falpha)); + // Paint.TextAlign indicates how the text is positioned relative to X. // LEFT is the default and there's nothing to do. if (paint.getTextAlign() != Align.LEFT) { @@ -644,9 +648,83 @@ public class Canvas extends _Original_Canvas { } } - g.drawChars(text, index, count, (int)x, (int)y); - - g.dispose(); + List<FontInfo> fonts = paint.getFonts(); + try { + if (fonts.size() > 0) { + FontInfo mainFont = fonts.get(0); + int i = index; + int lastIndex = index + count; + while (i < lastIndex) { + // always start with the main font. + int upTo = mainFont.mFont.canDisplayUpTo(text, i, lastIndex); + if (upTo == -1) { + // draw all the rest and exit. + g.setFont(mainFont.mFont); + g.drawChars(text, i, lastIndex - i, (int)x, (int)y); + return; + } else if (upTo > 0) { + // draw what's possible + g.setFont(mainFont.mFont); + g.drawChars(text, i, upTo - i, (int)x, (int)y); + + // compute the width that was drawn to increase x + x += mainFont.mMetrics.charsWidth(text, i, upTo - i); + + // move index to the first non displayed char. + i = upTo; + + // don't call continue at this point. Since it is certain the main font + // cannot display the font a index upTo (now ==i), we move on to the + // fallback fonts directly. + } + + // no char supported, attempt to read the next char(s) with the + // fallback font. In this case we only test the first character + // and then go back to test with the main font. + // Special test for 2-char characters. + boolean foundFont = false; + for (int f = 1 ; f < fonts.size() ; f++) { + FontInfo fontInfo = fonts.get(f); + + // need to check that the font can display the character. We test + // differently if the char is a high surrogate. + int charCount = Character.isHighSurrogate(text[i]) ? 2 : 1; + upTo = fontInfo.mFont.canDisplayUpTo(text, i, i + charCount); + if (upTo == -1) { + // draw that char + g.setFont(fontInfo.mFont); + g.drawChars(text, i, charCount, (int)x, (int)y); + + // update x + x += fontInfo.mMetrics.charsWidth(text, i, charCount); + + // update the index in the text, and move on + i += charCount; + foundFont = true; + break; + + } + } + + // in case no font can display the char, display it with the main font. + // (it'll put a square probably) + if (foundFont == false) { + int charCount = Character.isHighSurrogate(text[i]) ? 2 : 1; + + g.setFont(mainFont.mFont); + g.drawChars(text, i, charCount, (int)x, (int)y); + + // measure it to advance x + x += mainFont.mMetrics.charsWidth(text, i, charCount); + + // and move to the next chars. + i += charCount; + } + } + } + } finally { + g.dispose(); + } } /* (non-Javadoc) diff --git a/tools/layoutlib/bridge/src/android/graphics/Paint.java b/tools/layoutlib/bridge/src/android/graphics/Paint.java index ade07d6..86de56b 100644 --- a/tools/layoutlib/bridge/src/android/graphics/Paint.java +++ b/tools/layoutlib/bridge/src/android/graphics/Paint.java @@ -26,6 +26,9 @@ import java.awt.Toolkit; import java.awt.font.FontRenderContext; import java.awt.geom.AffineTransform; import java.awt.geom.Rectangle2D; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; /** * A paint implementation overridden by the LayoutLib bridge. @@ -33,17 +36,28 @@ import java.awt.geom.Rectangle2D; public class Paint extends _Original_Paint { private int mColor = 0xFFFFFFFF; + private float mStrokeWidth = 1.f; private float mTextSize = 20; private float mScaleX = 1; private float mSkewX = 0; private Align mAlign = Align.LEFT; private Style mStyle = Style.FILL; + private float mStrokeMiter = 4.0f; + private Cap mCap = Cap.BUTT; + private Join mJoin = Join.MITER; private int mFlags = 0; - - private Font mFont; + + /** + * Class associating a {@link Font} and it's {@link java.awt.FontMetrics}. + */ + public static final class FontInfo { + Font mFont; + java.awt.FontMetrics mMetrics; + } + + private List<FontInfo> mFonts; private final FontRenderContext mFontContext = new FontRenderContext( new AffineTransform(), true, true); - private java.awt.FontMetrics mMetrics; @SuppressWarnings("hiding") public static final int ANTI_ALIAS_FLAG = _Original_Paint.ANTI_ALIAS_FLAG; @@ -65,11 +79,11 @@ public class Paint extends _Original_Paint { public static final int DEV_KERN_TEXT_FLAG = _Original_Paint.DEV_KERN_TEXT_FLAG; public static class FontMetrics extends _Original_Paint.FontMetrics { - } + } public static class FontMetricsInt extends _Original_Paint.FontMetricsInt { } - + /** * The Style specifies if the primitive being drawn is filled, * stroked, or both (in the same color). The default is FILL. @@ -91,7 +105,7 @@ public class Paint extends _Original_Paint { * the paint. */ FILL_AND_STROKE (2); - + Style(int nativeInt) { this.nativeInt = nativeInt; } @@ -117,7 +131,7 @@ public class Paint extends _Original_Paint { * end of the path. */ SQUARE (2); - + private Cap(int nativeInt) { this.nativeInt = nativeInt; } @@ -141,7 +155,7 @@ public class Paint extends _Original_Paint { * The outer edges of a join meet with a straight line */ BEVEL (2); - + private Join(int nativeInt) { this.nativeInt = nativeInt; } @@ -165,7 +179,7 @@ public class Paint extends _Original_Paint { * The text is drawn to the left of the x,y origin */ RIGHT (2); - + private Align(int nativeInt) { this.nativeInt = nativeInt; } @@ -185,43 +199,61 @@ public class Paint extends _Original_Paint { set(paint); initFont(); } - + @Override public void finalize() throws Throwable { // pass } - + + @Override + public void reset() { + super.reset(); + } + /** - * Returns the {@link Font} object. + * Returns the list of {@link Font} objects. The first item is the main font, the rest + * are fall backs for characters not present in the main font. */ - public Font getFont() { - return mFont; + public List<FontInfo> getFonts() { + return mFonts; } - + private void initFont() { mTypeface = Typeface.DEFAULT; updateFontObject(); } - + /** * Update the {@link Font} object from the typeface, text size and scaling */ + @SuppressWarnings("deprecation") private void updateFontObject() { if (mTypeface != null) { - // get the typeface font object, and get our font object from it, based on the current size - mFont = mTypeface.getFont().deriveFont(mTextSize); - if (mScaleX != 1.0 || mSkewX != 0) { - // TODO: support skew - mFont = mFont.deriveFont(new AffineTransform( - mScaleX, mSkewX, 0, 0, 1, 0)); + // Get the fonts from the TypeFace object. + List<Font> fonts = mTypeface.getFonts(); + + // create new font objects as well as FontMetrics, based on the current text size + // and skew info. + ArrayList<FontInfo> infoList = new ArrayList<FontInfo>(fonts.size()); + for (Font font : fonts) { + FontInfo info = new FontInfo(); + info.mFont = font.deriveFont(mTextSize); + if (mScaleX != 1.0 || mSkewX != 0) { + // TODO: support skew + info.mFont = info.mFont.deriveFont(new AffineTransform( + mScaleX, mSkewX, 0, 0, 1, 0)); + } + info.mMetrics = Toolkit.getDefaultToolkit().getFontMetrics(info.mFont); + + infoList.add(info); } - - mMetrics = Toolkit.getDefaultToolkit().getFontMetrics(mFont); + + mFonts = Collections.unmodifiableList(infoList); } } - + //---------------------------------------- - + public void set(Paint src) { if (this != src) { mColor = src.mColor; @@ -237,6 +269,11 @@ public class Paint extends _Original_Paint { } @Override + public void setCompatibilityScaling(float factor) { + super.setCompatibilityScaling(factor); + } + + @Override public int getFlags() { return mFlags; } @@ -245,7 +282,47 @@ public class Paint extends _Original_Paint { public void setFlags(int flags) { mFlags = flags; } - + + @Override + public boolean isAntiAlias() { + return super.isAntiAlias(); + } + + @Override + public boolean isDither() { + return super.isDither(); + } + + @Override + public boolean isLinearText() { + return super.isLinearText(); + } + + @Override + public boolean isStrikeThruText() { + return super.isStrikeThruText(); + } + + @Override + public boolean isUnderlineText() { + return super.isUnderlineText(); + } + + @Override + public boolean isFakeBoldText() { + return super.isFakeBoldText(); + } + + @Override + public boolean isSubpixelText() { + return super.isSubpixelText(); + } + + @Override + public boolean isFilterBitmap() { + return super.isFilterBitmap(); + } + /** * Return the font's recommended interline spacing, given the Paint's * settings for typeface, textSize, etc. If metrics is not null, return the @@ -256,39 +333,41 @@ public class Paint extends _Original_Paint { * @return the font's recommended interline spacing. */ public float getFontMetrics(FontMetrics metrics) { - if (mMetrics != null) { + if (mFonts.size() > 0) { + java.awt.FontMetrics javaMetrics = mFonts.get(0).mMetrics; if (metrics != null) { - // ascent stuff should be negatif, but awt returns them as positive. - metrics.top = - mMetrics.getMaxAscent(); - metrics.ascent = - mMetrics.getAscent(); - metrics.descent = mMetrics.getDescent(); - metrics.bottom = mMetrics.getMaxDescent(); - metrics.leading = mMetrics.getLeading(); + // Android expects negative ascent so we invert the value from Java. + metrics.top = - javaMetrics.getMaxAscent(); + metrics.ascent = - javaMetrics.getAscent(); + metrics.descent = javaMetrics.getDescent(); + metrics.bottom = javaMetrics.getMaxDescent(); + metrics.leading = javaMetrics.getLeading(); } - - return mMetrics.getHeight(); + + return javaMetrics.getHeight(); } - + return 0; } public int getFontMetricsInt(FontMetricsInt metrics) { - if (mMetrics != null) { + if (mFonts.size() > 0) { + java.awt.FontMetrics javaMetrics = mFonts.get(0).mMetrics; if (metrics != null) { - // ascent stuff should be negatif, but awt returns them as positive. - metrics.top = - mMetrics.getMaxAscent(); - metrics.ascent = - mMetrics.getAscent(); - metrics.descent = mMetrics.getDescent(); - metrics.bottom = mMetrics.getMaxDescent(); - metrics.leading = mMetrics.getLeading(); + // Android expects negative ascent so we invert the value from Java. + metrics.top = - javaMetrics.getMaxAscent(); + metrics.ascent = - javaMetrics.getAscent(); + metrics.descent = javaMetrics.getDescent(); + metrics.bottom = javaMetrics.getMaxDescent(); + metrics.leading = javaMetrics.getLeading(); } - - return mMetrics.getHeight(); + + return javaMetrics.getHeight(); } - + return 0; } - + /** * Reimplemented to return Paint.FontMetrics instead of _Original_Paint.FontMetrics */ @@ -297,7 +376,7 @@ public class Paint extends _Original_Paint { getFontMetrics(fm); return fm; } - + /** * Reimplemented to return Paint.FontMetricsInt instead of _Original_Paint.FontMetricsInt */ @@ -311,16 +390,14 @@ public class Paint extends _Original_Paint { @Override public float getFontMetrics(_Original_Paint.FontMetrics metrics) { - // TODO implement if needed throw new UnsupportedOperationException("CALL TO PARENT FORBIDDEN"); } @Override public int getFontMetricsInt(_Original_Paint.FontMetricsInt metrics) { - // TODO implement if needed throw new UnsupportedOperationException("CALL TO PARENT FORBIDDEN"); } - + @Override public Typeface setTypeface(Typeface typeface) { if (typeface != null) { @@ -328,12 +405,17 @@ public class Paint extends _Original_Paint { } else { mTypeface = Typeface.DEFAULT; } - + updateFontObject(); return typeface; } - + + @Override + public Typeface getTypeface() { + return super.getTypeface(); + } + @Override public int getColor() { return mColor; @@ -344,17 +426,21 @@ public class Paint extends _Original_Paint { mColor = color; } + @Override + public void setARGB(int a, int r, int g, int b) { + super.setARGB(a, r, g, b); + } @Override public void setAlpha(int alpha) { mColor = (alpha << 24) | (mColor & 0x00FFFFFF); } - + @Override public int getAlpha() { return mColor >>> 24; } - + /** * Set or clear the shader object. * <p /> @@ -369,6 +455,11 @@ public class Paint extends _Original_Paint { return mShader = shader; } + @Override + public Shader getShader() { + return super.getShader(); + } + /** * Set or clear the paint's colorfilter, returning the parameter. * @@ -377,13 +468,15 @@ public class Paint extends _Original_Paint { */ @Override public ColorFilter setColorFilter(ColorFilter filter) { - int filterNative = 0; - if (filter != null) - filterNative = filter.native_instance; mColorFilter = filter; return filter; } + @Override + public ColorFilter getColorFilter() { + return super.getColorFilter(); + } + /** * Set or clear the xfermode object. * <p /> @@ -397,50 +490,172 @@ public class Paint extends _Original_Paint { public Xfermode setXfermode(Xfermode xfermode) { return mXfermode = xfermode; } - + + @Override + public Xfermode getXfermode() { + return super.getXfermode(); + } + + @Override + public Rasterizer setRasterizer(Rasterizer rasterizer) { + mRasterizer = rasterizer; + return rasterizer; + } + + @Override + public Rasterizer getRasterizer() { + return super.getRasterizer(); + } + + @Override + public void setShadowLayer(float radius, float dx, float dy, int color) { + // TODO Auto-generated method stub + } + + @Override + public void clearShadowLayer() { + super.clearShadowLayer(); + } + public void setTextAlign(Align align) { mAlign = align; } - + @Override public void setTextAlign(android.graphics._Original_Paint.Align align) { - // TODO implement if needed throw new UnsupportedOperationException("CALL TO PARENT FORBIDDEN"); } - + public Align getTextAlign() { return mAlign; } - + public void setStyle(Style style) { mStyle = style; } @Override public void setStyle(android.graphics._Original_Paint.Style style) { - // TODO implement if needed throw new UnsupportedOperationException("CALL TO PARENT FORBIDDEN"); } public Style getStyle() { return mStyle; } - + @Override public void setDither(boolean dither) { mFlags |= dither ? DITHER_FLAG : ~DITHER_FLAG; } - + @Override public void setAntiAlias(boolean aa) { mFlags |= aa ? ANTI_ALIAS_FLAG : ~ANTI_ALIAS_FLAG; } - + @Override public void setFakeBoldText(boolean flag) { mFlags |= flag ? FAKE_BOLD_TEXT_FLAG : ~FAKE_BOLD_TEXT_FLAG; } + @Override + public void setLinearText(boolean flag) { + mFlags |= flag ? LINEAR_TEXT_FLAG : ~LINEAR_TEXT_FLAG; + } + + @Override + public void setSubpixelText(boolean flag) { + mFlags |= flag ? SUBPIXEL_TEXT_FLAG : ~SUBPIXEL_TEXT_FLAG; + } + + @Override + public void setUnderlineText(boolean flag) { + mFlags |= flag ? UNDERLINE_TEXT_FLAG : ~UNDERLINE_TEXT_FLAG; + } + + @Override + public void setStrikeThruText(boolean flag) { + mFlags |= flag ? STRIKE_THRU_TEXT_FLAG : ~STRIKE_THRU_TEXT_FLAG; + } + + @Override + public void setFilterBitmap(boolean flag) { + mFlags |= flag ? FILTER_BITMAP_FLAG : ~FILTER_BITMAP_FLAG; + } + + @Override + public float getStrokeWidth() { + return mStrokeWidth; + } + + @Override + public void setStrokeWidth(float width) { + mStrokeWidth = width; + } + + @Override + public float getStrokeMiter() { + return mStrokeMiter; + } + + @Override + public void setStrokeMiter(float miter) { + mStrokeMiter = miter; + } + + @Override + public void setStrokeCap(android.graphics._Original_Paint.Cap cap) { + throw new UnsupportedOperationException("CALL TO PARENT FORBIDDEN"); + } + + public void setStrokeCap(Cap cap) { + mCap = cap; + } + + public Cap getStrokeCap() { + return mCap; + } + + @Override + public void setStrokeJoin(android.graphics._Original_Paint.Join join) { + throw new UnsupportedOperationException("CALL TO PARENT FORBIDDEN"); + } + + public void setStrokeJoin(Join join) { + mJoin = join; + } + + public Join getStrokeJoin() { + return mJoin; + } + + @Override + public boolean getFillPath(Path src, Path dst) { + return super.getFillPath(src, dst); + } + + @Override + public PathEffect setPathEffect(PathEffect effect) { + mPathEffect = effect; + return effect; + } + + @Override + public PathEffect getPathEffect() { + return super.getPathEffect(); + } + + @Override + public MaskFilter setMaskFilter(MaskFilter maskfilter) { + mMaskFilter = maskfilter; + return maskfilter; + } + + @Override + public MaskFilter getMaskFilter() { + return super.getMaskFilter(); + } + /** * Return the paint's text size. * @@ -459,7 +674,7 @@ public class Paint extends _Original_Paint { @Override public void setTextSize(float textSize) { mTextSize = textSize; - + updateFontObject(); } @@ -484,7 +699,7 @@ public class Paint extends _Original_Paint { @Override public void setTextScaleX(float scaleX) { mScaleX = scaleX; - + updateFontObject(); } @@ -508,10 +723,15 @@ public class Paint extends _Original_Paint { @Override public void setTextSkewX(float skewX) { mSkewX = skewX; - + updateFontObject(); } + @Override + public float getFontSpacing() { + return super.getFontSpacing(); + } + /** * Return the distance above (negative) the baseline (ascent) based on the * current typeface and text size. @@ -521,11 +741,12 @@ public class Paint extends _Original_Paint { */ @Override public float ascent() { - if (mMetrics != null) { - // ascent stuff should be negatif, but awt returns them as positive. - return - mMetrics.getAscent(); + if (mFonts.size() > 0) { + java.awt.FontMetrics javaMetrics = mFonts.get(0).mMetrics; + // Android expects negative ascent so we invert the value from Java. + return - javaMetrics.getAscent(); } - + return 0; } @@ -538,13 +759,14 @@ public class Paint extends _Original_Paint { */ @Override public float descent() { - if (mMetrics != null) { - return mMetrics.getDescent(); + if (mFonts.size() > 0) { + java.awt.FontMetrics javaMetrics = mFonts.get(0).mMetrics; + return javaMetrics.getDescent(); } - + return 0; } - + /** * Return the width of the text. * @@ -555,12 +777,57 @@ public class Paint extends _Original_Paint { */ @Override public float measureText(char[] text, int index, int count) { - if (mFont != null && text != null && text.length > 0) { - Rectangle2D bounds = mFont.getStringBounds(text, index, index + count, mFontContext); - - return (float)bounds.getWidth(); + // WARNING: the logic in this method is similar to Canvas.drawText. + // Any change to this method should be reflected in Canvas.drawText + if (mFonts.size() > 0) { + FontInfo mainFont = mFonts.get(0); + int i = index; + int lastIndex = index + count; + float total = 0f; + while (i < lastIndex) { + // always start with the main font. + int upTo = mainFont.mFont.canDisplayUpTo(text, i, lastIndex); + if (upTo == -1) { + // shortcut to exit + return total + mainFont.mMetrics.charsWidth(text, i, lastIndex - i); + } else if (upTo > 0) { + total += mainFont.mMetrics.charsWidth(text, i, upTo - i); + i = upTo; + // don't call continue at this point. Since it is certain the main font + // cannot display the font a index upTo (now ==i), we move on to the + // fallback fonts directly. + } + + // no char supported, attempt to read the next char(s) with the + // fallback font. In this case we only test the first character + // and then go back to test with the main font. + // Special test for 2-char characters. + boolean foundFont = false; + for (int f = 1 ; f < mFonts.size() ; f++) { + FontInfo fontInfo = mFonts.get(f); + + // need to check that the font can display the character. We test + // differently if the char is a high surrogate. + int charCount = Character.isHighSurrogate(text[i]) ? 2 : 1; + upTo = fontInfo.mFont.canDisplayUpTo(text, i, i + charCount); + if (upTo == -1) { + total += fontInfo.mMetrics.charsWidth(text, i, charCount); + i += charCount; + foundFont = true; + break; + + } + } + + // in case no font can display the char, measure it with the main font. + if (foundFont == false) { + int size = Character.isHighSurrogate(text[i]) ? 2 : 1; + total += mainFont.mMetrics.charsWidth(text, i, size); + i += size; + } + } } - + return 0; } @@ -587,7 +854,7 @@ public class Paint extends _Original_Paint { public float measureText(String text) { return measureText(text.toCharArray(), 0, text.length()); } - + /* * re-implement to call SpannableStringBuilder.measureText with a Paint object * instead of an _Original_Paint @@ -611,7 +878,7 @@ public class Paint extends _Original_Paint { TemporaryBuffer.recycle(buf); return result; } - + /** * Measure the text, stopping early if the measured width exceeds maxWidth. * Return the number of chars that were measured, and if measuredWidth is @@ -633,7 +900,7 @@ public class Paint extends _Original_Paint { public int breakText(char[] text, int index, int count, float maxWidth, float[] measuredWidth) { int inc = count > 0 ? 1 : -1; - + int measureIndex = 0; float measureAcc = 0; for (int i = index ; i != index + count ; i += inc, measureIndex++) { @@ -645,23 +912,23 @@ public class Paint extends _Original_Paint { start = index; end = i; } - + // measure from start to end float res = measureText(text, start, end - start + 1); - + if (measuredWidth != null) { measuredWidth[measureIndex] = res; } - + measureAcc += res; if (res > maxWidth) { // we should not return this char index, but since it's 0-based and we need // to return a count, we simply return measureIndex; return measureIndex; } - + } - + return measureIndex; } @@ -690,6 +957,28 @@ public class Paint extends _Original_Paint { } /** + * Measure the text, stopping early if the measured width exceeds maxWidth. + * Return the number of chars that were measured, and if measuredWidth is + * not null, return in it the actual width measured. + * + * @param text The text to measure + * @param start The offset into text to begin measuring at + * @param end The end of the text slice to measure. + * @param measureForwards If true, measure forwards, starting at start. + * Otherwise, measure backwards, starting with end. + * @param maxWidth The maximum width to accumulate. + * @param measuredWidth Optional. If not null, returns the actual width + * measured. + * @return The number of chars that were measured. Will always be <= + * abs(end - start). + */ + @Override + public int breakText(CharSequence text, int start, int end, boolean measureForwards, + float maxWidth, float[] measuredWidth) { + return super.breakText(text, start, end, measureForwards, maxWidth, measuredWidth); + } + + /** * Return the advance widths for the characters in the string. * * @param text The text to measure @@ -702,19 +991,35 @@ public class Paint extends _Original_Paint { @Override public int getTextWidths(char[] text, int index, int count, float[] widths) { - if (mMetrics != null) { + if (mFonts.size() > 0) { if ((index | count) < 0 || index + count > text.length || count > widths.length) { throw new ArrayIndexOutOfBoundsException(); } - + + // FIXME: handle multi-char characters. + // Need to figure out if the lengths of the width array takes into account + // multi-char characters. for (int i = 0; i < count; i++) { - widths[i] = mMetrics.charWidth(text[i + index]); + char c = text[i + index]; + boolean found = false; + for (FontInfo info : mFonts) { + if (info.mFont.canDisplay(c)) { + widths[i] = info.mMetrics.charWidth(c); + found = true; + break; + } + } + + if (found == false) { + // we stop there. + return i; + } } - + return count; } - + return 0; } @@ -736,10 +1041,10 @@ public class Paint extends _Original_Paint { if (end - start > widths.length) { throw new ArrayIndexOutOfBoundsException(); } - + return getTextWidths(text.toCharArray(), start, end - start, widths); } - + /* * re-implement to call SpannableStringBuilder.getTextWidths with a Paint object * instead of an _Original_Paint @@ -763,6 +1068,10 @@ public class Paint extends _Original_Paint { return result; } + @Override + public int getTextWidths(String text, float[] widths) { + return super.getTextWidths(text, widths); + } /** * Return the path (outline) for the specified text. @@ -782,13 +1091,13 @@ public class Paint extends _Original_Paint { float x, float y, Path path) { // TODO this is the ORIGINAL implementation. REPLACE AS NEEDED OR REMOVE - + if ((index | count) < 0 || index + count > text.length) { throw new ArrayIndexOutOfBoundsException(); } - + // TODO native_getTextPath(mNativePaint, text, index, count, x, y, path.ni()); - + throw new UnsupportedOperationException("IMPLEMENT AS NEEDED"); } @@ -811,10 +1120,10 @@ public class Paint extends _Original_Paint { if ((start | end | (end - start) | (text.length() - end)) < 0) { throw new IndexOutOfBoundsException(); } - + getTextPath(text.toCharArray(), start, end - start, x, y, path); } - + /** * Return in bounds (allocated by the caller) the smallest rectangle that * encloses all of the characters, with an implied origin at (0,0). @@ -833,10 +1142,10 @@ public class Paint extends _Original_Paint { if (bounds == null) { throw new NullPointerException("need bounds Rect"); } - + getTextBounds(text.toCharArray(), start, end - start, bounds); } - + /** * Return in bounds (allocated by the caller) the smallest rectangle that * encloses all of the characters, with an implied origin at (0,0). @@ -849,16 +1158,23 @@ public class Paint extends _Original_Paint { */ @Override public void getTextBounds(char[] text, int index, int count, Rect bounds) { - if (mFont != null) { + // FIXME + if (mFonts.size() > 0) { if ((index | count) < 0 || index + count > text.length) { throw new ArrayIndexOutOfBoundsException(); } if (bounds == null) { throw new NullPointerException("need bounds Rect"); } - - Rectangle2D rect = mFont.getStringBounds(text, index, index + count, mFontContext); + + FontInfo mainInfo = mFonts.get(0); + + Rectangle2D rect = mainInfo.mFont.getStringBounds(text, index, index + count, mFontContext); bounds.set(0, 0, (int)rect.getWidth(), (int)rect.getHeight()); } } + + public static void finalizer(int foo) { + // pass + } } diff --git a/tools/layoutlib/bridge/src/android/graphics/Typeface.java b/tools/layoutlib/bridge/src/android/graphics/Typeface.java index e878b04..af3adb5 100644 --- a/tools/layoutlib/bridge/src/android/graphics/Typeface.java +++ b/tools/layoutlib/bridge/src/android/graphics/Typeface.java @@ -21,9 +21,12 @@ import com.android.layoutlib.bridge.FontLoader; import android.content.res.AssetManager; import java.awt.Font; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; /** - * Re-implementation of Typeface over java.awt + * Re-implementation of Typeface over java.awt */ public class Typeface { private static final String DEFAULT_FAMILY = "sans-serif"; @@ -46,11 +49,11 @@ public class Typeface { private static Typeface[] sDefaults; private static FontLoader mFontLoader; - + private final int mStyle; - private final Font mFont; + private final List<Font> mFonts; private final String mFamily; - + // Style public static final int NORMAL = _Original_Typeface.NORMAL; public static final int BOLD = _Original_Typeface.BOLD; @@ -58,12 +61,13 @@ public class Typeface { public static final int BOLD_ITALIC = _Original_Typeface.BOLD_ITALIC; /** - * Returns the underlying {@link Font} object. + * Returns the underlying {@link Font} objects. The first item in the list is the real + * font. Any other items are fallback fonts for characters not found in the first one. */ - public Font getFont() { - return mFont; + public List<Font> getFonts() { + return mFonts; } - + /** Returns the typeface's intrinsic style attributes */ public int getStyle() { return mStyle; @@ -94,9 +98,12 @@ public class Typeface { styleBuffer[0] = style; Font font = mFontLoader.getFont(familyName, styleBuffer); if (font != null) { - return new Typeface(familyName, styleBuffer[0], font); + ArrayList<Font> list = new ArrayList<Font>(); + list.add(font); + list.addAll(mFontLoader.getFallBackFonts()); + return new Typeface(familyName, styleBuffer[0], list); } - + return null; } @@ -115,7 +122,10 @@ public class Typeface { styleBuffer[0] = style; Font font = mFontLoader.getFont(family.mFamily, styleBuffer); if (font != null) { - return new Typeface(family.mFamily, styleBuffer[0], font); + ArrayList<Font> list = new ArrayList<Font>(); + list.add(font); + list.addAll(mFontLoader.getFallBackFonts()); + return new Typeface(family.mFamily, styleBuffer[0], list); } return null; @@ -129,7 +139,7 @@ public class Typeface { public static Typeface defaultFromStyle(int style) { return sDefaults[style]; } - + /** * Create a new typeface from the specified font data. * @param mgr The application's asset manager @@ -140,17 +150,17 @@ public class Typeface { return null; //return new Typeface(nativeCreateFromAsset(mgr, path)); } - + // don't allow clients to call this directly - private Typeface(String family, int style, Font f) { + private Typeface(String family, int style, List<Font> fonts) { mFamily = family; - mFont = f; + mFonts = Collections.unmodifiableList(fonts); mStyle = style; } - + public static void init(FontLoader fontLoader) { mFontLoader = fontLoader; - + DEFAULT = create(DEFAULT_FAMILY, NORMAL); DEFAULT_BOLD = create(DEFAULT_FAMILY, BOLD); SANS_SERIF = create("sans-serif", NORMAL); @@ -162,14 +172,14 @@ public class Typeface { create(DEFAULT_FAMILY, ITALIC), create(DEFAULT_FAMILY, BOLD_ITALIC), }; - + /* DEFAULT = create((String)null, 0); DEFAULT_BOLD = create((String)null, Typeface.BOLD); SANS_SERIF = create("sans-serif", 0); SERIF = create("serif", 0); MONOSPACE = create("monospace", 0); - + sDefaults = new Typeface[] { DEFAULT, DEFAULT_BOLD, diff --git a/tools/layoutlib/bridge/src/android/webkit/WebView.java b/tools/layoutlib/bridge/src/android/webkit/WebView.java index 42e4dfa..3b66188 100644 --- a/tools/layoutlib/bridge/src/android/webkit/WebView.java +++ b/tools/layoutlib/bridge/src/android/webkit/WebView.java @@ -240,13 +240,6 @@ public class WebView extends MockView { return null; } - public static synchronized PluginList getPluginList() { - return null; - } - - public void refreshPlugins(boolean reloadOpenPages) { - } - public View getZoomControls() { return null; } diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java index 145a045..c455977 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java @@ -36,6 +36,7 @@ import android.graphics.Rect; import android.graphics.Region; import android.graphics.Typeface; import android.graphics.drawable.Drawable; +import android.os.Bundle; import android.os.Handler; import android.os.IBinder; import android.os.Looper; @@ -312,6 +313,7 @@ public final class Bridge implements ILayoutBridge { } /* + * For compatilibty purposes, we implement the old deprecated version of computeLayout. * (non-Javadoc) * @see com.android.layoutlib.api.ILayoutBridge#computeLayout(com.android.layoutlib.api.IXmlPullParser, java.lang.Object, int, int, int, float, float, java.lang.String, boolean, java.util.Map, java.util.Map, com.android.layoutlib.api.IProjectCallback, com.android.layoutlib.api.ILayoutLog) */ @@ -321,6 +323,23 @@ public final class Bridge implements ILayoutBridge { Map<String, Map<String, IResourceValue>> projectResources, Map<String, Map<String, IResourceValue>> frameworkResources, IProjectCallback customViewLoader, ILayoutLog logger) { + return computeLayout(layoutDescription, projectKey, + screenWidth, screenHeight, false /* renderFullSize */, + density, xdpi, ydpi, themeName, isProjectTheme, + projectResources, frameworkResources, customViewLoader, logger); + } + + /* + * (non-Javadoc) + * @see com.android.layoutlib.api.ILayoutBridge#computeLayout(com.android.layoutlib.api.IXmlPullParser, java.lang.Object, int, int, boolean, int, float, float, java.lang.String, boolean, java.util.Map, java.util.Map, com.android.layoutlib.api.IProjectCallback, com.android.layoutlib.api.ILayoutLog) + */ + public ILayoutResult computeLayout(IXmlPullParser layoutDescription, Object projectKey, + int screenWidth, int screenHeight, boolean renderFullSize, + int density, float xdpi, float ydpi, + String themeName, boolean isProjectTheme, + Map<String, Map<String, IResourceValue>> projectResources, + Map<String, Map<String, IResourceValue>> frameworkResources, + IProjectCallback customViewLoader, ILayoutLog logger) { if (logger == null) { logger = sDefaultLogger; } @@ -341,6 +360,7 @@ public final class Bridge implements ILayoutBridge { try { // setup the display Metrics. DisplayMetrics metrics = new DisplayMetrics(); + metrics.densityDpi = density; metrics.density = density / (float) DisplayMetrics.DENSITY_DEFAULT; metrics.scaledDensity = metrics.density; metrics.widthPixels = screenWidth; @@ -388,20 +408,44 @@ public final class Bridge implements ILayoutBridge { // get the background drawable if (windowBackground != null) { - Drawable d = ResourceHelper.getDrawable(windowBackground.getValue(), + Drawable d = ResourceHelper.getDrawable(windowBackground, context, true /* isFramework */); root.setBackgroundDrawable(d); } - int w_spec = MeasureSpec.makeMeasureSpec(screenWidth, MeasureSpec.EXACTLY); - int h_spec = MeasureSpec.makeMeasureSpec(screenHeight - screenOffset, - MeasureSpec.EXACTLY); - // measure the views + int w_spec, h_spec; + + if (renderFullSize) { + // measure the full size needed by the layout. + w_spec = MeasureSpec.makeMeasureSpec(screenWidth, + MeasureSpec.UNSPECIFIED); // this lets us know the actual needed size + h_spec = MeasureSpec.makeMeasureSpec(screenHeight - screenOffset, + MeasureSpec.UNSPECIFIED); // this lets us know the actual needed size + view.measure(w_spec, h_spec); + + int neededWidth = root.getChildAt(0).getMeasuredWidth(); + if (neededWidth > screenWidth) { + screenWidth = neededWidth; + } + + int neededHeight = root.getChildAt(0).getMeasuredHeight(); + if (neededHeight > screenHeight - screenOffset) { + screenHeight = neededHeight + screenOffset; + } + } + + // remeasure with only the size we need + // This must always be done before the call to layout + w_spec = MeasureSpec.makeMeasureSpec(screenWidth, MeasureSpec.EXACTLY); + h_spec = MeasureSpec.makeMeasureSpec(screenHeight - screenOffset, + MeasureSpec.EXACTLY); view.measure(w_spec, h_spec); + + // now do the layout. view.layout(0, screenOffset, screenWidth, screenHeight); - // draw them + // draw the views Canvas canvas = new Canvas(screenWidth, screenHeight - screenOffset, logger); root.draw(canvas); @@ -1009,11 +1053,40 @@ public final class Bridge implements ILayoutBridge { // pass for now. } + @SuppressWarnings("unused") public void setInsets(IWindow window, int touchable, Rect contentInsets, Rect visibleInsets) { // pass for now. } + @SuppressWarnings("unused") + public void setWallpaperPosition(IBinder window, float x, float y, + float xStep, float yStep) { + // pass for now. + } + + @SuppressWarnings("unused") + public void wallpaperOffsetsComplete(IBinder window) { + // pass for now. + } + + @SuppressWarnings("unused") + public Bundle sendWallpaperCommand(IBinder window, String action, int x, int y, + int z, Bundle extras, boolean sync) { + // pass for now. + return null; + } + + @SuppressWarnings("unused") + public void wallpaperCommandComplete(IBinder window, Bundle result) { + // pass for now. + } + + @SuppressWarnings("unused") + public void closeSystemDialogs(String reason) { + // pass for now. + } + public IBinder asBinder() { // pass for now. return null; @@ -1041,12 +1114,12 @@ public final class Bridge implements ILayoutBridge { } @SuppressWarnings("unused") - public void dispatchPointer(MotionEvent arg0, long arg1) throws RemoteException { + public void dispatchPointer(MotionEvent arg0, long arg1, boolean arg2) throws RemoteException { // pass for now. } @SuppressWarnings("unused") - public void dispatchTrackball(MotionEvent arg0, long arg1) throws RemoteException { + public void dispatchTrackball(MotionEvent arg0, long arg1, boolean arg2) throws RemoteException { // pass for now. } @@ -1067,6 +1140,23 @@ public final class Bridge implements ILayoutBridge { // pass for now. } + @SuppressWarnings("unused") + public void dispatchWallpaperOffsets(float x, float y, float xStep, float yStep, + boolean sync) { + // pass for now. + } + + @SuppressWarnings("unused") + public void dispatchWallpaperCommand(String action, int x, int y, + int z, Bundle extras, boolean sync) { + // pass for now. + } + + @SuppressWarnings("unused") + public void closeSystemDialogs(String reason) { + // pass for now. + } + public IBinder asBinder() { // pass for now. return null; diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeContext.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeContext.java index f48c8db..1e9f573 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeContext.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeContext.java @@ -27,6 +27,7 @@ import android.content.ContentResolver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; +import android.content.IntentSender; import android.content.ServiceConnection; import android.content.SharedPreferences; import android.content.pm.ApplicationInfo; @@ -1098,6 +1099,13 @@ public final class BridgeContext extends Context { } @Override + public void sendStickyOrderedBroadcast(Intent intent, + BroadcastReceiver resultReceiver, Handler scheduler, int initialCode, String initialData, + Bundle initialExtras) { + // TODO Auto-generated method stub + } + + @Override public void setTheme(int arg0) { // TODO Auto-generated method stub @@ -1124,6 +1132,13 @@ public final class BridgeContext extends Context { } @Override + public void startIntentSender(IntentSender intent, + Intent fillInIntent, int flagsMask, int flagsValues, int extraFlags) + throws IntentSender.SendIntentException { + // TODO Auto-generated method stub + } + + @Override public boolean startInstrumentation(ComponentName arg0, String arg1, Bundle arg2) { // TODO Auto-generated method stub diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeResources.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeResources.java index 2b0100b..1fafef4 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeResources.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeResources.java @@ -124,7 +124,7 @@ public final class BridgeResources extends Resources { IResourceValue value = getResourceValue(id, mPlatformResourceFlag); if (value != null) { - return ResourceHelper.getDrawable(value.getValue(), mContext, value.isFramework()); + return ResourceHelper.getDrawable(value, mContext, value.isFramework()); } // id was not found or not resolved. Throw a NotFoundException. diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeTypedArray.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeTypedArray.java index 10421de..957f737 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeTypedArray.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeTypedArray.java @@ -659,8 +659,9 @@ public final class BridgeTypedArray extends TypedArray { return null; } - String value = mData[index].getValue(); - if (value == null || BridgeConstants.REFERENCE_NULL.equals(value)) { + IResourceValue value = mData[index]; + String stringValue = value.getValue(); + if (stringValue == null || BridgeConstants.REFERENCE_NULL.equals(stringValue)) { return null; } @@ -672,7 +673,8 @@ public final class BridgeTypedArray extends TypedArray { // looks like we were unable to resolve the drawable mContext.getLogger().warning(String.format( - "Unable to resolve drawable \"%1$s\" in attribute \"%2$s\"", value, mNames[index])); + "Unable to resolve drawable \"%1$s\" in attribute \"%2$s\"", stringValue, + mNames[index])); return null; } diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/FontLoader.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/FontLoader.java index 1bdd1cc..801503b 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/FontLoader.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/FontLoader.java @@ -29,6 +29,7 @@ import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.util.ArrayList; +import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.List; @@ -47,14 +48,13 @@ import javax.xml.parsers.SAXParserFactory; */ public final class FontLoader { private static final String FONTS_DEFINITIONS = "fonts.xml"; - + private static final String NODE_FONTS = "fonts"; private static final String NODE_FONT = "font"; private static final String NODE_NAME = "name"; - - private static final String ATTR_TTF = "ttf"; + private static final String NODE_FALLBACK = "fallback"; - private static final String[] NODE_LEVEL = { NODE_FONTS, NODE_FONT, NODE_NAME }; + private static final String ATTR_TTF = "ttf"; private static final String FONT_EXT = ".ttf"; @@ -62,7 +62,7 @@ public final class FontLoader { private static final String[] FONT_STYLE_BOLD = { "-Bold" }; private static final String[] FONT_STYLE_ITALIC = { "-Italic" }; private static final String[] FONT_STYLE_BOLDITALIC = { "-BoldItalic" }; - + // list of font style, in the order matching the Typeface Font style private static final String[][] FONT_STYLES = { FONT_STYLE_DEFAULT, @@ -70,23 +70,25 @@ public final class FontLoader { FONT_STYLE_ITALIC, FONT_STYLE_BOLDITALIC }; - + private final Map<String, String> mFamilyToTtf = new HashMap<String, String>(); private final Map<String, Map<Integer, Font>> mTtfToFontMap = new HashMap<String, Map<Integer, Font>>(); - + + private List<Font> mFallBackFonts = null; + public static FontLoader create(String fontOsLocation) { try { SAXParserFactory parserFactory = SAXParserFactory.newInstance(); parserFactory.setNamespaceAware(true); - + SAXParser parser = parserFactory.newSAXParser(); File f = new File(fontOsLocation + File.separator + FONTS_DEFINITIONS); - + FontDefinitionParser definitionParser = new FontDefinitionParser( fontOsLocation + File.separator); parser.parse(new FileInputStream(f), definitionParser); - + return definitionParser.getFontLoader(); } catch (ParserConfigurationException e) { // return null below @@ -101,12 +103,35 @@ public final class FontLoader { return null; } - private FontLoader(List<FontInfo> fontList) { + private FontLoader(List<FontInfo> fontList, List<String> fallBackList) { for (FontInfo info : fontList) { for (String family : info.families) { mFamilyToTtf.put(family, info.ttf); } } + + ArrayList<Font> list = new ArrayList<Font>(); + for (String path : fallBackList) { + File f = new File(path + FONT_EXT); + if (f.isFile()) { + try { + Font font = Font.createFont(Font.TRUETYPE_FONT, f); + if (font != null) { + list.add(font); + } + } catch (FontFormatException e) { + // skip this font name + } catch (IOException e) { + // skip this font name + } + } + } + + mFallBackFonts = Collections.unmodifiableList(list); + } + + public List<Font> getFallBackFonts() { + return mFallBackFonts; } public synchronized Font getFont(String family, int[] style) { @@ -116,25 +141,25 @@ public final class FontLoader { // get the ttf name from the family String ttf = mFamilyToTtf.get(family); - + if (ttf == null) { return null; } - + // get the font from the ttf Map<Integer, Font> styleMap = mTtfToFontMap.get(ttf); - + if (styleMap == null) { styleMap = new HashMap<Integer, Font>(); mTtfToFontMap.put(ttf, styleMap); } - + Font f = styleMap.get(style); - + if (f != null) { return f; } - + // if it doesn't exist, we create it, and we can't, we try with a simpler style switch (style[0]) { case Typeface.NORMAL: @@ -178,7 +203,7 @@ public final class FontLoader { private Font getFont(String ttf, String[] fontFileSuffix) { for (String suffix : fontFileSuffix) { String name = ttf + suffix + FONT_EXT; - + File f = new File(name); if (f.isFile()) { try { @@ -193,14 +218,14 @@ public final class FontLoader { } } } - + return null; } private final static class FontInfo { String ttf; final Set<String> families; - + FontInfo() { families = new HashSet<String>(); } @@ -208,19 +233,19 @@ public final class FontLoader { private final static class FontDefinitionParser extends DefaultHandler { private final String mOsFontsLocation; - - private int mDepth = 0; + private FontInfo mFontInfo = null; private final StringBuilder mBuilder = new StringBuilder(); - private final List<FontInfo> mFontList = new ArrayList<FontInfo>(); - + private List<FontInfo> mFontList; + private List<String> mFallBackList; + private FontDefinitionParser(String osFontsLocation) { super(); mOsFontsLocation = osFontsLocation; } - + FontLoader getFontLoader() { - return new FontLoader(mFontList); + return new FontLoader(mFontList, mFallBackList); } /* (non-Javadoc) @@ -229,10 +254,11 @@ public final class FontLoader { @Override public void startElement(String uri, String localName, String name, Attributes attributes) throws SAXException { - if (localName.equals(NODE_LEVEL[mDepth])) { - mDepth++; - - if (mDepth == 2) { // font level. + if (NODE_FONTS.equals(localName)) { + mFontList = new ArrayList<FontInfo>(); + mFallBackList = new ArrayList<String>(); + } else if (NODE_FONT.equals(localName)) { + if (mFontList != null) { String ttf = attributes.getValue(ATTR_TTF); if (ttf != null) { mFontInfo = new FontInfo(); @@ -240,42 +266,50 @@ public final class FontLoader { mFontList.add(mFontInfo); } } + } else if (NODE_NAME.equals(localName)) { + // do nothing, we'll handle the name in the endElement + } else if (NODE_FALLBACK.equals(localName)) { + if (mFallBackList != null) { + String ttf = attributes.getValue(ATTR_TTF); + if (ttf != null) { + mFallBackList.add(mOsFontsLocation + ttf); + } + } } + mBuilder.setLength(0); + super.startElement(uri, localName, name, attributes); } /* (non-Javadoc) * @see org.xml.sax.helpers.DefaultHandler#characters(char[], int, int) */ - @SuppressWarnings("unused") @Override public void characters(char[] ch, int start, int length) throws SAXException { - if (mFontInfo != null) { - mBuilder.append(ch, start, length); - } + mBuilder.append(ch, start, length); } /* (non-Javadoc) * @see org.xml.sax.helpers.DefaultHandler#endElement(java.lang.String, java.lang.String, java.lang.String) */ - @SuppressWarnings("unused") @Override public void endElement(String uri, String localName, String name) throws SAXException { - if (localName.equals(NODE_LEVEL[mDepth-1])) { - mDepth--; - if (mDepth == 2) { // end of a <name> node - if (mFontInfo != null) { - String family = trimXmlWhitespaces(mBuilder.toString()); - mFontInfo.families.add(family); - mBuilder.setLength(0); - } - } else if (mDepth == 1) { // end of a <font> node - mFontInfo = null; + if (NODE_FONTS.equals(localName)) { + // top level, do nothing + } else if (NODE_FONT.equals(localName)) { + mFontInfo = null; + } else if (NODE_NAME.equals(localName)) { + // handle a new name for an existing Font Info + if (mFontInfo != null) { + String family = trimXmlWhitespaces(mBuilder.toString()); + mFontInfo.families.add(family); } + } else if (NODE_FALLBACK.equals(localName)) { + // nothing to do here. } } - + private String trimXmlWhitespaces(String value) { if (value == null) { return null; @@ -283,7 +317,7 @@ public final class FontLoader { // look for carriage return and replace all whitespace around it by just 1 space. int index; - + while ((index = value.indexOf('\n')) != -1) { // look for whitespace on each side int left = index - 1; @@ -294,7 +328,7 @@ public final class FontLoader { break; } } - + int right = index + 1; int count = value.length(); while (right < count) { @@ -304,7 +338,7 @@ public final class FontLoader { break; } } - + // remove all between left and right (non inclusive) and replace by a single space. String leftString = null; if (left >= 0) { @@ -314,7 +348,7 @@ public final class FontLoader { if (right < count) { rightString = value.substring(right); } - + if (leftString != null) { value = leftString; if (rightString != null) { @@ -324,24 +358,24 @@ public final class FontLoader { value = rightString != null ? rightString : ""; } } - + // now we un-escape the string int length = value.length(); char[] buffer = value.toCharArray(); - + for (int i = 0 ; i < length ; i++) { if (buffer[i] == '\\') { if (buffer[i+1] == 'n') { // replace the char with \n buffer[i+1] = '\n'; } - + // offset the rest of the buffer since we go from 2 to 1 char System.arraycopy(buffer, i+1, buffer, i, length - i - 1); length--; } } - + return new String(buffer, 0, length); } diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/ResourceHelper.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/ResourceHelper.java index fbdf8dc..3d0dd73 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/ResourceHelper.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/ResourceHelper.java @@ -16,6 +16,9 @@ package com.android.layoutlib.bridge; +import com.android.layoutlib.api.IDensityBasedResourceValue; +import com.android.layoutlib.api.IResourceValue; +import com.android.layoutlib.api.IDensityBasedResourceValue.Density; import com.android.ninepatch.NinePatch; import org.kxml2.io.KXmlParser; @@ -40,7 +43,7 @@ import java.util.regex.Pattern; * Helper class to provide various convertion method used in handling android resources. */ public final class ResourceHelper { - + private final static Pattern sFloatPattern = Pattern.compile("(-?[0-9]+(?:\\.[0-9]+)?)(.*)"); private final static float[] sFloatOut = new float[1]; @@ -59,12 +62,12 @@ public final class ResourceHelper { } value = value.substring(1); - + // make sure it's not longer than 32bit if (value.length() > 8) { throw new NumberFormatException(); } - + if (value.length() == 3) { // RGB format char[] color = new char[8]; color[0] = color[1] = 'F'; @@ -84,7 +87,7 @@ public final class ResourceHelper { } // this is a RRGGBB or AARRGGBB value - + // Integer.parseInt will fail to parse strings like "ff191919", so we use // a Long, but cast the result back into an int, since we know that we're only // dealing with 32 bit values. @@ -96,28 +99,30 @@ public final class ResourceHelper { /** * Returns a drawable from the given value. - * @param value The value. A path to a 9 patch, a bitmap or a xml based drawable, + * @param value The value that contains a path to a 9 patch, a bitmap or a xml based drawable, * or an hexadecimal color - * @param context + * @param context * @param isFramework indicates whether the resource is a framework resources. * Framework resources are cached, and loaded only once. */ - public static Drawable getDrawable(String value, BridgeContext context, boolean isFramework) { + public static Drawable getDrawable(IResourceValue value, BridgeContext context, boolean isFramework) { Drawable d = null; - - String lowerCaseValue = value.toLowerCase(); + + String stringValue = value.getValue(); + + String lowerCaseValue = stringValue.toLowerCase(); if (lowerCaseValue.endsWith(NinePatch.EXTENSION_9PATCH)) { - File f = new File(value); - if (f.isFile()) { - NinePatch ninePatch = Bridge.getCached9Patch(value, + File file = new File(stringValue); + if (file.isFile()) { + NinePatch ninePatch = Bridge.getCached9Patch(stringValue, isFramework ? null : context.getProjectKey()); - + if (ninePatch == null) { try { - ninePatch = NinePatch.load(new File(value).toURL(), false /* convert */); - - Bridge.setCached9Patch(value, ninePatch, + ninePatch = NinePatch.load(file.toURL(), false /* convert */); + + Bridge.setCached9Patch(stringValue, ninePatch, isFramework ? null : context.getProjectKey()); } catch (MalformedURLException e) { // URL is wrong, we'll return null below @@ -125,23 +130,23 @@ public final class ResourceHelper { // failed to read the file, we'll return null below. } } - + if (ninePatch != null) { return new NinePatchDrawable(ninePatch); } } - + return null; } else if (lowerCaseValue.endsWith(".xml")) { // create a blockparser for the file - File f = new File(value); + File f = new File(stringValue); if (f.isFile()) { try { // let the framework inflate the Drawable from the XML file. KXmlParser parser = new KXmlParser(); parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, true); parser.setInput(new FileReader(f)); - + d = Drawable.createFromXml(context.getResources(), // FIXME: we need to know if this resource is platform or not new BridgeXmlBlockParser(parser, context, false)); @@ -157,19 +162,43 @@ public final class ResourceHelper { return null; } else { - File bmpFile = new File(value); + File bmpFile = new File(stringValue); if (bmpFile.isFile()) { try { - Bitmap bitmap = Bridge.getCachedBitmap(value, + Bitmap bitmap = Bridge.getCachedBitmap(stringValue, isFramework ? null : context.getProjectKey()); - + if (bitmap == null) { bitmap = new Bitmap(bmpFile); - Bridge.setCachedBitmap(value, bitmap, + try { + bitmap.setDensity(Density.MEDIUM.getValue()); + } catch (NoClassDefFoundError error) { + // look like we're running in an older version of ADT that doesn't + // include the new layoutlib_api. Let's just ignore this, the drawing + // will just be wrong. + } + Bridge.setCachedBitmap(stringValue, bitmap, isFramework ? null : context.getProjectKey()); } - - return new BitmapDrawable(bitmap); + + try { + if (value instanceof IDensityBasedResourceValue) { + Density density = ((IDensityBasedResourceValue)value).getDensity(); + if (density != Density.MEDIUM) { + // create a copy of the bitmap + bitmap = Bitmap.createBitmap(bitmap); + + // apply the density + bitmap.setDensity(density.getValue()); + } + } + } catch (NoClassDefFoundError error) { + // look like we're running in an older version of ADT that doesn't include + // the new layoutlib_api. Let's just ignore this, the drawing will just be + // wrong. + } + + return new BitmapDrawable(context.getResources(), bitmap); } catch (IOException e) { // we'll return null below // TODO: log the error. @@ -177,7 +206,7 @@ public final class ResourceHelper { } else { // attempt to get a color from the value try { - int color = getColor(value); + int color = getColor(stringValue); return new ColorDrawable(color); } catch (NumberFormatException e) { // we'll return null below. @@ -185,20 +214,20 @@ public final class ResourceHelper { } } } - + return null; } - + // ------- TypedValue stuff // This is taken from //device/libs/utils/ResourceTypes.cpp - + private static final class UnitEntry { String name; int type; int unit; float scale; - + UnitEntry(String name, int type, int unit, float scale) { this.name = name; this.type = type; @@ -218,7 +247,7 @@ public final class ResourceHelper { new UnitEntry("%", TypedValue.TYPE_FRACTION, TypedValue.COMPLEX_UNIT_FRACTION, 1.0f/100), new UnitEntry("%p", TypedValue.TYPE_FRACTION, TypedValue.COMPLEX_UNIT_FRACTION_PARENT, 1.0f/100), }; - + /** * Returns the raw value from the given string. * This object is only valid until the next call on to {@link ResourceHelper}. @@ -227,10 +256,10 @@ public final class ResourceHelper { if (stringToFloat(s, mValue)) { return mValue; } - + return null; } - + /** * Convert the string into a {@link TypedValue}. * @param s @@ -258,7 +287,7 @@ public final class ResourceHelper { if (buf[0] < '0' && buf[0] > '9' && buf[0] != '.') { return false; } - + // now look for the string that is after the float... Matcher m = sFloatPattern.matcher(s); if (m.matches()) { @@ -272,11 +301,11 @@ public final class ResourceHelper { // this shouldn't happen with the regexp above. return false; } - + if (end.length() > 0 && end.charAt(0) != ' ') { // Might be a unit... if (parseUnit(end, outValue, sFloatOut)) { - + f *= sFloatOut[0]; boolean neg = f < 0; if (neg) { @@ -312,17 +341,17 @@ public final class ResourceHelper { if (neg) { mantissa = (-mantissa) & TypedValue.COMPLEX_MANTISSA_MASK; } - outValue.data |= + outValue.data |= (radix<<TypedValue.COMPLEX_RADIX_SHIFT) | (mantissa<<TypedValue.COMPLEX_MANTISSA_SHIFT); return true; } return false; } - + // make sure it's only spaces at the end. end = end.trim(); - + if (end.length() == 0) { if (outValue != null) { outValue.type = TypedValue.TYPE_FLOAT; @@ -334,7 +363,7 @@ public final class ResourceHelper { return false; } - + private static boolean parseUnit(String str, TypedValue outValue, float[] outScale) { str = str.trim(); @@ -343,7 +372,7 @@ public final class ResourceHelper { outValue.type = unit.type; outValue.data = unit.unit << TypedValue.COMPLEX_UNIT_SHIFT; outScale[0] = unit.scale; - + return true; } } diff --git a/tools/layoutlib/bridge/tests/com/android/layoutlib/bridge/AndroidGraphicsTests.java b/tools/layoutlib/bridge/tests/com/android/layoutlib/bridge/AndroidGraphicsTests.java index ac144e7..6e14e82 100644 --- a/tools/layoutlib/bridge/tests/com/android/layoutlib/bridge/AndroidGraphicsTests.java +++ b/tools/layoutlib/bridge/tests/com/android/layoutlib/bridge/AndroidGraphicsTests.java @@ -24,7 +24,7 @@ import android.text.TextPaint; import junit.framework.TestCase; /** - * + * */ public class AndroidGraphicsTests extends TestCase { @@ -40,24 +40,24 @@ public class AndroidGraphicsTests extends TestCase { public void testMatrix() { Matrix m1 = new Matrix(); - - assertFalse(m1.isIdentity()); - + + assertTrue(m1.isIdentity()); + m1.setValues(new float[] { 1,0,0, 0,1,0, 0,0,1 }); assertTrue(m1.isIdentity()); - + Matrix m2 = new Matrix(m1); - + float[] v1 = new float[9]; float[] v2 = new float[9]; m1.getValues(v1); m2.getValues(v2); - + for (int i = 0 ; i < 9; i++) { assertEquals(v1[i], v2[i]); } } - + public void testPaint() { _Original_Paint o = new _Original_Paint(); assertNotNull(o); @@ -65,7 +65,7 @@ public class AndroidGraphicsTests extends TestCase { Paint p = new Paint(); assertNotNull(p); } - + public void textTextPaint() { TextPaint p = new TextPaint(); assertNotNull(p); diff --git a/tools/layoutlib/bridge/tests/com/android/layoutlib/bridge/BridgeTest.java b/tools/layoutlib/bridge/tests/com/android/layoutlib/bridge/BridgeTest.java deleted file mode 100644 index e424f1d..0000000 --- a/tools/layoutlib/bridge/tests/com/android/layoutlib/bridge/BridgeTest.java +++ /dev/null @@ -1,229 +0,0 @@ -/* - * Copyright (C) 2008 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.layoutlib.bridge; - -import com.android.layoutlib.api.ILayoutResult; -import com.android.layoutlib.api.IResourceValue; -import com.android.layoutlib.api.IStyleResourceValue; -import com.android.layoutlib.api.IXmlPullParser; -import com.android.layoutlib.api.ILayoutResult.ILayoutViewInfo; - -import org.kxml2.io.KXmlParser; -import org.xmlpull.v1.XmlPullParser; - -import java.io.File; -import java.io.FileReader; -import java.net.URL; -import java.util.HashMap; -import java.util.Map; - -import junit.framework.TestCase; - -public class BridgeTest extends TestCase { - - /** the class being tested */ - private Bridge mBridge; - /** the path to the sample layout.xml file */ - private String mLayoutXml1Path; - private String mTextOnlyXmlPath; - - @Override - protected void setUp() throws Exception { - super.setUp(); - - mBridge = new Bridge(); - - // FIXME: need some fonts somewhere. - mBridge.init(null /* fontOsLocation */, getAttributeValues()); - - URL url = this.getClass().getClassLoader().getResource("data/layout1.xml"); - mLayoutXml1Path = url.getFile(); - - url = this.getClass().getClassLoader().getResource("data/textonly.xml"); - mTextOnlyXmlPath = url.getFile(); - } - - @Override - protected void tearDown() throws Exception { - super.tearDown(); - } - - // --------------- - - /** - * Test parser that implements {@link IXmlPullParser}. - */ - private static class TestParser extends KXmlParser implements IXmlPullParser { - public Object getViewKey() { - return null; - } - } - - public void testComputeLayout() throws Exception { - - TestParser parser = new TestParser(); - parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, true); - parser.setInput(new FileReader(new File(mLayoutXml1Path))); - - Map<String, Map<String, IResourceValue>> projectResources = getProjectResources(); - - Map<String, Map<String, IResourceValue>> frameworkResources = getFrameworkResources(); - - int screenWidth = 320; - int screenHeight = 480; - - // FIXME need a dummy font for the tests! - ILayoutResult result = mBridge.computeLayout(parser, new Integer(1) /* projectKey */, - screenWidth, screenHeight, - "Theme", projectResources, frameworkResources, null, null); - - display(result.getRootView(), ""); - } - - private Map<String, Map<String, Integer>> getAttributeValues() { - Map<String, Map<String, Integer>> attributeValues = - new HashMap<String, Map<String,Integer>>(); - - // lets create a map for the orientation attribute - Map<String, Integer> attributeMap = new HashMap<String, Integer>(); - - attributeMap.put("horizontal", Integer.valueOf(0)); - attributeMap.put("vertical", Integer.valueOf(1)); - - attributeValues.put("orientation", attributeMap); - - return attributeValues; - } - - private Map<String, Map<String, IResourceValue>> getFrameworkResources() { - Map<String, Map<String, IResourceValue>> frameworkResources = - new HashMap<String, Map<String, IResourceValue>>(); - - // create the style map - Map<String, IResourceValue> styleMap = new HashMap<String, IResourceValue>(); - frameworkResources.put("style", styleMap); - - // create a button style. - IStyleResourceValue style = createStyle("Widget.Button", - "background", "@android:drawable/something", - "focusable", "true", - "clickable", "true", - "textAppearance", "?android:attr/textAppearanceSmallInverse", - "textColor", "?android:attr/textColorBrightInverseNoDisable", - "gravity", "center_vertical|center_horizontal" - ); - styleMap.put(style.getName(), style); - - // create the parent style of button style - style = createStyle("Widget", - "textAppearance", "?textAppearance"); - styleMap.put(style.getName(), style); - - // link the buttonStyle info in the default theme. - style = createStyle("Theme", - BridgeConstants.RES_STYLE, "buttonStyle", "@android:style/Widget.Button", - BridgeConstants.RES_STYLE, "textAppearance", "@android:style/TextAppearance", - BridgeConstants.RES_STYLE, "textAppearanceSmallInverse", "@android:style/TextAppearance.Small.Inverse", - BridgeConstants.RES_COLOR, "textColorBrightInverseNoDisable", "@android:color/bright_text_light_nodisable" - ); - styleMap.put(style.getName(), style); - - // create a dummy drawable to go with it - Map<String, IResourceValue> drawableMap = new HashMap<String, IResourceValue>(); - frameworkResources.put("drawable", drawableMap); - - // get the 9 patch test location - URL url = this.getClass().getClassLoader().getResource("data/button.9.png"); - - IResourceValue drawable = new ResourceValue(BridgeConstants.RES_DRAWABLE, "something", - url.getPath()); - drawableMap.put(drawable.getName(), drawable); - return frameworkResources; - } - - private Map<String, Map<String, IResourceValue>> getProjectResources() { - Map<String, Map<String, IResourceValue>> projectResources = - new HashMap<String, Map<String, IResourceValue>>(); - - // create the style map (even empty there should be one) - Map<String, IResourceValue> styleMap = new HashMap<String, IResourceValue>(); - projectResources.put("style", styleMap); - - return projectResources; - } - - - private void display(ILayoutViewInfo result, String offset) { - - String msg = String.format("%s%s L:%d T:%d R:%d B:%d", - offset, - result.getName(), - result.getLeft(), result.getTop(), result.getRight(), result.getBottom()); - - System.out.println(msg); - ILayoutViewInfo[] children = result.getChildren(); - if (children != null) { - offset += "+-"; - for (ILayoutViewInfo child : children) { - display(child, offset); - } - } - } - - /** - * Creates a {@link IStyleResourceValue} based on the given values. - * @param styleName the name of the style. - * @param items An array of Strings. Even indices contain a style item name, and odd indices - * a style item value. If the number of string in the array is not even, an exception is thrown. - */ - private IStyleResourceValue createStyle(String styleName, String... items) { - StyleResourceValue value = new StyleResourceValue(styleName); - - if (items.length % 3 == 0) { - for (int i = 0 ; i < items.length;) { - value.addItem(new ResourceValue(items[i++], items[i++], items[i++])); - } - } else { - throw new IllegalArgumentException("Need a multiple of 3 for the number of strings"); - } - - return value; - } - - // --------------- - - public void testTextLayout() throws Exception { - - TestParser parser = new TestParser(); - parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, true); - parser.setInput(new FileReader(new File(mTextOnlyXmlPath))); - - Map<String, Map<String, IResourceValue>> projectResources = getProjectResources(); - Map<String, Map<String, IResourceValue>> frameworkResources = getFrameworkResources(); - - int screenWidth = 320; - int screenHeight = 480; - - // FIXME need a dummy font for the tests! - ILayoutResult result = mBridge.computeLayout(parser, new Integer(1) /* projectKey */, - screenWidth, screenHeight, - "Theme", projectResources, frameworkResources, null, null); - - display(result.getRootView(), ""); - } - -} diff --git a/tools/layoutlib/bridge/tests/com/android/layoutlib/bridge/BridgeXmlBlockParserTest.java b/tools/layoutlib/bridge/tests/com/android/layoutlib/bridge/BridgeXmlBlockParserTest.java index cac1f95..db1262f 100644 --- a/tools/layoutlib/bridge/tests/com/android/layoutlib/bridge/BridgeXmlBlockParserTest.java +++ b/tools/layoutlib/bridge/tests/com/android/layoutlib/bridge/BridgeXmlBlockParserTest.java @@ -17,33 +17,18 @@ package com.android.layoutlib.bridge; import org.kxml2.io.KXmlParser; -import org.w3c.dom.Document; import org.w3c.dom.Node; -import org.xml.sax.SAXException; import org.xmlpull.v1.XmlPullParser; -import java.io.File; -import java.io.FileReader; -import java.io.IOException; -import java.net.URL; - -import javax.xml.parsers.DocumentBuilder; -import javax.xml.parsers.DocumentBuilderFactory; -import javax.xml.parsers.ParserConfigurationException; +import java.io.InputStream; import junit.framework.TestCase; public class BridgeXmlBlockParserTest extends TestCase { - private String mXmlPath; - private Document mDoc; - @Override protected void setUp() throws Exception { super.setUp(); - URL url = this.getClass().getClassLoader().getResource("data/layout1.xml"); - mXmlPath = url.getFile(); - mDoc = getXmlDocument(mXmlPath); } @Override @@ -54,7 +39,10 @@ public class BridgeXmlBlockParserTest extends TestCase { public void testXmlBlockParser() throws Exception { XmlPullParser parser = new KXmlParser(); parser = new BridgeXmlBlockParser(parser, null, false /* platformResourceFlag */); - parser.setInput(new FileReader(new File(mXmlPath))); + + InputStream input = this.getClass().getClassLoader().getResourceAsStream( + "com/android/layoutlib/testdata/layout1.xml"); + parser.setInput(input, null /*encoding*/); assertEquals(XmlPullParser.START_DOCUMENT, parser.next()); @@ -85,24 +73,8 @@ public class BridgeXmlBlockParserTest extends TestCase { assertEquals(XmlPullParser.END_TAG, parser.next()); assertEquals(XmlPullParser.END_DOCUMENT, parser.next()); } - - //------------ - - private Document getXmlDocument(String xmlFilePath) - throws ParserConfigurationException, SAXException, IOException { - DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); - - // keep comments - factory.setIgnoringComments(false); - // don't validate our bogus DTD - factory.setValidating(false); - // we want namespaces - factory.setNamespaceAware(true); - - DocumentBuilder builder = factory.newDocumentBuilder(); - return builder.parse(new File(xmlFilePath)); - } + //------------ /** * Quick'n'dirty debug helper that dumps an XML structure to stdout. @@ -126,7 +98,7 @@ public class BridgeXmlBlockParserTest extends TestCase { "DOCUMENT_FRAGMENT_NODE", "NOTATION_NODE" }; - + String s = String.format("%s<%s> %s %s", prefix, types[node.getNodeType()], @@ -134,7 +106,7 @@ public class BridgeXmlBlockParserTest extends TestCase { node.getNodeValue() == null ? "" : node.getNodeValue().trim()); System.out.println(s); - + n = node.getFirstChild(); if (n != null) { dump(n, prefix + "- "); diff --git a/tools/layoutlib/bridge/tests/com/android/layoutlib/bridge/NinePatchTest.java b/tools/layoutlib/bridge/tests/com/android/layoutlib/bridge/NinePatchTest.java index 67ec5e1..d5993db 100644 --- a/tools/layoutlib/bridge/tests/com/android/layoutlib/bridge/NinePatchTest.java +++ b/tools/layoutlib/bridge/tests/com/android/layoutlib/bridge/NinePatchTest.java @@ -7,20 +7,21 @@ import java.net.URL; import junit.framework.TestCase; public class NinePatchTest extends TestCase { - + private NinePatch mPatch; @Override protected void setUp() throws Exception { - URL url = this.getClass().getClassLoader().getResource("data/button.9.png"); + URL url = this.getClass().getClassLoader().getResource( + "com/android/layoutlib/testdata/button.9.png"); mPatch = NinePatch.load(url, false /* convert */); } - + public void test9PatchLoad() throws Exception { assertNotNull(mPatch); } - + public void test9PatchMinSize() { int[] padding = new int[4]; mPatch.getPadding(padding); @@ -28,8 +29,8 @@ public class NinePatchTest extends TestCase { assertEquals(3, padding[1]); assertEquals(13, padding[2]); assertEquals(4, padding[3]); - assertEquals(38, mPatch.getWidth()); - assertEquals(27, mPatch.getHeight()); + assertEquals(36, mPatch.getWidth()); + assertEquals(25, mPatch.getHeight()); } } diff --git a/tools/layoutlib/bridge/tests/com/android/layoutlib/bridge/StyleResourceValue.java b/tools/layoutlib/bridge/tests/com/android/layoutlib/bridge/StyleResourceValue.java deleted file mode 100644 index 84bdc2f..0000000 --- a/tools/layoutlib/bridge/tests/com/android/layoutlib/bridge/StyleResourceValue.java +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Copyright (C) 2008 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.layoutlib.bridge; - -import com.android.layoutlib.api.IResourceValue; -import com.android.layoutlib.api.IStyleResourceValue; - -import java.util.HashMap; - -class StyleResourceValue extends ResourceValue implements IStyleResourceValue { - - private String mParentStyle = null; - private HashMap<String, IResourceValue> mItems = new HashMap<String, IResourceValue>(); - - StyleResourceValue(String name) { - super(name); - } - - StyleResourceValue(String name, String parentStyle) { - super(name); - mParentStyle = parentStyle; - } - - public String getParentStyle() { - return mParentStyle; - } - - public IResourceValue findItem(String name) { - return mItems.get(name); - } - - public void addItem(IResourceValue value) { - mItems.put(value.getName(), value); - } - - @Override - public void replaceWith(ResourceValue value) { - super.replaceWith(value); - - if (value instanceof StyleResourceValue) { - mItems.clear(); - mItems.putAll(((StyleResourceValue)value).mItems); - } - } - -} diff --git a/tools/layoutlib/bridge/tests/com/android/layoutlib/bridge/TestClassReplacement.java b/tools/layoutlib/bridge/tests/com/android/layoutlib/bridge/TestClassReplacement.java new file mode 100644 index 0000000..e0dc55f --- /dev/null +++ b/tools/layoutlib/bridge/tests/com/android/layoutlib/bridge/TestClassReplacement.java @@ -0,0 +1,139 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.layoutlib.bridge; + +import java.lang.reflect.Method; + +import junit.framework.TestCase; + +public class TestClassReplacement extends TestCase { + + public void testClassReplacements() { + // TODO: we want to test all the classes. For now only Paint passes the tests. +// final String[] classes = CreateInfo.RENAMED_CLASSES; + final String[] classes = new String[] { + "android.graphics.Paint", "android.graphics._Original_Paint" + }; + final int count = classes.length; + for (int i = 0 ; i < count ; i += 2) { + loadAndCompareClasses(classes[i], classes[i+1]); + } + } + + private void loadAndCompareClasses(String newClassName, String oldClassName) { + // load the classes + try { + Class<?> newClass = TestClassReplacement.class.getClassLoader().loadClass(newClassName); + Class<?> oldClass = TestClassReplacement.class.getClassLoader().loadClass(oldClassName); + + compare(newClass, oldClass); + } catch (ClassNotFoundException e) { + fail("Failed to load class: " + e.getMessage()); + } + } + + private void compare(Class<?> newClass, Class<?> oldClass) { + // first compare the methods. + Method[] newClassMethods = newClass.getDeclaredMethods(); + Method[] oldClassMethods = oldClass.getDeclaredMethods(); + + for (Method oldMethod : oldClassMethods) { + // we ignore anything that starts with native + if (oldMethod.getName().startsWith("native")) { + continue; + } + boolean found = false; + for (Method newMethod : newClassMethods) { + if (compareMethods(newClass, newMethod, oldClass, oldMethod)) { + found = true; + break; + } + } + + if (found == false) { + fail(String.format("Unable to find %1$s", oldMethod.toGenericString())); + } + } + + // TODO: check (somehow?) that the methods that were removed from the original class + // have been put back in the new class! + // For this we need the original unmodified class (ie renamed, but w/o the methods removed) + } + + private boolean compareMethods(Class<?> newClass, Method newMethod, + Class<?> oldClass, Method oldMethod) { + // first check the name of the method + if (newMethod.getName().equals(oldMethod.getName()) == false) { + return false; + } + + // check the return value + Class<?> oldReturnType = oldMethod.getReturnType(); + // if it's the old class, or if it's a inner class of the oldclass, we need to change this. + oldReturnType = adapt(oldReturnType, newClass, oldClass); + + // compare the return types + Class<?> newReturnType = newMethod.getReturnType(); + if (newReturnType.equals(oldReturnType) == false) { + return false; + } + + // now check the parameters type. + Class<?>[] oldParameters = oldMethod.getParameterTypes(); + Class<?>[] newParemeters = newMethod.getParameterTypes(); + if (oldParameters.length != newParemeters.length) { + return false; + } + + for (int i = 0 ; i < oldParameters.length ; i++) { + if (newParemeters[i].equals(adapt(oldParameters[i], newClass, oldClass)) == false) { + return false; + } + } + + return true; + } + + /** + * Adapts a class to deal with renamed classes. + * <p/>For instance if old class is <code>android.graphics._Original_Paint</code> and the + * new class is <code>android.graphics.Paint</code> and the class to adapt is + * <code>android.graphics._Original_Paint$Cap</code>, then the method will return a + * {@link Class} object representing <code>android.graphics.Paint$Cap</code>. + * <p/> + * This method will also ensure that all renamed classes contains all the proper inner classes + * that they should be declaring. + * @param theClass the class to adapt + * @param newClass the new class object + * @param oldClass the old class object + * @return the adapted class. + * @throws ClassNotFoundException + */ + private Class<?> adapt(Class<?> theClass, Class<?> newClass, Class<?> oldClass) { + // only look for a new class if it's not primitive as Class.forName() would fail otherwise. + if (theClass.isPrimitive() == false) { + String n = theClass.getName().replace(oldClass.getName(), newClass.getName()); + try { + return Class.forName(n); + } catch (ClassNotFoundException e) { + fail("Missing class: " + n); + } + } + + return theClass; + } +} diff --git a/tools/layoutlib/bridge/tests/data/button.9.png b/tools/layoutlib/bridge/tests/com/android/layoutlib/testdata/button.9.png Binary files differindex 9d52f40..9d52f40 100644 --- a/tools/layoutlib/bridge/tests/data/button.9.png +++ b/tools/layoutlib/bridge/tests/com/android/layoutlib/testdata/button.9.png diff --git a/tools/layoutlib/bridge/tests/data/layout1.xml b/tools/layoutlib/bridge/tests/com/android/layoutlib/testdata/layout1.xml index 554f541..554f541 100644 --- a/tools/layoutlib/bridge/tests/data/layout1.xml +++ b/tools/layoutlib/bridge/tests/com/android/layoutlib/testdata/layout1.xml |