From 376250122d71b0ca2043cb9f19b26b9aaac052b7 Mon Sep 17 00:00:00 2001 From: Worthing ~ <115107835+w-goog@users.noreply.github.com> Date: Wed, 28 Oct 2026 15:19:10 -0700 Subject: [PATCH] g-orchestrated: add the GIDEMMAlertPresenter seam with a fake --- .../API/GIDEMMAlertPresenter.h | 42 ++++++++++++ .../Fake/GIDFakeEMMAlertPresenter.h | 45 +++++++++++++ .../Fake/GIDFakeEMMAlertPresenter.m | 41 ++++++++++++ .../GIDEMMAlertWindowPresenter.h | 33 ++++++++++ .../GIDEMMAlertWindowPresenter.m | 66 +++++++++++++++++++ 5 files changed, 227 insertions(+) create mode 100644 GoogleSignIn/Sources/GIDEMMAlertPresenter/API/GIDEMMAlertPresenter.h create mode 100644 GoogleSignIn/Sources/GIDEMMAlertPresenter/Fake/GIDFakeEMMAlertPresenter.h create mode 100644 GoogleSignIn/Sources/GIDEMMAlertPresenter/Fake/GIDFakeEMMAlertPresenter.m create mode 100644 GoogleSignIn/Sources/GIDEMMAlertPresenter/Implementation/GIDEMMAlertWindowPresenter.h create mode 100644 GoogleSignIn/Sources/GIDEMMAlertPresenter/Implementation/GIDEMMAlertWindowPresenter.m diff --git a/GoogleSignIn/Sources/GIDEMMAlertPresenter/API/GIDEMMAlertPresenter.h b/GoogleSignIn/Sources/GIDEMMAlertPresenter/API/GIDEMMAlertPresenter.h new file mode 100644 index 00000000..7a5ffc25 --- /dev/null +++ b/GoogleSignIn/Sources/GIDEMMAlertPresenter/API/GIDEMMAlertPresenter.h @@ -0,0 +1,42 @@ +/* + * Copyright 2026 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#import + +#if TARGET_OS_IOS && !TARGET_OS_MACCATALYST + +#import +#import + +NS_ASSUME_NONNULL_BEGIN + +/// Shows and hides the alert that `GIDEMMErrorHandler` uses to explain an EMM error to the user. +/// +/// Production code uses `GIDEMMAlertWindowPresenter`; tests use `GIDFakeEMMAlertPresenter`. +@protocol GIDEMMAlertPresenter + +/// Presents `alert` above `keyWindow`. Must be called on the main thread. +- (void)presentAlert:(UIAlertController *)alert overKeyWindow:(UIWindow *)keyWindow; + +/// Tears down whatever `-presentAlert:overKeyWindow:` set up and makes that call's `keyWindow` the +/// key window again. Must be called on the main thread. +- (void)dismissAlert; + +@end + +NS_ASSUME_NONNULL_END + +#endif // TARGET_OS_IOS && !TARGET_OS_MACCATALYST diff --git a/GoogleSignIn/Sources/GIDEMMAlertPresenter/Fake/GIDFakeEMMAlertPresenter.h b/GoogleSignIn/Sources/GIDEMMAlertPresenter/Fake/GIDFakeEMMAlertPresenter.h new file mode 100644 index 00000000..34a1d5df --- /dev/null +++ b/GoogleSignIn/Sources/GIDEMMAlertPresenter/Fake/GIDFakeEMMAlertPresenter.h @@ -0,0 +1,45 @@ +/* + * Copyright 2026 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#import + +#if TARGET_OS_IOS && !TARGET_OS_MACCATALYST + +#import +#import + +#import "GoogleSignIn/Sources/GIDEMMAlertPresenter/API/GIDEMMAlertPresenter.h" + +NS_ASSUME_NONNULL_BEGIN + +/// An in-memory `GIDEMMAlertPresenter` for tests. Records the alert instead of showing it. +@interface GIDFakeEMMAlertPresenter : NSObject + +/// The alert passed to the most recent `-presentAlert:overKeyWindow:` that has not yet been +/// dismissed, or nil. +@property(nonatomic, readonly, nullable) UIAlertController *presentedAlert; + +/// The key window passed to the most recent `-presentAlert:overKeyWindow:`, or nil. +@property(nonatomic, readonly, nullable) UIWindow *presentedOverKeyWindow; + +/// The number of times `-dismissAlert` has been called. +@property(nonatomic, readonly) NSUInteger dismissCount; + +@end + +NS_ASSUME_NONNULL_END + +#endif // TARGET_OS_IOS && !TARGET_OS_MACCATALYST diff --git a/GoogleSignIn/Sources/GIDEMMAlertPresenter/Fake/GIDFakeEMMAlertPresenter.m b/GoogleSignIn/Sources/GIDEMMAlertPresenter/Fake/GIDFakeEMMAlertPresenter.m new file mode 100644 index 00000000..1f1c3e35 --- /dev/null +++ b/GoogleSignIn/Sources/GIDEMMAlertPresenter/Fake/GIDFakeEMMAlertPresenter.m @@ -0,0 +1,41 @@ +/* + * Copyright 2026 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#import + +#if TARGET_OS_IOS && !TARGET_OS_MACCATALYST + +#import "GoogleSignIn/Sources/GIDEMMAlertPresenter/Fake/GIDFakeEMMAlertPresenter.h" + +NS_ASSUME_NONNULL_BEGIN + +@implementation GIDFakeEMMAlertPresenter + +- (void)presentAlert:(UIAlertController *)alert overKeyWindow:(UIWindow *)keyWindow { + _presentedAlert = alert; + _presentedOverKeyWindow = keyWindow; +} + +- (void)dismissAlert { + _presentedAlert = nil; + _dismissCount++; +} + +@end + +NS_ASSUME_NONNULL_END + +#endif // TARGET_OS_IOS && !TARGET_OS_MACCATALYST diff --git a/GoogleSignIn/Sources/GIDEMMAlertPresenter/Implementation/GIDEMMAlertWindowPresenter.h b/GoogleSignIn/Sources/GIDEMMAlertPresenter/Implementation/GIDEMMAlertWindowPresenter.h new file mode 100644 index 00000000..9ac74e37 --- /dev/null +++ b/GoogleSignIn/Sources/GIDEMMAlertPresenter/Implementation/GIDEMMAlertWindowPresenter.h @@ -0,0 +1,33 @@ +/* + * Copyright 2026 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#import + +#if TARGET_OS_IOS && !TARGET_OS_MACCATALYST + +#import + +#import "GoogleSignIn/Sources/GIDEMMAlertPresenter/API/GIDEMMAlertPresenter.h" + +NS_ASSUME_NONNULL_BEGIN + +/// Presents the EMM alert in a transparent `UIWindow` of its own at `UIWindowLevelAlert`. +@interface GIDEMMAlertWindowPresenter : NSObject +@end + +NS_ASSUME_NONNULL_END + +#endif // TARGET_OS_IOS && !TARGET_OS_MACCATALYST diff --git a/GoogleSignIn/Sources/GIDEMMAlertPresenter/Implementation/GIDEMMAlertWindowPresenter.m b/GoogleSignIn/Sources/GIDEMMAlertPresenter/Implementation/GIDEMMAlertWindowPresenter.m new file mode 100644 index 00000000..e64c63fc --- /dev/null +++ b/GoogleSignIn/Sources/GIDEMMAlertPresenter/Implementation/GIDEMMAlertWindowPresenter.m @@ -0,0 +1,66 @@ +/* + * Copyright 2026 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#import + +#if TARGET_OS_IOS && !TARGET_OS_MACCATALYST + +#import "GoogleSignIn/Sources/GIDEMMAlertPresenter/Implementation/GIDEMMAlertWindowPresenter.h" + +#import + +NS_ASSUME_NONNULL_BEGIN + +@implementation GIDEMMAlertWindowPresenter { + // The transparent window the alert is presented in while it is showing, or nil. + UIWindow *_alertWindow; + // The window that was key before the alert was shown; made key again on dismissal. + UIWindow *_keyWindow; +} + +- (void)presentAlert:(UIAlertController *)alert overKeyWindow:(UIWindow *)keyWindow { + _keyWindow = keyWindow; + UIWindow *alertWindow; + if (keyWindow.windowScene) { + alertWindow = [[UIWindow alloc] initWithWindowScene:keyWindow.windowScene]; + } + if (!alertWindow) { + CGRect keyWindowBounds = CGRectIsEmpty(keyWindow.bounds) ? + keyWindow.bounds : [UIScreen mainScreen].bounds; + alertWindow = [[UIWindow alloc] initWithFrame:keyWindowBounds]; + } + alertWindow.backgroundColor = [UIColor clearColor]; + alertWindow.rootViewController = [[UIViewController alloc] init]; + alertWindow.rootViewController.view.backgroundColor = [UIColor clearColor]; + alertWindow.windowLevel = UIWindowLevelAlert; + [alertWindow makeKeyAndVisible]; + _alertWindow = alertWindow; + [alertWindow.rootViewController presentViewController:alert animated:YES completion:nil]; +} + +- (void)dismissAlert { + _alertWindow.hidden = YES; + _alertWindow.rootViewController = nil; + _alertWindow = nil; + [_keyWindow makeKeyAndVisible]; + _keyWindow = nil; +} + +@end + +NS_ASSUME_NONNULL_END + +#endif // TARGET_OS_IOS && !TARGET_OS_MACCATALYST