From 699b364ec72ba340843d02e92ca66d6d0bf1054a Mon Sep 17 00:00:00 2001 From: Matthew Chen Date: Fri, 27 Jan 2017 15:26:31 -0500 Subject: [PATCH] Show/hide call view controls in remote video mode if user taps screen. // FREEBIE --- Signal.xcodeproj/project.pbxproj | 6 ++ Signal/src/Signal-Bridging-Header.h | 1 + .../view controllers/CallViewController.swift | 29 +++++++ .../src/views/OWSAnyTouchGestureRecognizer.h | 16 ++++ .../src/views/OWSAnyTouchGestureRecognizer.m | 84 +++++++++++++++++++ 5 files changed, 136 insertions(+) create mode 100644 Signal/src/views/OWSAnyTouchGestureRecognizer.h create mode 100644 Signal/src/views/OWSAnyTouchGestureRecognizer.m diff --git a/Signal.xcodeproj/project.pbxproj b/Signal.xcodeproj/project.pbxproj index 3e7ad5693..bdb2b9f79 100644 --- a/Signal.xcodeproj/project.pbxproj +++ b/Signal.xcodeproj/project.pbxproj @@ -9,6 +9,7 @@ /* Begin PBXBuildFile section */ 341BB7491DB727EE001E2975 /* JSQMediaItem+OWS.m in Sources */ = {isa = PBXBuildFile; fileRef = 341BB7481DB727EE001E2975 /* JSQMediaItem+OWS.m */; }; 34535D821E256BE9008A4747 /* UIView+OWS.m in Sources */ = {isa = PBXBuildFile; fileRef = 34535D811E256BE9008A4747 /* UIView+OWS.m */; }; + 34FD93701E3BD43A00109093 /* OWSAnyTouchGestureRecognizer.m in Sources */ = {isa = PBXBuildFile; fileRef = 34FD936F1E3BD43A00109093 /* OWSAnyTouchGestureRecognizer.m */; }; 450873C31D9D5149006B54F2 /* OWSExpirationTimerView.m in Sources */ = {isa = PBXBuildFile; fileRef = 450873C21D9D5149006B54F2 /* OWSExpirationTimerView.m */; }; 450873C41D9D5149006B54F2 /* OWSExpirationTimerView.m in Sources */ = {isa = PBXBuildFile; fileRef = 450873C21D9D5149006B54F2 /* OWSExpirationTimerView.m */; }; 450873C71D9D867B006B54F2 /* OWSIncomingMessageCollectionViewCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 450873C61D9D867B006B54F2 /* OWSIncomingMessageCollectionViewCell.m */; }; @@ -598,6 +599,8 @@ 341BB7481DB727EE001E2975 /* JSQMediaItem+OWS.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "JSQMediaItem+OWS.m"; sourceTree = ""; }; 34535D801E256BE9008A4747 /* UIView+OWS.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIView+OWS.h"; sourceTree = ""; }; 34535D811E256BE9008A4747 /* UIView+OWS.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIView+OWS.m"; sourceTree = ""; }; + 34FD936E1E3BD43A00109093 /* OWSAnyTouchGestureRecognizer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OWSAnyTouchGestureRecognizer.h; path = views/OWSAnyTouchGestureRecognizer.h; sourceTree = ""; }; + 34FD936F1E3BD43A00109093 /* OWSAnyTouchGestureRecognizer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = OWSAnyTouchGestureRecognizer.m; path = views/OWSAnyTouchGestureRecognizer.m; sourceTree = ""; }; 450873C11D9D5149006B54F2 /* OWSExpirationTimerView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OWSExpirationTimerView.h; sourceTree = ""; }; 450873C21D9D5149006B54F2 /* OWSExpirationTimerView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OWSExpirationTimerView.m; sourceTree = ""; }; 450873C51D9D867B006B54F2 /* OWSIncomingMessageCollectionViewCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OWSIncomingMessageCollectionViewCell.h; sourceTree = ""; }; @@ -1297,6 +1300,8 @@ 450DF2061E0DD28D003D14BE /* UserInterface */ = { isa = PBXGroup; children = ( + 34FD936E1E3BD43A00109093 /* OWSAnyTouchGestureRecognizer.h */, + 34FD936F1E3BD43A00109093 /* OWSAnyTouchGestureRecognizer.m */, 450DF2071E0DD29E003D14BE /* Notifications */, 76EB052B18170B33006006FC /* Views */, 76EB04FE18170B33006006FC /* View Controllers */, @@ -3089,6 +3094,7 @@ 76EB063C18170B33006006FC /* NumberUtil.m in Sources */, B6A3EB4B1A423B3800B2236B /* TSPhotoAdapter.m in Sources */, 4509E79C1DD6545B0025A59F /* CallViewController.swift in Sources */, + 34FD93701E3BD43A00109093 /* OWSAnyTouchGestureRecognizer.m in Sources */, 76EB063A18170B33006006FC /* FunctionalUtil.m in Sources */, 76EB060A18170B33006006FC /* SignalUtil.m in Sources */, E197B61718BBEC1A00F073E5 /* AnonymousAudioCallbackHandler.m in Sources */, diff --git a/Signal/src/Signal-Bridging-Header.h b/Signal/src/Signal-Bridging-Header.h index 18ef28ddc..357ee43e4 100644 --- a/Signal/src/Signal-Bridging-Header.h +++ b/Signal/src/Signal-Bridging-Header.h @@ -8,6 +8,7 @@ #import "Asserts.h" #import "Environment.h" #import "NotificationsManager.h" +#import "OWSAnyTouchGestureRecognizer.h" #import "OWSCallNotificationsAdaptee.h" #import "OWSContactAvatarBuilder.h" #import "OWSContactsManager.h" diff --git a/Signal/src/view controllers/CallViewController.swift b/Signal/src/view controllers/CallViewController.swift index f1a7e64c6..9d99ee591 100644 --- a/Signal/src/view controllers/CallViewController.swift +++ b/Signal/src/view controllers/CallViewController.swift @@ -73,6 +73,12 @@ class CallViewController: UIViewController, CallObserver, CallServiceObserver, R var remoteVideoConstraints: [NSLayoutConstraint] = [] var localVideoConstraints: [NSLayoutConstraint] = [] + var areRemoteVideoControlsHidden = false { + didSet { + updateCallUI(callState: call.state) + } + } + // MARK: Initializers required init?(coder aDecoder: NSCoder) { @@ -138,9 +144,14 @@ class CallViewController: UIViewController, CallObserver, CallServiceObserver, R // MARK: - Create Views func createViews() { + self.view.isUserInteractionEnabled = true + self.view.addGestureRecognizer(OWSAnyTouchGestureRecognizer(target:self, + action:#selector(didTouchRootView))) + // Dark blurred background. let blurEffect = UIBlurEffect(style: .dark) blurView = UIVisualEffectView(effect: blurEffect) + blurView.isUserInteractionEnabled = false self.view.addSubview(blurView) // Create the video views first, as they are under the other views. @@ -151,9 +162,16 @@ class CallViewController: UIViewController, CallObserver, CallServiceObserver, R createIncomingCallControls() } + func didTouchRootView(sender: UIGestureRecognizer) { + if !remoteVideoView.isHidden { + areRemoteVideoControlsHidden = !areRemoteVideoControlsHidden + } + } + func createVideoViews() { remoteVideoView = RTCEAGLVideoView() remoteVideoView.delegate = self + remoteVideoView.isUserInteractionEnabled = false localVideoView = RTCCameraPreviewView() remoteVideoView.isHidden = true localVideoView.isHidden = true @@ -535,6 +553,16 @@ class CallViewController: UIViewController, CallObserver, CallServiceObserver, R audioModeVideoButton.isHidden = !remoteVideoView.isHidden videoModeVideoButton.isHidden = remoteVideoView.isHidden + // Also hide other controls if user has tapped to hide them. + if areRemoteVideoControlsHidden && !remoteVideoView.isHidden { + contactNameLabel.isHidden = true + callStatusLabel.isHidden = true + ongoingCallView.isHidden = true + } else { + contactNameLabel.isHidden = false + callStatusLabel.isHidden = false + } + // Dismiss Handling switch callState { case .remoteHangup, .remoteBusy, .localFailure: @@ -708,6 +736,7 @@ class CallViewController: UIViewController, CallObserver, CallServiceObserver, R remoteVideoView.renderFrame(nil) self.remoteVideoTrack = remoteVideoTrack self.remoteVideoTrack?.add(remoteVideoView) + areRemoteVideoControlsHidden = false if remoteVideoTrack == nil { remoteVideoSize = CGSize.zero diff --git a/Signal/src/views/OWSAnyTouchGestureRecognizer.h b/Signal/src/views/OWSAnyTouchGestureRecognizer.h new file mode 100644 index 000000000..fa310ebb6 --- /dev/null +++ b/Signal/src/views/OWSAnyTouchGestureRecognizer.h @@ -0,0 +1,16 @@ +// +// Copyright (c) 2017 Open Whisper Systems. All rights reserved. +// + +#import + +// This custom GR can be used to detect touches when they +// begin in a view. In order to honor touch dispatch, this +// GR will ignore touches that: +// +// * Are not single touches. +// * Are not in the view for this GR. +// * Are inside a visible, interaction-enabled subview. +@interface OWSAnyTouchGestureRecognizer : UIGestureRecognizer + +@end diff --git a/Signal/src/views/OWSAnyTouchGestureRecognizer.m b/Signal/src/views/OWSAnyTouchGestureRecognizer.m new file mode 100644 index 000000000..228710152 --- /dev/null +++ b/Signal/src/views/OWSAnyTouchGestureRecognizer.m @@ -0,0 +1,84 @@ +// +// Copyright (c) 2017 Open Whisper Systems. All rights reserved. +// + +#import "OWSAnyTouchGestureRecognizer.h" +#import + +@implementation OWSAnyTouchGestureRecognizer + +- (BOOL)canPreventGestureRecognizer:(UIGestureRecognizer *)preventedGestureRecognizer +{ + return NO; +} + +- (BOOL)canBePreventedByGestureRecognizer:(UIGestureRecognizer *)preventedGestureRecognizer +{ + return NO; +} + +- (BOOL)shouldRequireFailureOfGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer +{ + return NO; +} + +- (BOOL)shouldBeRequiredToFailByGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer +{ + return YES; +} + +- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event +{ + [super touchesBegan:touches withEvent:event]; + + if (self.state == UIGestureRecognizerStatePossible && [self isValidTouch:touches event:event]) { + self.state = UIGestureRecognizerStateRecognized; + } else { + self.state = UIGestureRecognizerStateFailed; + } +} + +- (BOOL)isValidTouch:(NSSet *)touches event:(UIEvent *)event +{ + if (event.allTouches.count > 1) { + return NO; + } + if (touches.count != 1) { + return NO; + } + + UITouch *touch = touches.anyObject; + CGPoint location = [touch locationInView:self.view]; + if (!CGRectContainsPoint(self.view.bounds, location)) { + return NO; + } + + if ([self subviewControlOfView:self.view containsTouch:touch]) { + return NO; + } + + return YES; +} + +- (BOOL)subviewControlOfView:(UIView *)superview containsTouch:(UITouch *)touch +{ + for (UIView *subview in superview.subviews) { + if (subview.hidden || !subview.userInteractionEnabled) { + continue; + } + CGPoint location = [touch locationInView:subview]; + if (!CGRectContainsPoint(subview.bounds, location)) { + continue; + } + if ([subview isKindOfClass:[UIControl class]]) { + return YES; + } + if ([self subviewControlOfView:subview containsTouch:touch]) { + return YES; + } + } + + return NO; +} + +@end