diff --git a/framework/assets/js/camera.js b/framework/assets/js/camera.js index 71d5659f..21383f02 100644 --- a/framework/assets/js/camera.js +++ b/framework/assets/js/camera.js @@ -1,40 +1,69 @@ /** * This class provides access to the device camera. + * * @constructor */ function Camera() { - -} + this.successCallback = null; + this.errorCallback = null; + this.options = null; +}; /** - * + * Takes a photo and returns the image as a base64 encoded `String`. + * * @param {Function} successCallback * @param {Function} errorCallback * @param {Object} options */ Camera.prototype.getPicture = function(successCallback, errorCallback, options) { - this.winCallback = successCallback; - this.failCallback = errorCallback; - if (options.quality) - { - GapCam.takePicture(options.quality); - } - else - { - GapCam.takePicture(80); - } -} + // successCallback required + if (typeof successCallback != "function") { + console.log("Camera Error: successCallback is not a function"); + return; + } -Camera.prototype.win = function(picture) -{ - this.winCallback(picture); -} + // errorCallback optional + if (errorCallback && (typeof errorCallback != "function")) { + console.log("Camera Error: errorCallback is not a function"); + return; + } -Camera.prototype.fail = function(err) -{ - this.failCallback(err); -} + this.successCallback = successCallback; + this.errorCallback = errorCallback; + this.options = options; + if (options.quality) { + GapCam.takePicture(options.quality); + } + else { + GapCam.takePicture(80); + } +}; + +/** + * Callback function from native code that is called when image has been captured. + * + * @param picture The base64 encoded string of the image + */ +Camera.prototype.success = function(picture) { + if (this.successCallback) { + this.successCallback(picture); + } +}; + + +/** + * Callback function from native code that is called when there is an error + * capturing an image, or the capture is cancelled. + * + * @param err The error message + */ +Camera.prototype.error = function(err) { + if (this.errorCallback) { + this.errorCallback(err); + } +}; PhoneGap.addConstructor(function() { if (typeof navigator.camera == "undefined") navigator.camera = new Camera(); diff --git a/framework/src/com/phonegap/CameraLauncher.java b/framework/src/com/phonegap/CameraLauncher.java index 609c5ffa..3ff9ec73 100644 --- a/framework/src/com/phonegap/CameraLauncher.java +++ b/framework/src/com/phonegap/CameraLauncher.java @@ -1,55 +1,122 @@ package com.phonegap; import java.io.ByteArrayOutputStream; +import java.io.File; import org.apache.commons.codec.binary.Base64; +import android.app.Activity; +import android.content.ContentResolver; +import android.content.Intent; import android.graphics.Bitmap; import android.graphics.Bitmap.CompressFormat; +import android.net.Uri; +import android.os.Environment; +import android.provider.MediaStore; import android.webkit.WebView; - +/** + * This class launches the camera view, allows the user to take a picture, closes the camera view, + * and returns the captured image. When the camera view is closed, the screen displayed before + * the camera view was shown is redisplayed. + */ public class CameraLauncher { - private WebView mAppView; - private DroidGap mGap; - int mQuality; + private WebView mAppView; // Webview object + private DroidGap mGap; // DroidGap object + private int mQuality; // Compression quality hint (0-100: 0=low quality & high compression, 100=compress of max quality) + private Uri imageUri; // Uri of captured image - CameraLauncher(WebView view, DroidGap gap) - { + /** + * Constructor. + * + * @param view + * @param gap + */ + CameraLauncher(WebView view, DroidGap gap) { mAppView = view; mGap = gap; } - - public void takePicture(int quality) - { - mQuality = quality; - mGap.startCamera(); + + /** + * Take a picture with the camera. + * When an image is captured or the camera view is cancelled, the result is returned + * in DroidGap.onActivityResult, which forwards the result to this.onActivityResult. + * + * @param quality Compression quality hint (0-100: 0=low quality & high compression, 100=compress of max quality) + */ + public void takePicture(int quality) { + this.mQuality = quality; + + // Display camera + Intent intent = new Intent("android.media.action.IMAGE_CAPTURE"); + File photo = new File(Environment.getExternalStorageDirectory(), "Pic.jpg"); + intent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(photo)); + this.imageUri = Uri.fromFile(photo); + mGap.startActivityForResult(intent, DroidGap.CAMERA_ACTIVIY_RESULT); + } + + /** + * Called when the camera view exits. + * + * @param requestCode The request code originally supplied to startActivityForResult(), + * allowing you to identify who this result came from. + * @param resultCode The integer result code returned by the child activity through its setResult(). + * @param data An Intent, which can return result data to the caller (various data can be attached to Intent "extras"). + */ + public void onActivityResult(int requestCode, int resultCode, Intent intent) { + + // If image available + if (resultCode == Activity.RESULT_OK) { + Uri selectedImage = this.imageUri; + mGap.getContentResolver().notifyChange(selectedImage, null); + ContentResolver cr = mGap.getContentResolver(); + Bitmap bitmap; + try { + bitmap = android.provider.MediaStore.Images.Media.getBitmap(cr, selectedImage); + this.processPicture(bitmap); + } catch (Exception e) { + this.failPicture("Error capturing image."); + } + } + + // If cancelled + else if (resultCode == Activity.RESULT_CANCELED) { + this.failPicture("Camera cancelled."); + } + + // If something else + else { + this.failPicture("Did not complete!"); + } } - /* Return Base64 Encoded String to Javascript */ - public void processPicture( Bitmap bitmap ) - { + /** + * Compress bitmap using jpeg, convert to Base64 encoded string, and return to JavaScript. + * + * @param bitmap + */ + public void processPicture(Bitmap bitmap) { ByteArrayOutputStream jpeg_data = new ByteArrayOutputStream(); try { - if (bitmap.compress(CompressFormat.JPEG, mQuality, jpeg_data)) - { + if (bitmap.compress(CompressFormat.JPEG, mQuality, jpeg_data)) { byte[] code = jpeg_data.toByteArray(); byte[] output = Base64.encodeBase64(code); String js_out = new String(output); - mGap.sendJavascript("navigator.camera.win('" + js_out + "');"); + mGap.sendJavascript("navigator.camera.success('" + js_out + "');"); } } - catch(Exception e) - { - failPicture("fail"); - } - + catch(Exception e) { + this.failPicture("Error compressing image."); + } } - public void failPicture(String err) - { - mGap.sendJavascript("navigator.camera.fail('" + err + "');"); + /** + * Send error message to JavaScript. + * + * @param err + */ + public void failPicture(String err) { + mGap.sendJavascript("navigator.camera.error('" + err + "');"); } - } diff --git a/framework/src/com/phonegap/DroidGap.java b/framework/src/com/phonegap/DroidGap.java index 18b249d7..246c3a99 100755 --- a/framework/src/com/phonegap/DroidGap.java +++ b/framework/src/com/phonegap/DroidGap.java @@ -23,7 +23,6 @@ */ -import java.io.File; import com.phonegap.api.Command; import com.phonegap.api.CommandManager; @@ -57,7 +56,6 @@ import android.webkit.WebSettings.LayoutAlgorithm; import android.widget.ImageView; import android.widget.LinearLayout; import android.os.Build.*; -import android.provider.MediaStore; /** * This class is the main Android activity that represents the PhoneGap @@ -82,7 +80,9 @@ import android.provider.MediaStore; public class DroidGap extends Activity { private static final String LOG_TAG = "DroidGap"; - protected WebView appView; + public static final int CAMERA_ACTIVIY_RESULT = 1; + + protected WebView appView; // The webview for our app protected ImageView splashScreen; private LinearLayout root; @@ -101,7 +101,6 @@ public class DroidGap extends Activity { private CallbackServer callbackServer; private CommandManager commandManager; - private Uri imageUri; private String url; // The initial URL for our app private String baseUrl; // The base of the initial URL for our app @@ -240,7 +239,6 @@ public class DroidGap extends Activity { accel.destroy(); } if (launcher != null) { - } if (mContacts != null) { @@ -610,36 +608,22 @@ public class DroidGap extends Activity { root.addView(appView); } - // This is required to start the camera activity! It has to come from the previous activity - public void startCamera() - { - Intent intent = new Intent("android.media.action.IMAGE_CAPTURE"); - File photo = new File(Environment.getExternalStorageDirectory(), "Pic.jpg"); - intent.putExtra(MediaStore.EXTRA_OUTPUT, - Uri.fromFile(photo)); - imageUri = Uri.fromFile(photo); - startActivityForResult(intent, 0); - } - - protected void onActivityResult(int requestCode, int resultCode, Intent intent) - { - super.onActivityResult(requestCode, resultCode, intent); - - if (resultCode == Activity.RESULT_OK) { - Uri selectedImage = imageUri; - getContentResolver().notifyChange(selectedImage, null); - ContentResolver cr = getContentResolver(); - Bitmap bitmap; - try { - bitmap = android.provider.MediaStore.Images.Media.getBitmap(cr, selectedImage); - launcher.processPicture(bitmap); - } catch (Exception e) { - launcher.failPicture("Did not complete!"); - } - } - else - { - launcher.failPicture("Did not complete!"); + @Override + /** + * Called when an activity you launched exits, giving you the requestCode you started it with, + * the resultCode it returned, and any additional data from it. + * + * @param requestCode The request code originally supplied to startActivityForResult(), + * allowing you to identify who this result came from. + * @param resultCode The integer result code returned by the child activity through its setResult(). + * @param data An Intent, which can return result data to the caller (various data can be attached to Intent "extras"). + */ + protected void onActivityResult(int requestCode, int resultCode, Intent intent) { + super.onActivityResult(requestCode, resultCode, intent); + + // If camera result + if (requestCode == DroidGap.CAMERA_ACTIVIY_RESULT) { + launcher.onActivityResult(requestCode, resultCode, intent); } } }