The JUCE cross-platform C++ framework, with DISTRHO/KXStudio specific changes
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1762 lines
88KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library.
  4. Copyright (c) 2017 - ROLI Ltd.
  5. JUCE is an open source library subject to commercial or open-source
  6. licensing.
  7. By using JUCE, you agree to the terms of both the JUCE 5 End-User License
  8. Agreement and JUCE 5 Privacy Policy (both updated and effective as of the
  9. 27th April 2017).
  10. End User License Agreement: www.juce.com/juce-5-licence
  11. Privacy Policy: www.juce.com/juce-5-privacy-policy
  12. Or: You may also use this code under the terms of the GPL v3 (see
  13. www.gnu.org/licenses).
  14. JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
  15. EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
  16. DISCLAIMED.
  17. ==============================================================================
  18. */
  19. namespace juce
  20. {
  21. #if __ANDROID_API__ >= 21
  22. #define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \
  23. METHOD (build, "build", "()Landroid/media/AudioAttributes;") \
  24. METHOD (constructor, "<init>", "()V") \
  25. METHOD (setContentType, "setContentType", "(I)Landroid/media/AudioAttributes$Builder;") \
  26. METHOD (setUsage, "setUsage", "(I)Landroid/media/AudioAttributes$Builder;")
  27. DECLARE_JNI_CLASS (AudioAttributesBuilder, "android/media/AudioAttributes$Builder")
  28. #undef JNI_CLASS_MEMBERS
  29. #endif
  30. #define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \
  31. STATICMETHOD (createBitmap, "createBitmap", "(IILandroid/graphics/Bitmap$Config;)Landroid/graphics/Bitmap;") \
  32. METHOD (setPixel, "setPixel", "(III)V")
  33. DECLARE_JNI_CLASS (Bitmap, "android/graphics/Bitmap");
  34. #undef JNI_CLASS_MEMBERS
  35. #define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \
  36. STATICMETHOD (valueOf, "valueOf", "(Ljava/lang/String;)Landroid/graphics/Bitmap$Config;")
  37. DECLARE_JNI_CLASS (BitmapConfig, "android/graphics/Bitmap$Config");
  38. #undef JNI_CLASS_MEMBERS
  39. #define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \
  40. METHOD (constructor, "<init>", "()V") \
  41. METHOD (containsKey, "containsKey", "(Ljava/lang/String;)Z") \
  42. METHOD (get, "get", "(Ljava/lang/String;)Ljava/lang/Object;") \
  43. METHOD (getBoolean, "getBoolean", "(Ljava/lang/String;)Z") \
  44. METHOD (getBundle, "getBundle", "(Ljava/lang/String;)Landroid/os/Bundle;") \
  45. METHOD (getCharSequence, "getCharSequence", "(Ljava/lang/String;)Ljava/lang/CharSequence;") \
  46. METHOD (getInt, "getInt", "(Ljava/lang/String;)I") \
  47. METHOD (getLong, "getLong", "(Ljava/lang/String;)J") \
  48. METHOD (getLongArray, "getLongArray", "(Ljava/lang/String;)[J") \
  49. METHOD (getString, "getString", "(Ljava/lang/String;)Ljava/lang/String;") \
  50. METHOD (keySet, "keySet", "()Ljava/util/Set;") \
  51. METHOD (putBoolean, "putBoolean", "(Ljava/lang/String;Z)V") \
  52. METHOD (putBundle, "putBundle", "(Ljava/lang/String;Landroid/os/Bundle;)V") \
  53. METHOD (putInt, "putInt", "(Ljava/lang/String;I)V") \
  54. METHOD (putLong, "putLong", "(Ljava/lang/String;J)V") \
  55. METHOD (putLongArray, "putLongArray", "(Ljava/lang/String;[J)V") \
  56. METHOD (putString, "putString", "(Ljava/lang/String;Ljava/lang/String;)V")
  57. DECLARE_JNI_CLASS (Bundle, "android/os/Bundle");
  58. #undef JNI_CLASS_MEMBERS
  59. #define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \
  60. METHOD (toString, "toString", "()Ljava/lang/String;")
  61. DECLARE_JNI_CLASS (CharSequence, "java/lang/CharSequence");
  62. #undef JNI_CLASS_MEMBERS
  63. #define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \
  64. METHOD (hasNext, "hasNext", "()Z") \
  65. METHOD (next, "next", "()Ljava/lang/Object;")
  66. DECLARE_JNI_CLASS (Iterator, "java/util/Iterator");
  67. #undef JNI_CLASS_MEMBERS
  68. #define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \
  69. METHOD (get, "get", "(Ljava/lang/Object;)Ljava/lang/Object;") \
  70. METHOD (keySet, "keySet", "()Ljava/util/Set;")
  71. DECLARE_JNI_CLASS (JavaMap, "java/util/Map");
  72. #undef JNI_CLASS_MEMBERS
  73. #define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \
  74. METHOD (concat, "concat", "(Ljava/lang/String;)Ljava/lang/String;")
  75. DECLARE_JNI_CLASS (JavaString, "java/lang/String");
  76. #undef JNI_CLASS_MEMBERS
  77. #if __ANDROID_API__ >= 26
  78. #define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \
  79. METHOD (constructor, "<init>", "(Ljava/lang/String;Ljava/lang/CharSequence;I)V") \
  80. METHOD (enableLights, "enableLights", "(Z)V") \
  81. METHOD (enableVibration, "enableVibration", "(Z)V") \
  82. METHOD (setBypassDnd, "setBypassDnd", "(Z)V") \
  83. METHOD (setDescription, "setDescription", "(Ljava/lang/String;)V") \
  84. METHOD (setGroup, "setGroup", "(Ljava/lang/String;)V") \
  85. METHOD (setImportance, "setImportance", "(I)V") \
  86. METHOD (setLightColor, "setLightColor", "(I)V") \
  87. METHOD (setLockscreenVisibility, "setLockscreenVisibility", "(I)V") \
  88. METHOD (setShowBadge, "setShowBadge", "(Z)V") \
  89. METHOD (setSound, "setSound", "(Landroid/net/Uri;Landroid/media/AudioAttributes;)V") \
  90. METHOD (setVibrationPattern, "setVibrationPattern", "([J)V")
  91. DECLARE_JNI_CLASS (NotificationChannel, "android/app/NotificationChannel");
  92. #undef JNI_CLASS_MEMBERS
  93. #define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \
  94. METHOD (constructor, "<init>", "(Ljava/lang/String;Ljava/lang/CharSequence;)V")
  95. DECLARE_JNI_CLASS (NotificationChannelGroup, "android/app/NotificationChannelGroup");
  96. #undef JNI_CLASS_MEMBERS
  97. #endif
  98. #if __ANDROID_API__ >= 19
  99. #define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \
  100. FIELD (extras, "extras", "Landroid/os/Bundle;")
  101. DECLARE_JNI_CLASS (AndroidNotification, "android/app/Notification");
  102. #undef JNI_CLASS_MEMBERS
  103. #endif
  104. #if __ANDROID_API__ >= 20
  105. #define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \
  106. METHOD (addExtras, "addExtras", "(Landroid/os/Bundle;)Landroid/app/Notification$Action$Builder;") \
  107. METHOD (addRemoteInput, "addRemoteInput", "(Landroid/app/RemoteInput;)Landroid/app/Notification$Action$Builder;") \
  108. METHOD (constructor, "<init>", "(ILjava/lang/CharSequence;Landroid/app/PendingIntent;)V") \
  109. METHOD (build, "build", "()Landroid/app/Notification$Action;")
  110. DECLARE_JNI_CLASS (NotificationActionBuilder, "android/app/Notification$Action$Builder");
  111. #undef JNI_CLASS_MEMBERS
  112. #endif
  113. #define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \
  114. METHOD (getNotification, "getNotification", "()Landroid/app/Notification;") \
  115. METHOD (setAutoCancel, "setAutoCancel", "(Z)Landroid/app/Notification$Builder;") \
  116. METHOD (setContentInfo, "setContentInfo", "(Ljava/lang/CharSequence;)Landroid/app/Notification$Builder;") \
  117. METHOD (setContentIntent, "setContentIntent", "(Landroid/app/PendingIntent;)Landroid/app/Notification$Builder;") \
  118. METHOD (setContentText, "setContentText", "(Ljava/lang/CharSequence;)Landroid/app/Notification$Builder;") \
  119. METHOD (setContentTitle, "setContentTitle", "(Ljava/lang/CharSequence;)Landroid/app/Notification$Builder;") \
  120. METHOD (setDefaults, "setDefaults", "(I)Landroid/app/Notification$Builder;") \
  121. METHOD (setDeleteIntent, "setDeleteIntent", "(Landroid/app/PendingIntent;)Landroid/app/Notification$Builder;") \
  122. METHOD (setLargeIcon, "setLargeIcon", "(Landroid/graphics/Bitmap;)Landroid/app/Notification$Builder;") \
  123. METHOD (setLights, "setLights", "(III)Landroid/app/Notification$Builder;") \
  124. METHOD (setNumber, "setNumber", "(I)Landroid/app/Notification$Builder;") \
  125. METHOD (setOngoing, "setOngoing", "(Z)Landroid/app/Notification$Builder;") \
  126. METHOD (setOnlyAlertOnce, "setOnlyAlertOnce", "(Z)Landroid/app/Notification$Builder;") \
  127. METHOD (setProgress, "setProgress", "(IIZ)Landroid/app/Notification$Builder;") \
  128. METHOD (setSmallIcon, "setSmallIcon", "(I)Landroid/app/Notification$Builder;") \
  129. METHOD (setSound, "setSound", "(Landroid/net/Uri;)Landroid/app/Notification$Builder;") \
  130. METHOD (setTicker, "setTicker", "(Ljava/lang/CharSequence;)Landroid/app/Notification$Builder;") \
  131. METHOD (setVibrate, "setVibrate", "([J)Landroid/app/Notification$Builder;") \
  132. METHOD (setWhen, "setWhen", "(J)Landroid/app/Notification$Builder;")
  133. DECLARE_JNI_CLASS (NotificationBuilderBase, "android/app/Notification$Builder");
  134. #undef JNI_CLASS_MEMBERS
  135. #if __ANDROID_API__ >= 16
  136. #define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \
  137. METHOD (addAction, "addAction", "(ILjava/lang/CharSequence;Landroid/app/PendingIntent;)Landroid/app/Notification$Builder;") \
  138. METHOD (build, "build", "()Landroid/app/Notification;") \
  139. METHOD (setPriority, "setPriority", "(I)Landroid/app/Notification$Builder;") \
  140. METHOD (setSubText, "setSubText", "(Ljava/lang/CharSequence;)Landroid/app/Notification$Builder;") \
  141. METHOD (setUsesChronometer, "setUsesChronometer", "(Z)Landroid/app/Notification$Builder;")
  142. DECLARE_JNI_CLASS (NotificationBuilderApi16, "android/app/Notification$Builder");
  143. #undef JNI_CLASS_MEMBERS
  144. #endif
  145. #if __ANDROID_API__ >= 17
  146. #define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \
  147. METHOD (setShowWhen, "setShowWhen", "(Z)Landroid/app/Notification$Builder;")
  148. DECLARE_JNI_CLASS (NotificationBuilderApi17, "android/app/Notification$Builder");
  149. #undef JNI_CLASS_MEMBERS
  150. #endif
  151. #if __ANDROID_API__ >= 20
  152. #define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \
  153. METHOD (addAction, "addAction", "(Landroid/app/Notification$Action;)Landroid/app/Notification$Builder;") \
  154. METHOD (addExtras, "addExtras", "(Landroid/os/Bundle;)Landroid/app/Notification$Builder;") \
  155. METHOD (setLocalOnly, "setLocalOnly", "(Z)Landroid/app/Notification$Builder;") \
  156. METHOD (setGroup, "setGroup", "(Ljava/lang/String;)Landroid/app/Notification$Builder;") \
  157. METHOD (setGroupSummary, "setGroupSummary", "(Z)Landroid/app/Notification$Builder;") \
  158. METHOD (setSortKey, "setSortKey", "(Ljava/lang/String;)Landroid/app/Notification$Builder;")
  159. DECLARE_JNI_CLASS (NotificationBuilderApi20, "android/app/Notification$Builder");
  160. #undef JNI_CLASS_MEMBERS
  161. #endif
  162. #if __ANDROID_API__ >= 21
  163. #define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \
  164. METHOD (addPerson, "addPerson", "(Ljava/lang/String;)Landroid/app/Notification$Builder;") \
  165. METHOD (setCategory, "setCategory", "(Ljava/lang/String;)Landroid/app/Notification$Builder;") \
  166. METHOD (setColor, "setColor", "(I)Landroid/app/Notification$Builder;") \
  167. METHOD (setPublicVersion, "setPublicVersion", "(Landroid/app/Notification;)Landroid/app/Notification$Builder;") \
  168. METHOD (setVisibility, "setVisibility", "(I)Landroid/app/Notification$Builder;")
  169. DECLARE_JNI_CLASS (NotificationBuilderApi21, "android/app/Notification$Builder");
  170. #undef JNI_CLASS_MEMBERS
  171. #endif
  172. #if __ANDROID_API__ >= 24
  173. #define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \
  174. METHOD (setChronometerCountDown, "setChronometerCountDown", "(Z)Landroid/app/Notification$Builder;")
  175. DECLARE_JNI_CLASS (NotificationBuilderApi24, "android/app/Notification$Builder");
  176. #undef JNI_CLASS_MEMBERS
  177. #endif
  178. #if __ANDROID_API__ >= 26
  179. #define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \
  180. METHOD (setBadgeIconType, "setBadgeIconType", "(I)Landroid/app/Notification$Builder;") \
  181. METHOD (setGroupAlertBehavior, "setGroupAlertBehavior", "(I)Landroid/app/Notification$Builder;") \
  182. METHOD (setTimeoutAfter, "setTimeoutAfter", "(J)Landroid/app/Notification$Builder;")
  183. DECLARE_JNI_CLASS (NotificationBuilderApi26, "android/app/Notification$Builder");
  184. #undef JNI_CLASS_MEMBERS
  185. #endif
  186. #define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \
  187. METHOD (cancel, "cancel", "(Ljava/lang/String;I)V") \
  188. METHOD (cancelAll, "cancelAll", "()V") \
  189. METHOD (notify, "notify", "(Ljava/lang/String;ILandroid/app/Notification;)V")
  190. DECLARE_JNI_CLASS (NotificationManagerBase, "android/app/NotificationManager");
  191. #undef JNI_CLASS_MEMBERS
  192. #if __ANDROID_API__ >= 23
  193. #define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \
  194. METHOD (getActiveNotifications, "getActiveNotifications", "()[Landroid/service/notification/StatusBarNotification;")
  195. DECLARE_JNI_CLASS (NotificationManagerApi23, "android/app/NotificationManager");
  196. #undef JNI_CLASS_MEMBERS
  197. #endif
  198. #if __ANDROID_API__ >= 24
  199. #define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \
  200. METHOD (areNotificationsEnabled, "areNotificationsEnabled", "()Z")
  201. DECLARE_JNI_CLASS (NotificationManagerApi24, "android/app/NotificationManager");
  202. #undef JNI_CLASS_MEMBERS
  203. #endif
  204. #if __ANDROID_API__ >= 26
  205. #define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \
  206. METHOD (createNotificationChannel, "createNotificationChannel", "(Landroid/app/NotificationChannel;)V") \
  207. METHOD (createNotificationChannelGroup, "createNotificationChannelGroup", "(Landroid/app/NotificationChannelGroup;)V")
  208. DECLARE_JNI_CLASS (NotificationManagerApi26, "android/app/NotificationManager");
  209. #undef JNI_CLASS_MEMBERS
  210. #endif
  211. #define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \
  212. METHOD (getClass, "getClass", "()Ljava/lang/Class;") \
  213. METHOD (toString, "toString", "()Ljava/lang/String;")
  214. DECLARE_JNI_CLASS (Object, "java/lang/Object");
  215. #undef JNI_CLASS_MEMBERS
  216. #define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \
  217. STATICMETHOD (getActivity, "getActivity", "(Landroid/content/Context;ILandroid/content/Intent;I)Landroid/app/PendingIntent;")
  218. DECLARE_JNI_CLASS (PendingIntent, "android/app/PendingIntent");
  219. #undef JNI_CLASS_MEMBERS
  220. #if __ANDROID_API__ >= 20
  221. #define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \
  222. STATICMETHOD (getResultsFromIntent, "getResultsFromIntent", "(Landroid/content/Intent;)Landroid/os/Bundle;")
  223. DECLARE_JNI_CLASS (RemoteInput, "android/app/RemoteInput");
  224. #undef JNI_CLASS_MEMBERS
  225. #define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \
  226. METHOD (constructor, "<init>", "(Ljava/lang/String;)V") \
  227. METHOD (build, "build", "()Landroid/app/RemoteInput;") \
  228. METHOD (setAllowFreeFormInput, "setAllowFreeFormInput", "(Z)Landroid/app/RemoteInput$Builder;") \
  229. METHOD (setChoices, "setChoices", "([Ljava/lang/CharSequence;)Landroid/app/RemoteInput$Builder;") \
  230. METHOD (setLabel, "setLabel", "(Ljava/lang/CharSequence;)Landroid/app/RemoteInput$Builder;")
  231. DECLARE_JNI_CLASS (RemoteInputBuilder, "android/app/RemoteInput$Builder");
  232. #undef JNI_CLASS_MEMBERS
  233. #endif
  234. #define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \
  235. METHOD (getIdentifier, "getIdentifier", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)I")
  236. DECLARE_JNI_CLASS (Resources, "android/content/res/Resources")
  237. #undef JNI_CLASS_MEMBERS
  238. #define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \
  239. METHOD (contains, "contains", "(Ljava/lang/Object;)Z") \
  240. METHOD (iterator, "iterator", "()Ljava/util/Iterator;") \
  241. METHOD (size, "size", "()I")
  242. DECLARE_JNI_CLASS (Set, "java/util/Set");
  243. #undef JNI_CLASS_MEMBERS
  244. #if __ANDROID_API__ >= 23
  245. #define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \
  246. METHOD (getNotification, "getNotification", "()Landroid/app/Notification;")
  247. DECLARE_JNI_CLASS (StatusBarNotification, "android/service/notification/StatusBarNotification");
  248. #undef JNI_CLASS_MEMBERS
  249. #endif
  250. #define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \
  251. STATICMETHOD (parse, "parse", "(Ljava/lang/String;)Landroid/net/Uri;") \
  252. METHOD (toString, "toString", "()Ljava/lang/String;")
  253. DECLARE_JNI_CLASS (Uri, "android/net/Uri");
  254. #undef JNI_CLASS_MEMBERS
  255. //==========================================================================
  256. #if defined(JUCE_FIREBASE_INSTANCE_ID_SERVICE_CLASSNAME)
  257. #define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \
  258. STATICMETHOD (getInstance, "getInstance", "()Lcom/google/firebase/iid/FirebaseInstanceId;") \
  259. METHOD (getToken, "getToken", "()Ljava/lang/String;")
  260. DECLARE_JNI_CLASS (FirebaseInstanceId, "com/google/firebase/iid/FirebaseInstanceId");
  261. #undef JNI_CLASS_MEMBERS
  262. #endif
  263. #if defined(JUCE_FIREBASE_MESSAGING_SERVICE_CLASSNAME)
  264. #define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \
  265. STATICMETHOD (getInstance, "getInstance", "()Lcom/google/firebase/messaging/FirebaseMessaging;") \
  266. METHOD (send, "send", "(Lcom/google/firebase/messaging/RemoteMessage;)V") \
  267. METHOD (subscribeToTopic, "subscribeToTopic", "(Ljava/lang/String;)V") \
  268. METHOD (unsubscribeFromTopic, "unsubscribeFromTopic", "(Ljava/lang/String;)V") \
  269. DECLARE_JNI_CLASS (FirebaseMessaging, "com/google/firebase/messaging/FirebaseMessaging");
  270. #undef JNI_CLASS_MEMBERS
  271. #define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \
  272. METHOD (getCollapseKey, "getCollapseKey", "()Ljava/lang/String;") \
  273. METHOD (getData, "getData", "()Ljava/util/Map;") \
  274. METHOD (getFrom, "getFrom", "()Ljava/lang/String;") \
  275. METHOD (getMessageId, "getMessageId", "()Ljava/lang/String;") \
  276. METHOD (getMessageType, "getMessageType", "()Ljava/lang/String;") \
  277. METHOD (getNotification, "getNotification", "()Lcom/google/firebase/messaging/RemoteMessage$Notification;") \
  278. METHOD (getSentTime, "getSentTime", "()J") \
  279. METHOD (getTo, "getTo", "()Ljava/lang/String;") \
  280. METHOD (getTtl, "getTtl", "()I")
  281. DECLARE_JNI_CLASS (RemoteMessage, "com/google/firebase/messaging/RemoteMessage");
  282. #undef JNI_CLASS_MEMBERS
  283. #define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \
  284. METHOD (addData, "addData", "(Ljava/lang/String;Ljava/lang/String;)Lcom/google/firebase/messaging/RemoteMessage$Builder;") \
  285. METHOD (build, "build", "()Lcom/google/firebase/messaging/RemoteMessage;") \
  286. METHOD (constructor, "<init>", "(Ljava/lang/String;)V") \
  287. METHOD (setCollapseKey, "setCollapseKey", "(Ljava/lang/String;)Lcom/google/firebase/messaging/RemoteMessage$Builder;") \
  288. METHOD (setMessageId, "setMessageId", "(Ljava/lang/String;)Lcom/google/firebase/messaging/RemoteMessage$Builder;") \
  289. METHOD (setMessageType, "setMessageType", "(Ljava/lang/String;)Lcom/google/firebase/messaging/RemoteMessage$Builder;") \
  290. METHOD (setTtl, "setTtl", "(I)Lcom/google/firebase/messaging/RemoteMessage$Builder;")
  291. DECLARE_JNI_CLASS (RemoteMessageBuilder, "com/google/firebase/messaging/RemoteMessage$Builder");
  292. #undef JNI_CLASS_MEMBERS
  293. #define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \
  294. METHOD (getBody, "getBody", "()Ljava/lang/String;") \
  295. METHOD (getBodyLocalizationArgs, "getBodyLocalizationArgs", "()[Ljava/lang/String;") \
  296. METHOD (getBodyLocalizationKey, "getBodyLocalizationKey", "()Ljava/lang/String;") \
  297. METHOD (getClickAction, "getClickAction", "()Ljava/lang/String;") \
  298. METHOD (getColor, "getColor", "()Ljava/lang/String;") \
  299. METHOD (getIcon, "getIcon", "()Ljava/lang/String;") \
  300. METHOD (getLink, "getLink", "()Landroid/net/Uri;") \
  301. METHOD (getSound, "getSound", "()Ljava/lang/String;") \
  302. METHOD (getTag, "getTag", "()Ljava/lang/String;") \
  303. METHOD (getTitle, "getTitle", "()Ljava/lang/String;") \
  304. METHOD (getTitleLocalizationArgs, "getTitleLocalizationArgs", "()[Ljava/lang/String;") \
  305. METHOD (getTitleLocalizationKey, "getTitleLocalizationKey", "()Ljava/lang/String;")
  306. DECLARE_JNI_CLASS (RemoteMessageNotification, "com/google/firebase/messaging/RemoteMessage$Notification");
  307. #undef JNI_CLASS_MEMBERS
  308. #endif
  309. //==============================================================================
  310. bool PushNotifications::Notification::isValid() const noexcept
  311. {
  312. auto* env = getEnv();
  313. bool isValidForPreApi26 = title.isNotEmpty() && body.isNotEmpty() && identifier.isNotEmpty() && icon.isNotEmpty();
  314. bool apiAtLeast26 = env->CallStaticIntMethod (JuceAppActivity, JuceAppActivity.getAndroidSDKVersion) >= 26;
  315. if (apiAtLeast26)
  316. return isValidForPreApi26 && channelId.isNotEmpty();
  317. return isValidForPreApi26;
  318. }
  319. //==========================================================================
  320. struct PushNotifications::Pimpl
  321. {
  322. Pimpl (PushNotifications& p)
  323. : owner (p)
  324. {}
  325. bool areNotificationsEnabled() const
  326. {
  327. #if __ANDROID_API__ >= 24
  328. auto* env = getEnv();
  329. auto notificationManager = getNotificationManager();
  330. if (notificationManager.get() != 0)
  331. return env->CallBooleanMethod (notificationManager, NotificationManagerApi24.areNotificationsEnabled);
  332. return false;
  333. #else
  334. return true;
  335. #endif
  336. }
  337. //==========================================================================
  338. void sendLocalNotification (const PushNotifications::Notification& n)
  339. {
  340. // All required fields have to be setup!
  341. jassert (n.isValid());
  342. auto* env = getEnv();
  343. auto notificationManager = getNotificationManager();
  344. if (notificationManager.get() != 0)
  345. {
  346. auto notification = juceNotificationToJavaNotification (n);
  347. auto tag = javaString (n.identifier);
  348. const int id = 0;
  349. env->CallVoidMethod (notificationManager.get(), NotificationManagerBase.notify, tag.get(), id, notification.get());
  350. }
  351. }
  352. void getDeliveredNotifications() const
  353. {
  354. #if __ANDROID_API__ >= 23
  355. auto* env = getEnv();
  356. Array<PushNotifications::Notification> notifications;
  357. auto notificationManager = getNotificationManager();
  358. jassert (notificationManager.get() != 0);
  359. if (notificationManager.get() != 0)
  360. {
  361. auto statusBarNotifications = LocalRef<jobjectArray> ((jobjectArray)env->CallObjectMethod (notificationManager,
  362. NotificationManagerApi23.getActiveNotifications));
  363. const int numNotifications = env->GetArrayLength (statusBarNotifications.get());
  364. for (int i = 0; i < numNotifications; ++i)
  365. {
  366. auto statusBarNotification = LocalRef<jobject> (env->GetObjectArrayElement (statusBarNotifications.get(), (jsize) i));
  367. auto notification = LocalRef<jobject> (env->CallObjectMethod (statusBarNotification, StatusBarNotification.getNotification));
  368. notifications.add (javaNotificationToJuceNotification (notification));
  369. }
  370. }
  371. owner.listeners.call (&Listener::deliveredNotificationsListReceived, notifications);
  372. #else
  373. // Not supported on this platform
  374. jassertfalse;
  375. owner.listeners.call (&Listener::deliveredNotificationsListReceived, {});
  376. #endif
  377. }
  378. void notifyListenersAboutLocalNotification (const LocalRef<jobject>& intent)
  379. {
  380. auto* env = getEnv();
  381. auto bundle = LocalRef<jobject> (env->CallObjectMethod (intent, Intent.getExtras));
  382. const auto notification = localNotificationBundleToJuceNotification (bundle);
  383. auto packageName = juceString ((jstring) (android.activity.callObjectMethod (JuceAppActivity.getPackageName)));
  384. String notificationString = packageName + ".JUCE_NOTIFICATION.";
  385. String notificationButtonActionString = packageName + ".JUCE_NOTIFICATION_BUTTON_ACTION.";
  386. String notificationTextInputActionString = packageName + ".JUCE_NOTIFICATION_TEXT_INPUT_ACTION.";
  387. auto actionString = juceString ((jstring) env->CallObjectMethod (intent, Intent.getAction));
  388. if (actionString.contains (notificationString))
  389. {
  390. owner.listeners.call (&PushNotifications::Listener::handleNotification, true, notification);
  391. }
  392. else if (actionString.contains (notificationButtonActionString))
  393. {
  394. String prefix = notificationButtonActionString + notification.identifier + ".";
  395. auto actionTitle = actionString.fromLastOccurrenceOf (prefix, false, false) // skip prefix
  396. .fromFirstOccurrenceOf (".", false, false); // skip action index
  397. owner.listeners.call (&PushNotifications::Listener::handleNotificationAction,
  398. true,
  399. notification,
  400. actionTitle,
  401. {});
  402. }
  403. #if __ANDROID_API__ >= 20
  404. else if (actionString.contains (notificationTextInputActionString))
  405. {
  406. String prefix = notificationTextInputActionString + notification.identifier + ".";
  407. auto actionTitle = actionString.fromLastOccurrenceOf (prefix, false, false) // skip prefix
  408. .fromFirstOccurrenceOf (".", false, false); // skip action index
  409. auto actionIndex = actionString.fromLastOccurrenceOf (prefix, false, false).upToFirstOccurrenceOf (".", false, false);
  410. auto resultKeyString = javaString (actionTitle + actionIndex);
  411. auto remoteInputResult = LocalRef<jobject> (env->CallStaticObjectMethod (RemoteInput, RemoteInput.getResultsFromIntent, intent.get()));
  412. if (remoteInputResult.get() != 0)
  413. {
  414. auto charSequence = LocalRef<jobject> (env->CallObjectMethod (remoteInputResult, Bundle.getCharSequence, resultKeyString.get()));
  415. auto responseString = LocalRef<jstring> ((jstring) env->CallObjectMethod (charSequence, CharSequence.toString));
  416. owner.listeners.call (&PushNotifications::Listener::handleNotificationAction,
  417. true,
  418. notification,
  419. actionTitle,
  420. juceString (responseString.get()));
  421. }
  422. else
  423. {
  424. owner.listeners.call (&PushNotifications::Listener::handleNotificationAction,
  425. true,
  426. notification,
  427. actionTitle,
  428. {});
  429. }
  430. }
  431. #endif
  432. }
  433. void notifyListenersAboutLocalNotificationDeleted (const LocalRef<jobject>& intent)
  434. {
  435. auto* env = getEnv();
  436. auto bundle = LocalRef<jobject> (env->CallObjectMethod (intent, Intent.getExtras));
  437. owner.listeners.call (&PushNotifications::Listener::localNotificationDismissedByUser,
  438. localNotificationBundleToJuceNotification (bundle));
  439. }
  440. void removeAllDeliveredNotifications()
  441. {
  442. auto* env = getEnv();
  443. auto notificationManager = getNotificationManager();
  444. if (notificationManager.get() != 0)
  445. env->CallVoidMethod (notificationManager.get(), NotificationManagerBase.cancelAll);
  446. }
  447. void removeDeliveredNotification (const String& identifier)
  448. {
  449. auto* env = getEnv();
  450. auto notificationManager = getNotificationManager();
  451. if (notificationManager.get() != 0)
  452. {
  453. auto tag = javaString (identifier);
  454. const int id = 0;
  455. env->CallVoidMethod (notificationManager.get(), NotificationManagerBase.cancel, tag.get(), id);
  456. }
  457. }
  458. //==========================================================================
  459. String getDeviceToken() const
  460. {
  461. #if defined(JUCE_FIREBASE_INSTANCE_ID_SERVICE_CLASSNAME)
  462. auto* env = getEnv();
  463. auto instanceId = LocalRef<jobject> (env->CallStaticObjectMethod (FirebaseInstanceId, FirebaseInstanceId.getInstance));
  464. return juceString ((jstring) env->CallObjectMethod (instanceId, FirebaseInstanceId.getToken));
  465. #else
  466. return {};
  467. #endif
  468. }
  469. void notifyListenersTokenRefreshed (const String& token)
  470. {
  471. #if defined(JUCE_FIREBASE_INSTANCE_ID_SERVICE_CLASSNAME)
  472. MessageManager::callAsync ([this, token]()
  473. {
  474. owner.listeners.call (&PushNotifications::Listener::deviceTokenRefreshed, token);
  475. });
  476. #else
  477. ignoreUnused (token);
  478. #endif
  479. }
  480. void subscribeToTopic (const String& topic)
  481. {
  482. #if defined(JUCE_FIREBASE_MESSAGING_SERVICE_CLASSNAME)
  483. auto* env = getEnv();
  484. auto firebaseMessaging = LocalRef<jobject> (env->CallStaticObjectMethod (FirebaseMessaging,
  485. FirebaseMessaging.getInstance));
  486. env->CallVoidMethod (firebaseMessaging, FirebaseMessaging.subscribeToTopic, javaString (topic).get());
  487. #else
  488. ignoreUnused (topic);
  489. #endif
  490. }
  491. void unsubscribeFromTopic (const String& topic)
  492. {
  493. #if defined(JUCE_FIREBASE_MESSAGING_SERVICE_CLASSNAME)
  494. auto* env = getEnv();
  495. auto firebaseMessaging = LocalRef<jobject> (env->CallStaticObjectMethod (FirebaseMessaging,
  496. FirebaseMessaging.getInstance));
  497. env->CallVoidMethod (firebaseMessaging, FirebaseMessaging.unsubscribeFromTopic, javaString (topic).get());
  498. #else
  499. ignoreUnused (topic);
  500. #endif
  501. }
  502. void sendUpstreamMessage (const String& serverSenderId,
  503. const String& collapseKey,
  504. const String& messageId,
  505. const String& messageType,
  506. int timeToLive,
  507. const StringPairArray& additionalData)
  508. {
  509. #if defined(JUCE_FIREBASE_MESSAGING_SERVICE_CLASSNAME)
  510. auto* env = getEnv();
  511. auto messageBuilder = LocalRef<jobject> (env->NewObject (RemoteMessageBuilder,
  512. RemoteMessageBuilder.constructor,
  513. javaString (serverSenderId + "@gcm_googleapis.com").get()));
  514. env->CallObjectMethod (messageBuilder, RemoteMessageBuilder.setCollapseKey, javaString (collapseKey).get());
  515. env->CallObjectMethod (messageBuilder, RemoteMessageBuilder.setMessageId, javaString (messageId).get());
  516. env->CallObjectMethod (messageBuilder, RemoteMessageBuilder.setMessageType, javaString (messageType).get());
  517. env->CallObjectMethod (messageBuilder, RemoteMessageBuilder.setTtl, timeToLive);
  518. auto keys = additionalData.getAllKeys();
  519. for (const auto& key : keys)
  520. env->CallObjectMethod (messageBuilder,
  521. RemoteMessageBuilder.addData,
  522. javaString (key).get(),
  523. javaString (additionalData[key]).get());
  524. auto message = LocalRef<jobject> (env->CallObjectMethod (messageBuilder, RemoteMessageBuilder.build));
  525. auto firebaseMessaging = LocalRef<jobject> (env->CallStaticObjectMethod (FirebaseMessaging,
  526. FirebaseMessaging.getInstance));
  527. env->CallVoidMethod (firebaseMessaging, FirebaseMessaging.send, message.get());
  528. #else
  529. ignoreUnused (serverSenderId, collapseKey, messageId, messageType);
  530. ignoreUnused (timeToLive, additionalData);
  531. #endif
  532. }
  533. void notifyListenersAboutRemoteNotificationFromSystemTray (const LocalRef<jobject>& intent)
  534. {
  535. #if defined(JUCE_FIREBASE_MESSAGING_SERVICE_CLASSNAME)
  536. auto* env = getEnv();
  537. auto bundle = LocalRef<jobject> (env->CallObjectMethod (intent, Intent.getExtras));
  538. owner.listeners.call (&PushNotifications::Listener::handleNotification, false,
  539. remoteNotificationBundleToJuceNotification (bundle));
  540. #else
  541. ignoreUnused (intent);
  542. #endif
  543. }
  544. void notifyListenersAboutRemoteNotificationFromService (const LocalRef<jobject>& remoteNotification)
  545. {
  546. #if defined(JUCE_FIREBASE_MESSAGING_SERVICE_CLASSNAME)
  547. GlobalRef rn (remoteNotification.get());
  548. MessageManager::callAsync ([this, rn]()
  549. {
  550. owner.listeners.call (&PushNotifications::Listener::handleNotification, false,
  551. firebaseRemoteNotificationToJuceNotification (rn.get()));
  552. });
  553. #else
  554. ignoreUnused (remoteNotification);
  555. #endif
  556. }
  557. void notifyListenersAboutRemoteNotificationsDeleted()
  558. {
  559. #if defined(JUCE_FIREBASE_MESSAGING_SERVICE_CLASSNAME)
  560. MessageManager::callAsync ([this]()
  561. {
  562. owner.listeners.call (&PushNotifications::Listener::remoteNotificationsDeleted);
  563. });
  564. #endif
  565. }
  566. void notifyListenersAboutUpstreamMessageSent (const LocalRef<jstring>& messageId)
  567. {
  568. #if defined(JUCE_FIREBASE_MESSAGING_SERVICE_CLASSNAME)
  569. GlobalRef mid (messageId);
  570. MessageManager::callAsync ([this, mid]()
  571. {
  572. owner.listeners.call (&PushNotifications::Listener::upstreamMessageSent,
  573. juceString ((jstring) mid.get()));
  574. });
  575. #else
  576. ignoreUnused (messageId);
  577. #endif
  578. }
  579. void notifyListenersAboutUpstreamMessageSendingError (const LocalRef<jstring>& messageId,
  580. const LocalRef<jstring>& error)
  581. {
  582. #if defined(JUCE_FIREBASE_MESSAGING_SERVICE_CLASSNAME)
  583. GlobalRef mid (messageId), e (error);
  584. MessageManager::callAsync ([this, mid, e]()
  585. {
  586. owner.listeners.call (&PushNotifications::Listener::upstreamMessageSendingError,
  587. juceString ((jstring) mid.get()),
  588. juceString ((jstring) e.get()));
  589. });
  590. #else
  591. ignoreUnused (messageId, error);
  592. #endif
  593. }
  594. static LocalRef<jobject> getNotificationManager()
  595. {
  596. auto* env = getEnv();
  597. return LocalRef<jobject> (env->CallObjectMethod (android.activity,
  598. JuceAppActivity.getSystemService,
  599. javaString ("notification").get()));
  600. }
  601. static LocalRef<jobject> juceNotificationToJavaNotification (const PushNotifications::Notification& n)
  602. {
  603. auto* env = getEnv();
  604. auto notificationBuilder = createNotificationBuilder (n);
  605. setupRequiredFields (n, notificationBuilder);
  606. setupOptionalFields (n, notificationBuilder);
  607. if (n.actions.size() > 0)
  608. setupActions (n, notificationBuilder);
  609. #if __ANDROID_API__ >= 16
  610. return LocalRef<jobject> (env->CallObjectMethod (notificationBuilder, NotificationBuilderApi16.build));
  611. #else
  612. return LocalRef<jobject> (env->CallObjectMethod (notificationBuilder, NotificationBuilderBase.getNotification));
  613. #endif
  614. }
  615. static LocalRef<jobject> createNotificationBuilder (const PushNotifications::Notification& n)
  616. {
  617. auto* env = getEnv();
  618. jclass builderClass = env->FindClass ("android/app/Notification$Builder");
  619. jassert (builderClass != 0);
  620. if (builderClass == 0)
  621. return LocalRef<jobject> (0);
  622. jmethodID builderConstructor = 0;
  623. const bool apiAtLeast26 = env->CallStaticIntMethod (JuceAppActivity, JuceAppActivity.getAndroidSDKVersion) >= 26;
  624. if (apiAtLeast26)
  625. builderConstructor = env->GetMethodID (builderClass, "<init>", "(Landroid/content/Context;Ljava/lang/String;)V");
  626. else
  627. builderConstructor = env->GetMethodID (builderClass, "<init>", "(Landroid/content/Context;)V");
  628. jassert (builderConstructor != 0);
  629. if (builderConstructor == 0)
  630. return LocalRef<jobject> (0);
  631. if (apiAtLeast26)
  632. return LocalRef<jobject> (env->NewObject (builderClass, builderConstructor,
  633. android.activity.get(), javaString (n.channelId).get()));
  634. return LocalRef<jobject> (env->NewObject (builderClass, builderConstructor, android.activity.get()));
  635. }
  636. static void setupRequiredFields (const PushNotifications::Notification& n, LocalRef<jobject>& notificationBuilder)
  637. {
  638. auto* env = getEnv();
  639. auto activityClass = LocalRef<jobject> (env->CallObjectMethod (android.activity, Object.getClass));
  640. auto notifyIntent = LocalRef<jobject> (env->NewObject (Intent, Intent.constructorWithContextAndClass, android.activity.get(), activityClass.get()));
  641. auto packageNameString = LocalRef<jstring> ((jstring) (android.activity.callObjectMethod (JuceAppActivity.getPackageName)));
  642. auto actionStringSuffix = javaString (".JUCE_NOTIFICATION." + n.identifier);
  643. auto actionString = LocalRef<jstring> ((jstring)env->CallObjectMethod (packageNameString, JavaString.concat, actionStringSuffix.get()));
  644. env->CallObjectMethod (notifyIntent, Intent.setAction, actionString.get());
  645. // Packaging entire notification into extras bundle here, so that we can retrieve all the details later on
  646. env->CallObjectMethod (notifyIntent, Intent.putExtras, juceNotificationToBundle (n).get());
  647. auto notifyPendingIntent = LocalRef<jobject> (env->CallStaticObjectMethod (PendingIntent,
  648. PendingIntent.getActivity,
  649. android.activity.get(),
  650. 1002,
  651. notifyIntent.get(),
  652. 0));
  653. env->CallObjectMethod (notificationBuilder, NotificationBuilderBase.setContentTitle, javaString (n.title).get());
  654. env->CallObjectMethod (notificationBuilder, NotificationBuilderBase.setContentText, javaString (n.body).get());
  655. env->CallObjectMethod (notificationBuilder, NotificationBuilderBase.setContentIntent, notifyPendingIntent.get());
  656. auto resources = LocalRef<jobject> (env->CallObjectMethod (android.activity, JuceAppActivity.getResources));
  657. const int iconId = env->CallIntMethod (resources, Resources.getIdentifier, javaString (n.icon).get(),
  658. javaString ("raw").get(), packageNameString.get());
  659. env->CallObjectMethod (notificationBuilder, NotificationBuilderBase.setSmallIcon, iconId);
  660. #if __ANDROID_API__ >= 21
  661. if (n.publicVersion != nullptr)
  662. {
  663. // Public version of a notification is not expected to have another public one!
  664. jassert (n.publicVersion->publicVersion == nullptr);
  665. auto publicNotificationBuilder = createNotificationBuilder (n);
  666. setupRequiredFields (*n.publicVersion, publicNotificationBuilder);
  667. setupOptionalFields (*n.publicVersion, publicNotificationBuilder);
  668. auto publicVersion = LocalRef<jobject> (env->CallObjectMethod (publicNotificationBuilder, NotificationBuilderApi16.build));
  669. env->CallObjectMethod (notificationBuilder, NotificationBuilderApi21.setPublicVersion, publicVersion.get());
  670. }
  671. #endif
  672. }
  673. static LocalRef<jobject> juceNotificationToBundle (const PushNotifications::Notification& n)
  674. {
  675. auto* env = getEnv();
  676. auto bundle = LocalRef<jobject> (env->NewObject (Bundle, Bundle.constructor));
  677. env->CallVoidMethod (bundle, Bundle.putString, javaString ("identifier") .get(), javaString (n.identifier).get());
  678. env->CallVoidMethod (bundle, Bundle.putString, javaString ("title") .get(), javaString (n.title).get());
  679. env->CallVoidMethod (bundle, Bundle.putString, javaString ("body") .get(), javaString (n.body).get());
  680. env->CallVoidMethod (bundle, Bundle.putString, javaString ("subtitle") .get(), javaString (n.subtitle).get());
  681. env->CallVoidMethod (bundle, Bundle.putInt, javaString ("badgeNumber") .get(), n.badgeNumber);
  682. env->CallVoidMethod (bundle, Bundle.putString, javaString ("soundToPlay") .get(), javaString (n.soundToPlay.toString (true)).get());
  683. env->CallVoidMethod (bundle, Bundle.putBundle, javaString ("properties") .get(), varToBundleWithPropertiesString (n.properties).get());
  684. env->CallVoidMethod (bundle, Bundle.putString, javaString ("icon") .get(), javaString (n.icon).get());
  685. env->CallVoidMethod (bundle, Bundle.putString, javaString ("channelId") .get(), javaString (n.channelId).get());
  686. env->CallVoidMethod (bundle, Bundle.putString, javaString ("tickerText") .get(), javaString (n.tickerText).get());
  687. env->CallVoidMethod (bundle, Bundle.putInt, javaString ("progressMax") .get(), n.progress.max);
  688. env->CallVoidMethod (bundle, Bundle.putInt, javaString ("progressCurrent") .get(), n.progress.current);
  689. env->CallVoidMethod (bundle, Bundle.putBoolean, javaString ("progressIndeterminate") .get(), n.progress.indeterminate);
  690. env->CallVoidMethod (bundle, Bundle.putString, javaString ("person") .get(), javaString (n.person).get());
  691. env->CallVoidMethod (bundle, Bundle.putInt, javaString ("type") .get(), n.type);
  692. env->CallVoidMethod (bundle, Bundle.putInt, javaString ("priority") .get(), n.priority);
  693. env->CallVoidMethod (bundle, Bundle.putInt, javaString ("lockScreenAppearance") .get(), n.lockScreenAppearance);
  694. env->CallVoidMethod (bundle, Bundle.putString, javaString ("groupId") .get(), javaString (n.groupId).get());
  695. env->CallVoidMethod (bundle, Bundle.putString, javaString ("groupSortKey") .get(), javaString (n.groupSortKey).get());
  696. env->CallVoidMethod (bundle, Bundle.putBoolean, javaString ("groupSummary") .get(), n.groupSummary);
  697. env->CallVoidMethod (bundle, Bundle.putInt, javaString ("accentColour") .get(), n.accentColour.getARGB());
  698. env->CallVoidMethod (bundle, Bundle.putInt, javaString ("ledColour") .get(), n.ledColour.getARGB());
  699. env->CallVoidMethod (bundle, Bundle.putInt, javaString ("ledBlinkPatternMsToBeOn") .get(), n.ledBlinkPattern.msToBeOn);
  700. env->CallVoidMethod (bundle, Bundle.putInt, javaString ("ledBlinkPatternMsToBeOff").get(), n.ledBlinkPattern.msToBeOff);
  701. env->CallVoidMethod (bundle, Bundle.putBoolean, javaString ("shouldAutoCancel") .get(), n.shouldAutoCancel);
  702. env->CallVoidMethod (bundle, Bundle.putBoolean, javaString ("localOnly") .get(), n.localOnly);
  703. env->CallVoidMethod (bundle, Bundle.putBoolean, javaString ("ongoing") .get(), n.ongoing);
  704. env->CallVoidMethod (bundle, Bundle.putBoolean, javaString ("alertOnlyOnce") .get(), n.alertOnlyOnce);
  705. env->CallVoidMethod (bundle, Bundle.putInt, javaString ("timestampVisibility") .get(), n.timestampVisibility);
  706. env->CallVoidMethod (bundle, Bundle.putInt, javaString ("badgeIconType") .get(), n.badgeIconType);
  707. env->CallVoidMethod (bundle, Bundle.putInt, javaString ("groupAlertBehaviour") .get(), n.groupAlertBehaviour);
  708. env->CallVoidMethod (bundle, Bundle.putLong, javaString ("timeoutAfterMs") .get(), (jlong)n.timeoutAfterMs);
  709. const int size = n.vibrationPattern.size();
  710. if (size > 0)
  711. {
  712. auto array = LocalRef<jlongArray> (env->NewLongArray (size));
  713. jlong* elements = env->GetLongArrayElements (array, 0);
  714. for (int i = 0; i < size; ++i)
  715. elements[i] = (jlong) n.vibrationPattern[i];
  716. env->SetLongArrayRegion (array, 0, size, elements);
  717. env->CallVoidMethod (bundle, Bundle.putLongArray, javaString ("vibrationPattern").get(), array.get());
  718. }
  719. return bundle;
  720. }
  721. static void setupOptionalFields (const PushNotifications::Notification& n, LocalRef<jobject>& notificationBuilder)
  722. {
  723. auto* env = getEnv();
  724. if (n.subtitle.isNotEmpty())
  725. env->CallObjectMethod (notificationBuilder, NotificationBuilderBase.setContentInfo, javaString (n.subtitle).get());
  726. auto soundName = n.soundToPlay.toString (true);
  727. if (soundName == "default_os_sound")
  728. {
  729. const int playDefaultSound = 1;
  730. env->CallObjectMethod (notificationBuilder, NotificationBuilderBase.setDefaults, playDefaultSound);
  731. }
  732. else if (! soundName.isEmpty())
  733. {
  734. env->CallObjectMethod (notificationBuilder, NotificationBuilderBase.setSound, juceUrlToAndroidUri (n.soundToPlay).get());
  735. }
  736. if (n.largeIcon.isValid())
  737. env->CallObjectMethod (notificationBuilder, NotificationBuilderBase.setLargeIcon, imagetoJavaBitmap (n.largeIcon).get());
  738. if (n.tickerText.isNotEmpty())
  739. env->CallObjectMethod (notificationBuilder, NotificationBuilderBase.setTicker, javaString (n.tickerText).get());
  740. if (n.ledColour != Colour())
  741. {
  742. env->CallObjectMethod (notificationBuilder,
  743. NotificationBuilderBase.setLights,
  744. n.ledColour.getARGB(),
  745. n.ledBlinkPattern.msToBeOn,
  746. n.ledBlinkPattern.msToBeOff);
  747. }
  748. if (! n.vibrationPattern.isEmpty())
  749. {
  750. const int size = n.vibrationPattern.size();
  751. if (size > 0)
  752. {
  753. auto array = LocalRef<jlongArray> (env->NewLongArray (size));
  754. jlong* elements = env->GetLongArrayElements (array, 0);
  755. for (int i = 0; i < size; ++i)
  756. elements[i] = (jlong) n.vibrationPattern[i];
  757. env->SetLongArrayRegion (array, 0, size, elements);
  758. env->CallObjectMethod (notificationBuilder, NotificationBuilderBase.setVibrate, array.get());
  759. }
  760. }
  761. env->CallObjectMethod (notificationBuilder, NotificationBuilderBase.setProgress, n.progress.max, n.progress.current, n.progress.indeterminate);
  762. env->CallObjectMethod (notificationBuilder, NotificationBuilderBase.setNumber, n.badgeNumber);
  763. env->CallObjectMethod (notificationBuilder, NotificationBuilderBase.setAutoCancel, n.shouldAutoCancel);
  764. env->CallObjectMethod (notificationBuilder, NotificationBuilderBase.setOngoing, n.ongoing);
  765. env->CallObjectMethod (notificationBuilder, NotificationBuilderBase.setOnlyAlertOnce, n.alertOnlyOnce);
  766. #if __ANDROID_API__ >= 16
  767. if (n.subtitle.isNotEmpty())
  768. env->CallObjectMethod (notificationBuilder, NotificationBuilderApi16.setSubText, javaString (n.subtitle).get());
  769. env->CallObjectMethod (notificationBuilder, NotificationBuilderApi16.setPriority, n.priority);
  770. #if __ANDROID_API__ < 24
  771. const bool useChronometer = n.timestampVisibility == PushNotifications::Notification::chronometer;
  772. env->CallObjectMethod (notificationBuilder, NotificationBuilderApi16.setUsesChronometer, useChronometer);
  773. #endif
  774. #endif
  775. #if __ANDROID_API__ >= 17
  776. const bool showTimeStamp = n.timestampVisibility != PushNotifications::Notification::off;
  777. env->CallObjectMethod (notificationBuilder, NotificationBuilderApi17.setShowWhen, showTimeStamp);
  778. #endif
  779. #if __ANDROID_API__ >= 20
  780. if (n.groupId.isNotEmpty())
  781. {
  782. env->CallObjectMethod (notificationBuilder, NotificationBuilderApi20.setGroup, javaString (n.groupId).get());
  783. env->CallObjectMethod (notificationBuilder, NotificationBuilderApi20.setGroupSummary, n.groupSummary);
  784. }
  785. if (n.groupSortKey.isNotEmpty())
  786. env->CallObjectMethod (notificationBuilder, NotificationBuilderApi20.setSortKey, javaString (n.groupSortKey).get());
  787. env->CallObjectMethod (notificationBuilder, NotificationBuilderApi20.setLocalOnly, n.localOnly);
  788. auto extras = LocalRef<jobject> (env->NewObject (Bundle, Bundle.constructor));
  789. env->CallVoidMethod (extras, Bundle.putBundle, javaString ("notificationData").get(),
  790. juceNotificationToBundle (n).get());
  791. env->CallObjectMethod (notificationBuilder, NotificationBuilderApi20.addExtras, extras.get());
  792. #endif
  793. #if __ANDROID_API__ >= 21
  794. if (n.person.isNotEmpty())
  795. env->CallObjectMethod (notificationBuilder, NotificationBuilderApi21.addPerson, javaString (n.person).get());
  796. auto categoryString = typeToCategory (n.type);
  797. if (categoryString.isNotEmpty())
  798. env->CallObjectMethod (notificationBuilder, NotificationBuilderApi21.setCategory, javaString (categoryString).get());
  799. if (n.accentColour != Colour())
  800. env->CallObjectMethod (notificationBuilder, NotificationBuilderApi21.setColor, n.accentColour.getARGB());
  801. env->CallObjectMethod (notificationBuilder, NotificationBuilderApi21.setVisibility, n.lockScreenAppearance);
  802. #endif
  803. #if __ANDROID_API__ >= 24
  804. const bool useChronometer = n.timestampVisibility == PushNotifications::Notification::chronometer;
  805. const bool useCountDownChronometer = n.timestampVisibility == PushNotifications::Notification::countDownChronometer;
  806. env->CallObjectMethod (notificationBuilder, NotificationBuilderApi24.setChronometerCountDown, useCountDownChronometer);
  807. env->CallObjectMethod (notificationBuilder, NotificationBuilderApi16.setUsesChronometer, useChronometer | useCountDownChronometer);
  808. #endif
  809. #if __ANDROID_API__ >= 26
  810. env->CallObjectMethod (notificationBuilder, NotificationBuilderApi26.setBadgeIconType, n.badgeIconType);
  811. env->CallObjectMethod (notificationBuilder, NotificationBuilderApi26.setGroupAlertBehavior, n.groupAlertBehaviour);
  812. env->CallObjectMethod (notificationBuilder, NotificationBuilderApi26.setTimeoutAfter, (jlong) n.timeoutAfterMs);
  813. #endif
  814. setupNotificationDeletedCallback (n, notificationBuilder);
  815. }
  816. static void setupNotificationDeletedCallback (const PushNotifications::Notification& n,
  817. LocalRef<jobject>& notificationBuilder)
  818. {
  819. auto* env = getEnv();
  820. auto activityClass = LocalRef<jobject> (env->CallObjectMethod (android.activity, Object.getClass));
  821. auto deleteIntent = LocalRef<jobject> (env->NewObject (Intent, Intent.constructorWithContextAndClass, android.activity.get(), activityClass.get()));
  822. auto packageNameString = LocalRef<jstring> ((jstring) (android.activity.callObjectMethod (JuceAppActivity.getPackageName)));
  823. auto actionStringSuffix = javaString (".JUCE_NOTIFICATION_DELETED." + n.identifier);
  824. auto actionString = LocalRef<jstring> ((jstring)env->CallObjectMethod (packageNameString, JavaString.concat, actionStringSuffix.get()));
  825. env->CallObjectMethod (deleteIntent, Intent.setAction, actionString.get());
  826. env->CallObjectMethod (deleteIntent, Intent.putExtras, juceNotificationToBundle (n).get());
  827. auto deletePendingIntent = LocalRef<jobject> (env->CallStaticObjectMethod (PendingIntent,
  828. PendingIntent.getActivity,
  829. android.activity.get(),
  830. 1002,
  831. deleteIntent.get(),
  832. 0));
  833. env->CallObjectMethod (notificationBuilder, NotificationBuilderBase.setDeleteIntent, deletePendingIntent.get());
  834. }
  835. static void setupActions (const PushNotifications::Notification& n, LocalRef<jobject>& notificationBuilder)
  836. {
  837. #if __ANDROID_API__ >= 16
  838. auto* env = getEnv();
  839. int actionIndex = 0;
  840. for (const auto& action : n.actions)
  841. {
  842. auto activityClass = LocalRef<jobject> (env->CallObjectMethod (android.activity, Object.getClass));
  843. auto notifyIntent = LocalRef<jobject> (env->NewObject (Intent, Intent.constructorWithContextAndClass, android.activity.get(), activityClass.get()));
  844. const bool isTextStyle = action.style == PushNotifications::Notification::Action::text;
  845. auto packageNameString = LocalRef<jstring> ((jstring) (android.activity.callObjectMethod (JuceAppActivity.getPackageName)));
  846. const String notificationActionString = isTextStyle ? ".JUCE_NOTIFICATION_TEXT_INPUT_ACTION." : ".JUCE_NOTIFICATION_BUTTON_ACTION.";
  847. auto actionStringSuffix = javaString (notificationActionString + n.identifier + "." + String (actionIndex) + "." + action.title);
  848. auto actionString = LocalRef<jstring> ((jstring)env->CallObjectMethod (packageNameString, JavaString.concat, actionStringSuffix.get()));
  849. env->CallObjectMethod (notifyIntent, Intent.setAction, actionString.get());
  850. // Packaging entire notification into extras bundle here, so that we can retrieve all the details later on
  851. env->CallObjectMethod (notifyIntent, Intent.putExtras, juceNotificationToBundle (n).get());
  852. auto notifyPendingIntent = LocalRef<jobject> (env->CallStaticObjectMethod (PendingIntent,
  853. PendingIntent.getActivity,
  854. android.activity.get(),
  855. 1002,
  856. notifyIntent.get(),
  857. 0));
  858. auto resources = LocalRef<jobject> (env->CallObjectMethod (android.activity, JuceAppActivity.getResources));
  859. int iconId = env->CallIntMethod (resources, Resources.getIdentifier, javaString (action.icon).get(),
  860. javaString ("raw").get(), packageNameString.get());
  861. if (iconId == 0)
  862. iconId = env->CallIntMethod (resources, Resources.getIdentifier, javaString (n.icon).get(),
  863. javaString ("raw").get(), packageNameString.get());
  864. #if __ANDROID_API__ >= 20
  865. auto actionBuilder = LocalRef<jobject> (env->NewObject (NotificationActionBuilder,
  866. NotificationActionBuilder.constructor,
  867. iconId,
  868. javaString (action.title).get(),
  869. notifyPendingIntent.get()));
  870. env->CallObjectMethod (actionBuilder, NotificationActionBuilder.addExtras,
  871. varToBundleWithPropertiesString (action.parameters).get());
  872. if (isTextStyle)
  873. {
  874. auto resultKey = javaString (action.title + String (actionIndex));
  875. auto remoteInputBuilder = LocalRef<jobject> (env->NewObject (RemoteInputBuilder,
  876. RemoteInputBuilder.constructor,
  877. resultKey.get()));
  878. if (! action.textInputPlaceholder.isEmpty())
  879. env->CallObjectMethod (remoteInputBuilder, RemoteInputBuilder.setLabel, javaString (action.textInputPlaceholder).get());
  880. if (! action.allowedResponses.isEmpty())
  881. {
  882. env->CallObjectMethod (remoteInputBuilder, RemoteInputBuilder.setAllowFreeFormInput, false);
  883. const int size = action.allowedResponses.size();
  884. auto array = LocalRef<jobjectArray> (env->NewObjectArray (size, env->FindClass ("java/lang/String"), 0));
  885. for (int i = 0; i < size; ++i)
  886. {
  887. const auto& response = action.allowedResponses[i];
  888. auto responseString = javaString (response);
  889. env->SetObjectArrayElement (array, i, responseString.get());
  890. }
  891. env->CallObjectMethod (remoteInputBuilder, RemoteInputBuilder.setChoices, array.get());
  892. }
  893. env->CallObjectMethod (actionBuilder, NotificationActionBuilder.addRemoteInput,
  894. env->CallObjectMethod (remoteInputBuilder, RemoteInputBuilder.build));
  895. }
  896. env->CallObjectMethod (notificationBuilder, NotificationBuilderApi20.addAction,
  897. env->CallObjectMethod (actionBuilder, NotificationActionBuilder.build));
  898. #else
  899. env->CallObjectMethod (notificationBuilder, NotificationBuilderApi16.addAction,
  900. iconId, javaString (action.title).get(), notifyPendingIntent.get());
  901. #endif
  902. ++actionIndex;
  903. }
  904. #else
  905. ignoreUnused (n, notificationBuilder);
  906. #endif // #if __ANDROID_API__ >= 16
  907. }
  908. static LocalRef<jobject> juceUrlToAndroidUri (const URL& url)
  909. {
  910. auto* env = getEnv();
  911. auto packageNameString = LocalRef<jstring> ((jstring) (android.activity.callObjectMethod (JuceAppActivity.getPackageName)));
  912. auto resources = LocalRef<jobject> (env->CallObjectMethod (android.activity, JuceAppActivity.getResources));
  913. const int id = env->CallIntMethod (resources, Resources.getIdentifier, javaString (url.toString (true)).get(),
  914. javaString ("raw").get(), packageNameString.get());
  915. auto schemeString = javaString ("android.resource://");
  916. auto resourceString = javaString ("/" + String (id));
  917. auto uriString = LocalRef<jstring> ((jstring) env->CallObjectMethod (schemeString, JavaString.concat, packageNameString.get()));
  918. uriString = LocalRef<jstring> ((jstring) env->CallObjectMethod (uriString, JavaString.concat, resourceString.get()));
  919. return LocalRef<jobject> (env->CallStaticObjectMethod (Uri, Uri.parse, uriString.get()));
  920. }
  921. static LocalRef<jobject> imagetoJavaBitmap (const Image& image)
  922. {
  923. auto* env = getEnv();
  924. Image imageToUse = image.convertedToFormat (Image::PixelFormat::ARGB);
  925. auto bitmapConfig = LocalRef<jobject> (env->CallStaticObjectMethod (BitmapConfig, BitmapConfig.valueOf, javaString ("ARGB_8888").get()));
  926. auto bitmap = LocalRef<jobject> (env->CallStaticObjectMethod (Bitmap,
  927. Bitmap.createBitmap,
  928. image.getWidth(),
  929. image.getHeight(),
  930. bitmapConfig.get()));
  931. for (int i = 0; i < image.getWidth(); ++i)
  932. for (int j = 0; j < image.getHeight(); ++j)
  933. env->CallVoidMethod (bitmap.get(), Bitmap.setPixel, i, j, image.getPixelAt (i, j).getARGB());
  934. return bitmap;
  935. }
  936. static String typeToCategory (PushNotifications::Notification::Type t)
  937. {
  938. switch (t)
  939. {
  940. case PushNotifications::Notification::unspecified: return {};
  941. case PushNotifications::Notification::alarm: return "alarm";
  942. case PushNotifications::Notification::call: return "call";
  943. case PushNotifications::Notification::email: return "email";
  944. case PushNotifications::Notification::error: return "err";
  945. case PushNotifications::Notification::event: return "event";
  946. case PushNotifications::Notification::message: return "msg";
  947. case PushNotifications::Notification::taskProgress: return "progress";
  948. case PushNotifications::Notification::promo: return "promo";
  949. case PushNotifications::Notification::recommendation: return "recommendation";
  950. case PushNotifications::Notification::reminder: return "reminder";
  951. case PushNotifications::Notification::service: return "service";
  952. case PushNotifications::Notification::social: return "social";
  953. case PushNotifications::Notification::status: return "status";
  954. case PushNotifications::Notification::system: return "sys";
  955. case PushNotifications::Notification::transport: return "transport";
  956. }
  957. return {};
  958. }
  959. static LocalRef<jobject> varToBundleWithPropertiesString (const var& varToParse)
  960. {
  961. auto* env = getEnv();
  962. auto bundle = LocalRef<jobject> (env->NewObject (Bundle, Bundle.constructor));
  963. env->CallVoidMethod (bundle, Bundle.putString, javaString ("properties").get(),
  964. javaString (JSON::toString (varToParse, false)).get());
  965. return bundle;
  966. }
  967. // Gets "properties" var from bundle.
  968. static var bundleWithPropertiesStringToVar (const LocalRef<jobject>& bundle)
  969. {
  970. auto* env = getEnv();
  971. auto varString = LocalRef<jstring> ((jstring)env->CallObjectMethod (bundle, Bundle.getString,
  972. javaString ("properties").get()));
  973. var resultVar;
  974. JSON::parse (juceString (varString.get()), resultVar);
  975. // Note: we are not checking if result of parsing was okay, because there may be no properties set at all.
  976. return resultVar;
  977. }
  978. // Reverse of juceNotificationToBundle().
  979. static PushNotifications::Notification localNotificationBundleToJuceNotification (const LocalRef<jobject>& bundle)
  980. {
  981. auto* env = getEnv();
  982. PushNotifications::Notification n;
  983. if (bundle.get() != 0)
  984. {
  985. n.identifier = getStringFromBundle (env, "identifier", bundle);
  986. n.title = getStringFromBundle (env, "title", bundle);
  987. n.body = getStringFromBundle (env, "body", bundle);
  988. n.subtitle = getStringFromBundle (env, "subtitle", bundle);
  989. n.badgeNumber = getIntFromBundle (env, "badgeNumber", bundle);
  990. n.soundToPlay = URL (getStringFromBundle (env, "soundToPlay", bundle));
  991. n.properties = getPropertiesVarFromBundle (env, "properties", bundle);
  992. n.tickerText = getStringFromBundle (env, "tickerText", bundle);
  993. n.icon = getStringFromBundle (env, "icon", bundle);
  994. n.channelId = getStringFromBundle (env, "channelId", bundle);
  995. PushNotifications::Notification::Progress progress;
  996. progress.max = getIntFromBundle (env, "progressMax", bundle);
  997. progress.current = getIntFromBundle (env, "progressCurrent", bundle);
  998. progress.indeterminate = getBoolFromBundle (env, "progressIndeterminate", bundle);
  999. n.progress = progress;
  1000. n.person = getStringFromBundle (env, "person", bundle);
  1001. n.type = (PushNotifications::Notification::Type) getIntFromBundle (env, "type", bundle);
  1002. n.priority = (PushNotifications::Notification::Priority) getIntFromBundle (env, "priority", bundle);
  1003. n.lockScreenAppearance = (PushNotifications::Notification::LockScreenAppearance) getIntFromBundle (env, "lockScreenAppearance", bundle);
  1004. n.groupId = getStringFromBundle (env, "groupId", bundle);
  1005. n.groupSortKey = getStringFromBundle (env, "groupSortKey", bundle);
  1006. n.groupSummary = getBoolFromBundle (env, "groupSummary", bundle);
  1007. n.accentColour = Colour ((uint32) getIntFromBundle (env, "accentColour", bundle));
  1008. n.ledColour = Colour ((uint32) getIntFromBundle (env, "ledColour", bundle));
  1009. PushNotifications::Notification::LedBlinkPattern ledBlinkPattern;
  1010. ledBlinkPattern.msToBeOn = getIntFromBundle (env, "ledBlinkPatternMsToBeOn", bundle);
  1011. ledBlinkPattern.msToBeOff = getIntFromBundle (env, "ledBlinkPatternMsToBeOff", bundle);
  1012. n.ledBlinkPattern = ledBlinkPattern;
  1013. n.vibrationPattern = getLongArrayFromBundle (env, "vibrationPattern", bundle);
  1014. n.shouldAutoCancel = getBoolFromBundle (env, "shouldAutoCancel", bundle);
  1015. n.localOnly = getBoolFromBundle (env, "localOnly", bundle);
  1016. n.ongoing = getBoolFromBundle (env, "ongoing", bundle);
  1017. n.alertOnlyOnce = getBoolFromBundle (env, "alertOnlyOnce", bundle);
  1018. n.timestampVisibility = (PushNotifications::Notification::TimestampVisibility) getIntFromBundle (env, "timestampVisibility", bundle);
  1019. n.badgeIconType = (PushNotifications::Notification::BadgeIconType) getIntFromBundle (env, "badgeIconType", bundle);
  1020. n.groupAlertBehaviour = (PushNotifications::Notification::GroupAlertBehaviour) getIntFromBundle (env, "groupAlertBehaviour", bundle);
  1021. n.timeoutAfterMs = getLongFromBundle (env, "timeoutAfterMs", bundle);
  1022. }
  1023. return n;
  1024. }
  1025. static String getStringFromBundle (JNIEnv* env, const String& key, const LocalRef<jobject>& bundle)
  1026. {
  1027. auto keyString = javaString (key);
  1028. if (env->CallBooleanMethod (bundle, Bundle.containsKey, keyString.get()))
  1029. {
  1030. auto value = LocalRef<jstring> ((jstring)env->CallObjectMethod (bundle, Bundle.getString, keyString.get()));
  1031. return juceString (value);
  1032. }
  1033. return {};
  1034. }
  1035. static int getIntFromBundle (JNIEnv* env, const String& key, const LocalRef<jobject>& bundle)
  1036. {
  1037. auto keyString = javaString (key);
  1038. if (env->CallBooleanMethod (bundle, Bundle.containsKey, keyString.get()))
  1039. return env->CallIntMethod (bundle, Bundle.getInt, keyString.get());
  1040. return 0;
  1041. }
  1042. // Converting to int on purpose!
  1043. static int getLongFromBundle (JNIEnv* env, const String& key, const LocalRef<jobject>& bundle)
  1044. {
  1045. auto keyString = javaString (key);
  1046. if (env->CallBooleanMethod (bundle, Bundle.containsKey, keyString.get()))
  1047. return (int) env->CallLongMethod (bundle, Bundle.getLong, keyString.get());
  1048. return 0;
  1049. }
  1050. static var getPropertiesVarFromBundle (JNIEnv* env, const String& key, const LocalRef<jobject>& bundle)
  1051. {
  1052. auto keyString = javaString (key);
  1053. if (env->CallBooleanMethod (bundle, Bundle.containsKey, keyString.get()))
  1054. {
  1055. auto value = LocalRef<jobject> (env->CallObjectMethod (bundle, Bundle.getBundle, keyString.get()));
  1056. return bundleWithPropertiesStringToVar (value);
  1057. }
  1058. return {};
  1059. }
  1060. static bool getBoolFromBundle (JNIEnv* env, const String& key, const LocalRef<jobject>& bundle)
  1061. {
  1062. auto keyString = javaString (key);
  1063. if (env->CallBooleanMethod (bundle, Bundle.containsKey, keyString.get()))
  1064. return env->CallBooleanMethod (bundle, Bundle.getBoolean, keyString.get());
  1065. return false;
  1066. }
  1067. static Array<int> getLongArrayFromBundle (JNIEnv* env, const String& key, const LocalRef<jobject>& bundle)
  1068. {
  1069. auto keyString = javaString (key);
  1070. if (env->CallBooleanMethod (bundle, Bundle.containsKey, keyString.get()))
  1071. {
  1072. auto array = LocalRef<jlongArray> ((jlongArray) env->CallObjectMethod (bundle, Bundle.getLongArray, keyString.get()));
  1073. const int size = env->GetArrayLength (array.get());
  1074. jlong* elements = env->GetLongArrayElements (array.get(), 0);
  1075. Array<int> resultArray;
  1076. for (int i = 0; i < size; ++i)
  1077. resultArray.add ((int) *elements++);
  1078. return resultArray;
  1079. }
  1080. return {};
  1081. }
  1082. static PushNotifications::Notification javaNotificationToJuceNotification (const LocalRef<jobject>& notification)
  1083. {
  1084. #if __ANDROID_API__ >= 20
  1085. auto* env = getEnv();
  1086. auto extras = LocalRef<jobject> (env->GetObjectField (notification, AndroidNotification.extras));
  1087. auto notificationData = LocalRef<jobject> (env->CallObjectMethod (extras, Bundle.getBundle,
  1088. javaString ("notificationData").get()));
  1089. return localNotificationBundleToJuceNotification (notificationData);
  1090. #else
  1091. ignoreUnused (notification);
  1092. return {};
  1093. #endif
  1094. }
  1095. static PushNotifications::Notification remoteNotificationBundleToJuceNotification (const LocalRef<jobject>& bundle)
  1096. {
  1097. // This will probably work only for remote notifications that get delivered to system tray
  1098. PushNotifications::Notification n;
  1099. n.properties = bundleToVar (bundle);
  1100. return n;
  1101. }
  1102. static var bundleToVar (const LocalRef<jobject>& bundle)
  1103. {
  1104. if (bundle.get() != 0)
  1105. {
  1106. auto* env = getEnv();
  1107. auto keySet = LocalRef<jobject> (env->CallObjectMethod (bundle, Bundle.keySet));
  1108. auto iterator = LocalRef<jobject> (env->CallObjectMethod (keySet, Set.iterator));
  1109. DynamicObject::Ptr dynamicObject = new DynamicObject();
  1110. for (;;)
  1111. {
  1112. if (! env->CallBooleanMethod (iterator, Iterator.hasNext))
  1113. break;
  1114. auto key = LocalRef<jstring> ((jstring) env->CallObjectMethod (iterator, Iterator.next));
  1115. auto object = LocalRef<jobject> (env->CallObjectMethod (bundle, Bundle.get, key.get()));
  1116. auto objectAsString = LocalRef<jstring> ((jstring) env->CallObjectMethod (object, Object.toString));
  1117. auto objectClass = LocalRef<jobject> (env->CallObjectMethod (object, Object.getClass));
  1118. auto classAsString = LocalRef<jstring> ((jstring) env->CallObjectMethod (objectClass, JavaClass.getName));
  1119. // Note: seems that Firebase delivers values as strings always, so this check is rather unnecessary,
  1120. // at least till they change the behaviour.
  1121. const var value = juceString (classAsString) == "java.lang.Bundle" ? bundleToVar (object) : var (juceString (objectAsString.get()));
  1122. dynamicObject->setProperty (juceString (key.get()), value);
  1123. }
  1124. return var (dynamicObject);
  1125. }
  1126. return {};
  1127. }
  1128. #if defined(JUCE_FIREBASE_MESSAGING_SERVICE_CLASSNAME)
  1129. static PushNotifications::Notification firebaseRemoteNotificationToJuceNotification (jobject remoteNotification)
  1130. {
  1131. auto* env = getEnv();
  1132. auto collapseKey = LocalRef<jstring> ((jstring) env->CallObjectMethod (remoteNotification, RemoteMessage.getCollapseKey));
  1133. auto from = LocalRef<jstring> ((jstring) env->CallObjectMethod (remoteNotification, RemoteMessage.getFrom));
  1134. auto messageId = LocalRef<jstring> ((jstring) env->CallObjectMethod (remoteNotification, RemoteMessage.getMessageId));
  1135. auto messageType = LocalRef<jstring> ((jstring) env->CallObjectMethod (remoteNotification, RemoteMessage.getMessageType));
  1136. auto to = LocalRef<jstring> ((jstring) env->CallObjectMethod (remoteNotification, RemoteMessage.getTo));
  1137. auto notification = LocalRef<jobject> (env->CallObjectMethod (remoteNotification, RemoteMessage.getNotification));
  1138. auto data = LocalRef<jobject> (env->CallObjectMethod (remoteNotification, RemoteMessage.getData));
  1139. const int64 sentTime = env->CallLongMethod (remoteNotification, RemoteMessage.getSentTime);
  1140. const int ttl = env->CallIntMethod (remoteNotification, RemoteMessage.getTtl);
  1141. auto keySet = LocalRef<jobject> (env->CallObjectMethod (data, JavaMap.keySet));
  1142. auto iterator = LocalRef<jobject> (env->CallObjectMethod (keySet, Set.iterator));
  1143. DynamicObject::Ptr dataDynamicObject = new DynamicObject();
  1144. for (;;)
  1145. {
  1146. if (! env->CallBooleanMethod (iterator, Iterator.hasNext))
  1147. break;
  1148. auto key = LocalRef<jstring> ((jstring) env->CallObjectMethod (iterator, Iterator.next));
  1149. auto value = LocalRef<jstring> ((jstring) env->CallObjectMethod (data, JavaMap.get, key.get()));
  1150. dataDynamicObject->setProperty (juceString (key.get()), juceString (value.get()));
  1151. }
  1152. var dataVar (dataDynamicObject);
  1153. DynamicObject::Ptr propertiesDynamicObject = new DynamicObject();
  1154. propertiesDynamicObject->setProperty ("collapseKey", juceString (collapseKey.get()));
  1155. propertiesDynamicObject->setProperty ("from", juceString (from.get()));
  1156. propertiesDynamicObject->setProperty ("messageId", juceString (messageId.get()));
  1157. propertiesDynamicObject->setProperty ("messageType", juceString (messageType.get()));
  1158. propertiesDynamicObject->setProperty ("to", juceString (to.get()));
  1159. propertiesDynamicObject->setProperty ("sentTime", sentTime);
  1160. propertiesDynamicObject->setProperty ("ttl", ttl);
  1161. propertiesDynamicObject->setProperty ("data", dataVar);
  1162. PushNotifications::Notification n;
  1163. if (notification != 0)
  1164. {
  1165. auto body = LocalRef<jstring> ((jstring) env->CallObjectMethod (notification, RemoteMessageNotification.getBody));
  1166. auto bodyLocalizationKey = LocalRef<jstring> ((jstring) env->CallObjectMethod (notification, RemoteMessageNotification.getBodyLocalizationKey));
  1167. auto clickAction = LocalRef<jstring> ((jstring) env->CallObjectMethod (notification, RemoteMessageNotification.getClickAction));
  1168. auto color = LocalRef<jstring> ((jstring) env->CallObjectMethod (notification, RemoteMessageNotification.getColor));
  1169. auto icon = LocalRef<jstring> ((jstring) env->CallObjectMethod (notification, RemoteMessageNotification.getIcon));
  1170. auto sound = LocalRef<jstring> ((jstring) env->CallObjectMethod (notification, RemoteMessageNotification.getSound));
  1171. auto tag = LocalRef<jstring> ((jstring) env->CallObjectMethod (notification, RemoteMessageNotification.getTag));
  1172. auto title = LocalRef<jstring> ((jstring) env->CallObjectMethod (notification, RemoteMessageNotification.getTitle));
  1173. auto titleLocalizationKey = LocalRef<jstring> ((jstring) env->CallObjectMethod (notification, RemoteMessageNotification.getTitleLocalizationKey));
  1174. auto link = LocalRef<jobject> (env->CallObjectMethod (notification, RemoteMessageNotification.getLink));
  1175. auto bodyLocalizationArgs = LocalRef<jobjectArray> ((jobjectArray) env->CallObjectMethod (notification, RemoteMessageNotification.getBodyLocalizationArgs));
  1176. auto titleLocalizationArgs = LocalRef<jobjectArray> ((jobjectArray) env->CallObjectMethod (notification, RemoteMessageNotification.getTitleLocalizationArgs));
  1177. n.identifier = juceString (tag.get());
  1178. n.title = juceString (title.get());
  1179. n.body = juceString (body.get());
  1180. n.soundToPlay = URL (juceString (sound.get()));
  1181. auto colourString = juceString (color.get()).substring (1);
  1182. const uint8 r = (uint8) colourString.substring (0, 2).getIntValue();
  1183. const uint8 g = (uint8) colourString.substring (2, 4).getIntValue();
  1184. const uint8 b = (uint8) colourString.substring (4, 6).getIntValue();
  1185. n.accentColour = Colour (r, g, b);
  1186. // Note: ignoring icon, because Firebase passes it as a string.
  1187. propertiesDynamicObject->setProperty ("clickAction", juceString (clickAction.get()));
  1188. propertiesDynamicObject->setProperty ("bodyLocalizationKey", juceString (bodyLocalizationKey.get()));
  1189. propertiesDynamicObject->setProperty ("titleLocalizationKey", juceString (titleLocalizationKey.get()));
  1190. propertiesDynamicObject->setProperty ("bodyLocalizationArgs", jobjectArrayToStringArray (bodyLocalizationArgs));
  1191. propertiesDynamicObject->setProperty ("titleLocalizationArgs", jobjectArrayToStringArray (titleLocalizationArgs));
  1192. propertiesDynamicObject->setProperty ("link", link.get() != 0 ? juceString ((jstring) env->CallObjectMethod (link, Uri.toString)) : String());
  1193. }
  1194. n.properties = var (propertiesDynamicObject);
  1195. return n;
  1196. }
  1197. static StringArray jobjectArrayToStringArray (const LocalRef<jobjectArray>& array)
  1198. {
  1199. if (array == 0)
  1200. return {};
  1201. auto* env = getEnv();
  1202. const int size = env->GetArrayLength (array.get());
  1203. StringArray stringArray;
  1204. for (int i = 0; i < size; ++i)
  1205. stringArray.add (juceString ((jstring) env->GetObjectArrayElement (array.get(), (jsize) i)));
  1206. return stringArray;
  1207. }
  1208. #endif
  1209. void setupChannels (const Array<ChannelGroup>& groups, const Array<Channel>& channels)
  1210. {
  1211. #if __ANDROID_API__ >= 26
  1212. auto* env = getEnv();
  1213. if (env->CallStaticIntMethod (JuceAppActivity, JuceAppActivity.getAndroidSDKVersion) < 26)
  1214. return;
  1215. auto notificationManager = getNotificationManager();
  1216. jassert (notificationManager.get() != 0);
  1217. if (notificationManager.get() == 0)
  1218. return;
  1219. for (const auto& g : groups)
  1220. {
  1221. // Channel group identifier and name have to be set.
  1222. jassert (g.identifier.isNotEmpty() && g.name.isNotEmpty());
  1223. if (g.identifier.isNotEmpty() && g.name.isNotEmpty())
  1224. {
  1225. auto group = LocalRef<jobject> (env->NewObject (NotificationChannelGroup, NotificationChannelGroup.constructor,
  1226. javaString (g.identifier).get(), javaString (g.name).get()));
  1227. env->CallVoidMethod (notificationManager, NotificationManagerApi26.createNotificationChannelGroup, group.get());
  1228. }
  1229. }
  1230. for (const auto& c : channels)
  1231. {
  1232. // Channel identifier, name and group have to be set.
  1233. jassert (c.identifier.isNotEmpty() && c.name.isNotEmpty() && c.groupId.isNotEmpty());
  1234. if (c.identifier.isEmpty() || c.name.isEmpty() || c.groupId.isEmpty())
  1235. continue;
  1236. auto channel = LocalRef<jobject> (env->NewObject (NotificationChannel, NotificationChannel.constructor,
  1237. javaString (c.identifier).get(), javaString (c.name).get(), c.importance));
  1238. env->CallVoidMethod (channel, NotificationChannel.enableLights, c.enableLights);
  1239. env->CallVoidMethod (channel, NotificationChannel.enableVibration, c.enableVibration);
  1240. env->CallVoidMethod (channel, NotificationChannel.setBypassDnd, c.bypassDoNotDisturb);
  1241. env->CallVoidMethod (channel, NotificationChannel.setDescription, javaString (c.description).get());
  1242. env->CallVoidMethod (channel, NotificationChannel.setGroup, javaString (c.groupId).get());
  1243. env->CallVoidMethod (channel, NotificationChannel.setImportance, c.importance);
  1244. env->CallVoidMethod (channel, NotificationChannel.setLightColor, c.ledColour.getARGB());
  1245. env->CallVoidMethod (channel, NotificationChannel.setLockscreenVisibility, c.lockScreenAppearance);
  1246. env->CallVoidMethod (channel, NotificationChannel.setShowBadge, c.canShowBadge);
  1247. const int size = c.vibrationPattern.size();
  1248. if (size > 0)
  1249. {
  1250. auto array = LocalRef<jlongArray> (env->NewLongArray (size));
  1251. jlong* elements = env->GetLongArrayElements (array, 0);
  1252. for (int i = 0; i < size; ++i)
  1253. elements[i] = (jlong) c.vibrationPattern[i];
  1254. env->SetLongArrayRegion (array, 0, size, elements);
  1255. env->CallVoidMethod (channel, NotificationChannel.setVibrationPattern, array.get());
  1256. env->CallVoidMethod (channel, NotificationChannel.enableVibration, c.enableVibration);
  1257. }
  1258. auto audioAttributesBuilder = LocalRef<jobject> (env->NewObject (AudioAttributesBuilder, AudioAttributesBuilder.constructor));
  1259. const int contentTypeSonification = 4;
  1260. const int usageNotification = 5;
  1261. env->CallObjectMethod (audioAttributesBuilder, AudioAttributesBuilder.setContentType, contentTypeSonification);
  1262. env->CallObjectMethod (audioAttributesBuilder, AudioAttributesBuilder.setUsage, usageNotification);
  1263. auto audioAttributes = LocalRef<jobject> (env->CallObjectMethod (audioAttributesBuilder, AudioAttributesBuilder.build));
  1264. env->CallVoidMethod (channel, NotificationChannel.setSound, juceUrlToAndroidUri (c.soundToPlay).get(), audioAttributes.get());
  1265. env->CallVoidMethod (notificationManager, NotificationManagerApi26.createNotificationChannel, channel.get());
  1266. }
  1267. #else
  1268. ignoreUnused (groups, channels);
  1269. #endif
  1270. }
  1271. void getPendingLocalNotifications() const {}
  1272. void removePendingLocalNotification (const String&) {}
  1273. void removeAllPendingLocalNotifications() {}
  1274. static bool intentActionContainsAnyOf (jobject intent, const StringArray& strings, bool includePackageName)
  1275. {
  1276. auto* env = getEnv();
  1277. String packageName = includePackageName ? juceString ((jstring) env->CallObjectMethod (android.activity,
  1278. JuceAppActivity.getPackageName))
  1279. : String{};
  1280. String intentAction = juceString ((jstring) env->CallObjectMethod (intent, Intent.getAction));
  1281. for (const auto& string : strings)
  1282. if (intentAction.contains (packageName + string))
  1283. return true;
  1284. return false;
  1285. }
  1286. static bool isDeleteNotificationIntent (jobject intent)
  1287. {
  1288. return intentActionContainsAnyOf (intent, StringArray (".JUCE_NOTIFICATION_DELETED"), true);
  1289. }
  1290. static bool isLocalNotificationIntent (jobject intent)
  1291. {
  1292. return intentActionContainsAnyOf (intent, { ".JUCE_NOTIFICATION.",
  1293. ".JUCE_NOTIFICATION_BUTTON_ACTION.",
  1294. ".JUCE_NOTIFICATION_TEXT_INPUT_ACTION." },
  1295. true);
  1296. }
  1297. static bool isRemoteNotificationIntent (jobject intent)
  1298. {
  1299. auto* env = getEnv();
  1300. auto categories = LocalRef<jobject> (env->CallObjectMethod (intent, Intent.getCategories));
  1301. int categoriesNum = categories != 0
  1302. ? env->CallIntMethod (categories, Set.size)
  1303. : 0;
  1304. if (categoriesNum == 0)
  1305. return false;
  1306. if (! env->CallBooleanMethod (categories, Set.contains, javaString ("android.intent.category.LAUNCHER").get()))
  1307. return false;
  1308. if (! intentActionContainsAnyOf (intent, StringArray ("android.intent.action.MAIN"), false))
  1309. return false;
  1310. auto extras = LocalRef<jobject> (env->CallObjectMethod (intent, Intent.getExtras));
  1311. if (extras == 0)
  1312. return false;
  1313. return env->CallBooleanMethod (extras, Bundle.containsKey, javaString ("google.sent_time").get())
  1314. && env->CallBooleanMethod (extras, Bundle.containsKey, javaString ("google.message_id").get());
  1315. }
  1316. PushNotifications& owner;
  1317. };
  1318. bool juce_handleNotificationIntent (void* intent)
  1319. {
  1320. auto* instance = PushNotifications::getInstanceWithoutCreating();
  1321. if (PushNotifications::Pimpl::isDeleteNotificationIntent ((jobject) intent))
  1322. {
  1323. if (instance)
  1324. instance->pimpl->notifyListenersAboutLocalNotificationDeleted (LocalRef<jobject> ((jobject) intent));
  1325. return true;
  1326. }
  1327. else if (PushNotifications::Pimpl::isLocalNotificationIntent ((jobject) intent))
  1328. {
  1329. if (instance)
  1330. instance->pimpl->notifyListenersAboutLocalNotification (LocalRef<jobject> ((jobject) intent));
  1331. return true;
  1332. }
  1333. #if defined(JUCE_FIREBASE_MESSAGING_SERVICE_CLASSNAME)
  1334. else if (PushNotifications::Pimpl::isRemoteNotificationIntent ((jobject) intent))
  1335. {
  1336. if (instance)
  1337. instance->pimpl->notifyListenersAboutRemoteNotificationFromSystemTray (LocalRef<jobject> ((jobject) intent));
  1338. return true;
  1339. }
  1340. #endif
  1341. return false;
  1342. }
  1343. void juce_firebaseDeviceNotificationsTokenRefreshed (void* token)
  1344. {
  1345. if (auto* instance = PushNotifications::getInstanceWithoutCreating())
  1346. instance->pimpl->notifyListenersTokenRefreshed (juceString (static_cast<jstring> (token)));
  1347. }
  1348. void juce_firebaseRemoteNotificationReceived (void* remoteMessage)
  1349. {
  1350. if (auto* instance = PushNotifications::getInstanceWithoutCreating())
  1351. instance->pimpl->notifyListenersAboutRemoteNotificationFromService (LocalRef<jobject> (static_cast<jobject> (remoteMessage)));
  1352. }
  1353. void juce_firebaseRemoteMessagesDeleted()
  1354. {
  1355. if (auto* instance = PushNotifications::getInstanceWithoutCreating())
  1356. instance->pimpl->notifyListenersAboutRemoteNotificationsDeleted();
  1357. }
  1358. void juce_firebaseRemoteMessageSent (void* messageId)
  1359. {
  1360. if (auto* instance = PushNotifications::getInstanceWithoutCreating())
  1361. instance->pimpl->notifyListenersAboutUpstreamMessageSent (LocalRef<jstring> (static_cast<jstring> (messageId)));
  1362. }
  1363. void juce_firebaseRemoteMessageSendError (void* messageId, void* error)
  1364. {
  1365. if (auto* instance = PushNotifications::getInstanceWithoutCreating())
  1366. instance->pimpl->notifyListenersAboutUpstreamMessageSendingError (LocalRef<jstring> (static_cast<jstring> (messageId)),
  1367. LocalRef<jstring> (static_cast<jstring> (error)));
  1368. }
  1369. } // namespace juce