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.
This commit is contained in:
Andrew Grieve 2015-01-29 20:42:44 -05:00
parent 20723896e1
commit 83120a5bea
4 changed files with 148 additions and 78 deletions

View File

@ -22,8 +22,6 @@ import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException; import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Locale; import java.util.Locale;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import org.json.JSONException; import org.json.JSONException;
import org.json.JSONObject; import org.json.JSONObject;
@ -78,23 +76,17 @@ import android.widget.LinearLayout;
* deprecated in favor of the config.xml file. * 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"; public static String TAG = "CordovaActivity";
// The webview for our app // The webview for our app
protected CordovaWebView appView; protected CordovaWebView appView;
private final ExecutorService threadPool = Executors.newCachedThreadPool();
private static int ACTIVITY_STARTING = 0; private static int ACTIVITY_STARTING = 0;
private static int ACTIVITY_RUNNING = 1; private static int ACTIVITY_RUNNING = 1;
private static int ACTIVITY_EXITING = 2; private static int ACTIVITY_EXITING = 2;
private int activityState = 0; // 0=starting, 1=running (after 1st resume), 2=shutting down 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. * 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. // when another application (activity) is started.
protected boolean keepRunning = true; protected boolean keepRunning = true;
private String initCallbackClass;
// Read from config.xml: // Read from config.xml:
protected CordovaPreferences preferences; protected CordovaPreferences preferences;
protected Whitelist internalWhitelist; protected Whitelist internalWhitelist;
protected Whitelist externalWhitelist; protected Whitelist externalWhitelist;
protected String launchUrl; protected String launchUrl;
protected ArrayList<PluginEntry> pluginEntries; protected ArrayList<PluginEntry> pluginEntries;
protected CordovaInterfaceImpl cordovaInterface;
/** /**
* Called when the activity is first created. * Called when the activity is first created.
@ -146,9 +138,10 @@ public class CordovaActivity extends Activity implements CordovaInterface {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
cordovaInterface = makeCordovaInterface();
if(savedInstanceState != null) if(savedInstanceState != null)
{ {
initCallbackClass = savedInstanceState.getString("callbackClass"); cordovaInterface.restoreInstanceState(savedInstanceState);
} }
} }
@ -156,6 +149,7 @@ public class CordovaActivity extends Activity implements CordovaInterface {
appView = makeWebView(); appView = makeWebView();
createViews(); createViews();
appView.init(this, pluginEntries, internalWhitelist, externalWhitelist, preferences); appView.init(this, pluginEntries, internalWhitelist, externalWhitelist, preferences);
cordovaInterface.setPluginManager(appView.getPluginManager());
// Wire the hardware volume controls to control media if desired. // Wire the hardware volume controls to control media if desired.
String volumePref = preferences.getString("DefaultVolumeStream", ""); String volumePref = preferences.getString("DefaultVolumeStream", "");
@ -206,14 +200,7 @@ public class CordovaActivity extends Activity implements CordovaInterface {
} }
/** /**
* Get the Android activity. * Construct the default web view object.
*/
@Override public Activity getActivity() {
return this;
}
/**
* Construct the CordovaWebView object.
* *
* Override this to customize the webview that is used. * Override this to customize the webview that is used.
*/ */
@ -227,6 +214,17 @@ public class CordovaActivity extends Activity implements CordovaInterface {
} catch (Exception e) { } catch (Exception e) {
throw new RuntimeException("Failed to create webview. ", 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);
}
};
} }
/** /**
@ -324,29 +322,10 @@ public class CordovaActivity extends Activity implements CordovaInterface {
super.finish(); 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 @Override
public void startActivityForResult(Intent intent, int requestCode, Bundle options) { public void startActivityForResult(Intent intent, int requestCode, Bundle options) {
// Capture requestCode here so that it is captured in the setActivityResultCallback() case. // Capture requestCode here so that it is captured in the setActivityResultCallback() case.
activityResultRequestCode = requestCode; cordovaInterface.setActivityResultRequestCode(requestCode);
super.startActivityForResult(intent, requestCode, options); 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) { protected void onActivityResult(int requestCode, int resultCode, Intent intent) {
LOG.d(TAG, "Incoming Result. Request code = " + requestCode); LOG.d(TAG, "Incoming Result. Request code = " + requestCode);
super.onActivityResult(requestCode, resultCode, intent); super.onActivityResult(requestCode, resultCode, intent);
CordovaPlugin callback = this.activityResultCallback; cordovaInterface.onActivityResult(requestCode, resultCode, intent);
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;
} }
/** /**
@ -499,24 +456,15 @@ public class CordovaActivity extends Activity implements CordovaInterface {
} catch (JSONException e) { } catch (JSONException e) {
e.printStackTrace(); e.printStackTrace();
} }
} } else if ("exit".equals(id)) {
else if ("exit".equals(id)) {
this.endActivity(); this.endActivity();
} }
return null; return null;
} }
public ExecutorService getThreadPool() {
return threadPool;
}
protected void onSaveInstanceState(Bundle outState) protected void onSaveInstanceState(Bundle outState)
{ {
super.onSaveInstanceState(outState); super.onSaveInstanceState(outState);
if(this.activityResultCallback != null) cordovaInterface.onSaveInstanceState(outState);
{
String cClass = this.activityResultCallback.getClass().getName();
outState.putString("callbackClass", cClass);
}
} }
} }

View File

@ -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");
}
}

View File

@ -32,8 +32,8 @@ public class userwebview extends MainTestActivity {
@Override @Override
public void onCreate(Bundle savedInstanceState) { public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
testViewClient = new TestViewClient(this, ((AndroidWebView)appView)); testViewClient = new TestViewClient(cordovaInterface, ((AndroidWebView)appView));
testChromeClient = new TestChromeClient(this, ((AndroidWebView)appView)); testChromeClient = new TestChromeClient(cordovaInterface, ((AndroidWebView)appView));
super.init(); super.init();
((AndroidWebView)appView).setWebViewClient(testViewClient); ((AndroidWebView)appView).setWebViewClient(testViewClient);
((AndroidWebView)appView).setWebChromeClient(testChromeClient); ((AndroidWebView)appView).setWebChromeClient(testChromeClient);

View File

@ -28,7 +28,7 @@ public class whitelist extends MainTestActivity {
public void onCreate(Bundle savedInstanceState) { public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
super.init(); 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"); super.loadUrl("file:///android_asset/www/whitelist/index.html");
} }