• adopt dynamic colors • use system-provided layout margins • use shared button styles • migrate away from PureLayout • added Preview for some view controllers
289 lines
12 KiB
Swift
289 lines
12 KiB
Swift
//
|
|
// Copyright 2023 Signal Messenger, LLC
|
|
// SPDX-License-Identifier: AGPL-3.0-only
|
|
//
|
|
|
|
import MultipeerConnectivity
|
|
import SignalServiceKit
|
|
public import SignalUI
|
|
import UIKit
|
|
|
|
public class RegistrationTransferProgressViewController: OWSViewController {
|
|
|
|
let progressView: TransferProgressView
|
|
|
|
public init(progress: Progress) {
|
|
self.progressView = TransferProgressView(progress: progress)
|
|
|
|
super.init()
|
|
|
|
navigationItem.hidesBackButton = true
|
|
}
|
|
|
|
override public func viewDidLoad() {
|
|
super.viewDidLoad()
|
|
|
|
view.backgroundColor = .Signal.background
|
|
|
|
let titleLabel = UILabel.titleLabelForRegistration(
|
|
text: OWSLocalizedString(
|
|
"DEVICE_TRANSFER_RECEIVING_TITLE",
|
|
comment: "The title on the view that shows receiving progress"
|
|
)
|
|
)
|
|
titleLabel.accessibilityIdentifier = "onboarding.transferProgress.titleLabel"
|
|
titleLabel.setContentHuggingHigh()
|
|
|
|
let explanationLabel = UILabel.explanationLabelForRegistration(
|
|
text: OWSLocalizedString(
|
|
"DEVICE_TRANSFER_RECEIVING_EXPLANATION",
|
|
comment: "The explanation on the view that shows receiving progress"
|
|
)
|
|
)
|
|
explanationLabel.accessibilityIdentifier = "onboarding.transferProgress.bodyLabel"
|
|
explanationLabel.setContentHuggingHigh()
|
|
|
|
let cancelButton = UIButton(
|
|
configuration: .mediumSecondary(title: CommonStrings.cancelButton),
|
|
primaryAction: UIAction { [weak self] _ in
|
|
self?.didTapCancel()
|
|
}
|
|
)
|
|
let cancelButtonContainer = UIView.container()
|
|
cancelButtonContainer.addSubview(cancelButton)
|
|
cancelButton.translatesAutoresizingMaskIntoConstraints = false
|
|
NSLayoutConstraint.activate([
|
|
cancelButton.topAnchor.constraint(equalTo: cancelButtonContainer.topAnchor),
|
|
cancelButton.centerXAnchor.constraint(equalTo: cancelButtonContainer.centerXAnchor),
|
|
cancelButton.bottomAnchor.constraint(equalTo: cancelButtonContainer.bottomAnchor, constant: -16),
|
|
])
|
|
|
|
let topSpacer = UIView.vStretchingSpacer()
|
|
let bottomSpacer = UIView.vStretchingSpacer()
|
|
|
|
let stackView = UIStackView(arrangedSubviews: [
|
|
titleLabel,
|
|
explanationLabel,
|
|
topSpacer,
|
|
progressView,
|
|
bottomSpacer,
|
|
cancelButtonContainer
|
|
])
|
|
stackView.axis = .vertical
|
|
stackView.alignment = .fill
|
|
stackView.spacing = 12
|
|
stackView.preservesSuperviewLayoutMargins = true
|
|
stackView.isLayoutMarginsRelativeArrangement = true
|
|
view.addSubview(stackView)
|
|
stackView.translatesAutoresizingMaskIntoConstraints = false
|
|
topSpacer.translatesAutoresizingMaskIntoConstraints = false
|
|
bottomSpacer.translatesAutoresizingMaskIntoConstraints = false
|
|
NSLayoutConstraint.activate([
|
|
stackView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor),
|
|
stackView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
|
|
stackView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor),
|
|
stackView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
|
|
|
|
topSpacer.heightAnchor.constraint(equalTo: bottomSpacer.heightAnchor),
|
|
])
|
|
}
|
|
|
|
override public func viewWillAppear(_ animated: Bool) {
|
|
super.viewWillAppear(animated)
|
|
|
|
progressView.startUpdatingProgress()
|
|
|
|
AppEnvironment.shared.deviceTransferServiceRef.addObserver(self)
|
|
}
|
|
|
|
public override func viewWillDisappear(_ animated: Bool) {
|
|
super.viewWillDisappear(animated)
|
|
|
|
progressView.stopUpdatingProgress()
|
|
|
|
AppEnvironment.shared.deviceTransferServiceRef.removeObserver(self)
|
|
AppEnvironment.shared.deviceTransferServiceRef.cancelTransferFromOldDevice()
|
|
}
|
|
|
|
// MARK: - Events
|
|
|
|
func didTapCancel() {
|
|
Logger.info("")
|
|
|
|
let actionSheet = ActionSheetController(
|
|
title: OWSLocalizedString("DEVICE_TRANSFER_CANCEL_CONFIRMATION_TITLE",
|
|
comment: "The title of the dialog asking the user if they want to cancel a device transfer"),
|
|
message: OWSLocalizedString("DEVICE_TRANSFER_CANCEL_CONFIRMATION_MESSAGE",
|
|
comment: "The message of the dialog asking the user if they want to cancel a device transfer")
|
|
)
|
|
actionSheet.addAction(OWSActionSheets.cancelAction)
|
|
|
|
let okAction = ActionSheetAction(
|
|
title: OWSLocalizedString("DEVICE_TRANSFER_CANCEL_CONFIRMATION_ACTION",
|
|
comment: "The stop action of the dialog asking the user if they want to cancel a device transfer"),
|
|
style: .destructive
|
|
) { [weak self] _ in
|
|
// viewWillDissapear will cancel the transfer
|
|
self?.navigationController?.popViewController(animated: true)
|
|
}
|
|
actionSheet.addAction(okAction)
|
|
|
|
present(actionSheet, animated: true)
|
|
}
|
|
}
|
|
|
|
extension RegistrationTransferProgressViewController: DeviceTransferServiceObserver {
|
|
func deviceTransferServiceDiscoveredNewDevice(peerId: MCPeerID, discoveryInfo: [String: String]?) {}
|
|
|
|
func deviceTransferServiceDidStartTransfer(progress: Progress) {}
|
|
|
|
func deviceTransferServiceDidEndTransfer(error: DeviceTransferService.Error?) {
|
|
guard let error = error else { return }
|
|
|
|
switch error {
|
|
case .assertion:
|
|
progressView.renderError(
|
|
text: OWSLocalizedString("DEVICE_TRANSFER_ERROR_GENERIC",
|
|
comment: "An error indicating that something went wrong with the transfer and it could not complete")
|
|
)
|
|
case .backgroundedDevice:
|
|
progressView.renderError(
|
|
text: OWSLocalizedString(
|
|
"DEVICE_TRANSFER_ERROR_BACKGROUNDED",
|
|
comment: "An error indicating that the other device closed signal mid-transfer and it could not complete"
|
|
)
|
|
)
|
|
case .cancel:
|
|
// User initiated, nothing to do
|
|
break
|
|
case .certificateMismatch:
|
|
owsFailDebug("This should never happen on the new device")
|
|
case .notEnoughSpace:
|
|
progressView.renderError(
|
|
text: OWSLocalizedString("DEVICE_TRANSFER_ERROR_NOT_ENOUGH_SPACE",
|
|
comment: "An error indicating that the user does not have enough free space on their device to complete the transfer")
|
|
)
|
|
case .unsupportedVersion:
|
|
progressView.renderError(
|
|
text: OWSLocalizedString("DEVICE_TRANSFER_ERROR_UNSUPPORTED_VERSION",
|
|
comment: "An error indicating the user must update their device before trying to transfer.")
|
|
)
|
|
case .modeMismatch:
|
|
owsFailDebug("This should never happen on the new device")
|
|
}
|
|
}
|
|
|
|
func deviceTransferServiceDidRequestAppRelaunch() {
|
|
self.present(TransferRelaunchSheet(), animated: true)
|
|
}
|
|
}
|
|
|
|
private class TransferRelaunchSheet: InteractiveSheetViewController {
|
|
|
|
public override var canBeDismissed: Bool { false }
|
|
|
|
public override var sheetBackgroundColor: UIColor { Theme.tableView2PresentedBackgroundColor }
|
|
|
|
override public func viewDidLoad() {
|
|
super.viewDidLoad()
|
|
|
|
minimizedHeight = 460
|
|
allowsExpansion = false
|
|
|
|
let imageView = UIImageView(image: UIImage(named: "transfer_complete"))
|
|
imageView.contentMode = .scaleAspectFit
|
|
imageView.translatesAutoresizingMaskIntoConstraints = false
|
|
imageView.addConstraints([
|
|
imageView.widthAnchor.constraint(equalToConstant: 128),
|
|
imageView.heightAnchor.constraint(equalToConstant: 64),
|
|
])
|
|
let imageViewContainer = UIView.container()
|
|
imageViewContainer.addSubview(imageView)
|
|
imageView.translatesAutoresizingMaskIntoConstraints = false
|
|
imageViewContainer.addConstraints([
|
|
imageView.topAnchor.constraint(equalTo: imageViewContainer.topAnchor),
|
|
imageView.centerXAnchor.constraint(equalTo: imageViewContainer.centerXAnchor),
|
|
imageView.leadingAnchor.constraint(greaterThanOrEqualTo: imageViewContainer.leadingAnchor),
|
|
imageView.bottomAnchor.constraint(equalTo: imageViewContainer.bottomAnchor),
|
|
])
|
|
|
|
let titleLabel = UILabel.titleLabelForRegistration(text: OWSLocalizedString(
|
|
"TRANSFER_COMPLETE_SHEET_TITLE",
|
|
comment: "Title for bottom sheet shown when device transfer completes on the receiving device."
|
|
))
|
|
|
|
let subtitleLabel = UILabel.explanationLabelForRegistration(text: OWSLocalizedString(
|
|
"TRANSFER_COMPLETE_SHEET_SUBTITLE",
|
|
comment: "Subtitle for bottom sheet shown when device transfer completes on the receiving device."
|
|
))
|
|
|
|
let exitButton = UIButton(
|
|
configuration: .largePrimary(title: OWSLocalizedString(
|
|
"TRANSFER_COMPLETE_SHEET_BUTTON",
|
|
comment: "Button for bottom sheet shown when device transfer completes on the receiving device. Tapping will terminate the Signal app and trigger a notification to relaunch."
|
|
)),
|
|
primaryAction: UIAction { [weak self] _ in
|
|
self?.didTapExitButton()
|
|
}
|
|
)
|
|
let buttonContainer = UIView.container()
|
|
buttonContainer.addSubview(exitButton)
|
|
exitButton.translatesAutoresizingMaskIntoConstraints = false
|
|
buttonContainer.addConstraints([
|
|
exitButton.topAnchor.constraint(equalTo: buttonContainer.topAnchor),
|
|
exitButton.centerXAnchor.constraint(equalTo: buttonContainer.centerXAnchor),
|
|
exitButton.leadingAnchor.constraint(equalTo: buttonContainer.leadingAnchor, constant: 22),
|
|
exitButton.bottomAnchor.constraint(equalTo: buttonContainer.bottomAnchor, constant: -16),
|
|
])
|
|
|
|
let stackView = UIStackView(arrangedSubviews: [
|
|
imageViewContainer,
|
|
titleLabel,
|
|
subtitleLabel,
|
|
.vStretchingSpacer(minHeight: 32),
|
|
buttonContainer,
|
|
])
|
|
stackView.axis = .vertical
|
|
stackView.alignment = .fill
|
|
stackView.directionalLayoutMargins = NSDirectionalEdgeInsets(top: 24, leading: 8, bottom: 0, trailing: 8)
|
|
stackView.spacing = 22
|
|
stackView.isLayoutMarginsRelativeArrangement = true
|
|
contentView.addSubview(stackView)
|
|
stackView.translatesAutoresizingMaskIntoConstraints = false
|
|
NSLayoutConstraint.activate([
|
|
stackView.topAnchor.constraint(equalTo: contentView.topAnchor),
|
|
stackView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor),
|
|
stackView.trailingAnchor.constraint(equalTo: contentView.trailingAnchor),
|
|
stackView.bottomAnchor.constraint(equalTo: contentView.safeAreaLayoutGuide.bottomAnchor),
|
|
])
|
|
}
|
|
|
|
private func didTapExitButton() {
|
|
Logger.info("")
|
|
SSKEnvironment.shared.notificationPresenterRef.notifyUserToRelaunchAfterTransfer {
|
|
Logger.info("Deliberately terminating app post-transfer.")
|
|
exit(0)
|
|
}
|
|
}
|
|
}
|
|
|
|
// MARK: -
|
|
|
|
#if DEBUG
|
|
|
|
@available(iOS 17, *)
|
|
#Preview("Transfer Progress") {
|
|
return UINavigationController(
|
|
rootViewController: RegistrationTransferProgressViewController(
|
|
progress: .discreteProgress(totalUnitCount: 1024)
|
|
)
|
|
)
|
|
}
|
|
|
|
@available(iOS 17, *)
|
|
#Preview("Relaunch Sheet") {
|
|
return TransferRelaunchSheet( )
|
|
}
|
|
|
|
#endif
|