diff options
Diffstat (limited to 'tools/layoutlib/bridge/src')
20 files changed, 264 insertions, 199 deletions
diff --git a/tools/layoutlib/bridge/src/android/graphics/BitmapShader_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/BitmapShader_Delegate.java index 610c867..e9b5d6e 100644 --- a/tools/layoutlib/bridge/src/android/graphics/BitmapShader_Delegate.java +++ b/tools/layoutlib/bridge/src/android/graphics/BitmapShader_Delegate.java @@ -23,6 +23,8 @@ import com.android.tools.layoutlib.annotations.LayoutlibDelegate; import android.graphics.Shader.TileMode; +import java.awt.image.ColorModel; + /** * Delegate implementing the native methods of android.graphics.BitmapShader * @@ -124,6 +126,11 @@ public class BitmapShader_Delegate extends Shader_Delegate { localMatrix = new java.awt.geom.AffineTransform(); } + if (!colorModel.isCompatibleRaster(mImage.getRaster())) { + // Fallback to the default ARGB color model + colorModel = ColorModel.getRGBdefault(); + } + return new BitmapShaderContext(canvasMatrix, localMatrix, colorModel); } @@ -153,8 +160,9 @@ public class BitmapShader_Delegate extends Shader_Delegate { @Override public java.awt.image.Raster getRaster(int x, int y, int w, int h) { - java.awt.image.BufferedImage image = new java.awt.image.BufferedImage(w, h, - java.awt.image.BufferedImage.TYPE_INT_ARGB); + java.awt.image.BufferedImage image = new java.awt.image.BufferedImage( + mColorModel, mColorModel.createCompatibleWritableRaster(w, h), + mColorModel.isAlphaPremultiplied(), null); int[] data = new int[w*h]; diff --git a/tools/layoutlib/bridge/src/android/graphics/LinearGradient_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/LinearGradient_Delegate.java index 55c4b98..703719c 100644 --- a/tools/layoutlib/bridge/src/android/graphics/LinearGradient_Delegate.java +++ b/tools/layoutlib/bridge/src/android/graphics/LinearGradient_Delegate.java @@ -172,8 +172,9 @@ public final class LinearGradient_Delegate extends Gradient_Delegate { @Override public java.awt.image.Raster getRaster(int x, int y, int w, int h) { - java.awt.image.BufferedImage image = new java.awt.image.BufferedImage(w, h, - java.awt.image.BufferedImage.TYPE_INT_ARGB); + java.awt.image.BufferedImage image = new java.awt.image.BufferedImage( + mColorModel, mColorModel.createCompatibleWritableRaster(w, h), + mColorModel.isAlphaPremultiplied(), null); int[] data = new int[w*h]; diff --git a/tools/layoutlib/bridge/src/android/graphics/RadialGradient_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/RadialGradient_Delegate.java index eb29835..04e423b 100644 --- a/tools/layoutlib/bridge/src/android/graphics/RadialGradient_Delegate.java +++ b/tools/layoutlib/bridge/src/android/graphics/RadialGradient_Delegate.java @@ -160,8 +160,9 @@ public class RadialGradient_Delegate extends Gradient_Delegate { @Override public java.awt.image.Raster getRaster(int x, int y, int w, int h) { - java.awt.image.BufferedImage image = new java.awt.image.BufferedImage(w, h, - java.awt.image.BufferedImage.TYPE_INT_ARGB); + java.awt.image.BufferedImage image = new java.awt.image.BufferedImage( + mColorModel, mColorModel.createCompatibleWritableRaster(w, h), + mColorModel.isAlphaPremultiplied(), null); int[] data = new int[w*h]; diff --git a/tools/layoutlib/bridge/src/android/graphics/SweepGradient_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/SweepGradient_Delegate.java index 95a57a9..544ba98 100644 --- a/tools/layoutlib/bridge/src/android/graphics/SweepGradient_Delegate.java +++ b/tools/layoutlib/bridge/src/android/graphics/SweepGradient_Delegate.java @@ -152,8 +152,9 @@ public class SweepGradient_Delegate extends Gradient_Delegate { @Override public java.awt.image.Raster getRaster(int x, int y, int w, int h) { - java.awt.image.BufferedImage image = new java.awt.image.BufferedImage(w, h, - java.awt.image.BufferedImage.TYPE_INT_ARGB); + java.awt.image.BufferedImage image = new java.awt.image.BufferedImage( + mColorModel, mColorModel.createCompatibleWritableRaster(w, h), + mColorModel.isAlphaPremultiplied(), null); int[] data = new int[w*h]; diff --git a/tools/layoutlib/bridge/src/android/view/BridgeInflater.java b/tools/layoutlib/bridge/src/android/view/BridgeInflater.java index 5db9556..2e649c3 100644 --- a/tools/layoutlib/bridge/src/android/view/BridgeInflater.java +++ b/tools/layoutlib/bridge/src/android/view/BridgeInflater.java @@ -16,19 +16,15 @@ package android.view; -import com.android.ide.common.rendering.api.LayoutlibCallback; import com.android.ide.common.rendering.api.LayoutLog; +import com.android.ide.common.rendering.api.LayoutlibCallback; import com.android.ide.common.rendering.api.MergeCookie; import com.android.ide.common.rendering.api.ResourceReference; import com.android.ide.common.rendering.api.ResourceValue; import com.android.layoutlib.bridge.Bridge; -import com.android.layoutlib.bridge.BridgeConstants; import com.android.layoutlib.bridge.android.BridgeContext; import com.android.layoutlib.bridge.android.BridgeXmlBlockParser; -import com.android.layoutlib.bridge.android.support.RecyclerViewUtil; -import com.android.layoutlib.bridge.android.support.RecyclerViewUtil.LayoutManagerType; import com.android.layoutlib.bridge.impl.ParserFactory; -import com.android.layoutlib.bridge.impl.RenderSessionImpl; import com.android.resources.ResourceType; import com.android.util.Pair; @@ -233,22 +229,6 @@ public final class BridgeInflater extends LayoutInflater { if (viewKey != null) { bc.addViewKey(view, viewKey); } - if (RenderSessionImpl.isInstanceOf(view, RecyclerViewUtil.CN_RECYCLER_VIEW)) { - String type = attrs.getAttributeValue(BridgeConstants.NS_RESOURCES, - BridgeConstants.ATTR_LAYOUT_MANAGER_TYPE); - if (type != null) { - LayoutManagerType layoutManagerType = LayoutManagerType.getByLogicalName(type); - if (layoutManagerType == null) { - layoutManagerType = LayoutManagerType.getByClassName(type); - } - if (layoutManagerType == null) { - // add the classname itself. - bc.addCookie(view, type); - } else { - bc.addCookie(view, layoutManagerType); - } - } - } } } diff --git a/tools/layoutlib/bridge/src/android/view/RectShadowPainter.java b/tools/layoutlib/bridge/src/android/view/RectShadowPainter.java index ec3a8d6..30512aa 100644 --- a/tools/layoutlib/bridge/src/android/view/RectShadowPainter.java +++ b/tools/layoutlib/bridge/src/android/view/RectShadowPainter.java @@ -19,6 +19,7 @@ package android.view; import com.android.layoutlib.bridge.impl.ResourceHelper; import android.graphics.Canvas; +import android.graphics.Canvas_Delegate; import android.graphics.LinearGradient; import android.graphics.Outline; import android.graphics.Paint; @@ -125,6 +126,9 @@ public class RectShadowPainter { private static void sideShadow(Canvas canvas, Paint edgePaint, RectF edgeShadowRect, float dx, float dy, int rotations) { + if (isRectEmpty(edgeShadowRect)) { + return; + } int saved = canvas.save(); canvas.translate(dx, dy); canvas.rotate(rotations * PERPENDICULAR_ANGLE); @@ -153,4 +157,15 @@ public class RectShadowPainter { canvas.drawPath(path, paint); canvas.restoreToCount(saved); } + + /** + * Differs from {@link RectF#isEmpty()} as this first converts the rect to int and then checks. + * <p/> + * This is required because {@link Canvas_Delegate#native_drawRect(long, float, float, float, + * float, long)} casts the co-ordinates to int and we want to ensure that it doesn't end up + * drawing empty rectangles, which results in IllegalArgumentException. + */ + private static boolean isRectEmpty(RectF rect) { + return (int) rect.left >= (int) rect.right || (int) rect.top >= (int) rect.bottom; + } } 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 4d2c2fc..c6d60f8 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java @@ -181,7 +181,7 @@ public final class Bridge extends com.android.ide.common.rendering.api.Bridge { */ private static LayoutLog sCurrentLog = sDefaultLog; - private static final int LAST_SUPPORTED_FEATURE = Features.PREFERENCES_RENDERING; + private static final int LAST_SUPPORTED_FEATURE = Features.RENDER_ALL_DRAWABLE_STATES; @Override public int getApiLevel() { diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeRenderSession.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeRenderSession.java index e0f87fd..feb2590 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeRenderSession.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeRenderSession.java @@ -86,11 +86,14 @@ public class BridgeRenderSession extends RenderSession { } @Override - public Result render(long timeout) { + public Result render(long timeout, boolean forceMeasure) { try { Bridge.prepareThread(); mLastResult = mSession.acquire(timeout); if (mLastResult.isSuccess()) { + if (forceMeasure) { + mSession.invalidateRenderingSize(); + } mLastResult = mSession.render(false /*freshRender*/); } } finally { 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 3f553e7..2cbbeba 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 @@ -92,6 +92,8 @@ import java.util.IdentityHashMap; import java.util.List; import java.util.Map; +import static com.android.layoutlib.bridge.android.RenderParamsFlags.FLAG_KEY_APPLICATION_PACKAGE; + /** * Custom implementation of Context/Activity to handle non compiled resources. */ @@ -305,7 +307,7 @@ public final class BridgeContext extends Context { // check if this is a style resource if (value instanceof StyleResourceValue) { // get the id that will represent this style. - outValue.resourceId = getDynamicIdByStyle((StyleResourceValue)value); + outValue.resourceId = getDynamicIdByStyle((StyleResourceValue) value); return true; } @@ -783,6 +785,14 @@ public final class BridgeContext extends Context { } + @Override + public String getPackageName() { + if (mApplicationInfo.packageName == null) { + mApplicationInfo.packageName = mLayoutlibCallback.getFlag(FLAG_KEY_APPLICATION_PACKAGE); + } + return mApplicationInfo.packageName; + } + // ------------- private new methods /** @@ -1190,12 +1200,6 @@ public final class BridgeContext extends Context { } @Override - public String getPackageName() { - // pass - return null; - } - - @Override public String getBasePackageName() { // pass return null; diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindow.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindow.java index 4c4454d..fb5d44f 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindow.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindow.java @@ -47,8 +47,8 @@ public final class BridgeWindow implements IWindow { } @Override - public void resized(Rect rect, Rect rect2, Rect rect3, Rect rect4, Rect rect5, boolean b, - Configuration configuration) throws RemoteException { + public void resized(Rect rect, Rect rect2, Rect rect3, Rect rect4, Rect rect5, Rect rect6, + boolean b, Configuration configuration) throws RemoteException { // pass for now. } diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindowSession.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindowSession.java index 25f7078..8575839 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindowSession.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindowSession.java @@ -89,7 +89,7 @@ public final class BridgeWindowSession implements IWindowSession { @Override public int relayout(IWindow iWindow, int i, LayoutParams layoutParams, int i2, int i3, int i4, int i5, Rect rect, Rect rect2, Rect rect3, Rect rect4, Rect rect5, - Configuration configuration, Surface surface) throws RemoteException { + Rect rect6, Configuration configuration, Surface surface) throws RemoteException { // pass for now. return 0; } diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/RenderParamsFlags.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/RenderParamsFlags.java new file mode 100644 index 0000000..b98f96f --- /dev/null +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/RenderParamsFlags.java @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.layoutlib.bridge.android; + +import com.android.ide.common.rendering.api.RenderParams; +import com.android.ide.common.rendering.api.SessionParams.Key; + +/** + * This contains all known keys for the {@link RenderParams#getFlag(Key)}. + * <p/> + * The IDE has its own copy of this class which may be newer or older than this one. + * <p/> + * Constants should never be modified or removed from this class. + */ +public final class RenderParamsFlags { + + public static final Key<String> FLAG_KEY_ROOT_TAG = + new Key<String>("rootTag", String.class); + public static final Key<Boolean> FLAG_KEY_DISABLE_BITMAP_CACHING = + new Key<Boolean>("disableBitmapCaching", Boolean.class); + public static final Key<Boolean> FLAG_KEY_RENDER_ALL_DRAWABLE_STATES = + new Key<Boolean>("renderAllDrawableStates", Boolean.class); + /** + * To tell LayoutLib that the IDE supports RecyclerView. + * <p/> + * Default is false. + */ + public static final Key<Boolean> FLAG_KEY_RECYCLER_VIEW_SUPPORT = + new Key<Boolean>("recyclerViewSupport", Boolean.class); + /** + * The application package name. Used via + * {@link com.android.ide.common.rendering.api.LayoutlibCallback#getFlag(Key)} + */ + public static final Key<String> FLAG_KEY_APPLICATION_PACKAGE = + new Key<String>("applicationPackage", String.class); + + // Disallow instances. + private RenderParamsFlags() {} +} diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/SessionParamsFlags.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/SessionParamsFlags.java deleted file mode 100644 index 22b5192..0000000 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/SessionParamsFlags.java +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright (C) 2014 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.layoutlib.bridge.android; - -import com.android.ide.common.rendering.api.SessionParams; - -/** - * This contains all known keys for the {@link SessionParams#getFlag(SessionParams.Key)}. - * <p/> - * The IDE has its own copy of this class which may be newer or older than this one. - * <p/> - * Constants should never be modified or removed from this class. - */ -public final class SessionParamsFlags { - - public static final SessionParams.Key<String> FLAG_KEY_ROOT_TAG = - new SessionParams.Key<String>("rootTag", String.class); - public static final SessionParams.Key<Boolean> FLAG_KEY_RECYCLER_VIEW_SUPPORT = - new SessionParams.Key<Boolean>("recyclerViewSupport", Boolean.class); - - // Disallow instances. - private SessionParamsFlags() {} -} 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 aac5d33..e4c7288 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 @@ -23,15 +23,16 @@ import com.android.ide.common.rendering.api.LayoutlibCallback; import com.android.ide.common.rendering.api.SessionParams; import com.android.layoutlib.bridge.Bridge; import com.android.layoutlib.bridge.android.BridgeContext; -import com.android.layoutlib.bridge.android.SessionParamsFlags; +import com.android.layoutlib.bridge.android.RenderParamsFlags; import android.content.Context; import android.view.View; -import android.widget.LinearLayout; import java.lang.reflect.Method; -import static com.android.layoutlib.bridge.util.ReflectionUtils.*; +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; /** * Utility class for working with android.support.v7.widget.RecyclerView @@ -39,17 +40,15 @@ import static com.android.layoutlib.bridge.util.ReflectionUtils.*; @SuppressWarnings("SpellCheckingInspection") // for "recycler". public class RecyclerViewUtil { - /** - * Used by {@link LayoutManagerType}. - * <p/> - * Not declared inside the enum, since it needs to be accessible in the constructor. - */ - private static final Object CONTEXT = new Object(); - - public static final String CN_RECYCLER_VIEW = "android.support.v7.widget.RecyclerView"; + private static final String RV_PKG_PREFIX = "android.support.v7.widget."; + public static final String CN_RECYCLER_VIEW = RV_PKG_PREFIX + "RecyclerView"; private static final String CN_LAYOUT_MANAGER = CN_RECYCLER_VIEW + "$LayoutManager"; private static final String CN_ADAPTER = CN_RECYCLER_VIEW + "$Adapter"; + // LinearLayoutManager related constants. + private static final String CN_LINEAR_LAYOUT_MANAGER = RV_PKG_PREFIX + "LinearLayoutManager"; + private static final Class<?>[] LLM_CONSTRUCTOR_SIGNATURE = new Class<?>[]{Context.class}; + /** * Tries to create an Adapter ({@code android.support.v7.widget.RecyclerView.Adapter} and a * LayoutManager {@code RecyclerView.LayoutManager} and assign these to the {@code RecyclerView} @@ -71,39 +70,35 @@ public class RecyclerViewUtil { private static void setLayoutManager(@NonNull View recyclerView, @NonNull BridgeContext context, @NonNull LayoutlibCallback callback) throws ReflectionException { - Object cookie = context.getCookie(recyclerView); - assert cookie == null || cookie instanceof LayoutManagerType || cookie instanceof String; - if (!(cookie instanceof LayoutManagerType)) { - if (cookie != null) { - // TODO: When layoutlib API is updated, try to load the class with a null - // constructor or a constructor taking one argument - the context. - Bridge.getLog().warning(LayoutLog.TAG_UNSUPPORTED, - "LayoutManager (" + cookie + ") not found, falling back to " + - "LinearLayoutManager", null); - } - cookie = LayoutManagerType.getDefault(); + if (getLayoutManager(recyclerView) == null) { + // Only set the layout manager if not already set by the recycler view. + Object layoutManager = createLayoutManager(context, callback); + setProperty(recyclerView, CN_LAYOUT_MANAGER, layoutManager, "setLayoutManager"); } - Object layoutManager = createLayoutManager((LayoutManagerType) cookie, context, callback); - setProperty(recyclerView, CN_LAYOUT_MANAGER, layoutManager, "setLayoutManager"); } + /** Creates a LinearLayoutManager using the provided context. */ @Nullable - private static Object createLayoutManager(@Nullable LayoutManagerType type, - @NonNull Context context, @NonNull LayoutlibCallback callback) + private static Object createLayoutManager(@NonNull Context context, + @NonNull LayoutlibCallback callback) throws ReflectionException { - if (type == null) { - type = LayoutManagerType.getDefault(); - } try { - return callback.loadView(type.getClassName(), type.getSignature(), type.getArgs(context)); + return callback.loadView(CN_LINEAR_LAYOUT_MANAGER, LLM_CONSTRUCTOR_SIGNATURE, + new Object[]{ context}); } catch (Exception e) { throw new ReflectionException(e); } } @Nullable + private static Object getLayoutManager(View recyclerview) throws ReflectionException { + Method getLayoutManager = getMethod(recyclerview.getClass(), "getLayoutManager"); + return getLayoutManager != null ? invoke(getLayoutManager, recyclerview) : null; + } + + @Nullable private static Object createAdapter(@NonNull SessionParams params) throws ReflectionException { - Boolean ideSupport = params.getFlag(SessionParamsFlags.FLAG_KEY_RECYCLER_VIEW_SUPPORT); + Boolean ideSupport = params.getFlag(RenderParamsFlags.FLAG_KEY_RECYCLER_VIEW_SUPPORT); if (ideSupport != Boolean.TRUE) { return null; } @@ -145,74 +140,4 @@ public class RecyclerViewUtil { } throw new RuntimeException("invalid object/classname combination."); } - - /** Supported LayoutManagers. */ - public enum LayoutManagerType { - LINEAR_LAYOUT_MANGER("Linear", - "android.support.v7.widget.LinearLayoutManager", - new Class[]{Context.class}, new Object[]{CONTEXT}), - GRID_LAYOUT_MANAGER("Grid", - "android.support.v7.widget.GridLayoutManager", - new Class[]{Context.class, int.class}, new Object[]{CONTEXT, 2}), - STAGGERED_GRID_LAYOUT_MANAGER("StaggeredGrid", - "android.support.v7.widget.StaggeredGridLayoutManager", - new Class[]{int.class, int.class}, new Object[]{2, LinearLayout.VERTICAL}); - - private String mLogicalName; - private String mClassName; - private Class[] mSignature; - private Object[] mArgs; - - LayoutManagerType(String logicalName, String className, Class[] signature, Object[] args) { - mLogicalName = logicalName; - mClassName = className; - mSignature = signature; - mArgs = args; - } - - String getClassName() { - return mClassName; - } - - Class[] getSignature() { - return mSignature; - } - - @NonNull - Object[] getArgs(Context context) { - Object[] args = new Object[mArgs.length]; - System.arraycopy(mArgs, 0, args, 0, mArgs.length); - for (int i = 0; i < args.length; i++) { - if (args[i] == CONTEXT) { - args[i] = context; - } - } - return args; - } - - @NonNull - public static LayoutManagerType getDefault() { - return LINEAR_LAYOUT_MANGER; - } - - @Nullable - public static LayoutManagerType getByLogicalName(@NonNull String logicalName) { - for (LayoutManagerType type : values()) { - if (logicalName.equals(type.mLogicalName)) { - return type; - } - } - return null; - } - - @Nullable - public static LayoutManagerType getByClassName(@NonNull String className) { - for (LayoutManagerType type : values()) { - if (className.equals(type.mClassName)) { - return type; - } - } - return null; - } - } } 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 9f9b968..dc89d0c 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,7 +74,7 @@ public class Config { } public static String getTime(int platformVersion) { - if (platformVersion == 0) { + if (isGreaterOrEqual(platformVersion, LOLLIPOP_MR1)) { return "5:10"; } if (platformVersion < GINGERBREAD) { @@ -117,7 +117,7 @@ public class Config { } public static String getWifiIconType(int platformVersion) { - return platformVersion == 0 ? "xml" : "png"; + return isGreaterOrEqual(platformVersion, LOLLIPOP) ? "xml" : "png"; } /** 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 9450b6c..04aadff 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 @@ -21,6 +21,10 @@ import com.android.resources.Density; import org.xmlpull.v1.XmlPullParserException; +import android.content.Context; +import android.content.pm.ApplicationInfo; +import android.util.AttributeSet; +import android.view.View; import android.widget.LinearLayout; import android.widget.TextView; @@ -29,6 +33,21 @@ public class NavigationBar extends CustomBar { /** Navigation bar background color attribute name. */ private static final String ATTR_COLOR = "navigationBarColor"; + /** + * Constructor to be used when creating the {@link NavigationBar} as a regular control. + * This is currently used by the theme editor. + */ + public NavigationBar(Context context, AttributeSet attrs) + throws XmlPullParserException { + this((BridgeContext) context, + Density.getEnum(((BridgeContext) context).getMetrics().densityDpi), + LinearLayout.HORIZONTAL, // In this mode, it doesn't need to be render vertically + ((BridgeContext) context).getConfiguration().getLayoutDirection() == + View.LAYOUT_DIRECTION_RTL, + (context.getApplicationInfo().flags & ApplicationInfo.FLAG_SUPPORTS_RTL) != 0, + context.getApplicationInfo().targetSdkVersion); + } + public NavigationBar(BridgeContext context, Density density, int orientation, boolean isRtl, boolean rtlEnabled, int simulatedPlatformVersion) throws XmlPullParserException { super(context, orientation, "/bars/navigation_bar.xml", "navigation_bar.xml", 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 e5f1f68..a0ed0e8 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 @@ -25,7 +25,9 @@ import com.android.resources.Density; import org.xmlpull.v1.XmlPullParserException; +import android.content.Context; import android.graphics.drawable.Drawable; +import android.util.AttributeSet; import android.view.Gravity; import android.view.View; import android.widget.ImageView; @@ -39,7 +41,20 @@ public class StatusBar extends CustomBar { private final int mSimulatedPlatformVersion; /** Status bar background color attribute name. */ - private static final String ATTR_COLOR = "colorPrimaryDark"; + private static final String ATTR_COLOR = "statusBarColor"; + + /** + * Constructor to be used when creating the {@link StatusBar} as a regular control. This + * is currently used by the theme editor. + */ + public StatusBar(Context context, AttributeSet attrs) throws XmlPullParserException { + this((BridgeContext) context, + Density.getEnum(((BridgeContext) context).getMetrics().densityDpi), + LinearLayout.HORIZONTAL, // In this mode, it doesn't need to be render vertically + ((BridgeContext) context).getConfiguration().getLayoutDirection() == + View.LAYOUT_DIRECTION_RTL, + context.getApplicationInfo().targetSdkVersion); + } public StatusBar(BridgeContext context, Density density, int direction, boolean RtlEnabled, int simulatedPlatformVersion) throws XmlPullParserException { @@ -50,6 +65,7 @@ public class StatusBar extends CustomBar { // FIXME: use FILL_H? setGravity(Gravity.START | Gravity.TOP | Gravity.RIGHT); + int color = getThemeAttrColor(ATTR_COLOR, true); setBackgroundColor(color == 0 ? Config.getStatusBarColor(simulatedPlatformVersion) : color); diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/GcSnapshot.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/GcSnapshot.java index 3a0321a..c34f9b5 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/GcSnapshot.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/GcSnapshot.java @@ -35,6 +35,7 @@ import java.awt.AlphaComposite; import java.awt.Color; import java.awt.Composite; import java.awt.Graphics2D; +import java.awt.Rectangle; import java.awt.RenderingHints; import java.awt.Shape; import java.awt.geom.AffineTransform; @@ -615,8 +616,22 @@ public class GcSnapshot { return; } - int width = layer.getImage().getWidth(); - int height = layer.getImage().getHeight(); + int width; + int height; + Rectangle clipBounds = originalGraphics.getClipBounds(); + if (clipBounds != null) { + if (clipBounds.width == 0 || clipBounds.height == 0) { + // Clip is 0 so no need to paint anything. + return; + } + // If we have clipBounds available, use them as they will always be + // smaller than the full layer size. + width = clipBounds.width; + height = clipBounds.height; + } else { + width = layer.getImage().getWidth(); + height = layer.getImage().getHeight(); + } // Create a temporary image to which the color filter will be applied. BufferedImage image = new BufferedImage(width, height, diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderDrawable.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderDrawable.java index 6513c5f..9e26502 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderDrawable.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderDrawable.java @@ -22,12 +22,14 @@ import com.android.ide.common.rendering.api.ResourceValue; import com.android.ide.common.rendering.api.Result; import com.android.ide.common.rendering.api.Result.Status; import com.android.layoutlib.bridge.android.BridgeContext; +import com.android.layoutlib.bridge.android.RenderParamsFlags; import com.android.resources.ResourceType; import android.graphics.Bitmap; import android.graphics.Bitmap_Delegate; import android.graphics.Canvas; import android.graphics.drawable.Drawable; +import android.graphics.drawable.StateListDrawable; import android.view.AttachInfo_Accessor; import android.view.View.MeasureSpec; import android.widget.FrameLayout; @@ -37,6 +39,10 @@ import java.awt.Color; import java.awt.Graphics2D; import java.awt.image.BufferedImage; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + /** * Action to render a given Drawable provided through {@link DrawableParams#getDrawable()}. * @@ -68,11 +74,37 @@ public class RenderDrawable extends RenderAction<DrawableParams> { return Status.ERROR_NOT_A_DRAWABLE.createResult(); } + Drawable d = ResourceHelper.getDrawable(drawableResource, context); + + final Boolean allStates = + params.getFlag(RenderParamsFlags.FLAG_KEY_RENDER_ALL_DRAWABLE_STATES); + if (allStates == Boolean.TRUE) { + final List<BufferedImage> result; + + if (d instanceof StateListDrawable) { + result = new ArrayList<BufferedImage>(); + final StateListDrawable stateList = (StateListDrawable) d; + for (int i = 0; i < stateList.getStateCount(); i++) { + final Drawable stateDrawable = stateList.getStateDrawable(i); + result.add(renderImage(hardwareConfig, stateDrawable, context)); + } + } else { + result = Collections.singletonList(renderImage(hardwareConfig, d, context)); + } + + return Status.SUCCESS.createResult(result); + } else { + BufferedImage image = renderImage(hardwareConfig, d, context); + return Status.SUCCESS.createResult(image); + } + } + + private BufferedImage renderImage(HardwareConfig hardwareConfig, Drawable d, + BridgeContext context) { // create a simple FrameLayout FrameLayout content = new FrameLayout(context); // get the actual Drawable object to draw - Drawable d = ResourceHelper.getDrawable(drawableResource, context); content.setBackground(d); // set the AttachInfo on the root view. @@ -80,8 +112,27 @@ public class RenderDrawable extends RenderAction<DrawableParams> { // measure - int w = hardwareConfig.getScreenWidth(); - int h = hardwareConfig.getScreenHeight(); + int w = d.getIntrinsicWidth(); + int h = d.getIntrinsicHeight(); + + final int screenWidth = hardwareConfig.getScreenWidth(); + final int screenHeight = hardwareConfig.getScreenHeight(); + + if (w == -1 || h == -1) { + // Use screen size when either intrinsic width or height isn't available + w = screenWidth; + h = screenHeight; + } else if (w > screenWidth || h > screenHeight) { + // If image wouldn't fit to the screen, resize it to avoid cropping. + + // We need to find scale such that scale * w <= screenWidth, scale * h <= screenHeight + double scale = Math.min((double) screenWidth / w, (double) screenHeight / h); + + // scale * w / scale * h = w / h, so, proportions are preserved. + w = (int) Math.floor(scale * w); + h = (int) Math.floor(scale * h); + } + int w_spec = MeasureSpec.makeMeasureSpec(w, MeasureSpec.EXACTLY); int h_spec = MeasureSpec.makeMeasureSpec(h, MeasureSpec.EXACTLY); content.measure(w_spec, h_spec); @@ -105,8 +156,7 @@ public class RenderDrawable extends RenderAction<DrawableParams> { // and draw content.draw(canvas); - - return Status.SUCCESS.createResult(image); + return image; } protected BufferedImage getImage(int w, int h) { 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 95576ef..f6e5ef1 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 @@ -45,7 +45,7 @@ import com.android.layoutlib.bridge.Bridge; import com.android.layoutlib.bridge.android.BridgeContext; import com.android.layoutlib.bridge.android.BridgeLayoutParamsMapAttributes; import com.android.layoutlib.bridge.android.BridgeXmlBlockParser; -import com.android.layoutlib.bridge.android.SessionParamsFlags; +import com.android.layoutlib.bridge.android.RenderParamsFlags; import com.android.layoutlib.bridge.android.support.RecyclerViewUtil; import com.android.layoutlib.bridge.bars.AppCompatActionBar; import com.android.layoutlib.bridge.bars.BridgeActionBar; @@ -403,7 +403,7 @@ public class RenderSessionImpl extends RenderAction<SessionParams> { // it can instantiate the custom Fragment. Fragment_Delegate.setLayoutlibCallback(params.getLayoutlibCallback()); - String rootTag = params.getFlag(SessionParamsFlags.FLAG_KEY_ROOT_TAG); + String rootTag = params.getFlag(RenderParamsFlags.FLAG_KEY_ROOT_TAG); boolean isPreference = "PreferenceScreen".equals(rootTag); View view; if (isPreference) { @@ -554,7 +554,14 @@ public class RenderSessionImpl extends RenderAction<SessionParams> { // draw the views // create the BufferedImage into which the layout will be rendered. boolean newImage = false; - if (newRenderSize || mCanvas == null) { + + // When disableBitmapCaching is true, we do not reuse mImage and + // we create a new one in every render. + // This is useful when mImage is just a wrapper of Graphics2D so + // it doesn't get cached. + boolean disableBitmapCaching = Boolean.TRUE.equals(params.getFlag( + RenderParamsFlags.FLAG_KEY_DISABLE_BITMAP_CACHING)); + if (newRenderSize || mCanvas == null || disableBitmapCaching) { if (params.getImageFactory() != null) { mImage = params.getImageFactory().getImage( mMeasuredScreenWidth, @@ -581,8 +588,12 @@ public class RenderSessionImpl extends RenderAction<SessionParams> { Bitmap bitmap = Bitmap_Delegate.createBitmap(mImage, true /*isMutable*/, hardwareConfig.getDensity()); - // create a Canvas around the Android bitmap - mCanvas = new Canvas(bitmap); + if (mCanvas == null) { + // create a Canvas around the Android bitmap + mCanvas = new Canvas(bitmap); + } else { + mCanvas.setBitmap(bitmap); + } mCanvas.setDensity(hardwareConfig.getDensity().getDpiValue()); } @@ -1064,7 +1075,7 @@ public class RenderSessionImpl extends RenderAction<SessionParams> { private void findStatusBar(RenderResources resources, DisplayMetrics metrics) { boolean windowFullscreen = getBooleanThemeValue(resources, - "windowFullscreen", false, !isThemeAppCompat(resources)); + "windowFullscreen", false, true); if (!windowFullscreen && !mWindowIsFloating) { // default value @@ -1199,15 +1210,15 @@ public class RenderSessionImpl extends RenderAction<SessionParams> { // between Theme.AppCompat.Light and Theme.AppCompat is Theme.Material (for v21). boolean isThemeAppCompat = false; for (int i = 0; i < 50; i++) { + if (defaultTheme == null) { + break; + } // for loop ensures that we don't run into cyclic theme inheritance. if (defaultTheme.getName().startsWith("Theme.AppCompat")) { isThemeAppCompat = true; break; } defaultTheme = resources.getParent(defaultTheme); - if (defaultTheme == null) { - break; - } } mIsThemeAppCompat = isThemeAppCompat; } @@ -1631,7 +1642,7 @@ public class RenderSessionImpl extends RenderAction<SessionParams> { return null; } - private void invalidateRenderingSize() { + public void invalidateRenderingSize() { mMeasuredScreenWidth = mMeasuredScreenHeight = -1; } |
