feat(notification): implement Android and iOS APIs (#340)

This commit is contained in:
Lucas Fernandes Nogueira
2023-05-05 05:22:19 -07:00
committed by GitHub
parent 1397172e95
commit be1c775b8d
25 changed files with 3700 additions and 90 deletions
@@ -1,31 +1,263 @@
package app.tauri.notification
import android.Manifest
import android.annotation.SuppressLint
import android.app.Activity
import android.app.NotificationManager
import android.content.Context
import android.content.Intent
import android.os.Build
import android.webkit.WebView
import app.tauri.PermissionState
import app.tauri.annotation.Command
import app.tauri.annotation.Permission
import app.tauri.annotation.PermissionCallback
import app.tauri.annotation.TauriPlugin
import app.tauri.plugin.Invoke
import app.tauri.plugin.JSArray
import app.tauri.plugin.JSObject
import app.tauri.plugin.Plugin
import app.tauri.plugin.Invoke
import org.json.JSONException
import org.json.JSONObject
@TauriPlugin
const val LOCAL_NOTIFICATIONS = "permissionState"
@TauriPlugin(
permissions = [
Permission(strings = [Manifest.permission.POST_NOTIFICATIONS], alias = "permissionState")
]
)
class NotificationPlugin(private val activity: Activity): Plugin(activity) {
@Command
fun requestPermission(invoke: Invoke) {
val ret = JSObject()
ret.put("permissionState", "granted")
invoke.resolve(ret)
private var webView: WebView? = null
private lateinit var manager: TauriNotificationManager
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)
val manager = TauriNotificationManager(
notificationStorage,
activity,
activity,
getConfig()
)
manager.createNotificationChannel()
this.manager = manager
notificationManager = activity.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
}
override fun onNewIntent(intent: Intent) {
super.onNewIntent(intent)
if (Intent.ACTION_MAIN != intent.action) {
return
}
val dataJson = manager.handleNotificationActionPerformed(intent, notificationStorage)
if (dataJson != null) {
trigger("actionPerformed", dataJson)
}
}
@Command
fun show(invoke: Invoke) {
val notification = Notification.fromJSObject(invoke.data)
val id = manager.schedule(notification)
val returnVal = JSObject().put("id", id)
invoke.resolve(returnVal)
}
@Command
fun batch(invoke: Invoke) {
val notificationArray = invoke.getArray("notifications")
if (notificationArray == null) {
invoke.reject("Missing `notifications` argument")
return
}
@Command
fun permissionState(invoke: Invoke) {
val ret = JSObject()
ret.put("permissionState", "granted")
invoke.resolve(ret)
val notifications: MutableList<Notification> =
ArrayList(notificationArray.length())
val notificationsInput: List<JSONObject> = try {
notificationArray.toList()
} catch (e: JSONException) {
invoke.reject("Provided notification format is invalid")
return
}
@Command
fun notify(invoke: Invoke) {
// TODO
invoke.resolve()
for (jsonNotification in notificationsInput) {
val notification = Notification.fromJson(jsonNotification)
notifications.add(notification)
}
val ids = manager.schedule(notifications)
notificationStorage.appendNotifications(notifications)
val result = JSObject()
result.put("notifications", ids)
invoke.resolve(result)
}
@Command
fun cancel(invoke: Invoke) {
val notifications: List<Int> = invoke.getArray("notifications", JSArray()).toList()
if (notifications.isEmpty()) {
invoke.reject("Must provide notifications array as notifications option")
return
}
manager.cancel(notifications)
invoke.resolve()
}
@Command
fun removeActive(invoke: Invoke) {
val notifications = invoke.getArray("notifications")
if (notifications == null) {
notificationManager.cancelAll()
invoke.resolve()
} else {
try {
for (o in notifications.toList<Any>()) {
if (o is JSONObject) {
val notification = JSObject.fromJSONObject((o))
val tag = notification.getString("tag", null)
val id = notification.getInteger("id", 0)
if (tag == null) {
notificationManager.cancel(id)
} else {
notificationManager.cancel(tag, id)
}
} else {
invoke.reject("Unexpected notification type")
return
}
}
} catch (e: JSONException) {
invoke.reject(e.message)
}
invoke.resolve()
}
}
@Command
fun getPending(invoke: Invoke) {
val notifications= notificationStorage.getSavedNotifications()
val result = Notification.buildNotificationPendingList(notifications)
invoke.resolve(result)
}
@Command
fun registerActionTypes(invoke: Invoke) {
val types = invoke.getArray("types", JSArray())
val typesArray = NotificationAction.buildTypes(types)
notificationStorage.writeActionGroup(typesArray)
invoke.resolve()
}
@SuppressLint("ObsoleteSdkInt")
@Command
fun getActive(invoke: Invoke) {
val notifications = JSArray()
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
val activeNotifications = notificationManager.activeNotifications
for (activeNotification in activeNotifications) {
val jsNotification = JSObject()
jsNotification.put("id", activeNotification.id)
jsNotification.put("tag", activeNotification.tag)
val notification = activeNotification.notification
if (notification != null) {
jsNotification.put("title", notification.extras.getCharSequence(android.app.Notification.EXTRA_TITLE))
jsNotification.put("body", notification.extras.getCharSequence(android.app.Notification.EXTRA_TEXT))
jsNotification.put("group", notification.group)
jsNotification.put(
"groupSummary",
0 != notification.flags and android.app.Notification.FLAG_GROUP_SUMMARY
)
val extras = JSObject()
for (key in notification.extras.keySet()) {
extras.put(key!!, notification.extras.getString(key))
}
jsNotification.put("data", extras)
}
notifications.put(jsNotification)
}
}
val result = JSObject()
result.put("notifications", notifications)
invoke.resolve(result)
}
@Command
fun createChannel(invoke: Invoke) {
channelManager.createChannel(invoke)
}
@Command
fun deleteChannel(invoke: Invoke) {
channelManager.deleteChannel(invoke)
}
@Command
fun listChannels(invoke: Invoke) {
channelManager.listChannels(invoke)
}
@Command
override fun checkPermissions(invoke: Invoke) {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU) {
val permissionsResultJSON = JSObject()
permissionsResultJSON.put("permissionState", getPermissionState())
invoke.resolve(permissionsResultJSON)
} else {
super.checkPermissions(invoke)
}
}
@Command
override fun requestPermissions(invoke: Invoke) {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU) {
permissionState(invoke)
} else {
if (getPermissionState(LOCAL_NOTIFICATIONS) !== PermissionState.GRANTED) {
requestPermissionForAlias(LOCAL_NOTIFICATIONS, invoke, "permissionsCallback")
}
}
}
@Command
fun permissionState(invoke: Invoke) {
val permissionsResultJSON = JSObject()
permissionsResultJSON.put("permissionState", getPermissionState())
invoke.resolve(permissionsResultJSON)
}
@PermissionCallback
private fun permissionsCallback(invoke: Invoke) {
val permissionsResultJSON = JSObject()
permissionsResultJSON.put("display", getPermissionState())
invoke.resolve(permissionsResultJSON)
}
private fun getPermissionState(): String {
return if (manager.areNotificationsEnabled()) {
"granted"
} else {
"denied"
}
}
}