diff --git a/linux-rust/src/devices/enums.rs b/linux-rust/src/devices/enums.rs index c5c5913..766598f 100644 --- a/linux-rust/src/devices/enums.rs +++ b/linux-rust/src/devices/enums.rs @@ -103,6 +103,7 @@ impl AirPodsNoiseControlMode { #[derive(Clone, Debug)] pub struct NothingState { pub anc_mode: NothingAncMode, + pub anc_mode_state: combo_box::State, } #[derive(Clone, Debug)] diff --git a/linux-rust/src/ui/nothing.rs b/linux-rust/src/ui/nothing.rs index 4a7c196..f40e70b 100644 --- a/linux-rust/src/ui/nothing.rs +++ b/linux-rust/src/ui/nothing.rs @@ -2,14 +2,20 @@ use std::collections::HashMap; use std::sync::Arc; use iced::{Background, Border, Length, Theme}; use iced::widget::{container, text, column, row, Space}; -use crate::bluetooth::att::ATTManager; -use crate::devices::enums::{DeviceData, DeviceInformation, NothingState}; +use iced::widget::combo_box; +use iced::border::Radius; +use iced::overlay::menu; +use iced::widget::text_input; +use tokio::runtime::Runtime; +use std::thread; +use crate::bluetooth::att::{ATTManager, ATTHandles}; +use crate::devices::enums::{DeviceData, DeviceInformation, NothingState, DeviceState}; use crate::ui::window::Message; pub fn nothing_view<'a>( - mac: &str, + mac: &'a str, devices_list: &HashMap, - state: &NothingState, + state: &'a NothingState, att_manager: Arc ) -> iced::widget::Container<'a, Message> { let mut information_col = iced::widget::column![]; @@ -53,13 +59,116 @@ pub fn nothing_view<'a>( ); } } + + let noise_control_mode = container(row![ + text("Noise Control Mode").size(16).style( + |theme: &Theme| { + let mut style = text::Style::default(); + style.color = Some(theme.palette().text); + style + } + ), + Space::with_width(Length::Fill), + { + let state_clone = state.clone(); + let mac = mac.clone(); + let att_manager_clone = att_manager.clone(); + combo_box( + &state.anc_mode_state, + "Select Noise Control Mode", + Some(&state.anc_mode.clone()), + { + move |selected_mode| { + let att_manager = att_manager_clone.clone(); + let selected_mode_c = selected_mode.clone(); + let mac_s = mac.clone(); + run_async_in_thread( + async move { + if let Err(e) = att_manager.write( + ATTHandles::NothingEverything, + &[ + 0x55, + 0x60, 0x01, + 0x0F, 0xF0, + 0x03, 0x00, + 0x00, 0x01, + selected_mode_c.to_byte(), 0x00, + 0x00, 0x00 + ] + ).await { + log::error!("Failed to set noise cancellation mode for device {}: {}", mac_s, e); + } + } + ); + let mut state = state_clone.clone(); + state.anc_mode = selected_mode.clone(); + Message::StateChanged(mac.to_string(), DeviceState::Nothing(state)) + } + } + ) + .width(Length::from(200)) + .input_style( + |theme: &Theme, _status| { + text_input::Style { + background: Background::Color(theme.palette().primary.scale_alpha(0.2)), + border: Border { + width: 1.0, + color: theme.palette().text.scale_alpha(0.3), + radius: Radius::from(4.0) + }, + icon: Default::default(), + placeholder: theme.palette().text, + value: theme.palette().text, + selection: Default::default(), + } + } + ) + .padding(iced::Padding{ + top: 5.0, + bottom: 5.0, + left: 10.0, + right: 10.0, + }) + .menu_style( + |theme: &Theme| { + menu::Style { + background: Background::Color(theme.palette().background), + border: Border { + width: 1.0, + color: theme.palette().text, + radius: Radius::from(4.0) + }, + text_color: theme.palette().text, + selected_text_color: theme.palette().text, + selected_background: Background::Color(theme.palette().primary.scale_alpha(0.3)), + } + } + ) + } + ] + .align_y(iced::Alignment::Center) + ) + .padding(iced::Padding{ + top: 5.0, + bottom: 5.0, + left: 18.0, + right: 18.0, + }) + .style( + |theme: &Theme| { + let mut style = container::Style::default(); + style.background = Some(Background::Color(theme.palette().primary.scale_alpha(0.1))); + let mut border = Border::default(); + border.color = theme.palette().primary.scale_alpha(0.5); + style.border = border.rounded(16); + style + } + ); + container( column![ - row![ - text("Noise Control Mode").size(18), - Space::with_width(Length::Fill), - // combobox here - ], + noise_control_mode, + Space::with_height(Length::from(20)), container(information_col) .style( |theme: &Theme| { @@ -79,18 +188,12 @@ pub fn nothing_view<'a>( .height(Length::Fill) } - -// if let Err(e) = manager.write( -// ATTHandles::NothingEverything, -// &[ -// 0x55, -// 0x60, 0x01, -// 0x0F, 0xF0, -// 0x03, 0x00, -// 0x00, 0x01, // the 0x00 is an incremental counter, but it works without it -// mode.to_byte(), 0x00, -// 0x00, 0x00 // these both bytes were something random, 0 works too -// ] -// ).await { -// log::error!("Failed to set noise cancellation mode for device {}: {}", mac, e); -// } \ No newline at end of file +fn run_async_in_thread(fut: F) +where + F: Future + Send + 'static, +{ + thread::spawn(move || { + let rt = Runtime::new().unwrap(); + rt.block_on(fut); + }); +} diff --git a/linux-rust/src/ui/window.rs b/linux-rust/src/ui/window.rs index aa41199..f92a604 100644 --- a/linux-rust/src/ui/window.rs +++ b/linux-rust/src/ui/window.rs @@ -341,6 +341,14 @@ impl App { Some(DeviceType::Nothing) => { self.device_states.insert(mac.clone(), DeviceState::Nothing(NothingState { anc_mode: NothingAncMode::Off, + anc_mode_state: combo_box::State::new(vec![ + NothingAncMode::Off, + NothingAncMode::Transparency, + NothingAncMode::AdaptiveNoiseCancellation, + NothingAncMode::LowNoiseCancellation, + NothingAncMode::MidNoiseCancellation, + NothingAncMode::HighNoiseCancellation + ]), })); } _ => {} @@ -444,6 +452,9 @@ impl App { } BluetoothUIMessage::ATTNotification(mac, handle, value) => { debug!("ATT Notification for {}: handle=0x{:04X}, value={:?}", mac, handle, value); + + // TODO: Handle Nothing's ANC Mode changes here + let ui_rx = Arc::clone(&self.ui_rx); let wait_task = Task::perform( wait_for_message(ui_rx),