From cd6bf51e6e8f300c9bfbc3fa2ed742d75155a313 Mon Sep 17 00:00:00 2001 From: Lucas Nogueira Date: Mon, 1 May 2023 15:32:27 -0300 Subject: [PATCH] trigger events on android --- .../android/src/main/java/Notification.kt | 4 +- .../src/main/java/NotificationPlugin.kt | 14 ++++++- .../src/main/java/NotificationSchedule.kt | 3 +- .../src/main/java/NotificationStorage.kt | 25 +----------- .../src/main/java/TauriNotificationManager.kt | 26 ++++++------ plugins/notification/guest-js/index.ts | 40 ++++++++++++++++++- 6 files changed, 68 insertions(+), 44 deletions(-) diff --git a/plugins/notification/android/src/main/java/Notification.kt b/plugins/notification/android/src/main/java/Notification.kt index f0f78972..3839807b 100644 --- a/plugins/notification/android/src/main/java/Notification.kt +++ b/plugins/notification/android/src/main/java/Notification.kt @@ -29,7 +29,7 @@ class Notification { var attachments: List? = null var schedule: NotificationSchedule? = null var channelId: String? = null - var source: String? = null + var source: JSObject? = null var visibility: Int? = null var number: Int? = null @@ -104,7 +104,7 @@ class Notification { fun fromJSObject(jsonObject: JSObject): Notification { val notification = Notification() - notification.source = jsonObject.toString() + notification.source = jsonObject notification.id = jsonObject.getInteger("id") ?: throw Exception("Missing notification identifier") notification.body = jsonObject.getString("body", null) notification.largeBody = jsonObject.getString("largeBody", null) diff --git a/plugins/notification/android/src/main/java/NotificationPlugin.kt b/plugins/notification/android/src/main/java/NotificationPlugin.kt index 8beb183b..f87bcf17 100644 --- a/plugins/notification/android/src/main/java/NotificationPlugin.kt +++ b/plugins/notification/android/src/main/java/NotificationPlugin.kt @@ -33,8 +33,18 @@ class NotificationPlugin(private val activity: Activity): Plugin(activity) { private lateinit var notificationManager: NotificationManager private lateinit var notificationStorage: NotificationStorage private var channelManager = ChannelManager(activity) - + + companion object { + var instance: NotificationPlugin? = null + + fun triggerNotification(notification: JSObject) { + instance?.trigger("notification", notification) + } + } + override fun load(webView: WebView) { + instance = this + super.load(webView) this.webView = webView notificationStorage = NotificationStorage(activity) @@ -59,7 +69,7 @@ class NotificationPlugin(private val activity: Activity): Plugin(activity) { } val dataJson = manager.handleNotificationActionPerformed(intent, notificationStorage) if (dataJson != null) { - // TODO trigger("actionPerformed", dataJson, true) + trigger("actionPerformed", dataJson) } } diff --git a/plugins/notification/android/src/main/java/NotificationSchedule.kt b/plugins/notification/android/src/main/java/NotificationSchedule.kt index c2f0952b..89edbc9d 100644 --- a/plugins/notification/android/src/main/java/NotificationSchedule.kt +++ b/plugins/notification/android/src/main/java/NotificationSchedule.kt @@ -1,8 +1,8 @@ package app.tauri.notification +import android.annotation.SuppressLint import android.text.format.DateUtils import app.tauri.plugin.JSObject -import java.text.ParseException import java.text.SimpleDateFormat import java.util.Calendar import java.util.Date @@ -36,6 +36,7 @@ sealed class ScheduleKind { class Every(val interval: NotificationInterval, val count: Int): ScheduleKind() } +@SuppressLint("SimpleDateFormat") class NotificationSchedule(val scheduleObj: JSObject) { val kind: ScheduleKind // Schedule this notification to fire even if app is idled (Doze) diff --git a/plugins/notification/android/src/main/java/NotificationStorage.kt b/plugins/notification/android/src/main/java/NotificationStorage.kt index 4512ee29..bfddfcc2 100644 --- a/plugins/notification/android/src/main/java/NotificationStorage.kt +++ b/plugins/notification/android/src/main/java/NotificationStorage.kt @@ -10,19 +10,15 @@ import java.text.ParseException private const val NOTIFICATION_STORE_ID = "NOTIFICATION_STORE" // Key used to save action types private const val ACTION_TYPES_ID = "ACTION_TYPE_STORE" -private const val ID_KEY = "notificationIds" class NotificationStorage(private val context: Context) { - /** - * Persist the id of currently scheduled notification - */ fun appendNotifications(localNotifications: List) { val storage = getStorage(NOTIFICATION_STORE_ID) val editor = storage.edit() for (request in localNotifications) { if (request.isScheduled) { val key: String = request.id.toString() - editor.putString(key, request.source) + editor.putString(key, request.source.toString()) } } editor.apply() @@ -96,28 +92,16 @@ class NotificationStorage(private val context: Context) { return notification } - /** - * Remove the stored notifications - */ fun deleteNotification(id: String?) { val editor = getStorage(NOTIFICATION_STORE_ID).edit() editor.remove(id) editor.apply() } - /** - * Shared private preferences for the application. - */ private fun getStorage(key: String): SharedPreferences { return context.getSharedPreferences(key, Context.MODE_PRIVATE) } - /** - * Writes new action types (actions that being displayed in notification) to storage. - * Write will override previous data. - * - * @param typesMap - map with groupId and actionArray assigned to group - */ fun writeActionGroup(typesMap: Map>) { for ((id, notificationActions) in typesMap) { val editor = getStorage(ACTION_TYPES_ID + id).edit() @@ -132,15 +116,10 @@ class NotificationStorage(private val context: Context) { } } - /** - * Retrieve array of notification actions per ActionTypeId - * - * @param forId - id of the group - */ fun getActionGroup(forId: String): Array { val storage = getStorage(ACTION_TYPES_ID + forId) val count = storage.getInt("count", 0) - val actions: Array = arrayOfNulls(count) + val actions: Array = arrayOfNulls(count) for (i in 0 until count) { val id = storage.getString("id$i", "") val title = storage.getString("title$i", "") diff --git a/plugins/notification/android/src/main/java/TauriNotificationManager.kt b/plugins/notification/android/src/main/java/TauriNotificationManager.kt index 072e8dfc..01d48da1 100644 --- a/plugins/notification/android/src/main/java/TauriNotificationManager.kt +++ b/plugins/notification/android/src/main/java/TauriNotificationManager.kt @@ -44,9 +44,6 @@ class TauriNotificationManager( private var defaultSoundID: Int = AssetUtils.RESOURCE_ID_ZERO_VALUE private var defaultSmallIconID: Int = AssetUtils.RESOURCE_ID_ZERO_VALUE - /** - * Method extecuted when notification is launched by user from the notification bar. - */ fun handleNotificationActionPerformed( data: Intent, notificationStorage: NotificationStorage @@ -89,7 +86,7 @@ class TauriNotificationManager( fun createNotificationChannel() { // Create the NotificationChannel, but only on API 26+ because // the NotificationChannel class is new and not in the support library - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + if (SDK_INT >= Build.VERSION_CODES.O) { val name: CharSequence = "Default" val description = "Default" val importance = NotificationManager.IMPORTANCE_DEFAULT @@ -212,16 +209,15 @@ class TauriNotificationManager( if (notification.isScheduled) { triggerScheduledNotification(buildNotification, notification) } else { + notificationManager.notify(notification.id, buildNotification) try { - // TODO notify - // val notificationJson = JSObject(notification.source ?: "") + NotificationPlugin.triggerNotification(notification.source ?: JSObject()) } catch (_: JSONException) { } - notificationManager.notify(notification.id, buildNotification) } } - // Create intents for open/dissmis actions + // Create intents for open/dismiss actions private fun createActionIntents( notification: Notification, mBuilder: NotificationCompat.Builder @@ -229,7 +225,7 @@ class TauriNotificationManager( // Open intent val intent = buildIntent(notification, DEFAULT_PRESS_ACTION) var flags = PendingIntent.FLAG_CANCEL_CURRENT - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { + if (SDK_INT >= Build.VERSION_CODES.S) { flags = flags or PendingIntent.FLAG_MUTABLE } val pendingIntent = PendingIntent.getActivity(context, notification.id, intent, flags) @@ -297,7 +293,7 @@ class TauriNotificationManager( intent.flags = Intent.FLAG_ACTIVITY_SINGLE_TOP or Intent.FLAG_ACTIVITY_CLEAR_TOP intent.putExtra(NOTIFICATION_INTENT_KEY, notification.id) intent.putExtra(ACTION_INTENT_KEY, action) - intent.putExtra(NOTIFICATION_OBJ_INTENT_KEY, notification.source) + intent.putExtra(NOTIFICATION_OBJ_INTENT_KEY, notification.source.toString()) val schedule = notification.schedule intent.putExtra(NOTIFICATION_IS_REMOVABLE_KEY, schedule == null || schedule.isRemovable()) return intent @@ -486,8 +482,10 @@ class TimedNotificationPublisher : BroadcastReceiver() { Logger.error(Logger.tags("Notification"), "No valid id supplied", null) } val storage = NotificationStorage(context) - // TODO notify - // val notificationJson = storage.getSavedNotificationAsJSObject(id.toString()) + val notificationJson = storage.getSavedNotificationAsJSObject(id.toString()) + if (notificationJson != null) { + NotificationPlugin.triggerNotification(notificationJson) + } notificationManager.notify(id, notification) if (!rescheduleNotificationIfNeeded(context, intent, id)) { storage.deleteNotification(id.toString()) @@ -508,11 +506,11 @@ class TimedNotificationPublisher : BroadcastReceiver() { val trigger = date.nextTrigger(Date()) val clone = intent.clone() as Intent var flags = PendingIntent.FLAG_CANCEL_CURRENT - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { + if (SDK_INT >= Build.VERSION_CODES.S) { flags = flags or PendingIntent.FLAG_MUTABLE } val pendingIntent = PendingIntent.getBroadcast(context, id, clone, flags) - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S && !alarmManager.canScheduleExactAlarms()) { + if (SDK_INT >= Build.VERSION_CODES.S && !alarmManager.canScheduleExactAlarms()) { alarmManager[AlarmManager.RTC, trigger] = pendingIntent } else { alarmManager.setExact(AlarmManager.RTC, trigger, pendingIntent) diff --git a/plugins/notification/guest-js/index.ts b/plugins/notification/guest-js/index.ts index fe83fd59..64c6f5ad 100644 --- a/plugins/notification/guest-js/index.ts +++ b/plugins/notification/guest-js/index.ts @@ -24,7 +24,7 @@ * @module */ -import { invoke } from '@tauri-apps/api/tauri' +import { invoke, transformCallback } from '@tauri-apps/api/tauri' /** * Options to send a notification. @@ -548,6 +548,39 @@ async function channels(): Promise { return invoke('plugin:notification|getActive') } +class EventChannel { + id: number + unregisterFn: (channel: EventChannel) => Promise + + constructor(id: number, unregisterFn: (channel: EventChannel) => Promise) { + this.id = id + this.unregisterFn = unregisterFn + } + + toJSON(): string { + return `__CHANNEL__:${this.id}` + } + + async unregister(): Promise { + return this.unregisterFn(this) + } +} + +// TODO: use addPluginListener API on @tauri-apps/api/tauri 2.0.0-alpha.4 +async function onNotificationReceived(cb: (notification: Options) => void): Promise { + const channelId = transformCallback(cb) + const handler = new EventChannel(channelId, (channel) => invoke('plugin:notification|remove_listener', { event: 'notification', channelId: channel.id })) + return invoke('plugin:notification|register_listener', { event: 'notification', handler }).then(() => handler) +} + +// TODO: use addPluginListener API on @tauri-apps/api/tauri 2.0.0-alpha.4 +async function onAction(cb: (notification: Options) => void): Promise { + const channelId = transformCallback(cb) + const handler = new EventChannel(channelId, (channel) => invoke('plugin:notification|remove_listener', { event: 'actionPerformed', channelId: channel.id })) + return invoke('plugin:notification|register_listener', { event: 'actionPerformed', handler }).then(() => handler) +} + + export type { Attachment, Options, Permission, Action, ActionType, PendingNotification, ActiveNotification, Channel } export { @@ -565,5 +598,8 @@ export { removeAllActive, createChannel, removeChannel, - channels + channels, + + onNotificationReceived, + onAction }