224 lines
6.9 KiB
Rust
224 lines
6.9 KiB
Rust
use std::{convert::Infallible, str::FromStr};
|
|
|
|
use pyo3::{types::PyString, Bound, IntoPyObject, Python};
|
|
use python_utils::IntoPyObjectViaDisplay;
|
|
use snafu::Snafu;
|
|
use strum::EnumString;
|
|
use url::Url;
|
|
|
|
pub mod command;
|
|
pub mod standard;
|
|
pub mod text_to_speech;
|
|
|
|
pub use command::*;
|
|
pub use standard::StandardNotification;
|
|
pub use text_to_speech::TextToSpeech;
|
|
|
|
#[derive(Debug, Clone, derive_more::Display)]
|
|
pub struct NonSpecialMessage(String);
|
|
|
|
#[derive(Debug, Clone, EnumString, strum::Display)]
|
|
#[strum(serialize_all = "snake_case")]
|
|
pub enum SpecialMessage {
|
|
ClearBadge,
|
|
ClearNotification,
|
|
CommandActivity,
|
|
CommandAppLock,
|
|
CommandAutoScreenBrightness,
|
|
CommandBluetooth,
|
|
CommandBleTransmitter,
|
|
CommandBeaconMonitor,
|
|
CommandBroadcastIntent,
|
|
CommandDnd,
|
|
CommandFlashlight,
|
|
CommandHighAccuracyMode,
|
|
CommandLaunchApp,
|
|
CommandMedia,
|
|
CommandRingerMode,
|
|
CommandScreenBrightnessLevel,
|
|
CommandScreenOffTimeout,
|
|
CommandScreenOn,
|
|
CommandStopTts,
|
|
CommandPersistentConnection,
|
|
CommandUpdateSensors,
|
|
CommandVolumeLevel,
|
|
CommandWebivew,
|
|
RemoveChannel,
|
|
RequestLocationUpdate,
|
|
// #[strum(serialize = "TTS")] TODO: WIP: TESTING
|
|
Tts,
|
|
UpdateComplications,
|
|
UpdateWidgets,
|
|
}
|
|
|
|
/// wasn't supposed to get a specially-behaving message here, but got {got}
|
|
#[derive(Debug, Clone, Snafu)]
|
|
pub struct WasSpecialMessage {
|
|
pub got: SpecialMessage,
|
|
}
|
|
|
|
impl FromStr for NonSpecialMessage {
|
|
type Err = WasSpecialMessage;
|
|
|
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
|
match SpecialMessage::from_str(s) {
|
|
Ok(special_message) => Err(WasSpecialMessage {
|
|
got: special_message,
|
|
}),
|
|
Err(_e) => Ok(NonSpecialMessage(s.into())),
|
|
}
|
|
}
|
|
}
|
|
|
|
/// How much of a notification is visible on the lock screen
|
|
#[derive(Debug, Clone, Default, EnumString, strum::Display)]
|
|
#[strum(serialize_all = "snake_case")]
|
|
pub enum Visibility {
|
|
/// always show all notification content
|
|
Public,
|
|
/// visibility depends on your setting in the system Settings app > Notifications;
|
|
/// if the option to show sensitive notifications when locked is enabled all notification content will be shown,
|
|
/// otherwise only basic information such as the icon and app name are visible
|
|
#[default]
|
|
Private,
|
|
/// always hide notification from lock screen
|
|
Secret,
|
|
}
|
|
|
|
// TODO: replace with a derive(DisplayToPy) (analogous to serde_with::SerializeDisplay) once I make one
|
|
impl<'py> IntoPyObject<'py> for Visibility {
|
|
type Target = PyString;
|
|
type Output = Bound<'py, Self::Target>;
|
|
type Error = Infallible;
|
|
|
|
fn into_pyobject(self, py: Python<'py>) -> Result<Self::Output, Self::Error> {
|
|
let s = self.to_string();
|
|
s.into_pyobject(py)
|
|
}
|
|
}
|
|
|
|
#[derive(Debug, Clone, EnumString, strum::Display)]
|
|
pub enum Behavior {
|
|
/// prompt for text to return with the event
|
|
#[strum(serialize = "textInput")]
|
|
TextInput,
|
|
}
|
|
|
|
// TODO: replace with a derive(DisplayToPy) (analogous to serde_with::SerializeDisplay) once I make one
|
|
impl<'py> IntoPyObject<'py> for Behavior {
|
|
type Target = PyString;
|
|
type Output = Bound<'py, Self::Target>;
|
|
type Error = Infallible;
|
|
|
|
fn into_pyobject(self, py: Python<'py>) -> Result<Self::Output, Self::Error> {
|
|
let s = self.to_string();
|
|
s.into_pyobject(py)
|
|
}
|
|
}
|
|
|
|
#[derive(Debug, Clone, Default, EnumString, strum::Display)]
|
|
#[strum(serialize_all = "camelCase")]
|
|
pub enum ActivationMode {
|
|
/// launch the app when tapped
|
|
Foreground,
|
|
/// just fires the event
|
|
#[default]
|
|
Background,
|
|
}
|
|
|
|
// TODO: replace with a derive(DisplayToPy) (analogous to serde_with::SerializeDisplay) once I make one
|
|
impl<'py> IntoPyObject<'py> for ActivationMode {
|
|
type Target = PyString;
|
|
type Output = Bound<'py, Self::Target>;
|
|
type Error = Infallible;
|
|
|
|
fn into_pyobject(self, py: Python<'py>) -> Result<Self::Output, Self::Error> {
|
|
let s = self.to_string();
|
|
s.into_pyobject(py)
|
|
}
|
|
}
|
|
|
|
// TODO: better typed versions like `CallNumber` or `OpenWebpage` where Action: From<CallNumber> and Action: From<OpenWebPage>
|
|
#[derive(Debug, Clone, IntoPyObject, typed_builder::TypedBuilder)]
|
|
#[builder(field_defaults(default, setter(strip_option(fallback_suffix = "_option"))))]
|
|
pub struct Action {
|
|
// TODO: proper type
|
|
// TODO: I wish I could call this identifier or something instead
|
|
#[builder(!default, setter(!strip_option))]
|
|
pub action: String,
|
|
#[builder(!default, setter(!strip_option))]
|
|
pub title: String,
|
|
|
|
pub uri: Option<IntoPyObjectViaDisplay<Url>>,
|
|
pub behavior: Option<Behavior>,
|
|
|
|
// TODO: make this written as activationMode
|
|
/// (iOS only) decide whether to open the app (to the foreground) when tapped
|
|
/// or merely fire an event (in the background)
|
|
pub activation_mode: Option<ActivationMode>,
|
|
|
|
// TODO: make this written as authenticationRequired
|
|
/// (iOS only) require entering a passcode to use the action
|
|
pub authentication_required: Option<bool>,
|
|
|
|
/// (iOS only) color the action's title red, indicating a destructive action
|
|
pub destructive: Option<bool>,
|
|
|
|
// TODO: make this written as textInputButtonTitle
|
|
/// (iOS only) Title to use for text input for actions that prompt
|
|
pub text_input_button_title: Option<String>,
|
|
|
|
// TODO: make this written as textInputPlaceholder
|
|
/// (iOS only) Placeholder to use for text input for actions that prompt
|
|
pub text_input_placeholder: Option<String>,
|
|
|
|
// TODO: proper type
|
|
/// (iOS only) icon to use for the notification
|
|
pub icon: Option<String>,
|
|
}
|
|
|
|
#[derive(Debug, Clone, Default, EnumString, strum::Display)]
|
|
#[strum(serialize_all = "snake_case", suffix = "_stream")]
|
|
pub enum MediaStream {
|
|
Alarm,
|
|
Call,
|
|
Dtmf,
|
|
#[default]
|
|
Music,
|
|
Notification,
|
|
Ring,
|
|
System,
|
|
}
|
|
|
|
// TODO: replace with a derive(DisplayToPy) (analogous to serde_with::SerializeDisplay) once I make one
|
|
impl<'py> IntoPyObject<'py> for MediaStream {
|
|
type Target = PyString;
|
|
type Output = Bound<'py, Self::Target>;
|
|
type Error = Infallible;
|
|
|
|
fn into_pyobject(self, py: Python<'py>) -> Result<Self::Output, Self::Error> {
|
|
let s = self.to_string();
|
|
s.into_pyobject(py)
|
|
}
|
|
}
|
|
|
|
#[derive(Debug, Default, Clone, IntoPyObject, typed_builder::TypedBuilder)]
|
|
#[builder(field_defaults(default, setter(strip_option(fallback_suffix = "_option"))))]
|
|
pub struct NotifyMobileAppServiceDataData {
|
|
actions: Option<Vec<Action>>,
|
|
media_stream: Option<MediaStream>,
|
|
tts_text: Option<String>,
|
|
visibility: Option<Visibility>,
|
|
}
|
|
|
|
#[derive(Debug, Clone, IntoPyObject, typed_builder::TypedBuilder)]
|
|
#[builder(field_defaults(default, setter(strip_option(fallback_suffix = "_option"))))]
|
|
pub struct NotifyMobileAppServiceData {
|
|
#[builder(!default, setter(!strip_option))]
|
|
message: String,
|
|
|
|
title: Option<String>,
|
|
#[builder(setter(!strip_option))]
|
|
data: NotifyMobileAppServiceDataData,
|
|
}
|