UNPKG

@nstudio/nativescript-camera-plus

Version:

An advanced, embeddable camera for NativeScript.

253 lines 10.1 kB
/********************************************************************************** * (c) 2017, nStudio, LLC & LiveShopper, LLC * Licensed under a Commercial license. * * Version 1.1.0 team@nstudio.io **********************************************************************************/ import { ImageAsset, ImageSource, Utils } from '@nativescript/core'; export class SelectedAsset extends ImageAsset { constructor(uri) { super(SelectedAsset._calculateFileUri(uri)); this._uri = uri; this._thumbRequested = false; } data() { return Promise.reject(new Error('Not implemented.')); } getImage(options) { return new Promise((resolve, reject) => { try { resolve(this.decodeUri(this._uri, options)); } catch (ex) { reject(ex); } }); } getImageData() { return new Promise((resolve, reject) => { try { if (!this._data) { const bb = this.getByteBuffer(this._uri); this._data = ArrayBuffer.from(bb); } resolve(this._data); } catch (ex) { reject(ex); } }); } // [Deprecated. Please use thumbAsset instead.] get thumb() { if (!this._thumbRequested) { this.decodeThumbUri(); } return this._thumb; } get thumbAsset() { return this._thumbAsset; } setThumbAsset(value) { this._thumbAsset = value; this.notifyPropertyChange('thumbAsset', value); } get uri() { return this._uri.toString(); } get fileUri() { if (!this._fileUri) { this._fileUri = SelectedAsset._calculateFileUri(this._uri); } return this._fileUri; } static _calculateFileUri(uri) { const isKitKat = android.os.Build.VERSION.SDK_INT >= 19; // android.os.Build.VERSION_CODES.KITKAT if (isKitKat && android.provider.DocumentsContract && android.provider.DocumentsContract.isDocumentUri(Utils.android.getApplicationContext(), uri)) { // externalStorageProvider if (SelectedAsset.isExternalStorageDocument(uri)) { const docId = android.provider.DocumentsContract.getDocumentId(uri); const id = docId.split(':')[1]; const type = docId.split(':')[0]; if ('primary' === type.toLowerCase()) { return android.os.Environment.getExternalStorageDirectory() + '/' + id; } // tODO handle non-primary volumes } else if (SelectedAsset.isDownloadsDocument(uri)) { // downloadsProvider const id = android.provider.DocumentsContract.getDocumentId(uri); const contentUri = android.content.ContentUris.withAppendedId(android.net.Uri.parse('content://downloads/public_downloads'), long(id)); return SelectedAsset.getDataColumn(contentUri, null, null); } else if (SelectedAsset.isMediaDocument(uri)) { // mediaProvider const docId = android.provider.DocumentsContract.getDocumentId(uri); const split = docId.split(':'); const type = split[0]; const id = split[1]; let contentUri = null; if ('image' === type) { contentUri = android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI; } else if ('video' === type) { contentUri = android.provider.MediaStore.Video.Media.EXTERNAL_CONTENT_URI; } else if ('audio' === type) { contentUri = android.provider.MediaStore.Audio.Media.EXTERNAL_CONTENT_URI; } const selection = '_id=?'; const selectionArgs = [id]; return SelectedAsset.getDataColumn(contentUri, selection, selectionArgs); } } else { // mediaStore (and general) if ('content' === uri.getScheme()) { return SelectedAsset.getDataColumn(uri, null, null); } else if ('file' === uri.getScheme()) { // file return uri.getPath(); } } return undefined; } static getDataColumn(uri, selection, selectionArgs) { let cursor = null; const columns = [android.provider.MediaStore.MediaColumns.DATA]; let filePath; try { cursor = this.getContentResolver().query(uri, columns, selection, selectionArgs, null); if (cursor != null && cursor.moveToFirst()) { const column_index = cursor.getColumnIndexOrThrow(columns[0]); filePath = cursor.getString(column_index); if (filePath) { return filePath; } } } catch (e) { console.log(e); } finally { if (cursor) { cursor.close(); } } return undefined; } static isExternalStorageDocument(uri) { return 'com.android.externalstorage.documents' === uri.getAuthority(); } static isDownloadsDocument(uri) { return 'com.android.providers.downloads.documents' === uri.getAuthority(); } static isMediaDocument(uri) { return 'com.android.providers.media.documents' === uri.getAuthority(); } decodeThumbUri() { // decode image size const REQUIRED_SIZE = { maxWidth: 100, maxHeight: 100, }; // decode with scale this._thumb = this.decodeUri(this._uri, REQUIRED_SIZE); this.notifyPropertyChange('thumb', this._thumb); } decodeThumbAssetUri() { // decode image size const REQUIRED_SIZE = { maxWidth: 100, maxHeight: 100, }; // decode with scale this._thumbAsset = this.decodeUriForImageAsset(this._uri, REQUIRED_SIZE); this.notifyPropertyChange('thumbAsset', this._thumbAsset); } /** * Discovers the sample size that a BitmapFactory.Options object should have * to scale the retrieved image to the given max size. * @param uri The URI of the image that should be scaled. * @param options The options that should be used to produce the correct image scale. */ getSampleSize(uri, options) { const boundsOptions = new android.graphics.BitmapFactory.Options(); boundsOptions.inJustDecodeBounds = true; android.graphics.BitmapFactory.decodeStream(this.openInputStream(uri), null, boundsOptions); // find the correct scale value. It should be the power of 2. let outWidth = boundsOptions.outWidth; let outHeight = boundsOptions.outHeight; let scale = 1; if (options) { // tODO: Refactor to accomodate different scaling options // right now, it just selects the smallest of the two sizes // and scales the image proportionally to that. const targetSize = options.maxWidth < options.maxHeight ? options.maxWidth : options.maxHeight; while (!(this.matchesSize(targetSize, outWidth) || this.matchesSize(targetSize, outHeight))) { outWidth /= 2; outHeight /= 2; scale *= 2; } } return scale; } matchesSize(targetSize, actualSize) { return targetSize && actualSize / 2 < targetSize; } /** * Decodes the given URI using the given options. * @param uri The URI that should be decoded into an ImageSource. * @param options The options that should be used to decode the image. */ decodeUri(uri, options) { const downsampleOptions = new android.graphics.BitmapFactory.Options(); downsampleOptions.inSampleSize = this.getSampleSize(uri, options); const bitmap = android.graphics.BitmapFactory.decodeStream(this.openInputStream(uri), null, downsampleOptions); const image = new ImageSource(); image.setNativeSource(bitmap); return image; } /** * Decodes the given URI using the given options. * @param uri The URI that should be decoded into an ImageAsset. * @param options The options that should be used to decode the image. */ decodeUriForImageAsset(uri, options) { const downsampleOptions = new android.graphics.BitmapFactory.Options(); downsampleOptions.inSampleSize = this.getSampleSize(uri, options); const bitmap = android.graphics.BitmapFactory.decodeStream(this.openInputStream(uri), null, downsampleOptions); return new ImageAsset(bitmap); } /** * Retrieves the raw data of the given file and exposes it as a byte buffer. */ getByteBuffer(uri) { let file = null; try { file = SelectedAsset.getContentResolver().openAssetFileDescriptor(uri, 'r'); // determine how many bytes to allocate in memory based on the file length const length = file.getLength(); const buffer = java.nio.ByteBuffer.allocateDirect(length); const bytes = buffer.array(); const stream = file.createInputStream(); // buffer the data in 4KiB amounts const reader = new java.io.BufferedInputStream(stream, 4096); reader.read(bytes, 0, bytes.length); return buffer; } finally { if (file) { file.close(); } } } openInputStream(uri) { return SelectedAsset.getContentResolver().openInputStream(uri); } static getContentResolver() { return Utils.android.getApplicationContext().getContentResolver(); } } //# sourceMappingURL=selected-asset.js.map