Strip out special casing for pre-iOS 9 users.
This commit is contained in:
parent
44e38709d6
commit
99aedca45f
|
@ -11,7 +11,6 @@ import UIKit
|
|||
import Contacts
|
||||
import SignalServiceKit
|
||||
|
||||
@available(iOS 9.0, *)
|
||||
public protocol ContactsPickerDelegate {
|
||||
func contactsPicker(_: ContactsPicker, didContactFetchFailed error: NSError)
|
||||
func contactsPicker(_: ContactsPicker, didCancel error: NSError)
|
||||
|
@ -20,7 +19,6 @@ public protocol ContactsPickerDelegate {
|
|||
func contactsPicker(_: ContactsPicker, shouldSelectContact contact: Contact) -> Bool
|
||||
}
|
||||
|
||||
@available(iOS 9.0, *)
|
||||
public extension ContactsPickerDelegate {
|
||||
func contactsPicker(_: ContactsPicker, didContactFetchFailed error: NSError) { }
|
||||
func contactsPicker(_: ContactsPicker, didCancel error: NSError) { }
|
||||
|
@ -34,7 +32,6 @@ public enum SubtitleCellValue {
|
|||
case email
|
||||
}
|
||||
|
||||
@available(iOS 9.0, *)
|
||||
open class ContactsPicker: OWSViewController, UITableViewDelegate, UITableViewDataSource, UISearchBarDelegate {
|
||||
|
||||
@IBOutlet var tableView: UITableView!
|
||||
|
@ -336,10 +333,8 @@ open class ContactsPicker: OWSViewController, UITableViewDelegate, UITableViewDa
|
|||
}
|
||||
}
|
||||
|
||||
@available(iOS 9.0, *)
|
||||
let ContactSortOrder = computeSortOrder()
|
||||
|
||||
@available(iOS 9.0, *)
|
||||
func computeSortOrder() -> CNContactSortOrder {
|
||||
let comparator = CNContact.comparator(forNameSortOrder: .userDefault)
|
||||
|
||||
|
@ -360,7 +355,6 @@ func computeSortOrder() -> CNContactSortOrder {
|
|||
}
|
||||
}
|
||||
|
||||
@available(iOS 9.0, *)
|
||||
fileprivate extension CNContact {
|
||||
/**
|
||||
* Sorting Key used by collation
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
//
|
||||
// Copyright (c) 2017 Open Whisper Systems. All rights reserved.
|
||||
// Copyright (c) 2018 Open Whisper Systems. All rights reserved.
|
||||
//
|
||||
|
||||
#import "ConversationInputTextView.h"
|
||||
|
@ -215,14 +215,10 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
action:(SEL)action
|
||||
discoverabilityTitle:(NSString *)discoverabilityTitle
|
||||
{
|
||||
if (SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(9, 0)) {
|
||||
return [UIKeyCommand keyCommandWithInput:input
|
||||
modifierFlags:modifierFlags
|
||||
action:action
|
||||
discoverabilityTitle:discoverabilityTitle];
|
||||
} else {
|
||||
return [UIKeyCommand keyCommandWithInput:input modifierFlags:modifierFlags action:action];
|
||||
}
|
||||
return [UIKeyCommand keyCommandWithInput:input
|
||||
modifierFlags:modifierFlags
|
||||
action:action
|
||||
discoverabilityTitle:discoverabilityTitle];
|
||||
}
|
||||
|
||||
- (void)modifiedReturnPressed:(UIKeyCommand *)sender
|
||||
|
|
|
@ -51,7 +51,7 @@ private class IntroductingReadReceiptsExperienceUpgradeViewController: Experienc
|
|||
|
||||
// Construct the "settings" view & push the "privacy settings" view.
|
||||
let navigationController = AppSettingsViewController.inModalNavigationController()
|
||||
navigationController.pushViewController(PrivacySettingsTableViewController(), animated:false)
|
||||
navigationController.pushViewController(PrivacySettingsTableViewController(), animated: false)
|
||||
|
||||
fromViewController.present(navigationController, animated: true)
|
||||
}
|
||||
|
@ -96,7 +96,7 @@ private class IntroductingReadReceiptsExperienceUpgradeViewController: Experienc
|
|||
button.setTitle(title, for: .normal)
|
||||
button.setTitleColor(UIColor.ows_signalBrandBlue, for: .normal)
|
||||
button.isUserInteractionEnabled = true
|
||||
button.addTarget(self, action:#selector(didTapButton), for: .touchUpInside)
|
||||
button.addTarget(self, action: #selector(didTapButton), for: .touchUpInside)
|
||||
button.contentEdgeInsets = UIEdgeInsets(top: 10, left: 10, bottom: 10, right: 10)
|
||||
button.titleLabel?.textAlignment = .center
|
||||
button.titleLabel?.font = UIFont.ows_mediumFont(withSize: ScaleFromIPhone5(18))
|
||||
|
@ -201,7 +201,7 @@ private class IntroductingProfilesExperienceUpgradeViewController: ExperienceUpg
|
|||
button.backgroundColor = UIColor.ows_materialBlue
|
||||
|
||||
button.isUserInteractionEnabled = true
|
||||
button.addTarget(self, action:#selector(didTapButton), for: .touchUpInside)
|
||||
button.addTarget(self, action: #selector(didTapButton), for: .touchUpInside)
|
||||
button.contentEdgeInsets = UIEdgeInsets(top: 10, left: 20, bottom: 10, right: 20)
|
||||
|
||||
button.titleLabel?.font = UIFont.ows_mediumFont(withSize: ScaleFromIPhone5(18))
|
||||
|
@ -261,7 +261,7 @@ private class CallKitExperienceUpgradeViewController: ExperienceUpgradeViewContr
|
|||
privacySettingsButton.setTitle(privacyTitle, for: .normal)
|
||||
privacySettingsButton.setTitleColor(UIColor.ows_signalBrandBlue, for: .normal)
|
||||
privacySettingsButton.isUserInteractionEnabled = true
|
||||
privacySettingsButton.addTarget(self, action:#selector(didTapPrivacySettingsButton), for: .touchUpInside)
|
||||
privacySettingsButton.addTarget(self, action: #selector(didTapPrivacySettingsButton), for: .touchUpInside)
|
||||
privacySettingsButton.titleLabel?.font = bodyLabel.font
|
||||
|
||||
// Privacy Settings Button layout
|
||||
|
@ -282,7 +282,7 @@ private class CallKitExperienceUpgradeViewController: ExperienceUpgradeViewContr
|
|||
|
||||
// Construct the "settings" view & push the "privacy settings" view.
|
||||
let navigationController = AppSettingsViewController.inModalNavigationController()
|
||||
navigationController.pushViewController(PrivacySettingsTableViewController(), animated:false)
|
||||
navigationController.pushViewController(PrivacySettingsTableViewController(), animated: false)
|
||||
|
||||
fromViewController?.present(navigationController, animated: true, completion: nil)
|
||||
}
|
||||
|
@ -365,13 +365,9 @@ private class ExperienceUpgradeViewController: OWSViewController {
|
|||
}
|
||||
|
||||
func setPageControlAppearance() {
|
||||
if #available(iOS 9.0, *) {
|
||||
let pageControl = UIPageControl.appearance(whenContainedInInstancesOf: [UIPageViewController.self])
|
||||
pageControl.pageIndicatorTintColor = UIColor.lightGray
|
||||
pageControl.currentPageIndicatorTintColor = UIColor.ows_materialBlue
|
||||
} else {
|
||||
// iOS8 won't see the page controls =(
|
||||
}
|
||||
let pageControl = UIPageControl.appearance(whenContainedInInstancesOf: [UIPageViewController.self])
|
||||
pageControl.pageIndicatorTintColor = UIColor.lightGray
|
||||
pageControl.currentPageIndicatorTintColor = UIColor.ows_materialBlue
|
||||
}
|
||||
|
||||
class ExperienceUpgradesPageViewController: OWSViewController, UIPageViewControllerDataSource {
|
||||
|
@ -390,7 +386,7 @@ class ExperienceUpgradesPageViewController: OWSViewController, UIPageViewControl
|
|||
self.experienceUpgrades = experienceUpgrades
|
||||
|
||||
setPageControlAppearance()
|
||||
self.pageViewController = UIPageViewController(transitionStyle: .scroll, navigationOrientation:.horizontal, options: nil)
|
||||
self.pageViewController = UIPageViewController(transitionStyle: .scroll, navigationOrientation: .horizontal, options: nil)
|
||||
super.init(nibName: nil, bundle: nil)
|
||||
self.pageViewController.dataSource = self
|
||||
|
||||
|
@ -402,7 +398,7 @@ class ExperienceUpgradesPageViewController: OWSViewController, UIPageViewControl
|
|||
assert(false)
|
||||
// This should never happen, but so as not to explode we give some bogus data
|
||||
self.experienceUpgrades = [ExperienceUpgrade()]
|
||||
self.pageViewController = UIPageViewController(transitionStyle: .scroll, navigationOrientation:.horizontal, options: nil)
|
||||
self.pageViewController = UIPageViewController(transitionStyle: .scroll, navigationOrientation: .horizontal, options: nil)
|
||||
super.init(coder: aDecoder)
|
||||
self.pageViewController.dataSource = self
|
||||
}
|
||||
|
@ -441,7 +437,7 @@ class ExperienceUpgradesPageViewController: OWSViewController, UIPageViewControl
|
|||
dismissButton.setTitle(CommonStrings.dismissButton, for: .normal)
|
||||
dismissButton.setTitleColor(UIColor.ows_signalBrandBlue, for: .normal)
|
||||
dismissButton.isUserInteractionEnabled = true
|
||||
dismissButton.addTarget(self, action:#selector(didTapDismissButton), for: .touchUpInside)
|
||||
dismissButton.addTarget(self, action: #selector(didTapDismissButton), for: .touchUpInside)
|
||||
dismissButton.titleLabel?.font = UIFont.ows_mediumFont(withSize: ScaleFromIPhone5(16))
|
||||
let dismissInsetValue: CGFloat = ScaleFromIPhone5(10)
|
||||
dismissButton.contentEdgeInsets = UIEdgeInsets(top: dismissInsetValue, left: dismissInsetValue, bottom: dismissInsetValue, right: dismissInsetValue)
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
//
|
||||
// Copyright (c) 2017 Open Whisper Systems. All rights reserved.
|
||||
// Copyright (c) 2018 Open Whisper Systems. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
@ -34,14 +34,12 @@ class InviteFlow: NSObject, MFMessageComposeViewControllerDelegate, MFMailCompos
|
|||
|
||||
actionSheetController.addAction(dismissAction())
|
||||
|
||||
if #available(iOS 9.0, *) {
|
||||
if let messageAction = messageAction() {
|
||||
actionSheetController.addAction(messageAction)
|
||||
}
|
||||
if let messageAction = messageAction() {
|
||||
actionSheetController.addAction(messageAction)
|
||||
}
|
||||
|
||||
if let mailAction = mailAction() {
|
||||
actionSheetController.addAction(mailAction)
|
||||
}
|
||||
if let mailAction = mailAction() {
|
||||
actionSheetController.addAction(mailAction)
|
||||
}
|
||||
|
||||
if let tweetAction = tweetAction() {
|
||||
|
@ -66,14 +64,14 @@ class InviteFlow: NSObject, MFMessageComposeViewControllerDelegate, MFMailCompos
|
|||
return nil
|
||||
}
|
||||
|
||||
let tweetString = NSLocalizedString("SETTINGS_INVITE_TWITTER_TEXT", comment:"content of tweet when inviting via twitter")
|
||||
let tweetString = NSLocalizedString("SETTINGS_INVITE_TWITTER_TEXT", comment: "content of tweet when inviting via twitter")
|
||||
twitterViewController.setInitialText(tweetString)
|
||||
|
||||
let tweetUrl = URL(string: installUrl)
|
||||
twitterViewController.add(tweetUrl)
|
||||
twitterViewController.add(#imageLiteral(resourceName: "twitter_sharing_image"))
|
||||
|
||||
let tweetTitle = NSLocalizedString("SHARE_ACTION_TWEET", comment:"action sheet item")
|
||||
let tweetTitle = NSLocalizedString("SHARE_ACTION_TWEET", comment: "action sheet item")
|
||||
return UIAlertAction(title: tweetTitle, style: .default) { _ in
|
||||
Logger.debug("\(self.TAG) Chose tweet")
|
||||
|
||||
|
@ -87,7 +85,6 @@ class InviteFlow: NSObject, MFMessageComposeViewControllerDelegate, MFMailCompos
|
|||
|
||||
// MARK: ContactsPickerDelegate
|
||||
|
||||
@available(iOS 9.0, *)
|
||||
func contactsPicker(_: ContactsPicker, didSelectMultipleContacts contacts: [Contact]) {
|
||||
Logger.debug("\(TAG) didSelectContacts:\(contacts)")
|
||||
|
||||
|
@ -108,7 +105,6 @@ class InviteFlow: NSObject, MFMessageComposeViewControllerDelegate, MFMailCompos
|
|||
}
|
||||
}
|
||||
|
||||
@available(iOS 9.0, *)
|
||||
func contactsPicker(_: ContactsPicker, shouldSelectContact contact: Contact) -> Bool {
|
||||
guard let inviteChannel = channel else {
|
||||
Logger.error("\(TAG) unexpected nil channel in contact picker.")
|
||||
|
@ -128,7 +124,6 @@ class InviteFlow: NSObject, MFMessageComposeViewControllerDelegate, MFMailCompos
|
|||
|
||||
// MARK: SMS
|
||||
|
||||
@available(iOS 9.0, *)
|
||||
func messageAction() -> UIAlertAction? {
|
||||
guard MFMessageComposeViewController.canSendText() else {
|
||||
Logger.info("\(TAG) Device cannot send text")
|
||||
|
@ -164,9 +159,9 @@ class InviteFlow: NSObject, MFMessageComposeViewControllerDelegate, MFMailCompos
|
|||
messageComposeViewController.messageComposeDelegate = self
|
||||
messageComposeViewController.recipients = phoneNumbers
|
||||
|
||||
let inviteText = NSLocalizedString("SMS_INVITE_BODY", comment:"body sent to contacts when inviting to Install Signal")
|
||||
let inviteText = NSLocalizedString("SMS_INVITE_BODY", comment: "body sent to contacts when inviting to Install Signal")
|
||||
messageComposeViewController.body = inviteText.appending(" \(self.installUrl)")
|
||||
self.presentingViewController.navigationController?.present(messageComposeViewController, animated:true)
|
||||
self.presentingViewController.navigationController?.present(messageComposeViewController, animated: true)
|
||||
}
|
||||
|
||||
// MARK: MessageComposeViewControllerDelegate
|
||||
|
@ -178,7 +173,7 @@ class InviteFlow: NSObject, MFMessageComposeViewControllerDelegate, MFMailCompos
|
|||
|
||||
switch result {
|
||||
case .failed:
|
||||
let warning = UIAlertController(title: nil, message: NSLocalizedString("SEND_INVITE_FAILURE", comment:"Alert body after invite failed"), preferredStyle: .alert)
|
||||
let warning = UIAlertController(title: nil, message: NSLocalizedString("SEND_INVITE_FAILURE", comment: "Alert body after invite failed"), preferredStyle: .alert)
|
||||
warning.addAction(UIAlertAction(title: CommonStrings.dismissButton, style: .default, handler: nil))
|
||||
self.presentingViewController.present(warning, animated: true, completion: nil)
|
||||
case .sent:
|
||||
|
@ -190,7 +185,6 @@ class InviteFlow: NSObject, MFMessageComposeViewControllerDelegate, MFMailCompos
|
|||
|
||||
// MARK: Mail
|
||||
|
||||
@available(iOS 9.0, *)
|
||||
func mailAction() -> UIAlertAction? {
|
||||
guard MFMailComposeViewController.canSendMail() else {
|
||||
Logger.info("\(TAG) Device cannot send mail")
|
||||
|
@ -208,21 +202,20 @@ class InviteFlow: NSObject, MFMessageComposeViewControllerDelegate, MFMailCompos
|
|||
}
|
||||
}
|
||||
|
||||
@available(iOS 9.0, *)
|
||||
func sendMailTo(emails recipientEmails: [String]) {
|
||||
let mailComposeViewController = MFMailComposeViewController()
|
||||
mailComposeViewController.mailComposeDelegate = self
|
||||
|
||||
mailComposeViewController.setBccRecipients(recipientEmails)
|
||||
|
||||
let subject = NSLocalizedString("EMAIL_INVITE_SUBJECT", comment:"subject of email sent to contacts when inviting to install Signal")
|
||||
let bodyFormat = NSLocalizedString("EMAIL_INVITE_BODY", comment:"body of email sent to contacts when inviting to install Signal. Embeds {{link to install Signal}} and {{link to WhisperSystems home page}}")
|
||||
let subject = NSLocalizedString("EMAIL_INVITE_SUBJECT", comment: "subject of email sent to contacts when inviting to install Signal")
|
||||
let bodyFormat = NSLocalizedString("EMAIL_INVITE_BODY", comment: "body of email sent to contacts when inviting to install Signal. Embeds {{link to install Signal}} and {{link to WhisperSystems home page}}")
|
||||
let body = String.init(format: bodyFormat, installUrl, homepageUrl)
|
||||
mailComposeViewController.setSubject(subject)
|
||||
mailComposeViewController.setMessageBody(body, isHTML: false)
|
||||
|
||||
self.presentingViewController.dismiss(animated: true) {
|
||||
self.presentingViewController.navigationController?.present(mailComposeViewController, animated:true) {
|
||||
self.presentingViewController.navigationController?.present(mailComposeViewController, animated: true) {
|
||||
UIUtil.applySignalAppearence()
|
||||
}
|
||||
}
|
||||
|
@ -235,7 +228,7 @@ class InviteFlow: NSObject, MFMessageComposeViewControllerDelegate, MFMailCompos
|
|||
|
||||
switch result {
|
||||
case .failed:
|
||||
let warning = UIAlertController(title: nil, message: NSLocalizedString("SEND_INVITE_FAILURE", comment:"Alert body after invite failed"), preferredStyle: .alert)
|
||||
let warning = UIAlertController(title: nil, message: NSLocalizedString("SEND_INVITE_FAILURE", comment: "Alert body after invite failed"), preferredStyle: .alert)
|
||||
warning.addAction(UIAlertAction(title: CommonStrings.dismissButton, style: .default, handler: nil))
|
||||
self.presentingViewController.present(warning, animated: true, completion: nil)
|
||||
case .sent:
|
||||
|
|
|
@ -312,18 +312,16 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
[self applyInitialMediaViewConstraints];
|
||||
|
||||
if (self.isVideo) {
|
||||
if (@available(iOS 9, *)) {
|
||||
PlayerProgressBar *videoProgressBar = [PlayerProgressBar new];
|
||||
videoProgressBar.delegate = self;
|
||||
videoProgressBar.player = self.videoPlayer.avPlayer;
|
||||
PlayerProgressBar *videoProgressBar = [PlayerProgressBar new];
|
||||
videoProgressBar.delegate = self;
|
||||
videoProgressBar.player = self.videoPlayer.avPlayer;
|
||||
|
||||
self.videoProgressBar = videoProgressBar;
|
||||
[self.view addSubview:videoProgressBar];
|
||||
[videoProgressBar autoPinWidthToSuperview];
|
||||
[videoProgressBar autoPinToTopLayoutGuideOfViewController:self withInset:0];
|
||||
CGFloat kVideoProgressBarHeight = 44;
|
||||
[videoProgressBar autoSetDimension:ALDimensionHeight toSize:kVideoProgressBarHeight];
|
||||
}
|
||||
self.videoProgressBar = videoProgressBar;
|
||||
[self.view addSubview:videoProgressBar];
|
||||
[videoProgressBar autoPinWidthToSuperview];
|
||||
[videoProgressBar autoPinToTopLayoutGuideOfViewController:self withInset:0];
|
||||
CGFloat kVideoProgressBarHeight = 44;
|
||||
[videoProgressBar autoSetDimension:ALDimensionHeight toSize:kVideoProgressBarHeight];
|
||||
|
||||
UIButton *playVideoButton = [UIButton new];
|
||||
self.playVideoButton = playVideoButton;
|
||||
|
@ -380,16 +378,13 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
]];
|
||||
|
||||
if (self.isVideo) {
|
||||
// bar button video controls only work on iOS9+
|
||||
if (@available(iOS 9.0, *)) {
|
||||
UIBarButtonItem *playerButton = isPlayingVideo ? self.videoPauseBarButton : self.videoPlayBarButton;
|
||||
[toolbarItems addObjectsFromArray:@[
|
||||
playerButton,
|
||||
[[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFlexibleSpace
|
||||
target:nil
|
||||
action:nil],
|
||||
]];
|
||||
}
|
||||
UIBarButtonItem *playerButton = isPlayingVideo ? self.videoPauseBarButton : self.videoPlayBarButton;
|
||||
[toolbarItems addObjectsFromArray:@[
|
||||
playerButton,
|
||||
[[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFlexibleSpace
|
||||
target:nil
|
||||
action:nil],
|
||||
]];
|
||||
}
|
||||
|
||||
[toolbarItems addObject:[[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemTrash
|
||||
|
@ -443,24 +438,20 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
OWSFail(@"%@ Missing video file: %@", self.logTag, self.attachmentStream.mediaURL);
|
||||
}
|
||||
|
||||
if (@available(iOS 9.0, *)) {
|
||||
OWSVideoPlayer *player = [[OWSVideoPlayer alloc] initWithUrl:self.attachmentUrl];
|
||||
[player seekToTime:kCMTimeZero];
|
||||
player.delegate = self;
|
||||
self.videoPlayer = player;
|
||||
OWSVideoPlayer *player = [[OWSVideoPlayer alloc] initWithUrl:self.attachmentUrl];
|
||||
[player seekToTime:kCMTimeZero];
|
||||
player.delegate = self;
|
||||
self.videoPlayer = player;
|
||||
|
||||
VideoPlayerView *playerView = [VideoPlayerView new];
|
||||
playerView.player = player.avPlayer;
|
||||
VideoPlayerView *playerView = [VideoPlayerView new];
|
||||
playerView.player = player.avPlayer;
|
||||
|
||||
[NSLayoutConstraint autoSetPriority:UILayoutPriorityDefaultLow
|
||||
forConstraints:^{
|
||||
[playerView autoSetDimensionsToSize:self.image.size];
|
||||
}];
|
||||
[NSLayoutConstraint autoSetPriority:UILayoutPriorityDefaultLow
|
||||
forConstraints:^{
|
||||
[playerView autoSetDimensionsToSize:self.image.size];
|
||||
}];
|
||||
|
||||
return playerView;
|
||||
} else {
|
||||
return [[UIImageView alloc] initWithImage:self.image];
|
||||
}
|
||||
return playerView;
|
||||
}
|
||||
|
||||
- (void)setAreToolbarsHidden:(BOOL)areToolbarsHidden
|
||||
|
@ -895,18 +886,13 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
|
||||
- (void)playVideo
|
||||
{
|
||||
if (@available(iOS 9, *)) {
|
||||
OWSAssert(self.videoPlayer);
|
||||
OWSAssert(self.videoPlayer);
|
||||
|
||||
[self updateFooterBarButtonItemsWithIsPlayingVideo:YES];
|
||||
self.playVideoButton.hidden = YES;
|
||||
self.areToolbarsHidden = YES;
|
||||
[self updateFooterBarButtonItemsWithIsPlayingVideo:YES];
|
||||
self.playVideoButton.hidden = YES;
|
||||
self.areToolbarsHidden = YES;
|
||||
|
||||
[self.videoPlayer play];
|
||||
} else {
|
||||
[self legacyPlayVideo];
|
||||
return;
|
||||
}
|
||||
[self.videoPlayer play];
|
||||
}
|
||||
|
||||
- (void)pauseVideo
|
||||
|
@ -958,21 +944,6 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
}
|
||||
}
|
||||
|
||||
#pragma mark iOS8 Video Playback
|
||||
|
||||
// AVPlayer was introduced in iOS9, so on iOS8 we fall back to MPMoviePlayer
|
||||
// This causes an unforutnate "double present" since we present the full screen view and then the MPMovie view over top.
|
||||
// And similarly a double dismiss.
|
||||
- (void)legacyPlayVideo
|
||||
{
|
||||
if (@available(iOS 9.0, *)) {
|
||||
OWSFail(@"legacy video is for iOS8 only");
|
||||
}
|
||||
MPMoviePlayerViewController *vc = [[MPMoviePlayerViewController alloc] initWithContentURL:self.attachmentUrl];
|
||||
|
||||
[self presentViewController:vc animated:YES completion:nil];
|
||||
}
|
||||
|
||||
#pragma mark - Saving images to Camera Roll
|
||||
|
||||
- (void)image:(UIImage *)image didFinishSavingWithError:(NSError *)error contextInfo:(void *)contextInfo
|
||||
|
|
|
@ -453,13 +453,7 @@ protocol CallAudioServiceDelegate: class {
|
|||
var availableInputs: [AudioSource] {
|
||||
guard let availableInputs = avAudioSession.availableInputs else {
|
||||
// I'm not sure why this would happen, but it may indicate an error.
|
||||
// In practice, I haven't seen it on iOS9+.
|
||||
//
|
||||
// I *have* seen it on iOS8, but it doesn't seem to cause any problems,
|
||||
// so we do *not* trigger the assert on that platform.
|
||||
if #available(iOS 9.0, *) {
|
||||
owsFail("No available inputs or inputs not ready")
|
||||
}
|
||||
owsFail("No available inputs or inputs not ready")
|
||||
return [AudioSource.builtInSpeaker]
|
||||
}
|
||||
|
||||
|
|
|
@ -300,12 +300,8 @@ NSString *const Signal_Message_MarkAsRead_Identifier = @"Signal_Message_MarkAsRe
|
|||
action_reply.title = NSLocalizedString(@"PUSH_MANAGER_REPLY", @"");
|
||||
action_reply.destructive = NO;
|
||||
action_reply.authenticationRequired = NO;
|
||||
if (SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(9, 0)) {
|
||||
action_reply.behavior = UIUserNotificationActionBehaviorTextInput;
|
||||
action_reply.activationMode = UIUserNotificationActivationModeBackground;
|
||||
} else {
|
||||
action_reply.activationMode = UIUserNotificationActivationModeForeground;
|
||||
}
|
||||
action_reply.behavior = UIUserNotificationActionBehaviorTextInput;
|
||||
action_reply.activationMode = UIUserNotificationActivationModeBackground;
|
||||
|
||||
UIMutableUserNotificationCategory *messageCategory = [UIMutableUserNotificationCategory new];
|
||||
messageCategory.identifier = Signal_Full_New_Message_Category;
|
||||
|
|
|
@ -1,15 +1,14 @@
|
|||
//
|
||||
// Copyright (c) 2017 Open Whisper Systems. All rights reserved.
|
||||
// Copyright (c) 2018 Open Whisper Systems. All rights reserved.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
import Contacts
|
||||
import SignalServiceKit
|
||||
|
||||
@available(iOS 9.0, *)
|
||||
class ContactCell: UITableViewCell {
|
||||
|
||||
static let nib = UINib(nibName:"ContactCell", bundle: nil)
|
||||
static let nib = UINib(nibName: "ContactCell", bundle: nil)
|
||||
|
||||
@IBOutlet weak var contactTextLabel: UILabel!
|
||||
@IBOutlet weak var contactDetailTextLabel: UILabel!
|
||||
|
@ -48,7 +47,7 @@ class ContactCell: UITableViewCell {
|
|||
self.contact = contact
|
||||
|
||||
if contactTextLabel != nil {
|
||||
contactTextLabel.attributedText = contact.cnContact?.formattedFullName(font:contactTextLabel.font)
|
||||
contactTextLabel.attributedText = contact.cnContact?.formattedFullName(font: contactTextLabel.font)
|
||||
}
|
||||
|
||||
updateSubtitleBasedonType(subtitleType, contact: contact)
|
||||
|
@ -65,7 +64,7 @@ class ContactCell: UITableViewCell {
|
|||
let avatarBuilder = OWSContactAvatarBuilder(nonSignalName: contact.fullName,
|
||||
colorSeed: contactIdForDeterminingBackgroundColor,
|
||||
diameter: kAvatarWidth,
|
||||
contactsManager:contactsManager)
|
||||
contactsManager: contactsManager)
|
||||
|
||||
self.contactImageView?.image = avatarBuilder.buildDefaultImage()
|
||||
} else {
|
||||
|
@ -92,7 +91,6 @@ class ContactCell: UITableViewCell {
|
|||
}
|
||||
}
|
||||
|
||||
@available(iOS 9.0, *)
|
||||
fileprivate extension CNContact {
|
||||
/**
|
||||
* Bold the sorting portion of the name. e.g. if we sort by family name, bold the family name.
|
||||
|
@ -102,7 +100,7 @@ fileprivate extension CNContact {
|
|||
|
||||
let boldDescriptor = font.fontDescriptor.withSymbolicTraits(.traitBold)
|
||||
let boldAttributes = [
|
||||
NSFontAttributeName: UIFont(descriptor:boldDescriptor!, size: 0)
|
||||
NSFontAttributeName: UIFont(descriptor: boldDescriptor!, size: 0)
|
||||
]
|
||||
|
||||
if let attributedName = CNContactFormatter.attributedString(from: self, style: .fullName, defaultAttributes: nil) {
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1,5 +1,5 @@
|
|||
//
|
||||
// Copyright (c) 2017 Open Whisper Systems. All rights reserved.
|
||||
// Copyright (c) 2018 Open Whisper Systems. All rights reserved.
|
||||
//
|
||||
|
||||
#import "RemoteVideoView.h"
|
||||
|
@ -84,12 +84,9 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
return self;
|
||||
}
|
||||
|
||||
// On iOS8: prints a message saying the feature is unavailable.
|
||||
if (!SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(9, 0)) {
|
||||
_videoRenderer = [NullVideoRenderer new];
|
||||
[self addSubview:_videoRenderer];
|
||||
[_videoRenderer autoPinEdgesToSuperviewEdges];
|
||||
}
|
||||
_videoRenderer = [NullVideoRenderer new];
|
||||
[self addSubview:_videoRenderer];
|
||||
[_videoRenderer autoPinEdgesToSuperviewEdges];
|
||||
|
||||
// Currently RTC only supports metal on 64bit machines
|
||||
#if defined(RTC_SUPPORTS_METAL)
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
//
|
||||
// Copyright (c) 2017 Open Whisper Systems. All rights reserved.
|
||||
// Copyright (c) 2018 Open Whisper Systems. All rights reserved.
|
||||
//
|
||||
|
||||
import XCTest
|
||||
|
@ -23,7 +23,6 @@ final class ContactsPickerTest: XCTestCase {
|
|||
}
|
||||
}
|
||||
|
||||
@available(iOS 9.0, *)
|
||||
func testContactSectionMatchesEmailFirstLetterWhenOnlyEmailContact() {
|
||||
setLangEN()
|
||||
|
||||
|
@ -48,7 +47,6 @@ final class ContactsPickerTest: XCTestCase {
|
|||
}
|
||||
}
|
||||
|
||||
@available(iOS 9.0, *)
|
||||
func testContactSectionMatchesNameFirstLetterWhenNameExistsInContact() {
|
||||
setLangEN()
|
||||
|
||||
|
|
|
@ -172,38 +172,36 @@ public class AttachmentApprovalViewController: OWSViewController, CaptioningTool
|
|||
// This allows us to zoom in on the media view without zooming in on the button
|
||||
if attachment.isVideo {
|
||||
|
||||
if #available(iOS 9.0, *) {
|
||||
guard let videoURL = attachment.dataUrl else {
|
||||
owsFail("Missing videoURL")
|
||||
return
|
||||
}
|
||||
|
||||
let player = OWSVideoPlayer(url: videoURL)
|
||||
self.videoPlayer = player
|
||||
player.delegate = self
|
||||
|
||||
let playerView = VideoPlayerView()
|
||||
playerView.player = player.avPlayer
|
||||
self.mediaMessageView.addSubview(playerView)
|
||||
playerView.autoPinEdgesToSuperviewEdges()
|
||||
|
||||
let pauseGesture = UITapGestureRecognizer(target: self, action: #selector(didTapPlayerView(_:)))
|
||||
playerView.addGestureRecognizer(pauseGesture)
|
||||
|
||||
let progressBar = PlayerProgressBar()
|
||||
progressBar.player = player.avPlayer
|
||||
progressBar.delegate = self
|
||||
|
||||
// we don't want the progress bar to zoom during "pinch-to-zoom"
|
||||
// but we do want it to shrink with the media content when the user
|
||||
// pops the keyboard.
|
||||
contentContainer.addSubview(progressBar)
|
||||
|
||||
progressBar.autoPinEdge(.top, to: .bottom, of: topToolbar)
|
||||
progressBar.autoPinWidthToSuperview()
|
||||
progressBar.autoSetDimension(.height, toSize: 44)
|
||||
guard let videoURL = attachment.dataUrl else {
|
||||
owsFail("Missing videoURL")
|
||||
return
|
||||
}
|
||||
|
||||
let player = OWSVideoPlayer(url: videoURL)
|
||||
self.videoPlayer = player
|
||||
player.delegate = self
|
||||
|
||||
let playerView = VideoPlayerView()
|
||||
playerView.player = player.avPlayer
|
||||
self.mediaMessageView.addSubview(playerView)
|
||||
playerView.autoPinEdgesToSuperviewEdges()
|
||||
|
||||
let pauseGesture = UITapGestureRecognizer(target: self, action: #selector(didTapPlayerView(_:)))
|
||||
playerView.addGestureRecognizer(pauseGesture)
|
||||
|
||||
let progressBar = PlayerProgressBar()
|
||||
progressBar.player = player.avPlayer
|
||||
progressBar.delegate = self
|
||||
|
||||
// we don't want the progress bar to zoom during "pinch-to-zoom"
|
||||
// but we do want it to shrink with the media content when the user
|
||||
// pops the keyboard.
|
||||
contentContainer.addSubview(progressBar)
|
||||
|
||||
progressBar.autoPinEdge(.top, to: .bottom, of: topToolbar)
|
||||
progressBar.autoPinWidthToSuperview()
|
||||
progressBar.autoSetDimension(.height, toSize: 44)
|
||||
|
||||
self.mediaMessageView.videoPlayButton?.isHidden = true
|
||||
let playButton = UIButton()
|
||||
self.playVideoButton = playButton
|
||||
|
@ -220,7 +218,6 @@ public class AttachmentApprovalViewController: OWSViewController, CaptioningTool
|
|||
}
|
||||
}
|
||||
|
||||
@available(iOS 9, *)
|
||||
public func didTapPlayerView(_ gestureRecognizer: UIGestureRecognizer) {
|
||||
assert(self.videoPlayer != nil)
|
||||
self.pauseVideo()
|
||||
|
@ -279,44 +276,21 @@ public class AttachmentApprovalViewController: OWSViewController, CaptioningTool
|
|||
private func playVideo() {
|
||||
Logger.info("\(TAG) in \(#function)")
|
||||
|
||||
if #available(iOS 9, *) {
|
||||
guard let videoPlayer = self.videoPlayer else {
|
||||
owsFail("\(TAG) video player was unexpectedly nil")
|
||||
return
|
||||
}
|
||||
|
||||
guard let playVideoButton = self.playVideoButton else {
|
||||
owsFail("\(TAG) playVideoButton was unexpectedly nil")
|
||||
return
|
||||
}
|
||||
UIView.animate(withDuration: 0.1) {
|
||||
playVideoButton.alpha = 0.0
|
||||
}
|
||||
videoPlayer.play()
|
||||
} else {
|
||||
self.playLegacyVideo()
|
||||
}
|
||||
}
|
||||
|
||||
private func playLegacyVideo() {
|
||||
if #available(iOS 9, *) {
|
||||
owsFail("should only use legacy video on iOS8")
|
||||
}
|
||||
|
||||
guard let videoURL = self.attachment.dataUrl else {
|
||||
owsFail("videoURL was unexpectedly nil")
|
||||
guard let videoPlayer = self.videoPlayer else {
|
||||
owsFail("\(TAG) video player was unexpectedly nil")
|
||||
return
|
||||
}
|
||||
|
||||
guard let playerVC = MPMoviePlayerViewController(contentURL: videoURL) else {
|
||||
owsFail("failed to init legacy video player")
|
||||
guard let playVideoButton = self.playVideoButton else {
|
||||
owsFail("\(TAG) playVideoButton was unexpectedly nil")
|
||||
return
|
||||
}
|
||||
|
||||
self.present(playerVC, animated: true)
|
||||
UIView.animate(withDuration: 0.1) {
|
||||
playVideoButton.alpha = 0.0
|
||||
}
|
||||
videoPlayer.play()
|
||||
}
|
||||
|
||||
@available(iOS 9, *)
|
||||
private func pauseVideo() {
|
||||
guard let videoPlayer = self.videoPlayer else {
|
||||
owsFail("\(TAG) video player was unexpectedly nil")
|
||||
|
@ -345,7 +319,6 @@ public class AttachmentApprovalViewController: OWSViewController, CaptioningTool
|
|||
}
|
||||
}
|
||||
|
||||
@available(iOS 9.0, *)
|
||||
public func playerProgressBarDidStartScrubbing(_ playerProgressBar: PlayerProgressBar) {
|
||||
// [self.videoPlayer pause];
|
||||
guard let videoPlayer = self.videoPlayer else {
|
||||
|
@ -355,7 +328,6 @@ public class AttachmentApprovalViewController: OWSViewController, CaptioningTool
|
|||
videoPlayer.pause()
|
||||
}
|
||||
|
||||
@available(iOS 9.0, *)
|
||||
public func playerProgressBar(_ playerProgressBar: PlayerProgressBar, scrubbedToTime time: CMTime) {
|
||||
guard let videoPlayer = self.videoPlayer else {
|
||||
owsFail("\(TAG) video player was unexpectedly nil")
|
||||
|
@ -365,7 +337,6 @@ public class AttachmentApprovalViewController: OWSViewController, CaptioningTool
|
|||
videoPlayer.seek(to: time)
|
||||
}
|
||||
|
||||
@available(iOS 9.0, *)
|
||||
public func playerProgressBar(_ playerProgressBar: PlayerProgressBar, didFinishScrubbingAtTime time: CMTime, shouldResumePlayback: Bool) {
|
||||
guard let videoPlayer = self.videoPlayer else {
|
||||
owsFail("\(TAG) video player was unexpectedly nil")
|
||||
|
|
|
@ -7,7 +7,6 @@ import AVFoundation
|
|||
|
||||
@objc
|
||||
protocol OWSVideoPlayerDelegate: class {
|
||||
@available(iOSApplicationExtension 9.0, *)
|
||||
func videoPlayerDidPlayToCompletion(_ videoPlayer: OWSVideoPlayer)
|
||||
}
|
||||
|
||||
|
@ -19,10 +18,9 @@ public class OWSVideoPlayer: NSObject {
|
|||
|
||||
weak var delegate: OWSVideoPlayerDelegate?
|
||||
|
||||
@available(iOS 9.0, *)
|
||||
init(url: URL) {
|
||||
self.avPlayer = AVPlayer(url: url)
|
||||
self.audioActivity = AudioActivity(audioDescription: "[OWSVideoPlayer] url:\(url)")
|
||||
self.audioActivity = AudioActivity(audioDescription: "[OWSVideoPlayer] url:\(url)")
|
||||
|
||||
super.init()
|
||||
|
||||
|
@ -34,13 +32,11 @@ public class OWSVideoPlayer: NSObject {
|
|||
|
||||
// MARK: Playback Controls
|
||||
|
||||
@available(iOS 9.0, *)
|
||||
public func pause() {
|
||||
avPlayer.pause()
|
||||
OWSAudioSession.shared.endAudioActivity(self.audioActivity)
|
||||
}
|
||||
|
||||
@available(iOS 9.0, *)
|
||||
public func play() {
|
||||
OWSAudioSession.shared.setPlaybackCategory(audioActivity: self.audioActivity)
|
||||
|
||||
|
@ -57,7 +53,6 @@ public class OWSVideoPlayer: NSObject {
|
|||
avPlayer.play()
|
||||
}
|
||||
|
||||
@available(iOS 9.0, *)
|
||||
@objc(seekToTime:)
|
||||
public func seek(to time: CMTime) {
|
||||
avPlayer.seek(to: time)
|
||||
|
@ -66,7 +61,6 @@ public class OWSVideoPlayer: NSObject {
|
|||
// MARK: private
|
||||
|
||||
@objc
|
||||
@available(iOS 9.0, *)
|
||||
private func playerItemDidPlayToCompletion(_ notification: Notification) {
|
||||
self.delegate?.videoPlayerDidPlayToCompletion(self)
|
||||
OWSAudioSession.shared.endAudioActivity(self.audioActivity)
|
||||
|
|
|
@ -5,7 +5,6 @@
|
|||
import Foundation
|
||||
import AVFoundation
|
||||
|
||||
@available(iOS 9.0, *)
|
||||
@objc
|
||||
public class VideoPlayerView: UIView {
|
||||
var player: AVPlayer? {
|
||||
|
@ -27,7 +26,6 @@ public class VideoPlayerView: UIView {
|
|||
}
|
||||
}
|
||||
|
||||
@available(iOS 9.0, *)
|
||||
@objc
|
||||
public protocol PlayerProgressBarDelegate {
|
||||
func playerProgressBarDidStartScrubbing(_ playerProgressBar: PlayerProgressBar)
|
||||
|
@ -35,7 +33,6 @@ public protocol PlayerProgressBarDelegate {
|
|||
func playerProgressBar(_ playerProgressBar: PlayerProgressBar, didFinishScrubbingAtTime time: CMTime, shouldResumePlayback: Bool)
|
||||
}
|
||||
|
||||
@available(iOS 9.0, *)
|
||||
@objc
|
||||
public class PlayerProgressBar: UIView {
|
||||
public let TAG = "[PlayerProgressBar]"
|
||||
|
@ -150,7 +147,7 @@ public class PlayerProgressBar: UIView {
|
|||
@objc
|
||||
private func handleSliderTouchUp(_ slider: UISlider) {
|
||||
let sliderTime = time(slider: slider)
|
||||
self.delegate?.playerProgressBar(self, didFinishScrubbingAtTime: sliderTime, shouldResumePlayback:wasPlayingWhenScrubbingStarted)
|
||||
self.delegate?.playerProgressBar(self, didFinishScrubbingAtTime: sliderTime, shouldResumePlayback: wasPlayingWhenScrubbingStarted)
|
||||
}
|
||||
|
||||
@objc
|
||||
|
|
|
@ -10,38 +10,22 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
|
||||
+ (UIFont *)ows_thinFontWithSize:(CGFloat)size
|
||||
{
|
||||
if (@available(iOS 8.2, *)) {
|
||||
return [UIFont systemFontOfSize:size weight:UIFontWeightThin];
|
||||
} else {
|
||||
return [UIFont fontWithName:@"HelveticaNeue-Thin" size:size];
|
||||
}
|
||||
return [UIFont systemFontOfSize:size weight:UIFontWeightThin];
|
||||
}
|
||||
|
||||
+ (UIFont *)ows_lightFontWithSize:(CGFloat)size
|
||||
{
|
||||
if (@available(iOS 8.2, *)) {
|
||||
return [UIFont systemFontOfSize:size weight:UIFontWeightLight];
|
||||
} else {
|
||||
return [UIFont fontWithName:@"HelveticaNeue-Light" size:size];
|
||||
}
|
||||
return [UIFont systemFontOfSize:size weight:UIFontWeightLight];
|
||||
}
|
||||
|
||||
+ (UIFont *)ows_regularFontWithSize:(CGFloat)size
|
||||
{
|
||||
if (@available(iOS 8.2, *)) {
|
||||
return [UIFont systemFontOfSize:size weight:UIFontWeightRegular];
|
||||
} else {
|
||||
return [UIFont fontWithName:@"HelveticaNeue" size:size];
|
||||
}
|
||||
return [UIFont systemFontOfSize:size weight:UIFontWeightRegular];
|
||||
}
|
||||
|
||||
+ (UIFont *)ows_mediumFontWithSize:(CGFloat)size
|
||||
{
|
||||
if (@available(iOS 8.2, *)) {
|
||||
return [UIFont systemFontOfSize:size weight:UIFontWeightMedium];
|
||||
} else {
|
||||
return [UIFont fontWithName:@"HelveticaNeue-Medium" size:size];
|
||||
}
|
||||
return [UIFont systemFontOfSize:size weight:UIFontWeightMedium];
|
||||
}
|
||||
|
||||
+ (UIFont *)ows_boldFontWithSize:(CGFloat)size
|
||||
|
@ -85,25 +69,12 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
|
||||
+ (UIFont *)ows_dynamicTypeTitle2Font
|
||||
{
|
||||
if (@available(iOS 9.0, *)) {
|
||||
return [UIFont preferredFontForTextStyle:UIFontTextStyleTitle2];
|
||||
} else {
|
||||
// Dynamic title font for ios8 defaults to bold 12.0 pt, whereas ios9+ it's 22.0pt regular weight.
|
||||
// Here we chose to break dynamic font, in order to have uniform style across versions.
|
||||
// It's already huge, so it's unlikely to present a usability issue.
|
||||
// Handy font translations: http://swiftiostutorials.com/comparison-of-system-fonts-on-ios-8-and-ios-9/
|
||||
return [self ows_regularFontWithSize:22.0];
|
||||
}
|
||||
return [UIFont preferredFontForTextStyle:UIFontTextStyleTitle2];
|
||||
}
|
||||
|
||||
+ (UIFont *)ows_dynamicTypeHeadlineFont
|
||||
{
|
||||
if (@available(iOS 9.0, *)) {
|
||||
return [UIFont preferredFontForTextStyle:UIFontTextStyleHeadline];
|
||||
} else {
|
||||
// See ows_dynamicTypeTitle2Font.
|
||||
return [self ows_regularFontWithSize:17.0];
|
||||
}
|
||||
return [UIFont preferredFontForTextStyle:UIFontTextStyleHeadline];
|
||||
}
|
||||
|
||||
@end
|
||||
|
|
|
@ -253,12 +253,8 @@ CGFloat ScaleFromIPhone5(CGFloat iPhone5Value)
|
|||
|
||||
- (BOOL)isRTL
|
||||
{
|
||||
if (@available(iOS 9.0, *)) {
|
||||
return ([UIView userInterfaceLayoutDirectionForSemanticContentAttribute:self.semanticContentAttribute]
|
||||
== UIUserInterfaceLayoutDirectionRightToLeft);
|
||||
} else {
|
||||
return [CurrentAppContext() isRTL];
|
||||
}
|
||||
return ([UIView userInterfaceLayoutDirectionForSemanticContentAttribute:self.semanticContentAttribute]
|
||||
== UIUserInterfaceLayoutDirectionRightToLeft);
|
||||
}
|
||||
|
||||
- (NSLayoutConstraint *)autoPinLeadingToSuperview
|
||||
|
@ -270,16 +266,10 @@ CGFloat ScaleFromIPhone5(CGFloat iPhone5Value)
|
|||
{
|
||||
self.translatesAutoresizingMaskIntoConstraints = NO;
|
||||
|
||||
if (@available(iOS 9.0, *)) {
|
||||
NSLayoutConstraint *constraint =
|
||||
[self.leadingAnchor constraintEqualToAnchor:self.superview.layoutMarginsGuide.leadingAnchor
|
||||
constant:margin];
|
||||
constraint.active = YES;
|
||||
return constraint;
|
||||
} else {
|
||||
margin += (self.isRTL ? self.superview.layoutMargins.right : self.superview.layoutMargins.left);
|
||||
return [self autoPinEdgeToSuperviewEdge:ALEdgeLeading withInset:margin];
|
||||
}
|
||||
NSLayoutConstraint *constraint =
|
||||
[self.leadingAnchor constraintEqualToAnchor:self.superview.layoutMarginsGuide.leadingAnchor constant:margin];
|
||||
constraint.active = YES;
|
||||
return constraint;
|
||||
}
|
||||
|
||||
- (NSLayoutConstraint *)autoPinTrailingToSuperview
|
||||
|
@ -291,16 +281,10 @@ CGFloat ScaleFromIPhone5(CGFloat iPhone5Value)
|
|||
{
|
||||
self.translatesAutoresizingMaskIntoConstraints = NO;
|
||||
|
||||
if (@available(iOS 9.0, *)) {
|
||||
NSLayoutConstraint *constraint =
|
||||
[self.trailingAnchor constraintEqualToAnchor:self.superview.layoutMarginsGuide.trailingAnchor
|
||||
constant:-margin];
|
||||
constraint.active = YES;
|
||||
return constraint;
|
||||
} else {
|
||||
margin += (self.isRTL ? self.superview.layoutMargins.left : self.superview.layoutMargins.right);
|
||||
return [self autoPinEdgeToSuperviewEdge:ALEdgeTrailing withInset:margin];
|
||||
}
|
||||
NSLayoutConstraint *constraint =
|
||||
[self.trailingAnchor constraintEqualToAnchor:self.superview.layoutMarginsGuide.trailingAnchor constant:-margin];
|
||||
constraint.active = YES;
|
||||
return constraint;
|
||||
}
|
||||
|
||||
- (NSLayoutConstraint *)autoPinBottomToSuperview
|
||||
|
@ -312,14 +296,10 @@ CGFloat ScaleFromIPhone5(CGFloat iPhone5Value)
|
|||
{
|
||||
self.translatesAutoresizingMaskIntoConstraints = NO;
|
||||
|
||||
if (@available(iOS 9.0, *)) {
|
||||
NSLayoutConstraint *constraint =
|
||||
[self.bottomAnchor constraintEqualToAnchor:self.superview.layoutMarginsGuide.bottomAnchor constant:-margin];
|
||||
constraint.active = YES;
|
||||
return constraint;
|
||||
} else {
|
||||
return [self autoPinEdgeToSuperviewEdge:ALEdgeBottom withInset:margin];
|
||||
}
|
||||
NSLayoutConstraint *constraint =
|
||||
[self.bottomAnchor constraintEqualToAnchor:self.superview.layoutMarginsGuide.bottomAnchor constant:-margin];
|
||||
constraint.active = YES;
|
||||
return constraint;
|
||||
}
|
||||
|
||||
- (NSLayoutConstraint *)autoPinTopToSuperview
|
||||
|
@ -331,14 +311,10 @@ CGFloat ScaleFromIPhone5(CGFloat iPhone5Value)
|
|||
{
|
||||
self.translatesAutoresizingMaskIntoConstraints = NO;
|
||||
|
||||
if (@available(iOS 9.0, *)) {
|
||||
NSLayoutConstraint *constraint =
|
||||
[self.topAnchor constraintEqualToAnchor:self.superview.layoutMarginsGuide.topAnchor constant:margin];
|
||||
constraint.active = YES;
|
||||
return constraint;
|
||||
} else {
|
||||
return [self autoPinEdgeToSuperviewEdge:ALEdgeTop withInset:margin];
|
||||
}
|
||||
NSLayoutConstraint *constraint =
|
||||
[self.topAnchor constraintEqualToAnchor:self.superview.layoutMarginsGuide.topAnchor constant:margin];
|
||||
constraint.active = YES;
|
||||
return constraint;
|
||||
}
|
||||
|
||||
- (NSLayoutConstraint *)autoPinLeadingToTrailingOfView:(UIView *)view
|
||||
|
@ -354,14 +330,9 @@ CGFloat ScaleFromIPhone5(CGFloat iPhone5Value)
|
|||
|
||||
self.translatesAutoresizingMaskIntoConstraints = NO;
|
||||
|
||||
if (@available(iOS 9.0, *)) {
|
||||
NSLayoutConstraint *constraint =
|
||||
[self.leadingAnchor constraintEqualToAnchor:view.trailingAnchor constant:margin];
|
||||
constraint.active = YES;
|
||||
return constraint;
|
||||
} else {
|
||||
return [self autoPinEdge:ALEdgeLeading toEdge:ALEdgeTrailing ofView:view withOffset:margin];
|
||||
}
|
||||
NSLayoutConstraint *constraint = [self.leadingAnchor constraintEqualToAnchor:view.trailingAnchor constant:margin];
|
||||
constraint.active = YES;
|
||||
return constraint;
|
||||
}
|
||||
|
||||
- (NSLayoutConstraint *)autoPinTrailingToLeadingOfView:(UIView *)view
|
||||
|
@ -377,14 +348,9 @@ CGFloat ScaleFromIPhone5(CGFloat iPhone5Value)
|
|||
|
||||
self.translatesAutoresizingMaskIntoConstraints = NO;
|
||||
|
||||
if (@available(iOS 9.0, *)) {
|
||||
NSLayoutConstraint *constraint =
|
||||
[self.trailingAnchor constraintEqualToAnchor:view.leadingAnchor constant:-margin];
|
||||
constraint.active = YES;
|
||||
return constraint;
|
||||
} else {
|
||||
return [self autoPinEdge:ALEdgeTrailing toEdge:ALEdgeLeading ofView:view withOffset:margin];
|
||||
}
|
||||
NSLayoutConstraint *constraint = [self.trailingAnchor constraintEqualToAnchor:view.leadingAnchor constant:-margin];
|
||||
constraint.active = YES;
|
||||
return constraint;
|
||||
}
|
||||
|
||||
- (NSLayoutConstraint *)autoPinLeadingToView:(UIView *)view
|
||||
|
@ -400,14 +366,9 @@ CGFloat ScaleFromIPhone5(CGFloat iPhone5Value)
|
|||
|
||||
self.translatesAutoresizingMaskIntoConstraints = NO;
|
||||
|
||||
if (@available(iOS 9.0, *)) {
|
||||
NSLayoutConstraint *constraint =
|
||||
[self.leadingAnchor constraintEqualToAnchor:view.leadingAnchor constant:margin];
|
||||
constraint.active = YES;
|
||||
return constraint;
|
||||
} else {
|
||||
return [self autoPinEdge:ALEdgeLeading toEdge:ALEdgeLeading ofView:view withOffset:margin];
|
||||
}
|
||||
NSLayoutConstraint *constraint = [self.leadingAnchor constraintEqualToAnchor:view.leadingAnchor constant:margin];
|
||||
constraint.active = YES;
|
||||
return constraint;
|
||||
}
|
||||
|
||||
- (NSLayoutConstraint *)autoPinTrailingToView:(UIView *)view
|
||||
|
@ -423,14 +384,9 @@ CGFloat ScaleFromIPhone5(CGFloat iPhone5Value)
|
|||
|
||||
self.translatesAutoresizingMaskIntoConstraints = NO;
|
||||
|
||||
if (@available(iOS 9.0, *)) {
|
||||
NSLayoutConstraint *constraint =
|
||||
[self.trailingAnchor constraintEqualToAnchor:view.trailingAnchor constant:margin];
|
||||
constraint.active = YES;
|
||||
return constraint;
|
||||
} else {
|
||||
return [self autoPinEdge:ALEdgeTrailing toEdge:ALEdgeTrailing ofView:view withOffset:margin];
|
||||
}
|
||||
NSLayoutConstraint *constraint = [self.trailingAnchor constraintEqualToAnchor:view.trailingAnchor constant:margin];
|
||||
constraint.active = YES;
|
||||
return constraint;
|
||||
}
|
||||
|
||||
- (NSTextAlignment)textAlignmentUnnatural
|
||||
|
|
|
@ -20,7 +20,6 @@ protocol ContactStoreAdaptee {
|
|||
func startObservingChanges(changeHandler: @escaping () -> Void)
|
||||
}
|
||||
|
||||
@available(iOS 9.0, *)
|
||||
class ContactsFrameworkContactStoreAdaptee: ContactStoreAdaptee {
|
||||
let TAG = "[ContactsFrameworkContactStoreAdaptee]"
|
||||
private let contactStore = CNContactStore()
|
||||
|
@ -107,177 +106,6 @@ class ContactsFrameworkContactStoreAdaptee: ContactStoreAdaptee {
|
|||
}
|
||||
}
|
||||
|
||||
let kAddressBookContactStoreDidChangeNotificationName = NSNotification.Name("AddressBookContactStoreAdapteeDidChange")
|
||||
/**
|
||||
* System contact fetching compatible with iOS8
|
||||
*/
|
||||
class AddressBookContactStoreAdaptee: ContactStoreAdaptee {
|
||||
|
||||
let TAG = "[AddressBookContactStoreAdaptee]"
|
||||
|
||||
private var addressBook: ABAddressBook = ABAddressBookCreateWithOptions(nil, nil).takeRetainedValue()
|
||||
private var changeHandler: (() -> Void)?
|
||||
let supportsContactEditing = false
|
||||
|
||||
var authorizationStatus: ContactStoreAuthorizationStatus {
|
||||
switch ABAddressBookGetAuthorizationStatus() {
|
||||
case .notDetermined:
|
||||
return .notDetermined
|
||||
case .restricted:
|
||||
return .restricted
|
||||
case .denied:
|
||||
return .denied
|
||||
case .authorized:
|
||||
return .authorized
|
||||
}
|
||||
}
|
||||
|
||||
@objc
|
||||
func runChangeHandler() {
|
||||
guard let changeHandler = self.changeHandler else {
|
||||
owsFail("\(TAG) trying to run change handler before it was registered")
|
||||
return
|
||||
}
|
||||
changeHandler()
|
||||
}
|
||||
|
||||
func startObservingChanges(changeHandler: @escaping () -> Void) {
|
||||
// should only call once
|
||||
assert(self.changeHandler == nil)
|
||||
self.changeHandler = changeHandler
|
||||
|
||||
NotificationCenter.default.addObserver(self, selector: #selector(runChangeHandler), name: kAddressBookContactStoreDidChangeNotificationName, object: nil)
|
||||
|
||||
let callback: ABExternalChangeCallback = { (_, _, _) in
|
||||
// Ideally we'd just call the changeHandler here, but because this is a C style callback in swift,
|
||||
// we can't capture any state in the closure, so we use a notification as a trampoline
|
||||
NotificationCenter.default.postNotificationNameAsync(kAddressBookContactStoreDidChangeNotificationName, object: nil)
|
||||
}
|
||||
|
||||
ABAddressBookRegisterExternalChangeCallback(addressBook, callback, nil)
|
||||
}
|
||||
|
||||
func requestAccess(completionHandler: @escaping (Bool, Error?) -> Void) {
|
||||
ABAddressBookRequestAccessWithCompletion(addressBook, completionHandler)
|
||||
}
|
||||
|
||||
func fetchContacts() -> Result<[Contact], Error> {
|
||||
// Changes are not reflected unless we create a new address book
|
||||
self.addressBook = ABAddressBookCreateWithOptions(nil, nil).takeRetainedValue()
|
||||
|
||||
let allPeople = ABAddressBookCopyArrayOfAllPeopleInSourceWithSortOrdering(addressBook, nil, ABPersonGetSortOrdering()).takeRetainedValue() as [ABRecord]
|
||||
|
||||
let contacts = allPeople.map { self.buildContact(abRecord: $0) }
|
||||
|
||||
return .success(contacts)
|
||||
}
|
||||
|
||||
private func buildContact(abRecord: ABRecord) -> Contact {
|
||||
|
||||
let addressBookRecord = OWSABRecord(abRecord: abRecord)
|
||||
|
||||
var firstName = addressBookRecord.firstName
|
||||
let lastName = addressBookRecord.lastName
|
||||
let phoneNumbers = addressBookRecord.phoneNumbers
|
||||
|
||||
if firstName == nil && lastName == nil {
|
||||
if let companyName = addressBookRecord.companyName {
|
||||
firstName = companyName
|
||||
} else {
|
||||
firstName = phoneNumbers.first
|
||||
}
|
||||
}
|
||||
|
||||
return Contact(firstName: firstName,
|
||||
lastName: lastName,
|
||||
userTextPhoneNumbers: phoneNumbers,
|
||||
imageData: addressBookRecord.imageData,
|
||||
contactID: addressBookRecord.recordId)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrapper around ABRecord for easy property extraction.
|
||||
* Some code lifted from:
|
||||
* https://github.com/SocialbitGmbH/SwiftAddressBook/blob/c1993fa/Pod/Classes/SwiftAddressBookPerson.swift
|
||||
*/
|
||||
struct OWSABRecord {
|
||||
|
||||
public struct MultivalueEntry<T> {
|
||||
public var value: T
|
||||
public var label: String?
|
||||
public let id: Int
|
||||
|
||||
public init(value: T, label: String?, id: Int) {
|
||||
self.value = value
|
||||
self.label = label
|
||||
self.id = id
|
||||
}
|
||||
}
|
||||
|
||||
let abRecord: ABRecord
|
||||
|
||||
init(abRecord: ABRecord) {
|
||||
self.abRecord = abRecord
|
||||
}
|
||||
|
||||
var firstName: String? {
|
||||
return self.extractProperty(kABPersonFirstNameProperty)
|
||||
}
|
||||
|
||||
var lastName: String? {
|
||||
return self.extractProperty(kABPersonLastNameProperty)
|
||||
}
|
||||
|
||||
var companyName: String? {
|
||||
return self.extractProperty(kABPersonOrganizationProperty)
|
||||
}
|
||||
|
||||
var recordId: ABRecordID {
|
||||
return ABRecordGetRecordID(abRecord)
|
||||
}
|
||||
|
||||
// We don't yet support labels for our iOS8 users.
|
||||
var phoneNumbers: [String] {
|
||||
if let result: [MultivalueEntry<String>] = extractMultivalueProperty(kABPersonPhoneProperty) {
|
||||
return result.map { $0.value }
|
||||
} else {
|
||||
return []
|
||||
}
|
||||
}
|
||||
|
||||
var imageData: Data? {
|
||||
guard ABPersonHasImageData(abRecord) else {
|
||||
return nil
|
||||
}
|
||||
guard let data = ABPersonCopyImageData(abRecord)?.takeRetainedValue() else {
|
||||
return nil
|
||||
}
|
||||
return data as Data
|
||||
}
|
||||
|
||||
private func extractProperty<T>(_ propertyName: ABPropertyID) -> T? {
|
||||
let value: AnyObject? = ABRecordCopyValue(self.abRecord, propertyName)?.takeRetainedValue()
|
||||
return value as? T
|
||||
}
|
||||
|
||||
fileprivate func extractMultivalueProperty<T>(_ propertyName: ABPropertyID) -> Array<MultivalueEntry<T>>? {
|
||||
guard let multivalue: ABMultiValue = extractProperty(propertyName) else { return nil }
|
||||
var array = Array<MultivalueEntry<T>>()
|
||||
for i: Int in 0..<(ABMultiValueGetCount(multivalue)) {
|
||||
let value: T? = ABMultiValueCopyValueAtIndex(multivalue, i).takeRetainedValue() as? T
|
||||
if let v: T = value {
|
||||
let id: Int = Int(ABMultiValueGetIdentifierAtIndex(multivalue, i))
|
||||
let optionalLabel = ABMultiValueCopyLabelAtIndex(multivalue, i)?.takeRetainedValue()
|
||||
array.append(MultivalueEntry(value: v,
|
||||
label: optionalLabel == nil ? nil : optionalLabel! as String,
|
||||
id: id))
|
||||
}
|
||||
}
|
||||
return !array.isEmpty ? array : nil
|
||||
}
|
||||
}
|
||||
|
||||
public enum ContactStoreAuthorizationStatus {
|
||||
case notDetermined,
|
||||
restricted,
|
||||
|
@ -290,11 +118,7 @@ class ContactStoreAdapter: ContactStoreAdaptee {
|
|||
let adaptee: ContactStoreAdaptee
|
||||
|
||||
init() {
|
||||
if #available(iOS 9.0, *) {
|
||||
self.adaptee = ContactsFrameworkContactStoreAdaptee()
|
||||
} else {
|
||||
self.adaptee = AddressBookContactStoreAdaptee()
|
||||
}
|
||||
self.adaptee = ContactsFrameworkContactStoreAdaptee()
|
||||
}
|
||||
|
||||
var supportsContactEditing: Bool {
|
||||
|
|
|
@ -10,8 +10,8 @@ import Foundation
|
|||
/// Cleanup and present alert for no permissions
|
||||
@objc
|
||||
public class func showNoMicrophonePermissionAlert() {
|
||||
let alertTitle = NSLocalizedString("CALL_AUDIO_PERMISSION_TITLE", comment:"Alert title when calling and permissions for microphone are missing")
|
||||
let alertMessage = NSLocalizedString("CALL_AUDIO_PERMISSION_MESSAGE", comment:"Alert message when calling and permissions for microphone are missing")
|
||||
let alertTitle = NSLocalizedString("CALL_AUDIO_PERMISSION_TITLE", comment: "Alert title when calling and permissions for microphone are missing")
|
||||
let alertMessage = NSLocalizedString("CALL_AUDIO_PERMISSION_MESSAGE", comment: "Alert message when calling and permissions for microphone are missing")
|
||||
let alertController = UIAlertController(title: alertTitle, message: alertMessage, preferredStyle: .alert)
|
||||
let dismissAction = UIAlertAction(title: CommonStrings.dismissButton, style: .cancel)
|
||||
|
||||
|
@ -68,6 +68,8 @@ import Foundation
|
|||
@objc
|
||||
public class func showIOSUpgradeNagIfNecessary() {
|
||||
// Only show the nag to iOS 8 users.
|
||||
//
|
||||
// NOTE: Our current minimum iOS version is 9, so this should never show.
|
||||
if #available(iOS 9.0, *) {
|
||||
return
|
||||
}
|
||||
|
@ -88,9 +90,9 @@ import Foundation
|
|||
|
||||
Environment.preferences().setIOSUpgradeNagDate(Date())
|
||||
|
||||
OWSAlerts.showAlert(withTitle:NSLocalizedString("UPGRADE_IOS_ALERT_TITLE",
|
||||
comment:"Title for the alert indicating that user should upgrade iOS."),
|
||||
message:NSLocalizedString("UPGRADE_IOS_ALERT_MESSAGE",
|
||||
comment:"Message for the alert indicating that user should upgrade iOS."))
|
||||
OWSAlerts.showAlert(withTitle: NSLocalizedString("UPGRADE_IOS_ALERT_TITLE",
|
||||
comment: "Title for the alert indicating that user should upgrade iOS."),
|
||||
message: NSLocalizedString("UPGRADE_IOS_ALERT_MESSAGE",
|
||||
comment: "Message for the alert indicating that user should upgrade iOS."))
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue