Merge remote-tracking branch 'origin/master'
This commit is contained in:
commit
88902ec298
4 changed files with 115 additions and 4 deletions
|
|
@ -37,6 +37,10 @@ enum Dialog {
|
||||||
device: String,
|
device: String,
|
||||||
pincode: String,
|
pincode: String,
|
||||||
},
|
},
|
||||||
|
RenameDevice {
|
||||||
|
path: OwnedObjectPath,
|
||||||
|
name: String,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
|
|
@ -353,6 +357,29 @@ impl page::Page<crate::pages::Message> for Page {
|
||||||
|
|
||||||
Some(dialog)
|
Some(dialog)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Dialog::RenameDevice { name, .. } => {
|
||||||
|
let is_valid = is_valid_bluetooth_alias(name);
|
||||||
|
let input = widget::text_input("", name)
|
||||||
|
.on_input(|value| Message::RenameDeviceInput(value))
|
||||||
|
.on_submit(|_| Message::RenameDeviceConfirm);
|
||||||
|
|
||||||
|
let rename_button =
|
||||||
|
widget::button::suggested(fl!("rename")).on_press_maybe(is_valid.then_some(Message::RenameDeviceConfirm));
|
||||||
|
|
||||||
|
let cancel_button =
|
||||||
|
widget::button::standard(fl!("cancel")).on_press(Message::RenameDeviceCancel);
|
||||||
|
|
||||||
|
let dialog = widget::dialog()
|
||||||
|
.title(fl!("bluetooth-rename-device"))
|
||||||
|
.control(input)
|
||||||
|
.primary_action(rename_button)
|
||||||
|
.secondary_action(cancel_button)
|
||||||
|
.apply(Element::from)
|
||||||
|
.map(Into::into);
|
||||||
|
|
||||||
|
Some(dialog)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -369,6 +396,10 @@ pub enum Message {
|
||||||
PinConfirm,
|
PinConfirm,
|
||||||
PopupDevice(Option<OwnedObjectPath>),
|
PopupDevice(Option<OwnedObjectPath>),
|
||||||
PopupSetting(bool),
|
PopupSetting(bool),
|
||||||
|
RenameDevice(OwnedObjectPath),
|
||||||
|
RenameDeviceInput(String),
|
||||||
|
RenameDeviceConfirm,
|
||||||
|
RenameDeviceCancel,
|
||||||
SelectAdapter(Option<OwnedObjectPath>),
|
SelectAdapter(Option<OwnedObjectPath>),
|
||||||
ServiceActivate,
|
ServiceActivate,
|
||||||
ServiceEnable,
|
ServiceEnable,
|
||||||
|
|
@ -629,6 +660,10 @@ impl Page {
|
||||||
|
|
||||||
_ => (),
|
_ => (),
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
Event::DeviceRenameFailed(path) => {
|
||||||
|
tracing::warn!("Failed to rename device {path}");
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
@ -836,6 +871,38 @@ impl Page {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Message::RenameDevice(path) => {
|
||||||
|
self.model.popup_device = None;
|
||||||
|
let name = self
|
||||||
|
.model
|
||||||
|
.devices
|
||||||
|
.get(&path)
|
||||||
|
.map_or_else(String::new, |d| d.alias_or_addr().to_owned());
|
||||||
|
self.dialog = Some(Dialog::RenameDevice { path, name });
|
||||||
|
}
|
||||||
|
|
||||||
|
Message::RenameDeviceInput(new_name) => {
|
||||||
|
if let Some(Dialog::RenameDevice { name, .. }) = &mut self.dialog {
|
||||||
|
*name = new_name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Message::RenameDeviceCancel => {
|
||||||
|
if matches!(self.dialog, Some(Dialog::RenameDevice { .. })) {
|
||||||
|
self.dialog = None;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Message::RenameDeviceConfirm => {
|
||||||
|
if let Some(Dialog::RenameDevice { path, name }) = self.dialog.take() {
|
||||||
|
if let Some(connection) = self.connection.clone() {
|
||||||
|
return cosmic::task::future(rename_device(connection, path, name.trim().into()));
|
||||||
|
} else {
|
||||||
|
tracing::warn!("No DBus connection ready");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Message::ServiceActivate => {
|
Message::ServiceActivate => {
|
||||||
let activate_future = self.service_manager.activate();
|
let activate_future = self.service_manager.activate();
|
||||||
return cosmic::task::future(async move {
|
return cosmic::task::future(async move {
|
||||||
|
|
@ -934,6 +1001,11 @@ fn status() -> Section<crate::pages::Message> {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn is_valid_bluetooth_alias(name: &str) -> bool {
|
||||||
|
let trimmed = name.trim();
|
||||||
|
!trimmed.is_empty() && trimmed.len() <= 248
|
||||||
|
}
|
||||||
|
|
||||||
fn popup_button(message: Option<Message>, text: &str) -> Element<'_, Message> {
|
fn popup_button(message: Option<Message>, text: &str) -> Element<'_, Message> {
|
||||||
let spacing = theme::spacing();
|
let spacing = theme::spacing();
|
||||||
widget::text::body(text)
|
widget::text::body(text)
|
||||||
|
|
@ -954,6 +1026,7 @@ fn connected_devices() -> Section<crate::pages::Message> {
|
||||||
device_connect = fl!("bluetooth", "connect");
|
device_connect = fl!("bluetooth", "connect");
|
||||||
device_disconnect = fl!("bluetooth", "disconnect");
|
device_disconnect = fl!("bluetooth", "disconnect");
|
||||||
device_forget = fl!("bluetooth", "forget");
|
device_forget = fl!("bluetooth", "forget");
|
||||||
|
device_rename = fl!("rename");
|
||||||
});
|
});
|
||||||
|
|
||||||
Section::default()
|
Section::default()
|
||||||
|
|
@ -991,13 +1064,17 @@ fn connected_devices() -> Section<crate::pages::Message> {
|
||||||
.position(widget::popover::Position::Bottom)
|
.position(widget::popover::Position::Bottom)
|
||||||
.on_close(Message::PopupDevice(None))
|
.on_close(Message::PopupDevice(None))
|
||||||
.popup(
|
.popup(
|
||||||
widget::column::with_capacity(2)
|
widget::column::with_capacity(3)
|
||||||
.push_maybe(device.is_connected().then(|| {
|
.push_maybe(device.is_connected().then(|| {
|
||||||
popup_button(
|
popup_button(
|
||||||
Some(Message::DisconnectDevice(path.clone())),
|
Some(Message::DisconnectDevice(path.clone())),
|
||||||
&descriptions[device_disconnect],
|
&descriptions[device_disconnect],
|
||||||
)
|
)
|
||||||
}))
|
}))
|
||||||
|
.push(popup_button(
|
||||||
|
Some(Message::RenameDevice(path.clone())),
|
||||||
|
&descriptions[device_rename],
|
||||||
|
))
|
||||||
.push_maybe(device.paired.then(|| {
|
.push_maybe(device.paired.then(|| {
|
||||||
popup_button(
|
popup_button(
|
||||||
Some(Message::ForgetDevice(path.clone())),
|
Some(Message::ForgetDevice(path.clone())),
|
||||||
|
|
|
||||||
3
i18n/en/cosmic_settings.ftl
vendored
3
i18n/en/cosmic_settings.ftl
vendored
|
|
@ -233,6 +233,7 @@ online-accounts = Online accounts
|
||||||
activate = Activate
|
activate = Activate
|
||||||
confirm = Confirm
|
confirm = Confirm
|
||||||
enable = Enable
|
enable = Enable
|
||||||
|
rename = Rename
|
||||||
|
|
||||||
bluetooth = Bluetooth
|
bluetooth = Bluetooth
|
||||||
.status = This system is visible as { $aliases } while Bluetooth settings are open.
|
.status = This system is visible as { $aliases } while Bluetooth settings are open.
|
||||||
|
|
@ -260,6 +261,8 @@ bluetooth-display-passkey = Bluetooth Pairing
|
||||||
bluetooth-display-pin = Bluetooth Pairing
|
bluetooth-display-pin = Bluetooth Pairing
|
||||||
.description = Please type the following PIN on { $device }, then press Enter
|
.description = Please type the following PIN on { $device }, then press Enter
|
||||||
|
|
||||||
|
bluetooth-rename-device = Rename Device
|
||||||
|
|
||||||
bluetooth-available = Nearby devices
|
bluetooth-available = Nearby devices
|
||||||
|
|
||||||
bluetooth-adapters = Bluetooth adapters
|
bluetooth-adapters = Bluetooth adapters
|
||||||
|
|
|
||||||
|
|
@ -32,7 +32,7 @@ fn device_type_to_icon(device_type: &str) -> &'static str {
|
||||||
|
|
||||||
#[derive(Default, Debug, Clone)]
|
#[derive(Default, Debug, Clone)]
|
||||||
pub struct Device {
|
pub struct Device {
|
||||||
alias: Option<String>,
|
pub alias: Option<String>,
|
||||||
pub address: String,
|
pub address: String,
|
||||||
pub adapter: OwnedObjectPath,
|
pub adapter: OwnedObjectPath,
|
||||||
pub enabled: Active,
|
pub enabled: Active,
|
||||||
|
|
@ -46,7 +46,7 @@ impl Device {
|
||||||
let (address, adapter, alias) = join!(
|
let (address, adapter, alias) = join!(
|
||||||
proxy.device.address(),
|
proxy.device.address(),
|
||||||
proxy.device.adapter(),
|
proxy.device.adapter(),
|
||||||
proxy.device.name()
|
proxy.device.alias()
|
||||||
);
|
);
|
||||||
let address = address?;
|
let address = address?;
|
||||||
if address.is_empty() {
|
if address.is_empty() {
|
||||||
|
|
@ -56,7 +56,10 @@ impl Device {
|
||||||
if adapter.is_empty() {
|
if adapter.is_empty() {
|
||||||
return Err(zbus::Error::Failure("Device has no adapter".to_owned()));
|
return Err(zbus::Error::Failure("Device has no adapter".to_owned()));
|
||||||
}
|
}
|
||||||
let alias = alias.ok();
|
let alias = match alias {
|
||||||
|
Ok(alias) if !alias.is_empty() && alias != address.replace(':', "-") => Some(alias),
|
||||||
|
_ => proxy.device.name().await.ok(),
|
||||||
|
};
|
||||||
let device_type: String = proxy.icon().await;
|
let device_type: String = proxy.icon().await;
|
||||||
let paired = proxy.device.paired().await.unwrap_or(false);
|
let paired = proxy.device.paired().await.unwrap_or(false);
|
||||||
let enabled = if proxy.device.connected().await.unwrap_or(false) {
|
let enabled = if proxy.device.connected().await.unwrap_or(false) {
|
||||||
|
|
@ -309,6 +312,33 @@ pub async fn forget_device(connection: zbus::Connection, device_path: OwnedObjec
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn rename_device(
|
||||||
|
connection: zbus::Connection,
|
||||||
|
device_path: OwnedObjectPath,
|
||||||
|
name: String,
|
||||||
|
) -> Event {
|
||||||
|
let proxy = match bluez_zbus::get_device(&connection, device_path.clone()).await {
|
||||||
|
Err(why) => {
|
||||||
|
tracing::error!("Unable to get the device: {why}");
|
||||||
|
return Event::DeviceFailed(device_path);
|
||||||
|
}
|
||||||
|
Ok(proxy) => proxy,
|
||||||
|
};
|
||||||
|
|
||||||
|
for attempt in 1..5 {
|
||||||
|
let result = proxy.device.set_alias(&name).await;
|
||||||
|
|
||||||
|
if let Err(why) = result {
|
||||||
|
tracing::warn!("Unable to rename device: {why}");
|
||||||
|
tokio::time::sleep(Duration::from_millis(1000 * attempt)).await;
|
||||||
|
} else {
|
||||||
|
return Event::Ok;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Event::DeviceRenameFailed(device_path)
|
||||||
|
}
|
||||||
|
|
||||||
pub async fn get_devices(connection: zbus::Connection, adapter_path: OwnedObjectPath) -> Event {
|
pub async fn get_devices(connection: zbus::Connection, adapter_path: OwnedObjectPath) -> Event {
|
||||||
// TODO error handling
|
// TODO error handling
|
||||||
let result: zbus::Result<HashMap<OwnedObjectPath, Device>> = async {
|
let result: zbus::Result<HashMap<OwnedObjectPath, Device>> = async {
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,7 @@ pub enum Event {
|
||||||
DBusError(zbus::Error),
|
DBusError(zbus::Error),
|
||||||
DBusServiceUnknown,
|
DBusServiceUnknown,
|
||||||
DeviceFailed(OwnedObjectPath),
|
DeviceFailed(OwnedObjectPath),
|
||||||
|
DeviceRenameFailed(OwnedObjectPath),
|
||||||
Ok,
|
Ok,
|
||||||
NameHasNoOwner,
|
NameHasNoOwner,
|
||||||
RemovedAdapter(OwnedObjectPath),
|
RemovedAdapter(OwnedObjectPath),
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue