Allow switching onboarding mode

This commit is contained in:
Nora Trapp 2019-11-08 19:00:09 -08:00
parent d416b99bef
commit f097ebaeda
17 changed files with 376 additions and 30 deletions

View File

@ -614,6 +614,7 @@
88D1D40622EF8F1100F472C5 /* ThreadDetailsCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 88D1D40522EF8F1100F472C5 /* ThreadDetailsCell.swift */; };
88E34F2722F269E900966CC2 /* StorageServiceManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 88E34F2622F269E900966CC2 /* StorageServiceManager.swift */; };
88E34F2922F26CC100966CC2 /* StorageServiceProto+Sync.swift in Sources */ = {isa = PBXBuildFile; fileRef = 88E34F2822F26CC100966CC2 /* StorageServiceProto+Sync.swift */; };
88EAD79223764F09004B797E /* OnboardingModeSwitchConfirmationViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 88EAD79123764F09004B797E /* OnboardingModeSwitchConfirmationViewController.swift */; };
88F7EE93230253C5003ADF7D /* UsernameViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 88F7EE92230253C5003ADF7D /* UsernameViewController.swift */; };
954AEE6A1DF33E01002E5410 /* ContactsPickerTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 954AEE681DF33D32002E5410 /* ContactsPickerTest.swift */; };
A10FDF79184FB4BB007FF963 /* MediaPlayer.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 76C87F18181EFCE600C4ACAB /* MediaPlayer.framework */; };
@ -1453,6 +1454,7 @@
88D1D40522EF8F1100F472C5 /* ThreadDetailsCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ThreadDetailsCell.swift; sourceTree = "<group>"; };
88E34F2622F269E900966CC2 /* StorageServiceManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StorageServiceManager.swift; sourceTree = "<group>"; };
88E34F2822F26CC100966CC2 /* StorageServiceProto+Sync.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "StorageServiceProto+Sync.swift"; sourceTree = "<group>"; };
88EAD79123764F09004B797E /* OnboardingModeSwitchConfirmationViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OnboardingModeSwitchConfirmationViewController.swift; sourceTree = "<group>"; };
88F7EE92230253C5003ADF7D /* UsernameViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UsernameViewController.swift; sourceTree = "<group>"; };
8981C8F64D94D3C52EB67A2C /* Pods-SignalTests.test.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-SignalTests.test.xcconfig"; path = "Pods/Target Support Files/Pods-SignalTests/Pods-SignalTests.test.xcconfig"; sourceTree = "<group>"; };
8EEE74B0753448C085B48721 /* Pods-SignalMessaging.app store release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-SignalMessaging.app store release.xcconfig"; path = "Pods/Target Support Files/Pods-SignalMessaging/Pods-SignalMessaging.app store release.xcconfig"; sourceTree = "<group>"; };
@ -1721,6 +1723,7 @@
3448E15F22134C88004B052E /* OnboardingSplashViewController.swift */,
34A4C61D221613D00042EF2E /* OnboardingVerificationViewController.swift */,
346E9D5321B040B600562252 /* RegistrationController.swift */,
88EAD79123764F09004B797E /* OnboardingModeSwitchConfirmationViewController.swift */,
);
path = Registration;
sourceTree = "<group>";
@ -4124,6 +4127,7 @@
45DF5DF21DDB843F00C936C7 /* CompareSafetyNumbersActivity.swift in Sources */,
451166C01FD86B98000739BA /* AccountManager.swift in Sources */,
3430FE181F7751D4000EC51B /* GiphyAPI.swift in Sources */,
88EAD79223764F09004B797E /* OnboardingModeSwitchConfirmationViewController.swift in Sources */,
4C2F454F214C00E1004871FF /* AvatarTableViewCell.swift in Sources */,
346E9D5421B040B700562252 /* RegistrationController.swift in Sources */,
4CD675BE22E7BE35008010D2 /* MediaDismissAnimationController.swift in Sources */,

View File

@ -0,0 +1,12 @@
{
"images" : [
{
"idiom" : "universal",
"filename" : "ipad_primary.pdf"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}

Binary file not shown.

View File

@ -0,0 +1,12 @@
{
"images" : [
{
"idiom" : "universal",
"filename" : "link-24.pdf"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}

Binary file not shown.

View File

@ -0,0 +1,12 @@
{
"images" : [
{
"idiom" : "universal",
"filename" : "link-broken-24.pdf"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}

View File

@ -0,0 +1,106 @@
%PDF-1.7
1 0 obj
<< >>
endobj
2 0 obj
<< /Length 3 0 R >>
stream
/DeviceRGB CS
/DeviceRGB cs
q
1.000000 0.000000 -0.000000 1.000000 1.500000 4.718994 cm
0.000000 0.000000 0.000000 scn
16.719000 16.062012 m
11.503500 10.845012 l
10.826029 11.446169 9.952233 11.779044 9.046500 11.781012 c
3.750000 11.781012 l
2.755438 11.781012 1.801611 11.385923 1.098350 10.682662 c
0.395088 9.979401 0.000000 9.025574 0.000000 8.031012 c
0.000000 7.036449 0.395088 6.082623 1.098350 5.379361 c
1.801611 4.676100 2.755438 4.281012 3.750000 4.281012 c
4.939500 4.281012 l
1.719000 1.062012 l
2.781000 0.000011 l
17.781000 15.000011 l
16.719000 16.062012 l
h
3.750000 5.781012 m
3.153263 5.781012 2.580967 6.018064 2.159010 6.440022 c
1.737053 6.861978 1.500000 7.434275 1.500000 8.031012 c
1.500000 8.627748 1.737053 9.200045 2.159010 9.622002 c
2.580967 10.043959 3.153263 10.281012 3.750000 10.281012 c
9.046500 10.281012 l
9.552755 10.275828 10.041739 10.096234 10.431000 9.772511 c
6.439500 5.781012 l
3.750000 5.781012 l
h
21.000000 8.031012 m
21.000000 7.036449 20.604912 6.082623 19.901649 5.379361 c
19.198389 4.676100 18.244562 4.281012 17.250000 4.281012 c
12.750000 4.281012 l
11.801718 4.282545 10.889729 4.645731 10.200001 5.296512 c
11.260500 6.358512 l
11.668746 5.988836 12.199255 5.783151 12.750000 5.781012 c
17.250000 5.781012 l
17.846737 5.781012 18.419033 6.018064 18.840990 6.440022 c
19.262947 6.861978 19.500000 7.434275 19.500000 8.031012 c
19.500000 8.627748 19.262947 9.200045 18.840990 9.622002 c
18.419033 10.043959 17.846737 10.281012 17.250000 10.281012 c
15.181500 10.281012 l
16.681499 11.781012 l
17.250000 11.781012 l
18.244562 11.781012 19.198389 11.385923 19.901649 10.682662 c
20.604912 9.979401 21.000000 9.025574 21.000000 8.031012 c
h
f
n
Q
endstream
endobj
3 0 obj
1701
endobj
4 0 obj
<< /MediaBox [ 0.000000 0.000000 24.000000 24.000000 ]
/Resources 1 0 R
/Contents 2 0 R
/Parent 5 0 R
/Type /Page
>>
endobj
5 0 obj
<< /Kids [ 4 0 R ]
/Count 1
/Type /Pages
>>
endobj
6 0 obj
<< /Type /Catalog
/Pages 5 0 R
>>
endobj
xref
0 7
0000000000 65535 f
0000000010 00000 n
0000000034 00000 n
0000001791 00000 n
0000001814 00000 n
0000001971 00000 n
0000002045 00000 n
trailer
<< /ID [ (some) (id) ]
/Root 6 0 R
/Size 7
>>
startxref
2104
%%EOF

View File

@ -200,8 +200,7 @@ class InviteFlow: NSObject, MFMessageComposeViewControllerDelegate, MFMailCompos
let warning = ActionSheetController(title: nil,
message: NSLocalizedString("INVITE_WARNING_MULTIPLE_INVITES_BY_TEXT",
comment: "Alert warning that sending an invite to multiple users will create a group message whose recipients will be able to see each other."))
warning.addAction(ActionSheetAction(title: NSLocalizedString("BUTTON_CONTINUE",
comment: "Label for 'continue' button."),
warning.addAction(ActionSheetAction(title: CommonStrings.continueButton,
style: .default, handler: { [weak self] _ in
self?.sendSMSTo(phoneNumbers: phoneNumbers)
}))

View File

@ -68,6 +68,10 @@ public class OnboardingBaseViewController: OWSViewController {
return button(title: title, selector: selector, titleColor: .ows_signalBlue, backgroundColor: .white)
}
func shouldShowBackButton() -> Bool {
return onboardingController.isOnboardingModeOverriden
}
private func button(title: String, selector: Selector, titleColor: UIColor, backgroundColor: UIColor) -> OWSFlatButton {
let font = UIFont.ows_dynamicTypeBodyClamped.ows_semibold()
let buttonHeight = OWSFlatButton.heightForFont(font)
@ -104,6 +108,22 @@ public class OnboardingBaseViewController: OWSViewController {
super.viewDidLoad()
self.shouldBottomViewReserveSpaceForKeyboard = true
if (shouldShowBackButton()) {
let backButton = UIButton()
let backButtonImage = CurrentAppContext().isRTL ? #imageLiteral(resourceName: "NavBarBackRTL") : #imageLiteral(resourceName: "NavBarBack")
backButton.setTemplateImage(backButtonImage, tintColor: Theme.secondaryTextAndIconColor)
backButton.addTarget(self, action: #selector(navigateBack), for: .touchUpInside)
view.addSubview(backButton)
backButton.autoSetDimensions(to: CGSize(width: 40, height: 40))
backButton.autoPinEdge(toSuperviewMargin: .leading)
backButton.autoPinEdge(toSuperviewMargin: .top)
}
}
@objc func navigateBack() {
navigationController?.popViewController(animated: true)
}
public override func viewWillAppear(_ animated: Bool) {

View File

@ -79,6 +79,22 @@ public class OnboardingController: NSObject {
return AppEnvironment.shared.backup
}
// MARK: -
public enum OnboardingMode {
case provisioning
case registering
}
public let defaultOnboardingMode: OnboardingMode = UIDevice.current.isIPad ? .provisioning : .registering
private var onboardingModeOverride: OnboardingMode?
public var onboardingMode: OnboardingMode {
return onboardingModeOverride ?? defaultOnboardingMode
}
public var isOnboardingModeOverriden: Bool {
return onboardingMode != defaultOnboardingMode
}
// MARK: -
@objc
@ -103,10 +119,45 @@ public class OnboardingController: NSObject {
Logger.info("")
onboardingModeOverride = nil
let view = OnboardingPermissionsViewController(onboardingController: self)
viewController.navigationController?.pushViewController(view, animated: true)
}
public func onboardingSplashRequestedModeSwitch(viewController: UIViewController) {
AssertIsOnMainThread()
Logger.info("")
let view = OnboardingModeSwitchConfirmationViewController(onboardingController: self)
viewController.navigationController?.pushViewController(view, animated: true)
}
public func toggleModeSwitch(viewController: UIViewController) {
AssertIsOnMainThread()
Logger.info("")
if isOnboardingModeOverriden {
onboardingModeOverride = nil
viewController.navigationController?.popToRootViewController(animated: true)
} else {
let newMode: OnboardingMode
switch defaultOnboardingMode {
case .provisioning:
newMode = .registering
case .registering:
newMode = .provisioning
}
onboardingModeOverride = newMode
let view = OnboardingPermissionsViewController(onboardingController: self)
viewController.navigationController?.pushViewController(view, animated: true)
}
}
public func onboardingPermissionsWasSkipped(viewController: UIViewController) {
AssertIsOnMainThread()
@ -136,9 +187,7 @@ public class OnboardingController: NSObject {
private func pushStartDeviceRegistrationView(onto navigationController: UINavigationController) {
AssertIsOnMainThread()
let shouldShowSecondaryLinking = UIDevice.current.isIPad
if shouldShowSecondaryLinking {
if onboardingMode == .provisioning {
let provisioningController = ProvisioningController(onboardingController: self)
let prepViewController = SecondaryLinkingPrepViewController(provisioningController: provisioningController)
navigationController.pushViewController(prepViewController, animated: true)

View File

@ -0,0 +1,98 @@
//
// Copyright (c) 2019 Open Whisper Systems. All rights reserved.
//
import Foundation
class OnboardingModeSwitchConfirmationViewController: OnboardingBaseViewController {
var warningText: String?
override func loadView() {
view = UIView()
view.addSubview(primaryView)
primaryView.autoPinEdgesToSuperviewEdges()
view.backgroundColor = Theme.backgroundColor
let titleText: String
let explanationText: String
let nextButtonText: String
let image: UIImage?
switch onboardingController.defaultOnboardingMode {
case .registering:
titleText = NSLocalizedString("ONBOARDING_MODE_SWITCH_TITLE_REGISTERING",
comment: "header text indicating to the user they're switching from registering to linking flow")
explanationText = NSLocalizedString("ONBOARDING_MODE_SWITCH_EXPLANATION_REGISTERING",
comment: "explanation to the user they're switching from registering to linking flow")
nextButtonText = NSLocalizedString("ONBOARDING_MODE_SWITCH_BUTTON_REGISTERING",
comment: "button indicating that the user will link their phone")
warningText = NSLocalizedString("ONBOARDING_MODE_SWITCH_WARNING_REGISTERING",
comment: "warning to the user that linking a phone is not recommended")
image = nil
case .provisioning:
titleText = NSLocalizedString("ONBOARDING_MODE_SWITCH_TITLE_PROVISIONING",
comment: "header text indicating to the user they're switching from linking to registering flow")
explanationText = NSLocalizedString("ONBOARDING_MODE_SWITCH_EXPLANATION_PROVISIONING",
comment: "explanation to the user they're switching from linking to registering flow")
nextButtonText = NSLocalizedString("ONBOARDING_MODE_SWITCH_BUTTON_PROVISIONING",
comment: "button indicating that the user will register their ipad")
warningText = NSLocalizedString("ONBOARDING_MODE_SWITCH_WARNING_PROVISIONING",
comment: "warning to the user that registering an ipad is not recommended")
image = #imageLiteral(resourceName: "ipad-primary")
}
let imageView = UIImageView(image: image)
imageView.contentMode = .scaleAspectFit
imageView.setContentHuggingHigh()
let titleLabel = self.titleLabel(text: titleText)
let explanationLabel = self.explanationLabel(explanationText: explanationText)
let nextButton = self.primaryButton(title: nextButtonText,
selector: #selector(didPressNext))
nextButton.accessibilityIdentifier = "onboarding.modeSwitch.nextButton"
let primaryButtonView = OnboardingBaseViewController.horizontallyWrap(primaryButton: nextButton)
let topSpacer = UIView.vStretchingSpacer(minHeight: 12)
let bottomSpacer = UIView.vStretchingSpacer(minHeight: 12)
let stackView = UIStackView(arrangedSubviews: [
titleLabel,
UIView.spacer(withHeight: 12),
explanationLabel,
topSpacer,
imageView,
bottomSpacer,
primaryButtonView
])
stackView.axis = .vertical
stackView.alignment = .fill
stackView.spacing = 12
primaryView.addSubview(stackView)
topSpacer.autoMatch(.height, to: .height, of: bottomSpacer)
stackView.autoPinEdgesToSuperviewMargins()
}
override func shouldShowBackButton() -> Bool {
return true
}
@objc func didPressNext() {
let actionSheet = ActionSheetController(message: warningText)
let continueAction = ActionSheetAction(title: CommonStrings.continueButton,
accessibilityIdentifier: "onboarding.modeSwitch.continue") { _ in
self.onboardingController.toggleModeSwitch(viewController: self)
}
actionSheet.addAction(continueAction)
actionSheet.addAction(OWSActionSheets.cancelAction)
presentActionSheet(actionSheet)
}
}

View File

@ -362,20 +362,7 @@ public class OnboardingPhoneNumberViewController: OnboardingBaseViewController {
let e164PhoneNumber = localNumber.toE164()
onboardingController.update(phoneNumber: OnboardingPhoneNumber(e164: e164PhoneNumber, userInput: phoneNumberText))
if UIDevice.current.isIPad || UIDevice.current.isCompatabilityModeIPad {
OWSActionSheets.showConfirmationAlert(title: NSLocalizedString("REGISTRATION_IPAD_CONFIRM_TITLE",
comment: "alert title when registering an iPad"),
message: NSLocalizedString("REGISTRATION_IPAD_CONFIRM_BODY",
comment: "alert body when registering an iPad"),
proceedTitle: NSLocalizedString("REGISTRATION_IPAD_CONFIRM_BUTTON",
comment: "button text to proceed with registration when on an iPad"),
proceedAction: { (_) in
self.onboardingController.requestVerification(fromViewController: self, isSMS: true)
})
} else {
onboardingController.requestVerification(fromViewController: self, isSMS: true)
}
onboardingController.requestVerification(fromViewController: self, isSMS: true)
}
}

View File

@ -9,6 +9,8 @@ import SafariServices
@objc
public class OnboardingSplashViewController: OnboardingBaseViewController {
let modeSwitchButton = UIButton()
override var primaryLayoutMargins: UIEdgeInsets {
var defaultMargins = super.primaryLayoutMargins
// we want the hero image a bit closer to the top than most
@ -22,6 +24,19 @@ public class OnboardingSplashViewController: OnboardingBaseViewController {
view.addSubview(primaryView)
primaryView.autoPinEdgesToSuperviewEdges()
view.addSubview(modeSwitchButton)
modeSwitchButton.setTemplateImageName(
onboardingController.defaultOnboardingMode == .registering ? "link-24" : "link-broken-24",
tintColor: .ows_gray25
)
modeSwitchButton.autoSetDimensions(to: CGSize(width: 40, height: 40))
modeSwitchButton.autoPinEdge(toSuperviewMargin: .trailing)
modeSwitchButton.autoPinEdge(toSuperviewMargin: .top)
modeSwitchButton.addTarget(self, action: #selector(didTapModeSwitch), for: .touchUpInside)
modeSwitchButton.accessibilityIdentifier = "onboarding.splash.modeSwitch"
modeSwitchButton.isHidden = onboardingController.defaultOnboardingMode == .registering && !FeatureFlags.linkedPhones
view.backgroundColor = Theme.backgroundColor
let heroImage = UIImage(named: "onboarding_splash_hero")
@ -49,8 +64,7 @@ public class OnboardingSplashViewController: OnboardingBaseViewController {
explanationLabel.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(explanationLabelTapped)))
explanationLabel.accessibilityIdentifier = "onboarding.splash." + "explanationLabel"
let continueButton = self.primaryButton(title: NSLocalizedString("BUTTON_CONTINUE",
comment: "Label for 'continue' button."),
let continueButton = self.primaryButton(title: CommonStrings.continueButton,
selector: #selector(continuePressed))
continueButton.accessibilityIdentifier = "onboarding.splash." + "continueButton"
let primaryButtonView = OnboardingBaseViewController.horizontallyWrap(primaryButton: continueButton)
@ -73,6 +87,12 @@ public class OnboardingSplashViewController: OnboardingBaseViewController {
// MARK: - Events
@objc func didTapModeSwitch() {
Logger.info("")
onboardingController.onboardingSplashRequestedModeSwitch(viewController: self)
}
@objc func explanationLabelTapped(sender: UIGestureRecognizer) {
guard sender.state == .recognized else {
return

View File

@ -73,6 +73,12 @@ public class SecondaryLinkingQRCodeViewController: OnboardingBaseViewController
// MARK: - Events
override func shouldShowBackButton() -> Bool {
// Never show the back buton here
// TODO: Linked phones, clean up state to allow backing out
return false
}
@objc
func didTapExplanationLabel(sender: UIGestureRecognizer) {
guard sender.state == .recognized else {

View File

@ -1760,6 +1760,30 @@
/* Title of the 'onboarding Captcha' view. */
"ONBOARDING_CAPTCHA_TITLE" = "Add a touch of humanity to your messages";
/* button indicating that the user will register their ipad */
"ONBOARDING_MODE_SWITCH_BUTTON_PROVISIONING" = "Register iPad";
/* button indicating that the user will link their phone */
"ONBOARDING_MODE_SWITCH_BUTTON_REGISTERING" = "Link iPhone";
/* explanation to the user they're switching from linking to registering flow */
"ONBOARDING_MODE_SWITCH_EXPLANATION_PROVISIONING" = "Tapping “Register iPad” will let you create a brand new Signal account or move an existing account to this iPad. This will disable Signal on any other device currently registered with the same phone number.";
/* explanation to the user they're switching from registering to linking flow */
"ONBOARDING_MODE_SWITCH_EXPLANATION_REGISTERING" = "Tapping “Link iPhone” will let you connect your phone to an existing account. Linking your phone is generally not recommended and will limit some functionality, such as calling.";
/* header text indicating to the user they're switching from linking to registering flow */
"ONBOARDING_MODE_SWITCH_TITLE_PROVISIONING" = "Register this iPad";
/* header text indicating to the user they're switching from registering to linking flow */
"ONBOARDING_MODE_SWITCH_TITLE_REGISTERING" = "Link this iPhone";
/* warning to the user that registering an ipad is not recommended */
"ONBOARDING_MODE_SWITCH_WARNING_PROVISIONING" = "Registering this iPad will disable Signal on any other device currently registered with the same phone number.";
/* warning to the user that linking a phone is not recommended */
"ONBOARDING_MODE_SWITCH_WARNING_REGISTERING" = "Linking your iPhone is not recommended and will limit core functionality.";
/* Label for the 'give access' button in the 'onboarding permissions' view. */
"ONBOARDING_PERMISSIONS_ENABLE_PERMISSIONS_BUTTON" = "Enable Permissions";
@ -2168,15 +2192,6 @@
/* alert body during registration */
"REGISTRATION_ERROR_BLANK_VERIFICATION_CODE" = "We can't activate your account until you verify the code we sent you.";
/* alert body when registering an iPad */
"REGISTRATION_IPAD_CONFIRM_BODY" = "Registering now will disable Signal on any other device currently registered with this phone number.";
/* button text to proceed with registration when on an iPad */
"REGISTRATION_IPAD_CONFIRM_BUTTON" = "Register this iPad";
/* alert title when registering an iPad */
"REGISTRATION_IPAD_CONFIRM_TITLE" = "Already have a Signal account?";
/* No comment provided by engineer. */
"REGISTRATION_NON_VALID_NUMBER" = "This phone number format is not supported, please contact support.";

View File

@ -12,6 +12,9 @@ import Foundation
@objc
static public let backButton = NSLocalizedString("BACK_BUTTON", comment: "return to the previous screen")
@objc
static public let continueButton = NSLocalizedString("BUTTON_CONTINUE", comment: "Label for 'continue' button.")
@objc
static public let dismissButton = NSLocalizedString("DISMISS_BUTTON_TEXT", comment: "Short text to dismiss current modal / actionsheet / screen")

View File

@ -234,4 +234,7 @@ public class FeatureFlags: NSObject {
@objc
public static let tryToCreateGroupsV2 = false
@objc
public static let linkedPhones = build.includes(.internalPreview)
}