fix(clippy): fix cargo clippy warnings

This commit is contained in:
doprz
2025-12-12 21:51:03 -06:00
committed by Kavish Devar
parent 6ded8ff3ff
commit 902b12a227
10 changed files with 236 additions and 292 deletions

View File

@@ -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::<Vec<_>>());
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);
}

View File

@@ -8,15 +8,12 @@ pub(crate) async fn find_connected_airpods(adapter: &Adapter) -> bluer::Result<b
let addrs = adapter.device_addresses().await?;
for addr in addrs {
let device = adapter.device(addr)?;
if device.is_connected().await.unwrap_or(false) {
if let Ok(uuids) = device.uuids().await {
if let Some(uuids) = uuids {
if uuids.iter().any(|u| *u == target_uuid) {
if device.is_connected().await.unwrap_or(false)
&& let Ok(uuids) = device.uuids().await
&& let Some(uuids) = uuids
&& uuids.iter().any(|u| *u == target_uuid) {
return Ok(device);
}
}
}
}
}
Err(bluer::Error::from(Error::new(std::io::ErrorKind::NotFound, "No connected AirPods found")))
}

View File

@@ -97,10 +97,10 @@ pub async fn start_le_monitor(tray_handle: Option<ksni::Handle<MyTray>>) -> 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<ksni::Handle<MyTray>>) -> blue
break;
}
}
}
}
}
}
if let Some(mac) = found_mac {
@@ -124,17 +121,13 @@ pub async fn start_le_monitor(tray_handle: Option<ksni::Handle<MyTray>>) -> 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<ksni::Handle<MyTray>>) -> 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<String, HashMap<String, bool>> =
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 <mac> 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<String, HashMap<String, bool>> =
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 <mac> 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);
}
}
}
_ => {}
}
}
}

View File

@@ -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;

View File

@@ -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);
}
});

View File

@@ -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;
}

View File

@@ -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::<String>("org.mpris.MediaPlayer2.Player", "PlaybackStatus") {
if playback_status == "Playing" {
if let Ok(playback_status) = proxy.get::<String>("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::<String>("org.mpris.MediaPlayer2.Player", "PlaybackStatus") {
if playback_status == "Playing" {
if let Ok(playback_status) = proxy.get::<String>("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::<String>("org.mpris.MediaPlayer2.Player", "PlaybackStatus") {
if playback_status == "Playing" {
if let Ok(playback_status) = proxy.get::<String>("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<u32> {
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<u32> {
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<String> {
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<String> {
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());
}
}
}
}
}

View File

@@ -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(

View File

@@ -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()
}

View File

@@ -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<String, Address> {
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());