Updated Branches:
  refs/heads/master [created] 746eb2754

Initial Commit


Project: 
http://git-wip-us.apache.org/repos/asf/cordova-plugin-media-capture/repo
Commit: 
http://git-wip-us.apache.org/repos/asf/cordova-plugin-media-capture/commit/746eb275
Tree: 
http://git-wip-us.apache.org/repos/asf/cordova-plugin-media-capture/tree/746eb275
Diff: 
http://git-wip-us.apache.org/repos/asf/cordova-plugin-media-capture/diff/746eb275

Branch: refs/heads/master
Commit: 746eb2754e318a3493335b2c75d827fc649e8729
Parents: 
Author: Joe Bowser <[email protected]>
Authored: Wed Apr 3 14:14:18 2013 -0700
Committer: Joe Bowser <[email protected]>
Committed: Wed Apr 3 14:14:18 2013 -0700

----------------------------------------------------------------------
 plugin.xml               |   17 ++
 src/android/Capture.java |  448 +++++++++++++++++++++++++++++++++++++++++
 2 files changed, 465 insertions(+), 0 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cordova-plugin-media-capture/blob/746eb275/plugin.xml
----------------------------------------------------------------------
diff --git a/plugin.xml b/plugin.xml
new file mode 100644
index 0000000..0eb0f67
--- /dev/null
+++ b/plugin.xml
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<plugin xmlns="http://www.phonegap.com/ns/plugins/1.0";
+xmlns:android="http://schemas.android.com/apk/res/android";
+id="org.apache.cordova.core">
+    version="0.1.0">
+    <name>Capture</name>
+
+    <!-- android -->
+    <platform name="android">
+        <config-file target="res/xml/config.xml" parent="/cordova/plugins">
+            <plugin name="Capture" value="org.apache.cordova.core.Capture"/>
+        </config-file>
+
+        <source-file src="Capture.java" target-dir="org/apache/cordova/core" />
+       </platform>
+</plugin>

http://git-wip-us.apache.org/repos/asf/cordova-plugin-media-capture/blob/746eb275/src/android/Capture.java
----------------------------------------------------------------------
diff --git a/src/android/Capture.java b/src/android/Capture.java
new file mode 100644
index 0000000..030ce99
--- /dev/null
+++ b/src/android/Capture.java
@@ -0,0 +1,448 @@
+/*
+       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.DirectoryManager;
+import org.apache.cordova.FileHelper;
+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);
+
+        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;
+        }
+    }
+}

Reply via email to