diff --git a/Session/Components/ConversationCell.swift b/Session/Components/ConversationCell.swift index c86a5d098..e4230a88d 100644 --- a/Session/Components/ConversationCell.swift +++ b/Session/Components/ConversationCell.swift @@ -166,7 +166,7 @@ final class ConversationCell : UITableViewCell { let image: UIImage let status = MessageRecipientStatusUtils.recipientStatus(outgoingMessage: lastMessage) switch status { - case .calculatingPoW, .uploading, .sending: image = #imageLiteral(resourceName: "CircleDotDotDot").asTintedImage(color: Colors.text)! + case .uploading, .sending: image = #imageLiteral(resourceName: "CircleDotDotDot").asTintedImage(color: Colors.text)! case .sent, .skipped, .delivered: image = #imageLiteral(resourceName: "CircleCheck").asTintedImage(color: Colors.text)! case .read: statusIndicatorView.backgroundColor = isLightMode ? .black : .white diff --git a/Session/Components/MentionCandidateSelectionView.swift b/Session/Components/MentionCandidateSelectionView.swift index a2eef63e9..f62d50d8b 100644 --- a/Session/Components/MentionCandidateSelectionView.swift +++ b/Session/Components/MentionCandidateSelectionView.swift @@ -1,6 +1,4 @@ -// MARK: - User Selection View - @objc(LKMentionCandidateSelectionView) final class MentionCandidateSelectionView : UIView, UITableViewDataSource, UITableViewDelegate { @objc var mentionCandidates: [Mention] = [] { didSet { tableView.reloadData() } } @@ -173,7 +171,8 @@ private extension MentionCandidateSelectionView { } } -// MARK: Delegate +// MARK: - Delegate + @objc(LKMentionCandidateSelectionViewDelegate) protocol MentionCandidateSelectionViewDelegate { diff --git a/Session/MessageHandler.swift b/Session/MessageReceiverDelegate.swift similarity index 100% rename from Session/MessageHandler.swift rename to Session/MessageReceiverDelegate.swift diff --git a/Session/Signal/AddToGroupViewController.h b/Session/Signal/AddToGroupViewController.h deleted file mode 100644 index dae373b41..000000000 --- a/Session/Signal/AddToGroupViewController.h +++ /dev/null @@ -1,27 +0,0 @@ -// -// Copyright (c) 2018 Open Whisper Systems. All rights reserved. -// - -#import "SelectRecipientViewController.h" - -NS_ASSUME_NONNULL_BEGIN - -@protocol AddToGroupViewControllerDelegate - -- (void)recipientIdWasAdded:(NSString *)recipientId; - -- (BOOL)isRecipientGroupMember:(NSString *)recipientId; - -@end - -#pragma mark - - -@interface AddToGroupViewController : SelectRecipientViewController - -@property (nonatomic, weak) id addToGroupDelegate; - -@property (nonatomic) BOOL hideContacts; - -@end - -NS_ASSUME_NONNULL_END diff --git a/Session/Signal/AddToGroupViewController.m b/Session/Signal/AddToGroupViewController.m deleted file mode 100644 index c021ce8c3..000000000 --- a/Session/Signal/AddToGroupViewController.m +++ /dev/null @@ -1,138 +0,0 @@ -// -// Copyright (c) 2018 Open Whisper Systems. All rights reserved. -// - -#import "AddToGroupViewController.h" -#import "BlockListUIUtils.h" - -#import "Session-Swift.h" - -#import - -NS_ASSUME_NONNULL_BEGIN - -@interface AddToGroupViewController () - -@end - -#pragma mark - - -@implementation AddToGroupViewController - -- (void)loadView -{ - self.delegate = self; - - [super loadView]; - - self.title = NSLocalizedString(@"ADD_GROUP_MEMBER_VIEW_TITLE", @"Title for the 'add group member' view."); -} - -- (NSString *)phoneNumberSectionTitle -{ - return NSLocalizedString(@"ADD_GROUP_MEMBER_VIEW_PHONE_NUMBER_TITLE", - @"Title for the 'add by phone number' section of the 'add group member' view."); -} - -- (NSString *)phoneNumberButtonText -{ - return NSLocalizedString(@"ADD_GROUP_MEMBER_VIEW_BUTTON", - @"A label for the 'add by phone number' button in the 'add group member' view"); -} - -- (NSString *)contactsSectionTitle -{ - return NSLocalizedString( - @"ADD_GROUP_MEMBER_VIEW_CONTACT_TITLE", @"Title for the 'add contact' section of the 'add group member' view."); -} - -- (void)phoneNumberWasSelected:(NSString *)phoneNumber -{ - OWSAssertDebug(phoneNumber.length > 0); - - __weak AddToGroupViewController *weakSelf = self; - - if ([SSKEnvironment.shared.blockingManager isRecipientIdBlocked:phoneNumber]) { - [BlockListUIUtils showUnblockPhoneNumberActionSheet:phoneNumber - fromViewController:self - blockingManager:SSKEnvironment.shared.blockingManager - completionBlock:^(BOOL isBlocked) { - if (!isBlocked) { - [weakSelf addToGroup:phoneNumber]; - } - }]; - return; - } - - [self addToGroup:phoneNumber]; -} - -- (BOOL)canSignalAccountBeSelected:(SignalAccount *)signalAccount -{ - OWSAssertDebug(signalAccount); - - return ![self.addToGroupDelegate isRecipientGroupMember:signalAccount.recipientId]; -} - -- (void)signalAccountWasSelected:(SignalAccount *)signalAccount -{ - OWSAssertDebug(signalAccount); - - __weak AddToGroupViewController *weakSelf = self; - if ([self.addToGroupDelegate isRecipientGroupMember:signalAccount.recipientId]) { - OWSFailDebug(@"Cannot add user to group member if already a member."); - return; - } - - if ([SSKEnvironment.shared.blockingManager isRecipientIdBlocked:signalAccount.recipientId]) { - [BlockListUIUtils showUnblockSignalAccountActionSheet:signalAccount - fromViewController:self - blockingManager:SSKEnvironment.shared.blockingManager - completionBlock:^(BOOL isBlocked) { - if (!isBlocked) { - [weakSelf addToGroup:signalAccount.recipientId]; - } - }]; - return; - } - - [self addToGroup:signalAccount.recipientId]; -} - -- (void)addToGroup:(NSString *)recipientId -{ - OWSAssertDebug(recipientId.length > 0); - - [self.addToGroupDelegate recipientIdWasAdded:recipientId]; - [self.navigationController popViewControllerAnimated:YES]; -} - -- (BOOL)shouldHideLocalNumber -{ - return YES; -} - -- (BOOL)shouldHideContacts -{ - return self.hideContacts; -} - -- (BOOL)shouldValidatePhoneNumbers -{ - return YES; -} - -- (nullable NSString *)accessoryMessageForSignalAccount:(SignalAccount *)signalAccount -{ - OWSAssertDebug(signalAccount); - - if ([self.addToGroupDelegate isRecipientGroupMember:signalAccount.recipientId]) { - return NSLocalizedString(@"NEW_GROUP_MEMBER_LABEL", @"An indicator that a user is a member of the new group."); - } - - return nil; -} - -@end - -NS_ASSUME_NONNULL_END diff --git a/Session/Signal/AppEnvironment.swift b/Session/Signal/AppEnvironment.swift index 2df0f0dc8..cfacd98f3 100644 --- a/Session/Signal/AppEnvironment.swift +++ b/Session/Signal/AppEnvironment.swift @@ -86,10 +86,7 @@ import SignalUtilitiesKit @objc public func setup() { -// callService.createCallUIAdapter() - // Hang certain singletons on SSKEnvironment too. SSKEnvironment.shared.notificationsManager = notificationPresenter -// SSKEnvironment.shared.callMessageHandler = callMessageHandler } } diff --git a/Session/Signal/AvatarViewHelper.m b/Session/Signal/AvatarViewHelper.m index b5d4b7972..bf4664007 100644 --- a/Session/Signal/AvatarViewHelper.m +++ b/Session/Signal/AvatarViewHelper.m @@ -36,14 +36,6 @@ NS_ASSUME_NONNULL_BEGIN preferredStyle:UIAlertControllerStyleActionSheet]; [actionSheet addAction:[OWSAlerts cancelAction]]; -// UIAlertAction *takePictureAction = [UIAlertAction -// actionWithTitle:NSLocalizedString(@"MEDIA_FROM_CAMERA_BUTTON", @"media picker option to take photo or video") -// style:UIAlertActionStyleDefault -// handler:^(UIAlertAction *_Nonnull action) { -// [self takePicture]; -// }]; -// [actionSheet addAction:takePictureAction]; - UIAlertAction *choosePictureAction = [UIAlertAction actionWithTitle:NSLocalizedString(@"MEDIA_FROM_LIBRARY_BUTTON", @"media picker option to choose from library") style:UIAlertActionStyleDefault diff --git a/Session/Signal/CallVideoHintView.swift b/Session/Signal/CallVideoHintView.swift deleted file mode 100644 index ba49030d0..000000000 --- a/Session/Signal/CallVideoHintView.swift +++ /dev/null @@ -1,83 +0,0 @@ -// -// Copyright (c) 2019 Open Whisper Systems. All rights reserved. -// - -import Foundation - -protocol CallVideoHintViewDelegate: AnyObject { - func didTapCallVideoHintView(_ videoHintView: CallVideoHintView) -} - -class CallVideoHintView: UIView { - let label = UILabel() - var tapGesture: UITapGestureRecognizer! - weak var delegate: CallVideoHintViewDelegate? - - let kTailHMargin: CGFloat = 12 - let kTailWidth: CGFloat = 16 - let kTailHeight: CGFloat = 8 - - init() { - super.init(frame: .zero) - - tapGesture = UITapGestureRecognizer(target: self, action: #selector(didTap(tapGesture:))) - addGestureRecognizer(tapGesture) - - let layerView = OWSLayerView(frame: .zero) { _ in } - let shapeLayer = CAShapeLayer() - shapeLayer.fillColor = UIColor.ows_signalBlue.cgColor - layerView.layer.addSublayer(shapeLayer) - addSubview(layerView) - layerView.autoPinEdgesToSuperviewEdges() - - let container = UIView() - addSubview(container) - container.autoSetDimension(.width, toSize: ScaleFromIPhone5(250), relation: .lessThanOrEqual) - container.layoutMargins = UIEdgeInsets(top: 7, leading: 12, bottom: 7, trailing: 12) - container.autoPinEdgesToSuperviewEdges(with: UIEdgeInsets(top: 0, leading: 0, bottom: kTailHeight, trailing: 0)) - - container.addSubview(label) - label.autoPinEdgesToSuperviewMargins() - label.setCompressionResistanceHigh() - label.setContentHuggingHigh() - label.font = UIFont.ows_dynamicTypeBody - label.textColor = .ows_white - label.numberOfLines = 0 - label.text = NSLocalizedString("CALL_VIEW_ENABLE_VIDEO_HINT", comment: "tooltip label when remote party has enabled their video") - - layerView.layoutCallback = { view in - let bezierPath = UIBezierPath() - - // Bubble - let bubbleBounds = container.bounds - bezierPath.append(UIBezierPath(roundedRect: bubbleBounds, cornerRadius: 8)) - - // Tail - var tailBottom = CGPoint(x: self.kTailHMargin + self.kTailWidth * 0.5, y: view.height()) - var tailLeft = CGPoint(x: self.kTailHMargin, y: view.height() - self.kTailHeight) - var tailRight = CGPoint(x: self.kTailHMargin + self.kTailWidth, y: view.height() - self.kTailHeight) - if (!CurrentAppContext().isRTL) { - tailBottom.x = view.width() - tailBottom.x - tailLeft.x = view.width() - tailLeft.x - tailRight.x = view.width() - tailRight.x - } - bezierPath.move(to: tailBottom) - bezierPath.addLine(to: tailLeft) - bezierPath.addLine(to: tailRight) - bezierPath.addLine(to: tailBottom) - shapeLayer.path = bezierPath.cgPath - shapeLayer.frame = view.bounds - } - } - - required init?(coder aDecoder: NSCoder) { - fatalError("init(coder:) has not been implemented") - } - - // MARK: - - - @objc - func didTap(tapGesture: UITapGestureRecognizer) { - self.delegate?.didTapCallVideoHintView(self) - } -} diff --git a/Session/Signal/CallViewController.swift b/Session/Signal/CallViewController.swift deleted file mode 100644 index 02e27363d..000000000 --- a/Session/Signal/CallViewController.swift +++ /dev/null @@ -1,1227 +0,0 @@ -//// -//// Copyright (c) 2019 Open Whisper Systems. All rights reserved. -//// -// -//import Foundation -//import WebRTC -//import PromiseKit -//import SignalUtilitiesKit -//import SignalUtilitiesKit -// -//// TODO: Add category so that button handlers can be defined where button is created. -//// TODO: Ensure buttons enabled & disabled as necessary. -//class CallViewController: OWSViewController, CallObserver, CallServiceObserver, CallAudioServiceDelegate { -// -// // Dependencies -// -// var callUIAdapter: CallUIAdapter { -// return AppEnvironment.shared.callService.callUIAdapter -// } -// -// var proximityMonitoringManager: OWSProximityMonitoringManager { -// return Environment.shared.proximityMonitoringManager -// } -// -// // Feature Flag -// @objc public static let kShowCallViewOnSeparateWindow = true -// -// let contactsManager: OWSContactsManager -// -// // MARK: - Properties -// -// let thread: TSContactThread -// let call: SignalCall -// var hasDismissed = false -// -// // MARK: - Views -// -// var hasConstraints = false -// var blurView: UIVisualEffectView! -// var dateFormatter: DateFormatter? -// -// // MARK: - Contact Views -// -// var contactNameLabel: MarqueeLabel! -// var contactAvatarView: AvatarImageView! -// var contactAvatarContainerView: UIView! -// var callStatusLabel: UILabel! -// var callDurationTimer: Timer? -// var leaveCallViewButton: UIButton! -// -// // MARK: - Ongoing Call Controls -// -// var ongoingCallControls: UIStackView! -// -// var ongoingAudioCallControls: UIStackView! -// var ongoingVideoCallControls: UIStackView! -// -// var hangUpButton: UIButton! -// var audioSourceButton: UIButton! -// var audioModeMuteButton: UIButton! -// var audioModeVideoButton: UIButton! -// var videoModeMuteButton: UIButton! -// var videoModeVideoButton: UIButton! -// var videoModeFlipCameraButton: UIButton! -// -// // MARK: - Incoming Call Controls -// -// var incomingCallControls: UIStackView! -// -// var acceptIncomingButton: UIButton! -// var declineIncomingButton: UIButton! -// -// // MARK: - Video Views -// -// var remoteVideoView: RemoteVideoView! -// var localVideoView: RTCCameraPreviewView! -// var hasShownLocalVideo = false -// weak var localCaptureSession: AVCaptureSession? -// weak var remoteVideoTrack: RTCVideoTrack? -// -// override public var canBecomeFirstResponder: Bool { -// return true -// } -// -// var shouldRemoteVideoControlsBeHidden = false { -// didSet { -// updateCallUI(callState: call.state) -// } -// } -// -// // MARK: - Settings Nag Views -// -// var isShowingSettingsNag = false { -// didSet { -// if oldValue != isShowingSettingsNag { -// updateCallUI(callState: call.state) -// } -// } -// } -// var settingsNagView: UIView! -// var settingsNagDescriptionLabel: UILabel! -// -// // MARK: - Audio Source -// -// var hasAlternateAudioSources: Bool { -// Logger.info("available audio sources: \(allAudioSources)") -// // internal mic and speakerphone will be the first two, any more than one indicates e.g. an attached bluetooth device. -// -// // TODO is this sufficient? Are their devices w/ bluetooth but no external speaker? e.g. ipod? -// return allAudioSources.count > 2 -// } -// -// var allAudioSources: Set = Set() -// -// var appropriateAudioSources: Set { -// if call.hasLocalVideo { -// let appropriateForVideo = allAudioSources.filter { audioSource in -// if audioSource.isBuiltInSpeaker { -// return true -// } else { -// guard let portDescription = audioSource.portDescription else { -// owsFailDebug("Only built in speaker should be lacking a port description.") -// return false -// } -// -// // Don't use receiver when video is enabled. Only bluetooth or speaker -// return portDescription.portType != AVAudioSession.Port.builtInMic -// } -// } -// return Set(appropriateForVideo) -// } else { -// return allAudioSources -// } -// } -// -// // MARK: - Initializers -// -// @available(*, unavailable, message: "use init(call:) constructor instead.") -// required init?(coder aDecoder: NSCoder) { -// notImplemented() -// } -// -// required init(call: SignalCall) { -// contactsManager = Environment.shared.contactsManager -// self.call = call -// self.thread = TSContactThread.getOrCreateThread(contactId: call.remotePhoneNumber) -// super.init(nibName: nil, bundle: nil) -// -// allAudioSources = Set(callUIAdapter.audioService.availableInputs) -// -// self.shouldUseTheme = false -// } -// -// deinit { -// Logger.info("") -// NotificationCenter.default.removeObserver(self) -// self.proximityMonitoringManager.remove(lifetime: self) -// } -// -// @objc func didBecomeActive() { -// if (self.isViewLoaded) { -// shouldRemoteVideoControlsBeHidden = false -// } -// } -// -// // MARK: - View Lifecycle -// -// override func viewDidDisappear(_ animated: Bool) { -// super.viewDidDisappear(animated) -// -// callDurationTimer?.invalidate() -// callDurationTimer = nil -// } -// -// override func viewWillAppear(_ animated: Bool) { -// super.viewWillAppear(animated) -// -// ensureProximityMonitoring() -// -// updateCallUI(callState: call.state) -// -// self.becomeFirstResponder() -// } -// -// override func viewDidAppear(_ animated: Bool) { -// super.viewDidAppear(animated) -// -// self.becomeFirstResponder() -// } -// -// override func loadView() { -// self.view = UIView() -// self.view.backgroundColor = UIColor.black -// self.view.layoutMargins = UIEdgeInsets(top: 16, left: 20, bottom: 16, right: 20) -// -// createViews() -// createViewConstraints() -// } -// -// override func viewDidLoad() { -// super.viewDidLoad() -// -// contactNameLabel.text = contactsManager.stringForConversationTitle(withPhoneIdentifier: thread.contactIdentifier()) -// updateAvatarImage() -// NotificationCenter.default.addObserver(forName: .OWSContactsManagerSignalAccountsDidChange, object: nil, queue: nil) { [weak self] _ in -// guard let strongSelf = self else { return } -// Logger.info("updating avatar image") -// strongSelf.updateAvatarImage() -// } -// -// // Subscribe for future call updates -// call.addObserverAndSyncState(observer: self) -// -// AppEnvironment.shared.callService.addObserverAndSyncState(observer: self) -// -// assert(callUIAdapter.audioService.delegate == nil) -// callUIAdapter.audioService.delegate = self -// -// NotificationCenter.default.addObserver(self, -// selector: #selector(didBecomeActive), -// name: NSNotification.Name.OWSApplicationDidBecomeActive, -// object: nil) -// } -// -// override var supportedInterfaceOrientations: UIInterfaceOrientationMask { -// return .portrait -// } -// -// override var preferredStatusBarStyle: UIStatusBarStyle { -// return .lightContent -// } -// -// // MARK: - Create Views -// -// func createViews() { -// self.view.isUserInteractionEnabled = true -// self.view.addGestureRecognizer(UITapGestureRecognizer(target: self, -// action: #selector(didTouchRootView))) -// -// videoHintView.delegate = self -// -// // Dark blurred background. -// let blurEffect = UIBlurEffect(style: .dark) -// blurView = UIVisualEffectView(effect: blurEffect) -// blurView.isUserInteractionEnabled = false -// self.view.addSubview(blurView) -// -// // Create the video views first, as they are under the other views. -// createVideoViews() -// createContactViews() -// createOngoingCallControls() -// createIncomingCallControls() -// createSettingsNagViews() -// } -// -// @objc func didTouchRootView(sender: UIGestureRecognizer) { -// if !remoteVideoView.isHidden { -// shouldRemoteVideoControlsBeHidden = !shouldRemoteVideoControlsBeHidden -// } -// } -// -// func createVideoViews() { -// remoteVideoView = RemoteVideoView() -// remoteVideoView.isUserInteractionEnabled = false -// localVideoView = RTCCameraPreviewView() -// -// remoteVideoView.isHidden = true -// localVideoView.isHidden = true -// self.view.addSubview(remoteVideoView) -// self.view.addSubview(localVideoView) -// } -// -// func createContactViews() { -// -// leaveCallViewButton = UIButton() -// let backButtonImage = CurrentAppContext().isRTL ? #imageLiteral(resourceName: "NavBarBackRTL") : #imageLiteral(resourceName: "NavBarBack") -// leaveCallViewButton.setImage(backButtonImage, for: .normal) -// leaveCallViewButton.autoSetDimensions(to: CGSize(width: 40, height: 40)) -// leaveCallViewButton.addTarget(self, action: #selector(didTapLeaveCall(sender:)), for: .touchUpInside) -// self.view.addSubview(leaveCallViewButton) -// -// contactNameLabel = MarqueeLabel() -// -// // marquee config -// contactNameLabel.type = .continuous -// // This feels pretty slow when you're initially waiting for it, but when you're overlaying video calls, anything faster is distracting. -// contactNameLabel.speed = .duration(30.0) -// contactNameLabel.animationCurve = .linear -// contactNameLabel.fadeLength = 10.0 -// contactNameLabel.animationDelay = 5 -// // Add trailing space after the name scrolls before it wraps around and scrolls back in. -// contactNameLabel.trailingBuffer = ScaleFromIPhone5(80.0) -// -// // label config -// contactNameLabel.font = UIFont.ows_dynamicTypeTitle1 -// contactNameLabel.textAlignment = .center -// contactNameLabel.textColor = UIColor.white -// contactNameLabel.layer.shadowOffset = CGSize.zero -// contactNameLabel.layer.shadowOpacity = 0.35 -// contactNameLabel.layer.shadowRadius = 4 -// -// self.view.addSubview(contactNameLabel) -// -// callStatusLabel = UILabel() -// callStatusLabel.font = UIFont.ows_dynamicTypeBody -// callStatusLabel.textAlignment = .center -// callStatusLabel.textColor = UIColor.white -// callStatusLabel.layer.shadowOffset = CGSize.zero -// callStatusLabel.layer.shadowOpacity = 0.35 -// callStatusLabel.layer.shadowRadius = 4 -// -// self.view.addSubview(callStatusLabel) -// -// contactAvatarContainerView = UIView.container() -// self.view.addSubview(contactAvatarContainerView) -// contactAvatarView = AvatarImageView() -// contactAvatarContainerView.addSubview(contactAvatarView) -// } -// -// func createSettingsNagViews() { -// settingsNagView = UIView() -// settingsNagView.isHidden = true -// self.view.addSubview(settingsNagView) -// -// let viewStack = UIView() -// settingsNagView.addSubview(viewStack) -// viewStack.autoPinWidthToSuperview() -// viewStack.autoVCenterInSuperview() -// -// settingsNagDescriptionLabel = UILabel() -// settingsNagDescriptionLabel.text = NSLocalizedString("CALL_VIEW_SETTINGS_NAG_DESCRIPTION_ALL", -// comment: "Reminder to the user of the benefits of enabling CallKit and disabling CallKit privacy.") -// settingsNagDescriptionLabel.font = UIFont.ows_regularFont(withSize: ScaleFromIPhone5To7Plus(16, 18)) -// settingsNagDescriptionLabel.textColor = UIColor.white -// settingsNagDescriptionLabel.numberOfLines = 0 -// settingsNagDescriptionLabel.lineBreakMode = .byWordWrapping -// viewStack.addSubview(settingsNagDescriptionLabel) -// settingsNagDescriptionLabel.autoPinWidthToSuperview() -// settingsNagDescriptionLabel.autoPinEdge(toSuperviewEdge: .top) -// -// let buttonHeight = ScaleFromIPhone5To7Plus(35, 45) -// let descriptionVSpacingHeight = ScaleFromIPhone5To7Plus(30, 60) -// -// let callSettingsButton = OWSFlatButton.button(title: NSLocalizedString("CALL_VIEW_SETTINGS_NAG_SHOW_CALL_SETTINGS", -// comment: "Label for button that shows the privacy settings."), -// font: OWSFlatButton.fontForHeight(buttonHeight), -// titleColor: UIColor.white, -// backgroundColor: UIColor.ows_signalBrandBlue, -// target: self, -// selector: #selector(didPressShowCallSettings)) -// viewStack.addSubview(callSettingsButton) -// callSettingsButton.autoSetDimension(.height, toSize: buttonHeight) -// callSettingsButton.autoPinWidthToSuperview() -// callSettingsButton.autoPinEdge(.top, to: .bottom, of: settingsNagDescriptionLabel, withOffset: descriptionVSpacingHeight) -// -// let notNowButton = OWSFlatButton.button(title: NSLocalizedString("CALL_VIEW_SETTINGS_NAG_NOT_NOW_BUTTON", -// comment: "Label for button that dismiss the call view's settings nag."), -// font: OWSFlatButton.fontForHeight(buttonHeight), -// titleColor: UIColor.white, -// backgroundColor: UIColor.ows_signalBrandBlue, -// target: self, -// selector: #selector(didPressDismissNag)) -// viewStack.addSubview(notNowButton) -// notNowButton.autoSetDimension(.height, toSize: buttonHeight) -// notNowButton.autoPinWidthToSuperview() -// notNowButton.autoPinEdge(toSuperviewEdge: .bottom) -// notNowButton.autoPinEdge(.top, to: .bottom, of: callSettingsButton, withOffset: 12) -// } -// -// func buttonSize() -> CGFloat { -// return ScaleFromIPhone5To7Plus(84, 108) -// } -// -// func buttonInset() -> CGFloat { -// return ScaleFromIPhone5To7Plus(7, 9) -// } -// -// func createOngoingCallControls() { -// -// audioSourceButton = createButton(image: #imageLiteral(resourceName: "audio-call-speaker-inactive"), -// action: #selector(didPressAudioSource)) -// audioSourceButton.accessibilityLabel = NSLocalizedString("CALL_VIEW_AUDIO_SOURCE_LABEL", -// comment: "Accessibility label for selection the audio source") -// -// hangUpButton = createButton(image: #imageLiteral(resourceName: "hangup-active-wide"), -// action: #selector(didPressHangup)) -// hangUpButton.accessibilityLabel = NSLocalizedString("CALL_VIEW_HANGUP_LABEL", -// comment: "Accessibility label for hang up call") -// -// audioModeMuteButton = createButton(image: #imageLiteral(resourceName: "audio-call-mute-inactive"), -// action: #selector(didPressMute)) -// audioModeMuteButton.setImage(#imageLiteral(resourceName: "audio-call-mute-active"), for: .selected) -// -// audioModeMuteButton.accessibilityLabel = NSLocalizedString("CALL_VIEW_MUTE_LABEL", -// comment: "Accessibility label for muting the microphone") -// -// audioModeVideoButton = createButton(image: #imageLiteral(resourceName: "audio-call-video-inactive"), -// action: #selector(didPressVideo)) -// audioModeVideoButton.setImage(#imageLiteral(resourceName: "audio-call-video-active"), for: .selected) -// audioModeVideoButton.accessibilityLabel = NSLocalizedString("CALL_VIEW_SWITCH_TO_VIDEO_LABEL", comment: "Accessibility label to switch to video call") -// -// videoModeMuteButton = createButton(image: #imageLiteral(resourceName: "video-mute-unselected"), -// action: #selector(didPressMute)) -// videoModeMuteButton.setImage(#imageLiteral(resourceName: "video-mute-selected"), for: .selected) -// videoModeMuteButton.accessibilityLabel = NSLocalizedString("CALL_VIEW_MUTE_LABEL", comment: "Accessibility label for muting the microphone") -// videoModeMuteButton.alpha = 0.9 -// -// videoModeFlipCameraButton = createButton(image: #imageLiteral(resourceName: "video-switch-camera-unselected"), -// action: #selector(didPressFlipCamera)) -// -// videoModeFlipCameraButton.accessibilityLabel = NSLocalizedString("CALL_VIEW_SWITCH_CAMERA_DIRECTION", comment: "Accessibility label to toggle front- vs. rear-facing camera") -// videoModeFlipCameraButton.alpha = 0.9 -// -// videoModeVideoButton = createButton(image: #imageLiteral(resourceName: "video-video-unselected"), -// action: #selector(didPressVideo)) -// videoModeVideoButton.setImage(#imageLiteral(resourceName: "video-video-selected"), for: .selected) -// videoModeVideoButton.accessibilityLabel = NSLocalizedString("CALL_VIEW_SWITCH_TO_AUDIO_LABEL", comment: "Accessibility label to switch to audio only") -// videoModeVideoButton.alpha = 0.9 -// -// ongoingCallControls = UIStackView(arrangedSubviews: [hangUpButton]) -// ongoingCallControls.axis = .vertical -// ongoingCallControls.alignment = .center -// view.addSubview(ongoingCallControls) -// -// ongoingAudioCallControls = UIStackView(arrangedSubviews: [audioModeMuteButton, audioSourceButton, audioModeVideoButton]) -// ongoingAudioCallControls.distribution = .equalSpacing -// ongoingAudioCallControls.axis = .horizontal -// -// ongoingVideoCallControls = UIStackView(arrangedSubviews: [videoModeMuteButton, videoModeFlipCameraButton, videoModeVideoButton]) -// ongoingAudioCallControls.distribution = .equalSpacing -// ongoingVideoCallControls.axis = .horizontal -// } -// -// func presentAudioSourcePicker() { -// AssertIsOnMainThread() -// -// let actionSheetController = UIAlertController(title: nil, message: nil, preferredStyle: .actionSheet) -// -// let dismissAction = UIAlertAction(title: CommonStrings.dismissButton, style: .cancel, handler: nil) -// actionSheetController.addAction(dismissAction) -// -// let currentAudioSource = callUIAdapter.audioService.currentAudioSource(call: self.call) -// for audioSource in self.appropriateAudioSources { -// let routeAudioAction = UIAlertAction(title: audioSource.localizedName, style: .default) { _ in -// self.callUIAdapter.setAudioSource(call: self.call, audioSource: audioSource) -// } -// -// // HACK: private API to create checkmark for active audio source. -// routeAudioAction.setValue(currentAudioSource == audioSource, forKey: "checked") -// -// // TODO: pick some icons. Leaving out for MVP -// // HACK: private API to add image to actionsheet -// // routeAudioAction.setValue(audioSource.image, forKey: "image") -// -// actionSheetController.addAction(routeAudioAction) -// } -// -// // Note: It's critical that we present from this view and -// // not the "frontmost view controller" since this view may -// // reside on a separate window. -// presentAlert(actionSheetController) -// } -// -// func updateAvatarImage() { -// contactAvatarView.image = OWSAvatarBuilder.buildImage(thread: thread, diameter: 400) -// } -// -// func createIncomingCallControls() { -// -// acceptIncomingButton = createButton(image: #imageLiteral(resourceName: "call-active-wide"), -// action: #selector(didPressAnswerCall)) -// acceptIncomingButton.accessibilityLabel = NSLocalizedString("CALL_VIEW_ACCEPT_INCOMING_CALL_LABEL", -// comment: "Accessibility label for accepting incoming calls") -// declineIncomingButton = createButton(image: #imageLiteral(resourceName: "hangup-active-wide"), -// action: #selector(didPressDeclineCall)) -// declineIncomingButton.accessibilityLabel = NSLocalizedString("CALL_VIEW_DECLINE_INCOMING_CALL_LABEL", -// comment: "Accessibility label for declining incoming calls") -// -// incomingCallControls = UIStackView(arrangedSubviews: [acceptIncomingButton, declineIncomingButton]) -// incomingCallControls.axis = .horizontal -// incomingCallControls.alignment = .center -// incomingCallControls.distribution = .equalSpacing -// -// view.addSubview(incomingCallControls) -// } -// -// func createButton(image: UIImage, action: Selector) -> UIButton { -// let button = UIButton() -// button.setImage(image, for: .normal) -// button.imageEdgeInsets = UIEdgeInsets(top: buttonInset(), -// left: buttonInset(), -// bottom: buttonInset(), -// right: buttonInset()) -// button.addTarget(self, action: action, for: .touchUpInside) -// button.autoSetDimension(.width, toSize: buttonSize()) -// button.autoSetDimension(.height, toSize: buttonSize()) -// return button -// } -// -// // MARK: - Layout -// -// var localVideoViewTopConstraintDefault: NSLayoutConstraint! -// var localVideoViewTopConstraintHidden: NSLayoutConstraint! -// -// func createViewConstraints() { -// -// let contactVSpacing = CGFloat(3) -// let settingsNagHMargin = CGFloat(30) -// let ongoingBottomMargin = ScaleFromIPhone5To7Plus(23, 41) -// let incomingHMargin = ScaleFromIPhone5To7Plus(30, 56) -// let incomingBottomMargin = CGFloat(41) -// let settingsNagBottomMargin = CGFloat(41) -// let avatarTopSpacing = ScaleFromIPhone5To7Plus(25, 50) -// // The buttons have built-in 10% margins, so to appear centered -// // the avatar's bottom spacing should be a bit less. -// let avatarBottomSpacing = ScaleFromIPhone5To7Plus(18, 41) -// // Layout of the local video view is a bit unusual because -// // although the view is square, it will be used -// let videoPreviewHMargin = CGFloat(0) -// -// // Dark blurred background. -// blurView.autoPinEdgesToSuperviewEdges() -// -// leaveCallViewButton.autoPinEdge(toSuperviewEdge: .leading) -// -// if #available(iOS 11, *) { -// leaveCallViewButton.autoPinEdge(toSuperviewMargin: .top) -// contactNameLabel.autoPinEdge(toSuperviewMargin: .top) -// } else { -// leaveCallViewButton.autoPinEdge(.top, to: .top, of: view) -// contactNameLabel.autoPinEdge(.top, to: .top, of: view) -// } -// -// contactNameLabel.autoPinEdge(.leading, to: .trailing, of: leaveCallViewButton, withOffset: 8, relation: .greaterThanOrEqual) -// contactNameLabel.autoHCenterInSuperview() -// contactNameLabel.setContentHuggingVerticalHigh() -// contactNameLabel.setCompressionResistanceHigh() -// -// callStatusLabel.autoPinEdge(.top, to: .bottom, of: contactNameLabel, withOffset: contactVSpacing) -// callStatusLabel.autoHCenterInSuperview() -// callStatusLabel.setContentHuggingVerticalHigh() -// callStatusLabel.setCompressionResistanceHigh() -// -// localVideoView.autoPinTrailingToSuperviewMargin(withInset: videoPreviewHMargin) -// -// self.localVideoViewTopConstraintDefault = localVideoView.autoPinEdge(.top, to: .bottom, of: callStatusLabel, withOffset: 4) -// self.localVideoViewTopConstraintHidden = localVideoView.autoPinEdge(toSuperviewMargin: .top) -// let localVideoSize = ScaleFromIPhone5To7Plus(80, 100) -// localVideoView.autoSetDimension(.width, toSize: localVideoSize) -// localVideoView.autoSetDimension(.height, toSize: localVideoSize) -// -// remoteVideoView.autoPinEdgesToSuperviewEdges() -// -// contactAvatarContainerView.autoPinEdge(.top, to: .bottom, of: callStatusLabel, withOffset: +avatarTopSpacing) -// contactAvatarContainerView.autoPinEdge(.bottom, to: .top, of: ongoingCallControls, withOffset: -avatarBottomSpacing) -// contactAvatarContainerView.autoPinWidthToSuperview(withMargin: avatarTopSpacing) -// -// contactAvatarView.autoCenterInSuperview() -// -// // Ensure ContacAvatarView gets as close as possible to it's superview edges while maintaining -// // aspect ratio. -// contactAvatarView.autoPinToSquareAspectRatio() -// contactAvatarView.autoPinEdge(toSuperviewEdge: .top, withInset: 0, relation: .greaterThanOrEqual) -// contactAvatarView.autoPinEdge(toSuperviewEdge: .right, withInset: 0, relation: .greaterThanOrEqual) -// contactAvatarView.autoPinEdge(toSuperviewEdge: .bottom, withInset: 0, relation: .greaterThanOrEqual) -// contactAvatarView.autoPinEdge(toSuperviewEdge: .left, withInset: 0, relation: .greaterThanOrEqual) -// NSLayoutConstraint.autoSetPriority(UILayoutPriority.defaultLow) { -// contactAvatarView.autoPinEdgesToSuperviewMargins() -// } -// -// // Ongoing call controls -// ongoingCallControls.autoPinEdge(toSuperviewEdge: .bottom, withInset: ongoingBottomMargin) -// ongoingCallControls.autoPinLeadingToSuperviewMargin() -// ongoingCallControls.autoPinTrailingToSuperviewMargin() -// ongoingCallControls.setContentHuggingVerticalHigh() -// -// // Incoming call controls -// incomingCallControls.autoPinEdge(toSuperviewEdge: .bottom, withInset: incomingBottomMargin) -// incomingCallControls.autoPinLeadingToSuperviewMargin(withInset: incomingHMargin) -// incomingCallControls.autoPinTrailingToSuperviewMargin(withInset: incomingHMargin) -// incomingCallControls.setContentHuggingVerticalHigh() -// -// // Settings nag views -// settingsNagView.autoPinEdge(toSuperviewEdge: .bottom, withInset: settingsNagBottomMargin) -// settingsNagView.autoPinWidthToSuperview(withMargin: settingsNagHMargin) -// settingsNagView.autoPinEdge(.top, to: .bottom, of: callStatusLabel) -// } -// -// override func updateViewConstraints() { -// updateRemoteVideoLayout() -// updateLocalVideoLayout() -// -// super.updateViewConstraints() -// } -// -// internal func updateRemoteVideoLayout() { -// remoteVideoView.isHidden = !self.hasRemoteVideoTrack -// updateCallUI(callState: call.state) -// } -// -// let videoHintView = CallVideoHintView() -// -// internal func updateLocalVideoLayout() { -// if !localVideoView.isHidden { -// localVideoView.superview?.bringSubviewToFront(localVideoView) -// } -// -// updateCallUI(callState: call.state) -// } -// -// // MARK: - Methods -// -// func showCallFailed(error: Error) { -// // TODO Show something in UI. -// Logger.error("call failed with error: \(error)") -// } -// -// // MARK: - View State -// -// func localizedTextForCallState(_ callState: CallState) -> String { -// assert(Thread.isMainThread) -// -// switch callState { -// case .idle, .remoteHangup, .localHangup: -// return NSLocalizedString("IN_CALL_TERMINATED", comment: "Call setup status label") -// case .dialing: -// return NSLocalizedString("IN_CALL_CONNECTING", comment: "Call setup status label") -// case .remoteRinging, .localRinging: -// return NSLocalizedString("IN_CALL_RINGING", comment: "Call setup status label") -// case .answering: -// return NSLocalizedString("IN_CALL_SECURING", comment: "Call setup status label") -// case .connected: -// let callDuration = call.connectionDuration() -// let callDurationDate = Date(timeIntervalSinceReferenceDate: callDuration) -// if dateFormatter == nil { -// dateFormatter = DateFormatter() -// dateFormatter!.dateFormat = "HH:mm:ss" -// dateFormatter!.timeZone = TimeZone(identifier: "UTC")! -// } -// var formattedDate = dateFormatter!.string(from: callDurationDate) -// if formattedDate.hasPrefix("00:") { -// // Don't show the "hours" portion of the date format unless the -// // call duration is at least 1 hour. -// formattedDate = String(formattedDate[formattedDate.index(formattedDate.startIndex, offsetBy: 3)...]) -// } else { -// // If showing the "hours" portion of the date format, strip any leading -// // zeroes. -// if formattedDate.hasPrefix("0") { -// formattedDate = String(formattedDate[formattedDate.index(formattedDate.startIndex, offsetBy: 1)...]) -// } -// } -// return formattedDate -// case .reconnecting: -// return NSLocalizedString("IN_CALL_RECONNECTING", comment: "Call setup status label") -// case .remoteBusy: -// return NSLocalizedString("END_CALL_RESPONDER_IS_BUSY", comment: "Call setup status label") -// case .localFailure: -// if let error = call.error { -// switch error { -// case .timeout(description: _): -// if self.call.direction == .outgoing { -// return NSLocalizedString("CALL_SCREEN_STATUS_NO_ANSWER", comment: "Call setup status label after outgoing call times out") -// } -// default: -// break -// } -// } -// -// return NSLocalizedString("END_CALL_UNCATEGORIZED_FAILURE", comment: "Call setup status label") -// } -// } -// -// var isBlinkingReconnectLabel = false -// func updateCallStatusLabel(callState: CallState) { -// assert(Thread.isMainThread) -// -// let text = String(format: CallStrings.callStatusFormat, -// localizedTextForCallState(callState)) -// self.callStatusLabel.text = text -// -// // Handle reconnecting blinking -// if case .reconnecting = callState { -// if !isBlinkingReconnectLabel { -// isBlinkingReconnectLabel = true -// UIView.animate(withDuration: 0.7, delay: 0, options: [.autoreverse, .repeat], -// animations: { -// self.callStatusLabel.alpha = 0.2 -// }, completion: nil) -// } else { -// // already blinking -// } -// } else { -// // We're no longer in a reconnecting state, either the call failed or we reconnected. -// // Stop the blinking animation -// if isBlinkingReconnectLabel { -// self.callStatusLabel.layer.removeAllAnimations() -// self.callStatusLabel.alpha = 1 -// isBlinkingReconnectLabel = false -// } -// } -// } -// -// func updateCallUI(callState: CallState) { -// assert(Thread.isMainThread) -// updateCallStatusLabel(callState: callState) -// if isShowingSettingsNag { -// settingsNagView.isHidden = false -// contactAvatarView.isHidden = true -// ongoingCallControls.isHidden = true -// return -// } -// -// // Marquee scrolling is distracting during a video call, disable it. -// contactNameLabel.labelize = call.hasLocalVideo -// -// audioModeMuteButton.isSelected = call.isMuted -// videoModeMuteButton.isSelected = call.isMuted -// audioModeVideoButton.isSelected = call.hasLocalVideo -// videoModeVideoButton.isSelected = call.hasLocalVideo -// -// // Show Incoming vs. Ongoing call controls -// let isRinging = callState == .localRinging -// incomingCallControls.isHidden = !isRinging -// incomingCallControls.isUserInteractionEnabled = isRinging -// ongoingCallControls.isHidden = isRinging -// ongoingCallControls.isUserInteractionEnabled = !isRinging -// -// // Rework control state if remote video is available. -// let hasRemoteVideo = !remoteVideoView.isHidden -// contactAvatarView.isHidden = hasRemoteVideo -// -// // Rework control state if local video is available. -// let hasLocalVideo = !localVideoView.isHidden -// -// if hasLocalVideo { -// ongoingAudioCallControls.removeFromSuperview() -// ongoingCallControls.insertArrangedSubview(ongoingVideoCallControls, at: 0) -// } else { -// ongoingVideoCallControls.removeFromSuperview() -// ongoingCallControls.insertArrangedSubview(ongoingAudioCallControls, at: 0) -// } -// // Layout immediately to avoid spurious animation. -// ongoingCallControls.layoutIfNeeded() -// -// // Also hide other controls if user has tapped to hide them. -// if shouldRemoteVideoControlsBeHidden && !remoteVideoView.isHidden { -// leaveCallViewButton.isHidden = true -// contactNameLabel.isHidden = true -// callStatusLabel.isHidden = true -// ongoingCallControls.isHidden = true -// videoHintView.isHidden = true -// } else { -// leaveCallViewButton.isHidden = false -// contactNameLabel.isHidden = false -// callStatusLabel.isHidden = false -// -// if hasRemoteVideo && !hasLocalVideo && !hasShownLocalVideo && !hasUserDismissedVideoHint { -// view.addSubview(videoHintView) -// videoHintView.isHidden = false -// videoHintView.autoPinEdge(.bottom, to: .top, of: audioModeVideoButton) -// videoHintView.autoPinEdge(.trailing, to: .leading, of: audioModeVideoButton, withOffset: buttonSize() / 2 + videoHintView.kTailHMargin + videoHintView.kTailWidth / 2) -// } else { -// videoHintView.removeFromSuperview() -// } -// } -// -// let doLocalVideoLayout = { -// self.localVideoViewTopConstraintDefault.isActive = !self.contactNameLabel.isHidden -// self.localVideoViewTopConstraintHidden.isActive = self.contactNameLabel.isHidden -// self.localVideoView.superview?.layoutIfNeeded() -// } -// if hasShownLocalVideo { -// // Animate. -// UIView.animate(withDuration: 0.25, animations: doLocalVideoLayout) -// } else { -// // Don't animate. -// doLocalVideoLayout() -// } -// -// // Audio Source Handling (bluetooth) -// if self.hasAlternateAudioSources { -// // With bluetooth, button does not stay selected. Pressing it pops an actionsheet -// // and the button should immediately "unselect". -// audioSourceButton.isSelected = false -// -// if hasLocalVideo { -// audioSourceButton.setImage(#imageLiteral(resourceName: "ic_speaker_bluetooth_inactive_video_mode"), for: .normal) -// audioSourceButton.setImage(#imageLiteral(resourceName: "ic_speaker_bluetooth_inactive_video_mode"), for: .selected) -// } else { -// audioSourceButton.setImage(#imageLiteral(resourceName: "ic_speaker_bluetooth_inactive_audio_mode"), for: .normal) -// audioSourceButton.setImage(#imageLiteral(resourceName: "ic_speaker_bluetooth_inactive_audio_mode"), for: .selected) -// } -// audioSourceButton.isHidden = false -// } else { -// // No bluetooth audio detected -// audioSourceButton.setImage(#imageLiteral(resourceName: "audio-call-speaker-inactive"), for: .normal) -// audioSourceButton.setImage(#imageLiteral(resourceName: "audio-call-speaker-active"), for: .selected) -// -// // If there's no bluetooth, we always use speakerphone, so no need for -// // a button, giving more screen back for the video. -// audioSourceButton.isHidden = hasLocalVideo -// } -// -// // Dismiss Handling -// switch callState { -// case .remoteHangup, .remoteBusy, .localFailure: -// Logger.debug("dismissing after delay because new state is \(callState)") -// dismissIfPossible(shouldDelay: true) -// case .localHangup: -// Logger.debug("dismissing immediately from local hangup") -// dismissIfPossible(shouldDelay: false) -// default: break -// } -// -// if callState == .connected { -// if callDurationTimer == nil { -// let kDurationUpdateFrequencySeconds = 1 / 20.0 -// callDurationTimer = WeakTimer.scheduledTimer(timeInterval: TimeInterval(kDurationUpdateFrequencySeconds), -// target: self, -// userInfo: nil, -// repeats: true) {[weak self] _ in -// self?.updateCallDuration() -// } -// } -// } else { -// callDurationTimer?.invalidate() -// callDurationTimer = nil -// } -// } -// -// func updateCallDuration() { -// updateCallStatusLabel(callState: call.state) -// } -// -// // We update the audioSourceButton outside of the main `updateCallUI` -// // because `updateCallUI` is intended to be idempotent, which isn't possible -// // with external speaker state because: -// // - the system API which enables the external speaker is a (somewhat slow) asyncronous -// // operation -// // - we want to give immediate UI feedback by marking the pressed button as selected -// // before the operation completes. -// func updateAudioSourceButtonIsSelected() { -// guard callUIAdapter.audioService.isSpeakerphoneEnabled else { -// self.audioSourceButton.isSelected = false -// return -// } -// -// // VideoChat mode enables the output speaker, but we don't -// // want to highlight the speaker button in that case. -// guard !call.hasLocalVideo else { -// self.audioSourceButton.isSelected = false -// return -// } -// -// self.audioSourceButton.isSelected = true -// } -// -// // MARK: - Actions -// -// /** -// * Ends a connected call. Do not confuse with `didPressDeclineCall`. -// */ -// @objc func didPressHangup(sender: UIButton) { -// Logger.info("") -// -// callUIAdapter.localHangupCall(call) -// -// dismissIfPossible(shouldDelay: false) -// } -// -// @objc func didPressMute(sender muteButton: UIButton) { -// Logger.info("") -// muteButton.isSelected = !muteButton.isSelected -// -// callUIAdapter.setIsMuted(call: call, isMuted: muteButton.isSelected) -// } -// -// @objc func didPressAudioSource(sender button: UIButton) { -// Logger.info("") -// -// if self.hasAlternateAudioSources { -// presentAudioSourcePicker() -// } else { -// didPressSpeakerphone(sender: button) -// } -// } -// -// func didPressSpeakerphone(sender button: UIButton) { -// Logger.info("") -// -// button.isSelected = !button.isSelected -// callUIAdapter.audioService.requestSpeakerphone(isEnabled: button.isSelected) -// } -// -// func didPressTextMessage(sender button: UIButton) { -// Logger.info("") -// -// dismissIfPossible(shouldDelay: false) -// } -// -// @objc func didPressAnswerCall(sender: UIButton) { -// Logger.info("") -// -// callUIAdapter.answerCall(call) -// } -// -// @objc func didPressVideo(sender: UIButton) { -// Logger.info("") -// let hasLocalVideo = !sender.isSelected -// -// callUIAdapter.setHasLocalVideo(call: call, hasLocalVideo: hasLocalVideo) -// } -// -// @objc func didPressFlipCamera(sender: UIButton) { -// sender.isSelected = !sender.isSelected -// -// let isUsingFrontCamera = !sender.isSelected -// Logger.info("with isUsingFrontCamera: \(isUsingFrontCamera)") -// -// callUIAdapter.setCameraSource(call: call, isUsingFrontCamera: isUsingFrontCamera) -// } -// -// /** -// * Denies an incoming not-yet-connected call, Do not confuse with `didPressHangup`. -// */ -// @objc func didPressDeclineCall(sender: UIButton) { -// Logger.info("") -// -// callUIAdapter.declineCall(call) -// -// dismissIfPossible(shouldDelay: false) -// } -// -// @objc func didPressShowCallSettings(sender: UIButton) { -// Logger.info("") -// -// markSettingsNagAsComplete() -// -// dismissIfPossible(shouldDelay: false, ignoreNag: true, completion: { -// // Find the frontmost presented UIViewController from which to present the -// // settings views. -// let fromViewController = UIApplication.shared.findFrontmostViewController(ignoringAlerts: true) -// assert(fromViewController != nil) -// -// // Construct the "settings" view & push the "privacy settings" view. -// let navigationController = AppSettingsViewController.inModalNavigationController() -// navigationController.pushViewController(PrivacySettingsTableViewController(), animated: false) -// -// fromViewController?.present(navigationController, animated: true, completion: nil) -// }) -// } -// -// @objc func didPressDismissNag(sender: UIButton) { -// Logger.info("") -// -// markSettingsNagAsComplete() -// -// dismissIfPossible(shouldDelay: false, ignoreNag: true) -// } -// -// // We only show the "blocking" settings nag until the user has chosen -// // to view the privacy settings _or_ dismissed the nag at least once. -// // -// // In either case, we set the "CallKit enabled" and "CallKit privacy enabled" -// // settings to their default values to indicate that the user has reviewed -// // them. -// private func markSettingsNagAsComplete() { -// Logger.info("") -// -// let preferences = Environment.shared.preferences! -// -// preferences.setIsCallKitEnabled(preferences.isCallKitEnabled()) -// preferences.setIsCallKitPrivacyEnabled(preferences.isCallKitPrivacyEnabled()) -// } -// -// @objc func didTapLeaveCall(sender: UIButton) { -// OWSWindowManager.shared().leaveCallView() -// } -// -// // MARK: - CallObserver -// -// internal func stateDidChange(call: SignalCall, state: CallState) { -// AssertIsOnMainThread() -// Logger.info("new call status: \(state)") -// -// self.updateCallUI(callState: state) -// } -// -// internal func hasLocalVideoDidChange(call: SignalCall, hasLocalVideo: Bool) { -// AssertIsOnMainThread() -// self.updateCallUI(callState: call.state) -// } -// -// internal func muteDidChange(call: SignalCall, isMuted: Bool) { -// AssertIsOnMainThread() -// self.updateCallUI(callState: call.state) -// } -// -// func holdDidChange(call: SignalCall, isOnHold: Bool) { -// AssertIsOnMainThread() -// self.updateCallUI(callState: call.state) -// } -// -// internal func audioSourceDidChange(call: SignalCall, audioSource: AudioSource?) { -// AssertIsOnMainThread() -// self.updateCallUI(callState: call.state) -// } -// -// // MARK: - CallAudioServiceDelegate -// -// func callAudioService(_ callAudioService: CallAudioService, didUpdateIsSpeakerphoneEnabled isSpeakerphoneEnabled: Bool) { -// AssertIsOnMainThread() -// -// updateAudioSourceButtonIsSelected() -// } -// -// func callAudioServiceDidChangeAudioSession(_ callAudioService: CallAudioService) { -// AssertIsOnMainThread() -// -// // Which sources are available depends on the state of your Session. -// // When the audio session is not yet in PlayAndRecord none are available -// // Then if we're in speakerphone, bluetooth isn't available. -// // So we accrue all possible audio sources in a set, and that list lives as longs as the CallViewController -// // The downside of this is that if you e.g. unpair your bluetooth mid call, it will still appear as an option -// // until your next call. -// // FIXME: There's got to be a better way, but this is where I landed after a bit of work, and seems to work -// // pretty well in practice. -// let availableInputs = callAudioService.availableInputs -// self.allAudioSources.formUnion(availableInputs) -// } -// -// // MARK: - Video -// -// internal func updateLocalVideo(captureSession: AVCaptureSession?) { -// -// AssertIsOnMainThread() -// -// guard localVideoView.captureSession != captureSession else { -// Logger.debug("ignoring redundant update") -// return -// } -// -// localVideoView.captureSession = captureSession -// let isHidden = captureSession == nil -// -// Logger.info("isHidden: \(isHidden)") -// localVideoView.isHidden = isHidden -// -// updateLocalVideoLayout() -// updateAudioSourceButtonIsSelected() -// -// // Don't animate layout of local video view until it has been presented. -// if !isHidden { -// hasShownLocalVideo = true -// } -// } -// -// var hasRemoteVideoTrack: Bool { -// return self.remoteVideoTrack != nil -// } -// -// var hasUserDismissedVideoHint: Bool = false -// -// internal func updateRemoteVideoTrack(remoteVideoTrack: RTCVideoTrack?) { -// AssertIsOnMainThread() -// -// guard self.remoteVideoTrack != remoteVideoTrack else { -// Logger.debug("ignoring redundant update") -// return -// } -// -// self.remoteVideoTrack?.remove(remoteVideoView) -// self.remoteVideoTrack = nil -// remoteVideoView.renderFrame(nil) -// self.remoteVideoTrack = remoteVideoTrack -// self.remoteVideoTrack?.add(remoteVideoView) -// -// shouldRemoteVideoControlsBeHidden = false -// -// if remoteVideoTrack != nil { -// playRemoteEnabledVideoHapticFeedback() -// } -// -// updateRemoteVideoLayout() -// } -// -// // MARK: Video Haptics -// -// let feedbackGenerator = NotificationHapticFeedback() -// var lastHapticTime: TimeInterval = CACurrentMediaTime() -// func playRemoteEnabledVideoHapticFeedback() { -// let currentTime = CACurrentMediaTime() -// guard currentTime - lastHapticTime > 5 else { -// Logger.debug("ignoring haptic feedback since it's too soon") -// return -// } -// feedbackGenerator.notificationOccurred(.success) -// lastHapticTime = currentTime -// } -// -// // MARK: - Dismiss -// -// internal func dismissIfPossible(shouldDelay: Bool, ignoreNag ignoreNagParam: Bool = false, completion: (() -> Void)? = nil) { -// callUIAdapter.audioService.delegate = nil -// -// let ignoreNag: Bool = { -// // Nothing to nag about on iOS11 -// if #available(iOS 11, *) { -// return true -// } else { -// // otherwise on iOS10, nag as specified -// return ignoreNagParam -// } -// }() -// -// if hasDismissed { -// // Don't dismiss twice. -// return -// } else if !ignoreNag && -// call.direction == .incoming && -// UIDevice.current.supportsCallKit && -// (!Environment.shared.preferences.isCallKitEnabled() || -// Environment.shared.preferences.isCallKitPrivacyEnabled()) { -// -// isShowingSettingsNag = true -// -// // Update the nag view's copy to reflect the settings state. -// if Environment.shared.preferences.isCallKitEnabled() { -// settingsNagDescriptionLabel.text = NSLocalizedString("CALL_VIEW_SETTINGS_NAG_DESCRIPTION_PRIVACY", -// comment: "Reminder to the user of the benefits of disabling CallKit privacy.") -// } else { -// settingsNagDescriptionLabel.text = NSLocalizedString("CALL_VIEW_SETTINGS_NAG_DESCRIPTION_ALL", -// comment: "Reminder to the user of the benefits of enabling CallKit and disabling CallKit privacy.") -// } -// settingsNagDescriptionLabel.superview?.setNeedsLayout() -// -// if Environment.shared.preferences.isCallKitEnabledSet() || -// Environment.shared.preferences.isCallKitPrivacySet() { -// // User has already touched these preferences, only show -// // the "fleeting" nag, not the "blocking" nag. -// -// // Show nag for N seconds. -// DispatchQueue.main.asyncAfter(deadline: .now() + 5) { [weak self] in -// guard let strongSelf = self else { return } -// strongSelf.dismissIfPossible(shouldDelay: false, ignoreNag: true) -// } -// } -// } else if shouldDelay { -// hasDismissed = true -// DispatchQueue.main.asyncAfter(deadline: .now() + 1.5) { [weak self] in -// guard let strongSelf = self else { return } -// strongSelf.dismissImmediately(completion: completion) -// } -// } else { -// hasDismissed = true -// dismissImmediately(completion: completion) -// } -// } -// -// internal func dismissImmediately(completion: (() -> Void)?) { -// if CallViewController.kShowCallViewOnSeparateWindow { -// OWSWindowManager.shared().endCall(self) -// completion?() -// } else { -// self.dismiss(animated: true, completion: completion) -// } -// } -// -// // MARK: - CallServiceObserver -// -// internal func didUpdateCall(call: SignalCall?) { -// // Do nothing. -// } -// -// internal func didUpdateVideoTracks(call: SignalCall?, -// localCaptureSession: AVCaptureSession?, -// remoteVideoTrack: RTCVideoTrack?) { -// AssertIsOnMainThread() -// -// updateLocalVideo(captureSession: localCaptureSession) -// updateRemoteVideoTrack(remoteVideoTrack: remoteVideoTrack) -// } -// -// // MARK: - Proximity Monitoring -// -// func ensureProximityMonitoring() { -// if #available(iOS 11, *) { -// // BUG: Adding `self` as a Weak reference to the proximityMonitoringManager results in -// // the CallViewController never being deallocated, which, besides being a memory leak -// // can interfere with subsequent video capture - presumably because the old capture -// // session is still retained via the callViewController.localVideoView. -// // -// // A code audit has not revealed a retain cycle. -// // -// // Using the XCode memory debugger shows that a strong reference is held by -// // windowManager.callNavigationController->_childViewControllers. -// // Even though, when inspecting via the debugger, the CallViewController is not shown as -// // a childViewController. -// // -// // (lldb) po [[[OWSWindowManager sharedManager] callNavigationController] childViewControllers] -// // <__NSSingleObjectArrayI 0x1c0418bd0>( -// // -// // ) -// // -// // Weirder still, when presenting another CallViewController, the old one remains unallocated -// // and inspecting it in the memory debugger shows _no_ strong references to it (yet it -// // is not deallocated). Some weak references do remain - from the proximityMonitoringManager -// // and the callObserver, both of which use the Weak struct, which could be related. -// // -// // In any case, we can apparently avoid this behavior by not adding self as a Weak lifetime -// // and as of iOS11, the system automatically managages proximityMonitoring -// // via CallKit and AudioSessions. Proximity monitoring will be enabled whenever a call -// // is active, unless we switch to VideoChat audio mode (which is actually desirable -// // behavior), so the proximityMonitoringManager is redundant for calls on iOS11+. -// } else { -// // before iOS11, manually enable proximityMonitoring while we're on a call. -// self.proximityMonitoringManager.add(lifetime: self) -// } -// } -//} -// -//extension CallViewController: CallVideoHintViewDelegate { -// func didTapCallVideoHintView(_ videoHintView: CallVideoHintView) { -// self.hasUserDismissedVideoHint = true -// updateRemoteVideoLayout() -// } -//} diff --git a/Session/Signal/CompareSafetyNumbersActivity.swift b/Session/Signal/CompareSafetyNumbersActivity.swift deleted file mode 100644 index 0d7a22222..000000000 --- a/Session/Signal/CompareSafetyNumbersActivity.swift +++ /dev/null @@ -1,103 +0,0 @@ -// -// Copyright (c) 2019 Open Whisper Systems. All rights reserved. -// - -import Foundation -import SignalUtilitiesKit - -let CompareSafetyNumbersActivityType = "org.whispersystems.signal.activity.CompareSafetyNumbers" - -@objc(OWSCompareSafetyNumbersActivityDelegate) -protocol CompareSafetyNumbersActivityDelegate { - func compareSafetyNumbersActivitySucceeded(activity: CompareSafetyNumbersActivity) - func compareSafetyNumbersActivity(_ activity: CompareSafetyNumbersActivity, failedWithError error: Error) -} - -@objc (OWSCompareSafetyNumbersActivity) -class CompareSafetyNumbersActivity: UIActivity { - - var mySafetyNumbers: String? - let delegate: CompareSafetyNumbersActivityDelegate - - @objc - required init(delegate: CompareSafetyNumbersActivityDelegate) { - self.delegate = delegate - super.init() - } - - // MARK: UIActivity - - override class var activityCategory: UIActivity.Category { - get { return .action } - } - - override var activityType: UIActivity.ActivityType? { - get { return UIActivity.ActivityType(rawValue: CompareSafetyNumbersActivityType) } - } - - override var activityTitle: String? { - get { - return NSLocalizedString("COMPARE_SAFETY_NUMBER_ACTION", comment: "Activity Sheet label") - } - } - - override var activityImage: UIImage? { - get { - return #imageLiteral(resourceName: "ic_lock_outline") - } - } - - override func canPerform(withActivityItems activityItems: [Any]) -> Bool { - return stringsFrom(activityItems: activityItems).count > 0 - } - - override func prepare(withActivityItems activityItems: [Any]) { - let myFormattedSafetyNumbers = stringsFrom(activityItems: activityItems).first - mySafetyNumbers = numericOnly(string: myFormattedSafetyNumbers) - } - - override func perform() { - defer { activityDidFinish(true) } - - let pasteboardString = numericOnly(string: UIPasteboard.general.string) - guard (pasteboardString != nil && pasteboardString!.count == 60) else { - Logger.warn("no valid safety numbers found in pasteboard: \(String(describing: pasteboardString))") - let error = OWSErrorWithCodeDescription(OWSErrorCode.userError, - NSLocalizedString("PRIVACY_VERIFICATION_FAILED_NO_SAFETY_NUMBERS_IN_CLIPBOARD", comment: "Alert body for user error")) - - delegate.compareSafetyNumbersActivity(self, failedWithError: error) - return - } - - let pasteboardSafetyNumbers = pasteboardString! - - if pasteboardSafetyNumbers == mySafetyNumbers { - Logger.info("successfully matched safety numbers. local numbers: \(String(describing: mySafetyNumbers)) pasteboard:\(pasteboardSafetyNumbers)") - delegate.compareSafetyNumbersActivitySucceeded(activity: self) - } else { - Logger.warn("local numbers: \(String(describing: mySafetyNumbers)) didn't match pasteboard:\(pasteboardSafetyNumbers)") - let error = OWSErrorWithCodeDescription(OWSErrorCode.privacyVerificationFailure, - NSLocalizedString("PRIVACY_VERIFICATION_FAILED_MISMATCHED_SAFETY_NUMBERS_IN_CLIPBOARD", comment: "Alert body")) - delegate.compareSafetyNumbersActivity(self, failedWithError: error) - } - } - - // MARK: Helpers - - func numericOnly(string: String?) -> String? { - guard let string = string else { - return nil - } - - var numericOnly: String? - if let regex = try? NSRegularExpression(pattern: "\\D", options: .caseInsensitive) { - numericOnly = regex.stringByReplacingMatches(in: string, options: .withTransparentBounds, range: NSRange(location: 0, length: string.utf16.count), withTemplate: "") - } - - return numericOnly - } - - func stringsFrom(activityItems: [Any]) -> [String] { - return activityItems.map { $0 as? String }.filter { $0 != nil }.map { $0! } - } -} diff --git a/Session/Signal/ConversationView/TypingIndicatorInteraction.swift b/Session/Signal/ConversationView/Cells/TypingIndicatorInteraction.swift similarity index 100% rename from Session/Signal/ConversationView/TypingIndicatorInteraction.swift rename to Session/Signal/ConversationView/Cells/TypingIndicatorInteraction.swift diff --git a/Session/Signal/LegacyNotificationsAdaptee.swift b/Session/Signal/LegacyNotificationsAdaptee.swift index 6d92bd199..10cb0e538 100644 --- a/Session/Signal/LegacyNotificationsAdaptee.swift +++ b/Session/Signal/LegacyNotificationsAdaptee.swift @@ -18,30 +18,6 @@ struct LegacyNotificationConfig { static func notificationAction(_ action: AppNotificationAction) -> UIUserNotificationAction { switch action { -// case .answerCall: -// let mutableAction = UIMutableUserNotificationAction() -// mutableAction.identifier = action.identifier -// mutableAction.title = CallStrings.answerCallButtonTitle -// mutableAction.activationMode = .foreground -// mutableAction.isDestructive = false -// mutableAction.isAuthenticationRequired = false -// return mutableAction -// case .callBack: -// let mutableAction = UIMutableUserNotificationAction() -// mutableAction.identifier = action.identifier -// mutableAction.title = CallStrings.callBackButtonTitle -// mutableAction.activationMode = .foreground -// mutableAction.isDestructive = false -// mutableAction.isAuthenticationRequired = true -// return mutableAction -// case .declineCall: -// let mutableAction = UIMutableUserNotificationAction() -// mutableAction.identifier = action.identifier -// mutableAction.title = CallStrings.declineCallButtonTitle -// mutableAction.activationMode = .background -// mutableAction.isDestructive = false -// mutableAction.isAuthenticationRequired = false -// return mutableAction case .markAsRead: let mutableAction = UIMutableUserNotificationAction() mutableAction.identifier = action.identifier @@ -283,12 +259,6 @@ public class LegacyNotificationActionHandler: NSObject { } switch action { -// case .answerCall: -// return try actionHandler.answerCall(userInfo: userInfo) -// case .callBack: -// return try actionHandler.callBack(userInfo: userInfo) -// case .declineCall: -// return try actionHandler.declineCall(userInfo: userInfo) case .markAsRead: return try actionHandler.markAsRead(userInfo: userInfo) case .reply: diff --git a/Session/Signal/MessageDetailViewController.swift b/Session/Signal/MessageDetailViewController.swift index f752c5391..89d6cdcf2 100644 --- a/Session/Signal/MessageDetailViewController.swift +++ b/Session/Signal/MessageDetailViewController.swift @@ -546,8 +546,6 @@ class MessageDetailViewController: OWSViewController, MediaGalleryDataSourceDele private func string(for messageReceiptStatus: MessageReceiptStatus) -> String { switch messageReceiptStatus { - case .calculatingPoW: - return NSLocalizedString("Calculating proof of work", comment: "") case .uploading: return NSLocalizedString("MESSAGE_METADATA_VIEW_MESSAGE_STATUS_UPLOADING", comment: "Status label for messages which are uploading.") diff --git a/Session/Signal/MessageRecipientStatusUtils.swift b/Session/Signal/MessageRecipientStatusUtils.swift index 5e7f8638e..52ac383ff 100644 --- a/Session/Signal/MessageRecipientStatusUtils.swift +++ b/Session/Signal/MessageRecipientStatusUtils.swift @@ -14,7 +14,6 @@ import SignalUtilitiesKit case read case failed case skipped - case calculatingPoW } @objc @@ -165,8 +164,6 @@ public class MessageRecipientStatusUtils: NSObject { return "failed" case .skipped: return "skipped" - case .calculatingPoW: - return "calculatingPoW" } } } diff --git a/Session/Signal/NotificationSettingsOptionsViewController.m b/Session/Signal/NotificationSettingsOptionsViewController.m index 2a15a33ba..8f675bd10 100644 --- a/Session/Signal/NotificationSettingsOptionsViewController.m +++ b/Session/Signal/NotificationSettingsOptionsViewController.m @@ -63,9 +63,6 @@ { [Environment.shared.preferences setNotificationPreviewType:notificationType]; - // rebuild callUIAdapter since notification configuration changed. -// [AppEnvironment.shared.callService createCallUIAdapter]; - [self.navigationController popViewControllerAnimated:YES]; } diff --git a/Session/Signal/OnboardingCaptchaViewController.swift b/Session/Signal/OnboardingCaptchaViewController.swift deleted file mode 100644 index a9f76abcc..000000000 --- a/Session/Signal/OnboardingCaptchaViewController.swift +++ /dev/null @@ -1,201 +0,0 @@ -// -// Copyright (c) 2019 Open Whisper Systems. All rights reserved. -// - -/* -import UIKit -import WebKit - -@objc -public class OnboardingCaptchaViewController: OnboardingBaseViewController { - - private var webView: WKWebView? - - override public func loadView() { - super.loadView() - - view.backgroundColor = Theme.backgroundColor - view.layoutMargins = .zero - - let titleLabel = self.createTitleLabel(text: NSLocalizedString("ONBOARDING_CAPTCHA_TITLE", comment: "Title of the 'onboarding Captcha' view.")) - titleLabel.accessibilityIdentifier = "onboarding.captcha." + "titleLabel" - - let titleRow = UIStackView(arrangedSubviews: [ - titleLabel - ]) - titleRow.axis = .vertical - titleRow.alignment = .fill - titleRow.layoutMargins = UIEdgeInsets(top: 10, left: 0, bottom: 0, right: 0) - titleRow.isLayoutMarginsRelativeArrangement = true - - // We want the CAPTCHA web content to "fill the screen (honoring margins)". - // The way to do this with WKWebView is to inject a javascript snippet that - // manipulates the viewport. - let jscript = "var meta = document.createElement('meta'); meta.setAttribute('name', 'viewport'); meta.setAttribute('content', 'width=device-width'); document.getElementsByTagName('head')[0].appendChild(meta);" - let userScript = WKUserScript(source: jscript, injectionTime: .atDocumentEnd, forMainFrameOnly: true) - let wkUController = WKUserContentController() - wkUController.addUserScript(userScript) - let wkWebConfig = WKWebViewConfiguration() - wkWebConfig.userContentController = wkUController - let webView = WKWebView(frame: self.view.bounds, configuration: wkWebConfig) - self.webView = webView - webView.navigationDelegate = self - webView.allowsBackForwardNavigationGestures = false - webView.customUserAgent = "Signal iOS (+https://signal.org/download)" - webView.allowsLinkPreview = false - webView.scrollView.contentInset = .zero - webView.layoutMargins = .zero - webView.accessibilityIdentifier = "onboarding.captcha." + "webView" - - let stackView = UIStackView(arrangedSubviews: [ - titleRow, - webView - ]) - stackView.axis = .vertical - stackView.alignment = .fill - stackView.layoutMargins = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 0) - stackView.isLayoutMarginsRelativeArrangement = true - view.addSubview(stackView) - stackView.autoPinWidthToSuperviewMargins() - stackView.autoPinEdge(.top, to: .top, of: view) - autoPinView(toBottomOfViewControllerOrKeyboard: stackView, avoidNotch: true) - - NotificationCenter.default.addObserver(self, - selector: #selector(didBecomeActive), - name: NSNotification.Name.OWSApplicationDidBecomeActive, - object: nil) - } - - deinit { - NotificationCenter.default.removeObserver(self) - } - - public override func viewWillAppear(_ animated: Bool) { - super.viewWillAppear(animated) - - loadContent() - - webView?.scrollView.contentOffset = .zero - } - - public override func viewDidAppear(_ animated: Bool) { - super.viewDidAppear(animated) - - webView?.scrollView.contentOffset = .zero - } - - fileprivate let contentUrl = "https://signalcaptchas.org/registration/generate.html" - - private func loadContent() { - guard let webView = webView else { - owsFailDebug("Missing webView.") - return - } - guard let url = URL(string: contentUrl) else { - owsFailDebug("Invalid URL.") - return - } - webView.load(URLRequest(url: url)) - webView.scrollView.contentOffset = .zero - } - - // MARK: - Notifications - - @objc func didBecomeActive() { - AssertIsOnMainThread() - - loadContent() - } - - // MARK: - - - private func parseCaptchaAndTryToRegister(url: URL) { - Logger.info("") - - guard let captchaToken = parseCaptcha(url: url) else