Merge commit '086c967286638deb9db21e4d62e6dca1fc1fba83' into 0.67-merge-latest

This commit is contained in:
Adam Gleitman 2022-05-03 16:01:38 -07:00
Родитель 33c28f7b6d 086c967286
Коммит b74e7f8241
86 изменённых файлов: 923 добавлений и 676 удалений

Просмотреть файл

@ -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
Просмотреть файл

@ -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"