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
+9 -13
View File
@@ -619,7 +619,7 @@ impl AACPManager {
} }
strings.remove(0); strings.remove(0);
let info = AirPodsInformation { 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(), model_number: strings.get(1).cloned().unwrap_or_default(),
manufacturer: strings.get(2).cloned().unwrap_or_default(), manufacturer: strings.get(2).cloned().unwrap_or_default(),
serial_number: strings.get(3).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; let mut state = self.state.lock().await;
if let Some(mac) = state.airpods_mac { if let Some(mac) = state.airpods_mac
if let Some(device_data) = state.devices.get_mut(&mac.to_string()) { && let Some(device_data) = state.devices.get_mut(&mac.to_string()) {
device_data.name = info.name.clone(); device_data.name = info.name.clone();
device_data.information = Some(DeviceInformation::AirPods(info.clone())); device_data.information = Some(DeviceInformation::AirPods(info.clone()));
} }
}
let json = serde_json::to_string(&state.devices).unwrap(); let json = serde_json::to_string(&state.devices).unwrap();
if let Some(parent) = get_devices_path().parent() { if let Some(parent) = get_devices_path().parent()
if let Err(e) = tokio::fs::create_dir_all(&parent).await { && let Err(e) = tokio::fs::create_dir_all(&parent).await {
error!("Failed to create directory for devices: {}", e); error!("Failed to create directory for devices: {}", e);
return; return;
} }
}
if let Err(e) = tokio::fs::write(&get_devices_path(), json).await { if let Err(e) = tokio::fs::write(&get_devices_path(), json).await {
error!("Failed to save devices: {}", e); 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<_>>()); info!("Received Proximity Keys Response: {:?}", keys.iter().map(|(kt, kd)| (kt, hex::encode(kd))).collect::<Vec<_>>());
let mut state = self.state.lock().await; let mut state = self.state.lock().await;
for (key_type, key_data) in &keys { for (key_type, key_data) in &keys {
if let Some(kt) = ProximityKeyType::from_u8(*key_type) { if let Some(kt) = ProximityKeyType::from_u8(*key_type)
if let Some(mac) = state.airpods_mac { && let Some(mac) = state.airpods_mac {
let mac_str = mac.to_string(); let mac_str = mac.to_string();
let device_data = state.devices.entry(mac_str.clone()).or_insert(DeviceData { let device_data = state.devices.entry(mac_str.clone()).or_insert(DeviceData {
name: mac_str.clone(), name: mac_str.clone(),
@@ -714,15 +712,13 @@ impl AACPManager {
} }
} }
} }
}
} }
let json = serde_json::to_string(&state.devices).unwrap(); let json = serde_json::to_string(&state.devices).unwrap();
if let Some(parent) = get_devices_path().parent() { if let Some(parent) = get_devices_path().parent()
if let Err(e) = tokio::fs::create_dir_all(&parent).await { && let Err(e) = tokio::fs::create_dir_all(&parent).await {
error!("Failed to create directory for devices: {}", e); error!("Failed to create directory for devices: {}", e);
return; return;
} }
}
if let Err(e) = tokio::fs::write(&get_devices_path(), json).await { if let Err(e) = tokio::fs::write(&get_devices_path(), json).await {
error!("Failed to save devices: {}", e); error!("Failed to save devices: {}", e);
} }
+4 -7
View File
@@ -8,15 +8,12 @@ pub(crate) async fn find_connected_airpods(adapter: &Adapter) -> bluer::Result<b
let addrs = adapter.device_addresses().await?; let addrs = adapter.device_addresses().await?;
for addr in addrs { for addr in addrs {
let device = adapter.device(addr)?; let device = adapter.device(addr)?;
if device.is_connected().await.unwrap_or(false) { if device.is_connected().await.unwrap_or(false)
if let Ok(uuids) = device.uuids().await { && let Ok(uuids) = device.uuids().await
if let Some(uuids) = uuids { && let Some(uuids) = uuids
if uuids.iter().any(|u| *u == target_uuid) { && uuids.iter().any(|u| *u == target_uuid) {
return Ok(device); return Ok(device);
} }
}
}
}
} }
Err(bluer::Error::from(Error::new(std::io::ErrorKind::NotFound, "No connected AirPods found"))) Err(bluer::Error::from(Error::new(std::io::ErrorKind::NotFound, "No connected AirPods found")))
} }
+116 -128
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); debug!("Checking RPA for device: {}", addr_str);
let mut found_mac = None; let mut found_mac = None;
for (airpods_mac, device_data) in &all_devices { for (airpods_mac, device_data) in &all_devices {
if device_data.type_ == DeviceType::AirPods { if device_data.type_ == DeviceType::AirPods
if let Some(DeviceInformation::AirPods(info)) = &device_data.information { && let Some(DeviceInformation::AirPods(info)) = &device_data.information
if let Ok(irk_bytes) = hex::decode(&info.le_keys.irk) { && let Ok(irk_bytes) = hex::decode(&info.le_keys.irk)
if irk_bytes.len() == 16 { && irk_bytes.len() == 16 {
let irk: [u8; 16] = irk_bytes.as_slice().try_into().unwrap(); 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); debug!("Verifying RPA {} for airpods MAC {} with IRK {}", addr_str, airpods_mac, info.le_keys.irk);
if verify_rpa(&addr_str, &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; break;
} }
} }
}
}
}
} }
if let Some(mac) = found_mac { 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(ref mac) = matched_airpods_mac
if let Some(device_data) = all_devices.get(mac) { && let Some(device_data) = all_devices.get(mac)
if let Some(DeviceInformation::AirPods(info)) = &device_data.information { && let Some(DeviceInformation::AirPods(info)) = &device_data.information
if let Ok(enc_key_bytes) = hex::decode(&info.le_keys.enc_key) { && let Ok(enc_key_bytes) = hex::decode(&info.le_keys.enc_key)
if enc_key_bytes.len() == 16 { && enc_key_bytes.len() == 16 {
matched_enc_key = Some(enc_key_bytes.as_slice().try_into().unwrap()); matched_enc_key = Some(enc_key_bytes.as_slice().try_into().unwrap());
} }
}
}
}
}
if matched_airpods_mac.is_some() { if matched_airpods_mac.is_some() {
let mut events = dev.events().await?; 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 { while let Some(ev) = events.next().await {
match ev { match ev {
bluer::DeviceEvent::PropertyChanged(prop) => { bluer::DeviceEvent::PropertyChanged(prop) => {
match prop { if let bluer::DeviceProperty::ManufacturerData(data) = prop {
bluer::DeviceProperty::ManufacturerData(data) => { if let Some(enc_key) = &matched_enc_key
if let Some(enc_key) = &matched_enc_key { && let Some(apple_data) = data.get(&76)
if let Some(apple_data) = data.get(&76) { && apple_data.len() > 20 {
if apple_data.len() > 20 { let last_16: [u8; 16] = apple_data[apple_data.len() - 16..].try_into().unwrap();
let last_16: [u8; 16] = apple_data[apple_data.len() - 16..].try_into().unwrap(); let decrypted = decrypt(enc_key, &last_16);
let decrypted = decrypt(enc_key, &last_16); debug!("Decrypted data from airpods_mac {}: {}",
debug!("Decrypted data from airpods_mac {}: {}", matched_airpods_mac.as_ref().unwrap_or(&"unknown".to_string()),
matched_airpods_mac.as_ref().unwrap_or(&"unknown".to_string()), hex::encode(decrypted));
hex::encode(&decrypted));
let connection_state = apple_data[10] as usize; let connection_state = apple_data[10] as usize;
debug!("Connection state: {}", connection_state); debug!("Connection state: {}", connection_state);
if connection_state == 0x00 { if connection_state == 0x00 {
let pref_path = get_preferences_path(); let pref_path = get_preferences_path();
let preferences: HashMap<String, HashMap<String, bool>> = let preferences: HashMap<String, HashMap<String, bool>> =
std::fs::read_to_string(&pref_path) std::fs::read_to_string(&pref_path)
.ok() .ok()
.and_then(|s| serde_json::from_str(&s).ok()) .and_then(|s| serde_json::from_str(&s).ok())
.unwrap_or_default(); .unwrap_or_default();
let auto_connect = preferences.get(matched_airpods_mac.as_ref().unwrap()) let auto_connect = preferences.get(matched_airpods_mac.as_ref().unwrap())
.and_then(|prefs| prefs.get("autoConnect")) .and_then(|prefs| prefs.get("autoConnect"))
.copied() .copied()
.unwrap_or(true); .unwrap_or(true);
debug!("Auto-connect preference for {}: {}", matched_airpods_mac.as_ref().unwrap(), auto_connect); debug!("Auto-connect preference for {}: {}", matched_airpods_mac.as_ref().unwrap(), auto_connect);
if auto_connect { if auto_connect {
let real_address = Address::from_str(&addr_str).unwrap(); let real_address = Address::from_str(&addr_str).unwrap();
let mut cm = connecting_macs_clone.lock().await; let mut cm = connecting_macs_clone.lock().await;
if cm.contains(&real_address) { if cm.contains(&real_address) {
info!("Already connecting to {}, skipping duplicate attempt.", matched_airpods_mac.as_ref().unwrap()); info!("Already connecting to {}, skipping duplicate attempt.", matched_airpods_mac.as_ref().unwrap());
return; 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());
} }
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);
} }
}
}
_ => {}
} }
} }
} }
+1 -2
View File
@@ -1,6 +1,5 @@
use crate::bluetooth::aacp::{AACPManager, ProximityKeyType, AACPEvent, AirPodsLEKeys}; use crate::bluetooth::aacp::{AACPManager, ProximityKeyType, AACPEvent, AirPodsLEKeys};
use crate::bluetooth::aacp::ControlCommandIdentifiers; use crate::bluetooth::aacp::ControlCommandIdentifiers;
use crate::bluetooth::att::ATTManager;
use crate::media_controller::MediaController; use crate::media_controller::MediaController;
use bluer::Address; use bluer::Address;
use log::{debug, info, error}; use log::{debug, info, error};
@@ -140,7 +139,7 @@ impl AirPodsDevice {
let mc_clone_owns = media_controller.clone(); let mc_clone_owns = media_controller.clone();
tokio::spawn(async move { tokio::spawn(async move {
while let Some(value) = owns_connection_rx.recv().await { 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 { if !owns {
info!("Lost ownership, pausing media and disconnecting audio"); info!("Lost ownership, pausing media and disconnecting audio");
let controller = mc_clone_owns.lock().await; let controller = mc_clone_owns.lock().await;
+1 -1
View File
@@ -154,7 +154,7 @@ impl NothingDevice{
debug!("Serial number format unexpected from Nothing device {}: {:?}", mac_address, data); debug!("Serial number format unexpected from Nothing device {}: {:?}", mac_address, data);
} }
} }
else {}
debug!("Received data from (Nothing) device {}, data: {:?}", mac_address, data); debug!("Received data from (Nothing) device {}, data: {:?}", mac_address, data);
} }
}); });
+24 -33
View File
@@ -89,11 +89,8 @@ async fn async_main(
HashMap::new() HashMap::new()
}); });
for (mac, device_data) in devices_list.iter() { for (mac, device_data) in devices_list.iter() {
match device_data.type_ { if device_data.type_ == devices::enums::DeviceType::Nothing {
devices::enums::DeviceType::Nothing => { managed_devices_mac.push(mac.clone());
managed_devices_mac.push(mac.clone());
}
_ => {}
} }
} }
@@ -166,17 +163,14 @@ async fn async_main(
let device_managers = device_managers.clone(); let device_managers = device_managers.clone();
tokio::spawn(async move { tokio::spawn(async move {
let mut managers = device_managers.write().await; let mut managers = device_managers.write().await;
match type_ { if type_ == devices::enums::DeviceType::Nothing {
devices::enums::DeviceType::Nothing => { let dev = devices::nothing::NothingDevice::new(device.address(), ui_tx_clone.clone()).await;
let dev = devices::nothing::NothingDevice::new(device.address(), ui_tx_clone.clone()).await; let dev_managers = DeviceManagers::with_att(dev.att_manager.clone());
let dev_managers = DeviceManagers::with_att(dev.att_manager.clone()); managers
managers .entry(addr_str.clone())
.entry(addr_str.clone()) .or_insert(dev_managers)
.or_insert(dev_managers) .set_att(dev.att_manager);
.set_att(dev.att_manager); ui_tx_clone.send(BluetoothUIMessage::DeviceConnected(addr_str)).unwrap();
ui_tx_clone.send(BluetoothUIMessage::DeviceConnected(addr_str)).unwrap();
}
_ => {}
} }
drop(managers) drop(managers)
}); });
@@ -221,23 +215,20 @@ async fn async_main(
if managed_devices_mac.contains(&addr_str) { if managed_devices_mac.contains(&addr_str) {
info!("Managed device connected: {}, initializing", addr_str); info!("Managed device connected: {}, initializing", addr_str);
let type_ = devices_list.get(&addr_str).unwrap().type_.clone(); let type_ = devices_list.get(&addr_str).unwrap().type_.clone();
match type_ { if type_ == devices::enums::DeviceType::Nothing {
devices::enums::DeviceType::Nothing => { let ui_tx_clone = ui_tx.clone();
let ui_tx_clone = ui_tx.clone(); let device_managers = device_managers.clone();
let device_managers = device_managers.clone(); tokio::spawn(async move {
tokio::spawn(async move { let mut managers = device_managers.write().await;
let mut managers = device_managers.write().await; let dev = devices::nothing::NothingDevice::new(addr, ui_tx_clone.clone()).await;
let dev = devices::nothing::NothingDevice::new(addr, ui_tx_clone.clone()).await; let dev_managers = DeviceManagers::with_att(dev.att_manager.clone());
let dev_managers = DeviceManagers::with_att(dev.att_manager.clone()); managers
managers .entry(addr_str.clone())
.entry(addr_str.clone()) .or_insert(dev_managers)
.or_insert(dev_managers) .set_att(dev.att_manager);
.set_att(dev.att_manager); drop(managers);
drop(managers); ui_tx_clone.send(BluetoothUIMessage::DeviceConnected(addr_str.clone())).unwrap();
ui_tx_clone.send(BluetoothUIMessage::DeviceConnected(addr_str.clone())).unwrap(); });
});
}
_ => {}
} }
return true; return true;
} }
+42 -53
View File
@@ -176,11 +176,10 @@ impl MediaController {
} }
let proxy = conn.with_proxy(&service, "/org/mpris/MediaPlayer2", Duration::from_secs(5)); 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 let Ok(playback_status) = proxy.get::<String>("org.mpris.MediaPlayer2.Player", "PlaybackStatus")
if playback_status == "Playing" { && playback_status == "Playing" {
return true; return true;
} }
}
} }
false false
} }
@@ -257,21 +256,19 @@ impl MediaController {
let mut state = self.state.lock().await; let mut state = self.state.lock().await;
state.i_paused_the_media = false; 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 { } else {
if !old_all_out { debug!("Playing media");
debug!("Pausing media as buds are not fully in ear"); self.resume().await;
self.pause().await; {
{ let mut state = self.state.lock().await;
let mut state = self.state.lock().await; state.i_paused_the_media = false;
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;
}
} }
} }
} }
@@ -374,8 +371,8 @@ impl MediaController {
debug!("Checking playback status for service: {}", service); debug!("Checking playback status for service: {}", service);
let proxy = conn.with_proxy(&service, "/org/mpris/MediaPlayer2", Duration::from_secs(5)); 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 let Ok(playback_status) = proxy.get::<String>("org.mpris.MediaPlayer2.Player", "PlaybackStatus")
if playback_status == "Playing" { && playback_status == "Playing" {
debug!("Service {} is playing, attempting to pause", service); debug!("Service {} is playing, attempting to pause", service);
if proxy.method_call::<(), _, &str, &str>("org.mpris.MediaPlayer2.Player", "Pause", ()).is_ok() { if proxy.method_call::<(), _, &str, &str>("org.mpris.MediaPlayer2.Player", "Pause", ()).is_ok() {
info!("Paused playback for: {}", service); info!("Paused playback for: {}", service);
@@ -385,7 +382,6 @@ impl MediaController {
error!("Failed to pause {}", service); error!("Failed to pause {}", service);
} }
} }
}
} }
paused_services paused_services
}).await.unwrap(); }).await.unwrap();
@@ -423,8 +419,8 @@ impl MediaController {
debug!("Checking playback status for service: {}", service); debug!("Checking playback status for service: {}", service);
let proxy = conn.with_proxy(&service, "/org/mpris/MediaPlayer2", Duration::from_secs(5)); 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 let Ok(playback_status) = proxy.get::<String>("org.mpris.MediaPlayer2.Player", "PlaybackStatus")
if playback_status == "Playing" { && playback_status == "Playing" {
debug!("Service {} is playing, attempting to pause", service); debug!("Service {} is playing, attempting to pause", service);
if proxy.method_call::<(), _, &str, &str>("org.mpris.MediaPlayer2.Player", "Pause", ()).is_ok() { if proxy.method_call::<(), _, &str, &str>("org.mpris.MediaPlayer2.Player", "Pause", ()).is_ok() {
info!("Paused playback for: {}", service); info!("Paused playback for: {}", service);
@@ -434,7 +430,6 @@ impl MediaController {
error!("Failed to pause {}", service); error!("Failed to pause {}", service);
} }
} }
}
} }
paused_count paused_count
}).await.unwrap(); }).await.unwrap();
@@ -510,7 +505,7 @@ impl MediaController {
tokio::task::spawn_blocking(move || { tokio::task::spawn_blocking(move || {
let mut mainloop = Mainloop::new().unwrap(); 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(); context.connect(None, ContextFlagSet::NOAUTOSPAWN, None).unwrap();
loop { loop {
match mainloop.iterate(false) { match mainloop.iterate(false) {
@@ -548,17 +543,16 @@ impl MediaController {
} }
mainloop.quit(Retval(0)); mainloop.quit(Retval(0));
if let Some(list) = card_info_list.borrow().as_ref() { if let Some(list) = card_info_list.borrow().as_ref()
if let Some(card) = list.iter().find(|c| c.index == index) { && let Some(card) = list.iter().find(|c| c.index == index) {
let available = card.profiles.iter().any(|p| { 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") name.starts_with("a2dp-sink")
}) })
}); });
debug!("A2DP profile available: {}", available); debug!("A2DP profile available: {}", available);
return available; return available;
} }
}
debug!("A2DP profile not available"); debug!("A2DP profile not available");
false false
}).await.unwrap_or(false) }).await.unwrap_or(false)
@@ -604,7 +598,7 @@ impl MediaController {
let profile_name = profile.to_string(); let profile_name = profile.to_string();
tokio::task::spawn_blocking(move || { tokio::task::spawn_blocking(move || {
let mut mainloop = Mainloop::new().unwrap(); 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(); context.connect(None, ContextFlagSet::NOAUTOSPAWN, None).unwrap();
loop { loop {
match mainloop.iterate(false) { 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 card_info_list = Rc::new(RefCell::new(None));
let op = introspector.get_card_info_list({ let op = introspector.get_card_info_list({
let card_info_list = card_info_list.clone(); let card_info_list = card_info_list.clone();
@@ -642,13 +636,12 @@ impl MediaController {
} }
mainloop.quit(Retval(0)); mainloop.quit(Retval(0));
if let Some(list) = card_info_list.borrow().as_ref() { if let Some(list) = card_info_list.borrow().as_ref()
if let Some(card) = list.iter().find(|c| c.index == card_index) { && 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)); let available = card.profiles.iter().any(|p| p.name.as_ref() == Some(&profile_name));
debug!("Profile {} available: {}", profile_name, available); debug!("Profile {} available: {}", profile_name, available);
return available; return available;
} }
}
debug!("Profile {} not available", profile_name); debug!("Profile {} not available", profile_name);
false false
}).await.unwrap_or(false) }).await.unwrap_or(false)
@@ -658,7 +651,7 @@ impl MediaController {
debug!("Entering restart_wire_plumber"); debug!("Entering restart_wire_plumber");
info!("Restarting WirePlumber to rediscover A2DP profiles"); info!("Restarting WirePlumber to rediscover A2DP profiles");
let result = Command::new("systemctl") let result = Command::new("systemctl")
.args(&["--user", "restart", "wireplumber"]) .args(["--user", "restart", "wireplumber"])
.output(); .output();
match result { match result {
@@ -684,7 +677,7 @@ impl MediaController {
tokio::task::spawn_blocking(move || { tokio::task::spawn_blocking(move || {
let mut mainloop = Mainloop::new().unwrap(); 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(); context.connect(None, ContextFlagSet::NOAUTOSPAWN, None).unwrap();
loop { loop {
@@ -727,12 +720,11 @@ impl MediaController {
for card in list { for card in list {
debug!("Checking card index {} for MAC match", card.index); debug!("Checking card index {} for MAC match", card.index);
let props = &card.proplist; let props = &card.proplist;
if let Some(device_string) = props.get_str("device.string") { if let Some(device_string) = props.get_str("device.string")
if device_string.contains(&mac_clone) { && device_string.contains(&mac_clone) {
info!("Found audio device index for MAC {}: {}", mac_clone, card.index); info!("Found audio device index for MAC {}: {}", mac_clone, card.index);
return Some(card.index); return Some(card.index);
} }
}
} }
} }
error!("No matching Bluetooth card found for MAC address: {}", mac_clone); error!("No matching Bluetooth card found for MAC address: {}", mac_clone);
@@ -824,7 +816,7 @@ impl MediaController {
let original = { let original = {
let state = self.state.lock().await; let state = self.state.lock().await;
state.conv_original_volume state.conv_original_volume
}.clone(); };
if let Some(orig) = original { if let Some(orig) = original {
debug!("Conversation reduce (2). Original: {}", orig); debug!("Conversation reduce (2). Original: {}", orig);
if orig > 15 { if orig > 15 {
@@ -945,7 +937,7 @@ impl MediaController {
fn get_sink_volume_percent_by_name_sync(sink_name: &str) -> Option<u32> { fn get_sink_volume_percent_by_name_sync(sink_name: &str) -> Option<u32> {
let mut mainloop = Mainloop::new().unwrap(); 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(); context.connect(None, ContextFlagSet::NOAUTOSPAWN, None).unwrap();
loop { loop {
match mainloop.iterate(false) { 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 { fn set_card_profile_sync(card_index: u32, profile_name: &str) -> bool {
let mut mainloop = Mainloop::new().unwrap(); 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(); context.connect(None, ContextFlagSet::NOAUTOSPAWN, None).unwrap();
loop { 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 { pub fn transition_sink_volume(sink_name: &str, target_volume: u32) -> bool {
let mut mainloop = Mainloop::new().unwrap(); 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(); context.connect(None, ContextFlagSet::NOAUTOSPAWN, None).unwrap();
loop { loop {
match mainloop.iterate(false) { match mainloop.iterate(false) {
@@ -1072,7 +1064,7 @@ async fn get_sink_name_by_mac(mac: &str) -> Option<String> {
tokio::task::spawn_blocking(move || { tokio::task::spawn_blocking(move || {
let mut mainloop = Mainloop::new().unwrap(); 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(); context.connect(None, ContextFlagSet::NOAUTOSPAWN, None).unwrap();
loop { 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() { if let Some(list) = sink_info_list.borrow().as_ref() {
for sink in list { for sink in list {
if let Some(device_string) = sink.proplist.get_str("device.string") { if let Some(device_string) = sink.proplist.get_str("device.string")
if device_string.to_uppercase().contains(&mac_clone.to_uppercase()) { && device_string.to_uppercase().contains(&mac_clone.to_uppercase())
if let Some(name) = &sink.name { && let Some(name) = &sink.name {
info!("Found sink name for MAC {}: {}", mac_clone, name); info!("Found sink name for MAC {}: {}", mac_clone, name);
return Some(name.to_string()); return Some(name.to_string());
} }
}
}
if let Some(bluez_path) = sink.proplist.get_str("bluez.path") { if let Some(bluez_path) = sink.proplist.get_str("bluez.path") {
let mac_from_path = bluez_path.split('/').last().unwrap_or("").replace("dev_", "").replace('_', ":"); let mac_from_path = bluez_path.split('/').next_back().unwrap_or("").replace("dev_", "").replace('_', ":");
if mac_from_path.eq_ignore_ascii_case(&mac_clone) { if mac_from_path.eq_ignore_ascii_case(&mac_clone)
if let Some(name) = &sink.name { && let Some(name) = &sink.name {
info!("Found sink name for MAC {}: {}", mac_clone, name); info!("Found sink name for MAC {}: {}", mac_clone, name);
return Some(name.to_string()); return Some(name.to_string());
} }
}
} }
} }
} }
+2 -3
View File
@@ -20,8 +20,8 @@ pub fn nothing_view<'a>(
) -> iced::widget::Container<'a, Message> { ) -> iced::widget::Container<'a, Message> {
let mut information_col = iced::widget::column![]; let mut information_col = iced::widget::column![];
let mac = mac.to_string(); let mac = mac.to_string();
if let Some(device) = devices_list.get(mac.as_str()) { if let Some(device) = devices_list.get(mac.as_str())
if let Some(DeviceInformation::Nothing(ref nothing_info)) = device.information { && let Some(DeviceInformation::Nothing(ref nothing_info)) = device.information {
information_col = information_col information_col = information_col
.push(text("Device Information").size(18).style( .push(text("Device Information").size(18).style(
|theme: &Theme| { |theme: &Theme| {
@@ -58,7 +58,6 @@ pub fn nothing_view<'a>(
] ]
); );
} }
}
let noise_control_mode = container(row![ let noise_control_mode = container(row![
text("Noise Control Mode").size(16).style( text("Noise Control Mode").size(16).style(
+6 -9
View File
@@ -41,16 +41,14 @@ impl ksni::Tray for MyTray {
levels.push(h); levels.push(h);
} }
} else { } else {
if let Some(l) = self.battery_l { if let Some(l) = self.battery_l
if self.battery_l_status != Some(BatteryStatus::Disconnected) { && self.battery_l_status != Some(BatteryStatus::Disconnected) {
levels.push(l); levels.push(l);
} }
} if let Some(r) = self.battery_r
if let Some(r) = self.battery_r { && self.battery_r_status != Some(BatteryStatus::Disconnected) {
if self.battery_r_status != Some(BatteryStatus::Disconnected) {
levels.push(r); levels.push(r);
} }
}
// if let Some(c) = self.battery_c { // if let Some(c) = self.battery_c {
// if self.battery_c_status != Some(BatteryStatus::Disconnected) { // if self.battery_c_status != Some(BatteryStatus::Disconnected) {
// levels.push(c); // levels.push(c);
@@ -157,14 +155,13 @@ impl ksni::Tray for MyTray {
checked: self.conversation_detect_enabled.unwrap_or(false), checked: self.conversation_detect_enabled.unwrap_or(false),
enabled: self.conversation_detect_enabled.is_some(), enabled: self.conversation_detect_enabled.is_some(),
activate: Box::new(|this: &mut Self| { activate: Box::new(|this: &mut Self| {
if let Some(tx) = &this.command_tx { if let Some(tx) = &this.command_tx
if let Some(is_enabled) = this.conversation_detect_enabled { && let Some(is_enabled) = this.conversation_detect_enabled {
let new_state = !is_enabled; let new_state = !is_enabled;
let value = if !new_state { 0x02 } else { 0x01 }; let value = if !new_state { 0x02 } else { 0x01 };
let _ = tx.send((ControlCommandIdentifiers::ConversationDetectConfig, vec![value])); let _ = tx.send((ControlCommandIdentifiers::ConversationDetectConfig, vec![value]));
this.conversation_detect_enabled = Some(new_state); this.conversation_detect_enabled = Some(new_state);
} }
}
}), }),
..Default::default() ..Default::default()
} }
+31 -43
View File
@@ -1,7 +1,7 @@
use std::collections::HashMap; use std::collections::HashMap;
use iced::widget::button::Style; 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::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 std::sync::Arc;
use bluer::{Address, Session}; use bluer::{Address, Session};
use iced::border::Radius; use iced::border::Radius;
@@ -228,11 +228,11 @@ impl App {
match ui_message { match ui_message {
BluetoothUIMessage::NoOp => { BluetoothUIMessage::NoOp => {
let ui_rx = Arc::clone(&self.ui_rx); let ui_rx = Arc::clone(&self.ui_rx);
let wait_task = Task::perform(
Task::perform(
wait_for_message(ui_rx), wait_for_message(ui_rx),
|msg| msg, |msg| msg,
); )
wait_task
} }
BluetoothUIMessage::OpenWindow => { BluetoothUIMessage::OpenWindow => {
let ui_rx = Arc::clone(&self.ui_rx); let ui_rx = Arc::clone(&self.ui_rx);
@@ -315,7 +315,7 @@ impl App {
battery: state.battery_info.clone(), battery: state.battery_info.clone(),
noise_control_mode: state.control_command_status_list.iter().find_map(|status| { noise_control_mode: state.control_command_status_list.iter().find_map(|status| {
if status.identifier == ControlCommandIdentifiers::ListeningMode { if status.identifier == ControlCommandIdentifiers::ListeningMode {
status.value.get(0).map(|b| AirPodsNoiseControlMode::from_byte(b)) status.value.first().map(AirPodsNoiseControlMode::from_byte)
} else { } else {
None None
} }
@@ -394,7 +394,7 @@ impl App {
AACPEvent::ControlCommand(status) => { AACPEvent::ControlCommand(status) => {
match status.identifier { match status.identifier {
ControlCommandIdentifiers::ListeningMode => { 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) { if let Some(DeviceState::AirPods(state)) = self.device_states.get_mut(&mac) {
state.noise_control_mode = mode; state.noise_control_mode = mode;
} }
@@ -503,8 +503,8 @@ impl App {
Task::none() Task::none()
} }
Message::ConfirmAddDevice => { Message::ConfirmAddDevice => {
if let Some((name, addr)) = self.pending_add_device.take() { if let Some((name, addr)) = self.pending_add_device.take()
if let Some(type_) = self.selected_device_type.take() { && let Some(type_) = self.selected_device_type.take() {
let devices_path = get_devices_path(); let devices_path = get_devices_path();
let devices_json = std::fs::read_to_string(&devices_path).unwrap_or_else(|e| { let devices_json = std::fs::read_to_string(&devices_path).unwrap_or_else(|e| {
error!("Failed to read devices file: {}", e); error!("Failed to read devices file: {}", e);
@@ -528,7 +528,6 @@ impl App {
} }
self.selected_tab = Tab::Device(addr.to_string()); self.selected_tab = Tab::Device(addr.to_string());
} }
}
Task::none() Task::none()
} }
Message::CancelAddDevice => { Message::CancelAddDevice => {
@@ -550,26 +549,22 @@ impl App {
}); });
devices_list.get(&mac).map(|d| d.type_.clone()) devices_list.get(&mac).map(|d| d.type_.clone())
}; };
match type_ { if let Some(DeviceType::AirPods) = type_
Some(DeviceType::AirPods) => { && let Some(DeviceState::AirPods(state)) = self.device_states.get_mut(&mac) {
if let Some(DeviceState::AirPods(state)) = self.device_states.get_mut(&mac) { state.noise_control_state = combo_box::State::new(
state.noise_control_state = combo_box::State::new( {
{ let mut modes = vec![
let mut modes = vec![ AirPodsNoiseControlMode::Transparency,
AirPodsNoiseControlMode::Transparency, AirPodsNoiseControlMode::NoiseCancellation,
AirPodsNoiseControlMode::NoiseCancellation, AirPodsNoiseControlMode::Adaptive
AirPodsNoiseControlMode::Adaptive ];
]; if state.allow_off_mode {
if state.allow_off_mode { modes.insert(0, AirPodsNoiseControlMode::Off);
modes.insert(0, AirPodsNoiseControlMode::Off);
}
modes
} }
); modes
} }
);
} }
_ => {}
}
Task::none() Task::none()
} }
Message::TrayTextModeChanged(is_enabled) => { Message::TrayTextModeChanged(is_enabled) => {
@@ -788,22 +783,17 @@ impl App {
debug!("Rendering device view for {}: type={:?}, state={:?}", id, device_type, device_state); debug!("Rendering device view for {}: type={:?}, state={:?}", id, device_type, device_state);
match device_type { match device_type {
Some(DeviceType::AirPods) => { Some(DeviceType::AirPods) => {
let view = device_state.as_ref().and_then(|state| {
device_state.as_ref().and_then(|state| {
match state { match state {
DeviceState::AirPods(state) => { DeviceState::AirPods(state) => {
device_managers.get(id).and_then(|managers| { device_managers.get(id).and_then(|managers| {
managers.get_aacp().and_then(|aacp_manager| { managers.get_aacp().map(|aacp_manager| airpods_view(
// managers.get_att().map(|att_manager| {
Some(airpods_view(
id, id,
&devices_list, &devices_list,
state, state,
aacp_manager.clone() aacp_manager.clone()
), ))
// att_manager.clone(),
)
// })
})
}) })
} }
_ => None, _ => None,
@@ -814,8 +804,7 @@ impl App {
) )
.center_x(Length::Fill) .center_x(Length::Fill)
.center_y(Length::Fill) .center_y(Length::Fill)
}); })
view
} }
Some(DeviceType::Nothing) => { Some(DeviceType::Nothing) => {
if let Some(DeviceState::Nothing(state)) = device_state { if let Some(DeviceState::Nothing(state)) = device_state {
@@ -1029,14 +1018,14 @@ impl App {
} }
) )
.padding(8) .padding(8)
.on_press(Message::StartAddDevice(device.0.clone(), device.1.clone())) .on_press(Message::StartAddDevice(device.0.clone(), device.1))
.into() .into()
); );
} }
item_col = item_col.push(row(row_elements).align_y(Center)); item_col = item_col.push(row(row_elements).align_y(Center));
if let Some((_, pending_addr)) = &self.pending_add_device { if let Some((_, pending_addr)) = &self.pending_add_device
if pending_addr == &device.1 { && pending_addr == &device.1 {
item_col = item_col.push( item_col = item_col.push(
row![ row![
text("Device Type:").size(16), text("Device Type:").size(16),
@@ -1112,7 +1101,6 @@ impl App {
.width(Length::Fill) .width(Length::Fill)
); );
} }
}
list_col = list_col.push( list_col = list_col.push(
container(item_col) container(item_col)
@@ -1189,7 +1177,7 @@ async fn load_paired_devices() -> HashMap<String, Address> {
let adapter = session.default_adapter().await.ok().unwrap(); let adapter = session.default_adapter().await.ok().unwrap();
let addresses = adapter.device_addresses().await.ok().unwrap(); let addresses = adapter.device_addresses().await.ok().unwrap();
for addr in addresses { 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(); let paired = device.is_paired().await.ok().unwrap();
if paired { if paired {
let name = device.name().await.ok().flatten().unwrap_or_else(|| "Unknown".to_string()); let name = device.name().await.ok().flatten().unwrap_or_else(|| "Unknown".to_string());