From 83120a5beacc0b47160cda0564456cc837db0488 Mon Sep 17 00:00:00 2001 From: Andrew Grieve <agrieve@chromium.org> Date: Thu, 29 Jan 2015 20:42:44 -0500 Subject: [PATCH] CB-8382 Make CordovaActivity not implement CordovaInterface Instead, use a CordovaInterfaceImpl class. This also makes it easier for apps to implement the interface without extending CordovaActivity. --- .../org/apache/cordova/CordovaActivity.java | 98 ++++---------- .../apache/cordova/CordovaInterfaceImpl.java | 122 ++++++++++++++++++ .../org/apache/cordova/test/userwebview.java | 4 +- .../org/apache/cordova/test/whitelist.java | 2 +- 4 files changed, 148 insertions(+), 78 deletions(-) create mode 100644 framework/src/org/apache/cordova/CordovaInterfaceImpl.java diff --git a/framework/src/org/apache/cordova/CordovaActivity.java b/framework/src/org/apache/cordova/CordovaActivity.java index 684c81b5..1dadf0b1 100755 --- a/framework/src/org/apache/cordova/CordovaActivity.java +++ b/framework/src/org/apache/cordova/CordovaActivity.java @@ -22,8 +22,6 @@ import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; import java.util.ArrayList; import java.util.Locale; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; import org.json.JSONException; import org.json.JSONObject; @@ -78,23 +76,17 @@ import android.widget.LinearLayout; * deprecated in favor of the config.xml file. * */ -public class CordovaActivity extends Activity implements CordovaInterface { +public class CordovaActivity extends Activity { public static String TAG = "CordovaActivity"; // The webview for our app protected CordovaWebView appView; - private final ExecutorService threadPool = Executors.newCachedThreadPool(); - private static int ACTIVITY_STARTING = 0; private static int ACTIVITY_RUNNING = 1; private static int ACTIVITY_EXITING = 2; private int activityState = 0; // 0=starting, 1=running (after 1st resume), 2=shutting down - // Plugin to call when activity result is received - protected int activityResultRequestCode; - protected CordovaPlugin activityResultCallback; - /* * The variables below are used to cache some of the activity properties. */ @@ -107,14 +99,14 @@ public class CordovaActivity extends Activity implements CordovaInterface { // when another application (activity) is started. protected boolean keepRunning = true; - private String initCallbackClass; - // Read from config.xml: protected CordovaPreferences preferences; protected Whitelist internalWhitelist; protected Whitelist externalWhitelist; protected String launchUrl; protected ArrayList<PluginEntry> pluginEntries; + protected CordovaInterfaceImpl cordovaInterface; + /** * Called when the activity is first created. @@ -146,9 +138,10 @@ public class CordovaActivity extends Activity implements CordovaInterface { super.onCreate(savedInstanceState); + cordovaInterface = makeCordovaInterface(); if(savedInstanceState != null) { - initCallbackClass = savedInstanceState.getString("callbackClass"); + cordovaInterface.restoreInstanceState(savedInstanceState); } } @@ -156,6 +149,7 @@ public class CordovaActivity extends Activity implements CordovaInterface { appView = makeWebView(); createViews(); appView.init(this, pluginEntries, internalWhitelist, externalWhitelist, preferences); + cordovaInterface.setPluginManager(appView.getPluginManager()); // Wire the hardware volume controls to control media if desired. String volumePref = preferences.getString("DefaultVolumeStream", ""); @@ -206,14 +200,7 @@ public class CordovaActivity extends Activity implements CordovaInterface { } /** - * Get the Android activity. - */ - @Override public Activity getActivity() { - return this; - } - - /** - * Construct the CordovaWebView object. + * Construct the default web view object. * * Override this to customize the webview that is used. */ @@ -227,6 +214,17 @@ public class CordovaActivity extends Activity implements CordovaInterface { } catch (Exception e) { throw new RuntimeException("Failed to create webview. ", e); } + return ret; + } + + protected CordovaInterfaceImpl makeCordovaInterface() { + return new CordovaInterfaceImpl(this) { + @Override + public Object onMessage(String id, Object data) { + // Plumb this to CordovaActivity.onMessage for backwards compatibility + return CordovaActivity.this.onMessage(id, data); + } + }; } /** @@ -317,36 +315,17 @@ public class CordovaActivity extends Activity implements CordovaInterface { public void endActivity() { finish(); } - + @Override public void finish() { this.activityState = ACTIVITY_EXITING; super.finish(); } - - /** - * Launch an activity for which you would like a result when it finished. When this activity exits, - * your onActivityResult() method will be called. - * - * @param command The command object - * @param intent The intent to start - * @param requestCode The request code that is passed to callback to identify the activity - */ - public void startActivityForResult(CordovaPlugin command, Intent intent, int requestCode) { - setActivityResultCallback(command); - 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; + cordovaInterface.setActivityResultRequestCode(requestCode); super.startActivityForResult(intent, requestCode, options); } @@ -363,29 +342,7 @@ public class CordovaActivity extends Activity implements CordovaInterface { protected void onActivityResult(int requestCode, int resultCode, Intent intent) { LOG.d(TAG, "Incoming Result. Request code = " + requestCode); super.onActivityResult(requestCode, resultCode, intent); - CordovaPlugin callback = this.activityResultCallback; - if(callback == null && initCallbackClass != null) { - // The application was restarted, but had defined an initial callback - // before being shut down. - callback = appView.getPluginManager().getPlugin(initCallbackClass); - } - 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; + cordovaInterface.onActivityResult(requestCode, resultCode, intent); } /** @@ -499,24 +456,15 @@ public class CordovaActivity extends Activity implements CordovaInterface { } catch (JSONException e) { e.printStackTrace(); } - } - else if ("exit".equals(id)) { + } else if ("exit".equals(id)) { this.endActivity(); } return null; } - public ExecutorService getThreadPool() { - return threadPool; - } - protected void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); - if(this.activityResultCallback != null) - { - String cClass = this.activityResultCallback.getClass().getName(); - outState.putString("callbackClass", cClass); - } + cordovaInterface.onSaveInstanceState(outState); } } diff --git a/framework/src/org/apache/cordova/CordovaInterfaceImpl.java b/framework/src/org/apache/cordova/CordovaInterfaceImpl.java new file mode 100644 index 00000000..a693764b --- /dev/null +++ b/framework/src/org/apache/cordova/CordovaInterfaceImpl.java @@ -0,0 +1,122 @@ +package org.apache.cordova; + +import android.app.Activity; +import android.content.Intent; +import android.os.Bundle; +import android.util.Log; + +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; + +/** + * Default implementation of CordovaInterface. + */ +public class CordovaInterfaceImpl implements CordovaInterface { + private static final String TAG = "CordovaInterfaceImpl"; + protected Activity activity; + protected ExecutorService threadPool; + protected PluginManager pluginManager; + + protected CordovaPlugin activityResultCallback; + protected String initCallbackClass; + protected int activityResultRequestCode; + + public CordovaInterfaceImpl(Activity activity) { + this(activity, Executors.newCachedThreadPool()); + } + + public CordovaInterfaceImpl(Activity activity, ExecutorService threadPool) { + this.activity = activity; + this.threadPool = threadPool; + } + + public void setPluginManager(PluginManager pluginManager) { + this.pluginManager = pluginManager; + } + + @Override + public void startActivityForResult(CordovaPlugin command, Intent intent, int requestCode) { + setActivityResultCallback(command); + try { + activity.startActivityForResult(intent, requestCode); + } catch (RuntimeException e) { // E.g.: ActivityNotFoundException + activityResultCallback = null; + throw e; + } + } + + @Override + public void setActivityResultCallback(CordovaPlugin plugin) { + // Cancel any previously pending activity. + if (activityResultCallback != null) { + activityResultCallback.onActivityResult(activityResultRequestCode, Activity.RESULT_CANCELED, null); + } + activityResultCallback = plugin; + } + + @Override + public Activity getActivity() { + return activity; + } + + @Override + public Object onMessage(String id, Object data) { + if ("exit".equals(id)) { + activity.finish(); + } + return null; + } + + @Override + public ExecutorService getThreadPool() { + return threadPool; + } + + /** + * Routes the result to the awaiting plugin. Returns false if no plugin was waiting. + */ + public boolean onActivityResult(int requestCode, int resultCode, Intent intent) { + CordovaPlugin callback = activityResultCallback; + if(callback == null && initCallbackClass != null) { + // The application was restarted, but had defined an initial callback + // before being shut down. + callback = pluginManager.getPlugin(initCallbackClass); + } + initCallbackClass = null; + activityResultCallback = null; + + if (callback != null) { + Log.d(TAG, "Sending activity result to plugin"); + callback.onActivityResult(requestCode, resultCode, intent); + return true; + } + Log.w(TAG, "Got an activity result, but no plugin was registered to receive it."); + return false; + } + + /** + * Call this from your startActivityForResult() overload. This is required to catch the case + * where plugins use Activity.startActivityForResult() + CordovaInterface.setActivityResultCallback() + * rather than CordovaInterface.startActivityForResult(). + */ + public void setActivityResultRequestCode(int requestCode) { + activityResultRequestCode = requestCode; + } + + /** + * Saves parameters for startActivityForResult(). + */ + public void onSaveInstanceState(Bundle outState) { + if (activityResultCallback != null) { + String cClass = activityResultCallback.getClass().getName(); + outState.putString("callbackClass", cClass); + } + } + + /** + * Call this from onCreate() so that any saved startActivityForResult parameters will be restored. + */ + public void restoreInstanceState(Bundle savedInstanceState) { + initCallbackClass = savedInstanceState.getString("callbackClass"); + } +} diff --git a/test/src/org/apache/cordova/test/userwebview.java b/test/src/org/apache/cordova/test/userwebview.java index 4237e199..6e16c6e7 100755 --- a/test/src/org/apache/cordova/test/userwebview.java +++ b/test/src/org/apache/cordova/test/userwebview.java @@ -32,8 +32,8 @@ public class userwebview extends MainTestActivity { @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - testViewClient = new TestViewClient(this, ((AndroidWebView)appView)); - testChromeClient = new TestChromeClient(this, ((AndroidWebView)appView)); + testViewClient = new TestViewClient(cordovaInterface, ((AndroidWebView)appView)); + testChromeClient = new TestChromeClient(cordovaInterface, ((AndroidWebView)appView)); super.init(); ((AndroidWebView)appView).setWebViewClient(testViewClient); ((AndroidWebView)appView).setWebChromeClient(testChromeClient); diff --git a/test/src/org/apache/cordova/test/whitelist.java b/test/src/org/apache/cordova/test/whitelist.java index 00173729..e2029a99 100755 --- a/test/src/org/apache/cordova/test/whitelist.java +++ b/test/src/org/apache/cordova/test/whitelist.java @@ -28,7 +28,7 @@ public class whitelist extends MainTestActivity { public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); super.init(); - ((AndroidWebView)appView).setWebViewClient(new TestViewClient(this, ((AndroidWebView)appView))); + ((AndroidWebView)appView).setWebViewClient(new TestViewClient(cordovaInterface, ((AndroidWebView)appView))); super.loadUrl("file:///android_asset/www/whitelist/index.html"); }