Add files via upload

This commit is contained in:
Khaled Alshehri 2018-08-08 02:19:41 +03:00 committed by GitHub
parent 47c0ba2719
commit ebbdffde33
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
32 changed files with 2233 additions and 0 deletions

26
Features/Core/SCFeature.h Normal file
View File

@ -0,0 +1,26 @@
//
// SCFeature.h
// SCCamera
//
// Created by Kristian Bauer on 1/4/18.
//
#import <UIKit/UIKit.h>
/**
* Top level protocol for UI features
*/
#define SCLogCameraFeatureInfo(fmt, ...) SCLogCoreCameraInfo(@"[SCFeature] " fmt, ##__VA_ARGS__)
@protocol SCFeatureContainerView;
@protocol SCFeature <NSObject>
@optional
- (void)configureWithView:(UIView<SCFeatureContainerView> *)view;
- (void)forwardCameraTimerGesture:(UIGestureRecognizer *)gestureRecognizer;
- (void)forwardCameraOverlayTapGesture:(UIGestureRecognizer *)gestureRecognizer;
- (void)forwardLongPressGesture:(UIGestureRecognizer *)gestureRecognizer;
- (void)forwardPinchGesture:(UIPinchGestureRecognizer *)gestureRecognizer;
- (void)forwardPanGesture:(UIPanGestureRecognizer *)gestureRecognizer;
- (BOOL)shouldBlockTouchAtPoint:(CGPoint)point;
@end

View File

@ -0,0 +1,13 @@
//
// SCFeatureContainerView.h
// SCCamera
//
// Created by Kristian Bauer on 4/17/18.
//
#import <UIKit/UIKit.h>
@protocol SCFeatureContainerView
- (BOOL)isTapGestureRecognizer:(UIGestureRecognizer *)gestureRecognizer;
- (CGRect)initialCameraTimerFrame;
@end

View File

@ -0,0 +1,44 @@
//
// SCFeatureCoordinator.h
// SCCamera
//
// Created by Kristian Bauer on 1/4/18.
//
#import "SCFeature.h"
#import <SCBase/SCMacros.h>
@protocol SCFeatureProvider;
@class SCCameraOverlayView;
/**
* Handles creation of SCFeatures and communication between owner and features.
*/
@interface SCFeatureCoordinator : NSObject
SC_INIT_AND_NEW_UNAVAILABLE;
- (instancetype)initWithFeatureContainerView:(SCCameraOverlayView *)containerView
provider:(id<SCFeatureProvider>)provider;
/**
* Asks provider for features with given featureTypes specified in initializer.
*/
- (void)reloadFeatures;
/**
* Eventually won't need this, but in order to use new framework w/ existing architecture, need a way to forward
* gestures to individual features.
*/
- (void)forwardCameraTimerGesture:(UIGestureRecognizer *)gestureRecognizer;
- (void)forwardCameraOverlayTapGesture:(UIGestureRecognizer *)gestureRecognizer;
- (void)forwardLongPressGesture:(UIGestureRecognizer *)gestureRecognizer;
- (void)forwardPinchGesture:(UIPinchGestureRecognizer *)recognizer;
- (void)forwardPanGesture:(UIPanGestureRecognizer *)recognizer;
/**
* To prevent gestures on AVCameraViewController from triggering at the same time as feature controls, need to provide a
* way for features to indicate that they will block a touch with given point.
*/
- (BOOL)shouldBlockTouchAtPoint:(CGPoint)point;
@end

View File

@ -0,0 +1,117 @@
//
// SCFeatureCoordinator.m
// SCCamera
//
// Created by Kristian Bauer on 1/4/18.
//
#import "SCFeatureCoordinator.h"
#import "SCFeature.h"
#import "SCFeatureProvider.h"
#import <SCFoundation/SCAssertWrapper.h>
#import <SCFoundation/SCTraceODPCompatible.h>
typedef NSString SCFeatureDictionaryKey;
@interface SCFeatureCoordinator ()
@property (nonatomic, weak) UIView<SCFeatureContainerView> *containerView;
@property (nonatomic, strong) id<SCFeatureProvider> provider;
@end
@implementation SCFeatureCoordinator
- (instancetype)initWithFeatureContainerView:(UIView<SCFeatureContainerView> *)containerView
provider:(id<SCFeatureProvider>)provider
{
SCTraceODPCompatibleStart(2);
SCAssert(containerView, @"SCFeatureCoordinator containerView must be non-nil");
SCAssert(provider, @"SCFeatureCoordinator provider must be non-nil");
self = [super init];
if (self) {
_containerView = containerView;
_provider = provider;
[self reloadFeatures];
}
return self;
}
- (void)reloadFeatures
{
SCTraceODPCompatibleStart(2);
[_provider resetInstances];
NSMutableArray *features = [NSMutableArray array];
for (id<SCFeature> feature in _provider.supportedFeatures) {
if ([feature respondsToSelector:@selector(configureWithView:)]) {
[feature configureWithView:_containerView];
}
if (feature) {
[features addObject:feature];
}
}
}
- (void)forwardCameraTimerGesture:(UIGestureRecognizer *)gestureRecognizer
{
SCTraceODPCompatibleStart(2);
for (id<SCFeature> feature in _provider.supportedFeatures) {
if ([feature respondsToSelector:@selector(forwardCameraTimerGesture:)]) {
[feature forwardCameraTimerGesture:gestureRecognizer];
}
}
}
- (void)forwardCameraOverlayTapGesture:(UIGestureRecognizer *)gestureRecognizer
{
SCTraceODPCompatibleStart(2);
for (id<SCFeature> feature in _provider.supportedFeatures) {
if ([feature respondsToSelector:@selector(forwardCameraOverlayTapGesture:)]) {
[feature forwardCameraOverlayTapGesture:gestureRecognizer];
}
}
}
- (void)forwardLongPressGesture:(UIGestureRecognizer *)gestureRecognizer
{
SCTraceODPCompatibleStart(2);
for (id<SCFeature> feature in _provider.supportedFeatures) {
if ([feature respondsToSelector:@selector(forwardLongPressGesture:)]) {
[feature forwardLongPressGesture:gestureRecognizer];
}
}
}
- (void)forwardPinchGesture:(UIPinchGestureRecognizer *)gestureRecognizer
{
SCTraceODPCompatibleStart(2);
for (id<SCFeature> feature in _provider.supportedFeatures) {
if ([feature respondsToSelector:@selector(forwardPinchGesture:)]) {
[feature forwardPinchGesture:gestureRecognizer];
}
}
}
- (void)forwardPanGesture:(UIPanGestureRecognizer *)gestureRecognizer
{
SCTraceODPCompatibleStart(2);
for (id<SCFeature> feature in _provider.supportedFeatures) {
if ([feature respondsToSelector:@selector(forwardPanGesture:)]) {
[feature forwardPanGesture:gestureRecognizer];
}
}
}
- (BOOL)shouldBlockTouchAtPoint:(CGPoint)point
{
SCTraceODPCompatibleStart(2);
for (id<SCFeature> feature in _provider.supportedFeatures) {
if ([feature respondsToSelector:@selector(shouldBlockTouchAtPoint:)] &&
[feature shouldBlockTouchAtPoint:point]) {
return YES;
}
}
return NO;
}
@end

View File

@ -0,0 +1,50 @@
//
// SCFeatureProvider.h
// SCCamera
//
// Created by Kristian Bauer on 1/4/18.
//
#import <SCCamera/AVCameraViewEnums.h>
#import <Foundation/Foundation.h>
@class SCFeatureSettingsManager, SCCapturerToken, SCUserSession;
@protocol SCFeature
, SCCapturer, SCFeatureFlash, SCFeatureHandsFree, SCFeatureLensSideButton, SCFeatureLensButtonZ, SCFeatureMemories,
SCFeatureNightMode, SCFeatureSnapKit, SCFeatureTapToFocusAndExposure, SCFeatureToggleCamera, SCFeatureShazam,
SCFeatureImageCapture, SCFeatureScanning, SCFeatureZooming;
/**
* Provides single location for creating and configuring SCFeatures.
*/
@protocol SCFeatureProvider <NSObject>
@property (nonatomic) AVCameraViewType cameraViewType;
@property (nonatomic, readonly) id<SCCapturer> capturer;
@property (nonatomic, strong, readwrite) SCCapturerToken *token;
@property (nonatomic, readonly) SCUserSession *userSession;
// TODO: We should not be reusing AVCameraViewController so eventually the
// context should be removed.
@property (nonatomic, readonly) AVCameraViewControllerContext context;
@property (nonatomic) id<SCFeatureHandsFree> handsFreeRecording;
@property (nonatomic) id<SCFeatureSnapKit> snapKit;
@property (nonatomic) id<SCFeatureTapToFocusAndExposure> tapToFocusAndExposure;
@property (nonatomic) id<SCFeatureMemories> memories;
@property (nonatomic) id<SCFeatureFlash> flash;
@property (nonatomic) id<SCFeatureLensSideButton> lensSideButton;
@property (nonatomic) id<SCFeatureLensButtonZ> lensZButton;
@property (nonatomic) id<SCFeatureNightMode> nightMode;
@property (nonatomic) id<SCFeatureToggleCamera> toggleCamera;
@property (nonatomic) id<SCFeatureShazam> shazam;
@property (nonatomic) id<SCFeatureScanning> scanning;
@property (nonatomic) id<SCFeatureImageCapture> imageCapture;
@property (nonatomic) id<SCFeatureZooming> zooming;
@property (nonatomic, readonly) NSArray<id<SCFeature>> *supportedFeatures;
- (void)resetInstances;
@end

View File

@ -0,0 +1,20 @@
//
// SCFeatureFlash.h
// SCCamera
//
// Created by Kristian Bauer on 3/27/18.
//
#import "SCFeature.h"
@class SCNavigationBarButtonItem;
/**
* Public interface for interacting with camera flash feature.
*/
@protocol SCFeatureFlash <SCFeature>
@property (nonatomic, readonly) SCNavigationBarButtonItem *navigationBarButtonItem;
- (void)interruptGestures;
@end

View File

@ -0,0 +1,23 @@
//
// SCFeatureFlashImpl.h
// SCCamera
//
// Created by Kristian Bauer on 3/27/18.
//
#import "SCFeatureFlash.h"
#import <SCBase/SCMacros.h>
@class SCLogger;
@protocol SCCapturer;
/**
* Interface for camera flash feature. Handles enabling/disabling of camera flash via SCCapturer and UI for displaying
* flash button.
* Should only expose initializer. All other vars and methods should be declared in SCFeatureFlash protocol.
*/
@interface SCFeatureFlashImpl : NSObject <SCFeatureFlash>
SC_INIT_AND_NEW_UNAVAILABLE
- (instancetype)initWithCapturer:(id<SCCapturer>)capturer logger:(SCLogger *)logger NS_DESIGNATED_INITIALIZER;
@end

View File

@ -0,0 +1,226 @@
//
// SCFeatureFlashImpl.m
// SCCamera
//
// Created by Kristian Bauer on 3/27/18.
//
#import "SCFeatureFlashImpl.h"
#import "SCCapturer.h"
#import "SCFlashButton.h"
#import "SCManagedCapturerListener.h"
#import "SCManagedCapturerState.h"
#import <SCFoundation/SCLocale.h>
#import <SCFoundation/SCLog.h>
#import <SCFoundation/SCTraceODPCompatible.h>
#import <SCLogger/SCLogger.h>
#import <SCUIKit/SCNavigationBarButtonItem.h>
static CGFloat const kSCFlashButtonInsets = -2.f;
static CGRect const kSCFlashButtonFrame = {0, 0, 36, 44};
static NSString *const kSCFlashEventName = @"TOGGLE_CAMERA_FLASH_BUTTON";
static NSString *const kSCFlashEventParameterFlashName = @"flash_on";
static NSString *const kSCFlashEventParameterCameraName = @"front_facing_camera_on";
@interface SCFeatureFlashImpl ()
@property (nonatomic, strong, readwrite) id<SCCapturer> capturer;
@property (nonatomic, strong, readwrite) SCLogger *logger;
@property (nonatomic, strong, readwrite) SCFlashButton *flashButton;
@property (nonatomic, weak, readwrite) UIView<SCFeatureContainerView> *containerView;
@property (nonatomic, strong, readwrite) SCManagedCapturerState *managedCapturerState;
@property (nonatomic, assign, readwrite) BOOL canEnable;
@end
@interface SCFeatureFlashImpl (SCManagedCapturerListener) <SCManagedCapturerListener>
@end
@implementation SCFeatureFlashImpl
@synthesize navigationBarButtonItem = _navigationBarButtonItem;
- (instancetype)initWithCapturer:(id<SCCapturer>)capturer logger:(SCLogger *)logger
{
SCTraceODPCompatibleStart(2);
self = [super init];
if (self) {
_capturer = capturer;
[_capturer addListener:self];
_logger = logger;
}
return self;
}
- (void)dealloc
{
SCTraceODPCompatibleStart(2);
[_capturer removeListener:self];
}
#pragma mark - SCFeature
- (void)configureWithView:(UIView<SCFeatureContainerView> *)view
{
SCTraceODPCompatibleStart(2);
_containerView = view;
}
- (BOOL)shouldBlockTouchAtPoint:(CGPoint)point
{
SCTraceODPCompatibleStart(2);
SC_GUARD_ELSE_RETURN_VALUE(_flashButton.userInteractionEnabled && !_flashButton.hidden, NO);
CGPoint convertedPoint = [_flashButton convertPoint:point fromView:_containerView];
return [_flashButton pointInside:convertedPoint withEvent:nil];
}
#pragma mark - SCFeatureFlash
- (void)interruptGestures
{
SCTraceODPCompatibleStart(2);
[_flashButton interruptGestures];
}
- (SCNavigationBarButtonItem *)navigationBarButtonItem
{
SCTraceODPCompatibleStart(2);
SC_GUARD_ELSE_RETURN_VALUE(!_navigationBarButtonItem, _navigationBarButtonItem);
_navigationBarButtonItem = [[SCNavigationBarButtonItem alloc] initWithCustomView:self.flashButton];
return _navigationBarButtonItem;
}
#pragma mark - Getters
- (SCFlashButton *)flashButton
{
SCTraceODPCompatibleStart(2);
SC_GUARD_ELSE_RETURN_VALUE(!_flashButton, _flashButton);
_flashButton = [[SCFlashButton alloc] initWithFrame:kSCFlashButtonFrame];
_flashButton.layer.sublayerTransform = CATransform3DMakeTranslation(kSCFlashButtonInsets, 0, 0);
_flashButton.buttonState = SCFlashButtonStateOff;
_flashButton.maximumScale = 1.1111f;
[_flashButton addTarget:self action:@selector(_flashTapped)];
_flashButton.accessibilityIdentifier = @"flash";
_flashButton.accessibilityLabel = SCLocalizedString(@"flash", 0);
return _flashButton;
}
#pragma mark - Setters
- (void)setCanEnable:(BOOL)canEnable
{
SCTraceODPCompatibleStart(2);
SCLogCameraFeatureInfo(@"[%@] setCanEnable new: %@ old: %@", NSStringFromClass([self class]),
canEnable ? @"YES" : @"NO", _canEnable ? @"YES" : @"NO");
self.flashButton.userInteractionEnabled = canEnable;
}
#pragma mark - Internal Helpers
- (void)_flashTapped
{
SCTraceODPCompatibleStart(2);
BOOL flashActive = !_managedCapturerState.flashActive;
SCLogCameraFeatureInfo(@"[%@] _flashTapped flashActive new: %@ old: %@", NSStringFromClass([self class]),
flashActive ? @"YES" : @"NO", !flashActive ? @"YES" : @"NO");
_containerView.userInteractionEnabled = NO;
@weakify(self);
[_capturer setFlashActive:flashActive
completionHandler:^{
@strongify(self);
SCLogCameraFeatureInfo(@"[%@] _flashTapped setFlashActive completion", NSStringFromClass([self class]));
self.containerView.userInteractionEnabled = YES;
}
context:SCCapturerContext];
NSDictionary *loggingParameters = @{
kSCFlashEventParameterFlashName : @(flashActive),
kSCFlashEventParameterCameraName :
@(_managedCapturerState.devicePosition == SCManagedCaptureDevicePositionFront)
};
[_logger logEvent:kSCFlashEventName parameters:loggingParameters];
}
- (BOOL)_shouldHideForState:(SCManagedCapturerState *)state
{
SCTraceODPCompatibleStart(2);
return (!state.flashSupported && !state.torchSupported &&
state.devicePosition != SCManagedCaptureDevicePositionFront) ||
state.arSessionActive;
}
@end
@implementation SCFeatureFlashImpl (SCManagedCapturerListener)
- (void)managedCapturer:(id<SCCapturer>)managedCapturer didChangeFlashActive:(SCManagedCapturerState *)state
{
SCTraceODPCompatibleStart(2);
SCLogCameraFeatureInfo(@"[%@] didChangeFlashActive flashActive: %@", NSStringFromClass([self class]),
state.flashActive ? @"YES" : @"NO");
self.flashButton.buttonState = state.flashActive ? SCFlashButtonStateOn : SCFlashButtonStateOff;
}
- (void)managedCapturer:(id<SCCapturer>)managedCapturer
didChangeFlashSupportedAndTorchSupported:(SCManagedCapturerState *)state
{
SCTraceODPCompatibleStart(2);
SCLogCameraFeatureInfo(
@"[%@] didChangeFlashSupportedAndTorchSupported flashSupported: %@ torchSupported: %@ devicePosition: %@",
NSStringFromClass([self class]), state.flashSupported ? @"YES" : @"NO", state.torchSupported ? @"YES" : @"NO",
state.devicePosition == SCManagedCaptureDevicePositionFront ? @"front" : @"back");
self.flashButton.hidden = [self _shouldHideForState:state];
}
- (void)managedCapturer:(id<SCCapturer>)managedCapturer didChangeState:(SCManagedCapturerState *)state
{
SCTraceODPCompatibleStart(2);
_managedCapturerState = [state copy];
}
- (void)managedCapturer:(id<SCCapturer>)managedCapturer didChangeARSessionActive:(SCManagedCapturerState *)state
{
SCTraceODPCompatibleStart(2);
SCLogCameraFeatureInfo(@"[%@] didChangeARSessionActive: %@", NSStringFromClass([self class]),
state.arSessionActive ? @"YES" : @"NO");
self.flashButton.hidden = [self _shouldHideForState:state];
}
- (void)managedCapturer:(id<SCCapturer>)managedCapturer
didBeginVideoRecording:(SCManagedCapturerState *)state
session:(SCVideoCaptureSessionInfo)session
{
SCTraceODPCompatibleStart(2);
self.canEnable = NO;
}
- (void)managedCapturer:(id<SCCapturer>)managedCapturer
didFinishRecording:(SCManagedCapturerState *)state
session:(SCVideoCaptureSessionInfo)session
recordedVideo:(SCManagedRecordedVideo *)recordedVideo
{
SCTraceODPCompatibleStart(2);
self.canEnable = YES;
}
- (void)managedCapturer:(id<SCCapturer>)managedCapturer
didFailRecording:(SCManagedCapturerState *)state
session:(SCVideoCaptureSessionInfo)session
error:(NSError *)error
{
SCTraceODPCompatibleStart(2);
self.canEnable = YES;
}
- (void)managedCapturer:(id<SCCapturer>)managedCapturer
didCancelRecording:(SCManagedCapturerState *)state
session:(SCVideoCaptureSessionInfo)session
{
SCTraceODPCompatibleStart(2);
self.canEnable = YES;
}
@end

View File

@ -0,0 +1,15 @@
//
// SCFlashButton.h
// SCCamera
//
// Created by Will Wu on 2/13/14.
// Copyright (c) 2014 Snapchat, Inc. All rights reserved.
//
#import <SCUIKit/SCGrowingButton.h>
typedef NS_ENUM(NSInteger, SCFlashButtonState) { SCFlashButtonStateOn = 0, SCFlashButtonStateOff = 1 };
@interface SCFlashButton : SCGrowingButton
@property (nonatomic, assign) SCFlashButtonState buttonState;
@end

View File

@ -0,0 +1,35 @@
//
// SCFlashButton.m
// SCCamera
//
// Created by Will Wu on 2/13/14.
// Copyright (c) 2014 Snapchat, Inc. All rights reserved.
//
#import "SCFlashButton.h"
#import <SCUIKit/SCPixelRounding.h>
@implementation SCFlashButton
- (void)setButtonState:(SCFlashButtonState)buttonState
{
// Don't reset flash button state if it doesn't change.
if (_buttonState == buttonState) {
return;
}
_buttonState = buttonState;
if (buttonState == SCFlashButtonStateOn) {
self.image = [UIImage imageNamed:@"camera_flash_on_v10"];
self.accessibilityValue = @"on";
} else {
self.image = [UIImage imageNamed:@"camera_flash_off_v10"];
self.accessibilityValue = @"off";
}
self.imageInset = SCRoundSizeToPixels(CGSizeMake((CGRectGetWidth(self.bounds) - self.image.size.width) / 2,
(CGRectGetHeight(self.bounds) - self.image.size.height) / 2));
}
@end

View File

@ -0,0 +1,30 @@
//
// SCFeatureHandsFree.h
// SCCamera
//
// Created by Kristian Bauer on 2/26/18.
//
#import "SCFeature.h"
#import <SCCamera/AVCameraViewEnums.h>
@class SCLongPressGestureRecognizer, SCPreviewPresenter;
@protocol SCFeatureHandsFree <SCFeature>
@property (nonatomic, weak) SCPreviewPresenter *previewPresenter;
@property (nonatomic, strong, readonly) SCLongPressGestureRecognizer *longPressGestureRecognizer;
/**
* Whether the feature is enabled or not.
*/
@property (nonatomic) BOOL enabled;
- (void)setupRecordLifecycleEventsWithMethod:(SCCameraRecordingMethod)method;
- (BOOL)shouldDisplayMultiSnapTooltip;
/**
* Block called when user cancels hands-free recording via X button.
*/
- (void)setCancelBlock:(dispatch_block_t)cancelBlock;
@end

View File

@ -0,0 +1,27 @@
//
// SCFeatureImageCapture.h
// SCCamera
//
// Created by Kristian Bauer on 4/18/18.
//
#import "SCFeature.h"
#import <SCFoundation/SCFuture.h>
@protocol SCFeatureImageCapture;
@protocol SCFeatureImageCaptureDelegate <NSObject>
- (void)featureImageCapture:(id<SCFeatureImageCapture>)featureImageCapture willCompleteWithImage:(UIImage *)image;
- (void)featureImageCapture:(id<SCFeatureImageCapture>)featureImageCapture didCompleteWithError:(NSError *)error;
- (void)featureImageCapturedDidComplete:(id<SCFeatureImageCapture>)featureImageCapture;
@end
/**
SCFeature protocol for capturing an image.
*/
@protocol SCFeatureImageCapture <SCFeature>
@property (nonatomic, weak, readwrite) id<SCFeatureImageCaptureDelegate> delegate;
@property (nonatomic, strong, readonly) SCPromise<UIImage *> *imagePromise;
- (void)captureImage:(NSString *)captureSessionID;
@end

View File

@ -0,0 +1,21 @@
//
// SCFeatureImageCaptureImpl.h
// SCCamera
//
// Created by Kristian Bauer on 4/18/18.
//
#import "AVCameraViewEnums.h"
#import "SCFeatureImageCapture.h"
#import <SCBase/SCMacros.h>
@protocol SCCapturer;
@class SCLogger;
@interface SCFeatureImageCaptureImpl : NSObject <SCFeatureImageCapture>
SC_INIT_AND_NEW_UNAVAILABLE
- (instancetype)initWithCapturer:(id<SCCapturer>)capturer
logger:(SCLogger *)logger
cameraViewType:(AVCameraViewType)cameraViewType NS_DESIGNATED_INITIALIZER;
@end

View File

@ -0,0 +1,184 @@
//
// SCFeatureImageCaptureImpl.m
// SCCamera
//
// Created by Kristian Bauer on 4/18/18.
//
#import "SCFeatureImageCaptureImpl.h"
#import "SCLogger+Camera.h"
#import "SCManagedCapturePreviewLayerController.h"
#import "SCManagedCapturerLensAPI.h"
#import "SCManagedCapturerListener.h"
#import "SCManagedCapturerUtils.h"
#import "SCManagedStillImageCapturer.h"
#import <SCFoundation/SCDeviceName.h>
#import <SCFoundation/SCLog.h>
#import <SCFoundation/SCQueuePerformer.h>
#import <SCFoundation/SCTraceODPCompatible.h>
#import <SCGhostToSnappable/SCGhostToSnappableSignal.h>
#import <SCLogger/SCCameraMetrics.h>
#import <SCLogger/SCLogger+Performance.h>
@interface SCFeatureImageCaptureImpl ()
@property (nonatomic, strong, readwrite) id<SCCapturer> capturer;
@property (nonatomic, strong, readwrite) SCLogger *logger;
@property (nonatomic, assign) AVCameraViewType cameraViewType;
@property (nonatomic, strong, readwrite) SCManagedCapturerState *managedCapturerState;
/**
* Whether user has attempted image capture in current session. Reset on foreground of app.
*/
@property (nonatomic, assign) BOOL hasTriedCapturing;
@end
@interface SCFeatureImageCaptureImpl (SCManagedCapturerListener) <SCManagedCapturerListener>
@end
@implementation SCFeatureImageCaptureImpl
@synthesize delegate = _delegate;
@synthesize imagePromise = _imagePromise;
- (instancetype)initWithCapturer:(id<SCCapturer>)capturer
logger:(SCLogger *)logger
cameraViewType:(AVCameraViewType)cameraViewType
{
SCTraceODPCompatibleStart(2);
self = [super init];
if (self) {
_capturer = capturer;
[_capturer addListener:self];
_logger = logger;
_cameraViewType = cameraViewType;
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(_viewWillEnterForeground)
name:UIApplicationWillEnterForegroundNotification
object:nil];
}
return self;
}
- (void)dealloc
{
[_capturer removeListener:self];
}
#pragma mark - SCFeatureImageCapture
- (void)captureImage:(NSString *)captureSessionID
{
SCTraceODPCompatibleStart(2);
[_logger logTimedEventStart:kSCCameraMetricsRecordingDelay uniqueId:@"IMAGE" isUniqueEvent:NO];
BOOL asyncCaptureEnabled = [self _asynchronousCaptureEnabled:_managedCapturerState];
SCLogCameraFeatureInfo(@"[%@] takeImage begin async: %@", NSStringFromClass([self class]),
asyncCaptureEnabled ? @"YES" : @"NO");
if (asyncCaptureEnabled) {
SCQueuePerformer *performer = [[SCQueuePerformer alloc] initWithLabel:"com.snapchat.image-capture-promise"
qualityOfService:QOS_CLASS_USER_INTERACTIVE
queueType:DISPATCH_QUEUE_SERIAL
context:SCQueuePerformerContextCoreCamera];
_imagePromise = [[SCPromise alloc] initWithPerformer:performer];
}
@weakify(self);
[_capturer captureStillImageAsynchronouslyWithAspectRatio:SCManagedCapturedImageAndVideoAspectRatio()
captureSessionID:captureSessionID
completionHandler:^(UIImage *fullScreenImage, NSDictionary *metadata,
NSError *error, SCManagedCapturerState *state) {
@strongify(self);
SC_GUARD_ELSE_RETURN(self);
[self _takeImageCallback:fullScreenImage
metadata:metadata
error:error
state:state];
}
context:SCCapturerContext];
[_logger logCameraCaptureFinishedWithDuration:0];
}
#pragma mark - Private
- (void)_viewWillEnterForeground
{
SCTraceODPCompatibleStart(2);
_hasTriedCapturing = NO;
}
- (void)_takeImageCallback:(UIImage *)image
metadata:(NSDictionary *)metadata
error:(NSError *)error
state:(SCManagedCapturerState *)state
{
SCTraceODPCompatibleStart(2);
[self _logCaptureComplete:state];
if (image) {
[_delegate featureImageCapture:self willCompleteWithImage:image];
if (_imagePromise) {
[_imagePromise completeWithValue:image];
}
} else {
if (_imagePromise) {
[_imagePromise completeWithError:[NSError errorWithDomain:@"" code:-1 userInfo:nil]];
}
[_delegate featureImageCapture:self didCompleteWithError:error];
}
_imagePromise = nil;
[_delegate featureImageCapturedDidComplete:self];
}
- (BOOL)_asynchronousCaptureEnabled:(SCManagedCapturerState *)state
{
SCTraceODPCompatibleStart(2);
BOOL shouldCaptureImageFromVideoBuffer =
[SCDeviceName isSimilarToIphone5orNewer] && ![SCDeviceName isSimilarToIphone6orNewer];
// Fast image capture is disabled in following cases
// (1) flash is on;
// (2) lenses are active;
// (3) SCPhotoCapturer is not supported;
// (4) not main camera for iPhoneX;
return !state.flashActive && !state.lensesActive && !_capturer.lensProcessingCore.appliedLens &&
(SCPhotoCapturerIsEnabled() || shouldCaptureImageFromVideoBuffer) &&
(![SCDeviceName isIphoneX] || (_cameraViewType == AVCameraViewNoReply));
}
- (void)_logCaptureComplete:(SCManagedCapturerState *)state
{
SCTraceODPCompatibleStart(2);
NSDictionary *params = @{
@"type" : @"image",
@"lenses_active" : @(state.lensesActive),
@"is_back_camera" : @(state.devicePosition != SCManagedCaptureDevicePositionFront),
@"is_main_camera" : @(_cameraViewType == AVCameraViewNoReply),
@"is_first_attempt_after_app_startup" : @(!_hasTriedCapturing),
@"app_startup_type" : SCLaunchType(),
@"app_startup_time" : @(SCAppStartupTimeMicros() / 1000.0),
@"time_elapse_after_app_startup" : @(SCTimeElapseAfterAppStartupMicros() / 1000.0),
};
[_logger logTimedEventEnd:kSCCameraMetricsRecordingDelay uniqueId:@"IMAGE" parameters:params];
_hasTriedCapturing = YES;
}
@end
@implementation SCFeatureImageCaptureImpl (SCManagedCapturerListener)
- (void)managedCapturer:(id<SCCapturer>)managedCapturer didChangeState:(SCManagedCapturerState *)state
{
SCTraceODPCompatibleStart(2);
_managedCapturerState = [state copy];
}
- (void)managedCapturer:(id<SCCapturer>)managedCapturer didCapturePhoto:(SCManagedCapturerState *)state
{
SCTraceODPCompatibleStart(2);
if (_imagePromise) {
[[SCManagedCapturePreviewLayerController sharedInstance] pause];
}
}
@end

View File

@ -0,0 +1,22 @@
//
// SCFeatureNightMode.h
// SCCamera
//
// Created by Kristian Bauer on 4/9/18.
//
#import "SCFeature.h"
@class SCNavigationBarButtonItem, SCPreviewPresenter;
/**
* Public interface for interacting with camera night mode feature.
* User spec: https://snapchat.quip.com/w4h4ArzcmXCS
*/
@protocol SCFeatureNightMode <SCFeature>
@property (nonatomic, weak, readwrite) SCPreviewPresenter *previewPresenter;
@property (nonatomic, readonly) SCNavigationBarButtonItem *navigationBarButtonItem;
- (void)interruptGestures;
- (void)hideWithDelayIfNeeded;
@end

View File

@ -0,0 +1,18 @@
//
// SCNightModeButton.h
// SCCamera
//
// Created by Liu Liu on 3/19/15.
// Copyright (c) 2015 Snapchat, Inc. All rights reserved.
//
#import <SCBase/SCMacros.h>
#import <SCUIKit/SCGrowingButton.h>
@interface SCNightModeButton : SCGrowingButton
@property (nonatomic, assign, getter=isSelected) BOOL selected;
SC_INIT_AND_NEW_UNAVAILABLE
- (void)show;
- (void)hideWithDelay:(BOOL)delay;
- (BOOL)willHideAfterDelay;
@end

View File

@ -0,0 +1,95 @@
//
// SCNightModeButton.m
// SCCamera
//
// Created by Liu Liu on 3/19/15.
// Copyright (c) 2015 Snapchat, Inc. All rights reserved.
//
#import "SCNightModeButton.h"
#import <SCFoundation/SCAssertWrapper.h>
static NSTimeInterval const kSCNightModeButtonHiddenDelay = 2.5;
@implementation SCNightModeButton {
dispatch_block_t _delayedHideBlock;
}
- (instancetype)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
self.image = [UIImage imageNamed:@"camera_nightmode_off_v10"];
self.imageInset = CGSizeMake((CGRectGetWidth(self.bounds) - self.image.size.width) / 2,
(CGRectGetHeight(self.bounds) - self.image.size.height) / 2);
}
return self;
}
- (void)setSelected:(BOOL)selected
{
SC_GUARD_ELSE_RETURN(_selected != selected);
if (selected) {
[self _cancelDelayedHideAnimation];
self.image = [UIImage imageNamed:@"camera_nightmode_on_v10"];
} else {
self.image = [UIImage imageNamed:@"camera_nightmode_off_v10"];
}
self.imageInset = CGSizeMake((CGRectGetWidth(self.bounds) - self.image.size.width) / 2,
(CGRectGetHeight(self.bounds) - self.image.size.height) / 2);
_selected = selected;
}
- (void)show
{
SC_GUARD_ELSE_RETURN(self.hidden);
SCAssertMainThread();
[self _cancelDelayedHideAnimation];
self.hidden = NO;
[self animate];
}
- (void)hideWithDelay:(BOOL)delay
{
SC_GUARD_ELSE_RETURN(!self.hidden);
SCAssertMainThread();
[self _cancelDelayedHideAnimation];
if (delay) {
@weakify(self);
_delayedHideBlock = dispatch_block_create(0, ^{
@strongify(self);
SC_GUARD_ELSE_RETURN(self);
[UIView animateWithDuration:0.3
animations:^{
self.alpha = 0;
}
completion:^(BOOL finished) {
self.alpha = 1;
self.hidden = YES;
_delayedHideBlock = nil;
}];
});
dispatch_time_t delayTime =
dispatch_time(DISPATCH_TIME_NOW, (int64_t)(kSCNightModeButtonHiddenDelay * NSEC_PER_SEC));
dispatch_after(delayTime, dispatch_get_main_queue(), _delayedHideBlock);
} else {
self.hidden = YES;
}
}
- (BOOL)willHideAfterDelay
{
return _delayedHideBlock != nil;
}
#pragma mark - Private
- (void)_cancelDelayedHideAnimation
{
SC_GUARD_ELSE_RETURN(_delayedHideBlock);
dispatch_cancel(_delayedHideBlock);
_delayedHideBlock = nil;
}
@end

View File

@ -0,0 +1,26 @@
//
// SCFeatureScanning.h
// Snapchat
//
// Created by Xiaokang Liu on 2018/4/19.
//
#import "SCFeature.h"
@protocol SCFeatureScanning;
@protocol SCFeatureScanningDelegate <NSObject>
- (void)featureScanning:(id<SCFeatureScanning>)featureScanning didFinishWithResult:(NSObject *)resultObject;
@end
/**
This SCFeature allows the user to long press on the screen to scan a snapcode.
*/
@protocol SCFeatureScanning <SCFeature>
@property (nonatomic, weak) id<SCFeatureScanningDelegate> delegate;
@property (nonatomic, assign) NSTimeInterval lastSuccessfulScanTime;
- (void)startScanning;
- (void)stopScanning;
- (void)stopSearch;
@end

View File

@ -0,0 +1,23 @@
//
// SCFeatureShazam.h
// SCCamera
//
// Created by Xiaokang Liu on 2018/4/18.
//
#import "SCFeature.h"
@class SCLens;
@protocol SCFeatureShazam;
@protocol SCFeatureShazamDelegate <NSObject>
- (void)featureShazam:(id<SCFeatureShazam>)featureShazam didFinishWithResult:(NSObject *)result;
- (void)featureShazamDidSubmitSearchRequest:(id<SCFeatureShazam>)featureShazam;
- (SCLens *)filterLensForFeatureShazam:(id<SCFeatureShazam>)featureShazam;
@end
@protocol SCFeatureShazam <SCFeature>
@property (nonatomic, weak) id<SCFeatureShazamDelegate> delegate;
- (void)stopAudioRecordingAsynchronously;
- (void)resetInfo;
@end

View File

@ -0,0 +1,14 @@
//
// SCFeatureSnapKit.h
// SCCamera
//
// Created by Michel Loenngren on 3/19/18.
//
#import "SCFeature.h"
@class SCCameraDeepLinkMetadata;
@protocol SCFeatureSnapKit <SCFeature>
- (void)setDeepLinkMetadata:(SCCameraDeepLinkMetadata *)metadata;
@end

View File

@ -0,0 +1,17 @@
//
// SCFeatureTapToFocusAndExposure.h
// SCCamera
//
// Created by Michel Loenngren on 4/5/18.
//
#import "SCFeature.h"
/**
This SCFeature allows the user to tap on the screen to adjust focus and exposure.
*/
@protocol SCFeatureTapToFocusAndExposure <SCFeature>
- (void)reset;
@end

View File

@ -0,0 +1,49 @@
//
// SCFeatureTapToFocusAndExposureImpl.h
// SCCamera
//
// Created by Michel Loenngren on 4/5/18.
//
#import "SCFeatureTapToFocusAndExposure.h"
#import <SCBase/SCMacros.h>
#import <Foundation/Foundation.h>
@protocol SCCapturer;
/**
Protocol describing unique camera commands to run when the user taps on screen. These could be focus, exposure or tap
to portrait mode.
*/
@protocol SCFeatureCameraTapCommand <NSObject>
- (void)execute:(CGPoint)pointOfInterest capturer:(id<SCCapturer>)capturer;
@end
/**
This is the default implementation of SCFeatureTapToFocusAndExposure allowing the user to tap on the camera overlay
view in order to adjust focus and exposure.
*/
@interface SCFeatureTapToFocusAndExposureImpl : NSObject <SCFeatureTapToFocusAndExposure>
SC_INIT_AND_NEW_UNAVAILABLE
- (instancetype)initWithCapturer:(id<SCCapturer>)capturer commands:(NSArray<id<SCFeatureCameraTapCommand>> *)commands;
@end
/**
Adjust focus on tap.
*/
@interface SCFeatureCameraFocusTapCommand : NSObject <SCFeatureCameraTapCommand>
@end
/**
Adjust exposure on tap.
*/
@interface SCFeatureCameraExposureTapCommand : NSObject <SCFeatureCameraTapCommand>
@end
/**
Adjust portrait mode point of interest on tap.
*/
@interface SCFeatureCameraPortraitTapCommand : NSObject <SCFeatureCameraTapCommand>
@end

View File

@ -0,0 +1,118 @@
//
// SCFeatureTapToFocusImpl.m
// SCCamera
//
// Created by Michel Loenngren on 4/5/18.
//
#import "SCFeatureTapToFocusAndExposureImpl.h"
#import "SCCameraTweaks.h"
#import "SCCapturer.h"
#import "SCFeatureContainerView.h"
#import "SCTapAnimationView.h"
#import <SCFoundation/SCLog.h>
#import <SCFoundation/SCTraceODPCompatible.h>
@interface SCFeatureTapToFocusAndExposureImpl ()
@property (nonatomic, weak) id<SCCapturer> capturer;
@property (nonatomic, weak) UIView<SCFeatureContainerView> *containerView;
@property (nonatomic) BOOL userTappedToFocusAndExposure;
@property (nonatomic) NSArray<id<SCFeatureCameraTapCommand>> *commands;
@end
@implementation SCFeatureTapToFocusAndExposureImpl
- (instancetype)initWithCapturer:(id<SCCapturer>)capturer commands:(NSArray<id<SCFeatureCameraTapCommand>> *)commands
{
if (self = [super init]) {
_capturer = capturer;
_commands = commands;
}
return self;
}
- (void)reset
{
SC_GUARD_ELSE_RETURN(_userTappedToFocusAndExposure);
_userTappedToFocusAndExposure = NO;
[_capturer continuousAutofocusAndExposureAsynchronouslyWithCompletionHandler:nil context:SCCapturerContext];
}
#pragma mark - SCFeature
- (void)configureWithView:(UIView<SCFeatureContainerView> *)view
{
SCTraceODPCompatibleStart(2);
_containerView = view;
}
- (void)forwardCameraOverlayTapGesture:(UIGestureRecognizer *)gestureRecognizer
{
SCTraceODPCompatibleStart(2);
CGPoint point = [gestureRecognizer locationInView:gestureRecognizer.view];
@weakify(self);
[_capturer convertViewCoordinates:[gestureRecognizer locationInView:_containerView]
completionHandler:^(CGPoint pointOfInterest) {
@strongify(self);
SC_GUARD_ELSE_RETURN(self);
SCLogCameraFeatureInfo(@"Tapped to focus: %@", NSStringFromCGPoint(pointOfInterest));
[self _applyTapCommands:pointOfInterest];
[self _showTapAnimationAtPoint:point forGesture:gestureRecognizer];
}
context:SCCapturerContext];
}
#pragma mark - Private helpers
- (void)_applyTapCommands:(CGPoint)pointOfInterest
{
SCTraceODPCompatibleStart(2);
for (id<SCFeatureCameraTapCommand> command in _commands) {
[command execute:pointOfInterest capturer:_capturer];
}
self.userTappedToFocusAndExposure = YES;
}
- (void)_showTapAnimationAtPoint:(CGPoint)point forGesture:(UIGestureRecognizer *)gestureRecognizer
{
SCTraceODPCompatibleStart(2);
SC_GUARD_ELSE_RETURN([self.containerView isTapGestureRecognizer:gestureRecognizer])
SCTapAnimationView *tapAnimationView = [SCTapAnimationView tapAnimationView];
[_containerView addSubview:tapAnimationView];
tapAnimationView.center = point;
[tapAnimationView showWithCompletion:^(SCTapAnimationView *view) {
[view removeFromSuperview];
}];
}
@end
@implementation SCFeatureCameraFocusTapCommand
- (void)execute:(CGPoint)pointOfInterest capturer:(id<SCCapturer>)capturer
{
[capturer setAutofocusPointOfInterestAsynchronously:pointOfInterest
completionHandler:nil
context:SCCapturerContext];
}
@end
@implementation SCFeatureCameraExposureTapCommand
- (void)execute:(CGPoint)pointOfInterest capturer:(id<SCCapturer>)capturer
{
[capturer setExposurePointOfInterestAsynchronously:pointOfInterest
fromUser:YES
completionHandler:nil
context:SCCapturerContext];
}
@end
@implementation SCFeatureCameraPortraitTapCommand
- (void)execute:(CGPoint)pointOfInterest capturer:(id<SCCapturer>)capturer
{
[capturer setPortraitModePointOfInterestAsynchronously:pointOfInterest
completionHandler:nil
context:SCCapturerContext];
}
@end

View File

@ -0,0 +1,21 @@
//
// SCTapAnimationView.h
// SCCamera
//
// Created by Alexander Grytsiuk on 8/26/15.
// Copyright (c) 2015 Snapchat, Inc. All rights reserved.
//
#import <UIKit/UIKit.h>
@class SCTapAnimationView;
typedef void (^SCTapAnimationViewCompletion)(SCTapAnimationView *);
@interface SCTapAnimationView : UIView
+ (instancetype)tapAnimationView;
- (void)showWithCompletion:(SCTapAnimationViewCompletion)completion;
@end

View File

@ -0,0 +1,178 @@
//
// SCTapAnimationView.m
// SCCamera
//
// Created by Alexander Grytsiuk on 8/26/15.
// Copyright (c) 2015 Snapchat, Inc. All rights reserved.
//
#import "SCTapAnimationView.h"
#import <SCBase/SCMacros.h>
@import QuartzCore;
static const CGFloat kSCAnimationStep = 0.167;
static const CGFloat kSCInnerCirclePadding = 2.5;
static const CGFloat kSCTapAnimationViewWidth = 55;
static const CGFloat kSCOuterRingBorderWidth = 1;
static NSString *const kSCOpacityAnimationKey = @"opacity";
static NSString *const kSCScaleAnimationKey = @"scale";
@implementation SCTapAnimationView {
CALayer *_outerRing;
CALayer *_innerCircle;
}
#pragma mark Class Methods
+ (instancetype)tapAnimationView
{
return [[self alloc] initWithFrame:CGRectMake(0, 0, kSCTapAnimationViewWidth, kSCTapAnimationViewWidth)];
}
#pragma mark Life Cycle
- (instancetype)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
self.userInteractionEnabled = NO;
_outerRing = [CALayer layer];
_outerRing.backgroundColor = [UIColor clearColor].CGColor;
_outerRing.borderColor = [UIColor whiteColor].CGColor;
_outerRing.borderWidth = kSCOuterRingBorderWidth;
_outerRing.shadowColor = [UIColor blackColor].CGColor;
_outerRing.shadowOpacity = 0.4;
_outerRing.shadowOffset = CGSizeMake(0.5, 0.5);
_outerRing.opacity = 0.0;
_outerRing.frame = self.bounds;
_outerRing.cornerRadius = CGRectGetMidX(_outerRing.bounds);
[self.layer addSublayer:_outerRing];
_innerCircle = [CALayer layer];
_innerCircle.backgroundColor = [UIColor whiteColor].CGColor;
_innerCircle.opacity = 0.0;
_innerCircle.frame = CGRectInset(self.bounds, kSCInnerCirclePadding, kSCInnerCirclePadding);
_innerCircle.cornerRadius = CGRectGetMidX(_innerCircle.bounds);
[self.layer addSublayer:_innerCircle];
}
return self;
}
#pragma mark Public
- (void)showWithCompletion:(SCTapAnimationViewCompletion)completion
{
[_outerRing removeAllAnimations];
[_innerCircle removeAllAnimations];
[CATransaction begin];
[CATransaction setCompletionBlock:^{
if (completion) {
completion(self);
}
}];
[self addOuterRingOpacityAnimation];
[self addOuterRingScaleAnimation];
[self addInnerCircleOpacityAnimation];
[self addInnerCircleScaleAnimation];
[CATransaction commit];
}
#pragma mark Private
- (CAKeyframeAnimation *)keyFrameAnimationWithKeyPath:(NSString *)keyPath
duration:(CGFloat)duration
values:(NSArray *)values
keyTimes:(NSArray *)keyTimes
timingFunctions:(NSArray *)timingFunctions
{
CAKeyframeAnimation *keyframeAnimation = [CAKeyframeAnimation animationWithKeyPath:keyPath];
keyframeAnimation.duration = duration;
keyframeAnimation.values = values;
keyframeAnimation.keyTimes = keyTimes;
keyframeAnimation.timingFunctions = timingFunctions;
keyframeAnimation.fillMode = kCAFillModeForwards;
keyframeAnimation.removedOnCompletion = NO;
return keyframeAnimation;
}
- (CABasicAnimation *)animationWithKeyPath:(NSString *)keyPath
duration:(CGFloat)duration
fromValue:(NSValue *)fromValue
toValue:(NSValue *)toValue
timingFunction:(CAMediaTimingFunction *)timingFunction
{
CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:keyPath];
animation.duration = duration;
animation.fromValue = fromValue;
animation.toValue = toValue;
animation.timingFunction = timingFunction;
animation.fillMode = kCAFillModeForwards;
animation.removedOnCompletion = NO;
return animation;
}
- (void)addOuterRingOpacityAnimation
{
CAKeyframeAnimation *animation =
[self keyFrameAnimationWithKeyPath:@keypath(_outerRing, opacity)
duration:kSCAnimationStep * 5
values:@[ @0.0, @1.0, @1.0, @0.0 ]
keyTimes:@[ @0.0, @0.2, @0.8, @1.0 ]
timingFunctions:@[
[CAMediaTimingFunction functionWithControlPoints:0.0:0.0:0.0:1.0],
[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear],
[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut],
]];
[_outerRing addAnimation:animation forKey:kSCOpacityAnimationKey];
}
- (void)addOuterRingScaleAnimation
{
CAKeyframeAnimation *animation =
[self keyFrameAnimationWithKeyPath:@keypath(_innerCircle, transform)
duration:kSCAnimationStep * 3
values:@[
[NSValue valueWithCATransform3D:CATransform3DMakeScale(0.50, 0.50, 1.0)],
[NSValue valueWithCATransform3D:CATransform3DIdentity],
[NSValue valueWithCATransform3D:CATransform3DMakeScale(0.83, 0.83, 1.0)],
]
keyTimes:@[ @0.0, @0.66, @1.0 ]
timingFunctions:@[
[CAMediaTimingFunction functionWithControlPoints:0.0:0.0:0.0:1.0],
[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut],
]];
[_outerRing addAnimation:animation forKey:kSCScaleAnimationKey];
}
- (void)addInnerCircleOpacityAnimation
{
CAKeyframeAnimation *animation =
[self keyFrameAnimationWithKeyPath:@keypath(_innerCircle, opacity)
duration:kSCAnimationStep * 3
values:@[ @0.0, @0.40, @0.0 ]
keyTimes:@[ @0.0, @0.33, @1.0 ]
timingFunctions:@[
[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseIn],
[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut],
]];
[_innerCircle addAnimation:animation forKey:kSCOpacityAnimationKey];
}
- (void)addInnerCircleScaleAnimation
{
CABasicAnimation *animation =
[self animationWithKeyPath:@keypath(_innerCircle, transform)
duration:kSCAnimationStep * 2
fromValue:[NSValue valueWithCATransform3D:CATransform3DMakeScale(0.0, 0.0, 1.0)]
toValue:[NSValue valueWithCATransform3D:CATransform3DIdentity]
timingFunction:[CAMediaTimingFunction functionWithControlPoints:0.0:0.0:0.0:1.0]];
[_innerCircle addAnimation:animation forKey:kSCScaleAnimationKey];
}
@end

View File

@ -0,0 +1,37 @@
//
// SCFeatureToggleCamera.h
// SCCamera
//
// Created by Michel Loenngren on 4/17/18.
//
#import <SCCamera/SCFeature.h>
#import <SCCameraFoundation/SCManagedCaptureDevicePosition.h>
@protocol SCCapturer
, SCFeatureToggleCamera, SCLensCameraScreenDataProviderProtocol;
@protocol SCFeatureToggleCameraDelegate <NSObject>
- (void)featureToggleCamera:(id<SCFeatureToggleCamera>)feature
willToggleToDevicePosition:(SCManagedCaptureDevicePosition)devicePosition;
- (void)featureToggleCamera:(id<SCFeatureToggleCamera>)feature
didToggleToDevicePosition:(SCManagedCaptureDevicePosition)devicePosition;
@end
/**
SCFeature protocol for toggling the camera.
*/
@protocol SCFeatureToggleCamera <SCFeature>
@property (nonatomic, weak) id<SCFeatureToggleCameraDelegate> delegate;
- (void)toggleCameraWithRecording:(BOOL)isRecording
takingPicture:(BOOL)isTakingPicture
lensDataProvider:(id<SCLensCameraScreenDataProviderProtocol>)lensDataProvider
completion:(void (^)(BOOL success))completion;
- (void)reset;
@end

View File

@ -0,0 +1,34 @@
//
// SCFeatureZooming.h
// SCCamera
//
// Created by Xiaokang Liu on 2018/4/19.
//
#import "SCFeature.h"
#import <SCCameraFoundation/SCManagedCaptureDevicePosition.h>
#import <SCSearch/SCSearchSnapZoomLevelProviding.h>
@class SCPreviewPresenter;
@protocol SCFeatureZooming;
@protocol SCFeatureZoomingDelegate <NSObject>
- (void)featureZoomingForceTouchedWhileRecording:(id<SCFeatureZooming>)featureZooming;
- (BOOL)featureZoomingIsInitiatedRecording:(id<SCFeatureZooming>)featureZooming;
@end
@protocol SCFeatureZooming <SCFeature, SCSearchSnapZoomLevelProviding>
@property (nonatomic, weak) id<SCFeatureZoomingDelegate> delegate;
@property (nonatomic, weak) SCPreviewPresenter *previewPresenter;
- (void)resetOffset;
- (void)resetScale;
- (void)cancelPreview;
- (void)flipOffset;
- (void)resetBeginningScale;
- (void)toggleCameraForReset:(SCManagedCaptureDevicePosition)devicePosition;
- (void)recordCurrentZoomStateForReset;
@end

View File

@ -0,0 +1,66 @@
//
// SCCoreCameraLogger.h
// Snapchat
//
// Created by Chao Pang on 3/6/18.
//
#import <Foundation/Foundation.h>
/**
* CAMERA_CREATION_DELAY event
*/
extern NSString *const kSCCameraCreationDelayEventStartTimeKey;
extern NSString *const kSCCameraCreationDelayEventStartTimeAdjustmentKey;
extern NSString *const kSCCameraCreationDelayEventEndTimeKey;
extern NSString *const kSCCameraCreationDelayEventCaptureSessionIdKey;
extern NSString *const kSCCameraCreationDelayEventFilterLensIdKey;
extern NSString *const kSCCameraCreationDelayEventNightModeDetectedKey;
extern NSString *const kSCCameraCreationDelayEventNightModeActiveKey;
extern NSString *const kSCCameraCreationDelayEventCameraApiKey;
extern NSString *const kSCCameraCreationDelayEventCameraLevelKey;
extern NSString *const kSCCameraCreationDelayEventCameraPositionKey;
extern NSString *const kSCCameraCreationDelayEventCameraOpenSourceKey;
extern NSString *const kSCCameraCreationDelayEventContentDurationKey;
extern NSString *const kSCCameraCreationDelayEventMediaTypeKey;
extern NSString *const kSCCameraCreationDelayEventStartTypeKey;
extern NSString *const kSCCameraCreationDelayEventStartSubTypeKey;
extern NSString *const kSCCameraCreationDelayEventAnalyticsVersion;
@interface SCCoreCameraLogger : NSObject
+ (instancetype)sharedInstance;
/**
* CAMERA_CREATION_DELAY event
*/
- (void)logCameraCreationDelayEventStartWithCaptureSessionId:(NSString *)captureSessionId
filterLensId:(NSString *)filterLensId
underLowLightCondition:(BOOL)underLowLightCondition
isNightModeActive:(BOOL)isNightModeActive
isBackCamera:(BOOL)isBackCamera
isMainCamera:(BOOL)isMainCamera;
- (void)logCameraCreationDelaySplitPointRecordingGestureFinished;
- (void)logCameraCreationDelaySplitPointStillImageCaptureApi:(NSString *)api;
- (void)logCameraCreationDelaySplitPointPreCaptureOperationRequested;
- (void)logCameraCreationDelaySplitPointPreCaptureOperationFinishedAt:(CFTimeInterval)time;
- (void)updatedCameraCreationDelayWithContentDuration:(CFTimeInterval)duration;
- (void)logCameraCreationDelaySplitPointCameraCaptureContentReady;
- (void)logCameraCreationDelaySplitPointPreviewFinishedPreparation;
- (void)logCameraCreationDelaySplitPointPreviewDisplayedForImage:(BOOL)isImage;
- (void)logCameraCreationDelaySplitPointPreviewAnimationComplete:(BOOL)isImage;
- (void)logCameraCreationDelaySplitPointPreviewFirstFramePlayed:(BOOL)isImage;
- (void)cancelCameraCreationDelayEvent;
@end

View File

@ -0,0 +1,304 @@
//
// SCCoreCameraLogger.m
// Snapchat
//
// Created by Chao Pang on 3/6/18.
//
#import "SCCoreCameraLogger.h"
#import <BlizzardSchema/SCAEvents.h>
#import <SCFoundation/SCQueuePerformer.h>
#import <SCGhostToSnappable/SCGhostToSnappableSignal.h>
#import <SCLogger/SCCameraMetrics+CameraCreationDelay.h>
static const char *kSCCoreCameraLoggerQueueLabel = "com.snapchat.core-camera-logger-queue";
NSString *const kSCCameraCreationDelayEventStartTimeKey = @"start_time";
NSString *const kSCCameraCreationDelayEventStartTimeAdjustmentKey = @"start_time_adjustment";
NSString *const kSCCameraCreationDelayEventEndTimeKey = @"end_time";
NSString *const kSCCameraCreationDelayEventCaptureSessionIdKey = @"capture_session_id";
NSString *const kSCCameraCreationDelayEventFilterLensIdKey = @"filter_lens_id";
NSString *const kSCCameraCreationDelayEventNightModeDetectedKey = @"night_mode_detected";
NSString *const kSCCameraCreationDelayEventNightModeActiveKey = @"night_mode_active";
NSString *const kSCCameraCreationDelayEventCameraApiKey = @"camera_api";
NSString *const kSCCameraCreationDelayEventCameraLevelKey = @"camera_level";
NSString *const kSCCameraCreationDelayEventCameraPositionKey = @"camera_position";
NSString *const kSCCameraCreationDelayEventCameraOpenSourceKey = @"camera_open_source";
NSString *const kSCCameraCreationDelayEventContentDurationKey = @"content_duration";
NSString *const kSCCameraCreationDelayEventMediaTypeKey = @"media_type";
NSString *const kSCCameraCreationDelayEventStartTypeKey = @"start_type";
NSString *const kSCCameraCreationDelayEventStartSubTypeKey = @"start_sub_type";
NSString *const kSCCameraCreationDelayEventAnalyticsVersion = @"ios_v1";
static inline NSUInteger SCTimeToMS(CFTimeInterval time)
{
return (NSUInteger)(time * 1000);
}
static NSString *SCDictionaryToJSONString(NSDictionary *dictionary)
{
NSData *dictData = [NSJSONSerialization dataWithJSONObject:dictionary options:0 error:nil];
return [[NSString alloc] initWithData:dictData encoding:NSUTF8StringEncoding];
}
@implementation SCCoreCameraLogger {
SCQueuePerformer *_performer;
NSMutableDictionary *_cameraCreationDelayParameters;
NSMutableDictionary *_cameraCreationDelaySplits;
}
- (instancetype)init
{
self = [super init];
if (self) {
_cameraCreationDelayParameters = [NSMutableDictionary dictionary];
_cameraCreationDelaySplits = [NSMutableDictionary dictionary];
_performer = [[SCQueuePerformer alloc] initWithLabel:kSCCoreCameraLoggerQueueLabel
qualityOfService:QOS_CLASS_UNSPECIFIED
queueType:DISPATCH_QUEUE_SERIAL
context:SCQueuePerformerContextCoreCamera];
}
return self;
}
+ (instancetype)sharedInstance
{
static SCCoreCameraLogger *sharedInstance;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedInstance = [[SCCoreCameraLogger alloc] init];
});
return sharedInstance;
}
// Camera creation delay metrics
- (void)logCameraCreationDelayEventStartWithCaptureSessionId:(NSString *)captureSessionId
filterLensId:(NSString *)filterLensId
underLowLightCondition:(BOOL)underLowLightCondition
isNightModeActive:(BOOL)isNightModeActive
isBackCamera:(BOOL)isBackCamera
isMainCamera:(BOOL)isMainCamera
{
CFTimeInterval startTime = CACurrentMediaTime();
[_performer perform:^{
[_cameraCreationDelayParameters removeAllObjects];
[_cameraCreationDelaySplits removeAllObjects];
_cameraCreationDelayParameters[kSCCameraCreationDelayEventStartTimeKey] = @(startTime);
_cameraCreationDelayParameters[kSCCameraCreationDelayEventCaptureSessionIdKey] = captureSessionId ?: @"null";
_cameraCreationDelayParameters[kSCCameraCreationDelayEventFilterLensIdKey] = filterLensId ?: @"null";
_cameraCreationDelayParameters[kSCCameraCreationDelayEventNightModeDetectedKey] = @(underLowLightCondition);
_cameraCreationDelayParameters[kSCCameraCreationDelayEventNightModeActiveKey] = @(isNightModeActive);
_cameraCreationDelayParameters[kSCCameraCreationDelayEventCameraPositionKey] =
isBackCamera ? @"back" : @"front";
_cameraCreationDelayParameters[kSCCameraCreationDelayEventCameraOpenSourceKey] =
isMainCamera ? @"main_camera" : @"reply_camera";
_cameraCreationDelayParameters[kSCCameraCreationDelayEventStartTypeKey] = SCLaunchType() ?: @"null";
_cameraCreationDelayParameters[kSCCameraCreationDelayEventStartSubTypeKey] = SCLaunchSubType() ?: @"null";
}];
}
- (void)logCameraCreationDelaySplitPointRecordingGestureFinished
{
CFTimeInterval time = CACurrentMediaTime();
[_performer perform:^{
CFTimeInterval endRecordingTimeOffset =
time - [_cameraCreationDelayParameters[kSCCameraCreationDelayEventStartTimeKey] doubleValue];
NSNumber *recordStartTimeMillis =
(NSNumber *)_cameraCreationDelaySplits[kSCCameraSubmetricsPreCaptureOperationFinished];
if (recordStartTimeMillis) {
CFTimeInterval timeDisplacement = ([recordStartTimeMillis doubleValue] / 1000.0) - endRecordingTimeOffset;
_cameraCreationDelayParameters[kSCCameraCreationDelayEventStartTimeAdjustmentKey] = @(timeDisplacement);
}
[self _addSplitPointForKey:kSCCameraSubmetricsRecordingGestureFinished atTime:time];
}];
}
- (void)logCameraCreationDelaySplitPointStillImageCaptureApi:(NSString *)api
{
CFTimeInterval time = CACurrentMediaTime();
[_performer perform:^{
if (api) {
_cameraCreationDelayParameters[kSCCameraCreationDelayEventCameraApiKey] = api;
}
[self _addSplitPointForKey:kSCCameraSubmetricsPreCaptureOperationRequested atTime:time];
}];
}
- (void)logCameraCreationDelaySplitPointPreCaptureOperationRequested
{
CFTimeInterval time = CACurrentMediaTime();
[_performer perform:^{
[self _addSplitPointForKey:kSCCameraSubmetricsPreCaptureOperationRequested atTime:time];
}];
}
- (void)logCameraCreationDelaySplitPointPreCaptureOperationFinishedAt:(CFTimeInterval)time
{
[_performer perform:^{
[self _addSplitPointForKey:kSCCameraSubmetricsPreCaptureOperationFinished atTime:time];
}];
}
- (void)updatedCameraCreationDelayWithContentDuration:(CFTimeInterval)duration
{
[_performer perform:^{
_cameraCreationDelayParameters[kSCCameraCreationDelayEventContentDurationKey] = @(SCTimeToMS(duration));
}];
}
- (void)logCameraCreationDelaySplitPointCameraCaptureContentReady
{
CFTimeInterval time = CACurrentMediaTime();
[_performer perform:^{
[self _addSplitPointForKey:kSCCameraSubmetricsCameraCaptureContentReady atTime:time];
}];
}
- (void)logCameraCreationDelaySplitPointPreviewFinishedPreparation
{
CFTimeInterval time = CACurrentMediaTime();
[_performer perform:^{
[self _addSplitPointForKey:kSCCameraSubmetricsCameraCaptureContentReady atTime:time];
}];
}
- (void)logCameraCreationDelaySplitPointPreviewDisplayedForImage:(BOOL)isImage
{
CFTimeInterval time = CACurrentMediaTime();
[_performer perform:^{
[self _addSplitPointForKey:kSCCameraSubmetricsPreviewLayoutReady atTime:time];
}];
}
- (void)logCameraCreationDelaySplitPointPreviewAnimationComplete:(BOOL)isImage
{
CFTimeInterval time = CACurrentMediaTime();
[_performer perform:^{
[self _addSplitPointForKey:kSCCameraSubmetricsPreviewAnimationFinish atTime:time];
if (_cameraCreationDelaySplits[kSCCameraSubmetricsPreviewPlayerReady]) {
[self _completeLogCameraCreationDelayEventWithIsImage:isImage atTime:time];
}
}];
}
- (void)logCameraCreationDelaySplitPointPreviewFirstFramePlayed:(BOOL)isImage
{
CFTimeInterval time = CACurrentMediaTime();
[_performer perform:^{
[self _addSplitPointForKey:kSCCameraSubmetricsPreviewPlayerReady atTime:time];
if (_cameraCreationDelaySplits[kSCCameraSubmetricsPreviewAnimationFinish]) {
[self _completeLogCameraCreationDelayEventWithIsImage:isImage atTime:time];
}
}];
}
- (void)cancelCameraCreationDelayEvent
{
[_performer perform:^{
[_cameraCreationDelayParameters removeAllObjects];
[_cameraCreationDelaySplits removeAllObjects];
}];
}
#pragma - Private methods
- (void)_completeLogCameraCreationDelayEventWithIsImage:(BOOL)isImage atTime:(CFTimeInterval)time
{
SCAssertPerformer(_performer);
if (_cameraCreationDelayParameters[kSCCameraCreationDelayEventCaptureSessionIdKey]) {
_cameraCreationDelayParameters[kSCCameraCreationDelayEventMediaTypeKey] = isImage ? @"image" : @"video";
_cameraCreationDelayParameters[kSCCameraCreationDelayEventEndTimeKey] = @(time);
[self _logCameraCreationDelayBlizzardEvent];
}
[_cameraCreationDelayParameters removeAllObjects];
[_cameraCreationDelaySplits removeAllObjects];
}
- (void)_addSplitPointForKey:(NSString *)key atTime:(CFTimeInterval)time
{
SCAssertPerformer(_performer);
if (key) {
CFTimeInterval timeOffset =
time - [_cameraCreationDelayParameters[kSCCameraCreationDelayEventStartTimeKey] doubleValue];
NSNumber *timeAdjustment =
_cameraCreationDelayParameters[kSCCameraCreationDelayEventStartTimeAdjustmentKey] ?: @(0);
_cameraCreationDelaySplits[key] = @(SCTimeToMS(timeOffset + [timeAdjustment doubleValue]));
}
}
- (void)_logCameraCreationDelayBlizzardEvent
{
SCAssertPerformer(_performer);
SCASharedCameraMetricParams *sharedCameraMetricsParams = [[SCASharedCameraMetricParams alloc] init];
[sharedCameraMetricsParams setAnalyticsVersion:kSCCameraCreationDelayEventAnalyticsVersion];
NSString *mediaType = _cameraCreationDelayParameters[kSCCameraCreationDelayEventMediaTypeKey];
if (mediaType) {
if ([mediaType isEqualToString:@"image"]) {
[sharedCameraMetricsParams setMediaType:SCAMediaType_IMAGE];
} else if ([mediaType isEqualToString:@"video"]) {
[sharedCameraMetricsParams setMediaType:SCAMediaType_VIDEO];
}
}
if (_cameraCreationDelayParameters[kSCCameraCreationDelayEventNightModeDetectedKey] &&
_cameraCreationDelayParameters[kSCCameraCreationDelayEventNightModeActiveKey]) {
BOOL isNightModeDetected =
[_cameraCreationDelayParameters[kSCCameraCreationDelayEventNightModeDetectedKey] boolValue];
BOOL isNightModeActive =
[_cameraCreationDelayParameters[kSCCameraCreationDelayEventNightModeActiveKey] boolValue];
if (!isNightModeDetected) {
[sharedCameraMetricsParams setLowLightStatus:SCALowLightStatus_NOT_DETECTED];
} else if (!isNightModeActive) {
[sharedCameraMetricsParams setLowLightStatus:SCALowLightStatus_DETECTED];
} else if (isNightModeActive) {
[sharedCameraMetricsParams setLowLightStatus:SCALowLightStatus_ENABLED];
}
}
[sharedCameraMetricsParams setPowerMode:[[NSProcessInfo processInfo] isLowPowerModeEnabled]
? @"LOW_POWER_MODE_ENABLED"
: @"LOW_POWER_MODE_DISABLED"];
[sharedCameraMetricsParams
setFilterLensId:_cameraCreationDelayParameters[kSCCameraCreationDelayEventFilterLensIdKey] ?: @"null"];
[sharedCameraMetricsParams
setCaptureSessionId:_cameraCreationDelayParameters[kSCCameraCreationDelayEventCaptureSessionIdKey] ?: @"null"];
[sharedCameraMetricsParams
setCameraApi:_cameraCreationDelayParameters[kSCCameraCreationDelayEventCameraApiKey] ?: @"null"];
[sharedCameraMetricsParams
setCameraPosition:_cameraCreationDelayParameters[kSCCameraCreationDelayEventCameraPositionKey] ?: @"null"];
[sharedCameraMetricsParams
setCameraOpenSource:_cameraCreationDelayParameters[kSCCameraCreationDelayEventCameraOpenSourceKey] ?: @"null"];
[sharedCameraMetricsParams
setCameraLevel:_cameraCreationDelayParameters[kSCCameraCreationDelayEventCameraLevelKey] ?: @"null"];
[sharedCameraMetricsParams
setStartType:_cameraCreationDelayParameters[kSCCameraCreationDelayEventStartTypeKey] ?: @"null"];
[sharedCameraMetricsParams
setStartSubType:_cameraCreationDelayParameters[kSCCameraCreationDelayEventStartSubTypeKey] ?: @"null"];
[sharedCameraMetricsParams setSplits:SCDictionaryToJSONString(_cameraCreationDelaySplits)];
SCACameraSnapCreateDelay *creationDelay = [[SCACameraSnapCreateDelay alloc] init];
if (_cameraCreationDelayParameters[kSCCameraCreationDelayEventStartTimeKey] &&
_cameraCreationDelayParameters[kSCCameraCreationDelayEventEndTimeKey]) {
double startTime = [_cameraCreationDelayParameters[kSCCameraCreationDelayEventStartTimeKey] doubleValue];
double endTime = [_cameraCreationDelayParameters[kSCCameraCreationDelayEventEndTimeKey] doubleValue];
NSNumber *timeAdjustment =
_cameraCreationDelayParameters[kSCCameraCreationDelayEventStartTimeAdjustmentKey] ?: @(0);
[creationDelay setLatencyMillis:SCTimeToMS(endTime - startTime + [timeAdjustment doubleValue])];
} else {
[creationDelay setLatencyMillis:0];
}
if (_cameraCreationDelayParameters[kSCCameraCreationDelayEventContentDurationKey]) {
[creationDelay
setContentDurationMillis:SCTimeToMS(
[_cameraCreationDelayParameters[kSCCameraCreationDelayEventContentDurationKey]
doubleValue])];
} else {
[creationDelay setContentDurationMillis:0];
}
[creationDelay setSharedCameraMetricParams:sharedCameraMetricsParams];
[[SCLogger sharedInstance] logUserTrackedEvent:creationDelay];
}
@end

53
Logging/SCLogger+Camera.h Normal file
View File

@ -0,0 +1,53 @@
//
// SCLogger+Camera.h
// Snapchat
//
// Created by Derek Peirce on 5/8/17.
// Copyright © 2017 Snapchat, Inc. All rights reserved.
//
#import "AVCameraViewEnums.h"
#import <SCBase/SCSignPost.h>
#import <SCLogger/SCLogger.h>
#import <CoreMedia/CoreMedia.h>
typedef NS_ENUM(NSUInteger, CameraCreationDelayLoggingStatus) {
CAMERA_CREATION_DELAY_LOGGING_START,
CAMERA_CREATION_DELAY_LOGGINT_LAST_STEP,
CAMERA_CREATION_DELAY_LOGGING_END,
};
@interface SCLogger (Camera)
@property (nonatomic, strong) NSNumber *cameraCreationDelayLoggingStatus;
- (void)logCameraCreationStartWithMethod:(SCCameraRecordingMethod)method
lensesEnabled:(BOOL)lensesEnabled
activeLensId:(NSString *)activeLensId
captureSessionId:(NSString *)captureSessionId;
- (void)logStillImageCaptureApi:(NSString *)api;
- (void)logPreCaptureOperationRequestedAt:(CFTimeInterval)requestTime;
- (void)logPreCaptureOperationFinishedAt:(CFTimeInterval)time;
- (void)logCameraCaptureRecordingGestureFinishedAtTime:(CFTimeInterval)endRecordingTime;
- (void)logCameraCaptureFinishedWithDuration:(CFTimeInterval)duration;
- (void)logCameraCaptureContentReady;
- (void)logPreviewFinishedPreparation;
- (void)logPreviewDisplayedForImage:(BOOL)isImage;
- (void)logPreviewAnimationComplete:(BOOL)isImage;
- (void)logPreviewFirstFramePlayed:(BOOL)isImage;
- (void)cancelCameraCreationEvent;
- (void)logRecordingMayBeTooShortWithMethod:(SCCameraRecordingMethod)method;
- (void)logRecordingWasTooShortWithFirstFrame:(CMTime)firstFrame
frontFacingCamera:(BOOL)isFrontFacing
cameraFlips:(NSInteger)cameraFlips;
- (void)logManagedCapturerSettingFailure:(NSString *)settingTask error:(NSError *)error;
- (void)logCameraExposureAdjustmentDelayStart;
- (void)logCameraExposureAdjustmentDelayEndWithStrategy:(NSString *)strategy;
- (void)logCameraCreationDelaySubMetricsStartWithSignCode:(kSCSignPostCodeEnum)signPostCode;
- (void)logCameraCreationDelaySubMetricsEndWithSignCode:(kSCSignPostCodeEnum)signPostCod;
@end

303
Logging/SCLogger+Camera.m Normal file
View File

@ -0,0 +1,303 @@
//
// SCLogger+Camera.m
// Snapchat
//
// Created by Derek Peirce on 5/8/17.
// Copyright © 2017 Snapchat, Inc. All rights reserved.
//
#import "SCLogger+Camera.h"
#import "SCCameraTweaks.h"
#import <SCFoundation/SCTrace.h>
#import <SCFoundation/SCTraceODPCompatible.h>
#import <SCLogger/SCCameraMetrics+CameraCreationDelay.h>
#import <SCLogger/SCCameraMetrics.h>
#import <SCLogger/SCLogger+Performance.h>
#import <objc/runtime.h>
@implementation SCLogger (Camera)
@dynamic cameraCreationDelayLoggingStatus;
- (NSNumber *)cameraCreationDelayLoggingStatus
{
return objc_getAssociatedObject(self, @selector(cameraCreationDelayLoggingStatus));
}
- (void)setCameraCreationDelayLoggingStatus:(NSNumber *)status
{
objc_setAssociatedObject(self, @selector(cameraCreationDelayLoggingStatus), status,
OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
- (BOOL)shouldLogCameraCreationDelay
{
return [[self cameraCreationDelayLoggingStatus] intValue] != CAMERA_CREATION_DELAY_LOGGING_END;
}
- (void)logCameraCreationDelayEnd
{
if ([[self cameraCreationDelayLoggingStatus] intValue] == CAMERA_CREATION_DELAY_LOGGINT_LAST_STEP) {
SCTraceSignPostEndForMetrics(kSCSignPostCameraCreationDelay, 0, 0, 0, 0);
[self setCameraCreationDelayLoggingStatus:@(CAMERA_CREATION_DELAY_LOGGING_END)];
} else {
[self setCameraCreationDelayLoggingStatus:@(CAMERA_CREATION_DELAY_LOGGINT_LAST_STEP)];
}
}
- (void)logCameraCreationStartWithMethod:(SCCameraRecordingMethod)method
lensesEnabled:(BOOL)lensesEnabled
activeLensId:(NSString *)activeLensId
captureSessionId:(NSString *)captureSessionId
{
NSMutableDictionary *parameters = [@{
@"lens_ui_enabled" : @(lensesEnabled),
@"analytics_version" : kSCCameraDelayEventVersion,
@"method" : @(method),
} mutableCopy];
if (lensesEnabled && activeLensId) {
[parameters setObject:activeLensId forKey:@"lens_id"];
}
if (captureSessionId) {
[parameters setObject:captureSessionId forKey:@"capture_session_id"];
}
[self setCameraCreationDelayLoggingStatus:@(CAMERA_CREATION_DELAY_LOGGING_START)];
[self logCameraCreationDelaySubMetricsStartWithSignCode:kSCSignPostCameraCreationDelay];
[self logCameraCreationDelaySubMetricsStartWithSignCode:kSCSignPostCameraRecordingGestureFinished];
[self logCameraCreationDelaySubMetricsStartWithSignCode:kSCSignPostCameraPreCaptureOperationRequested];
[[SCLogger sharedInstance] logTimedEventStart:kSCCameraCaptureDelayEvent
uniqueId:@""
isUniqueEvent:NO
parameters:parameters
shouldLogStartTime:YES];
}
- (void)logCameraExposureAdjustmentDelayStart
{
[[SCLogger sharedInstance] logTimedEventStart:kSCCameraExposureAdjustmentDelay
uniqueId:@""
isUniqueEvent:NO
parameters:nil
shouldLogStartTime:YES];
}
- (void)logCameraExposureAdjustmentDelayEndWithStrategy:(NSString *)strategy
{
[[SCLogger sharedInstance] logTimedEventEnd:kSCCameraExposureAdjustmentDelay
uniqueId:@""
parameters:@{
@"strategy" : strategy
}];
}
- (void)logCameraCaptureRecordingGestureFinishedAtTime:(CFTimeInterval)endRecordingTime
{
[self logCameraCreationDelaySubMetricsEndWithSignCode:kSCSignPostCameraRecordingGestureFinished];
[[SCLogger sharedInstance]
updateLogTimedEvent:kSCCameraCaptureDelayEvent
uniqueId:@""
update:^(NSMutableDictionary *startParameters) {
NSMutableDictionary *eventParameters =
startParameters[SCPerformanceMetricsKey.kSCLoggerStartEventParametersKey];
NSNumber *recordStartTime =
(NSNumber *)eventParameters[kSCCameraSubmetricsPreCaptureOperationFinished];
CFTimeInterval endRecordingTimeOffset =
endRecordingTime -
[startParameters[SCPerformanceMetricsKey.kSCLoggerStartEventTimeKey] doubleValue];
if (recordStartTime) {
CFTimeInterval timeDisplacement =
([recordStartTime doubleValue] / 1000.0) - endRecordingTimeOffset;
[eventParameters setObject:@(timeDisplacement)
forKey:SCPerformanceMetricsKey.kSCLoggerStartEventTimeAdjustmentKey];
}
[self addSplitPoint:kSCCameraSubmetricsRecordingGestureFinished
atTime:endRecordingTime
toEvent:startParameters];
}];
}
- (void)logStillImageCaptureApi:(NSString *)api
{
[self logCameraCreationDelaySubMetricsEndWithSignCode:kSCSignPostCameraPreCaptureOperationRequested];
[self logCameraCreationDelaySubMetricsStartWithSignCode:kSCSignPostCameraPreCaptureOperationFinished];
[self logCameraCreationDelaySubMetricsStartWithSignCode:kSCSignPostCameraCaptureContentReady];
CFTimeInterval requestTime = CACurrentMediaTime();
[self updateLogTimedEvent:kSCCameraCaptureDelayEvent
uniqueId:@""
update:^(NSMutableDictionary *startParameters) {
NSMutableDictionary *eventParameters =
startParameters[SCPerformanceMetricsKey.kSCLoggerStartEventParametersKey];
[eventParameters setObject:api forKey:@"api_type"];
[eventParameters setObject:@(1) forKey:@"camera_api_level"];
[self addSplitPoint:@"PRE_CAPTURE_OPERATION_REQUESTED"
atTime:requestTime
toEvent:startParameters];
}];
}
- (void)logPreCaptureOperationRequestedAt:(CFTimeInterval)requestTime
{
[self logCameraCreationDelaySubMetricsEndWithSignCode:kSCSignPostCameraPreCaptureOperationRequested];
[self logCameraCreationDelaySubMetricsStartWithSignCode:kSCSignPostCameraPreCaptureOperationFinished];
[self logCameraCreationDelaySubMetricsStartWithSignCode:kSCSignPostCameraCaptureContentReady];
[self updateLogTimedEvent:kSCCameraCaptureDelayEvent
uniqueId:@""
splitPoint:kSCCameraSubmetricsPreCaptureOperationRequested
time:requestTime];
}
- (void)logPreCaptureOperationFinishedAt:(CFTimeInterval)time
{
[self logCameraCreationDelaySubMetricsEndWithSignCode:kSCSignPostCameraPreCaptureOperationFinished];
[self logCameraCreationDelaySubMetricsStartWithSignCode:kSCSignPostCameraPreviewPlayerReady];
[self updateLogTimedEvent:kSCCameraCaptureDelayEvent
uniqueId:@""
splitPoint:kSCCameraSubmetricsPreCaptureOperationFinished
time:time];
}
- (void)logCameraCaptureFinishedWithDuration:(CFTimeInterval)duration
{
[[SCLogger sharedInstance]
updateLogTimedEvent:kSCCameraCaptureDelayEvent
uniqueId:@""
update:^(NSMutableDictionary *startParameters) {
NSMutableDictionary *eventParameters =
startParameters[SCPerformanceMetricsKey.kSCLoggerStartEventParametersKey];
[eventParameters setObject:@(SCTimeInMillisecond(duration)) forKey:@"content_duration"];
}];
}
- (void)logCameraCaptureContentReady
{
[self logCameraCreationDelaySubMetricsEndWithSignCode:kSCSignPostCameraCaptureContentReady];
[[SCLogger sharedInstance] updateLogTimedEvent:kSCCameraCaptureDelayEvent
uniqueId:@""
splitPoint:kSCCameraSubmetricsCameraCaptureContentReady];
}
- (void)logPreviewFinishedPreparation
{
[self logCameraCreationDelaySubMetricsEndWithSignCode:kSCSignPostCameraPreviewFinishPreparation];
[self logCameraCreationDelaySubMetricsStartWithSignCode:kSCSignPostCameraPreviewAnimationFinish];
[self updateLogTimedEvent:kSCCameraCaptureDelayEvent
uniqueId:@""
splitPoint:kSCCameraSubmetricsPreviewFinishPreparation];
}
- (void)logPreviewDisplayedForImage:(BOOL)isImage
{
[self logCameraCreationDelaySubMetricsEndWithSignCode:kSCSignPostCameraPreviewLayoutReady];
[self updateLogTimedEvent:kSCCameraCaptureDelayEvent uniqueId:@"" splitPoint:kSCCameraSubmetricsPreviewLayoutReady];
}
- (void)logPreviewAnimationComplete:(BOOL)isImage
{
[self updateLogTimedEvent:kSCCameraCaptureDelayEvent
uniqueId:@""
splitPoint:kSCCameraSubmetricsPreviewAnimationFinish];
[self logCameraCreationDelaySubMetricsEndWithSignCode:kSCSignPostCameraPreviewAnimationFinish];
[self logCameraCreationDelayEnd];
[self conditionallyLogTimedEventEnd:kSCCameraCaptureDelayEvent
uniqueId:@""
parameters:@{
@"type" : isImage ? @"image" : @"video",
}
shouldLog:^BOOL(NSDictionary *startParameters) {
// For video, PREVIEW_PLAYER_READY and PREVIEW_ANIMATION_FINISH can happen in either
// order. So here we check for existence of this key, and end timer if the other
// event have happened.
NSMutableDictionary *eventParameters =
startParameters[SCPerformanceMetricsKey.kSCLoggerStartEventParametersKey];
return eventParameters[kSCCameraSubmetricsPreviewPlayerReady] != nil;
}];
}
- (void)logPreviewFirstFramePlayed:(BOOL)isImage
{
[self updateLogTimedEvent:kSCCameraCaptureDelayEvent uniqueId:@"" splitPoint:kSCCameraSubmetricsPreviewPlayerReady];
[self logCameraCreationDelaySubMetricsEndWithSignCode:kSCSignPostCameraPreviewPlayerReady];
[self logCameraCreationDelayEnd];
[self conditionallyLogTimedEventEnd:kSCCameraCaptureDelayEvent
uniqueId:@""
parameters:@{
@"type" : isImage ? @"image" : @"video",
}
shouldLog:^BOOL(NSDictionary *startParameters) {
NSMutableDictionary *eventParameters =
startParameters[SCPerformanceMetricsKey.kSCLoggerStartEventParametersKey];
// See the comment above for PREVIEW_PLAYER_READY and PREVIEW_ANIMATION_FINISH.
return eventParameters[kSCCameraSubmetricsPreviewAnimationFinish] != nil;
}];
}
- (void)cancelCameraCreationEvent
{
[self cancelLogTimedEvent:kSCCameraCaptureDelayEvent uniqueId:@""];
}
- (void)logRecordingMayBeTooShortWithMethod:(SCCameraRecordingMethod)method
{
[[SCLogger sharedInstance] cancelLogTimedEvent:kSCCameraMetricsRecordingTooShort uniqueId:@""];
[[SCLogger sharedInstance] logTimedEventStart:kSCCameraMetricsRecordingTooShort
uniqueId:@""
isUniqueEvent:NO
parameters:@{
@"method" : @(method),
@"analytics_version" : kSCCameraRecordingTooShortVersion,
}
shouldLogStartTime:YES];
}
- (void)logRecordingWasTooShortWithFirstFrame:(CMTime)firstFrame
frontFacingCamera:(BOOL)isFrontFacing
cameraFlips:(NSInteger)cameraFlips
{
[self logTimedEventEnd:kSCCameraMetricsRecordingTooShort
uniqueId:@""
update:^(NSDictionary *startParameters, CFTimeInterval eventEndTime, CFTimeInterval adjustedTime) {
NSMutableDictionary *eventParameters =
startParameters[SCPerformanceMetricsKey.kSCLoggerStartEventParametersKey];
if (CMTIME_IS_VALID(firstFrame)) {
CFTimeInterval startTime =
[startParameters[SCPerformanceMetricsKey.kSCLoggerStartEventTimeKey] doubleValue];
CFTimeInterval firstFrameRelative = CMTimeGetSeconds(firstFrame) - startTime;
[eventParameters setObject:@(firstFrameRelative) forKey:@"first_frame_s"];
}
[eventParameters setObject:@(isFrontFacing) forKey:@"is_front_facing"];
if (cameraFlips) {
[eventParameters setObject:@(cameraFlips > 0) forKey:@"has_camera_been_flipped"];
}
}];
}
- (void)logManagedCapturerSettingFailure:(NSString *)settingTask error:(NSError *)error
{
NSMutableDictionary *parameters = [[NSMutableDictionary alloc] init];
parameters[@"setting_task"] = settingTask;
if (error) {
parameters[@"setting error"] = error;
}
[[SCLogger sharedInstance] logTimedEventEnd:kSCCameraManagedCaptureSettingFailure
uniqueId:@""
parameters:parameters];
}
- (void)logCameraCreationDelaySubMetricsStartWithSignCode:(kSCSignPostCodeEnum)signPostCode
{
if ([self shouldLogCameraCreationDelay]) {
SCTraceSignPostStartForMetrics(signPostCode, 0, 0, 0, 0);
}
}
- (void)logCameraCreationDelaySubMetricsEndWithSignCode:(kSCSignPostCodeEnum)signPostCode
{
if ([self shouldLogCameraCreationDelay]) {
SCTraceSignPostEndForMetrics(signPostCode, 0, 0, 0, 0);
}
}
@end

View File

@ -0,0 +1,24 @@
//
// SCManiphestTicketCreator.h
// SCCamera
//
// Created by Michel Loenngren on 4/16/18.
//
#import <Foundation/Foundation.h>
/**
Protocol for filing jira tickets and beta s2r.
*/
@protocol SCManiphestTicketCreator
- (void)createAndFile:(NSData *)image
creationTime:(long)reportCreationTime
description:(NSString *)bugDescription
email:(NSString *)otherEmail
project:(NSString *)projectName
subproject:(NSString *)subprojectName;
- (void)createAndFileBetaReport:(NSString *)msg;
@end