Skip to content

Add the ability to create action types/actions in only Rust; correctly write actions in Android #2805

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: v2
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
166 changes: 84 additions & 82 deletions plugins/notification/android/src/main/java/NotificationStorage.kt
Original file line number Diff line number Diff line change
Expand Up @@ -7,107 +7,109 @@ package app.tauri.notification
import android.content.Context
import android.content.SharedPreferences
import com.fasterxml.jackson.databind.ObjectMapper
import org.json.JSONException
import java.lang.Exception
import org.json.JSONException

// 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>) {
val storage = getStorage(NOTIFICATION_STORE_ID)
val editor = storage.edit()
for (request in localNotifications) {
if (request.schedule != null) {
val key: String = request.id.toString()
editor.putString(key, request.sourceJson.toString())
}
fun appendNotifications(localNotifications: List<Notification>) {
val storage = getStorage(NOTIFICATION_STORE_ID)
val editor = storage.edit()
for (request in localNotifications) {
if (request.schedule != null) {
val key: String = request.id.toString()
editor.putString(key, request.sourceJson.toString())
}
}
editor.apply()
}
editor.apply()
}

fun getSavedNotificationIds(): List<String> {
val storage = getStorage(NOTIFICATION_STORE_ID)
val all = storage.all
return if (all != null) {
ArrayList(all.keys)
} else ArrayList()
}
fun getSavedNotificationIds(): List<String> {
val storage = getStorage(NOTIFICATION_STORE_ID)
val all = storage.all
return if (all != null) {
ArrayList(all.keys)
} else ArrayList()
}

fun getSavedNotifications(): List<Notification> {
val storage = getStorage(NOTIFICATION_STORE_ID)
val all = storage.all
if (all != null) {
val notifications = ArrayList<Notification>()
for (key in all.keys) {
val notificationString = all[key] as String?
try {
val notification = jsonMapper.readValue(notificationString, Notification::class.java)
notifications.add(notification)
} catch (_: Exception) { }
}
return notifications
fun getSavedNotifications(): List<Notification> {
val storage = getStorage(NOTIFICATION_STORE_ID)
val all = storage.all
if (all != null) {
val notifications = ArrayList<Notification>()
for (key in all.keys) {
val notificationString = all[key] as String?
try {
val notification =
jsonMapper.readValue(notificationString, Notification::class.java)
notifications.add(notification)
} catch (_: Exception) {}
}
return notifications
}
return ArrayList()
}
return ArrayList()
}

fun getSavedNotification(key: String): Notification? {
val storage = getStorage(NOTIFICATION_STORE_ID)
val notificationString = try {
storage.getString(key, null)
} catch (ex: ClassCastException) {
return null
} ?: return null
fun getSavedNotification(key: String): Notification? {
val storage = getStorage(NOTIFICATION_STORE_ID)
val notificationString =
try {
storage.getString(key, null)
} catch (ex: ClassCastException) {
return null
} ?: return null

return try {
jsonMapper.readValue(notificationString, Notification::class.java)
} catch (ex: JSONException) {
null
return try {
jsonMapper.readValue(notificationString, Notification::class.java)
} catch (ex: JSONException) {
null
}
}
}

fun deleteNotification(id: String?) {
val editor = getStorage(NOTIFICATION_STORE_ID).edit()
editor.remove(id)
editor.apply()
}
fun deleteNotification(id: String?) {
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)
}
private fun getStorage(key: String): SharedPreferences {
return context.getSharedPreferences(key, Context.MODE_PRIVATE)
}

fun writeActionGroup(actions: List<ActionType>) {
for (type in actions) {
val i = type.id
val editor = getStorage(ACTION_TYPES_ID + type.id).edit()
editor.clear()
editor.putInt("count", type.actions.size)
for (action in type.actions) {
editor.putString("id$i", action.id)
editor.putString("title$i", action.title)
editor.putBoolean("input$i", action.input ?: false)
}
editor.apply()
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 (i in 0 until type.actions.size) {
val action = type.actions[i]
editor.putString("id$i", action.id)
editor.putString("title$i", action.title)
editor.putBoolean("input$i", action.input ?: false)
}
editor.apply()
}
}
}

fun getActionGroup(forId: String): Array<NotificationAction?> {
val storage = getStorage(ACTION_TYPES_ID + forId)
val count = storage.getInt("count", 0)
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)
fun getActionGroup(forId: String): Array<NotificationAction?> {
val storage = getStorage(ACTION_TYPES_ID + forId)
val count = storage.getInt("count", 0)
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)

val action = NotificationAction()
action.id = id ?: ""
action.title = title
action.input = input
actions[i] = action
val action = NotificationAction()
action.id = id ?: ""
action.title = title
action.input = input
actions[i] = action
}
return actions
}
return actions
}
}
}
172 changes: 172 additions & 0 deletions plugins/notification/src/models.rs
Original file line number Diff line number Diff line change
Expand Up @@ -320,6 +320,92 @@ pub struct ActionType {
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)]
#[derive(Debug, Serialize)]
#[serde(rename_all = "camelCase")]
Expand All @@ -334,6 +420,92 @@ pub struct Action {
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")]
pub use android::*;

Expand Down
Loading