use anyhow::{format_err, Error};
use serde_json::{from_value, to_value, Value};
use crate::setui::types::{IntlInfo, MicStates, NetworkType, SetUiResult};
use fidl_fuchsia_media::AudioRenderUsage;
use fidl_fuchsia_settings::{
self as fsettings, AudioMarker, AudioStreamSettingSource, AudioStreamSettings,
ConfigurationInterfaces, DeviceState, DisplayMarker, DisplaySettings, InputMarker, InputState,
IntlMarker, SetupMarker, SetupSettings, Volume,
};
use fuchsia_component::client::connect_to_protocol;
use tracing::info;
#[derive(Debug)]
pub struct SetUiFacade {
audio_proxy: Option<fsettings::AudioProxy>,
display_proxy: Option<fsettings::DisplayProxy>,
input_proxy: Option<fsettings::InputProxy>,
}
impl SetUiFacade {
pub fn new() -> SetUiFacade {
SetUiFacade { audio_proxy: None, display_proxy: None, input_proxy: None }
}
pub async fn set_network(&self, args: Value) -> Result<Value, Error> {
let network_type: NetworkType = from_value(args)?;
info!("set_network input {:?}", network_type);
let setup_service_proxy = match connect_to_protocol::<SetupMarker>() {
Ok(proxy) => proxy,
Err(e) => bail!("Failed to connect to Setup service {:?}.", e),
};
let mut settings = SetupSettings::default();
match network_type {
NetworkType::Ethernet => {
settings.enabled_configuration_interfaces = Some(ConfigurationInterfaces::ETHERNET);
}
NetworkType::Wifi => {
settings.enabled_configuration_interfaces = Some(ConfigurationInterfaces::WIFI);
}
_ => return Err(format_err!("Network type must either be ethernet or wifi.")),
}
match setup_service_proxy.set(&settings, false).await? {
Ok(_) => Ok(to_value(SetUiResult::Success)?),
Err(err) => Err(format_err!("Update network settings failed with err {:?}", err)),
}
}
pub async fn get_network_setting(&self) -> Result<Value, Error> {
let setup_service_proxy = match connect_to_protocol::<SetupMarker>() {
Ok(proxy) => proxy,
Err(e) => bail!("Failed to connect to Setup service {:?}.", e),
};
let setting = setup_service_proxy.watch().await?;
match setting.enabled_configuration_interfaces {
Some(ConfigurationInterfaces::ETHERNET) => Ok(to_value(NetworkType::Ethernet)?),
Some(ConfigurationInterfaces::WIFI) => Ok(to_value(NetworkType::Wifi)?),
_ => Ok(to_value(NetworkType::Unknown)?),
}
}
pub async fn set_intl_setting(&self, args: Value) -> Result<Value, Error> {
let intl_info: IntlInfo = from_value(args)?;
info!("Received Intl Settings Request {:?}", intl_info);
let intl_service_proxy = match connect_to_protocol::<IntlMarker>() {
Ok(proxy) => proxy,
Err(e) => bail!("Failed to connect to Intl service {:?}.", e),
};
match intl_service_proxy.set(&intl_info.into()).await? {
Ok(_) => Ok(to_value(SetUiResult::Success)?),
Err(err) => Err(format_err!("Update intl settings failed with err {:?}", err)),
}
}
pub async fn get_intl_setting(&self) -> Result<Value, Error> {
let intl_service_proxy = match connect_to_protocol::<IntlMarker>() {
Ok(proxy) => proxy,
Err(e) => bail!("Failed to connect to Intl service {:?}.", e),
};
let intl_info: IntlInfo = intl_service_proxy.watch().await?.into();
return Ok(to_value(&intl_info)?);
}
pub async fn is_mic_muted(&self) -> Result<Value, Error> {
let input_proxy = match self.input_proxy.as_ref() {
Some(proxy) => proxy.clone(),
None => match connect_to_protocol::<InputMarker>() {
Ok(proxy) => proxy,
Err(e) => bail!("isMicMuted - failed to connect to Input service {:?}.", e),
},
};
match input_proxy.watch().await?.devices {
Some(input_device) => {
let mut muted = false;
if let Some(device) = input_device
.into_iter()
.find(|device| device.device_type == Some(fsettings::DeviceType::Microphone))
{
match device.state {
Some(state) => {
muted = state.toggle_flags == Some(fsettings::ToggleStateFlags::MUTED);
}
_ => (),
}
return Ok(to_value(muted)?);
} else {
return Ok(to_value(false)?);
}
}
_ => Err(format_err!("isMicMuted - cannot read input state.")),
}
}
pub async fn set_brightness(&self, args: Value) -> Result<Value, Error> {
let brightness: f32 = from_value(args)?;
let display_proxy = match self.display_proxy.as_ref() {
Some(proxy) => proxy.clone(),
None => match connect_to_protocol::<DisplayMarker>() {
Ok(proxy) => proxy,
Err(e) => bail!("Failed to connect to Display service {:?}.", e),
},
};
let settings = DisplaySettings {
auto_brightness: Some(false),
brightness_value: Some(brightness),
..Default::default()
};
match display_proxy.set(&settings).await? {
Ok(_) => Ok(to_value(SetUiResult::Success)?),
Err(e) => Err(format_err!("SetBrightness failed with err {:?}", e)),
}
}
pub async fn set_media_volume(&self, args: Value) -> Result<Value, Error> {
let volume: f32 = from_value(args)?;
let audio_proxy = match self.audio_proxy.as_ref() {
Some(proxy) => proxy.clone(),
None => match connect_to_protocol::<AudioMarker>() {
Ok(proxy) => proxy,
Err(e) => bail!("Failed to connect to Display service {:?}.", e),
},
};
let stream_settings = AudioStreamSettings {
stream: Some(AudioRenderUsage::Media),
source: Some(AudioStreamSettingSource::User),
user_volume: Some(Volume {
level: Some(volume),
muted: Some(false),
..Default::default()
}),
..Default::default()
};
let settings =
fsettings::AudioSettings { streams: Some(vec![stream_settings]), ..Default::default() };
info!("Setting audio settings {:?}", settings);
match audio_proxy.set(&settings).await? {
Ok(_) => Ok(to_value(SetUiResult::Success)?),
Err(e) => Err(format_err!("SetVolume failed with err {:?}", e)),
}
}
pub async fn set_mic_mute(&self, args: Value) -> Result<Value, Error> {
let mic_state: MicStates = from_value(args)?;
let is_muted = self.is_mic_muted().await?.as_bool().unwrap_or(false);
let mut mute_mic: bool = false;
match mic_state {
MicStates::Muted => {
if is_muted {
return Ok(to_value(SetUiResult::Success)?);
}
mute_mic = true;
}
MicStates::Available => {
if !is_muted {
return Ok(to_value(SetUiResult::Success)?);
}
}
_ => return Err(format_err!("Mic state must either be muted or available.")),
}
let input_proxy = match self.input_proxy.as_ref() {
Some(proxy) => proxy.clone(),
None => match connect_to_protocol::<InputMarker>() {
Ok(proxy) => proxy,
Err(e) => bail!("Failed to connect to Microphone {:?}.", e),
},
};
let mic_device_name = "microphone";
let mut input_states = InputState {
name: Some(mic_device_name.to_string()),
device_type: Some(fsettings::DeviceType::Microphone),
state: Some(DeviceState {
toggle_flags: Some(fsettings::ToggleStateFlags::AVAILABLE),
..Default::default()
}),
..Default::default()
};
if mute_mic {
input_states.state = Some(DeviceState {
toggle_flags: Some(fsettings::ToggleStateFlags::MUTED),
..Default::default()
});
}
info!("SetMicMute: setting input state {:?}", input_states);
match input_proxy.set(&[input_states]).await? {
Ok(_) => Ok(to_value(SetUiResult::Success)?),
Err(e) => Err(format_err!("SetMicMute failed with err {:?}", e)),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::common_utils::test::assert_value_round_trips_as;
use crate::setui::types::MicStates::Muted;
use crate::setui::types::{HourCycle, LocaleId, TemperatureUnit};
use fidl::endpoints::create_proxy_and_stream;
use fidl_fuchsia_settings::InputDevice;
use fuchsia_async as fasync;
use futures::TryStreamExt;
use serde_json::json;
fn make_intl_info() -> IntlInfo {
return IntlInfo {
locales: Some(vec![LocaleId { id: "en-US".into() }]),
temperature_unit: Some(TemperatureUnit::Celsius),
time_zone_id: Some("UTC".into()),
hour_cycle: Some(HourCycle::H12),
};
}
#[test]
fn serde_intl_set() {
let intl_request = make_intl_info();
assert_value_round_trips_as(
intl_request,
json!(
{
"locales": [{"id": "en-US"}],
"temperature_unit":"Celsius",
"time_zone_id": "UTC",
"hour_cycle": "H12",
}),
);
}
#[fasync::run_singlethreaded(test)]
async fn test_set_brightness() {
let brightness = 0.5f32;
let (proxy, mut stream) = create_proxy_and_stream::<DisplayMarker>();
let facade =
SetUiFacade { audio_proxy: None, display_proxy: Some(proxy), input_proxy: None };
let facade_fut = async move {
assert_eq!(
facade.set_brightness(to_value(brightness).unwrap()).await.unwrap(),
to_value(SetUiResult::Success).unwrap()
);
};
let stream_fut = async move {
match stream.try_next().await {
Ok(Some(fsettings::DisplayRequest::Set { settings, responder })) => {
assert_eq!(
settings,
DisplaySettings {
auto_brightness: Some(false),
brightness_value: Some(brightness),
..Default::default()
}
);
responder.send(Ok(())).unwrap();
}
other => panic!("Unexpected stream item: {:?}", other),
}
};
futures::future::join(facade_fut, stream_fut).await;
}
#[fasync::run_singlethreaded(test)]
async fn test_set_media_volume() {
let volume = 0.5f32;
let (proxy, mut stream) = create_proxy_and_stream::<AudioMarker>();
let facade =
SetUiFacade { audio_proxy: Some(proxy), display_proxy: None, input_proxy: None };
let facade_fut = async move {
assert_eq!(
facade.set_media_volume(to_value(volume).unwrap()).await.unwrap(),
to_value(SetUiResult::Success).unwrap()
);
};
let stream_fut = async move {
match stream.try_next().await {
Ok(Some(fsettings::AudioRequest::Set { settings, responder })) => {
let mut streams = settings.streams.unwrap();
assert_eq!(1, streams.len());
assert_eq!(
streams.pop().unwrap(),
AudioStreamSettings {
stream: Some(AudioRenderUsage::Media),
source: Some(AudioStreamSettingSource::User),
user_volume: Some(Volume {
level: Some(volume),
muted: Some(false),
..Default::default()
}),
..Default::default()
}
);
responder.send(Ok(())).unwrap();
}
other => panic!("Unexpected stream item: {:?}", other),
}
};
futures::future::join(facade_fut, stream_fut).await;
}
#[fasync::run_singlethreaded(test)]
async fn test_set_mic_mute() {
let mic_state: MicStates = Muted;
let (proxy, mut stream) = create_proxy_and_stream::<InputMarker>();
let facade =
SetUiFacade { audio_proxy: None, display_proxy: None, input_proxy: Some(proxy) };
let facade_fut = async move {
assert_eq!(
facade.set_mic_mute(to_value(mic_state).unwrap()).await.unwrap(),
to_value(SetUiResult::Success).unwrap()
);
};
let input_stream_fut = async move {
match stream.try_next().await {
Ok(Some(fsettings::InputRequest::Watch { responder })) => {
let device = InputDevice {
device_name: None,
device_type: Some(fsettings::DeviceType::Microphone),
source_states: None,
mutable_toggle_state: None,
state: Some(DeviceState {
toggle_flags: Some(fsettings::ToggleStateFlags::AVAILABLE),
..Default::default()
}),
..Default::default()
};
let settings = fsettings::InputSettings {
devices: Some(vec![device]),
..Default::default()
};
responder.send(&settings).unwrap();
}
other => panic!("Unexpected Watch request: {:?}", other),
}
match stream.try_next().await {
Ok(Some(fsettings::InputRequest::Set { input_states, responder })) => {
assert_eq!(
input_states[0],
InputState {
name: Some("microphone".to_string()),
device_type: Some(fsettings::DeviceType::Microphone),
state: Some(DeviceState {
toggle_flags: Some(fsettings::ToggleStateFlags::MUTED),
..Default::default()
}),
..Default::default()
}
);
responder.send(Ok(())).unwrap();
}
other => panic!("Unexpected stream item: {:?}", other),
}
};
futures::future::join(facade_fut, input_stream_fut).await;
}
#[fasync::run_singlethreaded(test)]
async fn test_set_mic_mute_in_desired_state() {
let mic_state: MicStates = Muted;
let (proxy, mut stream) = create_proxy_and_stream::<InputMarker>();
let facade =
SetUiFacade { audio_proxy: None, display_proxy: None, input_proxy: Some(proxy) };
let facade_fut = async move {
assert_eq!(
facade.set_mic_mute(to_value(mic_state).unwrap()).await.unwrap(),
to_value(SetUiResult::Success).unwrap()
);
};
let input_stream_fut = async move {
match stream.try_next().await {
Ok(Some(fsettings::InputRequest::Watch { responder })) => {
let device = InputDevice {
device_name: None,
device_type: Some(fsettings::DeviceType::Microphone),
source_states: None,
mutable_toggle_state: None,
state: Some(DeviceState {
toggle_flags: Some(fsettings::ToggleStateFlags::MUTED),
..Default::default()
}),
..Default::default()
};
let settings = fsettings::InputSettings {
devices: Some(vec![device]),
..Default::default()
};
responder.send(&settings).unwrap();
}
other => panic!("Unexpected Watch request: {:?}", other),
}
match stream.try_next().await {
Ok(Some(fsettings::InputRequest::Set { input_states, responder: _ })) => {
panic!("Unexpected stream item: {:?}", input_states[0]);
}
_ => (),
}
};
futures::future::join(facade_fut, input_stream_fut).await;
}
#[fasync::run_singlethreaded(test)]
async fn test_is_mic_muted() {
let is_muted = true;
let (proxy, mut stream) = create_proxy_and_stream::<InputMarker>();
let facade =
SetUiFacade { audio_proxy: None, display_proxy: None, input_proxy: Some(proxy) };
let facade_fut = async move {
assert_eq!(facade.is_mic_muted().await.unwrap(), to_value(is_muted).unwrap());
};
let input_stream_fut = async move {
match stream.try_next().await {
Ok(Some(fsettings::InputRequest::Watch { responder })) => {
let device = InputDevice {
device_name: None,
device_type: Some(fsettings::DeviceType::Microphone),
source_states: None,
mutable_toggle_state: None,
state: Some(DeviceState {
toggle_flags: Some(fsettings::ToggleStateFlags::MUTED),
..Default::default()
}),
..Default::default()
};
let settings = fsettings::InputSettings {
devices: Some(vec![device]),
..Default::default()
};
responder.send(&settings).unwrap();
}
other => panic!("Unexpected Watch request: {:?}", other),
}
};
futures::future::join(facade_fut, input_stream_fut).await;
}
}