From 902b12a22718d54845a213a89ad6c9f074ddd8b0 Mon Sep 17 00:00:00 2001 From: doprz <52579214+doprz@users.noreply.github.com> Date: Fri, 12 Dec 2025 21:51:03 -0600 Subject: [PATCH] fix(clippy): fix cargo clippy warnings --- linux-rust/src/bluetooth/aacp.rs | 22 +-- linux-rust/src/bluetooth/discovery.rs | 11 +- linux-rust/src/bluetooth/le.rs | 244 ++++++++++++-------------- linux-rust/src/devices/airpods.rs | 3 +- linux-rust/src/devices/nothing.rs | 2 +- linux-rust/src/main.rs | 57 +++--- linux-rust/src/media_controller.rs | 95 +++++----- linux-rust/src/ui/nothing.rs | 5 +- linux-rust/src/ui/tray.rs | 15 +- linux-rust/src/ui/window.rs | 74 ++++---- 10 files changed, 236 insertions(+), 292 deletions(-) diff --git a/linux-rust/src/bluetooth/aacp.rs b/linux-rust/src/bluetooth/aacp.rs index 78b79d3..b045105 100644 --- a/linux-rust/src/bluetooth/aacp.rs +++ b/linux-rust/src/bluetooth/aacp.rs @@ -619,7 +619,7 @@ impl AACPManager { } strings.remove(0); let info = AirPodsInformation { - name: strings.get(0).cloned().unwrap_or_default(), + name: strings.first().cloned().unwrap_or_default(), model_number: strings.get(1).cloned().unwrap_or_default(), manufacturer: strings.get(2).cloned().unwrap_or_default(), serial_number: strings.get(3).cloned().unwrap_or_default(), @@ -636,19 +636,17 @@ impl AACPManager { }, }; let mut state = self.state.lock().await; - if let Some(mac) = state.airpods_mac { - if let Some(device_data) = state.devices.get_mut(&mac.to_string()) { + if let Some(mac) = state.airpods_mac + && let Some(device_data) = state.devices.get_mut(&mac.to_string()) { device_data.name = info.name.clone(); device_data.information = Some(DeviceInformation::AirPods(info.clone())); } - } let json = serde_json::to_string(&state.devices).unwrap(); - if let Some(parent) = get_devices_path().parent() { - if let Err(e) = tokio::fs::create_dir_all(&parent).await { + if let Some(parent) = get_devices_path().parent() + && let Err(e) = tokio::fs::create_dir_all(&parent).await { error!("Failed to create directory for devices: {}", e); return; } - } if let Err(e) = tokio::fs::write(&get_devices_path(), json).await { error!("Failed to save devices: {}", e); } @@ -683,8 +681,8 @@ impl AACPManager { info!("Received Proximity Keys Response: {:?}", keys.iter().map(|(kt, kd)| (kt, hex::encode(kd))).collect::>()); let mut state = self.state.lock().await; for (key_type, key_data) in &keys { - if let Some(kt) = ProximityKeyType::from_u8(*key_type) { - if let Some(mac) = state.airpods_mac { + if let Some(kt) = ProximityKeyType::from_u8(*key_type) + && let Some(mac) = state.airpods_mac { let mac_str = mac.to_string(); let device_data = state.devices.entry(mac_str.clone()).or_insert(DeviceData { name: mac_str.clone(), @@ -714,15 +712,13 @@ impl AACPManager { } } } - } } let json = serde_json::to_string(&state.devices).unwrap(); - if let Some(parent) = get_devices_path().parent() { - if let Err(e) = tokio::fs::create_dir_all(&parent).await { + if let Some(parent) = get_devices_path().parent() + && let Err(e) = tokio::fs::create_dir_all(&parent).await { error!("Failed to create directory for devices: {}", e); return; } - } if let Err(e) = tokio::fs::write(&get_devices_path(), json).await { error!("Failed to save devices: {}", e); } diff --git a/linux-rust/src/bluetooth/discovery.rs b/linux-rust/src/bluetooth/discovery.rs index a0d4c78..b5f8d66 100644 --- a/linux-rust/src/bluetooth/discovery.rs +++ b/linux-rust/src/bluetooth/discovery.rs @@ -8,15 +8,12 @@ pub(crate) async fn find_connected_airpods(adapter: &Adapter) -> bluer::Result>) -> blue debug!("Checking RPA for device: {}", addr_str); let mut found_mac = None; for (airpods_mac, device_data) in &all_devices { - if device_data.type_ == DeviceType::AirPods { - if let Some(DeviceInformation::AirPods(info)) = &device_data.information { - if let Ok(irk_bytes) = hex::decode(&info.le_keys.irk) { - if irk_bytes.len() == 16 { + if device_data.type_ == DeviceType::AirPods + && let Some(DeviceInformation::AirPods(info)) = &device_data.information + && let Ok(irk_bytes) = hex::decode(&info.le_keys.irk) + && irk_bytes.len() == 16 { let irk: [u8; 16] = irk_bytes.as_slice().try_into().unwrap(); debug!("Verifying RPA {} for airpods MAC {} with IRK {}", addr_str, airpods_mac, info.le_keys.irk); if verify_rpa(&addr_str, &irk) { @@ -110,9 +110,6 @@ pub async fn start_le_monitor(tray_handle: Option>) -> blue break; } } - } - } - } } if let Some(mac) = found_mac { @@ -124,17 +121,13 @@ pub async fn start_le_monitor(tray_handle: Option>) -> blue } } - if let Some(ref mac) = matched_airpods_mac { - if let Some(device_data) = all_devices.get(mac) { - if let Some(DeviceInformation::AirPods(info)) = &device_data.information { - if let Ok(enc_key_bytes) = hex::decode(&info.le_keys.enc_key) { - if enc_key_bytes.len() == 16 { + if let Some(ref mac) = matched_airpods_mac + && let Some(device_data) = all_devices.get(mac) + && let Some(DeviceInformation::AirPods(info)) = &device_data.information + && let Ok(enc_key_bytes) = hex::decode(&info.le_keys.enc_key) + && enc_key_bytes.len() == 16 { matched_enc_key = Some(enc_key_bytes.as_slice().try_into().unwrap()); } - } - } - } - } if matched_airpods_mac.is_some() { let mut events = dev.events().await?; @@ -144,123 +137,118 @@ pub async fn start_le_monitor(tray_handle: Option>) -> blue while let Some(ev) = events.next().await { match ev { bluer::DeviceEvent::PropertyChanged(prop) => { - match prop { - bluer::DeviceProperty::ManufacturerData(data) => { - if let Some(enc_key) = &matched_enc_key { - if let Some(apple_data) = data.get(&76) { - if apple_data.len() > 20 { - let last_16: [u8; 16] = apple_data[apple_data.len() - 16..].try_into().unwrap(); - let decrypted = decrypt(enc_key, &last_16); - debug!("Decrypted data from airpods_mac {}: {}", - matched_airpods_mac.as_ref().unwrap_or(&"unknown".to_string()), - hex::encode(&decrypted)); + if let bluer::DeviceProperty::ManufacturerData(data) = prop { + if let Some(enc_key) = &matched_enc_key + && let Some(apple_data) = data.get(&76) + && apple_data.len() > 20 { + let last_16: [u8; 16] = apple_data[apple_data.len() - 16..].try_into().unwrap(); + let decrypted = decrypt(enc_key, &last_16); + debug!("Decrypted data from airpods_mac {}: {}", + matched_airpods_mac.as_ref().unwrap_or(&"unknown".to_string()), + hex::encode(decrypted)); - let connection_state = apple_data[10] as usize; - debug!("Connection state: {}", connection_state); - if connection_state == 0x00 { - let pref_path = get_preferences_path(); - let preferences: HashMap> = - std::fs::read_to_string(&pref_path) - .ok() - .and_then(|s| serde_json::from_str(&s).ok()) - .unwrap_or_default(); - let auto_connect = preferences.get(matched_airpods_mac.as_ref().unwrap()) - .and_then(|prefs| prefs.get("autoConnect")) - .copied() - .unwrap_or(true); - debug!("Auto-connect preference for {}: {}", matched_airpods_mac.as_ref().unwrap(), auto_connect); - if auto_connect { - let real_address = Address::from_str(&addr_str).unwrap(); - let mut cm = connecting_macs_clone.lock().await; - if cm.contains(&real_address) { - info!("Already connecting to {}, skipping duplicate attempt.", matched_airpods_mac.as_ref().unwrap()); - return; - } - cm.insert(real_address); - // let adapter_clone = adapter_monitor_clone.clone(); - // let real_device = adapter_clone.device(real_address).unwrap(); - info!("AirPods are disconnected, attempting to connect to {}", matched_airpods_mac.as_ref().unwrap()); - // if let Err(e) = real_device.connect().await { - // info!("Failed to connect to AirPods {}: {}", matched_airpods_mac.as_ref().unwrap(), e); - // } else { - // info!("Successfully connected to AirPods {}", matched_airpods_mac.as_ref().unwrap()); - // } - // call bluetoothctl connect for now, I don't know why bluer connect isn't working - let output = tokio::process::Command::new("bluetoothctl") - .arg("connect") - .arg(matched_airpods_mac.as_ref().unwrap()) - .output() - .await; - match output { - Ok(output) => { - if output.status.success() { - info!("Successfully connected to AirPods {}", matched_airpods_mac.as_ref().unwrap()); - cm.remove(&real_address); - } else { - let stderr = String::from_utf8_lossy(&output.stderr); - info!("Failed to connect to AirPods {}: {}", matched_airpods_mac.as_ref().unwrap(), stderr); - } - } - Err(e) => { - info!("Failed to execute bluetoothctl to connect to AirPods {}: {}", matched_airpods_mac.as_ref().unwrap(), e); - } - } - info!("Auto-connect is disabled for {}, not attempting to connect.", matched_airpods_mac.as_ref().unwrap()); + let connection_state = apple_data[10] as usize; + debug!("Connection state: {}", connection_state); + if connection_state == 0x00 { + let pref_path = get_preferences_path(); + let preferences: HashMap> = + std::fs::read_to_string(&pref_path) + .ok() + .and_then(|s| serde_json::from_str(&s).ok()) + .unwrap_or_default(); + let auto_connect = preferences.get(matched_airpods_mac.as_ref().unwrap()) + .and_then(|prefs| prefs.get("autoConnect")) + .copied() + .unwrap_or(true); + debug!("Auto-connect preference for {}: {}", matched_airpods_mac.as_ref().unwrap(), auto_connect); + if auto_connect { + let real_address = Address::from_str(&addr_str).unwrap(); + let mut cm = connecting_macs_clone.lock().await; + if cm.contains(&real_address) { + info!("Already connecting to {}, skipping duplicate attempt.", matched_airpods_mac.as_ref().unwrap()); + return; } + cm.insert(real_address); + // let adapter_clone = adapter_monitor_clone.clone(); + // let real_device = adapter_clone.device(real_address).unwrap(); + info!("AirPods are disconnected, attempting to connect to {}", matched_airpods_mac.as_ref().unwrap()); + // if let Err(e) = real_device.connect().await { + // info!("Failed to connect to AirPods {}: {}", matched_airpods_mac.as_ref().unwrap(), e); + // } else { + // info!("Successfully connected to AirPods {}", matched_airpods_mac.as_ref().unwrap()); + // } + // call bluetoothctl connect for now, I don't know why bluer connect isn't working + let output = tokio::process::Command::new("bluetoothctl") + .arg("connect") + .arg(matched_airpods_mac.as_ref().unwrap()) + .output() + .await; + match output { + Ok(output) => { + if output.status.success() { + info!("Successfully connected to AirPods {}", matched_airpods_mac.as_ref().unwrap()); + cm.remove(&real_address); + } else { + let stderr = String::from_utf8_lossy(&output.stderr); + info!("Failed to connect to AirPods {}: {}", matched_airpods_mac.as_ref().unwrap(), stderr); + } + } + Err(e) => { + info!("Failed to execute bluetoothctl to connect to AirPods {}: {}", matched_airpods_mac.as_ref().unwrap(), e); + } + } + info!("Auto-connect is disabled for {}, not attempting to connect.", matched_airpods_mac.as_ref().unwrap()); } - - let status = apple_data[5] as usize; - let primary_left = (status >> 5) & 0x01 == 1; - let this_in_case = (status >> 6) & 0x01 == 1; - let xor_factor = primary_left ^ this_in_case; - let is_left_in_ear = if xor_factor { (status & 0x02) != 0 } else { (status & 0x08) != 0 }; - let is_right_in_ear = if xor_factor { (status & 0x08) != 0 } else { (status & 0x02) != 0 }; - let is_flipped = !primary_left; - - let left_byte_index = if is_flipped { 2 } else { 1 }; - let right_byte_index = if is_flipped { 1 } else { 2 }; - - let left_byte = decrypted[left_byte_index] as i32; - let right_byte = decrypted[right_byte_index] as i32; - let case_byte = decrypted[3] as i32; - - let (left_battery, left_charging) = if left_byte == 0xff { - (0, false) - } else { - (left_byte & 0x7F, (left_byte & 0x80) != 0) - }; - let (right_battery, right_charging) = if right_byte == 0xff { - (0, false) - } else { - (right_byte & 0x7F, (right_byte & 0x80) != 0) - }; - let (case_battery, case_charging) = if case_byte == 0xff { - (0, false) - } else { - (case_byte & 0x7F, (case_byte & 0x80) != 0) - }; - - if let Some(handle) = &tray_handle_clone { - handle.update(|tray: &mut MyTray| { - tray.battery_l = if left_byte == 0xff { None } else { Some(left_battery as u8) }; - tray.battery_l_status = if left_byte == 0xff { Some(BatteryStatus::Disconnected) } else if left_charging { Some(BatteryStatus::Charging) } else { Some(BatteryStatus::NotCharging) }; - tray.battery_r = if right_byte == 0xff { None } else { Some(right_battery as u8) }; - tray.battery_r_status = if right_byte == 0xff { Some(BatteryStatus::Disconnected) } else if right_charging { Some(BatteryStatus::Charging) } else { Some(BatteryStatus::NotCharging) }; - tray.battery_c = if case_byte == 0xff { None } else { Some(case_battery as u8) }; - tray.battery_c_status = if case_byte == 0xff { Some(BatteryStatus::Disconnected) } else if case_charging { Some(BatteryStatus::Charging) } else { Some(BatteryStatus::NotCharging) }; - }).await; - } - - debug!("Battery status: Left: {}, Right: {}, Case: {}, InEar: L:{} R:{}", - if left_byte == 0xff { "disconnected".to_string() } else { format!("{}% (charging: {})", left_battery, left_charging) }, - if right_byte == 0xff { "disconnected".to_string() } else { format!("{}% (charging: {})", right_battery, right_charging) }, - if case_byte == 0xff { "disconnected".to_string() } else { format!("{}% (charging: {})", case_battery, case_charging) }, - is_left_in_ear, is_right_in_ear); } + + let status = apple_data[5] as usize; + let primary_left = (status >> 5) & 0x01 == 1; + let this_in_case = (status >> 6) & 0x01 == 1; + let xor_factor = primary_left ^ this_in_case; + let is_left_in_ear = if xor_factor { (status & 0x02) != 0 } else { (status & 0x08) != 0 }; + let is_right_in_ear = if xor_factor { (status & 0x08) != 0 } else { (status & 0x02) != 0 }; + let is_flipped = !primary_left; + + let left_byte_index = if is_flipped { 2 } else { 1 }; + let right_byte_index = if is_flipped { 1 } else { 2 }; + + let left_byte = decrypted[left_byte_index] as i32; + let right_byte = decrypted[right_byte_index] as i32; + let case_byte = decrypted[3] as i32; + + let (left_battery, left_charging) = if left_byte == 0xff { + (0, false) + } else { + (left_byte & 0x7F, (left_byte & 0x80) != 0) + }; + let (right_battery, right_charging) = if right_byte == 0xff { + (0, false) + } else { + (right_byte & 0x7F, (right_byte & 0x80) != 0) + }; + let (case_battery, case_charging) = if case_byte == 0xff { + (0, false) + } else { + (case_byte & 0x7F, (case_byte & 0x80) != 0) + }; + + if let Some(handle) = &tray_handle_clone { + handle.update(|tray: &mut MyTray| { + tray.battery_l = if left_byte == 0xff { None } else { Some(left_battery as u8) }; + tray.battery_l_status = if left_byte == 0xff { Some(BatteryStatus::Disconnected) } else if left_charging { Some(BatteryStatus::Charging) } else { Some(BatteryStatus::NotCharging) }; + tray.battery_r = if right_byte == 0xff { None } else { Some(right_battery as u8) }; + tray.battery_r_status = if right_byte == 0xff { Some(BatteryStatus::Disconnected) } else if right_charging { Some(BatteryStatus::Charging) } else { Some(BatteryStatus::NotCharging) }; + tray.battery_c = if case_byte == 0xff { None } else { Some(case_battery as u8) }; + tray.battery_c_status = if case_byte == 0xff { Some(BatteryStatus::Disconnected) } else if case_charging { Some(BatteryStatus::Charging) } else { Some(BatteryStatus::NotCharging) }; + }).await; + } + + debug!("Battery status: Left: {}, Right: {}, Case: {}, InEar: L:{} R:{}", + if left_byte == 0xff { "disconnected".to_string() } else { format!("{}% (charging: {})", left_battery, left_charging) }, + if right_byte == 0xff { "disconnected".to_string() } else { format!("{}% (charging: {})", right_battery, right_charging) }, + if case_byte == 0xff { "disconnected".to_string() } else { format!("{}% (charging: {})", case_battery, case_charging) }, + is_left_in_ear, is_right_in_ear); } - } - } - _ => {} } } } diff --git a/linux-rust/src/devices/airpods.rs b/linux-rust/src/devices/airpods.rs index e42aaf8..c6d06c0 100644 --- a/linux-rust/src/devices/airpods.rs +++ b/linux-rust/src/devices/airpods.rs @@ -1,6 +1,5 @@ use crate::bluetooth::aacp::{AACPManager, ProximityKeyType, AACPEvent, AirPodsLEKeys}; use crate::bluetooth::aacp::ControlCommandIdentifiers; -use crate::bluetooth::att::ATTManager; use crate::media_controller::MediaController; use bluer::Address; use log::{debug, info, error}; @@ -140,7 +139,7 @@ impl AirPodsDevice { let mc_clone_owns = media_controller.clone(); tokio::spawn(async move { while let Some(value) = owns_connection_rx.recv().await { - let owns = value.get(0).copied().unwrap_or(0) != 0; + let owns = value.first().copied().unwrap_or(0) != 0; if !owns { info!("Lost ownership, pausing media and disconnecting audio"); let controller = mc_clone_owns.lock().await; diff --git a/linux-rust/src/devices/nothing.rs b/linux-rust/src/devices/nothing.rs index 1246270..af4e2cc 100644 --- a/linux-rust/src/devices/nothing.rs +++ b/linux-rust/src/devices/nothing.rs @@ -154,7 +154,7 @@ impl NothingDevice{ debug!("Serial number format unexpected from Nothing device {}: {:?}", mac_address, data); } } - else {} + debug!("Received data from (Nothing) device {}, data: {:?}", mac_address, data); } }); diff --git a/linux-rust/src/main.rs b/linux-rust/src/main.rs index 4f284f4..43764d5 100644 --- a/linux-rust/src/main.rs +++ b/linux-rust/src/main.rs @@ -89,11 +89,8 @@ async fn async_main( HashMap::new() }); for (mac, device_data) in devices_list.iter() { - match device_data.type_ { - devices::enums::DeviceType::Nothing => { - managed_devices_mac.push(mac.clone()); - } - _ => {} + if device_data.type_ == devices::enums::DeviceType::Nothing { + managed_devices_mac.push(mac.clone()); } } @@ -166,17 +163,14 @@ async fn async_main( let device_managers = device_managers.clone(); tokio::spawn(async move { let mut managers = device_managers.write().await; - match type_ { - devices::enums::DeviceType::Nothing => { - let dev = devices::nothing::NothingDevice::new(device.address(), ui_tx_clone.clone()).await; - let dev_managers = DeviceManagers::with_att(dev.att_manager.clone()); - managers - .entry(addr_str.clone()) - .or_insert(dev_managers) - .set_att(dev.att_manager); - ui_tx_clone.send(BluetoothUIMessage::DeviceConnected(addr_str)).unwrap(); - } - _ => {} + if type_ == devices::enums::DeviceType::Nothing { + let dev = devices::nothing::NothingDevice::new(device.address(), ui_tx_clone.clone()).await; + let dev_managers = DeviceManagers::with_att(dev.att_manager.clone()); + managers + .entry(addr_str.clone()) + .or_insert(dev_managers) + .set_att(dev.att_manager); + ui_tx_clone.send(BluetoothUIMessage::DeviceConnected(addr_str)).unwrap(); } drop(managers) }); @@ -221,23 +215,20 @@ async fn async_main( if managed_devices_mac.contains(&addr_str) { info!("Managed device connected: {}, initializing", addr_str); let type_ = devices_list.get(&addr_str).unwrap().type_.clone(); - match type_ { - devices::enums::DeviceType::Nothing => { - let ui_tx_clone = ui_tx.clone(); - let device_managers = device_managers.clone(); - tokio::spawn(async move { - let mut managers = device_managers.write().await; - let dev = devices::nothing::NothingDevice::new(addr, ui_tx_clone.clone()).await; - let dev_managers = DeviceManagers::with_att(dev.att_manager.clone()); - managers - .entry(addr_str.clone()) - .or_insert(dev_managers) - .set_att(dev.att_manager); - drop(managers); - ui_tx_clone.send(BluetoothUIMessage::DeviceConnected(addr_str.clone())).unwrap(); - }); - } - _ => {} + if type_ == devices::enums::DeviceType::Nothing { + let ui_tx_clone = ui_tx.clone(); + let device_managers = device_managers.clone(); + tokio::spawn(async move { + let mut managers = device_managers.write().await; + let dev = devices::nothing::NothingDevice::new(addr, ui_tx_clone.clone()).await; + let dev_managers = DeviceManagers::with_att(dev.att_manager.clone()); + managers + .entry(addr_str.clone()) + .or_insert(dev_managers) + .set_att(dev.att_manager); + drop(managers); + ui_tx_clone.send(BluetoothUIMessage::DeviceConnected(addr_str.clone())).unwrap(); + }); } return true; } diff --git a/linux-rust/src/media_controller.rs b/linux-rust/src/media_controller.rs index 5e540f0..242da85 100644 --- a/linux-rust/src/media_controller.rs +++ b/linux-rust/src/media_controller.rs @@ -176,11 +176,10 @@ impl MediaController { } let proxy = conn.with_proxy(&service, "/org/mpris/MediaPlayer2", Duration::from_secs(5)); - if let Ok(playback_status) = proxy.get::("org.mpris.MediaPlayer2.Player", "PlaybackStatus") { - if playback_status == "Playing" { + if let Ok(playback_status) = proxy.get::("org.mpris.MediaPlayer2.Player", "PlaybackStatus") + && playback_status == "Playing" { return true; } - } } false } @@ -257,21 +256,19 @@ impl MediaController { let mut state = self.state.lock().await; state.i_paused_the_media = false; } + } else if !old_all_out { + debug!("Pausing media as buds are not fully in ear"); + self.pause().await; + { + let mut state = self.state.lock().await; + state.i_paused_the_media = true; + } } else { - if !old_all_out { - debug!("Pausing media as buds are not fully in ear"); - self.pause().await; - { - let mut state = self.state.lock().await; - state.i_paused_the_media = true; - } - } else { - debug!("Playing media"); - self.resume().await; - { - let mut state = self.state.lock().await; - state.i_paused_the_media = false; - } + debug!("Playing media"); + self.resume().await; + { + let mut state = self.state.lock().await; + state.i_paused_the_media = false; } } } @@ -374,8 +371,8 @@ impl MediaController { debug!("Checking playback status for service: {}", service); let proxy = conn.with_proxy(&service, "/org/mpris/MediaPlayer2", Duration::from_secs(5)); - if let Ok(playback_status) = proxy.get::("org.mpris.MediaPlayer2.Player", "PlaybackStatus") { - if playback_status == "Playing" { + if let Ok(playback_status) = proxy.get::("org.mpris.MediaPlayer2.Player", "PlaybackStatus") + && playback_status == "Playing" { debug!("Service {} is playing, attempting to pause", service); if proxy.method_call::<(), _, &str, &str>("org.mpris.MediaPlayer2.Player", "Pause", ()).is_ok() { info!("Paused playback for: {}", service); @@ -385,7 +382,6 @@ impl MediaController { error!("Failed to pause {}", service); } } - } } paused_services }).await.unwrap(); @@ -423,8 +419,8 @@ impl MediaController { debug!("Checking playback status for service: {}", service); let proxy = conn.with_proxy(&service, "/org/mpris/MediaPlayer2", Duration::from_secs(5)); - if let Ok(playback_status) = proxy.get::("org.mpris.MediaPlayer2.Player", "PlaybackStatus") { - if playback_status == "Playing" { + if let Ok(playback_status) = proxy.get::("org.mpris.MediaPlayer2.Player", "PlaybackStatus") + && playback_status == "Playing" { debug!("Service {} is playing, attempting to pause", service); if proxy.method_call::<(), _, &str, &str>("org.mpris.MediaPlayer2.Player", "Pause", ()).is_ok() { info!("Paused playback for: {}", service); @@ -434,7 +430,6 @@ impl MediaController { error!("Failed to pause {}", service); } } - } } paused_count }).await.unwrap(); @@ -510,7 +505,7 @@ impl MediaController { tokio::task::spawn_blocking(move || { let mut mainloop = Mainloop::new().unwrap(); - let mut context = Context::new(&mut mainloop, "LibrePods-is_a2dp_profile_available").unwrap(); + let mut context = Context::new(&mainloop, "LibrePods-is_a2dp_profile_available").unwrap(); context.connect(None, ContextFlagSet::NOAUTOSPAWN, None).unwrap(); loop { match mainloop.iterate(false) { @@ -548,17 +543,16 @@ impl MediaController { } mainloop.quit(Retval(0)); - if let Some(list) = card_info_list.borrow().as_ref() { - if let Some(card) = list.iter().find(|c| c.index == index) { + if let Some(list) = card_info_list.borrow().as_ref() + && let Some(card) = list.iter().find(|c| c.index == index) { let available = card.profiles.iter().any(|p| { - p.name.as_ref().map_or(false, |name| { + p.name.as_ref().is_some_and(|name| { name.starts_with("a2dp-sink") }) }); debug!("A2DP profile available: {}", available); return available; } - } debug!("A2DP profile not available"); false }).await.unwrap_or(false) @@ -604,7 +598,7 @@ impl MediaController { let profile_name = profile.to_string(); tokio::task::spawn_blocking(move || { let mut mainloop = Mainloop::new().unwrap(); - let mut context = Context::new(&mut mainloop, "LibrePods-is_profile_available").unwrap(); + let mut context = Context::new(&mainloop, "LibrePods-is_profile_available").unwrap(); context.connect(None, ContextFlagSet::NOAUTOSPAWN, None).unwrap(); loop { match mainloop.iterate(false) { @@ -614,7 +608,7 @@ impl MediaController { } } - let mut introspector = context.introspect(); + let introspector = context.introspect(); let card_info_list = Rc::new(RefCell::new(None)); let op = introspector.get_card_info_list({ let card_info_list = card_info_list.clone(); @@ -642,13 +636,12 @@ impl MediaController { } mainloop.quit(Retval(0)); - if let Some(list) = card_info_list.borrow().as_ref() { - if let Some(card) = list.iter().find(|c| c.index == card_index) { - let available = card.profiles.iter().any(|p| p.name.as_ref().map_or(false, |n| n == &profile_name)); + if let Some(list) = card_info_list.borrow().as_ref() + && let Some(card) = list.iter().find(|c| c.index == card_index) { + let available = card.profiles.iter().any(|p| p.name.as_ref() == Some(&profile_name)); debug!("Profile {} available: {}", profile_name, available); return available; } - } debug!("Profile {} not available", profile_name); false }).await.unwrap_or(false) @@ -658,7 +651,7 @@ impl MediaController { debug!("Entering restart_wire_plumber"); info!("Restarting WirePlumber to rediscover A2DP profiles"); let result = Command::new("systemctl") - .args(&["--user", "restart", "wireplumber"]) + .args(["--user", "restart", "wireplumber"]) .output(); match result { @@ -684,7 +677,7 @@ impl MediaController { tokio::task::spawn_blocking(move || { let mut mainloop = Mainloop::new().unwrap(); - let mut context = Context::new(&mut mainloop, "LibrePods-get_audio_device_index").unwrap(); + let mut context = Context::new(&mainloop, "LibrePods-get_audio_device_index").unwrap(); context.connect(None, ContextFlagSet::NOAUTOSPAWN, None).unwrap(); loop { @@ -727,12 +720,11 @@ impl MediaController { for card in list { debug!("Checking card index {} for MAC match", card.index); let props = &card.proplist; - if let Some(device_string) = props.get_str("device.string") { - if device_string.contains(&mac_clone) { + if let Some(device_string) = props.get_str("device.string") + && device_string.contains(&mac_clone) { info!("Found audio device index for MAC {}: {}", mac_clone, card.index); return Some(card.index); } - } } } error!("No matching Bluetooth card found for MAC address: {}", mac_clone); @@ -824,7 +816,7 @@ impl MediaController { let original = { let state = self.state.lock().await; state.conv_original_volume - }.clone(); + }; if let Some(orig) = original { debug!("Conversation reduce (2). Original: {}", orig); if orig > 15 { @@ -945,7 +937,7 @@ impl MediaController { fn get_sink_volume_percent_by_name_sync(sink_name: &str) -> Option { let mut mainloop = Mainloop::new().unwrap(); - let mut context = Context::new(&mut mainloop, "LibrePods-get_sink_volume").unwrap(); + let mut context = Context::new(&mainloop, "LibrePods-get_sink_volume").unwrap(); context.connect(None, ContextFlagSet::NOAUTOSPAWN, None).unwrap(); loop { match mainloop.iterate(false) { @@ -991,7 +983,7 @@ fn get_sink_volume_percent_by_name_sync(sink_name: &str) -> Option { fn set_card_profile_sync(card_index: u32, profile_name: &str) -> bool { let mut mainloop = Mainloop::new().unwrap(); - let mut context = Context::new(&mut mainloop, "LibrePods-set_card_profile").unwrap(); + let mut context = Context::new(&mainloop, "LibrePods-set_card_profile").unwrap(); context.connect(None, ContextFlagSet::NOAUTOSPAWN, None).unwrap(); loop { @@ -1015,7 +1007,7 @@ fn set_card_profile_sync(card_index: u32, profile_name: &str) -> bool { pub fn transition_sink_volume(sink_name: &str, target_volume: u32) -> bool { let mut mainloop = Mainloop::new().unwrap(); - let mut context = Context::new(&mut mainloop, "LibrePods-transition_sink_volume").unwrap(); + let mut context = Context::new(&mainloop, "LibrePods-transition_sink_volume").unwrap(); context.connect(None, ContextFlagSet::NOAUTOSPAWN, None).unwrap(); loop { match mainloop.iterate(false) { @@ -1072,7 +1064,7 @@ async fn get_sink_name_by_mac(mac: &str) -> Option { tokio::task::spawn_blocking(move || { let mut mainloop = Mainloop::new().unwrap(); - let mut context = Context::new(&mut mainloop, "LibrePods-get_sink_name_by_mac").unwrap(); + let mut context = Context::new(&mainloop, "LibrePods-get_sink_name_by_mac").unwrap(); context.connect(None, ContextFlagSet::NOAUTOSPAWN, None).unwrap(); loop { @@ -1106,22 +1098,19 @@ async fn get_sink_name_by_mac(mac: &str) -> Option { if let Some(list) = sink_info_list.borrow().as_ref() { for sink in list { - if let Some(device_string) = sink.proplist.get_str("device.string") { - if device_string.to_uppercase().contains(&mac_clone.to_uppercase()) { - if let Some(name) = &sink.name { + if let Some(device_string) = sink.proplist.get_str("device.string") + && device_string.to_uppercase().contains(&mac_clone.to_uppercase()) + && let Some(name) = &sink.name { info!("Found sink name for MAC {}: {}", mac_clone, name); return Some(name.to_string()); } - } - } if let Some(bluez_path) = sink.proplist.get_str("bluez.path") { - let mac_from_path = bluez_path.split('/').last().unwrap_or("").replace("dev_", "").replace('_', ":"); - if mac_from_path.eq_ignore_ascii_case(&mac_clone) { - if let Some(name) = &sink.name { + let mac_from_path = bluez_path.split('/').next_back().unwrap_or("").replace("dev_", "").replace('_', ":"); + if mac_from_path.eq_ignore_ascii_case(&mac_clone) + && let Some(name) = &sink.name { info!("Found sink name for MAC {}: {}", mac_clone, name); return Some(name.to_string()); } - } } } } diff --git a/linux-rust/src/ui/nothing.rs b/linux-rust/src/ui/nothing.rs index f40e70b..d02e044 100644 --- a/linux-rust/src/ui/nothing.rs +++ b/linux-rust/src/ui/nothing.rs @@ -20,8 +20,8 @@ pub fn nothing_view<'a>( ) -> iced::widget::Container<'a, Message> { let mut information_col = iced::widget::column![]; let mac = mac.to_string(); - if let Some(device) = devices_list.get(mac.as_str()) { - if let Some(DeviceInformation::Nothing(ref nothing_info)) = device.information { + if let Some(device) = devices_list.get(mac.as_str()) + && let Some(DeviceInformation::Nothing(ref nothing_info)) = device.information { information_col = information_col .push(text("Device Information").size(18).style( |theme: &Theme| { @@ -58,7 +58,6 @@ pub fn nothing_view<'a>( ] ); } - } let noise_control_mode = container(row![ text("Noise Control Mode").size(16).style( diff --git a/linux-rust/src/ui/tray.rs b/linux-rust/src/ui/tray.rs index af97130..89f96d0 100644 --- a/linux-rust/src/ui/tray.rs +++ b/linux-rust/src/ui/tray.rs @@ -41,16 +41,14 @@ impl ksni::Tray for MyTray { levels.push(h); } } else { - if let Some(l) = self.battery_l { - if self.battery_l_status != Some(BatteryStatus::Disconnected) { + if let Some(l) = self.battery_l + && self.battery_l_status != Some(BatteryStatus::Disconnected) { levels.push(l); } - } - if let Some(r) = self.battery_r { - if self.battery_r_status != Some(BatteryStatus::Disconnected) { + if let Some(r) = self.battery_r + && self.battery_r_status != Some(BatteryStatus::Disconnected) { levels.push(r); } - } // if let Some(c) = self.battery_c { // if self.battery_c_status != Some(BatteryStatus::Disconnected) { // levels.push(c); @@ -157,14 +155,13 @@ impl ksni::Tray for MyTray { checked: self.conversation_detect_enabled.unwrap_or(false), enabled: self.conversation_detect_enabled.is_some(), activate: Box::new(|this: &mut Self| { - if let Some(tx) = &this.command_tx { - if let Some(is_enabled) = this.conversation_detect_enabled { + if let Some(tx) = &this.command_tx + && let Some(is_enabled) = this.conversation_detect_enabled { let new_state = !is_enabled; let value = if !new_state { 0x02 } else { 0x01 }; let _ = tx.send((ControlCommandIdentifiers::ConversationDetectConfig, vec![value])); this.conversation_detect_enabled = Some(new_state); } - } }), ..Default::default() } diff --git a/linux-rust/src/ui/window.rs b/linux-rust/src/ui/window.rs index 45f4933..cfd2ea0 100644 --- a/linux-rust/src/ui/window.rs +++ b/linux-rust/src/ui/window.rs @@ -1,7 +1,7 @@ use std::collections::HashMap; use iced::widget::button::Style; use iced::widget::{button, column, container, pane_grid, text, Space, combo_box, row, text_input, scrollable, vertical_rule, rule, toggler}; -use iced::{daemon, window, Background, Border, Center, Color, Element, Font, Length, Padding, Size, Subscription, Task, Theme}; +use iced::{daemon, window, Background, Border, Center, Element, Font, Length, Padding, Size, Subscription, Task, Theme}; use std::sync::Arc; use bluer::{Address, Session}; use iced::border::Radius; @@ -228,11 +228,11 @@ impl App { match ui_message { BluetoothUIMessage::NoOp => { let ui_rx = Arc::clone(&self.ui_rx); - let wait_task = Task::perform( + + Task::perform( wait_for_message(ui_rx), |msg| msg, - ); - wait_task + ) } BluetoothUIMessage::OpenWindow => { let ui_rx = Arc::clone(&self.ui_rx); @@ -315,7 +315,7 @@ impl App { battery: state.battery_info.clone(), noise_control_mode: state.control_command_status_list.iter().find_map(|status| { if status.identifier == ControlCommandIdentifiers::ListeningMode { - status.value.get(0).map(|b| AirPodsNoiseControlMode::from_byte(b)) + status.value.first().map(AirPodsNoiseControlMode::from_byte) } else { None } @@ -394,7 +394,7 @@ impl App { AACPEvent::ControlCommand(status) => { match status.identifier { ControlCommandIdentifiers::ListeningMode => { - let mode = status.value.get(0).map(|b| AirPodsNoiseControlMode::from_byte(b)).unwrap_or(AirPodsNoiseControlMode::Transparency); + let mode = status.value.first().map(AirPodsNoiseControlMode::from_byte).unwrap_or(AirPodsNoiseControlMode::Transparency); if let Some(DeviceState::AirPods(state)) = self.device_states.get_mut(&mac) { state.noise_control_mode = mode; } @@ -503,8 +503,8 @@ impl App { Task::none() } Message::ConfirmAddDevice => { - if let Some((name, addr)) = self.pending_add_device.take() { - if let Some(type_) = self.selected_device_type.take() { + if let Some((name, addr)) = self.pending_add_device.take() + && let Some(type_) = self.selected_device_type.take() { let devices_path = get_devices_path(); let devices_json = std::fs::read_to_string(&devices_path).unwrap_or_else(|e| { error!("Failed to read devices file: {}", e); @@ -528,7 +528,6 @@ impl App { } self.selected_tab = Tab::Device(addr.to_string()); } - } Task::none() } Message::CancelAddDevice => { @@ -550,26 +549,22 @@ impl App { }); devices_list.get(&mac).map(|d| d.type_.clone()) }; - match type_ { - Some(DeviceType::AirPods) => { - if let Some(DeviceState::AirPods(state)) = self.device_states.get_mut(&mac) { - state.noise_control_state = combo_box::State::new( - { - let mut modes = vec![ - AirPodsNoiseControlMode::Transparency, - AirPodsNoiseControlMode::NoiseCancellation, - AirPodsNoiseControlMode::Adaptive - ]; - if state.allow_off_mode { - modes.insert(0, AirPodsNoiseControlMode::Off); - } - modes + if let Some(DeviceType::AirPods) = type_ + && let Some(DeviceState::AirPods(state)) = self.device_states.get_mut(&mac) { + state.noise_control_state = combo_box::State::new( + { + let mut modes = vec![ + AirPodsNoiseControlMode::Transparency, + AirPodsNoiseControlMode::NoiseCancellation, + AirPodsNoiseControlMode::Adaptive + ]; + if state.allow_off_mode { + modes.insert(0, AirPodsNoiseControlMode::Off); } - ); - } + modes + } + ); } - _ => {} - } Task::none() } Message::TrayTextModeChanged(is_enabled) => { @@ -788,22 +783,17 @@ impl App { debug!("Rendering device view for {}: type={:?}, state={:?}", id, device_type, device_state); match device_type { Some(DeviceType::AirPods) => { - let view = device_state.as_ref().and_then(|state| { + + device_state.as_ref().and_then(|state| { match state { DeviceState::AirPods(state) => { device_managers.get(id).and_then(|managers| { - managers.get_aacp().and_then(|aacp_manager| { - // managers.get_att().map(|att_manager| { - Some(airpods_view( + managers.get_aacp().map(|aacp_manager| airpods_view( id, &devices_list, state, aacp_manager.clone() - ), - // att_manager.clone(), - ) - // }) - }) + )) }) } _ => None, @@ -814,8 +804,7 @@ impl App { ) .center_x(Length::Fill) .center_y(Length::Fill) - }); - view + }) } Some(DeviceType::Nothing) => { if let Some(DeviceState::Nothing(state)) = device_state { @@ -1029,14 +1018,14 @@ impl App { } ) .padding(8) - .on_press(Message::StartAddDevice(device.0.clone(), device.1.clone())) + .on_press(Message::StartAddDevice(device.0.clone(), device.1)) .into() ); } item_col = item_col.push(row(row_elements).align_y(Center)); - if let Some((_, pending_addr)) = &self.pending_add_device { - if pending_addr == &device.1 { + if let Some((_, pending_addr)) = &self.pending_add_device + && pending_addr == &device.1 { item_col = item_col.push( row![ text("Device Type:").size(16), @@ -1112,7 +1101,6 @@ impl App { .width(Length::Fill) ); } - } list_col = list_col.push( container(item_col) @@ -1189,7 +1177,7 @@ async fn load_paired_devices() -> HashMap { let adapter = session.default_adapter().await.ok().unwrap(); let addresses = adapter.device_addresses().await.ok().unwrap(); for addr in addresses { - let device = adapter.device(addr.clone()).ok().unwrap(); + let device = adapter.device(addr).ok().unwrap(); let paired = device.is_paired().await.ok().unwrap(); if paired { let name = device.name().await.ok().flatten().unwrap_or_else(|| "Unknown".to_string());