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_0RCTKeyboardObserver.h"
9
10#import <ABI47_0_0FBReactNativeSpec/ABI47_0_0FBReactNativeSpec.h>
11#import <ABI47_0_0React/ABI47_0_0RCTEventDispatcherProtocol.h>
12
13#import "ABI47_0_0CoreModulesPlugins.h"
14
15static NSDictionary *ABI47_0_0RCTParseKeyboardNotification(NSNotification *notification);
16
17@interface ABI47_0_0RCTKeyboardObserver () <ABI47_0_0NativeKeyboardObserverSpec>
18@end
19
20@implementation ABI47_0_0RCTKeyboardObserver
21
22ABI47_0_0RCT_EXPORT_MODULE()
23
24- (void)startObserving
25{
26  NSNotificationCenter *nc = [NSNotificationCenter defaultCenter];
27
28#define ADD_KEYBOARD_HANDLER(NAME, SELECTOR) [nc addObserver:self selector:@selector(SELECTOR:) name:NAME object:nil]
29
30  ADD_KEYBOARD_HANDLER(UIKeyboardWillShowNotification, keyboardWillShow);
31  ADD_KEYBOARD_HANDLER(UIKeyboardDidShowNotification, keyboardDidShow);
32  ADD_KEYBOARD_HANDLER(UIKeyboardWillHideNotification, keyboardWillHide);
33  ADD_KEYBOARD_HANDLER(UIKeyboardDidHideNotification, keyboardDidHide);
34  ADD_KEYBOARD_HANDLER(UIKeyboardWillChangeFrameNotification, keyboardWillChangeFrame);
35  ADD_KEYBOARD_HANDLER(UIKeyboardDidChangeFrameNotification, keyboardDidChangeFrame);
36
37#undef ADD_KEYBOARD_HANDLER
38}
39
40- (NSArray<NSString *> *)supportedEvents
41{
42  return @[
43    @"keyboardWillShow",
44    @"keyboardDidShow",
45    @"keyboardWillHide",
46    @"keyboardDidHide",
47    @"keyboardWillChangeFrame",
48    @"keyboardDidChangeFrame"
49  ];
50}
51
52- (void)stopObserving
53{
54  [[NSNotificationCenter defaultCenter] removeObserver:self];
55}
56
57// Bridge might be already invalidated by the time the keyboard is about to be dismissed.
58// This might happen, for example, when reload from the packager is performed.
59// Thus we need to check against nil here.
60#define IMPLEMENT_KEYBOARD_HANDLER(EVENT)                                              \
61  -(void)EVENT : (NSNotification *)notification                                        \
62  {                                                                                    \
63    if (!self.callableJSModules) {                                                     \
64      return;                                                                          \
65    }                                                                                  \
66    [self sendEventWithName:@ #EVENT body:ABI47_0_0RCTParseKeyboardNotification(notification)]; \
67  }
68
69IMPLEMENT_KEYBOARD_HANDLER(keyboardWillShow)
70IMPLEMENT_KEYBOARD_HANDLER(keyboardDidShow)
71IMPLEMENT_KEYBOARD_HANDLER(keyboardWillHide)
72IMPLEMENT_KEYBOARD_HANDLER(keyboardDidHide)
73IMPLEMENT_KEYBOARD_HANDLER(keyboardWillChangeFrame)
74IMPLEMENT_KEYBOARD_HANDLER(keyboardDidChangeFrame)
75
76- (std::shared_ptr<ABI47_0_0facebook::ABI47_0_0React::TurboModule>)getTurboModule:
77    (const ABI47_0_0facebook::ABI47_0_0React::ObjCTurboModule::InitParams &)params
78{
79  return std::make_shared<ABI47_0_0facebook::ABI47_0_0React::NativeKeyboardObserverSpecJSI>(params);
80}
81
82@end
83
84NS_INLINE NSDictionary *ABI47_0_0RCTRectDictionaryValue(CGRect rect)
85{
86  return @{
87    @"screenX" : @(rect.origin.x),
88    @"screenY" : @(rect.origin.y),
89    @"width" : @(rect.size.width),
90    @"height" : @(rect.size.height),
91  };
92}
93
94static NSString *ABI47_0_0RCTAnimationNameForCurve(UIViewAnimationCurve curve)
95{
96  switch (curve) {
97    case UIViewAnimationCurveEaseIn:
98      return @"easeIn";
99    case UIViewAnimationCurveEaseInOut:
100      return @"easeInEaseOut";
101    case UIViewAnimationCurveEaseOut:
102      return @"easeOut";
103    case UIViewAnimationCurveLinear:
104      return @"linear";
105    default:
106      return @"keyboard";
107  }
108}
109
110static NSDictionary *ABI47_0_0RCTParseKeyboardNotification(NSNotification *notification)
111{
112  NSDictionary *userInfo = notification.userInfo;
113  CGRect beginFrame = [userInfo[UIKeyboardFrameBeginUserInfoKey] CGRectValue];
114  CGRect endFrame = [userInfo[UIKeyboardFrameEndUserInfoKey] CGRectValue];
115  NSTimeInterval duration = [userInfo[UIKeyboardAnimationDurationUserInfoKey] doubleValue];
116  UIViewAnimationCurve curve =
117      static_cast<UIViewAnimationCurve>([userInfo[UIKeyboardAnimationCurveUserInfoKey] integerValue]);
118  NSInteger isLocalUserInfoKey = [userInfo[UIKeyboardIsLocalUserInfoKey] integerValue];
119
120  return @{
121    @"startCoordinates" : ABI47_0_0RCTRectDictionaryValue(beginFrame),
122    @"endCoordinates" : ABI47_0_0RCTRectDictionaryValue(endFrame),
123    @"duration" : @(duration * 1000.0), // ms
124    @"easing" : ABI47_0_0RCTAnimationNameForCurve(curve),
125    @"isEventFromThisApp" : isLocalUserInfoKey == 1 ? @YES : @NO,
126  };
127}
128
129Class ABI47_0_0RCTKeyboardObserverCls(void)
130{
131  return ABI47_0_0RCTKeyboardObserver.class;
132}
133