From 240f27ce97eaf1b58dd0b4d411fdd9117a0c2c66 Mon Sep 17 00:00:00 2001 From: Bas Bosman Date: Thu, 15 Jan 2015 14:33:24 +0100 Subject: [PATCH 1/5] CB-8314 Speed up Travis CI (close #148) --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index e9919cdf..1e376701 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,4 +1,5 @@ language: android +sudo: false install: npm install script: - npm test From 11002d4a56a4901087f514e2d01f8db392d0abe1 Mon Sep 17 00:00:00 2001 From: sgrebnov Date: Fri, 16 Jan 2015 13:04:01 +0300 Subject: [PATCH 2/5] CB-8201 Add support for auth dialogs into Cordova Android --- .../cordova/CordovaHttpAuthHandler.java | 51 +++++++++++++++++++ .../src/org/apache/cordova/CordovaPlugin.java | 16 ++++++ .../apache/cordova/CordovaWebViewClient.java | 15 ++++-- .../cordova/ICordovaHttpAuthHandler.java | 38 ++++++++++++++ .../src/org/apache/cordova/PluginManager.java | 21 ++++++++ 5 files changed, 137 insertions(+), 4 deletions(-) create mode 100644 framework/src/org/apache/cordova/CordovaHttpAuthHandler.java create mode 100644 framework/src/org/apache/cordova/ICordovaHttpAuthHandler.java diff --git a/framework/src/org/apache/cordova/CordovaHttpAuthHandler.java b/framework/src/org/apache/cordova/CordovaHttpAuthHandler.java new file mode 100644 index 00000000..724381e2 --- /dev/null +++ b/framework/src/org/apache/cordova/CordovaHttpAuthHandler.java @@ -0,0 +1,51 @@ +/* + Licensed to the Apache Software Foundation (ASF) under one + or more contributor license agreements. See the NOTICE file + distributed with this work for additional information + regarding copyright ownership. The ASF licenses this file + to you under the Apache License, Version 2.0 (the + "License"); you may not use this file except in compliance + with the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, + software distributed under the License is distributed on an + "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + KIND, either express or implied. See the License for the + specific language governing permissions and limitations + under the License. +*/ +package org.apache.cordova; + +import android.webkit.HttpAuthHandler; + +/** + * Specifies interface for HTTP auth handler object which is used to handle auth requests and + * specifying user credentials. + */ +public class CordovaHttpAuthHandler implements ICordovaHttpAuthHandler { + + private final HttpAuthHandler handler; + + public CordovaHttpAuthHandler(HttpAuthHandler handler) { + this.handler = handler; + } + + /** + * Instructs the WebView to cancel the authentication request. + */ + public void cancel () { + this.handler.cancel(); + } + + /** + * Instructs the WebView to proceed with the authentication with the given credentials. + * + * @param username + * @param password + */ + public void proceed (String username, String password) { + this.handler.proceed(username, password); + } +} diff --git a/framework/src/org/apache/cordova/CordovaPlugin.java b/framework/src/org/apache/cordova/CordovaPlugin.java index a68d3d7e..0367fc63 100644 --- a/framework/src/org/apache/cordova/CordovaPlugin.java +++ b/framework/src/org/apache/cordova/CordovaPlugin.java @@ -198,4 +198,20 @@ public class CordovaPlugin { */ public void onReset() { } + + /** + * Called when the system received an HTTP authentication request. Plugin can use + * the supplied HttpAuthHandler to process this auth challenge. + * + * @param view The WebView that is initiating the callback + * @param handler The HttpAuthHandler used to set the WebView's response + * @param host The host requiring authentication + * @param realm The realm for which authentication is required + * + * @return Returns True if plugin will resolve this auth challenge, otherwise False + * + */ + public boolean onReceivedHttpAuthRequest(CordovaWebView view, ICordovaHttpAuthHandler handler, String host, String realm) { + return false; + } } diff --git a/framework/src/org/apache/cordova/CordovaWebViewClient.java b/framework/src/org/apache/cordova/CordovaWebViewClient.java index f65a9760..2106c501 100755 --- a/framework/src/org/apache/cordova/CordovaWebViewClient.java +++ b/framework/src/org/apache/cordova/CordovaWebViewClient.java @@ -115,15 +115,22 @@ public class CordovaWebViewClient extends WebViewClient { @Override public void onReceivedHttpAuthRequest(WebView view, HttpAuthHandler handler, String host, String realm) { - // Get the authentication token + // Get the authentication token (if specified) AuthenticationToken token = this.getAuthenticationToken(host, realm); if (token != null) { handler.proceed(token.getUserName(), token.getPassword()); + return; } - else { - // Handle 401 like we'd normally do! - super.onReceivedHttpAuthRequest(view, handler, host, realm); + + // Check if there is some plugin which can resolve this auth challenge + PluginManager pluginManager = this.appView.pluginManager; + if (pluginManager != null && pluginManager.onReceivedHttpAuthRequest(this.appView, new CordovaHttpAuthHandler(handler), host, realm)) { + this.appView.loadUrlTimeout++; + return; } + + // By default handle 401 like we'd normally do! + super.onReceivedHttpAuthRequest(view, handler, host, realm); } /** diff --git a/framework/src/org/apache/cordova/ICordovaHttpAuthHandler.java b/framework/src/org/apache/cordova/ICordovaHttpAuthHandler.java new file mode 100644 index 00000000..c55818ac --- /dev/null +++ b/framework/src/org/apache/cordova/ICordovaHttpAuthHandler.java @@ -0,0 +1,38 @@ +/* + Licensed to the Apache Software Foundation (ASF) under one + or more contributor license agreements. See the NOTICE file + distributed with this work for additional information + regarding copyright ownership. The ASF licenses this file + to you under the Apache License, Version 2.0 (the + "License"); you may not use this file except in compliance + with the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, + software distributed under the License is distributed on an + "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + KIND, either express or implied. See the License for the + specific language governing permissions and limitations + under the License. +*/ +package org.apache.cordova; + +/** + * Specifies interface for HTTP auth handler object which is used to handle auth requests and + * specifying user credentials. + */ + public interface ICordovaHttpAuthHandler { + /** + * Instructs the WebView to cancel the authentication request. + */ + public void cancel (); + + /** + * Instructs the WebView to proceed with the authentication with the given credentials. + * + * @param username The user name + * @param password The password + */ + public void proceed (String username, String password); +} \ No newline at end of file diff --git a/framework/src/org/apache/cordova/PluginManager.java b/framework/src/org/apache/cordova/PluginManager.java index 7ddf3005..5bfd65bd 100755 --- a/framework/src/org/apache/cordova/PluginManager.java +++ b/framework/src/org/apache/cordova/PluginManager.java @@ -242,6 +242,27 @@ public class PluginManager { } } + /** + * Called when the system received an HTTP authentication request. Plugins can use + * the supplied HttpAuthHandler to process this auth challenge. + * + * @param view The WebView that is initiating the callback + * @param handler The HttpAuthHandler used to set the WebView's response + * @param host The host requiring authentication + * @param realm The realm for which authentication is required + * + * @return Returns True if there is a plugin which will resolve this auth challenge, otherwise False + * + */ + public boolean onReceivedHttpAuthRequest(CordovaWebView view, ICordovaHttpAuthHandler handler, String host, String realm) { + for (CordovaPlugin plugin : this.pluginMap.values()) { + if (plugin != null && plugin.onReceivedHttpAuthRequest(view, handler, host, realm)) { + return true; + } + } + return false; + } + /** * Called when the activity will start interacting with the user. * From 34c163be88fcc4cde5de50ab7f71f753f5de1012 Mon Sep 17 00:00:00 2001 From: Andrew Grieve Date: Mon, 19 Jan 2015 13:42:11 -0500 Subject: [PATCH 3/5] CB-8280 android: Don't apply SplashScreenDelay when .show() is called explicitly --- .../apache/cordova/SplashScreenInternal.java | 22 ++++++++++--------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/framework/src/org/apache/cordova/SplashScreenInternal.java b/framework/src/org/apache/cordova/SplashScreenInternal.java index 715e4182..e1154a53 100644 --- a/framework/src/org/apache/cordova/SplashScreenInternal.java +++ b/framework/src/org/apache/cordova/SplashScreenInternal.java @@ -63,7 +63,7 @@ public class SplashScreenInternal extends CordovaPlugin { firstShow = false; loadSpinner(); - showSplashScreen(); + showSplashScreen(true); } @Override @@ -115,7 +115,7 @@ public class SplashScreenInternal extends CordovaPlugin { if ("hide".equals(data.toString())) { this.removeSplashScreen(); } else { - this.showSplashScreen(); + this.showSplashScreen(false); } } else if ("spinner".equals(id)) { if ("stop".equals(data.toString())) { @@ -143,7 +143,7 @@ public class SplashScreenInternal extends CordovaPlugin { * Shows the splash screen over the full Activity */ @SuppressWarnings("deprecation") - private void showSplashScreen() { + private void showSplashScreen(final boolean hideAfterDelay) { final int splashscreenTime = preferences.getInteger("SplashScreenDelay", 3000); final int drawableId = preferences.getInteger("SplashDrawableId", 0); @@ -151,7 +151,7 @@ public class SplashScreenInternal extends CordovaPlugin { if (this.splashDialog != null && splashDialog.isShowing()) { return; } - if (drawableId == 0 || splashscreenTime <= 0) { + if (drawableId == 0 || (splashscreenTime <= 0 && hideAfterDelay)) { return; } @@ -187,12 +187,14 @@ public class SplashScreenInternal extends CordovaPlugin { splashDialog.show(); // Set Runnable to remove splash screen just in case - final Handler handler = new Handler(); - handler.postDelayed(new Runnable() { - public void run() { - removeSplashScreen(); - } - }, splashscreenTime); + if (hideAfterDelay) { + final Handler handler = new Handler(); + handler.postDelayed(new Runnable() { + public void run() { + removeSplashScreen(); + } + }, splashscreenTime); + } } }); } From 56204c57488dc602f0743562e5d5f15d91649e5d Mon Sep 17 00:00:00 2001 From: Andrew Grieve Date: Mon, 19 Jan 2015 16:13:48 -0500 Subject: [PATCH 4/5] CB-8329 Cancel outstanding ActivityResult requests when a new startActivityForResult occurs --- .../org/apache/cordova/CordovaActivity.java | 34 +++++++++++++++---- 1 file changed, 27 insertions(+), 7 deletions(-) diff --git a/framework/src/org/apache/cordova/CordovaActivity.java b/framework/src/org/apache/cordova/CordovaActivity.java index b61fa987..6a3c70b7 100755 --- a/framework/src/org/apache/cordova/CordovaActivity.java +++ b/framework/src/org/apache/cordova/CordovaActivity.java @@ -103,7 +103,8 @@ public class CordovaActivity extends Activity implements CordovaInterface { private int activityState = 0; // 0=starting, 1=running (after 1st resume), 2=shutting down // Plugin to call when activity result is received - protected CordovaPlugin activityResultCallback = null; + protected int activityResultRequestCode; + protected CordovaPlugin activityResultCallback; protected boolean activityResultKeepRunning; /* @@ -666,7 +667,7 @@ public class CordovaActivity extends Activity implements CordovaInterface { * @param requestCode The request code that is passed to callback to identify the activity */ public void startActivityForResult(CordovaPlugin command, Intent intent, int requestCode) { - this.activityResultCallback = command; + setActivityResultCallback(command); this.activityResultKeepRunning = this.keepRunning; // If multitasking turned on, then disable it for activities that return results @@ -674,8 +675,19 @@ public class CordovaActivity extends Activity implements CordovaInterface { this.keepRunning = false; } - // Start activity - super.startActivityForResult(intent, requestCode); + try { + startActivityForResult(intent, requestCode); + } catch (RuntimeException e) { // E.g.: ActivityNotFoundException + activityResultCallback = null; + throw e; + } + } + + @Override + public void startActivityForResult(Intent intent, int requestCode, Bundle options) { + // Capture requestCode here so that it is captured in the setActivityResultCallback() case. + activityResultRequestCode = requestCode; + super.startActivityForResult(intent, requestCode, options); } /** @@ -706,16 +718,24 @@ public class CordovaActivity extends Activity implements CordovaInterface { if(callback == null && initCallbackClass != null) { // The application was restarted, but had defined an initial callback // before being shut down. - this.activityResultCallback = appView.pluginManager.getPlugin(initCallbackClass); - callback = this.activityResultCallback; + callback = appView.pluginManager.getPlugin(initCallbackClass); } - if(callback != null) { + initCallbackClass = null; + activityResultCallback = null; + + if (callback != null) { LOG.d(TAG, "We have a callback to send this result to"); callback.onActivityResult(requestCode, resultCode, intent); + } else { + LOG.w(TAG, "Got an activity result, but no plugin was registered to receive it."); } } public void setActivityResultCallback(CordovaPlugin plugin) { + // Cancel any previously pending activity. + if (activityResultCallback != null) { + activityResultCallback.onActivityResult(activityResultRequestCode, Activity.RESULT_CANCELED, null); + } this.activityResultCallback = plugin; } From 62c1c5f38bcb9118a3984df9a96a449c2ea50e30 Mon Sep 17 00:00:00 2001 From: Andrew Grieve Date: Mon, 19 Jan 2015 16:15:25 -0500 Subject: [PATCH 5/5] CB-8017 Add support for `` for Lollipop Also refactors a bit to remove related special-case code from CordovaActivity --- .../org/apache/cordova/CordovaActivity.java | 17 +----- .../apache/cordova/CordovaChromeClient.java | 57 +++++++++++++------ 2 files changed, 43 insertions(+), 31 deletions(-) diff --git a/framework/src/org/apache/cordova/CordovaActivity.java b/framework/src/org/apache/cordova/CordovaActivity.java index 6a3c70b7..69a96f01 100755 --- a/framework/src/org/apache/cordova/CordovaActivity.java +++ b/framework/src/org/apache/cordova/CordovaActivity.java @@ -36,7 +36,6 @@ import android.content.DialogInterface; import android.content.Intent; import android.graphics.Color; import android.media.AudioManager; -import android.net.Uri; import android.os.Bundle; import android.util.Log; import android.view.Display; @@ -48,7 +47,6 @@ import android.view.ViewGroup; import android.view.ViewParent; import android.view.Window; import android.view.WindowManager; -import android.webkit.ValueCallback; import android.webkit.WebViewClient; import android.widget.LinearLayout; @@ -697,23 +695,12 @@ public class CordovaActivity extends Activity implements CordovaInterface { * @param requestCode The request code originally supplied to startActivityForResult(), * allowing you to identify who this result came from. * @param resultCode The integer result code returned by the child activity through its setResult(). - * @param data An Intent, which can return result data to the caller (various data can be attached to Intent "extras"). + * @param intent An Intent, which can return result data to the caller (various data can be attached to Intent "extras"). */ @Override protected void onActivityResult(int requestCode, int resultCode, Intent intent) { - LOG.d(TAG, "Incoming Result"); + LOG.d(TAG, "Incoming Result. Request code = " + requestCode); super.onActivityResult(requestCode, resultCode, intent); - Log.d(TAG, "Request code = " + requestCode); - if (appView != null && requestCode == CordovaChromeClient.FILECHOOSER_RESULTCODE) { - ValueCallback mUploadMessage = this.appView.getWebChromeClient().getValueCallback(); - Log.d(TAG, "did we get here?"); - if (null == mUploadMessage) - return; - Uri result = intent == null || resultCode != Activity.RESULT_OK ? null : intent.getData(); - Log.d(TAG, "result = " + result); - mUploadMessage.onReceiveValue(result); - mUploadMessage = null; - } CordovaPlugin callback = this.activityResultCallback; if(callback == null && initCallbackClass != null) { // The application was restarted, but had defined an initial callback diff --git a/framework/src/org/apache/cordova/CordovaChromeClient.java b/framework/src/org/apache/cordova/CordovaChromeClient.java index ff0c0f2f..31759d0a 100755 --- a/framework/src/org/apache/cordova/CordovaChromeClient.java +++ b/framework/src/org/apache/cordova/CordovaChromeClient.java @@ -22,10 +22,14 @@ import org.apache.cordova.CordovaInterface; import org.apache.cordova.LOG; import android.annotation.TargetApi; +import android.app.Activity; import android.app.AlertDialog; +import android.content.ActivityNotFoundException; import android.content.DialogInterface; import android.content.Intent; import android.net.Uri; +import android.os.Build; +import android.util.Log; import android.view.Gravity; import android.view.KeyEvent; import android.view.View; @@ -67,10 +71,7 @@ public class CordovaChromeClient extends WebChromeClient { //Keep track of last AlertDialog showed private AlertDialog lastHandledDialog; - - // File Chooser - public ValueCallback mUploadMessage; - + @Deprecated public CordovaChromeClient(CordovaInterface cordova) { this.cordova = cordova; @@ -309,7 +310,10 @@ public class CordovaChromeClient extends WebChromeClient { } return mVideoProgressView; } - + + // support: + // openFileChooser() is for pre KitKat and in KitKat mr1 (it's known broken in KitKat). + // For Lollipop, we use onShowFileChooser(). public void openFileChooser(ValueCallback uploadMsg) { this.openFileChooser(uploadMsg, "*/*"); } @@ -318,20 +322,41 @@ public class CordovaChromeClient extends WebChromeClient { this.openFileChooser(uploadMsg, acceptType, null); } - public void openFileChooser(ValueCallback uploadMsg, String acceptType, String capture) + public void openFileChooser(final ValueCallback uploadMsg, String acceptType, String capture) { - mUploadMessage = uploadMsg; - Intent i = new Intent(Intent.ACTION_GET_CONTENT); - i.addCategory(Intent.CATEGORY_OPENABLE); - i.setType("*/*"); - this.cordova.getActivity().startActivityForResult(Intent.createChooser(i, "File Browser"), - FILECHOOSER_RESULTCODE); + Intent intent = new Intent(Intent.ACTION_GET_CONTENT); + intent.addCategory(Intent.CATEGORY_OPENABLE); + intent.setType("*/*"); + cordova.startActivityForResult(new CordovaPlugin() { + @Override + public void onActivityResult(int requestCode, int resultCode, Intent intent) { + Uri result = intent == null || resultCode != Activity.RESULT_OK ? null : intent.getData(); + Log.d(TAG, "Receive file chooser URL: " + result); + uploadMsg.onReceiveValue(result); + } + }, intent, FILECHOOSER_RESULTCODE); } - - public ValueCallback getValueCallback() { - return this.mUploadMessage; + + @TargetApi(Build.VERSION_CODES.LOLLIPOP) + @Override + public boolean onShowFileChooser(WebView webView, final ValueCallback filePathsCallback, final WebChromeClient.FileChooserParams fileChooserParams) { + Intent intent = fileChooserParams.createIntent(); + try { + cordova.startActivityForResult(new CordovaPlugin() { + @Override + public void onActivityResult(int requestCode, int resultCode, Intent intent) { + Uri[] result = WebChromeClient.FileChooserParams.parseResult(resultCode, intent); + Log.d(TAG, "Receive file chooser URL: " + result); + filePathsCallback.onReceiveValue(result); + } + }, intent, FILECHOOSER_RESULTCODE); + } catch (ActivityNotFoundException e) { + Log.w("No activity found to handle file chooser intent.", e); + filePathsCallback.onReceiveValue(null); + } + return true; } - + public void destroyLastDialog(){ if(lastHandledDialog != null){ lastHandledDialog.cancel();