diff --git a/Signal.xcodeproj/project.pbxproj b/Signal.xcodeproj/project.pbxproj index 6f85706c06..f29eac74d5 100644 --- a/Signal.xcodeproj/project.pbxproj +++ b/Signal.xcodeproj/project.pbxproj @@ -11,6 +11,10 @@ 341BB7491DB727EE001E2975 /* JSQMediaItem+OWS.m in Sources */ = {isa = PBXBuildFile; fileRef = 341BB7481DB727EE001E2975 /* JSQMediaItem+OWS.m */; }; 344F2F671E57A932000D9322 /* UIViewController+OWS.m in Sources */ = {isa = PBXBuildFile; fileRef = 344F2F661E57A932000D9322 /* UIViewController+OWS.m */; }; 34535D821E256BE9008A4747 /* UIView+OWS.m in Sources */ = {isa = PBXBuildFile; fileRef = 34535D811E256BE9008A4747 /* UIView+OWS.m */; }; + 348A08421E6A044E0057E290 /* MessagesViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 348A08411E6A044E0057E290 /* MessagesViewController.xib */; }; + 348A08441E6A1D2C0057E290 /* OWSMessagesToolbarContentView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 348A08431E6A1D2C0057E290 /* OWSMessagesToolbarContentView.xib */; }; + 348A08511E6C73490057E290 /* AttachmentApprovalViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 348A08501E6C73490057E290 /* AttachmentApprovalViewController.swift */; }; + 348A08531E6C75590057E290 /* SignalAttachment.swift in Sources */ = {isa = PBXBuildFile; fileRef = 348A08521E6C75590057E290 /* SignalAttachment.swift */; }; 348F3A4F1E4A533900750D44 /* CallInterstitialViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 348F3A4E1E4A533900750D44 /* CallInterstitialViewController.swift */; }; 34FD93701E3BD43A00109093 /* OWSAnyTouchGestureRecognizer.m in Sources */ = {isa = PBXBuildFile; fileRef = 34FD936F1E3BD43A00109093 /* OWSAnyTouchGestureRecognizer.m */; }; 4505C2BF1E648EA300CEBF41 /* ExperienceUpgrade.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4505C2BE1E648EA300CEBF41 /* ExperienceUpgrade.swift */; }; @@ -624,6 +628,10 @@ 344F2F661E57A932000D9322 /* UIViewController+OWS.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "UIViewController+OWS.m"; path = "util/UIViewController+OWS.m"; sourceTree = ""; }; 34535D801E256BE9008A4747 /* UIView+OWS.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIView+OWS.h"; sourceTree = ""; }; 34535D811E256BE9008A4747 /* UIView+OWS.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIView+OWS.m"; sourceTree = ""; }; + 348A08411E6A044E0057E290 /* MessagesViewController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = MessagesViewController.xib; sourceTree = ""; }; + 348A08431E6A1D2C0057E290 /* OWSMessagesToolbarContentView.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = OWSMessagesToolbarContentView.xib; sourceTree = ""; }; + 348A08501E6C73490057E290 /* AttachmentApprovalViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AttachmentApprovalViewController.swift; sourceTree = ""; }; + 348A08521E6C75590057E290 /* SignalAttachment.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SignalAttachment.swift; sourceTree = ""; }; 348F3A4E1E4A533900750D44 /* CallInterstitialViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CallInterstitialViewController.swift; sourceTree = ""; }; 34FD936E1E3BD43A00109093 /* OWSAnyTouchGestureRecognizer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OWSAnyTouchGestureRecognizer.h; path = views/OWSAnyTouchGestureRecognizer.h; sourceTree = ""; }; 34FD936F1E3BD43A00109093 /* OWSAnyTouchGestureRecognizer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = OWSAnyTouchGestureRecognizer.m; path = views/OWSAnyTouchGestureRecognizer.m; sourceTree = ""; }; @@ -2653,6 +2661,7 @@ FC3196321A08142D0094C78E /* Signals */ = { isa = PBXGroup; children = ( + 348A08501E6C73490057E290 /* AttachmentApprovalViewController.swift */, 348F3A4E1E4A533900750D44 /* CallInterstitialViewController.swift */, 4509E79B1DD6545B0025A59F /* CallViewController.swift */, FC31962B1A06A2190094C78E /* FingerprintViewController.h */, @@ -2663,12 +2672,15 @@ FC3196291A067D8F0094C78E /* MessageComposeTableViewController.m */, FCAC964F19FF0A6E0046DFC5 /* MessagesViewController.h */, FCAC965019FF0A6E0046DFC5 /* MessagesViewController.m */, + 348A08411E6A044E0057E290 /* MessagesViewController.xib */, FCFD256D1A151BCB00F4C644 /* NewGroupViewController.h */, FCFD256E1A151BCB00F4C644 /* NewGroupViewController.m */, 452E3C8C1D935C77002A45B0 /* OWSConversationSettingsTableViewController.h */, 452E3C8D1D935C77002A45B0 /* OWSConversationSettingsTableViewController.m */, + 348A08431E6A1D2C0057E290 /* OWSMessagesToolbarContentView.xib */, A5D0699A1A50E9CB004CB540 /* ShowGroupMembersViewController.h */, A5D069991A50E9CB004CB540 /* ShowGroupMembersViewController.m */, + 348A08521E6C75590057E290 /* SignalAttachment.swift */, FC4FA0241A1B9DC600DA100A /* SignalsNavigationController.h */, FC4FA0251A1B9DC600DA100A /* SignalsNavigationController.m */, FCAC963A19FEF9280046DFC5 /* SignalsViewController.h */, @@ -2897,6 +2909,7 @@ E94066151DFC5B7B00B15392 /* ContactsPicker.xib in Resources */, AD41D7B61A6F6F0600241130 /* play_button@2x.png in Resources */, AD83FF3F1A73426500B5C81A /* audio_pause_button_blue.png in Resources */, + 348A08421E6A044E0057E290 /* MessagesViewController.xib in Resources */, 45E1F3A31DEF1DF000852CF1 /* NoSignalContactsView.xib in Resources */, A5509ECA1A69AB8B00ABA4BC /* Main.storyboard in Resources */, A507A3B11A6C60E300BEED0D /* InboxTableViewCell.xib in Resources */, @@ -2941,6 +2954,7 @@ E1370BE618A0686C00826894 /* sonarping.mp3 in Resources */, B10C9B5F1A7049EC00ECA2BF /* pause_icon.png in Resources */, AD83FF471A73428300B5C81A /* audio_play_button_blue.png in Resources */, + 348A08441E6A1D2C0057E290 /* OWSMessagesToolbarContentView.xib in Resources */, AD83FF451A73426500B5C81A /* audio_pause_button@2x.png in Resources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -3137,6 +3151,7 @@ 45387B041E36D650005D00B3 /* OWS102MoveLoggingPreferenceToUserDefaults.m in Sources */, E197B61818BBEC1A00F073E5 /* RemoteIOAudio.m in Sources */, B67ADDC41989FF8700E1A773 /* RPServerRequestsManager.m in Sources */, + 348A08511E6C73490057E290 /* AttachmentApprovalViewController.swift in Sources */, 348F3A4F1E4A533900750D44 /* CallInterstitialViewController.swift in Sources */, EF764C351DB67CC5000D9A87 /* UIViewController+CameraPermissions.m in Sources */, 453201251E71100C00F20761 /* DisplayableTextFilter.swift in Sources */, @@ -3147,6 +3162,7 @@ 76EB05E018170B33006006FC /* NetworkStream.m in Sources */, 45794E861E00620000066731 /* CallUIAdapter.swift in Sources */, FCFA64B71A24F6730007FB87 /* UIFont+OWS.m in Sources */, + 348A08531E6C75590057E290 /* SignalAttachment.swift in Sources */, B6B9ECFC198B31BA00C620D3 /* PushManager.m in Sources */, 45DF5DF21DDB843F00C936C7 /* CompareSafetyNumbersActivity.swift in Sources */, 76EB05D618170B33006006FC /* ZrtpResponder.m in Sources */, diff --git a/Signal/src/Storyboard/Main.storyboard b/Signal/src/Storyboard/Main.storyboard index 1ef96d8602..47cff25a39 100644 --- a/Signal/src/Storyboard/Main.storyboard +++ b/Signal/src/Storyboard/Main.storyboard @@ -120,12 +120,33 @@ + + + + + + + + + + + + + + + + + + + + + @@ -352,7 +373,7 @@ - + @@ -625,7 +646,7 @@ - + @@ -1701,7 +1722,7 @@ + - diff --git a/Signal/src/view controllers/AttachmentApprovalViewController.swift b/Signal/src/view controllers/AttachmentApprovalViewController.swift new file mode 100644 index 0000000000..8c49ba8b46 --- /dev/null +++ b/Signal/src/view controllers/AttachmentApprovalViewController.swift @@ -0,0 +1,991 @@ +// +// Copyright (c) 2017 Open Whisper Systems. All rights reserved. +// + +import Foundation +//import WebRTC +//import PromiseKit + +//// TODO: Add category so that button handlers can be defined where button is created. +//// TODO: Ensure buttons enabled & disabled as necessary. +//@objc(OWSAttachmentApprovalViewController) +class AttachmentApprovalViewController: UIViewController { + + let TAG = "[AttachmentApprovalViewController]" + + // Dependencies + +// let callUIAdapter: CallUIAdapter +// let contactsManager: OWSContactsManager + + // MARK: Properties + + let attachment : SignalAttachment! + +// var thread: TSContactThread! +// var call: SignalCall! +// var hasDismissed = false + + // MARK: Views + +// var hasConstraints = false +// var blurView: UIVisualEffectView! +// var dateFormatter: DateFormatter? +// +// // MARK: Contact Views +// +// var contactNameLabel: UILabel! +// var contactAvatarView: AvatarImageView! +// var callStatusLabel: UILabel! +// var callDurationTimer: Timer? +// +// // MARK: Ongoing Call Controls +// +// var ongoingCallView: UIView! +// +// var hangUpButton: UIButton! +// var speakerPhoneButton: UIButton! +// var audioModeMuteButton: UIButton! +// var audioModeVideoButton: UIButton! +// var videoModeMuteButton: UIButton! +// var videoModeVideoButton: UIButton! +// // TODO: Later, we'll re-enable the text message button +// // so users can send and read messages during a +// // call. +//// var textMessageButton: UIButton! +// +// // MARK: Incoming Call Controls +// +// var incomingCallView: UIView! +// +// var acceptIncomingButton: UIButton! +// var declineIncomingButton: UIButton! +// +// // MARK: Video Views +// +// var remoteVideoView: RTCEAGLVideoView! +// var localVideoView: RTCCameraPreviewView! +// weak var localVideoTrack: RTCVideoTrack? +// weak var remoteVideoTrack: RTCVideoTrack? +// var remoteVideoSize: CGSize! = CGSize.zero +// var remoteVideoConstraints: [NSLayoutConstraint] = [] +// var localVideoConstraints: [NSLayoutConstraint] = [] +// +// 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: Initializers + + required init?(coder aDecoder: NSCoder) { + self.attachment = SignalAttachment.genericAttachment(withData: nil, + dataUTI: kUTTypeContent as String) +// contactsManager = Environment.getCurrent().contactsManager +// callUIAdapter = Environment.getCurrent().callUIAdapter + super.init(coder: aDecoder) + // TODO: How to deprecate constructor in Swift? + assert(false) +// observeNotifications() + } + + required init(attachment : SignalAttachment!) { +// let attachmentData : NSData +// let attachmentType : String +// +// contactsManager = Environment.getCurrent().contactsManager +// callUIAdapter = Environment.getCurrent().callUIAdapter + self.attachment = attachment + super.init(nibName: nil, bundle: nil) +// observeNotifications() + } + +// func observeNotifications() { +// NotificationCenter.default.addObserver(self, +// selector:#selector(didBecomeActive), +// name:NSNotification.Name.UIApplicationDidBecomeActive, +// object:nil) +// } +// +// deinit { +// NotificationCenter.default.removeObserver(self) +// } +// +// func didBecomeActive() { +// 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) +// +// updateCallUI(callState: call.state) +// } +// + +// override func loadView() { +// self.view = +// } + + override func viewDidLoad() { + super.viewDidLoad() + + view.backgroundColor = UIColor.black +// +// guard let thread = self.thread else { +// Logger.error("\(TAG) tried to show call call without specifying thread.") +// showCallFailed(error: OWSErrorMakeAssertionError()) +// return +// } + + createViews() + +// contactNameLabel.text = contactsManager.displayName(forPhoneIdentifier: thread.contactIdentifier()) +// contactAvatarView.image = OWSAvatarBuilder.buildImage(for: thread, contactsManager: contactsManager) +// +// assert(call != nil) +// // Subscribe for future call updates +// call.addObserverAndSyncState(observer: self) +// +// Environment.getCurrent().callService.addObserverAndSyncState(observer:self) + } + + // MARK: - Create Views + + func createViews() { +// self.view.isUserInteractionEnabled = true +// self.view.addGestureRecognizer(OWSAnyTouchGestureRecognizer(target:self, +// action:#selector(didTouchRootView))) +// +// // 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() + } + +// func didTouchRootView(sender: UIGestureRecognizer) { +// if !remoteVideoView.isHidden { +// shouldRemoteVideoControlsBeHidden = !shouldRemoteVideoControlsBeHidden +// } +// } +// +// func createVideoViews() { +// remoteVideoView = RTCEAGLVideoView() +// remoteVideoView.delegate = self +// remoteVideoView.isUserInteractionEnabled = false +// localVideoView = RTCCameraPreviewView() +// remoteVideoView.isHidden = true +// localVideoView.isHidden = true +// self.view.addSubview(remoteVideoView) +// self.view.addSubview(localVideoView) +// } +// +// func createContactViews() { +// contactNameLabel = UILabel() +// contactNameLabel.font = UIFont.ows_lightFont(withSize:ScaleFromIPhone5To7Plus(32, 40)) +// 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_regularFont(withSize:ScaleFromIPhone5To7Plus(19, 25)) +// callStatusLabel.textColor = UIColor.white +// callStatusLabel.layer.shadowOffset = CGSize.zero +// callStatusLabel.layer.shadowOpacity = 0.35 +// callStatusLabel.layer.shadowRadius = 4 +// self.view.addSubview(callStatusLabel) +// +// contactAvatarView = AvatarImageView() +// self.view.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 buttonFont = UIFont.ows_regularFont(withSize:ScaleFromIPhone5To7Plus(14, 18)) +// let buttonCornerRadius = CGFloat(4) +// let descriptionVSpacingHeight = ScaleFromIPhone5To7Plus(30, 60) +// +// let callSettingsButton = UIButton() +// callSettingsButton.setTitle(NSLocalizedString("CALL_VIEW_SETTINGS_NAG_SHOW_CALL_SETTINGS", +// comment: "Label for button that shows the privacy settings"), for:.normal) +// callSettingsButton.setTitleColor(UIColor.white, for:.normal) +// callSettingsButton.titleLabel!.font = buttonFont +// callSettingsButton.addTarget(self, action:#selector(didPressShowCallSettings), for:.touchUpInside) +// callSettingsButton.backgroundColor = UIColor.ows_signalBrandBlue() +// callSettingsButton.layer.cornerRadius = buttonCornerRadius +// callSettingsButton.clipsToBounds = true +// viewStack.addSubview(callSettingsButton) +// callSettingsButton.autoSetDimension(.height, toSize:buttonHeight) +// callSettingsButton.autoPinWidthToSuperview() +// callSettingsButton.autoPinEdge(.top, to:.bottom, of:settingsNagDescriptionLabel, withOffset:descriptionVSpacingHeight) +// +// let notNowButton = UIButton() +// notNowButton.setTitle(NSLocalizedString("CALL_VIEW_SETTINGS_NAG_NOT_NOW_BUTTON", +// comment: "Label for button that dismiss the call view's settings nag."), for:.normal) +// notNowButton.setTitleColor(UIColor.white, for:.normal) +// notNowButton.titleLabel!.font = buttonFont +// notNowButton.addTarget(self, action:#selector(didPressDismissNag), for:.touchUpInside) +// notNowButton.backgroundColor = UIColor.ows_signalBrandBlue() +// notNowButton.layer.cornerRadius = buttonCornerRadius +// notNowButton.clipsToBounds = true +// 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() { +// +//// textMessageButton = createButton(imageName:"message-active-wide", +//// action:#selector(didPressTextMessage)) +// speakerPhoneButton = createButton(imageName:"speaker-inactive-wide", +// action:#selector(didPressSpeakerphone)) +// hangUpButton = createButton(imageName:"hangup-active-wide", +// action:#selector(didPressHangup)) +// audioModeMuteButton = createButton(imageName:"mute-unselected-wide", +// action:#selector(didPressMute)) +// videoModeMuteButton = createButton(imageName:"video-mute-unselected", +// action:#selector(didPressMute)) +// audioModeVideoButton = createButton(imageName:"video-inactive-wide", +// action:#selector(didPressVideo)) +// videoModeVideoButton = createButton(imageName:"video-video-unselected", +// action:#selector(didPressVideo)) +// +// setButtonSelectedImage(button: audioModeMuteButton, imageName: "mute-selected-wide") +// setButtonSelectedImage(button: videoModeMuteButton, imageName: "video-mute-selected") +// setButtonSelectedImage(button: audioModeVideoButton, imageName: "video-active-wide") +// setButtonSelectedImage(button: videoModeVideoButton, imageName: "video-video-selected") +// setButtonSelectedImage(button: speakerPhoneButton, imageName: "speaker-active-wide") +// +// ongoingCallView = createContainerForCallControls(controlGroups : [ +// [audioModeMuteButton, speakerPhoneButton, audioModeVideoButton ], +// [videoModeMuteButton, hangUpButton, videoModeVideoButton ] +// ]) +// } +// +// func setButtonSelectedImage(button: UIButton, imageName: String) { +// let image = UIImage(named:imageName) +// assert(image != nil) +// button.setImage(image, for:.selected) +// } +// +// func createIncomingCallControls() { +// +// acceptIncomingButton = createButton(imageName:"call-active-wide", +// action:#selector(didPressAnswerCall)) +// declineIncomingButton = createButton(imageName:"hangup-active-wide", +// action:#selector(didPressDeclineCall)) +// +// incomingCallView = createContainerForCallControls(controlGroups : [ +// [acceptIncomingButton, declineIncomingButton ] +// ]) +// } +// +// func createContainerForCallControls(controlGroups: [[UIView]]) -> UIView { +// let containerView = UIView() +// self.view.addSubview(containerView) +// var rows: [UIView] = [] +// for controlGroup in controlGroups { +// rows.append(rowWithSubviews(subviews:controlGroup)) +// } +// let rowspacing = ScaleFromIPhone5To7Plus(6, 7) +// var prevRow: UIView? +// for row in rows { +// containerView.addSubview(row) +// row.autoHCenterInSuperview() +// if prevRow != nil { +// row.autoPinEdge(.top, to:.bottom, of:prevRow!, withOffset:rowspacing) +// } +// prevRow = row +// } +// +// containerView.setContentHuggingVerticalHigh() +// rows.first!.autoPinEdge(toSuperviewEdge:.top) +// rows.last!.autoPinEdge(toSuperviewEdge:.bottom) +// return containerView +// } +// +// func createButton(imageName: String, action: Selector) -> UIButton { +// let image = UIImage(named:imageName) +// assert(image != nil) +// 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 +// } +// +// // Creates a row containing a given set of subviews. +// func rowWithSubviews(subviews: [UIView]) -> UIView { +// let row = UIView() +// row.setContentHuggingVerticalHigh() +// row.autoSetDimension(.height, toSize:buttonSize()) +// +// if subviews.count > 1 { +// // If there's more than one subview in the row, +// // space them evenly within the row. +// var lastSubview: UIView? +// for subview in subviews { +// row.addSubview(subview) +// subview.setContentHuggingHorizontalHigh() +// subview.autoVCenterInSuperview() +// +// if lastSubview != nil { +// let spacer = UIView() +// spacer.isHidden = true +// row.addSubview(spacer) +// spacer.autoPinEdge(.left, to:.right, of:lastSubview!) +// spacer.autoPinEdge(.right, to:.left, of:subview) +// spacer.setContentHuggingHorizontalLow() +// spacer.autoVCenterInSuperview() +// +// if subviews.count == 2 { +// // special case to hardcode the spacer's size when there is only 1 spacer. +// spacer.autoSetDimension(.width, toSize: ScaleFromIPhone5To7Plus(46, 60)) +// } else { +// spacer.autoSetDimension(.width, toSize: ScaleFromIPhone5To7Plus(3, 5)) +// } +// } +// +// lastSubview = subview +// } +// subviews.first!.autoPinEdge(toSuperviewEdge:.left) +// subviews.last!.autoPinEdge(toSuperviewEdge:.right) +// } else if subviews.count == 1 { +// // If there's only one subview in this row, center it. +// let subview = subviews.first! +// row.addSubview(subview) +// subview.autoVCenterInSuperview() +// subview.autoPinWidthToSuperview() +// } +// +// return row +// } +// +// // MARK: - Layout +// +// override func updateViewConstraints() { +// if !hasConstraints { +// // We only want to create our constraints once. +// // +// // Note that constraints are also created elsewhere. +// // This only creates the constraints for the top-level contents of the view. +// hasConstraints = true +// +// let topMargin = CGFloat(40) +// let contactHMargin = CGFloat(30) +// let contactVSpacing = CGFloat(3) +// let ongoingHMargin = ScaleFromIPhone5To7Plus(46, 72) +// let incomingHMargin = ScaleFromIPhone5To7Plus(46, 72) +// let settingsNagHMargin = CGFloat(30) +// let ongoingBottomMargin = ScaleFromIPhone5To7Plus(23, 41) +// 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() +// +// localVideoView.autoPinEdge(toSuperviewEdge:.right, withInset:videoPreviewHMargin) +// localVideoView.autoPinEdge(toSuperviewEdge:.top, withInset:topMargin) +// let localVideoSize = ScaleFromIPhone5To7Plus(80, 100) +// localVideoView.autoSetDimension(.width, toSize:localVideoSize) +// localVideoView.autoSetDimension(.height, toSize:localVideoSize) +// +// contactNameLabel.autoPinEdge(toSuperviewEdge:.top, withInset:topMargin) +// contactNameLabel.autoPinEdge(toSuperviewEdge:.left, withInset:contactHMargin) +// contactNameLabel.setContentHuggingVerticalHigh() +// +// callStatusLabel.autoPinEdge(.top, to:.bottom, of:contactNameLabel, withOffset:contactVSpacing) +// callStatusLabel.autoPinEdge(toSuperviewEdge:.left, withInset:contactHMargin) +// callStatusLabel.setContentHuggingVerticalHigh() +// +// contactAvatarView.autoPinEdge(.top, to:.bottom, of:callStatusLabel, withOffset:+avatarTopSpacing) +// contactAvatarView.autoPinEdge(.bottom, to:.top, of:ongoingCallView, withOffset:-avatarBottomSpacing) +// contactAvatarView.autoHCenterInSuperview() +// // Stretch that avatar to fill the available space. +// contactAvatarView.setContentHuggingLow() +// contactAvatarView.setCompressionResistanceLow() +// // Preserve square aspect ratio of contact avatar. +// contactAvatarView.autoMatch(.width, to:.height, of:contactAvatarView) +// +// // Ongoing call controls +// ongoingCallView.autoPinEdge(toSuperviewEdge:.bottom, withInset:ongoingBottomMargin) +// ongoingCallView.autoPinWidthToSuperview(withMargin:ongoingHMargin) +// ongoingCallView.setContentHuggingVerticalHigh() +// +// // Incoming call controls +// incomingCallView.autoPinEdge(toSuperviewEdge:.bottom, withInset:incomingBottomMargin) +// incomingCallView.autoPinWidthToSuperview(withMargin:incomingHMargin) +// incomingCallView.setContentHuggingVerticalHigh() +// +// // Settings nag views +// settingsNagView.autoPinEdge(toSuperviewEdge:.bottom, withInset:settingsNagBottomMargin) +// settingsNagView.autoPinWidthToSuperview(withMargin:settingsNagHMargin) +// settingsNagView.autoPinEdge(.top, to:.bottom, of:callStatusLabel) +// } +// +// updateRemoteVideoLayout() +// updateLocalVideoLayout() +// +// super.updateViewConstraints() +// } +// +// internal func updateRemoteVideoLayout() { +// NSLayoutConstraint.deactivate(self.remoteVideoConstraints) +// +// var constraints: [NSLayoutConstraint] = [] +// +// // We fill the screen with the remote video. The remote video's +// // aspect ratio may not (and in fact will very rarely) match the +// // aspect ratio of the current device, so parts of the remote +// // video will be hidden offscreen. +// // +// // It's better to trim the remote video than to adopt a letterboxed +// // layout. +// if remoteVideoSize.width > 0 && remoteVideoSize.height > 0 && +// self.view.bounds.size.width > 0 && self.view.bounds.size.height > 0 { +// +// var remoteVideoWidth = self.view.bounds.size.width +// var remoteVideoHeight = self.view.bounds.size.height +// if remoteVideoSize.width / self.view.bounds.size.width > remoteVideoSize.height / self.view.bounds.size.height { +// remoteVideoWidth = round(self.view.bounds.size.height * remoteVideoSize.width / remoteVideoSize.height) +// } else { +// remoteVideoHeight = round(self.view.bounds.size.width * remoteVideoSize.height / remoteVideoSize.width) +// } +// constraints.append(remoteVideoView.autoSetDimension(.width, toSize:remoteVideoWidth)) +// constraints.append(remoteVideoView.autoSetDimension(.height, toSize:remoteVideoHeight)) +// constraints += remoteVideoView.autoCenterInSuperview() +// +// remoteVideoView.frame = CGRect(origin:CGPoint.zero, +// size:CGSize(width:remoteVideoWidth, +// height:remoteVideoHeight)) +// +// remoteVideoView.isHidden = false +// } else { +// constraints += remoteVideoView.autoPinEdgesToSuperviewEdges() +// remoteVideoView.isHidden = true +// } +// +// self.remoteVideoConstraints = constraints +// +// // We need to force relayout to occur immediately (and not +// // wait for a UIKit layout/render pass) or the remoteVideoView +// // (which presumably is updating its CALayer directly) will +// // ocassionally appear to have bad frames. +// remoteVideoView.setNeedsLayout() +// remoteVideoView.superview?.setNeedsLayout() +// remoteVideoView.layoutIfNeeded() +// remoteVideoView.superview?.layoutIfNeeded() +// +// updateCallUI(callState: call.state) +// } +// +// internal func updateLocalVideoLayout() { +// +// NSLayoutConstraint.deactivate(self.localVideoConstraints) +// +// var constraints: [NSLayoutConstraint] = [] +// +// if localVideoView.isHidden { +// let contactHMargin = CGFloat(30) +// constraints.append(contactNameLabel.autoPinEdge(toSuperviewEdge:.right, withInset:contactHMargin)) +// constraints.append(callStatusLabel.autoPinEdge(toSuperviewEdge:.right, withInset:contactHMargin)) +// } else { +// let spacing = CGFloat(10) +// constraints.append(contactNameLabel.autoPinEdge(.right, to:.left, of:localVideoView, withOffset:-spacing)) +// constraints.append(callStatusLabel.autoPinEdge(.right, to:.left, of:localVideoView, withOffset:-spacing)) +// } +// +// self.localVideoConstraints = constraints +// updateCallUI(callState: call.state) +// } +// +// // MARK: - Methods +// +// func showCallFailed(error: Error) { +// // TODO Show something in UI. +// Logger.error("\(TAG) 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: +// if let call = self.call { +// 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 = formattedDate.substring(from: 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 = formattedDate.substring(from: formattedDate.index(formattedDate.startIndex, offsetBy: 1)) +// } +// } +// return formattedDate +// } else { +// return NSLocalizedString("IN_CALL_TALKING", 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") +// } +// } +// +// func updateCallStatusLabel(callState: CallState) { +// assert(Thread.isMainThread) +// +// let text = String(format: CallStrings.callStatusFormat, +// localizedTextForCallState(callState)) +// self.callStatusLabel.text = text +// } +// +// func updateCallUI(callState: CallState) { +// assert(Thread.isMainThread) +// updateCallStatusLabel(callState: callState) +// +// if isShowingSettingsNag { +// settingsNagView.isHidden = false +// contactAvatarView.isHidden = true +// ongoingCallView.isHidden = true +// return +// } +// +// audioModeMuteButton.isSelected = call.isMuted +// videoModeMuteButton.isSelected = call.isMuted +// audioModeVideoButton.isSelected = call.hasLocalVideo +// videoModeVideoButton.isSelected = call.hasLocalVideo +// speakerPhoneButton.isSelected = call.isSpeakerphoneEnabled +// +// // Show Incoming vs. Ongoing call controls +// let isRinging = callState == .localRinging +// incomingCallView.isHidden = !isRinging +// incomingCallView.isUserInteractionEnabled = isRinging +// ongoingCallView.isHidden = isRinging +// ongoingCallView.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 +// for subview in [speakerPhoneButton, audioModeMuteButton, audioModeVideoButton] { +// subview?.isHidden = hasLocalVideo +// } +// for subview in [videoModeMuteButton, videoModeVideoButton] { +// subview?.isHidden = !hasLocalVideo +// } +// +// // Also hide other controls if user has tapped to hide them. +// if shouldRemoteVideoControlsBeHidden && !remoteVideoView.isHidden { +// contactNameLabel.isHidden = true +// callStatusLabel.isHidden = true +// ongoingCallView.isHidden = true +// } else { +// contactNameLabel.isHidden = false +// callStatusLabel.isHidden = false +// } +// +// // Dismiss Handling +// switch callState { +// case .remoteHangup, .remoteBusy, .localFailure: +// Logger.debug("\(TAG) dismissing after delay because new state is \(callState)") +// dismissIfPossible(shouldDelay:true) +// case .localHangup: +// Logger.debug("\(TAG) dismissing immediately from local hangup") +// dismissIfPossible(shouldDelay:false) +// default: break +// } +// +// if callState == .connected { +// if callDurationTimer == nil { +// let kDurationUpdateFrequencySeconds = 1 / 20.0 +// callDurationTimer = Timer.scheduledTimer(timeInterval: kDurationUpdateFrequencySeconds, +// target:self, +// selector:#selector(updateCallDuration), +// userInfo:nil, +// repeats:true) +// } +// } else { +// callDurationTimer?.invalidate() +// callDurationTimer = nil +// } +// } +// +// func updateCallDuration(timer: Timer?) { +// updateCallStatusLabel(callState: call.state) +// } +// +// // MARK: - Actions +// +// /** +// * Ends a connected call. Do not confuse with `didPressDeclineCall`. +// */ +// func didPressHangup(sender: UIButton) { +// Logger.info("\(TAG) called \(#function)") +// if let call = self.call { +// callUIAdapter.localHangupCall(call) +// } else { +// Logger.warn("\(TAG) hung up, but call was unexpectedly nil") +// } +// +// dismissIfPossible(shouldDelay:false) +// } +// +// func didPressMute(sender muteButton: UIButton) { +// Logger.info("\(TAG) called \(#function)") +// muteButton.isSelected = !muteButton.isSelected +// if let call = self.call { +// callUIAdapter.setIsMuted(call: call, isMuted: muteButton.isSelected) +// } else { +// Logger.warn("\(TAG) pressed mute, but call was unexpectedly nil") +// } +// } +// +// func didPressSpeakerphone(sender speakerphoneButton: UIButton) { +// Logger.info("\(TAG) called \(#function)") +// speakerphoneButton.isSelected = !speakerphoneButton.isSelected +// if let call = self.call { +// callUIAdapter.setIsSpeakerphoneEnabled(call: call, isEnabled: speakerphoneButton.isSelected) +// } else { +// Logger.warn("\(TAG) pressed mute, but call was unexpectedly nil") +// } +// } +// +// func didPressTextMessage(sender speakerphoneButton: UIButton) { +// Logger.info("\(TAG) called \(#function)") +// +// dismissIfPossible(shouldDelay:false) +// } +// +// func didPressAnswerCall(sender: UIButton) { +// Logger.info("\(TAG) called \(#function)") +// +// guard let call = self.call else { +// Logger.error("\(TAG) call was unexpectedly nil. Terminating call.") +// +// let text = String(format: CallStrings.callStatusFormat, +// NSLocalizedString("END_CALL_UNCATEGORIZED_FAILURE", comment: "Call setup status label")) +// self.callStatusLabel.text = text +// +// dismissIfPossible(shouldDelay:true) +// return +// } +// +// callUIAdapter.answerCall(call) +// } +// +// func didPressVideo(sender: UIButton) { +// Logger.info("\(TAG) called \(#function)") +// let hasLocalVideo = !sender.isSelected +// if let call = self.call { +// callUIAdapter.setHasLocalVideo(call: call, hasLocalVideo: hasLocalVideo) +// } else { +// Logger.warn("\(TAG) pressed video, but call was unexpectedly nil") +// } +// } +// +// /** +// * Denies an incoming not-yet-connected call, Do not confuse with `didPressHangup`. +// */ +// func didPressDeclineCall(sender: UIButton) { +// Logger.info("\(TAG) called \(#function)") +// +// if let call = self.call { +// callUIAdapter.declineCall(call) +// } else { +// Logger.warn("\(TAG) denied call, but call was unexpectedly nil") +// } +// +// dismissIfPossible(shouldDelay:false) +// } +// +// func didPressShowCallSettings(sender: UIButton) { +// Logger.info("\(TAG) called \(#function)") +// +// markSettingsNagAsComplete() +// +// dismissIfPossible(shouldDelay: false, ignoreNag: true, completion: { +// // Find the frontmost presented UIViewController from which to present the +// // settings views. +// let fromViewController = UIApplication.shared.frontmostViewController +// assert(fromViewController != nil) +// +// // Construct the "settings" view & push the "privacy settings" view. +// let navigationController = UIStoryboard.main.instantiateViewController(withIdentifier: "SettingsNavigationController") as! UINavigationController +// assert(navigationController.viewControllers.count == 1) +// let privacySettingsViewController = PrivacySettingsTableViewController() +// navigationController.pushViewController(privacySettingsViewController, animated:false) +// +// fromViewController?.present(navigationController, animated: true, completion: nil) +// }) +// } +// +// func didPressDismissNag(sender: UIButton) { +// Logger.info("\(TAG) called \(#function)") +// +// 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("\(TAG) called \(#function)") +// +// let preferences = Environment.getCurrent().preferences! +// +// preferences.setIsCallKitEnabled(preferences.isCallKitEnabled()) +// preferences.setIsCallKitPrivacyEnabled(preferences.isCallKitPrivacyEnabled()) +// } +// +// // MARK: - CallObserver +// +// internal func stateDidChange(call: SignalCall, state: CallState) { +// AssertIsOnMainThread() +// Logger.info("\(self.TAG) 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) +// } +// +// internal func speakerphoneDidChange(call: SignalCall, isEnabled: Bool) { +// AssertIsOnMainThread() +// self.updateCallUI(callState: call.state) +// } +// +// // MARK: - Video +// +// internal func updateLocalVideoTrack(localVideoTrack: RTCVideoTrack?) { +// AssertIsOnMainThread() +// guard self.localVideoTrack != localVideoTrack else { +// return +// } +// +// self.localVideoTrack = localVideoTrack +// +// var source: RTCAVFoundationVideoSource? +// if localVideoTrack?.source is RTCAVFoundationVideoSource { +// source = localVideoTrack?.source as! RTCAVFoundationVideoSource +// } +// localVideoView.captureSession = source?.captureSession +// let isHidden = source == nil +// Logger.info("\(TAG) \(#function) isHidden: \(isHidden)") +// localVideoView.isHidden = isHidden +// +// updateLocalVideoLayout() +// } +// +// internal func updateRemoteVideoTrack(remoteVideoTrack: RTCVideoTrack?) { +// AssertIsOnMainThread() +// guard self.remoteVideoTrack != remoteVideoTrack else { +// return +// } +// +// self.remoteVideoTrack?.remove(remoteVideoView) +// self.remoteVideoTrack = nil +// remoteVideoView.renderFrame(nil) +// self.remoteVideoTrack = remoteVideoTrack +// self.remoteVideoTrack?.add(remoteVideoView) +// shouldRemoteVideoControlsBeHidden = false +// +// if remoteVideoTrack == nil { +// remoteVideoSize = CGSize.zero +// } +// +// updateRemoteVideoLayout() +// } +// +// internal func dismissIfPossible(shouldDelay: Bool, ignoreNag: Bool = false, completion: (() -> Swift.Void)? = nil) { +// if hasDismissed { +// // Don't dismiss twice. +// return +// } else if !ignoreNag && +// call.direction == .incoming && +// UIDevice.current.supportsCallKit && +// (!Environment.getCurrent().preferences.isCallKitEnabled() || +// Environment.getCurrent().preferences.isCallKitPrivacyEnabled()) { +// +// isShowingSettingsNag = true +// +// // Update the nag view's copy to reflect the settings state. +// if Environment.getCurrent().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.getCurrent().preferences.isCallKitEnabledSet() || +// Environment.getCurrent().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.dismiss(animated: true, completion:completion) +// } +// } else { +// hasDismissed = true +// self.dismiss(animated: false, completion:completion) +// } +// } +// +// // MARK: - CallServiceObserver +// +// internal func didUpdateCall(call: SignalCall?) { +// // Do nothing. +// } +// +// internal func didUpdateVideoTracks(localVideoTrack: RTCVideoTrack?, +// remoteVideoTrack: RTCVideoTrack?) { +// AssertIsOnMainThread() +// +// updateLocalVideoTrack(localVideoTrack:localVideoTrack) +// updateRemoteVideoTrack(remoteVideoTrack:remoteVideoTrack) +// } +// +// // MARK: - RTCEAGLVideoViewDelegate +// +// internal func videoView(_ videoView: RTCEAGLVideoView, didChangeVideoSize size: CGSize) { +// AssertIsOnMainThread() +// +// if videoView != remoteVideoView { +// return +// } +// +// Logger.info("\(TAG) \(#function): \(size)") +// +// remoteVideoSize = size +// updateRemoteVideoLayout() +// } +} diff --git a/Signal/src/view controllers/MessageComposeTableViewController.h b/Signal/src/view controllers/MessageComposeTableViewController.h index 788f39ad6e..16ab58dfb2 100644 --- a/Signal/src/view controllers/MessageComposeTableViewController.h +++ b/Signal/src/view controllers/MessageComposeTableViewController.h @@ -7,10 +7,6 @@ // #import -#import -#import -#import -#import #import "Contact.h" #import "LocalizableText.h" diff --git a/Signal/src/view controllers/MessagesViewController.xib b/Signal/src/view controllers/MessagesViewController.xib new file mode 100644 index 0000000000..d4b3087a1f --- /dev/null +++ b/Signal/src/view controllers/MessagesViewController.xib @@ -0,0 +1,58 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Signal/src/view controllers/OWSConversationSettingsTableViewController.m b/Signal/src/view controllers/OWSConversationSettingsTableViewController.m index b5abbe19c0..9b24646eae 100644 --- a/Signal/src/view controllers/OWSConversationSettingsTableViewController.m +++ b/Signal/src/view controllers/OWSConversationSettingsTableViewController.m @@ -93,12 +93,7 @@ static NSString *const OWSConversationSettingsTableViewControllerSegueShowGroupM return self; } - _storageManager = [TSStorageManager sharedManager]; - _contactsManager = [Environment getCurrent].contactsManager; - _messageSender = [[OWSMessageSender alloc] initWithNetworkManager:[Environment getCurrent].networkManager - storageManager:_storageManager - contactsManager:_contactsManager - contactsUpdater:[Environment getCurrent].contactsUpdater]; + [self commonInit]; return self; } @@ -110,14 +105,30 @@ static NSString *const OWSConversationSettingsTableViewControllerSegueShowGroupM return self; } + [self commonInit]; + + return self; +} + +- (instancetype)initWithNibName:(nullable NSString *)nibNameOrNil bundle:(nullable NSBundle *)nibBundleOrNil { + self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]; + if (!self) { + return self; + } + + [self commonInit]; + + return self; +} + +- (void)commonInit +{ _storageManager = [TSStorageManager sharedManager]; _contactsManager = [Environment getCurrent].contactsManager; _messageSender = [[OWSMessageSender alloc] initWithNetworkManager:[Environment getCurrent].networkManager storageManager:_storageManager contactsManager:_contactsManager contactsUpdater:[Environment getCurrent].contactsUpdater]; - - return self; } - (void)configureWithThread:(TSThread *)thread diff --git a/Signal/src/view controllers/OWSMessagesToolbarContentView.xib b/Signal/src/view controllers/OWSMessagesToolbarContentView.xib new file mode 100644 index 0000000000..45937cf82c --- /dev/null +++ b/Signal/src/view controllers/OWSMessagesToolbarContentView.xib @@ -0,0 +1,71 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Signal/src/view controllers/SignalsViewController.m b/Signal/src/view controllers/SignalsViewController.m index 7c442d6bdf..4fbacad5c8 100644 --- a/Signal/src/view controllers/SignalsViewController.m +++ b/Signal/src/view controllers/SignalsViewController.m @@ -564,8 +564,8 @@ NSString *const SignalsViewControllerSegueShowIncomingCall = @"ShowIncomingCallS - (void)presentThread:(TSThread *)thread keyboardOnViewAppearing:(BOOL)keyboardOnViewAppearing { dispatch_async(dispatch_get_main_queue(), ^{ - MessagesViewController *mvc = [[UIStoryboard storyboardWithName:AppDelegateStoryboardMain bundle:NULL] - instantiateViewControllerWithIdentifier:@"MessagesViewController"]; + MessagesViewController *mvc = [[MessagesViewController alloc] initWithNibName:@"MessagesViewController" + bundle:nil]; if (self.presentedViewController) { [self.presentedViewController dismissViewControllerAnimated:YES completion:nil];