From e3f9834ebd8a6e4ee155c8e4c9456dfc98e8b832 Mon Sep 17 00:00:00 2001 From: Deepanshu Gupta Date: Mon, 27 Jul 2015 11:50:43 -0700 Subject: [PATCH 1/8] LayoutLib: Fix Status/Navigation Bar color. If the color value for status bar or navigation bar is declared directly in the theme (i.e. doesn't reference a color resource via @color/foo), the ResourceType for the attribute is not assigned by the IDE. LayoutLib used to ignore resources that were not of type color. This change fixes the issue by also checking the resources without a type. Change-Id: I94735ec225415282db06ab9db5c3233ad89c052f --- .../layoutlib/bridge/bars/CustomBar.java | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) 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 145a03a272744..b76ec1707fcc9 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 @@ -16,6 +16,7 @@ package com.android.layoutlib.bridge.bars; +import com.android.ide.common.rendering.api.LayoutLog; import com.android.ide.common.rendering.api.RenderResources; import com.android.ide.common.rendering.api.ResourceValue; import com.android.ide.common.rendering.api.StyleResourceValue; @@ -258,8 +259,21 @@ abstract class CustomBar extends LinearLayout { ResourceValue resource = renderResources.findItemInTheme(attr, true); // Form @color/bar to the #AARRGGBB resource = renderResources.resolveResValue(resource); - if (resource != null && ResourceType.COLOR.equals(resource.getResourceType())) { - return ResourceHelper.getColor(resource.getValue()); + if (resource != null) { + ResourceType type = resource.getResourceType(); + if (type == null || type == ResourceType.COLOR) { + // if no type is specified, the value may have been specified directly in the style + // file, rather than referencing a color resource value. + try { + return ResourceHelper.getColor(resource.getValue()); + } catch (NumberFormatException e) { + // Conversion failed. + Bridge.getLog().warning(LayoutLog.TAG_RESOURCES_FORMAT, + "Theme attribute @android:" + attr + + " does not reference a color, instead is '" + + resource.getValue() + "'.", resource); + } + } } return 0; } From 6e8d8ad2b7cffed751b2a382ac7b761954767188 Mon Sep 17 00:00:00 2001 From: Deepanshu Gupta Date: Tue, 28 Jul 2015 18:27:28 -0700 Subject: [PATCH 2/8] LayoutLib: fix layout when nav bar is absent. Change-Id: I0d3ee6fd5a4656156a630d3b298533ea13eb8415 --- .../android/layoutlib/bridge/impl/Layout.java | 31 ++++++++++++------- 1 file changed, 19 insertions(+), 12 deletions(-) diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/Layout.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/Layout.java index 89d8319fc67dc..d1c9f21b7f915 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/Layout.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/Layout.java @@ -165,13 +165,13 @@ class Layout extends RelativeLayout { FrameLayout contentRoot = new FrameLayout(getContext()); LayoutParams params = createLayoutParams(MATCH_PARENT, MATCH_PARENT); int rule = mBuilder.isNavBarVertical() ? START_OF : ABOVE; - if (mBuilder.solidBars()) { + if (mBuilder.hasNavBar() && mBuilder.solidBars()) { params.addRule(rule, getId(ID_NAV_BAR)); } int below = -1; if (mBuilder.mActionBarSize <= 0 && mBuilder.mTitleBarSize > 0) { below = getId(ID_TITLE_BAR); - } else if (mBuilder.solidBars()) { + } else if (mBuilder.hasStatusBar() && mBuilder.solidBars()) { below = getId(ID_STATUS_BAR); } if (below != -1) { @@ -238,10 +238,10 @@ class Layout extends RelativeLayout { } LayoutParams layoutParams = createLayoutParams(MATCH_PARENT, MATCH_PARENT); int rule = mBuilder.isNavBarVertical() ? START_OF : ABOVE; - if (mBuilder.solidBars()) { + if (mBuilder.hasNavBar() && mBuilder.solidBars()) { layoutParams.addRule(rule, getId(ID_NAV_BAR)); } - if (mBuilder.solidBars()) { + if (mBuilder.hasStatusBar() && mBuilder.solidBars()) { layoutParams.addRule(BELOW, getId(ID_STATUS_BAR)); } actionBar.getRootView().setLayoutParams(layoutParams); @@ -254,7 +254,7 @@ class Layout extends RelativeLayout { int simulatedPlatformVersion) { TitleBar titleBar = new TitleBar(context, title, simulatedPlatformVersion); LayoutParams params = createLayoutParams(MATCH_PARENT, mBuilder.mTitleBarSize); - if (mBuilder.solidBars()) { + if (mBuilder.hasStatusBar() && mBuilder.solidBars()) { params.addRule(BELOW, getId(ID_STATUS_BAR)); } if (mBuilder.isNavBarVertical() && mBuilder.solidBars()) { @@ -333,10 +333,6 @@ class Layout extends RelativeLayout { findNavBar(); } - public boolean isNavBarVertical() { - return mNavBarOrientation == VERTICAL; - } - private void findBackground() { if (!mParams.isBgColorOverridden()) { mWindowBackground = mResources.findItemInTheme(ATTR_WINDOW_BACKGROUND, true); @@ -444,16 +440,27 @@ class Layout extends RelativeLayout { } /** - * Return if both status bar and nav bar are solid (content doesn't overlap with these - * bars). + * Return true if the status bar or nav bar are present, they are not translucent (i.e + * content doesn't overlap with them). */ private boolean solidBars() { - return hasNavBar() && !mTranslucentNav && !mTranslucentStatus && mStatusBarSize > 0; + return !(hasNavBar() && mTranslucentNav) && !(hasStatusBar() && mTranslucentStatus); } private boolean hasNavBar() { return Config.showOnScreenNavBar(mParams.getSimulatedPlatformVersion()) && hasSoftwareButtons() && mNavBarSize > 0; } + + private boolean hasStatusBar() { + return mStatusBarSize > 0; + } + + /** + * Return true if the nav bar is present and is vertical. + */ + private boolean isNavBarVertical() { + return hasNavBar() && mNavBarOrientation == VERTICAL; + } } } From 4a6ee98d39ab2300b2cd17c60221e43bf4ade8e7 Mon Sep 17 00:00:00 2001 From: Deepanshu Gupta Date: Wed, 29 Jul 2015 11:12:44 -0700 Subject: [PATCH 3/8] Fix theme namespace in tests. The tests used framework theme (Material), but the ResourceResolver was created with project theme. This caused tests to fail becasue of missing resources. Change-Id: Ia17ee19054f6689f85e3423e83c1f47a2cc4364f --- .../src/com/android/layoutlib/bridge/intensive/Main.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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 b2909c94de8e3..ee448caf97fe2 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 @@ -285,7 +285,7 @@ public class Main { ConfigGenerator.getEnumMap(attrs), getLayoutLog()); } - /** Text activity.xml */ + /** Test activity.xml */ @Test public void testActivity() throws ClassNotFoundException { renderAndVerify("activity.xml", "activity.png"); @@ -404,7 +404,7 @@ public class Main { ResourceResolver resourceResolver = ResourceResolver.create(sProjectResources.getConfiguredResources(config), sFrameworkRepo.getConfiguredResources(config), - themeName, true); + themeName, false); return new SessionParams( layoutParser, From 214466c777e11eb7c8ab569b8fd7d2c38357003a Mon Sep 17 00:00:00 2001 From: Deepanshu Gupta Date: Wed, 29 Jul 2015 14:23:38 -0700 Subject: [PATCH 4/8] Include EpicenterTranslateClipReveal in LayoutLib Needed by Spinner to create transitions that are then ignored :) Change-Id: Ibe7167a643ecb186e97c9af446c0e1d2eb2d6f86 --- .../create/src/com/android/tools/layoutlib/create/Main.java | 1 + 1 file changed, 1 insertion(+) diff --git a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/Main.java b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/Main.java index 2951edb0e5ac0..383168face865 100644 --- a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/Main.java +++ b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/Main.java @@ -119,6 +119,7 @@ public class Main { "android.icu.**", // needed by LayoutLib "android.annotation.NonNull", // annotations "android.annotation.Nullable", // annotations + "com.android.internal.transition.EpicenterTranslateClipReveal", }, excludeClasses, new String[] { From 78aa664b027dbcebb0d0dc1dca9f3a8172d9a78c Mon Sep 17 00:00:00 2001 From: Deepanshu Gupta Date: Wed, 29 Jul 2015 14:24:25 -0700 Subject: [PATCH 5/8] Poor implementation of Path.approximate() Include a very poor implementation of Path.approximate() in LayoutLib that only returns the first and last coordinate of the path. This avoids crashing the rendering when certain assumptions are made on the value returned from the method. Also change the error to a warning. Change-Id: I9241dec734d4df90a9eebd1e6de8b3e443eee3c8 --- .../src/android/graphics/Path_Delegate.java | 56 ++++++++++++++++++- 1 file changed, 53 insertions(+), 3 deletions(-) diff --git a/tools/layoutlib/bridge/src/android/graphics/Path_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/Path_Delegate.java index 776398fbb094e..34d09859e2b4f 100644 --- a/tools/layoutlib/bridge/src/android/graphics/Path_Delegate.java +++ b/tools/layoutlib/bridge/src/android/graphics/Path_Delegate.java @@ -21,6 +21,7 @@ import com.android.layoutlib.bridge.Bridge; import com.android.layoutlib.bridge.impl.DelegateManager; import com.android.tools.layoutlib.annotations.LayoutlibDelegate; +import android.annotation.NonNull; import android.graphics.Path.Direction; import android.graphics.Path.FillType; @@ -30,10 +31,12 @@ import java.awt.geom.Arc2D; import java.awt.geom.Area; import java.awt.geom.Ellipse2D; import java.awt.geom.GeneralPath; +import java.awt.geom.Path2D; import java.awt.geom.PathIterator; import java.awt.geom.Point2D; import java.awt.geom.Rectangle2D; import java.awt.geom.RoundRectangle2D; +import java.util.ArrayList; /** * Delegate implementing the native methods of android.graphics.Path @@ -56,7 +59,7 @@ public final class Path_Delegate { // ---- delegate data ---- private FillType mFillType = FillType.WINDING; - private GeneralPath mPath = new GeneralPath(); + private Path2D mPath = new Path2D.Double(); private float mLastX = 0; private float mLastY = 0; @@ -486,8 +489,54 @@ public final class Path_Delegate { @LayoutlibDelegate /*package*/ static float[] native_approximate(long nPath, float error) { - Bridge.getLog().error(LayoutLog.TAG_UNSUPPORTED, "Path.approximate() not supported", null); - return new float[0]; + Bridge.getLog().warning(LayoutLog.TAG_UNSUPPORTED, "Path.approximate() not fully supported", + null); + Path_Delegate pathDelegate = sManager.getDelegate(nPath); + if (pathDelegate == null) { + return null; + } + PathIterator pathIterator = pathDelegate.mPath.getPathIterator(null); + float[] tmp = new float[6]; + float[] coords = new float[6]; + boolean isFirstPoint = true; + while (!pathIterator.isDone()) { + int type = pathIterator.currentSegment(tmp); + switch (type) { + case PathIterator.SEG_MOVETO: + case PathIterator.SEG_LINETO: + store(coords, tmp, 1, isFirstPoint); + break; + case PathIterator.SEG_QUADTO: + store(coords, tmp, 2, isFirstPoint); + break; + case PathIterator.SEG_CUBICTO: + store(coords, tmp, 3, isFirstPoint); + break; + case PathIterator.SEG_CLOSE: + // No points returned. + } + isFirstPoint = false; + pathIterator.next(); + } + if (isFirstPoint) { + // No points found + return new float[0]; + } else { + return coords; + } + } + + private static void store(float[] src, float[] dst, int count, boolean isFirst) { + if (isFirst) { + dst[0] = 0; + dst[1] = src[0]; + dst[2] = src[1]; + } + if (count > 1 || !isFirst) { + dst[3] = 1; + dst[4] = src[2 * count]; + dst[5] = src[2 * count + 1]; + } } // ---- Private helper methods ---- @@ -522,6 +571,7 @@ public final class Path_Delegate { throw new IllegalArgumentException(); } + @NonNull private static Direction getDirection(int direction) { for (Direction d : Direction.values()) { if (direction == d.nativeInt) { From 9bf4b6f8c0a0d28402894b7a9660e8aced47545b Mon Sep 17 00:00:00 2001 From: Deepanshu Gupta Date: Wed, 29 Jul 2015 15:08:17 -0700 Subject: [PATCH 6/8] Add MockView constructor used by LayoutInflater. Change-Id: I0aede28866c1b87fb66c8a416c5026a9b2cb04fe --- .../bridge/src/com/android/layoutlib/bridge/MockView.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/MockView.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/MockView.java index 4a9f7187b29c4..44a9aad55daaa 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/MockView.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/MockView.java @@ -30,6 +30,10 @@ import android.widget.TextView; */ public class MockView extends TextView { + public MockView(Context context, AttributeSet attrs) { + this(context, attrs, 0); + } + public MockView(Context context, AttributeSet attrs, int defStyle) { this(context, attrs, defStyle, 0); } From 671b7f9b99316f7224c1213d6923d449a2de9b62 Mon Sep 17 00:00:00 2001 From: Deepanshu Gupta Date: Mon, 27 Jul 2015 10:11:03 -0700 Subject: [PATCH 7/8] Fix imports. Also remove null checks on return value of ReflectionUtils.getMethod() as the method always return non null. Change-Id: Ib476302c2cf0504a0f54db4e795955ad10e5df3f --- .../bridge/android/support/DesignLibUtil.java | 9 ++------- .../bridge/android/support/RecyclerViewUtil.java | 13 +++---------- .../com/android/layoutlib/bridge/impl/Layout.java | 13 +++++++------ 3 files changed, 12 insertions(+), 23 deletions(-) diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/support/DesignLibUtil.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/support/DesignLibUtil.java index 0426907b3a4c2..aa873a6476ae2 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/support/DesignLibUtil.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/support/DesignLibUtil.java @@ -18,14 +18,12 @@ package com.android.layoutlib.bridge.android.support; import com.android.ide.common.rendering.api.LayoutLog; import com.android.layoutlib.bridge.Bridge; +import com.android.layoutlib.bridge.util.ReflectionUtils.ReflectionException; import android.annotation.NonNull; import android.annotation.Nullable; import android.view.View; -import java.lang.reflect.Method; - -import static com.android.layoutlib.bridge.util.ReflectionUtils.ReflectionException; import static com.android.layoutlib.bridge.util.ReflectionUtils.getMethod; import static com.android.layoutlib.bridge.util.ReflectionUtils.invoke; @@ -53,10 +51,7 @@ public class DesignLibUtil { return; } try { - Method setTitle = getMethod(view.getClass(), "setTitle", CharSequence.class); - if (setTitle != null) { - invoke(setTitle, view, title); - } + invoke(getMethod(view.getClass(), "setTitle", CharSequence.class), view, title); } catch (ReflectionException e) { Bridge.getLog().warning(LayoutLog.TAG_INFO, "Error occurred while trying to set title.", e); diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/support/RecyclerViewUtil.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/support/RecyclerViewUtil.java index d14c80b908903..d432120ccb6f0 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/support/RecyclerViewUtil.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/support/RecyclerViewUtil.java @@ -21,16 +21,13 @@ import com.android.ide.common.rendering.api.LayoutlibCallback; import com.android.layoutlib.bridge.Bridge; import com.android.layoutlib.bridge.android.BridgeContext; import com.android.layoutlib.bridge.android.RenderParamsFlags; -import com.android.layoutlib.bridge.util.ReflectionUtils; +import com.android.layoutlib.bridge.util.ReflectionUtils.ReflectionException; import android.annotation.NonNull; import android.annotation.Nullable; import android.content.Context; import android.view.View; -import java.lang.reflect.Method; - -import static com.android.layoutlib.bridge.util.ReflectionUtils.ReflectionException; import static com.android.layoutlib.bridge.util.ReflectionUtils.getCause; import static com.android.layoutlib.bridge.util.ReflectionUtils.getMethod; import static com.android.layoutlib.bridge.util.ReflectionUtils.invoke; @@ -98,8 +95,7 @@ public class RecyclerViewUtil { @Nullable private static Object getLayoutManager(View recyclerView) throws ReflectionException { - Method getLayoutManager = getMethod(recyclerView.getClass(), "getLayoutManager"); - return getLayoutManager != null ? invoke(getLayoutManager, recyclerView) : null; + return invoke(getMethod(recyclerView.getClass(), "getLayoutManager"), recyclerView); } @Nullable @@ -127,10 +123,7 @@ public class RecyclerViewUtil { private static void setProperty(@NonNull Object object, @NonNull Class propertyClass, @Nullable Object propertyValue, @NonNull String propertySetter) throws ReflectionException { - Method setter = getMethod(object.getClass(), propertySetter, propertyClass); - if (setter != null) { - invoke(setter, object, propertyValue); - } + invoke(getMethod(object.getClass(), propertySetter, propertyClass), object, propertyValue); } /** diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/Layout.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/Layout.java index d1c9f21b7f915..8c7ea8a110853 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/Layout.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/Layout.java @@ -45,6 +45,7 @@ import android.widget.RelativeLayout; import static android.view.ViewGroup.LayoutParams.MATCH_PARENT; import static android.widget.LinearLayout.VERTICAL; +import static com.android.layoutlib.bridge.impl.ResourceHelper.getBooleanThemeValue; /** * The Layout used to create the system decor. @@ -325,7 +326,7 @@ class Layout extends RelativeLayout { mParams = params; mContext = context; mResources = mParams.getResources(); - mWindowIsFloating = ResourceHelper.getBooleanThemeValue(mResources, ATTR_WINDOW_FLOATING, true, true); + mWindowIsFloating = getBooleanThemeValue(mResources, ATTR_WINDOW_FLOATING, true, true); findBackground(); findStatusBar(); @@ -342,11 +343,11 @@ class Layout extends RelativeLayout { private void findStatusBar() { boolean windowFullScreen = - ResourceHelper.getBooleanThemeValue(mResources, ATTR_WINDOW_FULL_SCREEN, true, false); + getBooleanThemeValue(mResources, ATTR_WINDOW_FULL_SCREEN, true, false); if (!windowFullScreen && !mWindowIsFloating) { mStatusBarSize = getDimension(ATTR_STATUS_BAR_HEIGHT, true, DEFAULT_STATUS_BAR_HEIGHT); - mTranslucentStatus = ResourceHelper.getBooleanThemeValue(mResources, + mTranslucentStatus = getBooleanThemeValue(mResources, ATTR_WINDOW_TRANSLUCENT_STATUS, true, false); } } @@ -356,14 +357,14 @@ class Layout extends RelativeLayout { return; } // Check if an actionbar is needed - boolean windowActionBar = ResourceHelper.getBooleanThemeValue(mResources, ATTR_WINDOW_ACTION_BAR, + boolean windowActionBar = getBooleanThemeValue(mResources, ATTR_WINDOW_ACTION_BAR, !isThemeAppCompat(), true); if (windowActionBar) { mActionBarSize = getDimension(ATTR_ACTION_BAR_SIZE, true, DEFAULT_TITLE_BAR_HEIGHT); } else { // Maybe the gingerbread era title bar is needed boolean windowNoTitle = - ResourceHelper.getBooleanThemeValue(mResources, ATTR_WINDOW_NO_TITLE, true, false); + getBooleanThemeValue(mResources, ATTR_WINDOW_NO_TITLE, true, false); if (!windowNoTitle) { mTitleBarSize = getDimension(ATTR_WINDOW_TITLE_SIZE, true, DEFAULT_TITLE_BAR_HEIGHT); @@ -391,7 +392,7 @@ class Layout extends RelativeLayout { mNavBarOrientation = barOnBottom ? LinearLayout.HORIZONTAL : VERTICAL; mNavBarSize = getDimension(barOnBottom ? ATTR_NAV_BAR_HEIGHT : ATTR_NAV_BAR_WIDTH, true, DEFAULT_NAV_BAR_SIZE); - mTranslucentNav = ResourceHelper.getBooleanThemeValue(mResources, + mTranslucentNav = getBooleanThemeValue(mResources, ATTR_WINDOW_TRANSLUCENT_NAV, true, false); } } From 36e3fa55b6537d32d36e215cd5af9bcda82f7f1d Mon Sep 17 00:00:00 2001 From: Deepanshu Gupta Date: Wed, 29 Jul 2015 17:01:33 -0700 Subject: [PATCH 8/8] Update status bar time. Copy the value from the demo mode (commit 33b60bb45f) Change-Id: I4c215676e7ae26a68aa370e99db005226d8a05d9 --- .../src/com/android/layoutlib/bridge/bars/Config.java | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/Config.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/Config.java index dc89d0c6546a1..645634f902fec 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/Config.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/Config.java @@ -74,8 +74,8 @@ public class Config { } public static String getTime(int platformVersion) { - if (isGreaterOrEqual(platformVersion, LOLLIPOP_MR1)) { - return "5:10"; + if (isGreaterOrEqual(platformVersion, MNC)) { + return "6:00"; } if (platformVersion < GINGERBREAD) { return "2:20"; @@ -95,6 +95,9 @@ public class Config { if (platformVersion < LOLLIPOP_MR1) { return "5:00"; } + if (platformVersion < MNC) { + return "5:10"; + } // Should never happen. return "4:04"; }