Ilya Laktyushin ab5d37068d Various Fixes
2021-07-28 01:32:37 +03:00

3148 lines
128 KiB
Objective-C

#import "TGCameraController.h"
#import "LegacyComponentsInternal.h"
#import <objc/runtime.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/TGCameraPreviewView.h>
#import <LegacyComponents/TGCameraMainPhoneView.h>
#import <LegacyComponents/TGCameraMainTabletView.h>
#import "TGCameraFocusCrosshairsControl.h"
#import "TGCameraRectangleView.h"
#import <LegacyComponents/TGFullscreenContainerView.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 "TGMediaVideoConverter.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>
#import <LegacyComponents/TGMenuSheetController.h>
#import <LegacyComponents/TGMediaPickerSendActionSheetController.h>
#import "TGMediaPickerGallerySelectedItemsModel.h"
#import "TGCameraCapturedPhoto.h"
#import "TGCameraCapturedVideo.h"
#import "PGPhotoEditor.h"
#import "PGRectangleDetector.h"
#import "TGWarpedView.h"
#import "TGAnimationUtils.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;
UIView *_autorotationCorrectionView;
UIView *_backgroundView;
TGCameraPreviewView *_previewView;
TGCameraMainView *_interfaceView;
TGCameraCornersView *_cornersView;
UIView *_overlayView;
TGCameraFocusCrosshairsControl *_focusControl;
TGCameraRectangleView *_rectangleView;
UISwipeGestureRecognizer *_photoSwipeGestureRecognizer;
UISwipeGestureRecognizer *_videoSwipeGestureRecognizer;
TGModernGalleryZoomableScrollViewSwipeGestureRecognizer *_panGestureRecognizer;
UIPinchGestureRecognizer *_pinchGestureRecognizer;
CGFloat _dismissProgress;
bool _dismissing;
bool _finishedWithResult;
TGMediaPickerGallerySelectedItemsModel *_selectedItemsModel;
NSMutableArray<id<TGMediaEditableItem, TGMediaSelectableItem>> *_items;
TGMediaEditingContext *_editingContext;
TGMediaSelectionContext *_selectionContext;
NSTimer *_switchToVideoTimer;
NSTimer *_startRecordingTimer;
bool _stopRecordingOnRelease;
bool _shownMicrophoneAlert;
id<LegacyComponentsContext> _context;
bool _saveEditedPhotos;
bool _saveCapturedMedia;
bool _shutterIsBusy;
bool _crossfadingForZoom;
UIImpactFeedbackGenerator *_feedbackGenerator;
NSMutableSet *_previousQRCodes;
}
@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;
_items = [[NSMutableArray alloc] init];
if (_intent != TGCameraControllerGenericIntent)
_allowCaptions = false;
_saveEditedPhotos = saveEditedPhotos;
_saveCapturedMedia = saveCapturedMedia;
_previousQRCodes = [[NSMutableSet alloc] init];
if (iosMajorVersion() >= 10) {
_feedbackGenerator = [[UIImpactFeedbackGenerator alloc] initWithStyle:UIImpactFeedbackStyleLight];
}
}
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];
_rectangleView = [[TGCameraRectangleView alloc] initWithFrame:_overlayView.bounds];
_rectangleView.previewView = _previewView;
_rectangleView.hidden = true;
[_overlayView addSubview:_rectangleView];
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 avatar:_intent == TGCameraControllerAvatarIntent hasUltrawideCamera:_camera.hasUltrawideCamera hasTelephotoCamera:_camera.hasTelephotoCamera];
[_interfaceView setInterfaceOrientation:interfaceOrientation animated:false];
}
else
{
_interfaceView = [[TGCameraMainTabletView alloc] initWithFrame:screenBounds avatar:_intent == TGCameraControllerAvatarIntent hasUltrawideCamera:_camera.hasUltrawideCamera hasTelephotoCamera:_camera.hasTelephotoCamera];
[_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);
}
_cornersView = [[TGCameraCornersView alloc] init];
if (_intent == TGCameraControllerPassportIdIntent)
[_interfaceView setDocumentFrameHidden:false];
_selectedItemsModel = [[TGMediaPickerGallerySelectedItemsModel alloc] initWithSelectionContext:nil items:[_items copy]];
[_interfaceView setSelectedItemsModel:_selectedItemsModel];
_selectedItemsModel.selectionUpdated = ^(bool reload, bool incremental, bool add, NSInteger index)
{
__strong TGCameraController *strongSelf = weakSelf;
if (strongSelf == nil)
return;
[strongSelf->_interfaceView updateSelectedPhotosView:reload incremental:incremental add:add index:index];
NSInteger count = strongSelf->_items.count;
[strongSelf->_interfaceView updateSelectionInterface:count counterVisible:count > 0 animated:true];
};
_interfaceView.thumbnailSignalForItem = ^SSignal *(id item)
{
__strong TGCameraController *strongSelf = weakSelf;
if (strongSelf != nil)
return [strongSelf _signalForItem:item];
return nil;
};
_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)
{
return true;
};
_interfaceView.cameraModeChanged = ^(PGCameraMode mode)
{
__strong TGCameraController *strongSelf = weakSelf;
if (strongSelf == nil)
return;
[strongSelf _updateCameraMode:mode updateInterface:false];
};
_interfaceView.flashModeChanged = ^(PGCameraFlashMode mode)
{
__strong TGCameraController *strongSelf = weakSelf;
if (strongSelf == nil)
return;
[strongSelf->_camera setFlashMode:mode];
};
_interfaceView.zoomChanged = ^(CGFloat level, bool animated)
{
__strong TGCameraController *strongSelf = weakSelf;
if (strongSelf == nil)
return;
[strongSelf->_camera setZoomLevel:level animated:animated];
};
_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.shutterPanGesture = ^(UIPanGestureRecognizer *gesture) {
__strong TGCameraController *strongSelf = weakSelf;
if (strongSelf == nil)
return;
[strongSelf handleRamp:gesture];
};
_interfaceView.cancelPressed = ^
{
__strong TGCameraController *strongSelf = weakSelf;
if (strongSelf != nil)
[strongSelf cancelPressed];
};
_interfaceView.resultPressed = ^(NSInteger index)
{
__strong TGCameraController *strongSelf = weakSelf;
if (strongSelf != nil)
[strongSelf presentResultControllerForItem:index == -1 ? nil : strongSelf->_items[index] completion:nil];
};
_interfaceView.itemRemoved = ^(NSInteger index)
{
__strong TGCameraController *strongSelf = weakSelf;
if (strongSelf != nil)
{
id item = [strongSelf->_items objectAtIndex:index];
[strongSelf->_selectionContext setItem:item selected:false];
[strongSelf->_items removeObjectAtIndex:index];
[strongSelf->_selectedItemsModel removeSelectedItem:item];
[strongSelf->_interfaceView setResults:[strongSelf->_items copy]];
}
};
if (_intent != TGCameraControllerGenericIntent && _intent != TGCameraControllerAvatarIntent)
[_interfaceView setHasModeControl:false];
if (iosMajorVersion() >= 11)
{
_backgroundView.accessibilityIgnoresInvertColors = true;
_interfaceView.accessibilityIgnoresInvertColors = true;
_focusControl.accessibilityIgnoresInvertColors = true;
}
[_autorotationCorrectionView addSubview:_interfaceView];
if ((int)self.view.frame.size.width > 320 || [[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPhone) {
[_autorotationCorrectionView addSubview:_cornersView];
}
_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)_updateCameraMode:(PGCameraMode)mode updateInterface:(bool)updateInterface {
[_camera setCameraMode:mode];
if (updateInterface)
[_interfaceView setCameraMode:mode];
_focusControl.hidden = mode == PGCameraModePhotoScan;
_rectangleView.hidden = mode != PGCameraModePhotoScan;
if (mode == PGCameraModePhotoScan) {
[self _createContextsIfNeeded];
if (_items.count == 0) {
[_interfaceView setToastMessage:@"Position the document in view" animated:true];
} else {
}
}
}
- (void)_configureCamera
{
__weak TGCameraController *weakSelf = self;
_camera.requestedCurrentInterfaceOrientation = ^UIInterfaceOrientation(bool *mirrored)
{
__strong TGCameraController *strongSelf = weakSelf;
if (strongSelf == nil)
return UIInterfaceOrientationUnknown;
if (strongSelf->_intent == TGCameraControllerPassportIdIntent)
return UIInterfaceOrientationPortrait;
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 = [PGCamera isPhotoCameraMode:mode] == [PGCamera isPhotoCameraMode:currentMode];
if (strongSelf->_camera.captureSession.currentCameraPosition == PGCameraPositionFront && mode == PGCameraModePhotoScan) {
generalModeNotChanged = false;
}
if ([PGCamera isVideoCameraMode:mode] && !generalModeNotChanged)
{
[[LegacyComponentsGlobals provider] pauseMusicPlayback];
}
if (generalModeNotChanged)
{
if (commitBlock != nil)
commitBlock();
}
else
{
[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->_interfaceView setZoomLevel:1.0f displayNeeded:false];
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;
}
if (strongSelf->_camera.cameraMode == PGCameraModePhotoScan) {
strongSelf->_camera.captureSession.rectangleDetector.update = ^(bool capture, PGRectangle *rectangle) {
__strong TGCameraController *strongSelf = weakSelf;
if (strongSelf == nil)
return;
TGDispatchOnMainThread(^{
[strongSelf->_rectangleView drawRectangle:rectangle];
if (capture) {
[strongSelf _makeScan:rectangle];
}
});
};
}
}
[strongSelf->_previewView endTransitionAnimated: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];
if (!targetPositionHasZoom) {
[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;
}];
});
}];
if (iosMajorVersion() >= 13.0) {
[strongSelf->_feedbackGenerator impactOccurredWithIntensity:0.5];
} else {
[strongSelf->_feedbackGenerator impactOccurred];
}
};
_camera.finishedPositionChange = ^(bool targetPositionHasZoom)
{
__strong TGCameraController *strongSelf = weakSelf;
if (strongSelf == nil)
return;
TGDispatchOnMainThread(^
{
[strongSelf->_previewView endTransitionAnimated:true];
[strongSelf->_interfaceView setZoomLevel:1.0f displayNeeded:false];
if (targetPositionHasZoom) {
[strongSelf->_interfaceView setHasZoom:targetPositionHasZoom];
}
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(^
{
[strongSelf->_interfaceView setFlashActive:active];
});
};
_camera.flashAvailabilityChanged = ^(bool available)
{
__strong TGCameraController *strongSelf = weakSelf;
if (strongSelf == nil)
return;
[strongSelf->_interfaceView setFlashUnavailable:!available];
};
_camera.beganVideoRecording = ^(__unused bool moment)
{
__strong TGCameraController *strongSelf = weakSelf;
if (strongSelf == nil)
return;
strongSelf->_focusControl.ignoreAutofocusing = true;
[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 = ^(__unused bool moment)
{
__strong TGCameraController *strongSelf = weakSelf;
if (strongSelf == nil)
return;
strongSelf->_focusControl.ignoreAutofocusing = false;
[strongSelf->_interfaceView setFlashMode:PGCameraFlashModeOff];
};
_camera.deviceAngleSampler.deviceOrientationChanged = ^(UIDeviceOrientation orientation)
{
__strong TGCameraController *strongSelf = weakSelf;
if (strongSelf == nil)
return;
[strongSelf handleDeviceOrientationChangedTo:orientation];
};
_camera.captureSession.recognizedQRCode = ^(NSString *value, AVMetadataMachineReadableCodeObject *object)
{
__strong TGCameraController *strongSelf = weakSelf;
if (strongSelf != nil && strongSelf.recognizedQRCode != nil)
{
if (![strongSelf->_previousQRCodes containsObject:value])
{
strongSelf.recognizedQRCode(value);
[strongSelf->_previousQRCodes addObject:value];
}
}
};
_camera.captureSession.crossfadeNeeded = ^{
__strong TGCameraController *strongSelf = weakSelf;
if (strongSelf != nil)
{
if (strongSelf->_crossfadingForZoom) {
return;
}
strongSelf->_crossfadingForZoom = true;
[strongSelf->_camera captureNextFrameCompletion:^(UIImage *image)
{
TGDispatchOnMainThread(^
{
[strongSelf->_previewView beginTransitionWithSnapshotImage:image animated:false];
TGDispatchAfter(0.15, dispatch_get_main_queue(), ^{
[strongSelf->_previewView endTransitionAnimated:true];
strongSelf->_crossfadingForZoom = false;
});
});
}];
};
};
}
#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"];
CABasicAnimation *cornersAnimation = [CABasicAnimation animationWithKeyPath:@"opacity"];
cornersAnimation.fromValue = @(_cornersView.alpha);
cornersAnimation.toValue = @(hidden ? 0.0f : 1.0f);
cornersAnimation.duration = 0.2f;
[_cornersView.layer addAnimation:cornersAnimation forKey:@"opacity"];
_interfaceView.alpha = hidden ? 0.0f : 1.0f;
_cornersView.alpha = hidden ? 0.0 : 1.0;
}
else
{
[_interfaceView.layer removeAllAnimations];
_interfaceView.alpha = hidden ? 0.0 : 1.0;
[_cornersView.layer removeAllAnimations];
_cornersView.alpha = hidden ? 0.0 : 1.0;
}
}
#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)
{
TGCameraCapturedVideo *capturedVideo = [[TGCameraCapturedVideo alloc] initWithURL:outputURL];
[strongSelf addResultItem:capturedVideo];
if (![strongSelf maybePresentResultControllerForItem:capturedVideo completion:nil])
{
strongSelf->_camera.disabled = false;
[strongSelf->_interfaceView setRecordingVideo:false animated:true];
}
}
else
{
[strongSelf->_interfaceView setRecordingVideo:false animated:false];
}
}];
};
_camera.autoStartVideoRecording = true;
[self _updateCameraMode:PGCameraModeVideo updateInterface:true];
}
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)
{
TGCameraCapturedVideo *capturedVideo = [[TGCameraCapturedVideo alloc] initWithURL:outputURL];
[strongSelf addResultItem:capturedVideo];
if (![strongSelf maybePresentResultControllerForItem:capturedVideo completion:nil])
{
strongSelf->_camera.disabled = false;
[strongSelf->_interfaceView setRecordingVideo:false animated:true];
}
}
else
{
[strongSelf->_interfaceView setRecordingVideo:false animated:false];
}
}];
_stopRecordingOnRelease = true;
}
}
- (void)shutterPressed
{
if (iosMajorVersion() >= 13.0) {
[_feedbackGenerator impactOccurredWithIntensity:0.5];
} else {
[_feedbackGenerator impactOccurred];
}
PGCameraMode cameraMode = _camera.cameraMode;
switch (cameraMode)
{
case PGCameraModePhoto:
{
if (_intent == TGCameraControllerGenericIntent || _intent == TGCameraControllerAvatarIntent)
{
_switchToVideoTimer = [TGTimerTarget scheduledMainThreadTimerWithTarget:self action:@selector(startVideoRecording) interval:0.25 repeat:false];
}
}
break;
case PGCameraModeVideo:
case PGCameraModeSquareVideo:
case PGCameraModeSquareSwing:
{
if (!_camera.isRecordingVideo)
{
_startRecordingTimer = [TGTimerTarget scheduledMainThreadTimerWithTarget:self action:@selector(startVideoRecording) interval:0.25 repeat:false];
}
else
{
_stopRecordingOnRelease = true;
}
}
break;
default:
break;
}
}
- (void)shutterReleased
{
if (iosMajorVersion() >= 13.0) {
[_feedbackGenerator impactOccurredWithIntensity:0.6];
} else {
[_feedbackGenerator impactOccurred];
}
[_switchToVideoTimer invalidate];
_switchToVideoTimer = nil;
[_startRecordingTimer invalidate];
_startRecordingTimer = nil;
if (_shutterIsBusy)
return;
__weak TGCameraController *weakSelf = self;
PGCameraMode cameraMode = _camera.cameraMode;
if (cameraMode == PGCameraModePhoto || cameraMode == PGCameraModeSquarePhoto || cameraMode == PGCameraModePhotoScan)
{
_camera.disabled = true;
_shutterIsBusy = true;
TGDispatchAfter(0.05, dispatch_get_main_queue(), ^
{
[_previewView blink];
});
if (![self willPresentResultController])
{
}
else
{
_buttonHandler.enabled = false;
[_buttonHandler ignoreEventsFor:1.5f andDisable:true];
}
[_camera takePhotoWithCompletion:^(UIImage *result, PGCameraShotMetadata *metadata)
{
__strong TGCameraController *strongSelf = weakSelf;
if (strongSelf == nil)
return;
TGDispatchOnMainThread(^
{
strongSelf->_shutterIsBusy = false;
if (strongSelf->_intent == TGCameraControllerAvatarIntent || strongSelf->_intent == TGCameraControllerSignupAvatarIntent)
{
[strongSelf presentPhotoResultControllerWithImage:result metadata:metadata completion:^{}];
}
else
{
TGCameraCapturedPhoto *capturedPhoto = [[TGCameraCapturedPhoto alloc] initWithImage:result metadata:metadata];
[strongSelf addResultItem:capturedPhoto];
if (![strongSelf maybePresentResultControllerForItem:capturedPhoto completion:nil])
strongSelf->_camera.disabled = false;
}
});
}];
}
else if (cameraMode == PGCameraModeVideo || cameraMode == PGCameraModeSquareVideo || cameraMode == PGCameraModeSquareSwing)
{
if (!_camera.isRecordingVideo)
{
[_buttonHandler ignoreEventsFor:1.0f andDisable:false];
[_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)
{
TGCameraCapturedVideo *capturedVideo = [[TGCameraCapturedVideo alloc] initWithURL:outputURL];
if (strongSelf->_intent == TGCameraControllerAvatarIntent || strongSelf->_intent == TGCameraControllerSignupAvatarIntent)
{
[strongSelf presentPhotoResultControllerWithImage:capturedVideo metadata:nil completion:^{
[strongSelf->_interfaceView setRecordingVideo:false animated:true];
}];
} else {
[strongSelf addResultItem:capturedVideo];
if (![strongSelf maybePresentResultControllerForItem:capturedVideo completion:nil])
{
strongSelf->_camera.disabled = false;
[strongSelf->_interfaceView setRecordingVideo:false animated:true];
}
}
}
else
{
[strongSelf->_interfaceView setRecordingVideo:false animated:false];
}
}];
}
else if (_stopRecordingOnRelease)
{
_stopRecordingOnRelease = false;
[_camera stopVideoRecording];
TGDispatchAfter(0.3, dispatch_get_main_queue(), ^{
[_camera setZoomLevel:1.0];
[_interfaceView setZoomLevel:1.0 displayNeeded:false];
_camera.disabled = true;
});
[_buttonHandler ignoreEventsFor:1.0f andDisable:[self willPresentResultController]];
}
}
}
- (void)_makeScan:(PGRectangle *)rectangle
{
if (_shutterIsBusy)
return;
_camera.disabled = true;
_shutterIsBusy = true;
__weak TGCameraController *weakSelf = self;
[_camera takePhotoWithCompletion:^(UIImage *result, PGCameraShotMetadata *metadata)
{
__strong TGCameraController *strongSelf = weakSelf;
if (strongSelf == nil)
return;
TGDispatchOnMainThread(^
{
[strongSelf->_interfaceView setToastMessage:nil animated:true];
strongSelf->_shutterIsBusy = false;
[strongSelf->_rectangleView drawRectangle:nil];
strongSelf->_rectangleView.enabled = false;
TGDispatchAfter(2.0, dispatch_get_main_queue(), ^{
strongSelf->_rectangleView.enabled = true;
});
TGCameraCapturedPhoto *capturedPhoto = [[TGCameraCapturedPhoto alloc] initWithImage:result rectangle:rectangle];
[strongSelf addResultItem:capturedPhoto];
PGRectangle *cropRectangle = [[rectangle rotate90] transform:CGAffineTransformMakeScale(result.size.width, result.size.height)];
PGRectangle *convertedRectangle = [cropRectangle sort];
convertedRectangle = [convertedRectangle cartesian:result.size.height];
CIImage *ciImage = [[CIImage alloc] initWithImage:result];
CIImage *croppedImage = [ciImage imageByApplyingFilter:@"CIPerspectiveCorrection" withInputParameters:@{
@"inputTopLeft": [CIVector vectorWithCGPoint:convertedRectangle.topLeft],
@"inputTopRight": [CIVector vectorWithCGPoint:convertedRectangle.topRight],
@"inputBottomLeft": [CIVector vectorWithCGPoint:convertedRectangle.bottomLeft],
@"inputBottomRight": [CIVector vectorWithCGPoint:convertedRectangle.bottomRight]
}];
CIImage *enhancedImage = [croppedImage imageByApplyingFilter:@"CIDocumentEnhancer" withInputParameters:@{}];
CIContext *context = [CIContext contextWithOptions:nil];
UIImage *editedImage = [UIImage imageWithCGImage:[context createCGImage:enhancedImage fromRect:enhancedImage.extent]];
UIImage *thumbnailImage = TGScaleImage(editedImage, TGScaleToFillSize(editedImage.size, TGPhotoThumbnailSizeForCurrentScreen()));
[strongSelf->_editingContext setImage:editedImage thumbnailImage:thumbnailImage forItem:capturedPhoto synchronous:true];
[strongSelf->_editingContext setAdjustments:[PGPhotoEditorValues editorValuesWithOriginalSize:result.size cropRectangle:cropRectangle cropOrientation:UIImageOrientationUp cropSize:editedImage.size enhanceDocument:true paintingData:nil] forItem:capturedPhoto];
[strongSelf _playScanAnimation:editedImage rectangle:rectangle completion:^{
[strongSelf->_selectedItemsModel addSelectedItem:capturedPhoto];
[strongSelf->_selectionContext setItem:capturedPhoto selected:true];
[strongSelf->_interfaceView setResults:[strongSelf->_items copy]];
TGDispatchAfter(0.5, dispatch_get_main_queue(), ^{
[strongSelf->_interfaceView setToastMessage:@"Ready for next scan" animated:true];
});
}];
strongSelf->_camera.disabled = false;
});
}];
}
- (void)_playScanAnimation:(UIImage *)image rectangle:(PGRectangle *)rectangle completion:(void(^)(void))completion
{
TGWarpedView *warpedView = [[TGWarpedView alloc] initWithImage:image];
warpedView.layer.anchorPoint = CGPointMake(0, 0);
warpedView.frame = _rectangleView.frame;
[_rectangleView.superview addSubview:warpedView];
CGAffineTransform transform = CGAffineTransformMakeScale(_previewView.frame.size.width, _previewView.frame.size.height);
PGRectangle *displayRectangle = [[[rectangle rotate90] transform:transform] sort];
[warpedView transformToFitQuadTopLeft:displayRectangle.topLeft topRight:displayRectangle.topRight bottomLeft:displayRectangle.bottomLeft bottomRight:displayRectangle.bottomRight];
CGFloat inset = 16.0f;
CGSize targetSize = TGScaleToFit(image.size, CGSizeMake(_previewView.frame.size.width - inset * 2.0, _previewView.frame.size.height - inset * 2.0));
CGRect targetRect = CGRectMake(floor((_previewView.frame.size.width - targetSize.width) / 2.0), floor((_previewView.frame.size.height - targetSize.height) / 2.0), targetSize.width, targetSize.height);
[UIView animateWithDuration:0.3 delay:0.0 options:(7 << 16) animations:^{
[warpedView transformToFitQuadTopLeft:CGPointMake(targetRect.origin.x, targetRect.origin.y) topRight:CGPointMake(targetRect.origin.x + targetRect.size.width, targetRect.origin.y) bottomLeft:CGPointMake(targetRect.origin.x, targetRect.origin.y + targetRect.size.height) bottomRight:CGPointMake(targetRect.origin.x + targetRect.size.width, targetRect.origin.y + targetRect.size.height)];
} completion:^(BOOL finished) {
UIImageView *outView = [[UIImageView alloc] initWithImage:image];
outView.frame = targetRect;
[warpedView.superview addSubview:outView];
[warpedView removeFromSuperview];
TGDispatchAfter(0.2, dispatch_get_main_queue(), ^{
CGPoint sourcePoint = outView.center;
CGPoint targetPoint = CGPointMake(_previewView.frame.size.width - 44.0, _previewView.frame.size.height - 44.0);
CGPoint midPoint = CGPointMake((sourcePoint.x + targetPoint.x) / 2.0, sourcePoint.y - 30.0);
CGFloat x1 = sourcePoint.x;
CGFloat y1 = sourcePoint.y;
CGFloat x2 = midPoint.x;
CGFloat y2 = midPoint.y;
CGFloat x3 = targetPoint.x;
CGFloat y3 = targetPoint.y;
CGFloat a = (x3 * (y2 - y1) + x2 * (y1 - y3) + x1 * (y3 - y2)) / ((x1 - x2) * (x1 - x3) * (x2 - x3));
CGFloat b = (x1 * x1 * (y2 - y3) + x3 * x3 * (y1 - y2) + x2 * x2 * (y3 - y1)) / ((x1 - x2) * (x1 - x3) * (x2 - x3));
CGFloat c = (x2 * x2 * (x3 * y1 - x1 * y3) + x2 * (x1 * x1 * y3 - x3 * x3 * y1) + x1 * x3 * (x3 - x1) * y2) / ((x1 - x2) * (x1 - x3) * (x2 - x3));
[UIView animateWithDuration:0.3 animations:^{
outView.transform = CGAffineTransformMakeScale(0.1, 0.1);
} completion:^(BOOL finished) {
[outView removeFromSuperview];
}];
TGDispatchAfter(0.28, dispatch_get_main_queue(), ^{
completion();
});
CAKeyframeAnimation *animation = [CAKeyframeAnimation animationWithKeyPath:@"position"];
NSMutableArray *values = [[NSMutableArray alloc] init];
NSMutableArray *keyTimes = [[NSMutableArray alloc] init];
for (NSInteger i = 0; i < 10; i++) {
CGFloat k = (CGFloat)i / (CGFloat)(10 - 1);
CGFloat x = sourcePoint.x * (1.0 - k) + targetPoint.x * k;
CGFloat y = a * x * x + b * x + c;
[values addObject:[NSValue valueWithCGPoint:CGPointMake(x, y)]];
[keyTimes addObject:@(k)];
}
animation.values = values;
animation.keyTimes = keyTimes;
animation.duration = 0.35;
animation.removedOnCompletion = false;
[outView.layer addAnimation:animation forKey:@"position"];
});
}];
}
- (void)cancelPressed
{
if (_items.count > 0)
{
__weak TGCameraController *weakSelf = self;
TGMenuSheetController *controller = [[TGMenuSheetController alloc] initWithContext:_context dark:false];
controller.dismissesByOutsideTap = true;
controller.narrowInLandscape = true;
__weak TGMenuSheetController *weakController = controller;
NSArray *items = @
[
[[TGMenuSheetButtonItemView alloc] initWithTitle:TGLocalized(@"Camera.Discard") type:TGMenuSheetButtonTypeDefault fontSize:20.0 action:^
{
__strong TGMenuSheetController *strongController = weakController;
if (strongController == nil)
return;
__strong TGCameraController *strongSelf = weakSelf;
if (strongSelf == nil)
return;
[strongController dismissAnimated:true manual:false completion:nil];
[strongSelf beginTransitionOutWithVelocity:0.0f];
}],
[[TGMenuSheetButtonItemView alloc] initWithTitle:TGLocalized(@"Common.Cancel") type:TGMenuSheetButtonTypeCancel fontSize:20.0 action:^
{
__strong TGMenuSheetController *strongController = weakController;
if (strongController != nil)
[strongController dismissAnimated:true];
}]
];
[controller setItemViews:items];
controller.sourceRect = ^
{
__strong TGCameraController *strongSelf = weakSelf;
if (strongSelf == nil)
return CGRectZero;
UIButton *cancelButton = strongSelf->_interfaceView->_cancelButton;
return [cancelButton convertRect:cancelButton.bounds toView:strongSelf.view];
};
controller.permittedArrowDirections = UIPopoverArrowDirectionAny;
[controller presentInViewController:self sourceView:self.view animated:true];
}
else
{
[self beginTransitionOutWithVelocity:0.0f];
}
}
#pragma mark - Result
- (void)addResultItem:(id<TGMediaEditableItem, TGMediaSelectableItem>)item
{
[_items addObject:item];
}
- (bool)willPresentResultController
{
return _items.count == 0 || (_items.count > 0 && (_items.count + 1) % 10 == 0);
}
- (bool)shouldPresentResultController
{
return _items.count == 1 || (_items.count > 0 && _items.count % 10 == 0);
}
- (bool)maybePresentResultControllerForItem:(id<TGMediaEditableItem, TGMediaSelectableItem>)editableItem completion:(void (^)(void))completion
{
if ([self shouldPresentResultController])
{
[self presentResultControllerForItem:editableItem completion:^
{
[_selectedItemsModel addSelectedItem:editableItem];
[_selectionContext setItem:editableItem selected:true];
[_interfaceView setResults:[_items copy]];
if (completion != nil)
completion();
}];
return true;
}
else
{
[_selectedItemsModel addSelectedItem:editableItem];
[_selectionContext setItem:editableItem selected:true];
[_interfaceView setResults:[_items copy]];
return false;
}
}
- (NSArray *)prepareGalleryItemsForResults:(void (^)(TGMediaPickerGalleryItem *))enumerationBlock
{
NSMutableArray *galleryItems = [[NSMutableArray alloc] init];
for (id<TGMediaEditableItem, TGMediaSelectableItem> item in _items)
{
TGMediaPickerGalleryItem<TGModernGallerySelectableItem, TGModernGalleryEditableItem> *galleryItem = nil;
if ([item isKindOfClass:[TGCameraCapturedPhoto class]])
{
galleryItem = [[TGMediaPickerGalleryPhotoItem alloc] initWithAsset:item];
}
else if ([item isKindOfClass:[TGCameraCapturedVideo class]])
{
galleryItem = [[TGMediaPickerGalleryVideoItem alloc] initWithAsset:item];
}
galleryItem.selectionContext = _selectionContext;
galleryItem.editingContext = _editingContext;
galleryItem.stickersContext = _stickersContext;
if (enumerationBlock != nil)
enumerationBlock(galleryItem);
if (galleryItem != nil)
[galleryItems addObject:galleryItem];
}
return galleryItems;
}
- (void)_createContextsIfNeeded
{
TGMediaEditingContext *editingContext = _editingContext;
if (editingContext == nil)
{
editingContext = [[TGMediaEditingContext alloc] init];
if (self.forcedCaption != nil)
[editingContext setForcedCaption:self.forcedCaption entities:self.forcedEntities];
_editingContext = editingContext;
_interfaceView.editingContext = editingContext;
}
TGMediaSelectionContext *selectionContext = _selectionContext;
if (selectionContext == nil)
{
selectionContext = [[TGMediaSelectionContext alloc] initWithGroupingAllowed:self.allowGrouping selectionLimit:100];
if (self.allowGrouping)
selectionContext.grouping = true;
_selectionContext = selectionContext;
}
}
- (void)presentResultControllerForItem:(id<TGMediaEditableItem, TGMediaSelectableItem>)editableItemValue completion:(void (^)(void))completion
{
__block id<TGMediaEditableItem, TGMediaSelectableItem> editableItem = editableItemValue;
UIViewController *(^begin)(id<LegacyComponentsContext>) = ^(id<LegacyComponentsContext> windowContext) {
[self _createContextsIfNeeded];
TGMediaEditingContext *editingContext = _editingContext;
TGMediaSelectionContext *selectionContext = _selectionContext;
if (editableItem == nil)
editableItem = _items.lastObject;
[[[LegacyComponentsGlobals provider] applicationInstance] setIdleTimerDisabled:false];
if (_intent == TGCameraControllerPassportIdIntent)
{
TGCameraCapturedPhoto *photo = (TGCameraCapturedPhoto *)editableItem;
CGSize size = photo.originalSize;
CGFloat height = size.width * 0.704f;
PGPhotoEditorValues *values = [PGPhotoEditorValues editorValuesWithOriginalSize:size cropRect:CGRectMake(0, floor((size.height - height) / 2.0f), size.width, height) cropRotation:0.0f cropOrientation:UIImageOrientationUp cropLockedAspectRatio:0.0f cropMirrored:false toolValues:nil paintingData:nil sendAsGif:false];
SSignal *cropSignal = [[photo originalImageSignal:0.0] map:^UIImage *(UIImage *image)
{
UIImage *croppedImage = TGPhotoEditorCrop(image, nil, UIImageOrientationUp, 0.0f, values.cropRect, false, TGPhotoEditorResultImageMaxSize, size, true);
return croppedImage;
}];
[cropSignal startWithNext:^(UIImage *image)
{
CGSize fillSize = TGPhotoThumbnailSizeForCurrentScreen();
fillSize.width = CGCeil(fillSize.width);
fillSize.height = CGCeil(fillSize.height);
CGSize size = TGScaleToFillSize(image.size, fillSize);
UIGraphicsBeginImageContextWithOptions(size, true, 0.0f);
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetInterpolationQuality(context, kCGInterpolationMedium);
[image drawInRect:CGRectMake(0, 0, size.width, size.height)];
UIImage *thumbnailImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
[editingContext setAdjustments:values forItem:photo];
[editingContext setImage:image thumbnailImage:thumbnailImage forItem:photo synchronous:true];
}];
}
__weak TGCameraController *weakSelf = self;
TGModernGalleryController *galleryController = [[TGModernGalleryController alloc] initWithContext:windowContext];
galleryController.adjustsStatusBarVisibility = false;
galleryController.hasFadeOutTransition = true;
__block id<TGModernGalleryItem> focusItem = nil;
NSArray *galleryItems = [self prepareGalleryItemsForResults:^(TGMediaPickerGalleryItem *item)
{
if (focusItem == nil && [item.asset isEqual:editableItem])
{
focusItem = item;
if ([item.asset isKindOfClass:[TGCameraCapturedVideo class]])
{
AVAssetImageGenerator *generator = [[AVAssetImageGenerator alloc] initWithAsset:((TGCameraCapturedVideo *)item.asset).immediateAVAsset];
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);
item.immediateThumbnailImage = thumbnailImage;
}
}
}];
bool hasCamera = !self.inhibitMultipleCapture && (((_intent == TGCameraControllerGenericIntent || _intent == TGCameraControllerGenericPhotoOnlyIntent) && !_shortcut) || (_intent == TGCameraControllerPassportMultipleIntent));
TGMediaPickerGalleryModel *model = [[TGMediaPickerGalleryModel alloc] initWithContext:windowContext items:galleryItems focusItem:focusItem selectionContext:_items.count > 1 ? selectionContext : nil editingContext:editingContext hasCaptions:self.allowCaptions allowCaptionEntities:self.allowCaptionEntities hasTimer:self.hasTimer onlyCrop:_intent == TGCameraControllerPassportIntent || _intent == TGCameraControllerPassportIdIntent || _intent == TGCameraControllerPassportMultipleIntent inhibitDocumentCaptions:self.inhibitDocumentCaptions hasSelectionPanel:true hasCamera:hasCamera recipientName:self.recipientName];
model.inhibitMute = self.inhibitMute;
model.controller = galleryController;
model.suggestionContext = self.suggestionContext;
model.stickersContext = self.stickersContext;
__weak TGModernGalleryController *weakGalleryController = galleryController;
__weak TGMediaPickerGalleryModel *weakModel = model;
model.interfaceView.doneLongPressed = ^(TGMediaPickerGalleryItem *item) {
__strong TGCameraController *strongSelf = weakSelf;
__strong TGMediaPickerGalleryModel *strongModel = weakModel;
if (strongSelf == nil || !(strongSelf.hasSilentPosting || strongSelf.hasSchedule) || strongSelf->_shortcut)
return;
if (iosMajorVersion() >= 10) {
UIImpactFeedbackGenerator *generator = [[UIImpactFeedbackGenerator alloc] initWithStyle:UIImpactFeedbackStyleMedium];
[generator impactOccurred];
}
bool effectiveHasSchedule = strongSelf.hasSchedule;
for (id item in strongModel.selectionContext.selectedItems)
{
if ([item isKindOfClass:[TGMediaAsset class]])
{
if ([[strongSelf->_editingContext timerForItem:item] integerValue] > 0)
{
effectiveHasSchedule = false;
break;
}
}
}
TGMediaPickerSendActionSheetController *controller = [[TGMediaPickerSendActionSheetController alloc] initWithContext:strongSelf->_context isDark:true sendButtonFrame:strongModel.interfaceView.doneButtonFrame canSendSilently:strongSelf->_hasSilentPosting canSchedule:effectiveHasSchedule reminder:strongSelf->_reminder hasTimer:strongSelf->_hasTimer];
controller.send = ^{
__strong TGCameraController *strongSelf = weakSelf;
__strong TGMediaPickerGalleryModel *strongModel = weakModel;
if (strongSelf == nil || strongModel == nil)
return;
__strong TGModernGalleryController *strongController = weakGalleryController;
if (strongController == nil)
return;
if ([item isKindOfClass:[TGMediaPickerGalleryVideoItem class]])
{
TGMediaPickerGalleryVideoItemView *itemView = (TGMediaPickerGalleryVideoItemView *)[strongController itemViewForItem:item];
[itemView stop];
[itemView setPlayButtonHidden:true animated:true];
}
if (strongSelf->_selectionContext.allowGrouping)
[[NSUserDefaults standardUserDefaults] setObject:@(!strongSelf->_selectionContext.grouping) forKey:@"TG_mediaGroupingDisabled_v0"];
if (strongSelf.finishedWithResults != nil)
strongSelf.finishedWithResults(strongController, strongSelf->_selectionContext, strongSelf->_editingContext, item.asset, false, 0);
[strongSelf _dismissTransitionForResultController:strongController];
};
controller.sendSilently = ^{
__strong TGCameraController *strongSelf = weakSelf;
__strong TGMediaPickerGalleryModel *strongModel = weakModel;
if (strongSelf == nil || strongModel == nil)
return;
__strong TGModernGalleryController *strongController = weakGalleryController;
if (strongController == nil)
return;
if ([item isKindOfClass:[TGMediaPickerGalleryVideoItem class]])
{
TGMediaPickerGalleryVideoItemView *itemView = (TGMediaPickerGalleryVideoItemView *)[strongController itemViewForItem:item];
[itemView stop];
[itemView setPlayButtonHidden:true animated:true];
}
if (strongSelf->_selectionContext.allowGrouping)
[[NSUserDefaults standardUserDefaults] setObject:@(!strongSelf->_selectionContext.grouping) forKey:@"TG_mediaGroupingDisabled_v0"];
if (strongSelf.finishedWithResults != nil)
strongSelf.finishedWithResults(strongController, strongSelf->_selectionContext, strongSelf->_editingContext, item.asset, true, 0);
[strongSelf _dismissTransitionForResultController:strongController];
};
controller.schedule = ^{
__strong TGCameraController *strongSelf = weakSelf;
if (strongSelf == nil)
return;
strongSelf.presentScheduleController(^(int32_t time) {
__strong TGCameraController *strongSelf = weakSelf;
__strong TGMediaPickerGalleryModel *strongModel = weakModel;
if (strongSelf == nil || strongModel == nil)
return;
__strong TGModernGalleryController *strongController = weakGalleryController;
if (strongController == nil)
return;
if ([item isKindOfClass:[TGMediaPickerGalleryVideoItem class]])
{
TGMediaPickerGalleryVideoItemView *itemView = (TGMediaPickerGalleryVideoItemView *)[strongController itemViewForItem:item];
[itemView stop];
[itemView setPlayButtonHidden:true animated:true];
}
if (strongSelf->_selectionContext.allowGrouping)
[[NSUserDefaults standardUserDefaults] setObject:@(!strongSelf->_selectionContext.grouping) forKey:@"TG_mediaGroupingDisabled_v0"];
if (strongSelf.finishedWithResults != nil)
strongSelf.finishedWithResults(strongController, strongSelf->_selectionContext, strongSelf->_editingContext, item.asset, false, time);
[strongSelf _dismissTransitionForResultController:strongController];
});
};
controller.sendWithTimer = ^{
__strong TGCameraController *strongSelf = weakSelf;
if (strongSelf == nil)
return;
strongSelf.presentTimerController(^(int32_t time) {
__strong TGCameraController *strongSelf = weakSelf;
__strong TGMediaPickerGalleryModel *strongModel = weakModel;
if (strongSelf == nil || strongModel == nil)
return;
__strong TGModernGalleryController *strongController = weakGalleryController;
if (strongController == nil)
return;
if ([item isKindOfClass:[TGMediaPickerGalleryVideoItem class]])
{
TGMediaPickerGalleryVideoItemView *itemView = (TGMediaPickerGalleryVideoItemView *)[strongController itemViewForItem:item];
[itemView stop];
[itemView setPlayButtonHidden:true animated:true];
}
TGMediaEditingContext *editingContext = strongSelf->_editingContext;
NSMutableArray *items = [strongSelf->_selectionContext.selectedItems mutableCopy];
[items addObject:item.asset];
for (id<TGMediaEditableItem> editableItem in items) {
[editingContext setTimer:@(time) forItem:editableItem];
}
if (strongSelf.finishedWithResults != nil)
strongSelf.finishedWithResults(strongController, strongSelf->_selectionContext, strongSelf->_editingContext, item.asset, false, 0);
[strongSelf _dismissTransitionForResultController:strongController];
});
};
id<LegacyComponentsOverlayWindowManager> windowManager = nil;
id<LegacyComponentsContext> windowContext = nil;
windowManager = [strongSelf->_context makeOverlayWindowManager];
windowContext = [windowManager context];
TGOverlayControllerWindow *controllerWindow = [[TGOverlayControllerWindow alloc] initWithManager:windowManager parentController:strongSelf contentController:(TGOverlayController *)controller];
controllerWindow.hidden = false;
};
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 = ^(id<TGMediaEditableItem> editableItem, NSString *caption, NSArray *entities)
{
__strong TGCameraController *strongSelf = weakSelf;
if (strongSelf != nil)
[strongSelf->_editingContext setCaption:caption entities:entities forItem:editableItem];
};
model.interfaceView.hasSwipeGesture = false;
galleryController.model = model;
if (_items.count > 1)
[model.interfaceView updateSelectionInterface:selectionContext.count counterVisible:(selectionContext.count > 0) animated:false];
else
[model.interfaceView updateSelectionInterface:1 counterVisible:false animated:false];
model.interfaceView.thumbnailSignalForItem = ^SSignal *(id item)
{
__strong TGCameraController *strongSelf = weakSelf;
if (strongSelf != nil)
return [strongSelf _signalForItem:item];
return nil;
};
model.interfaceView.donePressed = ^(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;
if ([item isKindOfClass:[TGMediaPickerGalleryVideoItem class]])
{
TGMediaPickerGalleryVideoItemView *itemView = (TGMediaPickerGalleryVideoItemView *)[strongController itemViewForItem:item];
[itemView stop];
[itemView setPlayButtonHidden:true animated:true];
}
if (strongSelf->_selectionContext.allowGrouping)
[[NSUserDefaults standardUserDefaults] setObject:@(!strongSelf->_selectionContext.grouping) forKey:@"TG_mediaGroupingDisabled_v0"];
if (strongSelf.finishedWithResults != nil)
strongSelf.finishedWithResults(strongController, strongSelf->_selectionContext, strongSelf->_editingContext, item.asset, false, 0);
if (strongSelf->_shortcut)
return;
[strongSelf _dismissTransitionForResultController:strongController];
};
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;
TGMediaPickerGalleryModel *strongModel = weakModel;
if (strongModel == nil)
return;
[strongModel.interfaceView setSelectedItemsModel:strongModel.selectedItemsModel];
[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)
{
TGMediaPickerGalleryModel *strongModel = weakModel;
if (strongModel == nil)
return nil;
[[[LegacyComponentsGlobals provider] applicationInstance] setIdleTimerDisabled:true];
if (strongSelf->_camera.cameraMode == PGCameraModeVideo)
[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];
[UIView animateWithDuration:0.3f delay:0.1f options:UIViewAnimationOptionCurveLinear animations:^
{
strongSelf->_interfaceView.alpha = 1.0f;
} completion:nil];
if (!strongModel.interfaceView.capturing)
{
[strongSelf->_items removeAllObjects];
[strongSelf->_interfaceView setResults:nil];
[strongSelf->_selectionContext clear];
[strongSelf->_selectedItemsModel clear];
[strongSelf->_interfaceView updateSelectionInterface:0 counterVisible:false animated:false];
}
return snapshotView;
}
return nil;
};
void (^dismissGalleryImpl)() = nil;
galleryController.completedTransitionOut = ^
{
[snapshotView removeFromSuperview];
TGModernGalleryController *strongGalleryController = weakGalleryController;
if (strongGalleryController == nil) {
return;
}
if (strongGalleryController.customDismissSelf) {
strongGalleryController.customDismissSelf();
}
};
TGOverlayController *contentController = galleryController;
if (_shortcut)
{
contentController = [[TGOverlayController alloc] initWithContext:_context];
TGNavigationController *navigationController = [TGNavigationController navigationControllerWithControllers:@[galleryController]];
galleryController.navigationBarShouldBeHidden = true;
[contentController addChildViewController:navigationController];
[contentController.view addSubview:navigationController.view];
}
if (_customPresentOverlayController) {
dismissGalleryImpl = ^{
TGModernGalleryController *strongGalleryController = weakGalleryController;
if (strongGalleryController == nil) {
return;
}
if (strongGalleryController.customDismissSelf) {
strongGalleryController.customDismissSelf();
}
};
} else {
dismissGalleryImpl = ^{
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];
}
};
}
galleryController.view.clipsToBounds = true;
return contentController;
};
if (_customPresentOverlayController) {
_customPresentOverlayController(^TGOverlayController * (id<LegacyComponentsContext> context) {
return (TGOverlayController *)begin(context);
});
} else {
id<LegacyComponentsOverlayWindowManager> windowManager = nil;
id<LegacyComponentsContext> windowContext = nil;
windowManager = [_context makeOverlayWindowManager];
windowContext = [windowManager context];
UIViewController *controller = begin(windowContext);
TGOverlayControllerWindow *controllerWindow = [[TGOverlayControllerWindow alloc] initWithManager:windowManager parentController:self contentController:(TGOverlayController *)controller];
controllerWindow.hidden = false;
controllerWindow.windowLevel = self.view.window.windowLevel + 0.0001f;
}
}
- (SSignal *)_signalForItem:(id<TGMediaEditableItem>)item
{
SSignal *assetSignal = [item thumbnailImageSignal];
if (_editingContext == nil)
return assetSignal;
return [[_editingContext thumbnailImageSignalForItem:item] mapToSignal:^SSignal *(id result)
{
if (result != nil)
return [SSignal single:result];
else
return assetSignal;
}];
}
#pragma mark - Legacy Photo Result
- (void)presentPhotoResultControllerWithImage:(id<TGMediaEditableItem>)input metadata:(PGCameraShotMetadata *)metadata completion:(void (^)(void))completion
{
[[[LegacyComponentsGlobals provider] applicationInstance] setIdleTimerDisabled:false];
if (input == nil || ([input isKindOfClass:[UIImage class]] && ((UIImage *)input).size.width < FLT_EPSILON))
{
[self beginTransitionOutWithVelocity:0.0f];
return;
}
UIImage *image = nil;
if ([input isKindOfClass:[UIImage class]]) {
image = (UIImage *)input;
} else if ([input isKindOfClass:[TGCameraCapturedVideo class]]) {
AVAsset *asset = ((TGCameraCapturedVideo *)input).immediateAVAsset;
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];
image = [[UIImage alloc] initWithCGImage:imageRef];
CGImageRelease(imageRef);
}
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;
TGPhotoEditorControllerIntent intent = TGPhotoEditorControllerAvatarIntent;
if (_intent == TGCameraControllerSignupAvatarIntent) {
intent = TGPhotoEditorControllerSignupAvatarIntent;
}
TGPhotoEditorController *controller = [[TGPhotoEditorController alloc] initWithContext:windowContext item:input intent:(TGPhotoEditorControllerFromCameraIntent | intent) adjustments:nil caption:nil screenImage:image availableTabs:[TGPhotoEditorController defaultTabsForAvatarIntent] selectedTab:TGPhotoEditorCropTab];
controller.stickersContext = _stickersContext;
__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 (editorValues.paintingData.hasAnimation) {
TGVideoEditAdjustments *adjustments = [TGVideoEditAdjustments editAdjustmentsWithPhotoEditorValues:(PGPhotoEditorValues *)editorValues preset:TGMediaVideoConversionPresetProfileVeryHigh];
NSString *filePath = [NSTemporaryDirectory() stringByAppendingPathComponent:[[NSString alloc] initWithFormat:@"gifvideo_%x.jpg", (int)arc4random()]];
NSData *data = UIImageJPEGRepresentation(resultImage, 0.8);
[data writeToFile:filePath atomically:true];
UIImage *previewImage = resultImage;
if ([adjustments cropAppliedForAvatar:false] || adjustments.hasPainting || adjustments.toolsApplied)
{
UIImage *paintingImage = adjustments.paintingData.stillImage;
if (paintingImage == nil) {
paintingImage = adjustments.paintingData.image;
}
UIImage *croppedPaintingImage = TGPhotoEditorPaintingCrop(paintingImage, adjustments.cropOrientation, adjustments.cropRotation, adjustments.cropRect, adjustments.cropMirrored, resultImage.size, adjustments.originalSize, true, true, false);
UIImage *thumbnailImage = TGPhotoEditorVideoExtCrop(resultImage, croppedPaintingImage, adjustments.cropOrientation, adjustments.cropRotation, adjustments.cropRect, adjustments.cropMirrored, TGScaleToFill(resultImage.size, CGSizeMake(800, 800)), adjustments.originalSize, true, true, true, true);
if (thumbnailImage != nil) {
previewImage = thumbnailImage;
}
}
if (strongSelf.finishedWithVideo != nil)
strongSelf.finishedWithVideo(nil, [NSURL fileURLWithPath:filePath], previewImage, 0, CGSizeZero, adjustments, nil, nil, nil, nil);
} else {
if (strongSelf.finishedWithPhoto != nil)
strongSelf.finishedWithPhoto(nil, resultImage, nil, nil, nil, nil);
}
if (strongSelf.shouldStoreCapturedAssets && [input isKindOfClass:[UIImage class]])
{
[strongSelf _savePhotoToCameraRollWithOriginalImage:image editedImage:[editorValues toolsApplied] ? resultImage : nil];
}
__strong TGPhotoEditorController *strongController = weakController;
if (strongController != nil)
{
[strongController updateStatusBarAppearanceForDismiss];
[strongSelf _dismissTransitionForResultController:(TGOverlayController *)strongController];
}
});
};
controller.didFinishEditingVideo = ^(AVAsset *asset, id<TGMediaEditAdjustments> adjustments, UIImage *resultImage, UIImage *thumbnailImage, bool hasChanges) {
if (!hasChanges)
return;
__strong TGCameraController *strongSelf = weakSelf;
if (strongSelf == nil)
return;
TGDispatchOnMainThread(^
{
if (strongSelf.finishedWithVideo != nil)
strongSelf.finishedWithVideo(nil, [(AVURLAsset *)asset URL], resultImage, 0, CGSizeZero, adjustments, nil, nil, nil, 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)
{
if (editableItem.isVideo) {
if ([editableItem isKindOfClass:[TGMediaAsset class]]) {
return [TGMediaAssetImageSignals avAssetForVideoAsset:(TGMediaAsset *)editableItem allowNetworkAccess:true];
} else if ([editableItem isKindOfClass:[TGCameraCapturedVideo class]]) {
return ((TGCameraCapturedVideo *)editableItem).avAsset;
} else {
return [editableItem originalImageSignal:position];
}
} else {
return [editableItem originalImageSignal:position];
}
};
overlayController = (TGOverlayController *)controller;
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;
_cornersView.alpha = 1.0;
} completion:nil];
_interfaceView.previewViewFrame = _previewView.frame;
return targetFrame;
}
#pragma mark - Transition
- (void)beginTransitionInFromRect:(CGRect)rect
{
[_autorotationCorrectionView insertSubview:_previewView aboveSubview:_backgroundView];
_previewView.frame = rect;
_backgroundView.alpha = 0.0f;
_interfaceView.alpha = 0.0f;
_cornersView.alpha = 0.0;
[UIView animateWithDuration:0.3f animations:^
{
_backgroundView.alpha = 1.0f;
_interfaceView.alpha = 1.0f;
_cornersView.alpha = 1.0;
}];
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"];
POPSpringAnimation *cornersFrameAnimation = [POPSpringAnimation animationWithPropertyNamed:kPOPViewFrame];
cornersFrameAnimation.fromValue = [NSValue valueWithCGRect:fromFrame];
cornersFrameAnimation.toValue = [NSValue valueWithCGRect:toFrame];
cornersFrameAnimation.springSpeed = 20;
cornersFrameAnimation.springBounciness = 1;
[_cornersView pop_addAnimation:cornersFrameAnimation forKey:@"frame"];
}
else
{
_previewView.frame = toFrame;
_cornersView.frame = toFrame;
}
_interfaceView.previewViewFrame = toFrame;
}
- (void)beginTransitionOutWithVelocity:(CGFloat)velocity
{
_dismissing = true;
self.view.userInteractionEnabled = false;
_focusControl.active = false;
_rectangleView.hidden = true;
[self setInterfaceHidden:true animated:true];
[UIView animateWithDuration:0.25f animations:^
{
_backgroundView.alpha = 0.0f;
_cornersView.alpha = 0.0;
}];
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"];
POPSpringAnimation *cornersAnimation = [POPSpringAnimation animationWithPropertyNamed:kPOPViewFrame];
cornersAnimation.fromValue = [NSValue valueWithCGRect:_cornersView.frame];
cornersAnimation.toValue = [NSValue valueWithCGRect:referenceFrame];
cornersAnimation.springSpeed = 20;
cornersAnimation.springBounciness = 1;
[_cornersView pop_addAnimation:cornersAnimation 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;
[resultController.view.layer animatePositionFrom:resultController.view.layer.position to:CGPointMake(resultController.view.layer.position.x, resultController.view.layer.position.y + resultController.view.bounds.size.height) duration:0.3 timingFunction:kCAMediaTimingFunctionSpring removeOnCompletion:false completion:^(__unused bool finished) {
if (resultController.customDismissSelf) {
resultController.customDismissSelf();
} else {
[resultController dismiss];
}
[self dismiss];
}];
return;
[UIView animateWithDuration:0.3 delay:0.0f options:(7 << 16) animations:^
{
resultController.view.frame = CGRectOffset(resultController.view.frame, 0, resultController.view.frame.size.height);
} completion:^(__unused BOOL finished)
{
if (resultController.customDismissSelf) {
resultController.customDismissSelf();
} else {
[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;
_cornersView.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;
_cornersView.frame = frame;
}];
}
else
{
_previewView.frame = frame;
_cornersView.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 updateForCameraModeChangeAfterResize];
[UIView animateWithDuration:0.3f delay:0.0f options:UIViewAnimationOptionCurveEaseInOut | UIViewAnimationOptionLayoutSubviews animations:^
{
_previewView.frame = frame;
_cornersView.frame = frame;
_overlayView.frame = frame;
} completion:nil];
}
- (void)handleDeviceOrientationChangedTo:(UIDeviceOrientation)deviceOrientation
{
if (_camera.isRecordingVideo || _intent == TGCameraControllerPassportIdIntent)
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)
{
newMode = PGCameraModePhoto;
}
else if (gestureRecognizer == _videoSwipeGestureRecognizer)
{
if (_intent == TGCameraControllerAvatarIntent) {
newMode = PGCameraModeSquareVideo;
} else {
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.25;
if (_camera.zoomLevel > 2.0) {
delta *= 2.0;
}
CGFloat value = MAX(_camera.minZoomLevel, MIN(_camera.maxZoomLevel, _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;
}
}
- (void)handleRamp:(UIPanGestureRecognizer *)gestureRecognizer
{
if (!_stopRecordingOnRelease) {
[gestureRecognizer setTranslation:CGPointZero inView:self.view];
return;
}
switch (gestureRecognizer.state)
{
case UIGestureRecognizerStateChanged:
{
CGPoint translation = [gestureRecognizer translationInView:self.view];
CGFloat value = 1.0;
if (translation.y < 0.0) {
value = MIN(8.0, value + ABS(translation.y) / 60.0);
}
[_camera setZoomLevel:value];
[_interfaceView setZoomLevel:value displayNeeded:true];
}
break;
case UIGestureRecognizerStateEnded:
{
[self shutterReleased];
}
break;
default:
break;
}
}
- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer
{
if (gestureRecognizer == _panGestureRecognizer)
return !_camera.isRecordingVideo && _items.count == 0;
else if (gestureRecognizer == _photoSwipeGestureRecognizer || gestureRecognizer == _videoSwipeGestureRecognizer)
return (_intent == TGCameraControllerGenericIntent || _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 == 926.0f)
return CGRectMake(0, 82, screenSize.width, screenSize.height - 82 - 83);
else if (widescreenWidth == 896.0f)
return CGRectMake(0, 77, screenSize.width, screenSize.height - 77 - 83);
else 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 PGCameraModeSquarePhoto:
case PGCameraModeSquareVideo:
case PGCameraModeSquareSwing:
{
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 == 926.0f)
return CGRectMake(0, 121, screenSize.width, screenSize.height - 121 - 234);
if (widescreenWidth == 896.0f)
return CGRectMake(0, 121, screenSize.width, screenSize.height - 121 - 223);
else if (widescreenWidth == 844.0f)
return CGRectMake(0, 77, screenSize.width, screenSize.height - 77 - 191);
else if (widescreenWidth == 812.0f)
return CGRectMake(0, 121, screenSize.width, screenSize.height - 121 - 191);
else 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 == PGCameraModeSquarePhoto || mode == PGCameraModeSquareVideo || mode == PGCameraModeSquareSwing)
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 false;
}
+ (NSArray *)resultSignalsForSelectionContext:(TGMediaSelectionContext *)selectionContext editingContext:(TGMediaEditingContext *)editingContext currentItem:(id<TGMediaSelectableItem>)currentItem storeAssets:(bool)storeAssets saveEditedPhotos:(bool)saveEditedPhotos descriptionGenerator:(id (^)(id, NSString *, NSArray *, NSString *))descriptionGenerator
{
NSMutableArray *signals = [[NSMutableArray alloc] init];
NSMutableArray *selectedItems = selectionContext.selectedItems != nil ? [selectionContext.selectedItems mutableCopy] : [[NSMutableArray alloc] init];
if (selectedItems.count == 0 && currentItem != nil)
[selectedItems addObject:currentItem];
bool isScan = false;
for (id<TGMediaEditableItem> item in selectedItems) {
if ([item isKindOfClass:[TGCameraCapturedPhoto class]] && ((TGCameraCapturedPhoto *)item).rectangle != nil) {
isScan = true;
break;
}
}
if (storeAssets && !isScan) {
NSMutableArray *fullSizeSignals = [[NSMutableArray alloc] init];
for (id<TGMediaEditableItem> item in selectedItems)
{
if ([item isKindOfClass:[TGCameraCapturedPhoto class]] && ((TGCameraCapturedPhoto *)item).rectangle != nil) {
isScan = true;
}
if ([editingContext timerForItem:item] == nil)
{
SSignal *saveMedia = [SSignal defer:^SSignal *
{
if ([item isKindOfClass:[TGCameraCapturedPhoto class]])
{
TGCameraCapturedPhoto *photo = (TGCameraCapturedPhoto *)item;
return [SSignal single:@{@"type": @"photo", @"url": photo.url}];
}
else if ([item isKindOfClass:[TGCameraCapturedVideo class]])
{
TGCameraCapturedVideo *video = (TGCameraCapturedVideo *)item;
return [[video avAsset] mapToSignal:^SSignal *(AVURLAsset *avAsset) {
return [SSignal single:@{@"type": @"video", @"url": avAsset.URL}];
}];
}
return [SSignal complete];
}];
[fullSizeSignals addObject:saveMedia];
if (saveEditedPhotos)
{
[fullSizeSignals addObject:[[[editingContext fullSizeImageUrlForItem:item] filter:^bool(id result)
{
return [result isKindOfClass:[NSURL class]];
}] mapToSignal:^SSignal *(NSURL *url)
{
return [SSignal single:@{@"type": @"photo", @"url": url}];
}]];
}
}
}
SSignal *combinedSignal = nil;
SQueue *queue = [SQueue concurrentDefaultQueue];
for (SSignal *signal in fullSizeSignals)
{
if (combinedSignal == nil)
combinedSignal = [signal startOn:queue];
else
combinedSignal = [[combinedSignal then:signal] startOn:queue];
}
[[[combinedSignal deliverOn:[SQueue mainQueue]] mapToSignal:^SSignal *(NSDictionary *desc)
{
if ([desc[@"type"] isEqualToString:@"photo"])
{
return [[TGMediaAssetsLibrary sharedLibrary] saveAssetWithImageAtUrl:desc[@"url"]];
}
else if ([desc[@"type"] isEqualToString:@"video"])
{
return [[TGMediaAssetsLibrary sharedLibrary] saveAssetWithVideoAtUrl:desc[@"url"]];
}
else
{
return [SSignal complete];
}
}] startWithNext:nil];
}
static dispatch_once_t onceToken;
static UIImage *blankImage;
dispatch_once(&onceToken, ^
{
UIGraphicsBeginImageContextWithOptions(CGSizeMake(1, 1), true, 0.0f);
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetFillColorWithColor(context, [UIColor blackColor].CGColor);
CGContextFillRect(context, CGRectMake(0, 0, 1, 1));
blankImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
});
SSignal *(^inlineThumbnailSignal)(id<TGMediaEditableItem>) = ^SSignal *(id<TGMediaEditableItem> item)
{
return [item thumbnailImageSignal];
};
NSNumber *groupedId;
NSInteger i = 0;
if (selectionContext.grouping && selectedItems.count > 1)
groupedId = @([TGCameraController generateGroupedId]);
bool hasAnyTimers = false;
if (editingContext != nil)
{
for (id<TGMediaEditableItem> item in selectedItems)
{
if ([editingContext timerForItem:item] != nil)
{
hasAnyTimers = true;
break;
}
}
}
for (id<TGMediaEditableItem> asset in selectedItems)
{
if ([asset isKindOfClass:[TGCameraCapturedPhoto class]])
{
NSString *caption = [editingContext captionForItem:asset];
NSArray *entities = [editingContext entitiesForItem:asset];
id<TGMediaEditAdjustments> adjustments = [editingContext adjustmentsForItem:asset];
NSNumber *timer = [editingContext timerForItem:asset];
SSignal *inlineSignal = [[asset screenImageSignal:0.0] map:^id(UIImage *originalImage)
{
NSMutableDictionary *dict = [[NSMutableDictionary alloc] init];
dict[@"type"] = @"editedPhoto";
dict[@"image"] = originalImage;
if (timer != nil)
dict[@"timer"] = timer;
else if (groupedId != nil && !hasAnyTimers)
dict[@"groupedId"] = groupedId;
if (isScan) {
if (caption != nil)
dict[@"caption"] = caption;
return dict;
} else {
id generatedItem = descriptionGenerator(dict, caption, entities, nil);
return generatedItem;
}
}];
SSignal *assetSignal = inlineSignal;
SSignal *imageSignal = assetSignal;
if (editingContext != nil)
{
imageSignal = [[[[[editingContext imageSignalForItem:asset withUpdates:true] filter:^bool(id result)
{
return result == nil || ([result isKindOfClass:[UIImage class]] && !((UIImage *)result).degraded);
}] take:1] mapToSignal:^SSignal *(id result)
{
if (result == nil)
{
return [SSignal fail:nil];
}
else if ([result isKindOfClass:[UIImage class]])
{
UIImage *image = (UIImage *)result;
image.edited = true;
return [SSignal single:image];
}
return [SSignal complete];
}] onCompletion:^
{
}];
} else {
NSLog(@"Editing context is nil");
}
[signals addObject:[[[imageSignal map:^NSDictionary *(UIImage *image)
{
NSMutableDictionary *dict = [[NSMutableDictionary alloc] init];
dict[@"type"] = @"editedPhoto";
dict[@"image"] = image;
if (adjustments.paintingData.stickers.count > 0)
dict[@"stickers"] = adjustments.paintingData.stickers;
bool animated = false;
for (TGPhotoPaintEntity *entity in adjustments.paintingData.entities) {
if (entity.animated) {
animated = true;
break;
}
}
if (animated) {
dict[@"isAnimation"] = @true;
if ([adjustments isKindOfClass:[PGPhotoEditorValues class]]) {
dict[@"adjustments"] = [TGVideoEditAdjustments editAdjustmentsWithPhotoEditorValues:(PGPhotoEditorValues *)adjustments preset:TGMediaVideoConversionPresetAnimation];
} else {
dict[@"adjustments"] = adjustments;
}
NSString *filePath = [NSTemporaryDirectory() stringByAppendingPathComponent:[[NSString alloc] initWithFormat:@"gifvideo_%x.jpg", (int)arc4random()]];
NSData *data = UIImageJPEGRepresentation(image, 0.8);
[data writeToFile:filePath atomically:true];
dict[@"url"] = [NSURL fileURLWithPath:filePath];
if ([adjustments cropAppliedForAvatar:false] || adjustments.hasPainting || adjustments.toolsApplied)
{
UIImage *paintingImage = adjustments.paintingData.stillImage;
if (paintingImage == nil) {
paintingImage = adjustments.paintingData.image;
}
UIImage *thumbnailImage = TGPhotoEditorVideoExtCrop(image, paintingImage, adjustments.cropOrientation, adjustments.cropRotation, adjustments.cropRect, adjustments.cropMirrored, TGScaleToFill(image.size, CGSizeMake(512, 512)), adjustments.originalSize, true, true, true, false);
if (thumbnailImage != nil) {
dict[@"previewImage"] = thumbnailImage;
}
}
}
if (timer != nil)
dict[@"timer"] = timer;
else if (groupedId != nil && !hasAnyTimers)
dict[@"groupedId"] = groupedId;
if (isScan) {
if (caption != nil)
dict[@"caption"] = caption;
return dict;
} else {
id generatedItem = descriptionGenerator(dict, caption, entities, nil);
return generatedItem;
}
}] catch:^SSignal *(__unused id error)
{
return inlineSignal;
}] onCompletion:^{
[editingContext description];
}]];
i++;
}
else if ([asset isKindOfClass:[TGCameraCapturedVideo class]])
{
TGCameraCapturedVideo *video = (TGCameraCapturedVideo *)asset;
TGVideoEditAdjustments *adjustments = (TGVideoEditAdjustments *)[editingContext adjustmentsForItem:asset];
NSString *caption = [editingContext captionForItem:asset];
NSArray *entities = [editingContext entitiesForItem:asset];
NSNumber *timer = [editingContext timerForItem:asset];
UIImage *(^cropVideoThumbnail)(UIImage *, CGSize, CGSize, bool) = ^UIImage *(UIImage *image, CGSize targetSize, CGSize sourceSize, bool resize)
{
if ([adjustments cropAppliedForAvatar:false] || adjustments.hasPainting || adjustments.toolsApplied)
{
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);
UIImage *paintingImage = adjustments.paintingData.stillImage;
if (paintingImage == nil) {
paintingImage = adjustments.paintingData.image;
}
if (adjustments.toolsApplied) {
image = [PGPhotoEditor resultImageForImage:image adjustments:adjustments];
}
return TGPhotoEditorCrop(image, paintingImage, adjustments.cropOrientation, 0, scaledCropRect, adjustments.cropMirrored, targetSize, sourceSize, resize);
}
return image;
};
CGSize imageSize = TGFillSize(asset.originalSize, CGSizeMake(512, 512));
SSignal *trimmedVideoThumbnailSignal = [[video avAsset] mapToSignal:^SSignal *(AVURLAsset *avAsset) {
return [[TGMediaAssetImageSignals videoThumbnailForAVAsset:avAsset size:imageSize timestamp:CMTimeMakeWithSeconds(adjustments.trimStartValue, NSEC_PER_SEC)] map:^UIImage *(UIImage *image)
{
return cropVideoThumbnail(image, TGScaleToFill(asset.originalSize, CGSizeMake(512, 512)), asset.originalSize, true);
}];
}];
SSignal *videoThumbnailSignal = [inlineThumbnailSignal(asset) map:^UIImage *(UIImage *image)
{
return cropVideoThumbnail(image, image.size, image.size, false);
}];
SSignal *thumbnailSignal = adjustments.trimStartValue > FLT_EPSILON ? trimmedVideoThumbnailSignal : videoThumbnailSignal;
TGMediaVideoConversionPreset preset = [TGMediaVideoConverter presetFromAdjustments:adjustments];
CGSize dimensions = [TGMediaVideoConverter dimensionsFor:asset.originalSize adjustments:adjustments preset:preset];
NSTimeInterval duration = adjustments.trimApplied ? (adjustments.trimEndValue - adjustments.trimStartValue) : video.videoDuration;
[signals addObject:[thumbnailSignal mapToSignal:^id(UIImage *image)
{
return [video.avAsset map:^id(AVURLAsset *avAsset) {
NSMutableDictionary *dict = [[NSMutableDictionary alloc] init];
dict[@"type"] = @"cameraVideo";
dict[@"url"] = avAsset.URL;
dict[@"previewImage"] = image;
dict[@"adjustments"] = adjustments;
dict[@"dimensions"] = [NSValue valueWithCGSize:dimensions];
dict[@"duration"] = @(duration);
if (adjustments.paintingData.stickers.count > 0)
dict[@"stickers"] = adjustments.paintingData.stickers;
if (timer != nil)
dict[@"timer"] = timer;
else if (groupedId != nil && !hasAnyTimers)
dict[@"groupedId"] = groupedId;
id generatedItem = descriptionGenerator(dict, caption, entities, nil);
return generatedItem;
}];
}]];
i++;
}
if (groupedId != nil && i == 10)
{
i = 0;
groupedId = @([TGCameraController generateGroupedId]);
}
}
if (isScan) {
SSignal *scanSignal = [[SSignal combineSignals:signals] map:^NSDictionary *(NSArray *results) {
NSMutableData *data = [[NSMutableData alloc] init];
UIImage *previewImage = nil;
UIGraphicsBeginPDFContextToData(data, CGRectZero, nil);
for (NSDictionary *dict in results) {
if ([dict[@"type"] isEqual:@"editedPhoto"]) {
UIImage *image = dict[@"image"];
if (previewImage == nil) {
previewImage = image;
}
if (image != nil) {
CGRect rect = CGRectMake(0, 0, image.size.width, image.size.height);
UIGraphicsBeginPDFPageWithInfo(rect, nil);
CGContextRef pdfContext = UIGraphicsGetCurrentContext();
CGContextTranslateCTM(pdfContext, 0, image.size.height);
CGContextScaleCTM(pdfContext, 1.0, -1.0);
NSData *jpegData = UIImageJPEGRepresentation(image, 0.65);
CGDataProviderRef dataProvider = CGDataProviderCreateWithCFData((__bridge CFDataRef)jpegData);
CGImageRef cgImage = CGImageCreateWithJPEGDataProvider(dataProvider, NULL, true, kCGRenderingIntentDefault);
CGContextDrawImage(pdfContext, rect, cgImage);
CGDataProviderRelease(dataProvider);
CGImageRelease(cgImage);
}
}
}
UIGraphicsEndPDFContext();
NSString *filePath = [NSTemporaryDirectory() stringByAppendingPathComponent:[[NSString alloc] initWithFormat:@"scan_%x.pdf", (int)arc4random()]];
[data writeToFile:filePath atomically:true];
NSMutableDictionary *dict = [[NSMutableDictionary alloc] init];
dict[@"type"] = @"file";
dict[@"previewImage"] = previewImage;
dict[@"tempFileUrl"] = [NSURL fileURLWithPath:filePath];
dict[@"fileName"] = @"Document Scan.pdf";
dict[@"mimeType"] = @"application/pdf";
id generatedItem = descriptionGenerator(dict, dict[@"caption"], nil, nil);
return generatedItem;
}];
signals = [NSMutableArray arrayWithObject:scanSignal];
}
return signals;
}
+ (int64_t)generateGroupedId
{
int64_t value;
arc4random_buf(&value, sizeof(int64_t));
return value;
}
@end