From ffcc0d5c9e06e43c71cc35bdf7c4ff17cef33143 Mon Sep 17 00:00:00 2001 From: Justin Cohen Date: Wed, 28 Jun 2023 20:39:15 -0400 Subject: [PATCH 1/6] iOS17 support for keyboard accessory and input views. --- AppFramework/Keyboard/GREYKeyboard.h | 5 +++++ AppFramework/Keyboard/GREYKeyboard.m | 4 ++++ UILib/GREYScreenshotter.m | 3 ++- UILib/Provider/GREYUIWindowProvider.m | 3 +++ 4 files changed, 14 insertions(+), 1 deletion(-) diff --git a/AppFramework/Keyboard/GREYKeyboard.h b/AppFramework/Keyboard/GREYKeyboard.h index 789917542..4669c07e8 100644 --- a/AppFramework/Keyboard/GREYKeyboard.h +++ b/AppFramework/Keyboard/GREYKeyboard.h @@ -39,6 +39,11 @@ NS_ASSUME_NONNULL_BEGIN inFirstResponder:(id)firstResponder error:(__strong NSError **)errorOrNil; +/** + * @return @c YES if the keyboard is visible, @c NO otherwise. + */ ++ (BOOL)isKeyboardVisible; + /** * Waits until the keyboard is visible on the screen. * diff --git a/AppFramework/Keyboard/GREYKeyboard.m b/AppFramework/Keyboard/GREYKeyboard.m index 373c584d2..8aba54c69 100644 --- a/AppFramework/Keyboard/GREYKeyboard.m +++ b/AppFramework/Keyboard/GREYKeyboard.m @@ -283,6 +283,10 @@ + (BOOL)typeString:(NSString *)string return YES; } ++ (BOOL)isKeyboardVisible { + return atomic_load(&gIsKeyboardShown); +} + + (BOOL)waitForKeyboardToAppear { if (atomic_load(&gIsKeyboardShown)) { return YES; diff --git a/UILib/GREYScreenshotter.m b/UILib/GREYScreenshotter.m index f93809a9a..a3578bf3d 100644 --- a/UILib/GREYScreenshotter.m +++ b/UILib/GREYScreenshotter.m @@ -150,10 +150,11 @@ + (void)drawScreenInContext:(CGContextRef)bitmapContextRef // The bitmap context width and height are scaled, so we need to undo the scale adjustment. NSEnumerator *allWindowsInReverse = [[GREYUIWindowProvider allWindowsWithStatusBar:includeStatusBar] reverseObjectEnumerator]; + CGFloat maxLevel = [GREYKeyboard isKeyboardVisible] ? 1 : 0; for (UIWindow *window in allWindowsInReverse) { if (window.hidden || window.alpha == 0 || (iOS17_OR_ABOVE() && [window respondsToSelector:@selector(windowLevel)] && - window.windowLevel > 0)) { + window.windowLevel > maxLevel)) { continue; } [self drawViewInContext:bitmapContextRef diff --git a/UILib/Provider/GREYUIWindowProvider.m b/UILib/Provider/GREYUIWindowProvider.m index 9a30db068..9c08b98ec 100644 --- a/UILib/Provider/GREYUIWindowProvider.m +++ b/UILib/Provider/GREYUIWindowProvider.m @@ -106,6 +106,9 @@ + (NSArray *)windowsFromLevelOfWindow:(UIWindow *)window withStatusBar:(BOOL)inc UIResponder *firstResponder = GetFirstResponderSubview(keyWindow); UIView *inputView = firstResponder.inputView; if (inputView.window) { + if (@available(iOS 17, *)) { + inputView.window.windowLevel = 1; + } [windows addObject:inputView.window]; } UIWindow *keyboardWindow = GREYUILibUtilsGetKeyboardWindow(); From 1f594608508ec9c2cf9308dcbf0825bd5dce9f0b Mon Sep 17 00:00:00 2001 From: Justin Cohen Date: Thu, 29 Jun 2023 05:09:02 -0400 Subject: [PATCH 2/6] Fix compile --- UILib/GREYScreenshotter.m | 1 + 1 file changed, 1 insertion(+) diff --git a/UILib/GREYScreenshotter.m b/UILib/GREYScreenshotter.m index a3578bf3d..1a31e7671 100644 --- a/UILib/GREYScreenshotter.m +++ b/UILib/GREYScreenshotter.m @@ -21,6 +21,7 @@ #import "NSFileManager+GREYCommon.h" #import "NSObject+GREYCommon.h" #import "GREYFatalAsserts.h" +#import "../AppFramework/Keyboard/GREYKeyboard.h" #import "GREYLogger.h" #import "GREYUIWindowProvider.h" #import "GREYUILibUtils.h" From b6e7eb428532b263cd4db33bdfd813eafa03f2a4 Mon Sep 17 00:00:00 2001 From: Justin Cohen Date: Wed, 5 Jul 2023 22:40:36 -0400 Subject: [PATCH 3/6] Try looking for ViewService instead. --- AppFramework/Keyboard/GREYKeyboard.h | 5 ----- AppFramework/Keyboard/GREYKeyboard.m | 4 ---- UILib/GREYScreenshotter.m | 6 +----- UILib/Utils/GREYUILibUtils.m | 14 +++++++++++++- 4 files changed, 14 insertions(+), 15 deletions(-) diff --git a/AppFramework/Keyboard/GREYKeyboard.h b/AppFramework/Keyboard/GREYKeyboard.h index 4669c07e8..789917542 100644 --- a/AppFramework/Keyboard/GREYKeyboard.h +++ b/AppFramework/Keyboard/GREYKeyboard.h @@ -39,11 +39,6 @@ NS_ASSUME_NONNULL_BEGIN inFirstResponder:(id)firstResponder error:(__strong NSError **)errorOrNil; -/** - * @return @c YES if the keyboard is visible, @c NO otherwise. - */ -+ (BOOL)isKeyboardVisible; - /** * Waits until the keyboard is visible on the screen. * diff --git a/AppFramework/Keyboard/GREYKeyboard.m b/AppFramework/Keyboard/GREYKeyboard.m index 8aba54c69..373c584d2 100644 --- a/AppFramework/Keyboard/GREYKeyboard.m +++ b/AppFramework/Keyboard/GREYKeyboard.m @@ -283,10 +283,6 @@ + (BOOL)typeString:(NSString *)string return YES; } -+ (BOOL)isKeyboardVisible { - return atomic_load(&gIsKeyboardShown); -} - + (BOOL)waitForKeyboardToAppear { if (atomic_load(&gIsKeyboardShown)) { return YES; diff --git a/UILib/GREYScreenshotter.m b/UILib/GREYScreenshotter.m index 1a31e7671..7e84daa1b 100644 --- a/UILib/GREYScreenshotter.m +++ b/UILib/GREYScreenshotter.m @@ -21,7 +21,6 @@ #import "NSFileManager+GREYCommon.h" #import "NSObject+GREYCommon.h" #import "GREYFatalAsserts.h" -#import "../AppFramework/Keyboard/GREYKeyboard.h" #import "GREYLogger.h" #import "GREYUIWindowProvider.h" #import "GREYUILibUtils.h" @@ -151,11 +150,8 @@ + (void)drawScreenInContext:(CGContextRef)bitmapContextRef // The bitmap context width and height are scaled, so we need to undo the scale adjustment. NSEnumerator *allWindowsInReverse = [[GREYUIWindowProvider allWindowsWithStatusBar:includeStatusBar] reverseObjectEnumerator]; - CGFloat maxLevel = [GREYKeyboard isKeyboardVisible] ? 1 : 0; for (UIWindow *window in allWindowsInReverse) { - if (window.hidden || window.alpha == 0 || - (iOS17_OR_ABOVE() && [window respondsToSelector:@selector(windowLevel)] && - window.windowLevel > maxLevel)) { + if (window.hidden || window.alpha == 0) { continue; } [self drawViewInContext:bitmapContextRef diff --git a/UILib/Utils/GREYUILibUtils.m b/UILib/Utils/GREYUILibUtils.m index 68a628104..e88426287 100644 --- a/UILib/Utils/GREYUILibUtils.m +++ b/UILib/Utils/GREYUILibUtils.m @@ -18,6 +18,11 @@ #import "GREYAppleInternals.h" + +@interface UIWindow () +- (NSDictionary*)_perCanvasOptions; +@end + UIWindow *GREYUILibUtilsGetApplicationKeyWindow(UIApplication *application) { // New API only available on Xcode 13+ #if (defined(__MAC_OS_X_VERSION_MAX_ALLOWED) && __MAC_OS_X_VERSION_MAX_ALLOWED >= 120000) || \ @@ -95,7 +100,14 @@ if (@available(iOS 16.0, *)) { for (UIScene *scene in sharedApp.connectedScenes) { UIWindowScene *windowScene = (UIWindowScene *)scene; - [windows addObjectsFromArray:windowScene.windows]; + for (UIWindow* window in windowScene.windows) { + SEL perCanvasOptionsSel = NSSelectorFromString(@"_perCanvasOptions"); + NSString* viewService = [window respondsToSelector:perCanvasOptionsSel] ? + [window._perCanvasOptions objectForKey:@"ViewService"] : nil; + if (viewService && [viewService isEqualToString:@"NO"]) + continue; + [windows addObject:window]; + } } } else { #pragma clang diagnostic push From a069f5ae539b93a671cd486a1ef864b8de61e6b6 Mon Sep 17 00:00:00 2001 From: Justin Cohen Date: Thu, 6 Jul 2023 08:24:11 -0400 Subject: [PATCH 4/6] Revert "Try looking for ViewService instead." This reverts commit b6e7eb428532b263cd4db33bdfd813eafa03f2a4. --- AppFramework/Keyboard/GREYKeyboard.h | 5 +++++ AppFramework/Keyboard/GREYKeyboard.m | 4 ++++ UILib/GREYScreenshotter.m | 6 +++++- UILib/Utils/GREYUILibUtils.m | 14 +------------- 4 files changed, 15 insertions(+), 14 deletions(-) diff --git a/AppFramework/Keyboard/GREYKeyboard.h b/AppFramework/Keyboard/GREYKeyboard.h index 789917542..4669c07e8 100644 --- a/AppFramework/Keyboard/GREYKeyboard.h +++ b/AppFramework/Keyboard/GREYKeyboard.h @@ -39,6 +39,11 @@ NS_ASSUME_NONNULL_BEGIN inFirstResponder:(id)firstResponder error:(__strong NSError **)errorOrNil; +/** + * @return @c YES if the keyboard is visible, @c NO otherwise. + */ ++ (BOOL)isKeyboardVisible; + /** * Waits until the keyboard is visible on the screen. * diff --git a/AppFramework/Keyboard/GREYKeyboard.m b/AppFramework/Keyboard/GREYKeyboard.m index 373c584d2..8aba54c69 100644 --- a/AppFramework/Keyboard/GREYKeyboard.m +++ b/AppFramework/Keyboard/GREYKeyboard.m @@ -283,6 +283,10 @@ + (BOOL)typeString:(NSString *)string return YES; } ++ (BOOL)isKeyboardVisible { + return atomic_load(&gIsKeyboardShown); +} + + (BOOL)waitForKeyboardToAppear { if (atomic_load(&gIsKeyboardShown)) { return YES; diff --git a/UILib/GREYScreenshotter.m b/UILib/GREYScreenshotter.m index 7e84daa1b..1a31e7671 100644 --- a/UILib/GREYScreenshotter.m +++ b/UILib/GREYScreenshotter.m @@ -21,6 +21,7 @@ #import "NSFileManager+GREYCommon.h" #import "NSObject+GREYCommon.h" #import "GREYFatalAsserts.h" +#import "../AppFramework/Keyboard/GREYKeyboard.h" #import "GREYLogger.h" #import "GREYUIWindowProvider.h" #import "GREYUILibUtils.h" @@ -150,8 +151,11 @@ + (void)drawScreenInContext:(CGContextRef)bitmapContextRef // The bitmap context width and height are scaled, so we need to undo the scale adjustment. NSEnumerator *allWindowsInReverse = [[GREYUIWindowProvider allWindowsWithStatusBar:includeStatusBar] reverseObjectEnumerator]; + CGFloat maxLevel = [GREYKeyboard isKeyboardVisible] ? 1 : 0; for (UIWindow *window in allWindowsInReverse) { - if (window.hidden || window.alpha == 0) { + if (window.hidden || window.alpha == 0 || + (iOS17_OR_ABOVE() && [window respondsToSelector:@selector(windowLevel)] && + window.windowLevel > maxLevel)) { continue; } [self drawViewInContext:bitmapContextRef diff --git a/UILib/Utils/GREYUILibUtils.m b/UILib/Utils/GREYUILibUtils.m index e88426287..68a628104 100644 --- a/UILib/Utils/GREYUILibUtils.m +++ b/UILib/Utils/GREYUILibUtils.m @@ -18,11 +18,6 @@ #import "GREYAppleInternals.h" - -@interface UIWindow () -- (NSDictionary*)_perCanvasOptions; -@end - UIWindow *GREYUILibUtilsGetApplicationKeyWindow(UIApplication *application) { // New API only available on Xcode 13+ #if (defined(__MAC_OS_X_VERSION_MAX_ALLOWED) && __MAC_OS_X_VERSION_MAX_ALLOWED >= 120000) || \ @@ -100,14 +95,7 @@ - (NSDictionary*)_perCanvasOptions; if (@available(iOS 16.0, *)) { for (UIScene *scene in sharedApp.connectedScenes) { UIWindowScene *windowScene = (UIWindowScene *)scene; - for (UIWindow* window in windowScene.windows) { - SEL perCanvasOptionsSel = NSSelectorFromString(@"_perCanvasOptions"); - NSString* viewService = [window respondsToSelector:perCanvasOptionsSel] ? - [window._perCanvasOptions objectForKey:@"ViewService"] : nil; - if (viewService && [viewService isEqualToString:@"NO"]) - continue; - [windows addObject:window]; - } + [windows addObjectsFromArray:windowScene.windows]; } } else { #pragma clang diagnostic push From 124a6ca7be3d9c1321f980e449e12d4412816b13 Mon Sep 17 00:00:00 2001 From: Justin Cohen Date: Thu, 6 Jul 2023 08:24:35 -0400 Subject: [PATCH 5/6] Try looking for UIAutoRotatingWindow instead. --- AppFramework/Keyboard/GREYKeyboard.h | 5 ----- AppFramework/Keyboard/GREYKeyboard.m | 4 ---- UILib/GREYScreenshotter.m | 6 +----- UILib/Provider/GREYUIWindowProvider.m | 4 ++++ UILib/Utils/GREYUILibUtils.m | 10 +++++++++- 5 files changed, 14 insertions(+), 15 deletions(-) diff --git a/AppFramework/Keyboard/GREYKeyboard.h b/AppFramework/Keyboard/GREYKeyboard.h index 4669c07e8..789917542 100644 --- a/AppFramework/Keyboard/GREYKeyboard.h +++ b/AppFramework/Keyboard/GREYKeyboard.h @@ -39,11 +39,6 @@ NS_ASSUME_NONNULL_BEGIN inFirstResponder:(id)firstResponder error:(__strong NSError **)errorOrNil; -/** - * @return @c YES if the keyboard is visible, @c NO otherwise. - */ -+ (BOOL)isKeyboardVisible; - /** * Waits until the keyboard is visible on the screen. * diff --git a/AppFramework/Keyboard/GREYKeyboard.m b/AppFramework/Keyboard/GREYKeyboard.m index 8aba54c69..373c584d2 100644 --- a/AppFramework/Keyboard/GREYKeyboard.m +++ b/AppFramework/Keyboard/GREYKeyboard.m @@ -283,10 +283,6 @@ + (BOOL)typeString:(NSString *)string return YES; } -+ (BOOL)isKeyboardVisible { - return atomic_load(&gIsKeyboardShown); -} - + (BOOL)waitForKeyboardToAppear { if (atomic_load(&gIsKeyboardShown)) { return YES; diff --git a/UILib/GREYScreenshotter.m b/UILib/GREYScreenshotter.m index 1a31e7671..7e84daa1b 100644 --- a/UILib/GREYScreenshotter.m +++ b/UILib/GREYScreenshotter.m @@ -21,7 +21,6 @@ #import "NSFileManager+GREYCommon.h" #import "NSObject+GREYCommon.h" #import "GREYFatalAsserts.h" -#import "../AppFramework/Keyboard/GREYKeyboard.h" #import "GREYLogger.h" #import "GREYUIWindowProvider.h" #import "GREYUILibUtils.h" @@ -151,11 +150,8 @@ + (void)drawScreenInContext:(CGContextRef)bitmapContextRef // The bitmap context width and height are scaled, so we need to undo the scale adjustment. NSEnumerator *allWindowsInReverse = [[GREYUIWindowProvider allWindowsWithStatusBar:includeStatusBar] reverseObjectEnumerator]; - CGFloat maxLevel = [GREYKeyboard isKeyboardVisible] ? 1 : 0; for (UIWindow *window in allWindowsInReverse) { - if (window.hidden || window.alpha == 0 || - (iOS17_OR_ABOVE() && [window respondsToSelector:@selector(windowLevel)] && - window.windowLevel > maxLevel)) { + if (window.hidden || window.alpha == 0) { continue; } [self drawViewInContext:bitmapContextRef diff --git a/UILib/Provider/GREYUIWindowProvider.m b/UILib/Provider/GREYUIWindowProvider.m index 9c08b98ec..55f059196 100644 --- a/UILib/Provider/GREYUIWindowProvider.m +++ b/UILib/Provider/GREYUIWindowProvider.m @@ -111,6 +111,10 @@ + (NSArray *)windowsFromLevelOfWindow:(UIWindow *)window withStatusBar:(BOOL)inc } [windows addObject:inputView.window]; } + UIView *inputAccessoryView = firstResponder.inputAccessoryView; + if (inputAccessoryView.window) { + [windows addObject:inputAccessoryView.window]; + } UIWindow *keyboardWindow = GREYUILibUtilsGetKeyboardWindow(); if (keyboardWindow) { [windows addObject:keyboardWindow]; diff --git a/UILib/Utils/GREYUILibUtils.m b/UILib/Utils/GREYUILibUtils.m index 68a628104..09af8a5c1 100644 --- a/UILib/Utils/GREYUILibUtils.m +++ b/UILib/Utils/GREYUILibUtils.m @@ -18,6 +18,11 @@ #import "GREYAppleInternals.h" + +@interface UIWindow () +- (NSDictionary*)_perCanvasOptions; +@end + UIWindow *GREYUILibUtilsGetApplicationKeyWindow(UIApplication *application) { // New API only available on Xcode 13+ #if (defined(__MAC_OS_X_VERSION_MAX_ALLOWED) && __MAC_OS_X_VERSION_MAX_ALLOWED >= 120000) || \ @@ -95,7 +100,10 @@ if (@available(iOS 16.0, *)) { for (UIScene *scene in sharedApp.connectedScenes) { UIWindowScene *windowScene = (UIWindowScene *)scene; - [windows addObjectsFromArray:windowScene.windows]; + for (UIWindow* window in windowScene.windows) { + if (![window isKindOfClass:NSClassFromString(@"UIAutoRotatingWindow")]) + [windows addObject:window]; + } } } else { #pragma clang diagnostic push From 1223cae321fde28e88c008ed1dc56fb5c3bf8e0c Mon Sep 17 00:00:00 2001 From: Justin Cohen Date: Tue, 11 Jul 2023 21:32:35 -0400 Subject: [PATCH 6/6] more deflake --- UILib/GREYScreenshotter.m | 2 +- UILib/Provider/GREYUIWindowProvider.m | 4 ---- UILib/Utils/GREYUILibUtils.m | 10 +--------- 3 files changed, 2 insertions(+), 14 deletions(-) diff --git a/UILib/GREYScreenshotter.m b/UILib/GREYScreenshotter.m index a323ca1d3..1900b0519 100644 --- a/UILib/GREYScreenshotter.m +++ b/UILib/GREYScreenshotter.m @@ -128,7 +128,7 @@ + (UIImage *)grey_takeScreenshotAfterScreenUpdates:(BOOL)afterScreenUpdates inScreenRect:(CGRect)screenRect withStatusBar:(BOOL)includeStatusBar forDebugging:(BOOL)forDebugging { - UIGraphicsBeginImageContextWithOptions(screenRect.size, YES, 0); + UIGraphicsBeginImageContextWithOptions(screenRect.size, NO, 0); [self drawScreenInContext:UIGraphicsGetCurrentContext() afterScreenUpdates:afterScreenUpdates inScreenRect:screenRect diff --git a/UILib/Provider/GREYUIWindowProvider.m b/UILib/Provider/GREYUIWindowProvider.m index 55f059196..9c08b98ec 100644 --- a/UILib/Provider/GREYUIWindowProvider.m +++ b/UILib/Provider/GREYUIWindowProvider.m @@ -111,10 +111,6 @@ + (NSArray *)windowsFromLevelOfWindow:(UIWindow *)window withStatusBar:(BOOL)inc } [windows addObject:inputView.window]; } - UIView *inputAccessoryView = firstResponder.inputAccessoryView; - if (inputAccessoryView.window) { - [windows addObject:inputAccessoryView.window]; - } UIWindow *keyboardWindow = GREYUILibUtilsGetKeyboardWindow(); if (keyboardWindow) { [windows addObject:keyboardWindow]; diff --git a/UILib/Utils/GREYUILibUtils.m b/UILib/Utils/GREYUILibUtils.m index 09af8a5c1..68a628104 100644 --- a/UILib/Utils/GREYUILibUtils.m +++ b/UILib/Utils/GREYUILibUtils.m @@ -18,11 +18,6 @@ #import "GREYAppleInternals.h" - -@interface UIWindow () -- (NSDictionary*)_perCanvasOptions; -@end - UIWindow *GREYUILibUtilsGetApplicationKeyWindow(UIApplication *application) { // New API only available on Xcode 13+ #if (defined(__MAC_OS_X_VERSION_MAX_ALLOWED) && __MAC_OS_X_VERSION_MAX_ALLOWED >= 120000) || \ @@ -100,10 +95,7 @@ - (NSDictionary*)_perCanvasOptions; if (@available(iOS 16.0, *)) { for (UIScene *scene in sharedApp.connectedScenes) { UIWindowScene *windowScene = (UIWindowScene *)scene; - for (UIWindow* window in windowScene.windows) { - if (![window isKindOfClass:NSClassFromString(@"UIAutoRotatingWindow")]) - [windows addObject:window]; - } + [windows addObjectsFromArray:windowScene.windows]; } } else { #pragma clang diagnostic push