LocalNotification.java 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629
  1. /*
  2. * Apache 2.0 License
  3. *
  4. * Copyright (c) Sebastian Katzer 2017
  5. *
  6. * This file contains Original Code and/or Modifications of Original Code
  7. * as defined in and that are subject to the Apache License
  8. * Version 2.0 (the 'License'). You may not use this file except in
  9. * compliance with the License. Please obtain a copy of the License at
  10. * http://opensource.org/licenses/Apache-2.0/ and read it before using this
  11. * file.
  12. *
  13. * The Original Code and all software distributed under the License are
  14. * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
  15. * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
  16. * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
  17. * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
  18. * Please see the License for the specific language governing rights and
  19. * limitations under the License.
  20. */
  21. package de.appplant.cordova.plugin.localnotification;
  22. import android.app.Activity;
  23. import org.apache.cordova.CallbackContext;
  24. import org.apache.cordova.CordovaInterface;
  25. import org.apache.cordova.CordovaPlugin;
  26. import org.apache.cordova.CordovaWebView;
  27. import org.apache.cordova.PluginResult;
  28. import org.json.JSONArray;
  29. import org.json.JSONException;
  30. import org.json.JSONObject;
  31. import java.lang.reflect.Method;
  32. import java.util.ArrayList;
  33. import java.util.List;
  34. import java.lang.Exception;
  35. import de.appplant.cordova.plugin.notification.Manager;
  36. import de.appplant.cordova.plugin.notification.Notification;
  37. /**
  38. * This plugin utilizes the Android AlarmManager in combination with local
  39. * notifications. When a local notification is scheduled the alarm manager takes
  40. * care of firing the event. When the event is processed, a notification is put
  41. * in the Android notification center and status bar.
  42. */
  43. public class LocalNotification extends CordovaPlugin {
  44. // Reference to the web view for static access
  45. private static CordovaWebView webView = null;
  46. // Indicates if the device is ready (to receive events)
  47. private static Boolean deviceready = false;
  48. // To inform the user about the state of the app in callbacks
  49. protected static Boolean isInBackground = true;
  50. // Queues all events before deviceready
  51. private static ArrayList<String> eventQueue = new ArrayList<String>();
  52. /**
  53. * Called after plugin construction and fields have been initialized.
  54. * Prefer to use pluginInitialize instead since there is no value in
  55. * having parameters on the initialize() function.
  56. *
  57. * pluginInitialize is not available for cordova 3.0-3.5 !
  58. */
  59. @Override
  60. public void initialize (CordovaInterface cordova, CordovaWebView webView) {
  61. LocalNotification.webView = super.webView;
  62. }
  63. /**
  64. * Called when the system is about to start resuming a previous activity.
  65. *
  66. * @param multitasking
  67. * Flag indicating if multitasking is turned on for app
  68. */
  69. @Override
  70. public void onPause(boolean multitasking) {
  71. super.onPause(multitasking);
  72. isInBackground = true;
  73. }
  74. /**
  75. * Called when the activity will start interacting with the user.
  76. *
  77. * @param multitasking
  78. * Flag indicating if multitasking is turned on for app
  79. */
  80. @Override
  81. public void onResume(boolean multitasking) {
  82. super.onResume(multitasking);
  83. isInBackground = false;
  84. deviceready();
  85. }
  86. /**
  87. * The final call you receive before your activity is destroyed.
  88. */
  89. @Override
  90. public void onDestroy() {
  91. deviceready = false;
  92. isInBackground = true;
  93. }
  94. /**
  95. * Executes the request.
  96. *
  97. * This method is called from the WebView thread. To do a non-trivial
  98. * amount of work, use:
  99. * cordova.getThreadPool().execute(runnable);
  100. *
  101. * To run on the UI thread, use:
  102. * cordova.getActivity().runOnUiThread(runnable);
  103. *
  104. * @param action
  105. * The action to execute.
  106. * @param args
  107. * The exec() arguments in JSON form.
  108. * @param command
  109. * The callback context used when calling back into JavaScript.
  110. * @return
  111. * Whether the action was valid.
  112. */
  113. @Override
  114. public boolean execute (final String action, final JSONArray args,
  115. final CallbackContext command) throws JSONException {
  116. Notification.setDefaultTriggerReceiver(TriggerReceiver.class);
  117. cordova.getThreadPool().execute(new Runnable() {
  118. public void run() {
  119. if (action.equals("schedule")) {
  120. schedule(args);
  121. command.success();
  122. }
  123. else if (action.equals("update")) {
  124. update(args);
  125. command.success();
  126. }
  127. else if (action.equals("cancel")) {
  128. cancel(args);
  129. command.success();
  130. }
  131. else if (action.equals("cancelAll")) {
  132. cancelAll();
  133. command.success();
  134. }
  135. else if (action.equals("clear")) {
  136. clear(args);
  137. command.success();
  138. }
  139. else if (action.equals("clearAll")) {
  140. clearAll();
  141. command.success();
  142. }
  143. else if (action.equals("isPresent")) {
  144. isPresent(args.optInt(0), command);
  145. }
  146. else if (action.equals("isScheduled")) {
  147. isScheduled(args.optInt(0), command);
  148. }
  149. else if (action.equals("isTriggered")) {
  150. isTriggered(args.optInt(0), command);
  151. }
  152. else if (action.equals("getAllIds")) {
  153. getAllIds(command);
  154. }
  155. else if (action.equals("getScheduledIds")) {
  156. getScheduledIds(command);
  157. }
  158. else if (action.equals("getTriggeredIds")) {
  159. getTriggeredIds(command);
  160. }
  161. else if (action.equals("getSingle")) {
  162. getSingle(args, command);
  163. }
  164. else if (action.equals("getSingleScheduled")) {
  165. getSingleScheduled(args, command);
  166. }
  167. else if (action.equals("getSingleTriggered")) {
  168. getSingleTriggered(args, command);
  169. }
  170. else if (action.equals("getAll")) {
  171. getAll(args, command);
  172. }
  173. else if (action.equals("getScheduled")) {
  174. getScheduled(args, command);
  175. }
  176. else if (action.equals("getTriggered")) {
  177. getTriggered(args, command);
  178. }
  179. else if (action.equals("deviceready")) {
  180. deviceready();
  181. }
  182. }
  183. });
  184. return true;
  185. }
  186. /**
  187. * Schedule multiple local notifications.
  188. *
  189. * @param notifications
  190. * Properties for each local notification
  191. */
  192. private void schedule (JSONArray notifications) {
  193. for (int i = 0; i < notifications.length(); i++) {
  194. JSONObject options = notifications.optJSONObject(i);
  195. try {
  196. Notification notification =
  197. getNotificationMgr().schedule(options, TriggerReceiver.class);
  198. fireEvent("schedule", notification);
  199. }
  200. catch(Exception generic) {
  201. //silently ignore the exception
  202. //on some samsung devices there is a known bug where a 500 alarms limit can crash the app
  203. //http://developer.samsung.com/forum/board/thread/view.do?boardName=General&messageId=280286&listLines=15&startId=zzzzz%7E&searchSubId=0000000001
  204. }
  205. }
  206. }
  207. /**
  208. * Update multiple local notifications.
  209. *
  210. * @param updates
  211. * Notification properties including their IDs
  212. */
  213. private void update (JSONArray updates) {
  214. for (int i = 0; i < updates.length(); i++) {
  215. JSONObject update = updates.optJSONObject(i);
  216. int id = update.optInt("id", 0);
  217. Notification notification =
  218. getNotificationMgr().update(id, update, TriggerReceiver.class);
  219. if (notification == null)
  220. continue;
  221. fireEvent("update", notification);
  222. }
  223. }
  224. /**
  225. * Cancel multiple local notifications.
  226. *
  227. * @param ids
  228. * Set of local notification IDs
  229. */
  230. private void cancel (JSONArray ids) {
  231. for (int i = 0; i < ids.length(); i++) {
  232. int id = ids.optInt(i, 0);
  233. Notification notification =
  234. getNotificationMgr().cancel(id);
  235. if (notification == null)
  236. continue;
  237. fireEvent("cancel", notification);
  238. }
  239. }
  240. /**
  241. * Cancel all scheduled notifications.
  242. */
  243. private void cancelAll() {
  244. getNotificationMgr().cancelAll();
  245. fireEvent("cancelall");
  246. }
  247. /**
  248. * Clear multiple local notifications without canceling them.
  249. *
  250. * @param ids
  251. * Set of local notification IDs
  252. */
  253. private void clear(JSONArray ids){
  254. for (int i = 0; i < ids.length(); i++) {
  255. int id = ids.optInt(i, 0);
  256. Notification notification =
  257. getNotificationMgr().clear(id);
  258. if (notification == null)
  259. continue;
  260. fireEvent("clear", notification);
  261. }
  262. }
  263. /**
  264. * Clear all triggered notifications without canceling them.
  265. */
  266. private void clearAll() {
  267. getNotificationMgr().clearAll();
  268. fireEvent("clearall");
  269. }
  270. /**
  271. * If a notification with an ID is present.
  272. *
  273. * @param id
  274. * Notification ID
  275. * @param command
  276. * The callback context used when calling back into JavaScript.
  277. */
  278. private void isPresent (int id, CallbackContext command) {
  279. boolean exist = getNotificationMgr().exist(id);
  280. PluginResult result = new PluginResult(
  281. PluginResult.Status.OK, exist);
  282. command.sendPluginResult(result);
  283. }
  284. /**
  285. * If a notification with an ID is scheduled.
  286. *
  287. * @param id
  288. * Notification ID
  289. * @param command
  290. * The callback context used when calling back into JavaScript.
  291. */
  292. private void isScheduled (int id, CallbackContext command) {
  293. boolean exist = getNotificationMgr().exist(
  294. id, Notification.Type.SCHEDULED);
  295. PluginResult result = new PluginResult(
  296. PluginResult.Status.OK, exist);
  297. command.sendPluginResult(result);
  298. }
  299. /**
  300. * If a notification with an ID is triggered.
  301. *
  302. * @param id
  303. * Notification ID
  304. * @param command
  305. * The callback context used when calling back into JavaScript.
  306. */
  307. private void isTriggered (int id, CallbackContext command) {
  308. boolean exist = getNotificationMgr().exist(
  309. id, Notification.Type.TRIGGERED);
  310. PluginResult result = new PluginResult(
  311. PluginResult.Status.OK, exist);
  312. command.sendPluginResult(result);
  313. }
  314. /**
  315. * Set of IDs from all existent notifications.
  316. *
  317. * @param command
  318. * The callback context used when calling back into JavaScript.
  319. */
  320. private void getAllIds (CallbackContext command) {
  321. List<Integer> ids = getNotificationMgr().getIds();
  322. command.success(new JSONArray(ids));
  323. }
  324. /**
  325. * Set of IDs from all scheduled notifications.
  326. *
  327. * @param command
  328. * The callback context used when calling back into JavaScript.
  329. */
  330. private void getScheduledIds (CallbackContext command) {
  331. List<Integer> ids = getNotificationMgr().getIdsByType(
  332. Notification.Type.SCHEDULED);
  333. command.success(new JSONArray(ids));
  334. }
  335. /**
  336. * Set of IDs from all triggered notifications.
  337. *
  338. * @param command
  339. * The callback context used when calling back into JavaScript.
  340. */
  341. private void getTriggeredIds (CallbackContext command) {
  342. List<Integer> ids = getNotificationMgr().getIdsByType(
  343. Notification.Type.TRIGGERED);
  344. command.success(new JSONArray(ids));
  345. }
  346. /**
  347. * Options from local notification.
  348. *
  349. * @param ids
  350. * Set of local notification IDs
  351. * @param command
  352. * The callback context used when calling back into JavaScript.
  353. */
  354. private void getSingle (JSONArray ids, CallbackContext command) {
  355. getOptions(ids.optString(0), Notification.Type.ALL, command);
  356. }
  357. /**
  358. * Options from scheduled notification.
  359. *
  360. * @param ids
  361. * Set of local notification IDs
  362. * @param command
  363. * The callback context used when calling back into JavaScript.
  364. */
  365. private void getSingleScheduled (JSONArray ids, CallbackContext command) {
  366. getOptions(ids.optString(0), Notification.Type.SCHEDULED, command);
  367. }
  368. /**
  369. * Options from triggered notification.
  370. *
  371. * @param ids
  372. * Set of local notification IDs
  373. * @param command
  374. * The callback context used when calling back into JavaScript.
  375. */
  376. private void getSingleTriggered (JSONArray ids, CallbackContext command) {
  377. getOptions(ids.optString(0), Notification.Type.TRIGGERED, command);
  378. }
  379. /**
  380. * Set of options from local notification.
  381. *
  382. * @param ids
  383. * Set of local notification IDs
  384. * @param command
  385. * The callback context used when calling back into JavaScript.
  386. */
  387. private void getAll (JSONArray ids, CallbackContext command) {
  388. getOptions(ids, Notification.Type.ALL, command);
  389. }
  390. /**
  391. * Set of options from scheduled notifications.
  392. *
  393. * @param ids
  394. * Set of local notification IDs
  395. * @param command
  396. * The callback context used when calling back into JavaScript.
  397. */
  398. private void getScheduled (JSONArray ids, CallbackContext command) {
  399. getOptions(ids, Notification.Type.SCHEDULED, command);
  400. }
  401. /**
  402. * Set of options from triggered notifications.
  403. *
  404. * @param ids
  405. * Set of local notification IDs
  406. * @param command
  407. * The callback context used when calling back into JavaScript.
  408. */
  409. private void getTriggered (JSONArray ids, CallbackContext command) {
  410. getOptions(ids, Notification.Type.TRIGGERED, command);
  411. }
  412. /**
  413. * Options from local notification.
  414. *
  415. * @param id
  416. * Set of local notification IDs
  417. * @param type
  418. * The local notification life cycle type
  419. * @param command
  420. * The callback context used when calling back into JavaScript.
  421. */
  422. private void getOptions (String id, Notification.Type type,
  423. CallbackContext command) {
  424. JSONArray ids = new JSONArray().put(id);
  425. PluginResult result;
  426. List<JSONObject> options =
  427. getNotificationMgr().getOptionsBy(type, toList(ids));
  428. if (options.isEmpty()) {
  429. // Status.NO_RESULT led to no callback invocation :(
  430. // Status.OK led to no NPE and crash
  431. result = new PluginResult(PluginResult.Status.NO_RESULT);
  432. } else {
  433. result = new PluginResult(PluginResult.Status.OK, options.get(0));
  434. }
  435. command.sendPluginResult(result);
  436. }
  437. /**
  438. * Set of options from local notifications.
  439. *
  440. * @param ids
  441. * Set of local notification IDs
  442. * @param type
  443. * The local notification life cycle type
  444. * @param command
  445. * The callback context used when calling back into JavaScript.
  446. */
  447. private void getOptions (JSONArray ids, Notification.Type type,
  448. CallbackContext command) {
  449. List<JSONObject> options;
  450. if (ids.length() == 0) {
  451. options = getNotificationMgr().getOptionsByType(type);
  452. } else {
  453. options = getNotificationMgr().getOptionsBy(type, toList(ids));
  454. }
  455. command.success(new JSONArray(options));
  456. }
  457. /**
  458. * Call all pending callbacks after the deviceready event has been fired.
  459. */
  460. private static synchronized void deviceready () {
  461. isInBackground = false;
  462. deviceready = true;
  463. for (String js : eventQueue) {
  464. sendJavascript(js);
  465. }
  466. eventQueue.clear();
  467. }
  468. /**
  469. * Fire given event on JS side. Does inform all event listeners.
  470. *
  471. * @param event
  472. * The event name
  473. */
  474. private void fireEvent (String event) {
  475. fireEvent(event, null);
  476. }
  477. /**
  478. * Fire given event on JS side. Does inform all event listeners.
  479. *
  480. * @param event
  481. * The event name
  482. * @param notification
  483. * Optional local notification to pass the id and properties.
  484. */
  485. static void fireEvent (String event, Notification notification) {
  486. String state = getApplicationState();
  487. String params = "\"" + state + "\"";
  488. if (notification != null) {
  489. params = notification.toString() + "," + params;
  490. }
  491. String js = "cordova.plugins.notification.local.core.fireEvent(" +
  492. "\"" + event + "\"," + params + ")";
  493. sendJavascript(js);
  494. }
  495. /**
  496. * Use this instead of deprecated sendJavascript
  497. *
  498. * @param js
  499. * JS code snippet as string
  500. */
  501. private static synchronized void sendJavascript(final String js) {
  502. if (!deviceready) {
  503. eventQueue.add(js);
  504. return;
  505. }
  506. Runnable jsLoader = new Runnable() {
  507. public void run() {
  508. webView.loadUrl("javascript:" + js);
  509. }
  510. };
  511. try {
  512. Method post = webView.getClass().getMethod("post",Runnable.class);
  513. post.invoke(webView,jsLoader);
  514. } catch(Exception e) {
  515. ((Activity)(webView.getContext())).runOnUiThread(jsLoader);
  516. }
  517. }
  518. /**
  519. * Convert JSON array of integers to List.
  520. *
  521. * @param ary
  522. * Array of integers
  523. */
  524. private List<Integer> toList (JSONArray ary) {
  525. ArrayList<Integer> list = new ArrayList<Integer>();
  526. for (int i = 0; i < ary.length(); i++) {
  527. list.add(ary.optInt(i));
  528. }
  529. return list;
  530. }
  531. /**
  532. * Current application state.
  533. *
  534. * @return
  535. * "background" or "foreground"
  536. */
  537. static String getApplicationState () {
  538. return isInBackground ? "background" : "foreground";
  539. }
  540. /**
  541. * Notification manager instance.
  542. */
  543. private Manager getNotificationMgr() {
  544. return Manager.getInstance(cordova.getActivity());
  545. }
  546. }