com.phonegap.CameraLauncher.java Source code

Java tutorial

Introduction

Here is the source code for com.phonegap.CameraLauncher.java

Source

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