You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cordova.apache.org by lo...@apache.org on 2013/06/19 20:32:21 UTC
[30/36] [CB-3401] renamed blackberry root folder to bbos
http://git-wip-us.apache.org/repos/asf/cordova-blackberry/blob/041e4481/bbos/framework/ext/src/org/apache/cordova/camera/PhotoListener.java
----------------------------------------------------------------------
diff --git a/bbos/framework/ext/src/org/apache/cordova/camera/PhotoListener.java b/bbos/framework/ext/src/org/apache/cordova/camera/PhotoListener.java
new file mode 100644
index 0000000..8571788
--- /dev/null
+++ b/bbos/framework/ext/src/org/apache/cordova/camera/PhotoListener.java
@@ -0,0 +1,107 @@
+/*
+ * 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.camera;
+
+import net.rim.device.api.io.file.FileSystemJournal;
+import net.rim.device.api.io.file.FileSystemJournalEntry;
+import net.rim.device.api.io.file.FileSystemJournalListener;
+import net.rim.device.api.ui.UiApplication;
+
+/**
+ * Listens for photo added to file system and invokes the specified callback
+ * with the result formatted according the specified destination type.
+ */
+public class PhotoListener implements FileSystemJournalListener {
+
+ /**
+ * Image format options specified by the caller.
+ */
+ private CameraOptions options;
+
+ /**
+ * Callback to be invoked with the result.
+ */
+ private String callbackId;
+
+ /**
+ * Used to track file system changes.
+ */
+ private long lastUSN = 0;
+
+ /**
+ * Constructor.
+ * @param options Specifies the format of the image and result
+ * @param callbackId The id of the callback to receive the result
+ */
+ public PhotoListener(CameraOptions options, String callbackId)
+ {
+ this.options = options;
+ this.callbackId = callbackId;
+ }
+
+ /**
+ * Listens for file system changes. When a JPEG file is added, we process
+ * it and send it back.
+ */
+ public void fileJournalChanged()
+ {
+ // next sequence number file system will use
+ long USN = FileSystemJournal.getNextUSN();
+
+ for (long i = USN - 1; i >= lastUSN && i < USN; --i)
+ {
+ FileSystemJournalEntry entry = FileSystemJournal.getEntry(i);
+ if (entry == null)
+ {
+ break;
+ }
+
+ if (entry.getEvent() == FileSystemJournalEntry.FILE_ADDED)
+ {
+ String path = entry.getPath();
+ if (path != null && path.indexOf(".jpg") != -1)
+ {
+ // we found a new JPEG file
+ // first, stop listening to avoid processing the file more than once
+ synchronized(UiApplication.getEventLock()) {
+ UiApplication.getUiApplication().removeFileSystemJournalListener(this);
+ }
+
+ // process the image on a background thread to avoid clogging the event queue
+ final String filePath = "file://" + path;
+ Thread thread = new Thread(new Runnable() {
+ public void run() {
+ Camera.processImage(filePath, options, callbackId);
+ }
+ });
+ thread.start();
+
+ // clean up
+ Camera.closeCamera();
+
+ break;
+ }
+ }
+ }
+
+ // remember the file journal change number,
+ // so we don't search the same events again and again
+ lastUSN = USN;
+ }
+}
http://git-wip-us.apache.org/repos/asf/cordova-blackberry/blob/041e4481/bbos/framework/ext/src/org/apache/cordova/capture/AudioCaptureListener.java
----------------------------------------------------------------------
diff --git a/bbos/framework/ext/src/org/apache/cordova/capture/AudioCaptureListener.java b/bbos/framework/ext/src/org/apache/cordova/capture/AudioCaptureListener.java
new file mode 100644
index 0000000..9267e9b
--- /dev/null
+++ b/bbos/framework/ext/src/org/apache/cordova/capture/AudioCaptureListener.java
@@ -0,0 +1,80 @@
+/*
+ * 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.capture;
+
+import net.rim.device.api.io.file.FileSystemJournal;
+import net.rim.device.api.io.file.FileSystemJournalEntry;
+import net.rim.device.api.io.file.FileSystemJournalListener;
+
+/**
+ * Listens for audio recording files that are added to file system.
+ * <p>
+ * Audio recordings are added to the file system when the user stops the
+ * recording. The audio recording file extension is '.amr'. Therefore, we listen
+ * for the <code>FileSystemJournalEntry.FILE_ADDED</code> event, capturing when
+ * the new file is written.
+ * <p>
+ * The file system notifications will arrive on the application event thread.
+ * When it receives a notification, it adds the image file path to a MediaQueue
+ * so that the capture thread can process the file.
+ */
+public class AudioCaptureListener implements FileSystemJournalListener {
+ /**
+ * Used to track file system changes.
+ */
+ private long lastUSN = 0;
+
+ /**
+ * Queue to send media files to for processing.
+ */
+ private MediaQueue queue = null;
+
+ /**
+ * Constructor.
+ */
+ AudioCaptureListener(MediaQueue queue) {
+ this.queue = queue;
+ }
+
+ public void fileJournalChanged() {
+ // next sequence number file system will use
+ long USN = FileSystemJournal.getNextUSN();
+
+ for (long i = USN - 1; i >= lastUSN && i < USN; --i) {
+ FileSystemJournalEntry entry = FileSystemJournal.getEntry(i);
+ if (entry == null) {
+ break;
+ }
+
+ // has audio recording file has been added to the file system?
+ String path = entry.getPath();
+ if (entry.getEvent() == FileSystemJournalEntry.FILE_ADDED
+ && path.endsWith(".amr")) {
+ // add file path to the capture queue
+ queue.add("file://" + path);
+
+ break;
+ }
+ }
+
+ // remember the file journal change number,
+ // so we don't search the same events again and again
+ lastUSN = USN;
+ }
+}
http://git-wip-us.apache.org/repos/asf/cordova-blackberry/blob/041e4481/bbos/framework/ext/src/org/apache/cordova/capture/AudioCaptureOperation.java
----------------------------------------------------------------------
diff --git a/bbos/framework/ext/src/org/apache/cordova/capture/AudioCaptureOperation.java b/bbos/framework/ext/src/org/apache/cordova/capture/AudioCaptureOperation.java
new file mode 100644
index 0000000..f4fd9b4
--- /dev/null
+++ b/bbos/framework/ext/src/org/apache/cordova/capture/AudioCaptureOperation.java
@@ -0,0 +1,173 @@
+/*
+ * 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.capture;
+
+import java.io.IOException;
+import java.util.Date;
+
+import javax.microedition.io.Connector;
+import javax.microedition.io.file.FileConnection;
+
+import org.apache.cordova.file.File;
+import org.apache.cordova.util.FileUtils;
+import org.apache.cordova.util.Logger;
+
+import net.rim.device.api.io.MIMETypeAssociations;
+import net.rim.device.api.ui.UiApplication;
+
+public class AudioCaptureOperation extends CaptureOperation {
+
+ // content type
+ public static final String CONTENT_TYPE = "audio/";
+
+ // maximum duration to capture media (milliseconds)
+ private double duration = 0;
+
+ // file system listener
+ private AudioCaptureListener listener = null;
+
+ /**
+ * Creates and starts an audio capture operation.
+ *
+ * @param limit
+ * maximum number of media files to capture
+ * @param duration
+ * maximum duration to capture media (milliseconds)
+ * @param callbackId
+ * the callback to receive the files
+ * @param queue
+ * the queue from which to retrieve captured media files
+ */
+ public AudioCaptureOperation(long limit, double duration, String callbackId, MediaQueue queue) {
+ super(limit, callbackId, queue);
+
+ if (duration > 0) {
+ this.duration = duration;
+ }
+
+ // listener to capture image files added to file system
+ this.listener = new AudioCaptureListener(queue);
+
+ start();
+ }
+
+ /**
+ * Registers file system listener and launches native voice notes recorder
+ * application.
+ */
+ protected void setup() {
+ // register listener for files being written
+ synchronized(UiApplication.getEventLock()) {
+ UiApplication.getUiApplication().addFileSystemJournalListener(listener);
+ }
+
+ // launch the native voice notes recorder application
+ AudioControl.launchAudioRecorder();
+ }
+
+ /**
+ * Unregisters file system listener and closes native voice notes recorder
+ * application.
+ */
+ protected void teardown() {
+ // remove file system listener
+ synchronized(UiApplication.getEventLock()) {
+ UiApplication.getUiApplication().removeFileSystemJournalListener(listener);
+ }
+
+ // close the native voice notes recorder application
+ AudioControl.closeAudioRecorder();
+ }
+
+ /**
+ * Retrieves the file properties for the captured audio recording.
+ *
+ * @param filePath
+ * full path of the audio recording file
+ */
+ protected void processFile(String filePath) {
+ Logger.log(this.getClass().getName() + ": processing file: " + filePath);
+
+ // wait for file to finish writing and add it to captured files
+ addCaptureFile(getMediaFile(filePath));
+ }
+
+ /**
+ * Waits for file to be fully written to the file system before retrieving
+ * its file properties.
+ *
+ * @param filePath
+ * Full path of the image file
+ * @throws IOException
+ */
+ private File getMediaFile(String filePath) {
+ File file = new File(FileUtils.stripSeparator(filePath));
+
+ // time begin waiting for file write
+ long start = (new Date()).getTime();
+
+ // wait for the file to be fully written, then grab its properties
+ FileConnection fconn = null;
+ try {
+ fconn = (FileConnection) Connector.open(filePath, Connector.READ);
+ if (fconn.exists()) {
+ // wait for file to be fully written
+ long fileSize = fconn.fileSize();
+ long size = 0;
+ Thread thisThread = Thread.currentThread();
+ while (myThread == thisThread) {
+ try {
+ Thread.sleep(100);
+ }
+ catch (InterruptedException e) {
+ break;
+ }
+ size = fconn.fileSize();
+ if (fileSize != 0 && size == fileSize) {
+ break;
+ }
+ fileSize = size;
+ }
+ Logger.log(this.getClass().getName() + ": " + filePath + " size="
+ + Long.toString(fileSize) + " bytes");
+
+ // retrieve file properties
+ file.setLastModifiedDate(fconn.lastModified());
+ file.setName(FileUtils.stripSeparator(fconn.getName()));
+ file.setSize(fileSize);
+ file.setType(MIMETypeAssociations.getMIMEType(filePath));
+ }
+ }
+ catch (IOException e) {
+ Logger.log(this.getClass().getName() + ": " + e);
+ }
+ finally {
+ try {
+ if (fconn != null) fconn.close();
+ } catch (IOException ignored) {}
+ }
+
+ // log time it took to write the file
+ long end = (new Date()).getTime();
+ Logger.log(this.getClass().getName() + ": wait time="
+ + Long.toString(end - start) + " ms");
+
+ return file;
+ }
+}
http://git-wip-us.apache.org/repos/asf/cordova-blackberry/blob/041e4481/bbos/framework/ext/src/org/apache/cordova/capture/AudioControl.java
----------------------------------------------------------------------
diff --git a/bbos/framework/ext/src/org/apache/cordova/capture/AudioControl.java b/bbos/framework/ext/src/org/apache/cordova/capture/AudioControl.java
new file mode 100644
index 0000000..45e9f9c
--- /dev/null
+++ b/bbos/framework/ext/src/org/apache/cordova/capture/AudioControl.java
@@ -0,0 +1,75 @@
+/*
+ * 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.capture;
+
+import org.apache.cordova.util.ApplicationUtils;
+import org.apache.cordova.util.Logger;
+
+import net.rim.device.api.system.ApplicationDescriptor;
+import net.rim.device.api.system.ApplicationManager;
+import net.rim.device.api.system.ApplicationManagerException;
+import net.rim.device.api.system.CodeModuleManager;
+
+public class AudioControl {
+ /**
+ * Determines if the native voice notes recorder application is installed
+ * on the device.
+ *
+ * @return true if native voice notes recorder application is installed
+ */
+ public static boolean hasAudioRecorderApplication() {
+ return ApplicationUtils.isModuleInstalled("net_rim_bb_voicenotesrecorder");
+ }
+
+ /**
+ * Determines if the native voice notes recorder application is running in
+ * the foreground.
+ *
+ * @return true if native voice notes recorder application is running in
+ * foreground
+ */
+ public static boolean isAudioRecorderActive() {
+ return ApplicationUtils.isApplicationInForeground("net_rim_bb_voicenotesrecorder");
+ }
+
+ /**
+ * Launches the native audio recorder application.
+ */
+ public static void launchAudioRecorder() {
+ int handle = CodeModuleManager.getModuleHandle("net_rim_bb_voicenotesrecorder");
+ ApplicationDescriptor ad = CodeModuleManager.getApplicationDescriptors(handle)[0];
+ ApplicationDescriptor ad2 = new ApplicationDescriptor(ad, null);
+ try {
+ ApplicationManager.getApplicationManager().runApplication(ad2, true);
+ }
+ catch (ApplicationManagerException e) {
+ Logger.log(AudioControl.class.getName() + ": unable to launch net_rim_bb_voicenotesrecorder");
+ }
+ }
+
+ /**
+ * Closes the native audio recorder application.
+ */
+ public static void closeAudioRecorder() {
+ if (!isAudioRecorderActive()) {
+ return;
+ }
+ ApplicationUtils.injectEscKeyPress(1);
+ }
+}
http://git-wip-us.apache.org/repos/asf/cordova-blackberry/blob/041e4481/bbos/framework/ext/src/org/apache/cordova/capture/CameraControl.java
----------------------------------------------------------------------
diff --git a/bbos/framework/ext/src/org/apache/cordova/capture/CameraControl.java b/bbos/framework/ext/src/org/apache/cordova/capture/CameraControl.java
new file mode 100644
index 0000000..2ed9206
--- /dev/null
+++ b/bbos/framework/ext/src/org/apache/cordova/capture/CameraControl.java
@@ -0,0 +1,87 @@
+/*
+ * 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.capture;
+
+import org.apache.cordova.util.ApplicationUtils;
+
+import net.rim.blackberry.api.invoke.CameraArguments;
+import net.rim.blackberry.api.invoke.Invoke;
+import net.rim.device.api.ui.UiApplication;
+
+public class CameraControl {
+ /**
+ * Determines if the native camera application is running in the foreground.
+ *
+ * @return true if native camera application is running in foreground
+ */
+ public static boolean isCameraActive() {
+ return ApplicationUtils.isApplicationInForeground("net_rim_bb_camera");
+ }
+
+ /**
+ * Determines if the native video recorder application is running in the
+ * foreground.
+ *
+ * @return true if native video recorder application is running in
+ * foreground
+ */
+ public static boolean isVideoRecorderActive() {
+ return ApplicationUtils.isApplicationInForeground("net_rim_bb_videorecorder");
+ }
+
+ /**
+ * Launches the native camera application.
+ */
+ public static void launchCamera() {
+ synchronized(UiApplication.getEventLock()) {
+ Invoke.invokeApplication(Invoke.APP_TYPE_CAMERA,
+ new CameraArguments());
+ }
+ }
+
+ /**
+ * Launches the native video recorder application.
+ */
+ public static void launchVideoRecorder() {
+ synchronized(UiApplication.getEventLock()) {
+ Invoke.invokeApplication(Invoke.APP_TYPE_CAMERA,
+ new CameraArguments(CameraArguments.ARG_VIDEO_RECORDER));
+ }
+ }
+
+ /**
+ * Closes the native camera application.
+ */
+ public static void closeCamera() {
+ if (!isCameraActive()) {
+ return;
+ }
+ ApplicationUtils.injectEscKeyPress(2);
+ }
+
+ /**
+ * Closes the native video recorder application.
+ */
+ public static void closeVideoRecorder() {
+ if (!isVideoRecorderActive()) {
+ return;
+ }
+ ApplicationUtils.injectEscKeyPress(2);
+ }
+}
http://git-wip-us.apache.org/repos/asf/cordova-blackberry/blob/041e4481/bbos/framework/ext/src/org/apache/cordova/capture/CaptureControl.java
----------------------------------------------------------------------
diff --git a/bbos/framework/ext/src/org/apache/cordova/capture/CaptureControl.java b/bbos/framework/ext/src/org/apache/cordova/capture/CaptureControl.java
new file mode 100644
index 0000000..e37dd56
--- /dev/null
+++ b/bbos/framework/ext/src/org/apache/cordova/capture/CaptureControl.java
@@ -0,0 +1,169 @@
+/*
+ * 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.capture;
+
+import java.util.Enumeration;
+import java.util.Vector;
+
+public class CaptureControl {
+
+ /**
+ * Pending capture operations.
+ */
+ private Vector pendingOperations = new Vector();
+
+ /**
+ * Singleton.
+ */
+ private CaptureControl() {}
+
+ /**
+ * Holds the singleton for lazy instantiation.
+ */
+ private static class CaptureControlHolder {
+ static final CaptureControl INSTANCE = new CaptureControl();
+ }
+
+ /**
+ * Retrieves a CaptureControl instance.
+ * @return CaptureControl instance.
+ */
+ public static final CaptureControl getCaptureControl() {
+ return CaptureControlHolder.INSTANCE;
+ }
+
+ /**
+ * Add capture operation so we can stop it manually.
+ */
+ public void addCaptureOperation(CaptureOperation operation) {
+ if (operation == null) {
+ return;
+ }
+
+ synchronized (pendingOperations) {
+ pendingOperations.addElement(operation);
+ }
+ }
+
+ /**
+ * Remove capture operation.
+ */
+ public void removeCaptureOperation(CaptureOperation operation) {
+ if (operation == null) {
+ return;
+ }
+
+ synchronized (pendingOperations) {
+ pendingOperations.removeElement(operation);
+ }
+ }
+
+ /**
+ * Starts an image capture operation, during which a user can take multiple
+ * photos. The capture operation runs in the background.
+ *
+ * @param limit
+ * the maximum number of images to capture during the operation
+ * @param callbackId
+ * the callback to be invoked with capture file properties
+ */
+ public void startImageCaptureOperation(long limit, String callbackId) {
+ // setup a queue to receive image file paths
+ MediaQueue queue = new MediaQueue();
+
+ // start a capture operation on a background thread
+ CaptureOperation operation = new ImageCaptureOperation(limit,
+ callbackId, queue);
+
+ // track the operation so we can stop or cancel it later
+ addCaptureOperation(operation);
+ }
+
+ /**
+ * Starts a video capture operation, during which a user can record multiple
+ * recordings. The capture operation runs in the background.
+ *
+ * @param limit
+ * the maximum number of images to capture during the operation
+ * @param callbackId
+ * the callback to be invoked with capture file properties
+ */
+ public void startVideoCaptureOperation(long limit, String callbackId) {
+ // setup a queue to receive video recording file paths
+ MediaQueue queue = new MediaQueue();
+
+ // start a capture operation on a background thread
+ CaptureOperation operation = new VideoCaptureOperation(limit,
+ callbackId, queue);
+
+ // track the operation so we can stop or cancel it later
+ addCaptureOperation(operation);
+ }
+
+ /**
+ * Starts an audio capture operation using the native voice notes recorder
+ * application.
+ *
+ * @param limit
+ * the maximum number of audio clips to capture during the
+ * operation
+ * @param duration
+ * the maximum duration of each captured clip
+ * @param callbackId
+ * the callback to be invoked with the capture results
+ */
+ public void startAudioCaptureOperation(long limit, double duration, String callbackId) {
+ // setup a queue to receive recording file paths
+ MediaQueue queue = new MediaQueue();
+
+ // start a capture operation on a background thread
+ CaptureOperation operation = new AudioCaptureOperation(limit, duration,
+ callbackId, queue);
+
+ // track the operation so we can stop or cancel it later
+ addCaptureOperation(operation);
+ }
+
+ /**
+ * Stops all pending capture operations. If the <code>cancel</code>
+ * parameter is <code>true</code>, no results will be sent via the callback
+ * mechanism and any captured files will be removed from the file system.
+ *
+ * @param cancel
+ * true if operations should be canceled
+ */
+ public void stopPendingOperations(boolean cancel) {
+ // There are two scenarios where the capture operation would be stopped
+ // manually:
+ // 1- The user stops the capture application, and this application
+ // returns to the foreground.
+ // 2- It is canceled programmatically. No results should be sent.
+ synchronized (pendingOperations) {
+ for (Enumeration e = pendingOperations.elements(); e.hasMoreElements(); ) {
+ CaptureOperation operation = (CaptureOperation) e.nextElement();
+ if (cancel) {
+ operation.cancel();
+ }
+ else {
+ operation.stop();
+ }
+ }
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/cordova-blackberry/blob/041e4481/bbos/framework/ext/src/org/apache/cordova/capture/CaptureMode.java
----------------------------------------------------------------------
diff --git a/bbos/framework/ext/src/org/apache/cordova/capture/CaptureMode.java b/bbos/framework/ext/src/org/apache/cordova/capture/CaptureMode.java
new file mode 100644
index 0000000..7c71f96
--- /dev/null
+++ b/bbos/framework/ext/src/org/apache/cordova/capture/CaptureMode.java
@@ -0,0 +1,87 @@
+/*
+ * 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.capture;
+
+import org.apache.cordova.json4j.JSONException;
+import org.apache.cordova.json4j.JSONObject;
+
+public class CaptureMode {
+
+ private String mimeType = null;
+ private long height = 0;
+ private long width = 0;
+
+ public CaptureMode() {
+ }
+
+ public CaptureMode(String type) {
+ this.mimeType = type;
+ }
+
+ public CaptureMode(String type, long width, long height) {
+ this.mimeType = type;
+ this.height = height;
+ this.width = width;
+ }
+
+ public String getMimeType() {
+ return mimeType;
+ }
+
+ public long getHeight() {
+ return height;
+ }
+
+ public long getWidth() {
+ return width;
+ }
+
+ public JSONObject toJSONObject() {
+ JSONObject o = new JSONObject();
+ try {
+ o.put("type", getMimeType());
+ o.put("height", getHeight());
+ o.put("width", getWidth());
+ }
+ catch (JSONException ignored) {
+ }
+ return o;
+ }
+
+ public boolean equals(Object o) {
+ if (o == this) {
+ return true;
+ }
+ if (!(o instanceof CaptureMode)) {
+ return false;
+ }
+ CaptureMode cm = (CaptureMode)o;
+ return ((mimeType == null ? cm.mimeType == null :
+ mimeType.equals(cm.mimeType))
+ && (width == cm.width)
+ && (height == cm.height));
+ }
+
+ public int hashCode() {
+ int hash = (mimeType != null ? mimeType.hashCode() : 19);
+ hash = 37*hash + (int)width;
+ hash = 37*hash + (int)height;
+ return hash;
+ }
+}
http://git-wip-us.apache.org/repos/asf/cordova-blackberry/blob/041e4481/bbos/framework/ext/src/org/apache/cordova/capture/CaptureOperation.java
----------------------------------------------------------------------
diff --git a/bbos/framework/ext/src/org/apache/cordova/capture/CaptureOperation.java b/bbos/framework/ext/src/org/apache/cordova/capture/CaptureOperation.java
new file mode 100644
index 0000000..dc85bd8
--- /dev/null
+++ b/bbos/framework/ext/src/org/apache/cordova/capture/CaptureOperation.java
@@ -0,0 +1,202 @@
+/*
+ * 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.capture;
+
+import java.io.IOException;
+import java.util.Enumeration;
+import java.util.Vector;
+
+import org.apache.cordova.file.File;
+import org.apache.cordova.util.FileUtils;
+import org.apache.cordova.util.Logger;
+
+public abstract class CaptureOperation implements Runnable {
+ // max number of media files to capture
+ protected long limit = 1;
+
+ // for sending results
+ protected String callbackId = null;
+
+ // list of captured media files
+ protected final Vector captureFiles = new Vector();
+
+ // media file queue
+ protected MediaQueue mediaQueue = null;
+
+ // used to interrupt thread
+ protected volatile Thread myThread;
+
+ // to determine if operation has been canceled
+ protected boolean canceled = false;
+
+ /**
+ * Creates and starts a capture operation on a new thread.
+ *
+ * @param limit
+ * maximum number of media files to capture
+ * @param callbackId
+ * the callback to receive the files
+ * @param queue
+ * the queue from which to retrieve captured media files
+ */
+ public CaptureOperation(long limit, String callbackId, MediaQueue queue) {
+ if (limit > 1) {
+ this.limit = limit;
+ }
+
+ this.callbackId = callbackId;
+ this.mediaQueue = queue;
+ this.myThread = new Thread(this);
+ }
+
+ /**
+ * Waits for media file to be captured.
+ */
+ public void run() {
+ if (myThread == null) {
+ return; // stopped before started
+ }
+
+ Logger.log(this.getClass().getName() + ": " + callbackId + " started");
+
+ // tasks to be run before entering main loop
+ setup();
+
+ // capture until interrupted or we've reached capture limit
+ Thread thisThread = Thread.currentThread();
+ String filePath = null;
+ while (myThread == thisThread && captureFiles.size() < limit) {
+ try {
+ // consume file added to media capture queue
+ filePath = mediaQueue.remove();
+ }
+ catch (InterruptedException e) {
+ Logger.log(this.getClass().getName() + ": " + callbackId + " interrupted");
+ // and we're done
+ break;
+ }
+ processFile(filePath);
+ }
+
+ // perform cleanup tasks
+ teardown();
+
+ // process captured results
+ processResults();
+
+ // unregister the operation from the controller
+ CaptureControl.getCaptureControl().removeCaptureOperation(this);
+
+ Logger.log(this.getClass().getName() + ": " + callbackId + " finished");
+ }
+
+ /**
+ * Starts this capture operation on a new thread.
+ */
+ protected void start() {
+ if (myThread == null) {
+ return; // stopped before started
+ }
+ myThread.start();
+ }
+
+ /**
+ * Stops the operation.
+ */
+ public void stop() {
+ // interrupt capture thread
+ Thread tmpThread = myThread;
+ myThread = null;
+ if (tmpThread != null && tmpThread.isAlive()) {
+ tmpThread.interrupt();
+ }
+ }
+
+ /**
+ * Cancels the operation.
+ */
+ public void cancel() {
+ canceled = true;
+ stop();
+ }
+
+ /**
+ * Processes the results of the capture operation.
+ */
+ protected void processResults() {
+ // process results
+ if (!canceled) {
+ // invoke appropriate callback
+ if (captureFiles.size() > 0) {
+ // send capture files
+ MediaCapture.captureSuccess(captureFiles, callbackId);
+ }
+ else {
+ // error
+ MediaCapture.captureError(callbackId);
+ }
+ }
+ else {
+ removeCaptureFiles();
+ }
+ }
+
+ /**
+ * Adds a media file to list of collected media files for this operation.
+ *
+ * @param file
+ * object containing media file properties
+ */
+ protected void addCaptureFile(File file) {
+ captureFiles.addElement(file);
+ }
+
+ /**
+ * Removes captured files from the file system.
+ */
+ protected void removeCaptureFiles() {
+ for (Enumeration e = captureFiles.elements(); e.hasMoreElements();) {
+ File file = (File) e.nextElement();
+ try {
+ FileUtils.delete(file.getFullPath());
+ }
+ catch (IOException ignored) {
+ }
+ }
+ }
+
+ /**
+ * Override this method to perform tasks before the operation starts.
+ */
+ protected void setup() {
+ }
+
+ /**
+ * Override this method to perform tasks after the operation has
+ * stopped.
+ */
+ protected void teardown() {
+ }
+
+ /**
+ * Subclasses must implement this method to process a captured media file.
+ * @param filePath the full path of the media file
+ */
+ protected abstract void processFile(final String filePath);
+}
http://git-wip-us.apache.org/repos/asf/cordova-blackberry/blob/041e4481/bbos/framework/ext/src/org/apache/cordova/capture/ImageCaptureListener.java
----------------------------------------------------------------------
diff --git a/bbos/framework/ext/src/org/apache/cordova/capture/ImageCaptureListener.java b/bbos/framework/ext/src/org/apache/cordova/capture/ImageCaptureListener.java
new file mode 100644
index 0000000..4906ee8
--- /dev/null
+++ b/bbos/framework/ext/src/org/apache/cordova/capture/ImageCaptureListener.java
@@ -0,0 +1,84 @@
+/*
+ * 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.capture;
+
+import net.rim.device.api.io.file.FileSystemJournal;
+import net.rim.device.api.io.file.FileSystemJournalEntry;
+import net.rim.device.api.io.file.FileSystemJournalListener;
+
+/**
+ * Listens for image files that are added to file system.
+ * <p>
+ * The file system notifications will arrive on the application event thread.
+ * When it receives a notification, it adds the image file path to a MediaQueue
+ * so that the capture thread can process the file.
+ */
+class ImageCaptureListener implements FileSystemJournalListener {
+
+ /**
+ * Used to track file system changes.
+ */
+ private long lastUSN = 0;
+
+ /**
+ * Collection of media files.
+ */
+ private MediaQueue queue = null;
+
+ /**
+ * Constructor.
+ */
+ ImageCaptureListener(MediaQueue queue) {
+ this.queue = queue;
+ }
+
+ /**
+ * Listens for file system changes. When a JPEG file is added, we process
+ * it and send it back.
+ */
+ public void fileJournalChanged()
+ {
+ // next sequence number file system will use
+ long USN = FileSystemJournal.getNextUSN();
+
+ for (long i = USN - 1; i >= lastUSN && i < USN; --i)
+ {
+ FileSystemJournalEntry entry = FileSystemJournal.getEntry(i);
+ if (entry == null)
+ {
+ break;
+ }
+
+ if (entry.getEvent() == FileSystemJournalEntry.FILE_ADDED)
+ {
+ String path = entry.getPath();
+ if (path != null && path.indexOf(".jpg") != -1)
+ {
+ // add file path to the capture queue
+ queue.add("file://" + path);
+ break;
+ }
+ }
+ }
+
+ // remember the file journal change number,
+ // so we don't search the same events again and again
+ lastUSN = USN;
+ }
+}
http://git-wip-us.apache.org/repos/asf/cordova-blackberry/blob/041e4481/bbos/framework/ext/src/org/apache/cordova/capture/ImageCaptureOperation.java
----------------------------------------------------------------------
diff --git a/bbos/framework/ext/src/org/apache/cordova/capture/ImageCaptureOperation.java b/bbos/framework/ext/src/org/apache/cordova/capture/ImageCaptureOperation.java
new file mode 100644
index 0000000..a831dc2
--- /dev/null
+++ b/bbos/framework/ext/src/org/apache/cordova/capture/ImageCaptureOperation.java
@@ -0,0 +1,161 @@
+/*
+ * 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.capture;
+
+import java.io.IOException;
+import java.util.Date;
+import javax.microedition.io.Connector;
+import javax.microedition.io.file.FileConnection;
+
+import org.apache.cordova.file.File;
+import org.apache.cordova.util.FileUtils;
+import org.apache.cordova.util.Logger;
+
+import net.rim.device.api.io.MIMETypeAssociations;
+import net.rim.device.api.ui.UiApplication;
+
+public class ImageCaptureOperation extends CaptureOperation {
+ // content type
+ public static String CONTENT_TYPE = "image/";
+
+ // file system listener
+ private ImageCaptureListener listener = null;
+
+ /**
+ * Creates and starts an image capture operation.
+ *
+ * @param limit
+ * maximum number of media files to capture
+ * @param callbackId
+ * the callback to receive the files
+ * @param queue
+ * the queue from which to retrieve captured media files
+ */
+ public ImageCaptureOperation(long limit, String callbackId, MediaQueue queue) {
+ super(limit, callbackId, queue);
+
+ // listener to capture image files added to file system
+ this.listener = new ImageCaptureListener(queue);
+
+ start();
+ }
+
+ /**
+ * Registers file system listener and launches native camera application.
+ */
+ protected void setup() {
+ // register listener for files being written
+ synchronized(UiApplication.getEventLock()) {
+ UiApplication.getUiApplication().addFileSystemJournalListener(listener);
+ }
+
+ // launch the native camera application
+ CameraControl.launchCamera();
+ }
+
+ /**
+ * Unregisters file system listener and closes native camera application.
+ */
+ protected void teardown() {
+ // remove file system listener
+ synchronized(UiApplication.getEventLock()) {
+ UiApplication.getUiApplication().removeFileSystemJournalListener(listener);
+ }
+
+ // close the native camera application
+ CameraControl.closeCamera();
+ }
+
+ /**
+ * Waits for image file to be written to file system and retrieves its file
+ * properties.
+ *
+ * @param filePath
+ * the full path of the media file
+ */
+ protected void processFile(final String filePath) {
+ Logger.log(this.getClass().getName() + ": processing file: " + filePath);
+
+ // wait for file to finish writing and add it to captured files
+ addCaptureFile(getMediaFile(filePath));
+ }
+
+ /**
+ * Waits for file to be fully written to the file system before retrieving
+ * its file properties.
+ *
+ * @param filePath
+ * Full path of the image file
+ * @throws IOException
+ */
+ private File getMediaFile(String filePath) {
+ File file = new File(FileUtils.stripSeparator(filePath));
+
+ // time begin waiting for file write
+ long start = (new Date()).getTime();
+
+ // wait for the file to be fully written, then grab its properties
+ FileConnection fconn = null;
+ try {
+ fconn = (FileConnection) Connector.open(filePath, Connector.READ);
+ if (fconn.exists()) {
+ // wait for file to be fully written
+ long fileSize = fconn.fileSize();
+ long size = 0;
+ Thread thisThread = Thread.currentThread();
+ while (myThread == thisThread) {
+ try {
+ Thread.sleep(100);
+ }
+ catch (InterruptedException e) {
+ break;
+ }
+ size = fconn.fileSize();
+ if (size == fileSize) {
+ break;
+ }
+ fileSize = size;
+ }
+ Logger.log(this.getClass().getName() + ": " + filePath + " size="
+ + Long.toString(fileSize) + " bytes");
+
+ // retrieve file properties
+ file.setLastModifiedDate(fconn.lastModified());
+ file.setName(FileUtils.stripSeparator(fconn.getName()));
+ file.setSize(fileSize);
+ file.setType(MIMETypeAssociations.getMIMEType(filePath));
+ }
+ }
+ catch (IOException e) {
+ Logger.log(this.getClass().getName() + ": " + e);
+ }
+ finally {
+ try {
+ if (fconn != null) fconn.close();
+ } catch (IOException ignored) {}
+ }
+
+ // log time it took to write the file
+ long end = (new Date()).getTime();
+ Logger.log(this.getClass().getName() + ": wait time="
+ + Long.toString(end - start) + " ms");
+
+ return file;
+ }
+}
http://git-wip-us.apache.org/repos/asf/cordova-blackberry/blob/041e4481/bbos/framework/ext/src/org/apache/cordova/capture/MediaCapture.java
----------------------------------------------------------------------
diff --git a/bbos/framework/ext/src/org/apache/cordova/capture/MediaCapture.java b/bbos/framework/ext/src/org/apache/cordova/capture/MediaCapture.java
new file mode 100644
index 0000000..76b0eac
--- /dev/null
+++ b/bbos/framework/ext/src/org/apache/cordova/capture/MediaCapture.java
@@ -0,0 +1,502 @@
+/*
+ * 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.capture;
+
+import java.util.Enumeration;
+import java.util.Hashtable;
+import java.util.Vector;
+
+import javax.microedition.media.Manager;
+
+import org.apache.cordova.api.Plugin;
+import org.apache.cordova.api.PluginResult;
+import org.apache.cordova.file.File;
+import org.apache.cordova.json4j.JSONArray;
+import org.apache.cordova.json4j.JSONException;
+import org.apache.cordova.json4j.JSONObject;
+import org.apache.cordova.util.Logger;
+import org.apache.cordova.util.StringUtils;
+
+/**
+ * This plugin provides the ability to capture media from the native media
+ * applications. The appropriate media application is launched, and a capture
+ * operation is started in the background to identify captured media files and
+ * return the file info back to the caller.
+ */
+public class MediaCapture extends Plugin {
+
+ public static String PROTOCOL_CAPTURE = "capture";
+
+ private static final String LOG_TAG = "MediaCapture: ";
+
+ /**
+ * Error codes.
+ */
+ // Camera or microphone failed to capture image or sound.
+ private static final int CAPTURE_INTERNAL_ERR = 0;
+ // Camera application or audio capture application is currently serving other capture request.
+ private static final int CAPTURE_APPLICATION_BUSY = 1;
+ // Invalid use of the API (e.g. limit parameter has value less than one).
+ private static final int CAPTURE_INVALID_ARGUMENT = 2;
+ // User exited camera application or audio capture application before capturing anything.
+ private static final int CAPTURE_NO_MEDIA_FILES = 3;
+ // The requested capture operation is not supported.
+ private static final int CAPTURE_NOT_SUPPORTED = 20;
+
+ /**
+ * Possible actions.
+ */
+ protected static final String ACTION_GET_SUPPORTED_MODES = "captureModes";
+ protected static final String ACTION_CAPTURE_AUDIO = "captureAudio";
+ protected static final String ACTION_CAPTURE_IMAGE = "captureImage";
+ protected static final String ACTION_CAPTURE_VIDEO = "captureVideo";
+ protected static final String ACTION_CANCEL_CAPTURES = "stopCaptures";
+
+ /**
+ * Executes the requested action and returns a PluginResult.
+ *
+ * @param action
+ * The action to execute.
+ * @param callbackId
+ * The callback ID to be invoked upon action completion
+ * @param args
+ * JSONArry of arguments for the action.
+ * @return A PluginResult object with a status and message.
+ */
+ public PluginResult execute(String action, JSONArray args, String callbackId) {
+ PluginResult result = null;
+
+ if (ACTION_GET_SUPPORTED_MODES.equals(action)) {
+ result = getCaptureModes();
+ } else if (ACTION_CAPTURE_AUDIO.equals(action)) {
+ result = captureAudio(args, callbackId);
+ } else if (ACTION_CAPTURE_IMAGE.equals(action)) {
+ result = captureImage(args, callbackId);
+ } else if (ACTION_CAPTURE_VIDEO.equals(action)) {
+ result = captureVideo(args, callbackId);
+ } else if (ACTION_CANCEL_CAPTURES.equals(action)) {
+ CaptureControl.getCaptureControl().stopPendingOperations(true);
+ result = new PluginResult(PluginResult.Status.OK);
+ } else {
+ result = new PluginResult(PluginResult.Status.INVALID_ACTION,
+ "MediaCapture: invalid action " + action);
+ }
+
+ return result;
+ }
+
+ /**
+ * Determines if audio capture is supported.
+ * @return <code>true</code> if audio capture is supported
+ */
+ protected boolean isAudioCaptureSupported() {
+ return (System.getProperty("supports.audio.capture").equals(Boolean.TRUE.toString())
+ && AudioControl.hasAudioRecorderApplication());
+ }
+
+ /**
+ * Determines if video capture is supported.
+ * @return <code>true</code> if video capture is supported
+ */
+ protected boolean isVideoCaptureSupported() {
+ return (System.getProperty("supports.video.capture").equals(Boolean.TRUE.toString()));
+ }
+
+ /**
+ * Return the supported capture modes for audio, image and video.
+ * @return supported capture modes.
+ */
+ private PluginResult getCaptureModes() {
+ JSONArray audioModes = new JSONArray();
+ JSONArray imageModes = new JSONArray();
+ boolean audioSupported = isAudioCaptureSupported();
+
+ // need to get the recording dimensions from supported image encodings
+ String imageEncodings = System.getProperty("video.snapshot.encodings");
+ Logger.log(this.getClass().getName() + ": video.snapshot.encodings="
+ + imageEncodings);
+ String[] encodings = StringUtils.split(imageEncodings, "encoding=");
+ CaptureMode mode = null;
+ Vector list = new Vector();
+
+ // get all supported capture content types for audio and image
+ String[] contentTypes = getCaptureContentTypes();
+ for (int i = 0; i < contentTypes.length; i++) {
+ if (audioSupported
+ && contentTypes[i]
+ .startsWith(AudioCaptureOperation.CONTENT_TYPE)) {
+ audioModes.add(new CaptureMode(contentTypes[i]).toJSONObject());
+ } else if (contentTypes[i]
+ .startsWith(ImageCaptureOperation.CONTENT_TYPE)) {
+ String type = contentTypes[i]
+ .substring(ImageCaptureOperation.CONTENT_TYPE.length());
+ for (int j = 0; j < encodings.length; j++) {
+ // format: "jpeg&width=2592&height=1944 "
+ String enc = encodings[j];
+ if (enc.startsWith(type)) {
+ Hashtable parms = parseEncodingString(enc);
+ // "width="
+ String w = (String)parms.get("width");
+ long width = (w == null) ? 0 : Long.parseLong(w);
+ // "height="
+ String h = (String)parms.get("height");
+ long height = (h == null) ? 0 : Long.parseLong(h);
+ // new capture mode
+ mode = new CaptureMode(contentTypes[i], width, height);
+ // don't want duplicates
+ if (!list.contains(mode)) {
+ list.addElement(mode);
+ imageModes.add(mode.toJSONObject());
+ }
+ }
+ }
+ }
+ }
+
+ JSONObject captureModes = new JSONObject();
+ try {
+ captureModes.put("supportedAudioModes", audioModes.toString());
+ captureModes.put("supportedImageModes", imageModes.toString());
+ captureModes.put("supportedVideoModes", getVideoCaptureModes().toString());
+ } catch (JSONException e) {
+ Logger.error("JSONException: " + e.getMessage());
+ return new PluginResult(PluginResult.Status.JSON_EXCEPTION,
+ "Failed to build supported capture modes.");
+ }
+
+ return new PluginResult(PluginResult.Status.OK, captureModes);
+ }
+
+ /**
+ * Retrieves supported video capture modes (content type, width and height).
+ * @return supported video capture modes
+ */
+ protected JSONArray getVideoCaptureModes() {
+ JSONArray videoModes = new JSONArray();
+
+ if (!isVideoCaptureSupported()) {
+ // if the device does not support video capture, return an empty
+ // array of capture modes
+ Logger.log(this.getClass().getName() + ": video capture not supported");
+ return videoModes;
+ }
+
+ /**
+ * DOH! Even if video capture is supported, BlackBerry's API
+ * does not provide any 'video/' content types for the 'capture'
+ * protocol. So if we looked at only capture content types,
+ * it wouldn't return any results...
+ *
+ * // get all supported capture content types
+ * String[] contentTypes = getCaptureContentTypes();
+ *
+ * A better alternative, and probably not too inaccurate, would be to
+ * send back all supported video modes (not just capture). This will
+ * at least give the developer an idea of the capabilities.
+ */
+
+ // retrieve ALL supported video encodings
+ String videoEncodings = System.getProperty("video.encodings");
+ Logger.log(this.getClass().getName() + ": video.encodings=" + videoEncodings);
+ String[] encodings = StringUtils.split(videoEncodings, "encoding=");
+
+ // parse them into CaptureModes
+ String enc = null;
+ CaptureMode mode = null;
+ Vector list = new Vector();
+ for (int i = 0; i < encodings.length; i++) {
+ enc = encodings[i];
+ // format: "video/3gpp&width=640&height=480&video_codec=MPEG-4&audio_codec=AAC "
+ if (enc.startsWith(VideoCaptureOperation.CONTENT_TYPE)) {
+ Hashtable parms = parseEncodingString(enc);
+ // type "video/3gpp"
+ String t = (String)parms.get("type");
+ // "width="
+ String w = (String)parms.get("width");
+ long width = (w == null) ? 0 : Long.parseLong(w);
+ // "height="
+ String h = (String)parms.get("height");
+ long height = (h == null) ? 0 : Long.parseLong(h);
+ // new capture mode
+ mode = new CaptureMode(t, width, height);
+ // don't want duplicates
+ if (!list.contains(mode)) {
+ list.addElement(mode);
+ videoModes.add(mode.toJSONObject());
+ }
+ }
+ }
+
+ return videoModes;
+ }
+
+ /**
+ * Utility method to parse encoding strings.
+ *
+ * @param encodingString
+ * encoding string
+ * @return Hashtable containing key:value pairs
+ */
+ protected Hashtable parseEncodingString(final String encodingString) {
+ // format: "video/3gpp&width=640&height=480&video_codec=MPEG-4&audio_codec=AAC "
+ Hashtable props = new Hashtable();
+ String[] parms = StringUtils.split(encodingString, "&");
+ props.put("type", parms[0]);
+ for (int i = 0; i < parms.length; i++) {
+ String parameter = parms[i];
+ if (parameter.indexOf('=') != -1) {
+ String[] pair = StringUtils.split(parameter, "=");
+ props.put(pair[0].trim(), pair[1].trim());
+ }
+ }
+ return props;
+ }
+
+ /**
+ * Returns the content types supported for the <code>capture://</code>
+ * protocol.
+ *
+ * @return list of supported capture content types
+ */
+ protected static String[] getCaptureContentTypes() {
+ // retrieve list of all content types supported for capture protocol
+ return Manager.getSupportedContentTypes(PROTOCOL_CAPTURE);
+ }
+
+ /**
+ * Starts an audio capture operation using the native voice notes recorder
+ * application. If the native voice notes recorder application is already
+ * running, the <code>CAPTURE_APPLICATION_BUSY</code> error is returned.
+ *
+ * @param args
+ * capture options (e.g., limit)
+ * @param callbackId
+ * the callback to be invoked with the capture results
+ * @return PluginResult containing captured media file properties
+ */
+ protected PluginResult captureAudio(final JSONArray args, final String callbackId) {
+ PluginResult result = null;
+
+ // if audio is not being recorded, start audio capture
+ if (!AudioControl.hasAudioRecorderApplication()) {
+ result = errorResult(CAPTURE_NOT_SUPPORTED,
+ "Audio recorder application is not installed.");
+ } else if (AudioControl.isAudioRecorderActive()) {
+ result = errorResult(CAPTURE_APPLICATION_BUSY,
+ "Audio recorder application is busy.");
+ }
+ else {
+ // optional parameters
+ long limit = 1;
+ double duration = 0.0f;
+
+ try {
+ JSONObject options = args.getJSONObject(0);
+ if (options != null) {
+ limit = options.optLong("limit", 1);
+ duration = options.optDouble("duration", 0.0f);
+ }
+ } catch (JSONException e) {
+ // Eat it and use default value of 1.
+ Logger.log(this.getClass().getName()
+ + ": Invalid captureAudio options format. " + e.getMessage());
+ }
+
+ // start audio capture
+ // start capture operation in the background
+ CaptureControl.getCaptureControl().startAudioCaptureOperation(
+ limit, duration, callbackId);
+
+ // return NO_RESULT and allow callbacks to be invoked later
+ result = new PluginResult(PluginResult.Status.NO_RESULT);
+ result.setKeepCallback(true);
+ }
+
+ return result;
+ }
+
+ /**
+ * Starts an image capture operation using the native camera application. If
+ * the native camera application is already running, the
+ * <code>CAPTURE_APPLICATION_BUSY</code> error is returned.
+ *
+ * @param args
+ * capture options (e.g., limit)
+ * @param callbackId
+ * the callback to be invoked with the capture results
+ * @return PluginResult containing captured media file properties
+ */
+ protected PluginResult captureImage(final JSONArray args,
+ final String callbackId) {
+ PluginResult result = null;
+
+ if (CameraControl.isCameraActive()) {
+ result = errorResult(CAPTURE_APPLICATION_BUSY,
+ "Camera application is busy.");
+ }
+ else {
+ // optional parameters
+ long limit = 1;
+
+ try {
+ JSONObject options = args.getJSONObject(0);
+ if (options != null) {
+ limit = options.optLong("limit", 1);
+ }
+ } catch (JSONException e) {
+ // Eat it and use default value of 1.
+ Logger.log(this.getClass().getName()
+ + ": Invalid captureImage options format. " + e.getMessage());
+ }
+
+ // start capture operation in the background
+ CaptureControl.getCaptureControl().startImageCaptureOperation(
+ limit, callbackId);
+
+ // return NO_RESULT and allow callbacks to be invoked later
+ result = new PluginResult(PluginResult.Status.NO_RESULT);
+ result.setKeepCallback(true);
+ }
+
+ return result;
+ }
+
+ /**
+ * Starts an video capture operation using the native video recorder
+ * application. If the native video recorder application is already running,
+ * the <code>CAPTURE_APPLICATION_BUSY</code> error is returned.
+ *
+ * @param args
+ * capture options (e.g., limit)
+ * @param callbackId
+ * the callback to be invoked with the capture results
+ * @return PluginResult containing captured media file properties
+ */
+ protected PluginResult captureVideo(final JSONArray args,
+ final String callbackId) {
+ PluginResult result = null;
+
+ if (!isVideoCaptureSupported()) {
+ result = errorResult(CAPTURE_NOT_SUPPORTED,
+ "Video capture is not supported.");
+ } else if (CameraControl.isVideoRecorderActive()) {
+ result = errorResult(CAPTURE_APPLICATION_BUSY,
+ "Video recorder application is busy.");
+ }
+ else {
+ // optional parameters
+ long limit = 1;
+
+ try {
+ JSONObject options = args.getJSONObject(0);
+ if (options != null) {
+ limit = options.optLong("limit", 1);
+ }
+ } catch (JSONException e) {
+ // Eat it and use default value of 1.
+ Logger.log(this.getClass().getName()
+ + ": Invalid captureVideo options format. " + e.getMessage());
+ }
+
+ // start capture operation in the background
+ CaptureControl.getCaptureControl().startVideoCaptureOperation(
+ limit, callbackId);
+
+ // return NO_RESULT and allow callbacks to be invoked later
+ result = new PluginResult(PluginResult.Status.NO_RESULT);
+ result.setKeepCallback(true);
+ }
+
+ return result;
+ }
+
+ /**
+ * Sends media capture result back to JavaScript.
+ *
+ * @param mediaFiles
+ * list of File objects describing captured media files
+ * @param callbackId
+ * the callback to receive the file descriptions
+ */
+ public static void captureSuccess(Vector mediaFiles, String callbackId) {
+ PluginResult result = null;
+ File file = null;
+
+ JSONArray array = new JSONArray();
+ for (Enumeration e = mediaFiles.elements(); e.hasMoreElements();) {
+ file = (File) e.nextElement();
+ array.add(file.toJSONObject());
+ }
+
+ // invoke the appropriate callback
+ result = new PluginResult(PluginResult.Status.OK, array);
+ success(result, callbackId);
+ }
+
+ /**
+ * Sends error back to JavaScript.
+ *
+ * @param callbackId
+ * the callback to receive the error
+ */
+ public static void captureError(String callbackId) {
+ error(errorResult(CAPTURE_NO_MEDIA_FILES, ""), callbackId);
+ }
+
+ /**
+ * Called when application is resumed.
+ */
+ public void onResume() {
+ // We launch the native media applications for capture operations, which
+ // puts this application in the background. This application will come
+ // to the foreground when the user closes the native media application.
+ // So we close any running capture operations any time we resume.
+ //
+ // It would be nice if we could catch the EVT_APP_FOREGROUND event that
+ // is supposed to be triggered when the application comes to the
+ // foreground, but have not seen a way to do that on the Java side.
+ // Unfortunately, we have to get notification from the JavaScript side,
+ // which does get the event. (Argh! Only BlackBerry.)
+ //
+ // In this case, we're just stopping the capture operations, not
+ // canceling them.
+ CaptureControl.getCaptureControl().stopPendingOperations(false);
+ }
+
+ /**
+ * Invoked when this application terminates.
+ */
+ public void onDestroy() {
+ CaptureControl.getCaptureControl().stopPendingOperations(true);
+ }
+
+ private static PluginResult errorResult(int code, String message) {
+ Logger.log(LOG_TAG + message);
+
+ JSONObject obj = new JSONObject();
+ try {
+ obj.put("code", code);
+ obj.put("message", message);
+ } catch (JSONException e) {
+ // This will never happen
+ }
+
+ return new PluginResult(PluginResult.Status.ERROR, obj);
+ }
+}
http://git-wip-us.apache.org/repos/asf/cordova-blackberry/blob/041e4481/bbos/framework/ext/src/org/apache/cordova/capture/MediaQueue.java
----------------------------------------------------------------------
diff --git a/bbos/framework/ext/src/org/apache/cordova/capture/MediaQueue.java b/bbos/framework/ext/src/org/apache/cordova/capture/MediaQueue.java
new file mode 100644
index 0000000..56ffff5
--- /dev/null
+++ b/bbos/framework/ext/src/org/apache/cordova/capture/MediaQueue.java
@@ -0,0 +1,44 @@
+/*
+ * 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.capture;
+
+import java.util.Vector;
+
+/**
+ * Acts as a container for captured media files. The media applications will
+ * add to the queue when a media file is captured.
+ */
+class MediaQueue {
+ private Vector queue = new Vector();
+
+ synchronized void add(final String filePath) {
+ queue.addElement(filePath);
+ notifyAll();
+ }
+
+ synchronized String remove() throws InterruptedException {
+ while (queue.size() == 0) {
+ wait();
+ }
+ String filePath = (String) queue.firstElement();
+ queue.removeElement(filePath);
+ notifyAll();
+ return filePath;
+ }
+}
http://git-wip-us.apache.org/repos/asf/cordova-blackberry/blob/041e4481/bbos/framework/ext/src/org/apache/cordova/capture/VideoCaptureListener.java
----------------------------------------------------------------------
diff --git a/bbos/framework/ext/src/org/apache/cordova/capture/VideoCaptureListener.java b/bbos/framework/ext/src/org/apache/cordova/capture/VideoCaptureListener.java
new file mode 100644
index 0000000..361b7ee
--- /dev/null
+++ b/bbos/framework/ext/src/org/apache/cordova/capture/VideoCaptureListener.java
@@ -0,0 +1,107 @@
+/*
+ * 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.capture;
+
+import net.rim.device.api.io.file.FileSystemJournal;
+import net.rim.device.api.io.file.FileSystemJournalEntry;
+import net.rim.device.api.io.file.FileSystemJournalListener;
+
+/**
+ * Listens for video recording files that are added to file system.
+ * <p>
+ * Video recordings are added to the file system in a multi-step process. The
+ * video recorder application records the video on a background thread. While
+ * the recording is in progress, it is added to the file system with a '.lock'
+ * extension. When the user stops the recording, the file is renamed to the
+ * video recorder extension (e.g. .3GP). Therefore, we listen for the
+ * <code>FileSystemJournalEntry.FILE_RENAMED</code> event, capturing when the
+ * new path name ends in the video recording file extension.
+ * <p>
+ * The file system notifications will arrive on the application event thread.
+ * When it receives a notification, it adds the image file path to a MediaQueue
+ * so that the capture thread can process the file.
+ */
+class VideoCaptureListener implements FileSystemJournalListener {
+
+ /**
+ * Used to track file system changes.
+ */
+ private long lastUSN = 0;
+
+ /**
+ * Queue to send media files to for processing.
+ */
+ private MediaQueue queue = null;
+
+ /**
+ * Newly added video recording.
+ */
+ private String newFilePath = null;
+
+ /**
+ * Constructor.
+ */
+ VideoCaptureListener(MediaQueue queue) {
+ this.queue = queue;
+ }
+
+ public void fileJournalChanged() {
+ // next sequence number file system will use
+ long USN = FileSystemJournal.getNextUSN();
+
+ for (long i = USN - 1; i >= lastUSN && i < USN; --i)
+ {
+ FileSystemJournalEntry entry = FileSystemJournal.getEntry(i);
+ if (entry == null)
+ {
+ break;
+ }
+
+ String path = entry.getPath();
+ if (entry.getEvent() == FileSystemJournalEntry.FILE_ADDED
+ && newFilePath == null) {
+ // a new file has been added to the file system
+ // if it has a video recording extension, store it until
+ // it is renamed, indicating it has finished being written to
+ int index = path.indexOf(".3GP");
+ if (index == -1) {
+ index = path.indexOf(".MP4");
+ }
+ if (index != -1) {
+ newFilePath = path.substring(0, index + 4);
+ }
+ }
+ else if (entry.getEvent() == FileSystemJournalEntry.FILE_RENAMED) {
+ if (path != null && path.equals(newFilePath))
+ {
+ // add file path to the capture queue
+ queue.add("file://" + path);
+
+ // get ready for next file
+ newFilePath = null;
+ break;
+ }
+ }
+ }
+
+ // remember the file journal change number,
+ // so we don't search the same events again and again
+ lastUSN = USN;
+ }
+}
http://git-wip-us.apache.org/repos/asf/cordova-blackberry/blob/041e4481/bbos/framework/ext/src/org/apache/cordova/capture/VideoCaptureOperation.java
----------------------------------------------------------------------
diff --git a/bbos/framework/ext/src/org/apache/cordova/capture/VideoCaptureOperation.java b/bbos/framework/ext/src/org/apache/cordova/capture/VideoCaptureOperation.java
new file mode 100644
index 0000000..589bc68
--- /dev/null
+++ b/bbos/framework/ext/src/org/apache/cordova/capture/VideoCaptureOperation.java
@@ -0,0 +1,124 @@
+/*
+ * 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.capture;
+
+import java.io.IOException;
+
+import javax.microedition.io.Connector;
+import javax.microedition.io.file.FileConnection;
+
+import org.apache.cordova.file.File;
+import org.apache.cordova.util.FileUtils;
+import org.apache.cordova.util.Logger;
+
+import net.rim.device.api.io.MIMETypeAssociations;
+import net.rim.device.api.ui.UiApplication;
+
+public class VideoCaptureOperation extends CaptureOperation {
+
+ // content type
+ public static String CONTENT_TYPE = "video/";
+
+ // file system listener
+ private VideoCaptureListener listener = null;
+
+ /**
+ * Creates and starts an image capture operation.
+ *
+ * @param limit
+ * maximum number of media files to capture
+ * @param callbackId
+ * the callback to receive the files
+ * @param queue
+ * the queue from which to retrieve captured media files
+ */
+ public VideoCaptureOperation(long limit, String callbackId, MediaQueue queue) {
+ super(limit, callbackId, queue);
+
+ // listener to capture image files added to file system
+ this.listener = new VideoCaptureListener(queue);
+
+ start();
+ }
+
+ /**
+ * Registers file system listener and launches native video recorder
+ * application.
+ */
+ protected void setup() {
+ // register listener for files being written
+ synchronized(UiApplication.getEventLock()) {
+ UiApplication.getUiApplication().addFileSystemJournalListener(listener);
+ }
+
+ // launch the native video recorder application
+ CameraControl.launchVideoRecorder();
+ }
+
+ /**
+ * Unregisters file system listener and closes native video recorder
+ * application.
+ */
+ protected void teardown() {
+ // remove file system listener
+ synchronized(UiApplication.getEventLock()) {
+ UiApplication.getUiApplication().removeFileSystemJournalListener(listener);
+ }
+
+ // close the native video recorder application
+ CameraControl.closeVideoRecorder();
+ }
+
+ /**
+ * Retrieves the file properties for the captured video recording.
+ *
+ * @param filePath
+ * full path of the video recording file
+ */
+ protected void processFile(String filePath) {
+ Logger.log(this.getClass().getName() + ": processing file: " + filePath);
+
+ File file = new File(FileUtils.stripSeparator(filePath));
+
+ // grab file properties
+ FileConnection fconn = null;
+ try {
+ fconn = (FileConnection) Connector.open(filePath, Connector.READ);
+ if (fconn.exists()) {
+ long size = fconn.fileSize();
+ Logger.log(this.getClass().getName() + ": " + filePath + " size="
+ + Long.toString(size) + " bytes");
+ file.setLastModifiedDate(fconn.lastModified());
+ file.setName(FileUtils.stripSeparator(fconn.getName()));
+ file.setSize(size);
+ file.setType(MIMETypeAssociations.getMIMEType(filePath));
+ }
+ }
+ catch (IOException e) {
+ Logger.log(this.getClass().getName() + ": " + e);
+ }
+ finally {
+ try {
+ if (fconn != null) fconn.close();
+ } catch (IOException ignored) {}
+ }
+
+ addCaptureFile(file);
+ }
+}
http://git-wip-us.apache.org/repos/asf/cordova-blackberry/blob/041e4481/bbos/framework/ext/src/org/apache/cordova/device/Device.java
----------------------------------------------------------------------
diff --git a/bbos/framework/ext/src/org/apache/cordova/device/Device.java b/bbos/framework/ext/src/org/apache/cordova/device/Device.java
new file mode 100644
index 0000000..65a7bf5
--- /dev/null
+++ b/bbos/framework/ext/src/org/apache/cordova/device/Device.java
@@ -0,0 +1,70 @@
+/*
+ * 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.
+ *
+ * Copyright (c) 2011, Research In Motion Limited.
+ */
+package org.apache.cordova.device;
+
+import org.apache.cordova.api.Plugin;
+import org.apache.cordova.api.PluginResult;
+import org.apache.cordova.json4j.JSONArray;
+import org.apache.cordova.json4j.JSONException;
+import org.apache.cordova.json4j.JSONObject;
+
+import net.rim.device.api.system.DeviceInfo;
+
+/**
+ * Provides device information, including:
+ *
+ * - Device platform version (e.g. 2.13.0.95). Not to be confused with BlackBerry OS version.
+ * - Unique device identifier (UUID).
+ * - Cordova software version.
+ */
+public final class Device extends Plugin {
+
+ public static final String FIELD_PLATFORM = "platform";
+ public static final String FIELD_UUID = "uuid";
+ public static final String FIELD_CORDOVA = "cordova";
+ public static final String FIELD_MODEL = "model";
+ public static final String FIELD_NAME = "name";
+ public static final String FIELD_VERSION = "version";
+
+ public static final String ACTION_GET_DEVICE_INFO = "getDeviceInfo";
+
+ public PluginResult execute(String action, JSONArray args, String callbackId) {
+ PluginResult result = new PluginResult(PluginResult.Status.INVALID_ACTION, "Device: Invalid action:" + action);
+
+ if(action.equals(ACTION_GET_DEVICE_INFO)){
+ try {
+ JSONObject device = new JSONObject();
+ device.put( FIELD_PLATFORM, "BlackBerry");
+ device.put( FIELD_UUID, new Integer( DeviceInfo.getDeviceId()) );
+ device.put( FIELD_CORDOVA, "2.8.0" );
+ device.put( FIELD_MODEL, new String(DeviceInfo.getDeviceName()) );
+ device.put( FIELD_NAME, new String(DeviceInfo.getDeviceName()) );
+ device.put( FIELD_VERSION, new String(DeviceInfo.getSoftwareVersion()) );
+ result = new PluginResult(PluginResult.Status.OK, device);
+ } catch (JSONException e) {
+ result = new PluginResult(PluginResult.Status.JSON_EXCEPTION, e.getMessage());
+ }
+ }
+
+ return result;
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/cordova-blackberry/blob/041e4481/bbos/framework/ext/src/org/apache/cordova/file/Entry.java
----------------------------------------------------------------------
diff --git a/bbos/framework/ext/src/org/apache/cordova/file/Entry.java b/bbos/framework/ext/src/org/apache/cordova/file/Entry.java
new file mode 100644
index 0000000..66fb59b
--- /dev/null
+++ b/bbos/framework/ext/src/org/apache/cordova/file/Entry.java
@@ -0,0 +1,66 @@
+/*
+ * 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.file;
+
+import org.apache.cordova.json4j.JSONException;
+import org.apache.cordova.json4j.JSONObject;
+
+public class Entry {
+
+ private boolean isDirectory = false;
+ private String name = null;
+ private String fullPath = null;
+
+ public boolean isDirectory() {
+ return isDirectory;
+ }
+
+ public void setDirectory(boolean isDirectory) {
+ this.isDirectory = isDirectory;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public String getFullPath() {
+ return fullPath;
+ }
+
+ public void setFullPath(String fullPath) {
+ this.fullPath = fullPath;
+ }
+
+ public JSONObject toJSONObject() {
+ JSONObject o = new JSONObject();
+ try {
+ o.put("isDirectory", isDirectory);
+ o.put("isFile", !isDirectory);
+ o.put("name", name);
+ o.put("fullPath", fullPath);
+ }
+ catch (JSONException ignored) {
+ }
+ return o;
+ }
+}
http://git-wip-us.apache.org/repos/asf/cordova-blackberry/blob/041e4481/bbos/framework/ext/src/org/apache/cordova/file/File.java
----------------------------------------------------------------------
diff --git a/bbos/framework/ext/src/org/apache/cordova/file/File.java b/bbos/framework/ext/src/org/apache/cordova/file/File.java
new file mode 100644
index 0000000..3d04041
--- /dev/null
+++ b/bbos/framework/ext/src/org/apache/cordova/file/File.java
@@ -0,0 +1,84 @@
+/*
+ * 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.file;
+
+import org.apache.cordova.json4j.JSONException;
+import org.apache.cordova.json4j.JSONObject;
+
+public class File {
+ private String name = null;
+ private String fullPath = null;
+ private String type = null;
+ private long lastModifiedDate;
+ private long size = 0;
+
+ public File(String filePath) {
+ this.fullPath = filePath;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public String getType() {
+ return type;
+ }
+
+ public void setType(String type) {
+ this.type = type;
+ }
+
+ public long getLastModifiedDate() {
+ return lastModifiedDate;
+ }
+
+ public void setLastModifiedDate(long lastModifiedDate) {
+ this.lastModifiedDate = lastModifiedDate;
+ }
+
+ public long getSize() {
+ return size;
+ }
+
+ public void setSize(long size) {
+ this.size = size;
+ }
+
+ public String getFullPath() {
+ return fullPath;
+ }
+
+ public JSONObject toJSONObject() {
+ JSONObject o = new JSONObject();
+ try {
+ o.put("fullPath", fullPath);
+ o.put("type", type);
+ o.put("name", name);
+ o.put("lastModifiedDate", lastModifiedDate);
+ o.put("size", size);
+ }
+ catch (JSONException ignored) {
+ }
+ return o;
+ }
+}