diff --git a/Podfile b/Podfile index dc97c0ad0..f6a3ff716 100644 --- a/Podfile +++ b/Podfile @@ -2,15 +2,16 @@ platform :ios, '8.0' source 'https://github.com/CocoaPods/Specs.git' target 'Signal' do - pod 'SocketRocket', :git => 'https://github.com/facebook/SocketRocket.git' + pod 'ATAppUpdater' pod 'AxolotlKit', git: 'https://github.com/WhisperSystems/SignalProtocolKit.git' #pod 'AxolotlKit', path: '../SignalProtocolKit' - pod 'SignalServiceKit', git: 'https://github.com/WhisperSystems/SignalServiceKit.git' - #pod 'SignalServiceKit', path: '../SignalServiceKit' pod 'JSQMessagesViewController', git: 'https://github.com/WhisperSystems/JSQMessagesViewController.git', branch: 'mkirk/retain-keyboard-view' #pod 'JSQMessagesViewController', path: '../JSQMessagesViewController' pod 'PureLayout' pod 'Reachability' + pod 'SignalServiceKit', git: 'https://github.com/WhisperSystems/SignalServiceKit.git' + #pod 'SignalServiceKit', path: '../SignalServiceKit' + pod 'SocketRocket', :git => 'https://github.com/facebook/SocketRocket.git' target 'SignalTests' do inherit! :search_paths end diff --git a/Podfile.lock b/Podfile.lock index 1716e4af8..a04f35fc8 100644 --- a/Podfile.lock +++ b/Podfile.lock @@ -15,6 +15,7 @@ PODS: - AFNetworking/Serialization (3.1.0) - AFNetworking/UIKit (3.1.0): - AFNetworking/NSURLSession + - ATAppUpdater (2.0) - AxolotlKit (0.8.1): - 25519 (~> 2.0.1) - CocoaLumberjack @@ -108,6 +109,7 @@ PODS: - YapDatabase/SQLCipher/Core DEPENDENCIES: + - ATAppUpdater - AxolotlKit (from `https://github.com/WhisperSystems/SignalProtocolKit.git`) - JSQMessagesViewController (from `https://github.com/WhisperSystems/JSQMessagesViewController.git`, branch `mkirk/retain-keyboard-view`) - PureLayout @@ -143,6 +145,7 @@ CHECKOUT OPTIONS: SPEC CHECKSUMS: '25519': dc4bad7e2dbcbf1efa121068a705a44cd98c80fc AFNetworking: 5e0e199f73d8626b11e79750991f5d173d1f8b67 + ATAppUpdater: a9f7027060959d47e58733d3b48f6b9a28cb8de1 AxolotlKit: a9530d6835baae0f204b1f6b9dd79b7901176f0d CocoaLumberjack: aa9dcab71bdf9eaf2a63bbd9ddc87863efe45457 HKDFKit: c058305d6f64b84f28c50bd7aa89574625bcb62a @@ -161,6 +164,6 @@ SPEC CHECKSUMS: UnionFind: c33be5adb12983981d6e827ea94fc7f9e370f52d YapDatabase: cd911121580ff16675f65ad742a9eb0ab4d9e266 -PODFILE CHECKSUM: 01734aea935bf91b25ee9aed90e20403075e5d19 +PODFILE CHECKSUM: 4a21cd1bb2804a399fe7f9b696bd2f591f1df663 COCOAPODS: 1.2.1 diff --git a/Signal.xcodeproj/project.pbxproj b/Signal.xcodeproj/project.pbxproj index d031ace56..9666ec5b4 100644 --- a/Signal.xcodeproj/project.pbxproj +++ b/Signal.xcodeproj/project.pbxproj @@ -73,6 +73,7 @@ 34B3F89C1E8DF3270035BE1A /* BlockListViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 34B3F89B1E8DF3270035BE1A /* BlockListViewController.m */; }; 34B3F89F1E8DF5490035BE1A /* OWSTableViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 34B3F89E1E8DF5490035BE1A /* OWSTableViewController.m */; }; 34B3F8A21E8EA6040035BE1A /* ViewControllerUtils.m in Sources */ = {isa = PBXBuildFile; fileRef = 34B3F8A11E8EA6040035BE1A /* ViewControllerUtils.m */; }; + 34CCAF381F0C0599004084F4 /* AppUpdateNag.m in Sources */ = {isa = PBXBuildFile; fileRef = 34CCAF371F0C0599004084F4 /* AppUpdateNag.m */; }; 34D5CC961EA6AFAD005515DB /* OWSContactsSyncing.m in Sources */ = {isa = PBXBuildFile; fileRef = 34D5CC951EA6AFAD005515DB /* OWSContactsSyncing.m */; }; 34D5CCA91EAE3D30005515DB /* GroupViewHelper.m in Sources */ = {isa = PBXBuildFile; fileRef = 34D5CCA81EAE3D30005515DB /* GroupViewHelper.m */; }; 34D5CCB11EAE7E7F005515DB /* SelectRecipientViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 34D5CCB01EAE7E7F005515DB /* SelectRecipientViewController.m */; }; @@ -480,6 +481,8 @@ 34B3F89E1E8DF5490035BE1A /* OWSTableViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OWSTableViewController.m; sourceTree = ""; }; 34B3F8A01E8EA6040035BE1A /* ViewControllerUtils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ViewControllerUtils.h; sourceTree = ""; }; 34B3F8A11E8EA6040035BE1A /* ViewControllerUtils.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ViewControllerUtils.m; sourceTree = ""; }; + 34CCAF361F0C0599004084F4 /* AppUpdateNag.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AppUpdateNag.h; sourceTree = ""; }; + 34CCAF371F0C0599004084F4 /* AppUpdateNag.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AppUpdateNag.m; sourceTree = ""; }; 34D5CC941EA6AFAD005515DB /* OWSContactsSyncing.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OWSContactsSyncing.h; sourceTree = ""; }; 34D5CC951EA6AFAD005515DB /* OWSContactsSyncing.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OWSContactsSyncing.m; sourceTree = ""; }; 34D5CC981EA6EB79005515DB /* OWSMessageCollectionViewCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OWSMessageCollectionViewCell.h; sourceTree = ""; }; @@ -1318,6 +1321,8 @@ B68EF9B51C0B1E7D009C3DCD /* Animated GIFS */, B6DA6B051B8A2F9A00CA6F98 /* AppStoreRating.h */, B6DA6B061B8A2F9A00CA6F98 /* AppStoreRating.m */, + 34CCAF361F0C0599004084F4 /* AppUpdateNag.h */, + 34CCAF371F0C0599004084F4 /* AppUpdateNag.m */, 76EB04CF18170B33006006FC /* collections */, B90418E4183E9DD40038554A /* DateUtil.h */, B90418E5183E9DD40038554A /* DateUtil.m */, @@ -2098,6 +2103,7 @@ 4542F0961EBB9E9A00C7EE92 /* Promise+retainUntilComplete.swift in Sources */, 4516E3FF1DD2193B00DC4206 /* OWS101ExistingUsersBlockOnIdentityChange.m in Sources */, 4505C2BF1E648EA300CEBF41 /* ExperienceUpgrade.swift in Sources */, + 34CCAF381F0C0599004084F4 /* AppUpdateNag.m in Sources */, 45387B041E36D650005D00B3 /* OWS102MoveLoggingPreferenceToUserDefaults.m in Sources */, EF764C351DB67CC5000D9A87 /* UIViewController+CameraPermissions.m in Sources */, 45CD81EF1DC030E7004C9430 /* AccountManager.swift in Sources */, diff --git a/Signal/src/AppDelegate.m b/Signal/src/AppDelegate.m index eab768596..3e5712ebc 100644 --- a/Signal/src/AppDelegate.m +++ b/Signal/src/AppDelegate.m @@ -4,6 +4,7 @@ #import "AppDelegate.h" #import "AppStoreRating.h" +#import "AppUpdateNag.h" #import "CodeVerificationViewController.h" #import "DebugLogger.h" #import "Environment.h" @@ -53,6 +54,8 @@ static NSString *const kURLHostVerifyPrefix = @"verify"; @end +#pragma mark - + @implementation AppDelegate - (void)applicationDidEnterBackground:(UIApplication *)application { @@ -805,6 +808,8 @@ static NSString *const kURLHostVerifyPrefix = @"verify"; navigationController.navigationBarHidden = YES; self.window.rootViewController = navigationController; } + + [AppUpdateNag.sharedInstance showAppUpgradeNagIfNecessary]; } #pragma mark - Logging diff --git a/Signal/src/UIApplication+OWS.swift b/Signal/src/UIApplication+OWS.swift index b9062122f..11c5ad3a8 100644 --- a/Signal/src/UIApplication+OWS.swift +++ b/Signal/src/UIApplication+OWS.swift @@ -9,8 +9,19 @@ extension UIApplication { var frontmostViewController: UIViewController? { let window = UIApplication.shared.keyWindow var viewController = window!.rootViewController - while viewController?.presentedViewController != nil { - viewController = viewController?.presentedViewController + + while true { + if let nextViewController = viewController?.presentedViewController { + viewController = nextViewController + } else if let navigationController = viewController as? UINavigationController { + if let nextViewController = navigationController.topViewController { + viewController = nextViewController + } else { + break + } + } else { + break + } } return viewController diff --git a/Signal/src/util/AppUpdateNag.h b/Signal/src/util/AppUpdateNag.h new file mode 100644 index 000000000..e1f9eb186 --- /dev/null +++ b/Signal/src/util/AppUpdateNag.h @@ -0,0 +1,13 @@ +// +// Copyright (c) 2017 Open Whisper Systems. All rights reserved. +// + +@interface AppUpdateNag : NSObject + +- (instancetype)init NS_UNAVAILABLE; + ++ (instancetype)sharedInstance; + +- (void)showAppUpgradeNagIfNecessary; + +@end diff --git a/Signal/src/util/AppUpdateNag.m b/Signal/src/util/AppUpdateNag.m new file mode 100644 index 000000000..bec8a5a3f --- /dev/null +++ b/Signal/src/util/AppUpdateNag.m @@ -0,0 +1,128 @@ +// +// Copyright (c) 2017 Open Whisper Systems. All rights reserved. +// + +#import "AppUpdateNag.h" +#import "RegistrationViewController.h" +#import "Signal-Swift.h" +#import +#import + +NSString *const TSStorageManagerAppUpgradeNagCollection = @"TSStorageManagerAppUpgradeNagCollection"; +NSString *const TSStorageManagerAppUpgradeNagDate = @"TSStorageManagerAppUpgradeNagDate"; + +@interface AppUpdateNag () + +@property (nonatomic, readonly) TSStorageManager *storageManager; + +@end + +#pragma mark - + +@implementation AppUpdateNag + ++ (instancetype)sharedInstance +{ + static AppUpdateNag *sharedInstance = nil; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + sharedInstance = [[self alloc] initDefault]; + }); + return sharedInstance; +} + +- (instancetype)initDefault +{ + TSStorageManager *storageManager = [TSStorageManager sharedManager]; + + return [self initWithStorageManager:storageManager]; +} + +- (instancetype)initWithStorageManager:(TSStorageManager *)storageManager +{ + self = [super init]; + + if (!self) { + return self; + } + + OWSAssert(storageManager); + + _storageManager = storageManager; + + OWSSingletonAssert(); + + return self; +} + +- (void)showAppUpgradeNagIfNecessary +{ + // Only show nag if we are "at rest" in the home view or registration view without any + // alerts or dialogs showing. + UIViewController *frontmostViewController = + [UIApplication sharedApplication].frontmostViewController; + OWSAssert(frontmostViewController); + BOOL canPresent = ([frontmostViewController isKindOfClass:[SignalsViewController class]] || + [frontmostViewController isKindOfClass:[RegistrationViewController class]]); + if (!canPresent) { + return; + } + + NSDate *lastNagDate = [[TSStorageManager sharedManager] dateForKey:TSStorageManagerAppUpgradeNagDate + inCollection:TSStorageManagerAppUpgradeNagCollection]; + const NSTimeInterval kMinute = 60.f; + const NSTimeInterval kHour = 60 * kMinute; + const NSTimeInterval kDay = 24 * kHour; + const NSTimeInterval kNagFrequency = kDay * 14; + BOOL canNag = (!lastNagDate || fabs(lastNagDate.timeIntervalSinceNow) > kNagFrequency); + if (!canNag) { + return; + } + + ATAppUpdater *updater = [ATAppUpdater sharedUpdater]; + [updater setAlertTitle:NSLocalizedString( + @"APP_UPDATE_NAG_ALERT_TITLE", @"Title for the 'new app version available' alert.")]; + [updater setAlertMessage:NSLocalizedString(@"APP_UPDATE_NAG_ALERT_MESSAGE_FORMAT", + @"Message format for the 'new app version available' alert. Embeds: {{The latest app " + @"version number.}}.")]; + [updater setAlertUpdateButtonTitle:NSLocalizedString(@"APP_UPDATE_NAG_ALERT_UPDATE_BUTTON", + @"Label for the 'update' button in the 'new app version available' alert.")]; + [updater setAlertCancelButtonTitle:NSLocalizedString(@"TXT_CANCEL_TITLE", @"")]; + [updater setDelegate:self]; + [updater showUpdateWithConfirmation]; +} + +#pragma mark - ATAppUpdaterDelegate + +- (void)appUpdaterDidShowUpdateDialog +{ + DDLogInfo(@"%@ %s", self.tag, __PRETTY_FUNCTION__); + + [[TSStorageManager sharedManager] setDate:[NSDate new] + forKey:TSStorageManagerAppUpgradeNagDate + inCollection:TSStorageManagerAppUpgradeNagCollection]; +} + +- (void)appUpdaterUserDidLaunchAppStore +{ + DDLogInfo(@"%@ %s", self.tag, __PRETTY_FUNCTION__); +} + +- (void)appUpdaterUserDidCancel +{ + DDLogInfo(@"%@ %s", self.tag, __PRETTY_FUNCTION__); +} + +#pragma mark - Logging + ++ (NSString *)tag +{ + return [NSString stringWithFormat:@"[%@]", self.class]; +} + +- (NSString *)tag +{ + return self.class.tag; +} + +@end diff --git a/Signal/translations/en.lproj/Localizable.strings b/Signal/translations/en.lproj/Localizable.strings index 1fe35f5f6..54652f269 100644 --- a/Signal/translations/en.lproj/Localizable.strings +++ b/Signal/translations/en.lproj/Localizable.strings @@ -55,6 +55,15 @@ /* No comment provided by engineer. */ "APN_MESSAGE_IN_GROUP_DETAILED" = "%@ in group %@: %@"; +/* Message format for the 'new app version available' alert. Embeds: {{The latest app version number.}}. */ +"APP_UPDATE_NAG_ALERT_MESSAGE_FORMAT" = "Version %@ is available in the App Store."; + +/* Title for the 'new app version available' alert. */ +"APP_UPDATE_NAG_ALERT_TITLE" = "New Version"; + +/* Label for the 'update' button in the 'new app version available' alert. */ +"APP_UPDATE_NAG_ALERT_UPDATE_BUTTON" = "Update"; + /* Name of application */ "APPLICATION_NAME" = "Signal";