Move "attachment approval" into input toolbar.

// FREEBIE
pull/1/head
Matthew Chen 7 years ago
parent 7e41489d82
commit 0fe76aaab8

@ -4,6 +4,8 @@
NS_ASSUME_NONNULL_BEGIN
@class SignalAttachment;
@protocol ConversationInputToolbarDelegate <NSObject>
- (void)sendButtonPressed;
@ -20,6 +22,10 @@ NS_ASSUME_NONNULL_BEGIN
- (void)textViewDidChange;
#pragma mark - Attachment Approval
- (void)didApproveAttachment:(SignalAttachment *)attachment;
@end
#pragma mark -
@ -51,6 +57,12 @@ NS_ASSUME_NONNULL_BEGIN
- (void)cancelVoiceMemoIfNecessary;
#pragma mark - Attachment Approval
- (void)showApprovalUIForAttachment:(SignalAttachment *)attachment;
- (void)viewWillAppear:(BOOL)animated;
- (void)viewWillDisappear:(BOOL)animated;
@end
NS_ASSUME_NONNULL_END

@ -4,6 +4,7 @@
#import "ConversationInputToolbar.h"
#import "ConversationInputTextView.h"
#import "Signal-Swift.h"
#import "UIColor+OWS.h"
#import "UIFont+OWS.h"
#import "UIView+OWS.h"
@ -16,6 +17,7 @@ static void *kConversationInputTextViewObservingContext = &kConversationInputTex
@interface ConversationInputToolbar () <UIGestureRecognizerDelegate, ConversationTextViewToolbarDelegate>
@property (nonatomic, readonly) UIView *contentView;
@property (nonatomic, readonly) ConversationInputTextView *inputTextView;
@property (nonatomic, readonly) UIButton *attachmentButton;
@property (nonatomic, readonly) UIButton *sendButton;
@ -37,6 +39,12 @@ static void *kConversationInputTextViewObservingContext = &kConversationInputTex
@property (nonatomic) BOOL isRecordingVoiceMemo;
@property (nonatomic) CGPoint voiceMemoGestureStartLocation;
#pragma mark - Attachment Approval
@property (nonatomic) UIView *attachmentApprovalView;
@property (nonatomic, nullable) MediaMessageView *attachmentView;
@property (nonatomic, nullable) SignalAttachment *attachmentToApprove;
@end
#pragma mark -
@ -69,9 +77,13 @@ static void *kConversationInputTextViewObservingContext = &kConversationInputTex
[self addSubview:backgroundView];
[backgroundView autoPinEdgesToSuperviewEdges];
_contentView = [UIView containerView];
[self addSubview:self.contentView];
[self.contentView autoPinEdgesToSuperviewEdges];
_inputTextView = [ConversationInputTextView new];
self.inputTextView.textViewToolbarDelegate = self;
[self addSubview:self.inputTextView];
[self.contentView addSubview:self.inputTextView];
// We want to be permissive about taps on the send and attachment buttons,
// so we use wrapper views that capture nearby taps. This is a lot easier
@ -81,11 +93,11 @@ static void *kConversationInputTextViewObservingContext = &kConversationInputTex
_leftButtonWrapper = [UIView containerView];
[self.leftButtonWrapper
addGestureRecognizer:[[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(leftButtonTapped:)]];
[self addSubview:self.leftButtonWrapper];
[self.contentView addSubview:self.leftButtonWrapper];
_rightButtonWrapper = [UIView containerView];
[self.rightButtonWrapper
addGestureRecognizer:[[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(rightButtonTapped:)]];
[self addSubview:self.rightButtonWrapper];
[self.contentView addSubview:self.rightButtonWrapper];
_attachmentButton = [[UIButton alloc] init];
self.attachmentButton.accessibilityLabel
@ -118,6 +130,10 @@ static void *kConversationInputTextViewObservingContext = &kConversationInputTex
self.voiceMemoButton.imageView.tintColor = [UIColor ows_materialBlueColor];
[self.rightButtonWrapper addSubview:self.voiceMemoButton];
_attachmentApprovalView = [UIView containerView];
[self addSubview:self.attachmentApprovalView];
[self.attachmentApprovalView autoPinToSuperviewEdges];
// We want to be permissive about the voice message gesture, so we hang
// the long press GR on the button's wrapper, not the button itself.
UILongPressGestureRecognizer *longPressGestureRecognizer =
@ -192,6 +208,27 @@ static void *kConversationInputTextViewObservingContext = &kConversationInputTex
{
[NSLayoutConstraint deactivateConstraints:self.contentContraints];
if (self.attachmentToApprove) {
self.contentView.hidden = YES;
self.attachmentApprovalView.hidden = NO;
// Ensure the keyboard is dismissed.
[self.inputTextView resignFirstResponder];
self.contentContraints = @[
[self.attachmentApprovalView autoSetDimension:ALDimensionHeight toSize:300.f],
];
[self layoutIfNeeded];
return;
}
self.contentView.hidden = NO;
self.attachmentApprovalView.hidden = YES;
self.attachmentView = nil;
for (UIView *subview in self.attachmentApprovalView.subviews) {
[subview removeFromSuperview];
}
const int textViewVInset = 5;
const int contentHInset = 6;
const int contentHSpacing = 6;
@ -626,6 +663,99 @@ static void *kConversationInputTextViewObservingContext = &kConversationInputTex
}
}
#pragma mark - Attachment Approval
- (void)showApprovalUIForAttachment:(SignalAttachment *)attachment
{
OWSAssert(attachment);
self.attachmentToApprove = attachment;
MediaMessageView *attachmentView = [[MediaMessageView alloc] initWithAttachment:attachment];
self.attachmentView = attachmentView;
[self.attachmentApprovalView addSubview:attachmentView];
[attachmentView autoPinEdgeToSuperviewEdge:ALEdgeTop withInset:10];
[attachmentView autoPinWidthToSuperviewWithMargin:20];
UIView *buttonRow = [UIView containerView];
[self.attachmentApprovalView addSubview:buttonRow];
[buttonRow autoPinWidthToSuperviewWithMargin:20];
[buttonRow autoPinEdge:ALEdgeTop toEdge:ALEdgeBottom ofView:attachmentView withOffset:10];
[buttonRow autoPinEdgeToSuperviewEdge:ALEdgeBottom withInset:10];
// We use this invisible subview to ensure that the buttons are centered
// horizontally.
UIView *buttonSpacer = [UIView new];
[buttonRow addSubview:buttonSpacer];
// Vertical positioning of this view doesn't matter.
[buttonSpacer autoPinEdgeToSuperviewEdge:ALEdgeTop];
[buttonSpacer autoSetDimension:ALDimensionWidth toSize:ScaleFromIPhone5To7Plus(20, 30)];
[buttonSpacer autoSetDimension:ALDimensionHeight toSize:0];
[buttonSpacer autoHCenterInSuperview];
UIView *cancelButton = [self createAttachmentApprovalButton:[CommonStrings cancelButton]
color:[UIColor ows_destructiveRedColor]
selector:@selector(attachmentApprovalCancelPressed)];
[buttonRow addSubview:cancelButton];
[cancelButton autoPinHeightToSuperview];
[cancelButton autoPinEdge:ALEdgeRight toEdge:ALEdgeLeft ofView:buttonSpacer];
UIView *sendButton =
[self createAttachmentApprovalButton:NSLocalizedString(
@"ATTACHMENT_APPROVAL_SEND_BUTTON", comment
: @"Label for 'send' button in the 'attachment approval' dialog.")
color:[UIColor colorWithRGBHex:0x2ecc71]
selector:@selector(attachmentApprovalSendPressed)];
[buttonRow addSubview:sendButton];
[sendButton autoPinHeightToSuperview];
[sendButton autoPinEdge:ALEdgeLeft toEdge:ALEdgeRight ofView:buttonSpacer];
[self ensureContentConstraints];
}
- (UIView *)createAttachmentApprovalButton:(NSString *)title color:(UIColor *)color selector:(SEL)selector
{
const CGFloat buttonWidth = ScaleFromIPhone5To7Plus(110, 140);
const CGFloat buttonHeight = ScaleFromIPhone5To7Plus(35, 45);
return [OWSFlatButton buttonWithTitle:title
titleColor:[UIColor whiteColor]
backgroundColor:color
width:buttonWidth
height:buttonHeight
target:self
selector:selector];
}
- (void)attachmentApprovalCancelPressed
{
self.attachmentToApprove = nil;
[self ensureContentConstraints];
}
- (void)attachmentApprovalSendPressed
{
SignalAttachment *attachment = self.attachmentToApprove;
self.attachmentToApprove = nil;
if (attachment) {
[self.inputToolbarDelegate didApproveAttachment:attachment];
}
[self ensureContentConstraints];
}
- (void)viewWillAppear:(BOOL)animated
{
[self.attachmentView viewWillAppear:animated];
}
- (void)viewWillDisappear:(BOOL)animated
{
[self.attachmentView viewWillDisappear:animated];
}
#pragma mark - Logging
+ (NSString *)logTag

@ -555,6 +555,8 @@ typedef NS_ENUM(NSInteger, MessagesRangeSizeMode) {
// or on another device.
[self hideInputIfNeeded];
[self.inputToolbar viewWillAppear:animated];
self.isViewVisible = YES;
// We should have already requested contact access at this point, so this should be a no-op
@ -986,6 +988,8 @@ typedef NS_ENUM(NSInteger, MessagesRangeSizeMode) {
self.isViewVisible = NO;
[self.inputToolbar viewWillDisappear:animated];
[self.audioAttachmentPlayer stop];
self.audioAttachmentPlayer = nil;
@ -3448,18 +3452,18 @@ typedef NS_ENUM(NSInteger, MessagesRangeSizeMode) {
} else if (skipApprovalDialog) {
[self sendMessageAttachment:attachment];
} else {
UIViewController *viewController =
[[AttachmentApprovalViewController alloc] initWithAttachment:attachment
successCompletion:^{
[weakSelf sendMessageAttachment:attachment];
}];
UINavigationController *navigationController =
[[UINavigationController alloc] initWithRootViewController:viewController];
[self.navigationController presentViewController:navigationController animated:YES completion:nil];
[self.inputToolbar showApprovalUIForAttachment:attachment];
}
});
}
- (void)didApproveAttachment:(SignalAttachment *)attachment
{
OWSAssert(attachment);
[self sendMessageAttachment:attachment];
}
- (void)showErrorAlertForAttachment:(SignalAttachment *_Nullable)attachment
{
OWSAssert(attachment == nil || [attachment hasError]);

@ -62,8 +62,6 @@ class MediaMessageView: UIView, OWSAudioAttachmentPlayerDelegate {
// MARK: - Create Views
private func createViews() {
self.backgroundColor = UIColor.white
if attachment.isAnimatedImage {
createAnimatedPreview()
} else if attachment.isImage {
@ -89,15 +87,15 @@ class MediaMessageView: UIView, OWSAudioAttachmentPlayerDelegate {
subview.autoHCenterInSuperview()
if lastView == nil {
subview.autoPinEdge(toSuperviewEdge:.top)
subview.autoPinEdge(toSuperviewEdge: .top)
} else {
subview.autoPinEdge(.top, to:.bottom, of:lastView!, withOffset:10)
subview.autoPinEdge(.top, to: .bottom, of: lastView!, withOffset: 10)
}
lastView = subview
}
lastView?.autoPinEdge(toSuperviewEdge:.bottom)
lastView?.autoPinEdge(toSuperviewEdge: .bottom)
return stackView
}
@ -117,10 +115,10 @@ class MediaMessageView: UIView, OWSAudioAttachmentPlayerDelegate {
setAudioIconToPlay()
audioPlayButton.imageView?.layer.minificationFilter = kCAFilterTrilinear
audioPlayButton.imageView?.layer.magnificationFilter = kCAFilterTrilinear
audioPlayButton.addTarget(self, action:#selector(audioPlayButtonPressed), for:.touchUpInside)
audioPlayButton.addTarget(self, action: #selector(audioPlayButtonPressed), for: .touchUpInside)
let buttonSize = createHeroViewSize()
audioPlayButton.autoSetDimension(.width, toSize:buttonSize)
audioPlayButton.autoSetDimension(.height, toSize:buttonSize)
audioPlayButton.autoSetDimension(.width, toSize: buttonSize)
audioPlayButton.autoSetDimension(.height, toSize: buttonSize)
subviews.append(audioPlayButton)
let fileNameLabel = createFileNameLabel()
@ -136,7 +134,7 @@ class MediaMessageView: UIView, OWSAudioAttachmentPlayerDelegate {
updateAudioStatusLabel()
subviews.append(audioStatusLabel)
let stackView = wrapViewsInVerticalStack(subviews:subviews)
let stackView = wrapViewsInVerticalStack(subviews: subviews)
self.addSubview(stackView)
fileNameLabel?.autoPinWidthToSuperview(withMargin: 32)
stackView.autoPinWidthToSuperview()
@ -152,7 +150,7 @@ class MediaMessageView: UIView, OWSAudioAttachmentPlayerDelegate {
createGenericPreview()
return
}
guard let image = YYImage(contentsOfFile:dataUrl.path) else {
guard let image = YYImage(contentsOfFile: dataUrl.path) else {
createGenericPreview()
return
}
@ -166,14 +164,14 @@ class MediaMessageView: UIView, OWSAudioAttachmentPlayerDelegate {
private func createImagePreview() {
var image = attachment.image
if image == nil {
image = UIImage(data:attachment.data)
image = UIImage(data: attachment.data)
}
guard image != nil else {
createGenericPreview()
return
}
let imageView = UIImageView(image:image)
let imageView = UIImageView(image: image)
imageView.layer.minificationFilter = kCAFilterTrilinear
imageView.layer.magnificationFilter = kCAFilterTrilinear
imageView.contentMode = .scaleAspectFit
@ -186,7 +184,7 @@ class MediaMessageView: UIView, OWSAudioAttachmentPlayerDelegate {
createGenericPreview()
return
}
guard let videoPlayer = MPMoviePlayerController(contentURL:dataUrl) else {
guard let videoPlayer = MPMoviePlayerController(contentURL: dataUrl) else {
createGenericPreview()
return
}
@ -214,7 +212,7 @@ class MediaMessageView: UIView, OWSAudioAttachmentPlayerDelegate {
let fileSizeLabel = createFileSizeLabel()
subviews.append(fileSizeLabel)
let stackView = wrapViewsInVerticalStack(subviews:subviews)
let stackView = wrapViewsInVerticalStack(subviews: subviews)
self.addSubview(stackView)
fileNameLabel?.autoPinWidthToSuperview(withMargin: 32)
stackView.autoPinWidthToSuperview()
@ -227,9 +225,9 @@ class MediaMessageView: UIView, OWSAudioAttachmentPlayerDelegate {
private func createHeroImageView(imageName: String) -> UIView {
let imageSize = createHeroViewSize()
let image = UIImage(named:imageName)
let image = UIImage(named: imageName)
assert(image != nil)
let imageView = UIImageView(image:image)
let imageView = UIImageView(image: image)
imageView.layer.minificationFilter = kCAFilterTrilinear
imageView.layer.magnificationFilter = kCAFilterTrilinear
imageView.layer.shadowColor = UIColor.black.cgColor
@ -237,14 +235,14 @@ class MediaMessageView: UIView, OWSAudioAttachmentPlayerDelegate {
imageView.layer.shadowRadius = CGFloat(2.0 * shadowScaling)
imageView.layer.shadowOpacity = 0.25
imageView.layer.shadowOffset = CGSize(width: 0.75 * shadowScaling, height: 0.75 * shadowScaling)
imageView.autoSetDimension(.width, toSize:imageSize)
imageView.autoSetDimension(.height, toSize:imageSize)
imageView.autoSetDimension(.width, toSize: imageSize)
imageView.autoSetDimension(.height, toSize: imageSize)
return imageView
}
private func labelFont() -> UIFont {
return UIFont.ows_regularFont(withSize:ScaleFromIPhone5To7Plus(18, 24))
return UIFont.ows_regularFont(withSize: ScaleFromIPhone5To7Plus(18, 24))
}
private func formattedFileExtension() -> String? {
@ -252,7 +250,7 @@ class MediaMessageView: UIView, OWSAudioAttachmentPlayerDelegate {
return nil
}
return String(format:NSLocalizedString("ATTACHMENT_APPROVAL_FILE_EXTENSION_FORMAT",
return String(format: NSLocalizedString("ATTACHMENT_APPROVAL_FILE_EXTENSION_FORMAT",
comment: "Format string for file extension label in call interstitial view"),
fileExtension.uppercased())
}
@ -287,7 +285,7 @@ class MediaMessageView: UIView, OWSAudioAttachmentPlayerDelegate {
private func createFileSizeLabel() -> UIView {
let label = UILabel()
let fileSize = attachment.dataLength
label.text = String(format:NSLocalizedString("ATTACHMENT_APPROVAL_FILE_SIZE_FORMAT",
label.text = String(format: NSLocalizedString("ATTACHMENT_APPROVAL_FILE_SIZE_FORMAT",
comment: "Format string for file size label in call interstitial view. Embeds: {{file size as 'N mb' or 'N kb'}}."),
ViewControllerUtils.formatFileSize(UInt(fileSize)))
@ -346,7 +344,7 @@ class MediaMessageView: UIView, OWSAudioAttachmentPlayerDelegate {
let isAudioPlaying = playbackState == .playing
if isAudioPlaying && audioProgressSeconds > 0 && audioDurationSeconds > 0 {
audioStatusLabel.text = String(format:"%@ / %@",
audioStatusLabel.text = String(format: "%@ / %@",
ViewControllerUtils.formatDurationSeconds(Int(round(self.audioProgressSeconds))),
ViewControllerUtils.formatDurationSeconds(Int(round(self.audioDurationSeconds))))
} else {
@ -355,16 +353,16 @@ class MediaMessageView: UIView, OWSAudioAttachmentPlayerDelegate {
}
private func setAudioIconToPlay() {
let image = UIImage(named:"audio_play_black_large")?.withRenderingMode(.alwaysTemplate)
let image = UIImage(named: "audio_play_black_large")?.withRenderingMode(.alwaysTemplate)
assert(image != nil)
audioPlayButton?.setImage(image, for:.normal)
audioPlayButton?.setImage(image, for: .normal)
audioPlayButton?.imageView?.tintColor = UIColor.ows_materialBlue()
}
private func setAudioIconToPause() {
let image = UIImage(named:"audio_pause_black_large")?.withRenderingMode(.alwaysTemplate)
let image = UIImage(named: "audio_pause_black_large")?.withRenderingMode(.alwaysTemplate)
assert(image != nil)
audioPlayButton?.setImage(image, for:.normal)
audioPlayButton?.setImage(image, for: .normal)
audioPlayButton?.imageView?.tintColor = UIColor.ows_materialBlue()
}
}

@ -383,6 +383,7 @@ class MessageMetadataViewController: OWSViewController {
if let dataUTI = MIMETypeUtil.utiType(forMIMEType: contentType) {
let attachment = SignalAttachment(dataSource: dataSource, dataUTI: dataUTI)
let mediaMessageView = MediaMessageView(attachment: attachment)
mediaMessageView.backgroundColor = UIColor.white
self.mediaMessageView = mediaMessageView
rows.append(mediaMessageView)
}

Loading…
Cancel
Save