Add support for Choreographer animations
am: 29ed07524c
* commit '29ed07524ce0fc2e5950f5340d306247145d0efa':
Add support for Choreographer animations
This commit is contained in:
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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 @@ import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
|
||||
* 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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -44,7 +44,7 @@ public final class PathMeasure_Delegate {
|
||||
|
||||
// ---- 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:
|
||||
|
||||
@@ -18,6 +18,7 @@ package android.os;
|
||||
|
||||
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 @@ import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
|
||||
*
|
||||
*/
|
||||
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 @@ public class SystemClock_Delegate {
|
||||
*/
|
||||
@LayoutlibDelegate
|
||||
/*package*/ static long uptimeMillis() {
|
||||
return System.currentTimeMillis() - sBootTime;
|
||||
return System_Delegate.currentTimeMillis() - System_Delegate.bootTimeMillis();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -52,7 +50,7 @@ public class SystemClock_Delegate {
|
||||
*/
|
||||
@LayoutlibDelegate
|
||||
/*package*/ static long elapsedRealtime() {
|
||||
return System.currentTimeMillis() - sBootTime;
|
||||
return System_Delegate.currentTimeMillis() - System_Delegate.bootTimeMillis();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -62,7 +60,7 @@ public class SystemClock_Delegate {
|
||||
*/
|
||||
@LayoutlibDelegate
|
||||
/*package*/ static long elapsedRealtimeNanos() {
|
||||
return System.nanoTime() - sBootTimeNano;
|
||||
return System_Delegate.nanoTime() - System_Delegate.bootTime();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -72,7 +70,7 @@ public class SystemClock_Delegate {
|
||||
*/
|
||||
@LayoutlibDelegate
|
||||
/*package*/ static long currentThreadTimeMillis() {
|
||||
return System.currentTimeMillis();
|
||||
return System_Delegate.currentTimeMillis();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -84,7 +82,7 @@ public class SystemClock_Delegate {
|
||||
*/
|
||||
@LayoutlibDelegate
|
||||
/*package*/ static long currentThreadTimeMicro() {
|
||||
return System.currentTimeMillis() * 1000;
|
||||
return System_Delegate.currentTimeMillis() * 1000;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -17,6 +17,8 @@ package android.view;
|
||||
|
||||
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 @@ import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
|
||||
*
|
||||
*/
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -183,7 +183,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.RECYCLER_VIEW_ADAPTER;
|
||||
private static final int LAST_SUPPORTED_FEATURE = Features.CHOREOGRAPHER;
|
||||
|
||||
@Override
|
||||
public int getApiLevel() {
|
||||
|
||||
@@ -23,6 +23,7 @@ import com.android.ide.common.rendering.api.RenderSession;
|
||||
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;
|
||||
@@ -190,6 +191,21 @@ public class BridgeRenderSession extends RenderSession {
|
||||
return mLastResult;
|
||||
}
|
||||
|
||||
@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() {
|
||||
}
|
||||
|
||||
@@ -46,6 +46,7 @@ import com.android.layoutlib.bridge.android.support.DesignLibUtil;
|
||||
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.graphics.Canvas;
|
||||
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 @@ public class RenderSessionImpl extends RenderAction<SessionParams> {
|
||||
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;
|
||||
@@ -251,6 +257,14 @@ public class RenderSessionImpl extends RenderAction<SessionParams> {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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>
|
||||
@@ -428,6 +442,16 @@ public class RenderSessionImpl extends RenderAction<SessionParams> {
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 3.2 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 2.8 KiB |
@@ -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>
|
||||
|
||||
@@ -48,6 +48,8 @@ import java.io.IOException;
|
||||
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;
|
||||
|
||||
@@ -348,16 +350,46 @@ public class Main {
|
||||
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());
|
||||
@@ -376,6 +408,15 @@ public class Main {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
|
||||
@@ -174,7 +174,9 @@ public final class CreateInfo implements ICreateInfo {
|
||||
"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",
|
||||
@@ -298,6 +300,7 @@ public final class CreateInfo implements ICreateInfo {
|
||||
};
|
||||
|
||||
private final static String[] PROMOTED_FIELDS = new String[] {
|
||||
"android.view.Choreographer#mLastFrameTimeNanos",
|
||||
"android.widget.SimpleMonthView#mTitle",
|
||||
"android.widget.SimpleMonthView#mCalendar",
|
||||
"android.widget.SimpleMonthView#mDayOfWeekLabelCalendar"
|
||||
|
||||
@@ -134,7 +134,33 @@ public class ReplaceMethodCallsAdapter extends ClassVisitor {
|
||||
}
|
||||
});
|
||||
|
||||
// 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 @@ public class ReplaceMethodCallsAdapter extends ClassVisitor {
|
||||
}
|
||||
});
|
||||
|
||||
// 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
|
||||
|
||||
@@ -18,12 +18,22 @@ package com.android.tools.layoutlib.java;
|
||||
|
||||
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 class System_Delegate {
|
||||
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());
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user