fix(bluetooth): resolve issues with bluetooth settings page
This commit is contained in:
parent
db54245aa7
commit
f16244b400
3 changed files with 194 additions and 48 deletions
2
Cargo.lock
generated
2
Cargo.lock
generated
|
|
@ -1784,7 +1784,7 @@ dependencies = [
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cosmic-settings-subscriptions"
|
name = "cosmic-settings-subscriptions"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/pop-os/cosmic-settings-subscriptions#ebcb941f8bcff9dea9877b8f4e30b5a76c0469b3"
|
source = "git+https://github.com/pop-os/cosmic-settings-subscriptions#752429e70dc5a06f28922ce159afe52002683088"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bluez-zbus",
|
"bluez-zbus",
|
||||||
"cosmic-dbus-networkmanager",
|
"cosmic-dbus-networkmanager",
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,7 @@ use slab::Slab;
|
||||||
use slotmap::SlotMap;
|
use slotmap::SlotMap;
|
||||||
use std::collections::{HashMap, HashSet};
|
use std::collections::{HashMap, HashSet};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
use std::time::Duration;
|
||||||
use zbus::zvariant::OwnedObjectPath;
|
use zbus::zvariant::OwnedObjectPath;
|
||||||
|
|
||||||
enum Dialog {
|
enum Dialog {
|
||||||
|
|
@ -46,6 +47,8 @@ pub struct Page {
|
||||||
devices: HashMap<OwnedObjectPath, Device>,
|
devices: HashMap<OwnedObjectPath, Device>,
|
||||||
// Set to true when the org.bluez dbus service is unknown.
|
// Set to true when the org.bluez dbus service is unknown.
|
||||||
bluez_service_unknown: bool,
|
bluez_service_unknown: bool,
|
||||||
|
service_is_enabled: bool,
|
||||||
|
service_is_active: bool,
|
||||||
popup_setting: bool,
|
popup_setting: bool,
|
||||||
popup_device: Option<OwnedObjectPath>,
|
popup_device: Option<OwnedObjectPath>,
|
||||||
subscription: Option<tokio::sync::oneshot::Sender<()>>,
|
subscription: Option<tokio::sync::oneshot::Sender<()>>,
|
||||||
|
|
@ -75,9 +78,10 @@ impl page::Page<crate::pages::Message> for Page {
|
||||||
cosmic::task::future(async move {
|
cosmic::task::future(async move {
|
||||||
match zbus::Connection::system().await {
|
match zbus::Connection::system().await {
|
||||||
Ok(connection) => Message::DBusConnect(connection),
|
Ok(connection) => Message::DBusConnect(connection),
|
||||||
Err(why) => Message::DBusError(why.to_string()),
|
Err(why) => Message::DBusConnectFailed(why),
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
.chain(cosmic::Task::done(Message::SelectAdapter(None).into()))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn on_leave(&mut self) -> Task<crate::pages::Message> {
|
fn on_leave(&mut self) -> Task<crate::pages::Message> {
|
||||||
|
|
@ -147,16 +151,18 @@ pub enum Message {
|
||||||
BluetoothEvent(Event),
|
BluetoothEvent(Event),
|
||||||
ConnectDevice(OwnedObjectPath),
|
ConnectDevice(OwnedObjectPath),
|
||||||
DBusConnect(zbus::Connection),
|
DBusConnect(zbus::Connection),
|
||||||
DBusError(String),
|
DBusConnectFailed(zbus::Error),
|
||||||
DisconnectDevice(OwnedObjectPath),
|
DisconnectDevice(OwnedObjectPath),
|
||||||
ForgetDevice(OwnedObjectPath),
|
ForgetDevice(OwnedObjectPath),
|
||||||
PinCancel,
|
PinCancel,
|
||||||
PinConfirm,
|
PinConfirm,
|
||||||
PopupDevice(Option<OwnedObjectPath>),
|
PopupDevice(Option<OwnedObjectPath>),
|
||||||
PopupSetting(bool),
|
PopupSetting(bool),
|
||||||
Nop,
|
|
||||||
SelectAdapter(Option<OwnedObjectPath>),
|
SelectAdapter(Option<OwnedObjectPath>),
|
||||||
|
ServiceActivate,
|
||||||
|
ServiceEnable,
|
||||||
SetActive(bool),
|
SetActive(bool),
|
||||||
|
UpdateStatus,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<Message> for crate::app::Message {
|
impl From<Message> for crate::app::Message {
|
||||||
|
|
@ -197,15 +203,28 @@ impl Page {
|
||||||
match message {
|
match message {
|
||||||
Message::BluetoothEvent(event) => match event {
|
Message::BluetoothEvent(event) => match event {
|
||||||
Event::DBusError(why) => {
|
Event::DBusError(why) => {
|
||||||
|
tracing::debug!("bluetooth dbus error {why:?}");
|
||||||
tracing::error!(
|
tracing::error!(
|
||||||
"dbus connection failed. {}",
|
"bluetooth service error {}",
|
||||||
fl!("bluetooth", "dbus-error", why = why.to_string())
|
fl!("bluetooth", "dbus-error", why = why.to_string())
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Event::NameHasNoOwner => {
|
||||||
|
self.connection = None;
|
||||||
|
self.service_is_active = false;
|
||||||
|
self.service_is_enabled = false;
|
||||||
|
if let Some(abort_handle) = self.subscription.take() {
|
||||||
|
_ = abort_handle.send(());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Event::Ok => {}
|
Event::Ok => {}
|
||||||
|
|
||||||
Event::SetDevices(devices) => {
|
Event::SetDevices(devices) => {
|
||||||
self.devices = devices;
|
self.devices = devices;
|
||||||
}
|
}
|
||||||
|
|
||||||
Event::DeviceFailed(path) => {
|
Event::DeviceFailed(path) => {
|
||||||
tracing::warn!("Failed operation on device {path}");
|
tracing::warn!("Failed operation on device {path}");
|
||||||
if let Some(device) = self.devices.get_mut(&path) {
|
if let Some(device) = self.devices.get_mut(&path) {
|
||||||
|
|
@ -219,6 +238,7 @@ impl Page {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Event::SetAdapters(adapters) => {
|
Event::SetAdapters(adapters) => {
|
||||||
self.adapters = adapters;
|
self.adapters = adapters;
|
||||||
self.update_status();
|
self.update_status();
|
||||||
|
|
@ -229,6 +249,7 @@ impl Page {
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Event::UpdatedAdapter(path, update) => {
|
Event::UpdatedAdapter(path, update) => {
|
||||||
if let Some(existing) = self.adapters.get_mut(&path) {
|
if let Some(existing) = self.adapters.get_mut(&path) {
|
||||||
tracing::debug!("Adapter {} updated: {update:#?}", existing.address);
|
tracing::debug!("Adapter {} updated: {update:#?}", existing.address);
|
||||||
|
|
@ -250,12 +271,14 @@ impl Page {
|
||||||
tracing::warn!("No DBus connection ready");
|
tracing::warn!("No DBus connection ready");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Event::UpdatedDevice(path, update) => {
|
Event::UpdatedDevice(path, update) => {
|
||||||
if let Some(existing) = self.devices.get_mut(&path) {
|
if let Some(existing) = self.devices.get_mut(&path) {
|
||||||
tracing::debug!("Device {} updated", existing.address);
|
tracing::debug!("Device {} updated", existing.address);
|
||||||
existing.update(update);
|
existing.update(update);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Event::RemovedAdapter(path) => {
|
Event::RemovedAdapter(path) => {
|
||||||
tracing::debug!("Device {path} removed");
|
tracing::debug!("Device {path} removed");
|
||||||
self.adapters.remove(&path);
|
self.adapters.remove(&path);
|
||||||
|
|
@ -263,14 +286,17 @@ impl Page {
|
||||||
self.selected_adapter = None;
|
self.selected_adapter = None;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Event::RemovedDevice(path) => {
|
Event::RemovedDevice(path) => {
|
||||||
tracing::debug!("Device {path} removed");
|
tracing::debug!("Device {path} removed");
|
||||||
self.devices.remove(&path);
|
self.devices.remove(&path);
|
||||||
}
|
}
|
||||||
|
|
||||||
Event::AddedDevice(path, device) => {
|
Event::AddedDevice(path, device) => {
|
||||||
tracing::debug!("Device {} added", device.address);
|
tracing::debug!("Device {} added", device.address);
|
||||||
self.devices.insert(path, device);
|
self.devices.insert(path, device);
|
||||||
}
|
}
|
||||||
|
|
||||||
Event::AddedAdapter(path, adapter) => {
|
Event::AddedAdapter(path, adapter) => {
|
||||||
tracing::debug!("Adapter {} added", adapter.address);
|
tracing::debug!("Adapter {} added", adapter.address);
|
||||||
self.adapters.insert(path.clone(), adapter);
|
self.adapters.insert(path.clone(), adapter);
|
||||||
|
|
@ -278,9 +304,11 @@ impl Page {
|
||||||
return cosmic::task::message(Message::SelectAdapter(Some(path)));
|
return cosmic::task::message(Message::SelectAdapter(Some(path)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Event::DBusServiceUnknown => {
|
Event::DBusServiceUnknown => {
|
||||||
self.bluez_service_unknown = true;
|
self.bluez_service_unknown = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
Event::Agent(message) => {
|
Event::Agent(message) => {
|
||||||
let Some(message) = Arc::into_inner(message) else {
|
let Some(message) = Arc::into_inner(message) else {
|
||||||
return Task::none();
|
return Task::none();
|
||||||
|
|
@ -324,6 +352,7 @@ impl Page {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
Message::PinCancel => {
|
Message::PinCancel => {
|
||||||
if let Some(Dialog::RequestConfirmation { response, .. }) = self.dialog.take() {
|
if let Some(Dialog::RequestConfirmation { response, .. }) = self.dialog.take() {
|
||||||
_ = response.send(false);
|
_ = response.send(false);
|
||||||
|
|
@ -344,12 +373,20 @@ impl Page {
|
||||||
} else {
|
} else {
|
||||||
Active::Disabling
|
Active::Disabling
|
||||||
};
|
};
|
||||||
self.update_status();
|
|
||||||
return cosmic::task::future(change_adapter_status(
|
return cosmic::task::future(change_adapter_status(
|
||||||
connection.clone(),
|
connection.clone(),
|
||||||
path,
|
path,
|
||||||
active,
|
active,
|
||||||
));
|
))
|
||||||
|
.then(|event| {
|
||||||
|
if matches!(event, Event::Ok) {
|
||||||
|
Task::none()
|
||||||
|
} else {
|
||||||
|
Task::done(event.into())
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.chain(Task::done(Message::UpdateStatus.into()));
|
||||||
}
|
}
|
||||||
let tasks: Vec<Task<Message>> = self
|
let tasks: Vec<Task<Message>> = self
|
||||||
.adapters
|
.adapters
|
||||||
|
|
@ -365,15 +402,29 @@ impl Page {
|
||||||
path.clone(),
|
path.clone(),
|
||||||
active,
|
active,
|
||||||
))
|
))
|
||||||
|
.then(|event| {
|
||||||
|
if matches!(event, Event::Ok) {
|
||||||
|
Task::none()
|
||||||
|
} else {
|
||||||
|
Task::done(event.into())
|
||||||
|
}
|
||||||
|
})
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
self.update_status();
|
|
||||||
return cosmic::task::batch(tasks);
|
return cosmic::task::batch(tasks)
|
||||||
|
.chain(Task::done(Message::UpdateStatus.into()));
|
||||||
}
|
}
|
||||||
tracing::warn!("No DBus connection ready");
|
tracing::warn!("No DBus connection ready");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Message::UpdateStatus => {
|
||||||
|
self.update_status();
|
||||||
|
}
|
||||||
|
|
||||||
Message::DBusConnect(connection) => {
|
Message::DBusConnect(connection) => {
|
||||||
|
self.service_is_active = systemd::is_bluetooth_active();
|
||||||
|
self.service_is_enabled = systemd::is_bluetooth_enabled();
|
||||||
self.connection = Some(connection.clone());
|
self.connection = Some(connection.clone());
|
||||||
|
|
||||||
let get_adapters_fut = get_adapters(connection.clone());
|
let get_adapters_fut = get_adapters(connection.clone());
|
||||||
|
|
@ -415,38 +466,41 @@ impl Page {
|
||||||
return cosmic::task::future(get_adapters_fut);
|
return cosmic::task::future(get_adapters_fut);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Message::PopupDevice(popup) => {
|
Message::PopupDevice(popup) => {
|
||||||
self.popup_device = popup;
|
self.popup_device = popup;
|
||||||
}
|
}
|
||||||
|
|
||||||
Message::PopupSetting(popup) => {
|
Message::PopupSetting(popup) => {
|
||||||
self.popup_setting = popup;
|
self.popup_setting = popup;
|
||||||
}
|
}
|
||||||
|
|
||||||
Message::SelectAdapter(adapter_maybe) => {
|
Message::SelectAdapter(adapter_maybe) => {
|
||||||
tracing::debug!("Adapter selected: {adapter_maybe:?}");
|
tracing::debug!("Adapter selected: {adapter_maybe:?}");
|
||||||
self.selected_adapter = adapter_maybe;
|
self.selected_adapter = adapter_maybe;
|
||||||
self.update_status();
|
self.update_status();
|
||||||
if let Some(connection) = self.connection.as_ref() {
|
let Some(connection) = self.connection.as_ref() else {
|
||||||
let connection = connection.clone();
|
tracing::error!("No DBus connection ready");
|
||||||
if let Some((path, adapter)) = self.get_selected_adapter_mut() {
|
return Task::none();
|
||||||
let mut fut: Vec<Task<Message>> = vec![cosmic::task::future(get_devices(
|
};
|
||||||
connection.clone(),
|
|
||||||
path.clone(),
|
|
||||||
))];
|
|
||||||
if adapter.enabled == Active::Enabled
|
|
||||||
&& adapter.scanning == Active::Disabled
|
|
||||||
{
|
|
||||||
fut.push(cosmic::task::future(start_discovery(
|
|
||||||
connection,
|
|
||||||
path.clone(),
|
|
||||||
)));
|
|
||||||
}
|
|
||||||
|
|
||||||
return cosmic::task::batch(fut);
|
let connection = connection.clone();
|
||||||
|
if let Some((path, adapter)) = self.get_selected_adapter_mut() {
|
||||||
|
let mut fut: Vec<Task<Message>> = vec![cosmic::task::future(get_devices(
|
||||||
|
connection.clone(),
|
||||||
|
path.clone(),
|
||||||
|
))];
|
||||||
|
if adapter.enabled == Active::Enabled && adapter.scanning == Active::Disabled {
|
||||||
|
fut.push(cosmic::task::future(start_discovery(
|
||||||
|
connection,
|
||||||
|
path.clone(),
|
||||||
|
)));
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
tracing::warn!("No DBus connection ready");
|
return cosmic::task::batch(fut);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Message::ForgetDevice(path) => {
|
Message::ForgetDevice(path) => {
|
||||||
tracing::debug!("Forgetting to device {path}");
|
tracing::debug!("Forgetting to device {path}");
|
||||||
self.popup_device = None;
|
self.popup_device = None;
|
||||||
|
|
@ -463,6 +517,7 @@ impl Page {
|
||||||
tracing::warn!("No DBus connection ready");
|
tracing::warn!("No DBus connection ready");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Message::ConnectDevice(path) => {
|
Message::ConnectDevice(path) => {
|
||||||
tracing::debug!("Connecting device {path}");
|
tracing::debug!("Connecting device {path}");
|
||||||
if self.connection.is_none() {
|
if self.connection.is_none() {
|
||||||
|
|
@ -481,6 +536,7 @@ impl Page {
|
||||||
tracing::warn!("No DBus connection ready");
|
tracing::warn!("No DBus connection ready");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Message::DisconnectDevice(path) => {
|
Message::DisconnectDevice(path) => {
|
||||||
tracing::debug!("Disconnecting device {path}");
|
tracing::debug!("Disconnecting device {path}");
|
||||||
self.popup_device = None;
|
self.popup_device = None;
|
||||||
|
|
@ -497,11 +553,42 @@ impl Page {
|
||||||
tracing::warn!("No DBus connection ready");
|
tracing::warn!("No DBus connection ready");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Message::Nop => {}
|
|
||||||
Message::DBusError(why) => {
|
Message::ServiceActivate => {
|
||||||
tracing::error!("dbus connection failed. {why}");
|
return cosmic::task::future(async {
|
||||||
|
systemd::activate_bluetooth().await;
|
||||||
|
tokio::time::sleep(Duration::from_secs(3)).await;
|
||||||
|
|
||||||
|
match zbus::Connection::system().await {
|
||||||
|
Ok(connection) => Message::DBusConnect(connection),
|
||||||
|
Err(why) => Message::DBusConnectFailed(why),
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
Message::ServiceEnable => {
|
||||||
|
return cosmic::task::future(async {
|
||||||
|
systemd::enable_bluetooth().await;
|
||||||
|
tokio::time::sleep(Duration::from_secs(3)).await;
|
||||||
|
|
||||||
|
match zbus::Connection::system().await {
|
||||||
|
Ok(connection) => Message::DBusConnect(connection),
|
||||||
|
Err(why) => Message::DBusConnectFailed(why),
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
Message::DBusConnectFailed(why) => {
|
||||||
|
tracing::error!("dbus connection failed. {why:?}");
|
||||||
|
self.connection = None;
|
||||||
|
self.service_is_active = false;
|
||||||
|
self.service_is_enabled = false;
|
||||||
|
if let Some(abort_handle) = self.subscription.take() {
|
||||||
|
_ = abort_handle.send(());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
cosmic::Task::none()
|
cosmic::Task::none()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -528,16 +615,14 @@ impl Page {
|
||||||
}
|
}
|
||||||
self.active = if let Some((_, adapter)) = self.get_selected_adapter() {
|
self.active = if let Some((_, adapter)) = self.get_selected_adapter() {
|
||||||
adapter.enabled
|
adapter.enabled
|
||||||
|
} else if self
|
||||||
|
.adapters
|
||||||
|
.values()
|
||||||
|
.any(|adapter| matches!(adapter.enabled, Active::Enabled | Active::Enabling))
|
||||||
|
{
|
||||||
|
Active::Enabled
|
||||||
} else {
|
} else {
|
||||||
self.adapters
|
Active::Disabled
|
||||||
.values()
|
|
||||||
.fold(Active::Disabled, |current, adapter| {
|
|
||||||
if current == Active::Enabled || adapter.enabled == Active::Enabled {
|
|
||||||
Active::Enabled
|
|
||||||
} else {
|
|
||||||
Active::Disabled
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn adapter_connected(&self, adapter_path: &OwnedObjectPath) -> bool {
|
fn adapter_connected(&self, adapter_path: &OwnedObjectPath) -> bool {
|
||||||
|
|
@ -582,15 +667,35 @@ fn status() -> Section<crate::pages::Message> {
|
||||||
|
|
||||||
Section::default()
|
Section::default()
|
||||||
.descriptions(descriptions)
|
.descriptions(descriptions)
|
||||||
.show_while::<Page>(|page| !page.adapters.is_empty())
|
|
||||||
.view::<Page>(move |_binder, page, section| {
|
.view::<Page>(move |_binder, page, section| {
|
||||||
let descriptions = §ion.descriptions;
|
let descriptions = §ion.descriptions;
|
||||||
|
|
||||||
|
fn bluetooth_service_issue<'a>(
|
||||||
|
description: String,
|
||||||
|
label: String,
|
||||||
|
message: Message,
|
||||||
|
) -> Element<'a, crate::pages::Message> {
|
||||||
|
widget::settings::item::builder(description)
|
||||||
|
.control(widget::button::suggested(label).on_press(message.into()))
|
||||||
|
.apply(|control| Element::from(widget::settings::section().add(control)))
|
||||||
|
}
|
||||||
|
|
||||||
if page.bluez_service_unknown {
|
if page.bluez_service_unknown {
|
||||||
return widget::text::body(
|
let control = widget::text::body(fl!("bluetooth", "unknown"));
|
||||||
"The org.bluez DBus service could not be activated. Is bluez installed?",
|
|
||||||
)
|
return Element::from(widget::settings::section().add(control));
|
||||||
.apply(Element::from);
|
} else if !page.service_is_enabled {
|
||||||
|
return bluetooth_service_issue(
|
||||||
|
fl!("bluetooth", "disabled"),
|
||||||
|
fl!("enable"),
|
||||||
|
Message::ServiceEnable,
|
||||||
|
);
|
||||||
|
} else if !page.service_is_active {
|
||||||
|
return bluetooth_service_issue(
|
||||||
|
fl!("bluetooth", "inactive"),
|
||||||
|
fl!("activate"),
|
||||||
|
Message::ServiceEnable,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
let status = page
|
let status = page
|
||||||
|
|
@ -603,11 +708,13 @@ fn status() -> Section<crate::pages::Message> {
|
||||||
}
|
}
|
||||||
|
|
||||||
widget::list_column()
|
widget::list_column()
|
||||||
.add(bluetooth_toggle.control(
|
.add(
|
||||||
widget::toggler(status == Active::Enabled).on_toggle(Message::SetActive),
|
bluetooth_toggle.control(
|
||||||
))
|
widget::toggler(matches!(status, Active::Enabling | Active::Enabled))
|
||||||
|
.on_toggle(|active| Message::SetActive(active).into()),
|
||||||
|
),
|
||||||
|
)
|
||||||
.apply(Element::from)
|
.apply(Element::from)
|
||||||
.map(crate::pages::Message::Bluetooth)
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -828,3 +935,37 @@ fn multiple_adapter() -> Section<crate::pages::Message> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl page::AutoBind<crate::pages::Message> for Page {}
|
impl page::AutoBind<crate::pages::Message> for Page {}
|
||||||
|
|
||||||
|
mod systemd {
|
||||||
|
use futures::FutureExt;
|
||||||
|
|
||||||
|
pub fn activate_bluetooth() -> impl Future<Output = ()> + Send {
|
||||||
|
tokio::process::Command::new("pkexec")
|
||||||
|
.args(&["systemctl", "start", "bluetooth"])
|
||||||
|
.status()
|
||||||
|
.map(|_| ())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn enable_bluetooth() -> impl Future<Output = ()> + Send {
|
||||||
|
tokio::process::Command::new("pkexec")
|
||||||
|
.args(&["systemctl", "enable", "--now", "bluetooth"])
|
||||||
|
.status()
|
||||||
|
.map(|_| ())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_bluetooth_enabled() -> bool {
|
||||||
|
std::process::Command::new("systemctl")
|
||||||
|
.args(&["is-enabled", "bluetooth"])
|
||||||
|
.status()
|
||||||
|
.map(|status| status.success())
|
||||||
|
.unwrap_or(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_bluetooth_active() -> bool {
|
||||||
|
std::process::Command::new("systemctl")
|
||||||
|
.args(&["is-active", "bluetooth"])
|
||||||
|
.status()
|
||||||
|
.map(|status| status.success())
|
||||||
|
.unwrap_or(true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -105,7 +105,9 @@ online-accounts = Online Accounts
|
||||||
|
|
||||||
# Bluetooth
|
# Bluetooth
|
||||||
|
|
||||||
|
activate = Activate
|
||||||
confirm = Confirm
|
confirm = Confirm
|
||||||
|
enable = Enable
|
||||||
|
|
||||||
bluetooth = Bluetooth
|
bluetooth = Bluetooth
|
||||||
.desc = Manage Bluetooth devices
|
.desc = Manage Bluetooth devices
|
||||||
|
|
@ -117,6 +119,9 @@ bluetooth = Bluetooth
|
||||||
.disconnect = Disconnect
|
.disconnect = Disconnect
|
||||||
.forget = Forget
|
.forget = Forget
|
||||||
.dbus-error = An error has occurred while interacting with DBus: { $why }
|
.dbus-error = An error has occurred while interacting with DBus: { $why }
|
||||||
|
.disabled = The bluetooth service is disabled
|
||||||
|
.inactive = The bluetooth service is not active
|
||||||
|
.unknown = The bluetooth service could not be activated. Is bluez installed?
|
||||||
|
|
||||||
bluetooth-paired = Previously Connected Devices
|
bluetooth-paired = Previously Connected Devices
|
||||||
.connect = Connect
|
.connect = Connect
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue