tauri_plugin_notification/models.rs
1// Copyright 2019-2023 Tauri Programme within The Commons Conservancy
2// SPDX-License-Identifier: Apache-2.0
3// SPDX-License-Identifier: MIT
4
5use std::{collections::HashMap, fmt::Display};
6
7use serde::{Deserialize, Deserializer, Serialize, Serializer, de::Error as DeError};
8
9use url::Url;
10
11/// A media file attached to a notification.
12///
13/// Attachments are only used on mobile; desktop notifications ignore them.
14#[derive(Debug, Serialize, Deserialize)]
15#[serde(rename_all = "camelCase")]
16pub struct Attachment {
17 id: String,
18 url: Url,
19}
20
21impl Attachment {
22 /// Creates a new attachment with the given identifier and URL.
23 ///
24 /// The URL accepts the `asset` and `file` protocols.
25 pub fn new(id: impl Into<String>, url: Url) -> Self {
26 Self { id: id.into(), url }
27 }
28}
29
30/// The set of date fields a notification must match to be delivered.
31///
32/// Fields left as [`None`] match any value, so the notification fires on every date
33/// whose remaining components match. Used by [`Schedule::Interval`].
34#[derive(Debug, Default, Serialize, Deserialize)]
35#[serde(rename_all = "camelCase")]
36pub struct ScheduleInterval {
37 /// The year the notification fires on.
38 pub year: Option<u8>,
39 /// The month of the year the notification fires on.
40 pub month: Option<u8>,
41 /// The day of the month the notification fires on.
42 pub day: Option<u8>,
43 /// The day of the week the notification fires on.
44 ///
45 /// 1 - Sunday, 2 - Monday, 3 - Tuesday, 4 - Wednesday, 5 - Thursday, 6 - Friday, 7 - Saturday.
46 pub weekday: Option<u8>,
47 /// The hour of the day the notification fires on, in the 24-hour clock.
48 pub hour: Option<u8>,
49 /// The minute of the hour the notification fires on.
50 pub minute: Option<u8>,
51 /// The second of the minute the notification fires on.
52 pub second: Option<u8>,
53}
54
55/// The unit of the repeating interval used by [`Schedule::Every`].
56///
57/// It is serialized as its lowercase camelCase name, e.g. `twoWeeks`.
58#[derive(Debug)]
59pub enum ScheduleEvery {
60 /// Repeats every year.
61 ///
62 /// On Android a year is approximated as 52 weeks.
63 Year,
64 /// Repeats every month.
65 ///
66 /// On Android a month is approximated as 30 days.
67 Month,
68 /// Repeats every two weeks.
69 TwoWeeks,
70 /// Repeats every week.
71 Week,
72 /// Repeats every day.
73 Day,
74 /// Repeats every hour.
75 Hour,
76 /// Repeats every minute.
77 Minute,
78 /// Repeats every second.
79 ///
80 /// Not supported on iOS, where repeating triggers must be at least a minute apart.
81 Second,
82}
83
84impl Display for ScheduleEvery {
85 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
86 write!(
87 f,
88 "{}",
89 match self {
90 Self::Year => "year",
91 Self::Month => "month",
92 Self::TwoWeeks => "twoWeeks",
93 Self::Week => "week",
94 Self::Day => "day",
95 Self::Hour => "hour",
96 Self::Minute => "minute",
97 Self::Second => "second",
98 }
99 )
100 }
101}
102
103impl Serialize for ScheduleEvery {
104 fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
105 where
106 S: Serializer,
107 {
108 serializer.serialize_str(self.to_string().as_ref())
109 }
110}
111
112impl<'de> Deserialize<'de> for ScheduleEvery {
113 fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
114 where
115 D: Deserializer<'de>,
116 {
117 let s = String::deserialize(deserializer)?;
118 match s.to_lowercase().as_str() {
119 "year" => Ok(Self::Year),
120 "month" => Ok(Self::Month),
121 "twoweeks" => Ok(Self::TwoWeeks),
122 "week" => Ok(Self::Week),
123 "day" => Ok(Self::Day),
124 "hour" => Ok(Self::Hour),
125 "minute" => Ok(Self::Minute),
126 "second" => Ok(Self::Second),
127 _ => Err(DeError::custom(format!("unknown every kind '{s}'"))),
128 }
129 }
130}
131
132/// Defines when a notification is delivered.
133///
134/// Scheduling is only implemented on mobile; the desktop implementation delivers the
135/// notification immediately and ignores the schedule.
136#[derive(Debug, Serialize, Deserialize)]
137#[serde(rename_all = "camelCase")]
138pub enum Schedule {
139 /// Fires at a specific date and time, which must be in the future.
140 #[serde(rename_all = "camelCase")]
141 At {
142 /// The date and time the notification fires at, serialized as an ISO-8601 string.
143 #[serde(
144 serialize_with = "iso8601::serialize",
145 deserialize_with = "time::serde::iso8601::deserialize"
146 )]
147 date: time::OffsetDateTime,
148 /// Whether the notification keeps repeating, using the duration between the moment it is
149 /// scheduled and `date` as the interval. Defaults to `false`.
150 ///
151 /// The interval must be at least one minute on iOS.
152 #[serde(default)]
153 repeating: bool,
154 /// Whether the notification is allowed to fire while the device is in low-power idle
155 /// (Doze) mode. Defaults to `false`.
156 ///
157 /// Only used on Android.
158 #[serde(default)]
159 allow_while_idle: bool,
160 },
161 /// Fires whenever the current date matches every field set on the given interval.
162 #[serde(rename_all = "camelCase")]
163 Interval {
164 /// The date fields the current date must match for the notification to fire.
165 interval: ScheduleInterval,
166 /// Whether the notification is allowed to fire while the device is in low-power idle
167 /// (Doze) mode. Defaults to `false`.
168 ///
169 /// Only used on Android.
170 #[serde(default)]
171 allow_while_idle: bool,
172 },
173 /// Fires repeatedly, once every `count` times the given interval unit.
174 #[serde(rename_all = "camelCase")]
175 Every {
176 /// The unit of the repeating interval.
177 interval: ScheduleEvery,
178 /// How many interval units elapse between each notification.
179 count: u8,
180 /// Whether the notification is allowed to fire while the device is in low-power idle
181 /// (Doze) mode. Defaults to `false`.
182 ///
183 /// Only used on Android.
184 #[serde(default)]
185 allow_while_idle: bool,
186 },
187}
188
189// custom ISO-8601 serialization that does not use 6 digits for years.
190mod iso8601 {
191 use serde::{Serialize, Serializer, ser::Error as _};
192 use time::{
193 OffsetDateTime,
194 format_description::well_known::Iso8601,
195 format_description::well_known::iso8601::{Config, EncodedConfig},
196 };
197
198 const SERDE_CONFIG: EncodedConfig = Config::DEFAULT.encode();
199
200 pub fn serialize<S: Serializer>(
201 datetime: &OffsetDateTime,
202 serializer: S,
203 ) -> Result<S::Ok, S::Error> {
204 datetime
205 .format(&Iso8601::<SERDE_CONFIG>)
206 .map_err(S::Error::custom)?
207 .serialize(serializer)
208 }
209}
210
211/// The payload of a notification, as sent to the platform implementation.
212///
213/// Build it with [`NotificationBuilder`](crate::NotificationBuilder) rather than constructing it directly.
214/// The identifier defaults to a random 32-bit integer when it is not provided.
215#[derive(Debug, Serialize, Deserialize)]
216#[serde(rename_all = "camelCase")]
217pub struct NotificationData {
218 #[serde(default = "default_id")]
219 pub(crate) id: i32,
220 pub(crate) channel_id: Option<String>,
221 pub(crate) title: Option<String>,
222 pub(crate) body: Option<String>,
223 pub(crate) schedule: Option<Schedule>,
224 pub(crate) large_body: Option<String>,
225 pub(crate) summary: Option<String>,
226 pub(crate) action_type_id: Option<String>,
227 pub(crate) group: Option<String>,
228 #[serde(default)]
229 pub(crate) group_summary: bool,
230 pub(crate) sound: Option<String>,
231 #[serde(default)]
232 pub(crate) inbox_lines: Vec<String>,
233 pub(crate) icon: Option<String>,
234 pub(crate) large_icon: Option<String>,
235 pub(crate) icon_color: Option<String>,
236 #[serde(default)]
237 pub(crate) attachments: Vec<Attachment>,
238 #[serde(default)]
239 pub(crate) extra: HashMap<String, serde_json::Value>,
240 #[serde(default)]
241 pub(crate) ongoing: bool,
242 #[serde(default)]
243 pub(crate) auto_cancel: bool,
244 #[serde(default)]
245 pub(crate) silent: bool,
246}
247
248fn default_id() -> i32 {
249 rand::random()
250}
251
252impl Default for NotificationData {
253 fn default() -> Self {
254 Self {
255 id: default_id(),
256 channel_id: None,
257 title: None,
258 body: None,
259 schedule: None,
260 large_body: None,
261 summary: None,
262 action_type_id: None,
263 group: None,
264 group_summary: false,
265 sound: None,
266 inbox_lines: Vec::new(),
267 icon: None,
268 large_icon: None,
269 icon_color: None,
270 attachments: Vec::new(),
271 extra: Default::default(),
272 ongoing: false,
273 auto_cancel: false,
274 silent: false,
275 }
276 }
277}
278
279/// A notification that was scheduled and has not been delivered yet.
280///
281/// Returned by `Notification::pending`, which is only available on mobile.
282#[derive(Debug, Deserialize)]
283#[serde(rename_all = "camelCase")]
284pub struct PendingNotification {
285 id: i32,
286 title: Option<String>,
287 body: Option<String>,
288 schedule: Schedule,
289}
290
291impl PendingNotification {
292 /// The notification identifier.
293 pub fn id(&self) -> i32 {
294 self.id
295 }
296
297 /// The notification title, if it was set.
298 pub fn title(&self) -> Option<&str> {
299 self.title.as_deref()
300 }
301
302 /// The notification body, if it was set.
303 pub fn body(&self) -> Option<&str> {
304 self.body.as_deref()
305 }
306
307 /// The schedule that determines when the notification is delivered.
308 pub fn schedule(&self) -> &Schedule {
309 &self.schedule
310 }
311}
312
313/// A notification that was delivered and is still visible in the notification center.
314///
315/// Returned by `Notification::active`, which is only available on mobile.
316/// Which fields are populated depends on the platform, since Android and iOS expose
317/// different information about delivered notifications.
318#[derive(Debug, Deserialize)]
319#[serde(rename_all = "camelCase")]
320pub struct ActiveNotification {
321 id: i32,
322 tag: Option<String>,
323 title: Option<String>,
324 body: Option<String>,
325 group: Option<String>,
326 #[serde(default)]
327 group_summary: bool,
328 #[serde(default)]
329 data: HashMap<String, String>,
330 #[serde(default)]
331 extra: HashMap<String, serde_json::Value>,
332 #[serde(default)]
333 attachments: Vec<Attachment>,
334 action_type_id: Option<String>,
335 schedule: Option<Schedule>,
336 sound: Option<String>,
337}
338
339impl ActiveNotification {
340 /// The notification identifier.
341 pub fn id(&self) -> i32 {
342 self.id
343 }
344
345 /// The tag the notification was posted with.
346 ///
347 /// Only set on Android.
348 pub fn tag(&self) -> Option<&str> {
349 self.tag.as_deref()
350 }
351
352 /// The notification title, if it was set.
353 pub fn title(&self) -> Option<&str> {
354 self.title.as_deref()
355 }
356
357 /// The notification body, if it was set.
358 pub fn body(&self) -> Option<&str> {
359 self.body.as_deref()
360 }
361
362 /// The identifier of the group the notification belongs to.
363 ///
364 /// Only set on Android.
365 pub fn group(&self) -> Option<&str> {
366 self.group.as_deref()
367 }
368
369 /// Whether the notification is the summary of its group.
370 ///
371 /// Only set on Android. Defaults to `false`.
372 pub fn group_summary(&self) -> bool {
373 self.group_summary
374 }
375
376 /// The platform extras attached to the notification, as string values.
377 ///
378 /// Only set on Android, where it holds the `android.app.Notification` extras bundle.
379 pub fn data(&self) -> &HashMap<String, String> {
380 &self.data
381 }
382
383 /// The extra payload that was stored in the notification.
384 pub fn extra(&self) -> &HashMap<String, serde_json::Value> {
385 &self.extra
386 }
387
388 /// The attachments of the notification.
389 ///
390 /// Only set on iOS.
391 pub fn attachments(&self) -> &[Attachment] {
392 &self.attachments
393 }
394
395 /// The identifier of the action type the notification was registered with.
396 ///
397 /// Only set on iOS.
398 pub fn action_type_id(&self) -> Option<&str> {
399 self.action_type_id.as_deref()
400 }
401
402 /// The schedule the notification was delivered with, if it was scheduled.
403 pub fn schedule(&self) -> Option<&Schedule> {
404 self.schedule.as_ref()
405 }
406
407 /// The sound resource name of the notification.
408 ///
409 /// Only set on iOS.
410 pub fn sound(&self) -> Option<&str> {
411 self.sound.as_deref()
412 }
413}
414
415/// A group of [`Action`]s a notification can display, referenced by
416/// [`NotificationBuilder::action_type_id`](crate::NotificationBuilder::action_type_id).
417///
418/// Register it with `Notification::register_action_types` before sending a notification that uses it.
419/// It maps to a `UNNotificationCategory` on iOS and to an action group on Android.
420///
421/// Only available on mobile. Use [`ActionType::builder`] to construct one.
422#[cfg(mobile)]
423#[derive(Debug, Serialize)]
424#[serde(rename_all = "camelCase")]
425pub struct ActionType {
426 id: String,
427 actions: Vec<Action>,
428 hidden_previews_body_placeholder: Option<String>,
429 custom_dismiss_action: bool,
430 allow_in_car_play: bool,
431 hidden_previews_show_title: bool,
432 hidden_previews_show_subtitle: bool,
433}
434
435/// Builder for an [`ActionType`], created with [`ActionType::builder`].
436///
437/// Only available on mobile.
438#[cfg(mobile)]
439#[derive(Debug)]
440pub struct ActionTypeBuilder(ActionType);
441
442#[cfg(mobile)]
443impl ActionType {
444 /// Creates a builder for an action type with the given identifier.
445 ///
446 /// All the optional settings default to `false` or [`None`];
447 /// call [`ActionTypeBuilder::build`] to get the [`ActionType`].
448 pub fn builder(id: impl Into<String>) -> ActionTypeBuilder {
449 ActionTypeBuilder(Self {
450 id: id.into(),
451 actions: Vec::new(),
452 hidden_previews_body_placeholder: None,
453 custom_dismiss_action: false,
454 allow_in_car_play: false,
455 hidden_previews_show_title: false,
456 hidden_previews_show_subtitle: false,
457 })
458 }
459
460 /// The identifier of this action type.
461 pub fn id(&self) -> &str {
462 &self.id
463 }
464
465 /// The actions associated with this action type.
466 pub fn actions(&self) -> &[Action] {
467 &self.actions
468 }
469
470 /// The placeholder shown instead of the notification body when previews are hidden.
471 ///
472 /// Only used on iOS.
473 pub fn hidden_previews_body_placeholder(&self) -> Option<&str> {
474 self.hidden_previews_body_placeholder.as_deref()
475 }
476
477 /// Whether the app is notified when the user dismisses the notification.
478 ///
479 /// Only used on iOS.
480 pub fn custom_dismiss_action(&self) -> bool {
481 self.custom_dismiss_action
482 }
483
484 /// Whether the notification can be displayed in a CarPlay environment.
485 ///
486 /// Only used on iOS.
487 pub fn allow_in_car_play(&self) -> bool {
488 self.allow_in_car_play
489 }
490
491 /// Whether the notification title is shown even when previews are hidden.
492 ///
493 /// Only used on iOS.
494 pub fn hidden_previews_show_title(&self) -> bool {
495 self.hidden_previews_show_title
496 }
497
498 /// Whether the notification subtitle is shown even when previews are hidden.
499 ///
500 /// Only used on iOS.
501 pub fn hidden_previews_show_subtitle(&self) -> bool {
502 self.hidden_previews_show_subtitle
503 }
504}
505
506#[cfg(mobile)]
507impl ActionTypeBuilder {
508 /// Sets the actions associated with this action type.
509 pub fn actions(mut self, actions: Vec<Action>) -> Self {
510 self.0.actions = actions;
511 self
512 }
513
514 /// Sets the placeholder shown instead of the notification body when previews are hidden.
515 ///
516 /// Only used on iOS.
517 pub fn hidden_previews_body_placeholder(
518 mut self,
519 hidden_previews_body_placeholder: impl Into<String>,
520 ) -> Self {
521 self.0
522 .hidden_previews_body_placeholder
523 .replace(hidden_previews_body_placeholder.into());
524 self
525 }
526
527 /// Sets whether the app is notified when the user dismisses the notification.
528 ///
529 /// Only used on iOS.
530 pub fn custom_dismiss_action(mut self, custom_dismiss_action: bool) -> Self {
531 self.0.custom_dismiss_action = custom_dismiss_action;
532 self
533 }
534
535 /// Sets whether the notification can be displayed in a CarPlay environment.
536 ///
537 /// Only used on iOS.
538 pub fn allow_in_car_play(mut self, allow_in_car_play: bool) -> Self {
539 self.0.allow_in_car_play = allow_in_car_play;
540 self
541 }
542
543 /// Sets whether the notification title is shown even when previews are hidden.
544 ///
545 /// Only used on iOS.
546 pub fn hidden_previews_show_title(mut self, hidden_previews_show_title: bool) -> Self {
547 self.0.hidden_previews_show_title = hidden_previews_show_title;
548 self
549 }
550
551 /// Sets whether the notification subtitle is shown even when previews are hidden.
552 ///
553 /// Only used on iOS.
554 pub fn hidden_previews_show_subtitle(mut self, hidden_previews_show_subtitle: bool) -> Self {
555 self.0.hidden_previews_show_subtitle = hidden_previews_show_subtitle;
556 self
557 }
558
559 /// Builds the [`ActionType`].
560 pub fn build(self) -> ActionType {
561 self.0
562 }
563}
564
565/// A button the user can tap on a notification, belonging to an [`ActionType`].
566///
567/// It maps to a `UNNotificationAction` on iOS. On Android only the identifier, the title
568/// and the input flag are used.
569///
570/// Only available on mobile. Use [`Action::builder`] to construct one.
571#[cfg(mobile)]
572#[derive(Debug, Serialize)]
573#[serde(rename_all = "camelCase")]
574pub struct Action {
575 id: String,
576 title: String,
577 requires_authentication: bool,
578 foreground: bool,
579 destructive: bool,
580 input: bool,
581 input_button_title: Option<String>,
582 input_placeholder: Option<String>,
583}
584
585/// Builder for an [`Action`], created with [`Action::builder`].
586///
587/// Only available on mobile.
588#[cfg(mobile)]
589#[derive(Debug)]
590pub struct ActionBuilder(Action);
591
592#[cfg(mobile)]
593impl Action {
594 /// Creates a builder for an action with the given identifier and button title.
595 ///
596 /// All the optional settings default to `false` or [`None`];
597 /// call [`ActionBuilder::build`] to get the [`Action`].
598 pub fn builder(id: impl Into<String>, title: impl Into<String>) -> ActionBuilder {
599 ActionBuilder(Self {
600 id: id.into(),
601 title: title.into(),
602 requires_authentication: false,
603 foreground: false,
604 destructive: false,
605 input: false,
606 input_button_title: None,
607 input_placeholder: None,
608 })
609 }
610
611 /// The identifier of this action, reported back when the user triggers it.
612 pub fn id(&self) -> &str {
613 &self.id
614 }
615
616 /// The text displayed on the action button.
617 pub fn title(&self) -> &str {
618 &self.title
619 }
620
621 /// Whether the device must be unlocked for the action to run.
622 ///
623 /// Only used on iOS.
624 pub fn requires_authentication(&self) -> bool {
625 self.requires_authentication
626 }
627
628 /// Whether the app is brought to the foreground when the action is triggered.
629 ///
630 /// Only used on iOS.
631 pub fn foreground(&self) -> bool {
632 self.foreground
633 }
634
635 /// Whether the action is displayed as destructive, usually in red.
636 ///
637 /// Only used on iOS.
638 pub fn destructive(&self) -> bool {
639 self.destructive
640 }
641
642 /// Whether triggering the action lets the user type a text response.
643 pub fn input(&self) -> bool {
644 self.input
645 }
646
647 /// The text displayed on the button that submits the text input.
648 ///
649 /// Only used on iOS.
650 pub fn input_button_title(&self) -> Option<&str> {
651 self.input_button_title.as_deref()
652 }
653
654 /// The placeholder displayed on the empty text input field.
655 ///
656 /// Only used on iOS.
657 pub fn input_placeholder(&self) -> Option<&str> {
658 self.input_placeholder.as_deref()
659 }
660}
661
662#[cfg(mobile)]
663impl ActionBuilder {
664 /// Sets whether the device must be unlocked for the action to run.
665 ///
666 /// Only used on iOS.
667 pub fn requires_authentication(mut self, requires_authentication: bool) -> Self {
668 self.0.requires_authentication = requires_authentication;
669 self
670 }
671
672 /// Sets whether the app is brought to the foreground when the action is triggered.
673 ///
674 /// Only used on iOS.
675 pub fn foreground(mut self, foreground: bool) -> Self {
676 self.0.foreground = foreground;
677 self
678 }
679
680 /// Sets whether the action is displayed as destructive, usually in red.
681 ///
682 /// Only used on iOS.
683 pub fn destructive(mut self, destructive: bool) -> Self {
684 self.0.destructive = destructive;
685 self
686 }
687
688 /// Sets whether triggering the action lets the user type a text response.
689 pub fn input(mut self, input: bool) -> Self {
690 self.0.input = input;
691 self
692 }
693
694 /// Sets the text displayed on the button that submits the text input.
695 ///
696 /// Only used on iOS.
697 pub fn input_button_title(mut self, input_button_title: impl Into<String>) -> Self {
698 self.0.input_button_title.replace(input_button_title.into());
699 self
700 }
701
702 /// Sets the placeholder displayed on the empty text input field.
703 ///
704 /// Only used on iOS.
705 pub fn input_placeholder(mut self, input_placeholder: impl Into<String>) -> Self {
706 self.0.input_placeholder.replace(input_placeholder.into());
707 self
708 }
709
710 /// Builds the [`Action`].
711 pub fn build(self) -> Action {
712 self.0
713 }
714}
715
716#[cfg(target_os = "android")]
717pub use android::*;
718
719#[cfg(target_os = "android")]
720mod android {
721 use serde::{Deserialize, Serialize};
722 use serde_repr::{Deserialize_repr, Serialize_repr};
723
724 /// How much the notifications of a [`Channel`] interrupt the user.
725 ///
726 /// It maps to the `NotificationManager.IMPORTANCE_*` constants and is serialized as its
727 /// integer value. Only available on Android.
728 #[derive(Debug, Default, Clone, Copy, Serialize_repr, Deserialize_repr)]
729 #[repr(u8)]
730 pub enum Importance {
731 /// The notifications are not shown.
732 None = 0,
733 /// The notifications are only shown in the shade, below the fold, without a status bar icon.
734 Min = 1,
735 /// The notifications are shown without a sound.
736 Low = 2,
737 /// The notifications are shown and make a sound.
738 ///
739 /// This is the value used when the channel does not define an importance.
740 #[default]
741 Default = 3,
742 /// The notifications are shown, make a sound and pop up as a heads-up notification.
743 High = 4,
744 }
745
746 /// How much of a notification is shown on the lock screen.
747 ///
748 /// It maps to the `Notification.VISIBILITY_*` constants and is serialized as its
749 /// integer value. Only available on Android.
750 #[derive(Debug, Clone, Copy, Serialize_repr, Deserialize_repr)]
751 #[repr(i8)]
752 pub enum Visibility {
753 /// The notification is not shown on the lock screen at all.
754 Secret = -1,
755 /// The notification is shown on the lock screen with its sensitive content hidden.
756 ///
757 /// This is the value used when the channel does not define a visibility.
758 Private = 0,
759 /// The notification is shown in full on the lock screen.
760 Public = 1,
761 }
762
763 /// A notification channel, the category users configure notification behavior on.
764 ///
765 /// Notifications reference a channel through
766 /// [`NotificationBuilder::channel_id`](crate::NotificationBuilder::channel_id) and are not
767 /// delivered when the channel does not exist. Only available on Android.
768 /// Use [`Channel::builder`] to construct one.
769 #[derive(Debug, Serialize, Deserialize)]
770 #[serde(rename_all = "camelCase")]
771 pub struct Channel {
772 id: String,
773 name: String,
774 description: Option<String>,
775 sound: Option<String>,
776 lights: bool,
777 light_color: Option<String>,
778 vibration: bool,
779 importance: Importance,
780 visibility: Option<Visibility>,
781 }
782
783 /// Builder for a [`Channel`], created with [`Channel::builder`].
784 ///
785 /// Only available on Android.
786 #[derive(Debug)]
787 pub struct ChannelBuilder(Channel);
788
789 impl Channel {
790 /// Creates a builder for a channel with the given identifier and user visible name.
791 ///
792 /// Lights and vibration are disabled, the importance defaults to [`Importance::Default`]
793 /// and the remaining settings default to [`None`];
794 /// call [`ChannelBuilder::build`] to get the [`Channel`].
795 pub fn builder(id: impl Into<String>, name: impl Into<String>) -> ChannelBuilder {
796 ChannelBuilder(Self {
797 id: id.into(),
798 name: name.into(),
799 description: None,
800 sound: None,
801 lights: false,
802 light_color: None,
803 vibration: false,
804 importance: Default::default(),
805 visibility: None,
806 })
807 }
808
809 /// The identifier of this channel.
810 pub fn id(&self) -> &str {
811 &self.id
812 }
813
814 /// The user visible name of this channel.
815 pub fn name(&self) -> &str {
816 &self.name
817 }
818
819 /// The user visible description of this channel.
820 pub fn description(&self) -> Option<&str> {
821 self.description.as_deref()
822 }
823
824 /// The name of the sound resource played by the notifications of this channel.
825 ///
826 /// The resource must be placed in the app's `res/raw` folder.
827 pub fn sound(&self) -> Option<&str> {
828 self.sound.as_deref()
829 }
830
831 /// Whether the notifications of this channel blink the device light.
832 pub fn lights(&self) -> bool {
833 self.lights
834 }
835
836 /// The color of the device light, as a color string such as `#ff0000`.
837 pub fn light_color(&self) -> Option<&str> {
838 self.light_color.as_deref()
839 }
840
841 /// Whether the notifications of this channel vibrate the device.
842 pub fn vibration(&self) -> bool {
843 self.vibration
844 }
845
846 /// How much the notifications of this channel interrupt the user.
847 pub fn importance(&self) -> Importance {
848 self.importance
849 }
850
851 /// How much of the notifications of this channel is shown on the lock screen.
852 ///
853 /// [`Visibility::Private`] is used when this is [`None`].
854 pub fn visibility(&self) -> Option<Visibility> {
855 self.visibility
856 }
857 }
858
859 impl ChannelBuilder {
860 /// Sets the user visible description of the channel.
861 pub fn description(mut self, description: impl Into<String>) -> Self {
862 self.0.description.replace(description.into());
863 self
864 }
865
866 /// Sets the name of the sound resource played by the notifications of this channel.
867 ///
868 /// The resource must be placed in the app's `res/raw` folder.
869 pub fn sound(mut self, sound: impl Into<String>) -> Self {
870 self.0.sound.replace(sound.into());
871 self
872 }
873
874 /// Sets whether the notifications of this channel blink the device light.
875 pub fn lights(mut self, lights: bool) -> Self {
876 self.0.lights = lights;
877 self
878 }
879
880 /// Sets the color of the device light, as a color string such as `#ff0000`.
881 pub fn light_color(mut self, color: impl Into<String>) -> Self {
882 self.0.light_color.replace(color.into());
883 self
884 }
885
886 /// Sets whether the notifications of this channel vibrate the device.
887 pub fn vibration(mut self, vibration: bool) -> Self {
888 self.0.vibration = vibration;
889 self
890 }
891
892 /// Sets how much the notifications of this channel interrupt the user.
893 pub fn importance(mut self, importance: Importance) -> Self {
894 self.0.importance = importance;
895 self
896 }
897
898 /// Sets how much of the notifications of this channel is shown on the lock screen.
899 pub fn visibility(mut self, visibility: Visibility) -> Self {
900 self.0.visibility.replace(visibility);
901 self
902 }
903
904 /// Builds the [`Channel`].
905 pub fn build(self) -> Channel {
906 self.0
907 }
908 }
909}