Builder.java 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423
  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.notification;
  22. import android.app.PendingIntent;
  23. import android.content.Context;
  24. import android.content.Intent;
  25. import android.graphics.Bitmap;
  26. import android.net.Uri;
  27. import android.os.Bundle;
  28. import android.support.v4.app.NotificationCompat;
  29. import android.support.v4.app.NotificationCompat.MessagingStyle.Message;
  30. import android.support.v4.media.app.NotificationCompat.MediaStyle;
  31. import android.support.v4.media.session.MediaSessionCompat;
  32. import java.util.List;
  33. import java.util.Random;
  34. import de.appplant.cordova.plugin.notification.action.Action;
  35. import static android.app.PendingIntent.FLAG_CANCEL_CURRENT;
  36. import static android.app.PendingIntent.FLAG_UPDATE_CURRENT;
  37. import static de.appplant.cordova.plugin.notification.Notification.EXTRA_UPDATE;
  38. /**
  39. * Builder class for local notifications. Build fully configured local
  40. * notification specified by JSON object passed from JS side.
  41. */
  42. public final class Builder {
  43. // Application context passed by constructor
  44. private final Context context;
  45. // Notification options passed by JS
  46. private final Options options;
  47. // To generate unique request codes
  48. private final Random random = new Random();
  49. // Receiver to handle the clear event
  50. private Class<?> clearReceiver;
  51. // Activity to handle the click event
  52. private Class<?> clickActivity;
  53. // Additional extras to merge into each intent
  54. private Bundle extras;
  55. /**
  56. * Constructor
  57. *
  58. * @param options Notification options
  59. */
  60. public Builder(Options options) {
  61. this.context = options.getContext();
  62. this.options = options;
  63. }
  64. /**
  65. * Set clear receiver.
  66. *
  67. * @param receiver Broadcast receiver for the clear event.
  68. */
  69. public Builder setClearReceiver(Class<?> receiver) {
  70. this.clearReceiver = receiver;
  71. return this;
  72. }
  73. /**
  74. * Set click activity.
  75. *
  76. * @param activity The activity to handler the click event.
  77. */
  78. public Builder setClickActivity(Class<?> activity) {
  79. this.clickActivity = activity;
  80. return this;
  81. }
  82. /**
  83. * Set bundle extras.
  84. *
  85. * @param extras The bundled extras to merge into.
  86. */
  87. public Builder setExtras(Bundle extras) {
  88. this.extras = extras;
  89. return this;
  90. }
  91. /**
  92. * Creates the notification with all its options passed through JS.
  93. *
  94. * @return The final notification to display.
  95. */
  96. public Notification build() {
  97. NotificationCompat.Builder builder;
  98. if (options.isSilent()) {
  99. return new Notification(context, options);
  100. }
  101. Uri sound = options.getSound();
  102. Bundle extras = new Bundle();
  103. extras.putInt(Notification.EXTRA_ID, options.getId());
  104. extras.putString(Options.EXTRA_SOUND, sound.toString());
  105. builder = findOrCreateBuilder()
  106. .setDefaults(options.getDefaults())
  107. .setExtras(extras)
  108. .setOnlyAlertOnce(false)
  109. .setChannelId(options.getChannel())
  110. .setContentTitle(options.getTitle())
  111. .setContentText(options.getText())
  112. .setTicker(options.getText())
  113. .setNumber(options.getNumber())
  114. .setAutoCancel(options.isAutoClear())
  115. .setOngoing(options.isSticky())
  116. .setColor(options.getColor())
  117. .setVisibility(options.getVisibility())
  118. .setPriority(options.getPriority())
  119. .setShowWhen(options.getShowWhen())
  120. .setUsesChronometer(options.isWithProgressBar())
  121. .setGroup(options.getGroup())
  122. .setGroupSummary(options.getGroupSummary())
  123. .setLights(options.getLedColor(), options.getLedOn(), options.getLedOff());
  124. if (sound != Uri.EMPTY && !isUpdate()) {
  125. builder.setSound(sound);
  126. }
  127. if (options.isWithProgressBar()) {
  128. builder.setProgress(
  129. options.getProgressMaxValue(),
  130. options.getProgressValue(),
  131. options.isIndeterminateProgress());
  132. }
  133. if (options.hasLargeIcon()) {
  134. builder.setSmallIcon(options.getSmallIcon());
  135. builder.setLargeIcon(options.getLargeIcon());
  136. } else {
  137. builder.setSmallIcon(options.getSmallIcon());
  138. }
  139. applyStyle(builder);
  140. applyActions(builder);
  141. applyDeleteReceiver(builder);
  142. applyContentReceiver(builder);
  143. return new Notification(context, options, builder);
  144. }
  145. /**
  146. * Find out and set the notification style.
  147. *
  148. * @param builder Local notification builder instance.
  149. */
  150. private void applyStyle(NotificationCompat.Builder builder) {
  151. Message[] messages = options.getMessages();
  152. String summary = options.getSummary();
  153. if (messages != null) {
  154. applyMessagingStyle(builder, messages);
  155. return;
  156. }
  157. MediaSessionCompat.Token token = options.getMediaSessionToken();
  158. if (token != null) {
  159. applyMediaStyle(builder, token);
  160. return;
  161. }
  162. List<Bitmap> pics = options.getAttachments();
  163. if (pics.size() > 0) {
  164. applyBigPictureStyle(builder, pics);
  165. return;
  166. }
  167. String text = options.getText();
  168. if (text != null && text.contains("\n")) {
  169. applyInboxStyle(builder);
  170. return;
  171. }
  172. if (text == null || summary == null && text.length() < 45)
  173. return;
  174. applyBigTextStyle(builder);
  175. }
  176. /**
  177. * Apply inbox style.
  178. *
  179. * @param builder Local notification builder instance.
  180. * @param messages The messages to add to the conversation.
  181. */
  182. private void applyMessagingStyle(NotificationCompat.Builder builder,
  183. Message[] messages) {
  184. NotificationCompat.MessagingStyle style;
  185. style = new NotificationCompat.MessagingStyle("Me")
  186. .setConversationTitle(options.getTitle());
  187. for (Message msg : messages) {
  188. style.addMessage(msg);
  189. }
  190. builder.setStyle(style);
  191. }
  192. /**
  193. * Apply inbox style.
  194. *
  195. * @param builder Local notification builder instance.
  196. * @param pics The pictures to show.
  197. */
  198. private void applyBigPictureStyle(NotificationCompat.Builder builder,
  199. List<Bitmap> pics) {
  200. NotificationCompat.BigPictureStyle style;
  201. String summary = options.getSummary();
  202. String text = options.getText();
  203. style = new NotificationCompat.BigPictureStyle(builder)
  204. .setSummaryText(summary == null ? text : summary)
  205. .bigPicture(pics.get(0));
  206. builder.setStyle(style);
  207. }
  208. /**
  209. * Apply inbox style.
  210. *
  211. * @param builder Local notification builder instance.
  212. */
  213. private void applyInboxStyle(NotificationCompat.Builder builder) {
  214. NotificationCompat.InboxStyle style;
  215. String text = options.getText();
  216. style = new NotificationCompat.InboxStyle(builder)
  217. .setSummaryText(options.getSummary());
  218. for (String line : text.split("\n")) {
  219. style.addLine(line);
  220. }
  221. builder.setStyle(style);
  222. }
  223. /**
  224. * Apply big text style.
  225. *
  226. * @param builder Local notification builder instance.
  227. */
  228. private void applyBigTextStyle(NotificationCompat.Builder builder) {
  229. NotificationCompat.BigTextStyle style;
  230. style = new NotificationCompat.BigTextStyle(builder)
  231. .setSummaryText(options.getSummary())
  232. .bigText(options.getText());
  233. builder.setStyle(style);
  234. }
  235. /**
  236. * Apply media style.
  237. *
  238. * @param builder Local notification builder instance.
  239. * @param token The media session token.
  240. */
  241. private void applyMediaStyle(NotificationCompat.Builder builder,
  242. MediaSessionCompat.Token token) {
  243. MediaStyle style;
  244. style = new MediaStyle(builder)
  245. .setMediaSession(token)
  246. .setShowActionsInCompactView(1);
  247. builder.setStyle(style);
  248. }
  249. /**
  250. * Set intent to handle the delete event. Will clean up some persisted
  251. * preferences.
  252. *
  253. * @param builder Local notification builder instance.
  254. */
  255. private void applyDeleteReceiver(NotificationCompat.Builder builder) {
  256. if (clearReceiver == null)
  257. return;
  258. Intent intent = new Intent(context, clearReceiver)
  259. .putExtras(extras)
  260. .setAction(options.getIdentifier())
  261. .putExtra(Notification.EXTRA_ID, options.getId());
  262. int reqCode = random.nextInt();
  263. PendingIntent deleteIntent = PendingIntent.getBroadcast(
  264. context, reqCode, intent, FLAG_UPDATE_CURRENT);
  265. builder.setDeleteIntent(deleteIntent);
  266. }
  267. /**
  268. * Set intent to handle the click event. Will bring the app to
  269. * foreground.
  270. *
  271. * @param builder Local notification builder instance.
  272. */
  273. private void applyContentReceiver(NotificationCompat.Builder builder) {
  274. if (clickActivity == null)
  275. return;
  276. Intent intent = new Intent(context, clickActivity)
  277. .putExtras(extras)
  278. .putExtra(Notification.EXTRA_ID, options.getId())
  279. .putExtra(Action.EXTRA_ID, Action.CLICK_ACTION_ID)
  280. .putExtra(Options.EXTRA_LAUNCH, options.isLaunchingApp())
  281. .setFlags(Intent.FLAG_ACTIVITY_NO_HISTORY);
  282. int reqCode = random.nextInt();
  283. PendingIntent contentIntent = PendingIntent.getActivity(
  284. context, reqCode, intent, FLAG_UPDATE_CURRENT);
  285. builder.setContentIntent(contentIntent);
  286. }
  287. /**
  288. * Add all actions to the builder if there are any actions.
  289. *
  290. * @param builder Local notification builder instance.
  291. */
  292. private void applyActions (NotificationCompat.Builder builder) {
  293. Action[] actions = options.getActions();
  294. NotificationCompat.Action.Builder btn;
  295. if (actions == null || actions.length == 0)
  296. return;
  297. for (Action action : actions) {
  298. btn = new NotificationCompat.Action.Builder(
  299. action.getIcon(), action.getTitle(),
  300. getPendingIntentForAction(action));
  301. if (action.isWithInput()) {
  302. btn.addRemoteInput(action.getInput());
  303. }
  304. builder.addAction(btn.build());
  305. }
  306. }
  307. /**
  308. * Returns a new PendingIntent for a notification action, including the
  309. * action's identifier.
  310. *
  311. * @param action Notification action needing the PendingIntent
  312. */
  313. private PendingIntent getPendingIntentForAction (Action action) {
  314. Intent intent = new Intent(context, clickActivity)
  315. .putExtras(extras)
  316. .putExtra(Notification.EXTRA_ID, options.getId())
  317. .putExtra(Action.EXTRA_ID, action.getId())
  318. .putExtra(Options.EXTRA_LAUNCH, action.isLaunchingApp())
  319. .setFlags(Intent.FLAG_ACTIVITY_NO_HISTORY);
  320. int reqCode = random.nextInt();
  321. return PendingIntent.getActivity(
  322. context, reqCode, intent, FLAG_CANCEL_CURRENT);
  323. }
  324. /**
  325. * If the builder shall build an notification or an updated version.
  326. *
  327. * @return true in case of an updated version.
  328. */
  329. private boolean isUpdate() {
  330. return extras != null && extras.getBoolean(EXTRA_UPDATE, false);
  331. }
  332. /**
  333. * Returns a cached builder instance or creates a new one.
  334. */
  335. private NotificationCompat.Builder findOrCreateBuilder() {
  336. int key = options.getId();
  337. NotificationCompat.Builder builder = Notification.getCachedBuilder(key);
  338. if (builder == null) {
  339. builder = new NotificationCompat.Builder(context, options.getChannel());
  340. }
  341. return builder;
  342. }
  343. }