tauri-plugin-notifications 0.4.5

A Tauri v2 plugin for sending notifications on desktop and mobile platforms with support for system notifications and push delivery via FCM and APNs.
Documentation
package app.tauri.notification

import android.content.Context
import android.content.SharedPreferences
import app.tauri.Logger
import com.fasterxml.jackson.databind.ObjectMapper

private const val STORAGE_TAG = "NotificationStorage"
// Key for private preferences
private const val NOTIFICATION_STORE_ID = "NOTIFICATION_STORE"
// Key used to save action types
private const val ACTION_TYPES_ID = "ACTION_TYPE_STORE"

class NotificationStorage(private val context: Context, private val jsonMapper: ObjectMapper) {
  fun appendNotifications(localNotifications: List<Notification>) {
    Logger.debug(Logger.tags(STORAGE_TAG), "Appending ${localNotifications.size} notifications to storage")
    val storage = getStorage(NOTIFICATION_STORE_ID)
    val editor = storage.edit()
    var savedCount = 0
    for (request in localNotifications) {
      if (request.schedule != null) {
        val key: String = request.id.toString()
        val jsonValue = request.sourceJson
        Logger.debug(Logger.tags(STORAGE_TAG), "Saving notification $key, sourceJson is null: ${request.sourceJson == null}, value: ${jsonValue?.take(100)}")
        editor.putString(key, jsonValue)
        savedCount++
      } else {
        Logger.debug(Logger.tags(STORAGE_TAG), "Skipping notification ${request.id} - no schedule")
      }
    }
    editor.apply()
    Logger.debug(Logger.tags(STORAGE_TAG), "Actually saved $savedCount scheduled notifications")
  }

  fun getSavedNotificationIds(): List<String> {
    val storage = getStorage(NOTIFICATION_STORE_ID)
    val all = storage.all
    val ids = if (all != null) ArrayList(all.keys) else ArrayList()
    Logger.debug(Logger.tags(STORAGE_TAG), "Retrieved ${ids.size} saved notification IDs")
    return ids
  }

  fun getSavedNotifications(): List<Notification> {
    val storage = getStorage(NOTIFICATION_STORE_ID)
    val all = storage.all
    Logger.debug(Logger.tags(STORAGE_TAG), "Storage keys: ${all?.keys}")
    val notifications = all?.keys?.mapNotNull { key ->
      val value = all[key]
      Logger.debug(Logger.tags(STORAGE_TAG), "Key $key, value type: ${value?.javaClass?.name}, value: ${value.toString().take(100)}")
      val json = value as? String
      parseNotification(json)
    } ?: emptyList()
    Logger.debug(Logger.tags(STORAGE_TAG), "Retrieved ${notifications.size} saved notifications")
    return notifications
  }

  fun getSavedNotification(key: String): Notification? {
    val storage = getStorage(NOTIFICATION_STORE_ID)
    val notificationString = try {
      storage.getString(key, null)
    } catch (e: ClassCastException) {
      Logger.error(Logger.tags(STORAGE_TAG), "Failed to get notification string for key $key: ${e.message}", e)
      return null
    }
    return parseNotification(notificationString)
  }

  private fun parseNotification(json: String?): Notification? {
    if (json == null) return null
    return try {
      jsonMapper.readValue(json, Notification::class.java)
    } catch (e: Exception) {
      Logger.error(Logger.tags(STORAGE_TAG), "Failed to parse notification: ${e.message}", e)
      null
    }
  }

  fun deleteNotification(id: String?) {
    Logger.debug(Logger.tags(STORAGE_TAG), "Deleting notification with id: $id")
    val editor = getStorage(NOTIFICATION_STORE_ID).edit()
    editor.remove(id)
    editor.apply()
  }

  private fun getStorage(key: String): SharedPreferences {
    return context.getSharedPreferences(key, Context.MODE_PRIVATE)
  }

  fun writeActionGroup(actions: List<ActionType>) {
    for (type in actions) {
      val editor = getStorage(ACTION_TYPES_ID + type.id).edit()
      editor.clear()
      editor.putInt("count", type.actions.size)
      for ((index, action) in type.actions.withIndex()) {
        editor.putString("id$index", action.id)
        editor.putString("title$index", action.title)
        editor.putBoolean("input$index", action.input ?: false)
      }
      editor.apply()
      Logger.debug(Logger.tags(STORAGE_TAG), "Saved action group ${type.id} with ${type.actions.size} actions")
    }
  }

  fun getActionGroup(forId: String): Array<NotificationAction?> {
    val storage = getStorage(ACTION_TYPES_ID + forId)
    val count = storage.getInt("count", 0)
    Logger.debug(Logger.tags(STORAGE_TAG), "Getting action group $forId, count: $count")
    val actions: Array<NotificationAction?> = arrayOfNulls(count)
    for (i in 0 until count) {
      val id = storage.getString("id$i", "")
      val title = storage.getString("title$i", "")
      val input = storage.getBoolean("input$i", false)
      Logger.debug(Logger.tags(STORAGE_TAG), "Action $i: id=$id, title=$title, input=$input")

      val action = NotificationAction()
      action.id = id ?: ""
      action.title = title
      action.input = input
      actions[i] = action
    }
    return actions
  }
}