endSessionDialog: Offer offline updates in the shutdown dialog
This adds a checkbox for installing software updates to the shut down dialog. The implementation relies on an already prepared offline update and uses PackageKit's pk-trigger-offline-update helper to trigger the installation. https://bugzilla.gnome.org/show_bug.cgi?id=722898
This commit is contained in:
parent
7551e134da
commit
645ef093f7
@ -1947,10 +1947,6 @@ StScrollBar StButton#vhandle:active {
|
|||||||
padding-top: 20px;
|
padding-top: 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.end-session-dialog-subject {
|
|
||||||
padding-bottom: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.end-session-dialog-layout {
|
.end-session-dialog-layout {
|
||||||
padding-left: 17px;
|
padding-left: 17px;
|
||||||
}
|
}
|
||||||
@ -1961,9 +1957,12 @@ StScrollBar StButton#vhandle:active {
|
|||||||
|
|
||||||
.end-session-dialog-description {
|
.end-session-dialog-description {
|
||||||
width: 28em;
|
width: 28em;
|
||||||
|
padding-bottom: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.end-session-dialog-description:rtl {
|
.end-session-dialog-description:rtl {
|
||||||
|
width: 28em;
|
||||||
|
padding-bottom: 10px;
|
||||||
text-align: right;
|
text-align: right;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -25,9 +25,11 @@ const Gio = imports.gi.Gio;
|
|||||||
const GLib = imports.gi.GLib;
|
const GLib = imports.gi.GLib;
|
||||||
const Gtk = imports.gi.Gtk;
|
const Gtk = imports.gi.Gtk;
|
||||||
const Pango = imports.gi.Pango;
|
const Pango = imports.gi.Pango;
|
||||||
|
const Polkit = imports.gi.Polkit;
|
||||||
const St = imports.gi.St;
|
const St = imports.gi.St;
|
||||||
const Shell = imports.gi.Shell;
|
const Shell = imports.gi.Shell;
|
||||||
|
|
||||||
|
const CheckBox = imports.ui.checkBox;
|
||||||
const GnomeSession = imports.misc.gnomeSession;
|
const GnomeSession = imports.misc.gnomeSession;
|
||||||
const LoginManager = imports.misc.loginManager;
|
const LoginManager = imports.misc.loginManager;
|
||||||
const ModalDialog = imports.ui.modalDialog;
|
const ModalDialog = imports.ui.modalDialog;
|
||||||
@ -36,6 +38,8 @@ const UserWidget = imports.ui.userWidget;
|
|||||||
|
|
||||||
let _endSessionDialog = null;
|
let _endSessionDialog = null;
|
||||||
|
|
||||||
|
const TRIGGER_OFFLINE_UPDATE = '/usr/libexec/pk-trigger-offline-update';
|
||||||
|
|
||||||
const _ITEM_ICON_SIZE = 48;
|
const _ITEM_ICON_SIZE = 48;
|
||||||
const _DIALOG_ICON_SIZE = 48;
|
const _DIALOG_ICON_SIZE = 48;
|
||||||
|
|
||||||
@ -79,11 +83,13 @@ const logoutDialogContent = {
|
|||||||
|
|
||||||
const shutdownDialogContent = {
|
const shutdownDialogContent = {
|
||||||
subject: C_("title", "Power Off"),
|
subject: C_("title", "Power Off"),
|
||||||
|
subjectWithUpdates: C_("title", "Install Updates & Power Off"),
|
||||||
description: function(seconds) {
|
description: function(seconds) {
|
||||||
return ngettext("The system will power off automatically in %d second.",
|
return ngettext("The system will power off automatically in %d second.",
|
||||||
"The system will power off automatically in %d seconds.",
|
"The system will power off automatically in %d seconds.",
|
||||||
seconds).format(seconds);
|
seconds).format(seconds);
|
||||||
},
|
},
|
||||||
|
checkBoxText: C_("checkbox", "Install pending software updates"),
|
||||||
confirmButtons: [{ signal: 'ConfirmedReboot',
|
confirmButtons: [{ signal: 'ConfirmedReboot',
|
||||||
label: C_("button", "Restart") },
|
label: C_("button", "Restart") },
|
||||||
{ signal: 'ConfirmedShutdown',
|
{ signal: 'ConfirmedShutdown',
|
||||||
@ -195,6 +201,18 @@ function _setLabelText(label, text) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function _setCheckBoxLabel(checkBox, text) {
|
||||||
|
let label = checkBox.getLabelActor();
|
||||||
|
|
||||||
|
if (text) {
|
||||||
|
label.set_text(text);
|
||||||
|
checkBox.actor.show();
|
||||||
|
} else {
|
||||||
|
label.set_text('');
|
||||||
|
checkBox.actor.hide();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function init() {
|
function init() {
|
||||||
// This always returns the same singleton object
|
// This always returns the same singleton object
|
||||||
// By instantiating it initially, we register the
|
// By instantiating it initially, we register the
|
||||||
@ -214,6 +232,7 @@ const EndSessionDialog = new Lang.Class({
|
|||||||
this._userManager = AccountsService.UserManager.get_default();
|
this._userManager = AccountsService.UserManager.get_default();
|
||||||
this._user = this._userManager.get_user(GLib.get_user_name());
|
this._user = this._userManager.get_user(GLib.get_user_name());
|
||||||
this._updatesFile = Gio.File.new_for_path('/system-update');
|
this._updatesFile = Gio.File.new_for_path('/system-update');
|
||||||
|
this._preparedUpdateFile = Gio.File.new_for_path('/var/lib/PackageKit/prepared-update');
|
||||||
|
|
||||||
this._secondsLeft = 0;
|
this._secondsLeft = 0;
|
||||||
this._totalSecondsToStayOpen = 0;
|
this._totalSecondsToStayOpen = 0;
|
||||||
@ -261,6 +280,10 @@ const EndSessionDialog = new Lang.Class({
|
|||||||
{ y_fill: true,
|
{ y_fill: true,
|
||||||
y_align: St.Align.START });
|
y_align: St.Align.START });
|
||||||
|
|
||||||
|
this._checkBox = new CheckBox.CheckBox();
|
||||||
|
this._checkBox.actor.connect('clicked', Lang.bind(this, this._sync));
|
||||||
|
messageLayout.add(this._checkBox.actor);
|
||||||
|
|
||||||
this._scrollView = new St.ScrollView({ style_class: 'end-session-dialog-list' });
|
this._scrollView = new St.ScrollView({ style_class: 'end-session-dialog-list' });
|
||||||
this._scrollView.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC);
|
this._scrollView.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC);
|
||||||
this.contentLayout.add(this._scrollView,
|
this.contentLayout.add(this._scrollView,
|
||||||
@ -286,6 +309,12 @@ const EndSessionDialog = new Lang.Class({
|
|||||||
this._inhibitorSection.add_actor(this._sessionHeader);
|
this._inhibitorSection.add_actor(this._sessionHeader);
|
||||||
this._inhibitorSection.add_actor(this._sessionList);
|
this._inhibitorSection.add_actor(this._sessionList);
|
||||||
|
|
||||||
|
try {
|
||||||
|
this._updatesPermission = Polkit.Permission.new_sync("org.freedesktop.packagekit.trigger-offline-update", null, null);
|
||||||
|
} catch(e) {
|
||||||
|
log('No permission to trigger offline updates: %s'.format(e.toString()));
|
||||||
|
}
|
||||||
|
|
||||||
this._dbusImpl = Gio.DBusExportedObject.wrapJSObject(EndSessionDialogIface, this);
|
this._dbusImpl = Gio.DBusExportedObject.wrapJSObject(EndSessionDialogIface, this);
|
||||||
this._dbusImpl.export(Gio.DBus.session, '/org/gnome/SessionManager/EndSessionDialog');
|
this._dbusImpl.export(Gio.DBus.session, '/org/gnome/SessionManager/EndSessionDialog');
|
||||||
},
|
},
|
||||||
@ -304,6 +333,10 @@ const EndSessionDialog = new Lang.Class({
|
|||||||
|
|
||||||
let subject = dialogContent.subject;
|
let subject = dialogContent.subject;
|
||||||
|
|
||||||
|
// Use different title when we are installing updates
|
||||||
|
if (dialogContent.subjectWithUpdates && this._checkBox.actor.checked)
|
||||||
|
subject = dialogContent.subjectWithUpdates;
|
||||||
|
|
||||||
let description;
|
let description;
|
||||||
let displayTime = _roundSecondsToInterval(this._totalSecondsToStayOpen,
|
let displayTime = _roundSecondsToInterval(this._totalSecondsToStayOpen,
|
||||||
this._secondsLeft,
|
this._secondsLeft,
|
||||||
@ -386,15 +419,75 @@ const EndSessionDialog = new Lang.Class({
|
|||||||
},
|
},
|
||||||
|
|
||||||
_confirm: function(signal) {
|
_confirm: function(signal) {
|
||||||
this._fadeOutDialog();
|
let callback = Lang.bind(this, function() {
|
||||||
this._stopTimer();
|
this._fadeOutDialog();
|
||||||
this._dbusImpl.emit_signal(signal, null);
|
this._stopTimer();
|
||||||
|
this._dbusImpl.emit_signal(signal, null);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Offline update not available; just emit the signal
|
||||||
|
if (!this._checkBox.actor.visible) {
|
||||||
|
callback();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Trigger the offline update as requested
|
||||||
|
if (this._checkBox.actor.checked) {
|
||||||
|
switch (signal) {
|
||||||
|
case "ConfirmedReboot":
|
||||||
|
this._triggerOfflineUpdateReboot(callback);
|
||||||
|
break;
|
||||||
|
case "ConfirmedShutdown":
|
||||||
|
// To actually trigger the offline update, we need to
|
||||||
|
// reboot to do the upgrade. When the upgrade is complete,
|
||||||
|
// the computer will shut down automatically.
|
||||||
|
signal = "ConfirmedReboot";
|
||||||
|
this._triggerOfflineUpdateShutdown(callback);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
callback();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this._triggerOfflineUpdateCancel(callback);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
_onOpened: function() {
|
_onOpened: function() {
|
||||||
this._sync();
|
this._sync();
|
||||||
},
|
},
|
||||||
|
|
||||||
|
_triggerOfflineUpdateReboot: function(callback) {
|
||||||
|
this._pkexecSpawn([TRIGGER_OFFLINE_UPDATE, 'reboot'], callback);
|
||||||
|
},
|
||||||
|
|
||||||
|
_triggerOfflineUpdateShutdown: function(callback) {
|
||||||
|
this._pkexecSpawn([TRIGGER_OFFLINE_UPDATE, 'power-off'], callback);
|
||||||
|
},
|
||||||
|
|
||||||
|
_triggerOfflineUpdateCancel: function(callback) {
|
||||||
|
this._pkexecSpawn([TRIGGER_OFFLINE_UPDATE, '--cancel'], callback);
|
||||||
|
},
|
||||||
|
|
||||||
|
_pkexecSpawn: function(argv, callback) {
|
||||||
|
let ret, pid;
|
||||||
|
try {
|
||||||
|
[ret, pid] = GLib.spawn_async(null, ['pkexec'].concat(argv), null,
|
||||||
|
GLib.SpawnFlags.DO_NOT_REAP_CHILD | GLib.SpawnFlags.SEARCH_PATH,
|
||||||
|
null);
|
||||||
|
} catch (e) {
|
||||||
|
log('Error spawning "pkexec %s": %s'.format(argv.join(' '), e.toString()));
|
||||||
|
callback();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
GLib.child_watch_add(GLib.PRIORITY_DEFAULT, pid, function(pid, status) {
|
||||||
|
GLib.spawn_close_pid(pid);
|
||||||
|
|
||||||
|
callback();
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
_startTimer: function() {
|
_startTimer: function() {
|
||||||
let startTime = GLib.get_monotonic_time();
|
let startTime = GLib.get_monotonic_time();
|
||||||
this._secondsLeft = this._totalSecondsToStayOpen;
|
this._secondsLeft = this._totalSecondsToStayOpen;
|
||||||
@ -557,6 +650,8 @@ const EndSessionDialog = new Lang.Class({
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let dialogContent = DialogContent[this._type];
|
||||||
|
|
||||||
for (let i = 0; i < inhibitorObjectPaths.length; i++) {
|
for (let i = 0; i < inhibitorObjectPaths.length; i++) {
|
||||||
let inhibitor = new GnomeSession.Inhibitor(inhibitorObjectPaths[i], Lang.bind(this, function(proxy, error) {
|
let inhibitor = new GnomeSession.Inhibitor(inhibitorObjectPaths[i], Lang.bind(this, function(proxy, error) {
|
||||||
this._onInhibitorLoaded(proxy);
|
this._onInhibitorLoaded(proxy);
|
||||||
@ -565,9 +660,17 @@ const EndSessionDialog = new Lang.Class({
|
|||||||
this._applications.push(inhibitor);
|
this._applications.push(inhibitor);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (DialogContent[type].showOtherSessions)
|
if (dialogContent.showOtherSessions)
|
||||||
this._loadSessions();
|
this._loadSessions();
|
||||||
|
|
||||||
|
let preparedUpdate = this._preparedUpdateFile.query_exists(null);
|
||||||
|
let updateAlreadyTriggered = this._updatesFile.query_exists(null);
|
||||||
|
let updatesAllowed = this._updatesPermission && this._updatesPermission.allowed;
|
||||||
|
|
||||||
|
_setCheckBoxLabel(this._checkBox, dialogContent.checkBoxText);
|
||||||
|
this._checkBox.actor.visible = (dialogContent.checkBoxText && preparedUpdate && updatesAllowed);
|
||||||
|
this._checkBox.actor.checked = (preparedUpdate && updateAlreadyTriggered);
|
||||||
|
|
||||||
this._updateButtons();
|
this._updateButtons();
|
||||||
|
|
||||||
if (!this.open(timestamp)) {
|
if (!this.open(timestamp)) {
|
||||||
|
Loading…
Reference in New Issue
Block a user