Merge "Fix ninepatch scaling." into mnc-ub-dev
diff --git a/Android.mk b/Android.mk
index 99e0c46..92d6d8b 100644
--- a/Android.mk
+++ b/Android.mk
@@ -669,6 +669,16 @@
$(fwbase_dirs_to_document) \
$(non_base_dirs)
+###########################################################
+## Return all directories that have a 'NO_DOCS' file in
+## them, appending a '%' to them to form a pattern to
+## filter out files under those directories.
+## $(1): A list of base directories to look at.
+###########################################################
+define find-no-docs-pattern
+$(addsuffix %, $(dir $(foreach dir, $(1), $(shell cd $(LOCAL_PATH); find $(dir) -name NO_DOCS))))
+endef
+
# These are relative to frameworks/base
# FRAMEWORKS_BASE_SUBDIRS comes from build/core/pathmap.mk
dirs_to_document := \
@@ -676,6 +686,9 @@
$(addprefix ../../, $(FRAMEWORKS_DATA_BINDING_JAVA_SRC_DIRS)) \
$(addprefix ../../, $(FRAMEWORKS_SUPPORT_JAVA_SRC_DIRS)) \
+patterns_to_not_document := \
+ $(call find-no-docs-pattern, $(dirs_to_document))
+
# These are relative to frameworks/base
html_dirs := \
$(FRAMEWORKS_BASE_SUBDIRS) \
@@ -689,7 +702,7 @@
# These are relative to frameworks/base
framework_docs_LOCAL_SRC_FILES := \
- $(call find-other-java-files, $(dirs_to_document)) \
+ $(filter-out $(patterns_to_not_document), $(call find-other-java-files, $(dirs_to_document))) \
$(common_src_files)
# These are relative to frameworks/base
diff --git a/tools/layoutlib/bridge/src/android/animation/FakeAnimator.java b/tools/layoutlib/bridge/src/android/animation/FakeAnimator.java
deleted file mode 100644
index 78aedc5..0000000
--- a/tools/layoutlib/bridge/src/android/animation/FakeAnimator.java
+++ /dev/null
@@ -1,52 +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 android.animation;
-
-/**
- * A fake implementation of Animator which doesn't do anything.
- */
-public class FakeAnimator extends Animator {
- @Override
- public long getStartDelay() {
- return 0;
- }
-
- @Override
- public void setStartDelay(long startDelay) {
-
- }
-
- @Override
- public Animator setDuration(long duration) {
- return this;
- }
-
- @Override
- public long getDuration() {
- return 0;
- }
-
- @Override
- public void setInterpolator(TimeInterpolator value) {
-
- }
-
- @Override
- public boolean isRunning() {
- return false;
- }
-}
diff --git a/tools/layoutlib/bridge/src/android/animation/PropertyValuesHolder_Delegate.java b/tools/layoutlib/bridge/src/android/animation/PropertyValuesHolder_Delegate.java
index 4603b63..54021c9 100644
--- a/tools/layoutlib/bridge/src/android/animation/PropertyValuesHolder_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/animation/PropertyValuesHolder_Delegate.java
@@ -16,9 +16,16 @@
package android.animation;
+import com.android.layoutlib.bridge.Bridge;
import com.android.layoutlib.bridge.impl.DelegateManager;
import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Map;
+
/**
* Delegate implementing the native methods of android.animation.PropertyValuesHolder
*
@@ -29,81 +36,161 @@
* around to map int to instance of the delegate.
*
* The main goal of this class' methods are to provide a native way to access setters and getters
- * on some object. In this case we want to default to using Java reflection instead so the native
- * methods do nothing.
+ * on some object. We override these methods to use reflection since the original reflection
+ * implementation of the PropertyValuesHolder won't be able to access protected methods.
*
*/
-/*package*/ class PropertyValuesHolder_Delegate {
+/*package*/
+@SuppressWarnings("unused")
+class PropertyValuesHolder_Delegate {
+ // This code is copied from android.animation.PropertyValuesHolder and must be kept in sync
+ // We try several different types when searching for appropriate setter/getter functions.
+ // The caller may have supplied values in a type that does not match the setter/getter
+ // functions (such as the integers 0 and 1 to represent floating point values for alpha).
+ // Also, the use of generics in constructors means that we end up with the Object versions
+ // of primitive types (Float vs. float). But most likely, the setter/getter functions
+ // will take primitive types instead.
+ // So we supply an ordered array of other types to try before giving up.
+ private static Class[] FLOAT_VARIANTS = {float.class, Float.class, double.class, int.class,
+ Double.class, Integer.class};
+ private static Class[] INTEGER_VARIANTS = {int.class, Integer.class, float.class, double.class,
+ Float.class, Double.class};
+
+ private static final Object sMethodIndexLock = new Object();
+ private static final Map<Long, Method> ID_TO_METHOD = new HashMap<Long, Method>();
+ private static final Map<String, Long> METHOD_NAME_TO_ID = new HashMap<String, Long>();
+ private static long sNextId = 1;
+
+ private static long registerMethod(Class<?> targetClass, String methodName, Class[] types,
+ int nArgs) {
+ // Encode the number of arguments in the method name
+ String methodIndexName = String.format("%1$s#%2$d", methodName, nArgs);
+ synchronized (sMethodIndexLock) {
+ Long methodId = METHOD_NAME_TO_ID.get(methodIndexName);
+
+ if (methodId != null) {
+ // The method was already registered
+ return methodId;
+ }
+
+ Class[] args = new Class[nArgs];
+ Method method = null;
+ for (Class typeVariant : types) {
+ for (int i = 0; i < nArgs; i++) {
+ args[i] = typeVariant;
+ }
+ try {
+ method = targetClass.getDeclaredMethod(methodName, args);
+ } catch (NoSuchMethodException ignore) {
+ }
+ }
+
+ if (method != null) {
+ methodId = sNextId++;
+ ID_TO_METHOD.put(methodId, method);
+ METHOD_NAME_TO_ID.put(methodIndexName, methodId);
+
+ return methodId;
+ }
+ }
+
+ // Method not found
+ return 0;
+ }
+
+ private static void callMethod(Object target, long methodID, Object... args) {
+ Method method = ID_TO_METHOD.get(methodID);
+ assert method != null;
+
+ try {
+ method.setAccessible(true);
+ method.invoke(target, args);
+ } catch (IllegalAccessException e) {
+ Bridge.getLog().error(null, "Unable to update property during animation", e, null);
+ } catch (InvocationTargetException e) {
+ Bridge.getLog().error(null, "Unable to update property during animation", e, null);
+ }
+ }
@LayoutlibDelegate
/*package*/ static long nGetIntMethod(Class<?> targetClass, String methodName) {
- // return 0 to force PropertyValuesHolder to use Java reflection.
- return 0;
+ return nGetMultipleIntMethod(targetClass, methodName, 1);
}
@LayoutlibDelegate
/*package*/ static long nGetFloatMethod(Class<?> targetClass, String methodName) {
- // return 0 to force PropertyValuesHolder to use Java reflection.
- return 0;
+ return nGetMultipleFloatMethod(targetClass, methodName, 1);
}
@LayoutlibDelegate
/*package*/ static long nGetMultipleIntMethod(Class<?> targetClass, String methodName,
int numParams) {
- // TODO: return the right thing.
- return 0;
+ return registerMethod(targetClass, methodName, INTEGER_VARIANTS, numParams);
}
@LayoutlibDelegate
/*package*/ static long nGetMultipleFloatMethod(Class<?> targetClass, String methodName,
int numParams) {
- // TODO: return the right thing.
- return 0;
+ return registerMethod(targetClass, methodName, FLOAT_VARIANTS, numParams);
}
@LayoutlibDelegate
/*package*/ static void nCallIntMethod(Object target, long methodID, int arg) {
- // do nothing
+ callMethod(target, methodID, arg);
}
@LayoutlibDelegate
/*package*/ static void nCallFloatMethod(Object target, long methodID, float arg) {
- // do nothing
+ callMethod(target, methodID, arg);
}
@LayoutlibDelegate
/*package*/ static void nCallTwoIntMethod(Object target, long methodID, int arg1,
int arg2) {
- // do nothing
+ callMethod(target, methodID, arg1, arg2);
}
@LayoutlibDelegate
/*package*/ static void nCallFourIntMethod(Object target, long methodID, int arg1,
int arg2, int arg3, int arg4) {
- // do nothing
+ callMethod(target, methodID, arg1, arg2, arg3, arg4);
}
@LayoutlibDelegate
/*package*/ static void nCallMultipleIntMethod(Object target, long methodID,
int[] args) {
- // do nothing
+ assert args != null;
+
+ // Box parameters
+ Object[] params = new Object[args.length];
+ for (int i = 0; i < args.length; i++) {
+ params[i] = args;
+ }
+ callMethod(target, methodID, params);
}
@LayoutlibDelegate
/*package*/ static void nCallTwoFloatMethod(Object target, long methodID, float arg1,
float arg2) {
- // do nothing
+ callMethod(target, methodID, arg1, arg2);
}
@LayoutlibDelegate
/*package*/ static void nCallFourFloatMethod(Object target, long methodID, float arg1,
float arg2, float arg3, float arg4) {
- // do nothing
+ callMethod(target, methodID, arg1, arg2, arg3, arg4);
}
@LayoutlibDelegate
/*package*/ static void nCallMultipleFloatMethod(Object target, long methodID,
float[] args) {
- // do nothing
+ assert args != null;
+
+ // Box parameters
+ Object[] params = new Object[args.length];
+ for (int i = 0; i < args.length; i++) {
+ params[i] = args;
+ }
+ callMethod(target, methodID, params);
}
}
diff --git a/tools/layoutlib/bridge/src/android/content/res/BridgeTypedArray.java b/tools/layoutlib/bridge/src/android/content/res/BridgeTypedArray.java
index 31dd3d9..db4c6dc6 100644
--- a/tools/layoutlib/bridge/src/android/content/res/BridgeTypedArray.java
+++ b/tools/layoutlib/bridge/src/android/content/res/BridgeTypedArray.java
@@ -327,12 +327,19 @@
return null;
}
- // let the framework inflate the ColorStateList from the XML file.
- File f = new File(value);
- if (f.isFile()) {
- try {
- XmlPullParser parser = ParserFactory.create(f);
+ try {
+ // Get the state list file content from callback to parse PSI file
+ XmlPullParser parser = mContext.getLayoutlibCallback().getXmlFileParser(value);
+ if (parser == null) {
+ // If used with a version of Android Studio that does not implement getXmlFileParser
+ // fall back to reading the file from disk
+ File f = new File(value);
+ if (f.isFile()) {
+ parser = ParserFactory.create(f);
+ }
+ }
+ if (parser != null) {
BridgeXmlBlockParser blockParser = new BridgeXmlBlockParser(
parser, mContext, resValue.isFramework());
try {
@@ -341,18 +348,18 @@
} finally {
blockParser.ensurePopped();
}
- } catch (XmlPullParserException e) {
- Bridge.getLog().error(LayoutLog.TAG_BROKEN,
- "Failed to configure parser for " + value, e, null);
- return null;
- } catch (Exception e) {
- // this is an error and not warning since the file existence is checked before
- // attempting to parse it.
- Bridge.getLog().error(LayoutLog.TAG_RESOURCES_READ,
- "Failed to parse file " + value, e, null);
-
- return null;
}
+ } catch (XmlPullParserException e) {
+ Bridge.getLog().error(LayoutLog.TAG_BROKEN,
+ "Failed to configure parser for " + value, e, null);
+ return null;
+ } catch (Exception e) {
+ // this is an error and not warning since the file existence is checked before
+ // attempting to parse it.
+ Bridge.getLog().error(LayoutLog.TAG_RESOURCES_READ,
+ "Failed to parse file " + value, e, null);
+
+ return null;
}
try {
diff --git a/tools/layoutlib/bridge/src/android/graphics/PathMeasure_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/PathMeasure_Delegate.java
index dd2978f..3c71233 100644
--- a/tools/layoutlib/bridge/src/android/graphics/PathMeasure_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/graphics/PathMeasure_Delegate.java
@@ -44,7 +44,7 @@
// ---- delegate data ----
// This governs how accurate the approximation of the Path is.
- private static final float PRECISION = 0.002f;
+ private static final float PRECISION = 0.0002f;
/**
* Array containing the path points components. There are three components for each point:
diff --git a/tools/layoutlib/bridge/src/android/os/SystemClock_Delegate.java b/tools/layoutlib/bridge/src/android/os/SystemClock_Delegate.java
index 5f0d98b..9677aaf 100644
--- a/tools/layoutlib/bridge/src/android/os/SystemClock_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/os/SystemClock_Delegate.java
@@ -18,6 +18,7 @@
import com.android.layoutlib.bridge.impl.DelegateManager;
import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
+import com.android.tools.layoutlib.java.System_Delegate;
/**
* Delegate implementing the native methods of android.os.SystemClock
@@ -30,9 +31,6 @@
*
*/
public class SystemClock_Delegate {
- private static long sBootTime = System.currentTimeMillis();
- private static long sBootTimeNano = System.nanoTime();
-
/**
* Returns milliseconds since boot, not counting time spent in deep sleep.
* <b>Note:</b> This value may get reset occasionally (before it would
@@ -42,7 +40,7 @@
*/
@LayoutlibDelegate
/*package*/ static long uptimeMillis() {
- return System.currentTimeMillis() - sBootTime;
+ return System_Delegate.currentTimeMillis() - System_Delegate.bootTimeMillis();
}
/**
@@ -52,7 +50,7 @@
*/
@LayoutlibDelegate
/*package*/ static long elapsedRealtime() {
- return System.currentTimeMillis() - sBootTime;
+ return System_Delegate.currentTimeMillis() - System_Delegate.bootTimeMillis();
}
/**
@@ -62,7 +60,7 @@
*/
@LayoutlibDelegate
/*package*/ static long elapsedRealtimeNanos() {
- return System.nanoTime() - sBootTimeNano;
+ return System_Delegate.nanoTime() - System_Delegate.bootTime();
}
/**
@@ -72,7 +70,7 @@
*/
@LayoutlibDelegate
/*package*/ static long currentThreadTimeMillis() {
- return System.currentTimeMillis();
+ return System_Delegate.currentTimeMillis();
}
/**
@@ -84,7 +82,7 @@
*/
@LayoutlibDelegate
/*package*/ static long currentThreadTimeMicro() {
- return System.currentTimeMillis() * 1000;
+ return System_Delegate.currentTimeMillis() * 1000;
}
/**
diff --git a/tools/layoutlib/bridge/src/android/view/Choreographer_Delegate.java b/tools/layoutlib/bridge/src/android/view/Choreographer_Delegate.java
index f75ee50..01af669 100644
--- a/tools/layoutlib/bridge/src/android/view/Choreographer_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/view/Choreographer_Delegate.java
@@ -17,6 +17,8 @@
import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
+import java.util.concurrent.atomic.AtomicReference;
+
/**
* Delegate used to provide new implementation of a select few methods of {@link Choreographer}
*
@@ -25,9 +27,41 @@
*
*/
public class Choreographer_Delegate {
+ static final AtomicReference<Choreographer> mInstance = new AtomicReference<Choreographer>();
+
+ @LayoutlibDelegate
+ public static Choreographer getInstance() {
+ if (mInstance.get() == null) {
+ mInstance.compareAndSet(null, Choreographer.getInstance_Original());
+ }
+
+ return mInstance.get();
+ }
@LayoutlibDelegate
public static float getRefreshRate() {
return 60.f;
}
+
+ @LayoutlibDelegate
+ static void scheduleVsyncLocked(Choreographer thisChoreographer) {
+ // do nothing
+ }
+
+ public static void doFrame(long frameTimeNanos) {
+ Choreographer thisChoreographer = Choreographer.getInstance();
+
+ thisChoreographer.mLastFrameTimeNanos = frameTimeNanos;
+
+ thisChoreographer.mFrameInfo.markInputHandlingStart();
+ thisChoreographer.doCallbacks(Choreographer.CALLBACK_INPUT, frameTimeNanos);
+
+ thisChoreographer.mFrameInfo.markAnimationsStart();
+ thisChoreographer.doCallbacks(Choreographer.CALLBACK_ANIMATION, frameTimeNanos);
+
+ thisChoreographer.mFrameInfo.markPerformTraversalsStart();
+ thisChoreographer.doCallbacks(Choreographer.CALLBACK_TRAVERSAL, frameTimeNanos);
+
+ thisChoreographer.doCallbacks(Choreographer.CALLBACK_COMMIT, frameTimeNanos);
+ }
}
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 48ca7d8..c8e3d03 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java
@@ -183,7 +183,7 @@
*/
private static LayoutLog sCurrentLog = sDefaultLog;
- private static final int LAST_SUPPORTED_FEATURE = Features.RECYCLER_VIEW_ADAPTER;
+ private static final int LAST_SUPPORTED_FEATURE = Features.THEME_PREVIEW_NAVIGATION_BAR;
@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 feb2590..2ac212c 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeRenderSession.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeRenderSession.java
@@ -23,6 +23,7 @@
import com.android.ide.common.rendering.api.Result;
import com.android.ide.common.rendering.api.ViewInfo;
import com.android.layoutlib.bridge.impl.RenderSessionImpl;
+import com.android.tools.layoutlib.java.System_Delegate;
import android.view.View;
import android.view.ViewGroup;
@@ -191,6 +192,21 @@
}
@Override
+ public void setSystemTimeNanos(long nanos) {
+ System_Delegate.setNanosTime(nanos);
+ }
+
+ @Override
+ public void setSystemBootTimeNanos(long nanos) {
+ System_Delegate.setBootTimeNanos(nanos);
+ }
+
+ @Override
+ public void setElapsedFrameTimeNanos(long nanos) {
+ mSession.setElapsedFrameTimeNanos(nanos);
+ }
+
+ @Override
public void dispose() {
}
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 9c89bfe2..dfbc69b 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
@@ -19,9 +19,6 @@
import com.android.layoutlib.bridge.android.BridgeContext;
import com.android.resources.Density;
-import android.content.Context;
-import android.content.pm.ApplicationInfo;
-import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.view.View;
import android.widget.LinearLayout;
@@ -41,29 +38,18 @@
private static final int WIDTH_DEFAULT = 36;
private static final int WIDTH_SW360 = 40;
private static final int WIDTH_SW600 = 48;
- private static final String LAYOUT_XML = "/bars/navigation_bar.xml";
+ protected static final String LAYOUT_XML = "/bars/navigation_bar.xml";
private static final String LAYOUT_600DP_XML = "/bars/navigation_bar600dp.xml";
-
- /**
- * Constructor to be used when creating the {@link NavigationBar} as a regular control.
- * This is currently used by the theme editor.
- */
- @SuppressWarnings("unused")
- public NavigationBar(Context context, AttributeSet attrs) {
- 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,
- 0);
+ public NavigationBar(BridgeContext context, Density density, int orientation, boolean isRtl,
+ boolean rtlEnabled, int simulatedPlatformVersion) {
+ this(context, density, orientation, isRtl, rtlEnabled, simulatedPlatformVersion,
+ getShortestWidth(context)>= 600 ? LAYOUT_600DP_XML : LAYOUT_XML);
}
- public NavigationBar(BridgeContext context, Density density, int orientation, boolean isRtl,
- boolean rtlEnabled, int simulatedPlatformVersion) {
- super(context, orientation, getShortestWidth(context)>= 600 ? LAYOUT_600DP_XML : LAYOUT_XML,
- "navigation_bar.xml", simulatedPlatformVersion);
+ protected NavigationBar(BridgeContext context, Density density, int orientation, boolean isRtl,
+ boolean rtlEnabled, int simulatedPlatformVersion, String layoutPath) {
+ super(context, orientation, layoutPath, "navigation_bar.xml", simulatedPlatformVersion);
int color = getBarColor(ATTR_COLOR, ATTR_TRANSLUCENT);
setBackgroundColor(color == 0 ? 0xFF000000 : color);
@@ -117,7 +103,7 @@
view.setLayoutParams(layoutParams);
}
- private static int getSidePadding(float sw) {
+ protected int getSidePadding(float sw) {
if (sw >= 400) {
return PADDING_WIDTH_SW400;
}
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/ThemePreviewNavigationBar.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/ThemePreviewNavigationBar.java
new file mode 100644
index 0000000..0435280
--- /dev/null
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/ThemePreviewNavigationBar.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2015 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.bars;
+
+import com.android.layoutlib.bridge.android.BridgeContext;
+import com.android.resources.Density;
+
+import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.util.AttributeSet;
+import android.view.View;
+import android.widget.LinearLayout;
+
+/**
+ * Navigation Bar for the Theme Editor preview.
+ *
+ * For small bars, it is identical to {@link NavigationBar}.
+ * But wide bars from {@link NavigationBar} are too wide for the Theme Editor preview.
+ * To solve that problem, {@link ThemePreviewNavigationBar} use the layout for small bars,
+ * and have no padding on the sides. That way, they have a similar look as the true ones,
+ * and they fit in the Theme Editor preview.
+ */
+public class ThemePreviewNavigationBar extends NavigationBar {
+ private static final int PADDING_WIDTH_SW600 = 0;
+
+ @SuppressWarnings("unused")
+ public ThemePreviewNavigationBar(Context context, AttributeSet attrs) {
+ super((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,
+ 0, LAYOUT_XML);
+ }
+
+ @Override
+ protected int getSidePadding(float sw) {
+ if (sw >= 600) {
+ return PADDING_WIDTH_SW600;
+ }
+ return super.getSidePadding(sw);
+ }
+}
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 26f9000..d797eeca 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
@@ -42,10 +42,6 @@
import java.util.Collections;
import java.util.List;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-
/**
* Action to render a given Drawable provided through {@link DrawableParams#getDrawable()}.
*
@@ -72,8 +68,12 @@
BridgeContext context = getContext();
drawableResource = context.getRenderResources().resolveResValue(drawableResource);
- if (drawableResource == null ||
- drawableResource.getResourceType() != ResourceType.DRAWABLE) {
+ if (drawableResource == null) {
+ return Status.ERROR_NOT_A_DRAWABLE.createResult();
+ }
+
+ ResourceType resourceType = drawableResource.getResourceType();
+ if (resourceType != ResourceType.DRAWABLE && resourceType != ResourceType.MIPMAP) {
return Status.ERROR_NOT_A_DRAWABLE.createResult();
}
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 0ffa357..ec50cfe 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
@@ -46,6 +46,7 @@
import com.android.layoutlib.bridge.impl.binding.FakeAdapter;
import com.android.layoutlib.bridge.impl.binding.FakeExpandableAdapter;
import com.android.resources.ResourceType;
+import com.android.tools.layoutlib.java.System_Delegate;
import com.android.util.Pair;
import android.animation.AnimationThread;
@@ -62,6 +63,7 @@
import android.preference.Preference_Delegate;
import android.view.AttachInfo_Accessor;
import android.view.BridgeInflater;
+import android.view.Choreographer_Delegate;
import android.view.IWindowManager;
import android.view.IWindowManagerImpl;
import android.view.Surface;
@@ -120,6 +122,10 @@
private int mMeasuredScreenWidth = -1;
private int mMeasuredScreenHeight = -1;
private boolean mIsAlphaChannelImage;
+ /** If >= 0, a frame will be executed */
+ private long mElapsedFrameTimeNanos = -1;
+ /** True if one frame has been already executed to start the animations */
+ private boolean mFirstFrameExecuted = false;
// information being returned through the API
private BufferedImage mImage;
@@ -252,6 +258,14 @@
}
/**
+ * Sets the time for which the next frame will be selected. The time is the elapsed time from
+ * the current system nanos time. You
+ */
+ public void setElapsedFrameTimeNanos(long nanos) {
+ mElapsedFrameTimeNanos = nanos;
+ }
+
+ /**
* Renders the scene.
* <p>
* {@link #acquire(long)} must have been called before this.
@@ -428,6 +442,16 @@
gc.dispose();
}
+ if (mElapsedFrameTimeNanos >= 0) {
+ long initialTime = System_Delegate.nanoTime();
+ if (!mFirstFrameExecuted) {
+ // The first frame will initialize the animations
+ Choreographer_Delegate.doFrame(initialTime);
+ mFirstFrameExecuted = true;
+ }
+ // Second frame will move the animations
+ Choreographer_Delegate.doFrame(initialTime + mElapsedFrameTimeNanos);
+ }
mViewRoot.draw(mCanvas);
}
diff --git a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/golden/animated_vector.png b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/golden/animated_vector.png
new file mode 100644
index 0000000..9f26627
--- /dev/null
+++ b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/golden/animated_vector.png
Binary files differ
diff --git a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/golden/animated_vector_1.png b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/golden/animated_vector_1.png
new file mode 100644
index 0000000..89009be
--- /dev/null
+++ b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/golden/animated_vector_1.png
Binary files differ
diff --git a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/src/main/res/layout/indeterminate_progressbar.xml b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/src/main/res/layout/indeterminate_progressbar.xml
new file mode 100644
index 0000000..70d7396
--- /dev/null
+++ b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/src/main/res/layout/indeterminate_progressbar.xml
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:padding="16dp"
+ android:orientation="horizontal"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent">
+
+ <ProgressBar
+ android:layout_height="fill_parent"
+ android:layout_width="fill_parent" />
+
+</LinearLayout>
+
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 64aedb9..dea86bf 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
@@ -48,6 +48,8 @@
import java.net.URL;
import java.util.Arrays;
import java.util.Comparator;
+import java.util.Map;
+import java.util.concurrent.TimeUnit;
import static org.junit.Assert.fail;
@@ -353,16 +355,46 @@
renderAndVerify(params, "expand_horz_layout.png");
}
+ /** Test expand_layout.xml */
+ @Test
+ public void testVectorAnimation() throws ClassNotFoundException {
+ // Create the layout pull parser.
+ LayoutPullParser parser = new LayoutPullParser(APP_TEST_RES + "/layout/" +
+ "indeterminate_progressbar.xml");
+ // Create LayoutLibCallback.
+ LayoutLibTestCallback layoutLibCallback = new LayoutLibTestCallback(getLogger());
+ layoutLibCallback.initResources();
+
+ SessionParams params = getSessionParams(parser, ConfigGenerator.NEXUS_5,
+ layoutLibCallback, "Theme.Material.NoActionBar.Fullscreen", false,
+ RenderingMode.V_SCROLL, 22);
+
+ renderAndVerify(params, "animated_vector.png", TimeUnit.SECONDS.toNanos(2));
+
+ parser = new LayoutPullParser(APP_TEST_RES + "/layout/" +
+ "indeterminate_progressbar.xml");
+ params = getSessionParams(parser, ConfigGenerator.NEXUS_5,
+ layoutLibCallback, "Theme.Material.NoActionBar.Fullscreen", false,
+ RenderingMode.V_SCROLL, 22);
+ renderAndVerify(params, "animated_vector_1.png", TimeUnit.SECONDS.toNanos(3));
+ }
+
/**
* Create a new rendering session and test that rendering given layout on nexus 5
* doesn't throw any exceptions and matches the provided image.
+ * <p/>If frameTimeNanos is >= 0 a frame will be executed during the rendering. The time
+ * indicates how far in the future is.
*/
- private void renderAndVerify(SessionParams params, String goldenFileName)
+ private void renderAndVerify(SessionParams params, String goldenFileName, long frameTimeNanos)
throws ClassNotFoundException {
// TODO: Set up action bar handler properly to test menu rendering.
// Create session params.
RenderSession session = sBridge.createSession(params);
+ if (frameTimeNanos != -1) {
+ session.setElapsedFrameTimeNanos(frameTimeNanos);
+ }
+
if (!session.getResult().isSuccess()) {
getLogger().error(session.getResult().getException(),
session.getResult().getErrorMessage());
@@ -385,6 +417,15 @@
* Create a new rendering session and test that rendering given layout on nexus 5
* doesn't throw any exceptions and matches the provided image.
*/
+ private void renderAndVerify(SessionParams params, String goldenFileName)
+ throws ClassNotFoundException {
+ renderAndVerify(params, goldenFileName, -1);
+ }
+
+ /**
+ * Create a new rendering session and test that rendering given layout on nexus 5
+ * doesn't throw any exceptions and matches the provided image.
+ */
private void renderAndVerify(String layoutFileName, String goldenFileName)
throws ClassNotFoundException {
renderAndVerify(layoutFileName, goldenFileName, ConfigGenerator.NEXUS_5);
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 c209793..558a914 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
@@ -175,7 +175,9 @@
"android.text.format.DateFormat#is24HourFormat",
"android.text.Hyphenator#getSystemHyphenatorLocation",
"android.util.Xml#newPullParser",
+ "android.view.Choreographer#getInstance",
"android.view.Choreographer#getRefreshRate",
+ "android.view.Choreographer#scheduleVsyncLocked",
"android.view.Display#updateDisplayInfoLocked",
"android.view.Display#getWindowManager",
"android.view.LayoutInflater#rInflate",
@@ -299,6 +301,7 @@
};
private final static String[] PROMOTED_FIELDS = new String[] {
+ "android.view.Choreographer#mLastFrameTimeNanos",
"android.widget.SimpleMonthView#mTitle",
"android.widget.SimpleMonthView#mCalendar",
"android.widget.SimpleMonthView#mDayOfWeekLabelCalendar"
diff --git a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/ReplaceMethodCallsAdapter.java b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/ReplaceMethodCallsAdapter.java
index 0b85c48..5e47261 100644
--- a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/ReplaceMethodCallsAdapter.java
+++ b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/ReplaceMethodCallsAdapter.java
@@ -134,7 +134,33 @@
}
});
- // Case 5: java.util.LinkedHashMap.eldest()
+ // Case 5: java.lang.System time calls
+ METHOD_REPLACERS.add(new MethodReplacer() {
+ @Override
+ public boolean isNeeded(String owner, String name, String desc, String sourceClass) {
+ return JAVA_LANG_SYSTEM.equals(owner) && name.equals("nanoTime");
+ }
+
+ @Override
+ public void replace(MethodInformation mi) {
+ mi.name = "nanoTime";
+ mi.owner = Type.getInternalName(System_Delegate.class);
+ }
+ });
+ METHOD_REPLACERS.add(new MethodReplacer() {
+ @Override
+ public boolean isNeeded(String owner, String name, String desc, String sourceClass) {
+ return JAVA_LANG_SYSTEM.equals(owner) && name.equals("currentTimeMillis");
+ }
+
+ @Override
+ public void replace(MethodInformation mi) {
+ mi.name = "currentTimeMillis";
+ mi.owner = Type.getInternalName(System_Delegate.class);
+ }
+ });
+
+ // Case 6: java.util.LinkedHashMap.eldest()
METHOD_REPLACERS.add(new MethodReplacer() {
private final String VOID_TO_MAP_ENTRY =
@@ -157,7 +183,7 @@
}
});
- // Case 6: android.content.Context.getClassLoader() in LayoutInflater
+ // Case 7: android.content.Context.getClassLoader() in LayoutInflater
METHOD_REPLACERS.add(new MethodReplacer() {
// When LayoutInflater asks for a class loader, we must return the class loader that
// cannot return app's custom views/classes. This is so that in case of any failure
diff --git a/tools/layoutlib/create/src/com/android/tools/layoutlib/java/System_Delegate.java b/tools/layoutlib/create/src/com/android/tools/layoutlib/java/System_Delegate.java
index 613c8d9..be93744 100644
--- a/tools/layoutlib/create/src/com/android/tools/layoutlib/java/System_Delegate.java
+++ b/tools/layoutlib/create/src/com/android/tools/layoutlib/java/System_Delegate.java
@@ -18,12 +18,22 @@
import com.android.tools.layoutlib.create.ReplaceMethodCallsAdapter;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicLong;
+
/**
* Provides dummy implementation of methods that don't exist on the host VM.
+ * This also providers a time control that allows to set a specific system time.
*
* @see ReplaceMethodCallsAdapter
*/
+@SuppressWarnings("unused")
public class System_Delegate {
+ // Current system time
+ private static AtomicLong mNanosTime = new AtomicLong(System.nanoTime());
+ // Time that the system booted up in nanos
+ private static AtomicLong mBootNanosTime = new AtomicLong(System.nanoTime());
+
public static void log(String message) {
// ignore.
}
@@ -31,4 +41,28 @@
public static void log(String message, Throwable th) {
// ignore.
}
+
+ public static void setNanosTime(long nanos) {
+ mNanosTime.set(nanos);
+ }
+
+ public static void setBootTimeNanos(long nanos) {
+ mBootNanosTime.set(nanos);
+ }
+
+ public static long nanoTime() {
+ return mNanosTime.get();
+ }
+
+ public static long currentTimeMillis() {
+ return TimeUnit.NANOSECONDS.toMillis(mNanosTime.get());
+ }
+
+ public static long bootTime() {
+ return mBootNanosTime.get();
+ }
+
+ public static long bootTimeMillis() {
+ return TimeUnit.NANOSECONDS.toMillis(mBootNanosTime.get());
+ }
}