From 6a3b462541d290b7fb80d31f61ff26c06f900fdb Mon Sep 17 00:00:00 2001 From: Matthew Chen Date: Fri, 10 Feb 2017 23:21:10 -0500 Subject: [PATCH 1/5] Add save/copy menu to the image attachment view. // FREEBIE --- Signal/src/UIView+OWS.h | 1 + Signal/src/UIView+OWS.m | 1 - .../FullImageViewController.h | 8 +- .../FullImageViewController.m | 211 ++++++++++++------ .../view controllers/MessagesViewController.m | 15 +- .../translations/en.lproj/Localizable.strings | 9 + 6 files changed, 170 insertions(+), 75 deletions(-) diff --git a/Signal/src/UIView+OWS.h b/Signal/src/UIView+OWS.h index e31139d83..2fa8a51ca 100644 --- a/Signal/src/UIView+OWS.h +++ b/Signal/src/UIView+OWS.h @@ -2,6 +2,7 @@ // Copyright (c) 2017 Open Whisper Systems. All rights reserved. // +#import "PureLayout.h" #import // A convenience method for doing responsive layout. Scales between two diff --git a/Signal/src/UIView+OWS.m b/Signal/src/UIView+OWS.m index 05f5e8526..87cfe1458 100644 --- a/Signal/src/UIView+OWS.m +++ b/Signal/src/UIView+OWS.m @@ -2,7 +2,6 @@ // Copyright (c) 2017 Open Whisper Systems. All rights reserved. // -#import "PureLayout.h" #import "UIView+OWS.h" // TODO: We'll eventually want to promote these into an OWSMath.h header. diff --git a/Signal/src/view controllers/FullImageViewController.h b/Signal/src/view controllers/FullImageViewController.h index ca8a181b8..7d7acba32 100644 --- a/Signal/src/view controllers/FullImageViewController.h +++ b/Signal/src/view controllers/FullImageViewController.h @@ -1,20 +1,18 @@ // -// FullImageViewController.h -// Signal -// -// Created by Dylan Bourgeois on 11/11/14. -// Copyright (c) 2014 Open Whisper Systems. All rights reserved. +// Copyright (c) 2017 Open Whisper Systems. All rights reserved. // #import #import "TSAttachmentStream.h" #import "TSInteraction.h" +#import "OWSMessageData.h" @interface FullImageViewController : UIViewController - (instancetype)initWithAttachment:(TSAttachmentStream *)attachment fromRect:(CGRect)rect forInteraction:(TSInteraction *)interaction + messageItem:(id)messageItem isAnimated:(BOOL)animated; - (void)presentFromViewController:(UIViewController *)viewController; diff --git a/Signal/src/view controllers/FullImageViewController.m b/Signal/src/view controllers/FullImageViewController.m index 76e3842d7..e1fe10df1 100644 --- a/Signal/src/view controllers/FullImageViewController.m +++ b/Signal/src/view controllers/FullImageViewController.m @@ -1,44 +1,60 @@ // -// FullImageViewController.m -// 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. +// Copyright (c) 2017 Open Whisper Systems. All rights reserved. // #import "FLAnimatedImage.h" #import "FullImageViewController.h" #import "UIUtil.h" - -#define kImageViewCornerRadius 5.0f +#import "UIView+OWS.h" +#import "TSPhotoAdapter.h" +#import "TSMessageAdapter.h" +#import "TSAnimatedAdapter.h" #define kMinZoomScale 1.0f #define kMaxZoomScale 8.0f -#define kTargetDoubleTapZoom 3.0f #define kBackgroundAlpha 0.6f -@interface FullImageViewController () +// In order to use UIMenuController, the view from which it is +// presented must have certain custom behaviors. +@interface AttachmentMenuView : UIView + +@end + +#pragma mark - + +@implementation AttachmentMenuView + +- (BOOL)canBecomeFirstResponder { + return YES; +} -@property (nonatomic, strong) UIView *backgroundView; +// We only use custom actions in UIMenuController. +- (BOOL)canPerformAction:(SEL)action + withSender:(id)sender +{ + return NO; +} -@property (nonatomic, strong) UIScrollView *scrollView; +@end -@property (nonatomic, strong) UIImageView *imageView; +#pragma mark - -@property (nonatomic, strong) UITapGestureRecognizer *singleTap; -@property (nonatomic, strong) UITapGestureRecognizer *doubleTap; +@interface FullImageViewController () -@property (nonatomic, strong) UIButton *shareButton; +@property (nonatomic) UIView *backgroundView; +@property (nonatomic) UIScrollView *scrollView; +@property (nonatomic) UIImageView *imageView; +@property (nonatomic) UIButton *shareButton; -@property CGRect originRect; -@property BOOL isPresenting; -@property BOOL isAnimated; -@property NSData *fileData; +@property (nonatomic) CGRect originRect; +@property (nonatomic) BOOL isPresenting; +@property (nonatomic) BOOL isAnimated; +@property (nonatomic) NSData *fileData; -@property TSAttachmentStream *attachment; -@property TSInteraction *interaction; +@property (nonatomic) TSAttachmentStream *attachment; +@property (nonatomic) TSInteraction *interaction; +@property (nonatomic) id messageItem; @end @@ -48,6 +64,7 @@ - (instancetype)initWithAttachment:(TSAttachmentStream *)attachment fromRect:(CGRect)rect forInteraction:(TSInteraction *)interaction + messageItem:(id)messageItem isAnimated:(BOOL)animated { self = [super initWithNibName:nil bundle:nil]; @@ -55,6 +72,7 @@ self.attachment = attachment; self.originRect = rect; self.interaction = interaction; + self.messageItem = messageItem; self.isAnimated = animated; self.fileData = [NSData dataWithContentsOfURL:[attachment mediaURL]]; } @@ -66,6 +84,12 @@ 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 { [super viewDidLoad]; @@ -77,16 +101,24 @@ [self populateImageView:self.image]; } +- (void)viewWillDisappear:(BOOL)animated { + [super viewWillDisappear:animated]; + + if ([UIMenuController sharedMenuController].isMenuVisible) { + [[UIMenuController sharedMenuController] setMenuVisible:NO + animated:NO]; + } +} + #pragma mark - Initializers - (void)initializeBackground { self.imageView.backgroundColor = [UIColor colorWithWhite:0 alpha:kBackgroundAlpha]; - self.view.backgroundColor = [UIColor colorWithWhite:0 alpha:kBackgroundAlpha]; - self.view.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; - self.backgroundView = [[UIView alloc] initWithFrame:CGRectInset(self.view.bounds, -512, -512)]; + + self.backgroundView = [UIView new]; self.backgroundView.backgroundColor = [UIColor colorWithWhite:0 alpha:kBackgroundAlpha]; - [self.view addSubview:self.backgroundView]; + [self.backgroundView autoPinEdgesToSuperviewEdges]; } - (void)initializeScrollView { @@ -111,7 +143,6 @@ } else { // Present the static image using standard UIImageView self.imageView = [[UIImageView alloc] initWithFrame:self.originRect]; - self.imageView.layer.cornerRadius = kImageViewCornerRadius; self.imageView.contentMode = UIViewContentModeScaleAspectFill; self.imageView.userInteractionEnabled = YES; self.imageView.clipsToBounds = YES; @@ -128,56 +159,106 @@ } - (void)initializeGestureRecognizers { - self.doubleTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(imageDoubleTapped:)]; - self.doubleTap.numberOfTapsRequired = 2; - - self.singleTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(imageSingleTapped:)]; - [self.singleTap requireGestureRecognizerToFail:self.doubleTap]; - - self.singleTap.delegate = self; - self.doubleTap.delegate = self; + UITapGestureRecognizer *singleTap = [[UITapGestureRecognizer alloc] initWithTarget:self + action:@selector(imageDismissGesture:)]; + singleTap.delegate = self; + [self.view addGestureRecognizer:singleTap]; + + UITapGestureRecognizer *doubleTap = [[UITapGestureRecognizer alloc] initWithTarget:self + action:@selector(imageDismissGesture:)]; + 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]; - [self.view addGestureRecognizer:self.doubleTap]; + UILongPressGestureRecognizer *longPress = [[UILongPressGestureRecognizer alloc] initWithTarget:self + action:@selector(longPressGesture:)]; + longPress.delegate = self; + [self.view addGestureRecognizer:longPress]; } #pragma mark - Gesture Recognizers -- (void)imageDoubleTapped:(UITapGestureRecognizer *)doubleTap { - CGPoint tap = [doubleTap locationInView:doubleTap.view]; - CGPoint convertCoord = [self.scrollView convertPoint:tap fromView:doubleTap.view]; - CGRect targetZoomRect; - UIEdgeInsets targetInsets; +- (void)imageDismissGesture:(UIGestureRecognizer *)sender { + if (sender.state == UIGestureRecognizerStateRecognized) { + [self dismiss]; + } +} - 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) { - zoom = CGSizeMake(self.view.bounds.size.width / kTargetDoubleTapZoom, - self.view.bounds.size.height / kTargetDoubleTapZoom); - targetZoomRect = CGRectMake( - convertCoord.x - (zoom.width / 2.0f), convertCoord.y - (zoom.height / 2.0f), zoom.width, zoom.height); - targetInsets = [self contentInsetForScrollView:kTargetDoubleTapZoom]; - } else { - zoom = CGSizeMake(self.view.bounds.size.width * self.scrollView.zoomScale, - self.view.bounds.size.height * self.scrollView.zoomScale); - targetZoomRect = CGRectMake( - convertCoord.x - (zoom.width / 2.0f), convertCoord.y - (zoom.height / 2.0f), zoom.width, zoom.height); - targetInsets = [self contentInsetForScrollView:1.0f]; + [self.view becomeFirstResponder]; + + if ([UIMenuController sharedMenuController].isMenuVisible) { + [[UIMenuController sharedMenuController] setMenuVisible:NO + animated:NO]; + } + + NSArray *menuItems = @[ + [[UIMenuItem alloc] initWithTitle:NSLocalizedString(@"ATTACHMENT_VIEW_COPY_ACTION", @"Short name for edit menu item to copy contents of media message.") + action:@selector(copyAttachment:)], + [[UIMenuItem alloc] initWithTitle:NSLocalizedString(@"ATTACHMENT_VIEW_SAVE_ACTION", @"Short name for edit menu item to save contents of media message.") + 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 messageEditing = (id) self.messageItem.media; + OWSAssert([messageEditing canPerformEditingAction:selector]); + [messageEditing performEditingAction:selector]; +} + +- (void)copyAttachment:(id)sender { + [self performEditingActionWithSelector:NSSelectorFromString(@"copy:")]; +} - [CATransaction begin]; - [CATransaction setCompletionBlock:^{ - self.scrollView.contentInset = targetInsets; - self.view.userInteractionEnabled = YES; - }]; - [self.scrollView zoomToRect:targetZoomRect animated:YES]; - [CATransaction commit]; +- (void)saveAttachment:(id)sender { + [self performEditingActionWithSelector:NSSelectorFromString(@"save:")]; } -- (void)imageSingleTapped:(UITapGestureRecognizer *)singleTap { - [self dismiss]; +- (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 diff --git a/Signal/src/view controllers/MessagesViewController.m b/Signal/src/view controllers/MessagesViewController.m index 56eb94112..0220f76e0 100644 --- a/Signal/src/view controllers/MessagesViewController.m +++ b/Signal/src/view controllers/MessagesViewController.m @@ -252,8 +252,6 @@ typedef enum : NSUInteger { [JSQMessagesCollectionViewCell registerMenuAction:@selector(delete:)]; SEL saveSelector = NSSelectorFromString(@"save:"); [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 registerCustomMessageNibs]; @@ -391,6 +389,13 @@ typedef enum : NSUInteger { atScrollPosition:UICollectionViewScrollPositionBottom 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 { @@ -1174,7 +1179,8 @@ typedef enum : NSUInteger { FullImageViewController *vc = [[FullImageViewController alloc] initWithAttachment:attStream fromRect:convertedRect - forInteraction:[self interactionAtIndexPath:indexPath] + forInteraction:interaction + messageItem:messageItem isAnimated:NO]; [vc presentFromViewController:self.navigationController]; @@ -1200,7 +1206,8 @@ typedef enum : NSUInteger { FullImageViewController *vc = [[FullImageViewController alloc] initWithAttachment:attStream fromRect:convertedRect - forInteraction:[self interactionAtIndexPath:indexPath] + forInteraction:interaction + messageItem:messageItem isAnimated:YES]; [vc presentFromViewController:self.navigationController]; } diff --git a/Signal/translations/en.lproj/Localizable.strings b/Signal/translations/en.lproj/Localizable.strings index 2c13c6b34..eeefad8c0 100644 --- a/Signal/translations/en.lproj/Localizable.strings +++ b/Signal/translations/en.lproj/Localizable.strings @@ -67,6 +67,15 @@ /* No comment provided by engineer. */ "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. */ "AUDIO_PERMISSION_MESSAGE" = "Signal requires access to your microphone to work properly. You can grant this permission in the Settings app >> Privacy >> Microphone >> Signal"; From 593c3d53d46cd74730084bf2b203e7f14907487c Mon Sep 17 00:00:00 2001 From: Matthew Chen Date: Fri, 10 Feb 2017 23:21:40 -0500 Subject: [PATCH 2/5] Clean up present & dismiss animations for image attachment view. // FREEBIE --- .../view controllers/FullImageViewController.m | 17 ++++++++++------- .../view controllers/MessagesViewController.m | 18 ++++++++++++------ 2 files changed, 22 insertions(+), 13 deletions(-) diff --git a/Signal/src/view controllers/FullImageViewController.m b/Signal/src/view controllers/FullImageViewController.m index e1fe10df1..7cf124085 100644 --- a/Signal/src/view controllers/FullImageViewController.m +++ b/Signal/src/view controllers/FullImageViewController.m @@ -274,14 +274,19 @@ presentViewController:self animated:NO 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 - options:UIViewAnimationOptionBeginFromCurrentState | UIViewAnimationOptionCurveEaseInOut + options:UIViewAnimationOptionBeginFromCurrentState | UIViewAnimationOptionCurveEaseOut animations:^() { self.view.alpha = 1.0f; self.imageView.frame = [self resizedFrameForImageView:self.image.size]; 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) { self.scrollView.frame = self.view.bounds; @@ -296,9 +301,9 @@ - (void)dismiss { self.view.userInteractionEnabled = NO; - [UIView animateWithDuration:0.4f + [UIView animateWithDuration:0.25f delay:0 - options:UIViewAnimationOptionBeginFromCurrentState | UIViewAnimationOptionCurveEaseInOut + options:UIViewAnimationOptionBeginFromCurrentState | UIViewAnimationOptionCurveLinear animations:^() { self.backgroundView.backgroundColor = [UIColor clearColor]; self.scrollView.alpha = 0; @@ -315,7 +320,6 @@ [self updateLayouts]; } - - (void)updateLayouts { if (_isPresenting) { return; @@ -327,7 +331,6 @@ self.scrollView.contentInset = [self contentInsetForScrollView:self.scrollView.zoomScale]; } - #pragma mark - Resizing - (CGRect)resizedFrameForImageView:(CGSize)imageSize { diff --git a/Signal/src/view controllers/MessagesViewController.m b/Signal/src/view controllers/MessagesViewController.m index 0220f76e0..fd613a676 100644 --- a/Signal/src/view controllers/MessagesViewController.m +++ b/Signal/src/view controllers/MessagesViewController.m @@ -1165,9 +1165,12 @@ typedef enum : NSUInteger { if(tappedImage == nil) { DDLogWarn(@"tapped TSPhotoAdapter with nil image"); } else { - CGRect convertedRect = - [self.collectionView convertRect:[collectionView cellForItemAtIndexPath:indexPath].frame - toView:nil]; + UIWindow *window = [UIApplication sharedApplication].keyWindow; + JSQMessagesCollectionViewCell *cell = (JSQMessagesCollectionViewCell *) [collectionView cellForItemAtIndexPath:indexPath]; + OWSAssert([cell isKindOfClass:[JSQMessagesCollectionViewCell class]]); + CGRect convertedRect = [cell.mediaView convertRect:cell.mediaView.bounds + toView:window]; + __block TSAttachment *attachment = nil; [self.uiDatabaseConnection readWithBlock:^(YapDatabaseReadTransaction *transaction) { attachment = @@ -1193,9 +1196,12 @@ typedef enum : NSUInteger { if(tappedImage == nil) { DDLogWarn(@"tapped TSAnimatedAdapter with nil image"); } else { - CGRect convertedRect = - [self.collectionView convertRect:[collectionView cellForItemAtIndexPath:indexPath].frame - toView:nil]; + UIWindow *window = [UIApplication sharedApplication].keyWindow; + JSQMessagesCollectionViewCell *cell = (JSQMessagesCollectionViewCell *) [collectionView cellForItemAtIndexPath:indexPath]; + OWSAssert([cell isKindOfClass:[JSQMessagesCollectionViewCell class]]); + CGRect convertedRect = [cell.mediaView convertRect:cell.mediaView.bounds + toView:window]; + __block TSAttachment *attachment = nil; [self.uiDatabaseConnection readWithBlock:^(YapDatabaseReadTransaction *transaction) { attachment = From 3b1cc0dfa26f5cc55818bdd2fa2e9e24744e0c97 Mon Sep 17 00:00:00 2001 From: Matthew Chen Date: Fri, 10 Feb 2017 23:22:09 -0500 Subject: [PATCH 3/5] Fix present & dismiss animations for video attachment view & ensure this view is cleaned up. // FREEBIE --- .../view controllers/MessagesViewController.m | 47 +++++++++++++++---- 1 file changed, 39 insertions(+), 8 deletions(-) diff --git a/Signal/src/view controllers/MessagesViewController.m b/Signal/src/view controllers/MessagesViewController.m index fd613a676..e43b60367 100644 --- a/Signal/src/view controllers/MessagesViewController.m +++ b/Signal/src/view controllers/MessagesViewController.m @@ -1239,15 +1239,29 @@ typedef enum : NSUInteger { [_videoPlayer prepareToPlay]; [[NSNotificationCenter defaultCenter] - addObserver:self - selector:@selector(moviePlayBackDidFinish:) - name:MPMoviePlayerPlaybackDidFinishNotification - object:_videoPlayer]; + addObserver:self + selector:@selector(moviePlayBackDidFinish:) + name:MPMoviePlayerPlaybackDidFinishNotification + object:_videoPlayer]; + [[NSNotificationCenter defaultCenter] + addObserver:self + selector:@selector(moviePlayerWillExitFullscreen:) + name:MPMoviePlayerWillExitFullscreenNotification + object:_videoPlayer]; + [[NSNotificationCenter defaultCenter] + addObserver:self + selector:@selector(moviePlayerDidExitFullscreen:) + name:MPMoviePlayerDidExitFullscreenNotification + object:_videoPlayer]; - _videoPlayer.controlStyle = MPMovieControlStyleDefault; + _videoPlayer.controlStyle = MPMovieControlStyleDefault; _videoPlayer.shouldAutoplay = YES; [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]) { if (messageMedia.isAudioPlaying) { @@ -1378,9 +1392,26 @@ typedef enum : NSUInteger { } } - - (void)moviePlayBackDidFinish:(id)sender { - DDLogDebug(@"playback finished"); + DDLogDebug(@"%@ %s", self.tag, __PRETTY_FUNCTION__); +} + +- (void)moviePlayerWillExitFullscreen:(id)sender { + DDLogDebug(@"%@ %s", self.tag, __PRETTY_FUNCTION__); + + [self clearVideoPlayer]; +} + +- (void)moviePlayerDidExitFullscreen:(id)sender { + DDLogDebug(@"%@ %s", self.tag, __PRETTY_FUNCTION__); + + [self clearVideoPlayer]; +} + +- (void)clearVideoPlayer { + [_videoPlayer stop]; + [_videoPlayer.view removeFromSuperview]; + _videoPlayer = nil; } - (void)collectionView:(JSQMessagesCollectionView *)collectionView From e48efe01c96ebe9f8c9119bc683df9eb05cb65b1 Mon Sep 17 00:00:00 2001 From: Matthew Chen Date: Fri, 10 Feb 2017 23:26:43 -0500 Subject: [PATCH 4/5] Improve formatting of message view controller. // FREEBIE --- .../view controllers/MessagesViewController.m | 27 +++++++++---------- 1 file changed, 12 insertions(+), 15 deletions(-) diff --git a/Signal/src/view controllers/MessagesViewController.m b/Signal/src/view controllers/MessagesViewController.m index e43b60367..0d360281e 100644 --- a/Signal/src/view controllers/MessagesViewController.m +++ b/Signal/src/view controllers/MessagesViewController.m @@ -1238,21 +1238,18 @@ typedef enum : NSUInteger { _videoPlayer = [[MPMoviePlayerController alloc] initWithContentURL:attStream.mediaURL]; [_videoPlayer prepareToPlay]; - [[NSNotificationCenter defaultCenter] - addObserver:self - selector:@selector(moviePlayBackDidFinish:) - name:MPMoviePlayerPlaybackDidFinishNotification - object:_videoPlayer]; - [[NSNotificationCenter defaultCenter] - addObserver:self - selector:@selector(moviePlayerWillExitFullscreen:) - name:MPMoviePlayerWillExitFullscreenNotification - object:_videoPlayer]; - [[NSNotificationCenter defaultCenter] - addObserver:self - selector:@selector(moviePlayerDidExitFullscreen:) - name:MPMoviePlayerDidExitFullscreenNotification - object:_videoPlayer]; + [[NSNotificationCenter defaultCenter] addObserver:self + selector:@selector(moviePlayBackDidFinish:) + name:MPMoviePlayerPlaybackDidFinishNotification + object:_videoPlayer]; + [[NSNotificationCenter defaultCenter] addObserver:self + selector:@selector(moviePlayerWillExitFullscreen:) + name:MPMoviePlayerWillExitFullscreenNotification + object:_videoPlayer]; + [[NSNotificationCenter defaultCenter] addObserver:self + selector:@selector(moviePlayerDidExitFullscreen:) + name:MPMoviePlayerDidExitFullscreenNotification + object:_videoPlayer]; _videoPlayer.controlStyle = MPMovieControlStyleDefault; _videoPlayer.shouldAutoplay = YES; From a52771e2869b14d6578357330313042c35755045 Mon Sep 17 00:00:00 2001 From: Matthew Chen Date: Mon, 13 Feb 2017 16:30:54 -0500 Subject: [PATCH 5/5] Respond to CR. // FREEBIE --- Signal/src/Signal-Bridging-Header.h | 2 +- Signal/src/UIView+OWS.h | 2 +- .../src/view controllers/FullImageViewController.m | 6 +++--- .../src/view controllers/MessagesViewController.m | 14 ++++++-------- 4 files changed, 11 insertions(+), 13 deletions(-) diff --git a/Signal/src/Signal-Bridging-Header.h b/Signal/src/Signal-Bridging-Header.h index 9dcf0dadb..55fadea30 100644 --- a/Signal/src/Signal-Bridging-Header.h +++ b/Signal/src/Signal-Bridging-Header.h @@ -18,7 +18,6 @@ #import "OWSWebRTCDataProtos.pb.h" #import "PhoneManager.h" #import "PropertyListPreferences.h" -#import "PureLayout.h" #import "PushManager.h" #import "RPAccountManager.h" #import "TSSocketManager.h" @@ -27,6 +26,7 @@ #import "UIUtil.h" #import "UIView+OWS.h" #import +#import #import #import #import diff --git a/Signal/src/UIView+OWS.h b/Signal/src/UIView+OWS.h index 2fa8a51ca..f885d1c44 100644 --- a/Signal/src/UIView+OWS.h +++ b/Signal/src/UIView+OWS.h @@ -2,7 +2,7 @@ // Copyright (c) 2017 Open Whisper Systems. All rights reserved. // -#import "PureLayout.h" +#import #import // A convenience method for doing responsive layout. Scales between two diff --git a/Signal/src/view controllers/FullImageViewController.m b/Signal/src/view controllers/FullImageViewController.m index 7cf124085..89b849053 100644 --- a/Signal/src/view controllers/FullImageViewController.m +++ b/Signal/src/view controllers/FullImageViewController.m @@ -195,9 +195,9 @@ #pragma mark - Gesture Recognizers - (void)imageDismissGesture:(UIGestureRecognizer *)sender { - if (sender.state == UIGestureRecognizerStateRecognized) { - [self dismiss]; - } + if (sender.state == UIGestureRecognizerStateRecognized) { + [self dismiss]; + } } - (void)longPressGesture:(UIGestureRecognizer *)sender { diff --git a/Signal/src/view controllers/MessagesViewController.m b/Signal/src/view controllers/MessagesViewController.m index 0d360281e..a18fade99 100644 --- a/Signal/src/view controllers/MessagesViewController.m +++ b/Signal/src/view controllers/MessagesViewController.m @@ -1238,10 +1238,6 @@ typedef enum : NSUInteger { _videoPlayer = [[MPMoviePlayerController alloc] initWithContentURL:attStream.mediaURL]; [_videoPlayer prepareToPlay]; - [[NSNotificationCenter defaultCenter] addObserver:self - selector:@selector(moviePlayBackDidFinish:) - name:MPMoviePlayerPlaybackDidFinishNotification - object:_videoPlayer]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(moviePlayerWillExitFullscreen:) name:MPMoviePlayerWillExitFullscreenNotification @@ -1389,16 +1385,18 @@ typedef enum : NSUInteger { } } -- (void)moviePlayBackDidFinish:(id)sender { - DDLogDebug(@"%@ %s", self.tag, __PRETTY_FUNCTION__); -} - +// 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__);