Add files via upload
This commit is contained in:
parent
47c0ba2719
commit
ebbdffde33
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
Loading…
Reference in New Issue