Use FullSreen media VC for message details

// FREEBIE
pull/1/head
Michael Kirk 7 years ago
parent c7c433c59c
commit 8454e512d8

@ -4,6 +4,7 @@
#import "FullImageViewController.h"
#import "AttachmentSharing.h"
#import "ConversationViewController.h"
#import "ConversationViewItem.h"
#import "Signal-Swift.h"
#import "TSAttachmentStream.h"
@ -19,8 +20,6 @@
NS_ASSUME_NONNULL_BEGIN
#define kMaxZoomScale 8.0f
// In order to use UIMenuController, the view from which it is
// presented must have certain custom behaviors.
@interface AttachmentMenuView : UIView
@ -132,7 +131,11 @@ NS_ASSUME_NONNULL_BEGIN
if (self.attachmentStream) {
return self.attachmentStream.image;
} else if (self.attachment) {
return self.attachment.image;
if (self.isVideo) {
return self.attachment.videoPreview;
} else {
return self.attachment.image;
}
} else {
return nil;
}
@ -220,6 +223,7 @@ NS_ASSUME_NONNULL_BEGIN
if (minScale != self.scrollView.minimumZoomScale) {
self.scrollView.minimumZoomScale = minScale;
self.scrollView.maximumZoomScale = minScale * 8;
self.scrollView.zoomScale = minScale;
}
}
@ -235,8 +239,6 @@ NS_ASSUME_NONNULL_BEGIN
self.scrollView = scrollView;
scrollView.delegate = self;
// TODO set max based on MIN.
scrollView.maximumZoomScale = kMaxZoomScale;
scrollView.showsVerticalScrollIndicator = NO;
scrollView.showsHorizontalScrollIndicator = NO;
scrollView.decelerationRate = UIScrollViewDecelerationRateFast;
@ -247,7 +249,7 @@ NS_ASSUME_NONNULL_BEGIN
if (self.isAnimated) {
if ([self.fileData ows_isValidImage]) {
YYImage *animatedGif = [YYImage imageWithData:self.fileData];
YYAnimatedImageView *animatedView = [[YYAnimatedImageView alloc] init];
YYAnimatedImageView *animatedView = [YYAnimatedImageView new];
animatedView.image = animatedGif;
self.imageView = animatedView;
} else {
@ -308,21 +310,27 @@ NS_ASSUME_NONNULL_BEGIN
[playVideoButton autoCenterInSuperview];
}
UIToolbar *footerBar = [UIToolbar new];
_footerBar = footerBar;
footerBar.barTintColor = [UIColor ows_signalBrandBlueColor];
self.videoPlayBarButton = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemPlay
target:self
action:@selector(didPressPlayBarButton:)];
self.videoPauseBarButton = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemPause
target:self
action:@selector(didPressPauseBarButton:)];
[self updateFooterBarButtonItemsWithIsPlayingVideo:YES];
[self.view addSubview:footerBar];
[footerBar autoPinWidthToSuperview];
[footerBar autoPinToBottomLayoutGuideOfViewController:self withInset:0];
[footerBar autoSetDimension:ALDimensionHeight toSize:kFooterHeight];
// Don't show footer bar after tapping approval-view
if (self.viewItem) {
UIToolbar *footerBar = [UIToolbar new];
_footerBar = footerBar;
footerBar.barTintColor = [UIColor ows_signalBrandBlueColor];
self.videoPlayBarButton =
[[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemPlay
target:self
action:@selector(didPressPlayBarButton:)];
self.videoPauseBarButton =
[[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemPause
target:self
action:@selector(didPressPauseBarButton:)];
[self updateFooterBarButtonItemsWithIsPlayingVideo:YES];
[self.view addSubview:footerBar];
[footerBar autoPinWidthToSuperview];
[footerBar autoPinToBottomLayoutGuideOfViewController:self withInset:0];
[footerBar autoSetDimension:ALDimensionHeight toSize:kFooterHeight];
}
}
- (void)updateFooterBarButtonItemsWithIsPlayingVideo:(BOOL)isPlayingVideo
@ -438,8 +446,8 @@ NS_ASSUME_NONNULL_BEGIN
_areToolbarsHidden = areToolbarsHidden;
// Hiding the status bar affects the positioing of the navbar. We don't want to show that in the animation
// so when *showing* the toolbars, we show the status bar first. When hiding, we hide it last.
// Hiding the status bar affects the positioing of the navbar. We don't want to show that in an animation, it's
// better to just have everythign "flit" in/out.
[[UIApplication sharedApplication] setStatusBarHidden:areToolbarsHidden withAnimation:UIStatusBarAnimationNone];
[self.navigationController setNavigationBarHidden:areToolbarsHidden animated:NO];
self.videoProgressBar.hidden = areToolbarsHidden;
@ -490,7 +498,7 @@ NS_ASSUME_NONNULL_BEGIN
- (void)didTapDismissButton:(id)sender
{
[self dismiss];
[self dismissSelfAnimated:YES completion:nil];
}
- (void)didTapImage:(id)sender
@ -519,7 +527,7 @@ NS_ASSUME_NONNULL_BEGIN
return;
}
[self dismiss];
[self dismissSelfAnimated:YES completion:nil];
}
- (void)longPressGesture:(UIGestureRecognizer *)sender {
@ -551,23 +559,56 @@ NS_ASSUME_NONNULL_BEGIN
- (void)didPressShare:(id)sender
{
DDLogInfo(@"%@: sharing image.", self.logTag);
DDLogInfo(@"%@: didPressShare", self.logTag);
if (!self.viewItem) {
OWSFail(@"share should only be available when a viewItem is present");
return;
}
[self.viewItem shareAction];
}
- (void)didPressDelete:(id)sender
{
DDLogInfo(@"%@: sharing image.", self.logTag);
DDLogInfo(@"%@: didPressDelete", self.logTag);
if (!self.viewItem) {
OWSFail(@"delete should only be available when a viewItem is present");
return;
}
UIAlertController *actionSheet =
[UIAlertController alertControllerWithTitle:nil message:nil preferredStyle:UIAlertControllerStyleActionSheet];
[actionSheet addAction:[UIAlertAction actionWithTitle:NSLocalizedString(@"TXT_DELETE_TITLE", nil)
style:UIAlertActionStyleDestructive
handler:^(UIAlertAction *action) {
[self.viewItem deleteAction];
[self dismiss];
}]];
[actionSheet
addAction:[UIAlertAction actionWithTitle:NSLocalizedString(@"TXT_DELETE_TITLE", nil)
style:UIAlertActionStyleDestructive
handler:^(UIAlertAction *action) {
OWSAssert([self.presentingViewController
isKindOfClass:[UINavigationController class]]);
UINavigationController *navController
= (UINavigationController *)self.presentingViewController;
if ([navController.topViewController
isKindOfClass:[ConversationViewController class]]) {
[self dismissSelfAnimated:YES
completion:^{
[self.viewItem deleteAction];
}];
} else if ([navController.topViewController
isKindOfClass:[MessageDetailViewController class]]) {
[self dismissSelfAnimated:NO
completion:^{
[self.viewItem deleteAction];
}];
[navController popViewControllerAnimated:YES];
} else {
OWSFail(@"Unexpected presentation context.");
[self dismissSelfAnimated:YES
completion:^{
[self.viewItem deleteAction];
}];
}
}]];
[actionSheet addAction:[OWSAlerts cancelAction]];
@ -576,6 +617,10 @@ NS_ASSUME_NONNULL_BEGIN
- (BOOL)canPerformAction:(SEL)action withSender:(nullable id)sender
{
if (self.viewItem == nil) {
return NO;
}
if (action == self.viewItem.metadataActionSelector) {
return NO;
}
@ -584,21 +629,41 @@ NS_ASSUME_NONNULL_BEGIN
- (void)copyAction:(nullable id)sender
{
if (!self.viewItem) {
OWSFail(@"copy should only be available when a viewItem is present");
return;
}
[self.viewItem copyAction];
}
- (void)shareAction:(nullable id)sender
{
[self.viewItem shareAction];
if (!self.viewItem) {
OWSFail(@"share should only be available when a viewItem is present");
return;
}
[self didPressShare:sender];
}
- (void)saveAction:(nullable id)sender
{
if (!self.viewItem) {
OWSFail(@"save should only be available when a viewItem is present");
return;
}
[self.viewItem saveAction];
}
- (void)deleteAction:(nullable id)sender
{
if (!self.viewItem) {
OWSFail(@"delete should only be available when a viewItem is present");
return;
}
[self didPressDelete:sender];
}
@ -674,7 +739,7 @@ NS_ASSUME_NONNULL_BEGIN
}];
}
- (void)dismiss
- (void)dismissSelfAnimated:(BOOL)isAnimated completion:(void (^_Nullable)(void))completion
{
self.view.userInteractionEnabled = NO;
[UIApplication sharedApplication].statusBarHidden = NO;
@ -686,21 +751,26 @@ NS_ASSUME_NONNULL_BEGIN
// Move the image view pack to it's initial position, i.e. where
// it sits on the screen in the conversation view.
[self applyInitialImageViewConstraints];
[UIView animateWithDuration:0.2
delay:0.0
options:UIViewAnimationOptionCurveEaseInOut
animations:^(void) {
[self.imageView.superview layoutIfNeeded];
// In case user has hidden bars, which changes background to black.
self.view.backgroundColor = UIColor.whiteColor;
// fade out content and toolbars
self.navigationController.view.alpha = 0.0;
}
completion:^(BOOL finished) {
[self.presentingViewController dismissViewControllerAnimated:NO completion:nil];
}];
if (isAnimated) {
[UIView animateWithDuration:0.2
delay:0.0
options:UIViewAnimationOptionCurveEaseInOut
animations:^(void) {
[self.imageView.superview layoutIfNeeded];
// In case user has hidden bars, which changes background to black.
self.view.backgroundColor = UIColor.whiteColor;
// fade out content and toolbars
self.navigationController.view.alpha = 0.0;
}
completion:^(BOOL finished) {
[self.presentingViewController dismissViewControllerAnimated:NO completion:completion];
}];
} else {
[self.presentingViewController dismissViewControllerAnimated:NO completion:completion];
}
}
#pragma mark - UIScrollViewDelegate
@ -773,7 +843,6 @@ NS_ASSUME_NONNULL_BEGIN
OWSAssert(self.videoPlayer);
DDLogVerbose(@"%@ %s", self.logTag, __PRETTY_FUNCTION__);
// [self dismissViewControllerAnimated:NO completion:nil];
self.areToolbarsHidden = NO;
self.playVideoButton.hidden = NO;

@ -1,5 +1,5 @@
//
// Copyright (c) 2017 Open Whisper Systems. All rights reserved.
// Copyright (c) 2018 Open Whisper Systems. All rights reserved.
//
import Foundation
@ -19,6 +19,11 @@ class MediaMessageView: UIView, OWSAudioAttachmentPlayerDelegate {
let mode: MediaMessageViewMode
// If this is for a persisted interaction, viewItem and attachmentStream will not be nil
// If this is for an attachmet draft, before sending, viewItem and attachmentStream will be nil
var viewItem: ConversationViewItem?
var attachmentStream: TSAttachmentStream?
let attachment: SignalAttachment
var videoPlayer: MPMoviePlayerController?
@ -45,10 +50,16 @@ class MediaMessageView: UIView, OWSAudioAttachmentPlayerDelegate {
fatalError("\(#function) is unimplemented.")
}
required init(attachment: SignalAttachment, mode: MediaMessageViewMode) {
convenience init(attachment: SignalAttachment, mode: MediaMessageViewMode) {
self.init(attachment: attachment, mode: mode, viewItem: nil, attachmentStream: nil)
}
required init(attachment: SignalAttachment, mode: MediaMessageViewMode, viewItem: ConversationViewItem?, attachmentStream: TSAttachmentStream?) {
assert(!attachment.hasError)
self.mode = mode
self.attachment = attachment
self.mode = mode
self.viewItem = viewItem
self.attachmentStream = attachmentStream
super.init(frame: CGRect.zero)
createViews()
@ -401,13 +412,7 @@ class MediaMessageView: UIView, OWSAudioAttachmentPlayerDelegate {
guard let fromView = sender.view else {
return
}
guard let fromViewController = fromViewController() else {
return
}
let window = UIApplication.shared.keyWindow
let convertedRect = fromView.convert(fromView.bounds, to:window)
let viewController = FullImageViewController(attachment:attachment, from:convertedRect)
viewController.present(from:fromViewController)
showFullImageViewController(fromView: fromView)
}
private func fromViewController() -> UIViewController? {
@ -429,44 +434,28 @@ class MediaMessageView: UIView, OWSAudioAttachmentPlayerDelegate {
guard sender.state == .recognized else {
return
}
guard let dataUrl = attachment.dataUrl else {
return
}
guard let videoPlayer = MPMoviePlayerController(contentURL: dataUrl) else {
guard let fromView = sender.view else {
return
}
videoPlayer.prepareToPlay()
NotificationCenter.default.addObserver(forName: .MPMoviePlayerWillExitFullscreen, object: nil, queue: nil) { [weak self] _ in
self?.moviePlayerWillExitFullscreen()
}
NotificationCenter.default.addObserver(forName: .MPMoviePlayerDidExitFullscreen, object: nil, queue: nil) { [weak self] _ in
self?.moviePlayerDidExitFullscreen()
}
videoPlayer.controlStyle = .default
videoPlayer.shouldAutoplay = true
self.addSubview(videoPlayer.view)
videoPlayer.view.frame = self.bounds
self.videoPlayer = videoPlayer
videoPlayer.view.autoPinToSuperviewEdges()
ViewControllerUtils.setAudioIgnoresHardwareMuteSwitch(true)
videoPlayer.setFullscreen(true, animated:false)
showFullImageViewController(fromView: fromView)
}
private func moviePlayerWillExitFullscreen() {
clearVideoPlayer()
}
func showFullImageViewController(fromView: UIView) {
guard let fromViewController = fromViewController() else {
return
}
let window = UIApplication.shared.keyWindow
let convertedRect = fromView.convert(fromView.bounds, to:window)
private func moviePlayerDidExitFullscreen() {
clearVideoPlayer()
}
let viewController: FullImageViewController = {
if let viewItem = self.viewItem, let attachmentStream = self.attachmentStream {
return FullImageViewController(attachmentStream: attachmentStream, from: convertedRect, viewItem: viewItem)
} else {
// e.g. when MediaMessageView does not belong to a persisted interaction, e.g. approval view.
return FullImageViewController(attachment: attachment, from:convertedRect)
}
}()
private func clearVideoPlayer() {
videoPlayer?.stop()
videoPlayer?.view.removeFromSuperview()
videoPlayer = nil
ViewControllerUtils.setAudioIgnoresHardwareMuteSwitch(false)
viewController.present(from:fromViewController)
}
}

@ -1,5 +1,5 @@
//
// Copyright (c) 2017 Open Whisper Systems. All rights reserved.
// Copyright (c) 2018 Open Whisper Systems. All rights reserved.
//
import Foundation
@ -406,7 +406,7 @@ class MessageDetailViewController: OWSViewController, UIScrollViewDelegate {
}
guard let attachment = TSAttachment.fetch(uniqueId: attachmentId) else {
owsFail("Missing attachment")
Logger.warn("\(TAG) Missing attachment. Was it deleted?")
return rows
}
self.attachment = attachment
@ -433,7 +433,7 @@ class MessageDetailViewController: OWSViewController, UIScrollViewDelegate {
let contentType = attachment.contentType
if let dataUTI = MIMETypeUtil.utiType(forMIMEType: contentType) {
let attachment = SignalAttachment(dataSource: dataSource, dataUTI: dataUTI)
let mediaMessageView = MediaMessageView(attachment: attachment, mode: .small)
let mediaMessageView = MediaMessageView(attachment: attachment, mode: .small, viewItem: viewItem, attachmentStream: attachmentStream)
mediaMessageView.backgroundColor = UIColor.white
self.mediaMessageView = mediaMessageView
rows.append(mediaMessageView)

Loading…
Cancel
Save