use std::borrow::Cow;
use crate::{IdeviceError, ReadWrite, obf};
use super::{CoreDeviceError, CoreDeviceServiceClient};
#[derive(Debug)]
pub struct ConfigurationServiceClient<R: ReadWrite> {
inner: CoreDeviceServiceClient<R>,
}
#[cfg(feature = "rsd")]
impl crate::RsdService for ConfigurationServiceClient<Box<dyn ReadWrite>> {
fn rsd_service_name() -> Cow<'static, str> {
obf!("com.apple.coredevice.configuration")
}
async fn from_stream(stream: Box<dyn ReadWrite>) -> Result<Self, IdeviceError> {
Ok(Self {
inner: CoreDeviceServiceClient::new(stream).await?,
})
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum UserInterfaceStyle {
Light,
Dark,
}
impl UserInterfaceStyle {
pub fn as_str(self) -> &'static str {
match self {
UserInterfaceStyle::Light => "light",
UserInterfaceStyle::Dark => "dark",
}
}
fn from_wire(s: &str) -> Result<Self, IdeviceError> {
match s {
"light" => Ok(UserInterfaceStyle::Light),
"dark" => Ok(UserInterfaceStyle::Dark),
_ => Err(CoreDeviceError::MalformedField("style").into()),
}
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct ColorFilter {
pub enabled: bool,
pub filter_type: Option<String>,
pub intensity: Option<f64>,
}
impl<R: ReadWrite> ConfigurationServiceClient<R> {
pub async fn new(inner: R) -> Result<Self, IdeviceError> {
Ok(Self {
inner: CoreDeviceServiceClient::new(inner).await?,
})
}
pub async fn get_user_interface_style(&mut self) -> Result<UserInterfaceStyle, IdeviceError> {
let res = self
.invoke(obf!("com.apple.coredevice.action.getuserinterfacestyle"))
.await?;
let style = res
.as_dictionary()
.and_then(|d| d.get("style"))
.and_then(|v| v.as_string())
.ok_or(CoreDeviceError::MissingField("style"))?;
UserInterfaceStyle::from_wire(style)
}
pub async fn set_user_interface_style(
&mut self,
style: UserInterfaceStyle,
) -> Result<(), IdeviceError> {
self.invoke_with(
obf!("com.apple.coredevice.action.setuserinterfacestyle"),
crate::plist!({ "style": style.as_str() }),
)
.await?;
Ok(())
}
pub async fn set_liquid_glass_opacity(&mut self, opacity: f32) -> Result<(), IdeviceError> {
if !(0.0..=1.0).contains(&opacity) {
return Err(CoreDeviceError::InvalidArgument("opacity must be in [0.0, 1.0]").into());
}
self.invoke_with(
obf!("com.apple.coredevice.action.setliquidglassconfiguration"),
crate::plist!({ "configuration": { "opacity": opacity } }),
)
.await?;
Ok(())
}
pub async fn get_color_filter(&mut self) -> Result<ColorFilter, IdeviceError> {
let res = self
.invoke(obf!("com.apple.coredevice.action.getcolorfilter"))
.await?;
let filter = res
.as_dictionary()
.and_then(|d| d.get("colorFilter"))
.and_then(|v| v.as_dictionary())
.ok_or(CoreDeviceError::MissingField("colorFilter"))?;
Ok(ColorFilter {
enabled: filter
.get("enabled")
.and_then(|v| v.as_boolean())
.ok_or(CoreDeviceError::MissingField("enabled"))?,
filter_type: filter
.get("filterType")
.and_then(|v| v.as_dictionary())
.and_then(|d| d.get("name"))
.and_then(|v| v.as_string())
.map(str::to_string),
intensity: filter.get("intensity").and_then(|v| v.as_real()),
})
}
pub async fn set_color_filter(
&mut self,
enabled: bool,
filter_type: Option<&str>,
intensity: Option<f32>,
) -> Result<(), IdeviceError> {
let mut filter = plist::Dictionary::new();
filter.insert("enabled".into(), enabled.into());
if enabled {
let Some(filter_type) = filter_type else {
return Err(CoreDeviceError::InvalidArgument(
"filter_type is required when enabling the color filter",
)
.into());
};
filter.insert("filterType".into(), crate::plist!({ "name": filter_type }));
if let Some(intensity) = intensity {
filter.insert("intensity".into(), crate::plist!(intensity));
}
}
self.invoke_with(
obf!("com.apple.coredevice.action.setcolorfilter"),
crate::plist!({ "colorFilter": plist::Value::Dictionary(filter) }),
)
.await?;
Ok(())
}
pub async fn get_device_text_size(&mut self) -> Result<String, IdeviceError> {
let res = self
.invoke(obf!("com.apple.coredevice.action.getdevicetextsize"))
.await?;
res.as_dictionary()
.and_then(|d| d.get("textSize"))
.and_then(|v| v.as_dictionary())
.and_then(|d| d.get("size"))
.and_then(|v| v.as_dictionary())
.and_then(|d| d.keys().next())
.map(String::to_owned)
.ok_or(CoreDeviceError::MissingField("textSize").into())
}
pub async fn set_device_text_size(&mut self, size: &str) -> Result<(), IdeviceError> {
self.invoke_with(
obf!("com.apple.coredevice.action.setdevicetextsize"),
crate::plist!({ "textSize": { "size": { size: {} } } }),
)
.await?;
Ok(())
}
pub async fn get_reduce_motion(&mut self) -> Result<bool, IdeviceError> {
self.get_enabled(
obf!("com.apple.coredevice.action.getreducemotion"),
"reduceMotion",
)
.await
}
pub async fn set_reduce_motion(&mut self, enabled: bool) -> Result<(), IdeviceError> {
self.set_enabled(
obf!("com.apple.coredevice.action.setreducemotion"),
"reduceMotion",
enabled,
)
.await
}
pub async fn set_increase_contrast(&mut self, enabled: bool) -> Result<(), IdeviceError> {
self.set_enabled(
obf!("com.apple.coredevice.action.setdeviceincreasecontrast"),
"increaseContrast",
enabled,
)
.await
}
pub async fn get_show_borders(&mut self) -> Result<bool, IdeviceError> {
self.get_enabled(
obf!("com.apple.coredevice.action.getshowborders"),
"showBorders",
)
.await
}
pub async fn set_show_borders(&mut self, enabled: bool) -> Result<(), IdeviceError> {
self.set_enabled(
obf!("com.apple.coredevice.action.setshowborders"),
"showBorders",
enabled,
)
.await
}
pub async fn get_reduce_transparency(&mut self) -> Result<bool, IdeviceError> {
self.get_enabled(
obf!("com.apple.coredevice.action.getreducetransparency"),
"reduceTransparency",
)
.await
}
pub async fn set_reduce_transparency(&mut self, enabled: bool) -> Result<(), IdeviceError> {
self.set_enabled(
obf!("com.apple.coredevice.action.setreducetransparency"),
"reduceTransparency",
enabled,
)
.await
}
async fn get_enabled(
&mut self,
action: Cow<'static, str>,
knob: &'static str,
) -> Result<bool, IdeviceError> {
let res = self.invoke(action).await?;
res.as_dictionary()
.and_then(|d| d.get(knob))
.and_then(|v| v.as_dictionary())
.and_then(|d| d.get("enabled"))
.and_then(|v| v.as_boolean())
.ok_or(CoreDeviceError::MissingField(knob).into())
}
async fn set_enabled(
&mut self,
action: Cow<'static, str>,
knob: &str,
enabled: bool,
) -> Result<(), IdeviceError> {
self.invoke_with(action, crate::plist!({ knob: { "enabled": enabled } }))
.await?;
Ok(())
}
async fn invoke(&mut self, action: Cow<'static, str>) -> Result<plist::Value, IdeviceError> {
self.inner
.invoke_action_with_plist(action.to_string(), plist::Dictionary::new())
.await
}
async fn invoke_with(
&mut self,
action: Cow<'static, str>,
input: plist::Value,
) -> Result<plist::Value, IdeviceError> {
let input = input
.into_dictionary()
.ok_or(CoreDeviceError::MalformedField("(input)"))?;
self.inner
.invoke_action_with_plist(action.to_string(), input)
.await
}
}