Merge lp:~nathandyer/appcenter/restart-notification into lp:~elementary-apps/appcenter/appcenter

Proposed by Nathan Dyer
Status: Merged
Approved by: Cody Garver
Approved revision: 315
Merged at revision: 415
Proposed branch: lp:~nathandyer/appcenter/restart-notification
Merge into: lp:~elementary-apps/appcenter/appcenter
Diff against target: 329 lines (+177/-12)
6 files modified
src/CMakeLists.txt (+2/-0)
src/Core/Client.vala (+25/-0)
src/Services/DbusInterfaces.vala (+24/-0)
src/Views/AppListView.vala (+23/-2)
src/Widgets/RestartDialog.vala (+87/-0)
src/Widgets/UpdateHeaderRow.vala (+16/-10)
To merge this branch: bzr merge lp:~nathandyer/appcenter/restart-notification
Reviewer Review Type Date Requested Status
Nathan Dyer (community) Approve
Review via email: mp+320067@code.launchpad.net

Commit message

Send an urgent notification when updates require a system reboot

To post a comment you must log in.
Revision history for this message
Nathan Dyer (nathandyer) wrote :

All the code looks great. Made one change to make the notification stay on the screen, but otherwise, good to go.

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'src/CMakeLists.txt'
2--- src/CMakeLists.txt 2017-02-10 20:15:32 +0000
3+++ src/CMakeLists.txt 2017-03-16 15:17:06 +0000
4@@ -23,6 +23,8 @@
5 Widgets/AppListRow.vala
6 Widgets/AppActionButton.vala
7 Widgets/UpdateHeaderRow.vala
8+ Widgets/RestartDialog.vala
9+ Services/DbusInterfaces.vala
10 ${CMAKE_CURRENT_BINARY_DIR}/config.vala
11 PACKAGES
12 appstream
13
14=== modified file 'src/Core/Client.vala'
15--- src/Core/Client.vala 2017-02-04 17:18:30 +0000
16+++ src/Core/Client.vala 2017-03-16 15:17:06 +0000
17@@ -17,6 +17,8 @@
18 public class AppCenterCore.Client : Object {
19 public signal void updates_available ();
20
21+ private const string RESTART_REQUIRED_FILE = "/var/run/reboot-required";
22+
23 public bool connected { public get; private set; }
24 public bool updating_cache { public get; private set; }
25 public uint task_count { public get; private set; default = 0; }
26@@ -29,6 +31,7 @@
27 private GLib.DateTime last_cache_update;
28 private uint updates_number = 0U;
29 private uint update_cache_timeout_id = 0;
30+ private bool restart_required = false;
31 private bool refresh_in_progress = false;
32
33 private const int SECONDS_BETWEEN_REFRESHES = 60*60*24;
34@@ -354,6 +357,26 @@
35 task_count--;
36 refresh_in_progress = false;
37 }
38+
39+ public async bool check_restart () {
40+ if (FileUtils.test (RESTART_REQUIRED_FILE, FileTest.EXISTS)) {
41+ if (!restart_required) {
42+ string title = _("Restart Required");
43+ string body = _("Please restart your system to finalize updates");
44+ var notification = new Notification (title);
45+ notification.set_body (body);
46+ notification.set_icon (new ThemedIcon ("system-software-install"));
47+ notification.set_priority (NotificationPriority.URGENT);
48+ notification.set_default_action ("app.open-application");
49+ Application.get_default ().send_notification ("restart", notification);
50+ }
51+
52+ restart_required = true;
53+ return true;
54+ }
55+
56+ return false;
57+ }
58
59 public uint get_package_count (GLib.GenericArray<weak Pk.Package> package_array) {
60 bool os_update_found = false;
61@@ -436,6 +459,8 @@
62 } else {
63 refresh_in_progress = false; //Stops new timeout while no network.
64 }
65+
66+ check_restart.begin ();
67 } else {
68 debug ("Too soon to refresh and not forced");
69 }
70
71=== added directory 'src/Services'
72=== added file 'src/Services/DbusInterfaces.vala'
73--- src/Services/DbusInterfaces.vala 1970-01-01 00:00:00 +0000
74+++ src/Services/DbusInterfaces.vala 2017-03-16 15:17:06 +0000
75@@ -0,0 +1,24 @@
76+// -*- Mode: vala; indent-tabs-mode: nil; tab-width: 4 -*-
77+/*-
78+ * Copyright (c) 2014-2016 elementary LLC. (https://elementary.io)
79+ *
80+ * This program is free software: you can redistribute it and/or modify
81+ * it under the terms of the GNU General Public License as published by
82+ * the Free Software Foundation, either version 3 of the License, or
83+ * (at your option) any later version.
84+ *
85+ * This program is distributed in the hope that it will be useful,
86+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
87+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
88+ * GNU General Public License for more details.
89+ *
90+ * You should have received a copy of the GNU General Public License
91+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
92+ */
93+
94+/* Power and system control */
95+[DBus (name = "org.freedesktop.login1.Manager")]
96+interface SystemInterface : Object {
97+ public abstract void reboot (bool interactive) throws IOError;
98+}
99+
100
101=== modified file 'src/Views/AppListView.vala'
102--- src/Views/AppListView.vala 2017-02-15 18:02:21 +0000
103+++ src/Views/AppListView.vala 2017-03-16 15:17:06 +0000
104@@ -89,6 +89,7 @@
105 private Widgets.UpdateHeaderRow updates_header;
106 private Widgets.UpdateHeaderRow updated_header;
107 private Widgets.AppActionButton update_all_button;
108+ private Widgets.AppActionButton restart_button;
109 private bool updating_all_apps = false;
110 private bool apps_remaining_started = false;
111 private GLib.Mutex update_mutex;
112@@ -118,11 +119,21 @@
113 update_all_button.clicked.connect (on_update_all);
114 update_all_button.no_show_all = true;
115 action_button_group.add_widget (update_all_button);
116+
117+ restart_button = new Widgets.AppActionButton (_("Restart Now"));
118+ restart_button.set_suggested_action_header ();
119+ restart_button.clicked.connect (() => {
120+ var dialog = new Widgets.RestartDialog ();
121+ dialog.show_all ();
122+ });
123+ restart_button.no_show_all = true;
124+ action_button_group.add_widget (restart_button);
125
126 updates_header = new Widgets.UpdateHeaderRow.updates ();
127 updates_header.add_widget (update_all_button);
128
129 updated_header = new Widgets.UpdateHeaderRow.updated ();
130+ updated_header.add_widget (restart_button);
131
132 list_box.add (updates_header);
133 list_box.add (updated_header);
134@@ -308,12 +319,22 @@
135 }
136 }
137
138- updates_header.update (update_numbers, update_real_size, updating_cache);
139+ updates_header.update (update_numbers, update_real_size, updating_cache, false);
140 update_all_button.visible = !updating_cache && update_numbers > 0;
141 }
142
143 private void update_updated_grid () {
144- updated_header.update (0, 0, updating_cache);
145+ if (!updating_all_apps && !updating_cache) {
146+ var client = AppCenterCore.Client.get_default ();
147+ client.check_restart.begin ((obj, res) => {
148+ var restart_required = client.check_restart.end (res);
149+
150+ updated_header.update (0, 0, updating_cache, restart_required);
151+ restart_button.visible = restart_required;
152+ });
153+ } else {
154+ updated_header.update (0, 0, updating_cache, false);
155+ }
156 }
157 }
158 }
159
160=== added file 'src/Widgets/RestartDialog.vala'
161--- src/Widgets/RestartDialog.vala 1970-01-01 00:00:00 +0000
162+++ src/Widgets/RestartDialog.vala 2017-03-16 15:17:06 +0000
163@@ -0,0 +1,87 @@
164+// -*- Mode: vala; indent-tabs-mode: nil; tab-width: 4 -*-
165+/*-
166+ * Copyright (c) 2014-2016 elementary LLC. (https://elementary.io)
167+ *
168+ * This program is free software: you can redistribute it and/or modify
169+ * it under the terms of the GNU General Public License as published by
170+ * the Free Software Foundation, either version 3 of the License, or
171+ * (at your option) any later version.
172+ *
173+ * This program is distributed in the hope that it will be useful,
174+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
175+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
176+ * GNU General Public License for more details.
177+ *
178+ * You should have received a copy of the GNU General Public License
179+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
180+ */
181+
182+namespace AppCenter.Widgets {
183+ public class RestartDialog : Gtk.Dialog {
184+ private SystemInterface system_interface;
185+
186+ public RestartDialog () {
187+ Object (title: "", deletable: false, skip_taskbar_hint: true, skip_pager_hint: true, type_hint: Gdk.WindowTypeHint.DIALOG);
188+ }
189+
190+ construct {
191+ try {
192+ system_interface = Bus.get_proxy_sync (BusType.SYSTEM, "org.freedesktop.login1", "/org/freedesktop/login1");
193+ } catch (IOError e) {
194+ stderr.printf ("%s\n", e.message);
195+ }
196+
197+ /*
198+ * the restart type is currently used by the indicator for what is
199+ * labelled shutdown because of unity's implementation of it
200+ * apparently. So we got to adjust to that until they fix this.
201+ */
202+ string icon_name = "system-shutdown";
203+ string heading_text = _("Are you sure you want to Restart?");
204+ string content_text = _("This will close all open applications and restart this device.");
205+ string button_text = _("Restart");
206+
207+ set_position (Gtk.WindowPosition.CENTER_ALWAYS);
208+ set_keep_above (true);
209+ stick ();
210+ set_resizable (false);
211+
212+ var heading = new Gtk.Label ("<span weight='bold' size='larger'>" + heading_text + "</span>");
213+ heading.get_style_context ().add_class ("larger");
214+ heading.use_markup = true;
215+ heading.halign = Gtk.Align.START;
216+
217+ var grid = new Gtk.Grid ();
218+ grid.column_spacing = 12;
219+ grid.row_spacing = 12;
220+ grid.margin_left = grid.margin_right = grid.margin_bottom = 12;
221+ grid.attach (new Gtk.Image.from_icon_name (icon_name, Gtk.IconSize.DIALOG), 0, 0, 1, 2);
222+ grid.attach (heading, 1, 0, 1, 1);
223+ grid.attach (new Gtk.Label (content_text), 1, 1, 1, 1);
224+
225+ var cancel = add_button (_("Cancel"), Gtk.ResponseType.CANCEL) as Gtk.Button;
226+ cancel.clicked.connect (() => { destroy (); });
227+
228+ var confirm = add_button (button_text, Gtk.ResponseType.OK) as Gtk.Button;
229+ confirm.get_style_context ().add_class ("destructive-action");
230+ confirm.clicked.connect (() => {
231+ try {
232+ system_interface.reboot (false);
233+ } catch (IOError e) {
234+ stderr.printf ("%s\n", e.message);
235+ }
236+
237+ destroy ();
238+ });
239+
240+ set_default (confirm);
241+
242+ get_content_area ().add (grid);
243+
244+ var action_area = get_action_area ();
245+ action_area.margin_right = 6;
246+ action_area.margin_bottom = 6;
247+ }
248+ }
249+}
250+
251
252=== modified file 'src/Widgets/UpdateHeaderRow.vala'
253--- src/Widgets/UpdateHeaderRow.vala 2016-09-17 19:28:03 +0000
254+++ src/Widgets/UpdateHeaderRow.vala 2017-03-16 15:17:06 +0000
255@@ -70,8 +70,8 @@
256 grid.add (widget);
257 }
258
259- public void update (uint _update_numbers, uint64 _update_real_size, bool _is_updating) {
260- grid.update (_update_numbers, _update_real_size, _is_updating);
261+ public void update (uint _update_numbers, uint64 _update_real_size, bool _is_updating, bool _restart_required) {
262+ grid.update (_update_numbers, _update_real_size, _is_updating, _restart_required);
263 changed (); /* Triggers resort */
264 }
265 /** ---------------- **/
266@@ -81,19 +81,21 @@
267 public uint update_numbers {get; protected set; default = 0;}
268 public uint64 update_real_size {get; protected set; default = 0;}
269 public bool is_updating {get; protected set; default = false;}
270+ public bool restart_required {get; protected set; default = false;}
271
272 construct {
273 margin = 12;
274 column_spacing = 12;
275 }
276
277- protected void store_data (uint _update_numbers, uint64 _update_real_size, bool _is_updating) {
278+ protected void store_data (uint _update_numbers, uint64 _update_real_size, bool _is_updating, bool _restart_required) {
279 update_numbers = _update_numbers;
280 update_real_size = _update_real_size;
281 is_updating = _is_updating;
282+ restart_required = _restart_required;
283 }
284
285- public abstract void update (uint _update_numbers, uint64 _update_real_size, bool _is_updating);
286+ public abstract void update (uint _update_numbers, uint64 _update_real_size, bool _is_updating, bool _restart_required);
287 }
288
289 /** Header to show at top of list if there are updates available **/
290@@ -114,8 +116,8 @@
291 add (update_size_label);
292 }
293
294- public override void update (uint _update_numbers, uint64 _update_real_size, bool _is_updating) {
295- store_data (_update_numbers, _update_real_size, _is_updating);
296+ public override void update (uint _update_numbers, uint64 _update_real_size, bool _is_updating, bool _restart_required) {
297+ store_data (_update_numbers, _update_real_size, _is_updating, _restart_required);
298
299 if (!is_updating) {
300 updates_label.label = ngettext ("%u Update Available", "%u Updates Available", update_numbers).printf (update_numbers);
301@@ -147,8 +149,8 @@
302 add (spinner);
303 }
304
305- public override void update (uint _update_numbers, uint64 _update_real_size, bool _is_updating) {
306- store_data (_update_numbers, _update_real_size, _is_updating);
307+ public override void update (uint _update_numbers, uint64 _update_real_size, bool _is_updating, bool _restart_required) {
308+ store_data (_update_numbers, _update_real_size, _is_updating, _restart_required);
309
310 if (is_updating) {
311 halign = Gtk.Align.CENTER;
312@@ -158,11 +160,15 @@
313 label.label = _("Searching for updates…");
314 label.get_style_context ().remove_class ("h4");
315 } else {
316- halign = Gtk.Align.START;
317+ halign = Gtk.Align.FILL;
318 spinner.stop ();
319 spinner.no_show_all = true;
320 spinner.hide ();
321- label.label = _("Up to Date");
322+ if (restart_required) {
323+ label.label = _("Restart required");
324+ } else {
325+ label.label = _("Up to Date");
326+ }
327 label.get_style_context ().add_class ("h4");
328 }
329 }

Subscribers

People subscribed via source and target branches