From 848f055da1967773dca7a1783c117b2c13283e64 Mon Sep 17 00:00:00 2001 From: Matthew Chen Date: Wed, 6 Dec 2017 13:53:01 -0500 Subject: [PATCH] Add SAE error views. --- Signal.xcodeproj/project.pbxproj | 8 +- Signal/src/AppDelegate.m | 4 + .../translations/en.lproj/Localizable.strings | 12 +++ SignalMessaging/utils/OWSPreferences.h | 6 ++ SignalMessaging/utils/OWSPreferences.m | 18 ++++ .../SAEFailedViewController.swift | 98 +++++++++++++++++++ .../ShareViewController.swift | 40 ++++++-- 7 files changed, 176 insertions(+), 10 deletions(-) create mode 100644 SignalShareExtension/SAEFailedViewController.swift diff --git a/Signal.xcodeproj/project.pbxproj b/Signal.xcodeproj/project.pbxproj index 8750f6a2a..e68df7e17 100644 --- a/Signal.xcodeproj/project.pbxproj +++ b/Signal.xcodeproj/project.pbxproj @@ -118,6 +118,7 @@ 347850531FD74984007B8332 /* GTSR4.crt in Resources */ = {isa = PBXBuildFile; fileRef = 458D51471FCCD82500B5BC53 /* GTSR4.crt */; }; 347850541FD74984007B8332 /* textsecure.cer in Resources */ = {isa = PBXBuildFile; fileRef = 458D51451FCCD82500B5BC53 /* textsecure.cer */; }; 347850551FD749C0007B8332 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = B6F509951AA53F760068F56A /* Localizable.strings */; }; + 347850571FD86544007B8332 /* SAEFailedViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 347850561FD86544007B8332 /* SAEFailedViewController.swift */; }; 348F2EAE1F0D21BC00D4ECE0 /* DeviceSleepManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 348F2EAD1F0D21BC00D4ECE0 /* DeviceSleepManager.swift */; }; 3497DBEC1ECE257500DB2605 /* OWSCountryMetadata.m in Sources */ = {isa = PBXBuildFile; fileRef = 3497DBEB1ECE257500DB2605 /* OWSCountryMetadata.m */; }; 3497DBEF1ECE2E4700DB2605 /* DomainFrontingCountryViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 3497DBEE1ECE2E4700DB2605 /* DomainFrontingCountryViewController.m */; }; @@ -559,6 +560,7 @@ 3471B1D91EB7C63600F6AEC8 /* NewNonContactConversationViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = NewNonContactConversationViewController.m; sourceTree = ""; }; 3472229D1EB22FFE00E53955 /* AddToGroupViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AddToGroupViewController.h; sourceTree = ""; }; 3472229E1EB22FFE00E53955 /* AddToGroupViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AddToGroupViewController.m; sourceTree = ""; }; + 347850561FD86544007B8332 /* SAEFailedViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SAEFailedViewController.swift; sourceTree = ""; }; 348F2EAD1F0D21BC00D4ECE0 /* DeviceSleepManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DeviceSleepManager.swift; sourceTree = ""; }; 3495BC911F1426B800B478F5 /* ar */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ar; path = translations/ar.lproj/Localizable.strings; sourceTree = ""; }; 3497DBEA1ECE257500DB2605 /* OWSCountryMetadata.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OWSCountryMetadata.h; sourceTree = ""; }; @@ -1442,10 +1444,11 @@ isa = PBXGroup; children = ( 4535186F1FC635DD00210559 /* Info.plist */, - 346129D71FD5B84800532771 /* SAECallMessageHandler.swift */, - 346129D81FD5B84900532771 /* SAENotificationsManager.swift */, 4535186C1FC635DD00210559 /* MainInterface.storyboard */, + 346129D71FD5B84800532771 /* SAECallMessageHandler.swift */, + 347850561FD86544007B8332 /* SAEFailedViewController.swift */, 3461284A1FD0B93F00532771 /* SAELoadViewController.swift */, + 346129D81FD5B84900532771 /* SAENotificationsManager.swift */, 4535186A1FC635DD00210559 /* ShareViewController.swift */, 34480B371FD092A900BC14EF /* SignalShareExtension-Bridging-Header.h */, 34480B381FD092E300BC14EF /* SignalShareExtension-Prefix.pch */, @@ -2640,6 +2643,7 @@ 34480B361FD0929200BC14EF /* ShareAppExtensionContext.m in Sources */, 346129D91FD5B84900532771 /* SAECallMessageHandler.swift in Sources */, 3461284B1FD0B94000532771 /* SAELoadViewController.swift in Sources */, + 347850571FD86544007B8332 /* SAEFailedViewController.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/Signal/src/AppDelegate.m b/Signal/src/AppDelegate.m index ef0be82c5..574ac9896 100644 --- a/Signal/src/AppDelegate.m +++ b/Signal/src/AppDelegate.m @@ -873,6 +873,8 @@ static NSString *const kURLHostVerifyPrefix = @"verify"; { DDLogInfo(@"%@ databaseViewRegistrationComplete", self.logTag); + [OWSPreferences setIsRegistered:[TSAccountManager isRegistered]]; + if ([TSAccountManager isRegistered]) { DDLogInfo(@"localNumber: %@", [TSAccountManager localNumber]); @@ -923,6 +925,8 @@ static NSString *const kURLHostVerifyPrefix = @"verify"; DDLogInfo(@"registrationStateDidChange"); + [OWSPreferences setIsRegistered:[TSAccountManager isRegistered]]; + if ([TSAccountManager isRegistered]) { DDLogInfo(@"localNumber: %@", [TSAccountManager localNumber]); diff --git a/Signal/translations/en.lproj/Localizable.strings b/Signal/translations/en.lproj/Localizable.strings index 964b61b90..275c4f72c 100644 --- a/Signal/translations/en.lproj/Localizable.strings +++ b/Signal/translations/en.lproj/Localizable.strings @@ -1552,6 +1552,18 @@ /* Indicates that the share extension is still loading. */ "SHARE_EXTENSION_LOADING" = "Loading..."; +/* Message indicating that the share extension cannot be used until the user has registered in the main app. */ +"SHARE_EXTENSION_NOT_REGISTERED_MESSAGE" = "Launch the Signal app to register."; + +/* Title indicating that the share extension cannot be used until the user has registered in the main app. */ +"SHARE_EXTENSION_NOT_REGISTERED_TITLE" = "Not Registered"; + +/* Message indicating that the share extension cannot be used until the main app has been launched at least once. */ +"SHARE_EXTENSION_NOT_YET_MIGRATED_MESSAGE" = "Launch the Signal app to update."; + +/* Title indicating that the share extension cannot be used until the main app has been launched at least once. */ +"SHARE_EXTENSION_NOT_YET_MIGRATED_TITLE" = "Not Ready"; + /* Action sheet item */ "SHOW_SAFETY_NUMBER_ACTION" = "Show New Safety Number"; diff --git a/SignalMessaging/utils/OWSPreferences.h b/SignalMessaging/utils/OWSPreferences.h index b49728860..12a7ca83a 100644 --- a/SignalMessaging/utils/OWSPreferences.h +++ b/SignalMessaging/utils/OWSPreferences.h @@ -30,6 +30,12 @@ extern NSString *const OWSPreferencesKeyEnableDebugLog; + (BOOL)isReadyForAppExtensions; + (void)setIsReadyForAppExtensions:(BOOL)value; +// TSAccountManager is the source of truth; this is less reliable +// and should only be used in edge cases where the database is not +// yet available. ++ (BOOL)isRegistered; ++ (void)setIsRegistered:(BOOL)value; + - (BOOL)getHasSentAMessage; - (void)setHasSentAMessage:(BOOL)enabled; diff --git a/SignalMessaging/utils/OWSPreferences.m b/SignalMessaging/utils/OWSPreferences.m index db83da31b..9180a3fb3 100644 --- a/SignalMessaging/utils/OWSPreferences.m +++ b/SignalMessaging/utils/OWSPreferences.m @@ -24,6 +24,7 @@ NSString *const OWSPreferencesKeyCallsHideIPAddress = @"CallsHideIPAddress"; NSString *const OWSPreferencesKeyHasDeclinedNoContactsView = @"hasDeclinedNoContactsView"; NSString *const OWSPreferencesKeyIOSUpgradeNagVersion = @"iOSUpgradeNagVersion"; NSString *const OWSPreferencesKey_IsReadyForAppExtensions = @"isReadyForAppExtensions"; +NSString *const OWSPreferencesKey_IsRegistered = @"OWSPreferencesKey_IsRegistered"; @implementation OWSPreferences @@ -78,6 +79,23 @@ NSString *const OWSPreferencesKey_IsReadyForAppExtensions = @"isReadyForAppExten [NSUserDefaults.appUserDefaults synchronize]; } ++ (BOOL)isRegistered +{ + NSNumber *preference = [NSUserDefaults.appUserDefaults objectForKey:OWSPreferencesKey_IsRegistered]; + + if (preference) { + return [preference boolValue]; + } else { + return NO; + } +} + ++ (void)setIsRegistered:(BOOL)value +{ + [NSUserDefaults.appUserDefaults setObject:@(value) forKey:OWSPreferencesKey_IsRegistered]; + [NSUserDefaults.appUserDefaults synchronize]; +} + - (BOOL)screenSecurityIsEnabled { NSNumber *preference = [self tryGetValueForKey:OWSPreferencesKeyScreenSecurity]; diff --git a/SignalShareExtension/SAEFailedViewController.swift b/SignalShareExtension/SAEFailedViewController.swift new file mode 100644 index 000000000..45a6c2ba6 --- /dev/null +++ b/SignalShareExtension/SAEFailedViewController.swift @@ -0,0 +1,98 @@ +// +// Copyright (c) 2017 Open Whisper Systems. All rights reserved. +// + +import UIKit +import SignalMessaging +import PureLayout + +// All Observer methods will be invoked from the main thread. +protocol SAEFailedViewDelegate: class { + func shareExtensionWasCancelled() +} + +class SAEFailedViewController: UIViewController { + + weak var delegate: SAEFailedViewDelegate? + + let failureTitle: String + let failureMessage: String + + // MARK: Initializers and Factory Methods + + init(delegate: SAEFailedViewDelegate, title: String, message: String) { + self.delegate = delegate + self.failureTitle = title + self.failureMessage = message + super.init(nibName: nil, bundle: nil) + } + + required init?(coder aDecoder: NSCoder) { + self.failureTitle = "" + self.failureMessage = "" + super.init(coder: aDecoder) + } + + override func loadView() { + super.loadView() + + self.navigationItem.leftBarButtonItem = UIBarButtonItem(barButtonSystemItem: .cancel, + target: self, + action: #selector(cancelPressed)) + self.navigationItem.title = "Signal" + + self.view.backgroundColor = UIColor.ows_signalBrandBlue() + + let logoImage = UIImage(named: "logoSignal") + let logoImageView = UIImageView(image: logoImage) + self.view.addSubview(logoImageView) + logoImageView.autoCenterInSuperview() + let logoSize = CGFloat(120) + logoImageView.autoSetDimension(.width, toSize: logoSize) + logoImageView.autoSetDimension(.height, toSize: logoSize) + + let titleLabel = UILabel() + titleLabel.textColor = UIColor.white + titleLabel.font = UIFont.ows_mediumFont(withSize: 18) + titleLabel.text = failureTitle + titleLabel.textAlignment = .center + titleLabel.numberOfLines = 0 + titleLabel.lineBreakMode = .byWordWrapping + self.view.addSubview(titleLabel) + titleLabel.autoPinEdge(toSuperviewEdge: .leading, withInset: 20) + titleLabel.autoPinEdge(toSuperviewEdge: .trailing, withInset: 20) + titleLabel.autoPinEdge(.top, to: .bottom, of: logoImageView, withOffset: 25) + + let messageLabel = UILabel() + messageLabel.textColor = UIColor.white + messageLabel.font = UIFont.ows_regularFont(withSize: 14) + messageLabel.text = failureMessage + messageLabel.textAlignment = .center + messageLabel.numberOfLines = 0 + messageLabel.lineBreakMode = .byWordWrapping + self.view.addSubview(messageLabel) + messageLabel.autoPinEdge(toSuperviewEdge: .leading, withInset: 20) + messageLabel.autoPinEdge(toSuperviewEdge: .trailing, withInset: 20) + messageLabel.autoPinEdge(.top, to: .bottom, of: titleLabel, withOffset: 10) + } + + override func viewWillAppear(_ animated: Bool) { + super.viewWillAppear(animated) + + self.navigationController?.isNavigationBarHidden = false + } + + override func viewDidDisappear(_ animated: Bool) { + super.viewDidDisappear(animated) + } + + // MARK: - Event Handlers + + @objc func cancelPressed(sender: UIButton) { + guard let delegate = delegate else { + owsFail("\(self.logTag) missing delegate") + return + } + delegate.shareExtensionWasCancelled() + } +} diff --git a/SignalShareExtension/ShareViewController.swift b/SignalShareExtension/ShareViewController.swift index 3d9ac64f9..007415b25 100644 --- a/SignalShareExtension/ShareViewController.swift +++ b/SignalShareExtension/ShareViewController.swift @@ -9,7 +9,7 @@ import PureLayout import SignalServiceKit @objc -public class ShareViewController: UINavigationController, SAELoadViewDelegate { +public class ShareViewController: UINavigationController, SAELoadViewDelegate, SAEFailedViewDelegate { private var contactsSyncing: OWSContactsSyncing? @@ -51,7 +51,11 @@ public class ShareViewController: UINavigationController, SAELoadViewDelegate { isReadyForAppExtensions = OWSPreferences.isReadyForAppExtensions() if !isReadyForAppExtensions { - // TODO: Show the "You need to launch main app" view. + if (OWSPreferences.isRegistered()) { + showNotReadyView() + } else { + showNotRegisteredView() + } return } @@ -215,11 +219,7 @@ public class ShareViewController: UINavigationController, SAELoadViewDelegate { // [[SignalsNavigationController alloc] initWithRootViewController:homeView]; // self.window.rootViewController = navigationController; } else { - // RegistrationViewController *viewController = [RegistrationViewController new]; - // OWSNavigationController *navigationController = - // [[OWSNavigationController alloc] initWithRootViewController:viewController]; - // navigationController.navigationBarHidden = YES; - // self.window.rootViewController = navigationController; + showNotRegisteredView() } // We don't use the AppUpdateNag in the SAE. @@ -270,6 +270,30 @@ public class ShareViewController: UINavigationController, SAELoadViewDelegate { Environment.current().contactsManager.startObserving() } + // MARK: Error Views + + private func showNotReadyView() { + let failureTitle = NSLocalizedString("SHARE_EXTENSION_NOT_YET_MIGRATED_TITLE", + comment: "Title indicating that the share extension cannot be used until the main app has been launched at least once.") + let failureMessage = NSLocalizedString("SHARE_EXTENSION_NOT_YET_MIGRATED_MESSAGE", + comment: "Message indicating that the share extension cannot be used until the main app has been launched at least once.") + showErrorView(title:failureTitle, message:failureMessage) + } + + private func showNotRegisteredView() { + let failureTitle = NSLocalizedString("SHARE_EXTENSION_NOT_REGISTERED_TITLE", + comment: "Title indicating that the share extension cannot be used until the user has registered in the main app.") + let failureMessage = NSLocalizedString("SHARE_EXTENSION_NOT_REGISTERED_MESSAGE", + comment: "Message indicating that the share extension cannot be used until the user has registered in the main app.") + showErrorView(title:failureTitle, message:failureMessage) + } + + private func showErrorView(title: String, message: String) { + let viewController = SAEFailedViewController(delegate:self, title:title, message:message) + self.setViewControllers([viewController], animated: false) + self.isNavigationBarHidden = false + } + // MARK: View Lifecycle override open func viewDidLoad() { @@ -310,7 +334,7 @@ public class ShareViewController: UINavigationController, SAELoadViewDelegate { Logger.flush() } - // MARK: SAELoadViewDelegate + // MARK: SAELoadViewDelegate, SAEFailedViewDelegate public func shareExtensionWasCancelled() { self.extensionContext!.completeRequest(returningItems: [], completionHandler: nil)