Migrated camera to be a cordova plugin Reviewed by Bryan Higgins <[email protected]> Tested by Tracy Li <[email protected]>
Project: http://git-wip-us.apache.org/repos/asf/cordova-blackberry/repo Commit: http://git-wip-us.apache.org/repos/asf/cordova-blackberry/commit/042526cf Tree: http://git-wip-us.apache.org/repos/asf/cordova-blackberry/tree/042526cf Diff: http://git-wip-us.apache.org/repos/asf/cordova-blackberry/diff/042526cf Branch: refs/heads/master Commit: 042526cf06f779e0f1f110a044751f4e3678f463 Parents: b0edce5 Author: Rosa Tse <[email protected]> Authored: Thu Mar 28 16:23:42 2013 -0400 Committer: Bryan Higgins <[email protected]> Committed: Fri May 3 10:13:29 2013 -0400 ---------------------------------------------------------------------- .../project/cordova/lib/native-packager.js | 2 +- .../bin/templates/project/plugins/Camera/index.js | 124 ++++++ blackberry10/bin/test/plugins/Camera/index.js | 298 +++++++++++++++ blackberry10/javascript/cordova.blackberry10.js | 142 ++----- 4 files changed, 465 insertions(+), 101 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/cordova-blackberry/blob/042526cf/blackberry10/bin/templates/project/cordova/lib/native-packager.js ---------------------------------------------------------------------- diff --git a/blackberry10/bin/templates/project/cordova/lib/native-packager.js b/blackberry10/bin/templates/project/cordova/lib/native-packager.js index 2681bf2..8529458 100644 --- a/blackberry10/bin/templates/project/cordova/lib/native-packager.js +++ b/blackberry10/bin/templates/project/cordova/lib/native-packager.js @@ -68,7 +68,7 @@ function generateTabletXMLFile(session, config) { //Enable slog2 output if debugging if (session.debug) { xmlObject.env.push({ - _attr : { value : 'slog2', var : 'CONSOLE_MODE' } + _attr : { value : 'slog2', 'var' : 'CONSOLE_MODE' } }); } http://git-wip-us.apache.org/repos/asf/cordova-blackberry/blob/042526cf/blackberry10/bin/templates/project/plugins/Camera/index.js ---------------------------------------------------------------------- diff --git a/blackberry10/bin/templates/project/plugins/Camera/index.js b/blackberry10/bin/templates/project/plugins/Camera/index.js new file mode 100644 index 0000000..922f049 --- /dev/null +++ b/blackberry10/bin/templates/project/plugins/Camera/index.js @@ -0,0 +1,124 @@ +/* + * Copyright 2010-2011 Research In Motion Limited. + * + * Licensed 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. + */ +var PictureSourceType = { + PHOTOLIBRARY : 0, // Choose image from picture library (same as SAVEDPHOTOALBUM for Android) + CAMERA : 1, // Take picture from camera + SAVEDPHOTOALBUM : 2 // Choose image from picture library (same as PHOTOLIBRARY for Android) + }, + DestinationType = { + DATA_URL: 0, // Return base64 encoded string + FILE_URI: 1, // Return file uri (content://media/external/images/media/2 for Android) + NATIVE_URI: 2 // Return native uri (eg. asset-library://... for iOS) + }; + +function encodeBase64(filePath, callback) { + var sandbox = window.qnx.webplatform.getController().setFileSystemSandbox, // save original sandbox value + errorHandler = function (err) { + var msg = "An error occured: "; + + switch (err.code) { + case FileError.NOT_FOUND_ERR: + msg += "File or directory not found"; + break; + + case FileError.NOT_READABLE_ERR: + msg += "File or directory not readable"; + break; + + case FileError.PATH_EXISTS_ERR: + msg += "File or directory already exists"; + break; + + case FileError.TYPE_MISMATCH_ERR: + msg += "Invalid file type"; + break; + + default: + msg += "Unknown Error"; + break; + }; + + // set it back to original value + window.qnx.webplatform.getController().setFileSystemSandbox = sandbox; + callback(msg); + }, + gotFile = function (fileEntry) { + fileEntry.file(function (file) { + var reader = new FileReader(); + + reader.onloadend = function (e) { + // set it back to original value + window.qnx.webplatform.getController().setFileSystemSandbox = sandbox; + callback(this.result); + }; + + reader.readAsDataURL(file); + }, errorHandler); + }, + onInitFs = function (fs) { + window.qnx.webplatform.getController().setFileSystemSandbox = false; + fs.root.getFile(filePath, {create: false}, gotFile, errorHandler); + }; + + window.webkitRequestFileSystem(window.TEMPORARY, 10 * 1024 * 1024, onInitFs, errorHandler); // set size to 10MB max +} + +module.exports = { + takePicture: function (success, fail, args, env) { + var destinationType = JSON.parse(decodeURIComponent(args[1])), + sourceType = JSON.parse(decodeURIComponent(args[2])), + result = new PluginResult(args, env), + done = function (data) { + if (destinationType === DestinationType.FILE_URI) { + data = "file://" + data; + result.callbackOk(data, false); + } else { + encodeBase64(data, function (data) { + if (/^data:/.test(data)) { + data = data.slice(data.indexOf(",") + 1); + result.callbackOk(data, false); + } else { + result.callbackError(data, false); + } + }); + } + }, + cancel = function (reason) { + result.callbackError(reason, false); + }, + invoked = function (error) { + if (error) { + result.callbackError(error, false); + } + }; + + switch(sourceType) { + case PictureSourceType.CAMERA: + window.qnx.webplatform.getApplication().cards.camera.open("photo", done, cancel, invoked); + break; + + case PictureSourceType.PHOTOLIBRARY: + case PictureSourceType.SAVEDPHOTOALBUM: + window.qnx.webplatform.getApplication().cards.filePicker.open({ + mode: "Picker", + type: ["picture"] + }, done, cancel, invoked); + break; + } + + result.noResult(true); + } +}; http://git-wip-us.apache.org/repos/asf/cordova-blackberry/blob/042526cf/blackberry10/bin/test/plugins/Camera/index.js ---------------------------------------------------------------------- diff --git a/blackberry10/bin/test/plugins/Camera/index.js b/blackberry10/bin/test/plugins/Camera/index.js new file mode 100644 index 0000000..e629a41 --- /dev/null +++ b/blackberry10/bin/test/plugins/Camera/index.js @@ -0,0 +1,298 @@ +/* +* Copyright 2013 Research In Motion Limited. +* +* Licensed 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. +*/ +describe("Camera", function () { + var _apiDir = __dirname + "./../../../templates/project/plugins/Camera/", + index, + mockDone, + mockCancel, + mockError, + mockedEnv = { + response: { + send: jasmine.createSpy() + }, + webview: { + executeJavaScript: jasmine.createSpy() + } + }, + PictureSourceType = { + PHOTOLIBRARY : 0, // Choose image from picture library (same as SAVEDPHOTOALBUM for Android) + CAMERA : 1, // Take picture from camera + SAVEDPHOTOALBUM : 2 // Choose image from picture library (same as PHOTOLIBRARY for Android) + }, + DestinationType = { + DATA_URL: 0, // Return base64 encoded string + FILE_URI: 1, // Return file uri (content://media/external/images/media/2 for Android) + NATIVE_URI: 2 // Return native uri (eg. asset-library://... for iOS) + }, + readFail, + mockBase64Data = "/9j/4QHRw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg=="; + + function mockOpen(options, done, cancel, invoked) { + if (!mockError) { + invoked(); + } + + if (mockDone) { + done(mockDone.path); + } else if (mockCancel) { + cancel(mockCancel.reason); + } else if (mockError) { + invoked(mockError.error); + } + } + + beforeEach(function () { + index = require(_apiDir + "index"); + mockedEnv.response.send.reset(); + mockedEnv.webview.executeJavaScript.reset(); + }); + + afterEach(function () { + index = null; + mockDone = null; + mockCancel = null; + mockError = null; + readFail = false; + }); + + describe("takePicture", function () { + beforeEach(function () { + GLOBAL.window = { + qnx: { + webplatform: { + getApplication: function () { + return { + cards: { + camera: { + open: jasmine.createSpy().andCallFake(mockOpen) + }, + filePicker: { + open: jasmine.createSpy().andCallFake(mockOpen) + } + } + }; + }, + getController: function () { + return { + setFileSystemSandbox: true + }; + } + } + }, + webkitRequestFileSystem: jasmine.createSpy().andCallFake(function (type, size, success, error) { + success({ + root: { + getFile: jasmine.createSpy().andCallFake(function (path, options, success, error) { + if (readFail) { + error({ + code: -1 + }); + } else { + success({ + file: jasmine.createSpy().andCallFake(function (cb) { + cb(); + }) + }); + } + }) + } + }); + }) + }; + + GLOBAL.FileReader = function () { + return { + onloadend: jasmine.createSpy(), + readAsDataURL: jasmine.createSpy().andCallFake(function (file) { + this.onloadend.apply({ + result: "data:image/jpeg;base64," + mockBase64Data + }); + }) + }; + }; + + GLOBAL.FileError = { + NOT_FOUND_ERR: 1, + NOT_READABLE_ERR: 4, + PATH_EXISTS_ERR: 12, + TYPE_MISMATCH_ERR: 11 + }; + + GLOBAL.PluginResult = function (args, env) {}; + GLOBAL.PluginResult.prototype.callbackOk = jasmine.createSpy(); + GLOBAL.PluginResult.prototype.callbackError = jasmine.createSpy(); + GLOBAL.PluginResult.prototype.noResult = jasmine.createSpy(); + }); + + afterEach(function () { + delete GLOBAL.window; + delete GLOBAL.FileReader; + delete GLOBAL.PluginResult; + }); + + it("calls PluginResult.callbackOk if invoke camera is successful and image doesn't need encoding", function () { + mockDone = { + path: "/foo/bar/abc.jpg" + }; + + index.takePicture(undefined, undefined, { + "1": DestinationType.FILE_URI.toString(), + "2": PictureSourceType.CAMERA.toString(), + callbackId: "123" + }, mockedEnv); + + expect(PluginResult.prototype.noResult).toHaveBeenCalledWith(true); + expect(PluginResult.prototype.callbackOk).toHaveBeenCalledWith("file://" + mockDone.path, false); + }); + + it("calls PluginResult.callbackOk if invoke camera and base64 encode image is successful", function () { + mockDone = { + path: "/foo/bar/abc.jpg" + }; + + index.takePicture(undefined, undefined, { + "1": DestinationType.DATA_URL.toString(), + "2": PictureSourceType.CAMERA.toString(), + callbackId: "123" + }, mockedEnv); + + expect(PluginResult.prototype.noResult).toHaveBeenCalledWith(true); + expect(PluginResult.prototype.callbackOk).toHaveBeenCalledWith(mockBase64Data, false); + }); + + it("calls PluginResult.callbackError if invoke camera is successful but base64 encode image failed", function () { + mockDone = { + path: "/foo/bar/abc.jpg" + }; + readFail = true; + + index.takePicture(undefined, undefined, { + "1": DestinationType.DATA_URL.toString(), + "2": PictureSourceType.CAMERA.toString(), + callbackId: "123" + }, mockedEnv); + + expect(PluginResult.prototype.noResult).toHaveBeenCalledWith(true); + expect(PluginResult.prototype.callbackError).toHaveBeenCalledWith("An error occured: Unknown Error", false); + }); + + it("calls PluginResult.callbackError if invoke camera is cancelled by user", function () { + mockCancel = { + reason: "done" + }; + + index.takePicture(undefined, undefined, { + "1": DestinationType.FILE_URI.toString(), + "2": PictureSourceType.CAMERA.toString(), + callbackId: "123" + }, mockedEnv); + + expect(PluginResult.prototype.noResult).toHaveBeenCalledWith(true); + expect(PluginResult.prototype.callbackError).toHaveBeenCalledWith(mockCancel.reason, false); + }); + + it("calls PluginResult.callbackError if invoke camera encounters error", function () { + mockError = { + error: "Camera error" + }; + + index.takePicture(undefined, undefined, { + "1": DestinationType.FILE_URI.toString(), + "2": PictureSourceType.CAMERA.toString(), + callbackId: "123" + }, mockedEnv); + + expect(PluginResult.prototype.noResult).toHaveBeenCalledWith(true); + expect(PluginResult.prototype.callbackError).toHaveBeenCalledWith(mockError.error, false); + }); + + it("calls PluginResult.callbackOk if invoke file picker is successful and image doesn't need encoding", function () { + mockDone = { + path: "/foo/bar/abc.jpg" + }; + + index.takePicture(undefined, undefined, { + "1": DestinationType.FILE_URI.toString(), + "2": PictureSourceType.PHOTOLIBRARY.toString(), + callbackId: "123" + }, mockedEnv); + + expect(PluginResult.prototype.noResult).toHaveBeenCalledWith(true); + expect(PluginResult.prototype.callbackOk).toHaveBeenCalledWith("file://" + mockDone.path, false); + }); + + it("calls PluginResult.callbackOk if invoke file picker and base64 encode image is successful", function () { + mockDone = { + path: "/foo/bar/abc.jpg" + }; + + index.takePicture(undefined, undefined, { + "1": DestinationType.DATA_URL.toString(), + "2": PictureSourceType.PHOTOLIBRARY.toString(), + callbackId: "123" + }, mockedEnv); + + expect(PluginResult.prototype.noResult).toHaveBeenCalledWith(true); + expect(PluginResult.prototype.callbackOk).toHaveBeenCalledWith(mockBase64Data, false); + }); + + it("calls PluginResult.callbackError if invoke file picker is successful but base64 encode image failed", function () { + mockDone = { + path: "/foo/bar/abc.jpg" + }; + readFail = true; + + index.takePicture(undefined, undefined, { + "1": DestinationType.DATA_URL.toString(), + "2": PictureSourceType.PHOTOLIBRARY.toString(), + callbackId: "123" + }, mockedEnv); + + expect(PluginResult.prototype.noResult).toHaveBeenCalledWith(true); + expect(PluginResult.prototype.callbackError).toHaveBeenCalledWith("An error occured: Unknown Error", false); + }); + + it("calls PluginResult.callbackError if invoke file picker is cancelled by user", function () { + mockCancel = { + reason: "cancel" + }; + + index.takePicture(undefined, undefined, { + "1": DestinationType.DATA_URL.toString(), + "2": PictureSourceType.PHOTOLIBRARY.toString(), + callbackId: "123" + }, mockedEnv); + + expect(PluginResult.prototype.noResult).toHaveBeenCalledWith(true); + expect(PluginResult.prototype.callbackError).toHaveBeenCalledWith(mockCancel.reason, false); + }); + + it("calls PluginResult.callbackError if invoke file picker encounters error", function () { + mockError = { + error: "File picker error" + }; + + index.takePicture(undefined, undefined, { + "1": DestinationType.DATA_URL.toString(), + "2": PictureSourceType.PHOTOLIBRARY.toString(), + callbackId: "123" + }, mockedEnv); + + expect(PluginResult.prototype.noResult).toHaveBeenCalledWith(true); + expect(PluginResult.prototype.callbackError).toHaveBeenCalledWith(mockError.error, false); + }); + }); +}); http://git-wip-us.apache.org/repos/asf/cordova-blackberry/blob/042526cf/blackberry10/javascript/cordova.blackberry10.js ---------------------------------------------------------------------- diff --git a/blackberry10/javascript/cordova.blackberry10.js b/blackberry10/javascript/cordova.blackberry10.js index 36d9ad1..27c3c3a 100644 --- a/blackberry10/javascript/cordova.blackberry10.js +++ b/blackberry10/javascript/cordova.blackberry10.js @@ -1,8 +1,8 @@ // Platform: blackberry10 -// commit a69c579c923e9bb0b709a64b782d55a137edb043 +// commit 4c7d302ca09258a6ab9306e7647b1478b06c498a -// File generated at :: Tue Mar 26 2013 15:21:10 GMT-0400 (EDT) +// File generated at :: Mon Apr 01 2013 10:04:19 GMT-0400 (EDT) /* Licensed to the Apache Software Foundation (ASF) under one @@ -948,8 +948,15 @@ module.exports = { define("cordova/exec", function(require, exports, module) { var cordova = require('cordova'), - platform = require('cordova/platform'), - utils = require('cordova/utils'); + plugins = { + 'Accelerometer' : require('cordova/plugin/blackberry10/accelerometer'), + 'Compass' : require('cordova/plugin/blackberry10/magnetometer'), + 'Capture' : require('cordova/plugin/blackberry10/capture'), + 'Logger' : require('cordova/plugin/blackberry10/logger'), + 'Notification' : require('cordova/plugin/blackberry10/notification'), + 'Media': require('cordova/plugin/blackberry10/media'), + 'FileTransfer': require('cordova/plugin/blackberry10/fileTransfer') + }; /** * Execute a cordova command. It is up to the native side whether this action @@ -965,14 +972,11 @@ var cordova = require('cordova'), * @param {String} action Action to be run in cordova * @param {String[]} [args] Zero or more arguments to pass to the method */ - -module.exports = function(success, fail, service, action, args) { - try { - require('cordova/plugin/blackberry10/manager').exec(success, fail, service, action, args); - return null; - } catch (e) { - utils.alert("Error: "+e); +module.exports = function (success, fail, service, action, args) { + if (plugins[service] && plugins[service][action]) { + return plugins[service][action](args, success, fail); } + return webworks.exec(success, fail, service, action, args); }; }); @@ -3867,23 +3871,6 @@ module.exports = { }); -// file: lib/blackberry10/plugin/blackberry10/camera.js -define("cordova/plugin/blackberry10/camera", function(require, exports, module) { - -var cordova = require('cordova'); - -module.exports = { - takePicture: function (args, win, fail) { - var noop = function () {}; - blackberry.invoke.card.invokeCamera("photo", function (path) { - win("file://" + path); - }, noop, noop); - return { "status" : cordova.callbackStatus.NO_RESULT, "message" : "WebWorks Is On It" }; - } -}; - -}); - // file: lib/blackberry10/plugin/blackberry10/capture.js define("cordova/plugin/blackberry10/capture", function(require, exports, module) { @@ -4723,45 +4710,6 @@ module.exports = { }); -// file: lib/blackberry10/plugin/blackberry10/manager.js -define("cordova/plugin/blackberry10/manager", function(require, exports, module) { - -var cordova = require('cordova'), - plugins = { - 'Accelerometer' : require('cordova/plugin/blackberry10/accelerometer'), - 'Compass' : require('cordova/plugin/blackberry10/magnetometer'), - 'Camera' : require('cordova/plugin/blackberry10/camera'), - 'Capture' : require('cordova/plugin/blackberry10/capture'), - 'Logger' : require('cordova/plugin/blackberry10/logger'), - 'Notification' : require('cordova/plugin/blackberry10/notification'), - 'Media': require('cordova/plugin/blackberry10/media'), - 'File' : require('cordova/plugin/blackberry10/file'), - 'InAppBrowser' : require('cordova/plugin/blackberry10/InAppBrowser'), - 'FileTransfer': require('cordova/plugin/blackberry10/fileTransfer') - }; - -module.exports = { - addPlugin: function (key, module) { - plugins[key] = require(module); - }, - exec: function (win, fail, clazz, action, args) { - var result = {"status" : cordova.callbackStatus.CLASS_NOT_FOUND_EXCEPTION, "message" : "Class " + clazz + " cannot be found"}; - - if (plugins[clazz] && plugins[clazz][action]) { - result = plugins[clazz][action](args, win, fail); - } - else { - result = webworks.exec(win, fail, clazz, action, args); - } - return result; - }, - resume: function () {}, - pause: function () {}, - destroy: function () {} -}; - -}); - // file: lib/blackberry10/plugin/blackberry10/media.js define("cordova/plugin/blackberry10/media", function(require, exports, module) { @@ -7532,29 +7480,8 @@ window.cordova = require('cordova'); } }; - //Fire webworks ready once plugin javascript has been loaded - pluginUtils.getPlugins( - function (plugins) { - pluginUtils.loadClientJs(plugins, function () { - webworksReady = true; - fireWebworksReadyEvent(); - }); - }, - function () { - console.log('Unable to load plugins.json'); - webworksReady = true; - fireWebworksReadyEvent(); - } - ); - - /** - * webworks.exec - * - * This will all be moved into lib/blackberry10/exec once cordova.exec can be replaced - */ - function RemoteFunctionCall(functionUri) { - var params = {}; + var params = {}; function composeUri() { return require("cordova/plugin/blackberry10/utils").getURIPrefix() + functionUri; @@ -7573,11 +7500,11 @@ window.cordova = require('cordova'); this.makeSyncCall = function (success, error) { var requestUri = composeUri(), - request = createXhrRequest(requestUri, false), - response, - errored, - cb, - data; + request = createXhrRequest(requestUri, false), + response, + errored, + cb, + data; request.send(JSON.stringify(params)); response = JSON.parse(decodeURIComponent(request.responseText) || "null"); @@ -7588,11 +7515,11 @@ window.cordova = require('cordova'); window.webworks = { exec: function (success, fail, service, action, args) { var uri = service + "/" + action, - request = new RemoteFunctionCall(uri), - callbackId = service + cordova.callbackId++, - response, - name, - didSucceed; + request = new RemoteFunctionCall(uri), + callbackId = service + cordova.callbackId++, + response, + name, + didSucceed; for (name in args) { if (Object.hasOwnProperty.call(args, name)) { @@ -7618,7 +7545,7 @@ window.cordova = require('cordova'); delete cordova.callbacks[callbackId]; } else { didSucceed = response.code === cordova.callbackStatus.OK || response.code === cordova.callbackStatus.NO_RESULT; - cordova.callbackFromNative(callbackId, didSucceed, response.code, [didSucceed ? response.data : response.msg], !!response.keepCallback); + cordova.callbackFromNative(callbackId, didSucceed, response.code, didSucceed ? response.data : response.msg, !!response.keepCallback); } }, defineReadOnlyField: function (obj, field, value) { @@ -7629,6 +7556,21 @@ window.cordova = require('cordova'); }, event: require("cordova/plugin/blackberry10/event") }; + + //Fire webworks ready once plugin javascript has been loaded + pluginUtils.getPlugins( + function (plugins) { + pluginUtils.loadClientJs(plugins, function () { + webworksReady = true; + fireWebworksReadyEvent(); + }); + }, + function () { + console.log('Unable to load plugins.json'); + webworksReady = true; + fireWebworksReadyEvent(); + } + ); }()); document.addEventListener("DOMContentLoaded", function () {
