Skip to content

Commit

Permalink
Update browser plugin
Browse files Browse the repository at this point in the history
- Add ability to specify camera container, buttons and source input
- Add cancel button, that can stop video stream
- Refactor code
  • Loading branch information
GedasGa authored and erisu committed Apr 8, 2019
1 parent fae190e commit b05b77a
Show file tree
Hide file tree
Showing 2 changed files with 160 additions and 87 deletions.
197 changes: 134 additions & 63 deletions src/browser/CameraProxy.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,105 +19,176 @@
*
*/

var HIGHEST_POSSIBLE_Z_INDEX = 2147483647;
let localMediaStream;

function takePicture (success, error, opts) {
function takePicture (successCallback, errorCallback, opts) {
if (opts && opts[2] === 1) {
capture(success, error, opts);
capture(successCallback, errorCallback, opts);
} else {
var input = document.createElement('input');
input.style.position = 'relative';
input.style.zIndex = HIGHEST_POSSIBLE_Z_INDEX;
input.className = 'cordova-camera-select';
input.type = 'file';
input.name = 'files[]';
const customSourceInput = opts[15];

input.onchange = function (inputEvent) {
var reader = new FileReader(); /* eslint no-undef : 0 */
reader.onload = function (readerEvent) {
input.parentNode.removeChild(input);
let sourceInput = customSourceInput ? document.getElementById(customSourceInput) : createSourceInput();

var imageData = readerEvent.target.result;
handleSourceInput(successCallback, sourceInput);

return success(imageData.substr(imageData.indexOf(',') + 1));
};

reader.readAsDataURL(inputEvent.target.files[0]);
};
if (!customSourceInput) {
document.body.appendChild(sourceInput);
}
}
}

document.body.appendChild(input);
function capture (successCallback, errorCallback, opts) {
let targetWidth = opts[3];
let targetHeight = opts[4];
const customCameraContainer = opts[12];
const customCaptureButton = opts[13];
const customCancelButton = opts[14];

let parent = customCameraContainer ? document.getElementById(customCameraContainer) : createCameraContainer();
let video = createVideoStreamContainer(parent, targetWidth, targetHeight);
let captureButton = customCaptureButton ? document.getElementById(customCaptureButton) : createButton(parent, 'Capture');
let cancelButton = customCancelButton ? document.getElementById(customCancelButton) : createButton(parent, 'Cancel');

// start video stream
startLocalMediaStream(errorCallback, video);

// if custom camera container is not set by the user,
// append parent to the document.body
if (!customCameraContainer) {
document.body.appendChild(video.parentNode);
}

// handle button click events
handleCaptureButton(successCallback, errorCallback, captureButton, video, customCameraContainer);
handleCancelButton(cancelButton, video, customCameraContainer);
}

function capture (success, errorCallback, opts) {
var localMediaStream;
var targetWidth = opts[3];
var targetHeight = opts[4];
function createCameraContainer () {
let parent = document.createElement('div');
parent.style.position = 'relative';
parent.style.zIndex = '2147483647'; // set highest possible z index
parent.className = 'cordova-camera-capture';

return parent;
}

function createVideoStreamContainer (parent, targetWidth, targetHeight) {
targetWidth = targetWidth === -1 ? 320 : targetWidth;
targetHeight = targetHeight === -1 ? 240 : targetHeight;

var video = document.createElement('video');
var button = document.createElement('button');
var parent = document.createElement('div');
parent.style.position = 'relative';
parent.style.zIndex = HIGHEST_POSSIBLE_Z_INDEX;
parent.className = 'cordova-camera-capture';
let video = document.createElement('video');
video.width = targetWidth;
video.height = targetHeight;

parent.appendChild(video);

return video;
}

function createButton (parent, innerText) {
let button = document.createElement('button');
button.innerHTML = innerText;

parent.appendChild(button);

video.width = targetWidth;
video.height = targetHeight;
button.innerHTML = 'Capture!';
return button;
}

button.onclick = function () {
function handleCaptureButton (successCallback, errorCallback, captureButton, video, customCameraContainer) {
captureButton.onclick = function () {
// create a canvas and capture a frame from video stream
var canvas = document.createElement('canvas');
canvas.width = targetWidth;
canvas.height = targetHeight;
canvas.getContext('2d').drawImage(video, 0, 0, targetWidth, targetHeight);
let canvas = document.createElement('canvas');
canvas.width = video.width;
canvas.height = video.height;
canvas.getContext('2d').drawImage(video, 0, 0, video.width, video.height);

// convert image stored in canvas to base64 encoded image
var imageData = canvas.toDataURL('image/png');
let 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.
if (localMediaStream.stop) {
localMediaStream.stop();
} else {
localMediaStream.getTracks().forEach(function (track) {
track.stop();
});
}
parent.parentNode.removeChild(parent);
// stop video stream
stopLocalMediaStream(video, customCameraContainer);

return success(imageData);
return successCallback(imageData);
};
}

navigator.getUserMedia = navigator.getUserMedia ||
navigator.webkitGetUserMedia ||
navigator.mozGetUserMedia ||
navigator.msGetUserMedia;
function handleCancelButton (cancelButton, video, customCameraContainer) {
cancelButton.onclick = function () {
// stop video stream
stopLocalMediaStream(video, customCameraContainer);
};
}

var successCallback = function (stream) {
function startLocalMediaStream (errorCallback, video) {

const successCallback = function (stream) {
localMediaStream = stream;
if ('srcObject' in video) {
video.srcObject = localMediaStream;
} else {
video.src = window.URL.createObjectURL(localMediaStream);
}
video.src = window.URL.createObjectURL(localMediaStream);
video.play();
document.body.appendChild(parent);
};

navigator.getUserMedia = navigator.getUserMedia ||
navigator.webkitGetUserMedia ||
navigator.mozGetUserMedia ||
navigator.msGetUserMedia;

if (navigator.getUserMedia) {
navigator.getUserMedia({video: true, audio: false}, successCallback, errorCallback);
navigator.getUserMedia({video: true, audio: true}, successCallback, errorCallback);
} else {
alert('Browser does not support camera :(');
alert('Your browser does not support camera.');
}
}

function stopLocalMediaStream (video, customCameraContainer) {
// stop video stream, remove video and captureButton.
// note: MediaStream.stop() is deprecated as of Chrome 47.
if (localMediaStream.stop) {
localMediaStream.stop();
} else {
localMediaStream.getTracks().forEach(function (track) {
track.stop();
});
}

// remove newly created elements
removeAppendedCameraElements(video, customCameraContainer);
}

function removeAppendedCameraElements (video, customCameraContainer) {
const parent = video.parentNode;
if (!customCameraContainer) {
parent.parentNode.removeChild(parent);
} else {
parent.removeChild(video);
}
}

function createSourceInput () {
let input = document.createElement('input');
input.style.position = 'relative';
input.style.zIndex = '2147483647'; // set highest possible z index
input.className = 'cordova-camera-select';
input.type = 'file';
input.name = 'files[]';

return input;
}

function handleSourceInput (successCallback, sourceInput) {
sourceInput.onchange = function (inputEvent) {
let reader = new FileReader(); /* eslint no-undef : 0 */
reader.onload = function (readerEvent) {
sourceInput.parentNode.removeChild(sourceInput);

const imageData = readerEvent.target.result;

return successCallback(imageData.substr(imageData.indexOf(',') + 1));
};
reader.readAsDataURL(inputEvent.target.files[0]);
};
}

module.exports = {
takePicture: takePicture,
cleanup: function () {}
Expand Down
50 changes: 26 additions & 24 deletions www/Camera.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,9 @@
*
*/

var argscheck = require('cordova/argscheck');
var exec = require('cordova/exec');
var Camera = require('./Camera');
// XXX: commented out
// CameraPopoverHandle = require('./CameraPopoverHandle');
const argscheck = require('cordova/argscheck');
const exec = require('cordova/exec');
const Camera = require('./Camera');

/**
* @namespace navigator
Expand All @@ -32,10 +30,10 @@ var Camera = require('./Camera');
/**
* @exports camera
*/
var cameraExport = {};
const cameraExport = {};

// Tack on the Camera Constants to the base camera plugin.
for (var key in Camera) {
for (let key in Camera) {
cameraExport[key] = Camera[key];
}

Expand Down Expand Up @@ -134,27 +132,31 @@ for (var key in Camera) {
cameraExport.getPicture = function (successCallback, errorCallback, options) {
argscheck.checkArgs('fFO', 'Camera.getPicture', arguments);
options = options || {};
var getValue = argscheck.getValue;
const getValue = argscheck.getValue;

var quality = getValue(options.quality, 50);
var destinationType = getValue(options.destinationType, Camera.DestinationType.FILE_URI);
var sourceType = getValue(options.sourceType, Camera.PictureSourceType.CAMERA);
var targetWidth = getValue(options.targetWidth, -1);
var targetHeight = getValue(options.targetHeight, -1);
var encodingType = getValue(options.encodingType, Camera.EncodingType.JPEG);
var mediaType = getValue(options.mediaType, Camera.MediaType.PICTURE);
var allowEdit = !!options.allowEdit;
var correctOrientation = !!options.correctOrientation;
var saveToPhotoAlbum = !!options.saveToPhotoAlbum;
var popoverOptions = getValue(options.popoverOptions, null);
var cameraDirection = getValue(options.cameraDirection, Camera.Direction.BACK);
const quality = getValue(options.quality, 50);
const destinationType = getValue(options.destinationType, Camera.DestinationType.FILE_URI);
const sourceType = getValue(options.sourceType, Camera.PictureSourceType.CAMERA);
const targetWidth = getValue(options.targetWidth, -1);
const targetHeight = getValue(options.targetHeight, -1);
const encodingType = getValue(options.encodingType, Camera.EncodingType.JPEG);
const mediaType = getValue(options.mediaType, Camera.MediaType.PICTURE);
const allowEdit = !!options.allowEdit;
const correctOrientation = !!options.correctOrientation;
const saveToPhotoAlbum = !!options.saveToPhotoAlbum;
const popoverOptions = getValue(options.popoverOptions, null);
const cameraDirection = getValue(options.cameraDirection, Camera.Direction.BACK);

var args = [quality, destinationType, sourceType, targetWidth, targetHeight, encodingType,
mediaType, allowEdit, correctOrientation, saveToPhotoAlbum, popoverOptions, cameraDirection];
const customCameraContainer = getValue(options.customCameraContainer, null);
const customCaptureButton = getValue(options.customCaptureButton, null);
const customCancelButton = getValue(options.customCancelButton, null);
const customSourceInput = getValue(options.customSourceInput, null);

const args = [quality, destinationType, sourceType, targetWidth, targetHeight, encodingType,
mediaType, allowEdit, correctOrientation, saveToPhotoAlbum, popoverOptions, cameraDirection,
customCameraContainer, customCaptureButton, customCancelButton, customSourceInput];

exec(successCallback, errorCallback, 'Camera', 'takePicture', args);
// XXX: commented out
// return new CameraPopoverHandle();
};

/**
Expand Down

0 comments on commit b05b77a

Please sign in to comment.