diff --git a/bin/templates/cordova/lib/build.js b/bin/templates/cordova/lib/build.js index 54703ef8..47dd84cc 100644 --- a/bin/templates/cordova/lib/build.js +++ b/bin/templates/cordova/lib/build.js @@ -239,7 +239,8 @@ var builders = { // If the gradle distribution URL is set, make sure it points to version 1.12. // If it's not set, do nothing, assuming that we're using a future version of gradle that we don't want to mess with. - var distributionUrlRegex = '/^distributionUrl=.*$/'; + // For some reason, using ^ and $ don't work. This does the job, though. + var distributionUrlRegex = /distributionUrl.*zip/; var distributionUrl = 'distributionUrl=http\\://services.gradle.org/distributions/gradle-1.12-all.zip'; var gradleWrapperPropertiesPath = path.join(projectPath, 'gradle', 'wrapper', 'gradle-wrapper.properties'); shell.sed('-i', distributionUrlRegex, distributionUrl, gradleWrapperPropertiesPath); @@ -398,12 +399,44 @@ module.exports.run = function(options, optResolvedTarget) { * Returns "arm" or "x86". */ module.exports.detectArchitecture = function(target) { - return exec('adb -s ' + target + ' shell cat /proc/cpuinfo') - .then(function(output) { - if (/intel/i.exec(output)) { - return 'x86'; + function helper() { + return exec('adb -s ' + target + ' shell cat /proc/cpuinfo') + .then(function(output) { + if (/intel/i.exec(output)) { + return 'x86'; + } + return 'arm'; + }); + } + // It sometimes happens (at least on OS X), that this command will hang forever. + // To fix it, either unplug & replug device, or restart adb server. + return helper().timeout(1000, 'Device communication timed out. Try unplugging & replugging the device.') + .then(null, function(err) { + if (/timed out/.exec('' + err)) { + // adb kill-server doesn't seem to do the trick. + // Could probably find a x-platform version of killall, but I'm not actually + // sure that this scenario even happens on non-OSX machines. + return exec('killall adb') + .then(function() { + console.log('adb seems hung. retrying.'); + return helper() + .then(null, function() { + // The double kill is sadly often necessary, at least on mac. + console.log('Now device not found... restarting adb again.'); + return exec('killall adb') + .then(function() { + return helper() + .then(null, function() { + return Q.reject('USB is flakey. Try unplugging & replugging the device.'); + }); + }); + }); + }, function() { + // For non-killall OS's. + return Q.reject(err); + }) } - return 'arm'; + throw err; }); }; diff --git a/bin/templates/cordova/lib/device.js b/bin/templates/cordova/lib/device.js index 6be55e30..6430d732 100644 --- a/bin/templates/cordova/lib/device.js +++ b/bin/templates/cordova/lib/device.js @@ -28,23 +28,43 @@ var exec = require('./exec'), /** * Returns a promise for the list of the device ID's found + * @param lookHarder When true, try restarting adb if no devices are found. */ -module.exports.list = function() { - return exec('adb devices') - .then(function(output) { - var response = output.split('\n'); - var device_list = []; - for (var i = 1; i < response.length; i++) { - if (response[i].match(/\w+\tdevice/) && !response[i].match(/emulator/)) { - device_list.push(response[i].replace(/\tdevice/, '').replace('\r', '')); +module.exports.list = function(lookHarder) { + function helper() { + return exec('adb devices') + .then(function(output) { + var response = output.split('\n'); + var device_list = []; + for (var i = 1; i < response.length; i++) { + if (response[i].match(/\w+\tdevice/) && !response[i].match(/emulator/)) { + device_list.push(response[i].replace(/\tdevice/, '').replace('\r', '')); + } } + return device_list; + }); + } + return helper() + .then(function(list) { + if (list.length === 0 && lookHarder) { + // adb kill-server doesn't seem to do the trick. + // Could probably find a x-platform version of killall, but I'm not actually + // sure that this scenario even happens on non-OSX machines. + return exec('killall adb') + .then(function() { + console.log('Restarting adb to see if more devices are detected.'); + return helper(); + }, function() { + // For non-killall OS's. + return list; + }); } - return device_list; + return list; }); } module.exports.resolveTarget = function(target) { - return this.list() + return this.list(true) .then(function(device_list) { if (!device_list || !device_list.length) { return Q.reject('ERROR: Failed to deploy to device, no devices found.'); diff --git a/bin/templates/project/build.gradle b/bin/templates/project/build.gradle index 2f812716..b536d0f8 100644 --- a/bin/templates/project/build.gradle +++ b/bin/templates/project/build.gradle @@ -31,8 +31,18 @@ buildscript { mavenCentral() } - dependencies { - classpath 'com.android.tools.build:gradle:0.12.0+' + // Switch the Android Gradle plugin version requirement depending on the + // installed version of Gradle. This dependency is documented at + // http://tools.android.com/tech-docs/new-build-system/version-compatibility + // and https://issues.apache.org/jira/browse/CB-8143 + if (gradle.gradleVersion >= "2.1") { + dependencies { + classpath 'com.android.tools.build:gradle:0.14.0+' + } + } else { + dependencies { + classpath 'com.android.tools.build:gradle:0.12.0+' + } } } diff --git a/framework/src/org/apache/cordova/AndroidWebView.java b/framework/src/org/apache/cordova/AndroidWebView.java index c3c9e5b2..f63a6fb1 100755 --- a/framework/src/org/apache/cordova/AndroidWebView.java +++ b/framework/src/org/apache/cordova/AndroidWebView.java @@ -124,8 +124,9 @@ public class AndroidWebView extends WebView implements CordovaWebView { pluginManager = new PluginManager(this, this.cordova, pluginEntries); resourceApi = new CordovaResourceApi(this.getContext(), pluginManager); bridge = new CordovaBridge(pluginManager, new NativeToJsMessageQueue(this, cordova), this.cordova.getActivity().getPackageName()); - pluginManager.addService("App", "org.apache.cordova.CoreAndroid"); initWebViewSettings(); + pluginManager.addService("App", "org.apache.cordova.CoreAndroid"); + pluginManager.init(); if (this.viewClient == null) { setWebViewClient(Build.VERSION.SDK_INT < Build.VERSION_CODES.ICE_CREAM_SANDWICH ? @@ -304,8 +305,11 @@ public class AndroidWebView extends WebView implements CordovaWebView { recreatePlugins = recreatePlugins || (loadedUrl == null); if (recreatePlugins) { + // Don't re-initialize on first load. + if (loadedUrl != null) { + this.pluginManager.init(); + } this.loadedUrl = url; - this.pluginManager.init(); } // Create a timeout timer for loadUrl diff --git a/framework/src/org/apache/cordova/CordovaActivity.java b/framework/src/org/apache/cordova/CordovaActivity.java index b993f375..8dec8057 100755 --- a/framework/src/org/apache/cordova/CordovaActivity.java +++ b/framework/src/org/apache/cordova/CordovaActivity.java @@ -25,16 +25,12 @@ import java.util.Locale; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; -import org.apache.cordova.CordovaInterface; -import org.apache.cordova.CordovaPlugin; -import org.apache.cordova.LOG; +import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; import android.app.Activity; import android.app.AlertDialog; -import android.app.Dialog; -import android.app.ProgressDialog; import android.content.Context; import android.content.DialogInterface; import android.content.Intent; @@ -42,7 +38,6 @@ import android.graphics.Color; import android.media.AudioManager; import android.net.Uri; import android.os.Bundle; -import android.os.Handler; import android.util.Log; import android.view.Display; import android.view.Menu; @@ -92,7 +87,6 @@ public class CordovaActivity extends Activity implements CordovaInterface { // The webview for our app protected CordovaWebView appView; - protected ProgressDialog spinnerDialog = null; private final ExecutorService threadPool = Executors.newCachedThreadPool(); private static int ACTIVITY_STARTING = 0; @@ -108,10 +102,6 @@ public class CordovaActivity extends Activity implements CordovaInterface { * The variables below are used to cache some of the activity properties. */ - // Draw a splash screen using an image located in the drawable resource directory. - // This is not the same as calling super.loadSplashscreen(url) - protected int splashscreen = 0; - // LoadUrl timeout value in msec (default of 20 sec) protected int loadUrlTimeoutValue = 20000; @@ -213,8 +203,6 @@ public class CordovaActivity extends Activity implements CordovaInterface { ViewGroup.LayoutParams.MATCH_PARENT, 1.0F)); - // Add web view but make it invisible while loading URL - appView.getView().setVisibility(View.INVISIBLE); // need to remove appView from any existing parent before invoking root.addView(appView) ViewParent parent = appView.getView().getParent(); if ((parent != null) && (parent != root)) { @@ -264,7 +252,7 @@ public class CordovaActivity extends Activity implements CordovaInterface { e.printStackTrace(); } } - + if (ret == null) { // If all else fails, return a default WebView ret = new AndroidWebView(this); @@ -276,82 +264,15 @@ public class CordovaActivity extends Activity implements CordovaInterface { /** * Load the url into the webview. */ - public void loadUrl(String url, int splashscreenTime) { + public void loadUrl(String url) { if (appView == null) { init(); } - String splash = preferences.getString("SplashScreen", null); - if(splashscreenTime > 0 && splash != null) - { - this.splashscreen = getResources().getIdentifier(splash, "drawable", getClass().getPackage().getName());; - if(this.splashscreen != 0) - { - this.showSplashScreen(splashscreenTime); - } - } - + // If keepRunning this.keepRunning = preferences.getBoolean("KeepRunning", true); - //Check if the view is attached to anything - if(appView.getView().getParent() != null) - { - // Then load the spinner - this.loadSpinner(); - } - //Load the correct splashscreen - if(this.splashscreen != 0) - { - appView.getPluginManager().postMessage("splashscreen", "show"); - } - this.appView.loadUrlIntoView(url, true); - } - - /** - * Load the url into the webview after waiting for period of time. - * This is used to display the splashscreen for certain amount of time. - * - * @param url - * @param time The number of ms to wait before loading webview - */ - public void loadUrl(final String url) { - if (appView == null) { - init(); - } - this.loadUrl(url, preferences.getInteger("SplashScreenDelay", 3000)); - } - - /* - * Load the spinner - */ - void loadSpinner() { - - // If loadingDialog property, then show the App loading dialog for first page of app - String loading = null; - if ((this.appView == null) || !this.appView.canGoBack()) { - loading = preferences.getString("LoadingDialog", null); - } - else { - loading = preferences.getString("LoadingPageDialog", null); - } - if (loading != null) { - - String title = ""; - String message = "Loading Application..."; - - if (loading.length() > 0) { - int comma = loading.indexOf(','); - if (comma > 0) { - title = loading.substring(0, comma); - message = loading.substring(comma + 1); - } - else { - title = ""; - message = loading; - } - } - this.spinnerStart(title, message); - } + appView.loadUrlIntoView(url, true); } /** @@ -375,9 +296,6 @@ public class CordovaActivity extends Activity implements CordovaInterface { { this.appView.handlePause(this.keepRunning); } - - // hide the splash screen to avoid leaking a window - this.removeSplashScreen(); } /** @@ -432,9 +350,6 @@ public class CordovaActivity extends Activity implements CordovaInterface { LOG.d(TAG, "CordovaActivity.onDestroy()"); super.onDestroy(); - // hide the splash screen to avoid leaking a window - this.removeSplashScreen(); - if (this.appView != null) { appView.handleDestroy(); } @@ -443,36 +358,6 @@ public class CordovaActivity extends Activity implements CordovaInterface { } } - /** - * Show the spinner. Must be called from the UI thread. - * - * @param title Title of the dialog - * @param message The message of the dialog - */ - public void spinnerStart(final String title, final String message) { - if (this.spinnerDialog != null) { - this.spinnerDialog.dismiss(); - this.spinnerDialog = null; - } - final CordovaActivity me = this; - this.spinnerDialog = ProgressDialog.show(CordovaActivity.this, title, message, true, true, - new DialogInterface.OnCancelListener() { - public void onCancel(DialogInterface dialog) { - me.spinnerDialog = null; - } - }); - } - - /** - * Stop spinner - Must be called from UI thread - */ - public void spinnerStop() { - if (this.spinnerDialog != null && this.spinnerDialog.isShowing()) { - this.spinnerDialog.dismiss(); - this.spinnerDialog = null; - } - } - /** * End this activity by calling finish for activity */ @@ -561,8 +446,6 @@ public class CordovaActivity extends Activity implements CordovaInterface { // Load URL on UI thread me.runOnUiThread(new Runnable() { public void run() { - // Stop "app loading" spinner if showing - me.spinnerStop(); me.appView.showWebPage(errorUrl, false, true, null); } }); @@ -638,64 +521,6 @@ public class CordovaActivity extends Activity implements CordovaInterface { return true; } - protected Dialog splashDialog; - - /** - * Removes the Dialog that displays the splash screen - */ - public void removeSplashScreen() { - if (splashDialog != null && splashDialog.isShowing()) { - splashDialog.dismiss(); - splashDialog = null; - } - } - - /** - * Shows the splash screen over the full Activity - */ - @SuppressWarnings("deprecation") - protected void showSplashScreen(final int time) { - final CordovaActivity that = this; - - Runnable runnable = new Runnable() { - public void run() { - // Get reference to display - Display display = getWindowManager().getDefaultDisplay(); - - // Create the layout for the dialog - LinearLayout root = new LinearLayout(that.getActivity()); - root.setMinimumHeight(display.getHeight()); - root.setMinimumWidth(display.getWidth()); - root.setOrientation(LinearLayout.VERTICAL); - root.setBackgroundColor(preferences.getInteger("backgroundColor", Color.BLACK)); - root.setLayoutParams(new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, - ViewGroup.LayoutParams.MATCH_PARENT, 0.0F)); - root.setBackgroundResource(that.splashscreen); - - // Create and show the dialog - splashDialog = new Dialog(that, android.R.style.Theme_Translucent_NoTitleBar); - // check to see if the splash screen should be full screen - if ((getWindow().getAttributes().flags & WindowManager.LayoutParams.FLAG_FULLSCREEN) - == WindowManager.LayoutParams.FLAG_FULLSCREEN) { - splashDialog.getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, - WindowManager.LayoutParams.FLAG_FULLSCREEN); - } - splashDialog.setContentView(root); - splashDialog.setCancelable(false); - splashDialog.show(); - - // Set Runnable to remove splash screen just in case - final Handler handler = new Handler(); - handler.postDelayed(new Runnable() { - public void run() { - removeSplashScreen(); - } - }, time); - } - }; - this.runOnUiThread(runnable); - } - /** * Called when a message is sent to plugin. * @@ -708,28 +533,7 @@ public class CordovaActivity extends Activity implements CordovaInterface { LOG.d(TAG, "onMessage(" + id + "," + data + ")"); } - if ("splashscreen".equals(id)) { - if ("hide".equals(data.toString())) { - this.removeSplashScreen(); - } - else { - // If the splash dialog is showing don't try to show it again - if (this.splashDialog == null || !this.splashDialog.isShowing()) { - String splashResource = preferences.getString("SplashScreen", null); - if (splashResource != null) { - splashscreen = getResources().getIdentifier(splashResource, "drawable", getClass().getPackage().getName()); - } - this.showSplashScreen(preferences.getInteger("SplashScreenDelay", 3000)); - } - } - } - else if ("spinner".equals(id)) { - if ("stop".equals(data.toString())) { - this.spinnerStop(); - this.appView.getView().setVisibility(View.VISIBLE); - } - } - else if ("onReceivedError".equals(id)) { + if ("onReceivedError".equals(id)) { JSONObject d = (JSONObject) data; try { this.onReceivedError(d.getInt("errorCode"), d.getString("description"), d.getString("url"));