Camera support for birch - using default Android camera app
authorFabrice Desré <fabrice@mozilla.com>
Thu, 13 Oct 2011 16:52:43 -0700
changeset 83171 5bc12a451fce7130a849b8ea55778f76307d4047
parent 83170 5b8a01f59171b7ee71e89f1aa83799abf60c9da1
child 83172 6d144c6ffde0ea88676459d6b513675fc64e9d6d
push id519
push userakeybl@mozilla.com
push dateWed, 01 Feb 2012 00:38:35 +0000
treeherdermozilla-beta@788ea1ef610b [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone10.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Camera support for birch - using default Android camera app
embedding/android/GeckoApp.java
embedding/android/GeckoAppShell.java
mobile/components/CapturePicker.js
--- a/embedding/android/GeckoApp.java
+++ b/embedding/android/GeckoApp.java
@@ -93,16 +93,17 @@ abstract public class GeckoApp
 
     enum LaunchState {Launching, WaitButton,
                       Launched, GeckoRunning, GeckoExiting};
     private static LaunchState sLaunchState = LaunchState.Launching;
     private static boolean sTryCatchAttached = false;
 
     private static final int FILE_PICKER_REQUEST = 1;
     private static final int AWESOMEBAR_REQUEST = 2;
+    private static final int CAMERA_CAPTURE_REQUEST = 3;
 
     static boolean checkLaunchState(LaunchState checkState) {
         synchronized(sLaunchState) {
             return sLaunchState == checkState;
         }
     }
 
     static void setLaunchState(LaunchState setState) {
@@ -799,16 +800,18 @@ abstract public class GeckoApp
 
     @Override
     public void onBackPressed() {
         if (!mSessionHistory.doBack()) {
             finish();
         }
     }
 
+    static int kCaptureIndex = 0;
+
     @Override
     protected void onActivityResult(int requestCode, int resultCode,
                                     Intent data) {
         super.onActivityResult(requestCode, resultCode, data);
         switch (requestCode) {
         case FILE_PICKER_REQUEST:
             String filePickerResult = "";
             if (data != null && resultCode == RESULT_OK) {
@@ -871,17 +874,35 @@ abstract public class GeckoApp
                     mProgressBar.setVisibility(View.VISIBLE);
                     mProgressBar.setIndeterminate(true);
                     loadUrl(url);
                 }
             }
 
 
             break;
+        case CAMERA_CAPTURE_REQUEST:
+            Log.i(LOG_FILE_NAME, "Returning from CAMERA_CAPTURE_REQUEST: " + resultCode);
+            File file = new File(Environment.getExternalStorageDirectory(), "cameraCapture-" + Integer.toString(kCaptureIndex) + ".jpg");
+            kCaptureIndex++;
+            GeckoEvent e = new GeckoEvent("cameraCaptureDone", resultCode == Activity.RESULT_OK ?
+                                          "{\"ok\": true,  \"path\": \"" + file.getPath() + "\" }" :
+                                          "{\"ok\": false, \"path\": \"" + file.getPath() + "\" }");
+            GeckoAppShell.sendEventToGecko(e);
+            break;
         }
     }
 
+    public void doCameraCapture() {
+        File file = new File(Environment.getExternalStorageDirectory(), "cameraCapture-" + Integer.toString(kCaptureIndex) + ".jpg");
+
+        Intent intent = new Intent(android.provider.MediaStore.ACTION_IMAGE_CAPTURE);
+        intent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(file));
+
+        startActivityForResult(intent, CAMERA_CAPTURE_REQUEST);
+    }
+
     public void loadUrl(String url) {
         mAwesomeBar.setText(url);
         GeckoAppShell.sendEventToGecko(new GeckoEvent(url));
     }
 
 }
--- a/embedding/android/GeckoAppShell.java
+++ b/embedding/android/GeckoAppShell.java
@@ -1653,14 +1653,17 @@ public class GeckoAppShell
                             GeckoApp.mProgressBar.setProgress(current);
                         } else {
                             GeckoApp.mProgressBar.setIndeterminate(false);
                         }
                     }
                 });
 
                 Log.i("GeckoShell", "progress - " + current + "/" + total);
+            } else if (type.equals("onCameraCapture")) {
+                //GeckoApp.mAppContext.doCameraCapture(geckoObject.getString("path"));
+                GeckoApp.mAppContext.doCameraCapture();
             }
         } catch (Exception e) {
             Log.i("GeckoShell", "handleGeckoMessage throws " + e);
         }
     }
 }
--- a/mobile/components/CapturePicker.js
+++ b/mobile/components/CapturePicker.js
@@ -40,48 +40,70 @@ const Cc = Components.classes;
 const Ci = Components.interfaces;
 const Cr = Components.results;
 const Cu = Components.utils;
 
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://gre/modules/Services.jsm");
 
 function CapturePicker() {
-  this.messageManager = Cc["@mozilla.org/childprocessmessagemanager;1"].getService(Ci.nsISyncMessageSender);
+
 }
 
 CapturePicker.prototype = {
   _file: null,
   _mode: -1,
   _result: -1,
   _shown: false,
   _title: "",
   _type: "",
   _window: null,
+  _done: null,
 
   //
   // nsICapturePicker
   //
   init: function(aWindow, aTitle, aMode) {
     this._window = aWindow;
     this._title = aTitle;
     this._mode = aMode;
   },
 
   show: function() {
     if (this._shown)
       throw Cr.NS_ERROR_UNEXPECTED;
 
     this._shown = true;
+    this._file = null;
+    this._done = false;
 
-    let res = this.messageManager.sendSyncMessage("CapturePicker:Show", { title: this._title, mode: this._mode, type: this._type })[0];
-    if (res.value)
-      this._file = res.path;
+    Services.obs.addObserver(this, "cameraCaptureDone", false);
+
+    let msg = { gecko: { type: "onCameraCapture" } };
+    Cc["@mozilla.org/android/bridge;1"].getService(Ci.nsIAndroidBridge).handleGeckoMessage(JSON.stringify(msg));
+
+    // we need to turn all the async messaging into a blocking call
+    while (!this._done)
+      Services.tm.currentThread.processNextEvent(true);
 
-    return (res.value ? Ci.nsICapturePicker.RETURN_OK : Ci.nsICapturePicker.RETURN_CANCEL);
+    if (this._res.ok) {
+      this._file = this._res.path;
+      // delete the file when exiting
+      let file = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsILocalFile);
+      file.initWithPath(this._res.path);
+      Cc["@mozilla.org/uriloader/external-helper-app-service;1"].getService(Ci.nsPIExternalAppLauncher).deleteTemporaryFileOnExit(file);
+    }
+
+    return (this._res.ok ? Ci.nsICapturePicker.RETURN_OK : Ci.nsICapturePicker.RETURN_CANCEL);
+  },
+
+  observe: function(aObject, aTopic, aData) {
+    Services.obs.removeObserver(this, "cameraCaptureDone");
+    this._done = true;
+    this._res = JSON.parse(aData);
   },
 
   modeMayBeAvailable: function(aMode) {
     if (aMode != Ci.nsICapturePicker.MODE_STILL)
       return false;
     return true;
   },
 
@@ -103,16 +125,16 @@ CapturePicker.prototype = {
   set type(aNewType) {
     if (this._shown)
       throw Cr.NS_ERROR_UNEXPECTED;
     else 
       this._type = aNewType;
   },
 
   // QI
-  QueryInterface: XPCOMUtils.generateQI([Ci.nsICapturePicker]),
+  QueryInterface: XPCOMUtils.generateQI([Ci.nsICapturePicker, Ci.nsIObserver]),
 
   // XPCOMUtils factory
   classID: Components.ID("{cb5a47f0-b58c-4fc3-b61a-358ee95f8238}"),
 };
 
 var components = [ CapturePicker ];
 const NSGetFactory = XPCOMUtils.generateNSGetFactory(components);