Java tutorial
/* * PhoneGap is available under *either* the terms of the modified BSD license *or* the * MIT License (2008). See http://opensource.org/licenses/alphabetical for full text. * * Copyright (c) 2005-2010, Nitobi Software Inc. * Copyright (c) 2010, IBM Corporation */ package com.phonegap; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; import java.io.OutputStream; import org.apache.commons.codec.binary.Base64; import org.json.JSONArray; import org.json.JSONException; import com.phonegap.api.Plugin; import com.phonegap.api.PluginResult; import android.app.Activity; import android.content.ContentValues; 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; /** * 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 extends Plugin { 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) 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 public String callbackId; /** * Constructor. */ public CameraLauncher() { } /** * Executes the request and returns PluginResult. * * @param action The action to execute. * @param args JSONArry of arguments for the plugin. * @param callbackId The callback id used when calling back into JavaScript. * @return A PluginResult object with a status and message. */ public PluginResult execute(String action, JSONArray args, String callbackId) { PluginResult.Status status = PluginResult.Status.OK; String result = ""; this.callbackId = callbackId; try { if (action.equals("takePicture")) { 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(args.getInt(0), srcType, destType); } PluginResult r = new PluginResult(PluginResult.Status.NO_RESULT); r.setKeepCallback(true); return r; } return new PluginResult(status, result); } catch (JSONException e) { e.printStackTrace(); return new PluginResult(PluginResult.Status.JSON_EXCEPTION); } } //-------------------------------------------------------------------------- // LOCAL METHODS //-------------------------------------------------------------------------- /** * Take a picture with the camera. * When an image is captured or the camera view is cancelled, the result is returned * in PhonegapActivity.onActivityResult, which forwards the result to this.onActivityResult. * * The image can either be returned as a base64 string or a URI that points to the file. * To display base64 string in an img tag, set the source to: * img.src="data:image/jpeg;base64,"+result; * or to display URI in an img tag * img.src=result; * * @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, int returnType) { this.mQuality = quality; // Display camera Intent intent = new Intent("android.media.action.IMAGE_CAPTURE"); // Specify file so that large image is captured and returned // TODO: What if there isn't any external storage? File photo = new File(Environment.getExternalStorageDirectory(), "Pic.jpg"); intent.putExtra(android.provider.MediaStore.EXTRA_OUTPUT, Uri.fromFile(photo)); this.imageUri = Uri.fromFile(photo); this.ctx.startActivityForResult((Plugin) this, intent, (CAMERA + 1) * 16 + returnType + 1); } /** * Get image from photo library. * * @param quality Compression quality hint (0-100: 0=low quality & high compression, 100=compress of max quality) * @param srcType The album to get image from. * @param returnType Set the type of image to return. */ // TODO: Images selected from SDCARD don't display correctly, but from CAMERA ALBUM do! public void getImage(int quality, int srcType, int returnType) { this.mQuality = quality; 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); } /** * 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 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 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.success(new PluginResult(PluginResult.Status.OK, uri.toString()), this.callbackId); } bitmap.recycle(); bitmap = null; System.gc(); } 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 (destType == DATA_URL) { try { Bitmap bitmap = android.graphics.BitmapFactory.decodeStream(resolver.openInputStream(uri)); this.processPicture(bitmap); bitmap.recycle(); bitmap = null; System.gc(); } catch (FileNotFoundException e) { e.printStackTrace(); this.failPicture("Error retrieving image."); } } // If sending filename back else if (destType == FILE_URI) { this.success(new PluginResult(PluginResult.Status.OK, uri.toString()), this.callbackId); } } else if (resultCode == Activity.RESULT_CANCELED) { this.failPicture("Selection cancelled."); } else { this.failPicture("Selection did not complete!"); } } } /** * 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)) { byte[] code = jpeg_data.toByteArray(); byte[] output = Base64.encodeBase64(code); String js_out = new String(output); this.success(new PluginResult(PluginResult.Status.OK, js_out), this.callbackId); js_out = null; output = null; code = null; } } catch (Exception e) { this.failPicture("Error compressing image."); } jpeg_data = null; } /** * Send error message to JavaScript. * * @param err */ public void failPicture(String err) { this.error(new PluginResult(PluginResult.Status.ERROR, err), this.callbackId); } }