Strip out special casing for pre-iOS 9 users.

This commit is contained in:
Matthew Chen 2018-02-23 12:46:04 -05:00
parent 44e38709d6
commit 99aedca45f
18 changed files with 423 additions and 793 deletions

View File

@ -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

View File

@ -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

View File

@ -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)

View File

@ -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:

View File

@ -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

View File

@ -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]
}

View File

@ -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;

View File

@ -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

View File

@ -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)

View File

@ -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()

View File

@ -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")

View File

@ -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)

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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 {

View File

@ -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."))
}
}