/*jshint node: true */
|
|
/* 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: <error message>' 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, 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;
|
|
}
|
|
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);
|
|
});
|
|
};
|