diff --git a/framework/assets/js/camera.js b/framework/assets/js/camera.js old mode 100644 new mode 100755 index 4b42470f..0933c9da --- a/framework/assets/js/camera.js +++ b/framework/assets/js/camera.js @@ -4,14 +4,46 @@ * * @constructor */ -function Camera() { +Camera = function() { this.successCallback = null; this.errorCallback = null; this.options = null; }; /** - * Takes a photo and returns the image as a base64 encoded `String`. + * Format of image that returned from getPicture. + * + * Example: navigator.camera.getPicture(success, fail, + * { quality: 80, + * destinationType: Camera.DestinationType.DATA_URL, + * sourceType: Camera.PictureSourceType.PHOTOLIBRARY}) + */ +Camera.DestinationType = { + DATA_URL: 0, // Return base64 encoded string + FILE_URI: 1 // Return file uri (content://media/external/images/media/2 for Android) +}; +Camera.prototype.DestinationType = Camera.DestinationType; + +/** + * Source to getPicture from. + * + * Example: navigator.camera.getPicture(success, fail, + * { quality: 80, + * destinationType: Camera.DestinationType.DATA_URL, + * sourceType: Camera.PictureSourceType.PHOTOLIBRARY}) + */ +Camera.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) +}; +Camera.prototype.PictureSourceType = Camera.PictureSourceType; + +/** + * Gets a picture from source defined by "options.sourceType", and returns the + * image as defined by the "options.destinationType" option. + + * The defaults are sourceType=CAMERA and destinationType=DATA_URL. * * @param {Function} successCallback * @param {Function} errorCallback @@ -34,15 +66,19 @@ Camera.prototype.getPicture = function(successCallback, errorCallback, options) this.successCallback = successCallback; this.errorCallback = errorCallback; this.options = options; - var capturetype = "base64"; var quality = 80; - if (this.options.capturetype) { - capturetype = this.options.capturetype; - } if (options.quality) { quality = this.options.quality; } - PhoneGap.execAsync(null, null, "Camera", "takePicture", [quality, capturetype]); + var destinationType = Camera.DestinationType.DATA_URL; + if (this.options.destinationType) { + destinationType = this.options.destinationType; + } + var sourceType = Camera.PictureSourceType.CAMERA; + if (typeof this.options.sourceType == "number") { + sourceType = this.options.sourceType; + } + PhoneGap.execAsync(null, null, "Camera", "takePicture", [quality, destinationType, sourceType]); }; /** diff --git a/framework/src/com/phonegap/CameraLauncher.java b/framework/src/com/phonegap/CameraLauncher.java index 92183000..cb05721b 100755 --- a/framework/src/com/phonegap/CameraLauncher.java +++ b/framework/src/com/phonegap/CameraLauncher.java @@ -2,6 +2,7 @@ package com.phonegap; import java.io.ByteArrayOutputStream; import java.io.File; +import java.io.FileNotFoundException; import java.io.IOException; import java.io.OutputStream; @@ -20,6 +21,7 @@ import android.graphics.Bitmap.CompressFormat; import android.net.Uri; import android.os.Environment; import android.webkit.WebView; +import android.provider.MediaStore; /** * This class launches the camera view, allows the user to take a picture, closes the camera view, @@ -28,12 +30,18 @@ import android.webkit.WebView; */ public class CameraLauncher implements Plugin { - WebView webView; // WebView object - DroidGap ctx; // DroidGap object + private static final int DATA_URL = 0; // Return base64 encoded string + private static final int FILE_URI = 1; // Return file uri (content://media/external/images/media/2 for Android) + + private static final int PHOTOLIBRARY = 0; // Choose image from picture library (same as SAVEDPHOTOALBUM for Android) + private static final int CAMERA = 1; // Take picture from camera + private static final int SAVEDPHOTOALBUM = 2; // Choose image from picture library (same as PHOTOLIBRARY for Android) + + WebView webView; // WebView object + DroidGap ctx; // 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 - private boolean base64 = true; + 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 /** * Constructor. @@ -74,7 +82,20 @@ public class CameraLauncher implements Plugin { try { if (action.equals("takePicture")) { - this.takePicture(args.getInt(0), args.getString(1)); + int destType = DATA_URL; + if (args.length() > 1) { + destType = args.getInt(1); + } + int srcType = CAMERA; + if (args.length() > 2) { + srcType = args.getInt(2); + } + if (srcType == CAMERA) { + this.takePicture(args.getInt(0), destType); + } + else if ((srcType == PHOTOLIBRARY) || (srcType == SAVEDPHOTOALBUM)) { + this.getImage(srcType, destType); + } } return new PluginResult(status, result); } catch (JSONException e) { @@ -130,12 +151,8 @@ public class CameraLauncher implements Plugin { * @param quality Compression quality hint (0-100: 0=low quality & high compression, 100=compress of max quality) * @param returnType Set the type of image to return. */ - public void takePicture(int quality, String returnType) { + public void takePicture(int quality, int returnType) { this.mQuality = quality; - this.base64 = false; - if (returnType.equals("base64")) { - this.base64 = true; - } // Display camera Intent intent = new Intent("android.media.action.IMAGE_CAPTURE"); @@ -146,7 +163,22 @@ public class CameraLauncher implements Plugin { intent.putExtra(android.provider.MediaStore.EXTRA_OUTPUT, Uri.fromFile(photo)); this.imageUri = Uri.fromFile(photo); - this.ctx.startActivityForResult((Plugin) this, intent); + this.ctx.startActivityForResult((Plugin) this, intent, (CAMERA+1)*16 + returnType+1); + } + + /** + * Get image from photo library. + * + * @param returnType + */ + // TODO: Images selected from SDCARD don't display correctly, but from CAMERA ALBUM do! + public void getImage(int srcType, int returnType) { + Intent intent = new Intent(); + intent.setType("image/*"); + intent.setAction(Intent.ACTION_GET_CONTENT); + intent.addCategory(Intent.CATEGORY_OPENABLE); + this.ctx.startActivityForResult((Plugin) this, Intent.createChooser(intent, + new String("Get Picture")), (srcType+1)*16 + returnType + 1); } /** @@ -158,61 +190,98 @@ public class CameraLauncher implements Plugin { * @param intent 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) { + + // Get src and dest types from request code + int srcType = (requestCode/16) - 1; + int destType = (requestCode % 16) - 1; - // If image available - if (resultCode == Activity.RESULT_OK) { - try { - // Read in bitmap of captured image - Bitmap bitmap = android.provider.MediaStore.Images.Media.getBitmap(this.ctx.getContentResolver(), imageUri); - + // If CAMERA + if (srcType == CAMERA) { + + // If image available + if (resultCode == Activity.RESULT_OK) { + try { + // Read in bitmap of captured image + Bitmap bitmap = android.provider.MediaStore.Images.Media.getBitmap(this.ctx.getContentResolver(), imageUri); + + // If sending base64 image back + if (destType == DATA_URL) { + this.processPicture(bitmap); + } + + // If sending filename back + else if (destType == FILE_URI){ + // Create entry in media store for image + // (Don't use insertImage() because it uses default compression setting of 50 - no way to change it) + ContentValues values = new ContentValues(); + values.put(android.provider.MediaStore.Images.Media.MIME_TYPE, "image/jpeg"); + Uri uri = null; + try { + uri = this.ctx.getContentResolver().insert(android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values); + } catch (UnsupportedOperationException e) { + System.out.println("Can't write to external media storage."); + try { + uri = this.ctx.getContentResolver().insert(android.provider.MediaStore.Images.Media.INTERNAL_CONTENT_URI, values); + } catch (UnsupportedOperationException ex) { + System.out.println("Can't write to internal media storage."); + this.failPicture("Error capturing image - no media storage found."); + return; + } + } + + // Add compressed version of captured image to returned media store Uri + OutputStream os = this.ctx.getContentResolver().openOutputStream(uri); + bitmap.compress(Bitmap.CompressFormat.JPEG, this.mQuality, os); + os.close(); + + // Send Uri back to JavaScript for viewing image + this.ctx.sendJavascript("navigator.camera.success('" + uri.toString() + "');"); + } + } catch (IOException e) { + e.printStackTrace(); + 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!"); + } + } + + // If retrieving photo from library + else if ((srcType == PHOTOLIBRARY) || (srcType == SAVEDPHOTOALBUM)) { + if (resultCode == Activity.RESULT_OK) { + Uri uri = intent.getData(); + android.content.ContentResolver resolver = this.ctx.getContentResolver(); // If sending base64 image back - if (this.base64) { - this.processPicture(bitmap); + if (destType == DATA_URL) { + try { + Bitmap bitmap = android.graphics.BitmapFactory.decodeStream(resolver.openInputStream(uri)); + this.processPicture(bitmap); + } catch (FileNotFoundException e) { + e.printStackTrace(); + this.failPicture("Error retrieving image."); + } } // If sending filename back - else { - // Create entry in media store for image - // (Don't use insertImage() because it uses default compression setting of 50 - no way to change it) - ContentValues values = new ContentValues(); - values.put(android.provider.MediaStore.Images.Media.MIME_TYPE, "image/jpeg"); - Uri uri = null; - try { - uri = this.ctx.getContentResolver().insert(android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values); - } catch (UnsupportedOperationException e) { - System.out.println("Can't write to external media storage."); - try { - uri = this.ctx.getContentResolver().insert(android.provider.MediaStore.Images.Media.INTERNAL_CONTENT_URI, values); - } catch (UnsupportedOperationException ex) { - System.out.println("Can't write to internal media storage."); - this.failPicture("Error capturing image - no media storage found."); - return; - } - } - - // Add compressed version of captured image to returned media store Uri - OutputStream os = this.ctx.getContentResolver().openOutputStream(uri); - bitmap.compress(Bitmap.CompressFormat.JPEG, this.mQuality, os); - os.close(); - - // Send Uri back to JavaScript for viewing image - this.ctx.sendJavascript("navigator.camera.success('" + uri.toString() + "');"); + else if (destType == FILE_URI) { + this.ctx.sendJavascript("navigator.camera.success('" + uri + "');"); } - } catch (IOException e) { - e.printStackTrace(); - this.failPicture("Error capturing image."); - } + } + else if (resultCode == Activity.RESULT_CANCELED) { + this.failPicture("Selection cancelled."); + } + else { + this.failPicture("Selection did not complete!"); + } } - - // If cancelled - else if (resultCode == Activity.RESULT_CANCELED) { - this.failPicture("Camera cancelled."); - } - - // If something else - else { - this.failPicture("Did not complete!"); - } } /** diff --git a/framework/src/com/phonegap/DroidGap.java b/framework/src/com/phonegap/DroidGap.java index 3fa921ed..4786d357 100755 --- a/framework/src/com/phonegap/DroidGap.java +++ b/framework/src/com/phonegap/DroidGap.java @@ -25,7 +25,6 @@ package com.phonegap; import com.phonegap.api.Plugin; -import java.util.HashMap; import com.phonegap.api.PluginManager; import android.app.Activity; @@ -91,10 +90,8 @@ public class DroidGap extends Activity { private String url; // The initial URL for our app private String baseUrl; // The base of the initial URL for our app - // Variables to manage ActivityResultCallbacks - private int activityResultCallbackCounter = 1000; - private HashMap activityResultCallbacks = new HashMap(); - + private Plugin activityResultCallback = null; // Plugin to call when activity result is received + /** * Called when the activity is first created. * @@ -677,13 +674,11 @@ public class DroidGap extends Activity { * * @param command The command object * @param intent The intent to start - * @return The request code to use for the callback + * @param requestCode The request code that is passed to callback to identify the activity */ - public int startActivityForResult(Plugin command, Intent intent) { - int requestCode = this.activityResultCallbackCounter++; - this.activityResultCallbacks.put(requestCode, command); + public void startActivityForResult(Plugin command, Intent intent, int requestCode) { + this.activityResultCallback = command; super.startActivityForResult(intent, requestCode); - return requestCode; } @Override @@ -698,8 +693,7 @@ public class DroidGap extends Activity { */ protected void onActivityResult(int requestCode, int resultCode, Intent intent) { super.onActivityResult(requestCode, resultCode, intent); - - Plugin callback = this.activityResultCallbacks.remove(requestCode); + Plugin callback = this.activityResultCallback; if (callback != null) { callback.onActivityResult(requestCode, resultCode, intent); }