Attachment Approval

// FREEBIE
This commit is contained in:
Michael Kirk 2017-12-05 18:09:05 -05:00
parent a58f1f385c
commit 56fe9d057d
6 changed files with 241 additions and 41 deletions

View File

@ -294,6 +294,7 @@
45CD81EF1DC030E7004C9430 /* AccountManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 45CD81EE1DC030E7004C9430 /* AccountManager.swift */; };
45D231771DC7E8F10034FA89 /* SessionResetJob.swift in Sources */ = {isa = PBXBuildFile; fileRef = 45D231761DC7E8F10034FA89 /* SessionResetJob.swift */; };
45DF5DF21DDB843F00C936C7 /* CompareSafetyNumbersActivity.swift in Sources */ = {isa = PBXBuildFile; fileRef = 45DF5DF11DDB843F00C936C7 /* CompareSafetyNumbersActivity.swift */; };
45E547201FD755E700DFC09E /* AttachmentApprovalViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 45E5471F1FD755E700DFC09E /* AttachmentApprovalViewController.swift */; };
45E5A6991F61E6DE001E4A8A /* MarqueeLabel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 45E5A6981F61E6DD001E4A8A /* MarqueeLabel.swift */; };
45E615161E8C590B0018AD52 /* DisplayableText.swift in Sources */ = {isa = PBXBuildFile; fileRef = 45E615151E8C590B0018AD52 /* DisplayableText.swift */; };
45E7A6A81E71CA7E00D44FB5 /* DisplayableTextFilterTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 45E7A6A61E71CA7E00D44FB5 /* DisplayableTextFilterTest.swift */; };
@ -820,6 +821,7 @@
45E282DE1D08E67800ADD4C8 /* gl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = gl; path = translations/gl.lproj/Localizable.strings; sourceTree = "<group>"; };
45E282DF1D08E6CC00ADD4C8 /* id */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = id; path = translations/id.lproj/Localizable.strings; sourceTree = "<group>"; };
45E2E91E1E13EE3500457AA0 /* OWSCallNotificationsAdaptee.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; lineEnding = 0; name = OWSCallNotificationsAdaptee.h; path = UserInterface/OWSCallNotificationsAdaptee.h; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.objcpp; };
45E5471F1FD755E700DFC09E /* AttachmentApprovalViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AttachmentApprovalViewController.swift; sourceTree = "<group>"; };
45E5A6981F61E6DD001E4A8A /* MarqueeLabel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MarqueeLabel.swift; sourceTree = "<group>"; };
45E615151E8C590B0018AD52 /* DisplayableText.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DisplayableText.swift; sourceTree = "<group>"; };
45E7A6A61E71CA7E00D44FB5 /* DisplayableTextFilterTest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DisplayableTextFilterTest.swift; sourceTree = "<group>"; };
@ -1542,6 +1544,7 @@
454A96571FD600B4008D2A0E /* attachments */ = {
isa = PBXGroup;
children = (
45E5471F1FD755E700DFC09E /* AttachmentApprovalViewController.swift */,
3400C7901EAF89CD008A8584 /* SendExternalFileViewController.h */,
3400C7911EAF89CD008A8584 /* SendExternalFileViewController.m */,
34533F161EA8D2070006114F /* OWSAudioAttachmentPlayer.h */,
@ -2693,6 +2696,7 @@
45194F931FD7215C00333B2C /* OWSContactOffersInteraction.m in Sources */,
346129761FD1E0B500532771 /* WeakTimer.swift in Sources */,
346129F81FD5F31400532771 /* OWS100RemoveTSRecipientsMigration.m in Sources */,
45E547201FD755E700DFC09E /* AttachmentApprovalViewController.swift in Sources */,
346129B51FD1F7E800532771 /* OWSProfileManager.m in Sources */,
346129701FD1D74C00532771 /* Release.m in Sources */,
34480B621FD0A98800BC14EF /* UIColor+OWS.m in Sources */,

View File

@ -76,12 +76,18 @@
/* No comment provided by engineer. */
"ATTACHMENT" = "Attachment";
/* Title for the 'attachment approval' dialog. */
"ATTACHMENT_APPROVAL_DIALOG_TITLE" = "Attachment";
/* Format string for file extension label in call interstitial view */
"ATTACHMENT_APPROVAL_FILE_EXTENSION_FORMAT" = "File type: %@";
/* Format string for file size label in call interstitial view. Embeds: {{file size as 'N mb' or 'N kb'}}. */
"ATTACHMENT_APPROVAL_FILE_SIZE_FORMAT" = "Size: %@";
/* Label for 'send' button in the 'attachment approval' dialog. */
"ATTACHMENT_APPROVAL_SEND_BUTTON" = "Send";
/* Generic filename for an attachment with no known name */
"ATTACHMENT_DEFAULT_FILENAME" = "Attachment";

View File

@ -0,0 +1,171 @@
//
// Copyright (c) 2017 Open Whisper Systems. All rights reserved.
//
import Foundation
import MediaPlayer
@objc
public protocol AttachmentApprovalViewControllerDelegate: class {
func didApproveAttachment()
func didCancelAttachment()
}
@objc
public class AttachmentApprovalViewController: OWSViewController {
let TAG = "[AttachmentApprovalViewController]"
weak var delegate: AttachmentApprovalViewControllerDelegate?
// MARK: Properties
let attachment: SignalAttachment
let mediaMessageView: MediaMessageView
// MARK: Initializers
@available(*, unavailable, message:"use attachment: constructor instead.")
required public init?(coder aDecoder: NSCoder) {
fatalError("unimplemented")
}
required public init(attachment: SignalAttachment, delegate: AttachmentApprovalViewControllerDelegate) {
assert(!attachment.hasError)
self.attachment = attachment
self.delegate = delegate
self.mediaMessageView = MediaMessageView(attachment: attachment, mode: .large)
super.init(nibName: nil, bundle: nil)
}
// MARK: View Lifecycle
override public func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = UIColor.white
createViews()
self.navigationItem.title = dialogTitle()
}
private func dialogTitle() -> String {
guard let filename = mediaMessageView.formattedFileName() else {
return NSLocalizedString("ATTACHMENT_APPROVAL_DIALOG_TITLE",
comment: "Title for the 'attachment approval' dialog.")
}
return filename
}
override public func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
mediaMessageView.viewWillAppear(animated)
}
override public func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
mediaMessageView.viewWillDisappear(animated)
}
// MARK: - Create Views
private func createViews() {
let previewTopMargin: CGFloat = 30
let previewHMargin: CGFloat = 20
self.view.addSubview(mediaMessageView)
mediaMessageView.autoPinWidthToSuperview(withMargin:previewHMargin)
mediaMessageView.autoPin(toTopLayoutGuideOf: self, withInset:previewTopMargin)
createButtonRow(mediaMessageView:mediaMessageView)
}
private func wrapViewsInVerticalStack(subviews: [UIView]) -> UIView {
assert(subviews.count > 0)
let stackView = UIView()
var lastView: UIView?
for subview in subviews {
stackView.addSubview(subview)
subview.autoHCenterInSuperview()
if lastView == nil {
subview.autoPinEdge(toSuperviewEdge:.top)
} else {
subview.autoPinEdge(.top, to:.bottom, of:lastView!, withOffset:10)
}
lastView = subview
}
lastView?.autoPinEdge(toSuperviewEdge:.bottom)
return stackView
}
private func createButtonRow(mediaMessageView: UIView) {
let buttonTopMargin = ScaleFromIPhone5To7Plus(30, 40)
let buttonBottomMargin = ScaleFromIPhone5To7Plus(25, 40)
let buttonHSpacing = ScaleFromIPhone5To7Plus(20, 30)
let buttonRow = UIView()
self.view.addSubview(buttonRow)
buttonRow.autoPinWidthToSuperview()
buttonRow.autoPinEdge(toSuperviewEdge:.bottom, withInset:buttonBottomMargin)
buttonRow.autoPinEdge(.top, to:.bottom, of:mediaMessageView, withOffset:buttonTopMargin)
// We use this invisible subview to ensure that the buttons are centered
// horizontally.
let buttonSpacer = UIView()
buttonRow.addSubview(buttonSpacer)
// Vertical positioning of this view doesn't matter.
buttonSpacer.autoPinEdge(toSuperviewEdge:.top)
buttonSpacer.autoSetDimension(.width, toSize:buttonHSpacing)
buttonSpacer.autoHCenterInSuperview()
let cancelButton = createButton(title: CommonStrings.cancelButton,
color : UIColor.ows_destructiveRed(),
action: #selector(cancelPressed))
buttonRow.addSubview(cancelButton)
cancelButton.autoPinEdge(toSuperviewEdge:.top)
cancelButton.autoPinEdge(toSuperviewEdge:.bottom)
cancelButton.autoPinEdge(.right, to:.left, of:buttonSpacer)
let sendButton = createButton(title: NSLocalizedString("ATTACHMENT_APPROVAL_SEND_BUTTON",
comment: "Label for 'send' button in the 'attachment approval' dialog."),
color : UIColor(rgbHex:0x2ecc71),
action: #selector(sendPressed))
buttonRow.addSubview(sendButton)
sendButton.autoPinEdge(toSuperviewEdge:.top)
sendButton.autoPinEdge(toSuperviewEdge:.bottom)
sendButton.autoPinEdge(.left, to:.right, of:buttonSpacer)
}
private func createButton(title: String, color: UIColor, action: Selector) -> UIView {
let buttonWidth = ScaleFromIPhone5To7Plus(110, 140)
let buttonHeight = ScaleFromIPhone5To7Plus(35, 45)
return OWSFlatButton.button(title:title,
titleColor:UIColor.white,
backgroundColor:color,
width:buttonWidth,
height:buttonHeight,
target:target,
selector:action)
}
// MARK: - Event Handlers
func cancelPressed(sender: UIButton) {
self.delegate?.didCancelAttachment()
}
func sendPressed(sender: UIButton) {
self.delegate?.didApproveAttachment()
}
}

View File

@ -426,6 +426,7 @@ public class MediaMessageView: UIView, OWSAudioAttachmentPlayerDelegate {
return
}
// FIXME SHARINGEXTENSION
// let window = UIApplication.shared.keyWindow
// let convertedRect = fromView.convert(fromView.bounds, to:window)
// let viewController = FullImageViewController(attachment:attachment, from:convertedRect)

View File

@ -16,10 +16,12 @@
NS_ASSUME_NONNULL_BEGIN
@interface SendExternalFileViewController () <SelectThreadViewControllerDelegate>
@interface SendExternalFileViewController () <SelectThreadViewControllerDelegate,
AttachmentApprovalViewControllerDelegate>
@property (nonatomic, readonly) OWSContactsManager *contactsManager;
@property (nonatomic, readonly) OWSMessageSender *messageSender;
@property (nonatomic) TSThread *thread;
@end
@ -45,45 +47,6 @@ NS_ASSUME_NONNULL_BEGIN
self.title = NSLocalizedString(@"SEND_EXTERNAL_FILE_VIEW_TITLE", @"Title for the 'send external file' view.");
}
#pragma mark - SelectThreadViewControllerDelegate
- (void)threadWasSelected:(TSThread *)thread
{
OWSAssert(self.attachment);
OWSAssert(thread);
__weak typeof(self) weakSelf = self;
// FIXME SHARINGEXTENSION
// Handling safety number changes brings in a lot of machinery.
// How do we want to handle this?
// e.g. fingerprint scanning, etc. in the SAE or just redirect the user to the main app?
// BOOL didShowSNAlert =
// [SafetyNumberConfirmationAlert presentAlertIfNecessaryWithRecipientIds:thread.recipientIdentifiers
// confirmationText:[SafetyNumberStrings
// confirmSendButton]
// contactsManager:self.contactsManager
// completion:^(BOOL didConfirm) {
// if (didConfirm) {
// [weakSelf threadWasSelected:thread];
// }
// }];
// if (didShowSNAlert) {
// return;
// }
[ThreadUtil addThreadToProfileWhitelistIfEmptyContactThread:thread];
[ThreadUtil sendMessageWithAttachment:self.attachment inThread:thread messageSender:self.messageSender];
// FIXME SHARINGEXTENSION
// Show loading screen and dismiss when complete
// TODO Show share annotation UI (add caption, preview, cancel)
//
// This implemenation was for the "import with signal" functionality:
// [SignalApp.sharedApp presentConversationForThread:thread];
}
- (BOOL)canSelectBlockedContact
{
return NO;
@ -191,6 +154,61 @@ NS_ASSUME_NONNULL_BEGIN
return label;
}
#pragma mark - SelectThreadViewControllerDelegate
- (void)threadWasSelected:(TSThread *)thread
{
OWSAssert(self.attachment);
OWSAssert(thread);
self.thread = thread;
__weak typeof(self) weakSelf = self;
// FIXME SHARINGEXTENSION
// Handling safety number changes brings in a lot of machinery.
// How do we want to handle this?
// e.g. fingerprint scanning, etc. in the SAE or just redirect the user to the main app?
// BOOL didShowSNAlert =
// [SafetyNumberConfirmationAlert presentAlertIfNecessaryWithRecipientIds:thread.recipientIdentifiers
// confirmationText:[SafetyNumberStrings
// confirmSendButton]
// contactsManager:self.contactsManager
// completion:^(BOOL didConfirm) {
// if (didConfirm) {
// [weakSelf threadWasSelected:thread];
// }
// }];
// if (didShowSNAlert) {
// return;
// }
AttachmentApprovalViewController *approvalVC =
[[AttachmentApprovalViewController alloc] initWithAttachment:self.attachment delegate:self];
[self.navigationController pushViewController:approvalVC animated:YES];
// FIXME This implemenation was for the "import with signal" functionality,
// and is now broken. We need to be sure to remove the "import with signal" functionality.
// [SignalApp.sharedApp presentConversationForThread:thread];
}
#pragma mark - AttachmentApprovalViewControllerDelegate
- (void)didApproveAttachment
{
[ThreadUtil addThreadToProfileWhitelistIfEmptyContactThread:self.thread];
[ThreadUtil sendMessageWithAttachment:self.attachment inThread:self.thread messageSender:self.messageSender];
// FIXME SHARINGEXTENSION
// Show loading screen and dismiss entire share extnesion once entirely complete
[self.navigationController popViewControllerAnimated:YES];
}
- (void)didCancelAttachment
{
[self.navigationController popViewControllerAnimated:YES];
}
@end
NS_ASSUME_NONNULL_END

View File

@ -152,7 +152,7 @@ public class SignalAttachment: NSObject {
// This method should not be called directly; use the factory
// methods instead.
@objc
internal init(dataSource: DataSource, dataUTI: String) {
private init(dataSource: DataSource, dataUTI: String) {
self.dataSource = dataSource
self.dataUTI = dataUTI
super.init()