Enable NativeAnimationDriver in Fabric

Summary:
Enables NativeAnimationDriver in Fabric.

Fabric animations:
{F151048224}

Pre-Fabric animations:

{F151048344}

Reviewed By: mdvacca

Differential Revision: D14114388

fbshipit-source-id: 1f64db168ae037535a31def7da28b9e0474b7198
This commit is contained in:
Joshua Gross
2019-02-20 00:12:12 -08:00
committed by Facebook Github Bot
parent a89fe4165c
commit ab6ea9c938
8 changed files with 207 additions and 185 deletions

View File

@@ -19,7 +19,6 @@ import com.facebook.react.bridge.UiThreadUtil;
import com.facebook.react.bridge.WritableMap;
import com.facebook.react.common.ReactConstants;
import com.facebook.react.uimanager.IllegalViewOperationException;
import com.facebook.react.uimanager.UIImplementation;
import com.facebook.react.uimanager.UIManagerModule;
import com.facebook.react.uimanager.events.Event;
import com.facebook.react.uimanager.events.EventDispatcherListener;
@@ -57,13 +56,13 @@ import javax.annotation.Nullable;
// there will be only one driver per mapping so all code code should be optimized around that.
private final Map<String, List<EventAnimationDriver>> mEventDrivers = new HashMap<>();
private final UIManagerModule.CustomEventNamesResolver mCustomEventNamesResolver;
private final UIImplementation mUIImplementation;
private final UIManagerModule mUIManagerModule;
private int mAnimatedGraphBFSColor = 0;
// Used to avoid allocating a new array on every frame in `runUpdates` and `onEventDispatch`.
private final List<AnimatedNode> mRunUpdateNodeList = new LinkedList<>();
public NativeAnimatedNodesManager(UIManagerModule uiManager) {
mUIImplementation = uiManager.getUIImplementation();
mUIManagerModule = uiManager;
uiManager.getEventDispatcher().addListener(this);
mCustomEventNamesResolver = uiManager.getDirectEventNamesResolver();
}
@@ -88,7 +87,7 @@ import javax.annotation.Nullable;
} else if ("value".equals(type)) {
node = new ValueAnimatedNode(config);
} else if ("props".equals(type)) {
node = new PropsAnimatedNode(config, this, mUIImplementation);
node = new PropsAnimatedNode(config, this, mUIManagerModule);
} else if ("interpolation".equals(type)) {
node = new InterpolationAnimatedNode(config);
} else if ("addition".equals(type)) {

View File

@@ -11,9 +11,8 @@ import com.facebook.react.bridge.JSApplicationIllegalArgumentException;
import com.facebook.react.bridge.JavaOnlyMap;
import com.facebook.react.bridge.ReadableMap;
import com.facebook.react.bridge.ReadableMapKeySetIterator;
import com.facebook.react.uimanager.ReactStylesDiffMap;
import com.facebook.react.uimanager.UIImplementation;
import com.facebook.react.bridge.UIManager;
import java.util.HashMap;
import java.util.Map;
@@ -28,14 +27,11 @@ import javax.annotation.Nullable;
private int mConnectedViewTag = -1;
private final NativeAnimatedNodesManager mNativeAnimatedNodesManager;
private final UIImplementation mUIImplementation;
private final UIManager mUIManager;
private final Map<String, Integer> mPropNodeMapping;
// This is the backing map for `mDiffMap` we can mutate this to update it instead of having to
// create a new one for each update.
private final JavaOnlyMap mPropMap;
private final ReactStylesDiffMap mDiffMap;
PropsAnimatedNode(ReadableMap config, NativeAnimatedNodesManager nativeAnimatedNodesManager, UIImplementation uiImplementation) {
PropsAnimatedNode(ReadableMap config, NativeAnimatedNodesManager nativeAnimatedNodesManager, UIManager uiManager) {
ReadableMap props = config.getMap("props");
ReadableMapKeySetIterator iter = props.keySetIterator();
mPropNodeMapping = new HashMap<>();
@@ -45,9 +41,8 @@ import javax.annotation.Nullable;
mPropNodeMapping.put(propKey, nodeIndex);
}
mPropMap = new JavaOnlyMap();
mDiffMap = new ReactStylesDiffMap(mPropMap);
mNativeAnimatedNodesManager = nativeAnimatedNodesManager;
mUIImplementation = uiImplementation;
mUIManager = uiManager;
}
public void connectToView(int viewTag) {
@@ -73,9 +68,9 @@ import javax.annotation.Nullable;
mPropMap.putNull(it.nextKey());
}
mUIImplementation.synchronouslyUpdateViewOnUIThread(
mUIManager.synchronouslyUpdateViewOnUIThread(
mConnectedViewTag,
mDiffMap);
mPropMap);
}
public final void updateView() {
@@ -96,8 +91,8 @@ import javax.annotation.Nullable;
}
}
mUIImplementation.synchronouslyUpdateViewOnUIThread(
mUIManager.synchronouslyUpdateViewOnUIThread(
mConnectedViewTag,
mDiffMap);
mPropMap);
}
}

View File

@@ -7,7 +7,6 @@
package com.facebook.react.bridge;
import com.facebook.react.bridge.WritableMap;
import android.view.View;
import com.facebook.react.uimanager.common.MeasureSpecProvider;
import javax.annotation.Nullable;
@@ -44,4 +43,14 @@ public interface UIManager extends JSIModule, PerformanceCounter {
void clearJSResponder();
/**
* Used by native animated module to bypass the process of updating the values through the shadow
* view hierarchy. This method will directly update native views, which means that updates for
* layout-related propertied won't be handled properly.
* Make sure you know what you're doing before calling this method :)
*
* @param tag {@link int} that identifies the view that will be updated
* @param props {@link ReadableMap} props that should be immediately updated in view
*/
void synchronouslyUpdateViewOnUIThread(int reactTag, ReadableMap props);
}

View File

@@ -53,6 +53,7 @@ import com.facebook.react.fabric.mounting.mountitems.UpdateLocalDataMountItem;
import com.facebook.react.fabric.mounting.mountitems.UpdatePropsMountItem;
import com.facebook.react.modules.core.ReactChoreographer;
import com.facebook.react.uimanager.ReactRootViewTagGenerator;
import com.facebook.react.uimanager.ReactStylesDiffMap;
import com.facebook.react.uimanager.ThemedReactContext;
import com.facebook.react.uimanager.ViewManagerPropertyUpdater;
import com.facebook.react.uimanager.ViewManagerRegistry;
@@ -273,6 +274,12 @@ public class FabricUIManager implements UIManager, LifecycleEventListener {
getYogaMeasureMode(minHeight, maxHeight));
}
@Override
public void synchronouslyUpdateViewOnUIThread(int reactTag, ReadableMap props) {
long time = SystemClock.uptimeMillis();
scheduleMountItems(updatePropsMountItem(reactTag, props), time, time, time);
}
/**
* This method enqueues UI operations directly to the UI thread. This might change in the future
* to enforce execution order using {@link ReactChoreographer#CallbackType}.

View File

@@ -9,7 +9,9 @@ rn_android_library(
"events/*.java",
"layoutanimation/*.java",
],
exclude = ["DisplayMetricsHolder.java"],
exclude = [
"DisplayMetricsHolder.java",
],
),
provided_deps = [
react_native_dep("third-party/android/support/v4:lib-support-v4"),

View File

@@ -39,7 +39,7 @@ import java.util.Set;
import javax.annotation.Nullable;
/**
* An class that is used to receive React commands from JS and translate them into a
* A class that is used to receive React commands from JS and translate them into a
* shadow node hierarchy that is then mapped to a native view hierarchy.
*/
public class UIImplementation {

View File

@@ -9,6 +9,7 @@ package com.facebook.react.uimanager;
import static com.facebook.react.bridge.ReactMarkerConstants.CREATE_UI_MANAGER_MODULE_CONSTANTS_END;
import static com.facebook.react.bridge.ReactMarkerConstants.CREATE_UI_MANAGER_MODULE_CONSTANTS_START;
import static com.facebook.react.uimanager.common.UIManagerType.DEFAULT;
import static com.facebook.react.uimanager.common.UIManagerType.FABRIC;
import android.content.ComponentCallbacks2;
import android.content.Context;
@@ -32,6 +33,7 @@ import com.facebook.react.bridge.ReactMethod;
import com.facebook.react.bridge.ReadableArray;
import com.facebook.react.bridge.ReadableMap;
import com.facebook.react.bridge.UIManager;
import com.facebook.react.bridge.UiThreadUtil;
import com.facebook.react.bridge.WritableMap;
import com.facebook.react.common.MapBuilder;
import com.facebook.react.common.ReactConstants;
@@ -217,7 +219,6 @@ public class UIManagerModule extends ReactContextBaseJavaModule
@Override
public void onHostResume() {
mUIImplementation.onHostResume();
}
@@ -381,6 +382,23 @@ public class UIManagerModule extends ReactContextBaseJavaModule
return addRootView(rootView, null, null);
}
/**
* Used by native animated module to bypass the process of updating the values through the shadow
* view hierarchy. This method will directly update native views, which means that updates for
* layout-related propertied won't be handled properly.
* Make sure you know what you're doing before calling this method :)
*/
@Override
public void synchronouslyUpdateViewOnUIThread(int tag, ReadableMap props) {
int uiManagerType = ViewUtil.getUIManagerType(tag);
if (uiManagerType == FABRIC) {
UIManager fabricUIManager = UIManagerHelper.getUIManager(getReactApplicationContext(), uiManagerType);
fabricUIManager.synchronouslyUpdateViewOnUIThread(tag, props);
} else {
mUIImplementation.synchronouslyUpdateViewOnUIThread(tag, new ReactStylesDiffMap(props));
}
}
/**
* Registers a new root view. JS can use the returned tag with manageChildren to add/remove
* children to this view.
@@ -455,6 +473,7 @@ public class UIManagerModule extends ReactContextBaseJavaModule
FLog.d(ReactConstants.TAG, message);
PrinterHolder.getPrinter().logMessage(ReactDebugOverlayTags.UI_MANAGER, message);
}
mUIImplementation.updateView(tag, className, props);
}

View File

@@ -13,7 +13,6 @@ import com.facebook.react.bridge.JavaOnlyArray;
import com.facebook.react.bridge.JavaOnlyMap;
import com.facebook.react.bridge.ReadableMap;
import com.facebook.react.common.MapBuilder;
import com.facebook.react.uimanager.ReactStylesDiffMap;
import com.facebook.react.uimanager.UIImplementation;
import com.facebook.react.uimanager.UIManagerModule;
import com.facebook.react.uimanager.events.Event;
@@ -62,7 +61,6 @@ public class NativeAnimatedNodeTraversalTest {
private long mFrameTimeNanos;
private UIManagerModule mUIManagerMock;
private UIImplementation mUIImplementationMock;
private EventDispatcher mEventDispatcherMock;
private NativeAnimatedNodesManager mNativeAnimatedNodesManager;
@@ -88,14 +86,7 @@ public class NativeAnimatedNodeTraversalTest {
mFrameTimeNanos = INITIAL_FRAME_TIME_NANOS;
mUIManagerMock = mock(UIManagerModule.class);
mUIImplementationMock = mock(UIImplementation.class);
mEventDispatcherMock = mock(EventDispatcher.class);
PowerMockito.when(mUIManagerMock.getUIImplementation()).thenAnswer(new Answer<UIImplementation>() {
@Override
public UIImplementation answer(InvocationOnMock invocation) throws Throwable {
return mUIImplementationMock;
}
});
PowerMockito.when(mUIManagerMock.getEventDispatcher()).thenAnswer(new Answer<EventDispatcher>() {
@Override
public EventDispatcher answer(InvocationOnMock invocation) throws Throwable {
@@ -166,21 +157,21 @@ public class NativeAnimatedNodeTraversalTest {
JavaOnlyMap.of("type", "frames", "frames", frames, "toValue", 1d),
animationCallback);
ArgumentCaptor<ReactStylesDiffMap> stylesCaptor =
ArgumentCaptor.forClass(ReactStylesDiffMap.class);
ArgumentCaptor<ReadableMap> stylesCaptor =
ArgumentCaptor.forClass(ReadableMap.class);
for (int i = 0; i < frames.size(); i++) {
reset(mUIImplementationMock);
reset(mUIManagerMock);
mNativeAnimatedNodesManager.runUpdates(nextFrameTime());
verify(mUIImplementationMock)
verify(mUIManagerMock)
.synchronouslyUpdateViewOnUIThread(eq(1000), stylesCaptor.capture());
assertThat(stylesCaptor.getValue().getDouble("opacity", Double.NaN))
assertThat(stylesCaptor.getValue().getDouble("opacity"))
.isEqualTo(frames.getDouble(i));
}
reset(mUIImplementationMock);
reset(mUIManagerMock);
mNativeAnimatedNodesManager.runUpdates(nextFrameTime());
verifyNoMoreInteractions(mUIImplementationMock);
verifyNoMoreInteractions(mUIManagerMock);
}
@Test
@@ -195,23 +186,23 @@ public class NativeAnimatedNodeTraversalTest {
JavaOnlyMap.of("type", "frames", "frames", frames, "toValue", 1d, "iterations", 5),
animationCallback);
ArgumentCaptor<ReactStylesDiffMap> stylesCaptor =
ArgumentCaptor.forClass(ReactStylesDiffMap.class);
ArgumentCaptor<ReadableMap> stylesCaptor =
ArgumentCaptor.forClass(ReadableMap.class);
for (int iteration = 0; iteration < 5; iteration++) {
for (int i = 0; i < frames.size(); i++) {
reset(mUIImplementationMock);
reset(mUIManagerMock);
mNativeAnimatedNodesManager.runUpdates(nextFrameTime());
verify(mUIImplementationMock)
verify(mUIManagerMock)
.synchronouslyUpdateViewOnUIThread(eq(1000), stylesCaptor.capture());
assertThat(stylesCaptor.getValue().getDouble("opacity", Double.NaN))
assertThat(stylesCaptor.getValue().getDouble("opacity"))
.isEqualTo(frames.getDouble(i));
}
}
reset(mUIImplementationMock);
reset(mUIManagerMock);
mNativeAnimatedNodesManager.runUpdates(nextFrameTime());
verifyNoMoreInteractions(mUIImplementationMock);
verifyNoMoreInteractions(mUIManagerMock);
}
@Test
@@ -279,23 +270,23 @@ public class NativeAnimatedNodeTraversalTest {
config,
animationCallback);
ArgumentCaptor<ReactStylesDiffMap> stylesCaptor =
ArgumentCaptor.forClass(ReactStylesDiffMap.class);
ArgumentCaptor<ReadableMap> stylesCaptor =
ArgumentCaptor.forClass(ReadableMap.class);
reset(mUIImplementationMock);
reset(mUIManagerMock);
mNativeAnimatedNodesManager.runUpdates(nextFrameTime());
verify(mUIImplementationMock).synchronouslyUpdateViewOnUIThread(eq(1000), stylesCaptor.capture());
assertThat(stylesCaptor.getValue().getDouble("opacity", Double.NaN)).isEqualTo(0);
verify(mUIManagerMock).synchronouslyUpdateViewOnUIThread(eq(1000), stylesCaptor.capture());
assertThat(stylesCaptor.getValue().getDouble("opacity")).isEqualTo(0);
double previousValue = 0d;
boolean wasGreaterThanOne = false;
/* run 3 secs of animation */
for (int i = 0; i < 3 * 60; i++) {
reset(mUIImplementationMock);
reset(mUIManagerMock);
mNativeAnimatedNodesManager.runUpdates(nextFrameTime());
verify(mUIImplementationMock, atMost(1))
verify(mUIManagerMock, atMost(1))
.synchronouslyUpdateViewOnUIThread(eq(1000), stylesCaptor.capture());
double currentValue = stylesCaptor.getValue().getDouble("opacity", Double.NaN);
double currentValue = stylesCaptor.getValue().getDouble("opacity");
if (currentValue > 1d) {
wasGreaterThanOne = true;
}
@@ -311,9 +302,9 @@ public class NativeAnimatedNodeTraversalTest {
} else {
assertThat(wasGreaterThanOne);
}
reset(mUIImplementationMock);
reset(mUIManagerMock);
mNativeAnimatedNodesManager.runUpdates(nextFrameTime());
verifyNoMoreInteractions(mUIImplementationMock);
verifyNoMoreInteractions(mUIManagerMock);
}
@Test
@@ -402,13 +393,13 @@ public class NativeAnimatedNodeTraversalTest {
),
animationCallback);
ArgumentCaptor<ReactStylesDiffMap> stylesCaptor =
ArgumentCaptor.forClass(ReactStylesDiffMap.class);
ArgumentCaptor<ReadableMap> stylesCaptor =
ArgumentCaptor.forClass(ReadableMap.class);
reset(mUIImplementationMock);
reset(mUIManagerMock);
mNativeAnimatedNodesManager.runUpdates(nextFrameTime());
verify(mUIImplementationMock).synchronouslyUpdateViewOnUIThread(eq(1000), stylesCaptor.capture());
assertThat(stylesCaptor.getValue().getDouble("opacity", Double.NaN)).isEqualTo(0);
verify(mUIManagerMock).synchronouslyUpdateViewOnUIThread(eq(1000), stylesCaptor.capture());
assertThat(stylesCaptor.getValue().getDouble("opacity")).isEqualTo(0);
double previousValue = 0d;
boolean wasGreaterThanOne = false;
@@ -416,11 +407,11 @@ public class NativeAnimatedNodeTraversalTest {
int numberOfResets = 0;
/* run 3 secs of animation, five times */
for (int i = 0; i < 3 * 60 * 5; i++) {
reset(mUIImplementationMock);
reset(mUIManagerMock);
mNativeAnimatedNodesManager.runUpdates(nextFrameTime());
verify(mUIImplementationMock, atMost(1))
verify(mUIManagerMock, atMost(1))
.synchronouslyUpdateViewOnUIThread(eq(1000), stylesCaptor.capture());
double currentValue = stylesCaptor.getValue().getDouble("opacity", Double.NaN);
double currentValue = stylesCaptor.getValue().getDouble("opacity");
if (currentValue > 1d) {
wasGreaterThanOne = true;
}
@@ -446,9 +437,9 @@ public class NativeAnimatedNodeTraversalTest {
assertThat(wasGreaterThanOne);
// verify that value reset 4 times after finishing a full animation
assertThat(numberOfResets).isEqualTo(4);
reset(mUIImplementationMock);
reset(mUIManagerMock);
mNativeAnimatedNodesManager.runUpdates(nextFrameTime());
verifyNoMoreInteractions(mUIImplementationMock);
verifyNoMoreInteractions(mUIManagerMock);
}
@Test
@@ -468,22 +459,22 @@ public class NativeAnimatedNodeTraversalTest {
0.998d),
animationCallback);
ArgumentCaptor<ReactStylesDiffMap> stylesCaptor =
ArgumentCaptor.forClass(ReactStylesDiffMap.class);
ArgumentCaptor<ReadableMap> stylesCaptor =
ArgumentCaptor.forClass(ReadableMap.class);
reset(mUIImplementationMock);
reset(mUIManagerMock);
mNativeAnimatedNodesManager.runUpdates(nextFrameTime());
verify(mUIImplementationMock, atMost(1))
verify(mUIManagerMock, atMost(1))
.synchronouslyUpdateViewOnUIThread(eq(1000), stylesCaptor.capture());
double previousValue = stylesCaptor.getValue().getDouble("opacity", Double.NaN);
double previousValue = stylesCaptor.getValue().getDouble("opacity");
double previousDiff = Double.POSITIVE_INFINITY;
/* run 3 secs of animation */
for (int i = 0; i < 3 * 60; i++) {
reset(mUIImplementationMock);
reset(mUIManagerMock);
mNativeAnimatedNodesManager.runUpdates(nextFrameTime());
verify(mUIImplementationMock, atMost(1))
verify(mUIManagerMock, atMost(1))
.synchronouslyUpdateViewOnUIThread(eq(1000), stylesCaptor.capture());
double currentValue = stylesCaptor.getValue().getDouble("opacity", Double.NaN);
double currentValue = stylesCaptor.getValue().getDouble("opacity");
double currentDiff = currentValue - previousValue;
// verify monotonicity
// greater *or equal* because the animation stops during these 3 seconds
@@ -503,9 +494,9 @@ public class NativeAnimatedNodeTraversalTest {
previousDiff = currentDiff;
}
// should be done in 3s
reset(mUIImplementationMock);
reset(mUIManagerMock);
mNativeAnimatedNodesManager.runUpdates(nextFrameTime());
verifyNoMoreInteractions(mUIImplementationMock);
verifyNoMoreInteractions(mUIManagerMock);
}
@Test
@@ -527,25 +518,25 @@ public class NativeAnimatedNodeTraversalTest {
5),
animationCallback);
ArgumentCaptor<ReactStylesDiffMap> stylesCaptor =
ArgumentCaptor.forClass(ReactStylesDiffMap.class);
ArgumentCaptor<ReadableMap> stylesCaptor =
ArgumentCaptor.forClass(ReadableMap.class);
reset(mUIImplementationMock);
reset(mUIManagerMock);
mNativeAnimatedNodesManager.runUpdates(nextFrameTime());
verify(mUIImplementationMock, atMost(1))
verify(mUIManagerMock, atMost(1))
.synchronouslyUpdateViewOnUIThread(eq(1000), stylesCaptor.capture());
double previousValue = stylesCaptor.getValue().getDouble("opacity", Double.NaN);
double previousValue = stylesCaptor.getValue().getDouble("opacity");
double previousDiff = Double.POSITIVE_INFINITY;
double initialValue = stylesCaptor.getValue().getDouble("opacity", Double.NaN);
double initialValue = stylesCaptor.getValue().getDouble("opacity");
boolean didComeToRest = false;
int numberOfResets = 0;
/* run 3 secs of animation, five times */
for (int i = 0; i < 3 * 60 * 5; i++) {
reset(mUIImplementationMock);
reset(mUIManagerMock);
mNativeAnimatedNodesManager.runUpdates(nextFrameTime());
verify(mUIImplementationMock, atMost(1))
verify(mUIManagerMock, atMost(1))
.synchronouslyUpdateViewOnUIThread(eq(1000), stylesCaptor.capture());
double currentValue = stylesCaptor.getValue().getDouble("opacity", Double.NaN);
double currentValue = stylesCaptor.getValue().getDouble("opacity");
double currentDiff = currentValue - previousValue;
// Test to see if it reset after coming to rest (i.e. dropped back to )
if (didComeToRest && currentValue == initialValue) {
@@ -564,9 +555,9 @@ public class NativeAnimatedNodeTraversalTest {
// verify that value reset (looped) 4 times after finishing a full animation
assertThat(numberOfResets).isEqualTo(4);
reset(mUIImplementationMock);
reset(mUIManagerMock);
mNativeAnimatedNodesManager.runUpdates(nextFrameTime());
verifyNoMoreInteractions(mUIImplementationMock);
verifyNoMoreInteractions(mUIManagerMock);
}
@Test
@@ -653,23 +644,23 @@ public class NativeAnimatedNodeTraversalTest {
JavaOnlyMap.of("type", "frames", "frames", frames, "toValue", 1010d),
animationCallback);
ArgumentCaptor<ReactStylesDiffMap> stylesCaptor =
ArgumentCaptor.forClass(ReactStylesDiffMap.class);
ArgumentCaptor<ReadableMap> stylesCaptor =
ArgumentCaptor.forClass(ReadableMap.class);
reset(mUIImplementationMock);
reset(mUIManagerMock);
mNativeAnimatedNodesManager.runUpdates(nextFrameTime());
verify(mUIImplementationMock).synchronouslyUpdateViewOnUIThread(eq(50), stylesCaptor.capture());
assertThat(stylesCaptor.getValue().getDouble("translateX", Double.NaN)).isEqualTo(1100d);
verify(mUIManagerMock).synchronouslyUpdateViewOnUIThread(eq(50), stylesCaptor.capture());
assertThat(stylesCaptor.getValue().getDouble("translateX")).isEqualTo(1100d);
reset(mUIImplementationMock);
reset(mUIManagerMock);
mNativeAnimatedNodesManager.runUpdates(nextFrameTime());
verify(mUIImplementationMock)
verify(mUIManagerMock)
.synchronouslyUpdateViewOnUIThread(eq(50), stylesCaptor.capture());
assertThat(stylesCaptor.getValue().getDouble("translateX", Double.NaN)).isEqualTo(1111d);
assertThat(stylesCaptor.getValue().getDouble("translateX")).isEqualTo(1111d);
reset(mUIImplementationMock);
reset(mUIManagerMock);
mNativeAnimatedNodesManager.runUpdates(nextFrameTime());
verifyNoMoreInteractions(mUIImplementationMock);
verifyNoMoreInteractions(mUIManagerMock);
}
/**
@@ -692,23 +683,23 @@ public class NativeAnimatedNodeTraversalTest {
JavaOnlyMap.of("type", "frames", "frames", frames, "toValue", 101d),
animationCallback);
ArgumentCaptor<ReactStylesDiffMap> stylesCaptor =
ArgumentCaptor.forClass(ReactStylesDiffMap.class);
ArgumentCaptor<ReadableMap> stylesCaptor =
ArgumentCaptor.forClass(ReadableMap.class);
reset(mUIImplementationMock);
reset(mUIManagerMock);
mNativeAnimatedNodesManager.runUpdates(nextFrameTime());
verify(mUIImplementationMock).synchronouslyUpdateViewOnUIThread(eq(50), stylesCaptor.capture());
assertThat(stylesCaptor.getValue().getDouble("translateX", Double.NaN)).isEqualTo(1100d);
verify(mUIManagerMock).synchronouslyUpdateViewOnUIThread(eq(50), stylesCaptor.capture());
assertThat(stylesCaptor.getValue().getDouble("translateX")).isEqualTo(1100d);
reset(mUIImplementationMock);
reset(mUIManagerMock);
mNativeAnimatedNodesManager.runUpdates(nextFrameTime());
verify(mUIImplementationMock)
verify(mUIManagerMock)
.synchronouslyUpdateViewOnUIThread(eq(50), stylesCaptor.capture());
assertThat(stylesCaptor.getValue().getDouble("translateX", Double.NaN)).isEqualTo(1101d);
assertThat(stylesCaptor.getValue().getDouble("translateX")).isEqualTo(1101d);
reset(mUIImplementationMock);
reset(mUIManagerMock);
mNativeAnimatedNodesManager.runUpdates(nextFrameTime());
verifyNoMoreInteractions(mUIImplementationMock);
verifyNoMoreInteractions(mUIManagerMock);
}
/**
@@ -741,26 +732,26 @@ public class NativeAnimatedNodeTraversalTest {
JavaOnlyMap.of("type", "frames", "frames", secondFrames, "toValue", 1010d),
animationCallback);
ArgumentCaptor<ReactStylesDiffMap> stylesCaptor =
ArgumentCaptor.forClass(ReactStylesDiffMap.class);
ArgumentCaptor<ReadableMap> stylesCaptor =
ArgumentCaptor.forClass(ReadableMap.class);
reset(mUIImplementationMock);
reset(mUIManagerMock);
mNativeAnimatedNodesManager.runUpdates(nextFrameTime());
verify(mUIImplementationMock).synchronouslyUpdateViewOnUIThread(eq(50), stylesCaptor.capture());
assertThat(stylesCaptor.getValue().getDouble("translateX", Double.NaN)).isEqualTo(1100d);
verify(mUIManagerMock).synchronouslyUpdateViewOnUIThread(eq(50), stylesCaptor.capture());
assertThat(stylesCaptor.getValue().getDouble("translateX")).isEqualTo(1100d);
for (int i = 1; i < secondFrames.size(); i++) {
reset(mUIImplementationMock);
reset(mUIManagerMock);
mNativeAnimatedNodesManager.runUpdates(nextFrameTime());
verify(mUIImplementationMock)
verify(mUIManagerMock)
.synchronouslyUpdateViewOnUIThread(eq(50), stylesCaptor.capture());
assertThat(stylesCaptor.getValue().getDouble("translateX", Double.NaN))
assertThat(stylesCaptor.getValue().getDouble("translateX"))
.isEqualTo(1200d + secondFrames.getDouble(i) * 10d);
}
reset(mUIImplementationMock);
reset(mUIManagerMock);
mNativeAnimatedNodesManager.runUpdates(nextFrameTime());
verifyNoMoreInteractions(mUIImplementationMock);
verifyNoMoreInteractions(mUIManagerMock);
}
@Test
@@ -802,22 +793,22 @@ public class NativeAnimatedNodeTraversalTest {
JavaOnlyMap.of("type", "frames", "frames", frames, "toValue", 10d),
animationCallback);
ArgumentCaptor<ReactStylesDiffMap> stylesCaptor =
ArgumentCaptor.forClass(ReactStylesDiffMap.class);
ArgumentCaptor<ReadableMap> stylesCaptor =
ArgumentCaptor.forClass(ReadableMap.class);
reset(mUIImplementationMock);
reset(mUIManagerMock);
mNativeAnimatedNodesManager.runUpdates(nextFrameTime());
verify(mUIImplementationMock).synchronouslyUpdateViewOnUIThread(eq(50), stylesCaptor.capture());
assertThat(stylesCaptor.getValue().getDouble("translateX", Double.NaN)).isEqualTo(5d);
verify(mUIManagerMock).synchronouslyUpdateViewOnUIThread(eq(50), stylesCaptor.capture());
assertThat(stylesCaptor.getValue().getDouble("translateX")).isEqualTo(5d);
reset(mUIImplementationMock);
reset(mUIManagerMock);
mNativeAnimatedNodesManager.runUpdates(nextFrameTime());
verify(mUIImplementationMock).synchronouslyUpdateViewOnUIThread(eq(50), stylesCaptor.capture());
assertThat(stylesCaptor.getValue().getDouble("translateX", Double.NaN)).isEqualTo(20d);
verify(mUIManagerMock).synchronouslyUpdateViewOnUIThread(eq(50), stylesCaptor.capture());
assertThat(stylesCaptor.getValue().getDouble("translateX")).isEqualTo(20d);
reset(mUIImplementationMock);
reset(mUIManagerMock);
mNativeAnimatedNodesManager.runUpdates(nextFrameTime());
verifyNoMoreInteractions(mUIImplementationMock);
verifyNoMoreInteractions(mUIManagerMock);
}
/**
@@ -840,32 +831,32 @@ public class NativeAnimatedNodeTraversalTest {
ArgumentCaptor<ReadableMap> callbackResponseCaptor = ArgumentCaptor.forClass(ReadableMap.class);
reset(animationCallback);
reset(mUIImplementationMock);
reset(mUIManagerMock);
mNativeAnimatedNodesManager.runUpdates(nextFrameTime());
mNativeAnimatedNodesManager.runUpdates(nextFrameTime());
verify(mUIImplementationMock, times(2))
.synchronouslyUpdateViewOnUIThread(anyInt(), any(ReactStylesDiffMap.class));
verify(mUIManagerMock, times(2))
.synchronouslyUpdateViewOnUIThread(anyInt(), any(ReadableMap.class));
verifyNoMoreInteractions(animationCallback);
reset(animationCallback);
reset(mUIImplementationMock);
reset(mUIManagerMock);
mNativeAnimatedNodesManager.stopAnimation(404);
verify(animationCallback).invoke(callbackResponseCaptor.capture());
verifyNoMoreInteractions(animationCallback);
verifyNoMoreInteractions(mUIImplementationMock);
verifyNoMoreInteractions(mUIManagerMock);
assertThat(callbackResponseCaptor.getValue().hasKey("finished")).isTrue();
assertThat(callbackResponseCaptor.getValue().getBoolean("finished")).isFalse();
reset(animationCallback);
reset(mUIImplementationMock);
reset(mUIManagerMock);
// Run "update" loop a few more times -> we expect no further updates nor callback calls to be
// triggered
for (int i = 0; i < 5; i++) {
mNativeAnimatedNodesManager.runUpdates(nextFrameTime());
}
verifyNoMoreInteractions(mUIImplementationMock);
verifyNoMoreInteractions(mUIManagerMock);
verifyNoMoreInteractions(animationCallback);
}
@@ -908,21 +899,21 @@ public class NativeAnimatedNodeTraversalTest {
JavaOnlyMap.of("type", "frames", "frames", frames, "toValue", 20d),
animationCallback);
ArgumentCaptor<ReactStylesDiffMap> stylesCaptor =
ArgumentCaptor.forClass(ReactStylesDiffMap.class);
ArgumentCaptor<ReadableMap> stylesCaptor =
ArgumentCaptor.forClass(ReadableMap.class);
for (int i = 0; i < frames.size(); i++) {
reset(mUIImplementationMock);
reset(mUIManagerMock);
mNativeAnimatedNodesManager.runUpdates(nextFrameTime());
verify(mUIImplementationMock)
verify(mUIManagerMock)
.synchronouslyUpdateViewOnUIThread(eq(50), stylesCaptor.capture());
assertThat(stylesCaptor.getValue().getDouble("opacity", Double.NaN))
assertThat(stylesCaptor.getValue().getDouble("opacity"))
.isEqualTo(frames.getDouble(i));
}
reset(mUIImplementationMock);
reset(mUIManagerMock);
mNativeAnimatedNodesManager.runUpdates(nextFrameTime());
verifyNoMoreInteractions(mUIImplementationMock);
verifyNoMoreInteractions(mUIManagerMock);
}
private Event createScrollEvent(final int tag, final double value) {
@@ -952,13 +943,13 @@ public class NativeAnimatedNodeTraversalTest {
mNativeAnimatedNodesManager.onEventDispatch(createScrollEvent(viewTag, 10));
ArgumentCaptor<ReactStylesDiffMap> stylesCaptor =
ArgumentCaptor.forClass(ReactStylesDiffMap.class);
ArgumentCaptor<ReadableMap> stylesCaptor =
ArgumentCaptor.forClass(ReadableMap.class);
reset(mUIImplementationMock);
reset(mUIManagerMock);
mNativeAnimatedNodesManager.runUpdates(nextFrameTime());
verify(mUIImplementationMock).synchronouslyUpdateViewOnUIThread(eq(viewTag), stylesCaptor.capture());
assertThat(stylesCaptor.getValue().getDouble("opacity", Double.NaN)).isEqualTo(10);
verify(mUIManagerMock).synchronouslyUpdateViewOnUIThread(eq(viewTag), stylesCaptor.capture());
assertThat(stylesCaptor.getValue().getDouble("opacity")).isEqualTo(10);
}
@Test
@@ -977,13 +968,13 @@ public class NativeAnimatedNodeTraversalTest {
mNativeAnimatedNodesManager.onEventDispatch(createScrollEvent(viewTag, 10));
ArgumentCaptor<ReactStylesDiffMap> stylesCaptor =
ArgumentCaptor.forClass(ReactStylesDiffMap.class);
ArgumentCaptor<ReadableMap> stylesCaptor =
ArgumentCaptor.forClass(ReadableMap.class);
reset(mUIImplementationMock);
reset(mUIManagerMock);
mNativeAnimatedNodesManager.runUpdates(nextFrameTime());
verify(mUIImplementationMock).synchronouslyUpdateViewOnUIThread(eq(viewTag), stylesCaptor.capture());
assertThat(stylesCaptor.getValue().getDouble("opacity", Double.NaN)).isEqualTo(0);
verify(mUIManagerMock).synchronouslyUpdateViewOnUIThread(eq(viewTag), stylesCaptor.capture());
assertThat(stylesCaptor.getValue().getDouble("opacity")).isEqualTo(0);
}
@Test
@@ -1008,13 +999,13 @@ public class NativeAnimatedNodeTraversalTest {
mNativeAnimatedNodesManager.onEventDispatch(createScrollEvent(viewTag, 10));
ArgumentCaptor<ReactStylesDiffMap> stylesCaptor =
ArgumentCaptor.forClass(ReactStylesDiffMap.class);
ArgumentCaptor<ReadableMap> stylesCaptor =
ArgumentCaptor.forClass(ReadableMap.class);
reset(mUIImplementationMock);
reset(mUIManagerMock);
mNativeAnimatedNodesManager.runUpdates(nextFrameTime());
verify(mUIImplementationMock).synchronouslyUpdateViewOnUIThread(eq(viewTag), stylesCaptor.capture());
assertThat(stylesCaptor.getValue().getDouble("opacity", Double.NaN)).isEqualTo(10);
verify(mUIManagerMock).synchronouslyUpdateViewOnUIThread(eq(viewTag), stylesCaptor.capture());
assertThat(stylesCaptor.getValue().getDouble("opacity")).isEqualTo(10);
}
@Test
@@ -1042,20 +1033,20 @@ public class NativeAnimatedNodeTraversalTest {
JavaOnlyMap.of("type", "frames", "frames", frames, "toValue", 0d),
animationCallback);
ArgumentCaptor<ReactStylesDiffMap> stylesCaptor =
ArgumentCaptor.forClass(ReactStylesDiffMap.class);
ArgumentCaptor<ReadableMap> stylesCaptor =
ArgumentCaptor.forClass(ReadableMap.class);
for (int i = 0; i < frames.size(); i++) {
reset(mUIImplementationMock);
reset(mUIManagerMock);
mNativeAnimatedNodesManager.runUpdates(nextFrameTime());
}
verify(mUIImplementationMock).synchronouslyUpdateViewOnUIThread(eq(viewTag), stylesCaptor.capture());
assertThat(stylesCaptor.getValue().getDouble("opacity", Double.NaN)).isEqualTo(0);
verify(mUIManagerMock).synchronouslyUpdateViewOnUIThread(eq(viewTag), stylesCaptor.capture());
assertThat(stylesCaptor.getValue().getDouble("opacity")).isEqualTo(0);
reset(mUIImplementationMock);
reset(mUIManagerMock);
mNativeAnimatedNodesManager.restoreDefaultValues(propsNodeTag, viewTag);
verify(mUIImplementationMock).synchronouslyUpdateViewOnUIThread(eq(viewTag), stylesCaptor.capture());
verify(mUIManagerMock).synchronouslyUpdateViewOnUIThread(eq(viewTag), stylesCaptor.capture());
assertThat(stylesCaptor.getValue().isNull("opacity"));
}
@@ -1105,24 +1096,24 @@ public class NativeAnimatedNodeTraversalTest {
createAnimatedGraphWithTrackingNode(1000, 0d, animationConfig);
ArgumentCaptor<ReactStylesDiffMap> stylesCaptor =
ArgumentCaptor.forClass(ReactStylesDiffMap.class);
ArgumentCaptor<ReadableMap> stylesCaptor =
ArgumentCaptor.forClass(ReadableMap.class);
reset(mUIImplementationMock);
reset(mUIManagerMock);
mNativeAnimatedNodesManager.runUpdates(nextFrameTime());
verify(mUIImplementationMock).synchronouslyUpdateViewOnUIThread(eq(1000), stylesCaptor.capture());
assertThat(stylesCaptor.getValue().getDouble("translateX", Double.NaN)).isEqualTo(0d);
verify(mUIManagerMock).synchronouslyUpdateViewOnUIThread(eq(1000), stylesCaptor.capture());
assertThat(stylesCaptor.getValue().getDouble("translateX")).isEqualTo(0d);
// update "toValue" to 100, we expect tracking animation to animate now from 0 to 100 in 5 steps
mNativeAnimatedNodesManager.setAnimatedNodeValue(1, 100d);
mNativeAnimatedNodesManager.runUpdates(nextFrameTime()); // kick off the animation
for (int i = 0; i < frames.size(); i++) {
reset(mUIImplementationMock);
reset(mUIManagerMock);
mNativeAnimatedNodesManager.runUpdates(nextFrameTime());
verify(mUIImplementationMock)
verify(mUIManagerMock)
.synchronouslyUpdateViewOnUIThread(eq(1000), stylesCaptor.capture());
assertThat(stylesCaptor.getValue().getDouble("translateX", Double.NaN))
assertThat(stylesCaptor.getValue().getDouble("translateX"))
.isEqualTo(frames.getDouble(i) * 100d);
}
@@ -1132,11 +1123,11 @@ public class NativeAnimatedNodeTraversalTest {
mNativeAnimatedNodesManager.runUpdates(nextFrameTime()); // kick off the animation
for (int i = 0; i < 2; i++) {
reset(mUIImplementationMock);
reset(mUIManagerMock);
mNativeAnimatedNodesManager.runUpdates(nextFrameTime());
verify(mUIImplementationMock)
verify(mUIManagerMock)
.synchronouslyUpdateViewOnUIThread(eq(1000), stylesCaptor.capture());
assertThat(stylesCaptor.getValue().getDouble("translateX", Double.NaN))
assertThat(stylesCaptor.getValue().getDouble("translateX"))
.isEqualTo(100d * (1d - frames.getDouble(i)));
}
@@ -1149,11 +1140,11 @@ public class NativeAnimatedNodeTraversalTest {
mNativeAnimatedNodesManager.runUpdates(nextFrameTime()); // kick off the animation
for (int i = 0; i < frames.size(); i++) {
reset(mUIImplementationMock);
reset(mUIManagerMock);
mNativeAnimatedNodesManager.runUpdates(nextFrameTime());
verify(mUIImplementationMock)
verify(mUIManagerMock)
.synchronouslyUpdateViewOnUIThread(eq(1000), stylesCaptor.capture());
assertThat(stylesCaptor.getValue().getDouble("translateX", Double.NaN))
assertThat(stylesCaptor.getValue().getDouble("translateX"))
.isEqualTo(50d + 50d * frames.getDouble(i));
}
}
@@ -1174,38 +1165,38 @@ public class NativeAnimatedNodeTraversalTest {
mNativeAnimatedNodesManager.setAnimatedNodeValue(1, 100d);
mNativeAnimatedNodesManager.runUpdates(nextFrameTime()); // make sure animation starts
reset(mUIImplementationMock);
reset(mUIManagerMock);
for (int i = 0; i < frames.size(); i++) {
assertThat(mNativeAnimatedNodesManager.hasActiveAnimations()).isTrue();
mNativeAnimatedNodesManager.runUpdates(nextFrameTime());
}
verify(mUIImplementationMock, times(frames.size()))
.synchronouslyUpdateViewOnUIThread(eq(1000), any(ReactStylesDiffMap.class));
verify(mUIManagerMock, times(frames.size()))
.synchronouslyUpdateViewOnUIThread(eq(1000), any(ReadableMap.class));
// the animation has completed, we expect no updates to be done
reset(mUIImplementationMock);
reset(mUIManagerMock);
assertThat(mNativeAnimatedNodesManager.hasActiveAnimations()).isFalse();
mNativeAnimatedNodesManager.runUpdates(nextFrameTime());
verifyNoMoreInteractions(mUIImplementationMock);
verifyNoMoreInteractions(mUIManagerMock);
// we update end value and expect the animation to restart
mNativeAnimatedNodesManager.setAnimatedNodeValue(1, 200d);
mNativeAnimatedNodesManager.runUpdates(nextFrameTime()); // make sure animation starts
reset(mUIImplementationMock);
reset(mUIManagerMock);
for (int i = 0; i < frames.size(); i++) {
assertThat(mNativeAnimatedNodesManager.hasActiveAnimations()).isTrue();
mNativeAnimatedNodesManager.runUpdates(nextFrameTime());
}
verify(mUIImplementationMock, times(frames.size()))
.synchronouslyUpdateViewOnUIThread(eq(1000), any(ReactStylesDiffMap.class));
verify(mUIManagerMock, times(frames.size()))
.synchronouslyUpdateViewOnUIThread(eq(1000), any(ReadableMap.class));
// the animation has completed, we expect no updates to be done
reset(mUIImplementationMock);
reset(mUIManagerMock);
assertThat(mNativeAnimatedNodesManager.hasActiveAnimations()).isFalse();
mNativeAnimatedNodesManager.runUpdates(nextFrameTime());
verifyNoMoreInteractions(mUIImplementationMock);
verifyNoMoreInteractions(mUIManagerMock);
}
/**