LocalNotification.java 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606
  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.annotation.SuppressLint;
  23. import android.app.Activity;
  24. import android.app.KeyguardManager;
  25. import android.content.Context;
  26. import android.util.Pair;
  27. import android.view.View;
  28. import org.apache.cordova.CallbackContext;
  29. import org.apache.cordova.CordovaInterface;
  30. import org.apache.cordova.CordovaPlugin;
  31. import org.apache.cordova.CordovaWebView;
  32. import org.apache.cordova.PluginResult;
  33. import org.json.JSONArray;
  34. import org.json.JSONException;
  35. import org.json.JSONObject;
  36. import java.lang.ref.WeakReference;
  37. import java.util.ArrayList;
  38. import java.util.List;
  39. import de.appplant.cordova.plugin.notification.Manager;
  40. import de.appplant.cordova.plugin.notification.Notification;
  41. import de.appplant.cordova.plugin.notification.Options;
  42. import de.appplant.cordova.plugin.notification.Request;
  43. import de.appplant.cordova.plugin.notification.action.ActionGroup;
  44. import static de.appplant.cordova.plugin.notification.Notification.Type.SCHEDULED;
  45. import static de.appplant.cordova.plugin.notification.Notification.Type.TRIGGERED;
  46. /**
  47. * This plugin utilizes the Android AlarmManager in combination with local
  48. * notifications. When a local notification is scheduled the alarm manager takes
  49. * care of firing the event. When the event is processed, a notification is put
  50. * in the Android notification center and status bar.
  51. */
  52. public class LocalNotification extends CordovaPlugin {
  53. // Reference to the web view for static access
  54. private static WeakReference<CordovaWebView> webView = null;
  55. // Indicates if the device is ready (to receive events)
  56. private static Boolean deviceready = false;
  57. // Queues all events before deviceready
  58. private static ArrayList<String> eventQueue = new ArrayList<String>();
  59. // Launch details
  60. private static Pair<Integer, String> launchDetails;
  61. /**
  62. * Called after plugin construction and fields have been initialized.
  63. * Prefer to use pluginInitialize instead since there is no value in
  64. * having parameters on the initialize() function.
  65. */
  66. @Override
  67. public void initialize (CordovaInterface cordova, CordovaWebView webView) {
  68. LocalNotification.webView = new WeakReference<CordovaWebView>(webView);
  69. }
  70. /**
  71. * Called when the activity will start interacting with the user.
  72. *
  73. * @param multitasking Flag indicating if multitasking is turned on for app.
  74. */
  75. @Override
  76. public void onResume (boolean multitasking) {
  77. super.onResume(multitasking);
  78. deviceready();
  79. }
  80. /**
  81. * The final call you receive before your activity is destroyed.
  82. */
  83. @Override
  84. public void onDestroy() {
  85. deviceready = false;
  86. }
  87. /**
  88. * Executes the request.
  89. *
  90. * This method is called from the WebView thread. To do a non-trivial
  91. * amount of work, use:
  92. * cordova.getThreadPool().execute(runnable);
  93. *
  94. * To run on the UI thread, use:
  95. * cordova.getActivity().runOnUiThread(runnable);
  96. *
  97. * @param action The action to execute.
  98. * @param args The exec() arguments in JSON form.
  99. * @param command The callback context used when calling back into
  100. * JavaScript.
  101. *
  102. * @return Whether the action was valid.
  103. */
  104. @Override
  105. public boolean execute (final String action, final JSONArray args,
  106. final CallbackContext command) throws JSONException {
  107. if (action.equals("launch")) {
  108. launch(command);
  109. return true;
  110. }
  111. cordova.getThreadPool().execute(new Runnable() {
  112. public void run() {
  113. if (action.equals("ready")) {
  114. deviceready();
  115. } else
  116. if (action.equalsIgnoreCase("check")) {
  117. check(command);
  118. } else
  119. if (action.equalsIgnoreCase("request")) {
  120. request(command);
  121. } else
  122. if (action.equalsIgnoreCase("actions")) {
  123. actions(args.optJSONObject(0));
  124. command.success();
  125. } else
  126. if (action.equalsIgnoreCase("schedule")) {
  127. schedule(args);
  128. check(command);
  129. } else
  130. if (action.equals("update")) {
  131. update(args);
  132. check(command);
  133. } else
  134. if (action.equals("cancel")) {
  135. cancel(args);
  136. command.success();
  137. } else
  138. if (action.equals("cancelAll")) {
  139. cancelAll();
  140. command.success();
  141. } else
  142. if (action.equals("clear")) {
  143. clear(args);
  144. command.success();
  145. } else
  146. if (action.equals("clearAll")) {
  147. clearAll();
  148. command.success();
  149. } else
  150. if (action.equals("type")) {
  151. type(args.optInt(0), command);
  152. } else
  153. if (action.equals("ids")) {
  154. ids(command);
  155. } else
  156. if (action.equals("scheduledIds")) {
  157. scheduledIds(command);
  158. } else
  159. if (action.equals("triggeredIds")) {
  160. triggeredIds(command);
  161. } else
  162. if (action.equals("notification")) {
  163. notification(args.optInt(0), command);
  164. } else
  165. if (action.equals("notifications")) {
  166. notifications(args, command);
  167. } else
  168. if (action.equals("scheduledNotifications")) {
  169. scheduledNotifications(command);
  170. } else
  171. if (action.equals("triggeredNotifications")) {
  172. triggeredNotifications(command);
  173. }
  174. }
  175. });
  176. return true;
  177. }
  178. /**
  179. * Set launchDetails object.
  180. *
  181. * @param command The callback context used when calling back into
  182. * JavaScript.
  183. */
  184. @SuppressLint("DefaultLocale")
  185. private void launch(CallbackContext command) {
  186. if (launchDetails == null)
  187. return;
  188. JSONObject details = new JSONObject();
  189. try {
  190. details.put("id", launchDetails.first);
  191. details.put("action", launchDetails.second);
  192. } catch (JSONException e) {
  193. e.printStackTrace();
  194. }
  195. command.success(details);
  196. launchDetails = null;
  197. }
  198. /**
  199. * Ask if user has enabled permission for local notifications.
  200. *
  201. * @param command The callback context used when calling back into
  202. * JavaScript.
  203. */
  204. private void check (CallbackContext command) {
  205. boolean allowed = getNotMgr().hasPermission();
  206. PluginResult result = new PluginResult(PluginResult.Status.OK, allowed);
  207. command.sendPluginResult(result);
  208. }
  209. /**
  210. * Request permission for local notifications.
  211. *
  212. * @param command The callback context used when calling back into
  213. * JavaScript.
  214. */
  215. private void request (CallbackContext command) {
  216. check(command);
  217. }
  218. /**
  219. * Register action group.
  220. *
  221. * @param args The action group spec.
  222. */
  223. private void actions (JSONObject args) {
  224. ActionGroup group = ActionGroup.parse(cordova.getActivity(), args);
  225. if (group != null) {
  226. ActionGroup.register(group);
  227. }
  228. }
  229. /**
  230. * Schedule multiple local notifications.
  231. *
  232. * @param notifications The notifications to schedule.
  233. */
  234. private void schedule (JSONArray notifications) {
  235. Manager mgr = getNotMgr();
  236. for (int i = 0; i < notifications.length(); i++) {
  237. JSONObject dict = notifications.optJSONObject(i);
  238. Options options = new Options(dict);
  239. Request request = new Request(options);
  240. Notification notification =
  241. mgr.schedule(request, TriggerReceiver.class);
  242. if (notification != null) {
  243. fireEvent("add", notification);
  244. }
  245. }
  246. }
  247. /**
  248. * Update multiple local notifications.
  249. *
  250. * @param updates Notification properties including their IDs
  251. */
  252. private void update (JSONArray updates) {
  253. for (int i = 0; i < updates.length(); i++) {
  254. JSONObject update = updates.optJSONObject(i);
  255. int id = update.optInt("id", 0);
  256. Notification notification =
  257. getNotMgr().update(id, update, TriggerReceiver.class);
  258. if (notification == null)
  259. continue;
  260. fireEvent("update", notification);
  261. }
  262. }
  263. /**
  264. * Cancel multiple local notifications.
  265. *
  266. * @param ids Set of local notification IDs
  267. */
  268. private void cancel (JSONArray ids) {
  269. for (int i = 0; i < ids.length(); i++) {
  270. int id = ids.optInt(i, 0);
  271. Notification notification =
  272. getNotMgr().cancel(id);
  273. if (notification == null)
  274. continue;
  275. fireEvent("cancel", notification);
  276. }
  277. }
  278. /**
  279. * Cancel all scheduled notifications.
  280. */
  281. private void cancelAll() {
  282. getNotMgr().cancelAll();
  283. fireEvent("cancelall");
  284. }
  285. /**
  286. * Clear multiple local notifications without canceling them.
  287. *
  288. * @param ids Set of local notification IDs
  289. */
  290. private void clear(JSONArray ids){
  291. for (int i = 0; i < ids.length(); i++) {
  292. int id = ids.optInt(i, 0);
  293. Notification notification =
  294. getNotMgr().clear(id);
  295. if (notification == null)
  296. continue;
  297. fireEvent("clear", notification);
  298. }
  299. }
  300. /**
  301. * Clear all triggered notifications without canceling them.
  302. */
  303. private void clearAll() {
  304. getNotMgr().clearAll();
  305. fireEvent("clearall");
  306. }
  307. /**
  308. * Get the type of the notification (unknown, scheduled, triggered).
  309. *
  310. * @param id The ID of the notification to check.
  311. * @param command The callback context used when calling back into
  312. * JavaScript.
  313. */
  314. private void type (int id, CallbackContext command) {
  315. Notification toast = getNotMgr().get(id);
  316. if (toast == null) {
  317. command.success("unknown");
  318. return;
  319. }
  320. switch (toast.getType()) {
  321. case SCHEDULED:
  322. command.success("scheduled");
  323. break;
  324. case TRIGGERED:
  325. command.success("triggered");
  326. break;
  327. default:
  328. command.success("unknown");
  329. break;
  330. }
  331. }
  332. /**
  333. * Set of IDs from all existent notifications.
  334. *
  335. * @param command The callback context used when calling back into
  336. * JavaScript.
  337. */
  338. private void ids (CallbackContext command) {
  339. List<Integer> ids = getNotMgr().getIds();
  340. command.success(new JSONArray(ids));
  341. }
  342. /**
  343. * Set of IDs from all scheduled notifications.
  344. *
  345. * @param command The callback context used when calling back into
  346. * JavaScript.
  347. */
  348. private void scheduledIds (CallbackContext command) {
  349. List<Integer> ids = getNotMgr().getIdsByType(SCHEDULED);
  350. command.success(new JSONArray(ids));
  351. }
  352. /**
  353. * Set of IDs from all triggered notifications.
  354. *
  355. * @param command The callback context used when calling back into
  356. * JavaScript.
  357. */
  358. private void triggeredIds (CallbackContext command) {
  359. List<Integer> ids = getNotMgr().getIdsByType(TRIGGERED);
  360. command.success(new JSONArray(ids));
  361. }
  362. /**
  363. * Options from local notification.
  364. *
  365. * @param id The ID of the notification.
  366. * @param command The callback context used when calling back into
  367. * JavaScript.
  368. */
  369. private void notification (int id, CallbackContext command) {
  370. Options options = getNotMgr().getOptions(id);
  371. if (options != null) {
  372. command.success(options.getDict());
  373. } else {
  374. command.success();
  375. }
  376. }
  377. /**
  378. * Set of options from local notification.
  379. *
  380. * @param ids Set of local notification IDs.
  381. * @param command The callback context used when calling back into
  382. * JavaScript.
  383. */
  384. private void notifications (JSONArray ids, CallbackContext command) {
  385. List<JSONObject> options;
  386. if (ids.length() == 0) {
  387. options = getNotMgr().getOptions();
  388. } else {
  389. options = getNotMgr().getOptionsById(toList(ids));
  390. }
  391. command.success(new JSONArray(options));
  392. }
  393. /**
  394. * Set of options from scheduled notifications.
  395. *
  396. * @param command The callback context used when calling back into
  397. * JavaScript.
  398. */
  399. private void scheduledNotifications (CallbackContext command) {
  400. List<JSONObject> options = getNotMgr().getOptionsByType(SCHEDULED);
  401. command.success(new JSONArray(options));
  402. }
  403. /**
  404. * Set of options from triggered notifications.
  405. *
  406. * @param command The callback context used when calling back into
  407. * JavaScript.
  408. */
  409. private void triggeredNotifications (CallbackContext command) {
  410. List<JSONObject> options = getNotMgr().getOptionsByType(TRIGGERED);
  411. command.success(new JSONArray(options));
  412. }
  413. /**
  414. * Call all pending callbacks after the deviceready event has been fired.
  415. */
  416. private static synchronized void deviceready () {
  417. deviceready = true;
  418. for (String js : eventQueue) {
  419. sendJavascript(js);
  420. }
  421. eventQueue.clear();
  422. }
  423. /**
  424. * Fire given event on JS side. Does inform all event listeners.
  425. *
  426. * @param event The event name.
  427. */
  428. private void fireEvent (String event) {
  429. fireEvent(event, null, new JSONObject());
  430. }
  431. /**
  432. * Fire given event on JS side. Does inform all event listeners.
  433. *
  434. * @param event The event name.
  435. * @param notification Optional notification to pass with.
  436. */
  437. static void fireEvent (String event, Notification notification) {
  438. fireEvent(event, notification, new JSONObject());
  439. }
  440. /**
  441. * Fire given event on JS side. Does inform all event listeners.
  442. *
  443. * @param event The event name.
  444. * @param toast Optional notification to pass with.
  445. * @param data Event object with additional data.
  446. */
  447. static void fireEvent (String event, Notification toast, JSONObject data) {
  448. String params, js;
  449. try {
  450. data.put("event", event);
  451. data.put("foreground", isInForeground());
  452. data.put("queued", !deviceready);
  453. if (toast != null) {
  454. data.put("notification", toast.getId());
  455. }
  456. } catch (JSONException e) {
  457. e.printStackTrace();
  458. }
  459. if (toast != null) {
  460. params = toast.toString() + "," + data.toString();
  461. } else {
  462. params = data.toString();
  463. }
  464. js = "cordova.plugins.notification.local.core.fireEvent(" +
  465. "\"" + event + "\"," + params + ")";
  466. if (launchDetails == null && !deviceready && toast != null) {
  467. launchDetails = new Pair<Integer, String>(toast.getId(), event);
  468. }
  469. sendJavascript(js);
  470. }
  471. /**
  472. * Use this instead of deprecated sendJavascript
  473. *
  474. * @param js JS code snippet as string.
  475. */
  476. private static synchronized void sendJavascript(final String js) {
  477. if (!deviceready || webView == null) {
  478. eventQueue.add(js);
  479. return;
  480. }
  481. final CordovaWebView view = webView.get();
  482. ((Activity)(view.getContext())).runOnUiThread(new Runnable() {
  483. public void run() {
  484. view.loadUrl("javascript:" + js);
  485. }
  486. });
  487. }
  488. /**
  489. * If the app is running in foreground.
  490. */
  491. private static boolean isInForeground() {
  492. if (!deviceready || webView == null)
  493. return false;
  494. CordovaWebView view = webView.get();
  495. KeyguardManager km = (KeyguardManager) view.getContext()
  496. .getSystemService(Context.KEYGUARD_SERVICE);
  497. //noinspection SimplifiableIfStatement
  498. if (km != null && km.isKeyguardLocked())
  499. return false;
  500. return view.getView().getWindowVisibility() == View.VISIBLE;
  501. }
  502. /**
  503. * Convert JSON array of integers to List.
  504. *
  505. * @param ary Array of integers.
  506. */
  507. private List<Integer> toList (JSONArray ary) {
  508. List<Integer> list = new ArrayList<Integer>();
  509. for (int i = 0; i < ary.length(); i++) {
  510. list.add(ary.optInt(i));
  511. }
  512. return list;
  513. }
  514. /**
  515. * Notification manager instance.
  516. */
  517. private Manager getNotMgr() {
  518. return Manager.getInstance(cordova.getActivity());
  519. }
  520. }