|
|
@@ -0,0 +1,116 @@
|
|
|
+/*
|
|
|
+ * Copyright 2023 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 "GoogleSignIn/Sources/GIDTimedLoader/GIDTimedLoader.h"
|
|
|
+
|
|
|
+#if TARGET_OS_IOS && !TARGET_OS_MACCATALYST
|
|
|
+
|
|
|
+@import UIKit;
|
|
|
+@import CoreMedia;
|
|
|
+#import "GoogleSignIn/Sources/GIDAppCheck/Implementations/GIDAppCheck.h"
|
|
|
+#import "GoogleSignIn/Sources/GIDAppCheck/UI/GIDActivityIndicatorViewController.h"
|
|
|
+
|
|
|
+CFTimeInterval const kGIDTimedLoaderMinAnimationDuration = 1.0;
|
|
|
+CFTimeInterval const kGIDTimedLoaderMaxDelayBeforeAnimating = 0.5;
|
|
|
+
|
|
|
+@interface GIDTimedLoader ()
|
|
|
+
|
|
|
+@property(nonatomic, strong) UIViewController *presentingViewController;
|
|
|
+@property(nonatomic, strong) GIDActivityIndicatorViewController *loadingViewController;
|
|
|
+@property(nonatomic, strong, nullable) NSTimer *loadingTimer;
|
|
|
+/// Timestamp representing when the loading view controller was presented and started animating
|
|
|
+@property(nonatomic) CFTimeInterval loadingTimeStamp;
|
|
|
+
|
|
|
+@end
|
|
|
+
|
|
|
+@implementation GIDTimedLoader
|
|
|
+
|
|
|
+- (instancetype)initWithPresentingViewController:(UIViewController *)presentingViewController {
|
|
|
+ if (self = [super init]) {
|
|
|
+ _presentingViewController = presentingViewController;
|
|
|
+ _loadingViewController = [[GIDActivityIndicatorViewController alloc] init];
|
|
|
+ _animationStatus = GIDTimedLoaderAnimationStatusNotStarted;
|
|
|
+ }
|
|
|
+ return self;
|
|
|
+}
|
|
|
+
|
|
|
+- (void)startTiming {
|
|
|
+ if (self.animationStatus == GIDTimedLoaderAnimationStatusAnimating) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ self.animationStatus = GIDTimedLoaderAnimationStatusAnimating;
|
|
|
+ self.loadingTimer = [NSTimer scheduledTimerWithTimeInterval:kGIDTimedLoaderMaxDelayBeforeAnimating
|
|
|
+ target:self
|
|
|
+ selector:@selector(presentLoadingViewController)
|
|
|
+ userInfo:nil
|
|
|
+ repeats:NO];
|
|
|
+}
|
|
|
+
|
|
|
+- (void)presentLoadingViewController {
|
|
|
+ if (self.animationStatus == GIDTimedLoaderAnimationStatusStopped) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ self.animationStatus = GIDTimedLoaderAnimationStatusAnimating;
|
|
|
+ self.loadingTimeStamp = CACurrentMediaTime();
|
|
|
+ dispatch_async(dispatch_get_main_queue(), ^{
|
|
|
+ // Since this loading VC may be reused, the activity indicator may have been stopped; restart it
|
|
|
+ [self.loadingViewController.activityIndicator startAnimating];
|
|
|
+ [self.presentingViewController presentViewController:self.loadingViewController
|
|
|
+ animated:YES
|
|
|
+ completion:nil];
|
|
|
+ });
|
|
|
+}
|
|
|
+
|
|
|
+- (void)stopTimingWithCompletion:(void (^)(void))completion {
|
|
|
+ if (self.animationStatus != GIDTimedLoaderAnimationStatusAnimating) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ [self.loadingTimer invalidate];
|
|
|
+ self.loadingTimer = nil;
|
|
|
+
|
|
|
+ dispatch_time_t deadline = [self remainingDurationToAnimate];
|
|
|
+ dispatch_after(deadline, dispatch_get_main_queue(), ^{
|
|
|
+ self.animationStatus = GIDTimedLoaderAnimationStatusStopped;
|
|
|
+ [self.loadingViewController.activityIndicator stopAnimating];
|
|
|
+ [self.loadingViewController dismissViewControllerAnimated:YES completion:nil];
|
|
|
+ completion();
|
|
|
+ });
|
|
|
+}
|
|
|
+
|
|
|
+- (dispatch_time_t)remainingDurationToAnimate {
|
|
|
+ // If we are not animating, then no need to wait
|
|
|
+ if (self.animationStatus != GIDTimedLoaderAnimationStatusAnimating) {
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ CFTimeInterval now = CACurrentMediaTime();
|
|
|
+ CFTimeInterval durationWaited = now - self.loadingTimeStamp;
|
|
|
+ // If we have already waited for the minimum animation duration, then no need to wait
|
|
|
+ if (durationWaited >= kGIDTimedLoaderMinAnimationDuration) {
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ CFTimeInterval diff = kGIDTimedLoaderMinAnimationDuration - durationWaited;
|
|
|
+ int64_t diffNanos = diff * NSEC_PER_SEC;
|
|
|
+ dispatch_time_t timeToWait = dispatch_time(DISPATCH_TIME_NOW, diffNanos);
|
|
|
+ return timeToWait;
|
|
|
+}
|
|
|
+
|
|
|
+@end
|
|
|
+
|
|
|
+#endif // TARGET_OS_IOS && !TARGET_OS_MACCATALYST
|