endSessionDialog: default to not installing updates on low battery

If the user's battery power is low, we should not check the checkbox to
install updates by default. Rationale: if the user's battery is not low,
it's very unlikely to run out during a normal system upgrade. Low
battery is defined as any level below 30%, matching our battery status
indicator.

We'll also change the battery warning to only display when battery is
actually low. However, we will still always warn on battery for full
system upgrades, since these are expected to take a long time.

Future improvement: it would be nice to make the checkbox insensitive
when on low power. However, I don't think we currently have a proper
style for insensitive checkboxes. I was unable to make it look good.

Lastly, note that I did not test this on a laptop. I tested this by
mocking the return values of _isDischargingBattery() and
_isBatteryLow().

Fixes https://gitlab.gnome.org/GNOME/gnome-shell/-/issues/2717
This commit is contained in:
Michael Catanzaro 2020-07-24 19:08:16 -05:00 committed by Florian Müllner
parent adc8b1ba89
commit 6d38bc69ca

View File

@ -18,7 +18,7 @@
*/ */
const { AccountsService, Clutter, Gio, const { AccountsService, Clutter, Gio,
GLib, GObject, Pango, Polkit, Shell, St } = imports.gi; GLib, GObject, Pango, Polkit, Shell, St, UPowerGlib: UPower } = imports.gi;
const CheckBox = imports.ui.checkBox; const CheckBox = imports.ui.checkBox;
const Dialog = imports.ui.dialog; const Dialog = imports.ui.dialog;
@ -31,6 +31,8 @@ const { loadInterfaceXML } = imports.misc.fileUtils;
const _ITEM_ICON_SIZE = 64; const _ITEM_ICON_SIZE = 64;
const LOW_BATTERY_THRESHOLD = 30;
const EndSessionDialogIface = loadInterfaceXML('org.gnome.SessionManager.EndSessionDialog'); const EndSessionDialogIface = loadInterfaceXML('org.gnome.SessionManager.EndSessionDialog');
const logoutDialogContent = { const logoutDialogContent = {
@ -157,7 +159,7 @@ const LogindSession = Gio.DBusProxy.makeProxyWrapper(LogindSessionIface);
const PkOfflineIface = loadInterfaceXML('org.freedesktop.PackageKit.Offline'); const PkOfflineIface = loadInterfaceXML('org.freedesktop.PackageKit.Offline');
const PkOfflineProxy = Gio.DBusProxy.makeProxyWrapper(PkOfflineIface); const PkOfflineProxy = Gio.DBusProxy.makeProxyWrapper(PkOfflineIface);
const UPowerIface = loadInterfaceXML('org.freedesktop.UPower'); const UPowerIface = loadInterfaceXML('org.freedesktop.UPower.Device');
const UPowerProxy = Gio.DBusProxy.makeProxyWrapper(UPowerIface); const UPowerProxy = Gio.DBusProxy.makeProxyWrapper(UPowerIface);
function findAppFromInhibitor(inhibitor) { function findAppFromInhibitor(inhibitor) {
@ -244,7 +246,7 @@ class EndSessionDialog extends ModalDialog.ModalDialog {
this._powerProxy = new UPowerProxy(Gio.DBus.system, this._powerProxy = new UPowerProxy(Gio.DBus.system,
'org.freedesktop.UPower', 'org.freedesktop.UPower',
'/org/freedesktop/UPower', '/org/freedesktop/UPower/devices/DisplayDevice',
(proxy, error) => { (proxy, error) => {
if (error) { if (error) {
log(error.message); log(error.message);
@ -279,7 +281,7 @@ class EndSessionDialog extends ModalDialog.ModalDialog {
this._batteryWarning = new St.Label({ this._batteryWarning = new St.Label({
style_class: 'end-session-dialog-battery-warning', style_class: 'end-session-dialog-battery-warning',
text: _('Running on battery power: Please plug in before installing updates.'), text: _('Low battery power: please plug in before installing updates.'),
}); });
this._batteryWarning.clutter_text.ellipsize = Pango.EllipsizeMode.NONE; this._batteryWarning.clutter_text.ellipsize = Pango.EllipsizeMode.NONE;
this._batteryWarning.clutter_text.line_wrap = true; this._batteryWarning.clutter_text.line_wrap = true;
@ -329,6 +331,32 @@ class EndSessionDialog extends ModalDialog.ModalDialog {
this._user.disconnect(this._userChangedId); this._user.disconnect(this._userChangedId);
} }
_isDischargingBattery() {
return this._powerProxy.IsPresent &&
this._powerProxy.State !== UPower.DeviceState.CHARGING &&
this._powerProxy.State !== UPower.DeviceState.FULLY_CHARGED;
}
_isBatteryLow() {
return this._isDischargingBattery() && this._powerProxy.Percentage < LOW_BATTERY_THRESHOLD;
}
_shouldShowLowBatteryWarning(dialogContent) {
if (!dialogContent.showBatteryWarning)
return false;
if (!this._isBatteryLow())
return false;
if (this._checkBox.checked)
return true;
// Show the warning if updates have already been triggered, but
// the user doesn't have enough permissions to cancel them.
let updatesAllowed = this._updatesPermission && this._updatesPermission.allowed;
return this._updateInfo.UpdatePrepared && this._updateInfo.UpdateTriggered && !updatesAllowed;
}
_sync() { _sync() {
let open = this.state == ModalDialog.State.OPENING || this.state == ModalDialog.State.OPENED; let open = this.state == ModalDialog.State.OPENING || this.state == ModalDialog.State.OPENED;
if (!open) if (!open)
@ -342,10 +370,7 @@ class EndSessionDialog extends ModalDialog.ModalDialog {
if (dialogContent.subjectWithUpdates && this._checkBox.checked) if (dialogContent.subjectWithUpdates && this._checkBox.checked)
subject = dialogContent.subjectWithUpdates; subject = dialogContent.subjectWithUpdates;
if (dialogContent.showBatteryWarning) { this._batteryWarning.visible = this._shouldShowLowBatteryWarning(dialogContent);
this._batteryWarning.visible =
this._powerProxy.OnBattery && this._checkBox.checked;
}
let description; let description;
let displayTime = _roundSecondsToInterval(this._totalSecondsToStayOpen, let displayTime = _roundSecondsToInterval(this._totalSecondsToStayOpen,
@ -748,19 +773,17 @@ class EndSessionDialog extends ModalDialog.ModalDialog {
if (dialogContent.showOtherSessions) if (dialogContent.showOtherSessions)
this._loadSessions(); this._loadSessions();
let updateTriggered = this._updateInfo.UpdateTriggered;
let updatePrepared = this._updateInfo.UpdatePrepared;
let updatesAllowed = this._updatesPermission && this._updatesPermission.allowed; let updatesAllowed = this._updatesPermission && this._updatesPermission.allowed;
_setCheckBoxLabel(this._checkBox, dialogContent.checkBoxText || ''); _setCheckBoxLabel(this._checkBox, dialogContent.checkBoxText || '');
this._checkBox.visible = dialogContent.checkBoxText && updatePrepared && updatesAllowed; this._checkBox.visible = dialogContent.checkBoxText && this._updateInfo.UpdatePrepared && updatesAllowed;
this._checkBox.checked = this._checkBox.visible;
// We show the warning either together with the checkbox, or when if (this._type === DialogType.UPGRADE_RESTART)
// updates have already been triggered, but the user doesn't have this._checkBox.checked = this._checkBox.visible && this._updateInfo.UpdateTriggered && !this._isDischargingBattery();
// enough permissions to cancel them. else
this._batteryWarning.visible = dialogContent.showBatteryWarning && this._checkBox.checked = this._checkBox.visible && !this._isBatteryLow();
(this._checkBox.visible || updatePrepared && updateTriggered && !updatesAllowed);
this._batteryWarning.visible = this._shouldShowLowBatteryWarning(dialogContent);
this._updateButtons(); this._updateButtons();