// // QRCodeViewController.m // kneet // // Created by Jason Lee on 5/18/15. // Copyright (c) 2015 ntels. All rights reserved. // @import AVFoundation; #import "JDObject.h" #import "RequestHandler.h" #import "CustomTextField.h" #import "QRCodeInputPopupView.h" #import "UIDeviceUtil.h" #import "QRCodeViewController.h" #import "HomeHubInitViewController.h" #import "HomeHubChangeViewController.h" @interface QRCodeViewController () { CGRect _captureRect; QRCodeInputPopupView *_inputPopup; AVAuthorizationStatus _avAuthrizationStatus; } @property (nonatomic, strong) AVCaptureVideoPreviewLayer *previewLayer; @property (nonatomic, strong) CALayer *targetLayer; @property (nonatomic, strong) CAShapeLayer *guideLayer; @property (nonatomic, strong) AVCaptureSession *captureSession; @property (nonatomic, strong) NSMutableArray *codeObjects; @end #pragma mark - Class Definition @implementation QRCodeViewController - (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view. [self initUI]; [self prepareViewDidLoad]; } - (void)initUI { } - (void)prepareViewDidLoad { } - (void)viewDidAppear:(BOOL)animated { [super viewDidAppear:animated]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(applicationDidEnterBackground:) name:UIApplicationDidEnterBackgroundNotification object:nil]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(applicationWillEnterForeground:) name:UIApplicationWillEnterForegroundNotification object:nil]; [self startRunning]; } - (void)viewWillDisappear:(BOOL)animated { [super viewWillDisappear:animated]; [[NSNotificationCenter defaultCenter] removeObserver:self]; [self stopRunning]; } - (void)applicationDidEnterBackground:(NSNotification *)notification { [self stopRunning]; } - (void)applicationWillEnterForeground:(NSNotification *)notification { [self startRunning]; } #pragma mark - Main Logic - (NSMutableArray *)codeObjects { if (!_codeObjects) { _codeObjects = [NSMutableArray new]; } return _codeObjects; } - (void)requestQRAuthrize:(NSString *)activationCode password:(NSString *)password { //parameters NSDictionary *parameter = @{@"activation_code": activationCode, @"activation_password": password}; NSString *path = [NSString stringWithFormat:API_GET_PARTNER_QR]; [[RequestHandler handler] sendAsyncGetRequestAPIPath:path parameters:parameter modelClass:[QRAuthModel class] completion:^(id responseObject) { if (!responseObject) {//응답결과가 잘못되었거나 없을 경우, return; } QRAuthModel *qauth = (QRAuthModel *) responseObject; if (qauth) {//API 성공 , UIViewController *vc = nil; if (![qauth.ownerYn boolValue]) {//N : 장치의 소유자가 없음.(새 비밀번호 지정 필요) HomeHubInitViewController *tvc = (HomeHubInitViewController *)[CommonUtil instantiateViewControllerWithIdentifier:@"HomeHubInitViewController" storyboardName:@"HomeHub"]; tvc.activationCode = activationCode; tvc.activationPassword = password; vc = tvc; } else {//Y : 장치의 소유자가 있음.(이전 비밀번호 확인 필요) HomeHubChangeViewController *cvc = (HomeHubChangeViewController *)[CommonUtil instantiateViewControllerWithIdentifier:@"HomeHubChangeViewController" storyboardName:@"HomeHub"]; cvc.activationCode = activationCode; cvc.activationPassword = password; vc = cvc; } if (_inputPopup && _inputPopup.superview) { [_inputPopup hide]; } [self presentViewController:vc animated:YES completion:nil]; } } failure:^(id errorObject) { JDErrorModel *error = (JDErrorModel *)errorObject; [[JDFacade facade] toast:error.errorMessage]; }]; } #pragma mark - Main Logic - (AVCaptureSession *)captureSession { if (!_captureSession) { NSError *error = nil; AVCaptureDevice *device = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo]; if (device.isAutoFocusRangeRestrictionSupported) { if ([device lockForConfiguration:&error]) { [device setAutoFocusRangeRestriction:AVCaptureAutoFocusRangeRestrictionNear]; [device unlockForConfiguration]; } } // The first time AVCaptureDeviceInput creation will present a dialog to the user // requesting camera access. If the user refuses the creation fails. // See WWDC 2013 session #610 for details, but note this behaviour does not seem to // be enforced on iOS 7 where as it is with iOS 8. AVCaptureDeviceInput *deviceInput = [AVCaptureDeviceInput deviceInputWithDevice:device error:&error]; if (deviceInput) { _captureSession = [[AVCaptureSession alloc] init]; if ([_captureSession canAddInput:deviceInput]) { [_captureSession addInput:deviceInput]; } AVCaptureMetadataOutput *metadataOutput = [[AVCaptureMetadataOutput alloc] init]; if ([_captureSession canAddOutput:metadataOutput]) { [_captureSession addOutput:metadataOutput]; [metadataOutput setMetadataObjectsDelegate:self queue:dispatch_get_main_queue()]; [metadataOutput setMetadataObjectTypes:@[AVMetadataObjectTypeQRCode]]; } //카메라 프리뷰 레이어 self.previewLayer = [[AVCaptureVideoPreviewLayer alloc] initWithSession:_captureSession]; self.previewLayer.videoGravity = AVLayerVideoGravityResizeAspectFill; self.previewLayer.frame = self.view.bounds; [self.view.layer addSublayer:self.previewLayer]; //가이드 레이어 작성 UIImage *imgGuide = [UIImage imageNamed:@"tp_01_img_thingsadd_reg_02_QR_areapoint"]; CGFloat orix = (IPHONE_WIDTH - imgGuide.size.width) / 2; CGFloat oriy = (IPHONE_HEIGHT - _containerView.frame.size.height - imgGuide.size.height) / 2; _captureRect = CGRectMake(orix, oriy, imgGuide.size.width, imgGuide.size.height); self.guideLayer = [CAShapeLayer layer]; self.guideLayer.contents = (id)[imgGuide CGImage]; self.guideLayer.frame = _captureRect; // self.guideLayer.fillColor = nil; // self.guideLayer.strokeColor = [UIColor grayColor].CGColor; // self.guideLayer.lineWidth = 10.0f; [self.view.layer addSublayer:self.guideLayer]; [self.view addSubview:_containerView]; CGFloat width = [CommonUtil hardware] == IPHONE_6 ? 375 : 320; width = [CommonUtil hardware] == IPHONE_6_PLUS ? 414 : width; [_containerView mas_makeConstraints:^(MASConstraintMaker *make) { make.size.width.mas_equalTo(self.view.frame.size.width); make.bottom.equalTo(self.view.mas_baseline); }]; } else { NSLog(@"Input Device error: %@",[error localizedDescription]); [self showAlertForCameraError:error]; } } return _captureSession; } #pragma mark - AVCaptureMetadataOutputObjectsDelegate - (void)captureOutput:(AVCaptureOutput *)captureOutput didOutputMetadataObjects:(NSArray *)metadataObjects fromConnection:(AVCaptureConnection *)connection { self.codeObjects = nil; for (AVMetadataObject *metadataObject in metadataObjects) { AVMetadataMachineReadableCodeObject *transformed = (AVMetadataMachineReadableCodeObject *)[self.previewLayer transformedMetadataObjectForMetadataObject:metadataObject]; if ([transformed.type isEqualToString:AVMetadataObjectTypeQRCode]) { // Update the frame on the _boundingBox view, and show it CGRect mr = transformed.bounds; if (CGRectContainsRect(_captureRect, mr)) { NSLog(@"%@", transformed.stringValue); [self stopRunning]; [self gotoSetSecurityOfDevice:transformed.stringValue]; } } } } - (void)showAlertForCameraError:(NSError *)error { NSString *message = error.localizedFailureReason ? error.localizedFailureReason : error.localizedDescription; [[JDFacade facade] alert:message]; } - (void)startRunning { #if TARGET_IPHONE_SIMULATOR [self btnInsertTouched:nil]; return; #endif _avAuthrizationStatus = [AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeVideo]; if (_avAuthrizationStatus != AVAuthorizationStatusAuthorized) {//카메라 권한이 없을 경우, NSString *message = MSG_CAMERA_DISABLE; CustomAlertView *alert = [[CustomAlertView alloc] initWithTitle:@"알림" message:message delegate:nil OKButtonTitle:@"설정" cancelButtonTitle:@"취소"]; [alert showWithCompletion:^(CustomAlertView *alertView, NSInteger buttonIndex) { if (buttonIndex == 0) {//OK - 이전화면으로 NSURL *settingsURL = [NSURL URLWithString:UIApplicationOpenSettingsURLString]; [[UIApplication sharedApplication] openURL:settingsURL]; return; } else { [self btnInsertTouched:nil]; //직접 입력창을 띄움. } }]; } else {//권한이 있을 경우, self.codeObjects = nil; [self.captureSession startRunning]; // [AVCaptureDevice requestAccessForMediaType:AVMediaTypeVideo completionHandler:^(BOOL granted) { // if (granted) { // } // }]; } } - (void)stopRunning { #if TARGET_IPHONE_SIMULATOR return; #endif [self.captureSession stopRunning]; self.captureSession = nil; } - (void)clearTargetLayer { // NSArray *sublayers = [[self.targetLayer sublayers] copy]; // for (CALayer *sublayer in sublayers) // { // [sublayer removeFromSuperlayer]; // } } - (void)showDetectedObjects { for (AVMetadataObject *metadata in self.codeObjects) { if ([metadata.type isEqualToString:AVMetadataObjectTypeQRCode]) { AVMetadataMachineReadableCodeObject *transformed = (AVMetadataMachineReadableCodeObject *)[self.previewLayer transformedMetadataObjectForMetadataObject:metadata]; // Update the frame on the _boundingBox view, and show it CGRect mr = transformed.bounds; if (CGRectContainsRect(self.guideLayer.frame, mr)) { CAShapeLayer *shapeLayer = [CAShapeLayer layer]; // shapeLayer.strokeColor = [UIColor redColor].CGColor; // shapeLayer.fillColor = [UIColor clearColor].CGColor; // shapeLayer.lineWidth = 2.0; // shapeLayer.lineJoin = kCALineJoinRound; CGPathRef path = createPathForPoints([(AVMetadataMachineReadableCodeObject *)metadata corners]); shapeLayer.path = path; CGRect *pr = nil; if (CGPathIsRect(path, pr)) { NSLog(@"%@", NSStringFromCGRect(*pr)); NSLog(@"same"); } else { NSLog(@"not"); } } } } } CGMutablePathRef createPathForPoints(NSArray* points) { CGMutablePathRef path = CGPathCreateMutable(); CGPoint point; if ([points count] > 0) { CGPointMakeWithDictionaryRepresentation((CFDictionaryRef)[points objectAtIndex:0], &point); CGPathMoveToPoint(path, nil, point.x, point.y); int i = 1; while (i < [points count]) { CGPointMakeWithDictionaryRepresentation((CFDictionaryRef)[points objectAtIndex:i], &point); CGPathAddLineToPoint(path, nil, point.x, point.y); i++; } CGPathCloseSubpath(path); } return path; } - (void)gotoSetSecurityOfDevice:(NSString *)QRCode { NSArray *qrs = [QRCode componentsSeparatedByString:@"|"]; if (!qrs || qrs.count < 3 || ![qrs[1] isEqualToString:@"*_AC_*"]) {//니트 디바이스 [[JDFacade facade] alert:NSLocalizedString(@"서비스 지원장치가 아닙니다", @"서비스 지원장치가 아닙니다")]; return; } [self requestQRAuthrize:qrs[0] password:qrs[2]]; } #pragma mark - UI Events - (void)btnInsertTouched:(id)sender { if (!_inputPopup) { _inputPopup = [[QRCodeInputPopupView alloc] initFromNib]; } _inputPopup.qrcodeType = _qrcodeType; _inputPopup.txtActivationCode.text = ksEmptyString; [_inputPopup showWithCompletion:^(CustomAlertView *alertView, NSInteger buttonIndex) { if (buttonIndex == 0) {//OK [self requestQRAuthrize:_inputPopup.txtActivationCode.text password:_inputPopup.txtPasswd.text]; } else { if (_avAuthrizationStatus != AVAuthorizationStatusAuthorized) {//카메라 권한이 없을 경우, 리턴, [self btnCancelTouched:nil]; } } }]; } - (void)btnCancelTouched:(id)sender { [self dismissViewControllerAnimated:YES completion:nil]; } #pragma mark - MemoryWarning - (void)didReceiveMemoryWarning { [super didReceiveMemoryWarning]; // Dispose of any resources that can be recreated. } @end