From 34b4ea377f82b8ac13829130905e22a6dfb3c377 Mon Sep 17 00:00:00 2001 From: Matthew Chen Date: Thu, 8 Nov 2018 16:55:54 -0500 Subject: [PATCH] Revise media progress views. --- Signal.xcodeproj/project.pbxproj | 12 +- .../Cells/ConversationMediaView.swift | 45 ++++--- ...loadView.swift => MediaDownloadView.swift} | 19 ++- .../Cells/MediaUploadView.swift | 117 ++++++++++++++++++ 4 files changed, 163 insertions(+), 30 deletions(-) rename Signal/src/ViewControllers/ConversationView/Cells/{AttachmentDownloadView.swift => MediaDownloadView.swift} (76%) create mode 100644 Signal/src/ViewControllers/ConversationView/Cells/MediaUploadView.swift diff --git a/Signal.xcodeproj/project.pbxproj b/Signal.xcodeproj/project.pbxproj index b2e3f6447..1063dc953 100644 --- a/Signal.xcodeproj/project.pbxproj +++ b/Signal.xcodeproj/project.pbxproj @@ -283,7 +283,8 @@ 34E5DC8220D8050D00C08145 /* RegistrationUtils.m in Sources */ = {isa = PBXBuildFile; fileRef = 34E5DC8120D8050D00C08145 /* RegistrationUtils.m */; }; 34E88D262098C5AE00A608F4 /* ContactViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 34E88D252098C5AE00A608F4 /* ContactViewController.swift */; }; 34E8A8D12085238A00B272B1 /* ProtoParsingTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 34E8A8D02085238900B272B1 /* ProtoParsingTest.m */; }; - 34EA69402194933900702471 /* AttachmentDownloadView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 34EA693F2194933900702471 /* AttachmentDownloadView.swift */; }; + 34EA69402194933900702471 /* MediaDownloadView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 34EA693F2194933900702471 /* MediaDownloadView.swift */; }; + 34EA69422194DE8000702471 /* MediaUploadView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 34EA69412194DE7F00702471 /* MediaUploadView.swift */; }; 34F308A21ECB469700BB7697 /* OWSBezierPathView.m in Sources */ = {isa = PBXBuildFile; fileRef = 34F308A11ECB469700BB7697 /* OWSBezierPathView.m */; }; 34FD93701E3BD43A00109093 /* OWSAnyTouchGestureRecognizer.m in Sources */ = {isa = PBXBuildFile; fileRef = 34FD936F1E3BD43A00109093 /* OWSAnyTouchGestureRecognizer.m */; }; 4503F1BE20470A5B00CEE724 /* classic-quiet.aifc in Resources */ = {isa = PBXBuildFile; fileRef = 4503F1BB20470A5B00CEE724 /* classic-quiet.aifc */; }; @@ -982,7 +983,8 @@ 34E5DC8120D8050D00C08145 /* RegistrationUtils.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RegistrationUtils.m; sourceTree = ""; }; 34E88D252098C5AE00A608F4 /* ContactViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ContactViewController.swift; sourceTree = ""; }; 34E8A8D02085238900B272B1 /* ProtoParsingTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ProtoParsingTest.m; sourceTree = ""; }; - 34EA693F2194933900702471 /* AttachmentDownloadView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AttachmentDownloadView.swift; sourceTree = ""; }; + 34EA693F2194933900702471 /* MediaDownloadView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MediaDownloadView.swift; sourceTree = ""; }; + 34EA69412194DE7F00702471 /* MediaUploadView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MediaUploadView.swift; sourceTree = ""; }; 34F308A01ECB469700BB7697 /* OWSBezierPathView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OWSBezierPathView.h; sourceTree = ""; }; 34F308A11ECB469700BB7697 /* OWSBezierPathView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OWSBezierPathView.m; sourceTree = ""; }; 34FD936E1E3BD43A00109093 /* OWSAnyTouchGestureRecognizer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OWSAnyTouchGestureRecognizer.h; path = views/OWSAnyTouchGestureRecognizer.h; sourceTree = ""; }; @@ -1830,13 +1832,14 @@ 34D1F0951F867BFC0066283D /* Cells */ = { isa = PBXGroup; children = ( - 34EA693F2194933900702471 /* AttachmentDownloadView.swift */, 34D1F0BB1F8D108C0066283D /* AttachmentUploadView.h */, 34D1F0BC1F8D108C0066283D /* AttachmentUploadView.m */, 3488F9352191CC4000E524CC /* ConversationMediaView.swift */, 34D1F0961F867BFC0066283D /* ConversationViewCell.h */, 34D1F0971F867BFC0066283D /* ConversationViewCell.m */, 34A8B3502190A40E00218A25 /* MediaAlbumCellView.swift */, + 34EA693F2194933900702471 /* MediaDownloadView.swift */, + 34EA69412194DE7F00702471 /* MediaUploadView.swift */, 34D1F0B81F8800D90066283D /* OWSAudioMessageView.h */, 34D1F0B91F8800D90066283D /* OWSAudioMessageView.m */, 34DBF005206C3CB100025978 /* OWSBubbleShapeView.h */, @@ -3422,7 +3425,7 @@ 458E38371D668EBF0094BD24 /* OWSDeviceProvisioningURLParser.m in Sources */, 34B6A905218B4C91007C4606 /* TypingIndicatorInteraction.swift in Sources */, 4517642B1DE939FD00EDB8B9 /* ContactCell.swift in Sources */, - 34EA69402194933900702471 /* AttachmentDownloadView.swift in Sources */, + 34EA69402194933900702471 /* MediaDownloadView.swift in Sources */, 340FC8AB204DAC8D007AEB0F /* DomainFrontingCountryViewController.m in Sources */, 3496744D2076768700080B5F /* OWSMessageBubbleView.m in Sources */, 34B3F8751E8DF1700035BE1A /* CallViewController.swift in Sources */, @@ -3468,6 +3471,7 @@ 34DBF004206BD5A500025978 /* OWSBubbleView.m in Sources */, 34E88D262098C5AE00A608F4 /* ContactViewController.swift in Sources */, 34AC0A23211C829F00997B47 /* OWSLabel.m in Sources */, + 34EA69422194DE8000702471 /* MediaUploadView.swift in Sources */, 76EB054018170B33006006FC /* AppDelegate.m in Sources */, 34D1F0831F8678AA0066283D /* ConversationInputTextView.m in Sources */, 340FC8B6204DAC8D007AEB0F /* OWSQRCodeScanningViewController.m in Sources */, diff --git a/Signal/src/ViewControllers/ConversationView/Cells/ConversationMediaView.swift b/Signal/src/ViewControllers/ConversationView/Cells/ConversationMediaView.swift index 6cf805808..22a2d6eb3 100644 --- a/Signal/src/ViewControllers/ConversationView/Cells/ConversationMediaView.swift +++ b/Signal/src/ViewControllers/ConversationView/Cells/ConversationMediaView.swift @@ -67,8 +67,6 @@ public class ConversationMediaView: UIView { } // - typealias ProgressCallback = (Bool) -> Void - private func addDownloadProgressIfNecessary() { guard let attachmentPointer = attachment as? TSAttachmentPointer else { owsFailDebug("Attachment has unexpected type.") @@ -76,11 +74,10 @@ public class ConversationMediaView: UIView { return } guard let attachmentId = attachmentPointer.uniqueId else { - owsFailDebug("Attachment stream missing unique ID.") + owsFailDebug("Attachment missing unique ID.") configureForMissingOrInvalid() return } - guard nil != attachmentDownloads.downloadProgress(forAttachmentId: attachmentId) else { // Not being downloaded. configureForMissingOrInvalid() @@ -88,29 +85,30 @@ public class ConversationMediaView: UIView { } backgroundColor = UIColor.ows_gray05 - let progressView = AttachmentDownloadView(attachmentId: attachmentId, radius: maxMessageWidth * 0.1) + let progressView = MediaDownloadView(attachmentId: attachmentId, radius: maxMessageWidth * 0.1) self.addSubview(progressView) progressView.autoPinEdgesToSuperviewEdges() } - private func addUploadProgressIfNecessary(_ subview: UIView, - progressCallback: ProgressCallback? = nil) { + private func addUploadProgressIfNecessary(_ subview: UIView) -> Bool { guard isOutgoing else { - return + return false } guard let attachmentStream = attachment as? TSAttachmentStream else { - return + return false + } + guard let attachmentId = attachmentStream.uniqueId else { + owsFailDebug("Attachment missing unique ID.") + configureForMissingOrInvalid() + return false } guard !attachmentStream.isUploaded else { - return + return false } - let uploadView = AttachmentUploadView(attachment: attachmentStream) { (isAttachmentReady) in - if let progressCallback = progressCallback { - progressCallback(isAttachmentReady) - } - } - subview.addSubview(uploadView) - uploadView.autoPinEdgesToSuperviewEdges() + let progressView = MediaUploadView(attachmentId: attachmentId, radius: maxMessageWidth * 0.1) + self.addSubview(progressView) + progressView.autoPinEdgesToSuperviewEdges() + return true } private func configureForAnimatedImage(attachmentStream: TSAttachmentStream) { @@ -224,15 +222,14 @@ public class ConversationMediaView: UIView { stillImageView.layer.magnificationFilter = kCAFilterTrilinear stillImageView.backgroundColor = Theme.offBackgroundColor - let videoPlayIcon = UIImage(named: "play_button") - let videoPlayButton = UIImageView(image: videoPlayIcon) - stillImageView.addSubview(videoPlayButton) - videoPlayButton.autoCenterInSuperview() - addSubview(stillImageView) stillImageView.autoPinEdgesToSuperviewEdges() - addUploadProgressIfNecessary(stillImageView) { (isAttachmentReady) in - videoPlayButton.isHidden = !isAttachmentReady + + if !addUploadProgressIfNecessary(stillImageView) { + let videoPlayIcon = UIImage(named: "play_button") + let videoPlayButton = UIImageView(image: videoPlayIcon) + stillImageView.addSubview(videoPlayButton) + videoPlayButton.autoCenterInSuperview() } loadBlock = { [weak self] in diff --git a/Signal/src/ViewControllers/ConversationView/Cells/AttachmentDownloadView.swift b/Signal/src/ViewControllers/ConversationView/Cells/MediaDownloadView.swift similarity index 76% rename from Signal/src/ViewControllers/ConversationView/Cells/AttachmentDownloadView.swift rename to Signal/src/ViewControllers/ConversationView/Cells/MediaDownloadView.swift index 1915d6939..e7b5dfa52 100644 --- a/Signal/src/ViewControllers/ConversationView/Cells/AttachmentDownloadView.swift +++ b/Signal/src/ViewControllers/ConversationView/Cells/MediaDownloadView.swift @@ -5,7 +5,7 @@ import Foundation @objc -public class AttachmentDownloadView: UIView { +public class MediaDownloadView: UIView { // MARK: - Dependencies @@ -27,6 +27,17 @@ public class AttachmentDownloadView: UIView { super.init(frame: .zero) layer.addSublayer(shapeLayer) + + NotificationCenter.default.addObserver(forName: NSNotification.Name.attachmentDownloadProgress, object: nil, queue: nil) { [weak self] notification in + guard let strongSelf = self else { return } + guard let notificationAttachmentId = notification.userInfo?[kAttachmentDownloadAttachmentIDKey] as? String else { + return + } + guard notificationAttachmentId == strongSelf.attachmentId else { + return + } + strongSelf.updateLayers() + } } @available(*, unavailable, message: "use other init() instead.") @@ -34,6 +45,10 @@ public class AttachmentDownloadView: UIView { notImplemented() } + deinit { + NotificationCenter.default.removeObserver(self) + } + @objc public override var bounds: CGRect { didSet { if oldValue != bounds { @@ -53,7 +68,7 @@ public class AttachmentDownloadView: UIView { internal func updateLayers() { AssertIsOnMainThread() - self.shapeLayer.frame = self.bounds + shapeLayer.frame = self.bounds guard let progress = attachmentDownloads.downloadProgress(forAttachmentId: attachmentId) else { Logger.warn("No progress for attachment.") diff --git a/Signal/src/ViewControllers/ConversationView/Cells/MediaUploadView.swift b/Signal/src/ViewControllers/ConversationView/Cells/MediaUploadView.swift new file mode 100644 index 000000000..4bd7dfabf --- /dev/null +++ b/Signal/src/ViewControllers/ConversationView/Cells/MediaUploadView.swift @@ -0,0 +1,117 @@ +// +// Copyright (c) 2018 Open Whisper Systems. All rights reserved. +// + +import Foundation + +@objc +public class MediaUploadView: UIView { + + // MARK: - + + private let attachmentId: String + private let radius: CGFloat + private let shapeLayer1 = CAShapeLayer() + private let shapeLayer2 = CAShapeLayer() + + private var isAttachmentReady: Bool = false + private var lastProgress: CGFloat = 0 + + @objc + public required init(attachmentId: String, radius: CGFloat) { + self.attachmentId = attachmentId + self.radius = radius + + super.init(frame: .zero) + + layer.addSublayer(shapeLayer1) + layer.addSublayer(shapeLayer2) + + NotificationCenter.default.addObserver(forName: NSNotification.Name.attachmentUploadProgress, object: nil, queue: nil) { [weak self] notification in + guard let strongSelf = self else { return } + guard let notificationAttachmentId = notification.userInfo?[kAttachmentUploadAttachmentIDKey] as? String else { + return + } + guard notificationAttachmentId == strongSelf.attachmentId else { + return + } + guard let progress = notification.userInfo?[kAttachmentUploadProgressKey] as? NSNumber else { + return + } + strongSelf.lastProgress = CGFloat(progress.floatValue) + strongSelf.updateLayers() + } + } + + @available(*, unavailable, message: "use other init() instead.") + required public init?(coder aDecoder: NSCoder) { + notImplemented() + } + + deinit { + NotificationCenter.default.removeObserver(self) + } + + @objc public override var bounds: CGRect { + didSet { + if oldValue != bounds { + updateLayers() + } + } + } + + @objc public override var frame: CGRect { + didSet { + if oldValue != frame { + updateLayers() + } + } + } + + internal func updateLayers() { + AssertIsOnMainThread() + + shapeLayer1.frame = self.bounds + shapeLayer2.frame = self.bounds + + guard !isAttachmentReady else { + shapeLayer1.path = nil + shapeLayer2.path = nil + return + } + + // Prevent the shape layer from animating changes. + CATransaction.begin() + CATransaction.setDisableActions(true) + + let center = CGPoint(x: self.bounds.width * 0.5, + y: self.bounds.height * 0.5) + let outerRadius: CGFloat = radius * 1.0 + let innerRadius: CGFloat = radius * 0.9 + let startAngle: CGFloat = CGFloat.pi * 1.5 + let endAngle: CGFloat = CGFloat.pi * (1.5 + 2 * lastProgress) + + let bezierPath1 = UIBezierPath() + bezierPath1.addArc(withCenter: center, radius: outerRadius, startAngle: startAngle, endAngle: endAngle, clockwise: true) + bezierPath1.addArc(withCenter: center, radius: innerRadius, startAngle: endAngle, endAngle: startAngle, clockwise: false) + shapeLayer1.path = bezierPath1.cgPath + shapeLayer1.fillColor = UIColor.ows_white.cgColor + + let innerCircleBounds = CGRect(x: center.x - innerRadius, + y: center.y - innerRadius, + width: innerRadius * 2, + height: innerRadius * 2) + let outerCircleBounds = CGRect(x: center.x - outerRadius, + y: center.y - outerRadius, + width: outerRadius * 2, + height: outerRadius * 2) + let bezierPath2 = UIBezierPath() + bezierPath2.append(UIBezierPath(ovalIn: innerCircleBounds)) + bezierPath2.append(UIBezierPath(ovalIn: outerCircleBounds)) + shapeLayer2.path = bezierPath2.cgPath + shapeLayer2.fillColor = UIColor(white: 1.0, alpha: 0.4).cgColor + shapeLayer2.fillRule = kCAFillRuleEvenOdd + + CATransaction.commit() + } +}