Add the ability to create action types and actions with only Rust so you

can register them; fix Android incorrectly storing actions in an action
group
pull/2805/head
Michael Kadziela 3 weeks ago
parent 5779099688
commit d0b5d04892

@ -7,8 +7,8 @@ package app.tauri.notification
import android.content.Context import android.content.Context
import android.content.SharedPreferences import android.content.SharedPreferences
import com.fasterxml.jackson.databind.ObjectMapper import com.fasterxml.jackson.databind.ObjectMapper
import org.json.JSONException
import java.lang.Exception import java.lang.Exception
import org.json.JSONException
// Key for private preferences // Key for private preferences
private const val NOTIFICATION_STORE_ID = "NOTIFICATION_STORE" private const val NOTIFICATION_STORE_ID = "NOTIFICATION_STORE"
@ -16,98 +16,100 @@ private const val NOTIFICATION_STORE_ID = "NOTIFICATION_STORE"
private const val ACTION_TYPES_ID = "ACTION_TYPE_STORE" private const val ACTION_TYPES_ID = "ACTION_TYPE_STORE"
class NotificationStorage(private val context: Context, private val jsonMapper: ObjectMapper) { class NotificationStorage(private val context: Context, private val jsonMapper: ObjectMapper) {
fun appendNotifications(localNotifications: List<Notification>) { fun appendNotifications(localNotifications: List<Notification>) {
val storage = getStorage(NOTIFICATION_STORE_ID) val storage = getStorage(NOTIFICATION_STORE_ID)
val editor = storage.edit() val editor = storage.edit()
for (request in localNotifications) { for (request in localNotifications) {
if (request.schedule != null) { if (request.schedule != null) {
val key: String = request.id.toString() val key: String = request.id.toString()
editor.putString(key, request.sourceJson.toString()) editor.putString(key, request.sourceJson.toString())
} }
}
editor.apply()
} }
editor.apply()
}
fun getSavedNotificationIds(): List<String> { fun getSavedNotificationIds(): List<String> {
val storage = getStorage(NOTIFICATION_STORE_ID) val storage = getStorage(NOTIFICATION_STORE_ID)
val all = storage.all val all = storage.all
return if (all != null) { return if (all != null) {
ArrayList(all.keys) ArrayList(all.keys)
} else ArrayList() } else ArrayList()
} }
fun getSavedNotifications(): List<Notification> { fun getSavedNotifications(): List<Notification> {
val storage = getStorage(NOTIFICATION_STORE_ID) val storage = getStorage(NOTIFICATION_STORE_ID)
val all = storage.all val all = storage.all
if (all != null) { if (all != null) {
val notifications = ArrayList<Notification>() val notifications = ArrayList<Notification>()
for (key in all.keys) { for (key in all.keys) {
val notificationString = all[key] as String? val notificationString = all[key] as String?
try { try {
val notification = jsonMapper.readValue(notificationString, Notification::class.java) val notification =
notifications.add(notification) jsonMapper.readValue(notificationString, Notification::class.java)
} catch (_: Exception) { } notifications.add(notification)
} } catch (_: Exception) {}
return notifications }
return notifications
}
return ArrayList()
} }
return ArrayList()
}
fun getSavedNotification(key: String): Notification? { fun getSavedNotification(key: String): Notification? {
val storage = getStorage(NOTIFICATION_STORE_ID) val storage = getStorage(NOTIFICATION_STORE_ID)
val notificationString = try { val notificationString =
storage.getString(key, null) try {
} catch (ex: ClassCastException) { storage.getString(key, null)
return null } catch (ex: ClassCastException) {
} ?: return null return null
} ?: return null
return try { return try {
jsonMapper.readValue(notificationString, Notification::class.java) jsonMapper.readValue(notificationString, Notification::class.java)
} catch (ex: JSONException) { } catch (ex: JSONException) {
null null
}
} }
}
fun deleteNotification(id: String?) { fun deleteNotification(id: String?) {
val editor = getStorage(NOTIFICATION_STORE_ID).edit() val editor = getStorage(NOTIFICATION_STORE_ID).edit()
editor.remove(id) editor.remove(id)
editor.apply() editor.apply()
} }
private fun getStorage(key: String): SharedPreferences { private fun getStorage(key: String): SharedPreferences {
return context.getSharedPreferences(key, Context.MODE_PRIVATE) return context.getSharedPreferences(key, Context.MODE_PRIVATE)
} }
fun writeActionGroup(actions: List<ActionType>) { fun writeActionGroup(actions: List<ActionType>) {
for (type in actions) { for (type in actions) {
val i = type.id val editor = getStorage(ACTION_TYPES_ID + type.id).edit()
val editor = getStorage(ACTION_TYPES_ID + type.id).edit() editor.clear()
editor.clear() editor.putInt("count", type.actions.size)
editor.putInt("count", type.actions.size) for (i in 0 until type.actions.size) {
for (action in type.actions) { val action = type.actions[i]
editor.putString("id$i", action.id) editor.putString("id$i", action.id)
editor.putString("title$i", action.title) editor.putString("title$i", action.title)
editor.putBoolean("input$i", action.input ?: false) editor.putBoolean("input$i", action.input ?: false)
} }
editor.apply() editor.apply()
}
} }
}
fun getActionGroup(forId: String): Array<NotificationAction?> { fun getActionGroup(forId: String): Array<NotificationAction?> {
val storage = getStorage(ACTION_TYPES_ID + forId) val storage = getStorage(ACTION_TYPES_ID + forId)
val count = storage.getInt("count", 0) val count = storage.getInt("count", 0)
val actions: Array<NotificationAction?> = arrayOfNulls(count) val actions: Array<NotificationAction?> = arrayOfNulls(count)
for (i in 0 until count) { for (i in 0 until count) {
val id = storage.getString("id$i", "") val id = storage.getString("id$i", "")
val title = storage.getString("title$i", "") val title = storage.getString("title$i", "")
val input = storage.getBoolean("input$i", false) val input = storage.getBoolean("input$i", false)
val action = NotificationAction() val action = NotificationAction()
action.id = id ?: "" action.id = id ?: ""
action.title = title action.title = title
action.input = input action.input = input
actions[i] = action actions[i] = action
}
return actions
} }
return actions }
}
}

@ -320,6 +320,92 @@ pub struct ActionType {
hidden_previews_show_subtitle: bool, hidden_previews_show_subtitle: bool,
} }
#[derive(Debug)]
pub struct ActionTypeBuilder(ActionType);
impl ActionType {
pub fn builder(id: impl Into<String>) -> ActionTypeBuilder {
ActionTypeBuilder(Self {
id: id.into(),
actions: Vec::new(),
hidden_previews_body_placeholder: None,
custom_dismiss_action: false,
allow_in_car_play: false,
hidden_previews_show_title: false,
hidden_previews_show_subtitle: false,
})
}
pub fn id(&self) -> &str {
&self.id
}
pub fn actions(&self) -> &[Action] {
&self.actions
}
pub fn hidden_previews_body_placeholder(&self) -> Option<&str> {
self.hidden_previews_body_placeholder.as_deref()
}
pub fn custom_dismiss_action(&self) -> bool {
self.custom_dismiss_action
}
pub fn allow_in_car_play(&self) -> bool {
self.allow_in_car_play
}
pub fn hidden_previews_show_title(&self) -> bool {
self.hidden_previews_show_title
}
pub fn hidden_previews_show_subtitle(&self) -> bool {
self.hidden_previews_show_subtitle
}
}
impl ActionTypeBuilder {
pub fn actions(mut self, actions: Vec<Action>) -> Self {
self.0.actions = actions;
self
}
pub fn hidden_previews_body_placeholder(
mut self,
hidden_previews_body_placeholder: impl Into<String>,
) -> Self {
self.0
.hidden_previews_body_placeholder
.replace(hidden_previews_body_placeholder.into());
self
}
pub fn custom_dismiss_action(mut self, custom_dismiss_action: bool) -> Self {
self.0.custom_dismiss_action = custom_dismiss_action;
self
}
pub fn allow_in_car_play(mut self, allow_in_car_play: bool) -> Self {
self.0.allow_in_car_play = allow_in_car_play;
self
}
pub fn hidden_previews_show_title(mut self, hidden_previews_show_title: bool) -> Self {
self.0.hidden_previews_show_title = hidden_previews_show_title;
self
}
pub fn hidden_previews_show_subtitle(mut self, hidden_previews_show_subtitle: bool) -> Self {
self.0.hidden_previews_show_subtitle = hidden_previews_show_subtitle;
self
}
pub fn build(self) -> ActionType {
self.0
}
}
#[cfg(mobile)] #[cfg(mobile)]
#[derive(Debug, Serialize)] #[derive(Debug, Serialize)]
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
@ -334,6 +420,92 @@ pub struct Action {
input_placeholder: Option<String>, input_placeholder: Option<String>,
} }
#[derive(Debug)]
pub struct ActionBuilder(Action);
impl Action {
pub fn builder(id: impl Into<String>, title: impl Into<String>) -> ActionBuilder {
ActionBuilder(Self {
id: id.into(),
title: title.into(),
requires_authentication: false,
foreground: false,
destructive: false,
input: false,
input_button_title: None,
input_placeholder: None,
})
}
pub fn id(&self) -> &str {
&self.id
}
pub fn title(&self) -> &str {
&self.title
}
pub fn requires_authentication(&self) -> bool {
self.requires_authentication
}
pub fn foreground(&self) -> bool {
self.foreground
}
pub fn destructive(&self) -> bool {
self.destructive
}
pub fn input(&self) -> bool {
self.input
}
pub fn input_button_title(&self) -> Option<&str> {
self.input_button_title.as_deref()
}
pub fn input_placeholder(&self) -> Option<&str> {
self.input_placeholder.as_deref()
}
}
impl ActionBuilder {
pub fn requires_authentication(mut self, requires_authentication: bool) -> Self {
self.0.requires_authentication = requires_authentication;
self
}
pub fn foreground(mut self, foreground: bool) -> Self {
self.0.foreground = foreground;
self
}
pub fn destructive(mut self, destructive: bool) -> Self {
self.0.destructive = destructive;
self
}
pub fn input(mut self, input: bool) -> Self {
self.0.input = input;
self
}
pub fn input_button_title(mut self, input_button_title: impl Into<String>) -> Self {
self.0.input_button_title.replace(input_button_title.into());
self
}
pub fn input_placeholder(mut self, input_placeholder: impl Into<String>) -> Self {
self.0.input_placeholder.replace(input_placeholder.into());
self
}
pub fn build(self) -> Action {
self.0
}
}
#[cfg(target_os = "android")] #[cfg(target_os = "android")]
pub use android::*; pub use android::*;

Loading…
Cancel
Save