mirror of
https://github.com/apache/cordova-android.git
synced 2025-02-26 20:33:07 +08:00
[CB-352] Support initializing DroidGap with existing WebView, WebViewClient and webViewChrome.
[CB-353] Create PluginEntry object to use by PluginManager.
This commit is contained in:
parent
04b3e4d847
commit
7e70d76232
@ -64,7 +64,7 @@ public class CordovaWebViewClient extends WebViewClient {
|
|||||||
public boolean shouldOverrideUrlLoading(WebView view, String url) {
|
public boolean shouldOverrideUrlLoading(WebView view, String url) {
|
||||||
|
|
||||||
// First give any plugins the chance to handle the url themselves
|
// First give any plugins the chance to handle the url themselves
|
||||||
if (this.ctx.pluginManager.onOverrideUrlLoading(url)) {
|
if ((this.ctx.pluginManager != null) && this.ctx.pluginManager.onOverrideUrlLoading(url)) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// If dialing phone (tel:5551212)
|
// If dialing phone (tel:5551212)
|
||||||
|
@ -56,6 +56,7 @@ import android.view.View;
|
|||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
import android.view.Window;
|
import android.view.Window;
|
||||||
import android.view.WindowManager;
|
import android.view.WindowManager;
|
||||||
|
import android.webkit.WebChromeClient;
|
||||||
import android.webkit.WebSettings;
|
import android.webkit.WebSettings;
|
||||||
import android.webkit.WebSettings.LayoutAlgorithm;
|
import android.webkit.WebSettings.LayoutAlgorithm;
|
||||||
import android.webkit.WebView;
|
import android.webkit.WebView;
|
||||||
@ -345,22 +346,33 @@ public class DroidGap extends Activity implements CordovaInterface {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create and initialize web container.
|
* Create and initialize web container with default web view objects.
|
||||||
*/
|
*/
|
||||||
public void init() {
|
public void init() {
|
||||||
|
this.init(new WebView(DroidGap.this), new CordovaWebViewClient(this), new CordovaChromeClient(DroidGap.this));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize web container with web view objects.
|
||||||
|
*
|
||||||
|
* @param webView
|
||||||
|
* @param webViewClient
|
||||||
|
* @param webChromeClient
|
||||||
|
*/
|
||||||
|
public void init(WebView webView, WebViewClient webViewClient, WebChromeClient webChromeClient) {
|
||||||
LOG.d(TAG, "DroidGap.init()");
|
LOG.d(TAG, "DroidGap.init()");
|
||||||
|
|
||||||
// Create web container
|
// Set up web container
|
||||||
this.appView = new WebView(DroidGap.this);
|
this.appView = webView;
|
||||||
this.appView.setId(100);
|
this.appView.setId(100);
|
||||||
|
|
||||||
this.appView.setLayoutParams(new LinearLayout.LayoutParams(
|
this.appView.setLayoutParams(new LinearLayout.LayoutParams(
|
||||||
ViewGroup.LayoutParams.FILL_PARENT,
|
ViewGroup.LayoutParams.FILL_PARENT,
|
||||||
ViewGroup.LayoutParams.FILL_PARENT,
|
ViewGroup.LayoutParams.FILL_PARENT,
|
||||||
1.0F));
|
1.0F));
|
||||||
|
|
||||||
this.appView.setWebChromeClient(new CordovaChromeClient(DroidGap.this));
|
this.appView.setWebChromeClient(webChromeClient);
|
||||||
this.setWebViewClient(this.appView, new CordovaWebViewClient(this));
|
this.setWebViewClient(this.appView, webViewClient);
|
||||||
|
|
||||||
this.appView.setInitialScale(0);
|
this.appView.setInitialScale(0);
|
||||||
this.appView.setVerticalScrollBarEnabled(false);
|
this.appView.setVerticalScrollBarEnabled(false);
|
||||||
@ -393,6 +405,9 @@ public class DroidGap extends Activity implements CordovaInterface {
|
|||||||
|
|
||||||
// Clear cancel flag
|
// Clear cancel flag
|
||||||
this.cancelLoadUrl = false;
|
this.cancelLoadUrl = false;
|
||||||
|
|
||||||
|
// Create plugin manager
|
||||||
|
this.pluginManager = new PluginManager(this.appView, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -498,12 +513,7 @@ public class DroidGap extends Activity implements CordovaInterface {
|
|||||||
else {
|
else {
|
||||||
me.callbackServer.reinit(url);
|
me.callbackServer.reinit(url);
|
||||||
}
|
}
|
||||||
if (me.pluginManager == null) {
|
me.pluginManager.init();
|
||||||
me.pluginManager = new PluginManager(me.appView, me);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
me.pluginManager.reinit();
|
|
||||||
}
|
|
||||||
|
|
||||||
// If loadingDialog property, then show the App loading dialog for first page of app
|
// If loadingDialog property, then show the App loading dialog for first page of app
|
||||||
String loading = null;
|
String loading = null;
|
||||||
@ -834,8 +844,10 @@ public class DroidGap extends Activity implements CordovaInterface {
|
|||||||
this.appView.loadUrl("javascript:try{cordova.require('cordova/channel').onPause.fire();}catch(e){console.log('exception firing pause event from native');};");
|
this.appView.loadUrl("javascript:try{cordova.require('cordova/channel').onPause.fire();}catch(e){console.log('exception firing pause event from native');};");
|
||||||
|
|
||||||
// Forward to plugins
|
// Forward to plugins
|
||||||
this.pluginManager.onPause(this.keepRunning);
|
if (this.pluginManager != null) {
|
||||||
|
this.pluginManager.onPause(this.keepRunning);
|
||||||
|
}
|
||||||
|
|
||||||
// If app doesn't want to run in background
|
// If app doesn't want to run in background
|
||||||
if (!this.keepRunning) {
|
if (!this.keepRunning) {
|
||||||
|
|
||||||
@ -852,7 +864,9 @@ public class DroidGap extends Activity implements CordovaInterface {
|
|||||||
super.onNewIntent(intent);
|
super.onNewIntent(intent);
|
||||||
|
|
||||||
//Forward to plugins
|
//Forward to plugins
|
||||||
this.pluginManager.onNewIntent(intent);
|
if (this.pluginManager != null) {
|
||||||
|
this.pluginManager.onNewIntent(intent);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -875,8 +889,10 @@ public class DroidGap extends Activity implements CordovaInterface {
|
|||||||
this.appView.loadUrl("javascript:try{cordova.require('cordova/channel').onResume.fire();}catch(e){console.log('exception firing resume event from native');};");
|
this.appView.loadUrl("javascript:try{cordova.require('cordova/channel').onResume.fire();}catch(e){console.log('exception firing resume event from native');};");
|
||||||
|
|
||||||
// Forward to plugins
|
// Forward to plugins
|
||||||
this.pluginManager.onResume(this.keepRunning || this.activityResultKeepRunning);
|
if (this.pluginManager != null) {
|
||||||
|
this.pluginManager.onResume(this.keepRunning || this.activityResultKeepRunning);
|
||||||
|
}
|
||||||
|
|
||||||
// If app doesn't want to run in background
|
// If app doesn't want to run in background
|
||||||
if (!this.keepRunning || this.activityResultKeepRunning) {
|
if (!this.keepRunning || this.activityResultKeepRunning) {
|
||||||
|
|
||||||
@ -942,7 +958,9 @@ public class DroidGap extends Activity implements CordovaInterface {
|
|||||||
*/
|
*/
|
||||||
@Deprecated
|
@Deprecated
|
||||||
public void addService(String serviceType, String className) {
|
public void addService(String serviceType, String className) {
|
||||||
this.pluginManager.addService(serviceType, className);
|
if (this.pluginManager != null) {
|
||||||
|
this.pluginManager.addService(serviceType, className);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
119
framework/src/org/apache/cordova/api/PluginEntry.java
Executable file
119
framework/src/org/apache/cordova/api/PluginEntry.java
Executable file
@ -0,0 +1,119 @@
|
|||||||
|
/*
|
||||||
|
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.api;
|
||||||
|
|
||||||
|
import android.webkit.WebView;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class represents a service entry object.
|
||||||
|
*/
|
||||||
|
public class PluginEntry {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The name of the service that this plugin implements
|
||||||
|
*/
|
||||||
|
public String service = "";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The plugin class name that implements the service.
|
||||||
|
*/
|
||||||
|
public String pluginClass = "";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The plugin object.
|
||||||
|
* Plugin objects are only created when they are called from JavaScript. (see PluginManager.exec)
|
||||||
|
* The exception is if the onload flag is set, then they are created when PluginManager is initialized.
|
||||||
|
*/
|
||||||
|
public IPlugin plugin = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Flag that indicates the plugin object should be created when PluginManager is initialized.
|
||||||
|
*/
|
||||||
|
public boolean onload = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor
|
||||||
|
*
|
||||||
|
* @param service The name of the service
|
||||||
|
* @param pluginClass The plugin class name
|
||||||
|
* @param onload Create plugin object when HTML page is loaded
|
||||||
|
*/
|
||||||
|
public PluginEntry(String service, String pluginClass, boolean onload) {
|
||||||
|
this.service = service;
|
||||||
|
this.pluginClass = pluginClass;
|
||||||
|
this.onload = onload;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create plugin object.
|
||||||
|
* If plugin is already created, then just return it.
|
||||||
|
*
|
||||||
|
* @return The plugin object
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public IPlugin createPlugin(WebView webView, CordovaInterface ctx) {
|
||||||
|
if (this.plugin != null) {
|
||||||
|
return this.plugin;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
Class c = getClassByName(this.pluginClass);
|
||||||
|
if (isCordovaPlugin(c)) {
|
||||||
|
this.plugin = (IPlugin) c.newInstance();
|
||||||
|
this.plugin.setContext(ctx);
|
||||||
|
this.plugin.setView(webView);
|
||||||
|
return plugin;
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
System.out.println("Error adding plugin " + this.pluginClass + ".");
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the class.
|
||||||
|
*
|
||||||
|
* @param clazz
|
||||||
|
* @return
|
||||||
|
* @throws ClassNotFoundException
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
private Class getClassByName(final String clazz) throws ClassNotFoundException {
|
||||||
|
Class c = null;
|
||||||
|
if (clazz != null) {
|
||||||
|
c = Class.forName(clazz);
|
||||||
|
}
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the interfaces that a class implements and see if it implements the
|
||||||
|
* org.apache.cordova.api.Plugin interface.
|
||||||
|
*
|
||||||
|
* @param c The class to check the interfaces of.
|
||||||
|
* @return Boolean indicating if the class implements org.apache.cordova.api.Plugin
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
private boolean isCordovaPlugin(Class c) {
|
||||||
|
if (c != null) {
|
||||||
|
return org.apache.cordova.api.Plugin.class.isAssignableFrom(c) || org.apache.cordova.api.IPlugin.class.isAssignableFrom(c);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
@ -15,7 +15,7 @@
|
|||||||
KIND, either express or implied. See the License for the
|
KIND, either express or implied. See the License for the
|
||||||
specific language governing permissions and limitations
|
specific language governing permissions and limitations
|
||||||
under the License.
|
under the License.
|
||||||
*/
|
*/
|
||||||
package org.apache.cordova.api;
|
package org.apache.cordova.api;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
@ -29,7 +29,6 @@ import org.xmlpull.v1.XmlPullParserException;
|
|||||||
|
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.content.res.XmlResourceParser;
|
import android.content.res.XmlResourceParser;
|
||||||
import android.util.Log;
|
|
||||||
import android.webkit.WebView;
|
import android.webkit.WebView;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -39,321 +38,324 @@ import android.webkit.WebView;
|
|||||||
* from JavaScript.
|
* from JavaScript.
|
||||||
*/
|
*/
|
||||||
public class PluginManager {
|
public class PluginManager {
|
||||||
|
private static String TAG = "PluginManager";
|
||||||
|
|
||||||
|
// List of service entries
|
||||||
|
private final HashMap<String, PluginEntry> entries = new HashMap<String, PluginEntry>();
|
||||||
|
|
||||||
|
private final CordovaInterface ctx;
|
||||||
|
private final WebView app;
|
||||||
|
|
||||||
|
// Flag to track first time through
|
||||||
|
private boolean firstRun;
|
||||||
|
|
||||||
private HashMap<String, IPlugin> plugins = new HashMap<String,IPlugin>();
|
|
||||||
private HashMap<String, String> services = new HashMap<String,String>();
|
|
||||||
|
|
||||||
private final CordovaInterface ctx;
|
|
||||||
private final WebView app;
|
|
||||||
|
|
||||||
// Map URL schemes like foo: to plugins that want to handle those schemes
|
// Map URL schemes like foo: to plugins that want to handle those schemes
|
||||||
// This would allow how all URLs are handled to be offloaded to a plugin
|
// This would allow how all URLs are handled to be offloaded to a plugin
|
||||||
protected HashMap<String, String> urlMap = new HashMap<String,String>();
|
protected HashMap<String, String> urlMap = new HashMap<String, String>();
|
||||||
|
|
||||||
/**
|
|
||||||
* Constructor.
|
|
||||||
*
|
|
||||||
* @param app
|
|
||||||
* @param ctx
|
|
||||||
*/
|
|
||||||
public PluginManager(WebView app, CordovaInterface ctx) {
|
|
||||||
this.ctx = ctx;
|
|
||||||
this.app = app;
|
|
||||||
this.loadPlugins();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Re-init when loading a new HTML page into webview.
|
|
||||||
*/
|
|
||||||
public void reinit() {
|
|
||||||
|
|
||||||
// Stop plugins on current HTML page and discard
|
|
||||||
this.onPause(false);
|
|
||||||
this.onDestroy();
|
|
||||||
this.plugins = new HashMap<String, IPlugin>();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Load plugins from res/xml/plugins.xml
|
|
||||||
*/
|
|
||||||
public void loadPlugins() {
|
|
||||||
int id = ctx.getResources().getIdentifier("plugins", "xml", ctx.getPackageName());
|
|
||||||
if (id == 0) { pluginConfigurationMissing(); }
|
|
||||||
XmlResourceParser xml = ctx.getResources().getXml(id);
|
|
||||||
int eventType = -1;
|
|
||||||
String pluginClass = "", pluginName = "";
|
|
||||||
while (eventType != XmlResourceParser.END_DOCUMENT) {
|
|
||||||
if (eventType == XmlResourceParser.START_TAG) {
|
|
||||||
String strNode = xml.getName();
|
|
||||||
if (strNode.equals("plugin")) {
|
|
||||||
pluginClass = xml.getAttributeValue(null, "value");
|
|
||||||
pluginName = xml.getAttributeValue(null, "name");
|
|
||||||
//System.out.println("Plugin: "+name+" => "+value);
|
|
||||||
this.addService(pluginName, pluginClass);
|
|
||||||
|
|
||||||
// Create plugin at load time if attribute "onload"
|
|
||||||
if ("true".equals(xml.getAttributeValue(null, "onload"))) {
|
|
||||||
this.getPlugin(pluginName);
|
|
||||||
}
|
|
||||||
} else if (strNode.equals("url-filter")) {
|
|
||||||
this.urlMap.put(xml.getAttributeValue(null, "value"), pluginName);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
eventType = xml.next();
|
|
||||||
} catch (XmlPullParserException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
} catch (IOException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Receives a request for execution and fulfills it by finding the appropriate
|
|
||||||
* Java class and calling it's execute method.
|
|
||||||
*
|
|
||||||
* PluginManager.exec can be used either synchronously or async. In either case, a JSON encoded
|
|
||||||
* string is returned that will indicate if any errors have occurred when trying to find
|
|
||||||
* or execute the class denoted by the clazz argument.
|
|
||||||
*
|
|
||||||
* @param service String containing the service to run
|
|
||||||
* @param action String containt the action that the class is supposed to perform. This is
|
|
||||||
* passed to the plugin execute method and it is up to the plugin developer
|
|
||||||
* how to deal with it.
|
|
||||||
* @param callbackId String containing the id of the callback that is execute in JavaScript if
|
|
||||||
* this is an async plugin call.
|
|
||||||
* @param args An Array literal string containing any arguments needed in the
|
|
||||||
* plugin execute method.
|
|
||||||
* @param async Boolean indicating whether the calling JavaScript code is expecting an
|
|
||||||
* immediate return value. If true, either Cordova.callbackSuccess(...) or
|
|
||||||
* Cordova.callbackError(...) is called once the plugin code has executed.
|
|
||||||
*
|
|
||||||
* @return JSON encoded string with a response message and status.
|
|
||||||
*/
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
public String exec(final String service, final String action, final String callbackId, final String jsonArgs, final boolean async) {
|
|
||||||
PluginResult cr = null;
|
|
||||||
boolean runAsync = async;
|
|
||||||
try {
|
|
||||||
final JSONArray args = new JSONArray(jsonArgs);
|
|
||||||
final IPlugin plugin = this.getPlugin(service);
|
|
||||||
final CordovaInterface ctx = this.ctx;
|
|
||||||
if (plugin != null) {
|
|
||||||
runAsync = async && !plugin.isSynch(action);
|
|
||||||
if (runAsync) {
|
|
||||||
// Run this on a different thread so that this one can return back to JS
|
|
||||||
Thread thread = new Thread(new Runnable() {
|
|
||||||
public void run() {
|
|
||||||
try {
|
|
||||||
// Call execute on the plugin so that it can do it's thing
|
|
||||||
PluginResult cr = plugin.execute(action, args, callbackId);
|
|
||||||
int status = cr.getStatus();
|
|
||||||
|
|
||||||
// If no result to be sent and keeping callback, then no need to sent back to JavaScript
|
|
||||||
if ((status == PluginResult.Status.NO_RESULT.ordinal()) && cr.getKeepCallback()) {
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check the success (OK, NO_RESULT & !KEEP_CALLBACK)
|
|
||||||
else if ((status == PluginResult.Status.OK.ordinal()) || (status == PluginResult.Status.NO_RESULT.ordinal())) {
|
|
||||||
ctx.sendJavascript(cr.toSuccessCallbackString(callbackId));
|
|
||||||
}
|
|
||||||
|
|
||||||
// If error
|
|
||||||
else {
|
|
||||||
ctx.sendJavascript(cr.toErrorCallbackString(callbackId));
|
|
||||||
}
|
|
||||||
} catch (Exception e) {
|
|
||||||
PluginResult cr = new PluginResult(PluginResult.Status.ERROR, e.getMessage());
|
|
||||||
ctx.sendJavascript(cr.toErrorCallbackString(callbackId));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
thread.start();
|
|
||||||
return "";
|
|
||||||
} else {
|
|
||||||
// Call execute on the plugin so that it can do it's thing
|
|
||||||
cr = plugin.execute(action, args, callbackId);
|
|
||||||
|
|
||||||
// If no result to be sent and keeping callback, then no need to sent back to JavaScript
|
|
||||||
if ((cr.getStatus() == PluginResult.Status.NO_RESULT.ordinal()) && cr.getKeepCallback()) {
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (JSONException e) {
|
|
||||||
System.out.println("ERROR: "+e.toString());
|
|
||||||
cr = new PluginResult(PluginResult.Status.JSON_EXCEPTION);
|
|
||||||
}
|
|
||||||
// if async we have already returned at this point unless there was an error...
|
|
||||||
if (runAsync) {
|
|
||||||
if (cr == null) {
|
|
||||||
cr = new PluginResult(PluginResult.Status.CLASS_NOT_FOUND_EXCEPTION);
|
|
||||||
}
|
|
||||||
ctx.sendJavascript(cr.toErrorCallbackString(callbackId));
|
|
||||||
}
|
|
||||||
return ( cr != null ? cr.getJSONString() : "{ status: 0, message: 'all good' }" );
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the class.
|
|
||||||
*
|
|
||||||
* @param clazz
|
|
||||||
* @return
|
|
||||||
* @throws ClassNotFoundException
|
|
||||||
*/
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
private Class getClassByName(final String clazz) throws ClassNotFoundException {
|
|
||||||
Class c = null;
|
|
||||||
if (clazz != null) {
|
|
||||||
c = Class.forName(clazz);
|
|
||||||
}
|
|
||||||
return c;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the interfaces that a class implements and see if it implements the
|
|
||||||
* org.apache.cordova.api.Plugin interface.
|
|
||||||
*
|
|
||||||
* @param c The class to check the interfaces of.
|
|
||||||
* @return Boolean indicating if the class implements org.apache.cordova.api.Plugin
|
|
||||||
*/
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
private boolean isCordovaPlugin(Class c) {
|
|
||||||
if (c != null) {
|
|
||||||
return org.apache.cordova.api.Plugin.class.isAssignableFrom(c) || org.apache.cordova.api.IPlugin.class.isAssignableFrom(c);
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add plugin to be loaded and cached. This creates an instance of the plugin.
|
* Constructor.
|
||||||
* If plugin is already created, then just return it.
|
|
||||||
*
|
*
|
||||||
* @param className The class to load
|
* @param app
|
||||||
* @param clazz The class object (must be a class object of the className)
|
* @param ctx
|
||||||
* @param callbackId The callback id to use when calling back into JavaScript
|
|
||||||
* @return The plugin
|
|
||||||
*/
|
*/
|
||||||
@SuppressWarnings("unchecked")
|
public PluginManager(WebView app, CordovaInterface ctx) {
|
||||||
private IPlugin addPlugin(String pluginName, String className) {
|
this.ctx = ctx;
|
||||||
try {
|
this.app = app;
|
||||||
Class c = getClassByName(className);
|
this.firstRun = true;
|
||||||
if (isCordovaPlugin(c)) {
|
|
||||||
IPlugin plugin = (IPlugin)c.newInstance();
|
|
||||||
this.plugins.put(className, plugin);
|
|
||||||
plugin.setContext(this.ctx);
|
|
||||||
plugin.setView(this.app);
|
|
||||||
return plugin;
|
|
||||||
}
|
|
||||||
} catch (Exception e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
System.out.println("Error adding plugin "+className+".");
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the loaded plugin.
|
|
||||||
*
|
|
||||||
* If the plugin is not already loaded then load it.
|
|
||||||
*
|
|
||||||
* @param className The class of the loaded plugin.
|
|
||||||
* @return
|
|
||||||
*/
|
|
||||||
private IPlugin getPlugin(String pluginName) {
|
|
||||||
String className = this.services.get(pluginName);
|
|
||||||
if (this.plugins.containsKey(className)) {
|
|
||||||
return this.plugins.get(className);
|
|
||||||
} else {
|
|
||||||
return this.addPlugin(pluginName, className);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Add a class that implements a service.
|
|
||||||
* This does not create the class instance. It just maps service name to class name.
|
|
||||||
*
|
|
||||||
* @param serviceType
|
|
||||||
* @param className
|
|
||||||
*/
|
|
||||||
public void addService(String serviceType, String className) {
|
|
||||||
this.services.put(serviceType, className);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called when the system is about to start resuming a previous activity.
|
* Init when loading a new HTML page into webview.
|
||||||
|
*/
|
||||||
|
public void init() {
|
||||||
|
LOG.d(TAG, "init()");
|
||||||
|
|
||||||
|
// If first time, then load plugins from plugins.xml file
|
||||||
|
if (firstRun) {
|
||||||
|
this.loadPlugins();
|
||||||
|
firstRun = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stop plugins on current HTML page and discard plugin objects
|
||||||
|
else {
|
||||||
|
this.onPause(false);
|
||||||
|
this.onDestroy();
|
||||||
|
this.clearPluginObjects();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start up all plugins that have onload specified
|
||||||
|
this.startupPlugins();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Load plugins from res/xml/plugins.xml
|
||||||
|
*/
|
||||||
|
public void loadPlugins() {
|
||||||
|
int id = ctx.getResources().getIdentifier("plugins", "xml", ctx.getPackageName());
|
||||||
|
if (id == 0) {
|
||||||
|
pluginConfigurationMissing();
|
||||||
|
}
|
||||||
|
XmlResourceParser xml = ctx.getResources().getXml(id);
|
||||||
|
int eventType = -1;
|
||||||
|
String service = "", pluginClass = "";
|
||||||
|
boolean onload = false;
|
||||||
|
PluginEntry entry = null;
|
||||||
|
while (eventType != XmlResourceParser.END_DOCUMENT) {
|
||||||
|
if (eventType == XmlResourceParser.START_TAG) {
|
||||||
|
String strNode = xml.getName();
|
||||||
|
if (strNode.equals("plugin")) {
|
||||||
|
service = xml.getAttributeValue(null, "name");
|
||||||
|
pluginClass = xml.getAttributeValue(null, "value");
|
||||||
|
// System.out.println("Plugin: "+name+" => "+value);
|
||||||
|
onload = "true".equals(xml.getAttributeValue(null, "onload"));
|
||||||
|
entry = new PluginEntry(service, pluginClass, onload);
|
||||||
|
this.addService(entry);
|
||||||
|
} else if (strNode.equals("url-filter")) {
|
||||||
|
this.urlMap.put(xml.getAttributeValue(null, "value"), service);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
eventType = xml.next();
|
||||||
|
} catch (XmlPullParserException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete all plugin objects.
|
||||||
|
*/
|
||||||
|
public void clearPluginObjects() {
|
||||||
|
for (PluginEntry entry : this.entries.values()) {
|
||||||
|
entry.plugin = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create plugins objects that have onload set.
|
||||||
|
*/
|
||||||
|
public void startupPlugins() {
|
||||||
|
for (PluginEntry entry : this.entries.values()) {
|
||||||
|
if (entry.onload) {
|
||||||
|
entry.createPlugin(this.app, this.ctx);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Receives a request for execution and fulfills it by finding the appropriate
|
||||||
|
* Java class and calling it's execute method.
|
||||||
*
|
*
|
||||||
* @param multitasking Flag indicating if multitasking is turned on for app
|
* PluginManager.exec can be used either synchronously or async. In either case, a JSON encoded
|
||||||
|
* string is returned that will indicate if any errors have occurred when trying to find
|
||||||
|
* or execute the class denoted by the clazz argument.
|
||||||
|
*
|
||||||
|
* @param service String containing the service to run
|
||||||
|
* @param action String containt the action that the class is supposed to perform. This is
|
||||||
|
* passed to the plugin execute method and it is up to the plugin developer
|
||||||
|
* how to deal with it.
|
||||||
|
* @param callbackId String containing the id of the callback that is execute in JavaScript if
|
||||||
|
* this is an async plugin call.
|
||||||
|
* @param args An Array literal string containing any arguments needed in the
|
||||||
|
* plugin execute method.
|
||||||
|
* @param async Boolean indicating whether the calling JavaScript code is expecting an
|
||||||
|
* immediate return value. If true, either Cordova.callbackSuccess(...) or
|
||||||
|
* Cordova.callbackError(...) is called once the plugin code has executed.
|
||||||
|
*
|
||||||
|
* @return JSON encoded string with a response message and status.
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public String exec(final String service, final String action, final String callbackId, final String jsonArgs, final boolean async) {
|
||||||
|
PluginResult cr = null;
|
||||||
|
boolean runAsync = async;
|
||||||
|
try {
|
||||||
|
final JSONArray args = new JSONArray(jsonArgs);
|
||||||
|
final IPlugin plugin = this.getPlugin(service);
|
||||||
|
final CordovaInterface ctx = this.ctx;
|
||||||
|
if (plugin != null) {
|
||||||
|
runAsync = async && !plugin.isSynch(action);
|
||||||
|
if (runAsync) {
|
||||||
|
// Run this on a different thread so that this one can return back to JS
|
||||||
|
Thread thread = new Thread(new Runnable() {
|
||||||
|
public void run() {
|
||||||
|
try {
|
||||||
|
// Call execute on the plugin so that it can do it's thing
|
||||||
|
PluginResult cr = plugin.execute(action, args, callbackId);
|
||||||
|
int status = cr.getStatus();
|
||||||
|
|
||||||
|
// If no result to be sent and keeping callback, then no need to sent back to JavaScript
|
||||||
|
if ((status == PluginResult.Status.NO_RESULT.ordinal()) && cr.getKeepCallback()) {
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check the success (OK, NO_RESULT & !KEEP_CALLBACK)
|
||||||
|
else if ((status == PluginResult.Status.OK.ordinal()) || (status == PluginResult.Status.NO_RESULT.ordinal())) {
|
||||||
|
ctx.sendJavascript(cr.toSuccessCallbackString(callbackId));
|
||||||
|
}
|
||||||
|
|
||||||
|
// If error
|
||||||
|
else {
|
||||||
|
ctx.sendJavascript(cr.toErrorCallbackString(callbackId));
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
PluginResult cr = new PluginResult(PluginResult.Status.ERROR, e.getMessage());
|
||||||
|
ctx.sendJavascript(cr.toErrorCallbackString(callbackId));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
thread.start();
|
||||||
|
return "";
|
||||||
|
} else {
|
||||||
|
// Call execute on the plugin so that it can do it's thing
|
||||||
|
cr = plugin.execute(action, args, callbackId);
|
||||||
|
|
||||||
|
// If no result to be sent and keeping callback, then no need to sent back to JavaScript
|
||||||
|
if ((cr.getStatus() == PluginResult.Status.NO_RESULT.ordinal()) && cr.getKeepCallback()) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (JSONException e) {
|
||||||
|
System.out.println("ERROR: " + e.toString());
|
||||||
|
cr = new PluginResult(PluginResult.Status.JSON_EXCEPTION);
|
||||||
|
}
|
||||||
|
// if async we have already returned at this point unless there was an error...
|
||||||
|
if (runAsync) {
|
||||||
|
if (cr == null) {
|
||||||
|
cr = new PluginResult(PluginResult.Status.CLASS_NOT_FOUND_EXCEPTION);
|
||||||
|
}
|
||||||
|
ctx.sendJavascript(cr.toErrorCallbackString(callbackId));
|
||||||
|
}
|
||||||
|
return (cr != null ? cr.getJSONString() : "{ status: 0, message: 'all good' }");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the plugin object that implements the service.
|
||||||
|
* If the plugin object does not already exist, then create it.
|
||||||
|
* If the service doesn't exist, then return null.
|
||||||
|
*
|
||||||
|
* @param service The name of the service.
|
||||||
|
* @return IPlugin or null
|
||||||
|
*/
|
||||||
|
private IPlugin getPlugin(String service) {
|
||||||
|
PluginEntry entry = entries.get(service);
|
||||||
|
if (entry == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
IPlugin plugin = entry.plugin;
|
||||||
|
if (plugin == null) {
|
||||||
|
plugin = entry.createPlugin(this.app, this.ctx);
|
||||||
|
}
|
||||||
|
return plugin;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a plugin class that implements a service to the service entry table.
|
||||||
|
* This does not create the plugin object instance.
|
||||||
|
*
|
||||||
|
* @param service The service name
|
||||||
|
* @param className The plugin class name
|
||||||
|
*/
|
||||||
|
public void addService(String service, String className) {
|
||||||
|
PluginEntry entry = new PluginEntry(service, className, false);
|
||||||
|
this.addService(entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a plugin class that implements a service to the service entry table.
|
||||||
|
* This does not create the plugin object instance.
|
||||||
|
*
|
||||||
|
* @param entry The plugin entry
|
||||||
|
*/
|
||||||
|
public void addService(PluginEntry entry) {
|
||||||
|
this.entries.put(entry.service, entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when the system is about to start resuming a previous activity.
|
||||||
|
*
|
||||||
|
* @param multitasking Flag indicating if multitasking is turned on for app
|
||||||
*/
|
*/
|
||||||
public void onPause(boolean multitasking) {
|
public void onPause(boolean multitasking) {
|
||||||
for (IPlugin plugin : this.plugins.values()) {
|
for (PluginEntry entry : this.entries.values()) {
|
||||||
plugin.onPause(multitasking);
|
if (entry.plugin != null) {
|
||||||
|
entry.plugin.onPause(multitasking);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called when the activity will start interacting with the user.
|
* Called when the activity will start interacting with the user.
|
||||||
*
|
*
|
||||||
* @param multitasking Flag indicating if multitasking is turned on for app
|
* @param multitasking Flag indicating if multitasking is turned on for app
|
||||||
*/
|
*/
|
||||||
public void onResume(boolean multitasking) {
|
public void onResume(boolean multitasking) {
|
||||||
for (IPlugin plugin : this.plugins.values()) {
|
for (PluginEntry entry : this.entries.values()) {
|
||||||
plugin.onResume(multitasking);
|
if (entry.plugin != null) {
|
||||||
|
entry.plugin.onResume(multitasking);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The final call you receive before your activity is destroyed.
|
* The final call you receive before your activity is destroyed.
|
||||||
*/
|
*/
|
||||||
public void onDestroy() {
|
public void onDestroy() {
|
||||||
for (IPlugin plugin : this.plugins.values()) {
|
for (PluginEntry entry : this.entries.values()) {
|
||||||
plugin.onDestroy();
|
if (entry.plugin != null) {
|
||||||
|
entry.plugin.onDestroy();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Send a message to all plugins.
|
* Send a message to all plugins.
|
||||||
*
|
*
|
||||||
* @param id The message id
|
* @param id The message id
|
||||||
* @param data The message data
|
* @param data The message data
|
||||||
*/
|
*/
|
||||||
public void postMessage(String id, Object data) {
|
public void postMessage(String id, Object data) {
|
||||||
for (IPlugin plugin : this.plugins.values()) {
|
for (PluginEntry entry : this.entries.values()) {
|
||||||
plugin.onMessage(id, data);
|
if (entry.plugin != null) {
|
||||||
|
entry.plugin.onMessage(id, data);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called when the activity receives a new intent.
|
* Called when the activity receives a new intent.
|
||||||
*/
|
*/
|
||||||
public void onNewIntent(Intent intent) {
|
public void onNewIntent(Intent intent) {
|
||||||
for (IPlugin plugin : this.plugins.values()) {
|
for (PluginEntry entry : this.entries.values()) {
|
||||||
plugin.onNewIntent(intent);
|
if (entry.plugin != null) {
|
||||||
|
entry.plugin.onNewIntent(intent);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called when the URL of the webview changes.
|
* Called when the URL of the webview changes.
|
||||||
*
|
*
|
||||||
* @param url The URL that is being changed to.
|
* @param url The URL that is being changed to.
|
||||||
* @return Return false to allow the URL to load, return true to prevent the URL from loading.
|
* @return Return false to allow the URL to load, return true to prevent the URL from loading.
|
||||||
*/
|
*/
|
||||||
public boolean onOverrideUrlLoading(String url) {
|
public boolean onOverrideUrlLoading(String url) {
|
||||||
Iterator<Entry<String, String>> it = this.urlMap.entrySet().iterator();
|
Iterator<Entry<String, String>> it = this.urlMap.entrySet().iterator();
|
||||||
while (it.hasNext()) {
|
while (it.hasNext()) {
|
||||||
HashMap.Entry<String, String> pairs = it.next();
|
HashMap.Entry<String, String> pairs = it.next();
|
||||||
if (url.startsWith(pairs.getKey())) {
|
if (url.startsWith(pairs.getKey())) {
|
||||||
return this.getPlugin(pairs.getValue()).onOverrideUrlLoading(url);
|
return this.getPlugin(pairs.getValue()).onOverrideUrlLoading(url);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void pluginConfigurationMissing() {
|
private void pluginConfigurationMissing() {
|
||||||
System.err.println("=====================================================================================");
|
System.err.println("=====================================================================================");
|
||||||
System.err.println("ERROR: plugin.xml is missing. Add res/xml/plugins.xml to your project.");
|
System.err.println("ERROR: plugin.xml is missing. Add res/xml/plugins.xml to your project.");
|
||||||
System.err.println("https://git-wip-us.apache.org/repos/asf?p=incubator-cordova-android.git;a=blob;f=framework/res/xml/plugins.xml");
|
System.err.println("https://git-wip-us.apache.org/repos/asf?p=incubator-cordova-android.git;a=blob;f=framework/res/xml/plugins.xml");
|
||||||
System.err.println("=====================================================================================");
|
System.err.println("=====================================================================================");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user