From 7e70d762320420dddf04e61ab2543721566d5eaf Mon Sep 17 00:00:00 2001 From: Bryce Curtis Date: Mon, 19 Mar 2012 16:20:57 -0500 Subject: [PATCH] [CB-352] Support initializing DroidGap with existing WebView, WebViewClient and webViewChrome. [CB-353] Create PluginEntry object to use by PluginManager. --- .../apache/cordova/CordovaWebViewClient.java | 2 +- .../src/org/apache/cordova/DroidGap.java | 54 +- .../org/apache/cordova/api/PluginEntry.java | 119 ++++ .../org/apache/cordova/api/PluginManager.java | 536 +++++++++--------- 4 files changed, 425 insertions(+), 286 deletions(-) create mode 100755 framework/src/org/apache/cordova/api/PluginEntry.java diff --git a/framework/src/org/apache/cordova/CordovaWebViewClient.java b/framework/src/org/apache/cordova/CordovaWebViewClient.java index 042f743c..03ed2c1f 100755 --- a/framework/src/org/apache/cordova/CordovaWebViewClient.java +++ b/framework/src/org/apache/cordova/CordovaWebViewClient.java @@ -64,7 +64,7 @@ public class CordovaWebViewClient extends WebViewClient { public boolean shouldOverrideUrlLoading(WebView view, String url) { // 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) diff --git a/framework/src/org/apache/cordova/DroidGap.java b/framework/src/org/apache/cordova/DroidGap.java index 3a138ff3..4d1ae72e 100755 --- a/framework/src/org/apache/cordova/DroidGap.java +++ b/framework/src/org/apache/cordova/DroidGap.java @@ -56,6 +56,7 @@ import android.view.View; import android.view.ViewGroup; import android.view.Window; import android.view.WindowManager; +import android.webkit.WebChromeClient; import android.webkit.WebSettings; import android.webkit.WebSettings.LayoutAlgorithm; 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() { + 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()"); - // Create web container - this.appView = new WebView(DroidGap.this); + // Set up web container + this.appView = webView; this.appView.setId(100); - + this.appView.setLayoutParams(new LinearLayout.LayoutParams( ViewGroup.LayoutParams.FILL_PARENT, ViewGroup.LayoutParams.FILL_PARENT, 1.0F)); - this.appView.setWebChromeClient(new CordovaChromeClient(DroidGap.this)); - this.setWebViewClient(this.appView, new CordovaWebViewClient(this)); + this.appView.setWebChromeClient(webChromeClient); + this.setWebViewClient(this.appView, webViewClient); this.appView.setInitialScale(0); this.appView.setVerticalScrollBarEnabled(false); @@ -393,6 +405,9 @@ public class DroidGap extends Activity implements CordovaInterface { // Clear cancel flag 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 { me.callbackServer.reinit(url); } - if (me.pluginManager == null) { - me.pluginManager = new PluginManager(me.appView, me); - } - else { - me.pluginManager.reinit(); - } + me.pluginManager.init(); // If loadingDialog property, then show the App loading dialog for first page of app 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');};"); // 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 (!this.keepRunning) { @@ -852,7 +864,9 @@ public class DroidGap extends Activity implements CordovaInterface { super.onNewIntent(intent); //Forward to plugins - this.pluginManager.onNewIntent(intent); + if (this.pluginManager != null) { + this.pluginManager.onNewIntent(intent); + } } @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');};"); // 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 (!this.keepRunning || this.activityResultKeepRunning) { @@ -942,7 +958,9 @@ public class DroidGap extends Activity implements CordovaInterface { */ @Deprecated public void addService(String serviceType, String className) { - this.pluginManager.addService(serviceType, className); + if (this.pluginManager != null) { + this.pluginManager.addService(serviceType, className); + } } /** diff --git a/framework/src/org/apache/cordova/api/PluginEntry.java b/framework/src/org/apache/cordova/api/PluginEntry.java new file mode 100755 index 00000000..450fa4cb --- /dev/null +++ b/framework/src/org/apache/cordova/api/PluginEntry.java @@ -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; + } +} diff --git a/framework/src/org/apache/cordova/api/PluginManager.java b/framework/src/org/apache/cordova/api/PluginManager.java index 43676698..b2534278 100755 --- a/framework/src/org/apache/cordova/api/PluginManager.java +++ b/framework/src/org/apache/cordova/api/PluginManager.java @@ -15,7 +15,7 @@ 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 java.io.IOException; @@ -29,7 +29,6 @@ import org.xmlpull.v1.XmlPullParserException; import android.content.Intent; import android.content.res.XmlResourceParser; -import android.util.Log; import android.webkit.WebView; /** @@ -39,321 +38,324 @@ import android.webkit.WebView; * from JavaScript. */ public class PluginManager { + private static String TAG = "PluginManager"; + + // List of service entries + private final HashMap entries = new HashMap(); + + private final CordovaInterface ctx; + private final WebView app; + + // Flag to track first time through + private boolean firstRun; - private HashMap plugins = new HashMap(); - private HashMap services = new HashMap(); - - private final CordovaInterface ctx; - private final WebView app; - // 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 - protected HashMap urlMap = new HashMap(); - - /** - * 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(); - } - - /** - * 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; - } + protected HashMap urlMap = new HashMap(); /** - * Add plugin to be loaded and cached. This creates an instance of the plugin. - * If plugin is already created, then just return it. + * Constructor. * - * @param className The class to load - * @param clazz The class object (must be a class object of the className) - * @param callbackId The callback id to use when calling back into JavaScript - * @return The plugin + * @param app + * @param ctx */ - @SuppressWarnings("unchecked") - private IPlugin addPlugin(String pluginName, String className) { - try { - Class c = getClassByName(className); - 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); + public PluginManager(WebView app, CordovaInterface ctx) { + this.ctx = ctx; + this.app = app; + this.firstRun = true; } /** - * 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) { - for (IPlugin plugin : this.plugins.values()) { - plugin.onPause(multitasking); + for (PluginEntry entry : this.entries.values()) { + 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) { - for (IPlugin plugin : this.plugins.values()) { - plugin.onResume(multitasking); + for (PluginEntry entry : this.entries.values()) { + 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() { - for (IPlugin plugin : this.plugins.values()) { - plugin.onDestroy(); + for (PluginEntry entry : this.entries.values()) { + 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 data The message data + * @param id The message id + * @param data The message data */ public void postMessage(String id, Object data) { - for (IPlugin plugin : this.plugins.values()) { - plugin.onMessage(id, data); + for (PluginEntry entry : this.entries.values()) { + 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) { - for (IPlugin plugin : this.plugins.values()) { - plugin.onNewIntent(intent); + for (PluginEntry entry : this.entries.values()) { + if (entry.plugin != null) { + entry.plugin.onNewIntent(intent); + } } } /** * Called when the URL of the webview changes. * - * @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. + * @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. */ public boolean onOverrideUrlLoading(String url) { - Iterator> it = this.urlMap.entrySet().iterator(); + Iterator> it = this.urlMap.entrySet().iterator(); while (it.hasNext()) { HashMap.Entry pairs = it.next(); 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() { - System.err.println("====================================================================================="); - 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("====================================================================================="); - } + private void pluginConfigurationMissing() { + System.err.println("====================================================================================="); + 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("====================================================================================="); + } }