diff --git a/ReactAndroid/src/main/java/com/facebook/react/shell/BUCK b/ReactAndroid/src/main/java/com/facebook/react/shell/BUCK index 66f78ea103f..8c538aa0e96 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/shell/BUCK +++ b/ReactAndroid/src/main/java/com/facebook/react/shell/BUCK @@ -64,7 +64,6 @@ rn_android_library( react_native_target("java/com/facebook/react/views/text/frescosupport:frescosupport"), react_native_target("java/com/facebook/react/views/textinput:textinput"), react_native_target("java/com/facebook/react/views/view:view"), - react_native_target("java/com/facebook/react/views/viewpager:viewpager"), react_native_target("java/com/facebook/react/module/annotations:annotations"), react_native_target("res:shell"), ], diff --git a/ReactAndroid/src/main/java/com/facebook/react/views/viewpager/BUCK b/ReactAndroid/src/main/java/com/facebook/react/views/viewpager/BUCK deleted file mode 100644 index 9784b3051f2..00000000000 --- a/ReactAndroid/src/main/java/com/facebook/react/views/viewpager/BUCK +++ /dev/null @@ -1,31 +0,0 @@ -load("//tools/build_defs/oss:rn_defs.bzl", "react_native_dep", "react_native_target", "rn_android_library") - -rn_android_library( - name = "viewpager", - srcs = glob(["**/*.java"]), - is_androidx = True, - labels = ["supermodule:xplat/default/public.react_native.infra"], - provided_deps = [ - react_native_dep("third-party/android/androidx:annotation"), - react_native_dep("third-party/android/androidx:core"), - react_native_dep("third-party/android/androidx:fragment"), - react_native_dep("third-party/android/androidx:legacy-support-core-ui"), - react_native_dep("third-party/android/androidx:legacy-support-core-utils"), - ], - required_for_source_only_abi = True, - visibility = [ - "PUBLIC", - ], - deps = [ - react_native_dep("libraries/fbcore/src/main/java/com/facebook/common/logging:logging"), - react_native_dep("third-party/java/infer-annotations:infer-annotations"), - react_native_dep("third-party/java/jsr-305:jsr-305"), - react_native_target("java/com/facebook/react/bridge:bridge"), - react_native_target("java/com/facebook/react/common:common"), - react_native_target("java/com/facebook/react/module/annotations:annotations"), - react_native_target("java/com/facebook/react/uimanager:uimanager"), - react_native_target("java/com/facebook/react/uimanager/annotations:annotations"), - react_native_target("java/com/facebook/react/views/scroll:scroll"), - react_native_target("java/com/facebook/react/viewmanagers:viewmanagers"), - ], -) diff --git a/ReactAndroid/src/main/java/com/facebook/react/views/viewpager/PageScrollEvent.java b/ReactAndroid/src/main/java/com/facebook/react/views/viewpager/PageScrollEvent.java deleted file mode 100644 index 2fcc64de002..00000000000 --- a/ReactAndroid/src/main/java/com/facebook/react/views/viewpager/PageScrollEvent.java +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -package com.facebook.react.views.viewpager; - -import com.facebook.react.bridge.Arguments; -import com.facebook.react.bridge.WritableMap; -import com.facebook.react.uimanager.events.Event; -import com.facebook.react.uimanager.events.RCTEventEmitter; - -/** - * Event emitted by {@link ReactViewPager} when user scrolls between pages (or when animating - * between pages). - * - *

Additional data provided by this event: - * - *

- */ -/* package */ class PageScrollEvent extends Event { - - public static final String EVENT_NAME = "topPageScroll"; - - private final int mPosition; - private final float mOffset; - - protected PageScrollEvent(int viewTag, int position, float offset) { - super(viewTag); - mPosition = position; - - // folly::toJson default options don't support serialize NaN or Infinite value - mOffset = (Float.isInfinite(offset) || Float.isNaN(offset)) ? 0.0f : offset; - } - - @Override - public String getEventName() { - return EVENT_NAME; - } - - @Override - public void dispatch(RCTEventEmitter rctEventEmitter) { - rctEventEmitter.receiveEvent(getViewTag(), getEventName(), serializeEventData()); - } - - private WritableMap serializeEventData() { - WritableMap eventData = Arguments.createMap(); - eventData.putInt("position", mPosition); - eventData.putDouble("offset", mOffset); - return eventData; - } -} diff --git a/ReactAndroid/src/main/java/com/facebook/react/views/viewpager/PageScrollStateChangedEvent.java b/ReactAndroid/src/main/java/com/facebook/react/views/viewpager/PageScrollStateChangedEvent.java deleted file mode 100644 index 68585a75ca5..00000000000 --- a/ReactAndroid/src/main/java/com/facebook/react/views/viewpager/PageScrollStateChangedEvent.java +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -package com.facebook.react.views.viewpager; - -import com.facebook.react.bridge.Arguments; -import com.facebook.react.bridge.WritableMap; -import com.facebook.react.uimanager.events.Event; -import com.facebook.react.uimanager.events.RCTEventEmitter; - -/** - * Event emitted by {@link ReactViewPager} when user scrolling state changed. - * - *

Additional data provided by this event: - * - *

- */ -class PageScrollStateChangedEvent extends Event { - - public static final String EVENT_NAME = "topPageScrollStateChanged"; - - private final String mPageScrollState; - - protected PageScrollStateChangedEvent(int viewTag, String pageScrollState) { - super(viewTag); - mPageScrollState = pageScrollState; - } - - @Override - public String getEventName() { - return EVENT_NAME; - } - - @Override - public void dispatch(RCTEventEmitter rctEventEmitter) { - rctEventEmitter.receiveEvent(getViewTag(), getEventName(), serializeEventData()); - } - - private WritableMap serializeEventData() { - WritableMap eventData = Arguments.createMap(); - eventData.putString("pageScrollState", mPageScrollState); - return eventData; - } -} diff --git a/ReactAndroid/src/main/java/com/facebook/react/views/viewpager/PageSelectedEvent.java b/ReactAndroid/src/main/java/com/facebook/react/views/viewpager/PageSelectedEvent.java deleted file mode 100644 index 8d59e887cb6..00000000000 --- a/ReactAndroid/src/main/java/com/facebook/react/views/viewpager/PageSelectedEvent.java +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -package com.facebook.react.views.viewpager; - -import com.facebook.react.bridge.Arguments; -import com.facebook.react.bridge.WritableMap; -import com.facebook.react.uimanager.events.Event; -import com.facebook.react.uimanager.events.RCTEventEmitter; - -/** - * Event emitted by {@link ReactViewPager} when selected page changes. - * - *

Additional data provided by this event: - * - *

- */ -/* package */ class PageSelectedEvent extends Event { - - public static final String EVENT_NAME = "topPageSelected"; - - private final int mPosition; - - protected PageSelectedEvent(int viewTag, int position) { - super(viewTag); - mPosition = position; - } - - @Override - public String getEventName() { - return EVENT_NAME; - } - - @Override - public void dispatch(RCTEventEmitter rctEventEmitter) { - rctEventEmitter.receiveEvent(getViewTag(), getEventName(), serializeEventData()); - } - - private WritableMap serializeEventData() { - WritableMap eventData = Arguments.createMap(); - eventData.putInt("position", mPosition); - return eventData; - } -} 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 deleted file mode 100644 index 398751a8f6e..00000000000 --- a/ReactAndroid/src/main/java/com/facebook/react/views/viewpager/ReactViewPager.java +++ /dev/null @@ -1,263 +0,0 @@ -/* - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -package com.facebook.react.views.viewpager; - -import android.view.MotionEvent; -import android.view.View; -import android.view.ViewGroup; -import androidx.viewpager.widget.PagerAdapter; -import androidx.viewpager.widget.ViewPager; -import com.facebook.common.logging.FLog; -import com.facebook.infer.annotation.Assertions; -import com.facebook.react.bridge.ReactContext; -import com.facebook.react.common.ReactConstants; -import com.facebook.react.uimanager.UIManagerModule; -import com.facebook.react.uimanager.events.EventDispatcher; -import com.facebook.react.uimanager.events.NativeGestureUtil; -import java.util.ArrayList; -import java.util.List; - -/** - * Wrapper view for {@link ViewPager}. It's forwarding calls to {@link ViewGroup#addView} to add - * views to custom {@link PagerAdapter} instance which is used by {@link NativeViewHierarchyManager} - * to add children nodes according to react views hierarchy. - */ -public class ReactViewPager extends ViewPager { - - private class Adapter extends PagerAdapter { - - private final List mViews = new ArrayList<>(); - private boolean mIsViewPagerInIntentionallyInconsistentState = false; - - void addView(View child, int index) { - mViews.add(index, child); - notifyDataSetChanged(); - // This will prevent view pager from detaching views for pages that are not currently selected - // We need to do that since {@link ViewPager} relies on layout passes to position those views - // in a right way (also thanks to {@link ReactViewPagerManager#needsCustomLayoutForChildren} - // returning {@code true}). Currently we only call {@link View#measure} and - // {@link View#layout} after yoga step. - - // TODO(7323049): Remove this workaround once we figure out a way to re-layout some views on - // request - setOffscreenPageLimit(mViews.size()); - } - - void removeViewAt(int index) { - mViews.remove(index); - notifyDataSetChanged(); - - // TODO(7323049): Remove this workaround once we figure out a way to re-layout some views on - // request - setOffscreenPageLimit(mViews.size()); - } - - /** Replace a set of views to the ViewPager adapter and update the ViewPager */ - void setViews(List views) { - mViews.clear(); - mViews.addAll(views); - notifyDataSetChanged(); - - // we want to make sure we return POSITION_NONE for every view here, since this is only - // called after a removeAllViewsFromAdapter - mIsViewPagerInIntentionallyInconsistentState = false; - } - - /** - * Remove all the views from the adapter and de-parents them from the ViewPager After calling - * this, it is expected that notifyDataSetChanged should be called soon afterwards. - */ - void removeAllViewsFromAdapter(ViewPager pager) { - mViews.clear(); - pager.removeAllViews(); - // set this, so that when the next addViews is called, we return POSITION_NONE for every - // entry so we can remove whichever views we need to and add the ones that we need to. - mIsViewPagerInIntentionallyInconsistentState = true; - } - - View getViewAt(int index) { - return mViews.get(index); - } - - @Override - public int getCount() { - return mViews.size(); - } - - @Override - public int getItemPosition(Object object) { - // if we've removed all views, we want to return POSITION_NONE intentionally - return mIsViewPagerInIntentionallyInconsistentState || !mViews.contains(object) - ? POSITION_NONE - : mViews.indexOf(object); - } - - @Override - public Object instantiateItem(ViewGroup container, int position) { - View view = mViews.get(position); - container.addView(view, 0, generateDefaultLayoutParams()); - return view; - } - - @Override - public void destroyItem(ViewGroup container, int position, Object object) { - container.removeView((View) object); - } - - @Override - public boolean isViewFromObject(View view, Object object) { - return view == object; - } - } - - private class PageChangeListener implements ViewPager.OnPageChangeListener { - - @Override - public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { - mEventDispatcher.dispatchEvent(new PageScrollEvent(getId(), position, positionOffset)); - } - - @Override - public void onPageSelected(int position) { - if (!mIsCurrentItemFromJs) { - mEventDispatcher.dispatchEvent(new PageSelectedEvent(getId(), position)); - } - } - - @Override - public void onPageScrollStateChanged(int state) { - String pageScrollState; - switch (state) { - case SCROLL_STATE_IDLE: - pageScrollState = "idle"; - break; - case SCROLL_STATE_DRAGGING: - pageScrollState = "dragging"; - break; - case SCROLL_STATE_SETTLING: - pageScrollState = "settling"; - break; - default: - throw new IllegalStateException("Unsupported pageScrollState"); - } - mEventDispatcher.dispatchEvent(new PageScrollStateChangedEvent(getId(), pageScrollState)); - } - } - - private final EventDispatcher mEventDispatcher; - private boolean mIsCurrentItemFromJs; - private boolean mScrollEnabled = true; - - public ReactViewPager(ReactContext reactContext) { - super(reactContext); - mEventDispatcher = - Assertions.assertNotNull(reactContext.getNativeModule(UIManagerModule.class)) - .getEventDispatcher(); - mIsCurrentItemFromJs = false; - setOnPageChangeListener(new PageChangeListener()); - setAdapter(new Adapter()); - } - - @Override - public Adapter getAdapter() { - return (Adapter) super.getAdapter(); - } - - @Override - public boolean onInterceptTouchEvent(MotionEvent ev) { - if (!mScrollEnabled) { - return false; - } - - try { - if (super.onInterceptTouchEvent(ev)) { - NativeGestureUtil.notifyNativeGestureStarted(this, ev); - return true; - } - } catch (IllegalArgumentException e) { - // Log and ignore the error. This seems to be a bug in the android SDK and - // this is the commonly accepted workaround. - // https://tinyurl.com/mw6qkod (Stack Overflow) - FLog.w(ReactConstants.TAG, "Error intercepting touch event.", e); - } - - return false; - } - - @Override - public boolean onTouchEvent(MotionEvent ev) { - if (!mScrollEnabled) { - return false; - } - - try { - return super.onTouchEvent(ev); - } catch (IllegalArgumentException e) { - // Log and ignore the error. This seems to be a bug in the android SDK and - // this is the commonly accepted workaround. - // https://fburl.com/5d3iw7d9 - FLog.w(ReactConstants.TAG, "Error handling touch event.", e); - } - - return false; - } - - public void setCurrentItemFromJs(int item, boolean animated) { - mIsCurrentItemFromJs = true; - setCurrentItem(item, animated); - mIsCurrentItemFromJs = false; - } - - public void setScrollEnabled(boolean scrollEnabled) { - mScrollEnabled = scrollEnabled; - } - - @Override - protected void onAttachedToWindow() { - super.onAttachedToWindow(); - // The viewpager reset an internal flag on this method so we need to run another layout pass - // after attaching to window. - this.requestLayout(); - post(measureAndLayout); - } - - private final Runnable measureAndLayout = - new Runnable() { - @Override - public void run() { - measure( - MeasureSpec.makeMeasureSpec(getWidth(), MeasureSpec.EXACTLY), - MeasureSpec.makeMeasureSpec(getHeight(), MeasureSpec.EXACTLY)); - layout(getLeft(), getTop(), getRight(), getBottom()); - } - }; - - /*package*/ void addViewToAdapter(View child, int index) { - getAdapter().addView(child, index); - } - - /*package*/ void removeViewFromAdapter(int index) { - getAdapter().removeViewAt(index); - } - - /*package*/ int getViewCountInAdapter() { - return getAdapter().getCount(); - } - - /*package*/ View getViewFromAdapter(int index) { - return getAdapter().getViewAt(index); - } - - public void setViews(List views) { - getAdapter().setViews(views); - } - - public void removeAllViewsFromAdapter() { - getAdapter().removeAllViewsFromAdapter(this); - } -} diff --git a/ReactAndroid/src/main/java/com/facebook/react/views/viewpager/ReactViewPagerManager.java b/ReactAndroid/src/main/java/com/facebook/react/views/viewpager/ReactViewPagerManager.java deleted file mode 100644 index bf6af60998b..00000000000 --- a/ReactAndroid/src/main/java/com/facebook/react/views/viewpager/ReactViewPagerManager.java +++ /dev/null @@ -1,175 +0,0 @@ -/* - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -package com.facebook.react.views.viewpager; - -import android.view.View; -import androidx.annotation.Nullable; -import com.facebook.infer.annotation.Assertions; -import com.facebook.react.bridge.ReadableArray; -import com.facebook.react.common.MapBuilder; -import com.facebook.react.module.annotations.ReactModule; -import com.facebook.react.uimanager.PixelUtil; -import com.facebook.react.uimanager.ThemedReactContext; -import com.facebook.react.uimanager.ViewGroupManager; -import com.facebook.react.uimanager.ViewManagerDelegate; -import com.facebook.react.uimanager.annotations.ReactProp; -import com.facebook.react.viewmanagers.AndroidViewPagerManagerDelegate; -import com.facebook.react.viewmanagers.AndroidViewPagerManagerInterface; -import java.util.Map; - -/** Instance of {@link ViewManager} that provides native {@link ViewPager} view. */ -@ReactModule(name = ReactViewPagerManager.REACT_CLASS) -public class ReactViewPagerManager extends ViewGroupManager - implements AndroidViewPagerManagerInterface { - - public static final String REACT_CLASS = "AndroidViewPager"; - - public static final int COMMAND_SET_PAGE = 1; - public static final int COMMAND_SET_PAGE_WITHOUT_ANIMATION = 2; - - private final ViewManagerDelegate mDelegate; - - public ReactViewPagerManager() { - mDelegate = new AndroidViewPagerManagerDelegate<>(this); - } - - @Override - public String getName() { - return REACT_CLASS; - } - - @Override - protected ReactViewPager createViewInstance(ThemedReactContext reactContext) { - return new ReactViewPager(reactContext); - } - - @Override - @ReactProp(name = "scrollEnabled", defaultBoolean = true) - public void setScrollEnabled(ReactViewPager viewPager, boolean value) { - viewPager.setScrollEnabled(value); - } - - @Override - public boolean needsCustomLayoutForChildren() { - return true; - } - - @Override - public Map getExportedCustomDirectEventTypeConstants() { - return MapBuilder.of( - PageScrollEvent.EVENT_NAME, MapBuilder.of("registrationName", "onPageScroll"), - PageScrollStateChangedEvent.EVENT_NAME, - MapBuilder.of("registrationName", "onPageScrollStateChanged"), - PageSelectedEvent.EVENT_NAME, MapBuilder.of("registrationName", "onPageSelected")); - } - - @Override - public Map getCommandsMap() { - return MapBuilder.of( - "setPage", COMMAND_SET_PAGE, "setPageWithoutAnimation", COMMAND_SET_PAGE_WITHOUT_ANIMATION); - } - - @Override - public void receiveCommand( - ReactViewPager viewPager, int commandType, @Nullable ReadableArray args) { - Assertions.assertNotNull(viewPager); - Assertions.assertNotNull(args); - switch (commandType) { - case COMMAND_SET_PAGE: - { - viewPager.setCurrentItemFromJs(args.getInt(0), true); - return; - } - case COMMAND_SET_PAGE_WITHOUT_ANIMATION: - { - viewPager.setCurrentItemFromJs(args.getInt(0), false); - return; - } - default: - throw new IllegalArgumentException( - String.format( - "Unsupported command %d received by %s.", commandType, getClass().getSimpleName())); - } - } - - @Override - public void receiveCommand( - ReactViewPager viewPager, String commandType, @Nullable ReadableArray args) { - Assertions.assertNotNull(viewPager); - Assertions.assertNotNull(args); - switch (commandType) { - case "setPage": - { - viewPager.setCurrentItemFromJs(args.getInt(0), true); - return; - } - case "setPageWithoutAnimation": - { - viewPager.setCurrentItemFromJs(args.getInt(0), false); - return; - } - default: - throw new IllegalArgumentException( - String.format( - "Unsupported command %d received by %s.", commandType, getClass().getSimpleName())); - } - } - - @Override - public void addView(ReactViewPager parent, View child, int index) { - parent.addViewToAdapter(child, index); - } - - @Override - public int getChildCount(ReactViewPager parent) { - return parent.getViewCountInAdapter(); - } - - @Override - public View getChildAt(ReactViewPager parent, int index) { - return parent.getViewFromAdapter(index); - } - - @Override - public void removeViewAt(ReactViewPager parent, int index) { - parent.removeViewFromAdapter(index); - } - - @Override - @ReactProp(name = "pageMargin", defaultInt = 0) - public void setPageMargin(ReactViewPager pager, int margin) { - pager.setPageMargin((int) PixelUtil.toPixelFromDIP(margin)); - } - - @Override - @ReactProp(name = "peekEnabled", defaultBoolean = false) - public void setPeekEnabled(ReactViewPager pager, boolean peekEnabled) { - pager.setClipToPadding(!peekEnabled); - } - - @Override - public void setInitialPage(ReactViewPager view, int value) {} - - @Override - public void setKeyboardDismissMode(ReactViewPager view, @Nullable String value) {} - - @Override - public void setPage(ReactViewPager view, int page) { - view.setCurrentItemFromJs(page, true); - } - - @Override - public void setPageWithoutAnimation(ReactViewPager view, int page) { - view.setCurrentItemFromJs(page, false); - } - - @Override - public ViewManagerDelegate getDelegate() { - return mDelegate; - } -}