Repositorio del curso CCOM4030 el semestre B91 del proyecto Artesanías con el Instituto de Cultura

PluginManager.java 18KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526
  1. /*
  2. Licensed to the Apache Software Foundation (ASF) under one
  3. or more contributor license agreements. See the NOTICE file
  4. distributed with this work for additional information
  5. regarding copyright ownership. The ASF licenses this file
  6. to you under the Apache License, Version 2.0 (the
  7. "License"); you may not use this file except in compliance
  8. with the License. You may obtain a copy of the License at
  9. http://www.apache.org/licenses/LICENSE-2.0
  10. Unless required by applicable law or agreed to in writing,
  11. software distributed under the License is distributed on an
  12. "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  13. KIND, either express or implied. See the License for the
  14. specific language governing permissions and limitations
  15. under the License.
  16. */
  17. package org.apache.cordova;
  18. import java.util.Collection;
  19. import java.util.LinkedHashMap;
  20. import org.json.JSONException;
  21. import android.content.Intent;
  22. import android.content.res.Configuration;
  23. import android.net.Uri;
  24. import android.os.Bundle;
  25. import android.os.Debug;
  26. /**
  27. * PluginManager is exposed to JavaScript in the Cordova WebView.
  28. *
  29. * Calling native plugin code can be done by calling PluginManager.exec(...)
  30. * from JavaScript.
  31. */
  32. public class PluginManager {
  33. private static String TAG = "PluginManager";
  34. private static final int SLOW_EXEC_WARNING_THRESHOLD = Debug.isDebuggerConnected() ? 60 : 16;
  35. // List of service entries
  36. private final LinkedHashMap<String, CordovaPlugin> pluginMap = new LinkedHashMap<String, CordovaPlugin>();
  37. private final LinkedHashMap<String, PluginEntry> entryMap = new LinkedHashMap<String, PluginEntry>();
  38. private final CordovaInterface ctx;
  39. private final CordovaWebView app;
  40. private boolean isInitialized;
  41. private CordovaPlugin permissionRequester;
  42. public PluginManager(CordovaWebView cordovaWebView, CordovaInterface cordova, Collection<PluginEntry> pluginEntries) {
  43. this.ctx = cordova;
  44. this.app = cordovaWebView;
  45. setPluginEntries(pluginEntries);
  46. }
  47. public Collection<PluginEntry> getPluginEntries() {
  48. return entryMap.values();
  49. }
  50. public void setPluginEntries(Collection<PluginEntry> pluginEntries) {
  51. if (isInitialized) {
  52. this.onPause(false);
  53. this.onDestroy();
  54. pluginMap.clear();
  55. entryMap.clear();
  56. }
  57. for (PluginEntry entry : pluginEntries) {
  58. addService(entry);
  59. }
  60. if (isInitialized) {
  61. startupPlugins();
  62. }
  63. }
  64. /**
  65. * Init when loading a new HTML page into webview.
  66. */
  67. public void init() {
  68. LOG.d(TAG, "init()");
  69. isInitialized = true;
  70. this.onPause(false);
  71. this.onDestroy();
  72. pluginMap.clear();
  73. this.startupPlugins();
  74. }
  75. /**
  76. * Create plugins objects that have onload set.
  77. */
  78. private void startupPlugins() {
  79. for (PluginEntry entry : entryMap.values()) {
  80. // Add a null entry to for each non-startup plugin to avoid ConcurrentModificationException
  81. // When iterating plugins.
  82. if (entry.onload) {
  83. getPlugin(entry.service);
  84. } else {
  85. pluginMap.put(entry.service, null);
  86. }
  87. }
  88. }
  89. /**
  90. * Receives a request for execution and fulfills it by finding the appropriate
  91. * Java class and calling it's execute method.
  92. *
  93. * PluginManager.exec can be used either synchronously or async. In either case, a JSON encoded
  94. * string is returned that will indicate if any errors have occurred when trying to find
  95. * or execute the class denoted by the clazz argument.
  96. *
  97. * @param service String containing the service to run
  98. * @param action String containing the action that the class is supposed to perform. This is
  99. * passed to the plugin execute method and it is up to the plugin developer
  100. * how to deal with it.
  101. * @param callbackId String containing the id of the callback that is execute in JavaScript if
  102. * this is an async plugin call.
  103. * @param rawArgs An Array literal string containing any arguments needed in the
  104. * plugin execute method.
  105. */
  106. public void exec(final String service, final String action, final String callbackId, final String rawArgs) {
  107. CordovaPlugin plugin = getPlugin(service);
  108. if (plugin == null) {
  109. LOG.d(TAG, "exec() call to unknown plugin: " + service);
  110. PluginResult cr = new PluginResult(PluginResult.Status.CLASS_NOT_FOUND_EXCEPTION);
  111. app.sendPluginResult(cr, callbackId);
  112. return;
  113. }
  114. CallbackContext callbackContext = new CallbackContext(callbackId, app);
  115. try {
  116. long pluginStartTime = System.currentTimeMillis();
  117. boolean wasValidAction = plugin.execute(action, rawArgs, callbackContext);
  118. long duration = System.currentTimeMillis() - pluginStartTime;
  119. if (duration > SLOW_EXEC_WARNING_THRESHOLD) {
  120. LOG.w(TAG, "THREAD WARNING: exec() call to " + service + "." + action + " blocked the main thread for " + duration + "ms. Plugin should use CordovaInterface.getThreadPool().");
  121. }
  122. if (!wasValidAction) {
  123. PluginResult cr = new PluginResult(PluginResult.Status.INVALID_ACTION);
  124. callbackContext.sendPluginResult(cr);
  125. }
  126. } catch (JSONException e) {
  127. PluginResult cr = new PluginResult(PluginResult.Status.JSON_EXCEPTION);
  128. callbackContext.sendPluginResult(cr);
  129. } catch (Exception e) {
  130. LOG.e(TAG, "Uncaught exception from plugin", e);
  131. callbackContext.error(e.getMessage());
  132. }
  133. }
  134. /**
  135. * Get the plugin object that implements the service.
  136. * If the plugin object does not already exist, then create it.
  137. * If the service doesn't exist, then return null.
  138. *
  139. * @param service The name of the service.
  140. * @return CordovaPlugin or null
  141. */
  142. public CordovaPlugin getPlugin(String service) {
  143. CordovaPlugin ret = pluginMap.get(service);
  144. if (ret == null) {
  145. PluginEntry pe = entryMap.get(service);
  146. if (pe == null) {
  147. return null;
  148. }
  149. if (pe.plugin != null) {
  150. ret = pe.plugin;
  151. } else {
  152. ret = instantiatePlugin(pe.pluginClass);
  153. }
  154. ret.privateInitialize(service, ctx, app, app.getPreferences());
  155. pluginMap.put(service, ret);
  156. }
  157. return ret;
  158. }
  159. /**
  160. * Add a plugin class that implements a service to the service entry table.
  161. * This does not create the plugin object instance.
  162. *
  163. * @param service The service name
  164. * @param className The plugin class name
  165. */
  166. public void addService(String service, String className) {
  167. PluginEntry entry = new PluginEntry(service, className, false);
  168. this.addService(entry);
  169. }
  170. /**
  171. * Add a plugin class that implements a service to the service entry table.
  172. * This does not create the plugin object instance.
  173. *
  174. * @param entry The plugin entry
  175. */
  176. public void addService(PluginEntry entry) {
  177. this.entryMap.put(entry.service, entry);
  178. if (entry.plugin != null) {
  179. entry.plugin.privateInitialize(entry.service, ctx, app, app.getPreferences());
  180. pluginMap.put(entry.service, entry.plugin);
  181. }
  182. }
  183. /**
  184. * Called when the system is about to start resuming a previous activity.
  185. *
  186. * @param multitasking Flag indicating if multitasking is turned on for app
  187. */
  188. public void onPause(boolean multitasking) {
  189. for (CordovaPlugin plugin : this.pluginMap.values()) {
  190. if (plugin != null) {
  191. plugin.onPause(multitasking);
  192. }
  193. }
  194. }
  195. /**
  196. * Called when the system received an HTTP authentication request. Plugins can use
  197. * the supplied HttpAuthHandler to process this auth challenge.
  198. *
  199. * @param view The WebView that is initiating the callback
  200. * @param handler The HttpAuthHandler used to set the WebView's response
  201. * @param host The host requiring authentication
  202. * @param realm The realm for which authentication is required
  203. *
  204. * @return Returns True if there is a plugin which will resolve this auth challenge, otherwise False
  205. *
  206. */
  207. public boolean onReceivedHttpAuthRequest(CordovaWebView view, ICordovaHttpAuthHandler handler, String host, String realm) {
  208. for (CordovaPlugin plugin : this.pluginMap.values()) {
  209. if (plugin != null && plugin.onReceivedHttpAuthRequest(app, handler, host, realm)) {
  210. return true;
  211. }
  212. }
  213. return false;
  214. }
  215. /**
  216. * Called when he system received an SSL client certificate request. Plugin can use
  217. * the supplied ClientCertRequest to process this certificate challenge.
  218. *
  219. * @param view The WebView that is initiating the callback
  220. * @param request The client certificate request
  221. *
  222. * @return Returns True if plugin will resolve this auth challenge, otherwise False
  223. *
  224. */
  225. public boolean onReceivedClientCertRequest(CordovaWebView view, ICordovaClientCertRequest request) {
  226. for (CordovaPlugin plugin : this.pluginMap.values()) {
  227. if (plugin != null && plugin.onReceivedClientCertRequest(app, request)) {
  228. return true;
  229. }
  230. }
  231. return false;
  232. }
  233. /**
  234. * Called when the activity will start interacting with the user.
  235. *
  236. * @param multitasking Flag indicating if multitasking is turned on for app
  237. */
  238. public void onResume(boolean multitasking) {
  239. for (CordovaPlugin plugin : this.pluginMap.values()) {
  240. if (plugin != null) {
  241. plugin.onResume(multitasking);
  242. }
  243. }
  244. }
  245. /**
  246. * Called when the activity is becoming visible to the user.
  247. */
  248. public void onStart() {
  249. for (CordovaPlugin plugin : this.pluginMap.values()) {
  250. if (plugin != null) {
  251. plugin.onStart();
  252. }
  253. }
  254. }
  255. /**
  256. * Called when the activity is no longer visible to the user.
  257. */
  258. public void onStop() {
  259. for (CordovaPlugin plugin : this.pluginMap.values()) {
  260. if (plugin != null) {
  261. plugin.onStop();
  262. }
  263. }
  264. }
  265. /**
  266. * The final call you receive before your activity is destroyed.
  267. */
  268. public void onDestroy() {
  269. for (CordovaPlugin plugin : this.pluginMap.values()) {
  270. if (plugin != null) {
  271. plugin.onDestroy();
  272. }
  273. }
  274. }
  275. /**
  276. * Send a message to all plugins.
  277. *
  278. * @param id The message id
  279. * @param data The message data
  280. * @return Object to stop propagation or null
  281. */
  282. public Object postMessage(String id, Object data) {
  283. for (CordovaPlugin plugin : this.pluginMap.values()) {
  284. if (plugin != null) {
  285. Object obj = plugin.onMessage(id, data);
  286. if (obj != null) {
  287. return obj;
  288. }
  289. }
  290. }
  291. return ctx.onMessage(id, data);
  292. }
  293. /**
  294. * Called when the activity receives a new intent.
  295. */
  296. public void onNewIntent(Intent intent) {
  297. for (CordovaPlugin plugin : this.pluginMap.values()) {
  298. if (plugin != null) {
  299. plugin.onNewIntent(intent);
  300. }
  301. }
  302. }
  303. /**
  304. * Called when the webview is going to request an external resource.
  305. *
  306. * This delegates to the installed plugins, and returns true/false for the
  307. * first plugin to provide a non-null result. If no plugins respond, then
  308. * the default policy is applied.
  309. *
  310. * @param url The URL that is being requested.
  311. * @return Returns true to allow the resource to load,
  312. * false to block the resource.
  313. */
  314. public boolean shouldAllowRequest(String url) {
  315. for (PluginEntry entry : this.entryMap.values()) {
  316. CordovaPlugin plugin = pluginMap.get(entry.service);
  317. if (plugin != null) {
  318. Boolean result = plugin.shouldAllowRequest(url);
  319. if (result != null) {
  320. return result;
  321. }
  322. }
  323. }
  324. // Default policy:
  325. if (url.startsWith("blob:") || url.startsWith("data:") || url.startsWith("about:blank")) {
  326. return true;
  327. }
  328. // TalkBack requires this, so allow it by default.
  329. if (url.startsWith("https://ssl.gstatic.com/accessibility/javascript/android/")) {
  330. return true;
  331. }
  332. if (url.startsWith("file://")) {
  333. //This directory on WebKit/Blink based webviews contains SQLite databases!
  334. //DON'T CHANGE THIS UNLESS YOU KNOW WHAT YOU'RE DOING!
  335. return !url.contains("/app_webview/");
  336. }
  337. return false;
  338. }
  339. /**
  340. * Called when the webview is going to change the URL of the loaded content.
  341. *
  342. * This delegates to the installed plugins, and returns true/false for the
  343. * first plugin to provide a non-null result. If no plugins respond, then
  344. * the default policy is applied.
  345. *
  346. * @param url The URL that is being requested.
  347. * @return Returns true to allow the navigation,
  348. * false to block the navigation.
  349. */
  350. public boolean shouldAllowNavigation(String url) {
  351. for (PluginEntry entry : this.entryMap.values()) {
  352. CordovaPlugin plugin = pluginMap.get(entry.service);
  353. if (plugin != null) {
  354. Boolean result = plugin.shouldAllowNavigation(url);
  355. if (result != null) {
  356. return result;
  357. }
  358. }
  359. }
  360. // Default policy:
  361. return url.startsWith("file://") || url.startsWith("about:blank");
  362. }
  363. /**
  364. * Called when the webview is requesting the exec() bridge be enabled.
  365. */
  366. public boolean shouldAllowBridgeAccess(String url) {
  367. for (PluginEntry entry : this.entryMap.values()) {
  368. CordovaPlugin plugin = pluginMap.get(entry.service);
  369. if (plugin != null) {
  370. Boolean result = plugin.shouldAllowBridgeAccess(url);
  371. if (result != null) {
  372. return result;
  373. }
  374. }
  375. }
  376. // Default policy:
  377. return url.startsWith("file://");
  378. }
  379. /**
  380. * Called when the webview is going not going to navigate, but may launch
  381. * an Intent for an URL.
  382. *
  383. * This delegates to the installed plugins, and returns true/false for the
  384. * first plugin to provide a non-null result. If no plugins respond, then
  385. * the default policy is applied.
  386. *
  387. * @param url The URL that is being requested.
  388. * @return Returns true to allow the URL to launch an intent,
  389. * false to block the intent.
  390. */
  391. public Boolean shouldOpenExternalUrl(String url) {
  392. for (PluginEntry entry : this.entryMap.values()) {
  393. CordovaPlugin plugin = pluginMap.get(entry.service);
  394. if (plugin != null) {
  395. Boolean result = plugin.shouldOpenExternalUrl(url);
  396. if (result != null) {
  397. return result;
  398. }
  399. }
  400. }
  401. // Default policy:
  402. // External URLs are not allowed
  403. return false;
  404. }
  405. /**
  406. * Called when the URL of the webview changes.
  407. *
  408. * @param url The URL that is being changed to.
  409. * @return Return false to allow the URL to load, return true to prevent the URL from loading.
  410. */
  411. public boolean onOverrideUrlLoading(String url) {
  412. for (PluginEntry entry : this.entryMap.values()) {
  413. CordovaPlugin plugin = pluginMap.get(entry.service);
  414. if (plugin != null && plugin.onOverrideUrlLoading(url)) {
  415. return true;
  416. }
  417. }
  418. return false;
  419. }
  420. /**
  421. * Called when the app navigates or refreshes.
  422. */
  423. public void onReset() {
  424. for (CordovaPlugin plugin : this.pluginMap.values()) {
  425. if (plugin != null) {
  426. plugin.onReset();
  427. }
  428. }
  429. }
  430. Uri remapUri(Uri uri) {
  431. for (CordovaPlugin plugin : this.pluginMap.values()) {
  432. if (plugin != null) {
  433. Uri ret = plugin.remapUri(uri);
  434. if (ret != null) {
  435. return ret;
  436. }
  437. }
  438. }
  439. return null;
  440. }
  441. /**
  442. * Create a plugin based on class name.
  443. */
  444. private CordovaPlugin instantiatePlugin(String className) {
  445. CordovaPlugin ret = null;
  446. try {
  447. Class<?> c = null;
  448. if ((className != null) && !("".equals(className))) {
  449. c = Class.forName(className);
  450. }
  451. if (c != null & CordovaPlugin.class.isAssignableFrom(c)) {
  452. ret = (CordovaPlugin) c.newInstance();
  453. }
  454. } catch (Exception e) {
  455. e.printStackTrace();
  456. System.out.println("Error adding plugin " + className + ".");
  457. }
  458. return ret;
  459. }
  460. /**
  461. * Called by the system when the device configuration changes while your activity is running.
  462. *
  463. * @param newConfig The new device configuration
  464. */
  465. public void onConfigurationChanged(Configuration newConfig) {
  466. for (CordovaPlugin plugin : this.pluginMap.values()) {
  467. if (plugin != null) {
  468. plugin.onConfigurationChanged(newConfig);
  469. }
  470. }
  471. }
  472. public Bundle onSaveInstanceState() {
  473. Bundle state = new Bundle();
  474. for (CordovaPlugin plugin : this.pluginMap.values()) {
  475. if (plugin != null) {
  476. Bundle pluginState = plugin.onSaveInstanceState();
  477. if(pluginState != null) {
  478. state.putBundle(plugin.getServiceName(), pluginState);
  479. }
  480. }
  481. }
  482. return state;
  483. }
  484. }