Skip to main content

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}