diff options
Diffstat (limited to 'tools/layoutlib/bridge/src')
16 files changed, 535 insertions, 341 deletions
diff --git a/tools/layoutlib/bridge/src/android/graphics/BidiRenderer.java b/tools/layoutlib/bridge/src/android/graphics/BidiRenderer.java new file mode 100644 index 0000000..62d0a0d --- /dev/null +++ b/tools/layoutlib/bridge/src/android/graphics/BidiRenderer.java @@ -0,0 +1,215 @@ +/* + * 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 android.graphics; + +import java.awt.Font; +import java.awt.Graphics2D; +import java.awt.font.FontRenderContext; +import java.awt.font.GlyphVector; +import java.util.LinkedList; +import java.util.List; + +import com.ibm.icu.lang.UScript; +import com.ibm.icu.lang.UScriptRun; + +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. + */ +@SuppressWarnings("deprecation") +public class BidiRenderer { + + /* package */ static class ScriptRun { + int start; + int limit; + boolean isRtl; + int scriptCode; + FontInfo font; + + public ScriptRun(int start, int limit, boolean isRtl) { + this.start = start; + this.limit = limit; + this.isRtl = isRtl; + this.scriptCode = UScript.INVALID_CODE; + } + } + + /* package */ Graphics2D graphics; + /* package */ Paint_Delegate paint; + /* package */ char[] text; + + /** + * @param graphics May be null. + * @param paint The Paint to use to get the fonts. Should not be null. + * @param text Unidirectional text. Should not be null. + */ + /* package */ BidiRenderer(Graphics2D graphics, Paint_Delegate paint, char[] text) { + assert (paint != null); + this.graphics = graphics; + this.paint = paint; + this.text = text; + } + + /** + * Render unidirectional text. + * + * This method can also be used to measure the width of the text without actually drawing it. + * + * @param start index of the first character + * @param limit index of the first character that should not be rendered. + * @param isRtl is the text right-to-left + * @param advances If not null, then advances for each character to be rendered are returned + * here. + * @param advancesIndex index into advances from where the advances need to be filled. + * @param draw If true and {@link graphics} is not null, draw the rendered text on the graphics + * at the given co-ordinates + * @param x The x-coordinate of the left edge of where the text should be drawn on the given + * graphics. + * @param y The y-coordinate at which to draw the text on the given graphics. + * @return The x-coordinate of the right edge of the drawn text. In other words, + * x + the width of the text. + */ + /* package */ float renderText(int start, int limit, boolean isRtl, float advances[], + int advancesIndex, boolean draw, float x, float y) { + // We break the text into scripts and then select font based on it and then render each of + // the script runs. + for (ScriptRun run : getScriptRuns(text, start, limit, isRtl, paint.getFonts())) { + int flag = Font.LAYOUT_NO_LIMIT_CONTEXT | Font.LAYOUT_NO_START_CONTEXT; + flag |= isRtl ? Font.LAYOUT_RIGHT_TO_LEFT : Font.LAYOUT_LEFT_TO_RIGHT; + x = renderScript(run.start, run.limit, run.font, flag, advances, advancesIndex, draw, + x, y); + advancesIndex += run.limit - run.start; + } + return x; + } + + /** + * Render a script run. Use the preferred font to render as much as possible. This also + * implements a fallback mechanism to render characters that cannot be drawn using the + * preferred font. + * + * @return x + width of the text drawn. + */ + private float renderScript(int start, int limit, FontInfo preferredFont, int flag, + float advances[], int advancesIndex, boolean draw, float x, float y) { + List<FontInfo> fonts = paint.getFonts(); + if (fonts == null || preferredFont == null) { + return x; + } + + while (start < limit) { + boolean foundFont = false; + int canDisplayUpTo = preferredFont.mFont.canDisplayUpTo(text, start, limit); + if (canDisplayUpTo == -1) { + return render(start, limit, preferredFont, flag, advances, advancesIndex, draw, + x, y); + } else if (canDisplayUpTo > start) { // can draw something + x = render(start, canDisplayUpTo, preferredFont, flag, advances, advancesIndex, + draw, x, y); + advancesIndex += canDisplayUpTo - start; + start = canDisplayUpTo; + } + + int charCount = Character.isHighSurrogate(text[start]) ? 2 : 1; + for (FontInfo font : fonts) { + canDisplayUpTo = font.mFont.canDisplayUpTo(text, start, start + charCount); + if (canDisplayUpTo == -1) { + x = render(start, start+charCount, font, flag, advances, advancesIndex, draw, + x, y); + start += charCount; + advancesIndex += charCount; + foundFont = true; + break; + } + } + if (!foundFont) { + // No font can display this char. Use the preferred font. The char will most + // probably appear as a box or a blank space. We could, probably, use some + // heuristics and break the character into the base character and diacritics and + // then draw it, but it's probably not worth the effort. + x = render(start, start + charCount, preferredFont, flag, advances, advancesIndex, + draw, x, y); + start += charCount; + advancesIndex += charCount; + } + } + return x; + } + + /** + * Render the text with the given font. + */ + private float render(int start, int limit, FontInfo font, int flag, float advances[], + int advancesIndex, boolean draw, float x, float y) { + + float totalAdvance = 0; + // Since the metrics don't have anti-aliasing set, we create a new FontRenderContext with + // the anti-aliasing set. + FontRenderContext f = font.mMetrics.getFontRenderContext(); + FontRenderContext frc = new FontRenderContext(f.getTransform(), paint.isAntiAliased(), + f.usesFractionalMetrics()); + GlyphVector gv = font.mFont.layoutGlyphVector(frc, text, start, limit, flag); + int ng = gv.getNumGlyphs(); + int[] ci = gv.getGlyphCharIndices(0, ng, null); + for (int i = 0; i < ng; i++) { + float adv = gv.getGlyphMetrics(i).getAdvanceX(); + if (advances != null) { + int adv_idx = advancesIndex + ci[i]; + advances[adv_idx] += adv; + } + totalAdvance += adv; + } + if (draw && graphics != null) { + graphics.drawGlyphVector(gv, x, y); + } + return x + totalAdvance; + } + + // --- Static helper methods --- + + /* package */ static List<ScriptRun> getScriptRuns(char[] text, int start, int limit, + boolean isRtl, List<FontInfo> fonts) { + LinkedList<ScriptRun> scriptRuns = new LinkedList<ScriptRun>(); + + int count = limit - start; + UScriptRun uScriptRun = new UScriptRun(text, start, count); + while (uScriptRun.next()) { + int scriptStart = uScriptRun.getScriptStart(); + int scriptLimit = uScriptRun.getScriptLimit(); + ScriptRun run = new ScriptRun(scriptStart, scriptLimit, isRtl); + run.scriptCode = uScriptRun.getScriptCode(); + setScriptFont(text, run, fonts); + scriptRuns.add(run); + } + + return scriptRuns; + } + + // TODO: Replace this method with one which returns the font based on the scriptCode. + private static void setScriptFont(char[] text, ScriptRun run, + List<FontInfo> fonts) { + for (FontInfo fontInfo : fonts) { + if (fontInfo.mFont.canDisplayUpTo(text, run.start, run.limit) == -1) { + run.font = fontInfo; + return; + } + } + run.font = fonts.get(0); + } +} diff --git a/tools/layoutlib/bridge/src/android/graphics/Canvas_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/Canvas_Delegate.java index 4171bb5..da18864 100644 --- a/tools/layoutlib/bridge/src/android/graphics/Canvas_Delegate.java +++ b/tools/layoutlib/bridge/src/android/graphics/Canvas_Delegate.java @@ -23,7 +23,6 @@ import com.android.layoutlib.bridge.impl.GcSnapshot; import com.android.tools.layoutlib.annotations.LayoutlibDelegate; import android.graphics.Bitmap.Config; -import android.graphics.Paint_Delegate.FontInfo; import android.text.TextUtils; import java.awt.Color; @@ -35,7 +34,6 @@ import java.awt.Shape; import java.awt.geom.AffineTransform; import java.awt.geom.Arc2D; import java.awt.image.BufferedImage; -import java.util.List; /** @@ -978,7 +976,8 @@ public final class Canvas_Delegate { @LayoutlibDelegate /*package*/ static void native_drawText(int nativeCanvas, final char[] text, final int index, final int count, - final float startX, final float startY, int flags, int paint) { + final float startX, final float startY, final int flags, int paint) { + draw(nativeCanvas, paint, false /*compositeOnly*/, false /*forceSrcMode*/, new GcSnapshot.Drawable() { @Override @@ -988,10 +987,10 @@ public final class Canvas_Delegate { // Paint.TextAlign indicates how the text is positioned relative to X. // LEFT is the default and there's nothing to do. float x = startX; - float y = startY; + int limit = index + count; + boolean isRtl = flags == Canvas.DIRECTION_RTL; if (paintDelegate.getTextAlign() != Paint.Align.LEFT.nativeInt) { - // TODO: check the value of bidiFlags. - float m = paintDelegate.measureText(text, index, count, 0); + float m = paintDelegate.measureText(text, index, count, isRtl); if (paintDelegate.getTextAlign() == Paint.Align.CENTER.nativeInt) { x -= m / 2; } else if (paintDelegate.getTextAlign() == Paint.Align.RIGHT.nativeInt) { @@ -999,87 +998,15 @@ public final class Canvas_Delegate { } } - List<FontInfo> fonts = paintDelegate.getFonts(); - - 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. - graphics.setFont(mainFont.mFont); - graphics.drawChars(text, i, lastIndex - i, (int)x, (int)y); - return; - } else if (upTo > 0) { - // draw what's possible - graphics.setFont(mainFont.mFont); - graphics.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 - graphics.setFont(fontInfo.mFont); - graphics.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; - - graphics.setFont(mainFont.mFont); - graphics.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; - } - } - } + new BidiRenderer(graphics, paintDelegate, text).renderText( + index, limit, isRtl, null, 0, true, x, startY); } }); } @LayoutlibDelegate /*package*/ static void native_drawText(int nativeCanvas, String text, - int start, int end, float x, float y, int flags, int paint) { + int start, int end, float x, float y, final int flags, int paint) { int count = end - start; char[] buffer = TemporaryBuffer.obtain(count); TextUtils.getChars(text, start, end, buffer, 0); diff --git a/tools/layoutlib/bridge/src/android/graphics/Paint_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/Paint_Delegate.java index c9c9800..41953ed 100644 --- a/tools/layoutlib/bridge/src/android/graphics/Paint_Delegate.java +++ b/tools/layoutlib/bridge/src/android/graphics/Paint_Delegate.java @@ -32,7 +32,6 @@ import java.awt.Stroke; 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; @@ -576,7 +575,7 @@ public class Paint_Delegate { return 0; } - return delegate.measureText(text, index, count, bidiFlags); + return delegate.measureText(text, index, count, isRtl(bidiFlags)); } @LayoutlibDelegate @@ -615,7 +614,7 @@ public class Paint_Delegate { } // measure from start to end - float res = delegate.measureText(text, start, end - start + 1, bidiFlags); + float res = delegate.measureText(text, start, end - start + 1, isRtl(bidiFlags)); if (measuredWidth != null) { measuredWidth[measureIndex] = res; @@ -980,51 +979,27 @@ public class Paint_Delegate { /*package*/ static float native_getTextRunAdvances(int native_object, char[] text, int index, int count, int contextIndex, int contextCount, int flags, float[] advances, int advancesIndex) { + + if (advances != null) + for (int i = advancesIndex; i< advancesIndex+count; i++) + advances[i]=0; // get the delegate from the native int. Paint_Delegate delegate = sManager.getDelegate(native_object); - if (delegate == null) { + if (delegate == null || delegate.mFonts == null || delegate.mFonts.size() == 0) { return 0.f; } + boolean isRtl = isRtl(flags); - if (delegate.mFonts.size() > 0) { - // FIXME: handle multi-char characters (see measureText) - float totalAdvance = 0; - for (int i = 0; i < count; i++) { - char c = text[i + index]; - boolean found = false; - for (FontInfo info : delegate.mFonts) { - if (info.mFont.canDisplay(c)) { - float adv = info.mMetrics.charWidth(c); - totalAdvance += adv; - if (advances != null) { - advances[i] = adv; - } - - found = true; - break; - } - } - - if (found == false) { - // no advance for this char. - if (advances != null) { - advances[i] = 0.f; - } - } - } - - return totalAdvance; - } - - return 0; - + int limit = index + count; + return new BidiRenderer(null, delegate, text).renderText( + index, limit, isRtl, advances, advancesIndex, false, 0, 0); } @LayoutlibDelegate /*package*/ static float native_getTextRunAdvances(int native_object, String text, int start, int end, int contextStart, int contextEnd, int flags, float[] advances, int advancesIndex) { - // FIXME: support contextStart, contextEnd and direction flag + // FIXME: support contextStart and contextEnd int count = end - start; char[] buffer = TemporaryBuffer.obtain(count); TextUtils.getChars(text, start, end, buffer, 0); @@ -1080,19 +1055,12 @@ public class Paint_Delegate { // get the delegate from the native int. Paint_Delegate delegate = sManager.getDelegate(nativePaint); - if (delegate == null) { + if (delegate == null || delegate.mFonts == null || delegate.mFonts.size() == 0) { return; } - - // FIXME should test if the main font can display all those characters. - // See MeasureText - if (delegate.mFonts.size() > 0) { - FontInfo mainInfo = delegate.mFonts.get(0); - - Rectangle2D rect = mainInfo.mFont.getStringBounds(text, index, index + count, - delegate.mFontContext); - bounds.set(0, 0, (int) rect.getWidth(), (int) rect.getHeight()); - } + int w = (int) delegate.measureText(text, index, count, isRtl(bidiFlags)); + int h= delegate.getFonts().get(0).mMetrics.getHeight(); + bounds.set(0, 0, w, h); } @LayoutlibDelegate @@ -1176,6 +1144,7 @@ public class Paint_Delegate { info.mFont = info.mFont.deriveFont(new AffineTransform( mTextScaleX, mTextSkewX, 0, 1, 0, 0)); } + // The metrics here don't have anti-aliasing set. info.mMetrics = Toolkit.getDefaultToolkit().getFontMetrics(info.mFont); infoList.add(info); @@ -1185,64 +1154,9 @@ public class Paint_Delegate { } } - /*package*/ float measureText(char[] text, int index, int count, int bidiFlags) { - // TODO: find out what bidiFlags actually does. - - // WARNING: the logic in this method is similar to Canvas_Delegate.native_drawText - // Any change to this method should be reflected there as well - - 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 total; - } - - return 0; + /*package*/ float measureText(char[] text, int index, int count, boolean isRtl) { + return new BidiRenderer(null, this, text).renderText( + index, index + count, isRtl, null, 0, false, 0, 0); } private float getFontMetrics(FontMetrics metrics) { @@ -1281,4 +1195,14 @@ public class Paint_Delegate { } } + private static boolean isRtl(int flag) { + switch(flag) { + case Paint.BIDI_RTL: + case Paint.BIDI_FORCE_RTL: + case Paint.BIDI_DEFAULT_RTL: + return true; + default: + return false; + } + } } diff --git a/tools/layoutlib/bridge/src/android/text/AndroidBidi_Delegate.java b/tools/layoutlib/bridge/src/android/text/AndroidBidi_Delegate.java index 52b8f34..973fa0e 100644 --- a/tools/layoutlib/bridge/src/android/text/AndroidBidi_Delegate.java +++ b/tools/layoutlib/bridge/src/android/text/AndroidBidi_Delegate.java @@ -16,7 +16,10 @@ 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; /** @@ -29,9 +32,29 @@ import com.android.tools.layoutlib.annotations.LayoutlibDelegate; public class AndroidBidi_Delegate { @LayoutlibDelegate - /*package*/ static int runBidi(int dir, char[] chs, byte[] chInfo, int n, boolean haveInfo) { - // return the equivalent of Layout.DIR_LEFT_TO_RIGHT - // TODO: actually figure the direction. - return 0; + /*package*/ static int runBidi(int dir, char[] chars, byte[] charInfo, int count, + boolean haveInfo) { + + switch (dir) { + case 0: // Layout.DIR_REQUEST_LTR + case 1: // Layout.DIR_REQUEST_RTL + break; // No change. + case -1: + dir = Bidi.LEVEL_DEFAULT_LTR; + break; + case -2: + dir = Bidi.LEVEL_DEFAULT_RTL; + break; + default: + // Invalid code. Log error, assume LEVEL_DEFAULT_LTR and continue. + Bridge.getLog().error(LayoutLog.TAG_BROKEN, "Invalid direction flag", null); + dir = Bidi.LEVEL_DEFAULT_LTR; + } + Bidi bidi = new Bidi(chars, 0, null, 0, count, dir); + if (charInfo != null) { + for (int i = 0; i < count; ++i) + charInfo[i] = bidi.getLevelAt(i); + } + return bidi.getParaLevel(); } } 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 42257c5..ab4be71 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java @@ -35,6 +35,7 @@ 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 android.content.res.BridgeAssetManager; import android.graphics.Bitmap; @@ -64,6 +65,8 @@ import java.util.concurrent.locks.ReentrantLock; */ public final class Bridge extends com.android.ide.common.rendering.api.Bridge { + private static final String ICU_LOCALE_DIRECTION_RTL = "right-to-left"; + public static class StaticMethodNotImplementedException extends RuntimeException { private static final long serialVersionUID = 1L; @@ -211,7 +214,8 @@ public final class Bridge extends com.android.ide.common.rendering.api.Bridge { Capability.ANIMATED_VIEW_MANIPULATION, Capability.ADAPTER_BINDING, Capability.EXTENDED_VIEWINFO, - Capability.FIXED_SCALABLE_NINE_PATCH); + Capability.FIXED_SCALABLE_NINE_PATCH, + Capability.RTL); BridgeAssetManager.initSystem(); @@ -411,6 +415,20 @@ public final class Bridge extends com.android.ide.common.rendering.api.Bridge { throw new IllegalArgumentException("viewObject is not a View"); } + @Override + public boolean isRtl(String locale) { + return isLocaleRtl(locale); + } + + public static boolean isLocaleRtl(String locale) { + if (locale == null) { + locale = ""; + } + ULocale uLocale = new ULocale(locale); + return uLocale.getCharacterOrientation().equals(ICU_LOCALE_DIRECTION_RTL) ? + true : false; + } + /** * Returns the lock for the bridge */ 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 21bef1c..99aa228 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 @@ -132,7 +132,8 @@ public final class BridgeContext extends Context { RenderResources renderResources, IProjectCallback projectCallback, Configuration config, - int targetSdkVersion) { + int targetSdkVersion, + boolean hasRtlSupport) { mProjectKey = projectKey; mMetrics = metrics; mProjectCallback = projectCallback; @@ -142,6 +143,9 @@ public final class BridgeContext extends Context { mApplicationInfo = new ApplicationInfo(); mApplicationInfo.targetSdkVersion = targetSdkVersion; + if (hasRtlSupport) { + mApplicationInfo.flags = mApplicationInfo.flags | ApplicationInfo.FLAG_SUPPORTS_RTL; + } mWindowManager = new WindowManagerImpl(mMetrics); } diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/CustomBar.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/CustomBar.java index ea9d8d9..17b0eb6 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/CustomBar.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/CustomBar.java @@ -25,6 +25,7 @@ import com.android.layoutlib.bridge.android.BridgeXmlBlockParser; import com.android.layoutlib.bridge.impl.ParserFactory; import com.android.layoutlib.bridge.impl.ResourceHelper; import com.android.resources.Density; +import com.android.resources.LayoutDirection; import com.android.resources.ResourceType; import org.xmlpull.v1.XmlPullParser; @@ -86,38 +87,53 @@ abstract class CustomBar extends LinearLayout { } } - private InputStream getIcon(String iconName, Density[] densityInOut, String[] pathOut, - boolean tryOtherDensities) { + private InputStream getIcon(String iconName, Density[] densityInOut, LayoutDirection direction, + String[] pathOut, boolean tryOtherDensities) { // current density Density density = densityInOut[0]; // bitmap url relative to this class - pathOut[0] = "/bars/" + density.getResourceValue() + "/" + iconName; + if (direction != null) { + pathOut[0] = "/bars/" + direction.getResourceValue() + "-" + density.getResourceValue() + + "/" + iconName; + } else { + pathOut[0] = "/bars/" + density.getResourceValue() + "/" + iconName; + } InputStream stream = getClass().getResourceAsStream(pathOut[0]); if (stream == null && tryOtherDensities) { for (Density d : Density.values()) { if (d != density) { densityInOut[0] = d; - stream = getIcon(iconName, densityInOut, pathOut, false /*tryOtherDensities*/); + stream = getIcon(iconName, densityInOut, direction, pathOut, + false /*tryOtherDensities*/); if (stream != null) { return stream; } } } + // couldn't find resource with direction qualifier. try without. + if (direction != null) { + return getIcon(iconName, densityInOut, null, pathOut, true); + } } return stream; } protected void loadIcon(int index, String iconName, Density density) { + loadIcon(index, iconName, density, false); + } + + protected void loadIcon(int index, String iconName, Density density, boolean isRtl) { View child = getChildAt(index); if (child instanceof ImageView) { ImageView imageView = (ImageView) child; String[] pathOut = new String[1]; Density[] densityInOut = new Density[] { density }; - InputStream stream = getIcon(iconName, densityInOut, pathOut, + LayoutDirection dir = isRtl ? LayoutDirection.RTL : LayoutDirection.LTR; + InputStream stream = getIcon(iconName, densityInOut, dir, pathOut, true /*tryOtherDensities*/); density = densityInOut[0]; 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 cc90d6b..84e676e 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 @@ -17,6 +17,7 @@ package com.android.layoutlib.bridge.bars; import com.android.resources.Density; +import com.android.layoutlib.bridge.Bridge; import org.xmlpull.v1.XmlPullParserException; @@ -26,7 +27,8 @@ import android.widget.TextView; public class NavigationBar extends CustomBar { - public NavigationBar(Context context, Density density, int orientation) throws XmlPullParserException { + public NavigationBar(Context context, Density density, int orientation, boolean isRtl, + boolean rtlEnabled) throws XmlPullParserException { super(context, density, orientation, "/bars/navigation_bar.xml", "navigation_bar.xml"); setBackgroundColor(0xFF000000); @@ -37,14 +39,15 @@ public class NavigationBar extends CustomBar { // 0 is a spacer. int back = 1; int recent = 3; - if (orientation == LinearLayout.VERTICAL) { + if (orientation == LinearLayout.VERTICAL || (isRtl && !rtlEnabled)) { + // If RTL is enabled, then layoutlib mirrors the layout for us. back = 3; recent = 1; } - loadIcon(back, "ic_sysbar_back.png", density); - loadIcon(2, "ic_sysbar_home.png", density); - loadIcon(recent, "ic_sysbar_recent.png", density); + loadIcon(back, "ic_sysbar_back.png", density, isRtl); + loadIcon(2, "ic_sysbar_home.png", density, isRtl); + loadIcon(recent, "ic_sysbar_recent.png", density, isRtl); } @Override diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/StatusBar.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/StatusBar.java index 5c08412..baa956d 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/StatusBar.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/StatusBar.java @@ -30,7 +30,10 @@ import android.widget.TextView; public class StatusBar extends CustomBar { - public StatusBar(Context context, Density density) throws XmlPullParserException { + public StatusBar(Context context, Density density, int direction, boolean RtlEnabled) + throws XmlPullParserException { + // FIXME: if direction is RTL but it's not enabled in application manifest, mirror this bar. + super(context, density, LinearLayout.HORIZONTAL, "/bars/status_bar.xml", "status_bar.xml"); // FIXME: use FILL_H? diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/FontLoader.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/FontLoader.java index 081ce67..108b651 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/FontLoader.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/FontLoader.java @@ -52,6 +52,8 @@ public final class FontLoader { private static final String NODE_NAME = "name"; private static final String NODE_FILE = "file"; + private static final String ATTRIBUTE_VARIANT = "variant"; + private static final String ATTRIBUTE_VALUE_ELEGANT = "elegant"; private static final String FONT_SUFFIX_NONE = ".ttf"; private static final String FONT_SUFFIX_REGULAR = "-Regular.ttf"; private static final String FONT_SUFFIX_BOLD = "-Bold.ttf"; @@ -189,6 +191,7 @@ public final class FontLoader { private FontInfo mFontInfo = null; private final StringBuilder mBuilder = new StringBuilder(); private List<FontInfo> mFontList = new ArrayList<FontInfo>(); + private boolean isCompactFont = true; private FontHandler(String osFontsLocation) { super(); @@ -209,8 +212,21 @@ public final class FontLoader { mFontList = new ArrayList<FontInfo>(); } else if (NODE_FAMILY.equals(localName)) { if (mFontList != null) { + mFontInfo = null; + } + } else if (NODE_NAME.equals(localName)) { + if (mFontList != null && mFontInfo == null) { + mFontInfo = new FontInfo(); + } + } else if (NODE_FILE.equals(localName)) { + if (mFontList != null && mFontInfo == null) { mFontInfo = new FontInfo(); } + if (ATTRIBUTE_VALUE_ELEGANT.equals(attributes.getValue(ATTRIBUTE_VARIANT))) { + isCompactFont = false; + } else { + isCompactFont = true; + } } mBuilder.setLength(0); @@ -223,7 +239,9 @@ public final class FontLoader { */ @Override public void characters(char[] ch, int start, int length) throws SAXException { - mBuilder.append(ch, start, length); + if (isCompactFont) { + mBuilder.append(ch, start, length); + } } /* (non-Javadoc) @@ -259,7 +277,7 @@ public final class FontLoader { } } else if (NODE_FILE.equals(localName)) { // handle a new file for an existing Font Info - if (mFontInfo != null) { + if (isCompactFont && mFontInfo != null) { String fileName = trimXmlWhitespaces(mBuilder.toString()); Font font = getFont(fileName); if (font != null) { 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 b909bec..87047b3 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 @@ -121,7 +121,8 @@ public abstract class RenderAction<T extends RenderParams> extends FrameworkReso // build the context mContext = new BridgeContext(mParams.getProjectKey(), metrics, resources, - mParams.getProjectCallback(), getConfiguration(), mParams.getTargetSdkVersion()); + mParams.getProjectCallback(), getConfiguration(), mParams.getTargetSdkVersion(), + mParams.isRtlSupported()); setUp(); diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderSessionImpl.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderSessionImpl.java index c14af4a..9ddbbf1 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderSessionImpl.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderSessionImpl.java @@ -225,13 +225,15 @@ public class RenderSessionImpl extends RenderAction<SessionParams> { SessionParams params = getParams(); HardwareConfig hardwareConfig = params.getHardwareConfig(); BridgeContext context = getContext(); - + boolean isRtl = Bridge.isLocaleRtl(params.getLocale()); + int direction = isRtl ? View.LAYOUT_DIRECTION_RTL : View.LAYOUT_DIRECTION_LTR; // the view group that receives the window background. ViewGroup backgroundView = null; if (mWindowIsFloating || params.isForceNoDecor()) { backgroundView = mViewRoot = mContentRoot = new FrameLayout(context); + mViewRoot.setLayoutDirection(direction); } else { if (hasSoftwareButtons() && mNavigationBarOrientation == LinearLayout.VERTICAL) { /* @@ -253,12 +255,14 @@ public class RenderSessionImpl extends RenderAction<SessionParams> { the bottom */ LinearLayout topLayout = new LinearLayout(context); + topLayout.setLayoutDirection(direction); mViewRoot = topLayout; topLayout.setOrientation(LinearLayout.HORIZONTAL); try { NavigationBar navigationBar = new NavigationBar(context, - hardwareConfig.getDensity(), LinearLayout.VERTICAL); + hardwareConfig.getDensity(), LinearLayout.VERTICAL, isRtl, + params.isRtlSupported()); navigationBar.setLayoutParams( new LinearLayout.LayoutParams( mNavigationBarSize, @@ -290,6 +294,7 @@ public class RenderSessionImpl extends RenderAction<SessionParams> { LinearLayout topLayout = new LinearLayout(context); topLayout.setOrientation(LinearLayout.VERTICAL); + topLayout.setLayoutDirection(direction); // if we don't already have a view root this is it if (mViewRoot == null) { mViewRoot = topLayout; @@ -301,13 +306,22 @@ public class RenderSessionImpl extends RenderAction<SessionParams> { // this is the case of soft buttons + vertical bar. // this top layout is the first layout in the horizontal layout. see above) - mViewRoot.addView(topLayout, 0); + if (isRtl && params.isRtlSupported()) { + // If RTL is enabled, layoutlib will mirror the layouts. So, add the + // topLayout to the right of Navigation Bar and layoutlib will draw it + // to the left. + mViewRoot.addView(topLayout); + } else { + // Add the top layout to the left of the Navigation Bar. + mViewRoot.addView(topLayout, 0); + } } if (mStatusBarSize > 0) { // system bar try { - StatusBar systemBar = new StatusBar(context, hardwareConfig.getDensity()); + StatusBar systemBar = new StatusBar(context, hardwareConfig.getDensity(), + direction, params.isRtlSupported()); systemBar.setLayoutParams( new LinearLayout.LayoutParams( LayoutParams.MATCH_PARENT, mStatusBarSize)); @@ -366,7 +380,8 @@ public class RenderSessionImpl extends RenderAction<SessionParams> { // system bar try { NavigationBar navigationBar = new NavigationBar(context, - hardwareConfig.getDensity(), LinearLayout.HORIZONTAL); + hardwareConfig.getDensity(), LinearLayout.HORIZONTAL, isRtl, + params.isRtlSupported()); navigationBar.setLayoutParams( new LinearLayout.LayoutParams( LayoutParams.MATCH_PARENT, mNavigationBarSize)); diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/binding/BaseAdapter.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/binding/AdapterHelper.java index e0414fe..6c998af 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/binding/BaseAdapter.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/binding/AdapterHelper.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2011 The Android Open Source Project + * 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. @@ -16,7 +16,6 @@ package com.android.layoutlib.bridge.impl.binding; -import com.android.ide.common.rendering.api.AdapterBinding; import com.android.ide.common.rendering.api.DataBindingItem; import com.android.ide.common.rendering.api.IProjectCallback; import com.android.ide.common.rendering.api.LayoutLog; @@ -27,7 +26,6 @@ import com.android.layoutlib.bridge.android.BridgeContext; import com.android.layoutlib.bridge.impl.RenderAction; import com.android.util.Pair; -import android.database.DataSetObserver; import android.view.View; import android.view.ViewGroup; import android.widget.AdapterView; @@ -35,124 +33,27 @@ import android.widget.Checkable; import android.widget.ImageView; import android.widget.TextView; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; - /** - * Base adapter to do fake data binding in {@link AdapterView} objects. + * A Helper class to do fake data binding in {@link AdapterView} objects. */ -public class BaseAdapter { - - /** - * This is the items provided by the adapter. They are dynamically generated. - */ - protected final static class AdapterItem { - private final DataBindingItem mItem; - private final int mType; - private final int mFullPosition; - private final int mPositionPerType; - private List<AdapterItem> mChildren; - - protected AdapterItem(DataBindingItem item, int type, int fullPosition, - int positionPerType) { - mItem = item; - mType = type; - mFullPosition = fullPosition; - mPositionPerType = positionPerType; - } - - void addChild(AdapterItem child) { - if (mChildren == null) { - mChildren = new ArrayList<AdapterItem>(); - } - - mChildren.add(child); - } - - List<AdapterItem> getChildren() { - if (mChildren != null) { - return mChildren; - } - - return Collections.emptyList(); - } - - int getType() { - return mType; - } - - int getFullPosition() { - return mFullPosition; - } - - int getPositionPerType() { - return mPositionPerType; - } - - DataBindingItem getDataBindingItem() { - return mItem; - } - } - - private final AdapterBinding mBinding; - private final IProjectCallback mCallback; - private final ResourceReference mAdapterRef; - private boolean mSkipCallbackParser = false; - - protected final List<AdapterItem> mItems = new ArrayList<AdapterItem>(); - - protected BaseAdapter(ResourceReference adapterRef, AdapterBinding binding, - IProjectCallback callback) { - mAdapterRef = adapterRef; - mBinding = binding; - mCallback = callback; - } - - // ------- Some Adapter method used by all children classes. - - public boolean areAllItemsEnabled() { - return true; - } - - public boolean hasStableIds() { - return true; - } - - public boolean isEmpty() { - return mItems.size() == 0; - } - - public void registerDataSetObserver(DataSetObserver observer) { - // pass - } - - public void unregisterDataSetObserver(DataSetObserver observer) { - // pass - } - - // ------- - - - protected AdapterBinding getBinding() { - return mBinding; - } +@SuppressWarnings("deprecation") +public class AdapterHelper { - protected View getView(AdapterItem item, AdapterItem parentItem, View convertView, - ViewGroup parent) { + static Pair<View, Boolean> getView(AdapterItem item, AdapterItem parentItem, ViewGroup parent, + IProjectCallback callback, ResourceReference adapterRef, boolean skipCallbackParser) { // we don't care about recycling here because we never scroll. DataBindingItem dataBindingItem = item.getDataBindingItem(); BridgeContext context = RenderAction.getCurrentContext(); Pair<View, Boolean> pair = context.inflateView(dataBindingItem.getViewReference(), - parent, false /*attachToRoot*/, mSkipCallbackParser); + parent, false /*attachToRoot*/, skipCallbackParser); View view = pair.getFirst(); - mSkipCallbackParser |= pair.getSecond(); + skipCallbackParser |= pair.getSecond(); if (view != null) { - fillView(context, view, item, parentItem); + fillView(context, view, item, parentItem, callback, adapterRef); } else { // create a text view to display an error. TextView tv = new TextView(context); @@ -160,16 +61,16 @@ public class BaseAdapter { view = tv; } - return view; + return Pair.of(view, skipCallbackParser); } - private void fillView(BridgeContext context, View view, AdapterItem item, - AdapterItem parentItem) { + private static void fillView(BridgeContext context, View view, AdapterItem item, + AdapterItem parentItem, IProjectCallback callback, ResourceReference adapterRef) { if (view instanceof ViewGroup) { ViewGroup group = (ViewGroup) view; final int count = group.getChildCount(); for (int i = 0 ; i < count ; i++) { - fillView(context, group.getChildAt(i), item, parentItem); + fillView(context, group.getChildAt(i), item, parentItem, callback, adapterRef); } } else { int id = view.getId(); @@ -184,8 +85,8 @@ public class BaseAdapter { if (view instanceof TextView) { TextView tv = (TextView) view; - Object value = mCallback.getAdapterItemValue( - mAdapterRef, context.getViewKey(view), + Object value = callback.getAdapterItemValue( + adapterRef, context.getViewKey(view), item.getDataBindingItem().getViewReference(), fullPosition, positionPerType, fullParentPosition, parentPositionPerType, @@ -204,8 +105,8 @@ public class BaseAdapter { if (view instanceof Checkable) { Checkable cb = (Checkable) view; - Object value = mCallback.getAdapterItemValue( - mAdapterRef, context.getViewKey(view), + Object value = callback.getAdapterItemValue( + adapterRef, context.getViewKey(view), item.getDataBindingItem().getViewReference(), fullPosition, positionPerType, fullParentPosition, parentPositionPerType, @@ -224,8 +125,8 @@ public class BaseAdapter { if (view instanceof ImageView) { ImageView iv = (ImageView) view; - Object value = mCallback.getAdapterItemValue( - mAdapterRef, context.getViewKey(view), + Object value = callback.getAdapterItemValue( + adapterRef, context.getViewKey(view), item.getDataBindingItem().getViewReference(), fullPosition, positionPerType, fullParentPosition, parentPositionPerType, diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/binding/AdapterItem.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/binding/AdapterItem.java new file mode 100644 index 0000000..8e28dba --- /dev/null +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/binding/AdapterItem.java @@ -0,0 +1,74 @@ +/* + * 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 com.android.layoutlib.bridge.impl.binding; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import com.android.ide.common.rendering.api.DataBindingItem; + +/** + * This is the items provided by the adapter. They are dynamically generated. + */ +final class AdapterItem { + private final DataBindingItem mItem; + private final int mType; + private final int mFullPosition; + private final int mPositionPerType; + private List<AdapterItem> mChildren; + + protected AdapterItem(DataBindingItem item, int type, int fullPosition, + int positionPerType) { + mItem = item; + mType = type; + mFullPosition = fullPosition; + mPositionPerType = positionPerType; + } + + void addChild(AdapterItem child) { + if (mChildren == null) { + mChildren = new ArrayList<AdapterItem>(); + } + + mChildren.add(child); + } + + List<AdapterItem> getChildren() { + if (mChildren != null) { + return mChildren; + } + + return Collections.emptyList(); + } + + int getType() { + return mType; + } + + int getFullPosition() { + return mFullPosition; + } + + int getPositionPerType() { + return mPositionPerType; + } + + DataBindingItem getDataBindingItem() { + return mItem; + } +} diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/binding/FakeAdapter.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/binding/FakeAdapter.java index 22570b9..9a13f5a 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/binding/FakeAdapter.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/binding/FakeAdapter.java @@ -20,10 +20,12 @@ import com.android.ide.common.rendering.api.AdapterBinding; import com.android.ide.common.rendering.api.DataBindingItem; import com.android.ide.common.rendering.api.IProjectCallback; import com.android.ide.common.rendering.api.ResourceReference; +import com.android.util.Pair; import android.view.View; import android.view.ViewGroup; import android.widget.AdapterView; +import android.widget.BaseAdapter; import android.widget.ListAdapter; import android.widget.SpinnerAdapter; @@ -35,17 +37,23 @@ import java.util.List; * and {@link SpinnerAdapter}. * */ -public class FakeAdapter extends BaseAdapter implements ListAdapter, SpinnerAdapter { +@SuppressWarnings("deprecation") +public class FakeAdapter extends BaseAdapter { // don't use a set because the order is important. private final List<ResourceReference> mTypes = new ArrayList<ResourceReference>(); + private final IProjectCallback mCallback; + private final ResourceReference mAdapterRef; + private final List<AdapterItem> mItems = new ArrayList<AdapterItem>(); + private boolean mSkipCallbackParser = false; public FakeAdapter(ResourceReference adapterRef, AdapterBinding binding, IProjectCallback callback) { - super(adapterRef, binding, callback); + mAdapterRef = adapterRef; + mCallback = callback; - final int repeatCount = getBinding().getRepeatCount(); - final int itemCount = getBinding().getItemCount(); + final int repeatCount = binding.getRepeatCount(); + final int itemCount = binding.getItemCount(); // Need an array to count for each type. // This is likely too big, but is the max it can be. @@ -54,7 +62,7 @@ public class FakeAdapter extends BaseAdapter implements ListAdapter, SpinnerAdap // We put several repeating sets. for (int r = 0 ; r < repeatCount ; r++) { // loop on the type of list items, and add however many for each type. - for (DataBindingItem dataBindingItem : getBinding()) { + for (DataBindingItem dataBindingItem : binding) { ResourceReference viewRef = dataBindingItem.getViewReference(); int typeIndex = mTypes.indexOf(viewRef); if (typeIndex == -1) { @@ -103,7 +111,11 @@ public class FakeAdapter extends BaseAdapter implements ListAdapter, SpinnerAdap public View getView(int position, View convertView, ViewGroup parent) { // we don't care about recycling here because we never scroll. AdapterItem item = mItems.get(position); - return getView(item, null /*parentGroup*/, convertView, parent); + Pair<View, Boolean> pair = AdapterHelper.getView(item, null /*parentGroup*/, parent, + mCallback, mAdapterRef, mSkipCallbackParser); + mSkipCallbackParser = pair.getSecond(); + return pair.getFirst(); + } @Override diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/binding/FakeExpandableAdapter.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/binding/FakeExpandableAdapter.java index 199e040..e539579 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/binding/FakeExpandableAdapter.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/binding/FakeExpandableAdapter.java @@ -20,7 +20,9 @@ import com.android.ide.common.rendering.api.AdapterBinding; import com.android.ide.common.rendering.api.DataBindingItem; import com.android.ide.common.rendering.api.IProjectCallback; import com.android.ide.common.rendering.api.ResourceReference; +import com.android.util.Pair; +import android.database.DataSetObserver; import android.view.View; import android.view.ViewGroup; import android.widget.ExpandableListAdapter; @@ -29,8 +31,14 @@ import android.widget.HeterogeneousExpandableList; import java.util.ArrayList; import java.util.List; -public class FakeExpandableAdapter extends BaseAdapter implements ExpandableListAdapter, - HeterogeneousExpandableList { +@SuppressWarnings("deprecation") +public class FakeExpandableAdapter implements ExpandableListAdapter, HeterogeneousExpandableList { + + private final IProjectCallback mCallback; + private final ResourceReference mAdapterRef; + private boolean mSkipCallbackParser = false; + + protected final List<AdapterItem> mItems = new ArrayList<AdapterItem>(); // don't use a set because the order is important. private final List<ResourceReference> mGroupTypes = new ArrayList<ResourceReference>(); @@ -38,7 +46,8 @@ public class FakeExpandableAdapter extends BaseAdapter implements ExpandableList public FakeExpandableAdapter(ResourceReference adapterRef, AdapterBinding binding, IProjectCallback callback) { - super(adapterRef, binding, callback); + mAdapterRef = adapterRef; + mCallback = callback; createItems(binding, binding.getItemCount(), binding.getRepeatCount(), mGroupTypes, 1); } @@ -125,7 +134,10 @@ public class FakeExpandableAdapter extends BaseAdapter implements ExpandableList ViewGroup parent) { // we don't care about recycling here because we never scroll. AdapterItem item = mItems.get(groupPosition); - return getView(item, null /*parentItem*/, convertView, parent); + Pair<View, Boolean> pair = AdapterHelper.getView(item, null /*parentItem*/, parent, + mCallback, mAdapterRef, mSkipCallbackParser); + mSkipCallbackParser = pair.getSecond(); + return pair.getFirst(); } @Override @@ -134,7 +146,10 @@ public class FakeExpandableAdapter extends BaseAdapter implements ExpandableList // we don't care about recycling here because we never scroll. AdapterItem parentItem = mItems.get(groupPosition); AdapterItem item = getChildItem(groupPosition, childPosition); - return getView(item, parentItem, convertView, parent); + Pair<View, Boolean> pair = AdapterHelper.getView(item, parentItem, parent, mCallback, + mAdapterRef, mSkipCallbackParser); + mSkipCallbackParser = pair.getSecond(); + return pair.getFirst(); } @Override @@ -172,6 +187,31 @@ public class FakeExpandableAdapter extends BaseAdapter implements ExpandableList // pass } + @Override + public void registerDataSetObserver(DataSetObserver observer) { + // pass + } + + @Override + public void unregisterDataSetObserver(DataSetObserver observer) { + // pass + } + + @Override + public boolean hasStableIds() { + return true; + } + + @Override + public boolean areAllItemsEnabled() { + return true; + } + + @Override + public boolean isEmpty() { + return mItems.isEmpty(); + } + // ---- HeterogeneousExpandableList @Override |
