diff options
| author | 2017-01-16 18:05:48 +0000 | |
|---|---|---|
| committer | 2017-01-16 18:05:51 +0000 | |
| commit | 74f50acfc485f79564a2c2dae54731cbf7bbd985 (patch) | |
| tree | 19754fd9bfd334df18bccf7f9e84465b5e8bb86a /tools | |
| parent | e92b15ca062453d4ecbf8101cabb18ab1fad87ef (diff) | |
| parent | 0c864fde5882488623665c793701641329e5955f (diff) | |
Merge changes Ia7a8a27f,I0646aab6,Ia324f18d,I38c6c6df,Ic9214929, ...
* changes:
Add missing attributes to old themes
Fix plurals support in layoutlib
Do not log errors while loading string arrays
Hack to deal with android:pointerIcon in layoutlib
Force animateFirstValue to false
Avoid passing antialias when measuring scaled text
Catch all exceptions in NativeAllocationRegistry free
Diffstat (limited to 'tools')
11 files changed, 202 insertions, 32 deletions
diff --git a/tools/layoutlib/bridge/src/android/content/res/Resources_Delegate.java b/tools/layoutlib/bridge/src/android/content/res/Resources_Delegate.java index c3d4cef61b35..e0f8e1c33bc6 100644 --- a/tools/layoutlib/bridge/src/android/content/res/Resources_Delegate.java +++ b/tools/layoutlib/bridge/src/android/content/res/Resources_Delegate.java @@ -21,6 +21,7 @@ import com.android.ide.common.rendering.api.ArrayResourceValue; import com.android.ide.common.rendering.api.DensityBasedResourceValue; import com.android.ide.common.rendering.api.LayoutLog; import com.android.ide.common.rendering.api.LayoutlibCallback; +import com.android.ide.common.rendering.api.PluralsResourceValue; import com.android.ide.common.rendering.api.RenderResources; import com.android.ide.common.rendering.api.ResourceValue; import com.android.layoutlib.bridge.Bridge; @@ -43,6 +44,7 @@ import android.annotation.Nullable; import android.content.res.Resources.NotFoundException; import android.content.res.Resources.Theme; import android.graphics.drawable.Drawable; +import android.icu.text.PluralRules; import android.util.AttributeSet; import android.util.DisplayMetrics; import android.util.LruCache; @@ -405,9 +407,6 @@ public class Resources_Delegate { rv = resources.mContext.getRenderResources().resolveResValue(rv); if (rv != null) { return rv.getValue(); - } else { - Bridge.getLog().error(LayoutLog.TAG_RESOURCES_RESOLVE, - "Unable to resolve resource " + ref, null); } } // Not a reference. @@ -738,6 +737,48 @@ public class Resources_Delegate { } @LayoutlibDelegate + static String getQuantityString(Resources resources, int id, int quantity) throws + NotFoundException { + Pair<String, ResourceValue> value = getResourceValue(resources, id, mPlatformResourceFlag); + + if (value != null) { + if (value.getSecond() instanceof PluralsResourceValue) { + PluralsResourceValue pluralsResourceValue = (PluralsResourceValue) value.getSecond(); + PluralRules pluralRules = PluralRules.forLocale(resources.getConfiguration().getLocales() + .get(0)); + String strValue = pluralsResourceValue.getValue(pluralRules.select(quantity)); + if (strValue == null) { + strValue = pluralsResourceValue.getValue(PluralRules.KEYWORD_OTHER); + } + + return strValue; + } + else { + return value.getSecond().getValue(); + } + } + + // id was not found or not resolved. Throw a NotFoundException. + throwException(resources, id); + + // this is not used since the method above always throws + return null; + } + + @LayoutlibDelegate + static String getQuantityString(Resources resources, int id, int quantity, Object... formatArgs) + throws NotFoundException { + String raw = getQuantityString(resources, id, quantity); + return String.format(resources.getConfiguration().getLocales().get(0), raw, formatArgs); + } + + @LayoutlibDelegate + static CharSequence getQuantityText(Resources resources, int id, int quantity) throws + NotFoundException { + return getQuantityString(resources, id, quantity); + } + + @LayoutlibDelegate static void getValue(Resources resources, int id, TypedValue outValue, boolean resolveRefs) throws NotFoundException { Pair<String, ResourceValue> value = getResourceValue(resources, id, mPlatformResourceFlag); diff --git a/tools/layoutlib/bridge/src/android/graphics/BidiRenderer.java b/tools/layoutlib/bridge/src/android/graphics/BidiRenderer.java index 21f36ceb0c1b..c6827a3c9c5f 100644 --- a/tools/layoutlib/bridge/src/android/graphics/BidiRenderer.java +++ b/tools/layoutlib/bridge/src/android/graphics/BidiRenderer.java @@ -30,6 +30,7 @@ import java.awt.Graphics2D; import java.awt.Toolkit; import java.awt.font.FontRenderContext; import java.awt.font.GlyphVector; +import java.awt.geom.AffineTransform; import java.awt.geom.Rectangle2D; import java.util.ArrayList; import java.util.LinkedList; @@ -41,6 +42,7 @@ import java.util.List; */ @SuppressWarnings("deprecation") public class BidiRenderer { + private static String JAVA_VENDOR = System.getProperty("java.vendor"); private static class ScriptRun { int start; @@ -221,9 +223,16 @@ public class BidiRenderer { frc = mGraphics.getFontRenderContext(); } else { frc = Toolkit.getDefaultToolkit().getFontMetrics(font).getFontRenderContext(); + // Metrics obtained this way don't have anti-aliasing set. So, // we create a new FontRenderContext with anti-aliasing set. - frc = new FontRenderContext(font.getTransform(), mPaint.isAntiAliased(), frc.usesFractionalMetrics()); + AffineTransform transform = font.getTransform(); + if (mPaint.isAntiAliased() && + // Workaround for http://b.android.com/211659 + (transform.getScaleX() <= 9.9 || + !"JetBrains s.r.o".equals(JAVA_VENDOR))) { + frc = new FontRenderContext(transform, true, frc.usesFractionalMetrics()); + } } GlyphVector gv = font.layoutGlyphVector(frc, mText, start, limit, flag); int ng = gv.getNumGlyphs(); diff --git a/tools/layoutlib/bridge/src/android/view/PointerIcon_Delegate.java b/tools/layoutlib/bridge/src/android/view/PointerIcon_Delegate.java new file mode 100644 index 000000000000..4a5ea9b5bb65 --- /dev/null +++ b/tools/layoutlib/bridge/src/android/view/PointerIcon_Delegate.java @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.view; + +import com.android.tools.layoutlib.annotations.LayoutlibDelegate; + +import android.content.Context; +import android.content.res.Resources; + +public class PointerIcon_Delegate { + + @LayoutlibDelegate + /*package*/ static void loadResource(PointerIcon icon, Context context, Resources resources, + int resourceId) { + // HACK: This bypasses the problem of having an enum resolved as a resourceId. + // PointerIcon would not be displayed by layoutlib anyway, so we always return the null + // icon. + } +} diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java index 1b3b563fb51e..663e56d2ccdf 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 @@ -66,7 +66,6 @@ import android.graphics.Bitmap; import android.graphics.drawable.Drawable; import android.hardware.display.DisplayManager; import android.net.Uri; -import android.os.Build.VERSION_CODES; import android.os.Bundle; import android.os.Handler; import android.os.IBinder; @@ -104,6 +103,7 @@ import java.util.IdentityHashMap; import java.util.List; import java.util.Map; +import static android.os.Build.VERSION_CODES.JELLY_BEAN_MR1; import static com.android.layoutlib.bridge.android.RenderParamsFlags.FLAG_KEY_APPLICATION_PACKAGE; /** @@ -113,6 +113,28 @@ import static com.android.layoutlib.bridge.android.RenderParamsFlags.FLAG_KEY_AP public final class BridgeContext extends Context { private static final String PREFIX_THEME_APPCOMPAT = "Theme.AppCompat"; + private static final Map<String, ResourceValue> FRAMEWORK_PATCHED_VALUES = new HashMap<>(2); + private static final Map<String, ResourceValue> FRAMEWORK_REPLACE_VALUES = new HashMap<>(3); + + static { + FRAMEWORK_PATCHED_VALUES.put("animateFirstView", new ResourceValue( + ResourceType.BOOL, "animateFirstView", "false", false)); + FRAMEWORK_PATCHED_VALUES.put("animateLayoutChanges", + new ResourceValue(ResourceType.BOOL, "animateLayoutChanges", "false", false)); + + + FRAMEWORK_REPLACE_VALUES.put("textEditSuggestionItemLayout", + new ResourceValue(ResourceType.LAYOUT, "textEditSuggestionItemLayout", + "text_edit_suggestion_item", true)); + FRAMEWORK_REPLACE_VALUES.put("textEditSuggestionContainerLayout", + new ResourceValue(ResourceType.LAYOUT, "textEditSuggestionContainerLayout", + "text_edit_suggestion_container", true)); + FRAMEWORK_REPLACE_VALUES.put("textEditSuggestionHighlightStyle", + new ResourceValue(ResourceType.STYLE, "textEditSuggestionHighlightStyle", + "TextAppearance.Holo.SuggestionHighlight", true)); + + } + /** The map adds cookies to each view so that IDE can link xml tags to views. */ private final HashMap<View, Object> mViewKeyMap = new HashMap<>(); /** @@ -312,7 +334,7 @@ public final class BridgeContext extends Context { * Returns the current parser at the top the of the stack. * @return a parser or null. */ - public BridgeXmlBlockParser getCurrentParser() { + private BridgeXmlBlockParser getCurrentParser() { return mParserStack.peek(); } @@ -406,7 +428,8 @@ public final class BridgeContext extends Context { } public Pair<View, Boolean> inflateView(ResourceReference resource, ViewGroup parent, - boolean attachToRoot, boolean skipCallbackParser) { + @SuppressWarnings("SameParameterValue") boolean attachToRoot, + boolean skipCallbackParser) { boolean isPlatformLayout = resource.isFramework(); if (!isPlatformLayout && !skipCallbackParser) { @@ -711,11 +734,7 @@ public final class BridgeContext extends Context { Object key = parser.getViewCookie(); if (key != null) { - defaultPropMap = mDefaultPropMaps.get(key); - if (defaultPropMap == null) { - defaultPropMap = new PropertiesMap(); - mDefaultPropMaps.put(key, defaultPropMap); - } + defaultPropMap = mDefaultPropMaps.computeIfAbsent(key, k -> new PropertiesMap()); } } else if (set instanceof BridgeLayoutParamsMapAttributes) { @@ -909,6 +928,16 @@ public final class BridgeContext extends Context { // if there's no direct value for this attribute in the XML, we look for default // values in the widget defStyle, and then in the theme. if (value == null) { + if (frameworkAttr) { + // For some framework values, layoutlib patches the actual value in the + // theme when it helps to improve the final preview. In most cases + // we just disable animations. + ResourceValue patchedValue = FRAMEWORK_PATCHED_VALUES.get(attrName); + if (patchedValue != null) { + defaultValue = patchedValue; + } + } + // if we found a value, we make sure this doesn't reference another value. // So we resolve it. if (defaultValue != null) { @@ -916,16 +945,21 @@ public final class BridgeContext extends Context { // exist, we should log a warning and omit it. String val = defaultValue.getValue(); if (val != null && val.startsWith(SdkConstants.PREFIX_THEME_REF)) { - if (!attrName.equals(RTL_ATTRS.get(val)) || - getApplicationInfo().targetSdkVersion < - VERSION_CODES.JELLY_BEAN_MR1) { + // Because we always use the latest framework code, some resources might + // fail to resolve when using old themes (they haven't been backported). + // Since this is an artifact caused by us using always the latest + // code, we check for some of those values and replace them here. + defaultValue = FRAMEWORK_REPLACE_VALUES.get(attrName); + + if (defaultValue == null && + (getApplicationInfo().targetSdkVersion < JELLY_BEAN_MR1 || + !attrName.equals(RTL_ATTRS.get(val)))) { // Only log a warning if the referenced value isn't one of the RTL // attributes, or the app targets old API. Bridge.getLog().warning(LayoutLog.TAG_RESOURCES_RESOLVE_THEME_ATTR, String.format("Failed to find '%s' in current theme.", val), val); } - defaultValue = null; } } @@ -1944,7 +1978,7 @@ public final class BridgeContext extends Context { Map<List<StyleResourceValue>, Map<Integer, Pair<BridgeTypedArray, PropertiesMap>>>> mCache; - public TypedArrayCache() { + private TypedArrayCache() { mCache = new IdentityHashMap<>(); } @@ -1965,17 +1999,9 @@ public final class BridgeContext extends Context { public void put(int[] attrs, List<StyleResourceValue> themes, int resId, Pair<BridgeTypedArray, PropertiesMap> value) { Map<List<StyleResourceValue>, Map<Integer, Pair<BridgeTypedArray, PropertiesMap>>> - cacheFromThemes = mCache.get(attrs); - if (cacheFromThemes == null) { - cacheFromThemes = new HashMap<>(); - mCache.put(attrs, cacheFromThemes); - } + cacheFromThemes = mCache.computeIfAbsent(attrs, k -> new HashMap<>()); Map<Integer, Pair<BridgeTypedArray, PropertiesMap>> cacheFromResId = - cacheFromThemes.get(themes); - if (cacheFromResId == null) { - cacheFromResId = new HashMap<>(); - cacheFromThemes.put(themes, cacheFromResId); - } + cacheFromThemes.computeIfAbsent(themes, k -> new HashMap<>()); cacheFromResId.put(resId, value); } diff --git a/tools/layoutlib/bridge/src/libcore/util/NativeAllocationRegistry_Delegate.java b/tools/layoutlib/bridge/src/libcore/util/NativeAllocationRegistry_Delegate.java index 6246ec1b8661..04fabc242454 100644 --- a/tools/layoutlib/bridge/src/libcore/util/NativeAllocationRegistry_Delegate.java +++ b/tools/layoutlib/bridge/src/libcore/util/NativeAllocationRegistry_Delegate.java @@ -52,9 +52,15 @@ public class NativeAllocationRegistry_Delegate { @LayoutlibDelegate /*package*/ static void applyFreeFunction(long freeFunction, long nativePtr) { - NativeAllocationRegistry_Delegate delegate = sManager.getDelegate(freeFunction); - if (delegate != null) { - delegate.mFinalizer.free(nativePtr); + // This method MIGHT run in the context of the finalizer thread. If the delegate method + // crashes, it could bring down the VM. That's why we catch all the exceptions and ignore + // them. + try { + NativeAllocationRegistry_Delegate delegate = sManager.getDelegate(freeFunction); + if (delegate != null) { + delegate.mFinalizer.free(nativePtr); + } + } catch (Throwable ignore) { } } diff --git a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/golden/simple_activity-old-theme.png b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/golden/simple_activity-old-theme.png Binary files differnew file mode 100644 index 000000000000..eb431b0280da --- /dev/null +++ b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/golden/simple_activity-old-theme.png diff --git a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/src/main/res/layout/allwidgets.xml b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/src/main/res/layout/allwidgets.xml index adb58a322abb..05a3665deab1 100644 --- a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/src/main/res/layout/allwidgets.xml +++ b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/src/main/res/layout/allwidgets.xml @@ -28,7 +28,8 @@ android:layout_alignParentStart="true" android:layout_below="@id/frameLayout" android:text="Large Text" - android:textAppearance="?android:attr/textAppearanceLarge" /> + android:textAppearance="?android:attr/textAppearanceLarge" + android:pointerIcon="hand" /> <TextView android:id="@id/textView3" diff --git a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/src/main/res/values/arrays.xml b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/src/main/res/values/arrays.xml index f6e14d2b83b9..5f58d390bf32 100644 --- a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/src/main/res/values/arrays.xml +++ b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/src/main/res/values/arrays.xml @@ -17,6 +17,7 @@ <!-- theme ref in android NS. value = @string/candidates_style = <u>candidates</u> --> <item>?android:attr/candidatesTextStyleSpans</item> <item>@android:string/unknownName</item> <!-- value = Unknown --> + <item>?EC</item> </string-array> <!-- resources that the above array can refer to --> diff --git a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/src/main/res/values/styles.xml b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/src/main/res/values/styles.xml index c8a5fec71f09..debe33bea72e 100644 --- a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/src/main/res/values/styles.xml +++ b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/src/main/res/values/styles.xml @@ -3,7 +3,6 @@ <!-- Base application theme. --> <style name="AppTheme" parent="android:Theme.Material.Light.DarkActionBar"> <item name="myattr">@integer/ten</item> - <item name="android:animateFirstView">false</item> </style> </resources> diff --git a/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/intensive/Main.java b/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/intensive/Main.java index 6cdd0ead85f5..d0c04d7e3de4 100644 --- a/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/intensive/Main.java +++ b/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/intensive/Main.java @@ -69,6 +69,7 @@ import java.util.concurrent.TimeUnit; import com.google.android.collect.Lists; +import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; @@ -313,6 +314,12 @@ public class Main { sBridge = new Bridge(); sBridge.init(ConfigGenerator.loadProperties(buildProp), fontLocation, ConfigGenerator.getEnumMap(attrs), getLayoutLog()); + Bridge.getLock().lock(); + try { + Bridge.setLog(getLayoutLog()); + } finally { + Bridge.getLock().unlock(); + } } @Before @@ -327,6 +334,20 @@ public class Main { } @Test + public void testActivityOnOldTheme() throws ClassNotFoundException { + LayoutLibTestCallback layoutLibCallback = + new LayoutLibTestCallback(getLogger(), mDefaultClassLoader); + layoutLibCallback.initResources(); + + LayoutPullParser parser = createLayoutPullParser("simple_activity.xml"); + SessionParams params = getSessionParams(parser, ConfigGenerator.NEXUS_5, + layoutLibCallback, "Theme.NoTitleBar", false, + RenderingMode.NORMAL, 22); + + renderAndVerify(params, "simple_activity-old-theme.png"); + } + + @Test public void testTranslucentBars() throws ClassNotFoundException { LayoutLibTestCallback layoutLibCallback = new LayoutLibTestCallback(getLogger(), mDefaultClassLoader); @@ -641,6 +662,36 @@ public class Main { assertEquals("app_name", resources.getResourceEntryName(id)); } + @Test + public void testStringEscaping() throws Exception { + // Setup + // Create the layout pull parser for our resources (empty.xml can not be part of the test + // app as it won't compile). + LayoutPullParser parser = new LayoutPullParser("/empty.xml"); + // Create LayoutLibCallback. + LayoutLibTestCallback layoutLibCallback = + new LayoutLibTestCallback(getLogger(), mDefaultClassLoader); + layoutLibCallback.initResources(); + SessionParams params = getSessionParams(parser, ConfigGenerator.NEXUS_4, + layoutLibCallback, "AppTheme", true, RenderingMode.NORMAL, 22); + AssetManager assetManager = AssetManager.getSystem(); + DisplayMetrics metrics = new DisplayMetrics(); + Configuration configuration = RenderAction.getConfiguration(params); + Resources resources = new Resources(assetManager, metrics, configuration); + resources.mLayoutlibCallback = params.getLayoutlibCallback(); + resources.mContext = + new BridgeContext(params.getProjectKey(), metrics, params.getResources(), + params.getAssets(), params.getLayoutlibCallback(), configuration, + params.getTargetSdkVersion(), params.isRtlSupported()); + + int id = resources.mLayoutlibCallback.getResourceId(ResourceType.ARRAY, "string_array"); + String[] strings = resources.getStringArray(id); + assertArrayEquals( + new String[]{"mystring", "Hello world!", "candidates", "Unknown", "?EC"}, + strings); + assertTrue(sRenderMessages.isEmpty()); + } + @NonNull private LayoutPullParser createLayoutPullParser(String layoutPath) { return new LayoutPullParser(APP_TEST_RES + "/layout/" + layoutPath); diff --git a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java index b6aaac8d5b6e..741eb27558ed 100644 --- a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java +++ b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java @@ -154,6 +154,8 @@ public final class CreateInfo implements ICreateInfo { "android.content.res.Resources#getIntArray", "android.content.res.Resources#getInteger", "android.content.res.Resources#getLayout", + "android.content.res.Resources#getQuantityString", + "android.content.res.Resources#getQuantityText", "android.content.res.Resources#getResourceEntryName", "android.content.res.Resources#getResourceName", "android.content.res.Resources#getResourcePackageName", @@ -232,6 +234,7 @@ public final class CreateInfo implements ICreateInfo { "android.view.RenderNode#nSetScaleY", "android.view.RenderNode#nGetScaleY", "android.view.RenderNode#nIsPivotExplicitlySet", + "android.view.PointerIcon#loadResource", "android.view.ViewGroup#drawChild", "com.android.internal.view.menu.MenuBuilder#createNewMenuItem", "com.android.internal.util.XmlUtils#convertValueToInt", |