diff --git a/appium-tests/helpers/cameraHelper.js b/appium-tests/helpers/cameraHelper.js deleted file mode 100644 index 72f7a2700..000000000 --- a/appium-tests/helpers/cameraHelper.js +++ /dev/null @@ -1,311 +0,0 @@ -/* global Q, resolveLocalFileSystemURL, Camera, cordova */ -/* - * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - * -*/ - -'use strict'; - -var cameraConstants = require('../../www/CameraConstants'); - -function findKeyByValue(set, value) { - for (var k in set) { - if (set.hasOwnProperty(k)) { - if (set[k] == value) { - return k; - } - } - } - return undefined; -} - -function getDescription(spec) { - var desc = ''; - - desc += 'sourceType: ' + findKeyByValue(cameraConstants.PictureSourceType, spec.options.sourceType); - desc += ', destinationType: ' + findKeyByValue(cameraConstants.DestinationType, spec.options.destinationType); - desc += ', encodingType: ' + findKeyByValue(cameraConstants.EncodingType, spec.options.encodingType); - desc += ', allowEdit: ' + spec.options.allowEdit.toString(); - desc += ', correctOrientation: ' + spec.options.correctOrientation.toString(); - - return desc; -} - -module.exports.generateSpecs = function (sourceTypes, destinationTypes, encodingTypes, allowEditOptions, correctOrientationOptions) { - var destinationType, - sourceType, - encodingType, - allowEdit, - correctOrientation, - specs = [], - id = 1; - for (destinationType in destinationTypes) { - if (destinationTypes.hasOwnProperty(destinationType)) { - for (sourceType in sourceTypes) { - if (sourceTypes.hasOwnProperty(sourceType)) { - for (encodingType in encodingTypes) { - if (encodingTypes.hasOwnProperty(encodingType)) { - for (allowEdit in allowEditOptions) { - if (allowEditOptions.hasOwnProperty(allowEdit)) { - for (correctOrientation in correctOrientationOptions) { - // if taking picture from photolibrary, don't vary 'correctOrientation' option - if ((sourceTypes[sourceType] === cameraConstants.PictureSourceType.PHOTOLIBRARY || - sourceTypes[sourceType] === cameraConstants.PictureSourceType.SAVEDPHOTOALBUM) && - correctOrientation === true) { continue; } - var spec = { - 'id': id++, - 'options': { - 'destinationType': destinationTypes[destinationType], - 'sourceType': sourceTypes[sourceType], - 'encodingType': encodingTypes[encodingType], - 'allowEdit': allowEditOptions[allowEdit], - 'saveToPhotoAlbum': false, - 'correctOrientation': correctOrientationOptions[correctOrientation] - } - }; - spec.description = getDescription(spec); - specs.push(spec); - } - } - } - } - } - } - } - } - } - return specs; -}; - -// calls getPicture() and saves the result in promise -// note that this function is executed in the context of tested app -// and not in the context of tests -module.exports.getPicture = function (opts, pid) { - if (navigator._appiumPromises[pid - 1]) { - navigator._appiumPromises[pid - 1] = null; - } - navigator._appiumPromises[pid] = Q.defer(); - navigator.camera.getPicture(function (result) { - navigator._appiumPromises[pid].resolve(result); - }, function (err) { - navigator._appiumPromises[pid].reject(err); - }, opts); -}; - -// verifies taken picture when the promise is resolved, -// calls a callback with 'OK' if everything is good, -// calls a callback with 'ERROR: ' if something is wrong -// note that this function is executed in the context of tested app -// and not in the context of tests -module.exports.checkPicture = function (pid, options, skipContentCheck, cb) { - var isIos = cordova.platformId === "ios"; - var isAndroid = cordova.platformId === "android"; - // skip image type check if it's unmodified on Android: - // https://github.com/apache/cordova-plugin-camera/#android-quirks-1 - var skipFileTypeCheckAndroid = isAndroid && options.quality === 100 && - !options.targetWidth && !options.targetHeight && - !options.correctOrientation; - - // Skip image type check if destination is NATIVE_URI and source - device's photoalbum - // https://github.com/apache/cordova-plugin-camera/#ios-quirks-1 - var skipFileTypeCheckiOS = isIos && options.destinationType === Camera.DestinationType.NATIVE_URI && - (options.sourceType === Camera.PictureSourceType.PHOTOLIBRARY || - options.sourceType === Camera.PictureSourceType.SAVEDPHOTOALBUM); - - var skipFileTypeCheck = skipFileTypeCheckAndroid || skipFileTypeCheckiOS; - - var desiredType = 'JPEG'; - var mimeType = 'image/jpeg'; - if (options.encodingType === Camera.EncodingType.PNG) { - desiredType = 'PNG'; - mimeType = 'image/png'; - } - - function errorCallback(msg) { - if (msg.hasOwnProperty('message')) { - msg = msg.message; - } - cb('ERROR: ' + msg); - } - - // verifies the image we get from plugin - function verifyResult(result) { - if (result.length === 0) { - errorCallback('The result is empty.'); - return; - } else if (isIos && options.destinationType === Camera.DestinationType.NATIVE_URI && result.indexOf('assets-library:') !== 0) { - errorCallback('Expected "' + result.substring(0, 150) + '"to start with "assets-library:"'); - return; - } else if (isIos && options.destinationType === Camera.DestinationType.FILE_URI && result.indexOf('file:') !== 0) { - errorCallback('Expected "' + result.substring(0, 150) + '"to start with "file:"'); - return; - } - - try { - window.atob(result); - // if we got here it is a base64 string (DATA_URL) - result = "data:" + mimeType + ";base64," + result; - } catch (e) { - // not DATA_URL - if (options.destinationType === Camera.DestinationType.DATA_URL) { - errorCallback('Expected ' + result.substring(0, 150) + 'not to be DATA_URL'); - return; - } - } - - try { - if (result.indexOf('file:') === 0 || - result.indexOf('content:') === 0 || - result.indexOf('assets-library:') === 0) { - - if (!window.resolveLocalFileSystemURL) { - errorCallback('Cannot read file. Please install cordova-plugin-file to fix this.'); - return; - } - if (skipContentCheck) { - cb('OK'); - return; - } - resolveLocalFileSystemURL(result, function (entry) { - if (skipFileTypeCheck) { - displayFile(entry); - } else { - verifyFile(entry); - } - }, function (err) { - errorCallback(err); - }); - } else { - displayImage(result); - } - } catch (e) { - errorCallback(e); - } - } - - // verifies that the file type matches the requested type - function verifyFile(entry) { - try { - var reader = new FileReader(); - reader.onloadend = function(e) { - var arr = (new Uint8Array(e.target.result)).subarray(0, 4); - var header = ''; - for(var i = 0; i < arr.length; i++) { - header += arr[i].toString(16); - } - var actualType = 'unknown'; - - switch (header) { - case "89504e47": - actualType = 'PNG'; - break; - case 'ffd8ffe0': - case 'ffd8ffe1': - case 'ffd8ffe2': - actualType = 'JPEG'; - break; - } - - if (actualType === desiredType) { - displayFile(entry); - } else { - errorCallback('File type mismatch. Expected ' + desiredType + ', got ' + actualType); - } - }; - reader.onerror = function (e) { - errorCallback(e); - }; - entry.file(function (file) { - reader.readAsArrayBuffer(file); - }, function (e) { - errorCallback(e); - }); - } catch (e) { - errorCallback(e); - } - } - - // reads the file, then displays the image - function displayFile(entry) { - function onFileReceived(file) { - var reader = new FileReader(); - reader.onerror = function (e) { - errorCallback(e); - }; - reader.onloadend = function (evt) { - displayImage(evt.target.result); - }; - reader.readAsDataURL(file); - } - - entry.file(onFileReceived, function (e) { - errorCallback(e); - }); - } - - function displayImage(image) { - try { - var imgEl = document.getElementById('camera_test_image'); - if (!imgEl) { - imgEl = document.createElement('img'); - imgEl.id = 'camera_test_image'; - document.body.appendChild(imgEl); - } - var timedOut = false; - var loadTimeout = setTimeout(function () { - timedOut = true; - imgEl.src = ''; - errorCallback('The image did not load: ' + image.substring(0, 150)); - }, 10000); - var done = function (status) { - if (!timedOut) { - clearTimeout(loadTimeout); - imgEl.src = ''; - cb(status); - } - }; - imgEl.onload = function () { - try { - // aspect ratio is preserved so only one dimension should match - if ((typeof options.targetWidth === 'number' && imgEl.naturalWidth !== options.targetWidth) && - (typeof options.targetHeight === 'number' && imgEl.naturalHeight !== options.targetHeight)) - { - done('ERROR: Wrong image size: ' + imgEl.naturalWidth + 'x' + imgEl.naturalHeight + - '. Requested size: ' + options.targetWidth + 'x' + options.targetHeight); - } else { - done('OK'); - } - } catch (e) { - errorCallback(e); - } - }; - imgEl.src = image; - } catch (e) { - errorCallback(e); - } - } - - navigator._appiumPromises[pid].promise - .then(function (result) { - verifyResult(result); - }) - .fail(function (e) { - errorCallback(e); - }); -}; diff --git a/src/android/CameraLauncher.java b/src/android/CameraLauncher.java index 6cdf0c5f7..85238cde1 100644 --- a/src/android/CameraLauncher.java +++ b/src/android/CameraLauncher.java @@ -303,7 +303,7 @@ public void takePicture(int returnType, int encodingType) this.imageUri = new CordovaUri(FileProvider.getUriForFile(cordova.getActivity(), applicationId + ".provider", photo)); - intent.putExtra(android.provider.MediaStore.EXTRA_OUTPUT, imageUri.getCorrectUri()); + intent.putExtra(MediaStore.EXTRA_OUTPUT, imageUri.getCorrectUri()); //We can write to this URI, this will hopefully allow us to write files to get to the next step intent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION); @@ -389,7 +389,7 @@ public void getImage(int srcType, int returnType, int encodingType) { } File photo = createCaptureFile(JPEG); croppedUri = Uri.fromFile(photo); - intent.putExtra(android.provider.MediaStore.EXTRA_OUTPUT, croppedUri); + intent.putExtra(MediaStore.EXTRA_OUTPUT, croppedUri); } else { intent.setAction(Intent.ACTION_GET_CONTENT); intent.addCategory(Intent.CATEGORY_OPENABLE); @@ -912,14 +912,14 @@ private void writeUncompressedImage(Uri src, Uri dest) throws FileNotFoundExcept */ private Uri getUriFromMediaStore() { ContentValues values = new ContentValues(); - values.put(android.provider.MediaStore.Images.Media.MIME_TYPE, JPEG_MIME_TYPE); + values.put(MediaStore.Images.Media.MIME_TYPE, JPEG_MIME_TYPE); Uri uri; try { - uri = this.cordova.getActivity().getContentResolver().insert(android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values); + uri = this.cordova.getActivity().getContentResolver().insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values); } catch (RuntimeException e) { LOG.d(LOG_TAG, "Can't write to external media storage."); try { - uri = this.cordova.getActivity().getContentResolver().insert(android.provider.MediaStore.Images.Media.INTERNAL_CONTENT_URI, values); + uri = this.cordova.getActivity().getContentResolver().insert(MediaStore.Images.Media.INTERNAL_CONTENT_URI, values); } catch (RuntimeException ex) { LOG.d(LOG_TAG, "Can't write to internal media storage."); return null; @@ -1245,9 +1245,9 @@ private void checkForDuplicateImage(int type) { */ private Uri whichContentStore() { if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) { - return android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI; + return MediaStore.Images.Media.EXTERNAL_CONTENT_URI; } else { - return android.provider.MediaStore.Images.Media.INTERNAL_CONTENT_URI; + return MediaStore.Images.Media.INTERNAL_CONTENT_URI; } } @@ -1261,14 +1261,23 @@ public void processPicture(Bitmap bitmap, int encodingType) { CompressFormat compressFormat = encodingType == JPEG ? CompressFormat.JPEG : CompressFormat.PNG; + String imageMimeType = encodingType == JPEG ? + JPEG_MIME_TYPE : + PNG_MIME_TYPE; try { if (bitmap.compress(compressFormat, mQuality, jpeg_data)) { byte[] code = jpeg_data.toByteArray(); byte[] output = Base64.encode(code, Base64.NO_WRAP); String js_out = new String(output); - this.callbackContext.success(js_out); + String base64Prefix = "data:" + imageMimeType + ";base64,"; + String finalJsOut = base64Prefix + js_out; + + this.callbackContext.success(finalJsOut); + js_out = null; + base64Prefix = null; + finalJsOut = null; output = null; code = null; } @@ -1299,7 +1308,7 @@ private void scanForGallery(Uri newImage) { public void onMediaScannerConnected() { try { this.conn.scanFile(this.scanMe.toString(), "image/*"); - } catch (java.lang.IllegalStateException e) { + } catch (IllegalStateException e) { LOG.e(LOG_TAG, "Can't scan file in MediaScanner after taking picture"); } diff --git a/src/browser/CameraProxy.js b/src/browser/CameraProxy.js index ff81257a6..b59514f4d 100644 --- a/src/browser/CameraProxy.js +++ b/src/browser/CameraProxy.js @@ -36,10 +36,8 @@ function takePicture (success, error, opts) { var reader = new FileReader(); /* eslint no-undef : 0 */ reader.onload = function (readerEvent) { input.parentNode.removeChild(input); - var imageData = readerEvent.target.result; - - return success(imageData.substr(imageData.indexOf(',') + 1)); + return success(imageData); }; reader.readAsDataURL(inputEvent.target.files[0]); @@ -79,7 +77,6 @@ function capture (success, errorCallback, opts) { // convert image stored in canvas to base64 encoded image var imageData = canvas.toDataURL('image/png'); - imageData = imageData.replace('data:image/png;base64,', ''); // stop video stream, remove video and button. // Note that MediaStream.stop() is deprecated as of Chrome 47. diff --git a/src/ios/CDVCamera.m b/src/ios/CDVCamera.m index bf02ca2e6..91dda2786 100644 --- a/src/ios/CDVCamera.m +++ b/src/ios/CDVCamera.m @@ -493,7 +493,10 @@ - (void)resultForImage:(CDVPictureOptions*)options info:(NSDictionary*)info comp image = [self retrieveImage:info options:options]; NSData* data = [self processImage:image info:info options:options]; if (data) { - result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsString:toBase64(data)]; + NSString* mimeType = options.encodingType == EncodingTypePNG? @"image/png" : @"image/jpeg"; + NSString* finalDataURL = [NSString stringWithFormat:@"%@%@%@%@", @"data:", mimeType, @";base64,", toBase64(data)]; + + result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsString:finalDataURL]; } } break; @@ -701,7 +704,10 @@ - (void)imagePickerControllerReturnImageResult break; case DestinationTypeDataUrl: { - result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsString:toBase64(self.data)]; + NSString* mimeType = self.pickerController.pictureOptions.encodingType == EncodingTypePNG ? @"image/png" : @"image/jpeg"; + NSString* finalDataURL = [NSString stringWithFormat:@"%@%@%@%@", @"data:", mimeType, @";base64,", toBase64(self.data)]; + + result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsString:finalDataURL]; } break; case DestinationTypeNativeUri: diff --git a/src/osx/CDVCamera.m b/src/osx/CDVCamera.m index 9693eefb8..b4785752f 100644 --- a/src/osx/CDVCamera.m +++ b/src/osx/CDVCamera.m @@ -180,7 +180,10 @@ - (void)returnImage:(NSImage *)image command:(CDVInvokedUrlCommand *)command opt NSData *processedImageData = [self processImage:image options:pictureOptions]; if (pictureOptions.destinationType == DestinationTypeDataUrl) { - CDVPluginResult *result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsString:[processedImageData base64EncodedStringWithOptions:NSDataBase64EncodingEndLineWithLineFeed]]; + NSString* mimeType = pictureOptions.encodingType == EncodingTypeJPEG? @"image/jpeg" : @"image/png"; + NSString* finalDataURL = [NSString stringWithFormat:@"%@%@%@%@", @"data:", mimeType, @";base64,", [processedImageData base64EncodedStringWithOptions:NSDataBase64EncodingEndLineWithLineFeed]]; + + CDVPluginResult *result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsString:finalDataURL]; [self.commandDelegate sendPluginResult:result callbackId:command.callbackId]; } else { NSString *tempFilePath = [self uniqueImageName:pictureOptions]; @@ -255,4 +258,4 @@ - (NSString *)uniqueImageName:(CDVPictureOptions *)pictureOptions { return uniqueFileName; } -@end \ No newline at end of file +@end diff --git a/src/windows/CameraProxy.js b/src/windows/CameraProxy.js index 7ede906f9..56948c35d 100644 --- a/src/windows/CameraProxy.js +++ b/src/windows/CameraProxy.js @@ -153,10 +153,7 @@ function resizeImageBase64 (successCallback, errorCallback, file, targetWidth, t // The resized file ready for upload var finalFile = canvas.toDataURL(file.contentType); - // Remove the prefix such as "data:" + contentType + ";base64," , in order to meet the Cordova API. - var arr = finalFile.split(','); - var newStr = finalFile.substr(arr[0].length + 1); - successCallback(newStr); + successCallback(finalFile); }; }, function (err) { errorCallback(err); }); }