1/*
2 * Copyright (c) Meta Platforms, Inc. and affiliates.
3 *
4 * This source code is licensed under the MIT license found in the
5 * LICENSE file in the root directory of this source tree.
6 */
7
8#import "ABI47_0_0RCTStatusBarManager.h"
9#import "ABI47_0_0CoreModulesPlugins.h"
10
11#import <ABI47_0_0React/ABI47_0_0RCTEventDispatcherProtocol.h>
12#import <ABI47_0_0React/ABI47_0_0RCTLog.h>
13#import <ABI47_0_0React/ABI47_0_0RCTUtils.h>
14
15#import <ABI47_0_0FBReactNativeSpec/ABI47_0_0FBReactNativeSpec.h>
16
17@implementation ABI47_0_0RCTConvert (UIStatusBar)
18
19+ (UIStatusBarStyle)UIStatusBarStyle:(id)json ABI47_0_0RCT_DYNAMIC
20{
21  static NSDictionary *mapping;
22  static dispatch_once_t onceToken;
23  dispatch_once(&onceToken, ^{
24    if (@available(iOS 13.0, *)) {
25      mapping = @{
26        @"default" : @(UIStatusBarStyleDefault),
27        @"light-content" : @(UIStatusBarStyleLightContent),
28#if defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && defined(__IPHONE_13_0) && \
29    __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_13_0
30        @"dark-content" : @(UIStatusBarStyleDarkContent)
31#else
32          @"dark-content": @(UIStatusBarStyleDefault)
33#endif
34      };
35
36    } else {
37      mapping = @{
38        @"default" : @(UIStatusBarStyleDefault),
39        @"light-content" : @(UIStatusBarStyleLightContent),
40        @"dark-content" : @(UIStatusBarStyleDefault)
41      };
42    }
43  });
44  return _ABI47_0_0RCT_CAST(
45      UIStatusBarStyle,
46      [ABI47_0_0RCTConvertEnumValue("UIStatusBarStyle", mapping, @(UIStatusBarStyleDefault), json) integerValue]);
47}
48
49ABI47_0_0RCT_ENUM_CONVERTER(
50    UIStatusBarAnimation,
51    (@{
52      @"none" : @(UIStatusBarAnimationNone),
53      @"fade" : @(UIStatusBarAnimationFade),
54      @"slide" : @(UIStatusBarAnimationSlide),
55    }),
56    UIStatusBarAnimationNone,
57    integerValue);
58
59@end
60
61@interface ABI47_0_0RCTStatusBarManager () <ABI47_0_0NativeStatusBarManagerIOSSpec>
62@end
63
64@implementation ABI47_0_0RCTStatusBarManager
65
66static BOOL ABI47_0_0RCTViewControllerBasedStatusBarAppearance()
67{
68  static BOOL value;
69  static dispatch_once_t onceToken;
70  dispatch_once(&onceToken, ^{
71    value =
72        [[[NSBundle mainBundle] objectForInfoDictionaryKey:@"UIViewControllerBasedStatusBarAppearance"]
73                ?: @YES boolValue];
74  });
75
76  return value;
77}
78
79ABI47_0_0RCT_EXPORT_MODULE()
80
81+ (BOOL)requiresMainQueueSetup
82{
83  return YES;
84}
85
86- (NSArray<NSString *> *)supportedEvents
87{
88  return @[ @"statusBarFrameDidChange", @"statusBarFrameWillChange" ];
89}
90
91- (void)startObserving
92{
93  NSNotificationCenter *nc = [NSNotificationCenter defaultCenter];
94  [nc addObserver:self
95         selector:@selector(applicationDidChangeStatusBarFrame:)
96             name:UIApplicationDidChangeStatusBarFrameNotification
97           object:nil];
98  [nc addObserver:self
99         selector:@selector(applicationWillChangeStatusBarFrame:)
100             name:UIApplicationWillChangeStatusBarFrameNotification
101           object:nil];
102}
103
104- (void)stopObserving
105{
106  [[NSNotificationCenter defaultCenter] removeObserver:self];
107}
108
109- (dispatch_queue_t)methodQueue
110{
111  return dispatch_get_main_queue();
112}
113
114- (void)emitEvent:(NSString *)eventName forNotification:(NSNotification *)notification
115{
116  CGRect frame = [notification.userInfo[UIApplicationStatusBarFrameUserInfoKey] CGRectValue];
117  NSDictionary *event = @{
118    @"frame" : @{
119      @"x" : @(frame.origin.x),
120      @"y" : @(frame.origin.y),
121      @"width" : @(frame.size.width),
122      @"height" : @(frame.size.height),
123    },
124  };
125  [self sendEventWithName:eventName body:event];
126}
127
128- (void)applicationDidChangeStatusBarFrame:(NSNotification *)notification
129{
130  [self emitEvent:@"statusBarFrameDidChange" forNotification:notification];
131}
132
133- (void)applicationWillChangeStatusBarFrame:(NSNotification *)notification
134{
135  [self emitEvent:@"statusBarFrameWillChange" forNotification:notification];
136}
137
138ABI47_0_0RCT_EXPORT_METHOD(getHeight : (ABI47_0_0RCTResponseSenderBlock)callback)
139{
140  callback(@[ @{
141    @"height" : @(ABI47_0_0RCTSharedApplication().statusBarFrame.size.height),
142  } ]);
143}
144
145ABI47_0_0RCT_EXPORT_METHOD(setStyle : (NSString *)style animated : (BOOL)animated)
146{
147  UIStatusBarStyle statusBarStyle = [ABI47_0_0RCTConvert UIStatusBarStyle:style];
148  if (ABI47_0_0RCTViewControllerBasedStatusBarAppearance()) {
149    ABI47_0_0RCTLogError(@"ABI47_0_0RCTStatusBarManager module requires that the \
150                UIViewControllerBasedStatusBarAppearance key in the Info.plist is set to NO");
151  } else {
152#pragma clang diagnostic push
153#pragma clang diagnostic ignored "-Wdeprecated-declarations"
154    [ABI47_0_0RCTSharedApplication() setStatusBarStyle:statusBarStyle animated:animated];
155  }
156#pragma clang diagnostic pop
157}
158
159ABI47_0_0RCT_EXPORT_METHOD(setHidden : (BOOL)hidden withAnimation : (NSString *)withAnimation)
160{
161  UIStatusBarAnimation animation = [ABI47_0_0RCTConvert UIStatusBarAnimation:withAnimation];
162  if (ABI47_0_0RCTViewControllerBasedStatusBarAppearance()) {
163    ABI47_0_0RCTLogError(@"ABI47_0_0RCTStatusBarManager module requires that the \
164                UIViewControllerBasedStatusBarAppearance key in the Info.plist is set to NO");
165  } else {
166#pragma clang diagnostic push
167#pragma clang diagnostic ignored "-Wdeprecated-declarations"
168    [ABI47_0_0RCTSharedApplication() setStatusBarHidden:hidden withAnimation:animation];
169#pragma clang diagnostic pop
170  }
171}
172
173ABI47_0_0RCT_EXPORT_METHOD(setNetworkActivityIndicatorVisible : (BOOL)visible)
174{
175  ABI47_0_0RCTSharedApplication().networkActivityIndicatorVisible = visible;
176}
177
178- (ABI47_0_0facebook::ABI47_0_0React::ModuleConstants<ABI47_0_0JS::NativeStatusBarManagerIOS::Constants>)getConstants
179{
180  __block ABI47_0_0facebook::ABI47_0_0React::ModuleConstants<ABI47_0_0JS::NativeStatusBarManagerIOS::Constants> constants;
181  ABI47_0_0RCTUnsafeExecuteOnMainQueueSync(^{
182    constants = ABI47_0_0facebook::ABI47_0_0React::typedConstants<ABI47_0_0JS::NativeStatusBarManagerIOS::Constants>({
183        .HEIGHT = ABI47_0_0RCTSharedApplication().statusBarFrame.size.height,
184        .DEFAULT_BACKGROUND_COLOR = std::nullopt,
185    });
186  });
187
188  return constants;
189}
190
191- (ABI47_0_0facebook::ABI47_0_0React::ModuleConstants<ABI47_0_0JS::NativeStatusBarManagerIOS::Constants>)constantsToExport
192{
193  return (ABI47_0_0facebook::ABI47_0_0React::ModuleConstants<ABI47_0_0JS::NativeStatusBarManagerIOS::Constants>)[self getConstants];
194}
195
196- (std::shared_ptr<ABI47_0_0facebook::ABI47_0_0React::TurboModule>)getTurboModule:
197    (const ABI47_0_0facebook::ABI47_0_0React::ObjCTurboModule::InitParams &)params
198{
199  return std::make_shared<ABI47_0_0facebook::ABI47_0_0React::NativeStatusBarManagerIOSSpecJSI>(params);
200}
201
202@end
203
204Class ABI47_0_0RCTStatusBarManagerCls(void)
205{
206  return ABI47_0_0RCTStatusBarManager.class;
207}
208