Change camera to be more consistent with iPhone and BB widgets. Add support to choose image from library instead of only camera.

This commit is contained in:
Bryce Curtis 2010-09-16 11:04:27 -05:00
parent 9c2e4cfd9c
commit 92d2b5812c
3 changed files with 176 additions and 79 deletions

48
framework/assets/js/camera.js Normal file → Executable file
View File

@ -4,14 +4,44 @@
*
* @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,
* pictureSourceType: 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)
};
/**
* Source to getPicture from.
*
* Example: navigator.camera.getPicture(success, fail,
* { quality: 80,
* destinationType: Camera.DestinationType.DATA_URL,
* pictureSourceType: 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)
};
/**
* Gets a picture from source defined by "options.pictureSourceType", and returns the
* image as defined by the "options.destinationType" option.
* The defaults are pictureSourceType=CAMERA and destinationType=DATA_URL.
*
* @param {Function} successCallback
* @param {Function} errorCallback
@ -34,15 +64,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 pictureSourceType = Camera.PictureSourceType.CAMERA;
if (typeof this.options.pictureSourceType == "number") {
pictureSourceType = this.options.pictureSourceType;
}
PhoneGap.execAsync(null, null, "Camera", "takePicture", [quality, destinationType, pictureSourceType]);
};
/**

View File

@ -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!");
}
}
/**

View File

@ -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<Integer,Plugin> activityResultCallbacks = new HashMap<Integer,Plugin>();
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);
}