From 45680a562e31364d8cf0c386a703531884281bcd Mon Sep 17 00:00:00 2001 From: "Juan G. Hurtado" Date: Wed, 16 May 2012 08:37:40 +0200 Subject: [PATCH 01/25] Add Android 4.0 workaround for links with params Android 4.0.x has a known bug [1] while accessing local files with params: file://file.html?param=2 This commit adds a workaround for this problem by removing the params part of the local URI before accessing the file. [1] http://code.google.com/p/android/issues/detail?id=17535 --- .../apache/cordova/CordovaWebViewClient.java | 88 +++++++++++++------ 1 file changed, 62 insertions(+), 26 deletions(-) diff --git a/framework/src/org/apache/cordova/CordovaWebViewClient.java b/framework/src/org/apache/cordova/CordovaWebViewClient.java index 6bddfc35..7abaff5a 100755 --- a/framework/src/org/apache/cordova/CordovaWebViewClient.java +++ b/framework/src/org/apache/cordova/CordovaWebViewClient.java @@ -38,35 +38,35 @@ import android.webkit.WebViewClient; * This class is the WebViewClient that implements callbacks for our web view. */ public class CordovaWebViewClient extends WebViewClient { - + private static final String TAG = "Cordova"; DroidGap ctx; private boolean doClearHistory = false; /** * Constructor. - * + * * @param ctx */ public CordovaWebViewClient(DroidGap ctx) { this.ctx = ctx; } - + /** - * Give the host application a chance to take over the control when a new url + * Give the host application a chance to take over the control when a new url * is about to be loaded in the current WebView. - * + * * @param view The WebView that is initiating the callback. * @param url The url to be loaded. * @return true to override, false for default behavior */ @Override public boolean shouldOverrideUrlLoading(WebView view, String url) { - + // First give any plugins the chance to handle the url themselves if ((this.ctx.pluginManager != null) && this.ctx.pluginManager.onOverrideUrlLoading(url)) { } - + // If dialing phone (tel:5551212) else if (url.startsWith(WebView.SCHEME_TEL)) { try { @@ -154,11 +154,11 @@ public class CordovaWebViewClient extends WebViewClient { } return true; } - + /** * On received http auth request. - * The method reacts on all registered authentication tokens. There is one and only one authentication token for any host + realm combination - * + * The method reacts on all registered authentication tokens. There is one and only one authentication token for any host + realm combination + * * @param view * the view * @param handler @@ -171,27 +171,27 @@ public class CordovaWebViewClient extends WebViewClient { @Override public void onReceivedHttpAuthRequest(WebView view, HttpAuthHandler handler, String host, String realm) { - + // get the authentication token AuthenticationToken token = ctx.getAuthenticationToken(host,realm); - + if(token != null) { handler.proceed(token.getUserName(), token.getPassword()); } } - + @Override public void onPageStarted(WebView view, String url, Bitmap favicon) { - // Clear history so history.back() doesn't do anything. + // Clear history so history.back() doesn't do anything. // So we can reinit() native side CallbackServer & PluginManager. - view.clearHistory(); + view.clearHistory(); this.doClearHistory = true; } - + /** * Notify the host application that a page has finished loading. - * + * * @param view The webview initiating the callback. * @param url The url of the page. */ @@ -200,8 +200,8 @@ public class CordovaWebViewClient extends WebViewClient { super.onPageFinished(view, url); /** - * Because of a timing issue we need to clear this history in onPageFinished as well as - * onPageStarted. However we only want to do this if the doClearHistory boolean is set to + * Because of a timing issue we need to clear this history in onPageFinished as well as + * onPageStarted. However we only want to do this if the doClearHistory boolean is set to * true. You see when you load a url with a # in it which is common in jQuery applications * onPageStared is not called. Clearing the history at that point would break jQuery apps. */ @@ -252,15 +252,15 @@ public class CordovaWebViewClient extends WebViewClient { this.ctx.endActivity(); } } - + /** - * Report an error to the host application. These errors are unrecoverable (i.e. the main resource is unavailable). + * Report an error to the host application. These errors are unrecoverable (i.e. the main resource is unavailable). * The errorCode parameter corresponds to one of the ERROR_* constants. * * @param view The WebView that is initiating the callback. * @param errorCode The error code corresponding to an ERROR_* value. * @param description A String describing the error. - * @param failingUrl The url that failed to load. + * @param failingUrl The url that failed to load. */ @Override public void onReceivedError(WebView view, int errorCode, String description, String failingUrl) { @@ -275,9 +275,9 @@ public class CordovaWebViewClient extends WebViewClient { // Handle error this.ctx.onReceivedError(errorCode, description, failingUrl); } - + public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) { - + final String packageName = this.ctx.getPackageName(); final PackageManager pm = this.ctx.getPackageManager(); ApplicationInfo appInfo; @@ -289,7 +289,7 @@ public class CordovaWebViewClient extends WebViewClient { return; } else { // debug = false - super.onReceivedSslError(view, handler, error); + super.onReceivedSslError(view, handler, error); } } catch (NameNotFoundException e) { // When it doubt, lock it out! @@ -299,7 +299,7 @@ public class CordovaWebViewClient extends WebViewClient { @Override public void doUpdateVisitedHistory(WebView view, String url, boolean isReload) { - /* + /* * If you do a document.location.href the url does not get pushed on the stack * so we do a check here to see if the url should be pushed. */ @@ -307,4 +307,40 @@ public class CordovaWebViewClient extends WebViewClient { this.ctx.pushUrl(url); } } + + @Override + public WebResourceResponse shouldInterceptRequest(WebView view, String url) { + if(url.contains("?")){ + return generateWebResourceResponse(url); + } else { + return super.shouldInterceptRequest(view, url); + } + } + + private WebResourceResponse generateWebResourceResponse(String url) { + final String ANDROID_ASSET = "file:///android_asset/"; + if (url.startsWith(ANDROID_ASSET)) { + String niceUrl = url; + niceUrl = url.replaceFirst(ANDROID_ASSET, ""); + if(niceUrl.contains("?")){ + niceUrl = niceUrl.split("\\?")[0]; + } + + String mimetype = null; + if(niceUrl.endsWith(".html")){ + mimetype = "text/html"; + } + + try { + AssetManager assets = ctx.getAssets(); + Uri uri = Uri.parse(niceUrl); + InputStream stream = assets.open(uri.getPath(), AssetManager.ACCESS_STREAMING); + WebResourceResponse response = new WebResourceResponse(mimetype, "UTF-8", stream); + return response; + } catch (IOException e) { + Log.e("generateWebResourceResponse", e.getMessage(), e); + } + } + return null; + } } From 6de66b87cb84c4ad4e9c4bb0f1e053be70fabf59 Mon Sep 17 00:00:00 2001 From: "Juan G. Hurtado" Date: Wed, 16 May 2012 08:37:40 +0200 Subject: [PATCH 02/25] Add Android 4.0 workaround for links with params Android 4.0.x has a known bug [1] while accessing local files with params: file://file.html?param=2 This commit adds a workaround for this problem by removing the params part of the local URI before accessing the file. [1] http://code.google.com/p/android/issues/detail?id=17535 --- .../apache/cordova/CordovaWebViewClient.java | 88 +++++++++++++------ 1 file changed, 62 insertions(+), 26 deletions(-) diff --git a/framework/src/org/apache/cordova/CordovaWebViewClient.java b/framework/src/org/apache/cordova/CordovaWebViewClient.java index 6bddfc35..7abaff5a 100755 --- a/framework/src/org/apache/cordova/CordovaWebViewClient.java +++ b/framework/src/org/apache/cordova/CordovaWebViewClient.java @@ -38,35 +38,35 @@ import android.webkit.WebViewClient; * This class is the WebViewClient that implements callbacks for our web view. */ public class CordovaWebViewClient extends WebViewClient { - + private static final String TAG = "Cordova"; DroidGap ctx; private boolean doClearHistory = false; /** * Constructor. - * + * * @param ctx */ public CordovaWebViewClient(DroidGap ctx) { this.ctx = ctx; } - + /** - * Give the host application a chance to take over the control when a new url + * Give the host application a chance to take over the control when a new url * is about to be loaded in the current WebView. - * + * * @param view The WebView that is initiating the callback. * @param url The url to be loaded. * @return true to override, false for default behavior */ @Override public boolean shouldOverrideUrlLoading(WebView view, String url) { - + // First give any plugins the chance to handle the url themselves if ((this.ctx.pluginManager != null) && this.ctx.pluginManager.onOverrideUrlLoading(url)) { } - + // If dialing phone (tel:5551212) else if (url.startsWith(WebView.SCHEME_TEL)) { try { @@ -154,11 +154,11 @@ public class CordovaWebViewClient extends WebViewClient { } return true; } - + /** * On received http auth request. - * The method reacts on all registered authentication tokens. There is one and only one authentication token for any host + realm combination - * + * The method reacts on all registered authentication tokens. There is one and only one authentication token for any host + realm combination + * * @param view * the view * @param handler @@ -171,27 +171,27 @@ public class CordovaWebViewClient extends WebViewClient { @Override public void onReceivedHttpAuthRequest(WebView view, HttpAuthHandler handler, String host, String realm) { - + // get the authentication token AuthenticationToken token = ctx.getAuthenticationToken(host,realm); - + if(token != null) { handler.proceed(token.getUserName(), token.getPassword()); } } - + @Override public void onPageStarted(WebView view, String url, Bitmap favicon) { - // Clear history so history.back() doesn't do anything. + // Clear history so history.back() doesn't do anything. // So we can reinit() native side CallbackServer & PluginManager. - view.clearHistory(); + view.clearHistory(); this.doClearHistory = true; } - + /** * Notify the host application that a page has finished loading. - * + * * @param view The webview initiating the callback. * @param url The url of the page. */ @@ -200,8 +200,8 @@ public class CordovaWebViewClient extends WebViewClient { super.onPageFinished(view, url); /** - * Because of a timing issue we need to clear this history in onPageFinished as well as - * onPageStarted. However we only want to do this if the doClearHistory boolean is set to + * Because of a timing issue we need to clear this history in onPageFinished as well as + * onPageStarted. However we only want to do this if the doClearHistory boolean is set to * true. You see when you load a url with a # in it which is common in jQuery applications * onPageStared is not called. Clearing the history at that point would break jQuery apps. */ @@ -252,15 +252,15 @@ public class CordovaWebViewClient extends WebViewClient { this.ctx.endActivity(); } } - + /** - * Report an error to the host application. These errors are unrecoverable (i.e. the main resource is unavailable). + * Report an error to the host application. These errors are unrecoverable (i.e. the main resource is unavailable). * The errorCode parameter corresponds to one of the ERROR_* constants. * * @param view The WebView that is initiating the callback. * @param errorCode The error code corresponding to an ERROR_* value. * @param description A String describing the error. - * @param failingUrl The url that failed to load. + * @param failingUrl The url that failed to load. */ @Override public void onReceivedError(WebView view, int errorCode, String description, String failingUrl) { @@ -275,9 +275,9 @@ public class CordovaWebViewClient extends WebViewClient { // Handle error this.ctx.onReceivedError(errorCode, description, failingUrl); } - + public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) { - + final String packageName = this.ctx.getPackageName(); final PackageManager pm = this.ctx.getPackageManager(); ApplicationInfo appInfo; @@ -289,7 +289,7 @@ public class CordovaWebViewClient extends WebViewClient { return; } else { // debug = false - super.onReceivedSslError(view, handler, error); + super.onReceivedSslError(view, handler, error); } } catch (NameNotFoundException e) { // When it doubt, lock it out! @@ -299,7 +299,7 @@ public class CordovaWebViewClient extends WebViewClient { @Override public void doUpdateVisitedHistory(WebView view, String url, boolean isReload) { - /* + /* * If you do a document.location.href the url does not get pushed on the stack * so we do a check here to see if the url should be pushed. */ @@ -307,4 +307,40 @@ public class CordovaWebViewClient extends WebViewClient { this.ctx.pushUrl(url); } } + + @Override + public WebResourceResponse shouldInterceptRequest(WebView view, String url) { + if(url.contains("?")){ + return generateWebResourceResponse(url); + } else { + return super.shouldInterceptRequest(view, url); + } + } + + private WebResourceResponse generateWebResourceResponse(String url) { + final String ANDROID_ASSET = "file:///android_asset/"; + if (url.startsWith(ANDROID_ASSET)) { + String niceUrl = url; + niceUrl = url.replaceFirst(ANDROID_ASSET, ""); + if(niceUrl.contains("?")){ + niceUrl = niceUrl.split("\\?")[0]; + } + + String mimetype = null; + if(niceUrl.endsWith(".html")){ + mimetype = "text/html"; + } + + try { + AssetManager assets = ctx.getAssets(); + Uri uri = Uri.parse(niceUrl); + InputStream stream = assets.open(uri.getPath(), AssetManager.ACCESS_STREAMING); + WebResourceResponse response = new WebResourceResponse(mimetype, "UTF-8", stream); + return response; + } catch (IOException e) { + Log.e("generateWebResourceResponse", e.getMessage(), e); + } + } + return null; + } } From 8ff48b371e6090e0af942efdd66f32be6911d4bf Mon Sep 17 00:00:00 2001 From: "Juan G. Hurtado" Date: Fri, 18 May 2012 08:45:39 +0200 Subject: [PATCH 03/25] Fix imports for changes in 45680a5 Commit 45680a5 had errors importing packages. This commit fix them. --- framework/src/org/apache/cordova/CordovaWebViewClient.java | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/framework/src/org/apache/cordova/CordovaWebViewClient.java b/framework/src/org/apache/cordova/CordovaWebViewClient.java index 7abaff5a..63ea3012 100755 --- a/framework/src/org/apache/cordova/CordovaWebViewClient.java +++ b/framework/src/org/apache/cordova/CordovaWebViewClient.java @@ -18,12 +18,17 @@ */ package org.apache.cordova; +import java.io.IOException; +import java.io.InputStream; + import org.apache.cordova.api.LOG; +import android.content.Context; import android.content.Intent; import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; import android.content.pm.PackageManager.NameNotFoundException; +import android.content.res.AssetManager; import android.graphics.Bitmap; import android.net.Uri; import android.net.http.SslError; @@ -31,6 +36,7 @@ import android.util.Log; import android.view.View; import android.webkit.HttpAuthHandler; import android.webkit.SslErrorHandler; +import android.webkit.WebResourceResponse; import android.webkit.WebView; import android.webkit.WebViewClient; From 40b9810a63c9fc456a8e44331d7b0eae9222227f Mon Sep 17 00:00:00 2001 From: Joe Bowser Date: Mon, 18 Jun 2012 11:31:53 -0700 Subject: [PATCH 04/25] Starting the Buttons Branch --- framework/src/org/apache/cordova/App.java | 4 +- .../org/apache/cordova/CordovaWebView.java | 94 +++++++++++++++++++ .../src/org/apache/cordova/DroidGap.java | 82 +--------------- 3 files changed, 97 insertions(+), 83 deletions(-) diff --git a/framework/src/org/apache/cordova/App.java b/framework/src/org/apache/cordova/App.java index ed9ec779..629d4223 100755 --- a/framework/src/org/apache/cordova/App.java +++ b/framework/src/org/apache/cordova/App.java @@ -188,7 +188,7 @@ public class App extends Plugin { */ public void overrideBackbutton(boolean override) { LOG.i("App", "WARNING: Back Button Default Behaviour will be overridden. The backbutton event will be fired!"); - this.ctx.bindBackButton(override); + webView.bindButton(override); } /** @@ -200,7 +200,7 @@ public class App extends Plugin { */ public void overrideButton(String button, boolean override) { LOG.i("DroidGap", "WARNING: Volume Button Default Behaviour will be overridden. The volume event will be fired!"); - ((DroidGap)this.ctx).bindButton(button, override); + webView.bindButton(button, override); } /** * Return whether the Android back button is overridden by the user. diff --git a/framework/src/org/apache/cordova/CordovaWebView.java b/framework/src/org/apache/cordova/CordovaWebView.java index 308d222f..9f801120 100755 --- a/framework/src/org/apache/cordova/CordovaWebView.java +++ b/framework/src/org/apache/cordova/CordovaWebView.java @@ -39,6 +39,7 @@ import android.net.Uri; import android.os.Bundle; import android.util.AttributeSet; import android.util.Log; +import android.view.KeyEvent; import android.view.WindowManager; import android.webkit.WebSettings; import android.webkit.WebView; @@ -70,6 +71,12 @@ public class CordovaWebView extends WebView { // Flag to track that a loadUrl timeout occurred int loadUrlTimeout = 0; + private boolean bound; + + private boolean volumedownBound; + + private boolean volumeupBound; + /** * Constructor. * @@ -660,4 +667,91 @@ public class CordovaWebView extends WebView { } return p.toString(); } + + /* + * onKeyDown + */ + + @Override + public boolean onKeyDown(int keyCode, KeyEvent event) + { + // If volumedown key + if (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN) { + if (this.volumedownBound==true) { + // only override default behaviour is event bound + LOG.d(TAG, "Down Key Hit"); + this.loadUrl("javascript:cordova.fireDocumentEvent('volumedownbutton');"); + return true; + } + } + + // If volumeup key + else if (keyCode == KeyEvent.KEYCODE_VOLUME_UP) { + if (this.volumeupBound==true) { + // only override default behaviour is event bound + LOG.d(TAG, "Up Key Hit"); + this.loadUrl("javascript:cordova.fireDocumentEvent('volumeupbutton');"); + return true; + } + } + + return false; + } + + @Override + public boolean onKeyUp(int keyCode, KeyEvent event) + { + + Log.d(TAG, "KeyDown has been triggered on the view"); + + // If back key + if (keyCode == KeyEvent.KEYCODE_BACK) { + // If back key is bound, then send event to JavaScript + if (this.bound) { + this.loadUrl("javascript:cordova.fireDocumentEvent('backbutton');"); + return true; + } else { + // If not bound + // Go to previous page in webview if it is possible to go back + if (this.backHistory()) { + return true; + } + // If not, then invoke default behaviour + else { + //this.activityState = ACTIVITY_EXITING; + return false; + } + } + } + + // If menu key + else if (keyCode == KeyEvent.KEYCODE_MENU) { + this.loadUrl("javascript:cordova.fireDocumentEvent('menubutton');"); + return super.onKeyUp(keyCode, event); + } + + // If search key + else if (keyCode == KeyEvent.KEYCODE_SEARCH) { + this.loadUrl("javascript:cordova.fireDocumentEvent('searchbutton');"); + return true; + } + + Log.d(TAG, "KeyUp has been triggered on the view"); + return false; + } + + public void bindButton(boolean override) + { + this.bound = override; + } + + public void bindButton(String button, boolean override) { + // TODO Auto-generated method stub + if (button.compareTo("volumeup")==0) { + this.volumeupBound = override; + } + else if (button.compareTo("volumedown")==0) { + this.volumedownBound = override; + } + } } diff --git a/framework/src/org/apache/cordova/DroidGap.java b/framework/src/org/apache/cordova/DroidGap.java index d4c142d3..f0f31f14 100755 --- a/framework/src/org/apache/cordova/DroidGap.java +++ b/framework/src/org/apache/cordova/DroidGap.java @@ -784,87 +784,7 @@ public class DroidGap extends Activity implements CordovaInterface { super.finish(); } - /** - * Called when a key is released. (Key UP) - * - * @param keyCode - * @param event - */ - @Override - public boolean onKeyUp(int keyCode, KeyEvent event) { - if (this.appView == null) { - return super.onKeyUp(keyCode, event); - } - - // If back key - if (keyCode == KeyEvent.KEYCODE_BACK) { - // If back key is bound, then send event to JavaScript - if (this.bound) { - this.appView.loadUrl("javascript:cordova.fireDocumentEvent('backbutton');"); - return true; - } else { - // If not bound - // Go to previous page in webview if it is possible to go back - if (this.backHistory()) { - return true; - } - // If not, then invoke behavior of super class - else { - this.activityState = ACTIVITY_EXITING; - return super.onKeyUp(keyCode, event); - } - } - } - - // If menu key - else if (keyCode == KeyEvent.KEYCODE_MENU) { - this.appView.loadUrl("javascript:cordova.fireDocumentEvent('menubutton');"); - return super.onKeyUp(keyCode, event); - } - - // If search key - else if (keyCode == KeyEvent.KEYCODE_SEARCH) { - this.appView.loadUrl("javascript:cordova.fireDocumentEvent('searchbutton');"); - return true; - } - - return false; - } - - /** - * Called when a key is pressed. (Key DOWN) - * - * @param keyCode - * @param event - */ - @Override - public boolean onKeyDown(int keyCode, KeyEvent event) { - if (this.appView == null) { - return super.onKeyDown(keyCode, event); - } - - // If volumedown key - if (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN) { - if (this.volumedownBound==true) { - // only override default behaviour is event bound - LOG.d(TAG, "Down Key Hit"); - this.appView.loadUrl("javascript:cordova.fireDocumentEvent('volumedownbutton');"); - return true; - } - } - - // If volumeup key - else if (keyCode == KeyEvent.KEYCODE_VOLUME_UP) { - if (this.volumeupBound==true) { - // only override default behaviour is event bound - LOG.d(TAG, "Up Key Hit"); - this.appView.loadUrl("javascript:cordova.fireDocumentEvent('volumeupbutton');"); - return true; - } - } - return super.onKeyDown(keyCode, event); - } - + /** * Launch an activity for which you would like a result when it finished. When this activity exits, * your onActivityResult() method will be called. From f394f7457b52e7839c685abd615a6f3c95a94bda Mon Sep 17 00:00:00 2001 From: Joe Bowser Date: Tue, 19 Jun 2012 16:35:22 -0700 Subject: [PATCH 05/25] Tweaks to CordovaWebView to support other keys --- .../org/apache/cordova/CordovaWebView.java | 63 ++++++++++++------- 1 file changed, 40 insertions(+), 23 deletions(-) diff --git a/framework/src/org/apache/cordova/CordovaWebView.java b/framework/src/org/apache/cordova/CordovaWebView.java index 9f801120..17b92fc4 100755 --- a/framework/src/org/apache/cordova/CordovaWebView.java +++ b/framework/src/org/apache/cordova/CordovaWebView.java @@ -52,8 +52,12 @@ public class CordovaWebView extends WebView { /** The whitelist **/ private ArrayList whiteList = new ArrayList(); private HashMap whiteListCache = new HashMap(); + private ArrayList keyDownCodes = new ArrayList(); + private ArrayList keyUpCodes = new ArrayList(); + public PluginManager pluginManager; public CallbackServer callbackServer; + /** Actvities and other important classes **/ private CordovaInterface mCtx; @@ -675,26 +679,25 @@ public class CordovaWebView extends WebView { @Override public boolean onKeyDown(int keyCode, KeyEvent event) { - // If volumedown key - if (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN) { - if (this.volumedownBound==true) { - // only override default behaviour is event bound - LOG.d(TAG, "Down Key Hit"); - this.loadUrl("javascript:cordova.fireDocumentEvent('volumedownbutton');"); - return true; + if(keyDownCodes.contains(keyCode)) + { + if (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN) { + // only override default behaviour is event bound + LOG.d(TAG, "Down Key Hit"); + this.loadUrl("javascript:cordova.fireDocumentEvent('volumedownbutton');"); + return true; + } + // If volumeup key + else if (keyCode == KeyEvent.KEYCODE_VOLUME_UP) { + LOG.d(TAG, "Up Key Hit"); + this.loadUrl("javascript:cordova.fireDocumentEvent('volumeupbutton');"); + return true; + } + else + { + //Do some other stuff! } } - - // If volumeup key - else if (keyCode == KeyEvent.KEYCODE_VOLUME_UP) { - if (this.volumeupBound==true) { - // only override default behaviour is event bound - LOG.d(TAG, "Up Key Hit"); - this.loadUrl("javascript:cordova.fireDocumentEvent('volumeupbutton');"); - return true; - } - } - return false; } @@ -723,18 +726,21 @@ public class CordovaWebView extends WebView { } } } - - // If menu key + // Legacy else if (keyCode == KeyEvent.KEYCODE_MENU) { this.loadUrl("javascript:cordova.fireDocumentEvent('menubutton');"); return super.onKeyUp(keyCode, event); } - // If search key else if (keyCode == KeyEvent.KEYCODE_SEARCH) { this.loadUrl("javascript:cordova.fireDocumentEvent('searchbutton');"); return true; } + else if(keyUpCodes.contains(keyCode)) + { + //What the hell should this do? + } + Log.d(TAG, "KeyUp has been triggered on the view"); return false; @@ -748,10 +754,21 @@ public class CordovaWebView extends WebView { public void bindButton(String button, boolean override) { // TODO Auto-generated method stub if (button.compareTo("volumeup")==0) { - this.volumeupBound = override; + keyDownCodes.add(KeyEvent.KEYCODE_VOLUME_DOWN); } else if (button.compareTo("volumedown")==0) { - this.volumedownBound = override; + keyDownCodes.add(KeyEvent.KEYCODE_VOLUME_UP); } } + + public void bindButton(int keyCode, boolean keyDown, boolean override) { + if(keyDown) + { + keyDownCodes.add(keyCode); + } + else + { + keyUpCodes.add(keyCode); + } + } } From ccf0c5db674d09cf0538843f4823164a88e02415 Mon Sep 17 00:00:00 2001 From: Joe Bowser Date: Wed, 20 Jun 2012 12:07:04 -0700 Subject: [PATCH 06/25] Got up and down mixed --- framework/src/org/apache/cordova/CordovaWebView.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/framework/src/org/apache/cordova/CordovaWebView.java b/framework/src/org/apache/cordova/CordovaWebView.java index 17b92fc4..f5dec85e 100755 --- a/framework/src/org/apache/cordova/CordovaWebView.java +++ b/framework/src/org/apache/cordova/CordovaWebView.java @@ -754,10 +754,10 @@ public class CordovaWebView extends WebView { public void bindButton(String button, boolean override) { // TODO Auto-generated method stub if (button.compareTo("volumeup")==0) { - keyDownCodes.add(KeyEvent.KEYCODE_VOLUME_DOWN); + keyDownCodes.add(KeyEvent.KEYCODE_VOLUME_UP); } else if (button.compareTo("volumedown")==0) { - keyDownCodes.add(KeyEvent.KEYCODE_VOLUME_UP); + keyDownCodes.add(KeyEvent.KEYCODE_VOLUME_DOWN); } } From d1905dbee8edb720b25547a72e1f7f843036089d Mon Sep 17 00:00:00 2001 From: Joe Bowser Date: Wed, 20 Jun 2012 14:16:02 -0700 Subject: [PATCH 07/25] Fixing work-around to work for both ? and # --- framework/src/org/apache/cordova/CordovaWebViewClient.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/framework/src/org/apache/cordova/CordovaWebViewClient.java b/framework/src/org/apache/cordova/CordovaWebViewClient.java index 85261c3e..4934c8f3 100755 --- a/framework/src/org/apache/cordova/CordovaWebViewClient.java +++ b/framework/src/org/apache/cordova/CordovaWebViewClient.java @@ -461,7 +461,7 @@ public class CordovaWebViewClient extends WebViewClient { @Override public WebResourceResponse shouldInterceptRequest(WebView view, String url) { - if(url.contains("?")){ + if(url.contains("?") || url.contains("#")){ return generateWebResourceResponse(url); } else { return super.shouldInterceptRequest(view, url); @@ -476,6 +476,10 @@ public class CordovaWebViewClient extends WebViewClient { if(niceUrl.contains("?")){ niceUrl = niceUrl.split("\\?")[0]; } + else if(niceUrl.contains("#")) + { + niceUrl = niceUrl.split("#")[0]; + } String mimetype = null; if(niceUrl.endsWith(".html")){ From 56acd2953bfc6054fdbff6bb69e01f1a4bb68222 Mon Sep 17 00:00:00 2001 From: Joe Bowser Date: Thu, 21 Jun 2012 11:12:30 -0700 Subject: [PATCH 08/25] Adding updated JS --- framework/assets/js/cordova.android.js | 83 +++++++++++--------------- 1 file changed, 36 insertions(+), 47 deletions(-) diff --git a/framework/assets/js/cordova.android.js b/framework/assets/js/cordova.android.js index 16dab290..ae20b290 100644 --- a/framework/assets/js/cordova.android.js +++ b/framework/assets/js/cordova.android.js @@ -1,6 +1,6 @@ -// commit 347de1a785b7ecbe86c2189343ab2549a9297f9b +// commit 25033fceac7c800623f1f16881b784d19eba69cc -// File generated at :: Fri Jun 08 2012 16:17:50 GMT-0700 (PDT) +// File generated at :: Thu Jun 21 2012 10:45:32 GMT-0700 (PDT) /* Licensed to the Apache Software Foundation (ASF) under one @@ -190,7 +190,9 @@ var cordova = { fireDocumentEvent: function(type, data) { var evt = createEvent(type, data); if (typeof documentEventHandlers[type] != 'undefined') { - documentEventHandlers[type].fire(evt); + setTimeout(function() { + documentEventHandlers[type].fire(evt); + }, 0); } else { document.dispatchEvent(evt); } @@ -198,7 +200,9 @@ var cordova = { fireWindowEvent: function(type, data) { var evt = createEvent(type,data); if (typeof windowEventHandlers[type] != 'undefined') { - windowEventHandlers[type].fire(evt); + setTimeout(function() { + windowEventHandlers[type].fire(evt); + }, 0); } else { window.dispatchEvent(evt); } @@ -953,8 +957,7 @@ module.exports = function(success, fail, service, action, args) { // If a result was returned if (r.length > 0) { - var v; - eval("v="+r+";"); + var v = JSON.parse(r); // If status is OK, then return value back to caller if (v.status === cordova.callbackStatus.OK) { @@ -1067,28 +1070,6 @@ module.exports = { cordova.addDocumentEventHandler('menubutton'); cordova.addDocumentEventHandler('searchbutton'); - function bindButtonChannel(buttonName) { - // generic button bind used for volumeup/volumedown buttons - return cordova.addDocumentEventHandler(buttonName + 'button', { - onSubscribe:function() { - // If we just attached the first handler, let native know we need to override the button. - if (this.numHandlers === 1) { - exec(null, null, "App", "overrideButton", [buttonName, true]); - } - }, - onUnsubscribe:function() { - // If we just detached the last handler, let native know we no longer override the volumeup button. - if (this.numHandlers === 0) { - exec(null, null, "App", "overrideButton", [buttonName, false]); - } - } - }); - - } - // Inject a listener for the volume buttons on the document. - var volumeUpButtonChannel = bindButtonChannel('volumeup'); - var volumeDownButtonChannel = bindButtonChannel('volumedown'); - // Figure out if we need to shim-in localStorage and WebSQL // support from the native side. var storage = require('cordova/plugin/android/storage'); @@ -1308,7 +1289,7 @@ cameraExport.getPicture = function(successCallback, errorCallback, options) { cameraExport.cleanup = function(successCallback, errorCallback) { exec(successCallback, errorCallback, "Camera", "cleanup", []); -} +}; module.exports = cameraExport; }); @@ -2628,7 +2609,8 @@ module.exports = FileSystem; // file: lib/common/plugin/FileTransfer.js define("cordova/plugin/FileTransfer", function(require, exports, module) { -var exec = require('cordova/exec'); +var exec = require('cordova/exec'), + FileTransferError = require('cordova/plugin/FileTransferError'); /** * FileTransfer uploads a file to a remote server. @@ -2647,6 +2629,8 @@ var FileTransfer = function() {}; * @param trustAllHosts {Boolean} Optional trust all hosts (e.g. for self-signed certs), defaults to false */ FileTransfer.prototype.upload = function(filePath, server, successCallback, errorCallback, options, trustAllHosts) { + // sanity parameter checking + if (!filePath || !server) throw new Error("FileTransfer.upload requires filePath and server URL parameters at the minimum."); // check for options var fileKey = null; var fileName = null; @@ -2668,7 +2652,12 @@ FileTransfer.prototype.upload = function(filePath, server, successCallback, erro } } - exec(successCallback, errorCallback, 'FileTransfer', 'upload', [filePath, server, fileKey, fileName, mimeType, params, trustAllHosts, chunkedMode]); + var fail = function(e) { + var error = new FileTransferError(e.code, e.source, e.target, e.http_status); + errorCallback(error); + }; + + exec(successCallback, fail, 'FileTransfer', 'upload', [filePath, server, fileKey, fileName, mimeType, params, trustAllHosts, chunkedMode]); }; /** @@ -2679,6 +2668,8 @@ FileTransfer.prototype.upload = function(filePath, server, successCallback, erro * @param errorCallback {Function} Callback to be invoked upon error */ FileTransfer.prototype.download = function(source, target, successCallback, errorCallback) { + // sanity parameter checking + if (!source || !target) throw new Error("FileTransfer.download requires source URI and target URI parameters at the minimum."); var win = function(result) { var entry = null; if (result.isDirectory) { @@ -2693,6 +2684,12 @@ FileTransfer.prototype.download = function(source, target, successCallback, erro entry.fullPath = result.fullPath; successCallback(entry); }; + + var fail = function(e) { + var error = new FileTransferError(e.code, e.source, e.target, e.http_status); + errorCallback(error); + }; + exec(win, errorCallback, 'FileTransfer', 'download', [source, target]); }; @@ -2706,8 +2703,11 @@ define("cordova/plugin/FileTransferError", function(require, exports, module) { * FileTransferError * @constructor */ -var FileTransferError = function(code) { +var FileTransferError = function(code, source, target, status) { this.code = code || null; + this.source = source || null; + this.target = target || null; + this.http_status = status || null; }; FileTransferError.FILE_NOT_FOUND_ERR = 1; @@ -2715,6 +2715,7 @@ FileTransferError.INVALID_URL_ERR = 2; FileTransferError.CONNECTION_ERR = 3; module.exports = FileTransferError; + }); // file: lib/common/plugin/FileUploadOptions.js @@ -3665,21 +3666,6 @@ module.exports = { exec(null, null, "App", "overrideBackbutton", [override]); }, - /** - * Override the default behavior of the Android volume button. - * If overridden, when the volume button is pressed, the "volume[up|down]button" JavaScript event will be fired. - * - * Note: The user should not have to call this method. Instead, when the user - * registers for the "volume[up|down]button" event, this is automatically done. - * - * @param button volumeup, volumedown - * @param override T=override, F=cancel override - */ - overrideButton:function(button, override) { - exec(null, null, "App", "overrideButton", [button, override]); - }, - - /** * Exit and terminate the application. */ @@ -5509,6 +5495,9 @@ define("cordova/plugin/splashscreen", function(require, exports, module) { var exec = require('cordova/exec'); var splashscreen = { + show:function() { + exec(null, null, "SplashScreen", "show", []); + }, hide:function() { exec(null, null, "SplashScreen", "hide", []); } From b339330592fd7a4abffdf85f8fdc8cac64173eba Mon Sep 17 00:00:00 2001 From: Fil Maj Date: Tue, 19 Jun 2012 17:14:05 -0700 Subject: [PATCH 09/25] Added MediaScanner abilities to camera launcher plugin. Now images saved to SD card should show up in the android gallery app right away --- .../org/apache/cordova/CameraLauncher.java | 44 ++++++++++++++++--- 1 file changed, 37 insertions(+), 7 deletions(-) diff --git a/framework/src/org/apache/cordova/CameraLauncher.java b/framework/src/org/apache/cordova/CameraLauncher.java index e9f4ea8f..ed0f928c 100755 --- a/framework/src/org/apache/cordova/CameraLauncher.java +++ b/framework/src/org/apache/cordova/CameraLauncher.java @@ -42,6 +42,8 @@ import android.database.Cursor; import android.graphics.Bitmap; import android.graphics.Matrix; import android.graphics.Bitmap.CompressFormat; +import android.media.MediaScannerConnection; +import android.media.MediaScannerConnection.MediaScannerConnectionClient; import android.net.Uri; import android.os.Environment; import android.provider.MediaStore; @@ -52,7 +54,7 @@ import android.util.Log; * and returns the captured image. When the camera view is closed, the screen displayed before * the camera view was shown is redisplayed. */ -public class CameraLauncher extends Plugin { +public class CameraLauncher extends Plugin implements MediaScannerConnectionClient { private static final int DATA_URL = 0; // Return base64 encoded string private static final int FILE_URI = 1; // Return file uri (content://media/external/images/media/2 for Android) @@ -82,6 +84,8 @@ public class CameraLauncher extends Plugin { public String callbackId; private int numPics; + + private MediaScannerConnection conn; // Used to update gallery app with newly-written files //This should never be null! //private CordovaInterface cordova; @@ -330,13 +334,13 @@ public class CameraLauncher extends Plugin { // (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); + this.imageUri = 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); + this.imageUri = 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.failPicture("Error capturing image - no media storage found."); @@ -366,24 +370,28 @@ public class CameraLauncher extends Plugin { bitmap = scaleBitmap(getBitmapFromResult(intent)); // Add compressed version of captured image to returned media store Uri - OutputStream os = this.cordova.getActivity().getContentResolver().openOutputStream(uri); + OutputStream os = this.cordova.getActivity().getContentResolver().openOutputStream(this.imageUri); bitmap.compress(Bitmap.CompressFormat.JPEG, this.mQuality, os); os.close(); // Restore exif data to file if (this.encodingType == JPEG) { - exif.createOutFile(FileUtils.getRealPathFromURI(uri, this.cordova)); + exif.createOutFile(FileUtils.getRealPathFromURI(this.imageUri, this.cordova)); exif.writeExifData(); } + // Scan for the gallery to update pic refs in gallery + this.scanForGallery(); + // Send Uri back to JavaScript for viewing image - this.success(new PluginResult(PluginResult.Status.OK, uri.toString()), this.callbackId); + this.success(new PluginResult(PluginResult.Status.OK, this.imageUri.toString()), this.callbackId); } bitmap.recycle(); bitmap = null; System.gc(); checkForDuplicateImage(FILE_URI); + } catch (IOException e) { e.printStackTrace(); this.failPicture("Error capturing image."); @@ -584,4 +592,26 @@ public class CameraLauncher extends Plugin { public void failPicture(String err) { this.error(new PluginResult(PluginResult.Status.ERROR, err), this.callbackId); } + + private void scanForGallery() { + if(this.conn!=null) this.conn.disconnect(); + this.conn = new MediaScannerConnection(this.ctx.getActivity().getApplicationContext(), this); + conn.connect(); + } + + @Override + public void onMediaScannerConnected() { + try{ + this.conn.scanFile(this.imageUri.toString(), "image/*"); + } catch (java.lang.IllegalStateException e){ + e.printStackTrace(); + LOG.d(LOG_TAG, "Can;t scan file in MediaScanner aftering taking picture"); + } + + } + + @Override + public void onScanCompleted(String path, Uri uri) { + this.conn.disconnect(); + } } From 9f66ccb5f3f5dc7c2ae3610daf03b22f96173d32 Mon Sep 17 00:00:00 2001 From: Fil Maj Date: Wed, 20 Jun 2012 11:10:43 -0700 Subject: [PATCH 10/25] merge!!!!11one --- framework/src/org/apache/cordova/CameraLauncher.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/framework/src/org/apache/cordova/CameraLauncher.java b/framework/src/org/apache/cordova/CameraLauncher.java index ed0f928c..f2c3e12d 100755 --- a/framework/src/org/apache/cordova/CameraLauncher.java +++ b/framework/src/org/apache/cordova/CameraLauncher.java @@ -81,6 +81,7 @@ public class CameraLauncher extends Plugin implements MediaScannerConnectionClie private Uri imageUri; // Uri of captured image private int encodingType; // Type of encoding to use private int mediaType; // What type of media to retrieve + private boolean saveToPhotoAlbum; // Should the picture be saved to the device's photo album public String callbackId; private int numPics; @@ -121,6 +122,7 @@ public class CameraLauncher extends Plugin implements MediaScannerConnectionClie if (action.equals("takePicture")) { int srcType = CAMERA; int destType = FILE_URI; + this.saveToPhotoAlbum = false; this.targetHeight = 0; this.targetWidth = 0; this.encodingType = JPEG; @@ -134,6 +136,7 @@ public class CameraLauncher extends Plugin implements MediaScannerConnectionClie this.targetHeight = args.getInt(4); this.encodingType = args.getInt(5); this.mediaType = args.getInt(6); + this.saveToPhotoAlbum = args.getBoolean(9); if (srcType == CAMERA) { this.takePicture(destType, encodingType); From f6d4402fdc8eae41352f2f268a69e578bf33754f Mon Sep 17 00:00:00 2001 From: Fil Maj Date: Wed, 20 Jun 2012 14:12:06 -0700 Subject: [PATCH 11/25] Removing images and saving images to jail if SaveToPhotoAlbum is set to true --- .../org/apache/cordova/CameraLauncher.java | 27 ++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/framework/src/org/apache/cordova/CameraLauncher.java b/framework/src/org/apache/cordova/CameraLauncher.java index f2c3e12d..d9467fbc 100755 --- a/framework/src/org/apache/cordova/CameraLauncher.java +++ b/framework/src/org/apache/cordova/CameraLauncher.java @@ -338,6 +338,7 @@ public class CameraLauncher extends Plugin implements MediaScannerConnectionClie ContentValues values = new ContentValues(); values.put(android.provider.MediaStore.Images.Media.MIME_TYPE, "image/jpeg"); + try { this.imageUri = this.cordova.getActivity().getContentResolver().insert(android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values); } catch (UnsupportedOperationException e) { @@ -350,6 +351,22 @@ public class CameraLauncher extends Plugin implements MediaScannerConnectionClie return; } } + if (!this.saveToPhotoAlbum) { + File tempFile = new File(this.imageUri.toString()); + Uri jailURI = Uri.fromFile(new File("/data/data/" + this.cordova.getActivity().getPackageName() + "/", tempFile.getName())); + + // Clean up initial URI before writing out safe URI + boolean didWeDeleteIt = tempFile.delete(); + if (!didWeDeleteIt) { + int result = this.cordova.getActivity().getContentResolver().delete( + MediaStore.Images.Media.EXTERNAL_CONTENT_URI, + MediaStore.Images.Media.DATA + " = ?", + new String[] { this.imageUri.toString() } + ); + LOG.d("TAG!","result is " + result); + } + this.imageUri = jailURI; + } // If all this is true we shouldn't compress the image. if (this.targetHeight == -1 && this.targetWidth == -1 && this.mQuality == 100) { @@ -378,10 +395,18 @@ public class CameraLauncher extends Plugin implements MediaScannerConnectionClie os.close(); // Restore exif data to file + if (this.encodingType == JPEG) { - exif.createOutFile(FileUtils.getRealPathFromURI(this.imageUri, this.cordova)); + String exifPath; + if (this.saveToPhotoAlbum) { + exifPath = FileUtils.getRealPathFromURI(this.imageUri, this.cordova); + } else { + exifPath = this.imageUri.toString(); + } + exif.createOutFile(exifPath); exif.writeExifData(); } + // Scan for the gallery to update pic refs in gallery this.scanForGallery(); From 66872de8e53e837b1f5e6472e67e134e9c53eace Mon Sep 17 00:00:00 2001 From: Fil Maj Date: Wed, 20 Jun 2012 14:38:22 -0700 Subject: [PATCH 12/25] Tacked on file extension to camera file --- framework/src/org/apache/cordova/CameraLauncher.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/framework/src/org/apache/cordova/CameraLauncher.java b/framework/src/org/apache/cordova/CameraLauncher.java index d9467fbc..0e86d2c9 100755 --- a/framework/src/org/apache/cordova/CameraLauncher.java +++ b/framework/src/org/apache/cordova/CameraLauncher.java @@ -353,9 +353,10 @@ public class CameraLauncher extends Plugin implements MediaScannerConnectionClie } if (!this.saveToPhotoAlbum) { File tempFile = new File(this.imageUri.toString()); - Uri jailURI = Uri.fromFile(new File("/data/data/" + this.cordova.getActivity().getPackageName() + "/", tempFile.getName())); + Uri jailURI = Uri.fromFile(new File("/data/data/" + this.cordova.getActivity().getPackageName() + "/", tempFile.getName() + "." + (this.encodingType == JPEG ? "jpg" : "png" ))); - // Clean up initial URI before writing out safe URI + // Clean up initial URI before writing out safe URI. + // First try File-based approach to delete. Then use the media delete method. Neither seem to work on ICS right now... boolean didWeDeleteIt = tempFile.delete(); if (!didWeDeleteIt) { int result = this.cordova.getActivity().getContentResolver().delete( @@ -363,7 +364,6 @@ public class CameraLauncher extends Plugin implements MediaScannerConnectionClie MediaStore.Images.Media.DATA + " = ?", new String[] { this.imageUri.toString() } ); - LOG.d("TAG!","result is " + result); } this.imageUri = jailURI; } From ab3347d25d63ae48dafd7f1e705e4edd0a4fa8f3 Mon Sep 17 00:00:00 2001 From: Fil Maj Date: Wed, 20 Jun 2012 15:40:40 -0700 Subject: [PATCH 13/25] added . in front of the temp files passed into camera app. presumably this hsould stop the gallery app from picking it up --- framework/src/org/apache/cordova/CameraLauncher.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/framework/src/org/apache/cordova/CameraLauncher.java b/framework/src/org/apache/cordova/CameraLauncher.java index 0e86d2c9..68389833 100755 --- a/framework/src/org/apache/cordova/CameraLauncher.java +++ b/framework/src/org/apache/cordova/CameraLauncher.java @@ -202,9 +202,9 @@ public class CameraLauncher extends Plugin implements MediaScannerConnectionClie private File createCaptureFile(int encodingType) { File photo = null; if (encodingType == JPEG) { - photo = new File(DirectoryManager.getTempDirectoryPath(this.cordova.getActivity()), "Pic.jpg"); + photo = new File(DirectoryManager.getTempDirectoryPath(this.cordova.getActivity()), ".Pic.jpg"); } else if (encodingType == PNG) { - photo = new File(DirectoryManager.getTempDirectoryPath(this.cordova.getActivity()), "Pic.png"); + photo = new File(DirectoryManager.getTempDirectoryPath(this.cordova.getActivity()), ".Pic.png"); } else { throw new IllegalArgumentException("Invalid Encoding Type: " + encodingType); } @@ -310,7 +310,7 @@ public class CameraLauncher extends Plugin implements MediaScannerConnectionClie ExifHelper exif = new ExifHelper(); try { if (this.encodingType == JPEG) { - exif.createInFile(DirectoryManager.getTempDirectoryPath(this.cordova.getActivity()) + "/Pic.jpg"); + exif.createInFile(DirectoryManager.getTempDirectoryPath(this.cordova.getActivity()) + "/.Pic.jpg"); exif.readExifData(); } } catch (IOException e) { From b22c0e5b6d71aefd798ededb4f7781c2d3a2278b Mon Sep 17 00:00:00 2001 From: Fil Maj Date: Thu, 21 Jun 2012 12:08:07 -0700 Subject: [PATCH 14/25] Fixed the 0-byte files in gallery. Also fixed exif rewriter for saveToPhotoAlbum:false JPG files. Thanks for your help Simon! --- .../org/apache/cordova/CameraLauncher.java | 40 +++++++++---------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/framework/src/org/apache/cordova/CameraLauncher.java b/framework/src/org/apache/cordova/CameraLauncher.java index 68389833..d3938ffd 100755 --- a/framework/src/org/apache/cordova/CameraLauncher.java +++ b/framework/src/org/apache/cordova/CameraLauncher.java @@ -333,27 +333,9 @@ public class CameraLauncher extends Plugin implements MediaScannerConnectionClie // If sending filename back else if (destType == FILE_URI) { - // 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"); - - - try { - this.imageUri = 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 { - this.imageUri = 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.failPicture("Error capturing image - no media storage found."); - return; - } - } if (!this.saveToPhotoAlbum) { File tempFile = new File(this.imageUri.toString()); - Uri jailURI = Uri.fromFile(new File("/data/data/" + this.cordova.getActivity().getPackageName() + "/", tempFile.getName() + "." + (this.encodingType == JPEG ? "jpg" : "png" ))); + Uri jailURI = Uri.fromFile(new File("/data/data/" + this.cordova.getActivity().getPackageName() + "/", tempFile.getName())); // Clean up initial URI before writing out safe URI. // First try File-based approach to delete. Then use the media delete method. Neither seem to work on ICS right now... @@ -366,6 +348,24 @@ public class CameraLauncher extends Plugin implements MediaScannerConnectionClie ); } this.imageUri = jailURI; + } else { + // 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"); + + try { + this.imageUri = 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 { + this.imageUri = 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.failPicture("Error capturing image - no media storage found."); + return; + } + } } // If all this is true we shouldn't compress the image. @@ -401,7 +401,7 @@ public class CameraLauncher extends Plugin implements MediaScannerConnectionClie if (this.saveToPhotoAlbum) { exifPath = FileUtils.getRealPathFromURI(this.imageUri, this.cordova); } else { - exifPath = this.imageUri.toString(); + exifPath = this.imageUri.getPath(); } exif.createOutFile(exifPath); exif.writeExifData(); From 94568a4ec87219401711bc42e4f549b3bd49e3f9 Mon Sep 17 00:00:00 2001 From: Fil Maj Date: Thu, 21 Jun 2012 12:37:44 -0700 Subject: [PATCH 15/25] Merging in use of uri variable between Simon and my changes. --- .../src/org/apache/cordova/CameraLauncher.java | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/framework/src/org/apache/cordova/CameraLauncher.java b/framework/src/org/apache/cordova/CameraLauncher.java index d3938ffd..54ab9bbb 100755 --- a/framework/src/org/apache/cordova/CameraLauncher.java +++ b/framework/src/org/apache/cordova/CameraLauncher.java @@ -333,9 +333,10 @@ public class CameraLauncher extends Plugin implements MediaScannerConnectionClie // If sending filename back else if (destType == FILE_URI) { + Uri uri; if (!this.saveToPhotoAlbum) { File tempFile = new File(this.imageUri.toString()); - Uri jailURI = Uri.fromFile(new File("/data/data/" + this.cordova.getActivity().getPackageName() + "/", tempFile.getName())); + uri = Uri.fromFile(new File("/data/data/" + this.cordova.getActivity().getPackageName() + "/", tempFile.getName())); // Clean up initial URI before writing out safe URI. // First try File-based approach to delete. Then use the media delete method. Neither seem to work on ICS right now... @@ -347,7 +348,6 @@ public class CameraLauncher extends Plugin implements MediaScannerConnectionClie new String[] { this.imageUri.toString() } ); } - this.imageUri = jailURI; } else { // Create entry in media store for image // (Don't use insertImage() because it uses default compression setting of 50 - no way to change it) @@ -355,11 +355,11 @@ public class CameraLauncher extends Plugin implements MediaScannerConnectionClie values.put(android.provider.MediaStore.Images.Media.MIME_TYPE, "image/jpeg"); try { - this.imageUri = this.cordova.getActivity().getContentResolver().insert(android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values); + 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 { - this.imageUri = this.cordova.getActivity().getContentResolver().insert(android.provider.MediaStore.Images.Media.INTERNAL_CONTENT_URI, values); + 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.failPicture("Error capturing image - no media storage found."); @@ -390,7 +390,7 @@ public class CameraLauncher extends Plugin implements MediaScannerConnectionClie bitmap = scaleBitmap(getBitmapFromResult(intent)); // Add compressed version of captured image to returned media store Uri - OutputStream os = this.cordova.getActivity().getContentResolver().openOutputStream(this.imageUri); + OutputStream os = this.cordova.getActivity().getContentResolver().openOutputStream(uri); bitmap.compress(Bitmap.CompressFormat.JPEG, this.mQuality, os); os.close(); @@ -399,9 +399,9 @@ public class CameraLauncher extends Plugin implements MediaScannerConnectionClie if (this.encodingType == JPEG) { String exifPath; if (this.saveToPhotoAlbum) { - exifPath = FileUtils.getRealPathFromURI(this.imageUri, this.cordova); + exifPath = FileUtils.getRealPathFromURI(uri, this.cordova); } else { - exifPath = this.imageUri.getPath(); + exifPath = uri.getPath(); } exif.createOutFile(exifPath); exif.writeExifData(); @@ -412,7 +412,7 @@ public class CameraLauncher extends Plugin implements MediaScannerConnectionClie this.scanForGallery(); // Send Uri back to JavaScript for viewing image - this.success(new PluginResult(PluginResult.Status.OK, this.imageUri.toString()), this.callbackId); + this.success(new PluginResult(PluginResult.Status.OK, uri.toString()), this.callbackId); } bitmap.recycle(); bitmap = null; From 574731b853087f2044136fb728fac36eb1cbd4ff Mon Sep 17 00:00:00 2001 From: Joe Bowser Date: Thu, 21 Jun 2012 11:12:30 -0700 Subject: [PATCH 16/25] Adding updated JS --- framework/assets/js/cordova.android.js | 83 +++++++++++--------------- 1 file changed, 36 insertions(+), 47 deletions(-) diff --git a/framework/assets/js/cordova.android.js b/framework/assets/js/cordova.android.js index 16dab290..ae20b290 100644 --- a/framework/assets/js/cordova.android.js +++ b/framework/assets/js/cordova.android.js @@ -1,6 +1,6 @@ -// commit 347de1a785b7ecbe86c2189343ab2549a9297f9b +// commit 25033fceac7c800623f1f16881b784d19eba69cc -// File generated at :: Fri Jun 08 2012 16:17:50 GMT-0700 (PDT) +// File generated at :: Thu Jun 21 2012 10:45:32 GMT-0700 (PDT) /* Licensed to the Apache Software Foundation (ASF) under one @@ -190,7 +190,9 @@ var cordova = { fireDocumentEvent: function(type, data) { var evt = createEvent(type, data); if (typeof documentEventHandlers[type] != 'undefined') { - documentEventHandlers[type].fire(evt); + setTimeout(function() { + documentEventHandlers[type].fire(evt); + }, 0); } else { document.dispatchEvent(evt); } @@ -198,7 +200,9 @@ var cordova = { fireWindowEvent: function(type, data) { var evt = createEvent(type,data); if (typeof windowEventHandlers[type] != 'undefined') { - windowEventHandlers[type].fire(evt); + setTimeout(function() { + windowEventHandlers[type].fire(evt); + }, 0); } else { window.dispatchEvent(evt); } @@ -953,8 +957,7 @@ module.exports = function(success, fail, service, action, args) { // If a result was returned if (r.length > 0) { - var v; - eval("v="+r+";"); + var v = JSON.parse(r); // If status is OK, then return value back to caller if (v.status === cordova.callbackStatus.OK) { @@ -1067,28 +1070,6 @@ module.exports = { cordova.addDocumentEventHandler('menubutton'); cordova.addDocumentEventHandler('searchbutton'); - function bindButtonChannel(buttonName) { - // generic button bind used for volumeup/volumedown buttons - return cordova.addDocumentEventHandler(buttonName + 'button', { - onSubscribe:function() { - // If we just attached the first handler, let native know we need to override the button. - if (this.numHandlers === 1) { - exec(null, null, "App", "overrideButton", [buttonName, true]); - } - }, - onUnsubscribe:function() { - // If we just detached the last handler, let native know we no longer override the volumeup button. - if (this.numHandlers === 0) { - exec(null, null, "App", "overrideButton", [buttonName, false]); - } - } - }); - - } - // Inject a listener for the volume buttons on the document. - var volumeUpButtonChannel = bindButtonChannel('volumeup'); - var volumeDownButtonChannel = bindButtonChannel('volumedown'); - // Figure out if we need to shim-in localStorage and WebSQL // support from the native side. var storage = require('cordova/plugin/android/storage'); @@ -1308,7 +1289,7 @@ cameraExport.getPicture = function(successCallback, errorCallback, options) { cameraExport.cleanup = function(successCallback, errorCallback) { exec(successCallback, errorCallback, "Camera", "cleanup", []); -} +}; module.exports = cameraExport; }); @@ -2628,7 +2609,8 @@ module.exports = FileSystem; // file: lib/common/plugin/FileTransfer.js define("cordova/plugin/FileTransfer", function(require, exports, module) { -var exec = require('cordova/exec'); +var exec = require('cordova/exec'), + FileTransferError = require('cordova/plugin/FileTransferError'); /** * FileTransfer uploads a file to a remote server. @@ -2647,6 +2629,8 @@ var FileTransfer = function() {}; * @param trustAllHosts {Boolean} Optional trust all hosts (e.g. for self-signed certs), defaults to false */ FileTransfer.prototype.upload = function(filePath, server, successCallback, errorCallback, options, trustAllHosts) { + // sanity parameter checking + if (!filePath || !server) throw new Error("FileTransfer.upload requires filePath and server URL parameters at the minimum."); // check for options var fileKey = null; var fileName = null; @@ -2668,7 +2652,12 @@ FileTransfer.prototype.upload = function(filePath, server, successCallback, erro } } - exec(successCallback, errorCallback, 'FileTransfer', 'upload', [filePath, server, fileKey, fileName, mimeType, params, trustAllHosts, chunkedMode]); + var fail = function(e) { + var error = new FileTransferError(e.code, e.source, e.target, e.http_status); + errorCallback(error); + }; + + exec(successCallback, fail, 'FileTransfer', 'upload', [filePath, server, fileKey, fileName, mimeType, params, trustAllHosts, chunkedMode]); }; /** @@ -2679,6 +2668,8 @@ FileTransfer.prototype.upload = function(filePath, server, successCallback, erro * @param errorCallback {Function} Callback to be invoked upon error */ FileTransfer.prototype.download = function(source, target, successCallback, errorCallback) { + // sanity parameter checking + if (!source || !target) throw new Error("FileTransfer.download requires source URI and target URI parameters at the minimum."); var win = function(result) { var entry = null; if (result.isDirectory) { @@ -2693,6 +2684,12 @@ FileTransfer.prototype.download = function(source, target, successCallback, erro entry.fullPath = result.fullPath; successCallback(entry); }; + + var fail = function(e) { + var error = new FileTransferError(e.code, e.source, e.target, e.http_status); + errorCallback(error); + }; + exec(win, errorCallback, 'FileTransfer', 'download', [source, target]); }; @@ -2706,8 +2703,11 @@ define("cordova/plugin/FileTransferError", function(require, exports, module) { * FileTransferError * @constructor */ -var FileTransferError = function(code) { +var FileTransferError = function(code, source, target, status) { this.code = code || null; + this.source = source || null; + this.target = target || null; + this.http_status = status || null; }; FileTransferError.FILE_NOT_FOUND_ERR = 1; @@ -2715,6 +2715,7 @@ FileTransferError.INVALID_URL_ERR = 2; FileTransferError.CONNECTION_ERR = 3; module.exports = FileTransferError; + }); // file: lib/common/plugin/FileUploadOptions.js @@ -3665,21 +3666,6 @@ module.exports = { exec(null, null, "App", "overrideBackbutton", [override]); }, - /** - * Override the default behavior of the Android volume button. - * If overridden, when the volume button is pressed, the "volume[up|down]button" JavaScript event will be fired. - * - * Note: The user should not have to call this method. Instead, when the user - * registers for the "volume[up|down]button" event, this is automatically done. - * - * @param button volumeup, volumedown - * @param override T=override, F=cancel override - */ - overrideButton:function(button, override) { - exec(null, null, "App", "overrideButton", [button, override]); - }, - - /** * Exit and terminate the application. */ @@ -5509,6 +5495,9 @@ define("cordova/plugin/splashscreen", function(require, exports, module) { var exec = require('cordova/exec'); var splashscreen = { + show:function() { + exec(null, null, "SplashScreen", "show", []); + }, hide:function() { exec(null, null, "SplashScreen", "hide", []); } From c183d06ed1f48245f7d4ed2aceecdc63f4638eae Mon Sep 17 00:00:00 2001 From: Fil Maj Date: Tue, 19 Jun 2012 17:14:05 -0700 Subject: [PATCH 17/25] Added MediaScanner abilities to camera launcher plugin. Now images saved to SD card should show up in the android gallery app right away --- .../org/apache/cordova/CameraLauncher.java | 44 ++++++++++++++++--- 1 file changed, 37 insertions(+), 7 deletions(-) diff --git a/framework/src/org/apache/cordova/CameraLauncher.java b/framework/src/org/apache/cordova/CameraLauncher.java index e9f4ea8f..ed0f928c 100755 --- a/framework/src/org/apache/cordova/CameraLauncher.java +++ b/framework/src/org/apache/cordova/CameraLauncher.java @@ -42,6 +42,8 @@ import android.database.Cursor; import android.graphics.Bitmap; import android.graphics.Matrix; import android.graphics.Bitmap.CompressFormat; +import android.media.MediaScannerConnection; +import android.media.MediaScannerConnection.MediaScannerConnectionClient; import android.net.Uri; import android.os.Environment; import android.provider.MediaStore; @@ -52,7 +54,7 @@ import android.util.Log; * and returns the captured image. When the camera view is closed, the screen displayed before * the camera view was shown is redisplayed. */ -public class CameraLauncher extends Plugin { +public class CameraLauncher extends Plugin implements MediaScannerConnectionClient { private static final int DATA_URL = 0; // Return base64 encoded string private static final int FILE_URI = 1; // Return file uri (content://media/external/images/media/2 for Android) @@ -82,6 +84,8 @@ public class CameraLauncher extends Plugin { public String callbackId; private int numPics; + + private MediaScannerConnection conn; // Used to update gallery app with newly-written files //This should never be null! //private CordovaInterface cordova; @@ -330,13 +334,13 @@ public class CameraLauncher extends Plugin { // (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); + this.imageUri = 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); + this.imageUri = 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.failPicture("Error capturing image - no media storage found."); @@ -366,24 +370,28 @@ public class CameraLauncher extends Plugin { bitmap = scaleBitmap(getBitmapFromResult(intent)); // Add compressed version of captured image to returned media store Uri - OutputStream os = this.cordova.getActivity().getContentResolver().openOutputStream(uri); + OutputStream os = this.cordova.getActivity().getContentResolver().openOutputStream(this.imageUri); bitmap.compress(Bitmap.CompressFormat.JPEG, this.mQuality, os); os.close(); // Restore exif data to file if (this.encodingType == JPEG) { - exif.createOutFile(FileUtils.getRealPathFromURI(uri, this.cordova)); + exif.createOutFile(FileUtils.getRealPathFromURI(this.imageUri, this.cordova)); exif.writeExifData(); } + // Scan for the gallery to update pic refs in gallery + this.scanForGallery(); + // Send Uri back to JavaScript for viewing image - this.success(new PluginResult(PluginResult.Status.OK, uri.toString()), this.callbackId); + this.success(new PluginResult(PluginResult.Status.OK, this.imageUri.toString()), this.callbackId); } bitmap.recycle(); bitmap = null; System.gc(); checkForDuplicateImage(FILE_URI); + } catch (IOException e) { e.printStackTrace(); this.failPicture("Error capturing image."); @@ -584,4 +592,26 @@ public class CameraLauncher extends Plugin { public void failPicture(String err) { this.error(new PluginResult(PluginResult.Status.ERROR, err), this.callbackId); } + + private void scanForGallery() { + if(this.conn!=null) this.conn.disconnect(); + this.conn = new MediaScannerConnection(this.ctx.getActivity().getApplicationContext(), this); + conn.connect(); + } + + @Override + public void onMediaScannerConnected() { + try{ + this.conn.scanFile(this.imageUri.toString(), "image/*"); + } catch (java.lang.IllegalStateException e){ + e.printStackTrace(); + LOG.d(LOG_TAG, "Can;t scan file in MediaScanner aftering taking picture"); + } + + } + + @Override + public void onScanCompleted(String path, Uri uri) { + this.conn.disconnect(); + } } From 6c465e25d361f5ef5f61a818507b7fc9166ea49e Mon Sep 17 00:00:00 2001 From: Fil Maj Date: Wed, 20 Jun 2012 11:10:43 -0700 Subject: [PATCH 18/25] merge!!!!11one --- framework/src/org/apache/cordova/CameraLauncher.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/framework/src/org/apache/cordova/CameraLauncher.java b/framework/src/org/apache/cordova/CameraLauncher.java index ed0f928c..f2c3e12d 100755 --- a/framework/src/org/apache/cordova/CameraLauncher.java +++ b/framework/src/org/apache/cordova/CameraLauncher.java @@ -81,6 +81,7 @@ public class CameraLauncher extends Plugin implements MediaScannerConnectionClie private Uri imageUri; // Uri of captured image private int encodingType; // Type of encoding to use private int mediaType; // What type of media to retrieve + private boolean saveToPhotoAlbum; // Should the picture be saved to the device's photo album public String callbackId; private int numPics; @@ -121,6 +122,7 @@ public class CameraLauncher extends Plugin implements MediaScannerConnectionClie if (action.equals("takePicture")) { int srcType = CAMERA; int destType = FILE_URI; + this.saveToPhotoAlbum = false; this.targetHeight = 0; this.targetWidth = 0; this.encodingType = JPEG; @@ -134,6 +136,7 @@ public class CameraLauncher extends Plugin implements MediaScannerConnectionClie this.targetHeight = args.getInt(4); this.encodingType = args.getInt(5); this.mediaType = args.getInt(6); + this.saveToPhotoAlbum = args.getBoolean(9); if (srcType == CAMERA) { this.takePicture(destType, encodingType); From 167b60013536dd20f74fb7041fee52e4278c3a99 Mon Sep 17 00:00:00 2001 From: Fil Maj Date: Wed, 20 Jun 2012 14:12:06 -0700 Subject: [PATCH 19/25] Removing images and saving images to jail if SaveToPhotoAlbum is set to true --- .../org/apache/cordova/CameraLauncher.java | 27 ++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/framework/src/org/apache/cordova/CameraLauncher.java b/framework/src/org/apache/cordova/CameraLauncher.java index f2c3e12d..d9467fbc 100755 --- a/framework/src/org/apache/cordova/CameraLauncher.java +++ b/framework/src/org/apache/cordova/CameraLauncher.java @@ -338,6 +338,7 @@ public class CameraLauncher extends Plugin implements MediaScannerConnectionClie ContentValues values = new ContentValues(); values.put(android.provider.MediaStore.Images.Media.MIME_TYPE, "image/jpeg"); + try { this.imageUri = this.cordova.getActivity().getContentResolver().insert(android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values); } catch (UnsupportedOperationException e) { @@ -350,6 +351,22 @@ public class CameraLauncher extends Plugin implements MediaScannerConnectionClie return; } } + if (!this.saveToPhotoAlbum) { + File tempFile = new File(this.imageUri.toString()); + Uri jailURI = Uri.fromFile(new File("/data/data/" + this.cordova.getActivity().getPackageName() + "/", tempFile.getName())); + + // Clean up initial URI before writing out safe URI + boolean didWeDeleteIt = tempFile.delete(); + if (!didWeDeleteIt) { + int result = this.cordova.getActivity().getContentResolver().delete( + MediaStore.Images.Media.EXTERNAL_CONTENT_URI, + MediaStore.Images.Media.DATA + " = ?", + new String[] { this.imageUri.toString() } + ); + LOG.d("TAG!","result is " + result); + } + this.imageUri = jailURI; + } // If all this is true we shouldn't compress the image. if (this.targetHeight == -1 && this.targetWidth == -1 && this.mQuality == 100) { @@ -378,10 +395,18 @@ public class CameraLauncher extends Plugin implements MediaScannerConnectionClie os.close(); // Restore exif data to file + if (this.encodingType == JPEG) { - exif.createOutFile(FileUtils.getRealPathFromURI(this.imageUri, this.cordova)); + String exifPath; + if (this.saveToPhotoAlbum) { + exifPath = FileUtils.getRealPathFromURI(this.imageUri, this.cordova); + } else { + exifPath = this.imageUri.toString(); + } + exif.createOutFile(exifPath); exif.writeExifData(); } + // Scan for the gallery to update pic refs in gallery this.scanForGallery(); From 7c67f40fc45dce040608e6d96efa654c7b93ce97 Mon Sep 17 00:00:00 2001 From: Fil Maj Date: Wed, 20 Jun 2012 14:38:22 -0700 Subject: [PATCH 20/25] Tacked on file extension to camera file --- framework/src/org/apache/cordova/CameraLauncher.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/framework/src/org/apache/cordova/CameraLauncher.java b/framework/src/org/apache/cordova/CameraLauncher.java index d9467fbc..0e86d2c9 100755 --- a/framework/src/org/apache/cordova/CameraLauncher.java +++ b/framework/src/org/apache/cordova/CameraLauncher.java @@ -353,9 +353,10 @@ public class CameraLauncher extends Plugin implements MediaScannerConnectionClie } if (!this.saveToPhotoAlbum) { File tempFile = new File(this.imageUri.toString()); - Uri jailURI = Uri.fromFile(new File("/data/data/" + this.cordova.getActivity().getPackageName() + "/", tempFile.getName())); + Uri jailURI = Uri.fromFile(new File("/data/data/" + this.cordova.getActivity().getPackageName() + "/", tempFile.getName() + "." + (this.encodingType == JPEG ? "jpg" : "png" ))); - // Clean up initial URI before writing out safe URI + // Clean up initial URI before writing out safe URI. + // First try File-based approach to delete. Then use the media delete method. Neither seem to work on ICS right now... boolean didWeDeleteIt = tempFile.delete(); if (!didWeDeleteIt) { int result = this.cordova.getActivity().getContentResolver().delete( @@ -363,7 +364,6 @@ public class CameraLauncher extends Plugin implements MediaScannerConnectionClie MediaStore.Images.Media.DATA + " = ?", new String[] { this.imageUri.toString() } ); - LOG.d("TAG!","result is " + result); } this.imageUri = jailURI; } From 5143b8a492b24abf0f668014339e8246927e163e Mon Sep 17 00:00:00 2001 From: Fil Maj Date: Wed, 20 Jun 2012 15:40:40 -0700 Subject: [PATCH 21/25] added . in front of the temp files passed into camera app. presumably this hsould stop the gallery app from picking it up --- framework/src/org/apache/cordova/CameraLauncher.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/framework/src/org/apache/cordova/CameraLauncher.java b/framework/src/org/apache/cordova/CameraLauncher.java index 0e86d2c9..68389833 100755 --- a/framework/src/org/apache/cordova/CameraLauncher.java +++ b/framework/src/org/apache/cordova/CameraLauncher.java @@ -202,9 +202,9 @@ public class CameraLauncher extends Plugin implements MediaScannerConnectionClie private File createCaptureFile(int encodingType) { File photo = null; if (encodingType == JPEG) { - photo = new File(DirectoryManager.getTempDirectoryPath(this.cordova.getActivity()), "Pic.jpg"); + photo = new File(DirectoryManager.getTempDirectoryPath(this.cordova.getActivity()), ".Pic.jpg"); } else if (encodingType == PNG) { - photo = new File(DirectoryManager.getTempDirectoryPath(this.cordova.getActivity()), "Pic.png"); + photo = new File(DirectoryManager.getTempDirectoryPath(this.cordova.getActivity()), ".Pic.png"); } else { throw new IllegalArgumentException("Invalid Encoding Type: " + encodingType); } @@ -310,7 +310,7 @@ public class CameraLauncher extends Plugin implements MediaScannerConnectionClie ExifHelper exif = new ExifHelper(); try { if (this.encodingType == JPEG) { - exif.createInFile(DirectoryManager.getTempDirectoryPath(this.cordova.getActivity()) + "/Pic.jpg"); + exif.createInFile(DirectoryManager.getTempDirectoryPath(this.cordova.getActivity()) + "/.Pic.jpg"); exif.readExifData(); } } catch (IOException e) { From 9d1edc4554ef8d1cece0daea18ee51b41a157802 Mon Sep 17 00:00:00 2001 From: Fil Maj Date: Thu, 21 Jun 2012 12:08:07 -0700 Subject: [PATCH 22/25] Fixed the 0-byte files in gallery. Also fixed exif rewriter for saveToPhotoAlbum:false JPG files. Thanks for your help Simon! --- .../org/apache/cordova/CameraLauncher.java | 40 +++++++++---------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/framework/src/org/apache/cordova/CameraLauncher.java b/framework/src/org/apache/cordova/CameraLauncher.java index 68389833..d3938ffd 100755 --- a/framework/src/org/apache/cordova/CameraLauncher.java +++ b/framework/src/org/apache/cordova/CameraLauncher.java @@ -333,27 +333,9 @@ public class CameraLauncher extends Plugin implements MediaScannerConnectionClie // If sending filename back else if (destType == FILE_URI) { - // 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"); - - - try { - this.imageUri = 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 { - this.imageUri = 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.failPicture("Error capturing image - no media storage found."); - return; - } - } if (!this.saveToPhotoAlbum) { File tempFile = new File(this.imageUri.toString()); - Uri jailURI = Uri.fromFile(new File("/data/data/" + this.cordova.getActivity().getPackageName() + "/", tempFile.getName() + "." + (this.encodingType == JPEG ? "jpg" : "png" ))); + Uri jailURI = Uri.fromFile(new File("/data/data/" + this.cordova.getActivity().getPackageName() + "/", tempFile.getName())); // Clean up initial URI before writing out safe URI. // First try File-based approach to delete. Then use the media delete method. Neither seem to work on ICS right now... @@ -366,6 +348,24 @@ public class CameraLauncher extends Plugin implements MediaScannerConnectionClie ); } this.imageUri = jailURI; + } else { + // 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"); + + try { + this.imageUri = 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 { + this.imageUri = 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.failPicture("Error capturing image - no media storage found."); + return; + } + } } // If all this is true we shouldn't compress the image. @@ -401,7 +401,7 @@ public class CameraLauncher extends Plugin implements MediaScannerConnectionClie if (this.saveToPhotoAlbum) { exifPath = FileUtils.getRealPathFromURI(this.imageUri, this.cordova); } else { - exifPath = this.imageUri.toString(); + exifPath = this.imageUri.getPath(); } exif.createOutFile(exifPath); exif.writeExifData(); From f3f2ad9144950ff4ec414e02a4d0927b973c8502 Mon Sep 17 00:00:00 2001 From: Fil Maj Date: Thu, 21 Jun 2012 12:37:44 -0700 Subject: [PATCH 23/25] Merging in use of uri variable between Simon and my changes. --- .../src/org/apache/cordova/CameraLauncher.java | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/framework/src/org/apache/cordova/CameraLauncher.java b/framework/src/org/apache/cordova/CameraLauncher.java index d3938ffd..54ab9bbb 100755 --- a/framework/src/org/apache/cordova/CameraLauncher.java +++ b/framework/src/org/apache/cordova/CameraLauncher.java @@ -333,9 +333,10 @@ public class CameraLauncher extends Plugin implements MediaScannerConnectionClie // If sending filename back else if (destType == FILE_URI) { + Uri uri; if (!this.saveToPhotoAlbum) { File tempFile = new File(this.imageUri.toString()); - Uri jailURI = Uri.fromFile(new File("/data/data/" + this.cordova.getActivity().getPackageName() + "/", tempFile.getName())); + uri = Uri.fromFile(new File("/data/data/" + this.cordova.getActivity().getPackageName() + "/", tempFile.getName())); // Clean up initial URI before writing out safe URI. // First try File-based approach to delete. Then use the media delete method. Neither seem to work on ICS right now... @@ -347,7 +348,6 @@ public class CameraLauncher extends Plugin implements MediaScannerConnectionClie new String[] { this.imageUri.toString() } ); } - this.imageUri = jailURI; } else { // Create entry in media store for image // (Don't use insertImage() because it uses default compression setting of 50 - no way to change it) @@ -355,11 +355,11 @@ public class CameraLauncher extends Plugin implements MediaScannerConnectionClie values.put(android.provider.MediaStore.Images.Media.MIME_TYPE, "image/jpeg"); try { - this.imageUri = this.cordova.getActivity().getContentResolver().insert(android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values); + 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 { - this.imageUri = this.cordova.getActivity().getContentResolver().insert(android.provider.MediaStore.Images.Media.INTERNAL_CONTENT_URI, values); + 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.failPicture("Error capturing image - no media storage found."); @@ -390,7 +390,7 @@ public class CameraLauncher extends Plugin implements MediaScannerConnectionClie bitmap = scaleBitmap(getBitmapFromResult(intent)); // Add compressed version of captured image to returned media store Uri - OutputStream os = this.cordova.getActivity().getContentResolver().openOutputStream(this.imageUri); + OutputStream os = this.cordova.getActivity().getContentResolver().openOutputStream(uri); bitmap.compress(Bitmap.CompressFormat.JPEG, this.mQuality, os); os.close(); @@ -399,9 +399,9 @@ public class CameraLauncher extends Plugin implements MediaScannerConnectionClie if (this.encodingType == JPEG) { String exifPath; if (this.saveToPhotoAlbum) { - exifPath = FileUtils.getRealPathFromURI(this.imageUri, this.cordova); + exifPath = FileUtils.getRealPathFromURI(uri, this.cordova); } else { - exifPath = this.imageUri.getPath(); + exifPath = uri.getPath(); } exif.createOutFile(exifPath); exif.writeExifData(); @@ -412,7 +412,7 @@ public class CameraLauncher extends Plugin implements MediaScannerConnectionClie this.scanForGallery(); // Send Uri back to JavaScript for viewing image - this.success(new PluginResult(PluginResult.Status.OK, this.imageUri.toString()), this.callbackId); + this.success(new PluginResult(PluginResult.Status.OK, uri.toString()), this.callbackId); } bitmap.recycle(); bitmap = null; From 5ca4b4a8849a54edfbe61abcdd3bfe0f1db623f0 Mon Sep 17 00:00:00 2001 From: Fil Maj Date: Thu, 21 Jun 2012 14:22:09 -0700 Subject: [PATCH 24/25] Refactored cleanup in camera code a bit. Removed overrides for Scanner functionality --- .../org/apache/cordova/CameraLauncher.java | 84 +++++++++---------- 1 file changed, 39 insertions(+), 45 deletions(-) diff --git a/framework/src/org/apache/cordova/CameraLauncher.java b/framework/src/org/apache/cordova/CameraLauncher.java index 54ab9bbb..3b720b71 100755 --- a/framework/src/org/apache/cordova/CameraLauncher.java +++ b/framework/src/org/apache/cordova/CameraLauncher.java @@ -335,19 +335,7 @@ public class CameraLauncher extends Plugin implements MediaScannerConnectionClie else if (destType == FILE_URI) { Uri uri; if (!this.saveToPhotoAlbum) { - File tempFile = new File(this.imageUri.toString()); - uri = Uri.fromFile(new File("/data/data/" + this.cordova.getActivity().getPackageName() + "/", tempFile.getName())); - - // Clean up initial URI before writing out safe URI. - // First try File-based approach to delete. Then use the media delete method. Neither seem to work on ICS right now... - boolean didWeDeleteIt = tempFile.delete(); - if (!didWeDeleteIt) { - int result = this.cordova.getActivity().getContentResolver().delete( - MediaStore.Images.Media.EXTERNAL_CONTENT_URI, - MediaStore.Images.Media.DATA + " = ?", - new String[] { this.imageUri.toString() } - ); - } + uri = Uri.fromFile(new File("/data/data/" + this.cordova.getActivity().getPackageName() + "/", (new File(FileUtils.stripFileProtocol(this.imageUri.toString()))).getName())); } else { // Create entry in media store for image // (Don't use insertImage() because it uses default compression setting of 50 - no way to change it) @@ -381,44 +369,36 @@ public class CameraLauncher extends Plugin implements MediaScannerConnectionClie os.close(); fis.close(); - checkForDuplicateImage(FILE_URI); - this.success(new PluginResult(PluginResult.Status.OK, uri.toString()), this.callbackId); - return; - } + } else { - bitmap = scaleBitmap(getBitmapFromResult(intent)); - - // Add compressed version of captured image to returned media store Uri - OutputStream os = this.cordova.getActivity().getContentResolver().openOutputStream(uri); - bitmap.compress(Bitmap.CompressFormat.JPEG, this.mQuality, os); - os.close(); - - // Restore exif data to file - - if (this.encodingType == JPEG) { - String exifPath; - if (this.saveToPhotoAlbum) { - exifPath = FileUtils.getRealPathFromURI(uri, this.cordova); - } else { - exifPath = uri.getPath(); + bitmap = scaleBitmap(getBitmapFromResult(intent)); + + // Add compressed version of captured image to returned media store Uri + OutputStream os = this.cordova.getActivity().getContentResolver().openOutputStream(uri); + bitmap.compress(Bitmap.CompressFormat.JPEG, this.mQuality, os); + os.close(); + + // Restore exif data to file + + if (this.encodingType == JPEG) { + String exifPath; + if (this.saveToPhotoAlbum) { + exifPath = FileUtils.getRealPathFromURI(uri, this.cordova); + } else { + exifPath = uri.getPath(); + } + exif.createOutFile(exifPath); + exif.writeExifData(); } - exif.createOutFile(exifPath); - exif.writeExifData(); + } - - - // Scan for the gallery to update pic refs in gallery - this.scanForGallery(); - // Send Uri back to JavaScript for viewing image this.success(new PluginResult(PluginResult.Status.OK, uri.toString()), this.callbackId); } - bitmap.recycle(); + + this.cleanup(FILE_URI, this.imageUri, bitmap); bitmap = null; - System.gc(); - - checkForDuplicateImage(FILE_URI); } catch (IOException e) { e.printStackTrace(); @@ -550,6 +530,22 @@ public class CameraLauncher extends Plugin implements MediaScannerConnectionClie null, null); } + + /** + * Cleans up after picture taking. Checking for duplicates and that kind of stuff. + */ + private void cleanup(int imageType, Uri oldImage, Bitmap bitmap) { + bitmap.recycle(); + + // Clean up initial camera-written image file. + (new File(FileUtils.stripFileProtocol(oldImage.toString()))).delete(); + + checkForDuplicateImage(imageType); + // Scan for the gallery to update pic refs in gallery + this.scanForGallery(); + + System.gc(); + } /** * Used to find out if we are in a situation where the Camera Intent adds to images @@ -627,7 +623,6 @@ public class CameraLauncher extends Plugin implements MediaScannerConnectionClie conn.connect(); } - @Override public void onMediaScannerConnected() { try{ this.conn.scanFile(this.imageUri.toString(), "image/*"); @@ -638,7 +633,6 @@ public class CameraLauncher extends Plugin implements MediaScannerConnectionClie } - @Override public void onScanCompleted(String path, Uri uri) { this.conn.disconnect(); } From bf0df9f3c3590cb8629fc1558051352a7fe0fc4c Mon Sep 17 00:00:00 2001 From: Joe Bowser Date: Thu, 21 Jun 2012 15:21:56 -0700 Subject: [PATCH 25/25] Since we moved binding of buttons into a view, let's remove it from the Interface --- .../org/apache/cordova/api/CordovaInterface.java | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/framework/src/org/apache/cordova/api/CordovaInterface.java b/framework/src/org/apache/cordova/api/CordovaInterface.java index 64a2743e..480b17d8 100755 --- a/framework/src/org/apache/cordova/api/CordovaInterface.java +++ b/framework/src/org/apache/cordova/api/CordovaInterface.java @@ -44,19 +44,6 @@ public interface CordovaInterface { */ abstract public void setActivityResultCallback(IPlugin plugin); - /** - * Causes the Activity to override the back button behavior. - * - * @param override - */ - public abstract void bindBackButton(boolean override); - - /** - * A hook required to check if the Back Button is bound. - * - * @return - */ - public abstract boolean isBackButtonBound(); /** * Get the Android activity. @@ -77,5 +64,4 @@ public interface CordovaInterface { */ public Object onMessage(String id, Object data); - public abstract void bindButton(String button, boolean override); }