feat(power): add connected devices section
This commit is contained in:
parent
97bcbc64ec
commit
40d56e6ea7
8 changed files with 293 additions and 75 deletions
2
Cargo.lock
generated
2
Cargo.lock
generated
|
|
@ -6934,7 +6934,7 @@ checksum = "229730647fbc343e3a80e463c1db7f78f3855d3f3739bee0dda773c9a037c90a"
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "upower_dbus"
|
name = "upower_dbus"
|
||||||
version = "0.3.2"
|
version = "0.3.2"
|
||||||
source = "git+https://github.com/pop-os/dbus-settings-bindings#7aedc25e3295b95a90eb710f443029d4ec920aa8"
|
source = "git+https://github.com/pop-os/dbus-settings-bindings#e0d6a04d6ebf6bcede1580721c84a7f01e5ef8bb"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"serde",
|
"serde",
|
||||||
"serde_repr",
|
"serde_repr",
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,7 @@
|
||||||
use chrono::{Duration, TimeDelta};
|
use chrono::{Duration, TimeDelta};
|
||||||
|
use futures::future::join_all;
|
||||||
use futures::FutureExt;
|
use futures::FutureExt;
|
||||||
|
use upower_dbus::{BatteryType, DeviceProxy};
|
||||||
use zbus::Connection;
|
use zbus::Connection;
|
||||||
|
|
||||||
mod ppdaemon;
|
mod ppdaemon;
|
||||||
|
|
@ -238,6 +240,13 @@ pub struct Battery {
|
||||||
pub remaining_duration: Duration,
|
pub remaining_duration: Duration,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Default, Debug, Clone)]
|
||||||
|
pub struct ConnectedDevice {
|
||||||
|
pub model: String,
|
||||||
|
pub device_icon: &'static str,
|
||||||
|
pub battery: Battery,
|
||||||
|
}
|
||||||
|
|
||||||
async fn get_device_proxy<'a>() -> Result<upower_dbus::DeviceProxy<'a>, zbus::Error> {
|
async fn get_device_proxy<'a>() -> Result<upower_dbus::DeviceProxy<'a>, zbus::Error> {
|
||||||
let connection = match Connection::system().await {
|
let connection = match Connection::system().await {
|
||||||
Ok(c) => c,
|
Ok(c) => c,
|
||||||
|
|
@ -253,6 +262,44 @@ async fn get_device_proxy<'a>() -> Result<upower_dbus::DeviceProxy<'a>, zbus::Er
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn enumerate_devices<'a>() -> Result<Vec<upower_dbus::DeviceProxy<'a>>, zbus::Error> {
|
||||||
|
let connection = match Connection::system().await {
|
||||||
|
Ok(c) => c,
|
||||||
|
Err(e) => {
|
||||||
|
tracing::error!("zbus connection failed. {e}");
|
||||||
|
return Err(e);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let devices = upower_dbus::UPowerProxy::new(&connection)
|
||||||
|
.await?
|
||||||
|
.enumerate_devices()
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
let devices = futures::future::join_all(
|
||||||
|
devices
|
||||||
|
.into_iter()
|
||||||
|
.map(|path| DeviceProxy::new(&connection, path)),
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
|
||||||
|
let errors = devices.iter().filter(|device| device.is_err());
|
||||||
|
if errors.count() > 0 {
|
||||||
|
let mut errors: Vec<zbus::Error> = devices
|
||||||
|
.into_iter()
|
||||||
|
.filter_map(std::result::Result::err)
|
||||||
|
.collect();
|
||||||
|
if errors.len() > 1 {
|
||||||
|
eprintln!("Multiple errors occurs when fetching connected device: {errors:?}. Only the last one will be returned.");
|
||||||
|
}
|
||||||
|
return Err(errors.pop().unwrap());
|
||||||
|
}
|
||||||
|
Ok(devices
|
||||||
|
.into_iter()
|
||||||
|
.filter_map(std::result::Result::ok)
|
||||||
|
.collect())
|
||||||
|
}
|
||||||
|
|
||||||
async fn get_on_battery_status() -> Result<bool, zbus::Error> {
|
async fn get_on_battery_status() -> Result<bool, zbus::Error> {
|
||||||
let connection = match Connection::system().await {
|
let connection = match Connection::system().await {
|
||||||
Ok(c) => c,
|
Ok(c) => c,
|
||||||
|
|
@ -269,73 +316,76 @@ async fn get_on_battery_status() -> Result<bool, zbus::Error> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Battery {
|
impl Battery {
|
||||||
pub async fn update_battery() -> Self {
|
pub async fn from_device(proxy: DeviceProxy<'_>) -> Self {
|
||||||
let proxy = get_device_proxy().await;
|
let mut remaining_duration: Duration = Duration::default();
|
||||||
|
|
||||||
if let Ok(proxy) = proxy {
|
let (is_present, percentage, on_battery) = futures::join!(
|
||||||
let mut remaining_duration: Duration = Duration::default();
|
proxy.is_present().map(Result::unwrap_or_default),
|
||||||
|
proxy.percentage().map(Result::unwrap_or_default),
|
||||||
|
get_on_battery_status().map(Result::unwrap_or_default)
|
||||||
|
);
|
||||||
|
|
||||||
let (is_present, percentage, on_battery) = futures::join!(
|
let percent = percentage.clamp(0.0, 100.0);
|
||||||
proxy.is_present().map(Result::unwrap_or_default),
|
|
||||||
proxy.percentage().map(Result::unwrap_or_default),
|
|
||||||
get_on_battery_status().map(Result::unwrap_or_default)
|
|
||||||
);
|
|
||||||
|
|
||||||
let percent = percentage.clamp(0.0, 100.0);
|
if on_battery {
|
||||||
|
if let Ok(time) = proxy.time_to_empty().await {
|
||||||
if on_battery {
|
|
||||||
if let Ok(time) = proxy.time_to_empty().await {
|
|
||||||
if let Ok(dur) = Duration::from_std(std::time::Duration::from_secs(time as u64))
|
|
||||||
{
|
|
||||||
remaining_duration = dur;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if let Ok(time) = proxy.time_to_full().await {
|
|
||||||
if let Ok(dur) = Duration::from_std(std::time::Duration::from_secs(time as u64)) {
|
if let Ok(dur) = Duration::from_std(std::time::Duration::from_secs(time as u64)) {
|
||||||
remaining_duration = dur;
|
remaining_duration = dur;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} else if let Ok(time) = proxy.time_to_full().await {
|
||||||
|
if let Ok(dur) = Duration::from_std(std::time::Duration::from_secs(time as u64)) {
|
||||||
|
remaining_duration = dur;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let battery_percent = if percent > 95.0 {
|
let battery_percent = if percent > 95.0 {
|
||||||
100
|
100
|
||||||
} else if percent > 80.0 {
|
} else if percent > 80.0 {
|
||||||
90
|
90
|
||||||
} else if percent > 65.0 {
|
} else if percent > 65.0 {
|
||||||
80
|
80
|
||||||
} else if percent > 35.0 {
|
} else if percent > 35.0 {
|
||||||
50
|
50
|
||||||
} else if percent > 20.0 {
|
} else if percent > 20.0 {
|
||||||
35
|
35
|
||||||
} else if percent > 14.0 {
|
} else if percent > 14.0 {
|
||||||
20
|
20
|
||||||
} else if percent > 9.0 {
|
} else if percent > 9.0 {
|
||||||
10
|
10
|
||||||
} else if percent > 5.0 {
|
} else if percent > 5.0 {
|
||||||
5
|
5
|
||||||
} else {
|
} else {
|
||||||
0
|
0
|
||||||
};
|
};
|
||||||
let charging = if on_battery { "" } else { "charging-" };
|
let charging = if on_battery { "" } else { "charging-" };
|
||||||
|
|
||||||
let icon_name =
|
let icon_name =
|
||||||
format!("cosmic-applet-battery-level-{battery_percent}-{charging}symbolic",);
|
format!("cosmic-applet-battery-level-{battery_percent}-{charging}symbolic",);
|
||||||
|
|
||||||
return Battery {
|
Self {
|
||||||
icon_name,
|
icon_name,
|
||||||
is_present,
|
is_present,
|
||||||
percent,
|
percent,
|
||||||
on_battery,
|
on_battery,
|
||||||
remaining_duration,
|
remaining_duration,
|
||||||
};
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn update_battery() -> Self {
|
||||||
|
let proxy = get_device_proxy().await;
|
||||||
|
|
||||||
|
if let Ok(proxy) = proxy {
|
||||||
|
return Self::from_device(proxy).await;
|
||||||
}
|
}
|
||||||
|
|
||||||
Battery::default()
|
Battery::default()
|
||||||
}
|
}
|
||||||
pub fn remaining_time(&self) -> String {
|
pub fn remaining_time(&self) -> String {
|
||||||
if self.remaining_duration <= TimeDelta::zero() {
|
if self.remaining_duration <= TimeDelta::zero() {
|
||||||
return String::new()
|
return String::new();
|
||||||
}
|
}
|
||||||
|
|
||||||
let total_seconds = self.remaining_duration.num_seconds();
|
let total_seconds = self.remaining_duration.num_seconds();
|
||||||
|
|
||||||
let days = total_seconds / 86400;
|
let days = total_seconds / 86400;
|
||||||
|
|
@ -357,8 +407,11 @@ impl Battery {
|
||||||
let last = time.pop().unwrap();
|
let last = time.pop().unwrap();
|
||||||
time = vec![time.join(", "), last];
|
time = vec![time.join(", "), last];
|
||||||
}
|
}
|
||||||
let time = if time.is_empty() { fl!("battery", "less-than-minute")
|
let time = if time.is_empty() {
|
||||||
} else {time.join(&format!(" {} ", fl!("battery", "and"))) };
|
fl!("battery", "less-than-minute")
|
||||||
|
} else {
|
||||||
|
time.join(&format!(" {} ", fl!("battery", "and")))
|
||||||
|
};
|
||||||
|
|
||||||
fl!(
|
fl!(
|
||||||
"battery",
|
"battery",
|
||||||
|
|
@ -396,3 +449,67 @@ mod tests {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl ConnectedDevice {
|
||||||
|
async fn from_device_maybe(proxy: DeviceProxy<'_>) -> Option<Self> {
|
||||||
|
let device_type = proxy.type_().await.unwrap_or(BatteryType::Unknown);
|
||||||
|
if matches!(
|
||||||
|
device_type,
|
||||||
|
BatteryType::Unknown | BatteryType::LinePower | BatteryType::Battery
|
||||||
|
) {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
let model = proxy
|
||||||
|
.model()
|
||||||
|
.await
|
||||||
|
.unwrap_or(fl!("connected-devices", "unknown"));
|
||||||
|
let battery = Battery::from_device(proxy).await;
|
||||||
|
let device_icon = match device_type {
|
||||||
|
BatteryType::Ups => "uninterruptible-power-supply-symbolic",
|
||||||
|
BatteryType::Monitor => "display-symbolic",
|
||||||
|
BatteryType::Mouse => "input-mouse-symbolic",
|
||||||
|
BatteryType::Keyboard => "input-keyboard-symbolic",
|
||||||
|
BatteryType::Pda | BatteryType::Phone => "smartphone-symbolic",
|
||||||
|
BatteryType::MediaPlayer => "multimedia-player-symbolic",
|
||||||
|
BatteryType::Tablet => "tablet-symbolic",
|
||||||
|
BatteryType::Computer => "laptop-symbolic",
|
||||||
|
BatteryType::GamingInput => "input-gaming-symbolic",
|
||||||
|
BatteryType::Pen => "input-tablet-symbolic",
|
||||||
|
BatteryType::Touchpad => "input-touchpad-symbolic",
|
||||||
|
BatteryType::Network => "network-wired-symbolic",
|
||||||
|
BatteryType::Headset => "audio-headset-symbolic",
|
||||||
|
BatteryType::Speakers => "speaker-symbolic",
|
||||||
|
BatteryType::Headphones => "audio-headphones-symbolic",
|
||||||
|
BatteryType::Video => "video-display-symbolic",
|
||||||
|
BatteryType::OtherAudio => "audio-speakers-symbolic",
|
||||||
|
BatteryType::Printer => "printer-network-symbolic",
|
||||||
|
BatteryType::Scanner => "scanner-symbolic",
|
||||||
|
BatteryType::Camera => "camera-photo-symbolic",
|
||||||
|
_ => "bluetooth-symbolic",
|
||||||
|
};
|
||||||
|
|
||||||
|
Some(Self {
|
||||||
|
model,
|
||||||
|
device_icon,
|
||||||
|
battery,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn update_connected_devices() -> Vec<Self> {
|
||||||
|
let proxy = enumerate_devices().await;
|
||||||
|
|
||||||
|
if let Ok(devices) = proxy {
|
||||||
|
return join_all(
|
||||||
|
devices
|
||||||
|
.into_iter()
|
||||||
|
.map(|device| Self::from_device_maybe(device)),
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.into_iter()
|
||||||
|
.flatten()
|
||||||
|
.collect();
|
||||||
|
}
|
||||||
|
|
||||||
|
vec![]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,19 +1,24 @@
|
||||||
mod backend;
|
mod backend;
|
||||||
|
|
||||||
use self::backend::{GetCurrentPowerProfile, SetPowerProfile};
|
use self::backend::{GetCurrentPowerProfile, SetPowerProfile};
|
||||||
use backend::{Battery, PowerProfile};
|
use backend::{Battery, ConnectedDevice, PowerProfile};
|
||||||
|
|
||||||
|
use chrono::TimeDelta;
|
||||||
use cosmic::iced::{Alignment, Length};
|
use cosmic::iced::{Alignment, Length};
|
||||||
use cosmic::iced_widget::row;
|
use cosmic::iced_widget::{column, row};
|
||||||
use cosmic::widget::{self, column, radio, settings, text};
|
use cosmic::prelude::CollectionWidget;
|
||||||
|
use cosmic::widget::{self, radio, settings, text};
|
||||||
use cosmic::Apply;
|
use cosmic::Apply;
|
||||||
|
use cosmic::Command;
|
||||||
use cosmic_settings_page::{self as page, section, Section};
|
use cosmic_settings_page::{self as page, section, Section};
|
||||||
|
use itertools::Itertools;
|
||||||
use slab::Slab;
|
use slab::Slab;
|
||||||
use slotmap::SlotMap;
|
use slotmap::SlotMap;
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct Page {
|
pub struct Page {
|
||||||
battery: Battery,
|
battery: Battery,
|
||||||
|
connected_devices: Vec<ConnectedDevice>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl page::Page<crate::pages::Message> for Page {
|
impl page::Page<crate::pages::Message> for Page {
|
||||||
|
|
@ -29,6 +34,7 @@ impl page::Page<crate::pages::Message> for Page {
|
||||||
) -> Option<page::Content> {
|
) -> Option<page::Content> {
|
||||||
Some(vec![
|
Some(vec![
|
||||||
sections.insert(battery_info()),
|
sections.insert(battery_info()),
|
||||||
|
sections.insert(connected_devices()),
|
||||||
sections.insert(profiles()),
|
sections.insert(profiles()),
|
||||||
])
|
])
|
||||||
}
|
}
|
||||||
|
|
@ -38,11 +44,18 @@ impl page::Page<crate::pages::Message> for Page {
|
||||||
_page: cosmic_settings_page::Entity,
|
_page: cosmic_settings_page::Entity,
|
||||||
_sender: tokio::sync::mpsc::Sender<crate::pages::Message>,
|
_sender: tokio::sync::mpsc::Sender<crate::pages::Message>,
|
||||||
) -> cosmic::Command<crate::pages::Message> {
|
) -> cosmic::Command<crate::pages::Message> {
|
||||||
cosmic::command::future(async move {
|
let futures: Vec<Command<Message>> = vec![
|
||||||
let battery = Battery::update_battery().await;
|
cosmic::command::future(async move {
|
||||||
Message::UpdateBattery(battery)
|
let battery = Battery::update_battery().await;
|
||||||
})
|
Message::UpdateBattery(battery)
|
||||||
.map(crate::pages::Message::Power)
|
}),
|
||||||
|
cosmic::command::future(async move {
|
||||||
|
let devices = ConnectedDevice::update_connected_devices().await;
|
||||||
|
Message::UpdateConnectedDevices(devices)
|
||||||
|
}),
|
||||||
|
];
|
||||||
|
|
||||||
|
cosmic::command::batch(futures).map(crate::pages::Message::Power)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -50,6 +63,7 @@ impl page::Page<crate::pages::Message> for Page {
|
||||||
pub enum Message {
|
pub enum Message {
|
||||||
PowerProfileChange(PowerProfile),
|
PowerProfileChange(PowerProfile),
|
||||||
UpdateBattery(Battery),
|
UpdateBattery(Battery),
|
||||||
|
UpdateConnectedDevices(Vec<ConnectedDevice>),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Page {
|
impl Page {
|
||||||
|
|
@ -65,6 +79,9 @@ impl Page {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Message::UpdateBattery(battery) => self.battery = battery,
|
Message::UpdateBattery(battery) => self.battery = battery,
|
||||||
|
Message::UpdateConnectedDevices(connected_devices) => {
|
||||||
|
self.connected_devices = connected_devices;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -78,13 +95,14 @@ fn battery_info() -> Section<crate::pages::Message> {
|
||||||
.show_while::<Page>(|page| page.battery.is_present)
|
.show_while::<Page>(|page| page.battery.is_present)
|
||||||
.view::<Page>(move |_binder, page, section| {
|
.view::<Page>(move |_binder, page, section| {
|
||||||
let battery_icon = widget::icon::from_name(page.battery.icon_name.clone());
|
let battery_icon = widget::icon::from_name(page.battery.icon_name.clone());
|
||||||
let battery_label = text::body(format!(
|
let remaining_time = page.battery.remaining_time();
|
||||||
"{}% {}",
|
let battery_label = text::body(if remaining_time.is_empty() {
|
||||||
page.battery.percent,
|
format!("{}%", page.battery.percent)
|
||||||
page.battery.remaining_time()
|
} else {
|
||||||
));
|
format!("{}% ({})", page.battery.percent, remaining_time)
|
||||||
|
});
|
||||||
|
|
||||||
column::with_capacity(2)
|
widget::column::with_capacity(2)
|
||||||
.push(text::heading(§ion.title))
|
.push(text::heading(§ion.title))
|
||||||
.push(
|
.push(
|
||||||
row!(battery_icon, battery_label)
|
row!(battery_icon, battery_label)
|
||||||
|
|
@ -95,6 +113,83 @@ fn battery_info() -> Section<crate::pages::Message> {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn connected_devices() -> Section<crate::pages::Message> {
|
||||||
|
let descriptions = Slab::new();
|
||||||
|
|
||||||
|
Section::default()
|
||||||
|
.title(fl!("connected-devices"))
|
||||||
|
.descriptions(descriptions)
|
||||||
|
.show_while::<Page>(|page| !page.connected_devices.is_empty())
|
||||||
|
.view::<Page>(move |_binder, page, section| {
|
||||||
|
let devices: Vec<cosmic::Element<'_, _>> = page
|
||||||
|
.connected_devices
|
||||||
|
.iter()
|
||||||
|
.map(|connected_device| {
|
||||||
|
let battery_icon =
|
||||||
|
widget::icon::from_name(connected_device.battery.icon_name.clone());
|
||||||
|
|
||||||
|
let battery_percent_and_time = widget::text(
|
||||||
|
if connected_device.battery.remaining_duration > TimeDelta::zero() {
|
||||||
|
format!(
|
||||||
|
"{}% - {}",
|
||||||
|
connected_device.battery.percent,
|
||||||
|
&connected_device.battery.remaining_time()
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
format!("{}%", connected_device.battery.percent)
|
||||||
|
},
|
||||||
|
);
|
||||||
|
widget::container(
|
||||||
|
row!(
|
||||||
|
widget::icon::from_name(connected_device.device_icon).size(48),
|
||||||
|
column!(
|
||||||
|
text::heading(&connected_device.model),
|
||||||
|
row!(battery_icon, battery_percent_and_time)
|
||||||
|
.spacing(4)
|
||||||
|
.align_items(Alignment::Center),
|
||||||
|
)
|
||||||
|
.height(Length::Shrink)
|
||||||
|
)
|
||||||
|
.align_items(Alignment::Center)
|
||||||
|
.spacing(16)
|
||||||
|
.padding([8, 16])
|
||||||
|
.width(Length::Fill)
|
||||||
|
.height(Length::Fill),
|
||||||
|
)
|
||||||
|
.height(64)
|
||||||
|
.style(cosmic::theme::Container::List)
|
||||||
|
.into()
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
widget::column::with_capacity(2)
|
||||||
|
.spacing(8)
|
||||||
|
.push(text::heading(§ion.title))
|
||||||
|
.push(
|
||||||
|
widget::column()
|
||||||
|
.extend(
|
||||||
|
devices
|
||||||
|
.into_iter()
|
||||||
|
.chunks(2)
|
||||||
|
.into_iter()
|
||||||
|
.map(|mut device_row| {
|
||||||
|
row!(
|
||||||
|
device_row.next().unwrap_or(
|
||||||
|
widget::horizontal_space(Length::Fill).into()
|
||||||
|
),
|
||||||
|
device_row.next().unwrap_or(
|
||||||
|
widget::horizontal_space(Length::Fill).into()
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.spacing(8)
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
.spacing(8),
|
||||||
|
)
|
||||||
|
.into()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
fn profiles() -> Section<crate::pages::Message> {
|
fn profiles() -> Section<crate::pages::Message> {
|
||||||
let mut descriptions = Slab::new();
|
let mut descriptions = Slab::new();
|
||||||
|
|
||||||
|
|
@ -119,7 +214,7 @@ fn profiles() -> Section<crate::pages::Message> {
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|profile| {
|
.map(|profile| {
|
||||||
settings::item_row(vec![radio(
|
settings::item_row(vec![radio(
|
||||||
column::with_capacity(2)
|
widget::column::with_capacity(2)
|
||||||
.push(text::body(profile.title()))
|
.push(text::body(profile.title()))
|
||||||
.push(text::caption(profile.description())),
|
.push(text::caption(profile.description())),
|
||||||
profile.clone(),
|
profile.clone(),
|
||||||
|
|
|
||||||
|
|
@ -318,10 +318,13 @@ battery = Battery
|
||||||
}
|
}
|
||||||
.less-than-minute = Less than a minute
|
.less-than-minute = Less than a minute
|
||||||
.and = and
|
.and = and
|
||||||
.remaining-time = ({ $time } until { $action ->
|
.remaining-time = { $time } until { $action ->
|
||||||
[full] full
|
[full] full
|
||||||
*[other] empty
|
*[other] empty
|
||||||
})
|
}
|
||||||
|
|
||||||
|
connected-devices = Connected Devices
|
||||||
|
.unknown = Unknown device
|
||||||
|
|
||||||
power-mode = Power Mode
|
power-mode = Power Mode
|
||||||
.battery = Extended battery life
|
.battery = Extended battery life
|
||||||
|
|
|
||||||
|
|
@ -308,10 +308,13 @@ battery = Batterie
|
||||||
}
|
}
|
||||||
.less-than-minute = Moins d'une minute
|
.less-than-minute = Moins d'une minute
|
||||||
.and = et
|
.and = et
|
||||||
.remaining-time = ({ $time } jusqu'à la { $action ->
|
.remaining-time = { $time } jusqu'à la { $action ->
|
||||||
[full] charge
|
[full] charge
|
||||||
*[other] decharge
|
*[other] decharge
|
||||||
} complète)
|
} complète
|
||||||
|
|
||||||
|
connected-devices = Périphériques connectés
|
||||||
|
.unknown = Périphériques inconnu
|
||||||
|
|
||||||
power-profiles = Modes d'énergie
|
power-profiles = Modes d'énergie
|
||||||
.battery = Économie d'énergie
|
.battery = Économie d'énergie
|
||||||
|
|
|
||||||
|
|
@ -297,7 +297,7 @@ power = Alimentazione e batteria
|
||||||
.desc = Gestione impostazioni energetiche
|
.desc = Gestione impostazioni energetiche
|
||||||
|
|
||||||
battery = Batteria
|
battery = Batteria
|
||||||
.remaining-time = ({ $time } rimasti)
|
.remaining-time = { $time } rimasti
|
||||||
|
|
||||||
power-mode = Power Mode
|
power-mode = Power Mode
|
||||||
.battery = Estendi la vita della batteria
|
.battery = Estendi la vita della batteria
|
||||||
|
|
|
||||||
|
|
@ -308,7 +308,7 @@ power = Zasilanie
|
||||||
.desc = Zarządzaj ustawieniami zasilania
|
.desc = Zarządzaj ustawieniami zasilania
|
||||||
|
|
||||||
battery = Bateria
|
battery = Bateria
|
||||||
.remaining-time = ({ $time } pozostało)
|
.remaining-time = { $time } pozostało
|
||||||
|
|
||||||
power-mode = Profile Zasilania
|
power-mode = Profile Zasilania
|
||||||
.performance = Tryb Wysokowydajny
|
.performance = Tryb Wysokowydajny
|
||||||
|
|
|
||||||
|
|
@ -294,7 +294,7 @@ power = Energia & Bateria
|
||||||
.desc = Gere as configurações da energia
|
.desc = Gere as configurações da energia
|
||||||
|
|
||||||
battery = Bateria
|
battery = Bateria
|
||||||
.remaining-time = ({ $time } restante/s)
|
.remaining-time = { $time } restante/s
|
||||||
|
|
||||||
power-mode = Modo de Energia
|
power-mode = Modo de Energia
|
||||||
.battery = Expande a vida da bateria
|
.battery = Expande a vida da bateria
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue