| Flutter iOS Embedder |

FlutterKeyboardManager.mm
Go to the documentation of this file.
1 // Copyright 2013 The Flutter Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #import "flutter/shell/platform/darwin/ios/framework/Source/FlutterKeyboardManager.h"
6 #include "flutter/fml/platform/darwin/message_loop_darwin.h"
7 #include "flutter/fml/platform/darwin/weak_nsobject.h"
8
9 static constexpr CFTimeInterval kDistantFuture = 1.0e10;
10
11 @interface FlutterKeyboardManager ()
12
13 /**
14 * The primary responders added by addPrimaryResponder.
15 */
16 @property(nonatomic, retain, readonly)
17 NSMutableArray<id<FlutterKeyPrimaryResponder>>* primaryResponders;
18
19 /**
20 * The secondary responders added by addSecondaryResponder.
21 */
22 @property(nonatomic, retain, readonly)
23 NSMutableArray<id<FlutterKeySecondaryResponder>>* secondaryResponders;
24
25 - (void)dispatchToSecondaryResponders:(nonnull FlutterUIPressProxy*)press
26 complete:(nonnull KeyEventCompleteCallback)callback
27 API_AVAILABLE(ios(13.4));
28
29 @end
30
31 @implementation FlutterKeyboardManager {
32 std::unique_ptr<fml::WeakNSObjectFactory<FlutterKeyboardManager>> _weakFactory;
33 }
34
35 - (nonnull instancetype)init {
36 self = [super init];
37 if (self != nil) {
38 _primaryResponders = [[NSMutableArray alloc] init];
39 _secondaryResponders = [[NSMutableArray alloc] init];
40 _weakFactory = std::make_unique<fml::WeakNSObjectFactory<FlutterKeyboardManager>>(self);
41 }
42 return self;
43 }
44
45 - (void)addPrimaryResponder:(nonnull id<FlutterKeyPrimaryResponder>)responder {
46 [_primaryResponders addObject:responder];
47 }
48
49 - (void)addSecondaryResponder:(nonnull id<FlutterKeySecondaryResponder>)responder {
50 [_secondaryResponders addObject:responder];
51 }
52
53 - (void)dealloc {
54 // It will be destroyed and invalidate its weak pointers
55 // before any other members are destroyed.
56 _weakFactory.reset();
57
58 [_primaryResponders removeAllObjects];
59 [_secondaryResponders removeAllObjects];
60 [_primaryResponders release];
61 [_secondaryResponders release];
62 _primaryResponders = nil;
63 _secondaryResponders = nil;
64 [super dealloc];
65 }
66
67 - (fml::WeakNSObject<FlutterKeyboardManager>)getWeakNSObject {
68 return _weakFactory->GetWeakNSObject();
69 }
70
71 - (void)handlePress:(nonnull FlutterUIPressProxy*)press
72 nextAction:(nonnull void (^)())next API_AVAILABLE(ios(13.4)) {
73 if (@available(iOS 13.4, *)) {
74 // no-op
75 } else {
76 return;
77 }
78
79 bool __block wasHandled = false;
80 KeyEventCompleteCallback completeCallback = ^void(bool handled, FlutterUIPressProxy* press) {
81 wasHandled = handled;
82 CFRunLoopStop(CFRunLoopGetCurrent());
83 };
84 switch (press.phase) {
85 case UIPressPhaseBegan:
86 case UIPressPhaseEnded: {
87 // Having no primary responders requires extra logic, but Flutter hard-codes
88 // all primary responders, so this is a situation that Flutter will never
89 // encounter.
90 NSAssert([_primaryResponders count] >= 0, @"At least one primary responder must be added.");
91
92 __block auto weakSelf = [self getWeakNSObject];
93 __block NSUInteger unreplied = [self.primaryResponders count];
94 __block BOOL anyHandled = false;
95 FlutterAsyncKeyCallback replyCallback = ^(BOOL handled) {
96 unreplied--;
97 NSAssert(unreplied >= 0, @"More primary responders replied than expected.");
98 anyHandled = anyHandled || handled;
99 if (unreplied == 0) {
100 if (!anyHandled && weakSelf) {
101 [weakSelf.get() dispatchToSecondaryResponders:press complete:completeCallback];
102 } else {
103 completeCallback(true, press);
104 }
105 }
106 };
107 for (id<FlutterKeyPrimaryResponder> responder in _primaryResponders) {
108 [responder handlePress:press callback:replyCallback];
109 }
110 // Create a nested run loop while we wait for a response from the
111 // framework. Once the completeCallback is called, this run loop will exit
112 // and the main one will resume. The completeCallback MUST be called, or
113 // the app will get stuck in this run loop indefinitely.
114 //
115 // We need to run in this mode so that UIKit doesn't give us new
116 // events until we are done processing this one.
117 CFRunLoopRunInMode(fml::MessageLoopDarwin::kMessageLoopCFRunLoopMode, kDistantFuture, NO);
118 break;
119 }
120 case UIPressPhaseChanged:
121 case UIPressPhaseCancelled:
122 case UIPressPhaseStationary:
123 break;
124 }
125 if (!wasHandled) {
126 next();
127 }
128 }
129
130 #pragma mark - Private
131
132 - (void)dispatchToSecondaryResponders:(nonnull FlutterUIPressProxy*)press
133 complete:(nonnull KeyEventCompleteCallback)callback
134 API_AVAILABLE(ios(13.4)) {
135 if (@available(iOS 13.4, *)) {
136 // no-op
137 } else {
138 callback(false, press);
139 return;
140 }
141
142 for (id<FlutterKeySecondaryResponder> responder in _secondaryResponders) {
143 if ([responder handlePress:press]) {
144 callback(true, press);
145 return;
146 }
147 }
148 callback(false, press);
149 }
150
151 @end
FlutterKeyPrimaryResponder-p
Definition: FlutterKeyPrimaryResponder.h:19
FlutterKeySecondaryResponder-p
Definition: FlutterKeySecondaryResponder.h:17
FlutterAsyncKeyCallback
void(^ FlutterAsyncKeyCallback)(BOOL handled)
Definition: FlutterKeyPrimaryResponder.h:10
KeyEventCompleteCallback
void(^ KeyEventCompleteCallback)(bool, FlutterUIPressProxy *_Nonnull) API_AVAILABLE(ios(13.4))
Definition: FlutterKeyboardManager.h:17
FlutterKeyboardManager.h
_weakFactory
std::unique_ptr< fml::WeakPtrFactory< FlutterDartVMServicePublisher > > _weakFactory
Definition: FlutterDartVMServicePublisher.mm:161
FlutterUIPressProxy
Definition: FlutterUIPressProxy.h:17
kDistantFuture
static constexpr CFTimeInterval kDistantFuture
Definition: FlutterKeyboardManager.mm:9
FlutterKeyboardManager
Definition: FlutterKeyboardManager.h:53