From 4f5c2b48fe9e0e9d2f5024838ee16006f35ba326 Mon Sep 17 00:00:00 2001 From: Mike Lambert Date: Thu, 7 Jul 2016 05:50:58 -0700 Subject: [PATCH] Fix timestamps on android touch events to use milliseconds, to be consistent with iOS Summary: So `PanReponder.onPanResponderRelease/onPanResponderTerminate` receive a `gestureState` object containing a `onPanResponderTerminate.vx/vy` property. On Android and iOS, they appear to be orders of magnitude different, which appear to be due to the different scale of timestamps that are used when generating touch events. This pull request fixes the timestamps to be milliseconds on both platforms (since I assume iOS is the more authoritative one, and is the one that `react-native-viewpager`'s vx thresholds written written to compare against.) As far as I can tell, the RN code doesn't use the `vx/vy` properties, so they should be okay. And looks like the RN code only cares about relative values of `startTimestamp/currentTimestamp/previousTimestamp` though, so should be fine too. it's quite possible there will be downstream android breakage with this change, particularly for those who are already compensating for the RN discrepancy. Closes https://github.com/facebook/react-native/pull/8199 Differential Revision: D3528215 Pulled By: dmmiller fbshipit-source-id: cbd25bb7e7bb87fa77b661a057643a6ea97bc3f1 --- .../facebook/react/tests/TextInputTestCase.java | 12 ++++++------ .../com/facebook/react/common/SystemClock.java | 4 ++++ .../com/facebook/react/modules/core/Timing.java | 2 +- .../react/uimanager/JSTouchDispatcher.java | 12 ++++++------ .../facebook/react/uimanager/OnLayoutEvent.java | 2 +- .../views/drawer/ReactDrawerLayoutManager.java | 8 ++++---- .../facebook/react/views/image/ReactImageView.java | 10 +++++----- .../react/views/modal/ReactModalHostManager.java | 4 ++-- .../react/views/picker/ReactPickerManager.java | 2 +- .../recyclerview/RecyclerViewBackedScrollView.java | 4 ++-- .../react/views/scroll/ReactScrollViewHelper.java | 2 +- .../react/views/slider/ReactSliderManager.java | 4 ++-- .../swiperefresh/SwipeRefreshLayoutManager.java | 2 +- .../react/views/switchview/ReactSwitchManager.java | 2 +- .../views/textinput/ReactTextInputManager.java | 14 +++++++------- .../react/views/toolbar/ReactToolbarManager.java | 4 ++-- .../react/views/viewpager/ReactViewPager.java | 6 +++--- .../react/views/webview/ReactWebViewManager.java | 8 ++++---- .../test/java/com/facebook/react/RootViewTest.java | 6 +++--- .../react/modules/timing/TimingModuleTest.java | 2 +- 20 files changed, 57 insertions(+), 53 deletions(-) diff --git a/ReactAndroid/src/androidTest/java/com/facebook/react/tests/TextInputTestCase.java b/ReactAndroid/src/androidTest/java/com/facebook/react/tests/TextInputTestCase.java index 9558a5d91..44c2321b2 100644 --- a/ReactAndroid/src/androidTest/java/com/facebook/react/tests/TextInputTestCase.java +++ b/ReactAndroid/src/androidTest/java/com/facebook/react/tests/TextInputTestCase.java @@ -116,7 +116,7 @@ public class TextInputTestCase extends ReactAppInstrumentationTestCase { eventDispatcher.dispatchEvent( new ReactTextChangedEvent( reactEditText.getId(), - SystemClock.nanoTime(), + SystemClock.elapsedRealtime(), newText.toString(), (int) PixelUtil.toDIPFromPixel(contentWidth), (int) PixelUtil.toDIPFromPixel(contentHeight), @@ -125,7 +125,7 @@ public class TextInputTestCase extends ReactAppInstrumentationTestCase { eventDispatcher.dispatchEvent( new ReactTextInputEvent( reactEditText.getId(), - SystemClock.nanoTime(), + SystemClock.elapsedRealtime(), newText.toString(), "", start, @@ -150,7 +150,7 @@ public class TextInputTestCase extends ReactAppInstrumentationTestCase { eventDispatcher.dispatchEvent( new ReactTextChangedEvent( reactEditText.getId(), - SystemClock.nanoTime(), + SystemClock.elapsedRealtime(), newText.toString(), (int) PixelUtil.toDIPFromPixel(contentWidth), (int) PixelUtil.toDIPFromPixel(contentHeight), @@ -159,7 +159,7 @@ public class TextInputTestCase extends ReactAppInstrumentationTestCase { eventDispatcher.dispatchEvent( new ReactTextInputEvent( reactEditText.getId(), - SystemClock.nanoTime(), + SystemClock.elapsedRealtime(), moreText, "", start, @@ -184,7 +184,7 @@ public class TextInputTestCase extends ReactAppInstrumentationTestCase { eventDispatcher.dispatchEvent( new ReactTextChangedEvent( reactEditText.getId(), - SystemClock.nanoTime(), + SystemClock.elapsedRealtime(), newText.toString(), (int) PixelUtil.toDIPFromPixel(contentWidth), (int) PixelUtil.toDIPFromPixel(contentHeight), @@ -193,7 +193,7 @@ public class TextInputTestCase extends ReactAppInstrumentationTestCase { eventDispatcher.dispatchEvent( new ReactTextInputEvent( reactEditText.getId(), - SystemClock.nanoTime(), + SystemClock.elapsedRealtime(), moreText, "", start, diff --git a/ReactAndroid/src/main/java/com/facebook/react/common/SystemClock.java b/ReactAndroid/src/main/java/com/facebook/react/common/SystemClock.java index 29c31b416..1af4b5536 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/common/SystemClock.java +++ b/ReactAndroid/src/main/java/com/facebook/react/common/SystemClock.java @@ -22,4 +22,8 @@ public class SystemClock { public static long nanoTime() { return System.nanoTime(); } + + public static long elapsedRealtime() { + return android.os.SystemClock.elapsedRealtime(); + } } diff --git a/ReactAndroid/src/main/java/com/facebook/react/modules/core/Timing.java b/ReactAndroid/src/main/java/com/facebook/react/modules/core/Timing.java index d0ee51cfc..394d025fa 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/modules/core/Timing.java +++ b/ReactAndroid/src/main/java/com/facebook/react/modules/core/Timing.java @@ -244,7 +244,7 @@ public final class Timing extends ReactContextBaseJavaModule implements Lifecycl return; } - long initialTargetTime = SystemClock.nanoTime() / 1000000 + adjustedDuration; + long initialTargetTime = SystemClock.elapsedRealtime() + adjustedDuration; Timer timer = new Timer(executorToken, callbackID, initialTargetTime, duration, repeat); synchronized (mTimerGuard) { mTimers.add(timer); diff --git a/ReactAndroid/src/main/java/com/facebook/react/uimanager/JSTouchDispatcher.java b/ReactAndroid/src/main/java/com/facebook/react/uimanager/JSTouchDispatcher.java index bbe5fc995..a31179baa 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/uimanager/JSTouchDispatcher.java +++ b/ReactAndroid/src/main/java/com/facebook/react/uimanager/JSTouchDispatcher.java @@ -82,7 +82,7 @@ public class JSTouchDispatcher { eventDispatcher.dispatchEvent( TouchEvent.obtain( mTargetTag, - SystemClock.nanoTime(), + SystemClock.elapsedRealtime(), TouchEventType.START, ev, mTargetCoordinates[0], @@ -105,7 +105,7 @@ public class JSTouchDispatcher { eventDispatcher.dispatchEvent( TouchEvent.obtain( mTargetTag, - SystemClock.nanoTime(), + SystemClock.elapsedRealtime(), TouchEventType.END, ev, mTargetCoordinates[0], @@ -117,7 +117,7 @@ public class JSTouchDispatcher { eventDispatcher.dispatchEvent( TouchEvent.obtain( mTargetTag, - SystemClock.nanoTime(), + SystemClock.elapsedRealtime(), TouchEventType.MOVE, ev, mTargetCoordinates[0], @@ -128,7 +128,7 @@ public class JSTouchDispatcher { eventDispatcher.dispatchEvent( TouchEvent.obtain( mTargetTag, - SystemClock.nanoTime(), + SystemClock.elapsedRealtime(), TouchEventType.START, ev, mTargetCoordinates[0], @@ -139,7 +139,7 @@ public class JSTouchDispatcher { eventDispatcher.dispatchEvent( TouchEvent.obtain( mTargetTag, - SystemClock.nanoTime(), + SystemClock.elapsedRealtime(), TouchEventType.END, ev, mTargetCoordinates[0], @@ -180,7 +180,7 @@ public class JSTouchDispatcher { Assertions.assertNotNull(eventDispatcher).dispatchEvent( TouchEvent.obtain( mTargetTag, - SystemClock.nanoTime(), + SystemClock.elapsedRealtime(), TouchEventType.CANCEL, androidEvent, mTargetCoordinates[0], diff --git a/ReactAndroid/src/main/java/com/facebook/react/uimanager/OnLayoutEvent.java b/ReactAndroid/src/main/java/com/facebook/react/uimanager/OnLayoutEvent.java index c2b5c8a6e..19bf172ae 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/uimanager/OnLayoutEvent.java +++ b/ReactAndroid/src/main/java/com/facebook/react/uimanager/OnLayoutEvent.java @@ -45,7 +45,7 @@ public class OnLayoutEvent extends Event { } protected void init(int viewTag, int x, int y, int width, int height) { - super.init(viewTag, SystemClock.nanoTime()); + super.init(viewTag, SystemClock.elapsedRealtime()); mX = x; mY = y; mWidth = width; diff --git a/ReactAndroid/src/main/java/com/facebook/react/views/drawer/ReactDrawerLayoutManager.java b/ReactAndroid/src/main/java/com/facebook/react/views/drawer/ReactDrawerLayoutManager.java index 0595d0fd0..8c78b2c34 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/views/drawer/ReactDrawerLayoutManager.java +++ b/ReactAndroid/src/main/java/com/facebook/react/views/drawer/ReactDrawerLayoutManager.java @@ -188,25 +188,25 @@ public class ReactDrawerLayoutManager extends ViewGroupManager new ReactModalHostView.OnRequestCloseListener() { @Override public void onRequestClose(DialogInterface dialog) { - dispatcher.dispatchEvent(new RequestCloseEvent(view.getId(), SystemClock.nanoTime())); + dispatcher.dispatchEvent(new RequestCloseEvent(view.getId(), SystemClock.elapsedRealtime())); } }); view.setOnShowListener( new DialogInterface.OnShowListener() { @Override public void onShow(DialogInterface dialog) { - dispatcher.dispatchEvent(new ShowEvent(view.getId(), SystemClock.nanoTime())); + dispatcher.dispatchEvent(new ShowEvent(view.getId(), SystemClock.elapsedRealtime())); } }); } diff --git a/ReactAndroid/src/main/java/com/facebook/react/views/picker/ReactPickerManager.java b/ReactAndroid/src/main/java/com/facebook/react/views/picker/ReactPickerManager.java index 11ea4c839..e0a348d52 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/views/picker/ReactPickerManager.java +++ b/ReactAndroid/src/main/java/com/facebook/react/views/picker/ReactPickerManager.java @@ -157,7 +157,7 @@ public abstract class ReactPickerManager extends SimpleViewManager @Override public void onItemSelected(int position) { mEventDispatcher.dispatchEvent( new PickerItemSelectEvent( - mReactPicker.getId(), SystemClock.nanoTime(), position)); + mReactPicker.getId(), SystemClock.elapsedRealtime(), position)); } } } diff --git a/ReactAndroid/src/main/java/com/facebook/react/views/recyclerview/RecyclerViewBackedScrollView.java b/ReactAndroid/src/main/java/com/facebook/react/views/recyclerview/RecyclerViewBackedScrollView.java index df3f2c575..657a227f1 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/views/recyclerview/RecyclerViewBackedScrollView.java +++ b/ReactAndroid/src/main/java/com/facebook/react/views/recyclerview/RecyclerViewBackedScrollView.java @@ -344,7 +344,7 @@ public class RecyclerViewBackedScrollView extends RecyclerView { ((ReactContext) getContext()).getNativeModule(UIManagerModule.class).getEventDispatcher() .dispatchEvent(ScrollEvent.obtain( getId(), - SystemClock.nanoTime(), + SystemClock.elapsedRealtime(), ScrollEventType.SCROLL, 0, /* offsetX = 0, horizontal scrolling only */ calculateAbsoluteOffset(), @@ -359,7 +359,7 @@ public class RecyclerViewBackedScrollView extends RecyclerView { ((ReactContext) getContext()).getNativeModule(UIManagerModule.class).getEventDispatcher() .dispatchEvent(new ContentSizeChangeEvent( getId(), - SystemClock.nanoTime(), + SystemClock.elapsedRealtime(), getWidth(), newTotalChildrenHeight)); } diff --git a/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactScrollViewHelper.java b/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactScrollViewHelper.java index fd46f677b..5443bc33f 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactScrollViewHelper.java +++ b/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactScrollViewHelper.java @@ -57,7 +57,7 @@ public class ReactScrollViewHelper { reactContext.getNativeModule(UIManagerModule.class).getEventDispatcher().dispatchEvent( ScrollEvent.obtain( scrollView.getId(), - SystemClock.nanoTime(), + SystemClock.elapsedRealtime(), scrollEventType, scrollView.getScrollX(), scrollView.getScrollY(), diff --git a/ReactAndroid/src/main/java/com/facebook/react/views/slider/ReactSliderManager.java b/ReactAndroid/src/main/java/com/facebook/react/views/slider/ReactSliderManager.java index ec3f66d80..a38bf2496 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/views/slider/ReactSliderManager.java +++ b/ReactAndroid/src/main/java/com/facebook/react/views/slider/ReactSliderManager.java @@ -81,7 +81,7 @@ public class ReactSliderManager extends SimpleViewManager { reactContext.getNativeModule(UIManagerModule.class).getEventDispatcher().dispatchEvent( new ReactSliderEvent( seekbar.getId(), - SystemClock.nanoTime(), + SystemClock.elapsedRealtime(), ((ReactSlider)seekbar).toRealProgress(progress), fromUser)); } @@ -96,7 +96,7 @@ public class ReactSliderManager extends SimpleViewManager { reactContext.getNativeModule(UIManagerModule.class).getEventDispatcher().dispatchEvent( new ReactSlidingCompleteEvent( seekbar.getId(), - SystemClock.nanoTime(), + SystemClock.elapsedRealtime(), ((ReactSlider)seekbar).toRealProgress(seekbar.getProgress()))); } }; diff --git a/ReactAndroid/src/main/java/com/facebook/react/views/swiperefresh/SwipeRefreshLayoutManager.java b/ReactAndroid/src/main/java/com/facebook/react/views/swiperefresh/SwipeRefreshLayoutManager.java index 1ae2bacf5..1ff9d0edb 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/views/swiperefresh/SwipeRefreshLayoutManager.java +++ b/ReactAndroid/src/main/java/com/facebook/react/views/swiperefresh/SwipeRefreshLayoutManager.java @@ -89,7 +89,7 @@ public class SwipeRefreshLayoutManager extends ViewGroupManager { reactContext.getNativeModule(UIManagerModule.class).getEventDispatcher().dispatchEvent( new ReactSwitchEvent( buttonView.getId(), - SystemClock.nanoTime(), + SystemClock.elapsedRealtime(), isChecked)); } }; diff --git a/ReactAndroid/src/main/java/com/facebook/react/views/textinput/ReactTextInputManager.java b/ReactAndroid/src/main/java/com/facebook/react/views/textinput/ReactTextInputManager.java index ebb826444..3ca02b999 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/views/textinput/ReactTextInputManager.java +++ b/ReactAndroid/src/main/java/com/facebook/react/views/textinput/ReactTextInputManager.java @@ -567,7 +567,7 @@ public class ReactTextInputManager extends BaseViewManager { @Override public void onClick(View v) { mEventDispatcher.dispatchEvent( - new ToolbarClickEvent(view.getId(), SystemClock.nanoTime(), -1)); + new ToolbarClickEvent(view.getId(), SystemClock.elapsedRealtime(), -1)); } }); @@ -142,7 +142,7 @@ public class ReactToolbarManager extends ViewGroupManager { mEventDispatcher.dispatchEvent( new ToolbarClickEvent( view.getId(), - SystemClock.nanoTime(), + SystemClock.elapsedRealtime(), menuItem.getOrder())); return true; } diff --git a/ReactAndroid/src/main/java/com/facebook/react/views/viewpager/ReactViewPager.java b/ReactAndroid/src/main/java/com/facebook/react/views/viewpager/ReactViewPager.java index 8b86beb76..8ffb06fae 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/views/viewpager/ReactViewPager.java +++ b/ReactAndroid/src/main/java/com/facebook/react/views/viewpager/ReactViewPager.java @@ -91,14 +91,14 @@ import com.facebook.react.uimanager.events.NativeGestureUtil; @Override public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { mEventDispatcher.dispatchEvent( - new PageScrollEvent(getId(), SystemClock.nanoTime(), position, positionOffset)); + new PageScrollEvent(getId(), SystemClock.elapsedRealtime(), position, positionOffset)); } @Override public void onPageSelected(int position) { if (!mIsCurrentItemFromJs) { mEventDispatcher.dispatchEvent( - new PageSelectedEvent(getId(), SystemClock.nanoTime(), position)); + new PageSelectedEvent(getId(), SystemClock.elapsedRealtime(), position)); } } @@ -119,7 +119,7 @@ import com.facebook.react.uimanager.events.NativeGestureUtil; throw new IllegalStateException("Unsupported pageScrollState"); } mEventDispatcher.dispatchEvent( - new PageScrollStateChangedEvent(getId(), SystemClock.nanoTime(), pageScrollState)); + new PageScrollStateChangedEvent(getId(), SystemClock.elapsedRealtime(), pageScrollState)); } } diff --git a/ReactAndroid/src/main/java/com/facebook/react/views/webview/ReactWebViewManager.java b/ReactAndroid/src/main/java/com/facebook/react/views/webview/ReactWebViewManager.java index 11350d16e..6e88217e4 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/views/webview/ReactWebViewManager.java +++ b/ReactAndroid/src/main/java/com/facebook/react/views/webview/ReactWebViewManager.java @@ -107,7 +107,7 @@ public class ReactWebViewManager extends SimpleViewManager { webView, new TopLoadingStartEvent( webView.getId(), - SystemClock.nanoTime(), + SystemClock.elapsedRealtime(), createWebViewEvent(webView, url))); } @@ -130,7 +130,7 @@ public class ReactWebViewManager extends SimpleViewManager { dispatchEvent( webView, - new TopLoadingErrorEvent(webView.getId(), SystemClock.nanoTime(), eventData)); + new TopLoadingErrorEvent(webView.getId(), SystemClock.elapsedRealtime(), eventData)); } @Override @@ -141,7 +141,7 @@ public class ReactWebViewManager extends SimpleViewManager { webView, new TopLoadingStartEvent( webView.getId(), - SystemClock.nanoTime(), + SystemClock.elapsedRealtime(), createWebViewEvent(webView, url))); } @@ -150,7 +150,7 @@ public class ReactWebViewManager extends SimpleViewManager { webView, new TopLoadingFinishEvent( webView.getId(), - SystemClock.nanoTime(), + SystemClock.elapsedRealtime(), createWebViewEvent(webView, url))); } diff --git a/ReactAndroid/src/test/java/com/facebook/react/RootViewTest.java b/ReactAndroid/src/test/java/com/facebook/react/RootViewTest.java index 2eb7f5ed8..72074ebbe 100644 --- a/ReactAndroid/src/test/java/com/facebook/react/RootViewTest.java +++ b/ReactAndroid/src/test/java/com/facebook/react/RootViewTest.java @@ -65,7 +65,7 @@ public class RootViewTest { @Before public void setUp() { - final long ts = SystemClock.nanoTime(); + final long ts = SystemClock.elapsedRealtime(); PowerMockito.mockStatic(Arguments.class); PowerMockito.when(Arguments.createArray()).thenAnswer(new Answer() { @Override @@ -80,7 +80,7 @@ public class RootViewTest { } }); PowerMockito.mockStatic(SystemClock.class); - PowerMockito.when(SystemClock.nanoTime()).thenAnswer(new Answer() { + PowerMockito.when(SystemClock.elapsedRealtime()).thenAnswer(new Answer() { @Override public Object answer(InvocationOnMock invocation) throws Throwable { return ts; @@ -116,7 +116,7 @@ public class RootViewTest { rootView.startReactApplication(instanceManager, ""); rootView.simulateAttachForTesting(); - long ts = SystemClock.nanoTime(); + long ts = SystemClock.elapsedRealtime(); // Test ACTION_DOWN event rootView.onTouchEvent( diff --git a/ReactAndroid/src/test/java/com/facebook/react/modules/timing/TimingModuleTest.java b/ReactAndroid/src/test/java/com/facebook/react/modules/timing/TimingModuleTest.java index 7eebda7f1..7391974e4 100644 --- a/ReactAndroid/src/test/java/com/facebook/react/modules/timing/TimingModuleTest.java +++ b/ReactAndroid/src/test/java/com/facebook/react/modules/timing/TimingModuleTest.java @@ -74,7 +74,7 @@ public class TimingModuleTest { PowerMockito.mockStatic(SystemClock.class); when(SystemClock.currentTimeMillis()).thenReturn(mCurrentTimeNs / 1000000); - when(SystemClock.nanoTime()).thenReturn(mCurrentTimeNs); + when(SystemClock.elapsedRealtime()).thenReturn(mCurrentTimeNs / 1000000); mChoreographerMock = mock(ReactChoreographer.class); PowerMockito.mockStatic(ReactChoreographer.class);