Swiftgram/LegacyComponents/TGCameraController.m
Ilya Laktyushin 888ce523da no message
2018-02-13 14:50:55 +04:00

1974 lines
72 KiB
Objective-C

#import "TGCameraController.h"
#import "LegacyComponentsInternal.h"
#import <objc/runtime.h>
#import <LegacyComponents/UIDevice+PlatformInfo.h>
#import <LegacyComponents/TGPaintUtils.h>
#import <LegacyComponents/TGPhotoEditorUtils.h>
#import <LegacyComponents/TGPhotoEditorAnimation.h>
#import <LegacyComponents/PGCamera.h>
#import <LegacyComponents/PGCameraCaptureSession.h>
#import <LegacyComponents/PGCameraDeviceAngleSampler.h>
#import <LegacyComponents/PGCameraVolumeButtonHandler.h>
#import <LegacyComponents/PGCameraMomentSession.h>
#import <LegacyComponents/TGCameraPreviewView.h>
#import <LegacyComponents/TGCameraMainPhoneView.h>
#import <LegacyComponents/TGCameraMainTabletView.h>
#import "TGCameraFocusCrosshairsControl.h"
#import <LegacyComponents/TGFullscreenContainerView.h>
#import <LegacyComponents/TGCameraPhotoPreviewController.h>
#import <LegacyComponents/TGPhotoEditorController.h>
#import <LegacyComponents/TGModernGalleryController.h>
#import <LegacyComponents/TGMediaPickerGalleryModel.h>
#import <LegacyComponents/TGMediaPickerGalleryPhotoItem.h>
#import <LegacyComponents/TGMediaPickerGalleryVideoItem.h>
#import <LegacyComponents/TGMediaPickerGalleryVideoItemView.h>
#import <LegacyComponents/TGModernGalleryVideoView.h>
#import <LegacyComponents/TGMediaAssetImageSignals.h>
#import <LegacyComponents/PGPhotoEditorValues.h>
#import <LegacyComponents/TGVideoEditAdjustments.h>
#import <LegacyComponents/TGPaintingData.h>
#import <LegacyComponents/UIImage+TGMediaEditableItem.h>
#import <LegacyComponents/AVURLAsset+TGMediaItem.h>
#import <LegacyComponents/TGModernGalleryZoomableScrollViewSwipeGestureRecognizer.h>
#import <LegacyComponents/TGMediaAssetsLibrary.h>
#import <LegacyComponents/TGTimerTarget.h>
const CGFloat TGCameraSwipeMinimumVelocity = 600.0f;
const CGFloat TGCameraSwipeVelocityThreshold = 700.0f;
const CGFloat TGCameraSwipeDistanceThreshold = 128.0f;
const NSTimeInterval TGCameraMinimumClipDuration = 4.0f;
@implementation TGCameraControllerWindow
static CGPoint TGCameraControllerClampPointToScreenSize(__unused id self, __unused SEL _cmd, CGPoint point)
{
CGSize screenSize = TGScreenSize();
return CGPointMake(MAX(0, MIN(point.x, screenSize.width)), MAX(0, MIN(point.y, screenSize.height)));
}
+ (void)initialize
{
static bool initialized = false;
if (!initialized)
{
initialized = true;
if ([UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPhone && (iosMajorVersion() > 8 || (iosMajorVersion() == 8 && iosMinorVersion() >= 3)))
{
FreedomDecoration instanceDecorations[] =
{
{ .name = 0x4ea0b831U,
.imp = (IMP)&TGCameraControllerClampPointToScreenSize,
.newIdentifier = FreedomIdentifierEmpty,
.newEncoding = FreedomIdentifierEmpty
}
};
freedomClassAutoDecorate(0x913b3af6, NULL, 0, instanceDecorations, sizeof(instanceDecorations) / sizeof(instanceDecorations[0]));
}
}
}
@end
@interface TGCameraController () <UIGestureRecognizerDelegate>
{
bool _standalone;
TGCameraControllerIntent _intent;
PGCamera *_camera;
PGCameraVolumeButtonHandler *_buttonHandler;
PGCameraMomentSession *_momentSession;
UIView *_autorotationCorrectionView;
UIView *_backgroundView;
TGCameraPreviewView *_previewView;
TGCameraMainView *_interfaceView;
UIView *_overlayView;
TGCameraFocusCrosshairsControl *_focusControl;
TGModernGalleryVideoView *_segmentPreviewView;
bool _previewingSegment;
UISwipeGestureRecognizer *_photoSwipeGestureRecognizer;
UISwipeGestureRecognizer *_videoSwipeGestureRecognizer;
TGModernGalleryZoomableScrollViewSwipeGestureRecognizer *_panGestureRecognizer;
UIPinchGestureRecognizer *_pinchGestureRecognizer;
CGFloat _dismissProgress;
bool _dismissing;
bool _finishedWithResult;
TGMediaEditingContext *_editingContext;
NSTimer *_switchToVideoTimer;
NSTimer *_startRecordingTimer;
bool _recordingByShutterHold;
bool _stopRecordingOnRelease;
bool _shownMicrophoneAlert;
id<LegacyComponentsContext> _context;
bool _saveEditedPhotos;
bool _saveCapturedMedia;
}
@end
@implementation TGCameraController
- (instancetype)initWithContext:(id<LegacyComponentsContext>)context saveEditedPhotos:(bool)saveEditedPhotos saveCapturedMedia:(bool)saveCapturedMedia
{
return [self initWithContext:context saveEditedPhotos:saveEditedPhotos saveCapturedMedia:saveCapturedMedia intent:TGCameraControllerGenericIntent];
}
- (instancetype)initWithContext:(id<LegacyComponentsContext>)context saveEditedPhotos:(bool)saveEditedPhotos saveCapturedMedia:(bool)saveCapturedMedia intent:(TGCameraControllerIntent)intent
{
return [self initWithContext:context saveEditedPhotos:saveEditedPhotos saveCapturedMedia:saveCapturedMedia camera:[[PGCamera alloc] init] previewView:nil intent:intent];
}
- (instancetype)initWithContext:(id<LegacyComponentsContext>)context saveEditedPhotos:(bool)saveEditedPhotos saveCapturedMedia:(bool)saveCapturedMedia camera:(PGCamera *)camera previewView:(TGCameraPreviewView *)previewView intent:(TGCameraControllerIntent)intent
{
self = [super initWithContext:context];
if (self != nil)
{
_context = context;
if (previewView == nil)
_standalone = true;
_intent = intent;
_camera = camera;
_previewView = previewView;
if (_intent == TGCameraControllerAvatarIntent)
_allowCaptions = false;
_saveEditedPhotos = saveEditedPhotos;
_saveCapturedMedia = saveCapturedMedia;
}
return self;
}
- (void)dealloc
{
_camera.beganModeChange = nil;
_camera.finishedModeChange = nil;
_camera.beganPositionChange = nil;
_camera.finishedPositionChange = nil;
_camera.beganAdjustingFocus = nil;
_camera.finishedAdjustingFocus = nil;
_camera.flashActivityChanged = nil;
_camera.flashAvailabilityChanged = nil;
_camera.beganVideoRecording = nil;
_camera.finishedVideoRecording = nil;
_camera.captureInterrupted = nil;
_camera.requestedCurrentInterfaceOrientation = nil;
_camera.deviceAngleSampler.deviceOrientationChanged = nil;
PGCamera *camera = _camera;
if (_finishedWithResult || _standalone)
[camera stopCaptureForPause:false completion:nil];
[[[LegacyComponentsGlobals provider] applicationInstance] setIdleTimerDisabled:false];
}
- (void)loadView
{
[super loadView];
object_setClass(self.view, [TGFullscreenContainerView class]);
CGSize screenSize = TGScreenSize();
CGRect screenBounds = CGRectMake(0, 0, screenSize.width, screenSize.height);
if ([UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPhone)
self.view.frame = screenBounds;
_autorotationCorrectionView = [[UIView alloc] initWithFrame:screenBounds];
_autorotationCorrectionView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
[self.view addSubview:_autorotationCorrectionView];
_backgroundView = [[UIView alloc] initWithFrame:screenBounds];
_backgroundView.backgroundColor = [UIColor blackColor];
[_autorotationCorrectionView addSubview:_backgroundView];
if (_previewView == nil)
{
_previewView = [[TGCameraPreviewView alloc] initWithFrame:[TGCameraController _cameraPreviewFrameForScreenSize:screenSize mode:PGCameraModePhoto]];
[_camera attachPreviewView:_previewView];
[_autorotationCorrectionView addSubview:_previewView];
}
_overlayView = [[UIView alloc] initWithFrame:screenBounds];
_overlayView.clipsToBounds = true;
_overlayView.frame = [TGCameraController _cameraPreviewFrameForScreenSize:screenSize mode:_camera.cameraMode];
[_autorotationCorrectionView addSubview:_overlayView];
UIInterfaceOrientation interfaceOrientation = [[LegacyComponentsGlobals provider] applicationStatusBarOrientation];
if (interfaceOrientation == UIInterfaceOrientationPortrait)
interfaceOrientation = [TGCameraController _interfaceOrientationForDeviceOrientation:_camera.deviceAngleSampler.deviceOrientation];
__weak TGCameraController *weakSelf = self;
_focusControl = [[TGCameraFocusCrosshairsControl alloc] initWithFrame:_overlayView.bounds];
_focusControl.enabled = (_camera.supportsFocusPOI || _camera.supportsExposurePOI);
_focusControl.stopAutomatically = (_focusControl.enabled && !_camera.supportsFocusPOI);
_focusControl.previewView = _previewView;
_focusControl.focusPOIChanged = ^(CGPoint point)
{
__strong TGCameraController *strongSelf = weakSelf;
if (strongSelf == nil)
return;
[strongSelf->_camera setFocusPoint:point];
};
_focusControl.beganExposureChange = ^
{
__strong TGCameraController *strongSelf = weakSelf;
if (strongSelf == nil)
return;
[strongSelf->_camera beginExposureTargetBiasChange];
};
_focusControl.exposureChanged = ^(CGFloat value)
{
__strong TGCameraController *strongSelf = weakSelf;
if (strongSelf == nil)
return;
[strongSelf->_camera setExposureTargetBias:value];
};
_focusControl.endedExposureChange = ^
{
__strong TGCameraController *strongSelf = weakSelf;
if (strongSelf == nil)
return;
[strongSelf->_camera endExposureTargetBiasChange];
};
[_focusControl setInterfaceOrientation:interfaceOrientation animated:false];
[_overlayView addSubview:_focusControl];
if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPhone)
{
_panGestureRecognizer = [[TGModernGalleryZoomableScrollViewSwipeGestureRecognizer alloc] initWithTarget:self action:@selector(handlePan:)];
_panGestureRecognizer.delegate = self;
_panGestureRecognizer.delaysTouchesBegan = true;
_panGestureRecognizer.cancelsTouchesInView = false;
[_overlayView addGestureRecognizer:_panGestureRecognizer];
}
_pinchGestureRecognizer = [[UIPinchGestureRecognizer alloc] initWithTarget:self action:@selector(handlePinch:)];
_pinchGestureRecognizer.delegate = self;
[_overlayView addGestureRecognizer:_pinchGestureRecognizer];
if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPhone)
{
_interfaceView = [[TGCameraMainPhoneView alloc] initWithFrame:screenBounds];
[_interfaceView setInterfaceOrientation:interfaceOrientation animated:false];
}
else
{
_interfaceView = [[TGCameraMainTabletView alloc] initWithFrame:screenBounds];
[_interfaceView setInterfaceOrientation:interfaceOrientation animated:false];
CGSize referenceSize = [self referenceViewSizeForOrientation:interfaceOrientation];
if (referenceSize.width > referenceSize.height)
referenceSize = CGSizeMake(referenceSize.height, referenceSize.width);
_interfaceView.transform = CGAffineTransformMakeRotation(TGRotationForInterfaceOrientation(interfaceOrientation));
_interfaceView.frame = CGRectMake(0, 0, referenceSize.width, referenceSize.height);
}
_interfaceView.requestedVideoRecordingDuration = ^NSTimeInterval
{
__strong TGCameraController *strongSelf = weakSelf;
if (strongSelf == nil)
return 0.0;
return strongSelf->_camera.videoRecordingDuration;
};
_interfaceView.cameraFlipped = ^
{
__strong TGCameraController *strongSelf = weakSelf;
if (strongSelf == nil)
return;
[strongSelf->_camera togglePosition];
};
_interfaceView.cameraShouldLeaveMode = ^bool(__unused PGCameraMode mode)
{
__strong TGCameraController *strongSelf = weakSelf;
if (strongSelf == nil)
return true;
if (strongSelf->_momentSession != nil && strongSelf->_momentSession.hasSegments)
{
return false;
}
return true;
};
_interfaceView.cameraModeChanged = ^(PGCameraMode mode)
{
__strong TGCameraController *strongSelf = weakSelf;
if (strongSelf == nil)
return;
[strongSelf->_camera setCameraMode:mode];
if (mode == PGCameraModeClip)
strongSelf->_momentSession = [[PGCameraMomentSession alloc] initWithCamera:strongSelf->_camera];
else
strongSelf->_momentSession = nil;
};
_interfaceView.flashModeChanged = ^(PGCameraFlashMode mode)
{
__strong TGCameraController *strongSelf = weakSelf;
if (strongSelf == nil)
return;
[strongSelf->_camera setFlashMode:mode];
};
_interfaceView.shutterPressed = ^(bool fromHardwareButton)
{
__strong TGCameraController *strongSelf = weakSelf;
if (strongSelf == nil)
return;
if (fromHardwareButton)
[strongSelf->_interfaceView setShutterButtonHighlighted:true];
[strongSelf shutterPressed];
};
_interfaceView.shutterReleased = ^(bool fromHardwareButton)
{
__strong TGCameraController *strongSelf = weakSelf;
if (strongSelf == nil)
return;
if (fromHardwareButton)
[strongSelf->_interfaceView setShutterButtonHighlighted:false];
if (strongSelf->_previewView.hidden)
return;
[strongSelf shutterReleased];
};
_interfaceView.cancelPressed = ^
{
__strong TGCameraController *strongSelf = weakSelf;
if (strongSelf == nil)
return;
void (^cancelBlock)(void) = ^
{
[strongSelf beginTransitionOutWithVelocity:0.0f];
};
if (strongSelf->_momentSession != nil && strongSelf->_momentSession.hasSegments)
{
[strongSelf->_interfaceView showMomentCaptureDismissWarningWithCompletion:^(bool dismiss)
{
if (dismiss)
cancelBlock();
}];
}
else
{
cancelBlock();
}
};
_interfaceView.deleteSegmentButtonPressed = ^
{
__strong TGCameraController *strongSelf = weakSelf;
if (strongSelf == nil)
return;
if (!strongSelf->_momentSession.hasSegments)
return;
if (!strongSelf->_previewingSegment)
{
[strongSelf previewLastSegment];
}
else
{
strongSelf->_previewingSegment = false;
[strongSelf->_momentSession removeLastSegment];
}
};
if (_intent == TGCameraControllerAvatarIntent)
[_interfaceView setHasModeControl:false];
if (iosMajorVersion() >= 11)
{
_backgroundView.accessibilityIgnoresInvertColors = true;
_interfaceView.accessibilityIgnoresInvertColors = true;
_focusControl.accessibilityIgnoresInvertColors = true;
}
[_autorotationCorrectionView addSubview:_interfaceView];
_photoSwipeGestureRecognizer = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:@selector(handleSwipe:)];
_photoSwipeGestureRecognizer.delegate = self;
[_autorotationCorrectionView addGestureRecognizer:_photoSwipeGestureRecognizer];
_videoSwipeGestureRecognizer = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:@selector(handleSwipe:)];
_videoSwipeGestureRecognizer.delegate = self;
[_autorotationCorrectionView addGestureRecognizer:_videoSwipeGestureRecognizer];
if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPhone)
{
_photoSwipeGestureRecognizer.direction = UISwipeGestureRecognizerDirectionLeft;
_videoSwipeGestureRecognizer.direction = UISwipeGestureRecognizerDirectionRight;
}
else
{
_photoSwipeGestureRecognizer.direction = UISwipeGestureRecognizerDirectionUp;
_videoSwipeGestureRecognizer.direction = UISwipeGestureRecognizerDirectionDown;
}
void (^buttonPressed)(void) = ^
{
__strong TGCameraController *strongSelf = weakSelf;
if (strongSelf == nil)
return;
strongSelf->_interfaceView.shutterPressed(true);
};
void (^buttonReleased)(void) = ^
{ __strong TGCameraController *strongSelf = weakSelf;
if (strongSelf == nil)
return;
strongSelf->_interfaceView.shutterReleased(true);
};
_buttonHandler = [[PGCameraVolumeButtonHandler alloc] initWithUpButtonPressedBlock:buttonPressed upButtonReleasedBlock:buttonReleased downButtonPressedBlock:buttonPressed downButtonReleasedBlock:buttonReleased];
[self _configureCamera];
}
- (void)_configureCamera
{
__weak TGCameraController *weakSelf = self;
_camera.requestedCurrentInterfaceOrientation = ^UIInterfaceOrientation(bool *mirrored)
{
__strong TGCameraController *strongSelf = weakSelf;
if (strongSelf == nil)
return UIInterfaceOrientationUnknown;
if (mirrored != NULL)
{
TGCameraPreviewView *previewView = strongSelf->_previewView;
if (previewView != nil)
*mirrored = previewView.captureConnection.videoMirrored;
}
return [strongSelf->_interfaceView interfaceOrientation];
};
_camera.beganModeChange = ^(PGCameraMode mode, void(^commitBlock)(void))
{
__strong TGCameraController *strongSelf = weakSelf;
if (strongSelf == nil)
return;
strongSelf->_buttonHandler.ignoring = true;
[strongSelf->_focusControl reset];
strongSelf->_focusControl.active = false;
strongSelf.view.userInteractionEnabled = false;
PGCameraMode currentMode = strongSelf->_camera.cameraMode;
bool generalModeNotChanged = (mode == PGCameraModePhoto && currentMode == PGCameraModeSquare) || (mode == PGCameraModeSquare && currentMode == PGCameraModePhoto) || (mode == PGCameraModeVideo && currentMode == PGCameraModeClip) || (mode == PGCameraModeClip && currentMode == PGCameraModeVideo);
if ((mode == PGCameraModeVideo || mode == PGCameraModeClip) && !generalModeNotChanged)
{
[[LegacyComponentsGlobals provider] pauseMusicPlayback];
}
if (generalModeNotChanged)
{
if (commitBlock != nil)
commitBlock();
}
else
{
strongSelf->_camera.zoomLevel = 0.0f;
[strongSelf->_camera captureNextFrameCompletion:^(UIImage *image)
{
if (commitBlock != nil)
commitBlock();
image = TGCameraModeSwitchImage(image, CGSizeMake(image.size.width, image.size.height));
TGDispatchOnMainThread(^
{
[strongSelf->_previewView beginTransitionWithSnapshotImage:image animated:true];
});
}];
}
};
_camera.finishedModeChange = ^
{
__strong TGCameraController *strongSelf = weakSelf;
if (strongSelf == nil)
return;
TGDispatchOnMainThread(^
{
[strongSelf->_previewView endTransitionAnimated:true];
if (!strongSelf->_dismissing)
{
strongSelf.view.userInteractionEnabled = true;
[strongSelf resizePreviewViewForCameraMode:strongSelf->_camera.cameraMode];
strongSelf->_focusControl.active = true;
[strongSelf->_interfaceView setFlashMode:strongSelf->_camera.flashMode];
[strongSelf->_buttonHandler enableIn:1.5f];
if (strongSelf->_camera.cameraMode == PGCameraModeVideo && ([PGCamera microphoneAuthorizationStatus] == PGMicrophoneAuthorizationStatusRestricted || [PGCamera microphoneAuthorizationStatus] == PGMicrophoneAuthorizationStatusDenied) && !strongSelf->_shownMicrophoneAlert)
{
[[[LegacyComponentsGlobals provider] accessChecker] checkMicrophoneAuthorizationStatusForIntent:TGMicrophoneAccessIntentVideo alertDismissCompletion:nil];
strongSelf->_shownMicrophoneAlert = true;
}
}
});
};
_camera.beganPositionChange = ^(bool targetPositionHasFlash, bool targetPositionHasZoom, void(^commitBlock)(void))
{
__strong TGCameraController *strongSelf = weakSelf;
if (strongSelf == nil)
return;
[strongSelf->_focusControl reset];
[strongSelf->_interfaceView setHasFlash:targetPositionHasFlash];
[strongSelf->_interfaceView setHasZoom:targetPositionHasZoom];
strongSelf->_camera.zoomLevel = 0.0f;
strongSelf.view.userInteractionEnabled = false;
[strongSelf->_camera captureNextFrameCompletion:^(UIImage *image)
{
if (commitBlock != nil)
commitBlock();
image = TGCameraPositionSwitchImage(image, CGSizeMake(image.size.width, image.size.height));
TGDispatchOnMainThread(^
{
[UIView transitionWithView:strongSelf->_previewView duration:0.4f options:UIViewAnimationOptionTransitionFlipFromLeft | UIViewAnimationOptionCurveEaseOut animations:^
{
[strongSelf->_previewView beginTransitionWithSnapshotImage:image animated:false];
} completion:^(__unused BOOL finished)
{
strongSelf.view.userInteractionEnabled = true;
}];
});
}];
};
_camera.finishedPositionChange = ^
{
__strong TGCameraController *strongSelf = weakSelf;
if (strongSelf == nil)
return;
TGDispatchOnMainThread(^
{
[strongSelf->_previewView endTransitionAnimated:true];
[strongSelf->_interfaceView setZoomLevel:0.0f displayNeeded:false];
if (strongSelf->_camera.hasFlash && strongSelf->_camera.flashActive)
[strongSelf->_interfaceView setFlashActive:true];
strongSelf->_focusControl.enabled = (strongSelf->_camera.supportsFocusPOI || strongSelf->_camera.supportsExposurePOI);
strongSelf->_focusControl.stopAutomatically = (strongSelf->_focusControl.enabled && !strongSelf->_camera.supportsFocusPOI);
});
};
_camera.beganAdjustingFocus = ^
{
__strong TGCameraController *strongSelf = weakSelf;
if (strongSelf == nil)
return;
[strongSelf->_focusControl playAutoFocusAnimation];
};
_camera.finishedAdjustingFocus = ^
{
__strong TGCameraController *strongSelf = weakSelf;
if (strongSelf == nil)
return;
[strongSelf->_focusControl stopAutoFocusAnimation];
};
_camera.flashActivityChanged = ^(bool active)
{
__strong TGCameraController *strongSelf = weakSelf;
if (strongSelf == nil)
return;
if (strongSelf->_camera.flashMode != PGCameraFlashModeAuto)
active = false;
TGDispatchOnMainThread(^
{
if (!strongSelf->_camera.isRecordingVideo)
[strongSelf->_interfaceView setFlashActive:active];
});
};
_camera.flashAvailabilityChanged = ^(bool available)
{
__strong TGCameraController *strongSelf = weakSelf;
if (strongSelf == nil)
return;
[strongSelf->_interfaceView setFlashUnavailable:!available];
};
_camera.beganVideoRecording = ^(bool moment)
{
__strong TGCameraController *strongSelf = weakSelf;
if (strongSelf == nil)
return;
strongSelf->_focusControl.ignoreAutofocusing = true;
if (!moment)
[strongSelf->_interfaceView setRecordingVideo:true animated:true];
};
_camera.captureInterrupted = ^(AVCaptureSessionInterruptionReason reason)
{
__strong TGCameraController *strongSelf = weakSelf;
if (strongSelf == nil)
return;
if (reason == AVCaptureSessionInterruptionReasonVideoDeviceNotAvailableWithMultipleForegroundApps)
[strongSelf beginTransitionOutWithVelocity:0.0f];
};
_camera.finishedVideoRecording = ^(bool moment)
{
__strong TGCameraController *strongSelf = weakSelf;
if (strongSelf == nil)
return;
strongSelf->_focusControl.ignoreAutofocusing = false;
if (!moment)
[strongSelf->_interfaceView setFlashMode:PGCameraFlashModeOff];
};
_camera.deviceAngleSampler.deviceOrientationChanged = ^(UIDeviceOrientation orientation)
{
__strong TGCameraController *strongSelf = weakSelf;
if (strongSelf == nil)
return;
[strongSelf handleDeviceOrientationChangedTo:orientation];
};
}
#pragma mark - View Life Cycle
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
[UIView animateWithDuration:0.3f animations:^
{
[_context setApplicationStatusBarAlpha:0.0f];
}];
[[[LegacyComponentsGlobals provider] applicationInstance] setIdleTimerDisabled:true];
if (!_camera.isCapturing)
[_camera startCaptureForResume:false completion:nil];
}
- (void)viewWillDisappear:(BOOL)animated
{
[super viewWillDisappear:animated];
[UIView animateWithDuration:0.3f animations:^
{
[_context setApplicationStatusBarAlpha:1.0f];
}];
}
- (void)viewWillLayoutSubviews
{
[super viewWillLayoutSubviews];
if ([self shouldCorrectAutorotation])
[self applyAutorotationCorrectingTransformForOrientation:[[LegacyComponentsGlobals provider] applicationStatusBarOrientation]];
}
- (bool)shouldCorrectAutorotation
{
return [UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPad;
}
- (void)applyAutorotationCorrectingTransformForOrientation:(UIInterfaceOrientation)orientation
{
CGSize screenSize = TGScreenSize();
CGRect screenBounds = CGRectMake(0, 0, screenSize.width, screenSize.height);
_autorotationCorrectionView.transform = CGAffineTransformIdentity;
_autorotationCorrectionView.frame = screenBounds;
CGAffineTransform transform = CGAffineTransformIdentity;
switch (orientation)
{
case UIInterfaceOrientationPortraitUpsideDown:
transform = CGAffineTransformMakeRotation(M_PI);
break;
case UIInterfaceOrientationLandscapeLeft:
transform = CGAffineTransformMakeRotation(M_PI_2);
break;
case UIInterfaceOrientationLandscapeRight:
transform = CGAffineTransformMakeRotation(-M_PI_2);
break;
default:
break;
}
_autorotationCorrectionView.transform = transform;
CGSize bounds = [_context fullscreenBounds].size;
_autorotationCorrectionView.center = CGPointMake(bounds.width / 2, bounds.height / 2);
}
- (UIInterfaceOrientationMask)supportedInterfaceOrientations
{
if ([UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPad)
return UIInterfaceOrientationMaskAll;
return UIInterfaceOrientationMaskPortrait;
}
- (BOOL)shouldAutorotate
{
if ([UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPad)
return true;
return false;
}
- (void)setInterfaceHidden:(bool)hidden animated:(bool)animated
{
if (animated)
{
if (hidden && _interfaceView.alpha < FLT_EPSILON)
return;
CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"opacity"];
animation.fromValue = @(_interfaceView.alpha);
animation.toValue = @(hidden ? 0.0f : 1.0f);
animation.duration = 0.2f;
[_interfaceView.layer addAnimation:animation forKey:@"opacity"];
_interfaceView.alpha = hidden ? 0.0f : 1.0f;
}
else
{
[_interfaceView.layer removeAllAnimations];
_interfaceView.alpha = 0.0f;
}
}
#pragma mark -
- (void)previewLastSegment
{
PGCameraMomentSegment *segment = _momentSession.lastSegment;
AVPlayer *player = [AVPlayer playerWithURL:segment.fileURL];
_segmentPreviewView = [[TGModernGalleryVideoView alloc] initWithFrame:_previewView.frame player:player];
[_previewView.superview addSubview:_segmentPreviewView];
}
#pragma mark -
- (void)startVideoRecording
{
__weak TGCameraController *weakSelf = self;
if (_camera.cameraMode == PGCameraModePhoto)
{
_switchToVideoTimer = nil;
_camera.onAutoStartVideoRecording = ^
{
__strong TGCameraController *strongSelf = weakSelf;
if (strongSelf == nil)
return;
strongSelf->_stopRecordingOnRelease = true;
[strongSelf->_camera startVideoRecordingForMoment:false completion:^(NSURL *outputURL, __unused CGAffineTransform transform, CGSize dimensions, NSTimeInterval duration, bool success)
{
__strong TGCameraController *strongSelf = weakSelf;
if (strongSelf == nil)
return;
if (success)
[strongSelf presentVideoResultControllerWithURL:outputURL dimensions:dimensions duration:duration completion:nil];
else
[strongSelf->_interfaceView setRecordingVideo:false animated:false];
}];
};
_camera.autoStartVideoRecording = true;
[_camera setCameraMode:PGCameraModeVideo];
[_interfaceView setCameraMode:PGCameraModeVideo];
}
else if (_camera.cameraMode == PGCameraModeVideo)
{
_startRecordingTimer = nil;
[_camera startVideoRecordingForMoment:false completion:^(NSURL *outputURL, __unused CGAffineTransform transform, CGSize dimensions, NSTimeInterval duration, bool success)
{
__strong TGCameraController *strongSelf = weakSelf;
if (strongSelf == nil)
return;
if (success)
[strongSelf presentVideoResultControllerWithURL:outputURL dimensions:dimensions duration:duration completion:nil];
else
[strongSelf->_interfaceView setRecordingVideo:false animated:false];
}];
_stopRecordingOnRelease = true;
}
}
- (void)shutterPressed
{
PGCameraMode cameraMode = _camera.cameraMode;
switch (cameraMode)
{
case PGCameraModePhoto:
{
if (_intent != TGCameraControllerAvatarIntent)
{
_switchToVideoTimer = [TGTimerTarget scheduledMainThreadTimerWithTarget:self action:@selector(startVideoRecording) interval:0.25 repeat:false];
}
}
break;
case PGCameraModeVideo:
{
if (!_camera.isRecordingVideo)
{
_startRecordingTimer = [TGTimerTarget scheduledMainThreadTimerWithTarget:self action:@selector(startVideoRecording) interval:0.25 repeat:false];
}
else
{
_stopRecordingOnRelease = true;
}
}
break;
case PGCameraModeClip:
{
if (_momentSession == nil)
return;
[_momentSession captureSegment];
}
break;
default:
break;
}
}
- (void)shutterReleased
{
[_switchToVideoTimer invalidate];
_switchToVideoTimer = nil;
[_startRecordingTimer invalidate];
_startRecordingTimer = nil;
PGCameraMode cameraMode = _camera.cameraMode;
if (cameraMode == PGCameraModePhoto || cameraMode == PGCameraModeSquare)
{
self.view.userInteractionEnabled = false;
_buttonHandler.enabled = false;
[_buttonHandler ignoreEventsFor:1.5f andDisable:true];
_camera.disabled = true;
[_camera takePhotoWithCompletion:^(UIImage *result, PGCameraShotMetadata *metadata)
{
TGDispatchOnMainThread(^
{
[self presentPhotoResultControllerWithImage:result metadata:metadata completion:^
{
self.view.userInteractionEnabled = true;
}];
});
}];
}
else if (cameraMode == PGCameraModeVideo)
{
if (!_camera.isRecordingVideo)
{
[_buttonHandler ignoreEventsFor:1.0f andDisable:false];
__weak TGCameraController *weakSelf = self;
[_camera startVideoRecordingForMoment:false completion:^(NSURL *outputURL, __unused CGAffineTransform transform, CGSize dimensions, NSTimeInterval duration, bool success)
{
__strong TGCameraController *strongSelf = weakSelf;
if (strongSelf == nil)
return;
if (success)
[strongSelf presentVideoResultControllerWithURL:outputURL dimensions:dimensions duration:duration completion:nil];
else
[strongSelf->_interfaceView setRecordingVideo:false animated:false];
}];
}
else if (_stopRecordingOnRelease)
{
_stopRecordingOnRelease = false;
_camera.disabled = true;
[_buttonHandler ignoreEventsFor:1.0f andDisable:true];
[_camera stopVideoRecording];
}
}
else if (cameraMode == PGCameraModeClip)
{
[_momentSession commitSegment];
}
}
#pragma mark - Photo Result
- (void)presentPhotoResultControllerWithImage:(UIImage *)image metadata:(PGCameraShotMetadata *)metadata completion:(void (^)(void))completion
{
[[[LegacyComponentsGlobals provider] applicationInstance] setIdleTimerDisabled:false];
if (image == nil || image.size.width < FLT_EPSILON)
{
[self beginTransitionOutWithVelocity:0.0f];
return;
}
id<LegacyComponentsOverlayWindowManager> windowManager = nil;
id<LegacyComponentsContext> windowContext = nil;
if ([UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPhone) {
windowManager = [_context makeOverlayWindowManager];
windowContext = [windowManager context];
} else {
windowContext = _context;
}
__weak TGCameraController *weakSelf = self;
TGOverlayController *overlayController = nil;
_focusControl.ignoreAutofocusing = true;
switch (_intent)
{
case TGCameraControllerAvatarIntent:
{
TGPhotoEditorController *controller = [[TGPhotoEditorController alloc] initWithContext:windowContext item:image intent:(TGPhotoEditorControllerFromCameraIntent | TGPhotoEditorControllerAvatarIntent) adjustments:nil caption:nil screenImage:image availableTabs:[TGPhotoEditorController defaultTabsForAvatarIntent] selectedTab:TGPhotoEditorCropTab];
__weak TGPhotoEditorController *weakController = controller;
controller.beginTransitionIn = ^UIView *(CGRect *referenceFrame, __unused UIView **parentView)
{
__strong TGCameraController *strongSelf = weakSelf;
if (strongSelf == nil)
return nil;
strongSelf->_previewView.hidden = true;
*referenceFrame = strongSelf->_previewView.frame;
UIImageView *imageView = [[UIImageView alloc] initWithFrame:strongSelf->_previewView.frame];
imageView.image = image;
return imageView;
};
controller.beginTransitionOut = ^UIView *(CGRect *referenceFrame, __unused UIView **parentView)
{
__strong TGCameraController *strongSelf = weakSelf;
if (strongSelf == nil)
return nil;
CGRect startFrame = CGRectZero;
if (referenceFrame != NULL)
{
startFrame = *referenceFrame;
*referenceFrame = strongSelf->_previewView.frame;
}
[strongSelf transitionBackFromResultControllerWithReferenceFrame:startFrame];
return strongSelf->_previewView;
};
controller.didFinishEditing = ^(PGPhotoEditorValues *editorValues, UIImage *resultImage, __unused UIImage *thumbnailImage, bool hasChanges)
{
if (!hasChanges)
return;
__strong TGCameraController *strongSelf = weakSelf;
if (strongSelf == nil)
return;
TGDispatchOnMainThread(^
{
if (strongSelf.finishedWithPhoto != nil)
strongSelf.finishedWithPhoto(nil, resultImage, nil, nil, nil, nil);
if (strongSelf.shouldStoreCapturedAssets)
{
[strongSelf _savePhotoToCameraRollWithOriginalImage:image editedImage:[editorValues toolsApplied] ? resultImage : nil];
}
__strong TGPhotoEditorController *strongController = weakController;
if (strongController != nil)
{
[strongController updateStatusBarAppearanceForDismiss];
[strongSelf _dismissTransitionForResultController:(TGOverlayController *)strongController];
}
});
};
controller.requestThumbnailImage = ^(id<TGMediaEditableItem> editableItem)
{
return [editableItem thumbnailImageSignal];
};
controller.requestOriginalScreenSizeImage = ^(id<TGMediaEditableItem> editableItem, NSTimeInterval position)
{
return [editableItem screenImageSignal:position];
};
controller.requestOriginalFullSizeImage = ^(id<TGMediaEditableItem> editableItem, NSTimeInterval position)
{
return [editableItem originalImageSignal:position];
};
overlayController = (TGOverlayController *)controller;
}
break;
default:
{
TGCameraPhotoPreviewController *controller = _shortcut ? [[TGCameraPhotoPreviewController alloc] initWithContext:windowContext image:image metadata:metadata recipientName:self.recipientName backButtonTitle:TGLocalized(@"Camera.Retake") doneButtonTitle:TGLocalized(@"Common.Next") saveCapturedMedia:_saveCapturedMedia saveEditedPhotos:_saveEditedPhotos] : [[TGCameraPhotoPreviewController alloc] initWithContext:windowContext image:image metadata:metadata recipientName:self.recipientName saveCapturedMedia:_saveCapturedMedia saveEditedPhotos:_saveEditedPhotos];
controller.allowCaptions = self.allowCaptions;
controller.shouldStoreAssets = self.shouldStoreCapturedAssets;
controller.suggestionContext = self.suggestionContext;
controller.hasTimer = self.hasTimer;
__weak TGCameraPhotoPreviewController *weakController = controller;
controller.beginTransitionIn = ^CGRect
{
__strong TGCameraController *strongSelf = weakSelf;
if (strongSelf == nil)
return CGRectZero;
strongSelf->_previewView.hidden = true;
return strongSelf->_previewView.frame;
};
controller.finishedTransitionIn = ^
{
__strong TGCameraController *strongSelf = weakSelf;
if (strongSelf != nil)
[strongSelf->_camera stopCaptureForPause:true completion:nil];
};
controller.beginTransitionOut = ^CGRect(CGRect referenceFrame)
{
__strong TGCameraController *strongSelf = weakSelf;
if (strongSelf == nil)
return CGRectZero;
[strongSelf->_camera startCaptureForResume:true completion:nil];
return [strongSelf transitionBackFromResultControllerWithReferenceFrame:referenceFrame];
};
controller.retakePressed = ^
{
__strong TGCameraController *strongSelf = weakSelf;
if (strongSelf == nil)
return;
[[[LegacyComponentsGlobals provider] applicationInstance] setIdleTimerDisabled:true];
};
controller.sendPressed = ^(TGOverlayController *controller, UIImage *resultImage, NSString *caption, NSArray *entities, NSArray *stickers, NSNumber *timer)
{
__strong TGCameraController *strongSelf = weakSelf;
if (strongSelf == nil)
return;
if (strongSelf.finishedWithPhoto != nil)
strongSelf.finishedWithPhoto(controller, resultImage, caption, entities, stickers, timer);
if (strongSelf->_shortcut)
return;
__strong TGOverlayController *strongController = weakController;
if (strongController != nil)
[strongSelf _dismissTransitionForResultController:strongController];
};
overlayController = controller;
}
break;
}
if (windowManager != nil)
{
TGOverlayController *contentController = overlayController;
if (_shortcut)
{
contentController = [[TGOverlayController alloc] init];
TGNavigationController *navigationController = [TGNavigationController navigationControllerWithControllers:@[overlayController]];
overlayController.navigationBarShouldBeHidden = true;
[contentController addChildViewController:navigationController];
[contentController.view addSubview:navigationController.view];
}
TGOverlayControllerWindow *controllerWindow = [[TGOverlayControllerWindow alloc] initWithManager:windowManager parentController:self contentController:contentController];
controllerWindow.windowLevel = self.view.window.windowLevel + 0.0001f;
controllerWindow.hidden = false;
}
else
{
[self addChildViewController:overlayController];
[self.view addSubview:overlayController.view];
}
if (completion != nil)
completion();
[UIView animateWithDuration:0.3f animations:^
{
_interfaceView.alpha = 0.0f;
}];
}
- (void)_savePhotoToCameraRollWithOriginalImage:(UIImage *)originalImage editedImage:(UIImage *)editedImage
{
if (!_saveEditedPhotos || originalImage == nil)
return;
SSignal *savePhotoSignal = _saveCapturedMedia ? [[TGMediaAssetsLibrary sharedLibrary] saveAssetWithImage:originalImage] : [SSignal complete];
if (_saveEditedPhotos && editedImage != nil)
savePhotoSignal = [savePhotoSignal then:[[TGMediaAssetsLibrary sharedLibrary] saveAssetWithImage:editedImage]];
[savePhotoSignal startWithNext:nil];
}
- (void)_saveVideoToCameraRollWithURL:(NSURL *)url completion:(void (^)(void))completion
{
if (!_saveCapturedMedia)
return;
[[[TGMediaAssetsLibrary sharedLibrary] saveAssetWithVideoAtUrl:url] startWithNext:nil error:^(__unused NSError *error)
{
if (completion != nil)
completion();
} completed:completion];
}
- (CGRect)transitionBackFromResultControllerWithReferenceFrame:(CGRect)referenceFrame
{
_camera.disabled = false;
_buttonHandler.enabled = true;
[_buttonHandler ignoreEventsFor:2.0f andDisable:false];
_previewView.hidden = false;
_focusControl.ignoreAutofocusing = false;
CGRect targetFrame = _previewView.frame;
_previewView.frame = referenceFrame;
POPSpringAnimation *animation = [TGPhotoEditorAnimation prepareTransitionAnimationForPropertyNamed:kPOPViewFrame];
animation.fromValue = [NSValue valueWithCGRect:referenceFrame];
animation.toValue = [NSValue valueWithCGRect:targetFrame];
[_previewView pop_addAnimation:animation forKey:@"frame"];
[UIView animateWithDuration:0.3f delay:0.1f options:UIViewAnimationOptionCurveLinear animations:^
{
_interfaceView.alpha = 1.0f;
} completion:nil];
_interfaceView.previewViewFrame = _previewView.frame;
[_interfaceView layoutPreviewRelativeViews];
return targetFrame;
}
#pragma mark - Video Result
- (void)presentVideoResultControllerWithURL:(NSURL *)url dimensions:(CGSize)dimensions duration:(NSTimeInterval)duration completion:(void (^)(void))completion
{
TGMediaEditingContext *editingContext = [[TGMediaEditingContext alloc] init];
_editingContext = editingContext;
[[[LegacyComponentsGlobals provider] applicationInstance] setIdleTimerDisabled:false];
id<LegacyComponentsOverlayWindowManager> windowManager = nil;
id<LegacyComponentsContext> windowContext = nil;
windowManager = [_context makeOverlayWindowManager];
windowContext = [windowManager context];
AVURLAsset *asset = [AVURLAsset assetWithURL:url];
AVAssetImageGenerator *generator = [[AVAssetImageGenerator alloc] initWithAsset:asset];
generator.appliesPreferredTrackTransform = true;
generator.maximumSize = CGSizeMake(640.0f, 640.0f);
CGImageRef imageRef = [generator copyCGImageAtTime:kCMTimeZero actualTime:NULL error:NULL];
UIImage *thumbnailImage = [[UIImage alloc] initWithCGImage:imageRef];
CGImageRelease(imageRef);
__weak TGCameraController *weakSelf = self;
TGMediaPickerGalleryVideoItem *videoItem = [[TGMediaPickerGalleryVideoItem alloc] initWithFileURL:url dimensions:dimensions duration:duration];
videoItem.editingContext = _editingContext;
videoItem.immediateThumbnailImage = thumbnailImage;
TGModernGalleryController *galleryController = [[TGModernGalleryController alloc] initWithContext:windowContext];
galleryController.adjustsStatusBarVisibility = false;
galleryController.hasFadeOutTransition = true;
TGMediaPickerGalleryModel *model = [[TGMediaPickerGalleryModel alloc] initWithContext:windowContext items:@[ videoItem ] focusItem:videoItem selectionContext:nil editingContext:_editingContext hasCaptions:self.allowCaptions hasTimer:self.hasTimer inhibitDocumentCaptions:self.inhibitDocumentCaptions hasSelectionPanel:false recipientName:self.recipientName];
model.controller = galleryController;
model.suggestionContext = self.suggestionContext;
model.willFinishEditingItem = ^(id<TGMediaEditableItem> editableItem, id<TGMediaEditAdjustments> adjustments, id representation, bool hasChanges)
{
__strong TGCameraController *strongSelf = weakSelf;
if (strongSelf == nil)
return;
if (hasChanges)
{
[editingContext setAdjustments:adjustments forItem:editableItem];
[editingContext setTemporaryRep:representation forItem:editableItem];
}
};
model.didFinishEditingItem = ^(id<TGMediaEditableItem> editableItem, __unused id<TGMediaEditAdjustments> adjustments, UIImage *resultImage, UIImage *thumbnailImage)
{
[editingContext setImage:resultImage thumbnailImage:thumbnailImage forItem:editableItem synchronous:false];
};
model.saveItemCaption = ^(__unused id<TGMediaEditableItem> item, NSString *caption, NSArray *entities)
{
__strong TGCameraController *strongSelf = weakSelf;
if (strongSelf != nil)
[strongSelf->_editingContext setCaption:caption entities:entities forItem:videoItem.avAsset];
};
model.interfaceView.hasSwipeGesture = false;
galleryController.model = model;
__weak TGModernGalleryController *weakGalleryController = galleryController;
__weak TGMediaPickerGalleryModel *weakModel = model;
model.interfaceView.donePressed = ^(__unused TGMediaPickerGalleryItem *item)
{
__strong TGCameraController *strongSelf = weakSelf;
if (strongSelf == nil)
return;
TGMediaPickerGalleryModel *strongModel = weakModel;
if (strongModel == nil)
return;
__strong TGModernGalleryController *strongController = weakGalleryController;
if (strongController == nil)
return;
TGMediaPickerGalleryVideoItemView *itemView = (TGMediaPickerGalleryVideoItemView *)[strongController itemViewForItem:strongController.currentItem];
[itemView stop];
[itemView setPlayButtonHidden:true animated:true];
TGVideoEditAdjustments *adjustments = (TGVideoEditAdjustments *)[strongSelf->_editingContext adjustmentsForItem:videoItem.avAsset];
NSString *caption = [strongSelf->_editingContext captionForItem:videoItem.avAsset];
NSArray *entities = [strongSelf->_editingContext entitiesForItem:videoItem.avAsset];
NSNumber *timer = [strongSelf->_editingContext timerForItem:videoItem.avAsset];
SSignal *thumbnailSignal = [SSignal single:thumbnailImage];
if (adjustments.trimStartValue > FLT_EPSILON)
{
thumbnailSignal = [TGMediaAssetImageSignals videoThumbnailForAVAsset:[AVURLAsset URLAssetWithURL:url options:nil] size:dimensions timestamp:CMTimeMakeWithSeconds(adjustments.trimStartValue, NSEC_PER_SEC)];
}
if ([adjustments cropAppliedForAvatar:false] || adjustments.hasPainting)
{
thumbnailSignal = [thumbnailSignal map:^UIImage *(UIImage *image)
{
CGRect scaledCropRect = CGRectMake(adjustments.cropRect.origin.x * image.size.width / adjustments.originalSize.width, adjustments.cropRect.origin.y * image.size.height / adjustments.originalSize.height, adjustments.cropRect.size.width * image.size.width / adjustments.originalSize.width, adjustments.cropRect.size.height * image.size.height / adjustments.originalSize.height);
return TGPhotoEditorCrop(image, adjustments.paintingData.image, adjustments.cropOrientation, 0, scaledCropRect, adjustments.cropMirrored, CGSizeMake(256, 256), image.size, true);
}];
}
[[thumbnailSignal deliverOn:[SQueue mainQueue]] startWithNext:^(UIImage *thumbnailImage)
{
if (strongSelf.finishedWithVideo != nil)
strongSelf.finishedWithVideo(strongController, url, thumbnailImage, duration, dimensions, adjustments, caption, entities, adjustments.paintingData.stickers, timer);
}];
if (strongSelf->_shortcut)
return;
[strongSelf _dismissTransitionForResultController:strongController];
if (strongSelf.shouldStoreCapturedAssets && timer == nil)
[strongSelf _saveVideoToCameraRollWithURL:url completion:nil];
};
CGSize snapshotSize = TGScaleToFill(CGSizeMake(480, 640), CGSizeMake(self.view.frame.size.width, self.view.frame.size.width));
UIView *snapshotView = [_previewView snapshotViewAfterScreenUpdates:false];
snapshotView.contentMode = UIViewContentModeScaleAspectFill;
snapshotView.frame = CGRectMake(_previewView.center.x - snapshotSize.width / 2, _previewView.center.y - snapshotSize.height / 2, snapshotSize.width, snapshotSize.height);
snapshotView.hidden = true;
[_previewView.superview insertSubview:snapshotView aboveSubview:_previewView];
galleryController.beginTransitionIn = ^UIView *(__unused TGMediaPickerGalleryItem *item, __unused TGModernGalleryItemView *itemView)
{
__strong TGCameraController *strongSelf = weakSelf;
if (strongSelf != nil)
{
TGModernGalleryController *strongGalleryController = weakGalleryController;
strongGalleryController.view.alpha = 0.0f;
[UIView animateWithDuration:0.3f animations:^
{
strongGalleryController.view.alpha = 1.0f;
strongSelf->_interfaceView.alpha = 0.0f;
}];
return snapshotView;
}
return nil;
};
galleryController.finishedTransitionIn = ^(__unused TGMediaPickerGalleryItem *item, __unused TGModernGalleryItemView *itemView)
{
__strong TGCameraController *strongSelf = weakSelf;
if (strongSelf == nil)
return;
[strongSelf->_camera stopCaptureForPause:true completion:nil];
snapshotView.hidden = true;
if (completion != nil)
completion();
};
galleryController.beginTransitionOut = ^UIView *(__unused TGMediaPickerGalleryItem *item, __unused TGModernGalleryItemView *itemView)
{
__strong TGCameraController *strongSelf = weakSelf;
if (strongSelf != nil)
{
[[[LegacyComponentsGlobals provider] applicationInstance] setIdleTimerDisabled:true];
[strongSelf->_interfaceView setRecordingVideo:false animated:false];
strongSelf->_buttonHandler.enabled = true;
[strongSelf->_buttonHandler ignoreEventsFor:2.0f andDisable:false];
strongSelf->_camera.disabled = false;
[strongSelf->_camera startCaptureForResume:true completion:nil];
if ([[NSFileManager defaultManager] fileExistsAtPath:url.path isDirectory:NULL])
[[NSFileManager defaultManager] removeItemAtURL:url error:NULL];
[UIView animateWithDuration:0.3f delay:0.1f options:UIViewAnimationOptionCurveLinear animations:^
{
strongSelf->_interfaceView.alpha = 1.0f;
} completion:nil];
return snapshotView;
}
return nil;
};
galleryController.completedTransitionOut = ^
{
[snapshotView removeFromSuperview];
TGModernGalleryController *strongGalleryController = weakGalleryController;
if (strongGalleryController != nil && strongGalleryController.overlayWindow == nil)
{
TGNavigationController *navigationController = (TGNavigationController *)strongGalleryController.navigationController;
TGOverlayControllerWindow *window = (TGOverlayControllerWindow *)navigationController.view.window;
if ([window isKindOfClass:[TGOverlayControllerWindow class]])
[window dismiss];
}
};
TGOverlayController *contentController = galleryController;
if (_shortcut)
{
contentController = [[TGOverlayController alloc] init];
TGNavigationController *navigationController = [TGNavigationController navigationControllerWithControllers:@[galleryController]];
galleryController.navigationBarShouldBeHidden = true;
[contentController addChildViewController:navigationController];
[contentController.view addSubview:navigationController.view];
}
TGOverlayControllerWindow *controllerWindow = [[TGOverlayControllerWindow alloc] initWithManager:windowManager parentController:self contentController:contentController];
controllerWindow.hidden = false;
controllerWindow.windowLevel = self.view.window.windowLevel + 0.0001f;
galleryController.view.clipsToBounds = true;
}
#pragma mark - Transition
- (void)beginTransitionInFromRect:(CGRect)rect
{
[_autorotationCorrectionView insertSubview:_previewView aboveSubview:_backgroundView];
_previewView.frame = rect;
_backgroundView.alpha = 0.0f;
_interfaceView.alpha = 0.0f;
[UIView animateWithDuration:0.3f animations:^
{
_backgroundView.alpha = 1.0f;
_interfaceView.alpha = 1.0f;
}];
CGRect fromFrame = rect;
CGRect toFrame = [TGCameraController _cameraPreviewFrameForScreenSize:TGScreenSize() mode:_camera.cameraMode];
if (!CGRectEqualToRect(fromFrame, CGRectZero))
{
POPSpringAnimation *frameAnimation = [POPSpringAnimation animationWithPropertyNamed:kPOPViewFrame];
frameAnimation.fromValue = [NSValue valueWithCGRect:fromFrame];
frameAnimation.toValue = [NSValue valueWithCGRect:toFrame];
frameAnimation.springSpeed = 20;
frameAnimation.springBounciness = 1;
[_previewView pop_addAnimation:frameAnimation forKey:@"frame"];
}
else
{
_previewView.frame = toFrame;
}
_interfaceView.previewViewFrame = toFrame;
[_interfaceView layoutPreviewRelativeViews];
}
- (void)beginTransitionOutWithVelocity:(CGFloat)velocity
{
_dismissing = true;
self.view.userInteractionEnabled = false;
_focusControl.active = false;
[UIView animateWithDuration:0.3f animations:^
{
[_context setApplicationStatusBarAlpha:1.0f];
}];
[self setInterfaceHidden:true animated:true];
[UIView animateWithDuration:0.25f animations:^
{
_backgroundView.alpha = 0.0f;
}];
CGRect referenceFrame = CGRectZero;
if (self.beginTransitionOut != nil)
referenceFrame = self.beginTransitionOut();
__weak TGCameraController *weakSelf = self;
if (_standalone)
{
[self simpleTransitionOutWithVelocity:velocity completion:^
{
__strong TGCameraController *strongSelf = weakSelf;
if (strongSelf == nil)
return;
[strongSelf dismiss];
}];
return;
}
bool resetNeeded = _camera.isResetNeeded;
if (resetNeeded)
[_previewView beginResetTransitionAnimated:true];
[_camera resetSynchronous:false completion:^
{
TGDispatchOnMainThread(^
{
if (resetNeeded)
[_previewView endResetTransitionAnimated:true];
});
}];
[_previewView.layer removeAllAnimations];
if (!CGRectIsEmpty(referenceFrame))
{
POPSpringAnimation *frameAnimation = [POPSpringAnimation animationWithPropertyNamed:kPOPViewFrame];
frameAnimation.fromValue = [NSValue valueWithCGRect:_previewView.frame];
frameAnimation.toValue = [NSValue valueWithCGRect:referenceFrame];
frameAnimation.springSpeed = 20;
frameAnimation.springBounciness = 1;
frameAnimation.completionBlock = ^(__unused POPAnimation *animation, __unused BOOL finished)
{
__strong TGCameraController *strongSelf = weakSelf;
if (strongSelf == nil)
return;
if (strongSelf.finishedTransitionOut != nil)
strongSelf.finishedTransitionOut();
[strongSelf dismiss];
};
[_previewView pop_addAnimation:frameAnimation forKey:@"frame"];
}
else
{
if (self.finishedTransitionOut != nil)
self.finishedTransitionOut();
[self dismiss];
}
}
- (void)_dismissTransitionForResultController:(TGOverlayController *)resultController
{
_finishedWithResult = true;
[_context setApplicationStatusBarAlpha:1.0f];
self.view.hidden = true;
[UIView animateWithDuration:0.3f delay:0.0f options:(7 << 16) animations:^
{
resultController.view.frame = CGRectOffset(resultController.view.frame, 0, resultController.view.frame.size.height);
} completion:^(__unused BOOL finished)
{
[resultController dismiss];
[self dismiss];
}];
}
- (void)simpleTransitionOutWithVelocity:(CGFloat)velocity completion:(void (^)())completion
{
self.view.userInteractionEnabled = false;
const CGFloat minVelocity = 2000.0f;
if (ABS(velocity) < minVelocity)
velocity = (velocity < 0.0f ? -1.0f : 1.0f) * minVelocity;
CGFloat distance = (velocity < FLT_EPSILON ? -1.0f : 1.0f) * self.view.frame.size.height;
CGRect targetFrame = (CGRect){{_previewView.frame.origin.x, distance}, _previewView.frame.size};
[UIView animateWithDuration:ABS(distance / velocity) animations:^
{
_previewView.frame = targetFrame;
} completion:^(__unused BOOL finished)
{
if (completion)
completion();
}];
}
- (void)_updateDismissTransitionMovementWithDistance:(CGFloat)distance animated:(bool)animated
{
CGRect originalFrame = [TGCameraController _cameraPreviewFrameForScreenSize:TGScreenSize() mode:_camera.cameraMode];
CGRect frame = (CGRect){ { originalFrame.origin.x, originalFrame.origin.y + distance }, originalFrame.size };
if (animated)
{
[UIView animateWithDuration:0.3 animations:^
{
_previewView.frame = frame;
}];
}
else
{
_previewView.frame = frame;
}
}
- (void)_updateDismissTransitionWithProgress:(CGFloat)progress animated:(bool)animated
{
CGFloat alpha = 1.0f - MAX(0.0f, MIN(1.0f, progress * 4.0f));
CGFloat transitionProgress = MAX(0.0f, MIN(1.0f, progress * 2.0f));
if (transitionProgress > FLT_EPSILON)
{
[self setInterfaceHidden:true animated:true];
_focusControl.active = false;
}
else if (animated)
{
[self setInterfaceHidden:false animated:true];
_focusControl.active = true;
}
if (animated)
{
[UIView animateWithDuration:0.3 animations:^
{
_backgroundView.alpha = alpha;
}];
}
else
{
_backgroundView.alpha = alpha;
}
}
- (void)resizePreviewViewForCameraMode:(PGCameraMode)mode
{
CGRect frame = [TGCameraController _cameraPreviewFrameForScreenSize:TGScreenSize() mode:mode];
_interfaceView.previewViewFrame = frame;
[_interfaceView layoutPreviewRelativeViews];
[_interfaceView updateForCameraModeChangeAfterResize];
[UIView animateWithDuration:0.3f delay:0.0f options:UIViewAnimationOptionCurveEaseInOut | UIViewAnimationOptionLayoutSubviews animations:^
{
_previewView.frame = frame;
_overlayView.frame = frame;
} completion:nil];
}
- (void)handleDeviceOrientationChangedTo:(UIDeviceOrientation)deviceOrientation
{
if (_camera.isRecordingVideo)
return;
UIInterfaceOrientation orientation = [TGCameraController _interfaceOrientationForDeviceOrientation:deviceOrientation];
if ([_interfaceView isKindOfClass:[TGCameraMainPhoneView class]])
{
[_interfaceView setInterfaceOrientation:orientation animated:true];
}
else
{
if (orientation == UIInterfaceOrientationUnknown)
return;
switch (deviceOrientation)
{
case UIDeviceOrientationPortrait:
{
_photoSwipeGestureRecognizer.direction = UISwipeGestureRecognizerDirectionUp;
_videoSwipeGestureRecognizer.direction = UISwipeGestureRecognizerDirectionDown;
}
break;
case UIDeviceOrientationPortraitUpsideDown:
{
_photoSwipeGestureRecognizer.direction = UISwipeGestureRecognizerDirectionDown;
_videoSwipeGestureRecognizer.direction = UISwipeGestureRecognizerDirectionUp;
}
break;
case UIDeviceOrientationLandscapeLeft:
{
_photoSwipeGestureRecognizer.direction = UISwipeGestureRecognizerDirectionRight;
_videoSwipeGestureRecognizer.direction = UISwipeGestureRecognizerDirectionLeft;
}
break;
case UIDeviceOrientationLandscapeRight:
{
_photoSwipeGestureRecognizer.direction = UISwipeGestureRecognizerDirectionLeft;
_videoSwipeGestureRecognizer.direction = UISwipeGestureRecognizerDirectionRight;
}
break;
default:
break;
}
[_interfaceView setInterfaceOrientation:orientation animated:false];
CGSize referenceSize = [self referenceViewSizeForOrientation:orientation];
if (referenceSize.width > referenceSize.height)
referenceSize = CGSizeMake(referenceSize.height, referenceSize.width);
self.view.userInteractionEnabled = false;
[UIView animateWithDuration:0.5f delay:0.0f options:UIViewAnimationOptionBeginFromCurrentState | UIViewAnimationOptionLayoutSubviews animations:^
{
_interfaceView.transform = CGAffineTransformMakeRotation(TGRotationForInterfaceOrientation(orientation));
_interfaceView.frame = CGRectMake(0, 0, referenceSize.width, referenceSize.height);
[_interfaceView setNeedsLayout];
} completion:^(BOOL finished)
{
if (finished)
self.view.userInteractionEnabled = true;
}];
}
[_focusControl setInterfaceOrientation:orientation animated:true];
}
#pragma mark - Gesture Recognizers
- (CGFloat)dismissProgressForSwipeDistance:(CGFloat)distance
{
return MAX(0.0f, MIN(1.0f, ABS(distance / 150.0f)));
}
- (void)handleSwipe:(UISwipeGestureRecognizer *)gestureRecognizer
{
PGCameraMode newMode = PGCameraModeUndefined;
if (gestureRecognizer == _photoSwipeGestureRecognizer)
{
if (_camera.cameraMode == PGCameraModePhoto)
newMode = PGCameraModeSquare;
else if (_camera.cameraMode != PGCameraModeSquare)
newMode = PGCameraModePhoto;
}
else if (gestureRecognizer == _videoSwipeGestureRecognizer)
{
if (_camera.cameraMode == PGCameraModeSquare)
newMode = PGCameraModePhoto;
else
newMode = PGCameraModeVideo;
}
// if (gestureRecognizer == _photoSwipeGestureRecognizer)
// {
// if (_camera.cameraMode == PGCameraModePhoto)
// newMode = PGCameraModeClip;
// else if (_camera.cameraMode == PGCameraModeVideo)
// newMode = PGCameraModePhoto;
// }
// else if (gestureRecognizer == _videoSwipeGestureRecognizer)
// {
// if (_camera.cameraMode == PGCameraModeClip)
// newMode = PGCameraModePhoto;
// else if (_camera.cameraMode == PGCameraModePhoto)
// newMode = PGCameraModeVideo;
// }
if (newMode != PGCameraModeUndefined && _camera.cameraMode != newMode)
{
[_camera setCameraMode:newMode];
[_interfaceView setCameraMode:newMode];
}
}
- (void)handlePan:(TGModernGalleryZoomableScrollViewSwipeGestureRecognizer *)gestureRecognizer
{
switch (gestureRecognizer.state)
{
case UIGestureRecognizerStateChanged:
{
_dismissProgress = [self dismissProgressForSwipeDistance:[gestureRecognizer swipeDistance]];
[self _updateDismissTransitionWithProgress:_dismissProgress animated:false];
[self _updateDismissTransitionMovementWithDistance:[gestureRecognizer swipeDistance] animated:false];
}
break;
case UIGestureRecognizerStateEnded:
{
CGFloat swipeVelocity = [gestureRecognizer swipeVelocity];
if (ABS(swipeVelocity) < TGCameraSwipeMinimumVelocity)
swipeVelocity = (swipeVelocity < 0.0f ? -1.0f : 1.0f) * TGCameraSwipeMinimumVelocity;
__weak TGCameraController *weakSelf = self;
bool(^transitionOut)(CGFloat) = ^bool(CGFloat swipeVelocity)
{
__strong TGCameraController *strongSelf = weakSelf;
if (strongSelf == nil)
return false;
[strongSelf beginTransitionOutWithVelocity:swipeVelocity];
return true;
};
if ((ABS(swipeVelocity) < TGCameraSwipeVelocityThreshold && ABS([gestureRecognizer swipeDistance]) < TGCameraSwipeDistanceThreshold) || !transitionOut(swipeVelocity))
{
_dismissProgress = 0.0f;
[self _updateDismissTransitionWithProgress:0.0f animated:true];
[self _updateDismissTransitionMovementWithDistance:0.0f animated:true];
}
}
break;
case UIGestureRecognizerStateCancelled:
{
_dismissProgress = 0.0f;
[self _updateDismissTransitionWithProgress:0.0f animated:true];
[self _updateDismissTransitionMovementWithDistance:0.0f animated:true];
}
break;
default:
break;
}
}
- (void)handlePinch:(UIPinchGestureRecognizer *)gestureRecognizer
{
switch (gestureRecognizer.state)
{
case UIGestureRecognizerStateChanged:
{
CGFloat delta = (gestureRecognizer.scale - 1.0f) / 1.5f;
CGFloat value = MAX(0.0f, MIN(1.0f, _camera.zoomLevel + delta));
[_camera setZoomLevel:value];
[_interfaceView setZoomLevel:value displayNeeded:true];
gestureRecognizer.scale = 1.0f;
}
break;
case UIGestureRecognizerStateEnded:
case UIGestureRecognizerStateCancelled:
{
[_interfaceView zoomChangingEnded];
}
break;
default:
break;
}
}
- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer
{
if (gestureRecognizer == _panGestureRecognizer)
return !_camera.isRecordingVideo;
else if (gestureRecognizer == _photoSwipeGestureRecognizer || gestureRecognizer == _videoSwipeGestureRecognizer)
return _intent != TGCameraControllerAvatarIntent && !_camera.isRecordingVideo;
else if (gestureRecognizer == _pinchGestureRecognizer)
return _camera.isZoomAvailable;
return true;
}
+ (CGRect)_cameraPreviewFrameForScreenSize:(CGSize)screenSize mode:(PGCameraMode)mode
{
CGFloat widescreenWidth = MAX(screenSize.width, screenSize.height);
if ([UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPhone)
{
switch (mode)
{
case PGCameraModeVideo:
{
if (widescreenWidth == 812.0f)
return CGRectMake(0, 77, screenSize.width, screenSize.height - 77 - 68);
else
return CGRectMake(0, 0, screenSize.width, screenSize.height);
}
break;
case PGCameraModeSquare:
case PGCameraModeClip:
{
CGRect rect = [self _cameraPreviewFrameForScreenSize:screenSize mode:PGCameraModePhoto];
CGFloat topOffset = CGRectGetMidY(rect) - rect.size.width / 2;
if (widescreenWidth - 480.0f < FLT_EPSILON)
topOffset = 40.0f;
return CGRectMake(0, floor(topOffset), rect.size.width, rect.size.width);
}
break;
default:
{
if (widescreenWidth == 812.0f)
return CGRectMake(0, 121, screenSize.width, screenSize.height - 121 - 191);
if (widescreenWidth >= 736.0f - FLT_EPSILON)
return CGRectMake(0, 44, screenSize.width, screenSize.height - 50 - 136);
else if (widescreenWidth >= 667.0f - FLT_EPSILON)
return CGRectMake(0, 44, screenSize.width, screenSize.height - 44 - 123);
else if (widescreenWidth >= 568.0f - FLT_EPSILON)
return CGRectMake(0, 40, screenSize.width, screenSize.height - 40 - 101);
else
return CGRectMake(0, 0, screenSize.width, screenSize.height);
}
break;
}
}
else
{
if (mode == PGCameraModeSquare)
return CGRectMake(0, (screenSize.height - screenSize.width) / 2, screenSize.width, screenSize.width);
return CGRectMake(0, 0, screenSize.width, screenSize.height);
}
}
+ (UIInterfaceOrientation)_interfaceOrientationForDeviceOrientation:(UIDeviceOrientation)orientation
{
switch (orientation)
{
case UIDeviceOrientationPortrait:
return UIInterfaceOrientationPortrait;
case UIDeviceOrientationPortraitUpsideDown:
return UIInterfaceOrientationPortraitUpsideDown;
case UIDeviceOrientationLandscapeLeft:
return UIInterfaceOrientationLandscapeRight;
case UIDeviceOrientationLandscapeRight:
return UIInterfaceOrientationLandscapeLeft;
default:
return UIInterfaceOrientationUnknown;
}
}
+ (bool)useLegacyCamera
{
return iosMajorVersion() < 7 || [UIDevice currentDevice].platformType == UIDevice4iPhone || [UIDevice currentDevice].platformType == UIDevice4GiPod;
}
@end