Merge commit '086c967286638deb9db21e4d62e6dca1fc1fba83' into 0.67-merge-latest
This commit is contained in:
Коммит
b74e7f8241
|
@ -52,7 +52,7 @@ executors:
|
|||
reactnativeios:
|
||||
<<: *defaults
|
||||
macos:
|
||||
xcode: &_XCODE_VERSION "12.5.0"
|
||||
xcode: &_XCODE_VERSION "13.0.0"
|
||||
|
||||
# -------------------------
|
||||
# COMMANDS
|
||||
|
@ -387,7 +387,7 @@ jobs:
|
|||
|
||||
- run:
|
||||
name: Configure Watchman
|
||||
command: touch .watchmanconfig
|
||||
command: echo "{}" > .watchmanconfig
|
||||
|
||||
- when:
|
||||
condition: << parameters.use_frameworks >>
|
||||
|
@ -717,12 +717,11 @@ jobs:
|
|||
# -------------------------
|
||||
# JOBS: Releases
|
||||
# -------------------------
|
||||
# Publishes a new version onto npm
|
||||
publish_npm_package:
|
||||
build_npm_package:
|
||||
parameters:
|
||||
publish_npm_args:
|
||||
type: string
|
||||
default: --nonightly
|
||||
default: --dry-run
|
||||
executor: reactnativeandroid
|
||||
steps:
|
||||
- run:
|
||||
|
@ -735,11 +734,18 @@ jobs:
|
|||
- install_buck_tooling
|
||||
- download_buck_dependencies
|
||||
- download_gradle_dependencies
|
||||
- run: echo "//registry.npmjs.org/:_authToken=${CIRCLE_NPM_TOKEN}" > ~/.npmrc
|
||||
- run: |
|
||||
git config --global user.email "react-native-bot@users.noreply.github.com"
|
||||
git config --global user.name "npm Deployment Script"
|
||||
echo "machine github.com login react-native-bot password $GITHUB_TOKEN" > ~/.netrc
|
||||
# Only tagged releases and nightlies should be able to publish to npm
|
||||
- when:
|
||||
condition:
|
||||
or:
|
||||
- equal: [ --release, << parameters.publish_npm_args >> ]
|
||||
- equal: [ --nightly, << parameters.publish_npm_args >> ]
|
||||
steps:
|
||||
- run: echo "//registry.npmjs.org/:_authToken=${CIRCLE_NPM_TOKEN}" > ~/.npmrc
|
||||
- run: |
|
||||
git config --global user.email "react-native-bot@users.noreply.github.com"
|
||||
git config --global user.name "npm Deployment Script"
|
||||
echo "machine github.com login react-native-bot password $GITHUB_TOKEN" > ~/.netrc
|
||||
- run: node ./scripts/publish-npm.js << parameters.publish_npm_args >>
|
||||
- when:
|
||||
condition:
|
||||
|
@ -846,7 +852,9 @@ workflows:
|
|||
|
||||
releases:
|
||||
jobs:
|
||||
- publish_npm_package:
|
||||
- build_npm_package:
|
||||
name: build_and_publish_npm_package
|
||||
publish_npm_args: --release
|
||||
filters:
|
||||
# Both of the following conditions must be included!
|
||||
# Ignore any commit on any branch by default.
|
||||
|
@ -855,14 +863,15 @@ workflows:
|
|||
# Only act on version tags.
|
||||
tags:
|
||||
only: /v[0-9]+(\.[0-9]+)*(\-rc(\.[0-9]+)?)?/
|
||||
- publish_npm_package:
|
||||
- build_npm_package:
|
||||
# Build a release package on every untagged commit, but do not publish to npm.
|
||||
name: build_commit_package
|
||||
publish_npm_args: --dry-run
|
||||
filters:
|
||||
branches:
|
||||
only:
|
||||
- main
|
||||
- /^pull\/.*$/
|
||||
- /^(\d+)\.(\d+)-stable$/
|
||||
tags:
|
||||
ignore: /v[0-9]+(\.[0-9]+)*(\-rc(\.[0-9]+)?)?/
|
||||
|
||||
|
@ -901,5 +910,5 @@ workflows:
|
|||
jobs:
|
||||
- nightly_job
|
||||
|
||||
- publish_npm_package:
|
||||
- build_npm_package:
|
||||
publish_npm_args: --nightly
|
||||
|
|
|
@ -79,4 +79,4 @@ untyped-import
|
|||
untyped-type-import
|
||||
|
||||
[version]
|
||||
^0.160.2
|
||||
^0.161.0
|
||||
|
|
|
@ -82,4 +82,4 @@ untyped-import
|
|||
untyped-type-import
|
||||
|
||||
[version]
|
||||
^0.160.2
|
||||
^0.161.0
|
||||
|
|
|
@ -79,4 +79,4 @@ untyped-import
|
|||
untyped-type-import
|
||||
|
||||
[version]
|
||||
^0.160.2
|
||||
^0.161.0
|
||||
|
|
5
BUCK
5
BUCK
|
@ -1,7 +1,6 @@
|
|||
load("//tools/build_defs:fb_native_wrapper.bzl", "fb_native")
|
||||
load("//tools/build_defs/apple:config_utils_defs.bzl", "STATIC_LIBRARY_APPLETVOS_CONFIG", "fbobjc_configs")
|
||||
load("//tools/build_defs/apple:fb_apple_test.bzl", "fb_apple_test")
|
||||
load("//tools/build_defs/apple:flag_defs.bzl", "get_base_appletvos_flags", "get_objc_arc_preprocessor_flags", "get_preprocessor_flags_for_build_mode", "get_static_library_ios_flags")
|
||||
load("//tools/build_defs/apple:flag_defs.bzl", "get_objc_arc_preprocessor_flags", "get_preprocessor_flags_for_build_mode", "get_static_library_ios_flags")
|
||||
load("//tools/build_defs/apple/plugins:plugin_defs.bzl", "plugin")
|
||||
load("//tools/build_defs/oss:metro_defs.bzl", "rn_library")
|
||||
load(
|
||||
|
@ -184,8 +183,6 @@ rn_xplat_cxx_library2(
|
|||
prefix = "React",
|
||||
),
|
||||
apple_sdks = (IOS, APPLETVOS),
|
||||
appletvos_configs = fbobjc_configs(STATIC_LIBRARY_APPLETVOS_CONFIG),
|
||||
appletvos_inherited_buck_flags = get_base_appletvos_flags(),
|
||||
contacts = ["oncall+react_native@xmail.facebook.com"],
|
||||
fbobjc_enable_exceptions = True,
|
||||
frameworks = [
|
||||
|
|
|
@ -15,9 +15,9 @@ const createAnimatedComponent = require('../createAnimatedComponent');
|
|||
|
||||
import type {AnimatedComponentType} from '../createAnimatedComponent';
|
||||
|
||||
module.exports = (createAnimatedComponent((Image: $FlowFixMe), {
|
||||
collapsable: false,
|
||||
}): AnimatedComponentType<
|
||||
module.exports = (createAnimatedComponent(
|
||||
(Image: $FlowFixMe),
|
||||
): AnimatedComponentType<
|
||||
React.ElementConfig<typeof Image>,
|
||||
React.ElementRef<typeof Image>,
|
||||
>);
|
||||
|
|
|
@ -22,9 +22,9 @@ const ScrollViewWithEventThrottle = React.forwardRef((props, ref) => (
|
|||
<ScrollView scrollEventThrottle={0.0001} {...props} ref={ref} />
|
||||
));
|
||||
|
||||
module.exports = (createAnimatedComponent(ScrollViewWithEventThrottle, {
|
||||
collapsable: false,
|
||||
}): AnimatedComponentType<
|
||||
module.exports = (createAnimatedComponent(
|
||||
ScrollViewWithEventThrottle,
|
||||
): AnimatedComponentType<
|
||||
React.ElementConfig<typeof ScrollView>,
|
||||
React.ElementRef<typeof ScrollView>,
|
||||
>);
|
||||
|
|
|
@ -15,9 +15,9 @@ const createAnimatedComponent = require('../createAnimatedComponent');
|
|||
|
||||
import type {AnimatedComponentType} from '../createAnimatedComponent';
|
||||
|
||||
module.exports = (createAnimatedComponent((Text: $FlowFixMe), {
|
||||
collapsable: false,
|
||||
}): AnimatedComponentType<
|
||||
module.exports = (createAnimatedComponent(
|
||||
(Text: $FlowFixMe),
|
||||
): AnimatedComponentType<
|
||||
React.ElementConfig<typeof Text>,
|
||||
React.ElementRef<typeof Text>,
|
||||
>);
|
||||
|
|
|
@ -15,9 +15,7 @@ const createAnimatedComponent = require('../createAnimatedComponent');
|
|||
|
||||
import type {AnimatedComponentType} from '../createAnimatedComponent';
|
||||
|
||||
module.exports = (createAnimatedComponent(View, {
|
||||
collapsable: true,
|
||||
}): AnimatedComponentType<
|
||||
module.exports = (createAnimatedComponent(View): AnimatedComponentType<
|
||||
React.ElementConfig<typeof View>,
|
||||
React.ElementRef<typeof View>,
|
||||
>);
|
||||
|
|
|
@ -39,13 +39,8 @@ export type AnimatedComponentType<
|
|||
Instance,
|
||||
>;
|
||||
|
||||
type AnimatedComponentOptions = {
|
||||
collapsable?: boolean,
|
||||
};
|
||||
|
||||
function createAnimatedComponent<Props: {+[string]: mixed, ...}, Instance>(
|
||||
Component: React.AbstractComponent<Props, Instance>,
|
||||
options?: AnimatedComponentOptions,
|
||||
): AnimatedComponentType<Props, Instance> {
|
||||
invariant(
|
||||
typeof Component !== 'function' ||
|
||||
|
@ -210,39 +205,13 @@ function createAnimatedComponent<Props: {+[string]: mixed, ...}, Instance>(
|
|||
this.props.passthroughAnimatedPropExplicitValues || {};
|
||||
const mergedStyle = {...style, ...passthruStyle};
|
||||
|
||||
// On Fabric, we always want to ensure the container Animated View is *not*
|
||||
// flattened.
|
||||
// Because we do not get a host component ref immediately and thus cannot
|
||||
// do a proper Fabric vs non-Fabric detection immediately, we default to assuming
|
||||
// that Fabric *is* enabled until we know otherwise.
|
||||
// Thus, in Fabric, this view will never be flattened. In non-Fabric, the view will
|
||||
// not be flattened during the initial render but may be flattened in the second render
|
||||
// and onwards.
|
||||
const forceNativeIdFabric =
|
||||
(this._component == null &&
|
||||
(options?.collapsable === false || props.collapsable !== true)) ||
|
||||
this._isFabric();
|
||||
|
||||
const forceNativeId =
|
||||
props.collapsable ??
|
||||
(this._propsAnimated.__isNative ||
|
||||
forceNativeIdFabric ||
|
||||
options?.collapsable === false);
|
||||
// The native driver updates views directly through the UI thread so we
|
||||
// have to make sure the view doesn't get optimized away because it cannot
|
||||
// go through the NativeViewHierarchyManager since it operates on the shadow
|
||||
// thread. TODO: T68258846
|
||||
const collapsableProps = forceNativeId
|
||||
? {
|
||||
nativeID: props.nativeID ?? 'animatedComponent',
|
||||
collapsable: false,
|
||||
}
|
||||
: {};
|
||||
// Force `collapsable` to be false so that native view is not flattened.
|
||||
// Flattened views cannot be accurately referenced by a native driver.
|
||||
return (
|
||||
<Component
|
||||
{...props}
|
||||
{...passthruProps}
|
||||
{...collapsableProps}
|
||||
collapsable={false}
|
||||
style={mergedStyle}
|
||||
ref={this._setComponentRef}
|
||||
/>
|
||||
|
|
|
@ -346,17 +346,6 @@ type IOSProps = $ReadOnly<{|
|
|||
* The default value is true.
|
||||
*/
|
||||
showsHorizontalScrollIndicator?: ?boolean,
|
||||
/**
|
||||
* When `snapToInterval` is set, `snapToAlignment` will define the relationship
|
||||
* of the snapping to the scroll view.
|
||||
*
|
||||
* - `'start'` (the default) will align the snap at the left (horizontal) or top (vertical)
|
||||
* - `'center'` will align the snap in the center
|
||||
* - `'end'` will align the snap at the right (horizontal) or bottom (vertical)
|
||||
*
|
||||
* @platform ios
|
||||
*/
|
||||
snapToAlignment?: ?('start' | 'center' | 'end'),
|
||||
/**
|
||||
* The current scale of the scroll view content. The default value is 1.0.
|
||||
* @platform ios
|
||||
|
@ -611,6 +600,15 @@ export type Props = $ReadOnly<{|
|
|||
* for example when you want your list to have an animated hidable header.
|
||||
*/
|
||||
StickyHeaderComponent?: StickyHeaderComponentType,
|
||||
/**
|
||||
* When `snapToInterval` is set, `snapToAlignment` will define the relationship
|
||||
* of the snapping to the scroll view.
|
||||
*
|
||||
* - `'start'` (the default) will align the snap at the left (horizontal) or top (vertical)
|
||||
* - `'center'` will align the snap in the center
|
||||
* - `'end'` will align the snap at the right (horizontal) or bottom (vertical)
|
||||
*/
|
||||
snapToAlignment?: ?('start' | 'center' | 'end'),
|
||||
/**
|
||||
* When set, causes the scroll view to stop at multiples of the value of
|
||||
* `snapToInterval`. This can be used for paginating through children
|
||||
|
|
|
@ -7,7 +7,6 @@ exports[`TouchableOpacity renders correctly 1`] = `
|
|||
collapsable={false}
|
||||
enableFocusRing={true}
|
||||
focusable={true}
|
||||
nativeID="animatedComponent"
|
||||
onClick={[Function]}
|
||||
onKeyDown={[Function]}
|
||||
onKeyUp={[Function]}
|
||||
|
@ -41,7 +40,6 @@ exports[`TouchableOpacity renders in disabled state when a disabled prop is pass
|
|||
collapsable={false}
|
||||
enableFocusRing={false}
|
||||
focusable={false}
|
||||
nativeID="animatedComponent"
|
||||
onClick={[Function]}
|
||||
onKeyDown={[Function]}
|
||||
onKeyUp={[Function]}
|
||||
|
@ -75,7 +73,6 @@ exports[`TouchableOpacity renders in disabled state when a key disabled in acces
|
|||
collapsable={false}
|
||||
enableFocusRing={true}
|
||||
focusable={true}
|
||||
nativeID="animatedComponent"
|
||||
onClick={[Function]}
|
||||
onKeyDown={[Function]}
|
||||
onKeyUp={[Function]}
|
||||
|
|
|
@ -13,7 +13,6 @@ exports[`<Button /> should be disabled and it should set accessibilityState to d
|
|||
collapsable={false}
|
||||
enableFocusRing={false}
|
||||
focusable={false}
|
||||
nativeID="animatedComponent"
|
||||
onClick={[Function]}
|
||||
onKeyDown={[Function]}
|
||||
onKeyUp={[Function]}
|
||||
|
@ -72,7 +71,6 @@ exports[`<Button /> should be disabled when disabled is empty and accessibilityS
|
|||
collapsable={false}
|
||||
enableFocusRing={false}
|
||||
focusable={false}
|
||||
nativeID="animatedComponent"
|
||||
onClick={[Function]}
|
||||
onKeyDown={[Function]}
|
||||
onKeyUp={[Function]}
|
||||
|
@ -131,7 +129,6 @@ exports[`<Button /> should be disabled when disabled={true} and accessibilitySta
|
|||
collapsable={false}
|
||||
enableFocusRing={false}
|
||||
focusable={false}
|
||||
nativeID="animatedComponent"
|
||||
onClick={[Function]}
|
||||
onKeyDown={[Function]}
|
||||
onKeyUp={[Function]}
|
||||
|
@ -190,7 +187,6 @@ exports[`<Button /> should not be disabled when disabled={false} and accessibili
|
|||
collapsable={false}
|
||||
enableFocusRing={true}
|
||||
focusable={true}
|
||||
nativeID="animatedComponent"
|
||||
onClick={[Function]}
|
||||
onKeyDown={[Function]}
|
||||
onKeyUp={[Function]}
|
||||
|
@ -245,7 +241,6 @@ exports[`<Button /> should not be disabled when disabled={false} and accessibili
|
|||
collapsable={false}
|
||||
enableFocusRing={true}
|
||||
focusable={true}
|
||||
nativeID="animatedComponent"
|
||||
onClick={[Function]}
|
||||
onKeyDown={[Function]}
|
||||
onKeyUp={[Function]}
|
||||
|
@ -300,7 +295,6 @@ exports[`<Button /> should overwrite accessibilityState with value of disabled p
|
|||
collapsable={false}
|
||||
enableFocusRing={false}
|
||||
focusable={false}
|
||||
nativeID="animatedComponent"
|
||||
onClick={[Function]}
|
||||
onKeyDown={[Function]}
|
||||
onKeyUp={[Function]}
|
||||
|
@ -354,7 +348,6 @@ exports[`<Button /> should render as expected 1`] = `
|
|||
collapsable={false}
|
||||
enableFocusRing={true}
|
||||
focusable={true}
|
||||
nativeID="animatedComponent"
|
||||
onClick={[Function]}
|
||||
onKeyDown={[Function]}
|
||||
onKeyUp={[Function]}
|
||||
|
|
|
@ -335,6 +335,10 @@ RCT_NOT_IMPLEMENTED(- (instancetype)initWithFrame:(CGRect)frame)
|
|||
|
||||
[_imageLoader trackURLImageRequestDidDestroy:_loaderRequest];
|
||||
_loaderRequest = nil;
|
||||
|
||||
if (!self.image) {
|
||||
self.image = _defaultImage;
|
||||
}
|
||||
}
|
||||
|
||||
#if !TARGET_OS_OSX // TODO(macOS GH#774)
|
||||
|
|
|
@ -651,6 +651,19 @@ export default class Pressability {
|
|||
* and stores the new state. Validates the transition as well.
|
||||
*/
|
||||
_receiveSignal(signal: TouchSignal, event: PressEvent): void {
|
||||
// Especially on iOS, not all events have timestamps associated.
|
||||
// For telemetry purposes, this doesn't matter too much, as long as *some* do.
|
||||
// Since the native timestamp is integral for logging telemetry, just skip
|
||||
// events if they don't have a timestamp attached.
|
||||
if (event.nativeEvent.timestamp != null) {
|
||||
PressabilityPerformanceEventEmitter.emitEvent(() => {
|
||||
return {
|
||||
signal,
|
||||
nativeTimestamp: event.nativeEvent.timestamp,
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
const prevState = this._touchState;
|
||||
const nextState = Transitions[prevState]?.[signal];
|
||||
if (this._responderID == null && signal === 'RESPONDER_RELEASE') {
|
||||
|
@ -666,20 +679,6 @@ export default class Pressability {
|
|||
: '<<host component>>',
|
||||
);
|
||||
if (prevState !== nextState) {
|
||||
// Especially on iOS, not all events have timestamps associated.
|
||||
// For telemetry purposes, this doesn't matter too much, as long as *some* do.
|
||||
// Since the native timestamp is integral for logging telemetry, just skip
|
||||
// events if they don't have a timestamp attached.
|
||||
if (event.nativeEvent.timestamp != null) {
|
||||
PressabilityPerformanceEventEmitter.emitEvent(() => {
|
||||
return {
|
||||
signal,
|
||||
nativeTimestamp: event.nativeEvent.timestamp,
|
||||
touchDelayMs: Date.now() - event.nativeEvent.timestamp,
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
this._performTransitionSideEffects(prevState, nextState, signal, event);
|
||||
this._touchState = nextState;
|
||||
}
|
||||
|
|
|
@ -13,7 +13,6 @@ import {type PressabilityTouchSignal as TouchSignal} from './PressabilityTypes.j
|
|||
export type PressabilityPerformanceEvent = $ReadOnly<{|
|
||||
signal: TouchSignal,
|
||||
nativeTimestamp: number,
|
||||
touchDelayMs: number,
|
||||
|}>;
|
||||
export type PressabilityPerformanceEventListener = PressabilityPerformanceEvent => void;
|
||||
|
||||
|
|
|
@ -51,7 +51,7 @@ class Share {
|
|||
*
|
||||
* #### iOS
|
||||
*
|
||||
* - `url` - an URL to share
|
||||
* - `url` - a URL to share
|
||||
*
|
||||
* At least one of URL and message is required.
|
||||
*
|
||||
|
|
|
@ -76,6 +76,7 @@ import com.facebook.react.bridge.WritableNativeMap;
|
|||
import com.facebook.react.bridge.queue.ReactQueueConfigurationSpec;
|
||||
import com.facebook.react.common.LifecycleState;
|
||||
import com.facebook.react.common.ReactConstants;
|
||||
import com.facebook.react.common.SurfaceDelegateFactory;
|
||||
import com.facebook.react.common.annotations.VisibleForTesting;
|
||||
import com.facebook.react.config.ReactFeatureFlags;
|
||||
import com.facebook.react.devsupport.DevSupportManagerFactory;
|
||||
|
@ -232,7 +233,8 @@ public class ReactInstanceManager {
|
|||
int minTimeLeftInFrameForNonBatchedOperationMs,
|
||||
@Nullable JSIModulePackage jsiModulePackage,
|
||||
@Nullable Map<String, RequestHandler> customPackagerCommandHandlers,
|
||||
@Nullable ReactPackageTurboModuleManagerDelegate.Builder tmmDelegateBuilder) {
|
||||
@Nullable ReactPackageTurboModuleManagerDelegate.Builder tmmDelegateBuilder,
|
||||
@Nullable SurfaceDelegateFactory surfaceDelegateFactory) {
|
||||
FLog.d(TAG, "ReactInstanceManager.ctor()");
|
||||
initializeSoLoaderIfNecessary(applicationContext);
|
||||
|
||||
|
@ -259,7 +261,8 @@ public class ReactInstanceManager {
|
|||
redBoxHandler,
|
||||
devBundleDownloadListener,
|
||||
minNumShakes,
|
||||
customPackagerCommandHandlers);
|
||||
customPackagerCommandHandlers,
|
||||
surfaceDelegateFactory);
|
||||
Systrace.endSection(TRACE_TAG_REACT_JAVA_BRIDGE);
|
||||
mBridgeIdleDebugListener = bridgeIdleDebugListener;
|
||||
mLifecycleState = initialLifecycleState;
|
||||
|
|
|
@ -22,6 +22,7 @@ import com.facebook.react.bridge.JavaScriptExecutorFactory;
|
|||
import com.facebook.react.bridge.NativeModuleCallExceptionHandler;
|
||||
import com.facebook.react.bridge.NotThreadSafeBridgeIdleDebugListener;
|
||||
import com.facebook.react.common.LifecycleState;
|
||||
import com.facebook.react.common.SurfaceDelegateFactory;
|
||||
import com.facebook.react.devsupport.DefaultDevSupportManagerFactory;
|
||||
import com.facebook.react.devsupport.DevSupportManagerFactory;
|
||||
import com.facebook.react.devsupport.RedBoxHandler;
|
||||
|
@ -63,6 +64,7 @@ public class ReactInstanceManagerBuilder {
|
|||
private @Nullable JSIModulePackage mJSIModulesPackage;
|
||||
private @Nullable Map<String, RequestHandler> mCustomPackagerCommandHandlers;
|
||||
private @Nullable ReactPackageTurboModuleManagerDelegate.Builder mTMMDelegateBuilder;
|
||||
private @Nullable SurfaceDelegateFactory mSurfaceDelegateFactory;
|
||||
|
||||
/* package protected */ ReactInstanceManagerBuilder() {}
|
||||
|
||||
|
@ -195,6 +197,20 @@ public class ReactInstanceManagerBuilder {
|
|||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* When the {@link SurfaceDelegateFactory} is provided, it will be used for native modules to get
|
||||
* a {@link SurfaceDelegate} to interact with the platform specific surface that they that needs
|
||||
* to be rendered in. For mobile platform this is default to be null so that these modules will
|
||||
* need to provide a default surface delegate. One example of such native module is LogBoxModule,
|
||||
* which is rendered in mobile platform with LogBoxDialog, while in VR platform with custom layer
|
||||
* provided by runtime.
|
||||
*/
|
||||
public ReactInstanceManagerBuilder setSurfaceDelegateFactory(
|
||||
@Nullable final SurfaceDelegateFactory surfaceDelegateFactory) {
|
||||
mSurfaceDelegateFactory = surfaceDelegateFactory;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the initial lifecycle state of the host. For example, if the host is already resumed at
|
||||
* creation time, we wouldn't expect an onResume call until we get an onPause call.
|
||||
|
@ -322,7 +338,8 @@ public class ReactInstanceManagerBuilder {
|
|||
mMinTimeLeftInFrameForNonBatchedOperationMs,
|
||||
mJSIModulesPackage,
|
||||
mCustomPackagerCommandHandlers,
|
||||
mTMMDelegateBuilder);
|
||||
mTMMDelegateBuilder,
|
||||
mSurfaceDelegateFactory);
|
||||
}
|
||||
|
||||
private JavaScriptExecutorFactory getDefaultJSExecutorFactory(
|
||||
|
|
|
@ -15,6 +15,8 @@ import com.facebook.react.bridge.JavaScriptExecutorFactory;
|
|||
import com.facebook.react.bridge.ReactMarker;
|
||||
import com.facebook.react.bridge.ReactMarkerConstants;
|
||||
import com.facebook.react.common.LifecycleState;
|
||||
import com.facebook.react.common.SurfaceDelegate;
|
||||
import com.facebook.react.common.SurfaceDelegateFactory;
|
||||
import com.facebook.react.devsupport.DevSupportManagerFactory;
|
||||
import com.facebook.react.devsupport.RedBoxHandler;
|
||||
import com.facebook.react.uimanager.UIImplementationProvider;
|
||||
|
@ -71,6 +73,7 @@ public abstract class ReactNativeHost {
|
|||
.setUseDeveloperSupport(getUseDeveloperSupport())
|
||||
.setDevSupportManagerFactory(getDevSupportManagerFactory())
|
||||
.setRequireActivity(getShouldRequireActivity())
|
||||
.setSurfaceDelegateFactory(getSurfaceDelegateFactory())
|
||||
.setRedBoxHandler(getRedBoxHandler())
|
||||
.setJavaScriptExecutorFactory(getJavaScriptExecutorFactory())
|
||||
.setUIImplementationProvider(getUIImplementationProvider())
|
||||
|
@ -132,6 +135,21 @@ public abstract class ReactNativeHost {
|
|||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the {@link SurfaceDelegateFactory} used by NativeModules to get access to a {@link
|
||||
* SurfaceDelegate} to interact with a surface. By default in the mobile platform the {@link
|
||||
* SurfaceDelegate} it returns is null, and the NativeModule needs to implement its own {@link
|
||||
* SurfaceDelegate} to decide how it would interact with its own container surface.
|
||||
*/
|
||||
public SurfaceDelegateFactory getSurfaceDelegateFactory() {
|
||||
return new SurfaceDelegateFactory() {
|
||||
@Override
|
||||
public @Nullable SurfaceDelegate createSurfaceDelegate(String moduleName) {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the name of the main module. Determines the URL used to fetch the JS bundle from Metro.
|
||||
* It is only used when dev support is enabled. This is the first file to be executed once the
|
||||
|
|
|
@ -0,0 +1,40 @@
|
|||
/*
|
||||
* 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.common;
|
||||
|
||||
/**
|
||||
* Interface for handling a surface in React Native. In mobile platform a surface can be any
|
||||
* container that holds some {@link View}. For example, a Dialog can be a surface to wrap content
|
||||
* view object as needed. In VR platform, a surface is provided by Shell panel app sdk, which
|
||||
* requires custom logic to show/hide. NativeModules requires a surface will delegate interactions
|
||||
* with the surface to a SurfaceDelegate.
|
||||
*/
|
||||
public interface SurfaceDelegate {
|
||||
/**
|
||||
* Create the React content view that uses the appKey as the React application name
|
||||
*
|
||||
* @param appKey
|
||||
*/
|
||||
void createContentView(String appKey);
|
||||
|
||||
/**
|
||||
* Check if the content view is created and ready to be shown
|
||||
*
|
||||
* @return true if the content view is ready to be shown
|
||||
*/
|
||||
boolean isContentViewReady();
|
||||
|
||||
/** Destroy the React content view to avoid memory leak */
|
||||
void destroyContentView();
|
||||
|
||||
/** Show the surface containing the React content view */
|
||||
void show();
|
||||
|
||||
/** Hide the surface containing the React content view */
|
||||
void hide();
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
/*
|
||||
* 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.common;
|
||||
|
||||
/**
|
||||
* Factory to create a {@link SurfaceDelegate}. The moduleName is needed to help the factory decide
|
||||
* which surface to return {@link SurfaceDelegate} that the given module should use to interact
|
||||
* with.
|
||||
*/
|
||||
public interface SurfaceDelegateFactory {
|
||||
/**
|
||||
* Create a {@link SurfaceDelegate} instance which is used to interact with a surface of platform
|
||||
* the app is running in.
|
||||
*
|
||||
* @param moduleName the module name that will be using the surface
|
||||
* @return {@link SurfaceDelegate} instance
|
||||
*/
|
||||
SurfaceDelegate createSurfaceDelegate(String moduleName);
|
||||
}
|
|
@ -99,4 +99,8 @@ public class ReactFeatureFlags {
|
|||
public static boolean enableAggressiveEventEmitterCleanup = false;
|
||||
|
||||
public static boolean insertZReorderBarriersOnViewGroupChildren = true;
|
||||
|
||||
public static boolean enableScrollViewSnapToAlignmentProp = true;
|
||||
|
||||
public static boolean useDispatchUniqueForCoalescableEvents = false;
|
||||
}
|
||||
|
|
|
@ -57,6 +57,7 @@ rn_android_library(
|
|||
react_native_dep("third-party/android/androidx:annotation"),
|
||||
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/modules/debug:interfaces"),
|
||||
],
|
||||
)
|
||||
|
|
|
@ -22,6 +22,7 @@ import com.facebook.react.bridge.ReactMarker;
|
|||
import com.facebook.react.bridge.ReactMarkerConstants;
|
||||
import com.facebook.react.bridge.UiThreadUtil;
|
||||
import com.facebook.react.common.ReactConstants;
|
||||
import com.facebook.react.common.SurfaceDelegateFactory;
|
||||
import com.facebook.react.common.futures.SimpleSettableFuture;
|
||||
import com.facebook.react.devsupport.interfaces.DevBundleDownloadListener;
|
||||
import com.facebook.react.devsupport.interfaces.DevOptionHandler;
|
||||
|
@ -71,7 +72,8 @@ public final class BridgeDevSupportManager extends DevSupportManagerBase {
|
|||
@Nullable RedBoxHandler redBoxHandler,
|
||||
@Nullable DevBundleDownloadListener devBundleDownloadListener,
|
||||
int minNumShakes,
|
||||
@Nullable Map<String, RequestHandler> customPackagerCommandHandlers) {
|
||||
@Nullable Map<String, RequestHandler> customPackagerCommandHandlers,
|
||||
@Nullable SurfaceDelegateFactory surfaceDelegateFactory) {
|
||||
super(
|
||||
applicationContext,
|
||||
reactInstanceManagerHelper,
|
||||
|
@ -80,7 +82,8 @@ public final class BridgeDevSupportManager extends DevSupportManagerBase {
|
|||
redBoxHandler,
|
||||
devBundleDownloadListener,
|
||||
minNumShakes,
|
||||
customPackagerCommandHandlers);
|
||||
customPackagerCommandHandlers,
|
||||
surfaceDelegateFactory);
|
||||
|
||||
if (getDevSettings().isStartSamplingProfilerOnInit()) {
|
||||
// Only start the profiler. If its already running, there is an error
|
||||
|
|
|
@ -9,6 +9,7 @@ package com.facebook.react.devsupport;
|
|||
|
||||
import android.content.Context;
|
||||
import androidx.annotation.Nullable;
|
||||
import com.facebook.react.common.SurfaceDelegateFactory;
|
||||
import com.facebook.react.devsupport.interfaces.DevBundleDownloadListener;
|
||||
import com.facebook.react.devsupport.interfaces.DevSupportManager;
|
||||
import com.facebook.react.packagerconnection.RequestHandler;
|
||||
|
@ -41,6 +42,7 @@ public class DefaultDevSupportManagerFactory implements DevSupportManagerFactory
|
|||
null,
|
||||
null,
|
||||
minNumShakes,
|
||||
null,
|
||||
null);
|
||||
}
|
||||
|
||||
|
@ -53,7 +55,8 @@ public class DefaultDevSupportManagerFactory implements DevSupportManagerFactory
|
|||
@Nullable RedBoxHandler redBoxHandler,
|
||||
@Nullable DevBundleDownloadListener devBundleDownloadListener,
|
||||
int minNumShakes,
|
||||
@Nullable Map<String, RequestHandler> customPackagerCommandHandlers) {
|
||||
@Nullable Map<String, RequestHandler> customPackagerCommandHandlers,
|
||||
@Nullable SurfaceDelegateFactory surfaceDelegateFactory) {
|
||||
if (!enableOnCreate) {
|
||||
return new DisabledDevSupportManager();
|
||||
}
|
||||
|
@ -76,7 +79,8 @@ public class DefaultDevSupportManagerFactory implements DevSupportManagerFactory
|
|||
RedBoxHandler.class,
|
||||
DevBundleDownloadListener.class,
|
||||
int.class,
|
||||
Map.class);
|
||||
Map.class,
|
||||
SurfaceDelegateFactory.class);
|
||||
return (DevSupportManager)
|
||||
constructor.newInstance(
|
||||
applicationContext,
|
||||
|
@ -86,7 +90,8 @@ public class DefaultDevSupportManagerFactory implements DevSupportManagerFactory
|
|||
redBoxHandler,
|
||||
devBundleDownloadListener,
|
||||
minNumShakes,
|
||||
customPackagerCommandHandlers);
|
||||
customPackagerCommandHandlers,
|
||||
surfaceDelegateFactory);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(
|
||||
"Requested enabled DevSupportManager, but BridgeDevSupportManager class was not found"
|
||||
|
|
|
@ -41,6 +41,8 @@ import com.facebook.react.bridge.UiThreadUtil;
|
|||
import com.facebook.react.common.DebugServerException;
|
||||
import com.facebook.react.common.ReactConstants;
|
||||
import com.facebook.react.common.ShakeDetector;
|
||||
import com.facebook.react.common.SurfaceDelegate;
|
||||
import com.facebook.react.common.SurfaceDelegateFactory;
|
||||
import com.facebook.react.devsupport.DevServerHelper.PackagerCommandListener;
|
||||
import com.facebook.react.devsupport.interfaces.BundleLoadCallback;
|
||||
import com.facebook.react.devsupport.interfaces.DevBundleDownloadListener;
|
||||
|
@ -118,6 +120,7 @@ public abstract class DevSupportManagerBase implements DevSupportManager {
|
|||
private @Nullable Map<String, RequestHandler> mCustomPackagerCommandHandlers;
|
||||
|
||||
private @Nullable Activity currentActivity;
|
||||
private @Nullable final SurfaceDelegateFactory mSurfaceDelegateFactory;
|
||||
|
||||
public DevSupportManagerBase(
|
||||
Context applicationContext,
|
||||
|
@ -127,7 +130,8 @@ public abstract class DevSupportManagerBase implements DevSupportManager {
|
|||
@Nullable RedBoxHandler redBoxHandler,
|
||||
@Nullable DevBundleDownloadListener devBundleDownloadListener,
|
||||
int minNumShakes,
|
||||
@Nullable Map<String, RequestHandler> customPackagerCommandHandlers) {
|
||||
@Nullable Map<String, RequestHandler> customPackagerCommandHandlers,
|
||||
@Nullable SurfaceDelegateFactory surfaceDelegateFactory) {
|
||||
mReactInstanceDevHelper = reactInstanceDevHelper;
|
||||
mApplicationContext = applicationContext;
|
||||
mJSAppBundleName = packagerPathForJSBundleName;
|
||||
|
@ -202,7 +206,8 @@ public abstract class DevSupportManagerBase implements DevSupportManager {
|
|||
|
||||
mRedBoxHandler = redBoxHandler;
|
||||
mDevLoadingViewController = new DevLoadingViewController(reactInstanceDevHelper);
|
||||
}
|
||||
mSurfaceDelegateFactory = surfaceDelegateFactory;
|
||||
};
|
||||
|
||||
protected abstract String getUniqueTag();
|
||||
|
||||
|
@ -1204,4 +1209,18 @@ public abstract class DevSupportManagerBase implements DevSupportManager {
|
|||
DevSupportManager.PackagerLocationCustomizer packagerLocationCustomizer) {
|
||||
mPackagerLocationCustomizer = packagerLocationCustomizer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable Activity getCurrentActivity() {
|
||||
return mReactInstanceDevHelper.getCurrentActivity();
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable SurfaceDelegate createSurfaceDelegate(String moduleName) {
|
||||
if (mSurfaceDelegateFactory == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return mSurfaceDelegateFactory.createSurfaceDelegate(moduleName);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,6 +9,7 @@ package com.facebook.react.devsupport;
|
|||
|
||||
import android.content.Context;
|
||||
import androidx.annotation.Nullable;
|
||||
import com.facebook.react.common.SurfaceDelegateFactory;
|
||||
import com.facebook.react.devsupport.interfaces.DevBundleDownloadListener;
|
||||
import com.facebook.react.devsupport.interfaces.DevSupportManager;
|
||||
import com.facebook.react.packagerconnection.RequestHandler;
|
||||
|
@ -23,5 +24,6 @@ public interface DevSupportManagerFactory {
|
|||
@Nullable RedBoxHandler redBoxHandler,
|
||||
@Nullable DevBundleDownloadListener devBundleDownloadListener,
|
||||
int minNumShakes,
|
||||
@Nullable Map<String, RequestHandler> customPackagerCommandHandlers);
|
||||
@Nullable Map<String, RequestHandler> customPackagerCommandHandlers,
|
||||
@Nullable SurfaceDelegateFactory surfaceDelegateFactory);
|
||||
}
|
||||
|
|
|
@ -7,11 +7,13 @@
|
|||
|
||||
package com.facebook.react.devsupport;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.view.View;
|
||||
import androidx.annotation.Nullable;
|
||||
import com.facebook.react.bridge.DefaultNativeModuleCallExceptionHandler;
|
||||
import com.facebook.react.bridge.ReactContext;
|
||||
import com.facebook.react.bridge.ReadableArray;
|
||||
import com.facebook.react.common.SurfaceDelegate;
|
||||
import com.facebook.react.devsupport.interfaces.BundleLoadCallback;
|
||||
import com.facebook.react.devsupport.interfaces.DevOptionHandler;
|
||||
import com.facebook.react.devsupport.interfaces.DevSplitBundleCallback;
|
||||
|
@ -175,4 +177,14 @@ public class DisabledDevSupportManager implements DevSupportManager {
|
|||
public void handleException(Exception e) {
|
||||
mDefaultNativeModuleCallExceptionHandler.handleException(e);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable Activity getCurrentActivity() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable SurfaceDelegate createSurfaceDelegate(String moduleName) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,92 @@
|
|||
/*
|
||||
* 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.devsupport;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import androidx.annotation.Nullable;
|
||||
import com.facebook.infer.annotation.Assertions;
|
||||
import com.facebook.react.common.SurfaceDelegate;
|
||||
import com.facebook.react.devsupport.interfaces.DevSupportManager;
|
||||
import com.facebook.react.util.RNLog;
|
||||
|
||||
/**
|
||||
* The implementation of SurfaceDelegate with {@link Activity}. This is the default SurfaceDelegate
|
||||
* for Mobile.
|
||||
*/
|
||||
public class LogBoxDialogSurfaceDelegate implements SurfaceDelegate {
|
||||
|
||||
private @Nullable View mReactRootView;
|
||||
private @Nullable LogBoxDialog mDialog;
|
||||
private final DevSupportManager mDevSupportManager;
|
||||
|
||||
LogBoxDialogSurfaceDelegate(DevSupportManager devSupportManager) {
|
||||
mDevSupportManager = devSupportManager;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void createContentView(String appKey) {
|
||||
Assertions.assertCondition(
|
||||
appKey.equals("LogBox"), "This surface manager can only create LogBox React application");
|
||||
mReactRootView = mDevSupportManager.createRootView("LogBox");
|
||||
if (mReactRootView == null) {
|
||||
RNLog.e("Unable to launch logbox because react was unable to create the root view");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isContentViewReady() {
|
||||
return mReactRootView != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void destroyContentView() {
|
||||
if (mReactRootView != null) {
|
||||
mDevSupportManager.destroyRootView(mReactRootView);
|
||||
mReactRootView = null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void show() {
|
||||
if (isSurfaceVisible() || !isContentViewReady()) {
|
||||
return;
|
||||
}
|
||||
|
||||
final @Nullable Activity context = mDevSupportManager.getCurrentActivity();
|
||||
if (context == null || context.isFinishing()) {
|
||||
RNLog.e(
|
||||
"Unable to launch logbox because react activity "
|
||||
+ "is not available, here is the error that logbox would've displayed: ");
|
||||
return;
|
||||
}
|
||||
|
||||
mDialog = new LogBoxDialog(context, mReactRootView);
|
||||
mDialog.setCancelable(false);
|
||||
mDialog.show();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void hide() {
|
||||
if (!isSurfaceVisible()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (mReactRootView != null && mReactRootView.getParent() != null) {
|
||||
((ViewGroup) mReactRootView.getParent()).removeView(mReactRootView);
|
||||
}
|
||||
|
||||
mDialog.dismiss();
|
||||
mDialog = null;
|
||||
}
|
||||
|
||||
private boolean isSurfaceVisible() {
|
||||
return mDialog != null;
|
||||
}
|
||||
}
|
|
@ -7,16 +7,13 @@
|
|||
|
||||
package com.facebook.react.devsupport;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import androidx.annotation.Nullable;
|
||||
import com.facebook.fbreact.specs.NativeLogBoxSpec;
|
||||
import com.facebook.react.bridge.ReactApplicationContext;
|
||||
import com.facebook.react.bridge.UiThreadUtil;
|
||||
import com.facebook.react.common.SurfaceDelegate;
|
||||
import com.facebook.react.devsupport.interfaces.DevSupportManager;
|
||||
import com.facebook.react.module.annotations.ReactModule;
|
||||
import com.facebook.react.util.RNLog;
|
||||
|
||||
@ReactModule(name = LogBoxModule.NAME)
|
||||
public class LogBoxModule extends NativeLogBoxSpec {
|
||||
|
@ -24,23 +21,30 @@ public class LogBoxModule extends NativeLogBoxSpec {
|
|||
public static final String NAME = "LogBox";
|
||||
|
||||
private final DevSupportManager mDevSupportManager;
|
||||
private @Nullable View mReactRootView;
|
||||
private @Nullable LogBoxDialog mLogBoxDialog;
|
||||
private final SurfaceDelegate mSurfaceDelegate;
|
||||
|
||||
/**
|
||||
* LogBoxModule can be rendered in different surface. By default, it will use LogBoxDialog to wrap
|
||||
* the content of logs. In other platform (for example VR), a surfaceDelegate can be provided so
|
||||
* that the content can be wrapped in custom surface.
|
||||
*/
|
||||
public LogBoxModule(ReactApplicationContext reactContext, DevSupportManager devSupportManager) {
|
||||
super(reactContext);
|
||||
|
||||
mDevSupportManager = devSupportManager;
|
||||
|
||||
@Nullable SurfaceDelegate surfaceDelegate = devSupportManager.createSurfaceDelegate(NAME);
|
||||
if (surfaceDelegate != null) {
|
||||
mSurfaceDelegate = surfaceDelegate;
|
||||
} else {
|
||||
mSurfaceDelegate = new LogBoxDialogSurfaceDelegate(devSupportManager);
|
||||
}
|
||||
|
||||
UiThreadUtil.runOnUiThread(
|
||||
new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (mReactRootView == null && mDevSupportManager != null) {
|
||||
mReactRootView = mDevSupportManager.createRootView("LogBox");
|
||||
if (mReactRootView == null) {
|
||||
RNLog.e("Unable to launch logbox because react was unable to create the root view");
|
||||
}
|
||||
}
|
||||
mSurfaceDelegate.createContentView("LogBox");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -52,26 +56,17 @@ public class LogBoxModule extends NativeLogBoxSpec {
|
|||
|
||||
@Override
|
||||
public void show() {
|
||||
if (mReactRootView != null) {
|
||||
UiThreadUtil.runOnUiThread(
|
||||
new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (mLogBoxDialog == null && mReactRootView != null) {
|
||||
Activity context = getCurrentActivity();
|
||||
if (context == null || context.isFinishing()) {
|
||||
RNLog.e(
|
||||
"Unable to launch logbox because react activity "
|
||||
+ "is not available, here is the error that logbox would've displayed: ");
|
||||
return;
|
||||
}
|
||||
mLogBoxDialog = new LogBoxDialog(context, mReactRootView);
|
||||
mLogBoxDialog.setCancelable(false);
|
||||
mLogBoxDialog.show();
|
||||
}
|
||||
}
|
||||
});
|
||||
if (!mSurfaceDelegate.isContentViewReady()) {
|
||||
return;
|
||||
}
|
||||
|
||||
UiThreadUtil.runOnUiThread(
|
||||
new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
mSurfaceDelegate.show();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -80,13 +75,7 @@ public class LogBoxModule extends NativeLogBoxSpec {
|
|||
new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (mLogBoxDialog != null) {
|
||||
if (mReactRootView != null && mReactRootView.getParent() != null) {
|
||||
((ViewGroup) mReactRootView.getParent()).removeView(mReactRootView);
|
||||
}
|
||||
mLogBoxDialog.dismiss();
|
||||
mLogBoxDialog = null;
|
||||
}
|
||||
mSurfaceDelegate.hide();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -97,10 +86,7 @@ public class LogBoxModule extends NativeLogBoxSpec {
|
|||
new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (mReactRootView != null) {
|
||||
mDevSupportManager.destroyRootView(mReactRootView);
|
||||
mReactRootView = null;
|
||||
}
|
||||
mSurfaceDelegate.destroyContentView();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
@ -7,11 +7,13 @@
|
|||
|
||||
package com.facebook.react.devsupport.interfaces;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.view.View;
|
||||
import androidx.annotation.Nullable;
|
||||
import com.facebook.react.bridge.NativeModuleCallExceptionHandler;
|
||||
import com.facebook.react.bridge.ReactContext;
|
||||
import com.facebook.react.bridge.ReadableArray;
|
||||
import com.facebook.react.common.SurfaceDelegate;
|
||||
import com.facebook.react.modules.debug.interfaces.DeveloperSettings;
|
||||
import java.io.File;
|
||||
|
||||
|
@ -107,4 +109,16 @@ public interface DevSupportManager extends NativeModuleCallExceptionHandler {
|
|||
}
|
||||
|
||||
void setPackagerLocationCustomizer(PackagerLocationCustomizer packagerLocationCustomizer);
|
||||
|
||||
@Nullable
|
||||
Activity getCurrentActivity();
|
||||
|
||||
/**
|
||||
* Create the surface delegate that the provided module should use to interact with
|
||||
*
|
||||
* @param moduleName the module name that helps decide which surface it should interact with
|
||||
* @return a {@link SurfaceDelegate} instance
|
||||
*/
|
||||
@Nullable
|
||||
SurfaceDelegate createSurfaceDelegate(String moduleName);
|
||||
}
|
||||
|
|
|
@ -54,6 +54,7 @@ import com.facebook.react.common.build.ReactBuildConfig;
|
|||
import com.facebook.react.common.mapbuffer.ReadableMapBuffer;
|
||||
import com.facebook.react.config.ReactFeatureFlags;
|
||||
import com.facebook.react.fabric.events.EventBeatManager;
|
||||
import com.facebook.react.fabric.events.EventCategoryDef;
|
||||
import com.facebook.react.fabric.events.EventEmitterWrapper;
|
||||
import com.facebook.react.fabric.events.FabricEventEmitter;
|
||||
import com.facebook.react.fabric.mounting.MountItemDispatcher;
|
||||
|
@ -829,6 +830,23 @@ public class FabricUIManager implements UIManager, LifecycleEventListener {
|
|||
receiveEvent(surfaceId, reactTag, eventName, false, 0, params);
|
||||
}
|
||||
|
||||
public void receiveEvent(
|
||||
int surfaceId,
|
||||
int reactTag,
|
||||
String eventName,
|
||||
boolean canCoalesceEvent,
|
||||
int customCoalesceKey,
|
||||
@Nullable WritableMap params) {
|
||||
receiveEvent(
|
||||
surfaceId,
|
||||
reactTag,
|
||||
eventName,
|
||||
canCoalesceEvent,
|
||||
customCoalesceKey,
|
||||
params,
|
||||
EventCategoryDef.UNSPECIFIED);
|
||||
}
|
||||
|
||||
/**
|
||||
* receiveEvent API that emits an event to C++. If `canCoalesceEvent` is true, that signals that
|
||||
* C++ may coalesce the event optionally. Otherwise, coalescing can happen in Java before
|
||||
|
@ -842,6 +860,7 @@ public class FabricUIManager implements UIManager, LifecycleEventListener {
|
|||
* @param canCoalesceEvent
|
||||
* @param customCoalesceKey
|
||||
* @param params
|
||||
* @param eventCategory
|
||||
*/
|
||||
public void receiveEvent(
|
||||
int surfaceId,
|
||||
|
@ -849,7 +868,8 @@ public class FabricUIManager implements UIManager, LifecycleEventListener {
|
|||
String eventName,
|
||||
boolean canCoalesceEvent,
|
||||
int customCoalesceKey,
|
||||
@Nullable WritableMap params) {
|
||||
@Nullable WritableMap params,
|
||||
@EventCategoryDef int eventCategory) {
|
||||
if (ReactBuildConfig.DEBUG && surfaceId == View.NO_ID) {
|
||||
FLog.d(TAG, "Emitted event without surfaceId: [%d] %s", reactTag, eventName);
|
||||
}
|
||||
|
@ -870,7 +890,7 @@ public class FabricUIManager implements UIManager, LifecycleEventListener {
|
|||
if (canCoalesceEvent) {
|
||||
eventEmitter.invokeUnique(eventName, params, customCoalesceKey);
|
||||
} else {
|
||||
eventEmitter.invoke(eventName, params);
|
||||
eventEmitter.invoke(eventName, params, eventCategory);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,42 @@
|
|||
/*
|
||||
* 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.fabric.events;
|
||||
|
||||
import static com.facebook.react.fabric.events.EventCategoryDef.CONTINUOUS;
|
||||
import static com.facebook.react.fabric.events.EventCategoryDef.CONTINUOUS_END;
|
||||
import static com.facebook.react.fabric.events.EventCategoryDef.CONTINUOUS_START;
|
||||
import static com.facebook.react.fabric.events.EventCategoryDef.DISCRETE;
|
||||
import static com.facebook.react.fabric.events.EventCategoryDef.UNSPECIFIED;
|
||||
|
||||
import androidx.annotation.IntDef;
|
||||
|
||||
/**
|
||||
* Java specific declaration of the `RawEvent::Category` enum. Keep in sync with
|
||||
* `renderer/core/RawEvent.h`.
|
||||
*/
|
||||
@IntDef(value = {CONTINUOUS_START, CONTINUOUS_END, UNSPECIFIED, DISCRETE, CONTINUOUS})
|
||||
public @interface EventCategoryDef {
|
||||
/** Start of a continuous event. To be used with touchStart. */
|
||||
int CONTINUOUS_START = 0;
|
||||
|
||||
/** End of a continuous event. To be used with touchEnd. */
|
||||
int CONTINUOUS_END = 1;
|
||||
|
||||
/**
|
||||
* Priority for this event will be determined from other events in the queue. If it is triggered
|
||||
* by continuous event, its priority will be default. If it is not triggered by continuous event,
|
||||
* its priority will be discrete.
|
||||
*/
|
||||
int UNSPECIFIED = 2;
|
||||
|
||||
/** Forces discrete type for the event. Regardless if continuous event is ongoing. */
|
||||
int DISCRETE = 3;
|
||||
|
||||
/** Forces continuous type for the event. Regardless if continuous event isn't ongoing. */
|
||||
int CONTINUOUS = 4;
|
||||
}
|
|
@ -36,7 +36,8 @@ public class EventEmitterWrapper {
|
|||
mHybridData = initHybrid();
|
||||
}
|
||||
|
||||
private native void invokeEvent(@NonNull String eventName, @NonNull NativeMap params);
|
||||
private native void invokeEvent(
|
||||
@NonNull String eventName, @NonNull NativeMap params, @EventCategoryDef int category);
|
||||
|
||||
private native void invokeUniqueEvent(
|
||||
@NonNull String eventName, @NonNull NativeMap params, int customCoalesceKey);
|
||||
|
@ -47,12 +48,15 @@ public class EventEmitterWrapper {
|
|||
* @param eventName {@link String} name of the event to execute.
|
||||
* @param params {@link WritableMap} payload of the event
|
||||
*/
|
||||
public synchronized void invoke(@NonNull String eventName, @Nullable WritableMap params) {
|
||||
public synchronized void invoke(
|
||||
@NonNull String eventName,
|
||||
@Nullable WritableMap params,
|
||||
@EventCategoryDef int eventCategory) {
|
||||
if (!isValid()) {
|
||||
return;
|
||||
}
|
||||
NativeMap payload = params == null ? new WritableNativeMap() : (NativeMap) params;
|
||||
invokeEvent(eventName, payload);
|
||||
invokeEvent(eventName, payload, eventCategory);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -10,8 +10,6 @@ package com.facebook.react.fabric.events;
|
|||
import static com.facebook.react.uimanager.events.TouchesHelper.CHANGED_TOUCHES_KEY;
|
||||
import static com.facebook.react.uimanager.events.TouchesHelper.TARGET_KEY;
|
||||
import static com.facebook.react.uimanager.events.TouchesHelper.TARGET_SURFACE_KEY;
|
||||
import static com.facebook.react.uimanager.events.TouchesHelper.TOP_TOUCH_CANCEL_KEY;
|
||||
import static com.facebook.react.uimanager.events.TouchesHelper.TOP_TOUCH_END_KEY;
|
||||
import static com.facebook.react.uimanager.events.TouchesHelper.TOUCHES_KEY;
|
||||
|
||||
import android.util.Pair;
|
||||
|
@ -25,6 +23,7 @@ import com.facebook.react.bridge.WritableNativeArray;
|
|||
import com.facebook.react.bridge.WritableNativeMap;
|
||||
import com.facebook.react.fabric.FabricUIManager;
|
||||
import com.facebook.react.uimanager.events.RCTModernEventEmitter;
|
||||
import com.facebook.react.uimanager.events.TouchEventType;
|
||||
import com.facebook.systrace.Systrace;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
@ -71,15 +70,23 @@ public class FabricEventEmitter implements RCTModernEventEmitter {
|
|||
@NonNull String eventTopLevelType,
|
||||
@NonNull WritableArray touches,
|
||||
@NonNull WritableArray changedIndices) {
|
||||
Systrace.beginSection(
|
||||
Systrace.TRACE_TAG_REACT_JAVA_BRIDGE,
|
||||
"FabricEventEmitter.receiveTouches('" + eventTopLevelType + "')");
|
||||
|
||||
boolean isPointerEndEvent =
|
||||
TouchEventType.END.getJsName().equalsIgnoreCase(eventTopLevelType)
|
||||
|| TouchEventType.CANCEL.getJsName().equalsIgnoreCase(eventTopLevelType);
|
||||
|
||||
Pair<WritableArray, WritableArray> result =
|
||||
TOP_TOUCH_END_KEY.equalsIgnoreCase(eventTopLevelType)
|
||||
|| TOP_TOUCH_CANCEL_KEY.equalsIgnoreCase(eventTopLevelType)
|
||||
isPointerEndEvent
|
||||
? removeTouchesAtIndices(touches, changedIndices)
|
||||
: touchSubsequence(touches, changedIndices);
|
||||
|
||||
WritableArray changedTouches = result.first;
|
||||
touches = result.second;
|
||||
|
||||
int eventCategory = getTouchCategory(eventTopLevelType);
|
||||
for (int jj = 0; jj < changedTouches.size(); jj++) {
|
||||
WritableMap touch = getWritableMap(changedTouches.getMap(jj));
|
||||
// Touch objects can fulfill the role of `DOM` `Event` objects if we set
|
||||
|
@ -97,8 +104,11 @@ public class FabricEventEmitter implements RCTModernEventEmitter {
|
|||
rootNodeID = targetReactTag;
|
||||
}
|
||||
|
||||
receiveEvent(targetSurfaceId, rootNodeID, eventTopLevelType, false, 0, touch);
|
||||
mUIManager.receiveEvent(
|
||||
targetSurfaceId, rootNodeID, eventTopLevelType, false, 0, touch, eventCategory);
|
||||
}
|
||||
|
||||
Systrace.endSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE);
|
||||
}
|
||||
|
||||
/** TODO T31905686 optimize this to avoid copying arrays */
|
||||
|
@ -174,4 +184,19 @@ public class FabricEventEmitter implements RCTModernEventEmitter {
|
|||
map.merge(readableMap);
|
||||
return map;
|
||||
}
|
||||
|
||||
@EventCategoryDef
|
||||
private static int getTouchCategory(String touchEventType) {
|
||||
int category = EventCategoryDef.UNSPECIFIED;
|
||||
if (TouchEventType.MOVE.getJsName().equals(touchEventType)) {
|
||||
category = EventCategoryDef.CONTINUOUS;
|
||||
} else if (TouchEventType.START.getJsName().equals(touchEventType)) {
|
||||
category = EventCategoryDef.CONTINUOUS_START;
|
||||
} else if (TouchEventType.END.getJsName().equals(touchEventType)
|
||||
|| TouchEventType.CANCEL.getJsName().equals(touchEventType)) {
|
||||
category = EventCategoryDef.CONTINUOUS_END;
|
||||
}
|
||||
|
||||
return category;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -508,6 +508,9 @@ void Binding::installFabricUIManager(
|
|||
enableFabricLogs_ =
|
||||
config->getBool("react_fabric:enabled_android_fabric_logs");
|
||||
|
||||
disableRevisionCheckForPreallocation_ =
|
||||
config->getBool("react_fabric:disable_revision_check_for_preallocation");
|
||||
|
||||
if (enableFabricLogs_) {
|
||||
LOG(WARNING) << "Binding::installFabricUIManager() was called (address: "
|
||||
<< this << ").";
|
||||
|
@ -1242,11 +1245,13 @@ void Binding::schedulerDidCloneShadowNode(
|
|||
// 1. The revision is exactly 1
|
||||
// 2. At revision 0 (the old node), View Preallocation would have been skipped
|
||||
|
||||
if (newShadowNode.getProps()->revision != 1) {
|
||||
return;
|
||||
}
|
||||
if (oldShadowNode.getProps()->revision != 0) {
|
||||
return;
|
||||
if (!disableRevisionCheckForPreallocation_) {
|
||||
if (newShadowNode.getProps()->revision != 1) {
|
||||
return;
|
||||
}
|
||||
if (oldShadowNode.getProps()->revision != 0) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// If the new node is concrete and the old wasn't, we can preallocate
|
||||
|
|
|
@ -201,6 +201,7 @@ class Binding : public jni::HybridClass<Binding>,
|
|||
bool disablePreallocateViews_{false};
|
||||
bool enableFabricLogs_{false};
|
||||
bool enableEarlyEventEmitterUpdate_{false};
|
||||
bool disableRevisionCheckForPreallocation_{false};
|
||||
};
|
||||
|
||||
} // namespace react
|
||||
|
|
|
@ -20,13 +20,17 @@ EventEmitterWrapper::initHybrid(jni::alias_ref<jclass>) {
|
|||
|
||||
void EventEmitterWrapper::invokeEvent(
|
||||
std::string eventName,
|
||||
NativeMap *payload) {
|
||||
NativeMap *payload,
|
||||
int category) {
|
||||
// It is marginal, but possible for this to be constructed without a valid
|
||||
// EventEmitter. In those cases, make sure we noop/blackhole events instead of
|
||||
// crashing.
|
||||
if (eventEmitter != nullptr) {
|
||||
eventEmitter->dispatchEvent(
|
||||
eventName, payload->consume(), EventPriority::AsynchronousBatched);
|
||||
eventName,
|
||||
payload->consume(),
|
||||
EventPriority::AsynchronousBatched,
|
||||
static_cast<RawEvent::Category>(category));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -25,7 +25,7 @@ class EventEmitterWrapper : public jni::HybridClass<EventEmitterWrapper> {
|
|||
|
||||
SharedEventEmitter eventEmitter;
|
||||
|
||||
void invokeEvent(std::string eventName, NativeMap *params);
|
||||
void invokeEvent(std::string eventName, NativeMap *params, int category);
|
||||
void invokeUniqueEvent(
|
||||
std::string eventName,
|
||||
NativeMap *params,
|
||||
|
|
|
@ -910,10 +910,6 @@ public class SurfaceMountingManager {
|
|||
// We treat this as a perf problem and not a logical error. View Preallocation or unexpected
|
||||
// changes to Differ or C++ Binding could cause some redundant Create instructions.
|
||||
if (getNullableViewState(reactTag) != null) {
|
||||
ReactSoftExceptionLogger.logSoftException(
|
||||
TAG,
|
||||
new IllegalStateException(
|
||||
"Cannot Preallocate view with tag [" + reactTag + "], already exists."));
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
load("//tools/build_defs/oss:rn_defs.bzl", "react_native_dep", "react_native_target", "rn_android_library")
|
||||
load("//tools/build_defs/oss:rn_defs.bzl", "react_native_dep", "react_native_root_target", "react_native_target", "rn_android_library")
|
||||
|
||||
rn_android_library(
|
||||
name = "clipboard",
|
||||
|
@ -16,4 +16,7 @@ rn_android_library(
|
|||
react_native_target("java/com/facebook/react/common:common"),
|
||||
react_native_target("java/com/facebook/react/module/annotations:annotations"),
|
||||
],
|
||||
exported_deps = [
|
||||
react_native_root_target(":FBReactNativeSpec"),
|
||||
],
|
||||
)
|
||||
|
|
|
@ -9,17 +9,16 @@ package com.facebook.react.modules.clipboard;
|
|||
|
||||
import android.content.ClipData;
|
||||
import android.content.ClipboardManager;
|
||||
import android.content.Context;
|
||||
import com.facebook.react.bridge.ContextBaseJavaModule;
|
||||
import com.facebook.fbreact.specs.NativeClipboardSpec;
|
||||
import com.facebook.react.bridge.Promise;
|
||||
import com.facebook.react.bridge.ReactMethod;
|
||||
import com.facebook.react.bridge.ReactApplicationContext;
|
||||
import com.facebook.react.module.annotations.ReactModule;
|
||||
|
||||
/** A module that allows JS to get/set clipboard contents. */
|
||||
@ReactModule(name = ClipboardModule.NAME)
|
||||
public class ClipboardModule extends ContextBaseJavaModule {
|
||||
public class ClipboardModule extends NativeClipboardSpec {
|
||||
|
||||
public ClipboardModule(Context context) {
|
||||
public ClipboardModule(ReactApplicationContext context) {
|
||||
super(context);
|
||||
}
|
||||
|
||||
|
@ -31,10 +30,12 @@ public class ClipboardModule extends ContextBaseJavaModule {
|
|||
}
|
||||
|
||||
private ClipboardManager getClipboardService() {
|
||||
return (ClipboardManager) getContext().getSystemService(getContext().CLIPBOARD_SERVICE);
|
||||
return (ClipboardManager)
|
||||
getReactApplicationContext()
|
||||
.getSystemService(getReactApplicationContext().CLIPBOARD_SERVICE);
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
@Override
|
||||
public void getString(Promise promise) {
|
||||
try {
|
||||
ClipboardManager clipboard = getClipboardService();
|
||||
|
@ -50,7 +51,7 @@ public class ClipboardModule extends ContextBaseJavaModule {
|
|||
}
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
@Override
|
||||
public void setString(String text) {
|
||||
ClipData clipdata = ClipData.newPlainText(null, text);
|
||||
ClipboardManager clipboard = getClipboardService();
|
||||
|
|
|
@ -155,7 +155,7 @@ public class JSTouchDispatcher {
|
|||
mTargetCoordinates[1],
|
||||
mTouchEventCoalescingKeyHelper));
|
||||
} else if (action == MotionEvent.ACTION_POINTER_UP) {
|
||||
// Exactly onw of the pointers goes up
|
||||
// Exactly one of the pointers goes up
|
||||
eventDispatcher.dispatchEvent(
|
||||
TouchEvent.obtain(
|
||||
getSurfaceId(),
|
||||
|
|
|
@ -188,7 +188,6 @@ public abstract class Event<T extends Event> {
|
|||
* old-style dispatch function. For Event classes that need to do something different, this method
|
||||
* can always be overridden entirely, but it is not recommended.
|
||||
*/
|
||||
@Deprecated
|
||||
public void dispatchModern(RCTModernEventEmitter rctEventEmitter) {
|
||||
if (getSurfaceId() != -1) {
|
||||
WritableMap eventData = getEventData();
|
||||
|
|
|
@ -13,6 +13,7 @@ import com.facebook.react.bridge.LifecycleEventListener;
|
|||
import com.facebook.react.bridge.ReactApplicationContext;
|
||||
import com.facebook.react.bridge.UiThreadUtil;
|
||||
import com.facebook.react.common.MapBuilder;
|
||||
import com.facebook.react.config.ReactFeatureFlags;
|
||||
import com.facebook.react.modules.core.ChoreographerCompat;
|
||||
import com.facebook.react.modules.core.ReactChoreographer;
|
||||
import com.facebook.react.uimanager.common.UIManagerType;
|
||||
|
@ -366,7 +367,12 @@ public class EventDispatcherImpl implements EventDispatcher, LifecycleEventListe
|
|||
}
|
||||
Systrace.endAsyncFlow(
|
||||
Systrace.TRACE_TAG_REACT_JAVA_BRIDGE, event.getEventName(), event.getUniqueID());
|
||||
event.dispatchModern(mReactEventEmitter);
|
||||
|
||||
if (ReactFeatureFlags.useDispatchUniqueForCoalescableEvents) {
|
||||
event.dispatchModernV2(mReactEventEmitter);
|
||||
} else {
|
||||
event.dispatchModern(mReactEventEmitter);
|
||||
}
|
||||
event.dispose();
|
||||
}
|
||||
clearEventsToDispatch();
|
||||
|
|
|
@ -27,5 +27,12 @@ public interface RCTEventEmitter extends JavaScriptModule {
|
|||
@Deprecated
|
||||
void receiveEvent(int targetTag, String eventName, @Nullable WritableMap event);
|
||||
|
||||
/**
|
||||
* Receive and process touches
|
||||
*
|
||||
* @param eventName JS event name
|
||||
* @param touches active pointers data
|
||||
* @param changedIndices indices of changed pointers
|
||||
*/
|
||||
void receiveTouches(String eventName, WritableArray touches, WritableArray changedIndices);
|
||||
}
|
||||
|
|
|
@ -9,23 +9,22 @@ package com.facebook.react.uimanager.events;
|
|||
|
||||
/** Touch event types that JS module RCTEventEmitter can understand */
|
||||
public enum TouchEventType {
|
||||
START,
|
||||
END,
|
||||
MOVE,
|
||||
CANCEL;
|
||||
START("topTouchStart"),
|
||||
END("topTouchEnd"),
|
||||
MOVE("topTouchMove"),
|
||||
CANCEL("topTouchCancel");
|
||||
|
||||
private final String mJsName;
|
||||
|
||||
TouchEventType(String jsName) {
|
||||
mJsName = jsName;
|
||||
}
|
||||
|
||||
public String getJsName() {
|
||||
return mJsName;
|
||||
}
|
||||
|
||||
public static String getJSEventName(TouchEventType type) {
|
||||
switch (type) {
|
||||
case START:
|
||||
return "topTouchStart";
|
||||
case END:
|
||||
return "topTouchEnd";
|
||||
case MOVE:
|
||||
return "topTouchMove";
|
||||
case CANCEL:
|
||||
return "topTouchCancel";
|
||||
default:
|
||||
throw new IllegalArgumentException("Unexpected type " + type);
|
||||
}
|
||||
return type.getJsName();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,8 +20,6 @@ public class TouchesHelper {
|
|||
public static final String TARGET_KEY = "target";
|
||||
public static final String CHANGED_TOUCHES_KEY = "changedTouches";
|
||||
public static final String TOUCHES_KEY = "touches";
|
||||
public static final String TOP_TOUCH_END_KEY = "topTouchEnd";
|
||||
public static final String TOP_TOUCH_CANCEL_KEY = "topTouchCancel";
|
||||
private static final String PAGE_X_KEY = "pageX";
|
||||
private static final String PAGE_Y_KEY = "pageY";
|
||||
private static final String TIMESTAMP_KEY = "timestamp";
|
||||
|
|
|
@ -1,57 +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.
|
||||
*
|
||||
* @generated by codegen project: GeneratePropsJavaDelegate.js
|
||||
*/
|
||||
|
||||
package com.facebook.react.viewmanagers;
|
||||
|
||||
import android.view.View;
|
||||
import androidx.annotation.Nullable;
|
||||
import com.facebook.react.bridge.ReadableArray;
|
||||
import com.facebook.react.uimanager.BaseViewManagerDelegate;
|
||||
import com.facebook.react.uimanager.BaseViewManagerInterface;
|
||||
import com.facebook.react.uimanager.LayoutShadowNode;
|
||||
|
||||
public class AndroidViewPagerManagerDelegate<T extends View, U extends BaseViewManagerInterface<T> & AndroidViewPagerManagerInterface<T>> extends BaseViewManagerDelegate<T, U> {
|
||||
public AndroidViewPagerManagerDelegate(U viewManager) {
|
||||
super(viewManager);
|
||||
}
|
||||
@Override
|
||||
public void setProperty(T view, String propName, @Nullable Object value) {
|
||||
switch (propName) {
|
||||
case "initialPage":
|
||||
mViewManager.setInitialPage(view, value == null ? 0 : ((Double) value).intValue());
|
||||
break;
|
||||
case "pageMargin":
|
||||
mViewManager.setPageMargin(view, value == null ? 0 : ((Double) value).intValue());
|
||||
break;
|
||||
case "peekEnabled":
|
||||
mViewManager.setPeekEnabled(view, value == null ? false : (boolean) value);
|
||||
break;
|
||||
case "keyboardDismissMode":
|
||||
mViewManager.setKeyboardDismissMode(view, (String) value);
|
||||
break;
|
||||
case "scrollEnabled":
|
||||
mViewManager.setScrollEnabled(view, value == null ? true : (boolean) value);
|
||||
break;
|
||||
default:
|
||||
super.setProperty(view, propName, value);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void receiveCommand(T view, String commandName, ReadableArray args) {
|
||||
switch (commandName) {
|
||||
case "setPage":
|
||||
mViewManager.setPage(view, args.getInt(0));
|
||||
break;
|
||||
case "setPageWithoutAnimation":
|
||||
mViewManager.setPageWithoutAnimation(view, args.getInt(0));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,23 +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.
|
||||
*
|
||||
* @generated by codegen project: GeneratePropsJavaInterface.js
|
||||
*/
|
||||
|
||||
package com.facebook.react.viewmanagers;
|
||||
|
||||
import android.view.View;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
public interface AndroidViewPagerManagerInterface<T extends View> {
|
||||
void setInitialPage(T view, int value);
|
||||
void setPageMargin(T view, int value);
|
||||
void setPeekEnabled(T view, boolean value);
|
||||
void setKeyboardDismissMode(T view, @Nullable String value);
|
||||
void setScrollEnabled(T view, boolean value);
|
||||
void setPage(T view, int page);
|
||||
void setPageWithoutAnimation(T view, int page);
|
||||
}
|
|
@ -1,19 +0,0 @@
|
|||
load("//tools/build_defs/oss:rn_defs.bzl", "react_native_dep", "react_native_target", "rn_android_library")
|
||||
|
||||
rn_android_library(
|
||||
name = "viewmanagers",
|
||||
srcs = glob(["*.java"]),
|
||||
autoglob = False,
|
||||
is_androidx = True,
|
||||
labels = ["supermodule:xplat/default/public.react_native.infra"],
|
||||
provided_deps = [
|
||||
react_native_dep("third-party/android/androidx:annotation"),
|
||||
],
|
||||
visibility = [
|
||||
"PUBLIC",
|
||||
],
|
||||
deps = [
|
||||
react_native_target("java/com/facebook/react/bridge:bridge"),
|
||||
react_native_target("java/com/facebook/react/uimanager:uimanager"),
|
||||
],
|
||||
)
|
|
@ -1,62 +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.
|
||||
*
|
||||
* @generated by codegen project: GeneratePropsJavaDelegate.js
|
||||
*/
|
||||
|
||||
package com.facebook.react.viewmanagers;
|
||||
|
||||
import android.view.View;
|
||||
import androidx.annotation.Nullable;
|
||||
import com.facebook.react.bridge.ReadableArray;
|
||||
import com.facebook.react.uimanager.BaseViewManagerDelegate;
|
||||
import com.facebook.react.uimanager.BaseViewManagerInterface;
|
||||
import com.facebook.react.uimanager.LayoutShadowNode;
|
||||
|
||||
public class DatePickerManagerDelegate<T extends View, U extends BaseViewManagerInterface<T> & DatePickerManagerInterface<T>> extends BaseViewManagerDelegate<T, U> {
|
||||
public DatePickerManagerDelegate(U viewManager) {
|
||||
super(viewManager);
|
||||
}
|
||||
@Override
|
||||
public void setProperty(T view, String propName, @Nullable Object value) {
|
||||
switch (propName) {
|
||||
case "date":
|
||||
mViewManager.setDate(view, value == null ? 0f : ((Double) value).floatValue());
|
||||
break;
|
||||
case "initialDate":
|
||||
mViewManager.setInitialDate(view, value == null ? 0f : ((Double) value).floatValue());
|
||||
break;
|
||||
case "locale":
|
||||
mViewManager.setLocale(view, value == null ? null : (String) value);
|
||||
break;
|
||||
case "maximumDate":
|
||||
mViewManager.setMaximumDate(view, value == null ? 0f : ((Double) value).floatValue());
|
||||
break;
|
||||
case "minimumDate":
|
||||
mViewManager.setMinimumDate(view, value == null ? 0f : ((Double) value).floatValue());
|
||||
break;
|
||||
case "minuteInterval":
|
||||
mViewManager.setMinuteInterval(view, value == null ? 1 : ((Double) value).intValue());
|
||||
break;
|
||||
case "mode":
|
||||
mViewManager.setMode(view, (String) value);
|
||||
break;
|
||||
case "timeZoneOffsetInMinutes":
|
||||
mViewManager.setTimeZoneOffsetInMinutes(view, value == null ? 0f : ((Double) value).floatValue());
|
||||
break;
|
||||
default:
|
||||
super.setProperty(view, propName, value);
|
||||
}
|
||||
}
|
||||
|
||||
public void receiveCommand(DatePickerManagerInterface<T> viewManager, T view, String commandName, ReadableArray args) {
|
||||
switch (commandName) {
|
||||
case "setNativeDate":
|
||||
viewManager.setNativeDate(view, (float) args.getDouble(0));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,25 +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.
|
||||
*
|
||||
* @generated by codegen project: GeneratePropsJavaInterface.js
|
||||
*/
|
||||
|
||||
package com.facebook.react.viewmanagers;
|
||||
|
||||
import android.view.View;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
public interface DatePickerManagerInterface<T extends View> {
|
||||
void setDate(T view, float value);
|
||||
void setInitialDate(T view, float value);
|
||||
void setLocale(T view, @Nullable String value);
|
||||
void setMaximumDate(T view, float value);
|
||||
void setMinimumDate(T view, float value);
|
||||
void setMinuteInterval(T view, @Nullable Integer value);
|
||||
void setMode(T view, @Nullable String value);
|
||||
void setTimeZoneOffsetInMinutes(T view, float value);
|
||||
void setNativeDate(T view, float date);
|
||||
}
|
|
@ -1,33 +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.
|
||||
*
|
||||
* @generated by codegen project: GeneratePropsJavaDelegate.js
|
||||
*/
|
||||
|
||||
package com.facebook.react.viewmanagers;
|
||||
|
||||
import android.view.View;
|
||||
import androidx.annotation.Nullable;
|
||||
import com.facebook.react.bridge.ColorPropConverter;
|
||||
import com.facebook.react.uimanager.BaseViewManagerDelegate;
|
||||
import com.facebook.react.uimanager.BaseViewManagerInterface;
|
||||
import com.facebook.react.uimanager.LayoutShadowNode;
|
||||
|
||||
public class InputAccessoryViewManagerDelegate<T extends View, U extends BaseViewManagerInterface<T> & InputAccessoryViewManagerInterface<T>> extends BaseViewManagerDelegate<T, U> {
|
||||
public InputAccessoryViewManagerDelegate(U viewManager) {
|
||||
super(viewManager);
|
||||
}
|
||||
@Override
|
||||
public void setProperty(T view, String propName, @Nullable Object value) {
|
||||
switch (propName) {
|
||||
case "backgroundColor":
|
||||
mViewManager.setBackgroundColor(view, ColorPropConverter.getColor(value, view.getContext()));
|
||||
break;
|
||||
default:
|
||||
super.setProperty(view, propName, value);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,17 +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.
|
||||
*
|
||||
* @generated by codegen project: GeneratePropsJavaInterface.js
|
||||
*/
|
||||
|
||||
package com.facebook.react.viewmanagers;
|
||||
|
||||
import android.view.View;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
public interface InputAccessoryViewManagerInterface<T extends View> {
|
||||
void setBackgroundColor(T view, @Nullable Integer value);
|
||||
}
|
|
@ -1,51 +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.
|
||||
*
|
||||
* @generated by codegen project: GeneratePropsJavaDelegate.js
|
||||
*/
|
||||
|
||||
package com.facebook.react.viewmanagers;
|
||||
|
||||
import android.view.View;
|
||||
import androidx.annotation.Nullable;
|
||||
import com.facebook.react.bridge.ColorPropConverter;
|
||||
import com.facebook.react.bridge.ReadableArray;
|
||||
import com.facebook.react.uimanager.BaseViewManagerDelegate;
|
||||
import com.facebook.react.uimanager.BaseViewManagerInterface;
|
||||
import com.facebook.react.uimanager.LayoutShadowNode;
|
||||
|
||||
public class PullToRefreshViewManagerDelegate<T extends View, U extends BaseViewManagerInterface<T> & PullToRefreshViewManagerInterface<T>> extends BaseViewManagerDelegate<T, U> {
|
||||
public PullToRefreshViewManagerDelegate(U viewManager) {
|
||||
super(viewManager);
|
||||
}
|
||||
@Override
|
||||
public void setProperty(T view, String propName, @Nullable Object value) {
|
||||
switch (propName) {
|
||||
case "tintColor":
|
||||
mViewManager.setTintColor(view, ColorPropConverter.getColor(value, view.getContext()));
|
||||
break;
|
||||
case "titleColor":
|
||||
mViewManager.setTitleColor(view, ColorPropConverter.getColor(value, view.getContext()));
|
||||
break;
|
||||
case "title":
|
||||
mViewManager.setTitle(view, value == null ? null : (String) value);
|
||||
break;
|
||||
case "refreshing":
|
||||
mViewManager.setRefreshing(view, value == null ? false : (boolean) value);
|
||||
break;
|
||||
default:
|
||||
super.setProperty(view, propName, value);
|
||||
}
|
||||
}
|
||||
|
||||
public void receiveCommand(PullToRefreshViewManagerInterface<T> viewManager, T view, String commandName, ReadableArray args) {
|
||||
switch (commandName) {
|
||||
case "setNativeRefreshing":
|
||||
viewManager.setNativeRefreshing(view, args.getBoolean(0));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,21 +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.
|
||||
*
|
||||
* @generated by codegen project: GeneratePropsJavaInterface.js
|
||||
*/
|
||||
|
||||
package com.facebook.react.viewmanagers;
|
||||
|
||||
import android.view.View;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
public interface PullToRefreshViewManagerInterface<T extends View> {
|
||||
void setTintColor(T view, @Nullable Integer value);
|
||||
void setTitleColor(T view, @Nullable Integer value);
|
||||
void setTitle(T view, @Nullable String value);
|
||||
void setRefreshing(T view, boolean value);
|
||||
void setNativeRefreshing(T view, boolean refreshing);
|
||||
}
|
|
@ -1,63 +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.
|
||||
*
|
||||
* @generated by codegen project: GeneratePropsJavaDelegate.js
|
||||
*/
|
||||
|
||||
package com.facebook.react.viewmanagers;
|
||||
|
||||
import android.view.View;
|
||||
import androidx.annotation.Nullable;
|
||||
import com.facebook.react.bridge.ColorPropConverter;
|
||||
import com.facebook.react.bridge.ReadableArray;
|
||||
import com.facebook.react.uimanager.BaseViewManagerDelegate;
|
||||
import com.facebook.react.uimanager.BaseViewManagerInterface;
|
||||
|
||||
public class SwitchManagerDelegate<T extends View, U extends BaseViewManagerInterface<T> & SwitchManagerInterface<T>> extends BaseViewManagerDelegate<T, U> {
|
||||
public SwitchManagerDelegate(U viewManager) {
|
||||
super(viewManager);
|
||||
}
|
||||
@Override
|
||||
public void setProperty(T view, String propName, @Nullable Object value) {
|
||||
switch (propName) {
|
||||
case "disabled":
|
||||
mViewManager.setDisabled(view, value == null ? false : (boolean) value);
|
||||
break;
|
||||
case "value":
|
||||
mViewManager.setValue(view, value == null ? false : (boolean) value);
|
||||
break;
|
||||
case "tintColor":
|
||||
mViewManager.setTintColor(view, ColorPropConverter.getColor(value, view.getContext()));
|
||||
break;
|
||||
case "onTintColor":
|
||||
mViewManager.setOnTintColor(view, ColorPropConverter.getColor(value, view.getContext()));
|
||||
break;
|
||||
case "thumbTintColor":
|
||||
mViewManager.setThumbTintColor(view, ColorPropConverter.getColor(value, view.getContext()));
|
||||
break;
|
||||
case "thumbColor":
|
||||
mViewManager.setThumbColor(view, ColorPropConverter.getColor(value, view.getContext()));
|
||||
break;
|
||||
case "trackColorForFalse":
|
||||
mViewManager.setTrackColorForFalse(view, ColorPropConverter.getColor(value, view.getContext()));
|
||||
break;
|
||||
case "trackColorForTrue":
|
||||
mViewManager.setTrackColorForTrue(view, ColorPropConverter.getColor(value, view.getContext()));
|
||||
break;
|
||||
default:
|
||||
super.setProperty(view, propName, value);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void receiveCommand(T view, String commandName, ReadableArray args) {
|
||||
switch (commandName) {
|
||||
case "setValue":
|
||||
mViewManager.setValue(view, args.getBoolean(0));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,24 +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.
|
||||
*
|
||||
* @generated by codegen project: GeneratePropsJavaInterface.js
|
||||
*/
|
||||
|
||||
package com.facebook.react.viewmanagers;
|
||||
|
||||
import android.view.View;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
public interface SwitchManagerInterface<T extends View> {
|
||||
void setDisabled(T view, boolean value);
|
||||
void setValue(T view, boolean value);
|
||||
void setTintColor(T view, @Nullable Integer value);
|
||||
void setOnTintColor(T view, @Nullable Integer value);
|
||||
void setThumbTintColor(T view, @Nullable Integer value);
|
||||
void setThumbColor(T view, @Nullable Integer value);
|
||||
void setTrackColorForFalse(T view, @Nullable Integer value);
|
||||
void setTrackColorForTrue(T view, @Nullable Integer value);
|
||||
}
|
|
@ -7,6 +7,12 @@
|
|||
|
||||
package com.facebook.react.views.scroll;
|
||||
|
||||
import static com.facebook.react.config.ReactFeatureFlags.enableScrollViewSnapToAlignmentProp;
|
||||
import static com.facebook.react.views.scroll.ReactScrollViewHelper.SNAP_ALIGNMENT_CENTER;
|
||||
import static com.facebook.react.views.scroll.ReactScrollViewHelper.SNAP_ALIGNMENT_DISABLED;
|
||||
import static com.facebook.react.views.scroll.ReactScrollViewHelper.SNAP_ALIGNMENT_END;
|
||||
import static com.facebook.react.views.scroll.ReactScrollViewHelper.SNAP_ALIGNMENT_START;
|
||||
|
||||
import android.animation.Animator;
|
||||
import android.animation.ObjectAnimator;
|
||||
import android.animation.PropertyValuesHolder;
|
||||
|
@ -21,6 +27,7 @@ import android.view.FocusFinder;
|
|||
import android.view.KeyEvent;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.accessibility.AccessibilityEvent;
|
||||
import android.widget.HorizontalScrollView;
|
||||
import android.widget.OverScroller;
|
||||
|
@ -94,6 +101,7 @@ public class ReactHorizontalScrollView extends HorizontalScrollView
|
|||
private @Nullable List<Integer> mSnapOffsets;
|
||||
private boolean mSnapToStart = true;
|
||||
private boolean mSnapToEnd = true;
|
||||
private int mSnapToAlignment = SNAP_ALIGNMENT_DISABLED;
|
||||
private ReactViewBackgroundManager mReactBackgroundManager;
|
||||
private boolean mPagedArrowScrolling = false;
|
||||
private int pendingContentOffsetX = UNSET_CONTENT_OFFSET;
|
||||
|
@ -239,6 +247,10 @@ public class ReactHorizontalScrollView extends HorizontalScrollView
|
|||
mSnapToEnd = snapToEnd;
|
||||
}
|
||||
|
||||
public void setSnapToAlignment(int snapToAlignment) {
|
||||
mSnapToAlignment = snapToAlignment;
|
||||
}
|
||||
|
||||
public void flashScrollIndicators() {
|
||||
awakenScrollBars();
|
||||
}
|
||||
|
@ -905,7 +917,9 @@ public class ReactHorizontalScrollView extends HorizontalScrollView
|
|||
}
|
||||
|
||||
// pagingEnabled only allows snapping one interval at a time
|
||||
if (mSnapInterval == 0 && mSnapOffsets == null) {
|
||||
if (mSnapInterval == 0
|
||||
&& mSnapOffsets == null
|
||||
&& (!enableScrollViewSnapToAlignmentProp || mSnapToAlignment == SNAP_ALIGNMENT_DISABLED)) {
|
||||
smoothScrollAndSnap(velocityX);
|
||||
return;
|
||||
}
|
||||
|
@ -948,8 +962,46 @@ public class ReactHorizontalScrollView extends HorizontalScrollView
|
|||
}
|
||||
}
|
||||
}
|
||||
} else if (enableScrollViewSnapToAlignmentProp && mSnapToAlignment != SNAP_ALIGNMENT_DISABLED) {
|
||||
if (mSnapInterval > 0) {
|
||||
double ratio = (double) targetOffset / mSnapInterval;
|
||||
smallerOffset =
|
||||
Math.max(
|
||||
getItemStartOffset(
|
||||
mSnapToAlignment,
|
||||
(int) (Math.floor(ratio) * mSnapInterval),
|
||||
mSnapInterval,
|
||||
width),
|
||||
0);
|
||||
largerOffset =
|
||||
Math.min(
|
||||
getItemStartOffset(
|
||||
mSnapToAlignment,
|
||||
(int) (Math.ceil(ratio) * mSnapInterval),
|
||||
mSnapInterval,
|
||||
width),
|
||||
maximumOffset);
|
||||
} else {
|
||||
ViewGroup contentView = (ViewGroup) getContentView();
|
||||
for (int i = 1; i < contentView.getChildCount(); i++) {
|
||||
View item = contentView.getChildAt(i);
|
||||
int itemStartOffset =
|
||||
getItemStartOffset(mSnapToAlignment, item.getLeft(), item.getWidth(), width);
|
||||
if (itemStartOffset <= targetOffset) {
|
||||
if (targetOffset - itemStartOffset < targetOffset - smallerOffset) {
|
||||
smallerOffset = itemStartOffset;
|
||||
}
|
||||
}
|
||||
|
||||
if (itemStartOffset >= targetOffset) {
|
||||
if (itemStartOffset - targetOffset < largerOffset - targetOffset) {
|
||||
largerOffset = itemStartOffset;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
double interval = (double) getSnapInterval();
|
||||
double interval = getSnapInterval();
|
||||
double ratio = (double) targetOffset / interval;
|
||||
smallerOffset = (int) (Math.floor(ratio) * interval);
|
||||
largerOffset = Math.min((int) (Math.ceil(ratio) * interval), maximumOffset);
|
||||
|
@ -1032,6 +1084,25 @@ public class ReactHorizontalScrollView extends HorizontalScrollView
|
|||
}
|
||||
}
|
||||
|
||||
private int getItemStartOffset(
|
||||
int snapToAlignment, int itemStartPosition, int itemWidth, int viewPortWidth) {
|
||||
int itemStartOffset;
|
||||
switch (snapToAlignment) {
|
||||
case SNAP_ALIGNMENT_CENTER:
|
||||
itemStartOffset = itemStartPosition - (viewPortWidth - itemWidth) / 2;
|
||||
break;
|
||||
case SNAP_ALIGNMENT_START:
|
||||
itemStartOffset = itemStartPosition;
|
||||
break;
|
||||
case SNAP_ALIGNMENT_END:
|
||||
itemStartOffset = itemStartPosition - (viewPortWidth - itemWidth);
|
||||
break;
|
||||
default:
|
||||
throw new IllegalStateException("Invalid SnapToAlignment value: " + mSnapToAlignment);
|
||||
}
|
||||
return itemStartOffset;
|
||||
}
|
||||
|
||||
private void smoothScrollToNextPage(int direction) {
|
||||
if (DEBUG_MODE) {
|
||||
FLog.i(TAG, "smoothScrollToNextPage[%d] direction %d", getId(), direction);
|
||||
|
|
|
@ -102,6 +102,11 @@ public class ReactHorizontalScrollViewManager extends ViewGroupManager<ReactHori
|
|||
view.setSnapInterval((int) (snapToInterval * screenDisplayMetrics.density));
|
||||
}
|
||||
|
||||
@ReactProp(name = "snapToAlignment")
|
||||
public void setSnapToAlignment(ReactHorizontalScrollView view, String alignment) {
|
||||
view.setSnapToAlignment(ReactScrollViewHelper.parseSnapToAlignment(alignment));
|
||||
}
|
||||
|
||||
@ReactProp(name = "snapToOffsets")
|
||||
public void setSnapToOffsets(
|
||||
ReactHorizontalScrollView view, @Nullable ReadableArray snapToOffsets) {
|
||||
|
|
|
@ -7,6 +7,12 @@
|
|||
|
||||
package com.facebook.react.views.scroll;
|
||||
|
||||
import static com.facebook.react.config.ReactFeatureFlags.enableScrollViewSnapToAlignmentProp;
|
||||
import static com.facebook.react.views.scroll.ReactScrollViewHelper.SNAP_ALIGNMENT_CENTER;
|
||||
import static com.facebook.react.views.scroll.ReactScrollViewHelper.SNAP_ALIGNMENT_DISABLED;
|
||||
import static com.facebook.react.views.scroll.ReactScrollViewHelper.SNAP_ALIGNMENT_END;
|
||||
import static com.facebook.react.views.scroll.ReactScrollViewHelper.SNAP_ALIGNMENT_START;
|
||||
|
||||
import android.animation.Animator;
|
||||
import android.animation.ObjectAnimator;
|
||||
import android.animation.PropertyValuesHolder;
|
||||
|
@ -92,6 +98,7 @@ public class ReactScrollView extends ScrollView
|
|||
private @Nullable List<Integer> mSnapOffsets;
|
||||
private boolean mSnapToStart = true;
|
||||
private boolean mSnapToEnd = true;
|
||||
private int mSnapToAlignment = SNAP_ALIGNMENT_DISABLED;
|
||||
private @Nullable View mContentView;
|
||||
private ReactViewBackgroundManager mReactBackgroundManager;
|
||||
private int pendingContentOffsetX = UNSET_CONTENT_OFFSET;
|
||||
|
@ -218,6 +225,10 @@ public class ReactScrollView extends ScrollView
|
|||
mSnapToEnd = snapToEnd;
|
||||
}
|
||||
|
||||
public void setSnapToAlignment(int snapToAlignment) {
|
||||
mSnapToAlignment = snapToAlignment;
|
||||
}
|
||||
|
||||
public void flashScrollIndicators() {
|
||||
awakenScrollBars();
|
||||
}
|
||||
|
@ -634,6 +645,10 @@ public class ReactScrollView extends ScrollView
|
|||
return scroller.getFinalY();
|
||||
}
|
||||
|
||||
private View getContentView() {
|
||||
return getChildAt(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* This will smooth scroll us to the nearest snap offset point It currently just looks at where
|
||||
* the content is and slides to the nearest point. It is intended to be run after we are done
|
||||
|
@ -690,7 +705,9 @@ public class ReactScrollView extends ScrollView
|
|||
}
|
||||
|
||||
// pagingEnabled only allows snapping one interval at a time
|
||||
if (mSnapInterval == 0 && mSnapOffsets == null) {
|
||||
if (mSnapInterval == 0
|
||||
&& mSnapOffsets == null
|
||||
&& (!enableScrollViewSnapToAlignmentProp || mSnapToAlignment == SNAP_ALIGNMENT_DISABLED)) {
|
||||
smoothScrollAndSnap(velocityY);
|
||||
return;
|
||||
}
|
||||
|
@ -727,6 +744,57 @@ public class ReactScrollView extends ScrollView
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
} else if (enableScrollViewSnapToAlignmentProp && mSnapToAlignment != SNAP_ALIGNMENT_DISABLED) {
|
||||
if (mSnapInterval > 0) {
|
||||
double ratio = (double) targetOffset / mSnapInterval;
|
||||
smallerOffset =
|
||||
Math.max(
|
||||
getItemStartOffset(
|
||||
mSnapToAlignment,
|
||||
(int) (Math.floor(ratio) * mSnapInterval),
|
||||
mSnapInterval,
|
||||
height),
|
||||
0);
|
||||
largerOffset =
|
||||
Math.min(
|
||||
getItemStartOffset(
|
||||
mSnapToAlignment,
|
||||
(int) (Math.ceil(ratio) * mSnapInterval),
|
||||
mSnapInterval,
|
||||
height),
|
||||
maximumOffset);
|
||||
} else {
|
||||
ViewGroup contentView = (ViewGroup) getContentView();
|
||||
for (int i = 1; i < contentView.getChildCount(); i++) {
|
||||
View item = contentView.getChildAt(i);
|
||||
int itemStartOffset;
|
||||
switch (mSnapToAlignment) {
|
||||
case SNAP_ALIGNMENT_CENTER:
|
||||
itemStartOffset = item.getTop() - (height - item.getHeight()) / 2;
|
||||
break;
|
||||
case SNAP_ALIGNMENT_START:
|
||||
itemStartOffset = item.getTop();
|
||||
break;
|
||||
case SNAP_ALIGNMENT_END:
|
||||
itemStartOffset = item.getTop() - (height - item.getHeight());
|
||||
break;
|
||||
default:
|
||||
throw new IllegalStateException("Invalid SnapToAlignment value: " + mSnapToAlignment);
|
||||
}
|
||||
if (itemStartOffset <= targetOffset) {
|
||||
if (targetOffset - itemStartOffset < targetOffset - smallerOffset) {
|
||||
smallerOffset = itemStartOffset;
|
||||
}
|
||||
}
|
||||
|
||||
if (itemStartOffset >= targetOffset) {
|
||||
if (itemStartOffset - targetOffset < largerOffset - targetOffset) {
|
||||
largerOffset = itemStartOffset;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
double interval = (double) getSnapInterval();
|
||||
double ratio = (double) targetOffset / interval;
|
||||
|
@ -802,6 +870,25 @@ public class ReactScrollView extends ScrollView
|
|||
}
|
||||
}
|
||||
|
||||
private int getItemStartOffset(
|
||||
int snapToAlignment, int itemStartPosition, int itemHeight, int viewPortHeight) {
|
||||
int itemStartOffset;
|
||||
switch (snapToAlignment) {
|
||||
case SNAP_ALIGNMENT_CENTER:
|
||||
itemStartOffset = itemStartPosition - (viewPortHeight - itemHeight) / 2;
|
||||
break;
|
||||
case SNAP_ALIGNMENT_START:
|
||||
itemStartOffset = itemStartPosition;
|
||||
break;
|
||||
case SNAP_ALIGNMENT_END:
|
||||
itemStartOffset = itemStartPosition - (viewPortHeight - itemHeight);
|
||||
break;
|
||||
default:
|
||||
throw new IllegalStateException("Invalid SnapToAlignment value: " + mSnapToAlignment);
|
||||
}
|
||||
return itemStartOffset;
|
||||
}
|
||||
|
||||
private int getSnapInterval() {
|
||||
if (mSnapInterval != 0) {
|
||||
return mSnapInterval;
|
||||
|
|
|
@ -11,6 +11,7 @@ import android.content.Context;
|
|||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.OverScroller;
|
||||
import androidx.annotation.Nullable;
|
||||
import com.facebook.react.bridge.JSApplicationIllegalArgumentException;
|
||||
import com.facebook.react.bridge.ReactContext;
|
||||
import com.facebook.react.uimanager.UIManagerHelper;
|
||||
|
@ -26,6 +27,11 @@ public class ReactScrollViewHelper {
|
|||
public static final String AUTO = "auto";
|
||||
public static final String OVER_SCROLL_NEVER = "never";
|
||||
|
||||
public static final int SNAP_ALIGNMENT_DISABLED = 0;
|
||||
public static final int SNAP_ALIGNMENT_START = 1;
|
||||
public static final int SNAP_ALIGNMENT_CENTER = 2;
|
||||
public static final int SNAP_ALIGNMENT_END = 3;
|
||||
|
||||
public interface ScrollListener {
|
||||
void onScroll(
|
||||
ViewGroup scrollView, ScrollEventType scrollEventType, float xVelocity, float yVelocity);
|
||||
|
@ -34,7 +40,7 @@ public class ReactScrollViewHelper {
|
|||
}
|
||||
|
||||
// Support global native listeners for scroll events
|
||||
private static Set<ScrollListener> sScrollListeners =
|
||||
private static final Set<ScrollListener> sScrollListeners =
|
||||
Collections.newSetFromMap(new WeakHashMap<ScrollListener, Boolean>());
|
||||
|
||||
// If all else fails, this is the hardcoded value in OverScroller.java, in AOSP.
|
||||
|
@ -119,6 +125,20 @@ public class ReactScrollViewHelper {
|
|||
}
|
||||
}
|
||||
|
||||
public static int parseSnapToAlignment(@Nullable String alignment) {
|
||||
if (alignment == null) {
|
||||
return SNAP_ALIGNMENT_DISABLED;
|
||||
} else if ("start".equalsIgnoreCase(alignment)) {
|
||||
return SNAP_ALIGNMENT_START;
|
||||
} else if ("center".equalsIgnoreCase(alignment)) {
|
||||
return SNAP_ALIGNMENT_CENTER;
|
||||
} else if ("end".equals(alignment)) {
|
||||
return SNAP_ALIGNMENT_END;
|
||||
} else {
|
||||
throw new JSApplicationIllegalArgumentException("wrong snap alignment value: " + alignment);
|
||||
}
|
||||
}
|
||||
|
||||
public static int getDefaultScrollAnimationDuration(Context context) {
|
||||
if (!mSmoothScrollDurationInitialized) {
|
||||
mSmoothScrollDurationInitialized = true;
|
||||
|
|
|
@ -115,6 +115,11 @@ public class ReactScrollViewManager extends ViewGroupManager<ReactScrollView>
|
|||
view.setSnapOffsets(offsets);
|
||||
}
|
||||
|
||||
@ReactProp(name = "snapToAlignment")
|
||||
public void setSnapToAlignment(ReactScrollView view, String alignment) {
|
||||
view.setSnapToAlignment(ReactScrollViewHelper.parseSnapToAlignment(alignment));
|
||||
}
|
||||
|
||||
@ReactProp(name = "snapToStart")
|
||||
public void setSnapToStart(ReactScrollView view, boolean snapToStart) {
|
||||
view.setSnapToStart(snapToStart);
|
||||
|
|
|
@ -371,7 +371,7 @@ public class ReactTextView extends AppCompatTextView implements ReactCompoundVie
|
|||
for (int i = 0; i < spans.length; i++) {
|
||||
int spanStart = spannedText.getSpanStart(spans[i]);
|
||||
int spanEnd = spannedText.getSpanEnd(spans[i]);
|
||||
if (spanEnd > index && (spanEnd - spanStart) <= targetSpanTextLength) {
|
||||
if (spanEnd >= index && (spanEnd - spanStart) <= targetSpanTextLength) {
|
||||
target = spans[i].getReactTag();
|
||||
targetSpanTextLength = (spanEnd - spanStart);
|
||||
}
|
||||
|
|
|
@ -13,6 +13,7 @@ import static org.junit.Assert.assertTrue;
|
|||
import android.annotation.SuppressLint;
|
||||
import android.content.Context;
|
||||
import android.text.ClipboardManager;
|
||||
import com.facebook.react.bridge.ReactApplicationContext;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
@ -32,7 +33,8 @@ public class ClipboardModuleTest {
|
|||
|
||||
@Before
|
||||
public void setUp() {
|
||||
mClipboardModule = new ClipboardModule(RuntimeEnvironment.application);
|
||||
mClipboardModule =
|
||||
new ClipboardModule(new ReactApplicationContext(RuntimeEnvironment.application));
|
||||
mClipboardManager =
|
||||
(ClipboardManager)
|
||||
RuntimeEnvironment.application.getSystemService(Context.CLIPBOARD_SERVICE);
|
||||
|
|
|
@ -52,10 +52,15 @@ EventEmitter::EventEmitter(
|
|||
void EventEmitter::dispatchEvent(
|
||||
const std::string &type,
|
||||
const folly::dynamic &payload,
|
||||
EventPriority priority) const {
|
||||
dispatchEvent(type, [payload](jsi::Runtime &runtime) {
|
||||
return valueFromDynamic(runtime, payload);
|
||||
});
|
||||
EventPriority priority,
|
||||
RawEvent::Category category) const {
|
||||
dispatchEvent(
|
||||
type,
|
||||
[payload](jsi::Runtime &runtime) {
|
||||
return valueFromDynamic(runtime, payload);
|
||||
},
|
||||
priority,
|
||||
category);
|
||||
}
|
||||
|
||||
void EventEmitter::dispatchUniqueEvent(
|
||||
|
|
|
@ -77,7 +77,8 @@ class EventEmitter {
|
|||
void dispatchEvent(
|
||||
const std::string &type,
|
||||
const folly::dynamic &payload,
|
||||
EventPriority priority = EventPriority::AsynchronousBatched) const;
|
||||
EventPriority priority = EventPriority::AsynchronousBatched,
|
||||
RawEvent::Category category = RawEvent::Category::Unspecified) const;
|
||||
|
||||
void dispatchUniqueEvent(
|
||||
const std::string &type,
|
||||
|
|
|
@ -23,17 +23,19 @@ struct RawEvent {
|
|||
/*
|
||||
* Defines category of a native platform event. This is used to deduce types
|
||||
* of events for Concurrent Mode.
|
||||
* This enum is duplicated for JNI access in `EventCategoryDef.java`, keep in
|
||||
* sync.
|
||||
*/
|
||||
enum class Category {
|
||||
/*
|
||||
* Start of a continuous event. To be used with touchStart.
|
||||
*/
|
||||
ContinuousStart,
|
||||
ContinuousStart = 0,
|
||||
|
||||
/*
|
||||
* End of a continuous event. To be used with touchEnd.
|
||||
*/
|
||||
ContinuousEnd,
|
||||
ContinuousEnd = 1,
|
||||
|
||||
/*
|
||||
* Priority for this event will be determined from other events in the
|
||||
|
@ -41,19 +43,19 @@ struct RawEvent {
|
|||
* default. If it is not triggered by continuous event, its priority will be
|
||||
* discrete.
|
||||
*/
|
||||
Unspecified,
|
||||
Unspecified = 2,
|
||||
|
||||
/*
|
||||
* Forces discrete type for the event. Regardless if continuous event is
|
||||
* ongoing.
|
||||
*/
|
||||
Discrete,
|
||||
Discrete = 3,
|
||||
|
||||
/*
|
||||
* Forces continuous type for the event. Regardless if continuous event
|
||||
* isn't ongoing.
|
||||
*/
|
||||
Continuous
|
||||
Continuous = 4
|
||||
};
|
||||
|
||||
RawEvent(
|
||||
|
|
|
@ -106,7 +106,6 @@
|
|||
"hermes-engine": "~0.9.0",
|
||||
"invariant": "^2.2.4",
|
||||
"jsc-android": "^250230.2.1",
|
||||
"metro-babel-register": "0.66.2",
|
||||
"metro-react-native-babel-transformer": "0.66.2",
|
||||
"metro-runtime": "0.66.2",
|
||||
"metro-source-map": "0.66.2",
|
||||
|
@ -152,7 +151,7 @@
|
|||
"eslint-plugin-react-hooks": "^4.2.0",
|
||||
"eslint-plugin-react-native": "^3.11.0",
|
||||
"eslint-plugin-relay": "1.8.1",
|
||||
"flow-bin": "^0.160.2",
|
||||
"flow-bin": "^0.161.0",
|
||||
"jest": "^26.6.3",
|
||||
"jest-junit": "^10.0.0",
|
||||
"jscodeshift": "^0.11.0",
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
# Gemfile
|
||||
source 'https://rubygems.org'
|
||||
|
||||
gem 'cocoapods', '= 1.10.1'
|
||||
gem 'cocoapods', '= 1.11.2'
|
||||
gem 'rexml'
|
||||
|
|
|
@ -102,5 +102,7 @@ end
|
|||
|
||||
post_install do |installer|
|
||||
react_native_post_install(installer)
|
||||
# TODO(macOS GH#774) - This was temporarily removed with 51bf5579489 but reintroduced with 03a09078681 on 2021-10-25.
|
||||
# Plus, we still need the fix in folly before v2022.02.07.00. (See https://github.com/facebook/folly/commit/4a8837f)
|
||||
__apply_Xcode_12_5_M1_post_install_workaround(installer)
|
||||
end
|
||||
|
|
|
@ -550,8 +550,8 @@ SPEC CHECKSUMS:
|
|||
boost-for-react-native: 8f7c9ecfe357664c072ffbe2432569667cbf1f1b
|
||||
CocoaAsyncSocket: 065fd1e645c7abab64f7a6a2007a48038fdc6a99
|
||||
DoubleConversion: ed15e075aa758ac0e4c1f8b830bd4e4d40d669e8
|
||||
FBLazyVector: bb83d2d7ac3804c71daacdcbd7ec0ef803656465
|
||||
FBReactNativeSpec: 530ae9cee355483b88d39c36dd51c0a635a409dc
|
||||
FBLazyVector: ed88e014fd84249d01c1f499fec5e300b42f9b40
|
||||
FBReactNativeSpec: c01109c60c5e3910a9bc7efb0c69f2c8ad0c7fd3
|
||||
Flipper: 30e8eeeed6abdc98edaf32af0cda2f198be4b733
|
||||
Flipper-Boost-iOSX: fd1e2b8cbef7e662a122412d7ac5f5bea715403c
|
||||
Flipper-DoubleConversion: 57ffbe81ef95306cc9e69c4aa3aeeeeb58a6a28c
|
||||
|
@ -566,37 +566,37 @@ SPEC CHECKSUMS:
|
|||
libevent: 4049cae6c81cdb3654a443be001fb9bdceff7913
|
||||
OpenSSL-Universal: 1aa4f6a6ee7256b83db99ec1ccdaa80d10f9af9b
|
||||
RCT-Folly: 43adc9ce880eb76792f88c011773cb5c664c1419
|
||||
RCTRequired: cedfb6c63456a874433eb5edcfaed1bdb08add24
|
||||
RCTTypeSafety: aa14693ea045b7d547cc67df9a277ac76ef68932
|
||||
React: 6c3beb80b5b8a88776b514a7b83e333b9e76618f
|
||||
React-callinvoker: c345de4f7965f30e064911a0ad933b06064aceb4
|
||||
React-Core: 559d514734609d53a187939d40ac14ed46d621b4
|
||||
React-CoreModules: 4237c5852522d538e4b0dd48b4f25f577973bc40
|
||||
React-cxxreact: 1569c84dacbe2d1b2a8104047ff53670f9df0b98
|
||||
React-jsi: 6bb09f56fad95f987647c43f052b7a577b8b0808
|
||||
React-jsiexecutor: 61e528a1ba5c5e761dd931073fb040d687f9fae1
|
||||
React-jsinspector: 59d0fb00e549e931512a2592ff03cdcffbe2f0d3
|
||||
React-logger: 77c0b6b4f95cd0ca0bf717b920f7f551d6eba60e
|
||||
React-perflogger: 34edb1530aa902ce7e6fbf9a939d1e77306549fa
|
||||
React-RCTActionSheet: 18f487cce60a164707b8b635f3f283fd5d9bda4e
|
||||
React-RCTAnimation: 56c81f96571f0fd4d340f781ce8fffe55755a852
|
||||
React-RCTBlob: 1a1dbb41ad8c71065bed4658e7f45322101d23e5
|
||||
React-RCTImage: d281e12555f766c3e89e9d984bb4aa29b3bd27c2
|
||||
React-RCTLinking: e9a1304c27e21b3484b674d6157c1f508a2eefbb
|
||||
React-RCTNetwork: 62485b060df7ceae39986993b254d8f1be497fd9
|
||||
React-RCTPushNotification: 5d6f6da6f5bf961e5a52cd19b0594201b991e8ef
|
||||
React-RCTSettings: 3af66a2e248e66b270589f7424533c23d70385eb
|
||||
React-RCTTest: eba636be492c6cf8c464e4498c4d2543c9eb2796
|
||||
React-RCTText: e7a1b0b537a4b050b04bad10cab630db887fcd06
|
||||
React-RCTVibration: fa27a8b8f6ca3253d34e20398bc8cb86ffc16a26
|
||||
React-runtimeexecutor: 072c40f4aa12df60c62d432075e0c4d11326cdfa
|
||||
RCTRequired: cce7d716bd987082a0503d0ae74ea01af34fa687
|
||||
RCTTypeSafety: b8623da080ef1e53825646acfef37325c07525f6
|
||||
React: 1380385fe86c1109345cb6e11c75b73629635562
|
||||
React-callinvoker: 8db6cfaee71fb50a19879d7c590dd3ae18c38fde
|
||||
React-Core: 6363185b7a27abe56578a5fa19b928e1f49eb205
|
||||
React-CoreModules: 9cf94cef254689ca53aebee01e1329add9add152
|
||||
React-cxxreact: ce4505e49604d686d066411e5430d754002f7d7b
|
||||
React-jsi: 3a83380c9f6897e496cf67fde8bce70c2b7e23b3
|
||||
React-jsiexecutor: feb2fd3d299bd0df98b4facce5e52b8c4d46f5c6
|
||||
React-jsinspector: 5e6791037409e135beff6295dfb1d473cafd11ca
|
||||
React-logger: 1fb2545ea6d70399000da65285322d86358b2780
|
||||
React-perflogger: 8f85b27984a91d03d5387e36cd4bf42e936f48fb
|
||||
React-RCTActionSheet: 6e4c954021a7ebb9dd121cfc01cace4a9809c503
|
||||
React-RCTAnimation: c791900ee9a1d42cc37fd34c8ca33b90b14bf255
|
||||
React-RCTBlob: 960a49350aca480f82bd0e7b2cead58f42a0acde
|
||||
React-RCTImage: 9da77a4989fc0ee904d5541f6e0f02b84e348bd2
|
||||
React-RCTLinking: 211d94b681003f1f495e3ad794b4a9c16a5aa59e
|
||||
React-RCTNetwork: 89c3f89a3fa9b9d04db519d3c7f9a0b3caa6e7b0
|
||||
React-RCTPushNotification: 45737eedb3657021b8bdf1162cd3eacc829eba99
|
||||
React-RCTSettings: f8951006b51c38278025a93a08639851319c9ba9
|
||||
React-RCTTest: 28d8b5358f7a47eecd2f9181972b16c8320266f9
|
||||
React-RCTText: d47ea296ea899a0755cab46fd394a447e8c1fd60
|
||||
React-RCTVibration: 3e62363a4e154fa50d165d084f16c964e4aa2ef0
|
||||
React-runtimeexecutor: 9d7d286e9fc07dc24545d630769c0c95cbc1499e
|
||||
React-TurboModuleCxx-RNW: f2e32cbfced49190a61d66c993a8975de79a158a
|
||||
React-TurboModuleCxx-WinRTPort: 0b7e5a1d2b3ad61fd859162e549284ac6396f933
|
||||
ReactCommon: ea9da28850c0e7899490aa31c647827485bda271
|
||||
React-TurboModuleCxx-WinRTPort: bb518875d3185d9c73d0b56e946ac7452d2d250d
|
||||
ReactCommon: a475ee396320ee2b499f6a3979eccf469f96b1b0
|
||||
ScreenshotManager: aaab73e8039aef3fad444d4e86cbd7842fd17fac
|
||||
Yoga: ff976ef91e2cab219ab675f57c16fc928296be01
|
||||
Yoga: 9472d9fbfbf44a24008239832cc953e2ac18fa7e
|
||||
YogaKit: f782866e155069a2cca2517aafea43200b01fd5a
|
||||
|
||||
PODFILE CHECKSUM: 54afd7797b4306f176be8caf7e8a1239ca2d8f4d
|
||||
PODFILE CHECKSUM: 638c126b1cdfeb6ab1bdafd06eb9d9065fea2b85
|
||||
|
||||
COCOAPODS: 1.11.2
|
||||
|
|
|
@ -1636,7 +1636,6 @@
|
|||
LIBRARY_SEARCH_PATHS = (
|
||||
"\"$(SDKROOT)/usr/lib/swift\"",
|
||||
"$(inherited)",
|
||||
"$(TOOLCHAIN_DIR)/usr/lib/swift-5.0/$(PLATFORM_NAME)",
|
||||
"$(TOOLCHAIN_DIR)/usr/lib/swift/$(PLATFORM_NAME)",
|
||||
);
|
||||
"LIBRARY_SEARCH_PATHS[arch=*]" = (
|
||||
|
@ -1681,7 +1680,6 @@
|
|||
LIBRARY_SEARCH_PATHS = (
|
||||
"\"$(SDKROOT)/usr/lib/swift\"",
|
||||
"$(inherited)",
|
||||
"$(TOOLCHAIN_DIR)/usr/lib/swift-5.0/$(PLATFORM_NAME)",
|
||||
"$(TOOLCHAIN_DIR)/usr/lib/swift/$(PLATFORM_NAME)",
|
||||
);
|
||||
OTHER_CFLAGS = (
|
||||
|
@ -1885,7 +1883,7 @@
|
|||
ENABLE_BITCODE = NO;
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
ENABLE_TESTABILITY = YES;
|
||||
"EXCLUDED_ARCHS[sdk=iphonesimulator*]" = "arm64 ";
|
||||
"EXCLUDED_ARCHS[sdk=iphonesimulator*]" = "";
|
||||
GCC_C_LANGUAGE_STANDARD = gnu11;
|
||||
GCC_DYNAMIC_NO_PIC = NO;
|
||||
GCC_NO_COMMON_BLOCKS = YES;
|
||||
|
@ -1969,7 +1967,7 @@
|
|||
ENABLE_BITCODE = NO;
|
||||
ENABLE_NS_ASSERTIONS = NO;
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
"EXCLUDED_ARCHS[sdk=iphonesimulator*]" = "arm64 ";
|
||||
"EXCLUDED_ARCHS[sdk=iphonesimulator*]" = "";
|
||||
GCC_C_LANGUAGE_STANDARD = gnu11;
|
||||
GCC_NO_COMMON_BLOCKS = YES;
|
||||
GCC_TREAT_INCOMPATIBLE_POINTER_TYPE_WARNINGS_AS_ERRORS = YES;
|
||||
|
|
|
@ -586,7 +586,7 @@ class OnPartialLoadExample extends React.Component<
|
|||
}
|
||||
}
|
||||
|
||||
const fullImage = {
|
||||
const fullImage: ImageSource = {
|
||||
uri: 'https://www.facebook.com/ads/pics/successstories.png',
|
||||
};
|
||||
const smallImage = {
|
||||
|
@ -748,7 +748,9 @@ exports.examples = [
|
|||
<Image
|
||||
defaultSource={require('../../assets/bunny.png')}
|
||||
source={{
|
||||
uri: 'https://origami.design/public/images/bird-logo.png',
|
||||
// Note: Use a large image and bust cache so we can in fact
|
||||
// visualize the `defaultSource` image.
|
||||
uri: fullImage.uri + '?cacheBust=notinCache' + Date.now(),
|
||||
}}
|
||||
style={styles.base}
|
||||
/>
|
||||
|
|
|
@ -61,6 +61,45 @@ class ScrollViewSimpleExample extends React.Component<{...}> {
|
|||
])}
|
||||
</ScrollView>,
|
||||
);
|
||||
items.push(
|
||||
<ScrollView
|
||||
key={'scrollViewSnapStart'}
|
||||
horizontal
|
||||
snapToAlignment={'start'}
|
||||
pagingEnabled>
|
||||
{this.makeItems(NUM_ITEMS, [
|
||||
styles.itemWrapper,
|
||||
styles.horizontalItemWrapper,
|
||||
styles.horizontalPagingItemWrapper,
|
||||
])}
|
||||
</ScrollView>,
|
||||
);
|
||||
items.push(
|
||||
<ScrollView
|
||||
key={'scrollViewSnapCenter'}
|
||||
horizontal
|
||||
snapToAlignment={'center'}
|
||||
pagingEnabled>
|
||||
{this.makeItems(NUM_ITEMS, [
|
||||
styles.itemWrapper,
|
||||
styles.horizontalItemWrapper,
|
||||
styles.horizontalPagingItemWrapper,
|
||||
])}
|
||||
</ScrollView>,
|
||||
);
|
||||
items.push(
|
||||
<ScrollView
|
||||
key={'scrollViewSnapEnd'}
|
||||
horizontal
|
||||
snapToAlignment={'end'}
|
||||
pagingEnabled>
|
||||
{this.makeItems(NUM_ITEMS, [
|
||||
styles.itemWrapper,
|
||||
styles.horizontalItemWrapper,
|
||||
styles.horizontalPagingItemWrapper,
|
||||
])}
|
||||
</ScrollView>,
|
||||
);
|
||||
|
||||
const verticalScrollView = (
|
||||
<ScrollView style={styles.verticalScrollView}>{items}</ScrollView>
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
'use strict';
|
||||
|
||||
const React = require('react');
|
||||
const {Switch, Text, View} = require('react-native');
|
||||
const {Switch, Text, View, Platform} = require('react-native');
|
||||
|
||||
type OnOffIndicatorProps = $ReadOnly<{|on: boolean, testID: string|}>;
|
||||
function OnOffIndicator({on, testID}: OnOffIndicatorProps) {
|
||||
|
@ -212,6 +212,38 @@ class EventSwitchExample extends React.Component<{...}, $FlowFixMeState> {
|
|||
}
|
||||
}
|
||||
|
||||
class IOSBackgroundColEx extends React.Component<{...}, $FlowFixMeState> {
|
||||
state = {
|
||||
iosBackgroundColor: '#ffa500',
|
||||
};
|
||||
|
||||
render() {
|
||||
return (
|
||||
<View>
|
||||
<Switch
|
||||
disabled
|
||||
ios_backgroundColor={this.state.iosBackgroundColor}
|
||||
style={{marginBottom: 20}}
|
||||
/>
|
||||
<Text>
|
||||
The background color can be seen either when the switch value is false
|
||||
or when the switch is disabled (and the switch is translucent).{' '}
|
||||
</Text>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class OnChangeExample extends React.Component<{...}, $FlowFixMeState> {
|
||||
render() {
|
||||
return (
|
||||
<View>
|
||||
<Switch onChange={() => alert('OnChange Called')} />
|
||||
</View>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
exports.title = 'Switch';
|
||||
exports.documentationURL = 'https://reactnative.dev/docs/switch';
|
||||
exports.category = 'UI';
|
||||
|
@ -253,4 +285,19 @@ exports.examples = [
|
|||
return <ColorSwitchExample />;
|
||||
},
|
||||
},
|
||||
{
|
||||
title: 'OnChange receives the change event as an argument',
|
||||
render(): React.Element<any> {
|
||||
return <OnChangeExample />;
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
if (Platform.OS === 'ios') {
|
||||
exports.examples.push({
|
||||
title: '[iOS Only] Custom background colors can be set',
|
||||
render(): React.Element<any> {
|
||||
return <IOSBackgroundColEx />;
|
||||
},
|
||||
});
|
||||
}
|
||||
|
|
|
@ -36,7 +36,7 @@
|
|||
"eslint-plugin-react-hooks": "^4.2.0",
|
||||
"eslint-plugin-react-native": "^3.11.0",
|
||||
"eslint-plugin-relay": "^1.8.2",
|
||||
"flow-bin": "^0.160.2",
|
||||
"flow-bin": "^0.161.0",
|
||||
"jest": "^26.6.3",
|
||||
"jest-junit": "^10.0.0",
|
||||
"jscodeshift": "^0.11.0",
|
||||
|
|
|
@ -6,6 +6,10 @@
|
|||
|
||||
set -e
|
||||
|
||||
# remove global prefix if it's already set
|
||||
# the running shell process will choose a node binary and a global package directory breaks version managers
|
||||
unset PREFIX
|
||||
|
||||
# Support Homebrew on M1
|
||||
HOMEBREW_M1_BIN=/opt/homebrew/bin
|
||||
if [[ -d $HOMEBREW_M1_BIN && ! $PATH =~ $HOMEBREW_M1_BIN ]]; then
|
||||
|
|
|
@ -16,6 +16,13 @@ def use_react_native! (options={})
|
|||
# Include Hermes dependencies
|
||||
hermes_enabled = options[:hermes_enabled] ||= false
|
||||
|
||||
if `/usr/sbin/sysctl -n hw.optional.arm64 2>&1`.to_i == 1 && !RUBY_PLATFORM.start_with?('arm64')
|
||||
Pod::UI.warn 'Do not use "pod install" from inside Rosetta2 (x86_64 emulation on arm64).'
|
||||
Pod::UI.warn ' - Emulated x86_64 is slower than native arm64'
|
||||
Pod::UI.warn ' - May result in mixed architectures in rubygems (eg: ffi_c.bundle files may be x86_64 with an arm64 interpreter)'
|
||||
Pod::UI.warn 'Run "env /usr/bin/arch -arm64 /bin/bash --login" then try again.'
|
||||
end
|
||||
|
||||
# The Pods which should be included in all projects
|
||||
pod 'FBLazyVector', :path => "#{prefix}/Libraries/FBLazyVector"
|
||||
pod 'FBReactNativeSpec', :path => "#{prefix}/React/FBReactNativeSpec"
|
||||
|
@ -130,30 +137,60 @@ def exclude_architectures(installer)
|
|||
.uniq{ |p| p.path }
|
||||
.push(installer.pods_project)
|
||||
|
||||
arm_value = `/usr/sbin/sysctl -n hw.optional.arm64 2>&1`.to_i
|
||||
|
||||
# Hermes does not support `i386` architecture
|
||||
excluded_archs_default = has_pod(installer, 'hermes-engine') ? "i386" : ""
|
||||
|
||||
projects.each do |project|
|
||||
project.build_configurations.each do |config|
|
||||
if arm_value == 1 then
|
||||
config.build_settings["EXCLUDED_ARCHS[sdk=iphonesimulator*]"] = excluded_archs_default
|
||||
else
|
||||
config.build_settings["EXCLUDED_ARCHS[sdk=iphonesimulator*]"] = "arm64 " + excluded_archs_default
|
||||
end
|
||||
config.build_settings["EXCLUDED_ARCHS[sdk=iphonesimulator*]"] = excluded_archs_default
|
||||
end
|
||||
|
||||
project.save()
|
||||
end
|
||||
end
|
||||
|
||||
def fix_library_search_paths(installer)
|
||||
def fix_config(config)
|
||||
lib_search_paths = config.build_settings["LIBRARY_SEARCH_PATHS"]
|
||||
if lib_search_paths
|
||||
if lib_search_paths.include?("$(TOOLCHAIN_DIR)/usr/lib/swift-5.0/$(PLATFORM_NAME)") || lib_search_paths.include?("\"$(TOOLCHAIN_DIR)/usr/lib/swift-5.0/$(PLATFORM_NAME)\"")
|
||||
# $(TOOLCHAIN_DIR)/usr/lib/swift-5.0/$(PLATFORM_NAME) causes problem with Xcode 12.5 + arm64 (Apple M1)
|
||||
# since the libraries there are only built for x86_64 and i386.
|
||||
lib_search_paths.delete("$(TOOLCHAIN_DIR)/usr/lib/swift-5.0/$(PLATFORM_NAME)")
|
||||
lib_search_paths.delete("\"$(TOOLCHAIN_DIR)/usr/lib/swift-5.0/$(PLATFORM_NAME)\"")
|
||||
if !(lib_search_paths.include?("$(SDKROOT)/usr/lib/swift") || lib_search_paths.include?("\"$(SDKROOT)/usr/lib/swift\""))
|
||||
# however, $(SDKROOT)/usr/lib/swift is required, at least if user is not running CocoaPods 1.11
|
||||
lib_search_paths.insert(0, "$(SDKROOT)/usr/lib/swift")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
projects = installer.aggregate_targets
|
||||
.map{ |t| t.user_project }
|
||||
.uniq{ |p| p.path }
|
||||
.push(installer.pods_project)
|
||||
|
||||
projects.each do |project|
|
||||
project.build_configurations.each do |config|
|
||||
fix_config(config)
|
||||
end
|
||||
project.native_targets.each do |target|
|
||||
target.build_configurations.each do |config|
|
||||
fix_config(config)
|
||||
end
|
||||
end
|
||||
project.save()
|
||||
end
|
||||
end
|
||||
|
||||
def react_native_post_install(installer)
|
||||
if has_pod(installer, 'Flipper')
|
||||
flipper_post_install(installer)
|
||||
end
|
||||
|
||||
exclude_architectures(installer)
|
||||
fix_library_search_paths(installer)
|
||||
end
|
||||
|
||||
def use_react_native_codegen!(spec, options={})
|
||||
|
@ -340,6 +377,9 @@ end
|
|||
# See https://github.com/facebook/react-native/issues/31480#issuecomment-902912841 for more context.
|
||||
# Actual fix was authored by https://github.com/mikehardy.
|
||||
# New app template will call this for now until the underlying issue is resolved.
|
||||
#
|
||||
# TODO(macOS GH#774) - This was temporarily removed with 51bf5579489 but reintroduced with 03a09078681 on 2021-10-25.
|
||||
# Plus, we still need the fix in folly before v2022.02.07.00. (See https://github.com/facebook/folly/commit/4a8837f)
|
||||
def __apply_Xcode_12_5_M1_post_install_workaround(installer)
|
||||
# Apple Silicon builds require a library path tweak for Swift library discovery to resolve Swift-related "symbol not found".
|
||||
# Note: this was fixed via https://github.com/facebook/react-native/commit/eb938863063f5535735af2be4e706f70647e5b90
|
||||
|
|
|
@ -12,7 +12,7 @@ THIS_DIR=$(cd -P "$(dirname "$(readlink "${BASH_SOURCE[0]}" || echo "${BASH_SOUR
|
|||
RNTESTER_DIR="$THIS_DIR/../packages/rn-tester"
|
||||
|
||||
# Note: Keep in sync with FB internal.
|
||||
REQUIRED_COCOAPODS_VERSION="1.11.2" # [(macOS) FB was 1.10.1, agents are currently 1.11.2 ]
|
||||
REQUIRED_COCOAPODS_VERSION="1.11.2"
|
||||
|
||||
validate_env () {
|
||||
# Check that CocoaPods is working.
|
||||
|
|
|
@ -65,4 +65,4 @@ untyped-import
|
|||
untyped-type-import
|
||||
|
||||
[version]
|
||||
^0.160.2
|
||||
^0.161.0
|
||||
|
|
|
@ -437,7 +437,6 @@
|
|||
LIBRARY_SEARCH_PATHS = (
|
||||
"\"$(SDKROOT)/usr/lib/swift\"",
|
||||
"\"$(TOOLCHAIN_DIR)/usr/lib/swift/$(PLATFORM_NAME)\"",
|
||||
"\"$(TOOLCHAIN_DIR)/usr/lib/swift-5.0/$(PLATFORM_NAME)\"",
|
||||
"\"$(inherited)\"",
|
||||
);
|
||||
MTL_ENABLE_DEBUG_INFO = YES;
|
||||
|
@ -495,7 +494,6 @@
|
|||
LIBRARY_SEARCH_PATHS = (
|
||||
"\"$(SDKROOT)/usr/lib/swift\"",
|
||||
"\"$(TOOLCHAIN_DIR)/usr/lib/swift/$(PLATFORM_NAME)\"",
|
||||
"\"$(TOOLCHAIN_DIR)/usr/lib/swift-5.0/$(PLATFORM_NAME)\"",
|
||||
"\"$(inherited)\"",
|
||||
);
|
||||
MTL_ENABLE_DEBUG_INFO = NO;
|
||||
|
|
|
@ -25,6 +25,8 @@ target 'HelloWorld' do
|
|||
|
||||
post_install do |installer|
|
||||
react_native_post_install(installer)
|
||||
# TODO(macOS GH#774) - This was temporarily removed with 51bf5579489 but reintroduced with 03a09078681 on 2021-10-25.
|
||||
# Plus, we still need the fix in folly before v2022.02.07.00. (See https://github.com/facebook/folly/commit/4a8837f)
|
||||
__apply_Xcode_12_5_M1_post_install_workaround(installer)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -3993,10 +3993,10 @@ flatted@^3.1.0:
|
|||
resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.2.5.tgz#76c8584f4fc843db64702a6bd04ab7a8bd666da3"
|
||||
integrity sha512-WIWGi2L3DyTUvUrwRKgGi9TwxQMUEqPOPQBVi71R96jZXJdFskXEmf54BoZaS1kknGODoIGASGEzBUYdyMCBJg==
|
||||
|
||||
flow-bin@^0.160.2:
|
||||
version "0.160.2"
|
||||
resolved "https://registry.yarnpkg.com/flow-bin/-/flow-bin-0.160.2.tgz#4d579f6bbd48ca975e13237b66e503e0c519c8b9"
|
||||
integrity sha512-vrsiPwcqdaBsuQmPZXjF0tdKqgoVc2wH+nHamvqFzvgnQLJMLN+N0seqaOMH8t/3YKnAEbY/uZ440mALE/+WLw==
|
||||
flow-bin@^0.161.0:
|
||||
version "0.161.0"
|
||||
resolved "https://registry.yarnpkg.com/flow-bin/-/flow-bin-0.161.0.tgz#1c03ea4a9e3036a8bc639f050bd8dc6f5288e8bb"
|
||||
integrity sha512-5BKQi+sjOXz67Kbc1teiBwd5e0Da6suW7S5oUCm9VclnzSZ3nlfNZUdrkvgJ5S4w5KDBDToEL7rREpn8rKF1zg==
|
||||
|
||||
flow-parser@0.*:
|
||||
version "0.176.2"
|
||||
|
|
Загрузка…
Ссылка в новой задаче