cordova-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From ste...@apache.org
Subject [17/53] android commit: Moving them temporarily to core in this repo, we're going to move this again before the release, because change is scary
Date Tue, 30 Apr 2013 21:35:33 GMT
Moving them temporarily to core in this repo, we're going to move this again before the release, because change is scary


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

Branch: refs/heads/3.0.0
Commit: 5cc3852cb9faf5606bfdeb5e20c186081910e1dc
Parents: e52404f 66b827e
Author: Joe Bowser <bowserj@apache.org>
Authored: Fri Mar 15 15:02:00 2013 -0700
Committer: Joe Bowser <bowserj@apache.org>
Committed: Fri Mar 15 15:02:00 2013 -0700

----------------------------------------------------------------------
 bin/create                                         |   16 +-
 framework/res/xml/config.xml                       |   38 +-
 framework/src/org/apache/cordova/FileHelper.java   |  142 ++++++
 .../cordova/IceCreamCordovaWebViewClient.java      |   16 +-
 .../org/apache/cordova/NativeToJsMessageQueue.java |    9 +-
 .../src/org/apache/cordova/api/PluginResult.java   |   11 +-
 .../src/org/apache/cordova/core/AudioHandler.java  |    6 +-
 .../org/apache/cordova/core/CameraLauncher.java    |   16 +-
 framework/src/org/apache/cordova/core/Capture.java |    8 +-
 .../src/org/apache/cordova/core/FileTransfer.java  |    3 +-
 .../src/org/apache/cordova/core/FileUtils.java     |  347 +++++----------
 .../src/org/apache/cordova/core/GeoBroker.java     |    2 +-
 .../src/org/apache/cordova/core/Notification.java  |   63 ++--
 13 files changed, 364 insertions(+), 313 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cordova-android/blob/5cc3852c/framework/res/xml/config.xml
----------------------------------------------------------------------
diff --cc framework/res/xml/config.xml
index bc6ca0c,bc6ca0c..90ed78f
--- a/framework/res/xml/config.xml
+++ b/framework/res/xml/config.xml
@@@ -36,25 -36,25 +36,25 @@@
      <preference name="useBrowserHistory" value="true" />
      <preference name="exit-on-suspend" value="false" />
  <plugins>
--    <plugin name="App" value="org.apache.cordova.App"/>
--    <plugin name="Geolocation" value="org.apache.cordova.GeoBroker"/>
--    <plugin name="Device" value="org.apache.cordova.Device"/>
--    <plugin name="Accelerometer" value="org.apache.cordova.AccelListener"/>
--    <plugin name="Compass" value="org.apache.cordova.CompassListener"/>
--    <plugin name="Media" value="org.apache.cordova.AudioHandler"/>
--    <plugin name="Camera" value="org.apache.cordova.CameraLauncher"/>
--    <plugin name="Contacts" value="org.apache.cordova.ContactManager"/>
--    <plugin name="File" value="org.apache.cordova.FileUtils"/>
--    <plugin name="NetworkStatus" value="org.apache.cordova.NetworkManager"/>
--    <plugin name="Notification" value="org.apache.cordova.Notification"/>
--    <plugin name="Storage" value="org.apache.cordova.Storage"/>
--    <plugin name="FileTransfer" value="org.apache.cordova.FileTransfer"/>
--    <plugin name="Capture" value="org.apache.cordova.Capture"/>
--    <plugin name="Battery" value="org.apache.cordova.BatteryListener"/>
--    <plugin name="SplashScreen" value="org.apache.cordova.SplashScreen"/>
--    <plugin name="Echo" value="org.apache.cordova.Echo" />
--    <plugin name="Globalization" value="org.apache.cordova.Globalization"/>
--    <plugin name="InAppBrowser" value="org.apache.cordova.InAppBrowser"/>
++    <plugin name="App" value="org.apache.cordova.core.App"/>
++    <plugin name="Geolocation" value="org.apache.cordova.core.GeoBroker"/>
++    <plugin name="Device" value="org.apache.cordova.core.Device"/>
++    <plugin name="Accelerometer" value="org.apache.cordova.core.AccelListener"/>
++    <plugin name="Compass" value="org.apache.cordova.core.CompassListener"/>
++    <plugin name="Media" value="org.apache.cordova.core.AudioHandler"/>
++    <plugin name="Camera" value="org.apache.cordova.core.CameraLauncher"/>
++    <plugin name="Contacts" value="org.apache.cordova.core.ContactManager"/>
++    <plugin name="File" value="org.apache.cordova.core.FileUtils"/>
++    <plugin name="NetworkStatus" value="org.apache.cordova.core.NetworkManager"/>
++    <plugin name="Notification" value="org.apache.cordova.core.Notification"/>
++    <plugin name="Storage" value="org.apache.cordova.core.Storage"/>
++    <plugin name="FileTransfer" value="org.apache.cordova.core.FileTransfer"/>
++    <plugin name="Capture" value="org.apache.cordova.core.Capture"/>
++    <plugin name="Battery" value="org.apache.cordova.core.BatteryListener"/>
++    <plugin name="SplashScreen" value="org.apache.cordova.core.SplashScreen"/>
++    <plugin name="Echo" value="org.apache.cordova.core.Echo" />
++    <plugin name="Globalization" value="org.apache.cordova.core.Globalization"/>
++    <plugin name="InAppBrowser" value="org.apache.cordova.core.InAppBrowser"/>
  </plugins>
  </cordova>
  

http://git-wip-us.apache.org/repos/asf/cordova-android/blob/5cc3852c/framework/src/org/apache/cordova/NativeToJsMessageQueue.java
----------------------------------------------------------------------

http://git-wip-us.apache.org/repos/asf/cordova-android/blob/5cc3852c/framework/src/org/apache/cordova/core/AudioHandler.java
----------------------------------------------------------------------
diff --cc framework/src/org/apache/cordova/core/AudioHandler.java
index 1877af0,0000000..e8884df
mode 100644,000000..100644
--- a/framework/src/org/apache/cordova/core/AudioHandler.java
+++ b/framework/src/org/apache/cordova/core/AudioHandler.java
@@@ -1,357 -1,0 +1,357 @@@
 +/*
 +       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 org.apache.cordova.api.CallbackContext;
 +import org.apache.cordova.api.CordovaPlugin;
 +
 +import android.content.Context;
 +import android.media.AudioManager;
 +
 +import java.util.ArrayList;
 +
 +import org.apache.cordova.api.PluginResult;
 +import org.json.JSONArray;
 +import org.json.JSONException;
 +import java.util.HashMap;
 +
 +/**
 + * This class called by CordovaActivity to play and record audio.
 + * The file can be local or over a network using http.
 + *
 + * Audio formats supported (tested):
 + * 	.mp3, .wav
 + *
 + * Local audio files must reside in one of two places:
 + * 		android_asset: 		file name must start with /android_asset/sound.mp3
 + * 		sdcard:				file name is just sound.mp3
 + */
 +public class AudioHandler extends CordovaPlugin {
 +
 +    public static String TAG = "AudioHandler";
 +    HashMap<String, AudioPlayer> players;	// Audio player object
 +    ArrayList<AudioPlayer> pausedForPhone;     // Audio players that were paused when phone call came in
 +
 +    /**
 +     * Constructor.
 +     */
 +    public AudioHandler() {
 +        this.players = new HashMap<String, AudioPlayer>();
 +        this.pausedForPhone = new ArrayList<AudioPlayer>();
 +    }
 +
 +    /**
 +     * Executes the request and returns PluginResult.
 +     * @param action 		The action to execute.
 +     * @param args 			JSONArry of arguments for the plugin.
 +     * @param callbackContext		The callback context 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 {
 +        PluginResult.Status status = PluginResult.Status.OK;
 +        String result = "";
 +
 +        if (action.equals("startRecordingAudio")) {
-             this.startRecordingAudio(args.getString(0), FileUtils.stripFileProtocol(args.getString(1)));
++            this.startRecordingAudio(args.getString(0), FileHelper.stripFileProtocol(args.getString(1)));
 +        }
 +        else if (action.equals("stopRecordingAudio")) {
 +            this.stopRecordingAudio(args.getString(0));
 +        }
 +        else if (action.equals("startPlayingAudio")) {
-             this.startPlayingAudio(args.getString(0), FileUtils.stripFileProtocol(args.getString(1)));
++            this.startPlayingAudio(args.getString(0), FileHelper.stripFileProtocol(args.getString(1)));
 +        }
 +        else if (action.equals("seekToAudio")) {
 +            this.seekToAudio(args.getString(0), args.getInt(1));
 +        }
 +        else if (action.equals("pausePlayingAudio")) {
 +            this.pausePlayingAudio(args.getString(0));
 +        }
 +        else if (action.equals("stopPlayingAudio")) {
 +            this.stopPlayingAudio(args.getString(0));
 +        } else if (action.equals("setVolume")) {
 +           try {
 +               this.setVolume(args.getString(0), Float.parseFloat(args.getString(1)));
 +           } catch (NumberFormatException nfe) {
 +               //no-op
 +           }
 +        } else if (action.equals("getCurrentPositionAudio")) {
 +            float f = this.getCurrentPositionAudio(args.getString(0));
 +            callbackContext.sendPluginResult(new PluginResult(status, f));
 +            return true;
 +        }
 +        else if (action.equals("getDurationAudio")) {
 +            float f = this.getDurationAudio(args.getString(0), args.getString(1));
 +            callbackContext.sendPluginResult(new PluginResult(status, f));
 +            return true;
 +        }
 +        else if (action.equals("create")) {
 +            String id = args.getString(0);
-             String src = FileUtils.stripFileProtocol(args.getString(1));
++            String src = FileHelper.stripFileProtocol(args.getString(1));
 +            AudioPlayer audio = new AudioPlayer(this, id, src);
 +            this.players.put(id, audio);
 +        }
 +        else if (action.equals("release")) {
 +            boolean b = this.release(args.getString(0));
 +            callbackContext.sendPluginResult(new PluginResult(status, b));
 +            return true;
 +        }
 +        else { // Unrecognized action.
 +            return false;
 +        }
 +
 +        callbackContext.sendPluginResult(new PluginResult(status, result));
 +
 +        return true;
 +    }
 +
 +    /**
 +     * Stop all audio players and recorders.
 +     */
 +    public void onDestroy() {
 +        for (AudioPlayer audio : this.players.values()) {
 +            audio.destroy();
 +        }
 +        this.players.clear();
 +    }
 +
 +    /**
 +     * Stop all audio players and recorders on navigate.
 +     */
 +    @Override
 +    public void onReset() {
 +        onDestroy();
 +    }
 +
 +    /**
 +     * Called when a message is sent to plugin.
 +     *
 +     * @param id            The message id
 +     * @param data          The message data
 +     * @return              Object to stop propagation or null
 +     */
 +    public Object onMessage(String id, Object data) {
 +
 +        // If phone message
 +        if (id.equals("telephone")) {
 +
 +            // If phone ringing, then pause playing
 +            if ("ringing".equals(data) || "offhook".equals(data)) {
 +
 +                // Get all audio players and pause them
 +                for (AudioPlayer audio : this.players.values()) {
 +                    if (audio.getState() == AudioPlayer.STATE.MEDIA_RUNNING.ordinal()) {
 +                        this.pausedForPhone.add(audio);
 +                        audio.pausePlaying();
 +                    }
 +                }
 +
 +            }
 +
 +            // If phone idle, then resume playing those players we paused
 +            else if ("idle".equals(data)) {
 +                for (AudioPlayer audio : this.pausedForPhone) {
 +                    audio.startPlaying(null);
 +                }
 +                this.pausedForPhone.clear();
 +            }
 +        }
 +        return null;
 +    }
 +
 +    //--------------------------------------------------------------------------
 +    // LOCAL METHODS
 +    //--------------------------------------------------------------------------
 +
 +    /**
 +     * Release the audio player instance to save memory.
 +     * @param id				The id of the audio player
 +     */
 +    private boolean release(String id) {
 +        if (!this.players.containsKey(id)) {
 +            return false;
 +        }
 +        AudioPlayer audio = this.players.get(id);
 +        this.players.remove(id);
 +        audio.destroy();
 +        return true;
 +    }
 +
 +    /**
 +     * Start recording and save the specified file.
 +     * @param id				The id of the audio player
 +     * @param file				The name of the file
 +     */
 +    public void startRecordingAudio(String id, String file) {
 +        AudioPlayer audio = this.players.get(id);
 +        if ( audio == null) {
 +            audio = new AudioPlayer(this, id, file);
 +            this.players.put(id, audio);
 +        }
 +        audio.startRecording(file);
 +    }
 +
 +    /**
 +     * Stop recording and save to the file specified when recording started.
 +     * @param id				The id of the audio player
 +     */
 +    public void stopRecordingAudio(String id) {
 +        AudioPlayer audio = this.players.get(id);
 +        if (audio != null) {
 +            audio.stopRecording();
 +        }
 +    }
 +
 +    /**
 +     * Start or resume playing audio file.
 +     * @param id				The id of the audio player
 +     * @param file				The name of the audio file.
 +     */
 +    public void startPlayingAudio(String id, String file) {
 +        AudioPlayer audio = this.players.get(id);
 +        if (audio == null) {
 +            audio = new AudioPlayer(this, id, file);
 +            this.players.put(id, audio);
 +        }
 +        audio.startPlaying(file);
 +    }
 +
 +    /**
 +     * Seek to a location.
 +     * @param id				The id of the audio player
 +     * @param milliseconds		int: number of milliseconds to skip 1000 = 1 second
 +     */
 +    public void seekToAudio(String id, int milliseconds) {
 +        AudioPlayer audio = this.players.get(id);
 +        if (audio != null) {
 +            audio.seekToPlaying(milliseconds);
 +        }
 +    }
 +
 +    /**
 +     * Pause playing.
 +     * @param id				The id of the audio player
 +     */
 +    public void pausePlayingAudio(String id) {
 +        AudioPlayer audio = this.players.get(id);
 +        if (audio != null) {
 +            audio.pausePlaying();
 +        }
 +    }
 +
 +    /**
 +     * Stop playing the audio file.
 +     * @param id				The id of the audio player
 +     */
 +    public void stopPlayingAudio(String id) {
 +        AudioPlayer audio = this.players.get(id);
 +        if (audio != null) {
 +            audio.stopPlaying();
 +            //audio.destroy();
 +            //this.players.remove(id);
 +        }
 +    }
 +
 +    /**
 +     * Get current position of playback.
 +     * @param id				The id of the audio player
 +     * @return 					position in msec
 +     */
 +    public float getCurrentPositionAudio(String id) {
 +        AudioPlayer audio = this.players.get(id);
 +        if (audio != null) {
 +            return (audio.getCurrentPosition() / 1000.0f);
 +        }
 +        return -1;
 +    }
 +
 +    /**
 +     * Get the duration of the audio file.
 +     * @param id				The id of the audio player
 +     * @param file				The name of the audio file.
 +     * @return					The duration in msec.
 +     */
 +    public float getDurationAudio(String id, String file) {
 +
 +        // Get audio file
 +        AudioPlayer audio = this.players.get(id);
 +        if (audio != null) {
 +            return (audio.getDuration(file));
 +        }
 +
 +        // If not already open, then open the file
 +        else {
 +            audio = new AudioPlayer(this, id, file);
 +            this.players.put(id, audio);
 +            return (audio.getDuration(file));
 +        }
 +    }
 +
 +    /**
 +     * Set the audio device to be used for playback.
 +     *
 +     * @param output			1=earpiece, 2=speaker
 +     */
 +    @SuppressWarnings("deprecation")
 +    public void setAudioOutputDevice(int output) {
 +        AudioManager audiMgr = (AudioManager) this.cordova.getActivity().getSystemService(Context.AUDIO_SERVICE);
 +        if (output == 2) {
 +            audiMgr.setRouting(AudioManager.MODE_NORMAL, AudioManager.ROUTE_SPEAKER, AudioManager.ROUTE_ALL);
 +        }
 +        else if (output == 1) {
 +            audiMgr.setRouting(AudioManager.MODE_NORMAL, AudioManager.ROUTE_EARPIECE, AudioManager.ROUTE_ALL);
 +        }
 +        else {
 +            System.out.println("AudioHandler.setAudioOutputDevice() Error: Unknown output device.");
 +        }
 +    }
 +
 +    /**
 +     * Get the audio device to be used for playback.
 +     *
 +     * @return					1=earpiece, 2=speaker
 +     */
 +    @SuppressWarnings("deprecation")
 +    public int getAudioOutputDevice() {
 +        AudioManager audiMgr = (AudioManager) this.cordova.getActivity().getSystemService(Context.AUDIO_SERVICE);
 +        if (audiMgr.getRouting(AudioManager.MODE_NORMAL) == AudioManager.ROUTE_EARPIECE) {
 +            return 1;
 +        }
 +        else if (audiMgr.getRouting(AudioManager.MODE_NORMAL) == AudioManager.ROUTE_SPEAKER) {
 +            return 2;
 +        }
 +        else {
 +            return -1;
 +        }
 +    }
 +
 +    /**
 +     * Set the volume for an audio device
 +     *
 +     * @param id				The id of the audio player
 +     * @param volume            Volume to adjust to 0.0f - 1.0f
 +     */
 +    public void setVolume(String id, float volume) {
 +        AudioPlayer audio = this.players.get(id);
 +        if (audio != null) {
 +            audio.setVolume(volume);
 +        } else {
 +            System.out.println("AudioHandler.setVolume() Error: Unknown Audio Player " + id);
 +        }
 +    }
 +}

http://git-wip-us.apache.org/repos/asf/cordova-android/blob/5cc3852c/framework/src/org/apache/cordova/core/CameraLauncher.java
----------------------------------------------------------------------
diff --cc framework/src/org/apache/cordova/core/CameraLauncher.java
index ca9b4b8,0000000..4d97fed
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,793 @@@
 +/*
 +       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(FileUtils.stripFileProtocol(imageUri.toString()));
++                        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"));
 +                        } else {
 +                            uri = getUriFromMediaStore();
 +                        }
 +
 +                        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(FileUtils.stripFileProtocol(imageUri.toString()));
++                            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 = FileUtils.getRealPathFromURI(uri, this.cordova);
++                                    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 = FileUtils.getRealPathFromURI(uri, this.cordova);
-                         String mimeType = FileUtils.getMimeType(imagePath);
++                        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.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(FileUtils.getRealPathFromURI(uri, this.cordova));
++                                        exif.createOutFile(FileHelper.getRealPath(uri, this.cordova));
 +                                        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(FileUtils.stripFileProtocol(imageUri.toString()));
++        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(FileUtils.stripFileProtocol(oldImage.toString()))).delete();
++        (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/5cc3852c/framework/src/org/apache/cordova/core/Capture.java
----------------------------------------------------------------------
diff --cc framework/src/org/apache/cordova/core/Capture.java
index 1f978e9,0000000..7d1710a
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,443 @@@
 +/*
 +       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 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 = FileUtils.getMimeType(filePath);
++            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(FileUtils.stripFileProtocol(filePath), options);
++        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);
 +
 +        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(FileUtils.getRealPathFromURI(data, this.cordova));
++        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", FileUtils.getMimeType(fp.getAbsolutePath()));
++                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;
 +        }
 +    }
 +}


Mime
View raw message