Skip to content

Commit 3731c04

Browse files
author
Michael Kadziela
committed
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
1 parent 2574ec8 commit 3731c04

2 files changed

Lines changed: 262 additions & 82 deletions

File tree

plugins/notification/android/src/main/java/NotificationStorage.kt

Lines changed: 84 additions & 82 deletions
Original file line numberDiff line numberDiff line change
@@ -7,107 +7,109 @@ package app.tauri.notification
77
import android.content.Context
88
import android.content.SharedPreferences
99
import com.fasterxml.jackson.databind.ObjectMapper
10-
import org.json.JSONException
1110
import java.lang.Exception
11+
import org.json.JSONException
1212

1313
// Key for private preferences
1414
private const val NOTIFICATION_STORE_ID = "NOTIFICATION_STORE"
1515
// Key used to save action types
1616
private const val ACTION_TYPES_ID = "ACTION_TYPE_STORE"
1717

1818
class NotificationStorage(private val context: Context, private val jsonMapper: ObjectMapper) {
19-
fun appendNotifications(localNotifications: List<Notification>) {
20-
val storage = getStorage(NOTIFICATION_STORE_ID)
21-
val editor = storage.edit()
22-
for (request in localNotifications) {
23-
if (request.schedule != null) {
24-
val key: String = request.id.toString()
25-
editor.putString(key, request.sourceJson.toString())
26-
}
19+
fun appendNotifications(localNotifications: List<Notification>) {
20+
val storage = getStorage(NOTIFICATION_STORE_ID)
21+
val editor = storage.edit()
22+
for (request in localNotifications) {
23+
if (request.schedule != null) {
24+
val key: String = request.id.toString()
25+
editor.putString(key, request.sourceJson.toString())
26+
}
27+
}
28+
editor.apply()
2729
}
28-
editor.apply()
29-
}
3030

31-
fun getSavedNotificationIds(): List<String> {
32-
val storage = getStorage(NOTIFICATION_STORE_ID)
33-
val all = storage.all
34-
return if (all != null) {
35-
ArrayList(all.keys)
36-
} else ArrayList()
37-
}
31+
fun getSavedNotificationIds(): List<String> {
32+
val storage = getStorage(NOTIFICATION_STORE_ID)
33+
val all = storage.all
34+
return if (all != null) {
35+
ArrayList(all.keys)
36+
} else ArrayList()
37+
}
3838

39-
fun getSavedNotifications(): List<Notification> {
40-
val storage = getStorage(NOTIFICATION_STORE_ID)
41-
val all = storage.all
42-
if (all != null) {
43-
val notifications = ArrayList<Notification>()
44-
for (key in all.keys) {
45-
val notificationString = all[key] as String?
46-
try {
47-
val notification = jsonMapper.readValue(notificationString, Notification::class.java)
48-
notifications.add(notification)
49-
} catch (_: Exception) { }
50-
}
51-
return notifications
39+
fun getSavedNotifications(): List<Notification> {
40+
val storage = getStorage(NOTIFICATION_STORE_ID)
41+
val all = storage.all
42+
if (all != null) {
43+
val notifications = ArrayList<Notification>()
44+
for (key in all.keys) {
45+
val notificationString = all[key] as String?
46+
try {
47+
val notification =
48+
jsonMapper.readValue(notificationString, Notification::class.java)
49+
notifications.add(notification)
50+
} catch (_: Exception) {}
51+
}
52+
return notifications
53+
}
54+
return ArrayList()
5255
}
53-
return ArrayList()
54-
}
5556

56-
fun getSavedNotification(key: String): Notification? {
57-
val storage = getStorage(NOTIFICATION_STORE_ID)
58-
val notificationString = try {
59-
storage.getString(key, null)
60-
} catch (ex: ClassCastException) {
61-
return null
62-
} ?: return null
57+
fun getSavedNotification(key: String): Notification? {
58+
val storage = getStorage(NOTIFICATION_STORE_ID)
59+
val notificationString =
60+
try {
61+
storage.getString(key, null)
62+
} catch (ex: ClassCastException) {
63+
return null
64+
} ?: return null
6365

64-
return try {
65-
jsonMapper.readValue(notificationString, Notification::class.java)
66-
} catch (ex: JSONException) {
67-
null
66+
return try {
67+
jsonMapper.readValue(notificationString, Notification::class.java)
68+
} catch (ex: JSONException) {
69+
null
70+
}
6871
}
69-
}
7072

71-
fun deleteNotification(id: String?) {
72-
val editor = getStorage(NOTIFICATION_STORE_ID).edit()
73-
editor.remove(id)
74-
editor.apply()
75-
}
73+
fun deleteNotification(id: String?) {
74+
val editor = getStorage(NOTIFICATION_STORE_ID).edit()
75+
editor.remove(id)
76+
editor.apply()
77+
}
7678

77-
private fun getStorage(key: String): SharedPreferences {
78-
return context.getSharedPreferences(key, Context.MODE_PRIVATE)
79-
}
79+
private fun getStorage(key: String): SharedPreferences {
80+
return context.getSharedPreferences(key, Context.MODE_PRIVATE)
81+
}
8082

81-
fun writeActionGroup(actions: List<ActionType>) {
82-
for (type in actions) {
83-
val i = type.id
84-
val editor = getStorage(ACTION_TYPES_ID + type.id).edit()
85-
editor.clear()
86-
editor.putInt("count", type.actions.size)
87-
for (action in type.actions) {
88-
editor.putString("id$i", action.id)
89-
editor.putString("title$i", action.title)
90-
editor.putBoolean("input$i", action.input ?: false)
91-
}
92-
editor.apply()
83+
fun writeActionGroup(actions: List<ActionType>) {
84+
for (type in actions) {
85+
val editor = getStorage(ACTION_TYPES_ID + type.id).edit()
86+
editor.clear()
87+
editor.putInt("count", type.actions.size)
88+
for (i in 0 until type.actions.size) {
89+
val action = type.actions[i]
90+
editor.putString("id$i", action.id)
91+
editor.putString("title$i", action.title)
92+
editor.putBoolean("input$i", action.input ?: false)
93+
}
94+
editor.apply()
95+
}
9396
}
94-
}
9597

96-
fun getActionGroup(forId: String): Array<NotificationAction?> {
97-
val storage = getStorage(ACTION_TYPES_ID + forId)
98-
val count = storage.getInt("count", 0)
99-
val actions: Array<NotificationAction?> = arrayOfNulls(count)
100-
for (i in 0 until count) {
101-
val id = storage.getString("id$i", "")
102-
val title = storage.getString("title$i", "")
103-
val input = storage.getBoolean("input$i", false)
98+
fun getActionGroup(forId: String): Array<NotificationAction?> {
99+
val storage = getStorage(ACTION_TYPES_ID + forId)
100+
val count = storage.getInt("count", 0)
101+
val actions: Array<NotificationAction?> = arrayOfNulls(count)
102+
for (i in 0 until count) {
103+
val id = storage.getString("id$i", "")
104+
val title = storage.getString("title$i", "")
105+
val input = storage.getBoolean("input$i", false)
104106

105-
val action = NotificationAction()
106-
action.id = id ?: ""
107-
action.title = title
108-
action.input = input
109-
actions[i] = action
107+
val action = NotificationAction()
108+
action.id = id ?: ""
109+
action.title = title
110+
action.input = input
111+
actions[i] = action
112+
}
113+
return actions
110114
}
111-
return actions
112-
}
113-
}
115+
}

plugins/notification/src/models.rs

Lines changed: 178 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -320,6 +320,95 @@ pub struct ActionType {
320320
hidden_previews_show_subtitle: bool,
321321
}
322322

323+
#[cfg(mobile)]
324+
#[derive(Debug)]
325+
pub struct ActionTypeBuilder(ActionType);
326+
327+
#[cfg(mobile)]
328+
impl ActionType {
329+
pub fn builder(id: impl Into<String>) -> ActionTypeBuilder {
330+
ActionTypeBuilder(Self {
331+
id: id.into(),
332+
actions: Vec::new(),
333+
hidden_previews_body_placeholder: None,
334+
custom_dismiss_action: false,
335+
allow_in_car_play: false,
336+
hidden_previews_show_title: false,
337+
hidden_previews_show_subtitle: false,
338+
})
339+
}
340+
341+
pub fn id(&self) -> &str {
342+
&self.id
343+
}
344+
345+
pub fn actions(&self) -> &[Action] {
346+
&self.actions
347+
}
348+
349+
pub fn hidden_previews_body_placeholder(&self) -> Option<&str> {
350+
self.hidden_previews_body_placeholder.as_deref()
351+
}
352+
353+
pub fn custom_dismiss_action(&self) -> bool {
354+
self.custom_dismiss_action
355+
}
356+
357+
pub fn allow_in_car_play(&self) -> bool {
358+
self.allow_in_car_play
359+
}
360+
361+
pub fn hidden_previews_show_title(&self) -> bool {
362+
self.hidden_previews_show_title
363+
}
364+
365+
pub fn hidden_previews_show_subtitle(&self) -> bool {
366+
self.hidden_previews_show_subtitle
367+
}
368+
}
369+
370+
#[cfg(mobile)]
371+
impl ActionTypeBuilder {
372+
pub fn actions(mut self, actions: Vec<Action>) -> Self {
373+
self.0.actions = actions;
374+
self
375+
}
376+
377+
pub fn hidden_previews_body_placeholder(
378+
mut self,
379+
hidden_previews_body_placeholder: impl Into<String>,
380+
) -> Self {
381+
self.0
382+
.hidden_previews_body_placeholder
383+
.replace(hidden_previews_body_placeholder.into());
384+
self
385+
}
386+
387+
pub fn custom_dismiss_action(mut self, custom_dismiss_action: bool) -> Self {
388+
self.0.custom_dismiss_action = custom_dismiss_action;
389+
self
390+
}
391+
392+
pub fn allow_in_car_play(mut self, allow_in_car_play: bool) -> Self {
393+
self.0.allow_in_car_play = allow_in_car_play;
394+
self
395+
}
396+
397+
pub fn hidden_previews_show_title(mut self, hidden_previews_show_title: bool) -> Self {
398+
self.0.hidden_previews_show_title = hidden_previews_show_title;
399+
self
400+
}
401+
402+
pub fn hidden_previews_show_subtitle(mut self, hidden_previews_show_subtitle: bool) -> Self {
403+
self.0.hidden_previews_show_subtitle = hidden_previews_show_subtitle;
404+
self
405+
}
406+
407+
pub fn build(self) -> ActionType {
408+
self.0
409+
}
410+
}
411+
323412
#[cfg(mobile)]
324413
#[derive(Debug, Serialize)]
325414
#[serde(rename_all = "camelCase")]
@@ -334,6 +423,95 @@ pub struct Action {
334423
input_placeholder: Option<String>,
335424
}
336425

426+
#[cfg(mobile)]
427+
#[derive(Debug)]
428+
pub struct ActionBuilder(Action);
429+
430+
#[cfg(mobile)]
431+
impl Action {
432+
pub fn builder(id: impl Into<String>, title: impl Into<String>) -> ActionBuilder {
433+
ActionBuilder(Self {
434+
id: id.into(),
435+
title: title.into(),
436+
requires_authentication: false,
437+
foreground: false,
438+
destructive: false,
439+
input: false,
440+
input_button_title: None,
441+
input_placeholder: None,
442+
})
443+
}
444+
445+
pub fn id(&self) -> &str {
446+
&self.id
447+
}
448+
449+
pub fn title(&self) -> &str {
450+
&self.title
451+
}
452+
453+
pub fn requires_authentication(&self) -> bool {
454+
self.requires_authentication
455+
}
456+
457+
pub fn foreground(&self) -> bool {
458+
self.foreground
459+
}
460+
461+
pub fn destructive(&self) -> bool {
462+
self.destructive
463+
}
464+
465+
pub fn input(&self) -> bool {
466+
self.input
467+
}
468+
469+
pub fn input_button_title(&self) -> Option<&str> {
470+
self.input_button_title.as_deref()
471+
}
472+
473+
pub fn input_placeholder(&self) -> Option<&str> {
474+
self.input_placeholder.as_deref()
475+
}
476+
}
477+
478+
#[cfg(mobile)]
479+
impl ActionBuilder {
480+
pub fn requires_authentication(mut self, requires_authentication: bool) -> Self {
481+
self.0.requires_authentication = requires_authentication;
482+
self
483+
}
484+
485+
pub fn foreground(mut self, foreground: bool) -> Self {
486+
self.0.foreground = foreground;
487+
self
488+
}
489+
490+
pub fn destructive(mut self, destructive: bool) -> Self {
491+
self.0.destructive = destructive;
492+
self
493+
}
494+
495+
pub fn input(mut self, input: bool) -> Self {
496+
self.0.input = input;
497+
self
498+
}
499+
500+
pub fn input_button_title(mut self, input_button_title: impl Into<String>) -> Self {
501+
self.0.input_button_title.replace(input_button_title.into());
502+
self
503+
}
504+
505+
pub fn input_placeholder(mut self, input_placeholder: impl Into<String>) -> Self {
506+
self.0.input_placeholder.replace(input_placeholder.into());
507+
self
508+
}
509+
510+
pub fn build(self) -> Action {
511+
self.0
512+
}
513+
}
514+
337515
#[cfg(target_os = "android")]
338516
pub use android::*;
339517

0 commit comments

Comments
 (0)