# This patch fixes two issues: # - Updates RCTDeviceInfo.m to match https://github.com/facebook/react-native/commit/0b8db7e5e814cfbf9974cc5b6ceb64e8006d8a3c. # This fixes an issue in which useWindowDimensions returns incorrect # values in landscape mode in iOS. # This should be fixed in React Native 0.80. See https://github.com/facebook/react-native/issues/51086. # - Updates NativeAnimatedModule.java to work around an Android 12-specific crash. diff --git a/React/CoreModules/RCTDeviceInfo.mm b/React/CoreModules/RCTDeviceInfo.mm index 6b4fcef852252e8d4ac2aceb12175fdfafb4def7..8ceab21e8653d429876d10e2d12ed1342780ad7d 100644 --- a/React/CoreModules/RCTDeviceInfo.mm +++ b/React/CoreModules/RCTDeviceInfo.mm @@ -14,9 +14,7 @@ #import #import #import -#import #import -#import #import #import "CoreModulesPlugins.h" @@ -31,8 +29,13 @@ using namespace facebook::react; NSDictionary *_currentInterfaceDimensions; BOOL _isFullscreen; std::atomic _invalidated; + NSDictionary *_constants; + + __weak UIWindow *_applicationWindow; } +static NSString *const kFrameKeyPath = @"frame"; + @synthesize moduleRegistry = _moduleRegistry; RCT_EXPORT_MODULE() @@ -40,14 +43,26 @@ RCT_EXPORT_MODULE() - (instancetype)init { if (self = [super init]) { - [[RCTKeyWindowValuesProxy sharedInstance] startObservingWindowSizeIfNecessary]; + _applicationWindow = RCTKeyWindow(); + [_applicationWindow addObserver:self forKeyPath:kFrameKeyPath options:NSKeyValueObservingOptionNew context:nil]; } return self; } +- (void)observeValueForKeyPath:(NSString *)keyPath + ofObject:(id)object + change:(NSDictionary *)change + context:(void *)context +{ + if ([keyPath isEqualToString:kFrameKeyPath]) { + [self interfaceFrameDidChange]; + [[NSNotificationCenter defaultCenter] postNotificationName:RCTWindowFrameDidChangeNotification object:self]; + } +} + + (BOOL)requiresMainQueueSetup { - return NO; + return YES; } - (dispatch_queue_t)methodQueue @@ -81,7 +96,7 @@ RCT_EXPORT_MODULE() #if TARGET_OS_IOS - _currentInterfaceOrientation = [RCTKeyWindowValuesProxy sharedInstance].currentInterfaceOrientation; + _currentInterfaceOrientation = RCTKeyWindow().windowScene.interfaceOrientation; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(interfaceFrameDidChange) @@ -98,6 +113,15 @@ RCT_EXPORT_MODULE() selector:@selector(invalidate) name:RCTBridgeWillInvalidateModulesNotification object:nil]; + + _constants = @{ + @"Dimensions" : [self _exportedDimensions], + // Note: + // This prop is deprecated and will be removed in a future release. + // Please use this only for a quick and temporary solution. + // Use instead. + @"isIPhoneX_deprecated" : @(RCTIsIPhoneNotched()), + }; } - (void)invalidate @@ -120,6 +144,8 @@ RCT_EXPORT_MODULE() [[NSNotificationCenter defaultCenter] removeObserver:self name:RCTBridgeWillInvalidateModulesNotification object:nil]; + [_applicationWindow removeObserver:self forKeyPath:kFrameKeyPath]; + #if TARGET_OS_IOS [[NSNotificationCenter defaultCenter] removeObserver:self name:UIDeviceOrientationDidChangeNotification object:nil]; #endif @@ -132,8 +158,13 @@ static BOOL RCTIsIPhoneNotched() #if TARGET_OS_IOS dispatch_once(&onceToken, ^{ + RCTAssertMainQueue(); + // 20pt is the top safeArea value in non-notched devices - isIPhoneNotched = [RCTWindowSafeAreaProxy sharedInstance].currentSafeAreaInsets.top > 20; + UIWindow *keyWindow = RCTKeyWindow(); + if (keyWindow) { + isIPhoneNotched = keyWindow.safeAreaInsets.top > 20; + } }); #endif @@ -142,11 +173,13 @@ static BOOL RCTIsIPhoneNotched() static NSDictionary *RCTExportedDimensions(CGFloat fontScale) { + RCTAssertMainQueue(); UIScreen *mainScreen = UIScreen.mainScreen; CGSize screenSize = mainScreen.bounds.size; + UIView *mainWindow = RCTKeyWindow(); // We fallback to screen size if a key window is not found. - CGSize windowSize = [RCTKeyWindowValuesProxy sharedInstance].windowSize; + CGSize windowSize = mainWindow ? mainWindow.bounds.size : screenSize; NSDictionary *dimsWindow = @{ @"width" : @(windowSize.width), @@ -170,7 +203,10 @@ static NSDictionary *RCTExportedDimensions(CGFloat fontScale) RCTAssert(_moduleRegistry, @"Failed to get exported dimensions: RCTModuleRegistry is nil"); RCTAccessibilityManager *accessibilityManager = (RCTAccessibilityManager *)[_moduleRegistry moduleForName:"AccessibilityManager"]; - RCTAssert(accessibilityManager, @"Failed to get exported dimensions: AccessibilityManager is nil"); + // TOOD(T225745315): For some reason, accessibilityManager is nil in some cases. + // We default the fontScale to 1.0 in this case. This should be okay: if we assume + // that accessibilityManager will eventually become available, js will eventually + // be updated with the correct fontScale. CGFloat fontScale = accessibilityManager ? accessibilityManager.multiplier : 1.0; return RCTExportedDimensions(fontScale); } @@ -182,14 +218,7 @@ static NSDictionary *RCTExportedDimensions(CGFloat fontScale) - (NSDictionary *)getConstants { - return @{ - @"Dimensions" : [self _exportedDimensions], - // Note: - // This prop is deprecated and will be removed in a future release. - // Please use this only for a quick and temporary solution. - // Use instead. - @"isIPhoneX_deprecated" : @(RCTIsIPhoneNotched()), - }; + return _constants; } - (void)didReceiveNewContentSizeMultiplier @@ -209,10 +238,11 @@ static NSDictionary *RCTExportedDimensions(CGFloat fontScale) - (void)interfaceOrientationDidChange { #if TARGET_OS_IOS && !TARGET_OS_MACCATALYST - UIWindow *keyWindow = RCTKeyWindow(); - UIInterfaceOrientation nextOrientation = keyWindow.windowScene.interfaceOrientation; + UIApplication *application = RCTSharedApplication(); + UIInterfaceOrientation nextOrientation = RCTKeyWindow().windowScene.interfaceOrientation; - BOOL isRunningInFullScreen = CGRectEqualToRect(keyWindow.frame, keyWindow.screen.bounds); + BOOL isRunningInFullScreen = + CGRectEqualToRect(application.delegate.window.frame, application.delegate.window.screen.bounds); // We are catching here two situations for multitasking view: // a) The app is in Split View and the container gets resized -> !isRunningInFullScreen // b) The app changes to/from fullscreen example: App runs in slide over mode and goes into fullscreen-> @@ -276,3 +306,4 @@ Class RCTDeviceInfoCls(void) { return RCTDeviceInfo.class; } + diff --git a/ReactAndroid/src/main/java/com/facebook/react/animated/NativeAnimatedModule.java b/ReactAndroid/src/main/java/com/facebook/react/animated/NativeAnimatedModule.java index cf14e51cf5f561b84f1b6ace8410fc77d626758e..abc8c64adf26fbf73429aee7fd4f76877e98849a 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/animated/NativeAnimatedModule.java +++ b/ReactAndroid/src/main/java/com/facebook/react/animated/NativeAnimatedModule.java @@ -42,6 +42,7 @@ import java.util.List; import java.util.Queue; import java.util.Set; import java.util.concurrent.ConcurrentLinkedQueue; +import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.atomic.AtomicReference; /** @@ -155,8 +156,15 @@ public class NativeAnimatedModule extends NativeAnimatedModuleSpec } private class ConcurrentOperationQueue { - private final Queue mQueue = new ConcurrentLinkedQueue<>(); - @Nullable private UIThreadOperation mPeekedOperation = null; + // Patch: Use LinkedBlockingQueue instead of ConcurrentLinkedQueue. + // In some versions of Android, ConcurrentLinkedQueue is known to drop + // items, causing crashing. See https://github.com/laurent22/joplin/issues/8425 + private final Queue mQueue = ( + // The issue exists for Android 12, which corresponds to API levels 31 and 32. + Build.VERSION.SDK_INT == 31 || Build.VERSION.SDK_INT == 32 + ) ? new LinkedBlockingQueue<>() : new ConcurrentLinkedQueue<>(); + + @Nullable private UIThreadOperation mPeekedOperation = null; @AnyThread boolean isEmpty() {