diff --git a/Podfile b/Podfile index 5452d4e81..7b71a5767 100644 --- a/Podfile +++ b/Podfile @@ -22,6 +22,7 @@ target 'SessionShareExtension' do pod 'CryptoSwift', :inhibit_warnings => true pod 'Curve25519Kit', git: 'https://github.com/signalapp/Curve25519Kit.git', :inhibit_warnings => true pod 'Mantle', git: 'https://github.com/signalapp/Mantle', branch: 'signal-master', :inhibit_warnings => true + pod 'NVActivityIndicatorView', :inhibit_warnings => true pod 'PromiseKit', :inhibit_warnings => true pod 'PureLayout', '~> 3.1.8', :inhibit_warnings => true pod 'SignalCoreKit', git: 'https://github.com/signalapp/SignalCoreKit.git', :inhibit_warnings => true diff --git a/Podfile.lock b/Podfile.lock index 804a148c7..2e195a17d 100644 --- a/Podfile.lock +++ b/Podfile.lock @@ -29,6 +29,7 @@ PODS: - NVActivityIndicatorView (5.0.1): - NVActivityIndicatorView/Base (= 5.0.1) - NVActivityIndicatorView/Base (5.0.1) + - OpenSSL-Universal (1.1.1200) - PromiseKit (6.13.1): - PromiseKit/CorePromise (= 6.13.1) - PromiseKit/Foundation (= 6.13.1) @@ -43,7 +44,7 @@ PODS: - SAMKeychain (1.5.3) - SignalCoreKit (1.0.0): - CocoaLumberjack - - GRKOpenSSLFramework + - OpenSSL-Universal - Sodium (0.8.0) - SQLCipher (4.4.0): - SQLCipher/standard (= 4.4.0) @@ -147,6 +148,7 @@ SPEC REPOS: - GRKOpenSSLFramework - HKDFKit - NVActivityIndicatorView + - OpenSSL-Universal - PromiseKit - PureLayout - Reachability @@ -196,11 +198,12 @@ SPEC CHECKSUMS: HKDFKit: c058305d6f64b84f28c50bd7aa89574625bcb62a Mantle: 2fa750afa478cd625a94230fbf1c13462f29395b NVActivityIndicatorView: 738e843cb8924e9e4fc3e559d0728031624bf860 + OpenSSL-Universal: 3b8c0d6268fbd5d3ac44f97338e2fd16a73d9dbf PromiseKit: 28fda91c973cc377875d8c0ea4f973013c05b6db PureLayout: a4afb3d79dd958564ce33d22c89f407280d8e6a8 Reachability: 33e18b67625424e47b6cde6d202dce689ad7af96 SAMKeychain: 483e1c9f32984d50ca961e26818a534283b4cd5c - SignalCoreKit: 4562b2bbd9830077439ca003f952a798457d4ea5 + SignalCoreKit: 1fbd8732163ef76de16cd1107d1fa3684b607e5d Sodium: 63c0ca312a932e6da481689537d4b35568841bdc SQLCipher: e434ed542b24f38ea7b36468a13f9765e1b5c072 SwiftProtobuf: 241400280f912735c1e1b9fe675fdd2c6c4d42e2 @@ -208,6 +211,6 @@ SPEC CHECKSUMS: YYImage: 6db68da66f20d9f169ceb94dfb9947c3867b9665 ZXingObjC: fdbb269f25dd2032da343e06f10224d62f537bdb -PODFILE CHECKSUM: 50e6a35c838ba28d2ee02bc6018fdd297c04e55f +PODFILE CHECKSUM: e7aa4e20c8329a413eadd5e6f0d900340810f4c7 -COCOAPODS: 1.10.1 +COCOAPODS: 1.11.2 diff --git a/SessionMessagingKit/Sending & Receiving/Attachments/SignalAttachment.swift b/SessionMessagingKit/Sending & Receiving/Attachments/SignalAttachment.swift index e6222d59e..f65d8640c 100644 --- a/SessionMessagingKit/Sending & Receiving/Attachments/SignalAttachment.swift +++ b/SessionMessagingKit/Sending & Receiving/Attachments/SignalAttachment.swift @@ -20,18 +20,6 @@ public enum SignalAttachmentError: Error { case couldNotResizeImage } -@objc -public enum SignalAttachmentType: Int { - case text - case oversizeText - case image - case animatedImage - case video - case audio - case url - case unknown -} - extension String { public var filenameWithoutExtension: String { return (self as NSString).deletingPathExtension @@ -446,19 +434,6 @@ public class SignalAttachment: NSObject { private class var mediaUTISet: Set { return audioUTISet.union(videoUTISet).union(animatedImageUTISet).union(inputImageUTISet) } - - @objc - public var fileType: SignalAttachmentType { - if isAnimatedImage { return .animatedImage } - if isImage { return .image } - if isVideo { return .video } - if isAudio { return .audio } - if isUrl { return .url } - if isOversizeText { return .oversizeText } - if isText { return .text } - - return .unknown - } @objc public var isImage: Bool { diff --git a/SignalUtilitiesKit/Media Viewing & Editing/MediaMessageView.swift b/SignalUtilitiesKit/Media Viewing & Editing/MediaMessageView.swift index 6743f556b..2740baac8 100644 --- a/SignalUtilitiesKit/Media Viewing & Editing/MediaMessageView.swift +++ b/SignalUtilitiesKit/Media Viewing & Editing/MediaMessageView.swift @@ -44,37 +44,36 @@ public class MediaMessageView: UIView, OWSAudioPlayerDelegate { } private lazy var validImage: UIImage? = { - switch attachment.fileType { - case .image: - guard - attachment.isValidImage, - let image: UIImage = attachment.image(), - image.size.width > 0, - image.size.height > 0 - else { - return nil - } - - return image - - case .video: - guard - attachment.isValidVideo, - let image: UIImage = attachment.videoPreview(), - image.size.width > 0, - image.size.height > 0 - else { - return nil - } - - return image + if attachment.isImage { + guard + attachment.isValidImage, + let image: UIImage = attachment.image(), + image.size.width > 0, + image.size.height > 0 + else { + return nil + } + + return image + } + else if attachment.isVideo { + guard + attachment.isValidVideo, + let image: UIImage = attachment.videoPreview(), + image.size.width > 0, + image.size.height > 0 + else { + return nil + } - default: return nil + return image } + + return nil }() private lazy var validAnimatedImage: YYImage? = { guard - attachment.fileType == .animatedImage, + attachment.isAnimatedImage, attachment.isValidImage, let dataUrl: URL = attachment.dataUrl, let image: YYImage = YYImage(contentsOfFile: dataUrl.path), @@ -153,22 +152,19 @@ public class MediaMessageView: UIView, OWSAudioPlayerDelegate { view.isHidden = true // Override the image to the correct one - switch attachment.fileType { - case .image, .video: - if let validImage: UIImage = validImage { - view.layer.minificationFilter = .trilinear - view.layer.magnificationFilter = .trilinear - view.image = validImage - } - - case .url: - view.clipsToBounds = true - view.image = UIImage(named: "Link")?.withTint(Colors.text) - view.contentMode = .center - view.backgroundColor = (isDarkMode ? .black : UIColor.black.withAlphaComponent(0.06)) - view.layer.cornerRadius = 8 - - default: break + if attachment.isImage || attachment.isVideo { + if let validImage: UIImage = validImage { + view.layer.minificationFilter = .trilinear + view.layer.magnificationFilter = .trilinear + view.image = validImage + } + } + else if attachment.isUrl { + view.clipsToBounds = true + view.image = UIImage(named: "Link")?.withTint(Colors.text) + view.contentMode = .center + view.backgroundColor = (isDarkMode ? .black : UIColor.black.withAlphaComponent(0.06)) + view.layer.cornerRadius = 8 } return view @@ -225,7 +221,7 @@ public class MediaMessageView: UIView, OWSAudioPlayerDelegate { let stackView: UIStackView = UIStackView() stackView.translatesAutoresizingMaskIntoConstraints = false stackView.axis = .vertical - stackView.alignment = (attachment.fileType == .url ? .leading : .center) + stackView.alignment = (attachment.isUrl ? .leading : .center) stackView.distribution = .fill switch mode { @@ -257,35 +253,33 @@ public class MediaMessageView: UIView, OWSAudioPlayerDelegate { } // Content - switch attachment.fileType { - case .image, .animatedImage, .video: break // No title for these - - case .url: - // If we have no link preview info at this point then assume link previews are disabled - guard let linkPreviewURL: String = linkPreviewInfo?.url else { - label.text = "vc_share_link_previews_disabled_title".localized() - break - } - + if attachment.isUrl { + // If we have no link preview info at this point then assume link previews are disabled + if let linkPreviewURL: String = linkPreviewInfo?.url { label.font = .boldSystemFont(ofSize: Values.smallFontSize) label.text = linkPreviewURL label.textAlignment = .left label.lineBreakMode = .byTruncatingTail label.numberOfLines = 2 - - default: - if let fileName: String = attachment.sourceFilename?.trimmingCharacters(in: .whitespacesAndNewlines), fileName.count > 0 { - label.text = fileName - } - else if let fileExtension: String = attachment.fileExtension { - label.text = String( - format: "ATTACHMENT_APPROVAL_FILE_EXTENSION_FORMAT".localized(), - fileExtension.uppercased() - ) - } - - label.textAlignment = .center - label.lineBreakMode = .byTruncatingMiddle + } + else { + label.text = "vc_share_link_previews_disabled_title".localized() + } + } + // Title for everything except these types + else if !attachment.isImage && !attachment.isAnimatedImage && !attachment.isVideo { + if let fileName: String = attachment.sourceFilename?.trimmingCharacters(in: .whitespacesAndNewlines), fileName.count > 0 { + label.text = fileName + } + else if let fileExtension: String = attachment.fileExtension { + label.text = String( + format: "ATTACHMENT_APPROVAL_FILE_EXTENSION_FORMAT".localized(), + fileExtension.uppercased() + ) + } + + label.textAlignment = .center + label.lineBreakMode = .byTruncatingMiddle } // Hide the label if it has no content @@ -314,32 +308,30 @@ public class MediaMessageView: UIView, OWSAudioPlayerDelegate { } // Content - switch attachment.fileType { - case .image, .animatedImage, .video: break // No size for these - - case .url: - // If we have no link preview info at this point then assume link previews are disabled - guard let linkPreviewURL: String = linkPreviewInfo?.url else { - label.text = "vc_share_link_previews_disabled_explanation".localized() - label.textColor = Colors.text - label.textAlignment = .center - label.numberOfLines = 0 - break - } - - // We only load Link Previews for HTTPS urls so append an explanation for not + if attachment.isUrl { + // We only load Link Previews for HTTPS urls so append an explanation for not + if let linkPreviewURL: String = linkPreviewInfo?.url { if let targetUrl: URL = URL(string: linkPreviewURL), targetUrl.scheme?.lowercased() != "https" { label.font = UIFont.ows_regularFont(withSize: Values.verySmallFontSize) label.text = "vc_share_link_previews_unsecure".localized() label.textColor = (mode == .attachmentApproval ? Colors.pinIcon : Colors.accent) } - - default: - // Format string for file size label in call interstitial view. - // Embeds: {{file size as 'N mb' or 'N kb'}}. - let fileSize: UInt = attachment.dataLength - label.text = String(format: "ATTACHMENT_APPROVAL_FILE_SIZE_FORMAT".localized(), OWSFormat.formatFileSize(UInt(fileSize))) + } + // If we have no link preview info at this point then assume link previews are disabled + else { + label.text = "vc_share_link_previews_disabled_explanation".localized() + label.textColor = Colors.text label.textAlignment = .center + label.numberOfLines = 0 + } + } + // Subtitle for everything else except these types + else if !attachment.isImage && !attachment.isAnimatedImage && !attachment.isVideo { + // Format string for file size label in call interstitial view. + // Embeds: {{file size as 'N mb' or 'N kb'}}. + let fileSize: UInt = attachment.dataLength + label.text = String(format: "ATTACHMENT_APPROVAL_FILE_SIZE_FORMAT".localized(), OWSFormat.formatFileSize(UInt(fileSize))) + label.textAlignment = .center } // Hide the label if it has no content @@ -352,7 +344,7 @@ public class MediaMessageView: UIView, OWSAudioPlayerDelegate { private func setupViews() { // Plain text will just be put in the 'message' input so do nothing - guard attachment.fileType != .text && attachment.fileType != .oversizeText else { return } + guard !attachment.isText && !attachment.isOversizeText else { return } // Setup the view hierarchy addSubview(stackView) @@ -370,95 +362,90 @@ public class MediaMessageView: UIView, OWSAudioPlayerDelegate { imageView.addSubview(fileTypeImageView) // Type-specific configurations - switch attachment.fileType { - case .animatedImage: animatedImageView.isHidden = false - case .image: imageView.isHidden = false - - case .video: - // Note: The 'attachmentApproval' mode provides it's own play button to keep - // it at the proper scale when zooming - imageView.isHidden = false - videoPlayButton.isHidden = (mode == .attachmentApproval) - - case .audio: - // Hide the 'audioPlayPauseButton' if the 'audioPlayer' failed to get created - imageView.isHidden = false - audioPlayPauseButton.isHidden = (audioPlayer == nil) - setAudioIconToPlay() - setAudioProgress(0, duration: (audioPlayer?.duration ?? 0)) - - fileTypeImageView.image = UIImage(named: "table_ic_notification_sound")? - .withRenderingMode(.alwaysTemplate) - fileTypeImageView.tintColor = Colors.text - fileTypeImageView.isHidden = false - - // Note: There is an annoying bug where the MediaMessageView will fill the screen if the - // 'audioPlayPauseButton' is added anywhere within the view hierarchy causing issues with - // the min scale on 'image' and 'animatedImage' file types (assume it's actually any UIButton) - addSubview(audioPlayPauseButton) - - case .url: - imageView.isHidden = false - imageView.alpha = 0 // Not 'isHidden' because we want it to take up space in the UIStackView - loadingView.isHidden = false - - if let linkPreviewUrl: String = linkPreviewInfo?.url { - // Don't want to change the axis until we have a URL to start loading, otherwise the - // error message will be broken - stackView.axis = .horizontal - - loadLinkPreview(linkPreviewURL: linkPreviewUrl) - } + if attachment.isAnimatedImage { + animatedImageView.isHidden = false + } + else if attachment.isImage { + imageView.isHidden = false + } + else if attachment.isVideo { + // Note: The 'attachmentApproval' mode provides it's own play button to keep + // it at the proper scale when zooming + imageView.isHidden = false + videoPlayButton.isHidden = (mode == .attachmentApproval) + } + else if attachment.isAudio { + // Hide the 'audioPlayPauseButton' if the 'audioPlayer' failed to get created + imageView.isHidden = false + audioPlayPauseButton.isHidden = (audioPlayer == nil) + setAudioIconToPlay() + setAudioProgress(0, duration: (audioPlayer?.duration ?? 0)) + + fileTypeImageView.image = UIImage(named: "table_ic_notification_sound")? + .withRenderingMode(.alwaysTemplate) + fileTypeImageView.tintColor = Colors.text + fileTypeImageView.isHidden = false + + // Note: There is an annoying bug where the MediaMessageView will fill the screen if the + // 'audioPlayPauseButton' is added anywhere within the view hierarchy causing issues with + // the min scale on 'image' and 'animatedImage' file types (assume it's actually any UIButton) + addSubview(audioPlayPauseButton) + } + else if attachment.isUrl { + imageView.isHidden = false + imageView.alpha = 0 // Not 'isHidden' because we want it to take up space in the UIStackView + loadingView.isHidden = false + + if let linkPreviewUrl: String = linkPreviewInfo?.url { + // Don't want to change the axis until we have a URL to start loading, otherwise the + // error message will be broken + stackView.axis = .horizontal - default: imageView.isHidden = false + loadLinkPreview(linkPreviewURL: linkPreviewUrl) + } + } + else { + imageView.isHidden = false } } private func setupLayout() { // Plain text will just be put in the 'message' input so do nothing - guard attachment.fileType != .text && attachment.fileType != .oversizeText else { return } + guard !attachment.isText && !attachment.isOversizeText else { return } // Sizing calculations let clampedRatio: CGFloat = { - switch attachment.fileType { - case .url: return 1 - - case .image, .video, .audio, .unknown: - let imageSize: CGSize = (imageView.image?.size ?? CGSize(width: 1, height: 1)) - let aspectRatio: CGFloat = (imageSize.width / imageSize.height) - - return CGFloatClamp(aspectRatio, 0.05, 95.0) - - case .animatedImage: - let imageSize: CGSize = (animatedImageView.image?.size ?? CGSize(width: 1, height: 1)) - let aspectRatio: CGFloat = (imageSize.width / imageSize.height) - - return CGFloatClamp(aspectRatio, 0.05, 95.0) - - default: return 0 + if attachment.isUrl { + return 1 } + + if attachment.isAnimatedImage { + let imageSize: CGSize = (animatedImageView.image?.size ?? CGSize(width: 1, height: 1)) + let aspectRatio: CGFloat = (imageSize.width / imageSize.height) + + return CGFloatClamp(aspectRatio, 0.05, 95.0) + } + + // All other types should maintain the ratio of the image in the 'imageView' + let imageSize: CGSize = (imageView.image?.size ?? CGSize(width: 1, height: 1)) + let aspectRatio: CGFloat = (imageSize.width / imageSize.height) + + return CGFloatClamp(aspectRatio, 0.05, 95.0) }() let maybeImageSize: CGFloat? = { - switch attachment.fileType { - case .image, .video: - if validImage != nil { return nil } - - // If we don't have a valid image then use the 'generic' case - break - - case .animatedImage: - if validAnimatedImage != nil { return nil } - - // If we don't have a valid image then use the 'generic' case - break - - case .url: return 80 - - // Use the 'generic' case for these - case .audio, .unknown: break - - default: return nil + if attachment.isImage || attachment.isVideo { + if validImage != nil { return nil } + + // If we don't have a valid image then use the 'generic' case + } + else if attachment.isAnimatedImage { + if validAnimatedImage != nil { return nil } + + // If we don't have a valid image then use the 'generic' case + } + else if attachment.isUrl { + return 80 } // Generic file size @@ -531,7 +518,7 @@ public class MediaMessageView: UIView, OWSAudioPlayerDelegate { ]) // No inset for the text for URLs but there is for all other layouts - if (attachment.fileType != .url) { + if !attachment.isUrl { NSLayoutConstraint.activate([ titleLabel.widthAnchor.constraint(equalTo: stackView.widthAnchor, constant: -(32 * 2)), subtitleLabel.widthAnchor.constraint(equalTo: stackView.widthAnchor, constant: -(32 * 2)) @@ -541,7 +528,7 @@ public class MediaMessageView: UIView, OWSAudioPlayerDelegate { // Note: There is an annoying bug where the MediaMessageView will fill the screen if the // 'audioPlayPauseButton' is added anywhere within the view hierarchy causing issues with // the min scale on 'image' and 'animatedImage' file types (assume it's actually any UIButton) - if attachment.fileType == .audio { + if attachment.isAudio { NSLayoutConstraint.activate([ audioPlayPauseButton.centerXAnchor.constraint(equalTo: imageView.centerXAnchor), audioPlayPauseButton.centerYAnchor.constraint(equalTo: imageView.centerYAnchor),