5#import <OCMock/OCMock.h>
6#import <XCTest/XCTest.h>
8#include "flutter/fml/platform/darwin/message_loop_darwin.h"
9#import "flutter/lib/ui/window/platform_configuration.h"
10#include "flutter/lib/ui/window/pointer_data.h"
11#import "flutter/lib/ui/window/viewport_metrics.h"
12#import "flutter/shell/platform/darwin/common/framework/Headers/FlutterBinaryMessenger.h"
13#import "flutter/shell/platform/darwin/common/framework/Headers/FlutterMacros.h"
14#import "flutter/shell/platform/darwin/ios/framework/Headers/FlutterViewController.h"
15#import "flutter/shell/platform/darwin/ios/framework/Source/FlutterEmbedderKeyResponder.h"
16#import "flutter/shell/platform/darwin/ios/framework/Source/FlutterFakeKeyEvents.h"
17#import "flutter/shell/platform/darwin/ios/framework/Source/FlutterTextInputPlugin.h"
18#import "flutter/shell/platform/darwin/ios/framework/Source/FlutterView.h"
19#import "flutter/shell/platform/darwin/ios/framework/Source/FlutterViewController_Internal.h"
20#import "flutter/shell/platform/darwin/ios/framework/Source/UIViewController+FlutterScreenAndSceneIfLoaded.h"
21#import "flutter/shell/platform/darwin/ios/framework/Source/vsync_waiter_ios.h"
22#import "flutter/shell/platform/embedder/embedder.h"
23#import "flutter/third_party/spring_animation/spring_animation.h"
33 userData:(nullable
void*)userData;
34- (
fml::RefPtr<fml::TaskRunner>)uiTaskRunner;
48 userData:(nullable
void*)userData;
58 _didCallNotifyLowMemory = YES;
63 userData:(
void*)userData API_AVAILABLE(
ios(9.0)) {
78- (
BOOL)createShell:(NSString*)entrypoint
79 libraryURI:(NSString*)libraryURI
80 initialRoute:(NSString*)initialRoute;
114@property(nonatomic, retain, readonly)
115 NSMutableArray<id<FlutterKeyPrimaryResponder>>* primaryResponders;
124@property(nonatomic, assign)
double targetViewInsetBottom;
125@property(nonatomic, assign)
BOOL isKeyboardInOrTransitioningFromBackground;
126@property(nonatomic, assign)
BOOL keyboardAnimationIsShowing;
127@property(nonatomic, strong)
VSyncClient* keyboardAnimationVSyncClient;
128@property(nonatomic, strong)
VSyncClient* touchRateCorrectionVSyncClient;
130- (void)createTouchRateCorrectionVSyncClientIfNeeded;
131- (void)surfaceUpdated:(
BOOL)appeared;
132- (void)performOrientationUpdate:(UIInterfaceOrientationMask)new_preferences;
134 nextAction:(
void (^)())next API_AVAILABLE(
ios(13.4));
135- (void)discreteScrollEvent:(UIPanGestureRecognizer*)recognizer;
136- (void)updateViewportMetricsIfNeeded;
137- (void)onUserSettingsChanged:(NSNotification*)notification;
138- (void)applicationWillTerminate:(NSNotification*)notification;
139- (void)goToApplicationLifecycle:(nonnull NSString*)state;
140- (void)handleKeyboardNotification:(NSNotification*)notification;
141- (CGFloat)calculateKeyboardInset:(CGRect)keyboardFrame keyboardMode:(
int)keyboardMode;
142- (
BOOL)shouldIgnoreKeyboardNotification:(NSNotification*)notification;
143- (FlutterKeyboardMode)calculateKeyboardAttachMode:(NSNotification*)notification;
144- (CGFloat)calculateMultitaskingAdjustment:(CGRect)screenRect keyboardFrame:(CGRect)keyboardFrame;
145- (void)startKeyBoardAnimation:(NSTimeInterval)duration;
146- (UIView*)keyboardAnimationView;
148- (void)setUpKeyboardSpringAnimationIfNeeded:(CAAnimation*)keyboardAnimation;
149- (void)setUpKeyboardAnimationVsyncClient:
151- (void)ensureViewportMetricsIsCorrect;
152- (void)invalidateKeyboardAnimationVSyncClient;
154- (
flutter::PointerData)generatePointerDataForFake;
156 initialRoute:(nullable NSString*)initialRoute;
157- (void)applicationBecameActive:(NSNotification*)notification;
158- (void)applicationWillResignActive:(NSNotification*)notification;
159- (void)applicationWillTerminate:(NSNotification*)notification;
160- (void)applicationDidEnterBackground:(NSNotification*)notification;
161- (void)applicationWillEnterForeground:(NSNotification*)notification;
162- (void)sceneBecameActive:(NSNotification*)notification API_AVAILABLE(
ios(13.0));
163- (void)sceneWillResignActive:(NSNotification*)notification API_AVAILABLE(
ios(13.0));
164- (void)sceneWillDisconnect:(NSNotification*)notification API_AVAILABLE(
ios(13.0));
165- (void)sceneDidEnterBackground:(NSNotification*)notification API_AVAILABLE(
ios(13.0));
166- (void)sceneWillEnterForeground:(NSNotification*)notification API_AVAILABLE(
ios(13.0));
167- (void)triggerTouchRateCorrectionIfNeeded:(NSSet*)touches;
171@property(nonatomic, strong)
id mockEngine;
172@property(nonatomic, strong)
id mockTextInputPlugin;
173@property(nonatomic, strong)
id messageSent;
174- (void)sendMessage:(
id _Nullable)message reply:(
FlutterReply _Nullable)callback;
179@property(nonatomic, readwrite) UITouchPhase phase;
185- (CADisplayLink*)getDisplayLink;
195 self.messageSent = nil;
201 [
self.mockEngine stopMocking];
202 self.mockEngine = nil;
203 self.mockTextInputPlugin = nil;
204 self.messageSent = nil;
208 UIScreen* mockScreen = OCMClassMock([UIScreen
class]);
210 CGRect screenBounds = CGRectMake(0, 0, 1170, 2532);
211 OCMStub([mockScreen
bounds]).andReturn(screenBounds);
212 CGFloat screenScale = 1;
213 OCMStub([mockScreen
scale]).andReturn(screenScale);
219 screen:(UIScreen*)screen
220 viewFrame:(CGRect)viewFrame
221 convertedFrame:(CGRect)convertedFrame {
222 OCMStub([viewControllerMock flutterScreenIfViewLoaded]).andReturn(screen);
223 id mockView = OCMClassMock([UIView
class]);
224 OCMStub([mockView
frame]).andReturn(viewFrame);
225 OCMStub([mockView convertRect:viewFrame toCoordinateSpace:[OCMArg
any]])
226 .andReturn(convertedFrame);
227 OCMStub([viewControllerMock viewIfLoaded]).andReturn(mockView);
241 OCMVerify([viewControllerMock createTouchRateCorrectionVSyncClientIfNeeded]);
254 CAAnimation* keyboardAnimation =
257 OCMVerify([viewControllerMock setUpKeyboardSpringAnimationIfNeeded:keyboardAnimation]);
267 UIScreen* screen = [
self setUpMockScreen];
268 CGRect viewFrame = screen.bounds;
269 [
self setUpMockView:viewControllerMock
272 convertedFrame:viewFrame];
277 XCTAssertTrue(keyboardSpringAnimation == nil);
280 CABasicAnimation* nonSpringAnimation = [CABasicAnimation animation];
281 nonSpringAnimation.duration = 1.0;
282 nonSpringAnimation.fromValue = [NSNumber numberWithFloat:0.0];
283 nonSpringAnimation.toValue = [NSNumber numberWithFloat:1.0];
284 nonSpringAnimation.keyPath =
@"position";
288 XCTAssertTrue(keyboardSpringAnimation == nil);
291 CASpringAnimation* springAnimation = [CASpringAnimation animation];
292 springAnimation.mass = 1.0;
293 springAnimation.stiffness = 100.0;
294 springAnimation.damping = 10.0;
295 springAnimation.keyPath =
@"position";
296 springAnimation.fromValue = [NSValue valueWithCGPoint:CGPointMake(0, 0)];
297 springAnimation.toValue = [NSValue valueWithCGPoint:CGPointMake(100, 100)];
300 XCTAssertTrue(keyboardSpringAnimation != nil);
310 UIScreen* screen = [
self setUpMockScreen];
311 CGRect viewFrame = screen.bounds;
312 [
self setUpMockView:viewControllerMock
315 convertedFrame:viewFrame];
318 CGFloat screenHeight = screen.bounds.size.height;
319 CGFloat screenWidth = screen.bounds.size.height;
322 CGRect initialShowKeyboardBeginFrame = CGRectMake(0, screenHeight, screenWidth, 250);
323 CGRect initialShowKeyboardEndFrame = CGRectMake(0, screenHeight - 250, screenWidth, 500);
324 NSNotification* fakeNotification = [NSNotification
325 notificationWithName:UIKeyboardWillChangeFrameNotification
328 @"UIKeyboardFrameBeginUserInfoKey" : @(initialShowKeyboardBeginFrame),
329 @"UIKeyboardFrameEndUserInfoKey" : @(initialShowKeyboardEndFrame),
330 @"UIKeyboardAnimationDurationUserInfoKey" : @(0.25),
331 @"UIKeyboardIsLocalUserInfoKey" : @(isLocal)
336 XCTAssertTrue(isShowingAnimation1);
339 CGRect compoundingShowKeyboardBeginFrame = CGRectMake(0, screenHeight - 250, screenWidth, 250);
340 CGRect compoundingShowKeyboardEndFrame = CGRectMake(0, screenHeight - 500, screenWidth, 500);
341 fakeNotification = [NSNotification
342 notificationWithName:UIKeyboardWillChangeFrameNotification
345 @"UIKeyboardFrameBeginUserInfoKey" : @(compoundingShowKeyboardBeginFrame),
346 @"UIKeyboardFrameEndUserInfoKey" : @(compoundingShowKeyboardEndFrame),
347 @"UIKeyboardAnimationDurationUserInfoKey" : @(0.25),
348 @"UIKeyboardIsLocalUserInfoKey" : @(isLocal)
353 XCTAssertTrue(isShowingAnimation2);
354 XCTAssertTrue(isShowingAnimation1 == isShowingAnimation2);
357 CGRect initialHideKeyboardBeginFrame = CGRectMake(0, screenHeight - 500, screenWidth, 250);
358 CGRect initialHideKeyboardEndFrame = CGRectMake(0, screenHeight - 250, screenWidth, 500);
359 fakeNotification = [NSNotification
360 notificationWithName:UIKeyboardWillChangeFrameNotification
363 @"UIKeyboardFrameBeginUserInfoKey" : @(initialHideKeyboardBeginFrame),
364 @"UIKeyboardFrameEndUserInfoKey" : @(initialHideKeyboardEndFrame),
365 @"UIKeyboardAnimationDurationUserInfoKey" : @(0.25),
366 @"UIKeyboardIsLocalUserInfoKey" : @(isLocal)
371 XCTAssertFalse(isShowingAnimation3);
372 XCTAssertTrue(isShowingAnimation2 != isShowingAnimation3);
375 CGRect compoundingHideKeyboardBeginFrame = CGRectMake(0, screenHeight - 250, screenWidth, 250);
376 CGRect compoundingHideKeyboardEndFrame = CGRectMake(0, screenHeight, screenWidth, 500);
377 fakeNotification = [NSNotification
378 notificationWithName:UIKeyboardWillChangeFrameNotification
381 @"UIKeyboardFrameBeginUserInfoKey" : @(compoundingHideKeyboardBeginFrame),
382 @"UIKeyboardFrameEndUserInfoKey" : @(compoundingHideKeyboardEndFrame),
383 @"UIKeyboardAnimationDurationUserInfoKey" : @(0.25),
384 @"UIKeyboardIsLocalUserInfoKey" : @(isLocal)
389 XCTAssertFalse(isShowingAnimation4);
390 XCTAssertTrue(isShowingAnimation3 == isShowingAnimation4);
400 UIScreen* screen = [
self setUpMockScreen];
401 CGRect viewFrame = screen.bounds;
402 [
self setUpMockView:viewControllerMock
405 convertedFrame:viewFrame];
407 CGFloat screenWidth = screen.bounds.size.width;
408 CGFloat screenHeight = screen.bounds.size.height;
409 CGRect emptyKeyboard = CGRectZero;
410 CGRect zeroHeightKeyboard = CGRectMake(0, 0, screenWidth, 0);
411 CGRect validKeyboardEndFrame = CGRectMake(0, screenHeight - 320, screenWidth, 320);
415 NSNotification* notification =
416 [NSNotification notificationWithName:UIKeyboardWillHideNotification
419 @"UIKeyboardFrameEndUserInfoKey" : @(validKeyboardEndFrame),
420 @"UIKeyboardAnimationDurationUserInfoKey" : @0.25,
421 @"UIKeyboardIsLocalUserInfoKey" : @(isLocal)
425 XCTAssertTrue(shouldIgnore == NO);
429 notification = [NSNotification notificationWithName:UIKeyboardWillChangeFrameNotification
432 @"UIKeyboardFrameEndUserInfoKey" : @(emptyKeyboard),
433 @"UIKeyboardAnimationDurationUserInfoKey" : @0.25,
434 @"UIKeyboardIsLocalUserInfoKey" : @(isLocal)
437 XCTAssertTrue(shouldIgnore == YES);
442 [NSNotification notificationWithName:UIKeyboardWillChangeFrameNotification
445 @"UIKeyboardFrameEndUserInfoKey" : @(zeroHeightKeyboard),
446 @"UIKeyboardAnimationDurationUserInfoKey" : @0.25,
447 @"UIKeyboardIsLocalUserInfoKey" : @(isLocal)
450 XCTAssertTrue(shouldIgnore == NO);
455 [NSNotification notificationWithName:UIKeyboardWillChangeFrameNotification
458 @"UIKeyboardFrameEndUserInfoKey" : @(validKeyboardEndFrame),
459 @"UIKeyboardAnimationDurationUserInfoKey" : @0.25,
460 @"UIKeyboardIsLocalUserInfoKey" : @(isLocal)
463 XCTAssertTrue(shouldIgnore == YES);
468 [NSNotification notificationWithName:UIKeyboardWillChangeFrameNotification
471 @"UIKeyboardFrameEndUserInfoKey" : @(validKeyboardEndFrame),
472 @"UIKeyboardAnimationDurationUserInfoKey" : @0.25,
473 @"UIKeyboardIsLocalUserInfoKey" : @(isLocal)
476 XCTAssertTrue(shouldIgnore == NO);
478 if (@available(iOS 13.0, *)) {
482 OCMStub([viewControllerMock isKeyboardInOrTransitioningFromBackground]).andReturn(YES);
486 [NSNotification notificationWithName:UIKeyboardWillChangeFrameNotification
489 @"UIKeyboardFrameEndUserInfoKey" : @(validKeyboardEndFrame),
490 @"UIKeyboardAnimationDurationUserInfoKey" : @0.25,
491 @"UIKeyboardIsLocalUserInfoKey" : @(isLocal)
494 XCTAssertTrue(shouldIgnore == YES);
521 const int delayTime = 1;
522 [engine
uiTaskRunner]->PostTask([] { sleep(delayTime); });
523 XCTestExpectation*
expectation = [
self expectationWithDescription:@"keyboard animation callback"];
525 __block CFTimeInterval fulfillTime;
527 fulfillTime = CACurrentMediaTime();
528 [expectation fulfill];
530 CFTimeInterval startTime = CACurrentMediaTime();
532 [
self waitForExpectationsWithTimeout:5.0 handler:nil];
533 XCTAssertTrue(fulfillTime - startTime > delayTime);
544 UIScreen* screen = [
self setUpMockScreen];
545 CGRect viewFrame = screen.bounds;
546 [
self setUpMockView:viewControllerMock
549 convertedFrame:viewFrame];
551 CGFloat screenWidth = screen.bounds.size.width;
552 CGFloat screenHeight = screen.bounds.size.height;
555 CGRect keyboardFrame = CGRectZero;
556 NSNotification* notification =
557 [NSNotification notificationWithName:UIKeyboardWillHideNotification
560 @"UIKeyboardFrameEndUserInfoKey" : @(keyboardFrame),
561 @"UIKeyboardAnimationDurationUserInfoKey" : @0.25,
562 @"UIKeyboardIsLocalUserInfoKey" : @(YES)
565 XCTAssertTrue(keyboardMode == FlutterKeyboardModeHidden);
568 keyboardFrame = CGRectZero;
569 notification = [NSNotification notificationWithName:UIKeyboardWillChangeFrameNotification
572 @"UIKeyboardFrameEndUserInfoKey" : @(keyboardFrame),
573 @"UIKeyboardAnimationDurationUserInfoKey" : @0.25,
574 @"UIKeyboardIsLocalUserInfoKey" : @(YES)
577 XCTAssertTrue(keyboardMode == FlutterKeyboardModeFloating);
580 keyboardFrame = CGRectMake(0, 0, screenWidth, 0);
581 notification = [NSNotification notificationWithName:UIKeyboardWillChangeFrameNotification
584 @"UIKeyboardFrameEndUserInfoKey" : @(keyboardFrame),
585 @"UIKeyboardAnimationDurationUserInfoKey" : @0.25,
586 @"UIKeyboardIsLocalUserInfoKey" : @(YES)
589 XCTAssertTrue(keyboardMode == FlutterKeyboardModeHidden);
592 keyboardFrame = CGRectMake(0, 0, 320, 320);
593 notification = [NSNotification notificationWithName:UIKeyboardWillChangeFrameNotification
596 @"UIKeyboardFrameEndUserInfoKey" : @(keyboardFrame),
597 @"UIKeyboardAnimationDurationUserInfoKey" : @0.25,
598 @"UIKeyboardIsLocalUserInfoKey" : @(YES)
601 XCTAssertTrue(keyboardMode == FlutterKeyboardModeFloating);
604 keyboardFrame = CGRectMake(0, 0, screenWidth, 320);
605 notification = [NSNotification notificationWithName:UIKeyboardWillChangeFrameNotification
608 @"UIKeyboardFrameEndUserInfoKey" : @(keyboardFrame),
609 @"UIKeyboardAnimationDurationUserInfoKey" : @0.25,
610 @"UIKeyboardIsLocalUserInfoKey" : @(YES)
613 XCTAssertTrue(keyboardMode == FlutterKeyboardModeFloating);
616 keyboardFrame = CGRectMake(0, screenHeight - 320, screenWidth, 320);
617 notification = [NSNotification notificationWithName:UIKeyboardWillChangeFrameNotification
620 @"UIKeyboardFrameEndUserInfoKey" : @(keyboardFrame),
621 @"UIKeyboardAnimationDurationUserInfoKey" : @0.25,
622 @"UIKeyboardIsLocalUserInfoKey" : @(YES)
625 XCTAssertTrue(keyboardMode == FlutterKeyboardModeDocked);
628 CGFloat longDecimalHeight = 320.666666666666666;
629 keyboardFrame = CGRectMake(0, screenHeight - longDecimalHeight, screenWidth, longDecimalHeight);
630 notification = [NSNotification notificationWithName:UIKeyboardWillChangeFrameNotification
633 @"UIKeyboardFrameEndUserInfoKey" : @(keyboardFrame),
634 @"UIKeyboardAnimationDurationUserInfoKey" : @0.25,
635 @"UIKeyboardIsLocalUserInfoKey" : @(YES)
638 XCTAssertTrue(keyboardMode == FlutterKeyboardModeDocked);
641 keyboardFrame = CGRectMake(0, screenHeight - .0000001, screenWidth, longDecimalHeight);
642 notification = [NSNotification notificationWithName:UIKeyboardWillChangeFrameNotification
645 @"UIKeyboardFrameEndUserInfoKey" : @(keyboardFrame),
646 @"UIKeyboardAnimationDurationUserInfoKey" : @0.25,
647 @"UIKeyboardIsLocalUserInfoKey" : @(YES)
650 XCTAssertTrue(keyboardMode == FlutterKeyboardModeHidden);
653 keyboardFrame = CGRectMake(0, screenHeight, screenWidth, 320);
654 notification = [NSNotification notificationWithName:UIKeyboardWillChangeFrameNotification
657 @"UIKeyboardFrameEndUserInfoKey" : @(keyboardFrame),
658 @"UIKeyboardAnimationDurationUserInfoKey" : @0.25,
659 @"UIKeyboardIsLocalUserInfoKey" : @(YES)
662 XCTAssertTrue(keyboardMode == FlutterKeyboardModeHidden);
673 UIScreen* screen = [
self setUpMockScreen];
674 CGFloat screenWidth = screen.bounds.size.width;
675 CGFloat screenHeight = screen.bounds.size.height;
676 CGRect screenRect = screen.bounds;
677 CGRect viewOrigFrame = CGRectMake(0, 0, 320, screenHeight - 40);
678 CGRect convertedViewFrame = CGRectMake(20, 20, 320, screenHeight - 40);
679 CGRect keyboardFrame = CGRectMake(20, screenHeight - 320, screenWidth, 300);
680 id mockView = [
self setUpMockView:viewControllerMock
682 viewFrame:viewOrigFrame
683 convertedFrame:convertedViewFrame];
684 id mockTraitCollection = OCMClassMock([UITraitCollection
class]);
685 OCMStub([mockTraitCollection userInterfaceIdiom]).andReturn(UIUserInterfaceIdiomPad);
686 OCMStub([mockTraitCollection horizontalSizeClass]).andReturn(UIUserInterfaceSizeClassCompact);
687 OCMStub([mockTraitCollection verticalSizeClass]).andReturn(UIUserInterfaceSizeClassRegular);
688 OCMStub([mockView traitCollection]).andReturn(mockTraitCollection);
692 XCTAssertTrue(adjustment == 20);
702 UIScreen* screen = [
self setUpMockScreen];
703 OCMStub([viewControllerMock flutterScreenIfViewLoaded]).andReturn(screen);
705 CGFloat screenWidth = screen.bounds.size.width;
706 CGFloat screenHeight = screen.bounds.size.height;
707 CGRect viewOrigFrame = CGRectMake(0, 0, 320, screenHeight - 40);
708 CGRect convertedViewFrame = CGRectMake(20, 20, 320, screenHeight - 40);
709 CGRect keyboardFrame = CGRectMake(20, screenHeight - 320, screenWidth, 300);
711 [
self setUpMockView:viewControllerMock
713 viewFrame:viewOrigFrame
714 convertedFrame:convertedViewFrame];
718 XCTAssertTrue(
inset == 300 * screen.scale);
728 UIScreen* screen = [
self setUpMockScreen];
729 CGFloat screenWidth = screen.bounds.size.width;
730 CGFloat screenHeight = screen.bounds.size.height;
731 CGRect keyboardFrame = CGRectMake(0, screenHeight - 320, screenWidth, 320);
732 CGRect viewFrame = screen.bounds;
734 NSNotification* notification =
735 [NSNotification notificationWithName:UIKeyboardWillShowNotification
738 @"UIKeyboardFrameEndUserInfoKey" : @(keyboardFrame),
739 @"UIKeyboardAnimationDurationUserInfoKey" : @0.25,
740 @"UIKeyboardIsLocalUserInfoKey" : @(isLocal)
743 [
self setUpMockView:viewControllerMock
746 convertedFrame:viewFrame];
748 XCTestExpectation*
expectation = [
self expectationWithDescription:@"update viewport"];
749 OCMStub([viewControllerMock updateViewportMetricsIfNeeded]).andDo(^(NSInvocation* invocation) {
750 [expectation fulfill];
755 OCMVerify([viewControllerMock startKeyBoardAnimation:0.25]);
756 [
self waitForExpectationsWithTimeout:5.0 handler:nil];
767 CGRect keyboardFrame = CGRectZero;
769 NSNotification* fakeNotification =
770 [NSNotification notificationWithName:UIKeyboardWillHideNotification
773 @"UIKeyboardFrameEndUserInfoKey" : @(keyboardFrame),
774 @"UIKeyboardAnimationDurationUserInfoKey" : @(0.25),
775 @"UIKeyboardIsLocalUserInfoKey" : @(isLocal)
791 OCMVerify([viewControllerMock ensureViewportMetricsIsCorrect]);
792 OCMVerify([viewControllerMock invalidateKeyboardAnimationVSyncClient]);
803 id viewControllerMock = OCMPartialMock(viewControllerA);
804 OCMStub([viewControllerMock surfaceUpdated:NO]);
808 OCMReject([viewControllerMock surfaceUpdated:[OCMArg
any]]);
818 OCMStub([viewControllerMock goToApplicationLifecycle:
@"AppLifecycleState.detached"]);
821 OCMVerify([viewControllerMock goToApplicationLifecycle:
@"AppLifecycleState.detached"]);
836 OCMStub([viewControllerMock surfaceUpdated:NO]);
839 OCMVerify([viewControllerMock surfaceUpdated:NO]);
841 XCTAssertNil(weakViewController);
869 OCMVerify(never(), [viewControllerA onUserSettingsChanged:nil]);
897 OCMVerify(never(), [viewControllerA onUserSettingsChanged:nil]);
942 OCMVerify(never(), [
mockEngine updateViewportMetrics:viewportMetrics]);
953 OCMExpect([
mockEngine updateViewportMetrics:viewportMetrics]).ignoringNonObjectArgs();
965 UIScreen* screen = [
self setUpMockScreen];
966 OCMStub([viewControllerMock flutterScreenIfViewLoaded]).andReturn(screen);
969 id mockCoordinator = OCMProtocolMock(
@protocol(UIViewControllerTransitionCoordinator));
970 OCMStub([mockCoordinator transitionDuration]).andReturn(0.5);
987 UIScreen* screen = [
self setUpMockScreen];
988 OCMStub([viewControllerMock flutterScreenIfViewLoaded]).andReturn(screen);
992 NSTimeInterval transitionDuration = 0.5;
993 id mockCoordinator = OCMProtocolMock(
@protocol(UIViewControllerTransitionCoordinator));
994 OCMStub([mockCoordinator transitionDuration]).andReturn(transitionDuration);
997 OCMExpect([
mockEngine updateViewportMetrics:viewportMetrics]).ignoringNonObjectArgs();
1006 XCTWaiterResult
result = [XCTWaiter
1007 waitForExpectations:@[ [
self expectationWithDescription:@"Waiting for rotation duration"] ]
1008 timeout:transitionDuration];
1009 XCTAssertEqual(
result, XCTWaiterResultTimedOut);
1021 UIScreen* screen = [
self setUpMockScreen];
1022 OCMStub([viewControllerMock flutterScreenIfViewLoaded]).andReturn(screen);
1026 id mockCoordinator = OCMProtocolMock(
@protocol(UIViewControllerTransitionCoordinator));
1027 OCMStub([mockCoordinator transitionDuration]).andReturn(0);
1030 OCMExpect([
mockEngine updateViewportMetrics:viewportMetrics]).ignoringNonObjectArgs();
1050 UIView* view = viewControllerA.
view;
1051 XCTAssertNotNil(view);
1064 XCTAssertNotNil(view);
1079 XCTAssertNotNil(view);
1101 [(NSArray<id<FlutterKeyPrimaryResponder>>*)keyboardManager.
primaryResponders firstObject];
1102 sendEvent = [keyPrimaryResponder
sendEvent];
1106 sendEvent({}, nil, nil);
1121 XCTAssertNotNil(view);
1130 XCTAssertNotNil(vc);
1139 __weak UIView* weakView;
1152 XCTAssertNil(weakViewController);
1153 XCTAssertNil(weakView);
1156#pragma mark - Platform Brightness
1172 return [message[@"platformBrightness"] isEqualToString:@"light"];
1176 [settingsChannel stopMocking];
1194 return [message[@"platformBrightness"] isEqualToString:@"light"];
1198 [settingsChannel stopMocking];
1202 if (@available(iOS 13, *)) {
1211 id mockTraitCollection =
1212 [
self fakeTraitCollectionWithUserInterfaceStyle:UIUserInterfaceStyleDark];
1221 OCMStub([partialMockVC traitCollection]).andReturn(mockTraitCollection);
1224 [partialMockVC traitCollectionDidChange:nil];
1228 return [message[@"platformBrightness"] isEqualToString:@"dark"];
1232 [partialMockVC stopMocking];
1233 [settingsChannel stopMocking];
1234 [mockTraitCollection stopMocking];
1239- (UITraitCollection*)fakeTraitCollectionWithUserInterfaceStyle:(UIUserInterfaceStyle)style {
1240 id mockTraitCollection = OCMClassMock([UITraitCollection
class]);
1241 OCMStub([mockTraitCollection userInterfaceStyle]).andReturn(style);
1242 return mockTraitCollection;
1245#pragma mark - Platform Contrast
1248 if (@available(iOS 13, *)) {
1267 return [message[@"platformContrast"] isEqualToString:@"normal"];
1271 [settingsChannel stopMocking];
1275 if (@available(iOS 13, *)) {
1295 return [message[@"platformContrast"] isEqualToString:@"normal"];
1299 [settingsChannel stopMocking];
1303 if (@available(iOS 13, *)) {
1313 id mockTraitCollection = [
self fakeTraitCollectionWithContrast:UIAccessibilityContrastHigh];
1322 OCMStub([partialMockVC traitCollection]).andReturn(mockTraitCollection);
1325 [partialMockVC traitCollectionDidChange:mockTraitCollection];
1329 return [message[@"platformContrast"] isEqualToString:@"high"];
1333 [partialMockVC stopMocking];
1334 [settingsChannel stopMocking];
1335 [mockTraitCollection stopMocking];
1339 if (@available(iOS 13, *)) {
1349 OCMStub([partialMockViewController accessibilityIsOnOffSwitchLabelsEnabled]).andReturn(NO);
1352 int32_t
flags = [partialMockViewController accessibilityFlags];
1359 if (@available(iOS 13, *)) {
1369 OCMStub([partialMockViewController accessibilityIsOnOffSwitchLabelsEnabled]).andReturn(YES);
1372 int32_t
flags = [partialMockViewController accessibilityFlags];
1389 OCMVerify([mockNavigationChannel invokeMethod:
@"popRoute" arguments:nil]);
1391 [mockNavigationChannel stopMocking];
1395 [
self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskPortrait
1396 currentOrientation:UIInterfaceOrientationLandscapeLeft
1397 didChangeOrientation:YES
1398 resultingOrientation:UIInterfaceOrientationPortrait];
1400 [
self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskPortrait
1401 currentOrientation:UIInterfaceOrientationLandscapeRight
1402 didChangeOrientation:YES
1403 resultingOrientation:UIInterfaceOrientationPortrait];
1405 [
self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskPortrait
1406 currentOrientation:UIInterfaceOrientationPortraitUpsideDown
1407 didChangeOrientation:YES
1408 resultingOrientation:UIInterfaceOrientationPortrait];
1410 [
self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskPortraitUpsideDown
1411 currentOrientation:UIInterfaceOrientationLandscapeLeft
1412 didChangeOrientation:YES
1413 resultingOrientation:UIInterfaceOrientationPortraitUpsideDown];
1415 [
self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskPortraitUpsideDown
1416 currentOrientation:UIInterfaceOrientationLandscapeRight
1417 didChangeOrientation:YES
1418 resultingOrientation:UIInterfaceOrientationPortraitUpsideDown];
1420 [
self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskPortraitUpsideDown
1421 currentOrientation:UIInterfaceOrientationPortrait
1422 didChangeOrientation:YES
1423 resultingOrientation:UIInterfaceOrientationPortraitUpsideDown];
1425 [
self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskLandscape
1426 currentOrientation:UIInterfaceOrientationPortrait
1427 didChangeOrientation:YES
1428 resultingOrientation:UIInterfaceOrientationLandscapeLeft];
1430 [
self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskLandscape
1431 currentOrientation:UIInterfaceOrientationPortraitUpsideDown
1432 didChangeOrientation:YES
1433 resultingOrientation:UIInterfaceOrientationLandscapeLeft];
1435 [
self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskLandscapeLeft
1436 currentOrientation:UIInterfaceOrientationPortrait
1437 didChangeOrientation:YES
1438 resultingOrientation:UIInterfaceOrientationLandscapeLeft];
1440 [
self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskLandscapeLeft
1441 currentOrientation:UIInterfaceOrientationLandscapeRight
1442 didChangeOrientation:YES
1443 resultingOrientation:UIInterfaceOrientationLandscapeLeft];
1445 [
self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskLandscapeLeft
1446 currentOrientation:UIInterfaceOrientationPortraitUpsideDown
1447 didChangeOrientation:YES
1448 resultingOrientation:UIInterfaceOrientationLandscapeLeft];
1450 [
self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskLandscapeRight
1451 currentOrientation:UIInterfaceOrientationPortrait
1452 didChangeOrientation:YES
1453 resultingOrientation:UIInterfaceOrientationLandscapeRight];
1455 [
self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskLandscapeRight
1456 currentOrientation:UIInterfaceOrientationLandscapeLeft
1457 didChangeOrientation:YES
1458 resultingOrientation:UIInterfaceOrientationLandscapeRight];
1460 [
self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskLandscapeRight
1461 currentOrientation:UIInterfaceOrientationPortraitUpsideDown
1462 didChangeOrientation:YES
1463 resultingOrientation:UIInterfaceOrientationLandscapeRight];
1465 [
self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskAllButUpsideDown
1466 currentOrientation:UIInterfaceOrientationPortraitUpsideDown
1467 didChangeOrientation:YES
1468 resultingOrientation:UIInterfaceOrientationPortrait];
1472 [
self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskAll
1473 currentOrientation:UIInterfaceOrientationPortrait
1474 didChangeOrientation:NO
1475 resultingOrientation:static_cast<UIInterfaceOrientation>(0)];
1477 [
self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskAll
1478 currentOrientation:UIInterfaceOrientationPortraitUpsideDown
1479 didChangeOrientation:NO
1480 resultingOrientation:static_cast<UIInterfaceOrientation>(0)];
1482 [
self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskAll
1483 currentOrientation:UIInterfaceOrientationLandscapeLeft
1484 didChangeOrientation:NO
1485 resultingOrientation:static_cast<UIInterfaceOrientation>(0)];
1487 [
self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskAll
1488 currentOrientation:UIInterfaceOrientationLandscapeRight
1489 didChangeOrientation:NO
1490 resultingOrientation:static_cast<UIInterfaceOrientation>(0)];
1492 [
self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskAllButUpsideDown
1493 currentOrientation:UIInterfaceOrientationPortrait
1494 didChangeOrientation:NO
1495 resultingOrientation:static_cast<UIInterfaceOrientation>(0)];
1497 [
self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskAllButUpsideDown
1498 currentOrientation:UIInterfaceOrientationLandscapeLeft
1499 didChangeOrientation:NO
1500 resultingOrientation:static_cast<UIInterfaceOrientation>(0)];
1502 [
self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskAllButUpsideDown
1503 currentOrientation:UIInterfaceOrientationLandscapeRight
1504 didChangeOrientation:NO
1505 resultingOrientation:static_cast<UIInterfaceOrientation>(0)];
1507 [
self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskPortrait
1508 currentOrientation:UIInterfaceOrientationPortrait
1509 didChangeOrientation:NO
1510 resultingOrientation:static_cast<UIInterfaceOrientation>(0)];
1512 [
self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskPortraitUpsideDown
1513 currentOrientation:UIInterfaceOrientationPortraitUpsideDown
1514 didChangeOrientation:NO
1515 resultingOrientation:static_cast<UIInterfaceOrientation>(0)];
1517 [
self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskLandscape
1518 currentOrientation:UIInterfaceOrientationLandscapeLeft
1519 didChangeOrientation:NO
1520 resultingOrientation:static_cast<UIInterfaceOrientation>(0)];
1522 [
self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskLandscape
1523 currentOrientation:UIInterfaceOrientationLandscapeRight
1524 didChangeOrientation:NO
1525 resultingOrientation:static_cast<UIInterfaceOrientation>(0)];
1527 [
self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskLandscapeLeft
1528 currentOrientation:UIInterfaceOrientationLandscapeLeft
1529 didChangeOrientation:NO
1530 resultingOrientation:static_cast<UIInterfaceOrientation>(0)];
1532 [
self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskLandscapeRight
1533 currentOrientation:UIInterfaceOrientationLandscapeRight
1534 didChangeOrientation:NO
1535 resultingOrientation:static_cast<UIInterfaceOrientation>(0)];
1540- (void)orientationTestWithOrientationUpdate:(UIInterfaceOrientationMask)mask
1541 currentOrientation:(UIInterfaceOrientation)currentOrientation
1542 didChangeOrientation:(
BOOL)didChange
1543 resultingOrientation:(UIInterfaceOrientation)resultingOrientation {
1544 id mockApplication = OCMClassMock([UIApplication
class]);
1548 __block __weak
id weakPreferences;
1554 if (@available(iOS 16.0, *)) {
1555 mockWindowScene = OCMClassMock([UIWindowScene
class]);
1556 mockVC = OCMPartialMock(realVC);
1557 OCMStub([mockVC flutterWindowSceneIfViewLoaded]).andReturn(mockWindowScene);
1559 OCMReject([mockWindowScene requestGeometryUpdateWithPreferences:[OCMArg
any]
1560 errorHandler:[OCMArg
any]]);
1564 OCMExpect([mockWindowScene
1565 requestGeometryUpdateWithPreferences:[OCMArg checkWithBlock:^
BOOL(
1566 UIWindowSceneGeometryPreferencesIOS*
1568 weakPreferences = preferences;
1569 return preferences.interfaceOrientations == mask;
1571 errorHandler:[OCMArg any]]);
1573 OCMStub([mockApplication sharedApplication]).andReturn(mockApplication);
1574 OCMStub([mockApplication connectedScenes]).andReturn([NSSet setWithObject:mockWindowScene]);
1576 deviceMock = OCMPartialMock([UIDevice currentDevice]);
1578 OCMReject([deviceMock setValue:[OCMArg
any] forKey:
@"orientation"]);
1580 OCMExpect([deviceMock setValue:@(resultingOrientation) forKey:
@"orientation"]);
1582 if (@available(iOS 13.0, *)) {
1583 mockWindowScene = OCMClassMock([UIWindowScene
class]);
1584 mockVC = OCMPartialMock(realVC);
1585 OCMStub([mockVC flutterWindowSceneIfViewLoaded]).andReturn(mockWindowScene);
1586 OCMStub(((UIWindowScene*)mockWindowScene).interfaceOrientation)
1587 .andReturn(currentOrientation);
1589 OCMStub([mockApplication sharedApplication]).andReturn(mockApplication);
1590 OCMStub([mockApplication statusBarOrientation]).andReturn(currentOrientation);
1595 if (@available(iOS 16.0, *)) {
1596 OCMVerifyAll(mockWindowScene);
1598 OCMVerifyAll(deviceMock);
1601 [mockWindowScene stopMocking];
1602 [deviceMock stopMocking];
1603 [mockApplication stopMocking];
1604 XCTAssertNil(weakPreferences);
1609- (UITraitCollection*)fakeTraitCollectionWithContrast:(UIAccessibilityContrast)contrast {
1610 id mockTraitCollection = OCMClassMock([UITraitCollection
class]);
1611 OCMStub([mockTraitCollection accessibilityContrast]).andReturn(contrast);
1612 return mockTraitCollection;
1617 [[XCTestExpectation alloc] initWithDescription:@"notification called"];
1624 [[NSNotificationCenter defaultCenter] addObserverForName:FlutterViewControllerWillDealloc
1626 queue:[NSOperationQueue mainQueue]
1627 usingBlock:^(NSNotification* _Nonnull note) {
1628 [expectation fulfill];
1630 XCTAssertNotNil(realVC);
1633 [
self waitForExpectations:@[ expectation ] timeout:1.0];
1643 XCTAssertNotNil(weakKeyboardManager);
1648 XCTAssertNil(weakKeyboardManager);
1658 XCTAssertFalse([realVC isViewLoaded],
@"shouldn't have loaded since it hasn't been shown");
1670 [[NSNotificationCenter defaultCenter] postNotificationName:FlutterViewControllerHideHomeIndicator
1682 OCMStub([viewControllerMock surfaceUpdated:NO]);
1683 [viewController beginAppearanceTransition:NO animated:NO];
1684 [viewController endAppearanceTransition];
1685 XCTAssertTrue(
mockEngine.didCallNotifyLowMemory);
1688- (void)sendMessage:(
id _Nullable)message reply:(
FlutterReply _Nullable)callback {
1689 NSMutableDictionary* replyMessage = [@{
1702 if (@available(iOS 13.4, *)) {
1709 OCMStub([
mockEngine.keyEventChannel sendMessage:[OCMArg
any] reply:[OCMArg
any]])
1710 .andCall(
self,
@selector(sendMessage:reply:));
1711 OCMStub([
self.mockTextInputPlugin handlePress:[OCMArg
any]]).andReturn(YES);
1722 [vc
handlePressEvent:keyUpEvent(UIKeyboardHIDUsageKeyboardA, UIKeyModifierShift, 123.0)
1726 XCTAssert(
self.messageSent != nil);
1727 XCTAssert([
self.messageSent[
@"keymap"] isEqualToString:
@"ios"]);
1728 XCTAssert([
self.messageSent[
@"type"] isEqualToString:
@"keyup"]);
1729 XCTAssert([
self.messageSent[
@"keyCode"] isEqualToNumber:[NSNumber numberWithInt:4]]);
1730 XCTAssert([
self.messageSent[
@"modifiers"] isEqualToNumber:[NSNumber numberWithInt:0]]);
1731 XCTAssert([
self.messageSent[
@"characters"] isEqualToString:
@""]);
1732 XCTAssert([
self.messageSent[
@"charactersIgnoringModifiers"] isEqualToString:
@""]);
1737 if (@available(iOS 13.4, *)) {
1745 OCMStub([
mockEngine.keyEventChannel sendMessage:[OCMArg
any] reply:[OCMArg
any]])
1746 .andCall(
self,
@selector(sendMessage:reply:));
1747 OCMStub([
self.mockTextInputPlugin handlePress:[OCMArg
any]]).andReturn(YES);
1755 [vc addInternalPlugins];
1757 [vc handlePressEvent:keyDownEvent(UIKeyboardHIDUsageKeyboardA, UIKeyModifierShift, 123.0f, "A",
1762 XCTAssert(
self.messageSent != nil);
1763 XCTAssert([
self.messageSent[
@"keymap"] isEqualToString:
@"ios"]);
1764 XCTAssert([
self.messageSent[
@"type"] isEqualToString:
@"keydown"]);
1765 XCTAssert([
self.messageSent[
@"keyCode"] isEqualToNumber:[NSNumber numberWithInt:4]]);
1766 XCTAssert([
self.messageSent[
@"modifiers"] isEqualToNumber:[NSNumber numberWithInt:0]]);
1767 XCTAssert([
self.messageSent[
@"characters"] isEqualToString:
@"A"]);
1768 XCTAssert([
self.messageSent[
@"charactersIgnoringModifiers"] isEqualToString:
@"a"]);
1769 [vc deregisterNotifications];
1774 if (@available(iOS 13.4, *)) {
1781 .andCall(
self,
@selector(sendMessage:reply:));
1782 OCMStub([
self.mockTextInputPlugin handlePress:[OCMArg
any]]).andReturn(YES);
1793 [vc
handlePressEvent:keyEventWithPhase(UIPressPhaseStationary, UIKeyboardHIDUsageKeyboardA,
1794 UIKeyModifierShift, 123.0)
1797 [vc
handlePressEvent:keyEventWithPhase(UIPressPhaseCancelled, UIKeyboardHIDUsageKeyboardA,
1798 UIKeyModifierShift, 123.0)
1801 [vc
handlePressEvent:keyEventWithPhase(UIPressPhaseChanged, UIKeyboardHIDUsageKeyboardA,
1802 UIKeyModifierShift, 123.0)
1806 XCTAssert(
self.messageSent == nil);
1812 if (@available(iOS 13.4, *)) {
1821 XCTAssertNotNil(vc);
1822 UIView* view = vc.
view;
1823 XCTAssertNotNil(view);
1824 NSArray* gestureRecognizers = view.gestureRecognizers;
1825 XCTAssertNotNil(gestureRecognizers);
1828 for (
id gesture in gestureRecognizers) {
1829 if ([gesture isKindOfClass:[UIPanGestureRecognizer
class]]) {
1834 XCTAssertTrue(found);
1838 if (@available(iOS 13.4, *)) {
1847 XCTAssertNotNil(vc);
1849 id mockPanGestureRecognizer = OCMClassMock([UIPanGestureRecognizer
class]);
1850 XCTAssertNotNil(mockPanGestureRecognizer);
1854 [[[
self.mockEngine verify] ignoringNonObjectArgs]
1855 dispatchPointerDataPacket:std::make_unique<flutter::PointerDataPacket>(0)];
1862 XCTAssertNotNil(vc);
1865 int64_t current_micros = [[NSProcessInfo processInfo] systemUptime] * 1000 * 1000;
1866 int64_t interval_micros = current_micros - pointer_data.time_stamp;
1867 const int64_t tolerance_millis = 2;
1868 XCTAssertTrue(interval_micros / 1000 < tolerance_millis,
1869 @"PointerData.time_stamp should be equal to NSProcessInfo.systemUptime");
1883 UIWindow*
window = [[UIWindow alloc] init];
1884 [window addSubview:flutterViewController.view];
1887 NSNotification* sceneNotification =
1888 [NSNotification notificationWithName:UISceneDidActivateNotification object:nil userInfo:nil];
1889 NSNotification* applicationNotification =
1890 [NSNotification notificationWithName:UIApplicationDidBecomeActiveNotification
1894 [[NSNotificationCenter defaultCenter] postNotification:sceneNotification];
1895 [[NSNotificationCenter defaultCenter] postNotification:applicationNotification];
1896#if APPLICATION_EXTENSION_API_ONLY
1897 OCMVerify([mockVC sceneBecameActive:[OCMArg
any]]);
1898 OCMReject([mockVC applicationBecameActive:[OCMArg
any]]);
1900 OCMReject([mockVC sceneBecameActive:[OCMArg
any]]);
1901 OCMVerify([mockVC applicationBecameActive:[OCMArg
any]]);
1904 OCMVerify([mockVC surfaceUpdated:YES]);
1905 XCTestExpectation* timeoutApplicationLifeCycle =
1906 [
self expectationWithDescription:@"timeoutApplicationLifeCycle"];
1907 dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 *
NSEC_PER_SEC)),
1908 dispatch_get_main_queue(), ^{
1909 [timeoutApplicationLifeCycle fulfill];
1910 OCMVerify([mockVC goToApplicationLifecycle:
@"AppLifecycleState.resumed"]);
1913 [
self waitForExpectationsWithTimeout:5.0 handler:nil];
1921 NSNotification* sceneNotification =
1922 [NSNotification notificationWithName:UISceneWillDeactivateNotification
1925 NSNotification* applicationNotification =
1926 [NSNotification notificationWithName:UIApplicationWillResignActiveNotification
1930 [[NSNotificationCenter defaultCenter] postNotification:sceneNotification];
1931 [[NSNotificationCenter defaultCenter] postNotification:applicationNotification];
1932#if APPLICATION_EXTENSION_API_ONLY
1933 OCMVerify([mockVC sceneWillResignActive:[OCMArg
any]]);
1934 OCMReject([mockVC applicationWillResignActive:[OCMArg
any]]);
1936 OCMReject([mockVC sceneWillResignActive:[OCMArg
any]]);
1937 OCMVerify([mockVC applicationWillResignActive:[OCMArg
any]]);
1939 OCMVerify([mockVC goToApplicationLifecycle:
@"AppLifecycleState.inactive"]);
1948 NSNotification* sceneNotification =
1949 [NSNotification notificationWithName:UISceneDidDisconnectNotification
1952 NSNotification* applicationNotification =
1953 [NSNotification notificationWithName:UIApplicationWillTerminateNotification
1959 [[NSNotificationCenter defaultCenter] postNotification:sceneNotification];
1960 [[NSNotificationCenter defaultCenter] postNotification:applicationNotification];
1961#if APPLICATION_EXTENSION_API_ONLY
1962 OCMVerify([mockVC sceneWillDisconnect:[OCMArg
any]]);
1963 OCMReject([mockVC applicationWillTerminate:[OCMArg
any]]);
1965 OCMReject([mockVC sceneWillDisconnect:[OCMArg
any]]);
1966 OCMVerify([mockVC applicationWillTerminate:[OCMArg
any]]);
1968 OCMVerify([mockVC goToApplicationLifecycle:
@"AppLifecycleState.detached"]);
1978 NSNotification* sceneNotification =
1979 [NSNotification notificationWithName:UISceneDidEnterBackgroundNotification
1982 NSNotification* applicationNotification =
1983 [NSNotification notificationWithName:UIApplicationDidEnterBackgroundNotification
1987 [[NSNotificationCenter defaultCenter] postNotification:sceneNotification];
1988 [[NSNotificationCenter defaultCenter] postNotification:applicationNotification];
1989#if APPLICATION_EXTENSION_API_ONLY
1990 OCMVerify([mockVC sceneDidEnterBackground:[OCMArg
any]]);
1991 OCMReject([mockVC applicationDidEnterBackground:[OCMArg
any]]);
1993 OCMReject([mockVC sceneDidEnterBackground:[OCMArg
any]]);
1994 OCMVerify([mockVC applicationDidEnterBackground:[OCMArg
any]]);
1997 OCMVerify([mockVC surfaceUpdated:NO]);
1998 OCMVerify([mockVC goToApplicationLifecycle:
@"AppLifecycleState.paused"]);
2007 NSNotification* sceneNotification =
2008 [NSNotification notificationWithName:UISceneWillEnterForegroundNotification
2011 NSNotification* applicationNotification =
2012 [NSNotification notificationWithName:UIApplicationWillEnterForegroundNotification
2016 [[NSNotificationCenter defaultCenter] postNotification:sceneNotification];
2017 [[NSNotificationCenter defaultCenter] postNotification:applicationNotification];
2018#if APPLICATION_EXTENSION_API_ONLY
2019 OCMVerify([mockVC sceneWillEnterForeground:[OCMArg
any]]);
2020 OCMReject([mockVC applicationWillEnterForeground:[OCMArg
any]]);
2022 OCMReject([mockVC sceneWillEnterForeground:[OCMArg
any]]);
2023 OCMVerify([mockVC applicationWillEnterForeground:[OCMArg
any]]);
2025 OCMVerify([mockVC goToApplicationLifecycle:
@"AppLifecycleState.inactive"]);
2034 NSNotification* applicationDidBecomeActiveNotification =
2035 [NSNotification notificationWithName:UIApplicationDidBecomeActiveNotification
2038 NSNotification* applicationWillResignActiveNotification =
2039 [NSNotification notificationWithName:UIApplicationWillResignActiveNotification
2043 [[NSNotificationCenter defaultCenter] postNotification:applicationDidBecomeActiveNotification];
2044 [[NSNotificationCenter defaultCenter] postNotification:applicationWillResignActiveNotification];
2045#if APPLICATION_EXTENSION_API_ONLY
2047 OCMVerify([mockVC goToApplicationLifecycle:
@"AppLifecycleState.inactive"]);
2050 XCTestExpectation* timeoutApplicationLifeCycle =
2051 [
self expectationWithDescription:@"timeoutApplicationLifeCycle"];
2052 dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 *
NSEC_PER_SEC)),
2053 dispatch_get_main_queue(), ^{
2054 OCMReject([mockVC goToApplicationLifecycle:
@"AppLifecycleState.resumed"]);
2055 [timeoutApplicationLifeCycle fulfill];
2058 [
self waitForExpectationsWithTimeout:5.0 handler:nil];
2062 id bundleMock = OCMPartialMock([NSBundle mainBundle]);
2063 OCMStub([bundleMock objectForInfoDictionaryKey:
@"CADisableMinimumFrameDurationOnPhone"])
2065 id mockDisplayLinkManager = [OCMockObject mockForClass:[DisplayLinkManager class]];
2066 double maxFrameRate = 120;
2067 [[[mockDisplayLinkManager stub] andReturnValue:@(maxFrameRate)] displayRefreshRate];
2077 CADisplayLink*
link = [viewController.keyboardAnimationVSyncClient getDisplayLink];
2078 XCTAssertNotNil(
link);
2079 if (@available(iOS 15.0, *)) {
2080 XCTAssertEqual(
link.preferredFrameRateRange.maximum, maxFrameRate);
2081 XCTAssertEqual(
link.preferredFrameRateRange.preferred, maxFrameRate);
2082 XCTAssertEqual(
link.preferredFrameRateRange.minimum, maxFrameRate / 2);
2084 XCTAssertEqual(
link.preferredFramesPerSecond, maxFrameRate);
2090 id mockDisplayLinkManager = [OCMockObject mockForClass:[DisplayLinkManager class]];
2091 double maxFrameRate = 120;
2092 [[[mockDisplayLinkManager stub] andReturnValue:@(maxFrameRate)] displayRefreshRate];
2103 id mockDisplayLinkManager = [OCMockObject mockForClass:[DisplayLinkManager class]];
2104 double maxFrameRate = 120;
2105 [[[mockDisplayLinkManager stub] andReturnValue:@(maxFrameRate)] displayRefreshRate];
2114 XCTAssertNotNil(clientBefore);
2118 XCTAssertNotNil(clientAfter);
2120 XCTAssertTrue(clientBefore == clientAfter);
2124 id mockDisplayLinkManager = [OCMockObject mockForClass:[DisplayLinkManager class]];
2125 double maxFrameRate = 60;
2126 [[[mockDisplayLinkManager stub] andReturnValue:@(maxFrameRate)] displayRefreshRate];
2137 id mockDisplayLinkManager = [OCMockObject mockForClass:[DisplayLinkManager class]];
2138 double maxFrameRate = 120;
2139 [[[mockDisplayLinkManager stub] andReturnValue:@(maxFrameRate)] displayRefreshRate];
2151 UITouch* fakeTouchBegan = [[UITouch alloc] init];
2152 fakeTouchBegan.phase = UITouchPhaseBegan;
2154 UITouch* fakeTouchMove = [[UITouch alloc] init];
2155 fakeTouchMove.phase = UITouchPhaseMoved;
2157 UITouch* fakeTouchEnd = [[UITouch alloc] init];
2158 fakeTouchEnd.phase = UITouchPhaseEnded;
2160 UITouch* fakeTouchCancelled = [[UITouch alloc] init];
2161 fakeTouchCancelled.phase = UITouchPhaseCancelled;
2165 XCTAssertFalse(
link.isPaused);
2169 XCTAssertTrue(
link.isPaused);
2173 XCTAssertFalse(
link.isPaused);
2177 XCTAssertTrue(
link.isPaused);
2181 initWithObjects:fakeTouchBegan, fakeTouchEnd, nil]];
2182 XCTAssertFalse(
link.isPaused);
2186 fakeTouchCancelled, nil]];
2187 XCTAssertTrue(
link.isPaused);
2191 initWithObjects:fakeTouchMove, fakeTouchEnd, nil]];
2192 XCTAssertFalse(
link.isPaused);
2224 if (@available(iOS 16.0, *)) {
2225 XCTAssertTrue(supportsShowingSystemContextMenu);
2227 XCTAssertFalse(supportsShowingSystemContextMenu);
static SkISize times(const SkISize &size, float factor)
NS_ASSUME_NONNULL_BEGIN typedef void(^ FlutterReply)(id _Nullable reply)
static void copy(void *dst, const uint8_t *src, int width, int bpp, int deltaSrc, int offset, const SkPMColor ctable[])
static CFStringRef kMessageLoopCFRunLoopMode
void(* FlutterKeyEventCallback)(bool, void *)
FlutterSemanticsFlag flags
FlKeyEvent uint64_t FlKeyResponderAsyncCallback callback
FlutterSendKeyEvent sendEvent
FlutterTextInputPlugin * textInputPlugin
FlutterBasicMessageChannel * lifecycleChannel
BOOL didCallNotifyLowMemory
FlutterViewController * viewController
FlutterBasicMessageChannel * keyEventChannel
BOOL runWithEntrypoint:(nullable NSString *entrypoint)
FlutterMethodChannel * navigationChannel
NSObject< FlutterBinaryMessenger > * binaryMessenger
fml::RefPtr< fml::TaskRunner > uiTaskRunner()
FlutterBasicMessageChannel * settingsChannel
FlutterDartProject * project()
BOOL createShell:libraryURI:initialRoute:(NSString *entrypoint, [libraryURI] NSString *libraryURI, [initialRoute] NSString *initialRoute)
void addInternalPlugins()
FlutterViewController * viewController
NSMutableArray< id< FlutterKeyPrimaryResponder > > * primaryResponders
void testHandleKeyboardNotification()
void testLifeCycleNotificationWillTerminate()
void testEngineConfigSyncMethodWillNotExecuteWhenViewControllerInEngineIsNotCurrentViewControllerInViewWillAppear()
void testCalculateMultitaskingAdjustment()
void testEnsureBottomInsetIsZeroWhenKeyboardDismissed()
void testItReportsAccessibilityOnOffSwitchLabelsFlagNotSet()
void testInternalPluginsInvokeInViewDidLoad()
void testStartKeyboardAnimationWillInvokeSetupKeyboardSpringAnimationIfNeeded()
void testItReportsPlatformBrightnessWhenViewWillAppear()
void testAccessibilityPerformEscapePopsRoute()
void testSetupKeyboardSpringAnimationIfNeeded()
void testSetupKeyboardAnimationVsyncClientWillCreateNewVsyncClientForFlutterViewController()
void testCalculateKeyboardAttachMode()
void testReleasesKeyboardManagerOnDealloc()
void testLifeCycleNotificationWillEnterForeground()
void testLifeCycleNotificationWillResignActive()
void testEngineConfigSyncMethodWillExecuteWhenViewControllerInEngineIsCurrentViewControllerInViewDidAppear()
void testUpdateViewportMetricsIfNeeded_DoesInvokeEngineWhenIsTheViewController()
void testItReportsNormalPlatformContrastByDefault()
void testItReportsPlatformContrastWhenViewWillAppear()
void testSupportsShowingSystemContextMenuForIOS16AndAbove()
void testUpdateViewportMetricsIfNeeded_DoesNotInvokeEngineWhenShouldBeIgnoredDuringRotation()
FlutterViewController * flutterViewController
void testPerformOrientationUpdateDoesNotForceOrientationChange()
void testViewDidDisappearDoesntPauseEngineWhenNotTheViewController()
void testDoesntLoadViewInInit()
void testLifeCycleNotificationBecameActive()
void testKeyboardAnimationIsShowingAndCompounding()
void testFlutterViewControllerStartKeyboardAnimationWillCreateVsyncClientCorrectly()
void testInternalPluginsWeakPtrNotCrash()
void testItReportsLightPlatformBrightnessByDefault()
void testEngineConfigSyncMethodWillNotExecuteWhenViewControllerInEngineIsNotCurrentViewControllerInViewDidAppear()
void testViewDidLoadDoesntInvokeEngineWhenNotTheViewController()
void testSetupKeyboardAnimationVsyncClientWillNotCreateNewVsyncClientWhenKeyboardAnimationCallbackIsNil()
void testBinaryMessenger()
void testItReportsDarkPlatformBrightnessWhenTraitCollectionRequestsIt()
void testSplashScreenViewRemoveNotCrash()
void testCalculateKeyboardInset()
void testViewControllerIsReleased()
void testKeyboardAnimationWillWaitUIThreadVsync()
void testViewDidDisappearDoesPauseEngineWhenIsTheViewController()
void testWillDeallocNotification()
void testNotifyLowMemory()
void testViewDidLoadDoesInvokeEngineWhenIsTheViewController()
void testPerformOrientationUpdateForcesOrientationChange()
void testViewWillTransitionToSize_DoesNotDelayEngineCallIfZeroDuration()
void testItReportsAccessibilityOnOffSwitchLabelsFlagSet()
void testTriggerTouchRateCorrectionVSyncClientCorrectly()
void testLifeCycleNotificationDidEnterBackground()
void testCreateTouchRateCorrectionVSyncClientWillCreateVsyncClientWhenRefreshRateIsLargerThan60HZ()
void testAppWillTerminateViewDidDestroyTheEngine()
void testViewDidLoadWillInvokeCreateTouchRateCorrectionVSyncClient()
void testFakeEventTimeStamp()
void testViewDidLoadDoesntInvokeEngineAttachViewWhenEngineNeedsLaunch()
void testEnsureViewportMetricsWillInvokeAndDisplayLinkWillInvalidateInViewDidDisappear()
void testKeyboardAnimationWillNotCrashWhenEngineDestroyed()
void testItReportsHighContrastWhenTraitCollectionRequestsIt()
void testEngineConfigSyncMethodWillNotExecuteWhenViewControllerInEngineIsNotCurrentViewControllerInViewWillDisappear()
void testCreateTouchRateCorrectionVSyncClientWillNotCreateVsyncClientWhenRefreshRateIs60HZ()
void testSplashScreenViewCanSetNil()
void testViewWillTransitionToSize_DoesDelayEngineCallIfNonZeroDuration()
void testShouldIgnoreKeyboardNotification()
void testCreateTouchRateCorrectionVSyncClientWillNotCreateNewVSyncClientWhenClientAlreadyExists()
void testEngineConfigSyncMethodWillExecuteWhenViewControllerInEngineIsCurrentViewControllerInViewWillAppear()
void testEngineConfigSyncMethodWillExecuteWhenViewControllerInEngineIsCurrentViewControllerInViewWillDisappear()
void testLifeCycleNotificationCancelledInvalidResumed()
void testUpdateViewportMetricsIfNeeded_DoesntInvokeEngineWhenNotTheViewController()
BOOL shouldIgnoreKeyboardNotification:(NSNotification *notification)
void viewDidDisappear:(BOOL animated)
void updateViewportMetricsIfNeeded()
void performOrientationUpdate:(UIInterfaceOrientationMask new_preferences)
BOOL prefersHomeIndicatorAutoHidden()
void addInternalPlugins()
void setSplashScreenView:(UIView *view)
VSyncClient * touchRateCorrectionVSyncClient
void discreteScrollEvent:(ios(13.4) API_AVAILABLE)
void viewDidAppear:(BOOL animated)
void viewWillDisappear:(BOOL animated)
FlutterKeyboardManager * keyboardManager
void deregisterNotifications()
SpringAnimation * keyboardSpringAnimation()
void viewWillAppear:(BOOL animated)
void setUpKeyboardAnimationVsyncClient:(FlutterKeyboardAnimationCallback keyboardAnimationCallback)
void viewWillTransitionToSize:withTransitionCoordinator:(CGSize size, [withTransitionCoordinator] id< UIViewControllerTransitionCoordinator > coordinator)
CGFloat targetViewInsetBottom
void startKeyBoardAnimation:(NSTimeInterval duration)
void sharedSetupWithProject:initialRoute:(nullable FlutterDartProject *project, [initialRoute] nullable NSString *initialRoute)
NSUInteger supportedInterfaceOrientations()
void createTouchRateCorrectionVSyncClientIfNeeded()
NSObject< FlutterBinaryMessenger > * binaryMessenger
void setUpKeyboardSpringAnimationIfNeeded:(CAAnimation *keyboardAnimation)
void triggerTouchRateCorrectionIfNeeded:(NSSet *touches)
flutter::PointerData generatePointerDataForFake()
VSyncClient * keyboardAnimationVSyncClient
void handleKeyboardNotification:(NSNotification *notification)
CGFloat calculateKeyboardInset:keyboardMode:(CGRect keyboardFrame, [keyboardMode] NSInteger keyboardMode)
BOOL supportsShowingSystemContextMenu()
BOOL keyboardAnimationIsShowing
void handlePressEvent:nextAction:(FlutterUIPressProxy *press, [nextAction] ios(13.4) API_AVAILABLE)
UIView * keyboardAnimationView()
void applicationWillTerminate:(NSNotification *notification)
FlutterKeyboardMode calculateKeyboardAttachMode:(NSNotification *notification)
void viewDidLayoutSubviews()
CGFloat calculateMultitaskingAdjustment:keyboardFrame:(CGRect screenRect, [keyboardFrame] CGRect keyboardFrame)
void traitCollectionDidChange:(UITraitCollection *previousTraitCollection)
BOOL isKeyboardInOrTransitioningFromBackground
CADisplayLink * getDisplayLink()
void(^ FlutterSendKeyEvent)(const FlutterKeyEvent &, _Nullable FlutterKeyEventCallback, void *_Nullable)
FlutterTextInputPlugin * textInputPlugin
NSNotificationName const FlutterViewControllerWillDealloc
void(^ FlutterKeyboardAnimationCallback)(fml::TimePoint)
Optional< SkRect > bounds
SK_API sk_sp< SkSurface > ios(9.0)
def link(from_root, to_root)
API_AVAILABLE(ios(14.0), macos(11.0)) static NSString *MTLCommandEncoderErrorStateToString(MTLCommandEncoderErrorState state)
SIT bool any(const Vec< 1, T > &x)
static SkRect inset(const SkRect &r)