UNPKG

@nstudio/nativescript-camera-plus

Version:

An advanced, embeddable camera for NativeScript.

1,097 lines 72 kB
/********************************************************************************** * (c) 2017, nStudio, LLC & LiveShopper, LLC * * Version 1.1.0 team@nstudio.io **********************************************************************************/ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; return c > 3 && r && Object.defineProperty(target, key, r), r; }; var __metadata = (this && this.__metadata) || function (k, v) { if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); }; import { Color, Device, File, ImageAsset, knownFolders, path, Utils, View } from '@nativescript/core'; import { CameraPlusBase, CameraVideoQuality, CLog, GetSetProperty, CameraLens as CLens } from './common'; export * from './common'; export { CameraVideoQuality, WhiteBalance } from './common'; var QBImagePickerControllerDelegateImpl = /** @class */ (function (_super) { __extends(QBImagePickerControllerDelegateImpl, _super); function QBImagePickerControllerDelegateImpl() { return _super !== null && _super.apply(this, arguments) || this; } QBImagePickerControllerDelegateImpl.new = function () { return _super.new.call(this); }; QBImagePickerControllerDelegateImpl.prototype.initWithCallback = function (owner, callback) { this._owner = owner; this._callback = callback; return this; }; QBImagePickerControllerDelegateImpl.prototype.initWithCallbackAndOptions = function (owner, callback, options) { this._owner = owner; this._callback = callback; if (options) { this._width = options.width; this._height = options.height; this._keepAspectRatio = Utils.isNullOrUndefined(options.keepAspectRatio) ? true : options.keepAspectRatio; } else { this._keepAspectRatio = true; // always default to true } return this; }; // create date from a string with format yyyy:MM:dd HH:mm:ss (like the format used in image description) QBImagePickerControllerDelegateImpl.prototype.createDateFromString = function (value) { var year = parseInt(value.substr(0, 4)); var month = parseInt(value.substr(5, 2)); var date = parseInt(value.substr(8, 2)); var hour = parseInt(value.substr(11, 2)); var minutes = parseInt(value.substr(14, 2)); var seconds = parseInt(value.substr(17, 2)); return new Date(year, month - 1, date, hour, minutes, seconds); }; QBImagePickerControllerDelegateImpl.prototype.qb_imagePickerControllerDidFinishPickingAssets = function (picker, assets) { var _this = this; var selection = []; var manager = PHImageManager.defaultManager(); // let scale = UIScreen.mainScreen.scale; var targetSize = PHImageManagerMaximumSize; var requestOptions = PHImageRequestOptions.alloc().init(); requestOptions.resizeMode = PHImageRequestOptionsResizeMode.Exact; requestOptions.synchronous = false; requestOptions.deliveryMode = PHImageRequestOptionsDeliveryMode.HighQualityFormat; requestOptions.normalizedCropRect = CGRectMake(0, 0, 1, 1); requestOptions.version = PHImageRequestOptionsVersion.Original; requestOptions.networkAccessAllowed = true; var cnt = 0; var next = function () { cnt++; if (cnt === assets.count) { this._callback(selection); this._owner.get().closePicker(); } else { requestImg(cnt); } }; var requestImg = function (i) { // Do something with the asset var asset = assets.objectAtIndex(i); if (asset.mediaType === PHAssetMediaType.Image) { // ios >= 13 added this api to get the original image if (manager.requestImageDataAndOrientationForAssetOptionsResultHandler) { manager.requestImageDataAndOrientationForAssetOptionsResultHandler(asset, requestOptions, function (imageData, dataUti, orientation, info) { var image = new UIImage({ data: imageData }); var imageAsset = new ImageAsset(image); if (_this._width) imageAsset.options.width = _this._width; if (_this._height) imageAsset.options.height = _this._height; imageAsset.options.keepAspectRatio = _this._keepAspectRatio; selection.push(imageAsset); next.call(_this); }); } else { manager.requestImageForAssetTargetSizeContentModeOptionsResultHandler(asset, targetSize, PHImageContentMode.AspectFill, requestOptions, function (image, info) { var imageAsset = new ImageAsset(image); if (_this._width) imageAsset.options.width = _this._width; if (_this._height) imageAsset.options.height = _this._height; imageAsset.options.keepAspectRatio = _this._keepAspectRatio; selection.push(imageAsset); next.call(_this); }); } } else if (asset.mediaType === PHAssetMediaType.Video) { var requestOptions_1 = PHVideoRequestOptions.alloc().init(); requestOptions_1.version = PHVideoRequestOptionsVersion.Original; requestOptions_1.networkAccessAllowed = true; manager.requestAVAssetForVideoOptionsResultHandler(asset, requestOptions_1, function (videoAsset, audioMix, info) { if (videoAsset.isKindOfClass(AVURLAsset.class())) { var docsPath = knownFolders.documents(); var pathParts = videoAsset.URL.toString().split(path.separator); var filename = pathParts[pathParts.length - 1]; var localFilePath = path.join(docsPath.path, 'camera-plus-videos', filename); var targetURL = NSURL.fileURLWithPath(localFilePath); if (File.exists(localFilePath)) { docsPath.getFile('camera-plus-videos/' + filename).remove(); } else { // make sure the folder exists, or else copyItemAtURLToURLError // will complain about it docsPath.getFolder('camera-plus-videos'); } // the video can be compied from gallery only when the request is open // so we move the video to the documents folder var result = NSFileManager.defaultManager.copyItemAtURLToURLError(videoAsset.URL, targetURL); if (result) { selection.push(localFilePath); } } next.call(_this); }); } }; requestImg(0); }; QBImagePickerControllerDelegateImpl.prototype.qb_imagePickerControllerDidCancel = function (picker) { this._owner.get().closePicker(); }; QBImagePickerControllerDelegateImpl.ObjCProtocols = [QBImagePickerControllerDelegate]; return QBImagePickerControllerDelegateImpl; }(NSObject)); var SwiftyDelegate = /** @class */ (function (_super) { __extends(SwiftyDelegate, _super); function SwiftyDelegate() { return _super !== null && _super.apply(this, arguments) || this; } SwiftyDelegate.initWithOwner = function (owner) { var delegate = SwiftyDelegate.new(); delegate._owner = owner; return delegate; }; SwiftyDelegate.prototype.swiftyCamDidFailToConfigure = function (swiftyCam) { CLog('swiftyCamDidFailToConfigure:'); }; SwiftyDelegate.prototype.swiftyCamDidFailToRecordVideo = function (swiftyCam, error) { CLog('swiftyCamDidFailToRecordVideo:'); }; SwiftyDelegate.prototype.swiftyCamNotAuthorized = function (swiftyCam) { CLog('swiftyCamNotAuthorized:'); }; SwiftyDelegate.prototype.swiftyCamSessionDidStopRunning = function (swiftyCam) { CLog('swiftyCamSessionDidStopRunning:'); }; SwiftyDelegate.prototype.swiftyCamSessionDidStartRunning = function (swiftyCam) { CLog('swiftyCamSessionDidStartRunning:'); this._owner.get().doLayout(); }; SwiftyDelegate.prototype.swiftyCamDidBeginRecordingVideo = function (swiftyCam, camera) { CLog('swiftyCamDidBeginRecordingVideo:', camera); this._owner.get().didStartRecording(camera); }; SwiftyDelegate.prototype.swiftyCamDidChangeZoomLevel = function (swiftyCam, zoom) { CLog('swiftyCamDidChangeZoomLevel:', zoom); }; SwiftyDelegate.prototype.swiftyCamDidFinishProcessVideoAt = function (swiftyCam, url) { CLog('swiftyCamDidFinishProcessVideoAt:', url); this._owner.get().recordingReady(url.path); }; SwiftyDelegate.prototype.swiftyCamDidFinishRecordingVideo = function (swiftyCam, camera) { CLog('swiftyCamDidFinishRecordingVideo:', camera); this._owner.get().didFinishRecording(camera); }; SwiftyDelegate.prototype.swiftyCamDidFocusAtPoint = function (swiftyCam, point) { CLog('swiftyCamDidFocusAtPoint:', point); }; SwiftyDelegate.prototype.swiftyCamDidSwitchCameras = function (swiftyCam, camera) { CLog('swiftyCamDidSwitchCameras:', camera); this._owner.get().didSwitchCamera(camera); }; SwiftyDelegate.prototype.swiftyCamDidTake = function (swiftyCam, photo) { CLog('swiftyCamDidTake:', photo); try { // UIImageWriteToSavedPhotosAlbum(photo, this._owner.get(), 'thisImage:hasBeenSavedInPhotoAlbumWithError:usingContextInfo:', null); this._owner.get().tookPhoto(photo); } catch (err) { CLog(err); } }; SwiftyDelegate.ObjCProtocols = [SwiftyCamViewControllerDelegate]; return SwiftyDelegate; }(NSObject)); const MySwifty = SwiftyCamViewController.extend({ cleanup() { this._swiftyDelegate = null; }, set enableVideo(value) { this._enableVideo = value; }, set pickerDelegate(value) { this._pickerDelegate = value; }, closePicker() { Utils.dispatchToMainThread(() => { rootVC().dismissViewControllerAnimatedCompletion(true, () => { this.pickerDelegate = null; }); }); }, viewDidLoad() { CLog('MySwifty viewdidload'); super.viewDidLoad(); const owner = this._owner && this._owner.get(); if (owner) { owner._updatePhotoQuality(); } this.view.userInteractionEnabled = true; const doubleTapEnabled = this._owner.get().doubleTapCameraSwitch; this.doubleTapCameraSwitch = doubleTapEnabled; CLog('doubleTapCameraSwitch:', doubleTapEnabled); // CLog('view.frame.size:', this.view.frame.size.width + 'x' + this.view.frame.size.height); // retain delegate in javascript to ensure garbage collector does not get it this._swiftyDelegate = SwiftyDelegate.initWithOwner(new WeakRef(this)); this.cameraDelegate = this._swiftyDelegate; CLog('this.cameraDelegate:', this.cameraDelegate); }, doLayout() { const size = this._owner.get().getActualSize(); const nativeView = this._owner.get().nativeView; const frame = nativeView.frame; nativeView.frame = CGRectMake(frame.origin.x, frame.origin.y, size.width, size.height); nativeView.setNeedsLayout(); }, viewDidLayoutSubviews() { CLog('MySwifty viewDidLayoutSubviews'); super.viewDidLayoutSubviews(); }, viewDidAppear(animated) { super.viewDidAppear(animated); CLog('MySwifty viewDidAppear'); }, viewWillAppear(animated) { super.viewWillAppear(animated); CLog('MySwifty viewWillAppear'); }, // public deviceDidRotate() { // super.deviceDidRotate(); // CLog('deviceDidRotate!'); // if (this.previewLayer && this.previewLayer.videoPreviewLayer) { // this.previewLayer.videoPreviewLayer.connection.videoOrientation = this.getPreviewLayerOrientation(); // } // } // public resize(width?: any, height?: any) { // if (typeof width !== 'number') { // width = screen.mainScreen.widthDIPs; // } // if (typeof height !== 'number') { // height = screen.mainScreen.heightDIPs; // } // CLog('resizing to:', width + 'x' + height); // this.view.frame = CGRectMake(0, 0, width, height); // CLog('view.bounds:', this.view.bounds.size.width + 'x' + this.view.bounds.size.height); // if (!this._resized) { // this._resized = true; // this._addButtons(); // this.viewDidAppear(true); // } // } snapPicture(options) { CLog('CameraPlus takePic options:', options); if (options) { this._snapPicOptions = options; } else { this._snapPicOptions = { confirm: this._owner.get().confirmPhotos, // from property setter confirmRetakeText: this._owner.get().confirmRetakeText, confirmSaveText: this._owner.get().confirmSaveText, saveToGallery: this._owner.get().saveToGallery, }; } this.takePhoto(); }, recordVideo(options) { options = options || {}; if (this._enableVideo) { if (this.isVideoRecording) { CLog('CameraPlus stop video recording.'); this.stopVideoRecording(); } else { CLog('CameraPlus record video options:', options); if (options) { this._videoOptions = options; } else { this._videoOptions = { confirm: this._owner.get().confirmVideo, // from property setter saveToGallery: this._owner.get().saveToGallery, }; } if (!this._videoOptions.disableHEVC && parseFloat(Device.sdkVersion) >= 11) { this.videoCodecType = AVVideoCodecTypeHEVC; } switch (this._videoOptions.quality) { case CameraVideoQuality.MAX_2160P: this.videoQuality = 7 /* VideoQuality.Resolution3840x2160 */; break; case CameraVideoQuality.MAX_1080P: this.videoQuality = 6 /* VideoQuality.Resolution1920x1080 */; break; case CameraVideoQuality.MAX_720P: this.videoQuality = 5 /* VideoQuality.Resolution1280x720 */; break; case CameraVideoQuality.HIGHEST: this.videoQuality = 0 /* VideoQuality.High */; break; case CameraVideoQuality.LOWEST: this.videoQuality = 2 /* VideoQuality.Low */; break; case CameraVideoQuality.QVGA: this.videoQuality = 3 /* VideoQuality.Resolution352x288 */; break; default: this.videoQuality = 4 /* VideoQuality.Resolution640x480 */; break; } const status = PHPhotoLibrary.authorizationStatus(); if (status === 0 /* PHAuthorizationStatus.NotDetermined */) { PHPhotoLibrary.requestAuthorization(() => { this.startVideoRecording(); }); } else { this.startVideoRecording(); } } } }, didStartRecording(camera) { this._owner.get().sendEvent(CameraPlus.videoRecordingStartedEvent, camera); }, recordingReady(recordingPath) { CLog(`recordingReady path: ${recordingPath}`); const configSaveToGallery = this._videoOptions.saveToGallery || this._owner.get().saveToGallery; if (configSaveToGallery) { CLog(`recordingReady saveToGallery ${configSaveToGallery}`); // TODO: discuss why callback handler(videoDidFinishSavingWithErrorContextInfo) does not emit event correctly - the path passed to the handler is the same as handled here so just go ahead and emit here for now this._owner.get().sendEvent(CameraPlus.videoRecordingReadyEvent, recordingPath); const status = PHPhotoLibrary.authorizationStatus(); if (status === 3 /* PHAuthorizationStatus.Authorized */) { UISaveVideoAtPathToSavedPhotosAlbum(recordingPath, this, 'videoDidFinishSavingWithErrorContextInfo', null); } } else { CLog(`video not saved to gallery but recording is at: ${recordingPath}`); this._owner.get().sendEvent(CameraPlus.videoRecordingReadyEvent, recordingPath); } }, didFinishRecording(camera) { this._owner.get().sendEvent(CameraPlus.videoRecordingFinishedEvent, camera); }, videoDidFinishSavingWithErrorContextInfo(vidPath, error, contextInfo) { if (error) { CLog('video save to camera roll error:'); CLog(error); return; } CLog(`video saved`, vidPath); // ideally could just rely on this, but this will not emit the event (commenting for now and instead doing above in recordready - TODO: discuss why) // this._owner.get().sendEvent(CameraPlus.videoRecordingReadyEvent, path); }, switchCam() { CLog('CameraPlus switchCam'); this.switchCamera(); }, toggleFlash() { this._flashEnabled = !this._flashEnabled; this.flashEnabled = this._flashEnabled; // super class behavior CLog('CameraPlus flash enabled:', this._flashEnabled); this._flashBtnHandler(); }, openGallery() { CLog('CameraPlus openGallery'); const width = this._owner.get().galleryPickerWidth; const height = this._owner.get().galleryPickerHeight; const keepAspectRatio = this._owner.get().keepAspectRatio; const showVideos = this._enableVideo; this.chooseFromLibrary({ width, height, keepAspectRatio, showVideos }); }, // public thisImageHasBeenSavedInPhotoAlbumWithErrorUsingContextInfo(image, error, context) { // CLog('thisImageHasBeenSavedInPhotoAlbumWithErrorUsingContextInfo', image); // if (error) { // CLog(error); // } // } tookPhoto(photo) { this._photoToSave = photo; CLog('tookPhoto!'); if (this._snapPicOptions && this._snapPicOptions.autoSquareCrop) { const width = photo.size.width; const height = photo.size.height; let originalWidth = width; let originalHeight = height; let x = 0; let y = 0; if (originalWidth < originalHeight) { x = (originalHeight - originalWidth) / 2; originalHeight = originalWidth; } else { y = (originalWidth - originalHeight) / 2; originalWidth = originalHeight; } const rect = CGRectMake(x, y, originalWidth, originalHeight); const ref = CGImageCreateWithImageInRect(photo.CGImage, rect); this._photoToSave = UIImage.imageWithCGImageScaleOrientation(ref, photo.scale, photo.imageOrientation); CGImageRelease(ref); } if (this._snapPicOptions && this._snapPicOptions.confirm) { // show the confirmation const safeAreaWidthOffset = this.view.safeAreaInsets ? this.view.safeAreaInsets.left + this.view.safeAreaInsets.right : 0; const safeAreaHeightOffset = this.view.safeAreaInsets ? this.view.safeAreaInsets.bottom + this.view.safeAreaInsets.top : 0; const safeAreaLeft = this.view.safeAreaInsets ? this.view.safeAreaInsets.left : 0; const safeAreaTop = this.view.safeAreaInsets ? this.view.safeAreaInsets.top : 0; const width = this.view.bounds.size.width - safeAreaWidthOffset; const height = this.view.bounds.size.height - safeAreaHeightOffset; this._imageConfirmBg = UIView.alloc().initWithFrame(CGRectMake(safeAreaLeft, safeAreaTop, width, height)); this._imageConfirmBg.backgroundColor = UIColor.blackColor; // confirm user wants to keep photo const imageConfirm = UIImageView.alloc().init(); imageConfirm.contentMode = 1 /* UIViewContentMode.ScaleAspectFit */; imageConfirm.image = this._photoToSave; imageConfirm.frame = CGRectMake(0, 50, width, height - 50); // add 'Retake' in bottom left and 'Save Photo' in bottom right const retakeBtn = createButton(this, CGRectMake(10, 10, 75, 50), this._snapPicOptions.confirmRetakeText ? this._snapPicOptions.confirmRetakeText : 'Retake', 'resetPreview'); const saveBtn = createButton(this, CGRectMake(width - 170, 10, 150, 50), this._snapPicOptions.confirmSaveText ? this._snapPicOptions.confirmSaveText : 'Save', 'savePhoto', 'right'); this._imageConfirmBg.addSubview(imageConfirm); this._imageConfirmBg.addSubview(retakeBtn); this._imageConfirmBg.addSubview(saveBtn); this.view.addSubview(this._imageConfirmBg); this._owner.get().sendEvent(CameraPlus.confirmScreenShownEvent); } else { // no confirmation - just save this.savePhoto(); return; } }, resetPreview() { if (this._imageConfirmBg) { this._imageConfirmBg.removeFromSuperview(); this._imageConfirmBg = null; this._owner.get().sendEvent(CameraPlus.confirmScreenDismissedEvent); } }, savePhoto() { if (this._photoToSave) { const asset = new ImageAsset(this._photoToSave); const useCameraOptions = this._snapPicOptions ? this._snapPicOptions.useCameraOptions : false; const handleSuccess = () => { this._owner.get().sendEvent(CameraPlus.photoCapturedEvent, asset); this.resetPreview(); }; if (!useCameraOptions) { if ((this._snapPicOptions && this._snapPicOptions.saveToGallery) || this._owner.get().saveToGallery) { UIImageWriteToSavedPhotosAlbum(this._photoToSave, null, null, null); } } if (this._snapPicOptions) { if (typeof this._snapPicOptions.keepAspectRatio === 'boolean') { asset.options.keepAspectRatio = this._snapPicOptions.keepAspectRatio; } if (typeof this._snapPicOptions.height === 'number') { asset.options.height = this._snapPicOptions.height; } if (typeof this._snapPicOptions.width === 'number') { asset.options.width = this._snapPicOptions.width; } } if (!useCameraOptions) { handleSuccess(); } else { if ((this._snapPicOptions && this._snapPicOptions.saveToGallery) || this._owner.get().saveToGallery) { asset.getImageAsync((image, error) => { if (image) { UIImageWriteToSavedPhotosAlbum(image, null, null, null); handleSuccess(); } else { CLog(`Failed to save image: ${error}`); } }); } } } }, didSwitchCamera(camera) { this._owner.get().sendEvent(CameraPlus.toggleCameraEvent, camera); }, isCameraAvailable() { return UIImagePickerController.isSourceTypeAvailable(1 /* UIImagePickerControllerSourceType.Camera */); }, chooseFromLibrary(options) { return new Promise((resolve, reject) => { this._pickerDelegate = null; const imagePickerController = QBImagePickerController.new(); let reqWidth = 0; let reqHeight = 0; let keepAspectRatio = true; if (options) { reqWidth = options.width || reqWidth; reqHeight = options.height || reqHeight; keepAspectRatio = Utils.isNullOrUndefined(options.keepAspectRatio) ? true : options.keepAspectRatio; } else { options = { showImages: true, showVideos: this._enableVideo, }; } const authStatus = PHPhotoLibrary.authorizationStatus(); if (reqWidth && reqHeight) { this._pickerDelegate = QBImagePickerControllerDelegateImpl.new().initWithCallbackAndOptions(new WeakRef(this), (result) => { CLog('chosen from library:', result); this._owner.get().sendEvent(CameraPlus.imagesSelectedEvent, result); resolve(result); }, { width: reqWidth, height: reqHeight, keepAspectRatio: keepAspectRatio }); } else { this._pickerDelegate = QBImagePickerControllerDelegateImpl.new().initWithCallback(new WeakRef(this), (result) => { CLog('chosen from library:', result); this._owner.get().sendEvent(CameraPlus.imagesSelectedEvent, result); resolve(result); }); } imagePickerController.delegate = this._pickerDelegate; const galleryPickerMode = this._owner.get().galleryPickerMode; CLog('galleryPickerMode:', galleryPickerMode); const galleryMax = this._owner.get().galleryMax; CLog('galleryMax:', galleryMax); imagePickerController.allowsMultipleSelection = galleryPickerMode === 'multiple'; imagePickerController.maximumNumberOfSelection = galleryMax; imagePickerController.showsNumberOfSelectedAssets = true; imagePickerController.modalPresentationStyle = 3 /* UIModalPresentationStyle.CurrentContext */; let mediaType = 0 /* QBImagePickerMediaType.Any */; if (options.showImages === undefined) { options.showImages = true; } if (!options.showImages && options.showVideos) { mediaType = 2 /* QBImagePickerMediaType.Video */; } else { if (options.showImages && !options.showVideos) { mediaType = 1 /* QBImagePickerMediaType.Image */; } } imagePickerController.mediaType = mediaType; rootVC().presentViewControllerAnimatedCompletion(imagePickerController, true, null); }); }, _addButtons() { CLog('adding buttons...'); const width = this.view.bounds.size.width; const height = this.view.bounds.size.height; if (this._owner.get().showToggleIcon) { CLog('adding toggle/switch camera button...'); const switchCameraBtn = createButton(this, CGRectMake(width - 100, 20, 100, 50), null, 'switchCam', null, createIcon('toggle', CGSizeMake(65, 50))); switchCameraBtn.transform = CGAffineTransformMakeScale(0.75, 0.75); this.view.addSubview(switchCameraBtn); } this._flashBtnHandler(); if (this._owner.get().showGalleryIcon === true) { CLog('adding show gallery button...'); const galleryBtn = createButton(this, CGRectMake(20, height - 80, 50, 50), null, 'openGallery', null, createIcon('gallery')); galleryBtn.transform = CGAffineTransformMakeScale(0.75, 0.75); this.view.addSubview(galleryBtn); } if (this._owner.get().showCaptureIcon) { CLog('adding show capture button...'); const heightOffset = this._owner.get().isIPhoneX ? 200 : 110; const picOutline = createButton(this, CGRectMake(width / 2 - 20, height - heightOffset, 50, 50), null, null, null, createIcon('picOutline')); picOutline.transform = CGAffineTransformMakeScale(1.5, 1.5); this.view.addSubview(picOutline); const takePicBtn = createButton(this, CGRectMake(width / 2 - 21.5, height - (heightOffset + 0.7), 50, 50), null, this._enableVideo ? 'recordVideo' : 'snapPicture', null, createIcon('takePic')); // takePicBtn.transform = CGAffineTransformMakeScale(1.5, 1.5); this.view.addSubview(takePicBtn); } }, _flashBtnHandler() { if (this._owner.get().showFlashIcon) { CLog('adding flash button...'); if (this._flashBtn) this._flashBtn.removeFromSuperview(); if (this.flashEnabled) { this._flashBtn = createButton(this, CGRectMake(20, 20, 50, 50), null, 'toggleFlash', null, createIcon('flash')); } else { this._flashBtn = createButton(this, CGRectMake(20, 20, 50, 50), null, 'toggleFlash', null, createIcon('flashOff')); } this._flashBtn.transform = CGAffineTransformMakeScale(0.75, 0.75); this.view.addSubview(this._flashBtn); } }, }, { exposedMethods: { switchCam: { returns: interop.types.void }, resetPreview: { returns: interop.types.void }, savePhoto: { returns: interop.types.void }, snapPicture: { returns: interop.types.void }, toggleFlash: { returns: interop.types.void }, openGallery: { returns: interop.types.void }, recordVideo: { returns: interop.types.void }, videoDidFinishSavingWithErrorContextInfo: { returns: interop.types.void, params: [NSString, NSError, interop.Pointer], }, }, }); MySwifty['initWithOwner'] = function (owner, defaultCamera = 'rear') { CLog('MySwifty initWithOwner'); // const ctrl: MySwifty = <MySwifty>MySwifty.alloc().init(); const ctrl = MySwifty.new(); CLog('view', ctrl); CLog('ctrl', ctrl); ctrl._owner = owner; // set default camera ctrl.defaultCamera = defaultCamera === 'rear' ? 0 /* CameraSelection.Rear */ : 1 /* CameraSelection.Front */; CLog('ctrl.disableAudio:', ctrl.disableAudio); CLog('ctrl.defaultCamera:', defaultCamera); return ctrl; }; export class CameraPlus extends CameraPlusBase { constructor() { super(); this.enableAudio = true; // library picker handling this._galleryMax = 3; this._pictureQuality = 'High'; CLog('CameraPlus constructor'); this._onLayoutChangeListener = this._onLayoutChangeFn.bind(this); this._swifty = MySwifty.initWithOwner(new WeakRef(this), CameraPlus.defaultCamera); this._swifty.shouldPrompToAppSettings = false; // experimenting with static flag (this is usually explicitly false) // enable device orientation this._swifty.shouldUseDeviceOrientation = CameraPlus.useDeviceOrientation; this._detectDevice(); } isVideoEnabled() { return this.enableVideo === true || CameraPlus.enableVideo; } isAudioEnabled() { return this.enableAudio === true || CameraPlus.enableAudio; } isWideAngleSupported() { if (Utils.ios.MajorVersion >= 13) { return true; } return false; } // @ts-ignore get defaultLens() { if (this._swifty) { switch (this._swifty.defaultLens) { case 0 /* CameraLens.Auto */: return CLens.Auto; case 1 /* CameraLens.Telephoto */: return CLens.TelePhoto; case 2 /* CameraLens.Wide */: return CLens.Wide; case 3 /* CameraLens.Ultrawide */: return CLens.UltraWide; } } return CLens.TelePhoto; } set defaultLens(value) { if (this._swifty) { switch (value) { case CLens.Auto: this._swifty.defaultLens = 0 /* CameraLens.Auto */; break; case CLens.TelePhoto: this._swifty.defaultLens = 1 /* CameraLens.Telephoto */; break; case CLens.Wide: this._swifty.defaultLens = 2 /* CameraLens.Wide */; break; case CLens.UltraWide: this._swifty.defaultLens = 3 /* CameraLens.Ultrawide */; break; } } } createNativeView() { // this._swifty.videoGravity = SwiftyCamVideoGravity.ResizeAspectFill; const videoEnabled = this.isVideoEnabled(); this._swifty.enableVideo = videoEnabled; if (!videoEnabled) { // disable audio if no video support this._swifty.disableAudio = !this.isVideoEnabled(); } else { this._swifty.disableAudio = !this.isAudioEnabled(); } CLog('CameraPlus createNativeView'); CLog('video enabled:', this.isVideoEnabled()); CLog('default camera:', CameraPlus.defaultCamera); CLog('view:', this._swifty.view); this._swifty.view.autoresizingMask = 2 /* UIViewAutoresizing.FlexibleWidth */ | 16 /* UIViewAutoresizing.FlexibleHeight */; return this._swifty.view; } get cropByPreview() { return this._cropByPreview; } set cropByPreview(value) { if (typeof value === 'string') { value = Boolean(value); } if (typeof value === 'boolean') { if (this._swifty) { this._swifty.cropByPreview = value; } } this._cropByPreview = value; } // @ts-ignore get pinchToZoom() { return this._swifty?.pinchToZoom ?? false; } set pinchToZoom(value) { if (this._swifty) { this._swifty.pinchToZoom = value; } } // @ts-ignore get tapToFocus() { return this._swifty?.tapToFocus ?? false; } set tapToFocus(value) { if (this._swifty) { this._swifty.tapToFocus = value; } } _updatePhotoQuality() { if (this._swifty) { switch (this._pictureQuality) { case '3840x2160': this._swifty.videoQuality = 7 /* VideoQuality.Resolution3840x2160 */; this._pictureQuality = '3840x2160'; break; case '1920x1080': this._swifty.videoQuality = 6 /* VideoQuality.Resolution1920x1080 */; this._pictureQuality = '1920x1080'; break; case '1280x720': this._swifty.videoQuality = 5 /* VideoQuality.Resolution1280x720 */; this._pictureQuality = '1280x720'; break; case '640x480': this._swifty.videoQuality = 4 /* VideoQuality.Resolution640x480 */; this._pictureQuality = '640x480'; break; case '352x288': this._swifty.videoQuality = 3 /* VideoQuality.Resolution352x288 */; this._pictureQuality = '352x288'; break; case 'Medium': this._swifty.videoQuality = 1 /* VideoQuality.Medium */; this._pictureQuality = 'Medium'; break; case 'Low': this._swifty.videoQuality = 2 /* VideoQuality.Low */; this._pictureQuality = 'Low'; break; case 'Photo': this._swifty.videoQuality = 10 /* VideoQuality.Photo */; this._pictureQuality = 'Photo'; break; default: this._swifty.videoQuality = 0 /* VideoQuality.High */; this._pictureQuality = 'High'; break; } } } getAvailablePictureSizes(ratio) { return ['3840x2160', '1920x1080', '1280x720', '640x480', '352x288', 'Photo', 'High', 'Medium', 'Low']; } // @ts-ignore set pictureSize(value) { this._pictureQuality = value; this._updatePhotoQuality(); } get pictureSize() { return this._pictureQuality; } _onLayoutChangeFn(args) { if (this._swifty && this._swifty.view) { const size = this.getActualSize(); CLog(`xml width/height: ${size.width}x${size.height}`); const frame = this._swifty.view.frame; this._swifty.view.frame = CGRectMake(frame.origin.x, frame.origin.y, size.width, size.height); if (this._swifty.previewLayer) { this._swifty.previewLayer.frame = CGRectMake(frame.origin.x, frame.origin.y, size.width, size.height); this._swifty.view.setNeedsLayout(); this._swifty.previewLayer.setNeedsLayout(); } } } initNativeView() { CLog('initNativeView.'); this.on(View.layoutChangedEvent, this._onLayoutChangeListener); this._updatePhotoQuality(); if (this.cropByPreview) { this._swifty.cropByPreview = this.cropByPreview; } this._swifty.viewWillAppear(true); } disposeNativeView() { CLog('disposeNativeView.'); this._swifty.cleanup(); this.off(View.layoutChangedEvent, this._onLayoutChangeListener); super.disposeNativeView(); } onLoaded() { super.onLoaded(); this._swifty._addButtons(); this._swifty.viewDidAppear(true); } onUnloaded() { this._swifty.viewDidDisappear(true); super.onUnloaded(); } get isIPhoneX() { return this._isIPhoneX; } get galleryPickerWidth() { return this._galleryPickerWidth; } set galleryPickerWidth(value) { this._galleryPickerWidth = value; } get galleryPickerHeight() { return this._galleryPickerHeight; } set galleryPickerHeight(value) { this._galleryPickerHeight = value; } get keepAspectRatio() { return this._keepAspectRatio; } set keepAspectRatio(value) { this._keepAspectRatio = value; } get galleryMax() { return this._galleryMax; } set galleryMax(value) { this._galleryMax = value; } /** * Toggle Camera front/back */ toggleCamera() { this._swifty.switchCam(); } /** * Toggle flash mode */ toggleFlash() { this._swifty.toggleFlash(); } /** * Return the current flash mode (either 'on' or 'off' for iOS) */ getFlashMode() { return this._swifty.flashEnabled ? 'on' : 'off'; } /** * Open library picker * @param options IChooseOptions */ chooseFromLibrary(options) { return this._swifty.chooseFromLibrary(options); } /** * Snap photo and display confirm save */ takePicture(options) { this._updatePhotoQuality(); this._swifty.snapPicture(options); } /** * Record video */ record(options) { if (this.isVideoEnabled()) { CLog(`Start Video Recording: ${options && JSON.stringify(options)}`); this._swifty.recordVideo(options); } return Promise.resolve(); } /** * Stop recording video */ stop() { if (this.isVideoEnabled()) { CLog('Stop Video Recording'); this._swifty.stopVideoRecording(); } } /** * Gets current camera selection */ getCurrentCamera() { const cam = this._swifty.currentCamera; if (cam === 1 /* CameraSelection.Front */) { return 'front'; } else { return 'rear'; } } /** * Is camera available for use */ isCameraAvailable() { return this._swifty.isCameraAvailable(); } _detectDevice() { if (typeof this._isIPhoneX === 'undefined') { /* tslint:disable-next-line: no-any */ let name = NSCCamplusHelpers.getDeviceIdentifier(); // Get machine name for Simulator if (name === 'x86_64' || name === 'i386' || name === 'arm64') { name = NSProcessInfo.processInfo.environment.objectForKey('SIMULATOR_MODEL_IDENTIFIER'); } // this._log.debug('isIPhoneX name:', name); const parts = name.toLowerCase().split('iphone'); if (parts && parts.length > 1) { const versionNumber = parseInt(parts[1]); if (!isNaN(versionNumber)) { // all above or greater than 11 are X devices this._isIPhoneX = versionNumber >= 11; } } if (!this._isIPhoneX) { // consider iphone x global and iphone x gsm this._isIPhoneX = name.indexOf('iPhone10,3') === 0 || name.indexOf('iPhone10,6') === 0; } } } } CameraPlus.useDeviceOrientation = false; // experimental __decorate([ GetSetProperty(), __metadata("design:type", Boolean) ], CameraPlus.prototype, "enableVideo", void 0); __decorate([ GetSetProperty(), __metadata("design:type", Boolean) ], CameraPlus.prototype, "enableAudio", void 0); const rootVC = function () { const appWindow = UIApplication.sharedApplication.keyWindow; return Utils.ios.getVisibleViewController(appWindow.rootViewController); }; const createButton = function (target, frame, label, eventName, align, img, imgSelected) { let btn; if (frame) { btn = UIButton.alloc().initWithFrame(frame); } else { btn = UIButton.alloc().init(); } if (label) { btn.setTitleForState(label, 0 /* UIControlState.Normal */); btn.setTitleColorForState(new Color('#fff').ios, 0 /* UIControlState.Normal */); btn.titleLabel.font = UIFont.systemFontOfSize(19); } else if (img) { btn.setImageForState(img, 0 /* UIControlState.Normal */); if (imgSelected) { btn.setImageForState(img, 1 /* UIControlState.Highlighted */); btn.setImageForState(img, 4 /* UIControlState.Selected */); } } if (align) { btn.contentHorizontalAlignment = align === 'right' ? 2 /* UIControlContentHorizontalAlignment.Right */ : 1 /* UIControlContentHorizontalAlignment.Left */; // if (align == 'right') { // btn.contentEdgeInsets = UIEdgeInsetsMake(0, 10, 0, 0); // } } if (eventName) { btn.addTargetActionForControlEvents(target, eventName, 64 /* UIControlEvents.TouchUpInside */); } return btn; }; const addShadow = function (button) { button.layer.shadowColor = UIColor.blackColor.CGColor; button.layer.shadowOffset = CGSizeMake(0, 0); button.layer.shadowRadius = 5; button.layer.shadowOpacity = 1; }; const createIcon = function (type, size, color) { switch (type) { case 'flash': UIGraphicsBeginImageContextWithOptions(size || CGSizeMake(50, 50), false, 0); drawFlash(color); break; case 'flashOff': UIGraphicsBeginImageContextWithOptions(size || CGSizeMake(50, 50), false, 0); drawFlashOff(color); break; case 'toggle': UIGraphicsBeginImageContextWithOptions(size || CGSizeMake(50, 50), false, 0); drawToggle(color); break; case 'picOutline': UIGraphicsBeginImageContextWithOptions(size || CGSizeMake(50, 50), false, 0); drawPicOutline(color); break; case 'takePic': UIGraphicsBeginImageContextWithOptions(size || CGSizeMake(50, 50), false, 0); drawCircle(color); break; case 'gallery': UIGraphicsBeginImageContextWithOptions(size || CGSizeMake(50, 50), false, 0); drawGallery(color); break; } const img = UIGraphicsGetImageFromCurrentImageContext(); UIGraphicsEndImageContext(); return img; }; const drawFlash = function (color) { const iconColor = new Color(color || '#fff').ios; //// Bezier Drawing const bezierPath = UIBezierPath.bezierPath(); bezierPath.moveToPoint(CGPointMake(23.17, 0.58)); bezierPath.addCurveToPointControlPoint1ControlPoint2(CGPointMake(11.19, 13.65), CGPointMake(22.79, 0.97), CGPointMake(17.38, 6.83)); bezierPath.addCurveToPointControlPoint1ControlPoint2(CGPointMake(0, 26.66), CGPointMake(3.2, 22.41), CGPointMake(-0.07, 26.24)); bezierPath.addCurveToPointControlPoint1ControlPoint2(CGPointMake(6.91, 27.44), CGPointMake(0.1, 27.26), CGPointMake(0.34, 27.29)); bezierPath.addCurveToPointControlPoint1ControlPoint2(CGPointMake(13.61, 28.15), CGPointMake(13.34, 27.58), CGPointMake(13.71, 27.61)); bezierPath.addCurveToPointControlPoint1ControlPoint2(CGPointMake(10.61, 37.07), CGPointMake(13.54, 28.45), CGPointMake(12.18, 32.46)); bezierPath.addCurveToPointControlPoint1ControlPoint2(CGPointMake(7.83, 45.92), CGPointMake(9.02, 41.64), CGPointMake(7.76, 45.62)); bezierPath.addCurveToPointControlPoint1ControlPoint2(CGPointMake(8.85, 46.43), CGPointMake(7.89, 46.25), CGPointMake(8.27, 46.43)); bezierPath.addCurveToPointControlPoint1ControlPoint2(CGPointMake(21.54, 33.48), CGPointMake(9.59, 46.43), CGPointMake(11.36, 44.63)); bezierPath.addCurveToPointControlPoint1ControlPoint2(CGPointMake(33.2, 19.87), CGPointMake(30.18, 23.97), CGPointMake(33.27, 20.35)); bezierPath.addCurveToPointControlPoint1ControlPoint2(CGPointMake(26.57, 19.12), CGPointMake(33.1, 19.21), CGPointMake(33, 19.21)); bezierPath.addCurveToPointControlPoint1ControlPoint2(CGPointMake(20, 18.67), CGPointMake(21.71, 19.06), CGPointMake(20, 18.94)); bezierPath.addCurveToPointControlPoint1ControlPoint2(CGPointMake(22.76, 9.88), CGPointMake(20, 18.49), CGPointMake(21.23, 14.52)); bezierPath.addCurveToPointControlPoint1ControlPoint2(CGPointMake(25.38, 0.73), CGPointMake(24.26, 5.21), CGPointMake(25.45, 1.12)); bezierPath.addCurveToPointControlPoint1ControlPoint2(CGPointMake(23.17, 0.58), CGPointMake(25.24, -0.17), CGPointMake(24.05, -0.26)); bezierPath.miterLimit = 4; bezierPath.closePath(); iconColor.setFill(); bezierPath.fill(); }; const drawFlashOff = function (color) { const iconColor = new Color(color || '#fff').ios; //// Bezier Drawing const bezierPath = UIBezierPath.bezierPath(); bezierPath.moveToPoint(CGPointMake(21.13, 4.5)); bezierPath.addCurveToPointControlPoint1ControlPoint2(CGPointMake(15.1, 12.28), CGPointMake(19.18, 7.01), CGPointMake(16.45, 10.51)); bezierPath.addLineToPoint(CGPointMake(12.66, 15.45)); bezierPath.addLineToPoint(CGPointMake(7.09, 9.64)); bezierPath.addCurveToPointControlPoint1ControlPoint2(CGPointMake(0.8, 3.9), CGPointMake(2.5, 4.82), CGPointMake(1.41, 3.84)); bezierPath.addCurveToPointControlPoint1ControlPoint2(CGPointMake(0, 4.73), CGPointMake(0.29, 3.96), CGPointMake(0.06, 4.2)); bezierPath.addCurveToPointControlPoint1ControlPoint2(CGPointMake(17.83, 24.13), CGPointMake(-0.06, 5.36), CGPointMake(2.7, 8.39)); bezierPath.addCurveToPointControlPoint1ControlPoint2(CGPointMake(36.44, 42.69), CGPointMake(32.87, 39.81), CGPointMake(35.86, 42.78)); bezierPath.addCurveToPointControlPoint1ControlPoint2(CGPointMake(37.21, 41.88), CGPointMake(36.89, 42.63), CGPointMake(37.15, 42.36)); bezierPath.addCurveToPointControlPoint1ControlPoint2(CGPointMake(31.64, 35.24), CGPointMake(37.3, 41.28), CGPointMake(36.29, 40.11)); bezierPath.addLineToPoint(CGPointMake(25.98, 29.31)); bezierPath.addLineToPoint(CGPointMake(29.34, 24.94)); bezierPath.addCurveToPointControlPoint1ControlPoint2(CGPointMake(32.62, 19.91), CGPointMake(31.76, 21.83), CGPointMake(32.67, 20.39)); bezierPath.addCurveToPointControlPoint1ControlPoint2(CGPointMake(27.02, 19.16), CGPointMake(32.53, 19.25), CGPointMake(32.44, 19.25)); bezierPath.addCurveToPointControlPoint1ControlPoint2(CGPointMake(21.48, 18.71), CGPointMake(22.91, 19.1), CGPointMake(21.48, 18.98)); bezierPath.addCurveToPointControlPoint1ControlPoint2(CGPointMake(23.8, 9.91), CGPointMake(21.48, 18.53), CGPointMake(22.51, 14.55)); bezierPath.addCurveToPointControlPoint1ControlPoint2(CGPointMake