refactor: simplify state updates from network manager subscription
This commit is contained in:
parent
c10087a55d
commit
833e68d63b
3 changed files with 230 additions and 174 deletions
|
|
@ -15,10 +15,11 @@ futures = "0.3"
|
|||
zbus = { version = "3.7", no-default-features = true }
|
||||
log = "0.4"
|
||||
pretty_env_logger = "0.4"
|
||||
itertools = "0.10.3"
|
||||
slotmap = "1.0.6"
|
||||
tokio = { version = "1.15.0", features = ["full"] }
|
||||
anyhow = "1.0"
|
||||
# Application i18n
|
||||
i18n-embed = { version = "0.13.4", features = ["fluent-system", "desktop-requester"] }
|
||||
i18n-embed-fl = "0.6.4"
|
||||
rust-embed = "6.3.0"
|
||||
itertools = "0.10.3"
|
||||
slotmap = "1.0.6"
|
||||
tokio = { version = "1.15.0", features = ["full"] }
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
use cosmic::iced_style;
|
||||
use cosmic::{
|
||||
applet::CosmicAppletHelper,
|
||||
iced::{
|
||||
|
|
@ -20,19 +21,10 @@ use cosmic::{
|
|||
widget::{button, horizontal_rule, icon, list_column, toggler},
|
||||
Element, Theme,
|
||||
};
|
||||
use cosmic::{
|
||||
iced_style,
|
||||
widget::segmented_button::{
|
||||
self,
|
||||
cosmic::{
|
||||
horizontal_segmented_selection, horizontal_view_switcher, vertical_segmented_selection,
|
||||
vertical_view_switcher,
|
||||
},
|
||||
},
|
||||
};
|
||||
use cosmic_dbus_networkmanager::{access_point, interface::enums::DeviceState};
|
||||
use futures::channel::mpsc::UnboundedSender;
|
||||
|
||||
use crate::network_manager::NetworkManagerState;
|
||||
use crate::{
|
||||
config, fl,
|
||||
network_manager::{
|
||||
|
|
@ -174,7 +166,9 @@ impl Application for CosmicNetworkApplet {
|
|||
Message::Ignore => {}
|
||||
Message::ToggleAirplaneMode(enabled) => {
|
||||
self.airplane_mode = enabled;
|
||||
// TODO apply changes
|
||||
if let Some(tx) = self.nm_sender.as_mut() {
|
||||
let _ = tx.unbounded_send(NetworkManagerRequest::SetAirplaneMode(enabled));
|
||||
}
|
||||
}
|
||||
Message::ToggleWiFi(enabled) => {
|
||||
self.wifi = enabled;
|
||||
|
|
@ -185,11 +179,14 @@ impl Application for CosmicNetworkApplet {
|
|||
Message::NetworkManagerEvent(event) => match event {
|
||||
NetworkManagerEvent::Init {
|
||||
sender,
|
||||
wireless_access_points,
|
||||
active_conns,
|
||||
known_access_points,
|
||||
wifi_enabled,
|
||||
airplane_mode,
|
||||
state:
|
||||
NetworkManagerState {
|
||||
wireless_access_points,
|
||||
active_conns,
|
||||
known_access_points,
|
||||
wifi_enabled,
|
||||
airplane_mode,
|
||||
},
|
||||
} => {
|
||||
self.nm_sender.replace(sender);
|
||||
self.wireless_access_points = wireless_access_points;
|
||||
|
|
@ -210,16 +207,22 @@ impl Application for CosmicNetworkApplet {
|
|||
self.update_icon_name();
|
||||
}
|
||||
NetworkManagerEvent::RequestResponse {
|
||||
wireless_access_points,
|
||||
active_conns,
|
||||
wifi_enabled,
|
||||
state:
|
||||
NetworkManagerState {
|
||||
wireless_access_points,
|
||||
active_conns,
|
||||
known_access_points,
|
||||
wifi_enabled,
|
||||
airplane_mode,
|
||||
},
|
||||
success,
|
||||
..
|
||||
} => {
|
||||
if success {
|
||||
self.wireless_access_points = wireless_access_points;
|
||||
self.active_conns = active_conns;
|
||||
|
||||
self.known_access_points = known_access_points;
|
||||
self.airplane_mode = airplane_mode;
|
||||
self.wifi = wifi_enabled;
|
||||
self.update_icon_name();
|
||||
}
|
||||
|
|
@ -243,19 +246,27 @@ impl Application for CosmicNetworkApplet {
|
|||
_ => {}
|
||||
},
|
||||
Message::SubmitPassword => {
|
||||
// TODO setup connection
|
||||
// save password
|
||||
let tx = if let Some(tx) = self.nm_sender.as_ref() {
|
||||
tx
|
||||
} else {
|
||||
return Command::none();
|
||||
};
|
||||
|
||||
match self.new_connection.take() {
|
||||
Some(new_connection) => {
|
||||
let ap: AccessPoint = new_connection.into();
|
||||
if let Some(tx) = self.nm_sender.as_ref() {
|
||||
let _ = tx.unbounded_send(NetworkManagerRequest::SelectAccessPoint(
|
||||
ap.ssid.clone(),
|
||||
));
|
||||
}
|
||||
self.new_connection.replace(NewConnectionState::Failure(ap));
|
||||
Some(NewConnectionState::EnterPassword {
|
||||
password,
|
||||
access_point,
|
||||
}) => {
|
||||
let _ = tx.unbounded_send(NetworkManagerRequest::Password(
|
||||
access_point.ssid.clone(),
|
||||
password.to_string(),
|
||||
));
|
||||
self.new_connection
|
||||
.replace(NewConnectionState::Failure(access_point.clone()));
|
||||
}
|
||||
None => {}
|
||||
}
|
||||
_ => {}
|
||||
};
|
||||
}
|
||||
Message::ActivateKnownWifi(ssid) => {}
|
||||
Message::CancelNewConnection => {
|
||||
|
|
@ -386,7 +397,7 @@ impl Application for CosmicNetworkApplet {
|
|||
.padding([8, 24])
|
||||
.width(Length::Fill)
|
||||
.style(button_style.clone());
|
||||
let btn = match known.state {
|
||||
btn = match known.state {
|
||||
// DeviceState::Prepare => todo!(),
|
||||
// DeviceState::Config => todo!(),
|
||||
// DeviceState::NeedAuth => todo!(),
|
||||
|
|
|
|||
|
|
@ -1,13 +1,13 @@
|
|||
pub mod available_wifi;
|
||||
pub mod current_networks;
|
||||
|
||||
use std::{fmt::Debug, hash::Hash, time::Duration};
|
||||
use std::{collections::HashMap, fmt::Debug, hash::Hash, time::Duration};
|
||||
|
||||
use cosmic::iced::{self, subscription};
|
||||
use cosmic_dbus_networkmanager::{
|
||||
device::SpecificDevice,
|
||||
interface::{enums::DeviceType, settings::connection::ConnectionSettingsProxy},
|
||||
nm::NetworkManager,
|
||||
nm::{self, NetworkManager},
|
||||
settings::{
|
||||
connection::{ConnectionSettings, Secrets, Settings},
|
||||
NetworkManagerSettings,
|
||||
|
|
@ -18,7 +18,11 @@ use futures::{
|
|||
future::ok,
|
||||
FutureExt, StreamExt,
|
||||
};
|
||||
use zbus::Connection;
|
||||
use tokio::process::Command;
|
||||
use zbus::{
|
||||
zvariant::{self, Value},
|
||||
Connection,
|
||||
};
|
||||
|
||||
use self::{
|
||||
available_wifi::{handle_wireless_device, AccessPoint},
|
||||
|
|
@ -52,88 +56,15 @@ async fn start_listening<I: Copy>(
|
|||
Ok(c) => c,
|
||||
Err(_) => return (None, State::Finished),
|
||||
};
|
||||
let network_manager = match NetworkManager::new(&conn).await {
|
||||
Ok(n) => n,
|
||||
Err(_) => return (None, State::Finished),
|
||||
};
|
||||
let s = match NetworkManagerSettings::new(&conn).await {
|
||||
Ok(s) => s,
|
||||
Err(_) => return (None, State::Finished),
|
||||
};
|
||||
let known_conns = s.list_connections().await.unwrap_or_default();
|
||||
|
||||
let (tx, rx) = unbounded();
|
||||
let mut active_conns = active_connections(
|
||||
network_manager
|
||||
.active_connections()
|
||||
.await
|
||||
.unwrap_or_default(),
|
||||
)
|
||||
.await
|
||||
.unwrap_or_default();
|
||||
active_conns.sort_by(|a, b| {
|
||||
let helper = |conn: &ActiveConnectionInfo| match conn {
|
||||
ActiveConnectionInfo::Vpn { name, .. } => format!("0{name}"),
|
||||
ActiveConnectionInfo::Wired { name, .. } => format!("1{name}"),
|
||||
ActiveConnectionInfo::WiFi { name, .. } => format!("2{name}"),
|
||||
};
|
||||
helper(a).cmp(&helper(b))
|
||||
});
|
||||
let wifi_enabled = network_manager.wireless_enabled().await.unwrap_or_default();
|
||||
let devices = network_manager.devices().await.ok().unwrap_or_default();
|
||||
let wireless_access_point_futures: Vec<_> = devices
|
||||
.into_iter()
|
||||
.map(|device| async move {
|
||||
if let Ok(Some(SpecificDevice::Wireless(wireless_device))) =
|
||||
device.downcast_to_device().await
|
||||
{
|
||||
handle_wireless_device(wireless_device)
|
||||
.await
|
||||
.unwrap_or_default()
|
||||
} else {
|
||||
Vec::new()
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
let mut wireless_access_points =
|
||||
Vec::with_capacity(wireless_access_point_futures.len());
|
||||
for f in wireless_access_point_futures {
|
||||
let mut access_points = f.await;
|
||||
wireless_access_points.append(&mut access_points);
|
||||
}
|
||||
let mut known_ssid = Vec::with_capacity(known_conns.len());
|
||||
for c in known_conns {
|
||||
let s = c.get_settings().await.unwrap();
|
||||
let s = Settings::new(s);
|
||||
if let Some(cur_ssid) = s
|
||||
.wifi
|
||||
.clone()
|
||||
.and_then(|w| w.ssid)
|
||||
.and_then(|ssid| String::from_utf8(ssid).ok())
|
||||
{
|
||||
known_ssid.push(cur_ssid);
|
||||
}
|
||||
}
|
||||
let known_access_points: Vec<_> = wireless_access_points
|
||||
.iter()
|
||||
.filter(|a| {
|
||||
known_ssid.contains(&a.ssid)
|
||||
&& !active_conns.iter().any(|ac| ac.name() == a.ssid)
|
||||
})
|
||||
.cloned()
|
||||
.collect();
|
||||
wireless_access_points.sort_by(|a, b| b.strength.cmp(&a.strength));
|
||||
drop(network_manager);
|
||||
let nm_state = NetworkManagerState::new(&conn).await.unwrap_or_default();
|
||||
return (
|
||||
Some((
|
||||
id,
|
||||
NetworkManagerEvent::Init {
|
||||
sender: tx,
|
||||
wireless_access_points,
|
||||
wifi_enabled,
|
||||
airplane_mode: false,
|
||||
known_access_points,
|
||||
active_conns,
|
||||
state: nm_state,
|
||||
},
|
||||
)),
|
||||
State::Waiting(conn, rx),
|
||||
|
|
@ -156,63 +87,83 @@ async fn start_listening<I: Copy>(
|
|||
let (update, should_exit) = futures::select! {
|
||||
req = req => {
|
||||
match req {
|
||||
Some(NetworkManagerRequest::SetAirplaneMode(state)) => {
|
||||
// TODO set airplane mode
|
||||
let _ = network_manager.set_wireless_enabled(state).await;
|
||||
(None, false)
|
||||
Some(NetworkManagerRequest::SetAirplaneMode(airplane_mode)) => {
|
||||
// wifi
|
||||
let mut success = network_manager.set_wireless_enabled(!airplane_mode).await.is_ok();
|
||||
// bluetooth
|
||||
success = success && Command::new("rfkill")
|
||||
.arg(if airplane_mode { "block" } else { "unblock" })
|
||||
.arg("bluetooth")
|
||||
.output()
|
||||
.await
|
||||
.is_ok();
|
||||
let response = NetworkManagerEvent::RequestResponse {
|
||||
req: NetworkManagerRequest::SetAirplaneMode(airplane_mode),
|
||||
success: true,
|
||||
state: NetworkManagerState::new(&conn).await.unwrap_or_default(),
|
||||
};
|
||||
(Some((id, response)), false)
|
||||
}
|
||||
Some(NetworkManagerRequest::SetWiFi(enabled)) => {
|
||||
let success = network_manager.set_wireless_enabled(enabled).await.is_ok();
|
||||
let active_conns = active_connections(network_manager.active_connections().await.unwrap_or_default()).await.unwrap_or_default();
|
||||
let devices = network_manager.devices().await.ok().unwrap_or_default();
|
||||
let wireless_access_point_futures: Vec<_> = devices.into_iter().map(|device| async move {
|
||||
if let Ok(Some(SpecificDevice::Wireless(wireless_device))) =
|
||||
device.downcast_to_device().await
|
||||
{
|
||||
handle_wireless_device(wireless_device).await.unwrap_or_default()
|
||||
} else {
|
||||
Vec::new()
|
||||
}
|
||||
}).collect();
|
||||
let mut wireless_access_points = Vec::with_capacity(wireless_access_point_futures.len());
|
||||
for f in wireless_access_point_futures {
|
||||
wireless_access_points.append(&mut f.await);
|
||||
}
|
||||
(Some((id, NetworkManagerEvent::RequestResponse {
|
||||
req: NetworkManagerRequest::SetWiFi(enabled),
|
||||
let response = NetworkManagerEvent::RequestResponse {
|
||||
req: NetworkManagerRequest::SetAirplaneMode(enabled),
|
||||
success,
|
||||
active_conns,
|
||||
wireless_access_points,
|
||||
wifi_enabled: enabled,
|
||||
airplane_mode: false,
|
||||
})), false)
|
||||
state: NetworkManagerState::new(&conn).await.unwrap_or_default(),
|
||||
};
|
||||
(Some((id, response)), false)
|
||||
}
|
||||
Some(NetworkManagerRequest::Password(ssid, password)) => {
|
||||
let s = match NetworkManagerSettings::new(&conn).await {
|
||||
Ok(s) => s,
|
||||
Err(_) => return (None, State::Finished),
|
||||
};
|
||||
|
||||
// TODO more convenient methods of managing settings
|
||||
for c in s.list_connections().await.unwrap_or_default() {
|
||||
let mut settings = match c.get_settings().await.ok() {
|
||||
Some(s) => s,
|
||||
None => continue,
|
||||
};
|
||||
dbg!(&settings);
|
||||
let cur_ssid = settings
|
||||
.get("802-11-wireless")
|
||||
.and_then(|w| w.get("ssid"))
|
||||
.cloned()
|
||||
.and_then(|ssid| ssid.try_into().ok())
|
||||
.and_then(|ssid| String::from_utf8(ssid).ok());
|
||||
dbg!(&cur_ssid);
|
||||
if cur_ssid.as_ref() != Some(&ssid) {
|
||||
break;
|
||||
}
|
||||
|
||||
let mut secrets = match
|
||||
c.get_secrets("802-11-wireless-security")
|
||||
.await {
|
||||
Ok(s) => s,
|
||||
_ => continue,
|
||||
};
|
||||
if let Some(s) = secrets.get_mut("802-11-wireless-security") {
|
||||
s.insert("psk".into(), Value::Str(password.into()).to_owned());
|
||||
drop(s);
|
||||
settings.extend(secrets.into_iter());
|
||||
let settings: HashMap<_, _> = settings.iter().map(|(k, v)| {
|
||||
let map = (k.as_str(), v.iter()
|
||||
.map(|(k, v)| (k.as_str(), v.into()))
|
||||
.collect::<HashMap<_, _>>());
|
||||
map
|
||||
}).collect();
|
||||
dbg!(settings.clone());
|
||||
dbg!(c.update(settings).await);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
(None, false)
|
||||
}
|
||||
Some(NetworkManagerRequest::SelectAccessPoint(ssid)) => {
|
||||
'device_loop: for device in network_manager.devices().await.ok().unwrap_or_default() {
|
||||
if matches!(device.device_type().await.unwrap_or(DeviceType::Other), DeviceType::Wifi) {
|
||||
let connection_settings = NetworkManagerSettings::new(&conn).await.unwrap();
|
||||
for conn in connection_settings.list_connections().await.unwrap() {
|
||||
let s = conn.get_settings().await.unwrap();
|
||||
let s = Settings::new(s);
|
||||
|
||||
let cur_ssid = s
|
||||
.wifi
|
||||
.clone()
|
||||
.and_then(|w| w.ssid)
|
||||
.and_then(|ssid| String::from_utf8(ssid).ok());
|
||||
if cur_ssid.as_ref() == Some(&ssid) {
|
||||
// dbg!(s);
|
||||
// dbg!(conn.get_secrets("connection").await);
|
||||
// dbg!(Secrets::new(&conn).await);
|
||||
// dbg!(psk);
|
||||
// connection update can be used to set password
|
||||
}
|
||||
}
|
||||
for conn in device.available_connections().await.unwrap_or_default() {
|
||||
// network_manager.activate_connection(conn, device.clone());
|
||||
// dbg!(&conn.path());
|
||||
// TODO activate connection
|
||||
}
|
||||
}
|
||||
}
|
||||
(None, false)
|
||||
|
|
@ -273,27 +224,120 @@ pub enum NetworkManagerRequest {
|
|||
SetAirplaneMode(bool),
|
||||
SetWiFi(bool),
|
||||
SelectAccessPoint(String),
|
||||
Password(String, String),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum NetworkManagerEvent {
|
||||
Init {
|
||||
sender: UnboundedSender<NetworkManagerRequest>,
|
||||
wireless_access_points: Vec<AccessPoint>,
|
||||
active_conns: Vec<ActiveConnectionInfo>,
|
||||
known_access_points: Vec<AccessPoint>,
|
||||
wifi_enabled: bool,
|
||||
airplane_mode: bool,
|
||||
},
|
||||
RequestResponse {
|
||||
req: NetworkManagerRequest,
|
||||
wireless_access_points: Vec<AccessPoint>,
|
||||
active_conns: Vec<ActiveConnectionInfo>,
|
||||
wifi_enabled: bool,
|
||||
airplane_mode: bool,
|
||||
state: NetworkManagerState,
|
||||
success: bool,
|
||||
},
|
||||
Init {
|
||||
sender: UnboundedSender<NetworkManagerRequest>,
|
||||
state: NetworkManagerState,
|
||||
},
|
||||
WiFiEnabled(bool),
|
||||
WirelessAccessPoints(Vec<AccessPoint>),
|
||||
ActiveConns(Vec<ActiveConnectionInfo>),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default)]
|
||||
pub struct NetworkManagerState {
|
||||
pub wireless_access_points: Vec<AccessPoint>,
|
||||
pub active_conns: Vec<ActiveConnectionInfo>,
|
||||
pub known_access_points: Vec<AccessPoint>,
|
||||
pub wifi_enabled: bool,
|
||||
pub airplane_mode: bool,
|
||||
}
|
||||
|
||||
impl NetworkManagerState {
|
||||
pub async fn new(conn: &Connection) -> anyhow::Result<Self> {
|
||||
let network_manager = NetworkManager::new(&conn).await?;
|
||||
let mut _self = Self::default();
|
||||
|
||||
// airplane mode
|
||||
let airplaine_mode = Command::new("rfkill")
|
||||
.arg("list")
|
||||
.arg("bluetooth")
|
||||
.output()
|
||||
.await?;
|
||||
let airplane_mode = std::str::from_utf8(&airplaine_mode.stdout).unwrap_or_default();
|
||||
let bluetooth_disabled = airplane_mode.contains("Soft blocked: yes");
|
||||
|
||||
if !network_manager.wireless_enabled().await.unwrap_or_default() {
|
||||
_self.airplane_mode = bluetooth_disabled;
|
||||
return Ok(_self);
|
||||
} else {
|
||||
_self.wifi_enabled = true;
|
||||
};
|
||||
|
||||
let s = NetworkManagerSettings::new(&conn).await?;
|
||||
|
||||
let known_conns = s.list_connections().await.unwrap_or_default();
|
||||
|
||||
let mut active_conns = active_connections(
|
||||
network_manager
|
||||
.active_connections()
|
||||
.await
|
||||
.unwrap_or_default(),
|
||||
)
|
||||
.await
|
||||
.unwrap_or_default();
|
||||
active_conns.sort_by(|a, b| {
|
||||
let helper = |conn: &ActiveConnectionInfo| match conn {
|
||||
ActiveConnectionInfo::Vpn { name, .. } => format!("0{name}"),
|
||||
ActiveConnectionInfo::Wired { name, .. } => format!("1{name}"),
|
||||
ActiveConnectionInfo::WiFi { name, .. } => format!("2{name}"),
|
||||
};
|
||||
helper(a).cmp(&helper(b))
|
||||
});
|
||||
let devices = network_manager.devices().await.ok().unwrap_or_default();
|
||||
let wireless_access_point_futures: Vec<_> = devices
|
||||
.into_iter()
|
||||
.map(|device| async move {
|
||||
if let Ok(Some(SpecificDevice::Wireless(wireless_device))) =
|
||||
device.downcast_to_device().await
|
||||
{
|
||||
handle_wireless_device(wireless_device)
|
||||
.await
|
||||
.unwrap_or_default()
|
||||
} else {
|
||||
Vec::new()
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
let mut wireless_access_points = Vec::with_capacity(wireless_access_point_futures.len());
|
||||
for f in wireless_access_point_futures {
|
||||
let mut access_points = f.await;
|
||||
wireless_access_points.append(&mut access_points);
|
||||
}
|
||||
let mut known_ssid = Vec::with_capacity(known_conns.len());
|
||||
for c in known_conns {
|
||||
let s = c.get_settings().await.unwrap();
|
||||
let s = Settings::new(s);
|
||||
if let Some(cur_ssid) = s
|
||||
.wifi
|
||||
.clone()
|
||||
.and_then(|w| w.ssid)
|
||||
.and_then(|ssid| String::from_utf8(ssid).ok())
|
||||
{
|
||||
known_ssid.push(cur_ssid);
|
||||
}
|
||||
}
|
||||
let known_access_points: Vec<_> = wireless_access_points
|
||||
.iter()
|
||||
.filter(|a| {
|
||||
known_ssid.contains(&a.ssid) && !active_conns.iter().any(|ac| ac.name() == a.ssid)
|
||||
})
|
||||
.cloned()
|
||||
.collect();
|
||||
wireless_access_points.sort_by(|a, b| b.strength.cmp(&a.strength));
|
||||
|
||||
_self.wireless_access_points = wireless_access_points;
|
||||
_self.active_conns = active_conns;
|
||||
_self.known_access_points = known_access_points;
|
||||
Ok(_self)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue