Merge branch 'charlesmchen/attachmentViews'

pull/1/head
Matthew Chen 8 years ago
commit b1f5540938

@ -18,7 +18,6 @@
#import "OWSWebRTCDataProtos.pb.h" #import "OWSWebRTCDataProtos.pb.h"
#import "PhoneManager.h" #import "PhoneManager.h"
#import "PropertyListPreferences.h" #import "PropertyListPreferences.h"
#import "PureLayout.h"
#import "PushManager.h" #import "PushManager.h"
#import "RPAccountManager.h" #import "RPAccountManager.h"
#import "TSSocketManager.h" #import "TSSocketManager.h"
@ -27,6 +26,7 @@
#import "UIUtil.h" #import "UIUtil.h"
#import "UIView+OWS.h" #import "UIView+OWS.h"
#import <JSQSystemSoundPlayer.h> #import <JSQSystemSoundPlayer.h>
#import <PureLayout/PureLayout.h>
#import <SignalServiceKit/Contact.h> #import <SignalServiceKit/Contact.h>
#import <SignalServiceKit/ContactsUpdater.h> #import <SignalServiceKit/ContactsUpdater.h>
#import <SignalServiceKit/Cryptography.h> #import <SignalServiceKit/Cryptography.h>

@ -2,6 +2,7 @@
// Copyright (c) 2017 Open Whisper Systems. All rights reserved. // Copyright (c) 2017 Open Whisper Systems. All rights reserved.
// //
#import <PureLayout/PureLayout.h>
#import <UIKit/UIKit.h> #import <UIKit/UIKit.h>
// A convenience method for doing responsive layout. Scales between two // A convenience method for doing responsive layout. Scales between two

@ -2,7 +2,6 @@
// Copyright (c) 2017 Open Whisper Systems. All rights reserved. // Copyright (c) 2017 Open Whisper Systems. All rights reserved.
// //
#import "PureLayout.h"
#import "UIView+OWS.h" #import "UIView+OWS.h"
// TODO: We'll eventually want to promote these into an OWSMath.h header. // TODO: We'll eventually want to promote these into an OWSMath.h header.

@ -1,20 +1,18 @@
// //
// FullImageViewController.h // Copyright (c) 2017 Open Whisper Systems. All rights reserved.
// Signal
//
// Created by Dylan Bourgeois on 11/11/14.
// Copyright (c) 2014 Open Whisper Systems. All rights reserved.
// //
#import <UIKit/UIKit.h> #import <UIKit/UIKit.h>
#import "TSAttachmentStream.h" #import "TSAttachmentStream.h"
#import "TSInteraction.h" #import "TSInteraction.h"
#import "OWSMessageData.h"
@interface FullImageViewController : UIViewController @interface FullImageViewController : UIViewController
- (instancetype)initWithAttachment:(TSAttachmentStream *)attachment - (instancetype)initWithAttachment:(TSAttachmentStream *)attachment
fromRect:(CGRect)rect fromRect:(CGRect)rect
forInteraction:(TSInteraction *)interaction forInteraction:(TSInteraction *)interaction
messageItem:(id<OWSMessageData>)messageItem
isAnimated:(BOOL)animated; isAnimated:(BOOL)animated;
- (void)presentFromViewController:(UIViewController *)viewController; - (void)presentFromViewController:(UIViewController *)viewController;

@ -1,44 +1,60 @@
// //
// FullImageViewController.m // Copyright (c) 2017 Open Whisper Systems. All rights reserved.
// Signal
//
// Created by Dylan Bourgeois on 11/11/14.
// Animated GIF support added by Mike Okner (@mikeokner) on 11/27/15.
// Copyright (c) 2014 Open Whisper Systems. All rights reserved.
// //
#import "FLAnimatedImage.h" #import "FLAnimatedImage.h"
#import "FullImageViewController.h" #import "FullImageViewController.h"
#import "UIUtil.h" #import "UIUtil.h"
#import "UIView+OWS.h"
#define kImageViewCornerRadius 5.0f #import "TSPhotoAdapter.h"
#import "TSMessageAdapter.h"
#import "TSAnimatedAdapter.h"
#define kMinZoomScale 1.0f #define kMinZoomScale 1.0f
#define kMaxZoomScale 8.0f #define kMaxZoomScale 8.0f
#define kTargetDoubleTapZoom 3.0f
#define kBackgroundAlpha 0.6f #define kBackgroundAlpha 0.6f
@interface FullImageViewController () <UIScrollViewDelegate, UIGestureRecognizerDelegate> // In order to use UIMenuController, the view from which it is
// presented must have certain custom behaviors.
@interface AttachmentMenuView : UIView
@property (nonatomic, strong) UIView *backgroundView; @end
@property (nonatomic, strong) UIScrollView *scrollView; #pragma mark -
@property (nonatomic, strong) UIImageView *imageView; @implementation AttachmentMenuView
@property (nonatomic, strong) UITapGestureRecognizer *singleTap; - (BOOL)canBecomeFirstResponder {
@property (nonatomic, strong) UITapGestureRecognizer *doubleTap; return YES;
}
@property (nonatomic, strong) UIButton *shareButton; // We only use custom actions in UIMenuController.
- (BOOL)canPerformAction:(SEL)action
withSender:(id)sender
{
return NO;
}
@end
@property CGRect originRect; #pragma mark -
@property BOOL isPresenting;
@property BOOL isAnimated;
@property NSData *fileData;
@property TSAttachmentStream *attachment; @interface FullImageViewController () <UIScrollViewDelegate, UIGestureRecognizerDelegate>
@property TSInteraction *interaction;
@property (nonatomic) UIView *backgroundView;
@property (nonatomic) UIScrollView *scrollView;
@property (nonatomic) UIImageView *imageView;
@property (nonatomic) UIButton *shareButton;
@property (nonatomic) CGRect originRect;
@property (nonatomic) BOOL isPresenting;
@property (nonatomic) BOOL isAnimated;
@property (nonatomic) NSData *fileData;
@property (nonatomic) TSAttachmentStream *attachment;
@property (nonatomic) TSInteraction *interaction;
@property (nonatomic) id<OWSMessageData> messageItem;
@end @end
@ -48,6 +64,7 @@
- (instancetype)initWithAttachment:(TSAttachmentStream *)attachment - (instancetype)initWithAttachment:(TSAttachmentStream *)attachment
fromRect:(CGRect)rect fromRect:(CGRect)rect
forInteraction:(TSInteraction *)interaction forInteraction:(TSInteraction *)interaction
messageItem:(id<OWSMessageData>)messageItem
isAnimated:(BOOL)animated { isAnimated:(BOOL)animated {
self = [super initWithNibName:nil bundle:nil]; self = [super initWithNibName:nil bundle:nil];
@ -55,6 +72,7 @@
self.attachment = attachment; self.attachment = attachment;
self.originRect = rect; self.originRect = rect;
self.interaction = interaction; self.interaction = interaction;
self.messageItem = messageItem;
self.isAnimated = animated; self.isAnimated = animated;
self.fileData = [NSData dataWithContentsOfURL:[attachment mediaURL]]; self.fileData = [NSData dataWithContentsOfURL:[attachment mediaURL]];
} }
@ -66,6 +84,12 @@
return self.attachment.image; return self.attachment.image;
} }
- (void)loadView {
self.view = [AttachmentMenuView new];
self.view.backgroundColor = [UIColor colorWithWhite:0 alpha:kBackgroundAlpha];
self.view.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
}
- (void)viewDidLoad { - (void)viewDidLoad {
[super viewDidLoad]; [super viewDidLoad];
@ -77,16 +101,24 @@
[self populateImageView:self.image]; [self populateImageView:self.image];
} }
- (void)viewWillDisappear:(BOOL)animated {
[super viewWillDisappear:animated];
if ([UIMenuController sharedMenuController].isMenuVisible) {
[[UIMenuController sharedMenuController] setMenuVisible:NO
animated:NO];
}
}
#pragma mark - Initializers #pragma mark - Initializers
- (void)initializeBackground { - (void)initializeBackground {
self.imageView.backgroundColor = [UIColor colorWithWhite:0 alpha:kBackgroundAlpha]; self.imageView.backgroundColor = [UIColor colorWithWhite:0 alpha:kBackgroundAlpha];
self.view.backgroundColor = [UIColor colorWithWhite:0 alpha:kBackgroundAlpha];
self.view.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; self.backgroundView = [UIView new];
self.backgroundView = [[UIView alloc] initWithFrame:CGRectInset(self.view.bounds, -512, -512)];
self.backgroundView.backgroundColor = [UIColor colorWithWhite:0 alpha:kBackgroundAlpha]; self.backgroundView.backgroundColor = [UIColor colorWithWhite:0 alpha:kBackgroundAlpha];
[self.view addSubview:self.backgroundView]; [self.view addSubview:self.backgroundView];
[self.backgroundView autoPinEdgesToSuperviewEdges];
} }
- (void)initializeScrollView { - (void)initializeScrollView {
@ -111,7 +143,6 @@
} else { } else {
// Present the static image using standard UIImageView // Present the static image using standard UIImageView
self.imageView = [[UIImageView alloc] initWithFrame:self.originRect]; self.imageView = [[UIImageView alloc] initWithFrame:self.originRect];
self.imageView.layer.cornerRadius = kImageViewCornerRadius;
self.imageView.contentMode = UIViewContentModeScaleAspectFill; self.imageView.contentMode = UIViewContentModeScaleAspectFill;
self.imageView.userInteractionEnabled = YES; self.imageView.userInteractionEnabled = YES;
self.imageView.clipsToBounds = YES; self.imageView.clipsToBounds = YES;
@ -128,56 +159,106 @@
} }
- (void)initializeGestureRecognizers { - (void)initializeGestureRecognizers {
self.doubleTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(imageDoubleTapped:)]; UITapGestureRecognizer *singleTap = [[UITapGestureRecognizer alloc] initWithTarget:self
self.doubleTap.numberOfTapsRequired = 2; action:@selector(imageDismissGesture:)];
singleTap.delegate = self;
self.singleTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(imageSingleTapped:)]; [self.view addGestureRecognizer:singleTap];
[self.singleTap requireGestureRecognizerToFail:self.doubleTap];
UITapGestureRecognizer *doubleTap = [[UITapGestureRecognizer alloc] initWithTarget:self
self.singleTap.delegate = self; action:@selector(imageDismissGesture:)];
self.doubleTap.delegate = self; doubleTap.numberOfTapsRequired = 2;
doubleTap.delegate = self;
[self.view addGestureRecognizer:doubleTap];
// UISwipeGestureRecognizer supposedly supports multiple directions,
// but in practice it works better if you use a separate GR for each
// direction.
for (NSNumber *direction in @[
@(UISwipeGestureRecognizerDirectionRight),
@(UISwipeGestureRecognizerDirectionLeft),
@(UISwipeGestureRecognizerDirectionUp),
@(UISwipeGestureRecognizerDirectionDown),
]) {
UISwipeGestureRecognizer *swipe = [[UISwipeGestureRecognizer alloc] initWithTarget:self
action:@selector(imageDismissGesture:)];
swipe.direction = (UISwipeGestureRecognizerDirection) direction.integerValue;
swipe.delegate = self;
[self.view addGestureRecognizer:swipe];
}
[self.view addGestureRecognizer:self.singleTap]; UILongPressGestureRecognizer *longPress = [[UILongPressGestureRecognizer alloc] initWithTarget:self
[self.view addGestureRecognizer:self.doubleTap]; action:@selector(longPressGesture:)];
longPress.delegate = self;
[self.view addGestureRecognizer:longPress];
} }
#pragma mark - Gesture Recognizers #pragma mark - Gesture Recognizers
- (void)imageDoubleTapped:(UITapGestureRecognizer *)doubleTap { - (void)imageDismissGesture:(UIGestureRecognizer *)sender {
CGPoint tap = [doubleTap locationInView:doubleTap.view]; if (sender.state == UIGestureRecognizerStateRecognized) {
CGPoint convertCoord = [self.scrollView convertPoint:tap fromView:doubleTap.view]; [self dismiss];
CGRect targetZoomRect; }
UIEdgeInsets targetInsets; }
CGSize zoom; - (void)longPressGesture:(UIGestureRecognizer *)sender {
// We "eagerly" respond when the long press begins, not when it ends.
if (sender.state == UIGestureRecognizerStateBegan) {
if (self.scrollView.zoomScale == 1.0f) { [self.view becomeFirstResponder];
zoom = CGSizeMake(self.view.bounds.size.width / kTargetDoubleTapZoom,
self.view.bounds.size.height / kTargetDoubleTapZoom); if ([UIMenuController sharedMenuController].isMenuVisible) {
targetZoomRect = CGRectMake( [[UIMenuController sharedMenuController] setMenuVisible:NO
convertCoord.x - (zoom.width / 2.0f), convertCoord.y - (zoom.height / 2.0f), zoom.width, zoom.height); animated:NO];
targetInsets = [self contentInsetForScrollView:kTargetDoubleTapZoom]; }
} else {
zoom = CGSizeMake(self.view.bounds.size.width * self.scrollView.zoomScale, NSArray *menuItems = @[
self.view.bounds.size.height * self.scrollView.zoomScale); [[UIMenuItem alloc] initWithTitle:NSLocalizedString(@"ATTACHMENT_VIEW_COPY_ACTION", @"Short name for edit menu item to copy contents of media message.")
targetZoomRect = CGRectMake( action:@selector(copyAttachment:)],
convertCoord.x - (zoom.width / 2.0f), convertCoord.y - (zoom.height / 2.0f), zoom.width, zoom.height); [[UIMenuItem alloc] initWithTitle:NSLocalizedString(@"ATTACHMENT_VIEW_SAVE_ACTION", @"Short name for edit menu item to save contents of media message.")
targetInsets = [self contentInsetForScrollView:1.0f]; action:@selector(saveAttachment:)],
// TODO: We should implement sharing.
// [[UIMenuItem alloc] initWithTitle:NSLocalizedString(@"ATTACHMENT_VIEW_SHARE_ACTION", @"Short name for edit menu item to share contents of media message.")
// action:@selector(shareAttachment:)],
];
[UIMenuController sharedMenuController].menuItems = menuItems;
CGPoint location = [sender locationInView:self.view];
CGRect targetRect = CGRectMake(location.x,
location.y,
1, 1);
[[UIMenuController sharedMenuController] setTargetRect:targetRect
inView:self.view];
[[UIMenuController sharedMenuController] setMenuVisible:YES
animated:YES];
} }
}
self.view.userInteractionEnabled = NO; - (void)performEditingActionWithSelector:(SEL)selector {
OWSAssert(self.messageItem.messageType == TSIncomingMessageAdapter ||
self.messageItem.messageType == TSOutgoingMessageAdapter);
OWSAssert([self.messageItem isMediaMessage]);
OWSAssert([self.messageItem isKindOfClass:[TSMessageAdapter class]]);
OWSAssert([self.messageItem conformsToProtocol:@protocol(OWSMessageEditing)]);
OWSAssert([[self.messageItem media] isKindOfClass:[TSPhotoAdapter class]] ||
[[self.messageItem media] isKindOfClass:[TSAnimatedAdapter class]]);
id<OWSMessageEditing> messageEditing = (id<OWSMessageEditing>) self.messageItem.media;
OWSAssert([messageEditing canPerformEditingAction:selector]);
[messageEditing performEditingAction:selector];
}
[CATransaction begin]; - (void)copyAttachment:(id)sender {
[CATransaction setCompletionBlock:^{ [self performEditingActionWithSelector:NSSelectorFromString(@"copy:")];
self.scrollView.contentInset = targetInsets;
self.view.userInteractionEnabled = YES;
}];
[self.scrollView zoomToRect:targetZoomRect animated:YES];
[CATransaction commit];
} }
- (void)imageSingleTapped:(UITapGestureRecognizer *)singleTap { - (void)saveAttachment:(id)sender {
[self dismiss]; [self performEditingActionWithSelector:NSSelectorFromString(@"save:")];
}
- (void)shareAttachment:(id)sender {
// TODO: We should implement sharing with UIActivityViewController.
//
// It seems that loading of the contents of the attachment is done
// with TSAttachment and TSAttachmentStream.
} }
#pragma mark - Presentation #pragma mark - Presentation
@ -193,14 +274,19 @@
presentViewController:self presentViewController:self
animated:NO animated:NO
completion:^{ completion:^{
[UIView animateWithDuration:0.4f UIWindow *window = [UIApplication sharedApplication].keyWindow;
self.imageView.frame = [self.view convertRect:self.originRect
fromView:window];
[UIView animateWithDuration:0.25f
delay:0 delay:0
options:UIViewAnimationOptionBeginFromCurrentState | UIViewAnimationOptionCurveEaseInOut options:UIViewAnimationOptionBeginFromCurrentState | UIViewAnimationOptionCurveEaseOut
animations:^() { animations:^() {
self.view.alpha = 1.0f; self.view.alpha = 1.0f;
self.imageView.frame = [self resizedFrameForImageView:self.image.size]; self.imageView.frame = [self resizedFrameForImageView:self.image.size];
self.imageView.center = self.imageView.center =
CGPointMake(self.view.bounds.size.width / 2.0f, self.view.bounds.size.height / 2.0f); CGPointMake(self.view.bounds.size.width / 2.0f,
self.view.bounds.size.height / 2.0f);
} }
completion:^(BOOL completed) { completion:^(BOOL completed) {
self.scrollView.frame = self.view.bounds; self.scrollView.frame = self.view.bounds;
@ -215,9 +301,9 @@
- (void)dismiss { - (void)dismiss {
self.view.userInteractionEnabled = NO; self.view.userInteractionEnabled = NO;
[UIView animateWithDuration:0.4f [UIView animateWithDuration:0.25f
delay:0 delay:0
options:UIViewAnimationOptionBeginFromCurrentState | UIViewAnimationOptionCurveEaseInOut options:UIViewAnimationOptionBeginFromCurrentState | UIViewAnimationOptionCurveLinear
animations:^() { animations:^() {
self.backgroundView.backgroundColor = [UIColor clearColor]; self.backgroundView.backgroundColor = [UIColor clearColor];
self.scrollView.alpha = 0; self.scrollView.alpha = 0;
@ -234,7 +320,6 @@
[self updateLayouts]; [self updateLayouts];
} }
- (void)updateLayouts { - (void)updateLayouts {
if (_isPresenting) { if (_isPresenting) {
return; return;
@ -246,7 +331,6 @@
self.scrollView.contentInset = [self contentInsetForScrollView:self.scrollView.zoomScale]; self.scrollView.contentInset = [self contentInsetForScrollView:self.scrollView.zoomScale];
} }
#pragma mark - Resizing #pragma mark - Resizing
- (CGRect)resizedFrameForImageView:(CGSize)imageSize { - (CGRect)resizedFrameForImageView:(CGSize)imageSize {

@ -252,8 +252,6 @@ typedef enum : NSUInteger {
[JSQMessagesCollectionViewCell registerMenuAction:@selector(delete:)]; [JSQMessagesCollectionViewCell registerMenuAction:@selector(delete:)];
SEL saveSelector = NSSelectorFromString(@"save:"); SEL saveSelector = NSSelectorFromString(@"save:");
[JSQMessagesCollectionViewCell registerMenuAction:saveSelector]; [JSQMessagesCollectionViewCell registerMenuAction:saveSelector];
[UIMenuController sharedMenuController].menuItems = @[ [[UIMenuItem alloc] initWithTitle:NSLocalizedString(@"EDIT_ITEM_SAVE_ACTION", @"Short name for edit menu item to save contents of media message.")
action:saveSelector] ];
[self initializeCollectionViewLayout]; [self initializeCollectionViewLayout];
[self registerCustomMessageNibs]; [self registerCustomMessageNibs];
@ -391,6 +389,13 @@ typedef enum : NSUInteger {
atScrollPosition:UICollectionViewScrollPositionBottom atScrollPosition:UICollectionViewScrollPositionBottom
animated:NO]; animated:NO];
} }
// Other views might change these custom menu items, so we
// need to set them every time we enter this view.
SEL saveSelector = NSSelectorFromString(@"save:");
[UIMenuController sharedMenuController].menuItems = @[ [[UIMenuItem alloc] initWithTitle:NSLocalizedString(@"EDIT_ITEM_SAVE_ACTION",
@"Short name for edit menu item to save contents of media message.")
action:saveSelector]];
} }
- (void)startReadTimer { - (void)startReadTimer {
@ -1160,9 +1165,12 @@ typedef enum : NSUInteger {
if(tappedImage == nil) { if(tappedImage == nil) {
DDLogWarn(@"tapped TSPhotoAdapter with nil image"); DDLogWarn(@"tapped TSPhotoAdapter with nil image");
} else { } else {
CGRect convertedRect = UIWindow *window = [UIApplication sharedApplication].keyWindow;
[self.collectionView convertRect:[collectionView cellForItemAtIndexPath:indexPath].frame JSQMessagesCollectionViewCell *cell = (JSQMessagesCollectionViewCell *) [collectionView cellForItemAtIndexPath:indexPath];
toView:nil]; OWSAssert([cell isKindOfClass:[JSQMessagesCollectionViewCell class]]);
CGRect convertedRect = [cell.mediaView convertRect:cell.mediaView.bounds
toView:window];
__block TSAttachment *attachment = nil; __block TSAttachment *attachment = nil;
[self.uiDatabaseConnection readWithBlock:^(YapDatabaseReadTransaction *transaction) { [self.uiDatabaseConnection readWithBlock:^(YapDatabaseReadTransaction *transaction) {
attachment = attachment =
@ -1174,7 +1182,8 @@ typedef enum : NSUInteger {
FullImageViewController *vc = [[FullImageViewController alloc] FullImageViewController *vc = [[FullImageViewController alloc]
initWithAttachment:attStream initWithAttachment:attStream
fromRect:convertedRect fromRect:convertedRect
forInteraction:[self interactionAtIndexPath:indexPath] forInteraction:interaction
messageItem:messageItem
isAnimated:NO]; isAnimated:NO];
[vc presentFromViewController:self.navigationController]; [vc presentFromViewController:self.navigationController];
@ -1187,9 +1196,12 @@ typedef enum : NSUInteger {
if(tappedImage == nil) { if(tappedImage == nil) {
DDLogWarn(@"tapped TSAnimatedAdapter with nil image"); DDLogWarn(@"tapped TSAnimatedAdapter with nil image");
} else { } else {
CGRect convertedRect = UIWindow *window = [UIApplication sharedApplication].keyWindow;
[self.collectionView convertRect:[collectionView cellForItemAtIndexPath:indexPath].frame JSQMessagesCollectionViewCell *cell = (JSQMessagesCollectionViewCell *) [collectionView cellForItemAtIndexPath:indexPath];
toView:nil]; OWSAssert([cell isKindOfClass:[JSQMessagesCollectionViewCell class]]);
CGRect convertedRect = [cell.mediaView convertRect:cell.mediaView.bounds
toView:window];
__block TSAttachment *attachment = nil; __block TSAttachment *attachment = nil;
[self.uiDatabaseConnection readWithBlock:^(YapDatabaseReadTransaction *transaction) { [self.uiDatabaseConnection readWithBlock:^(YapDatabaseReadTransaction *transaction) {
attachment = attachment =
@ -1200,7 +1212,8 @@ typedef enum : NSUInteger {
FullImageViewController *vc = FullImageViewController *vc =
[[FullImageViewController alloc] initWithAttachment:attStream [[FullImageViewController alloc] initWithAttachment:attStream
fromRect:convertedRect fromRect:convertedRect
forInteraction:[self interactionAtIndexPath:indexPath] forInteraction:interaction
messageItem:messageItem
isAnimated:YES]; isAnimated:YES];
[vc presentFromViewController:self.navigationController]; [vc presentFromViewController:self.navigationController];
} }
@ -1225,16 +1238,23 @@ typedef enum : NSUInteger {
_videoPlayer = [[MPMoviePlayerController alloc] initWithContentURL:attStream.mediaURL]; _videoPlayer = [[MPMoviePlayerController alloc] initWithContentURL:attStream.mediaURL];
[_videoPlayer prepareToPlay]; [_videoPlayer prepareToPlay];
[[NSNotificationCenter defaultCenter] [[NSNotificationCenter defaultCenter] addObserver:self
addObserver:self selector:@selector(moviePlayerWillExitFullscreen:)
selector:@selector(moviePlayBackDidFinish:) name:MPMoviePlayerWillExitFullscreenNotification
name:MPMoviePlayerPlaybackDidFinishNotification object:_videoPlayer];
object:_videoPlayer]; [[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(moviePlayerDidExitFullscreen:)
name:MPMoviePlayerDidExitFullscreenNotification
object:_videoPlayer];
_videoPlayer.controlStyle = MPMovieControlStyleDefault; _videoPlayer.controlStyle = MPMovieControlStyleDefault;
_videoPlayer.shouldAutoplay = YES; _videoPlayer.shouldAutoplay = YES;
[self.view addSubview:_videoPlayer.view]; [self.view addSubview:_videoPlayer.view];
[_videoPlayer setFullscreen:YES animated:YES]; // We can't animate from the cell media frame;
// MPMoviePlayerController will animate a crop of its
// contents rather than scaling them.
_videoPlayer.view.frame = self.view.bounds;
[_videoPlayer setFullscreen:YES animated:NO];
} }
} else if ([messageMedia isAudio]) { } else if ([messageMedia isAudio]) {
if (messageMedia.isAudioPlaying) { if (messageMedia.isAudioPlaying) {
@ -1365,9 +1385,28 @@ typedef enum : NSUInteger {
} }
} }
// There's more than one way to exit the fullscreen video playback.
// There's a done button, a "toggle fullscreen" button and I think
// there's some gestures too. These fire slightly different notifications.
// We want to hide & clean up the video player immediately in all of
// these cases.
- (void)moviePlayerWillExitFullscreen:(id)sender {
DDLogDebug(@"%@ %s", self.tag, __PRETTY_FUNCTION__);
[self clearVideoPlayer];
}
// See comment on moviePlayerWillExitFullscreen:
- (void)moviePlayerDidExitFullscreen:(id)sender {
DDLogDebug(@"%@ %s", self.tag, __PRETTY_FUNCTION__);
[self clearVideoPlayer];
}
- (void)moviePlayBackDidFinish:(id)sender { - (void)clearVideoPlayer {
DDLogDebug(@"playback finished"); [_videoPlayer stop];
[_videoPlayer.view removeFromSuperview];
_videoPlayer = nil;
} }
- (void)collectionView:(JSQMessagesCollectionView *)collectionView - (void)collectionView:(JSQMessagesCollectionView *)collectionView

@ -67,6 +67,15 @@
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"ATTACHMENT_QUEUED" = "New attachment queued for retrieval."; "ATTACHMENT_QUEUED" = "New attachment queued for retrieval.";
/* Short name for edit menu item to copy contents of media message. */
"ATTACHMENT_VIEW_COPY_ACTION" = "Copy";
/* Short name for edit menu item to save contents of media message. */
"ATTACHMENT_VIEW_SAVE_ACTION" = "Save";
/* Short name for edit menu item to share contents of media message. */
"ATTACHMENT_VIEW_SHARE_ACTION" = "Share";
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"AUDIO_PERMISSION_MESSAGE" = "Signal requires access to your microphone to work properly. You can grant this permission in the Settings app >> Privacy >> Microphone >> Signal"; "AUDIO_PERMISSION_MESSAGE" = "Signal requires access to your microphone to work properly. You can grant this permission in the Settings app >> Privacy >> Microphone >> Signal";

Loading…
Cancel
Save