cordova-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From ste...@apache.org
Subject [21/53] android commit: Merge branch 'master' of https://git-wip-us.apache.org/repos/asf/cordova-android into gutting
Date Tue, 30 Apr 2013 21:35:37 GMT
Merge branch 'master' of https://git-wip-us.apache.org/repos/asf/cordova-android into gutting


Project: http://git-wip-us.apache.org/repos/asf/cordova-android/repo
Commit: http://git-wip-us.apache.org/repos/asf/cordova-android/commit/7ab30348
Tree: http://git-wip-us.apache.org/repos/asf/cordova-android/tree/7ab30348
Diff: http://git-wip-us.apache.org/repos/asf/cordova-android/diff/7ab30348

Branch: refs/heads/3.0.0
Commit: 7ab303488cd267dbf365db13bbcaee959c709240
Parents: 5cc3852 f12262e
Author: Joe Bowser <bowserj@apache.org>
Authored: Thu Mar 28 12:01:57 2013 -0700
Committer: Joe Bowser <bowserj@apache.org>
Committed: Thu Mar 28 12:01:57 2013 -0700

----------------------------------------------------------------------
 VERSION                                            |    2 +-
 bin/create.bat                                     |   36 +-
 bin/templates/project/assets/www/index.html        |    2 +-
 framework/assets/js/cordova.android.js             |  481 ++++++++++++---
 framework/assets/www/index.html                    |    2 +-
 framework/src/org/apache/cordova/Config.java       |   24 +-
 .../src/org/apache/cordova/api/PluginManager.java  |    2 +
 .../org/apache/cordova/core/CameraLauncher.java    |   12 +-
 framework/src/org/apache/cordova/core/Capture.java |    6 +-
 framework/src/org/apache/cordova/core/Device.java  |    2 +-
 .../src/org/apache/cordova/core/FileTransfer.java  |  105 +++-
 .../src/org/apache/cordova/core/FileUtils.java     |   30 +-
 .../src/org/apache/cordova/core/InAppBrowser.java  |   50 ++-
 .../src/org/apache/cordova/core/Notification.java  |  104 ++++
 14 files changed, 725 insertions(+), 133 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cordova-android/blob/7ab30348/framework/src/org/apache/cordova/core/CameraLauncher.java
----------------------------------------------------------------------
diff --cc framework/src/org/apache/cordova/core/CameraLauncher.java
index 4d97fed,0000000..cf7971b
mode 100755,000000..100755
--- a/framework/src/org/apache/cordova/core/CameraLauncher.java
+++ b/framework/src/org/apache/cordova/core/CameraLauncher.java
@@@ -1,793 -1,0 +1,795 @@@
 +/*
 +       Licensed to the Apache Software Foundation (ASF) under one
 +       or more contributor license agreements.  See the NOTICE file
 +       distributed with this work for additional information
 +       regarding copyright ownership.  The ASF licenses this file
 +       to you under the Apache License, Version 2.0 (the
 +       "License"); you may not use this file except in compliance
 +       with the License.  You may obtain a copy of the License at
 +
 +         http://www.apache.org/licenses/LICENSE-2.0
 +
 +       Unless required by applicable law or agreed to in writing,
 +       software distributed under the License is distributed on an
 +       "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 +       KIND, either express or implied.  See the License for the
 +       specific language governing permissions and limitations
 +       under the License.
 +*/
 +package org.apache.cordova.core;
 +
 +import java.io.ByteArrayOutputStream;
 +import java.io.File;
 +import java.io.FileInputStream;
 +import java.io.FileNotFoundException;
 +import java.io.FileOutputStream;
 +import java.io.IOException;
 +import java.io.OutputStream;
 +
 +import org.apache.commons.codec.binary.Base64;
 +import org.apache.cordova.api.CallbackContext;
 +import org.apache.cordova.api.CordovaPlugin;
 +import org.apache.cordova.api.LOG;
 +import org.apache.cordova.api.PluginResult;
 +import org.json.JSONArray;
 +import org.json.JSONException;
 +
 +import android.app.Activity;
 +import android.content.ContentValues;
 +import android.content.Intent;
 +import android.database.Cursor;
 +import android.graphics.Bitmap;
 +import android.graphics.BitmapFactory;
 +import android.graphics.Matrix;
 +import android.graphics.Bitmap.CompressFormat;
 +import android.media.MediaScannerConnection;
 +import android.media.MediaScannerConnection.MediaScannerConnectionClient;
 +import android.net.Uri;
 +import android.os.Environment;
 +import android.provider.MediaStore;
 +import android.util.Log;
 +
 +/**
 + * 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 CordovaPlugin implements MediaScannerConnectionClient {
 +
 +    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 NATIVE_URI = 2;			// On Android, this is the same as FILE_URI
 +
 +    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 static final int PICTURE = 0;               // allow selection of still pictures only. DEFAULT. Will return format specified via DestinationType
 +    private static final int VIDEO = 1;                 // allow selection of video only, ONLY RETURNS URL
 +    private static final int ALLMEDIA = 2;              // allow selection from all media types
 +
 +    private static final int JPEG = 0;                  // Take a picture of type JPEG
 +    private static final int PNG = 1;                   // Take a picture of type PNG
 +    private static final String GET_PICTURE = "Get Picture";
 +    private static final String GET_VIDEO = "Get Video";
 +    private static final String GET_All = "Get All";
 +
 +    private static final String LOG_TAG = "CameraLauncher";
 +
 +    private int mQuality;                   // Compression quality hint (0-100: 0=low quality & high compression, 100=compress of max quality)
 +    private int targetWidth;                // desired width of the image
 +    private int targetHeight;               // desired height of the image
 +    private Uri imageUri;                   // Uri of captured image
 +    private int encodingType;               // Type of encoding to use
 +    private int mediaType;                  // What type of media to retrieve
 +    private boolean saveToPhotoAlbum;       // Should the picture be saved to the device's photo album
 +    private boolean correctOrientation;     // Should the pictures orientation be corrected
 +    //private boolean allowEdit;              // Should we allow the user to crop the image. UNUSED.
 +
 +    public CallbackContext callbackContext;
 +    private int numPics;
 +
 +    private MediaScannerConnection conn;    // Used to update gallery app with newly-written files
 +    private Uri scanMe;                     // Uri of image to be added to content store
 +
 +    //This should never be null!
 +    //private CordovaInterface cordova;
 +
 +    /**
 +     * Constructor.
 +     */
 +    public CameraLauncher() {
 +    }
 +
 +//    public void setContext(CordovaInterface mCtx) {
 +//        super.setContext(mCtx);
 +//        if (CordovaInterface.class.isInstance(mCtx))
 +//            cordova = (CordovaInterface) mCtx;
 +//        else
 +//            LOG.d(LOG_TAG, "ERROR: You must use the CordovaInterface for this to work correctly. Please implement it in your activity");
 +//    }
 +
 +    /**
 +     * Executes the request and returns PluginResult.
 +     *
 +     * @param action        	The action to execute.
 +     * @param args          	JSONArry of arguments for the plugin.
 +     * @param callbackContext   The callback id used when calling back into JavaScript.
 +     * @return              	A PluginResult object with a status and message.
 +     */
 +    public boolean execute(String action, JSONArray args, CallbackContext callbackContext) throws JSONException {
 +        this.callbackContext = callbackContext;
 +
 +        if (action.equals("takePicture")) {
 +            int srcType = CAMERA;
 +            int destType = FILE_URI;
 +            this.saveToPhotoAlbum = false;
 +            this.targetHeight = 0;
 +            this.targetWidth = 0;
 +            this.encodingType = JPEG;
 +            this.mediaType = PICTURE;
 +            this.mQuality = 80;
 +
 +            this.mQuality = args.getInt(0);
 +            destType = args.getInt(1);
 +            srcType = args.getInt(2);
 +            this.targetWidth = args.getInt(3);
 +            this.targetHeight = args.getInt(4);
 +            this.encodingType = args.getInt(5);
 +            this.mediaType = args.getInt(6);
 +            //this.allowEdit = args.getBoolean(7); // This field is unused.
 +            this.correctOrientation = args.getBoolean(8);
 +            this.saveToPhotoAlbum = args.getBoolean(9);
 +
 +            // If the user specifies a 0 or smaller width/height
 +            // make it -1 so later comparisons succeed
 +            if (this.targetWidth < 1) {
 +                this.targetWidth = -1;
 +            }
 +            if (this.targetHeight < 1) {
 +                this.targetHeight = -1;
 +            }
 +
 +            if (srcType == CAMERA) {
 +                this.takePicture(destType, encodingType);
 +            }
 +            else if ((srcType == PHOTOLIBRARY) || (srcType == SAVEDPHOTOALBUM)) {
 +                this.getImage(srcType, destType);
 +            }
 +            PluginResult r = new PluginResult(PluginResult.Status.NO_RESULT);
 +            r.setKeepCallback(true);
 +            callbackContext.sendPluginResult(r);
 +            return true;
 +        }
 +        return false;
 +    }
 +
 +    //--------------------------------------------------------------------------
 +    // LOCAL METHODS
 +    //--------------------------------------------------------------------------
 +
 +    /**
 +     * Take a picture with the camera.
 +     * When an image is captured or the camera view is cancelled, the result is returned
 +     * in CordovaActivity.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 returnType, int encodingType) {
 +        // Save the number of images currently on disk for later
 +        this.numPics = queryImgDB(whichContentStore()).getCount();
 +
 +        // Display camera
 +        Intent intent = new Intent("android.media.action.IMAGE_CAPTURE");
 +
 +        // Specify file so that large image is captured and returned
 +        File photo = createCaptureFile(encodingType);
 +        intent.putExtra(android.provider.MediaStore.EXTRA_OUTPUT, Uri.fromFile(photo));
 +        this.imageUri = Uri.fromFile(photo);
 +
 +        if (this.cordova != null) {
 +            this.cordova.startActivityForResult((CordovaPlugin) this, intent, (CAMERA + 1) * 16 + returnType + 1);
 +        }
 +//        else
 +//            LOG.d(LOG_TAG, "ERROR: You must use the CordovaInterface for this to work correctly. Please implement it in your activity");
 +    }
 +
 +    /**
 +     * Create a file in the applications temporary directory based upon the supplied encoding.
 +     *
 +     * @param encodingType of the image to be taken
 +     * @return a File object pointing to the temporary picture
 +     */
 +    private File createCaptureFile(int encodingType) {
 +        File photo = null;
 +        if (encodingType == JPEG) {
 +            photo = new File(DirectoryManager.getTempDirectoryPath(this.cordova.getActivity()), ".Pic.jpg");
 +        } else if (encodingType == PNG) {
 +            photo = new File(DirectoryManager.getTempDirectoryPath(this.cordova.getActivity()), ".Pic.png");
 +        } else {
 +            throw new IllegalArgumentException("Invalid Encoding Type: " + encodingType);
 +        }
 +        return photo;
 +    }
 +
 +    /**
 +     * 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 srcType, int returnType) {
 +        Intent intent = new Intent();
 +        String title = GET_PICTURE;
 +        if (this.mediaType == PICTURE) {
 +            intent.setType("image/*");
 +        }
 +        else if (this.mediaType == VIDEO) {
 +            intent.setType("video/*");
 +            title = GET_VIDEO;
 +        }
 +        else if (this.mediaType == ALLMEDIA) {
 +            // I wanted to make the type 'image/*, video/*' but this does not work on all versions
 +            // of android so I had to go with the wildcard search.
 +            intent.setType("*/*");
 +            title = GET_All;
 +        }
 +
 +        intent.setAction(Intent.ACTION_GET_CONTENT);
 +        intent.addCategory(Intent.CATEGORY_OPENABLE);
 +        if (this.cordova != null) {
 +            this.cordova.startActivityForResult((CordovaPlugin) this, Intent.createChooser(intent,
 +                    new String(title)), (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;
 +        int rotate = 0;
 +
 +        // If CAMERA
 +        if (srcType == CAMERA) {
 +            // If image available
 +            if (resultCode == Activity.RESULT_OK) {
 +                try {
 +                    // Create an ExifHelper to save the exif data that is lost during compression
 +                    ExifHelper exif = new ExifHelper();
 +                    try {
 +                        if (this.encodingType == JPEG) {
 +                            exif.createInFile(DirectoryManager.getTempDirectoryPath(this.cordova.getActivity()) + "/.Pic.jpg");
 +                            exif.readExifData();
 +                            rotate = exif.getOrientation();
 +                        }
 +                    } catch (IOException e) {
 +                        e.printStackTrace();
 +                    }
 +
 +                    Bitmap bitmap = null;
 +                    Uri uri = null;
 +
 +                    // If sending base64 image back
 +                    if (destType == DATA_URL) {
 +                        bitmap = getScaledBitmap(FileHelper.stripFileProtocol(imageUri.toString()));
 +                        if (bitmap == null) {
 +                            // Try to get the bitmap from intent.
 +                            bitmap = (Bitmap)intent.getExtras().get("data");
 +                        }
 +                        
 +                        // Double-check the bitmap.
 +                        if (bitmap == null) {
 +                            Log.d(LOG_TAG, "I either have a null image path or bitmap");
 +                            this.failPicture("Unable to create bitmap!");
 +                            return;
 +                        }
 +
 +                        if (rotate != 0 && this.correctOrientation) {
 +                            bitmap = getRotatedBitmap(rotate, bitmap, exif);
 +                        }
 +
 +                        this.processPicture(bitmap);
 +                        checkForDuplicateImage(DATA_URL);
 +                    }
 +
 +                    // If sending filename back
 +                    else if (destType == FILE_URI || destType == NATIVE_URI) {
-                         if (!this.saveToPhotoAlbum) {
-                             uri = Uri.fromFile(new File(DirectoryManager.getTempDirectoryPath(this.cordova.getActivity()), System.currentTimeMillis() + ".jpg"));
++                        if (this.saveToPhotoAlbum) {
++                            Uri inputUri = getUriFromMediaStore();
++                            //Just because we have a media URI doesn't mean we have a real file, we need to make it
++                            uri = Uri.fromFile(new File(FileHelper.getRealPath(inputUri, this.cordova)));
 +                        } else {
-                             uri = getUriFromMediaStore();
++                            uri = Uri.fromFile(new File(DirectoryManager.getTempDirectoryPath(this.cordova.getActivity()), System.currentTimeMillis() + ".jpg"));
 +                        }
 +
 +                        if (uri == null) {
 +                            this.failPicture("Error capturing image - no media storage found.");
 +                        }
 +
 +                        // If all this is true we shouldn't compress the image.
 +                        if (this.targetHeight == -1 && this.targetWidth == -1 && this.mQuality == 100 && 
 +                                !this.correctOrientation) {
 +                            writeUncompressedImage(uri);
 +
 +                            this.callbackContext.success(uri.toString());
 +                        } else {
 +                            bitmap = getScaledBitmap(FileHelper.stripFileProtocol(imageUri.toString()));
 +
 +                            if (rotate != 0 && this.correctOrientation) {
 +                                bitmap = getRotatedBitmap(rotate, bitmap, exif);
 +                            }
 +
 +                            // Add compressed version of captured image to returned media store Uri
 +                            OutputStream os = this.cordova.getActivity().getContentResolver().openOutputStream(uri);
 +                            bitmap.compress(Bitmap.CompressFormat.JPEG, this.mQuality, os);
 +                            os.close();
 +
 +                            // Restore exif data to file
 +                            if (this.encodingType == JPEG) {
 +                                String exifPath;
 +                                if (this.saveToPhotoAlbum) {
 +                                    exifPath = FileHelper.getRealPath(uri, this.cordova);
 +                                } else {
 +                                    exifPath = uri.getPath();
 +                                }
 +                                exif.createOutFile(exifPath);
 +                                exif.writeExifData();
 +                            }
 +
 +                        }
 +                        // Send Uri back to JavaScript for viewing image
 +                        this.callbackContext.success(uri.toString());
 +                    }
 +
 +                    this.cleanup(FILE_URI, this.imageUri, uri, bitmap);
 +                    bitmap = null;
 +
 +                } 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();
 +
 +                // If you ask for video or all media type you will automatically get back a file URI
 +                // and there will be no attempt to resize any returned data
 +                if (this.mediaType != PICTURE) {
 +                    this.callbackContext.success(uri.toString());
 +                }
 +                else {
 +                    // This is a special case to just return the path as no scaling,
 +                    // rotating, nor compressing needs to be done
 +                    if (this.targetHeight == -1 && this.targetWidth == -1 &&
 +                            (destType == FILE_URI || destType == NATIVE_URI) && !this.correctOrientation) {
 +                        this.callbackContext.success(uri.toString());
 +                    } else {
 +                        // Get the path to the image. Makes loading so much easier.
 +                        String imagePath = FileHelper.getRealPath(uri, this.cordova);
 +                        String mimeType = FileHelper.getMimeType(imagePath, this.cordova);
 +                        // Log.d(LOG_TAG, "Real path = " + imagePath);
 +                        // Log.d(LOG_TAG, "mime type = " + mimeType);
 +                        // If we don't have a valid image so quit.
 +                        if (imagePath == null || mimeType == null || 
 +                                !(mimeType.equalsIgnoreCase("image/jpeg") || mimeType.equalsIgnoreCase("image/png"))) {
 +                        	Log.d(LOG_TAG, "I either have a null image path or bitmap");
 +                            this.failPicture("Unable to retrieve path to picture!");
 +                            return;
 +                        }
 +                        Bitmap bitmap = getScaledBitmap(imagePath);
 +                        if (bitmap == null) {
 +                        	Log.d(LOG_TAG, "I either have a null image path or bitmap");
 +                            this.failPicture("Unable to create bitmap!");
 +                            return;
 +                        }
 +
 +                        if (this.correctOrientation) {
 +                            String[] cols = { MediaStore.Images.Media.ORIENTATION };
 +                            Cursor cursor = this.cordova.getActivity().getContentResolver().query(intent.getData(),
 +                                    cols, null, null, null);
 +                            if (cursor != null) {
 +                                cursor.moveToPosition(0);
 +                                rotate = cursor.getInt(0);
 +                                cursor.close();
 +                            }
 +                            if (rotate != 0) {
 +                                Matrix matrix = new Matrix();
 +                                matrix.setRotate(rotate);
 +                                bitmap = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true);
 +                            }
 +                        }
 +
 +                        // If sending base64 image back
 +                        if (destType == DATA_URL) {
 +                            this.processPicture(bitmap);
 +                        }
 +
 +                        // If sending filename back
 +                        else if (destType == FILE_URI || destType == NATIVE_URI) {
 +                            // Do we need to scale the returned file
 +                            if (this.targetHeight > 0 && this.targetWidth > 0) {
 +                                try {
 +                                    // Create an ExifHelper to save the exif data that is lost during compression
 +                                    String resizePath = DirectoryManager.getTempDirectoryPath(this.cordova.getActivity()) + "/resize.jpg";
 +                                    ExifHelper exif = new ExifHelper();
 +                                    try {
 +                                        if (this.encodingType == JPEG) {
-                                             exif.createInFile(resizePath);
++                                            exif.createInFile(FileHelper.getRealPath(uri, this.cordova));
 +                                            exif.readExifData();
 +                                            rotate = exif.getOrientation();
 +                                        }
 +                                    } catch (IOException e) {
 +                                        e.printStackTrace();
 +                                    }
 +
 +                                    OutputStream os = new FileOutputStream(resizePath);
 +                                    bitmap.compress(Bitmap.CompressFormat.JPEG, this.mQuality, os);
 +                                    os.close();
 +
 +                                    // Restore exif data to file
 +                                    if (this.encodingType == JPEG) {
-                                         exif.createOutFile(FileHelper.getRealPath(uri, this.cordova));
++                                        exif.createOutFile(resizePath);
 +                                        exif.writeExifData();
 +                                    }
 +
 +                                    // The resized image is cached by the app in order to get around this and not have to delete you
 +                                    // application cache I'm adding the current system time to the end of the file url.
 +                                    this.callbackContext.success("file://" + resizePath + "?" + System.currentTimeMillis());
 +                                } catch (Exception e) {
 +                                    e.printStackTrace();
 +                                    this.failPicture("Error retrieving image.");
 +                                }
 +                            }
 +                            else {
 +                                this.callbackContext.success(uri.toString());
 +                            }
 +                        }
 +                        if (bitmap != null) {
 +	                        bitmap.recycle();
 +	                        bitmap = null;
 +                        }
 +                        System.gc();
 +                    }
 +                }
 +            }
 +            else if (resultCode == Activity.RESULT_CANCELED) {
 +                this.failPicture("Selection cancelled.");
 +            }
 +            else {
 +                this.failPicture("Selection did not complete!");
 +            }
 +        }
 +    }
 +
 +    /**
 +     * Figure out if the bitmap should be rotated. For instance if the picture was taken in
 +     * portrait mode
 +     *
 +     * @param rotate
 +     * @param bitmap
 +     * @return rotated bitmap
 +     */
 +    private Bitmap getRotatedBitmap(int rotate, Bitmap bitmap, ExifHelper exif) {
 +        Matrix matrix = new Matrix();
 +        if (rotate == 180) {
 +            matrix.setRotate(rotate);
 +        } else {
 +            matrix.setRotate(rotate, (float) bitmap.getWidth() / 2, (float) bitmap.getHeight() / 2);
 +        }
 +        bitmap = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true);
 +        exif.resetOrientation();
 +        return bitmap;
 +    }
 +
 +    /**
 +     * In the special case where the default width, height and quality are unchanged
 +     * we just write the file out to disk saving the expensive Bitmap.compress function.
 +     *
 +     * @param uri
 +     * @throws FileNotFoundException
 +     * @throws IOException
 +     */
 +    private void writeUncompressedImage(Uri uri) throws FileNotFoundException,
 +            IOException {
 +        FileInputStream fis = new FileInputStream(FileHelper.stripFileProtocol(imageUri.toString()));
 +        OutputStream os = this.cordova.getActivity().getContentResolver().openOutputStream(uri);
 +        byte[] buffer = new byte[4096];
 +        int len;
 +        while ((len = fis.read(buffer)) != -1) {
 +            os.write(buffer, 0, len);
 +        }
 +        os.flush();
 +        os.close();
 +        fis.close();
 +    }
 +
 +    /**
 +     * Create entry in media store for image
 +     *
 +     * @return uri
 +     */
 +    private Uri getUriFromMediaStore() {
 +        ContentValues values = new ContentValues();
 +        values.put(android.provider.MediaStore.Images.Media.MIME_TYPE, "image/jpeg");
 +        Uri uri;
 +        try {
 +            uri = this.cordova.getActivity().getContentResolver().insert(android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values);
 +        } catch (UnsupportedOperationException e) {
 +            LOG.d(LOG_TAG, "Can't write to external media storage.");
 +            try {
 +                uri = this.cordova.getActivity().getContentResolver().insert(android.provider.MediaStore.Images.Media.INTERNAL_CONTENT_URI, values);
 +            } catch (UnsupportedOperationException ex) {
 +                LOG.d(LOG_TAG, "Can't write to internal media storage.");
 +                return null;
 +            }
 +        }
 +        return uri;
 +    }
 +
 +    /**
 +     * Return a scaled bitmap based on the target width and height
 +     *
 +     * @param imagePath
 +     * @return
 +     */
 +    private Bitmap getScaledBitmap(String imagePath) {
 +        // If no new width or height were specified return the original bitmap
 +        if (this.targetWidth <= 0 && this.targetHeight <= 0) {
 +            return BitmapFactory.decodeFile(imagePath);
 +        }
 +
 +        // figure out the original width and height of the image
 +        BitmapFactory.Options options = new BitmapFactory.Options();
 +        options.inJustDecodeBounds = true;
 +        BitmapFactory.decodeFile(imagePath, options);
 +        
 +        //CB-2292: WTF? Why is the width null?
 +        if(options.outWidth == 0 || options.outHeight == 0)
 +        {
 +            return null;
 +        }
 +        
 +        // determine the correct aspect ratio
 +        int[] widthHeight = calculateAspectRatio(options.outWidth, options.outHeight);
 +
 +        // Load in the smallest bitmap possible that is closest to the size we want
 +        options.inJustDecodeBounds = false;
 +        options.inSampleSize = calculateSampleSize(options.outWidth, options.outHeight, this.targetWidth, this.targetHeight);
 +        Bitmap unscaledBitmap = BitmapFactory.decodeFile(imagePath, options);
 +        if (unscaledBitmap == null) {
 +            return null;
 +        }
 +
 +        return Bitmap.createScaledBitmap(unscaledBitmap, widthHeight[0], widthHeight[1], true);
 +    }
 +
 +    /**
 +     * Maintain the aspect ratio so the resulting image does not look smooshed
 +     *
 +     * @param origWidth
 +     * @param origHeight
 +     * @return
 +     */
 +    public int[] calculateAspectRatio(int origWidth, int origHeight) {
 +        int newWidth = this.targetWidth;
 +        int newHeight = this.targetHeight;
 +
 +        // If no new width or height were specified return the original bitmap
 +        if (newWidth <= 0 && newHeight <= 0) {
 +            newWidth = origWidth;
 +            newHeight = origHeight;
 +        }
 +        // Only the width was specified
 +        else if (newWidth > 0 && newHeight <= 0) {
 +            newHeight = (newWidth * origHeight) / origWidth;
 +        }
 +        // only the height was specified
 +        else if (newWidth <= 0 && newHeight > 0) {
 +            newWidth = (newHeight * origWidth) / origHeight;
 +        }
 +        // If the user specified both a positive width and height
 +        // (potentially different aspect ratio) then the width or height is
 +        // scaled so that the image fits while maintaining aspect ratio.
 +        // Alternatively, the specified width and height could have been
 +        // kept and Bitmap.SCALE_TO_FIT specified when scaling, but this
 +        // would result in whitespace in the new image.
 +        else {
 +            double newRatio = newWidth / (double) newHeight;
 +            double origRatio = origWidth / (double) origHeight;
 +
 +            if (origRatio > newRatio) {
 +                newHeight = (newWidth * origHeight) / origWidth;
 +            } else if (origRatio < newRatio) {
 +                newWidth = (newHeight * origWidth) / origHeight;
 +            }
 +        }
 +
 +        int[] retval = new int[2];
 +        retval[0] = newWidth;
 +        retval[1] = newHeight;
 +        return retval;
 +    }
 +
 +    /**
 +     * Figure out what ratio we can load our image into memory at while still being bigger than
 +     * our desired width and height
 +     *
 +     * @param srcWidth
 +     * @param srcHeight
 +     * @param dstWidth
 +     * @param dstHeight
 +     * @return
 +     */
 +    public static int calculateSampleSize(int srcWidth, int srcHeight, int dstWidth, int dstHeight) {
 +        final float srcAspect = (float)srcWidth / (float)srcHeight;
 +        final float dstAspect = (float)dstWidth / (float)dstHeight;
 +
 +        if (srcAspect > dstAspect) {
 +            return srcWidth / dstWidth;
 +        } else {
 +            return srcHeight / dstHeight;
 +        }
 +      }
 +
 +    /**
 +     * Creates a cursor that can be used to determine how many images we have.
 +     *
 +     * @return a cursor
 +     */
 +    private Cursor queryImgDB(Uri contentStore) {
 +        return this.cordova.getActivity().getContentResolver().query(
 +                contentStore,
 +                new String[] { MediaStore.Images.Media._ID },
 +                null,
 +                null,
 +                null);
 +    }
 +
 +    /**
 +     * Cleans up after picture taking. Checking for duplicates and that kind of stuff.
 +     * @param newImage
 +     */
 +    private void cleanup(int imageType, Uri oldImage, Uri newImage, Bitmap bitmap) {
 +        if (bitmap != null) {
 +            bitmap.recycle();
 +        }
 +
 +        // Clean up initial camera-written image file.
 +        (new File(FileHelper.stripFileProtocol(oldImage.toString()))).delete();
 +
 +        checkForDuplicateImage(imageType);
 +        // Scan for the gallery to update pic refs in gallery
 +        if (this.saveToPhotoAlbum && newImage != null) {
 +            this.scanForGallery(newImage);
 +        }
 +
 +        System.gc();
 +    }
 +
 +    /**
 +     * Used to find out if we are in a situation where the Camera Intent adds to images
 +     * to the content store. If we are using a FILE_URI and the number of images in the DB
 +     * increases by 2 we have a duplicate, when using a DATA_URL the number is 1.
 +     *
 +     * @param type FILE_URI or DATA_URL
 +     */
 +    private void checkForDuplicateImage(int type) {
 +        int diff = 1;
 +        Uri contentStore = whichContentStore();
 +        Cursor cursor = queryImgDB(contentStore);
 +        int currentNumOfImages = cursor.getCount();
 +
 +        if (type == FILE_URI && this.saveToPhotoAlbum) {
 +            diff = 2;
 +        }
 +
 +        // delete the duplicate file if the difference is 2 for file URI or 1 for Data URL
 +        if ((currentNumOfImages - numPics) == diff) {
 +            cursor.moveToLast();
 +            int id = Integer.valueOf(cursor.getString(cursor.getColumnIndex(MediaStore.Images.Media._ID)));
 +            if (diff == 2) {
 +                id--;
 +            }
 +            Uri uri = Uri.parse(contentStore + "/" + id);
 +            this.cordova.getActivity().getContentResolver().delete(uri, null, null);
 +        }
 +    }
 +
 +    /**
 +     * Determine if we are storing the images in internal or external storage
 +     * @return Uri
 +     */
 +    private Uri whichContentStore() {
 +        if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
 +            return android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
 +        } else {
 +            return android.provider.MediaStore.Images.Media.INTERNAL_CONTENT_URI;
 +        }
 +    }
 +
 +    /**
 +     * 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.callbackContext.success(js_out);
 +                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.callbackContext.error(err);
 +    }
 +
 +    private void scanForGallery(Uri newImage) {
 +        this.scanMe = newImage;
 +        if(this.conn != null) {
 +            this.conn.disconnect();
 +        }
 +        this.conn = new MediaScannerConnection(this.cordova.getActivity().getApplicationContext(), this);
 +        conn.connect();
 +    }
 +
 +    public void onMediaScannerConnected() {
 +        try{
 +            this.conn.scanFile(this.scanMe.toString(), "image/*");
 +        } catch (java.lang.IllegalStateException e){
 +            LOG.e(LOG_TAG, "Can't scan file in MediaScanner after taking picture");
 +        }
 +
 +    }
 +
 +    public void onScanCompleted(String path, Uri uri) {
 +        this.conn.disconnect();
 +    }
 +}

http://git-wip-us.apache.org/repos/asf/cordova-android/blob/7ab30348/framework/src/org/apache/cordova/core/Capture.java
----------------------------------------------------------------------
diff --cc framework/src/org/apache/cordova/core/Capture.java
index 7d1710a,0000000..9a48eaa
mode 100644,000000..100644
--- a/framework/src/org/apache/cordova/core/Capture.java
+++ b/framework/src/org/apache/cordova/core/Capture.java
@@@ -1,443 -1,0 +1,445 @@@
 +/*
 +       Licensed to the Apache Software Foundation (ASF) under one
 +       or more contributor license agreements.  See the NOTICE file
 +       distributed with this work for additional information
 +       regarding copyright ownership.  The ASF licenses this file
 +       to you under the Apache License, Version 2.0 (the
 +       "License"); you may not use this file except in compliance
 +       with the License.  You may obtain a copy of the License at
 +
 +         http://www.apache.org/licenses/LICENSE-2.0
 +
 +       Unless required by applicable law or agreed to in writing,
 +       software distributed under the License is distributed on an
 +       "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 +       KIND, either express or implied.  See the License for the
 +       specific language governing permissions and limitations
 +       under the License.
 +*/
 +package org.apache.cordova.core;
 +
 +import java.io.File;
 +import java.io.FileInputStream;
 +import java.io.IOException;
 +import java.io.OutputStream;
 +
++import android.os.Build;
 +import org.apache.cordova.api.CallbackContext;
 +import org.apache.cordova.api.CordovaPlugin;
 +import org.apache.cordova.api.LOG;
 +import org.apache.cordova.api.PluginResult;
 +import org.json.JSONArray;
 +import org.json.JSONException;
 +import org.json.JSONObject;
 +
 +import android.app.Activity;
 +import android.content.ContentValues;
 +import android.content.Intent;
 +import android.database.Cursor;
 +import android.graphics.BitmapFactory;
 +import android.media.MediaPlayer;
 +import android.net.Uri;
 +import android.os.Environment;
 +import android.provider.MediaStore;
 +import android.util.Log;
 +
 +public class Capture extends CordovaPlugin {
 +
 +    private static final String VIDEO_3GPP = "video/3gpp";
 +    private static final String VIDEO_MP4 = "video/mp4";
 +    private static final String AUDIO_3GPP = "audio/3gpp";
 +    private static final String IMAGE_JPEG = "image/jpeg";
 +
 +    private static final int CAPTURE_AUDIO = 0;     // Constant for capture audio
 +    private static final int CAPTURE_IMAGE = 1;     // Constant for capture image
 +    private static final int CAPTURE_VIDEO = 2;     // Constant for capture video
 +    private static final String LOG_TAG = "Capture";
 +
 +    private static final int CAPTURE_INTERNAL_ERR = 0;
 +//    private static final int CAPTURE_APPLICATION_BUSY = 1;
 +//    private static final int CAPTURE_INVALID_ARGUMENT = 2;
 +    private static final int CAPTURE_NO_MEDIA_FILES = 3;
 +
 +    private CallbackContext callbackContext;        // The callback context from which we were invoked.
 +    private long limit;                             // the number of pics/vids/clips to take
 +    private double duration;                        // optional duration parameter for video recording
 +    private JSONArray results;                      // The array of results to be returned to the user
 +    private int numPics;                            // Number of pictures before capture activity
 +
 +    //private CordovaInterface cordova;
 +
 +//    public void setContext(Context mCtx)
 +//    {
 +//        if (CordovaInterface.class.isInstance(mCtx))
 +//            cordova = (CordovaInterface) mCtx;
 +//        else
 +//            LOG.d(LOG_TAG, "ERROR: You must use the CordovaInterface for this to work correctly. Please implement it in your activity");
 +//    }
 +
 +    @Override
 +    public boolean execute(String action, JSONArray args, CallbackContext callbackContext) throws JSONException {
 +        this.callbackContext = callbackContext;
 +        this.limit = 1;
 +        this.duration = 0.0f;
 +        this.results = new JSONArray();
 +
 +        JSONObject options = args.optJSONObject(0);
 +        if (options != null) {
 +            limit = options.optLong("limit", 1);
 +            duration = options.optDouble("duration", 0.0f);
 +        }
 +
 +        if (action.equals("getFormatData")) {
 +            JSONObject obj = getFormatData(args.getString(0), args.getString(1));
 +            callbackContext.success(obj);
 +            return true;
 +        }
 +        else if (action.equals("captureAudio")) {
 +            this.captureAudio();
 +        }
 +        else if (action.equals("captureImage")) {
 +            this.captureImage();
 +        }
 +        else if (action.equals("captureVideo")) {
 +            this.captureVideo(duration);
 +        }
 +        else {
 +            return false;
 +        }
 +
 +        return true;
 +    }
 +
 +    /**
 +     * Provides the media data file data depending on it's mime type
 +     *
 +     * @param filePath path to the file
 +     * @param mimeType of the file
 +     * @return a MediaFileData object
 +     */
 +    private JSONObject getFormatData(String filePath, String mimeType) throws JSONException {
 +        JSONObject obj = new JSONObject();
 +        // setup defaults
 +        obj.put("height", 0);
 +        obj.put("width", 0);
 +        obj.put("bitrate", 0);
 +        obj.put("duration", 0);
 +        obj.put("codecs", "");
 +
 +        // If the mimeType isn't set the rest will fail
 +        // so let's see if we can determine it.
 +        if (mimeType == null || mimeType.equals("") || "null".equals(mimeType)) {
 +            mimeType = FileHelper.getMimeType(filePath, cordova);
 +        }
 +        Log.d(LOG_TAG, "Mime type = " + mimeType);
 +
 +        if (mimeType.equals(IMAGE_JPEG) || filePath.endsWith(".jpg")) {
 +            obj = getImageData(filePath, obj);
 +        }
 +        else if (mimeType.endsWith(AUDIO_3GPP)) {
 +            obj = getAudioVideoData(filePath, obj, false);
 +        }
 +        else if (mimeType.equals(VIDEO_3GPP) || mimeType.equals(VIDEO_MP4)) {
 +            obj = getAudioVideoData(filePath, obj, true);
 +        }
 +        return obj;
 +    }
 +
 +    /**
 +     * Get the Image specific attributes
 +     *
 +     * @param filePath path to the file
 +     * @param obj represents the Media File Data
 +     * @return a JSONObject that represents the Media File Data
 +     * @throws JSONException
 +     */
 +    private JSONObject getImageData(String filePath, JSONObject obj) throws JSONException {
 +        BitmapFactory.Options options = new BitmapFactory.Options();
 +        options.inJustDecodeBounds = true;
 +        BitmapFactory.decodeFile(FileHelper.stripFileProtocol(filePath), options);
 +        obj.put("height", options.outHeight);
 +        obj.put("width", options.outWidth);
 +        return obj;
 +    }
 +
 +    /**
 +     * Get the Image specific attributes
 +     *
 +     * @param filePath path to the file
 +     * @param obj represents the Media File Data
 +     * @param video if true get video attributes as well
 +     * @return a JSONObject that represents the Media File Data
 +     * @throws JSONException
 +     */
 +    private JSONObject getAudioVideoData(String filePath, JSONObject obj, boolean video) throws JSONException {
 +        MediaPlayer player = new MediaPlayer();
 +        try {
 +            player.setDataSource(filePath);
 +            player.prepare();
 +            obj.put("duration", player.getDuration() / 1000);
 +            if (video) {
 +                obj.put("height", player.getVideoHeight());
 +                obj.put("width", player.getVideoWidth());
 +            }
 +        } catch (IOException e) {
 +            Log.d(LOG_TAG, "Error: loading video file");
 +        }
 +        return obj;
 +    }
 +
 +    /**
 +     * Sets up an intent to capture audio.  Result handled by onActivityResult()
 +     */
 +    private void captureAudio() {
 +        Intent intent = new Intent(android.provider.MediaStore.Audio.Media.RECORD_SOUND_ACTION);
 +
 +        this.cordova.startActivityForResult((CordovaPlugin) this, intent, CAPTURE_AUDIO);
 +    }
 +
 +    /**
 +     * Sets up an intent to capture images.  Result handled by onActivityResult()
 +     */
 +    private void captureImage() {
 +        // Save the number of images currently on disk for later
 +        this.numPics = queryImgDB(whichContentStore()).getCount();
 +
 +        Intent intent = new Intent(android.provider.MediaStore.ACTION_IMAGE_CAPTURE);
 +
 +        // Specify file so that large image is captured and returned
 +        File photo = new File(DirectoryManager.getTempDirectoryPath(this.cordova.getActivity()), "Capture.jpg");
 +        intent.putExtra(android.provider.MediaStore.EXTRA_OUTPUT, Uri.fromFile(photo));
 +
 +        this.cordova.startActivityForResult((CordovaPlugin) this, intent, CAPTURE_IMAGE);
 +    }
 +
 +    /**
 +     * Sets up an intent to capture video.  Result handled by onActivityResult()
 +     */
 +    private void captureVideo(double duration) {
 +        Intent intent = new Intent(android.provider.MediaStore.ACTION_VIDEO_CAPTURE);
-         // Introduced in API 8
-         //intent.putExtra(android.provider.MediaStore.EXTRA_DURATION_LIMIT, duration);
 +
++        if(Build.VERSION.SDK_INT > 8){
++            intent.putExtra("android.intent.extra.durationLimit", duration);
++        }
 +        this.cordova.startActivityForResult((CordovaPlugin) this, intent, CAPTURE_VIDEO);
 +    }
 +
 +    /**
 +     * Called when the video 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").
 +     * @throws JSONException
 +     */
 +    public void onActivityResult(int requestCode, int resultCode, Intent intent) {
 +
 +        // Result received okay
 +        if (resultCode == Activity.RESULT_OK) {
 +            // An audio clip was requested
 +            if (requestCode == CAPTURE_AUDIO) {
 +                // Get the uri of the audio clip
 +                Uri data = intent.getData();
 +                // create a file object from the uri
 +                results.put(createMediaFile(data));
 +
 +                if (results.length() >= limit) {
 +                    // Send Uri back to JavaScript for listening to audio
 +                    this.callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.OK, results));
 +                } else {
 +                    // still need to capture more audio clips
 +                    captureAudio();
 +                }
 +            } else if (requestCode == CAPTURE_IMAGE) {
 +                // For some reason if I try to do:
 +                // Uri data = intent.getData();
 +                // It crashes in the emulator and on my phone with a null pointer exception
 +                // To work around it I had to grab the code from CameraLauncher.java
 +                try {
 +                    // 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.cordova.getActivity().getContentResolver().insert(android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values);
 +                    } catch (UnsupportedOperationException e) {
 +                        LOG.d(LOG_TAG, "Can't write to external media storage.");
 +                        try {
 +                            uri = this.cordova.getActivity().getContentResolver().insert(android.provider.MediaStore.Images.Media.INTERNAL_CONTENT_URI, values);
 +                        } catch (UnsupportedOperationException ex) {
 +                            LOG.d(LOG_TAG, "Can't write to internal media storage.");
 +                            this.fail(createErrorObject(CAPTURE_INTERNAL_ERR, "Error capturing image - no media storage found."));
 +                            return;
 +                        }
 +                    }
 +                    FileInputStream fis = new FileInputStream(DirectoryManager.getTempDirectoryPath(this.cordova.getActivity()) + "/Capture.jpg");
 +                    OutputStream os = this.cordova.getActivity().getContentResolver().openOutputStream(uri);
 +                    byte[] buffer = new byte[4096];
 +                    int len;
 +                    while ((len = fis.read(buffer)) != -1) {
 +                        os.write(buffer, 0, len);
 +                    }
 +                    os.flush();
 +                    os.close();
 +                    fis.close();
 +
 +                    // Add image to results
 +                    results.put(createMediaFile(uri));
 +
 +                    checkForDuplicateImage();
 +
 +                    if (results.length() >= limit) {
 +                        // Send Uri back to JavaScript for viewing image
 +                        this.callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.OK, results));
 +                    } else {
 +                        // still need to capture more images
 +                        captureImage();
 +                    }
 +                } catch (IOException e) {
 +                    e.printStackTrace();
 +                    this.fail(createErrorObject(CAPTURE_INTERNAL_ERR, "Error capturing image."));
 +                }
 +            } else if (requestCode == CAPTURE_VIDEO) {
 +                // Get the uri of the video clip
 +                Uri data = intent.getData();
 +                // create a file object from the uri
 +                results.put(createMediaFile(data));
 +
 +                if (results.length() >= limit) {
 +                    // Send Uri back to JavaScript for viewing video
 +                    this.callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.OK, results));
 +                } else {
 +                    // still need to capture more video clips
 +                    captureVideo(duration);
 +                }
 +            }
 +        }
 +        // If canceled
 +        else if (resultCode == Activity.RESULT_CANCELED) {
 +            // If we have partial results send them back to the user
 +            if (results.length() > 0) {
 +                this.callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.OK, results));
 +            }
 +            // user canceled the action
 +            else {
 +                this.fail(createErrorObject(CAPTURE_NO_MEDIA_FILES, "Canceled."));
 +            }
 +        }
 +        // If something else
 +        else {
 +            // If we have partial results send them back to the user
 +            if (results.length() > 0) {
 +                this.callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.OK, results));
 +            }
 +            // something bad happened
 +            else {
 +                this.fail(createErrorObject(CAPTURE_NO_MEDIA_FILES, "Did not complete!"));
 +            }
 +        }
 +    }
 +
 +    /**
 +     * Creates a JSONObject that represents a File from the Uri
 +     *
 +     * @param data the Uri of the audio/image/video
 +     * @return a JSONObject that represents a File
 +     * @throws IOException
 +     */
 +    private JSONObject createMediaFile(Uri data) {
 +        File fp = new File(FileHelper.getRealPath(data, this.cordova));
 +        JSONObject obj = new JSONObject();
 +
 +        try {
 +            // File properties
 +            obj.put("name", fp.getName());
 +            obj.put("fullPath", "file://" + fp.getAbsolutePath());
 +            // Because of an issue with MimeTypeMap.getMimeTypeFromExtension() all .3gpp files
 +            // are reported as video/3gpp. I'm doing this hacky check of the URI to see if it
 +            // is stored in the audio or video content store.
 +            if (fp.getAbsoluteFile().toString().endsWith(".3gp") || fp.getAbsoluteFile().toString().endsWith(".3gpp")) {
 +                if (data.toString().contains("/audio/")) {
 +                    obj.put("type", AUDIO_3GPP);
 +                } else {
 +                    obj.put("type", VIDEO_3GPP);
 +                }
 +            } else {
 +                obj.put("type", FileHelper.getMimeType(fp.getAbsolutePath(), cordova));
 +            }
 +
 +            obj.put("lastModifiedDate", fp.lastModified());
 +            obj.put("size", fp.length());
 +        } catch (JSONException e) {
 +            // this will never happen
 +            e.printStackTrace();
 +        }
 +
 +        return obj;
 +    }
 +
 +    private JSONObject createErrorObject(int code, String message) {
 +        JSONObject obj = new JSONObject();
 +        try {
 +            obj.put("code", code);
 +            obj.put("message", message);
 +        } catch (JSONException e) {
 +            // This will never happen
 +        }
 +        return obj;
 +    }
 +
 +    /**
 +     * Send error message to JavaScript.
 +     *
 +     * @param err
 +     */
 +    public void fail(JSONObject err) {
 +        this.callbackContext.error(err);
 +    }
 +
 +
 +    /**
 +     * Creates a cursor that can be used to determine how many images we have.
 +     *
 +     * @return a cursor
 +     */
 +    private Cursor queryImgDB(Uri contentStore) {
 +        return this.cordova.getActivity().getContentResolver().query(
 +            contentStore,
 +            new String[] { MediaStore.Images.Media._ID },
 +            null,
 +            null,
 +            null);
 +    }
 +
 +    /**
 +     * Used to find out if we are in a situation where the Camera Intent adds to images
 +     * to the content store.
 +     */
 +    private void checkForDuplicateImage() {
 +        Uri contentStore = whichContentStore();
 +        Cursor cursor = queryImgDB(contentStore);
 +        int currentNumOfImages = cursor.getCount();
 +
 +        // delete the duplicate file if the difference is 2
 +        if ((currentNumOfImages - numPics) == 2) {
 +            cursor.moveToLast();
 +            int id = Integer.valueOf(cursor.getString(cursor.getColumnIndex(MediaStore.Images.Media._ID))) - 1;
 +            Uri uri = Uri.parse(contentStore + "/" + id);
 +            this.cordova.getActivity().getContentResolver().delete(uri, null, null);
 +        }
 +    }
 +
 +    /**
 +     * Determine if we are storing the images in internal or external storage
 +     * @return Uri
 +     */
 +    private Uri whichContentStore() {
 +        if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
 +            return android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
 +        } else {
 +            return android.provider.MediaStore.Images.Media.INTERNAL_CONTENT_URI;
 +        }
 +    }
 +}

http://git-wip-us.apache.org/repos/asf/cordova-android/blob/7ab30348/framework/src/org/apache/cordova/core/Device.java
----------------------------------------------------------------------
diff --cc framework/src/org/apache/cordova/core/Device.java
index 93992e8,0000000..3778b22
mode 100644,000000..100644
--- a/framework/src/org/apache/cordova/core/Device.java
+++ b/framework/src/org/apache/cordova/core/Device.java
@@@ -1,201 -1,0 +1,201 @@@
 +/*
 +       Licensed to the Apache Software Foundation (ASF) under one
 +       or more contributor license agreements.  See the NOTICE file
 +       distributed with this work for additional information
 +       regarding copyright ownership.  The ASF licenses this file
 +       to you under the Apache License, Version 2.0 (the
 +       "License"); you may not use this file except in compliance
 +       with the License.  You may obtain a copy of the License at
 +
 +         http://www.apache.org/licenses/LICENSE-2.0
 +
 +       Unless required by applicable law or agreed to in writing,
 +       software distributed under the License is distributed on an
 +       "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 +       KIND, either express or implied.  See the License for the
 +       specific language governing permissions and limitations
 +       under the License.
 +*/
 +package org.apache.cordova.core;
 +
 +import java.util.TimeZone;
 +
 +import org.apache.cordova.CordovaWebView;
 +import org.apache.cordova.api.CallbackContext;
 +import org.apache.cordova.api.CordovaPlugin;
 +import org.apache.cordova.api.LOG;
 +import org.apache.cordova.api.CordovaInterface;
 +import org.json.JSONArray;
 +import org.json.JSONException;
 +import org.json.JSONObject;
 +
 +import android.content.BroadcastReceiver;
 +import android.content.Context;
 +import android.content.Intent;
 +import android.content.IntentFilter;
 +import android.provider.Settings;
 +import android.telephony.TelephonyManager;
 +
 +public class Device extends CordovaPlugin {
 +    public static final String TAG = "Device";
 +
-     public static String cordovaVersion = "2.5.0";              // Cordova version
++    public static String cordovaVersion = "2.6.0rc1";              // Cordova version
 +    public static String platform = "Android";                  // Device OS
 +    public static String uuid;                                  // Device UUID
 +
 +    BroadcastReceiver telephonyReceiver = null;
 +
 +    /**
 +     * Constructor.
 +     */
 +    public Device() {
 +    }
 +
 +    /**
 +     * Sets the context of the Command. This can then be used to do things like
 +     * get file paths associated with the Activity.
 +     *
 +     * @param cordova The context of the main Activity.
 +     * @param webView The CordovaWebView Cordova is running in.
 +     */
 +    public void initialize(CordovaInterface cordova, CordovaWebView webView) {
 +        super.initialize(cordova, webView);
 +        Device.uuid = getUuid();
 +        this.initTelephonyReceiver();
 +    }
 +
 +    /**
 +     * Executes the request and returns PluginResult.
 +     *
 +     * @param action            The action to execute.
 +     * @param args              JSONArry of arguments for the plugin.
 +     * @param callbackContext   The callback id used when calling back into JavaScript.
 +     * @return                  True if the action was valid, false if not.
 +     */
 +    public boolean execute(String action, JSONArray args, CallbackContext callbackContext) throws JSONException {
 +        if (action.equals("getDeviceInfo")) {
 +            JSONObject r = new JSONObject();
 +            r.put("uuid", Device.uuid);
 +            r.put("version", this.getOSVersion());
 +            r.put("platform", Device.platform);
 +            r.put("name", this.getProductName());
 +            r.put("cordova", Device.cordovaVersion);
 +            r.put("model", this.getModel());
 +            callbackContext.success(r);
 +        }
 +        else {
 +            return false;
 +        }
 +        return true;
 +    }
 +
 +    /**
 +     * Unregister receiver.
 +     */
 +    public void onDestroy() {
 +        this.cordova.getActivity().unregisterReceiver(this.telephonyReceiver);
 +    }
 +
 +    //--------------------------------------------------------------------------
 +    // LOCAL METHODS
 +    //--------------------------------------------------------------------------
 +
 +    /**
 +     * Listen for telephony events: RINGING, OFFHOOK and IDLE
 +     * Send these events to all plugins using
 +     *      DroidGap.onMessage("telephone", "ringing" | "offhook" | "idle")
 +     */
 +    private void initTelephonyReceiver() {
 +        IntentFilter intentFilter = new IntentFilter();
 +        intentFilter.addAction(TelephonyManager.ACTION_PHONE_STATE_CHANGED);
 +        //final CordovaInterface mycordova = this.cordova;
 +        this.telephonyReceiver = new BroadcastReceiver() {
 +
 +            @Override
 +            public void onReceive(Context context, Intent intent) {
 +
 +                // If state has changed
 +                if ((intent != null) && intent.getAction().equals(TelephonyManager.ACTION_PHONE_STATE_CHANGED)) {
 +                    if (intent.hasExtra(TelephonyManager.EXTRA_STATE)) {
 +                        String extraData = intent.getStringExtra(TelephonyManager.EXTRA_STATE);
 +                        if (extraData.equals(TelephonyManager.EXTRA_STATE_RINGING)) {
 +                            LOG.i(TAG, "Telephone RINGING");
 +                            webView.postMessage("telephone", "ringing");
 +                        }
 +                        else if (extraData.equals(TelephonyManager.EXTRA_STATE_OFFHOOK)) {
 +                            LOG.i(TAG, "Telephone OFFHOOK");
 +                            webView.postMessage("telephone", "offhook");
 +                        }
 +                        else if (extraData.equals(TelephonyManager.EXTRA_STATE_IDLE)) {
 +                            LOG.i(TAG, "Telephone IDLE");
 +                            webView.postMessage("telephone", "idle");
 +                        }
 +                    }
 +                }
 +            }
 +        };
 +
 +        // Register the receiver
 +        this.cordova.getActivity().registerReceiver(this.telephonyReceiver, intentFilter);
 +    }
 +
 +    /**
 +     * Get the OS name.
 +     *
 +     * @return
 +     */
 +    public String getPlatform() {
 +        return Device.platform;
 +    }
 +
 +    /**
 +     * Get the device's Universally Unique Identifier (UUID).
 +     *
 +     * @return
 +     */
 +    public String getUuid() {
 +        String uuid = Settings.Secure.getString(this.cordova.getActivity().getContentResolver(), android.provider.Settings.Secure.ANDROID_ID);
 +        return uuid;
 +    }
 +
 +    /**
 +     * Get the Cordova version.
 +     *
 +     * @return
 +     */
 +    public String getCordovaVersion() {
 +        return Device.cordovaVersion;
 +    }
 +
 +    public String getModel() {
 +        String model = android.os.Build.MODEL;
 +        return model;
 +    }
 +
 +    public String getProductName() {
 +        String productname = android.os.Build.PRODUCT;
 +        return productname;
 +    }
 +
 +    /**
 +     * Get the OS version.
 +     *
 +     * @return
 +     */
 +    public String getOSVersion() {
 +        String osversion = android.os.Build.VERSION.RELEASE;
 +        return osversion;
 +    }
 +
 +    public String getSDKVersion() {
 +        @SuppressWarnings("deprecation")
 +        String sdkversion = android.os.Build.VERSION.SDK;
 +        return sdkversion;
 +    }
 +
 +    public String getTimeZoneID() {
 +        TimeZone tz = TimeZone.getDefault();
 +        return (tz.getID());
 +    }
 +
 +}


Mime
View raw message