Files
cordova-plugin-camera-dev/src/windows/CameraProxy.js
T

850 lines
36 KiB
JavaScript
Raw Normal View History

2013-07-29 19:04:44 -07:00
/*
*
* 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.
*
*/
/*jshint unused:true, undef:true, browser:true */
2015-08-04 12:04:17 -07:00
/*global Windows:true, URL:true, module:true, require:true, WinJS:true */
2013-07-29 19:04:44 -07:00
var Camera = require('./Camera');
2013-07-29 19:04:44 -07:00
2015-05-18 21:38:34 -07:00
var getAppData = function () {
return Windows.Storage.ApplicationData.current;
};
var encodeToBase64String = function (buffer) {
return Windows.Security.Cryptography.CryptographicBuffer.encodeToBase64String(buffer);
};
var OptUnique = Windows.Storage.CreationCollisionOption.generateUniqueName;
var CapMSType = Windows.Media.Capture.MediaStreamType;
var webUIApp = Windows.UI.WebUI.WebUIApplication;
var fileIO = Windows.Storage.FileIO;
var pickerLocId = Windows.Storage.Pickers.PickerLocationId;
2013-07-29 19:04:44 -07:00
module.exports = {
// args will contain :
// ... it is an array, so be careful
// 0 quality:50,
// 1 destinationType:Camera.DestinationType.FILE_URI,
// 2 sourceType:Camera.PictureSourceType.CAMERA,
// 3 targetWidth:-1,
// 4 targetHeight:-1,
// 5 encodingType:Camera.EncodingType.JPEG,
// 6 mediaType:Camera.MediaType.PICTURE,
// 7 allowEdit:false,
// 8 correctOrientation:false,
// 9 saveToPhotoAlbum:false,
// 10 popoverOptions:null
2014-08-27 17:15:47 +04:00
// 11 cameraDirection:0
2013-07-29 19:04:44 -07:00
takePicture: function (successCallback, errorCallback, args) {
var sourceType = args[2];
if (sourceType != Camera.PictureSourceType.CAMERA) {
takePictureFromFile(successCallback, errorCallback, args);
} else {
takePictureFromCamera(successCallback, errorCallback, args);
}
}
};
// https://msdn.microsoft.com/en-us/library/windows/apps/ff462087(v=vs.105).aspx
var windowsVideoContainers = [".avi", ".flv", ".asx", ".asf", ".mov", ".mp4", ".mpg", ".rm", ".srt", ".swf", ".wmv", ".vob"];
2015-04-22 14:23:47 -07:00
var windowsPhoneVideoContainers = [".avi", ".3gp", ".3g2", ".wmv", ".3gp", ".3g2", ".mp4", ".m4v"];
2013-07-29 19:04:44 -07:00
// Default aspect ratio 1.78 (16:9 hd video standard)
var DEFAULT_ASPECT_RATIO = '1.8';
// Highest possible z-index supported across browsers. Anything used above is converted to this value.
var HIGHEST_POSSIBLE_Z_INDEX = 2147483647;
// Resize method
function resizeImage(successCallback, errorCallback, file, targetWidth, targetHeight, encodingType) {
var tempPhotoFileName = "";
if (encodingType == Camera.EncodingType.PNG) {
tempPhotoFileName = "camera_cordova_temp_return.png";
} else {
tempPhotoFileName = "camera_cordova_temp_return.jpg";
}
2013-07-29 19:04:44 -07:00
2015-05-18 21:38:34 -07:00
var storageFolder = getAppData().localFolder;
file.copyAsync(storageFolder, file.name, Windows.Storage.NameCollisionOption.replaceExisting)
2015-05-18 21:38:34 -07:00
.then(function (storageFile) {
return fileIO.readBufferAsync(storageFile);
})
.then(function(buffer) {
var strBase64 = encodeToBase64String(buffer);
var imageData = "data:" + file.contentType + ";base64," + strBase64;
var image = new Image();
image.src = imageData;
image.onload = function() {
var ratio = Math.min(targetWidth / this.width, targetHeight / this.height);
var imageWidth = ratio * this.width;
var imageHeight = ratio * this.height;
var canvas = document.createElement('canvas');
var storageFileName;
canvas.width = imageWidth;
canvas.height = imageHeight;
canvas.getContext("2d").drawImage(this, 0, 0, imageWidth, imageHeight);
var fileContent = canvas.toDataURL(file.contentType).split(',')[1];
2015-05-18 21:38:34 -07:00
var storageFolder = getAppData().localFolder;
2015-05-18 21:38:34 -07:00
storageFolder.createFileAsync(tempPhotoFileName, OptUnique)
.then(function (storagefile) {
var content = Windows.Security.Cryptography.CryptographicBuffer.decodeFromBase64String(fileContent);
storageFileName = storagefile.name;
2015-05-18 21:38:34 -07:00
return fileIO.writeBufferAsync(storagefile, content);
})
.done(function () {
successCallback("ms-appdata:///local/" + storageFileName);
}, errorCallback);
};
})
.done(null, function(err) {
errorCallback(err);
}
);
}
2013-07-29 19:04:44 -07:00
// Because of asynchronous method, so let the successCallback be called in it.
function resizeImageBase64(successCallback, errorCallback, file, targetWidth, targetHeight) {
2015-05-18 21:38:34 -07:00
fileIO.readBufferAsync(file).done( function(buffer) {
var strBase64 = encodeToBase64String(buffer);
var imageData = "data:" + file.contentType + ";base64," + strBase64;
2013-07-29 19:04:44 -07:00
var image = new Image();
image.src = imageData;
2013-07-29 19:04:44 -07:00
image.onload = function() {
var ratio = Math.min(targetWidth / this.width, targetHeight / this.height);
var imageWidth = ratio * this.width;
var imageHeight = ratio * this.height;
var canvas = document.createElement('canvas');
2013-07-29 19:04:44 -07:00
canvas.width = imageWidth;
canvas.height = imageHeight;
2013-07-29 19:04:44 -07:00
var ctx = canvas.getContext("2d");
ctx.drawImage(this, 0, 0, imageWidth, imageHeight);
2013-07-29 19:04:44 -07:00
// The resized file ready for upload
var finalFile = canvas.toDataURL(file.contentType);
2013-07-29 19:04:44 -07:00
// 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);
2013-07-29 19:04:44 -07:00
};
}, function(err) { errorCallback(err); });
}
function takePictureFromFile(successCallback, errorCallback, args) {
// Detect Windows Phone
if (navigator.appVersion.indexOf('Windows Phone 8.1') >= 0) {
takePictureFromFileWP(successCallback, errorCallback, args);
} else {
takePictureFromFileWindows(successCallback, errorCallback, args);
}
}
function takePictureFromFileWP(successCallback, errorCallback, args) {
var mediaType = args[6],
destinationType = args[1],
targetWidth = args[3],
targetHeight = args[4],
encodingType = args[5];
/*
Need to add and remove an event listener to catch activation state
Using FileOpenPicker will suspend the app and it's required to catch the PickSingleFileAndContinue
https://msdn.microsoft.com/en-us/library/windows/apps/xaml/dn631755.aspx
*/
var filePickerActivationHandler = function(eventArgs) {
if (eventArgs.kind === Windows.ApplicationModel.Activation.ActivationKind.pickFileContinuation) {
var file = eventArgs.files[0];
if (!file) {
errorCallback("User didn't choose a file.");
2015-05-18 21:38:34 -07:00
webUIApp.removeEventListener("activated", filePickerActivationHandler);
return;
}
if (destinationType == Camera.DestinationType.FILE_URI || destinationType == Camera.DestinationType.NATIVE_URI) {
if (targetHeight > 0 && targetWidth > 0) {
resizeImage(successCallback, errorCallback, file, targetWidth, targetHeight, encodingType);
}
else {
2015-05-18 21:38:34 -07:00
var storageFolder = getAppData().localFolder;
file.copyAsync(storageFolder, file.name, Windows.Storage.NameCollisionOption.replaceExisting).done(function (storageFile) {
2015-05-18 17:32:14 -07:00
if(destinationType == Camera.DestinationType.NATIVE_URI) {
successCallback("ms-appdata:///local/" + storageFile.name);
}
else {
successCallback(URL.createObjectURL(storageFile));
}
}, function () {
errorCallback("Can't access localStorage folder.");
});
}
}
else {
if (targetHeight > 0 && targetWidth > 0) {
resizeImageBase64(successCallback, errorCallback, file, targetWidth, targetHeight);
} else {
2015-05-18 21:38:34 -07:00
fileIO.readBufferAsync(file).done(function (buffer) {
var strBase64 =encodeToBase64String(buffer);
successCallback(strBase64);
}, errorCallback);
}
}
2015-05-18 21:38:34 -07:00
webUIApp.removeEventListener("activated", filePickerActivationHandler);
}
};
var fileOpenPicker = new Windows.Storage.Pickers.FileOpenPicker();
if (mediaType == Camera.MediaType.PICTURE) {
fileOpenPicker.fileTypeFilter.replaceAll([".png", ".jpg", ".jpeg"]);
2015-05-18 21:38:34 -07:00
fileOpenPicker.suggestedStartLocation = pickerLocId.picturesLibrary;
}
else if (mediaType == Camera.MediaType.VIDEO) {
fileOpenPicker.fileTypeFilter.replaceAll(windowsPhoneVideoContainers);
2015-05-18 21:38:34 -07:00
fileOpenPicker.suggestedStartLocation = pickerLocId.videosLibrary;
}
else {
fileOpenPicker.fileTypeFilter.replaceAll(["*"]);
2015-05-18 21:38:34 -07:00
fileOpenPicker.suggestedStartLocation = pickerLocId.documentsLibrary;
}
2015-05-18 21:38:34 -07:00
webUIApp.addEventListener("activated", filePickerActivationHandler);
fileOpenPicker.pickSingleFileAndContinue();
}
function takePictureFromFileWindows(successCallback, errorCallback, args) {
var mediaType = args[6],
destinationType = args[1],
targetWidth = args[3],
targetHeight = args[4],
encodingType = args[5];
2013-07-29 19:04:44 -07:00
var fileOpenPicker = new Windows.Storage.Pickers.FileOpenPicker();
if (mediaType == Camera.MediaType.PICTURE) {
fileOpenPicker.fileTypeFilter.replaceAll([".png", ".jpg", ".jpeg"]);
2015-05-18 21:38:34 -07:00
fileOpenPicker.suggestedStartLocation = pickerLocId.picturesLibrary;
}
else if (mediaType == Camera.MediaType.VIDEO) {
fileOpenPicker.fileTypeFilter.replaceAll(windowsVideoContainers);
2015-05-18 21:38:34 -07:00
fileOpenPicker.suggestedStartLocation = pickerLocId.videosLibrary;
}
else {
fileOpenPicker.fileTypeFilter.replaceAll(["*"]);
2015-05-18 21:38:34 -07:00
fileOpenPicker.suggestedStartLocation = pickerLocId.documentsLibrary;
}
2014-08-27 17:15:47 +04:00
fileOpenPicker.pickSingleFileAsync().done(function (file) {
2015-04-03 11:08:00 -07:00
if (!file) {
errorCallback("User didn't choose a file.");
return;
}
if (destinationType == Camera.DestinationType.FILE_URI || destinationType == Camera.DestinationType.NATIVE_URI) {
if (targetHeight > 0 && targetWidth > 0) {
resizeImage(successCallback, errorCallback, file, targetWidth, targetHeight, encodingType);
}
else {
2015-05-18 21:38:34 -07:00
var storageFolder = getAppData().localFolder;
2015-04-03 11:08:00 -07:00
file.copyAsync(storageFolder, file.name, Windows.Storage.NameCollisionOption.replaceExisting).done(function (storageFile) {
2015-06-17 16:14:58 -05:00
if(destinationType == Camera.DestinationType.NATIVE_URI) {
successCallback("ms-appdata:///local/" + storageFile.name);
}
else {
successCallback(URL.createObjectURL(storageFile));
}
2015-04-03 11:08:00 -07:00
}, function () {
errorCallback("Can't access localStorage folder.");
});
}
}
else {
if (targetHeight > 0 && targetWidth > 0) {
resizeImageBase64(successCallback, errorCallback, file, targetWidth, targetHeight);
} else {
2015-05-18 21:38:34 -07:00
fileIO.readBufferAsync(file).done(function (buffer) {
var strBase64 =encodeToBase64String(buffer);
2015-04-03 11:08:00 -07:00
successCallback(strBase64);
}, errorCallback);
}
}
}, function () {
errorCallback("User didn't choose a file.");
});
}
function takePictureFromCamera(successCallback, errorCallback, args) {
// Check if necessary API available
if (!Windows.Media.Capture.CameraCaptureUI) {
takePictureFromCameraWP(successCallback, errorCallback, args);
} else {
takePictureFromCameraWindows(successCallback, errorCallback, args);
}
}
function takePictureFromCameraWP(successCallback, errorCallback, args) {
// We are running on WP8.1 which lacks CameraCaptureUI class
// so we need to use MediaCapture class instead and implement custom UI for camera
var destinationType = args[1],
targetWidth = args[3],
targetHeight = args[4],
encodingType = args[5],
saveToPhotoAlbum = args[9],
cameraDirection = args[11],
capturePreview = null,
2015-07-27 12:15:25 -07:00
cameraCaptureButton = null,
cameraCancelButton = null,
capture = null,
captureSettings = null,
2015-05-14 10:35:53 -07:00
CaptureNS = Windows.Media.Capture,
sensor = null;
2016-02-18 15:18:09 +03:00
function createCameraUI() {
2015-07-09 12:27:32 +01:00
// create style for take and cancel buttons
var buttonStyle = "width:45%;padding: 10px 16px;font-size: 18px;line-height: 1.3333333;color: #333;background-color: #fff;border-color: #ccc; border: 1px solid transparent;border-radius: 6px; display: block; margin: 20px; z-index: 1000;border-color: #adadad;";
2015-07-09 12:27:32 +01:00
// Create fullscreen preview
2015-07-27 12:15:25 -07:00
// z-order style element for capturePreview and cameraCancelButton elts
// is necessary to avoid overriding by another page elements, -1 sometimes is not enough
2016-02-18 15:18:09 +03:00
capturePreview = document.createElement("video");
capturePreview.style.cssText = "position: fixed; left: 0; top: 0; width: 100%; height: 100%; z-index: " + (HIGHEST_POSSIBLE_Z_INDEX - 1) + ";";
2015-07-09 12:27:32 +01:00
// Create capture button
2015-07-27 12:15:25 -07:00
cameraCaptureButton = document.createElement("button");
cameraCaptureButton.innerText = "Take";
cameraCaptureButton.style.cssText = buttonStyle + "position: fixed; left: 0; bottom: 0; margin: 20px; z-index: " + HIGHEST_POSSIBLE_Z_INDEX + ";";
// Create cancel button
2015-07-27 12:15:25 -07:00
cameraCancelButton = document.createElement("button");
cameraCancelButton.innerText = "Cancel";
cameraCancelButton.style.cssText = buttonStyle + "position: fixed; right: 0; bottom: 0; margin: 20px; z-index: " + HIGHEST_POSSIBLE_Z_INDEX + ";";
capture = new CaptureNS.MediaCapture();
captureSettings = new CaptureNS.MediaCaptureInitializationSettings();
captureSettings.streamingCaptureMode = CaptureNS.StreamingCaptureMode.video;
2016-02-18 15:18:09 +03:00
}
2016-02-18 15:18:09 +03:00
function startCameraPreview() {
// Search for available camera devices
// This is necessary to detect which camera (front or back) we should use
var DeviceEnum = Windows.Devices.Enumeration;
var expectedPanel = cameraDirection === 1 ? DeviceEnum.Panel.front : DeviceEnum.Panel.back;
2015-05-14 10:35:53 -07:00
DeviceEnum.DeviceInformation.findAllAsync(DeviceEnum.DeviceClass.videoCapture).then(function (devices) {
2015-04-03 11:08:00 -07:00
if (devices.length <= 0) {
destroyCameraPreview();
errorCallback('Camera not found');
2015-04-03 11:08:00 -07:00
return;
}
2015-04-03 11:08:00 -07:00
devices.forEach(function(currDev) {
if (currDev.enclosureLocation.panel && currDev.enclosureLocation.panel == expectedPanel) {
captureSettings.videoDeviceId = currDev.id;
}
});
captureSettings.photoCaptureSource = Windows.Media.Capture.PhotoCaptureSource.photo;
2015-04-03 11:08:00 -07:00
return capture.initializeAsync(captureSettings);
}).then(function () {
2015-07-09 12:27:32 +01:00
// create focus control if available
var VideoDeviceController = capture.videoDeviceController;
var FocusControl = VideoDeviceController.focusControl;
2015-07-27 12:15:25 -07:00
if (FocusControl.supported === true) {
2015-07-09 12:27:32 +01:00
capturePreview.addEventListener('click', function () {
2016-01-21 14:11:59 -08:00
// Make sure function isn't called again before previous focus is completed
2015-12-28 15:24:24 +01:00
if (this.getAttribute('clicked') === '1') {
return false;
} else {
this.setAttribute('clicked', '1');
}
2015-07-09 12:27:32 +01:00
var preset = Windows.Media.Devices.FocusPreset.autoNormal;
2015-12-28 15:24:24 +01:00
var parent = this;
2015-07-09 12:27:32 +01:00
FocusControl.setPresetAsync(preset).done(function () {
2016-01-21 14:11:59 -08:00
// set the clicked attribute back to '0' to allow focus again
2015-12-28 15:24:24 +01:00
parent.setAttribute('clicked', '0');
2015-07-09 12:27:32 +01:00
});
});
}
// msdn.microsoft.com/en-us/library/windows/apps/hh452807.aspx
capturePreview.msZoom = true;
capturePreview.src = URL.createObjectURL(capture);
capturePreview.play();
2015-04-03 11:08:00 -07:00
// Bind events to controls
2015-05-14 10:35:53 -07:00
sensor = Windows.Devices.Sensors.SimpleOrientationSensor.getDefault();
if (sensor !== null) {
sensor.addEventListener("orientationchanged", onOrientationChange);
}
2015-07-09 12:27:32 +01:00
2015-07-27 12:15:25 -07:00
// add click events to capture and cancel buttons
cameraCaptureButton.addEventListener('click', onCameraCaptureButtonClick);
cameraCancelButton.addEventListener('click', onCameraCancelButtonClick);
2015-05-14 10:35:53 -07:00
// Change default orientation
if (sensor) {
setPreviewRotation(sensor.getCurrentOrientation());
} else {
setPreviewRotation(Windows.Graphics.Display.DisplayInformation.getForCurrentView().currentOrientation);
}
// Get available aspect ratios
var aspectRatios = getAspectRatios(capture);
// Couldn't find a good ratio
if (aspectRatios.length === 0) {
destroyCameraPreview();
errorCallback('There\'s not a good aspect ratio available');
return;
}
2015-07-09 12:27:32 +01:00
// add elements to body
2015-05-14 10:35:53 -07:00
document.body.appendChild(capturePreview);
2015-07-27 12:15:25 -07:00
document.body.appendChild(cameraCaptureButton);
document.body.appendChild(cameraCancelButton);
2015-05-14 10:35:53 -07:00
if (aspectRatios.indexOf(DEFAULT_ASPECT_RATIO) > -1) {
return setAspectRatio(capture, DEFAULT_ASPECT_RATIO);
} else {
// Doesn't support 16:9 - pick next best
return setAspectRatio(capture, aspectRatios[0]);
}
}).done(null, function (err) {
destroyCameraPreview();
errorCallback('Camera intitialization error ' + err);
});
2016-02-18 15:18:09 +03:00
}
2016-02-18 15:18:09 +03:00
function destroyCameraPreview() {
2015-07-27 12:15:25 -07:00
// If sensor is available, remove event listener
2015-05-14 10:35:53 -07:00
if (sensor !== null) {
sensor.removeEventListener('orientationchanged', onOrientationChange);
}
2015-07-27 12:15:25 -07:00
// Pause and dispose preview element
capturePreview.pause();
capturePreview.src = null;
2015-07-27 12:15:25 -07:00
// Remove event listeners from buttons
cameraCaptureButton.removeEventListener('click', onCameraCaptureButtonClick);
cameraCancelButton.removeEventListener('click', onCameraCancelButtonClick);
// Remove elements
[capturePreview, cameraCaptureButton, cameraCancelButton].forEach(function (elem) {
if (elem /* && elem in document.body.childNodes */) {
document.body.removeChild(elem);
}
});
2015-07-27 12:15:25 -07:00
// Stop and dispose media capture manager
if (capture) {
capture.stopRecordAsync();
capture = null;
}
2016-02-18 15:18:09 +03:00
}
2014-08-27 17:15:47 +04:00
2016-02-18 15:18:09 +03:00
function captureAction() {
2014-08-27 17:15:47 +04:00
var encodingProperties,
fileName,
2015-05-18 21:38:34 -07:00
tempFolder = getAppData().temporaryFolder;
2014-08-27 17:15:47 +04:00
if (encodingType == Camera.EncodingType.PNG) {
fileName = 'photo.png';
encodingProperties = Windows.Media.MediaProperties.ImageEncodingProperties.createPng();
} else {
fileName = 'photo.jpg';
encodingProperties = Windows.Media.MediaProperties.ImageEncodingProperties.createJpeg();
}
2014-08-27 17:15:47 +04:00
2015-05-18 21:38:34 -07:00
tempFolder.createFileAsync(fileName, OptUnique)
.then(function(tempCapturedFile) {
return new WinJS.Promise(function (complete) {
2015-05-14 10:35:53 -07:00
var photoStream = new Windows.Storage.Streams.InMemoryRandomAccessStream();
var finalStream = new Windows.Storage.Streams.InMemoryRandomAccessStream();
capture.capturePhotoToStreamAsync(encodingProperties, photoStream)
2015-07-09 12:27:32 +01:00
.then(function() {
return Windows.Graphics.Imaging.BitmapDecoder.createAsync(photoStream);
})
.then(function(dec) {
finalStream.size = 0; // BitmapEncoder requires the output stream to be empty
return Windows.Graphics.Imaging.BitmapEncoder.createForTranscodingAsync(finalStream, dec);
})
.then(function(enc) {
// We need to rotate the photo wrt sensor orientation
enc.bitmapTransform.rotation = orientationToRotation(sensor.getCurrentOrientation());
return enc.flushAsync();
})
.then(function() {
return tempCapturedFile.openAsync(Windows.Storage.FileAccessMode.readWrite);
})
.then(function(fileStream) {
return Windows.Storage.Streams.RandomAccessStream.copyAndCloseAsync(finalStream, fileStream);
2015-07-09 12:27:32 +01:00
})
.done(function() {
photoStream.close();
finalStream.close();
complete(tempCapturedFile);
}, function() {
photoStream.close();
finalStream.close();
throw new Error("An error has occured while capturing the photo.");
});
});
})
.done(function(capturedFile) {
destroyCameraPreview();
savePhoto(capturedFile, {
destinationType: destinationType,
targetHeight: targetHeight,
targetWidth: targetWidth,
encodingType: encodingType,
saveToPhotoAlbum: saveToPhotoAlbum
}, successCallback, errorCallback);
}, function(err) {
2015-07-09 12:27:32 +01:00
destroyCameraPreview();
errorCallback(err);
});
2016-02-18 15:18:09 +03:00
}
2014-08-27 17:15:47 +04:00
2016-02-18 15:18:09 +03:00
function getAspectRatios(capture) {
2015-05-14 10:35:53 -07:00
var videoDeviceController = capture.videoDeviceController;
var photoAspectRatios = videoDeviceController.getAvailableMediaStreamProperties(CapMSType.photo).map(function (element) {
return (element.width / element.height).toFixed(1);
}).filter(function (element, index, array) { return (index === array.indexOf(element)); });
2015-05-14 10:35:53 -07:00
var videoAspectRatios = videoDeviceController.getAvailableMediaStreamProperties(CapMSType.videoRecord).map(function (element) {
return (element.width / element.height).toFixed(1);
}).filter(function (element, index, array) { return (index === array.indexOf(element)); });
2015-05-14 10:35:53 -07:00
var videoPreviewAspectRatios = videoDeviceController.getAvailableMediaStreamProperties(CapMSType.videoPreview).map(function (element) {
return (element.width / element.height).toFixed(1);
}).filter(function (element, index, array) { return (index === array.indexOf(element)); });
var allAspectRatios = [].concat(photoAspectRatios, videoAspectRatios, videoPreviewAspectRatios);
var aspectObj = allAspectRatios.reduce(function (map, item) {
if (!map[item]) {
map[item] = 0;
}
map[item]++;
return map;
}, {});
return Object.keys(aspectObj).filter(function (k) {
return aspectObj[k] === 3;
});
2016-02-18 15:18:09 +03:00
}
2016-02-18 15:18:09 +03:00
function setAspectRatio(capture, aspect) {
// Max photo resolution with desired aspect ratio
2015-05-14 10:35:53 -07:00
var videoDeviceController = capture.videoDeviceController;
var photoResolution = videoDeviceController.getAvailableMediaStreamProperties(CapMSType.photo)
2015-05-18 21:38:34 -07:00
.filter(function (elem) {
return ((elem.width / elem.height).toFixed(1) === aspect);
})
.reduce(function (prop1, prop2) {
return (prop1.width * prop1.height) > (prop2.width * prop2.height) ? prop1 : prop2;
});
// Max video resolution with desired aspect ratio
2015-05-14 10:35:53 -07:00
var videoRecordResolution = videoDeviceController.getAvailableMediaStreamProperties(CapMSType.videoRecord)
2015-05-18 21:38:34 -07:00
.filter(function (elem) {
return ((elem.width / elem.height).toFixed(1) === aspect);
})
.reduce(function (prop1, prop2) {
return (prop1.width * prop1.height) > (prop2.width * prop2.height) ? prop1 : prop2;
});
// Max video preview resolution with desired aspect ratio
2015-05-14 10:35:53 -07:00
var videoPreviewResolution = videoDeviceController.getAvailableMediaStreamProperties(CapMSType.videoPreview)
2015-05-18 21:38:34 -07:00
.filter(function (elem) {
return ((elem.width / elem.height).toFixed(1) === aspect);
})
.reduce(function (prop1, prop2) {
return (prop1.width * prop1.height) > (prop2.width * prop2.height) ? prop1 : prop2;
});
2015-05-14 10:35:53 -07:00
return videoDeviceController.setMediaStreamPropertiesAsync(CapMSType.photo, photoResolution)
2015-05-18 21:38:34 -07:00
.then(function () {
2015-05-14 10:35:53 -07:00
return videoDeviceController.setMediaStreamPropertiesAsync(CapMSType.videoPreview, videoPreviewResolution);
2015-05-18 21:38:34 -07:00
})
.then(function () {
2015-05-14 10:35:53 -07:00
return videoDeviceController.setMediaStreamPropertiesAsync(CapMSType.videoRecord, videoRecordResolution);
2015-05-18 21:38:34 -07:00
});
2016-02-18 15:18:09 +03:00
}
2015-07-27 12:15:25 -07:00
/**
* When Capture button is clicked, try to capture a picture and return
*/
2016-02-18 15:18:09 +03:00
function onCameraCaptureButtonClick() {
2015-07-27 12:15:25 -07:00
// Make sure user can't click more than once
if (this.getAttribute('clicked') === '1') {
return false;
} else {
this.setAttribute('clicked', '1');
}
captureAction();
2016-02-18 15:18:09 +03:00
}
2015-07-27 12:15:25 -07:00
/**
* When Cancel button is clicked, destroy camera preview and return with error callback
*/
2016-02-18 15:18:09 +03:00
function onCameraCancelButtonClick() {
2015-07-27 12:15:25 -07:00
// Make sure user can't click more than once
if (this.getAttribute('clicked') === '1') {
return false;
} else {
this.setAttribute('clicked', '1');
}
destroyCameraPreview();
errorCallback('no image selected');
2016-02-18 15:18:09 +03:00
}
2015-07-27 12:15:25 -07:00
2015-05-14 10:35:53 -07:00
/**
* When the phone orientation change, get the event and change camera preview rotation
* @param {Object} e - SimpleOrientationSensorOrientationChangedEventArgs
*/
2016-02-18 15:18:09 +03:00
function onOrientationChange(e) {
2015-05-14 10:35:53 -07:00
setPreviewRotation(e.orientation);
2016-02-18 15:18:09 +03:00
}
2015-05-14 10:35:53 -07:00
/**
* Converts SimpleOrientation to a VideoRotation to remove difference between camera sensor orientation
* and video orientation
* @param {number} orientation - Windows.Devices.Sensors.SimpleOrientation
* @return {number} - Windows.Media.Capture.VideoRotation
*/
2016-02-18 15:18:09 +03:00
function orientationToRotation(orientation) {
2015-05-14 10:35:53 -07:00
// VideoRotation enumerable and BitmapRotation enumerable have the same values
// https://msdn.microsoft.com/en-us/library/windows/apps/windows.media.capture.videorotation.aspx
// https://msdn.microsoft.com/en-us/library/windows/apps/windows.graphics.imaging.bitmaprotation.aspx
switch (orientation) {
// portrait
case Windows.Devices.Sensors.SimpleOrientation.notRotated:
return Windows.Media.Capture.VideoRotation.clockwise90Degrees;
// landscape
case Windows.Devices.Sensors.SimpleOrientation.rotated90DegreesCounterclockwise:
return Windows.Media.Capture.VideoRotation.none;
// portrait-flipped (not supported by WinPhone Apps)
case Windows.Devices.Sensors.SimpleOrientation.rotated180DegreesCounterclockwise:
// Falling back to portrait default
return Windows.Media.Capture.VideoRotation.clockwise90Degrees;
// landscape-flipped
case Windows.Devices.Sensors.SimpleOrientation.rotated270DegreesCounterclockwise:
return Windows.Media.Capture.VideoRotation.clockwise180Degrees;
// faceup & facedown
default:
// Falling back to portrait default
return Windows.Media.Capture.VideoRotation.clockwise90Degrees;
}
2016-02-18 15:18:09 +03:00
}
2015-05-14 10:35:53 -07:00
/**
* Rotates the current MediaCapture's video
* @param {number} orientation - Windows.Devices.Sensors.SimpleOrientation
*/
2016-02-18 15:18:09 +03:00
function setPreviewRotation(orientation) {
2015-05-14 10:35:53 -07:00
capture.setPreviewRotation(orientationToRotation(orientation));
2016-02-18 15:18:09 +03:00
}
2015-05-14 10:35:53 -07:00
try {
createCameraUI();
startCameraPreview();
} catch (ex) {
errorCallback(ex);
}
}
function takePictureFromCameraWindows(successCallback, errorCallback, args) {
var destinationType = args[1],
targetWidth = args[3],
targetHeight = args[4],
encodingType = args[5],
allowCrop = !!args[7],
saveToPhotoAlbum = args[9],
2015-05-20 16:31:50 -07:00
WMCapture = Windows.Media.Capture,
cameraCaptureUI = new WMCapture.CameraCaptureUI();
2015-05-14 10:35:53 -07:00
cameraCaptureUI.photoSettings.allowCropping = allowCrop;
if (encodingType == Camera.EncodingType.PNG) {
2015-05-20 16:31:50 -07:00
cameraCaptureUI.photoSettings.format = WMCapture.CameraCaptureUIPhotoFormat.png;
} else {
2015-05-20 16:31:50 -07:00
cameraCaptureUI.photoSettings.format = WMCapture.CameraCaptureUIPhotoFormat.jpeg;
}
2014-08-27 17:15:47 +04:00
// decide which max pixels should be supported by targetWidth or targetHeight.
2015-05-18 21:38:34 -07:00
var maxRes = null;
2015-05-20 16:31:50 -07:00
var UIMaxRes = WMCapture.CameraCaptureUIMaxPhotoResolution;
2015-08-03 12:57:58 -07:00
var totalPixels = targetWidth * targetHeight;
if (targetWidth == -1 && targetHeight == -1) {
maxRes = UIMaxRes.highestAvailable;
}
// Temp fix for CB-10539
/*else if (totalPixels <= 320 * 240) {
maxRes = UIMaxRes.verySmallQvga;
}*/
else if (totalPixels <= 640 * 480) {
2015-08-04 12:04:17 -07:00
maxRes = UIMaxRes.smallVga;
} else if (totalPixels <= 1024 * 768) {
maxRes = UIMaxRes.mediumXga;
} else if (totalPixels <= 3 * 1000 * 1000) {
maxRes = UIMaxRes.large3M;
} else if (totalPixels <= 5 * 1000 * 1000) {
maxRes = UIMaxRes.veryLarge5M;
} else {
maxRes = UIMaxRes.highestAvailable;
}
2014-08-27 17:15:47 +04:00
2015-05-18 21:38:34 -07:00
cameraCaptureUI.photoSettings.maxResolution = maxRes;
var cameraPicture;
var savePhotoOnFocus = function() {
2015-04-03 11:08:00 -07:00
window.removeEventListener("focus", savePhotoOnFocus);
// call only when the app is in focus again
savePhoto(cameraPicture, {
destinationType: destinationType,
targetHeight: targetHeight,
targetWidth: targetWidth,
encodingType: encodingType,
saveToPhotoAlbum: saveToPhotoAlbum
}, successCallback, errorCallback);
}
// add and delete focus eventHandler to capture the focus back from cameraUI to app
window.addEventListener("focus", savePhotoOnFocus);
cameraCaptureUI.captureFileAsync(WMCapture.CameraCaptureUIMode.photo).done(function(picture) {
if (!picture) {
errorCallback("User didn't capture a photo.");
window.removeEventListener("focus", savePhotoOnFocus);
return;
}
cameraPicture = picture;
}, function() {
errorCallback("Fail to capture a photo.");
window.removeEventListener("focus", savePhotoOnFocus);
});
}
function savePhoto(picture, options, successCallback, errorCallback) {
// success callback for capture operation
var success = function(picture) {
if (options.destinationType == Camera.DestinationType.FILE_URI || options.destinationType == Camera.DestinationType.NATIVE_URI) {
if (options.targetHeight > 0 && options.targetWidth > 0) {
resizeImage(successCallback, errorCallback, picture, options.targetWidth, options.targetHeight, options.encodingType);
2015-04-03 11:08:00 -07:00
} else {
2015-05-18 21:38:34 -07:00
picture.copyAsync(getAppData().localFolder, picture.name, OptUnique).done(function (copiedFile) {
successCallback("ms-appdata:///local/" + copiedFile.name);
},errorCallback);
}
} else {
if (options.targetHeight > 0 && options.targetWidth > 0) {
resizeImageBase64(successCallback, errorCallback, picture, options.targetWidth, options.targetHeight);
} else {
2015-05-18 21:38:34 -07:00
fileIO.readBufferAsync(picture).done(function(buffer) {
var strBase64 = encodeToBase64String(buffer);
picture.deleteAsync().done(function() {
2015-04-03 11:08:00 -07:00
successCallback(strBase64);
}, function(err) {
errorCallback(err);
});
}, errorCallback);
2015-04-03 11:08:00 -07:00
}
}
};
if (!options.saveToPhotoAlbum) {
success(picture);
return;
} else {
2015-04-03 11:08:00 -07:00
var savePicker = new Windows.Storage.Pickers.FileSavePicker();
var saveFile = function(file) {
if (file) {
// Prevent updates to the remote version of the file until we're done
Windows.Storage.CachedFileManager.deferUpdates(file);
picture.moveAndReplaceAsync(file)
.then(function() {
// Let Windows know that we're finished changing the file so
// the other app can update the remote version of the file.
return Windows.Storage.CachedFileManager.completeUpdatesAsync(file);
})
.done(function(updateStatus) {
if (updateStatus === Windows.Storage.Provider.FileUpdateStatus.complete) {
success(picture);
} else {
errorCallback("File update status is not complete.");
}
}, errorCallback);
} else {
errorCallback("Failed to select a file.");
}
};
2015-05-18 21:38:34 -07:00
savePicker.suggestedStartLocation = pickerLocId.picturesLibrary;
if (options.encodingType === Camera.EncodingType.PNG) {
2015-04-03 11:08:00 -07:00
savePicker.fileTypeChoices.insert("PNG", [".png"]);
savePicker.suggestedFileName = "photo.png";
} else {
2015-04-03 11:08:00 -07:00
savePicker.fileTypeChoices.insert("JPEG", [".jpg"]);
savePicker.suggestedFileName = "photo.jpg";
2013-07-29 19:04:44 -07:00
}
2015-04-03 11:08:00 -07:00
// If Windows Phone 8.1 use pickSaveFileAndContinue()
if (navigator.appVersion.indexOf('Windows Phone 8.1') >= 0) {
/*
Need to add and remove an event listener to catch activation state
Using FileSavePicker will suspend the app and it's required to catch the pickSaveFileContinuation
https://msdn.microsoft.com/en-us/library/windows/apps/xaml/dn631755.aspx
*/
var fileSaveHandler = function(eventArgs) {
if (eventArgs.kind === Windows.ApplicationModel.Activation.ActivationKind.pickSaveFileContinuation) {
var file = eventArgs.file;
saveFile(file);
2015-05-18 21:38:34 -07:00
webUIApp.removeEventListener("activated", fileSaveHandler);
2015-04-03 11:08:00 -07:00
}
};
2015-05-18 21:38:34 -07:00
webUIApp.addEventListener("activated", fileSaveHandler);
savePicker.pickSaveFileAndContinue();
} else {
savePicker.pickSaveFileAsync()
.done(saveFile, errorCallback);
}
}
}
2013-07-29 19:04:44 -07:00
2014-04-08 15:53:51 -07:00
require("cordova/exec/proxy").add("Camera",module.exports);