Compare commits
	
		
			2 Commits
		
	
	
		
			3.34.0
			...
			gbsneto/au
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|   | 47a2e58d45 | ||
|   | 20d73be57d | 
| @@ -1,6 +1,9 @@ | ||||
| <?xml version="1.0" encoding="UTF-8"?> | ||||
| <gresources> | ||||
|   <gresource prefix="/org/gnome/shell/theme"> | ||||
|     <file>automatic-updates-off-symbolic.svg</file> | ||||
|     <file>automatic-updates-on-symbolic.svg</file> | ||||
|     <file>automatic-updates-scheduled-symbolic.svg</file> | ||||
|     <file>calendar-today.svg</file> | ||||
|     <file>checkbox-focused.svg</file> | ||||
|     <file>checkbox-off-focused.svg</file> | ||||
|   | ||||
							
								
								
									
										1
									
								
								data/theme/automatic-updates-off-symbolic.svg
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								data/theme/automatic-updates-off-symbolic.svg
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1 @@ | ||||
| <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><title>EOS_symbolic-icons_v0.1auto-updates_OFF</title><path d="M11.03,10.074a.125.125,0,0,0-.086.213l1.347,1.35a5.733,5.733,0,0,1-4.238,1.9,5.6,5.6,0,0,1-2.282-.484l-1.11,1.11a7.024,7.024,0,0,0,3.392.875,7.3,7.3,0,0,0,5.3-2.339l1.357,1.36a.125.125,0,0,0,.213-.086L15,10Z" style="fill:#999"/><path d="M12.921,5.9a6.354,6.354,0,0,1,.326,1.863.248.248,0,0,0,.244.244h1a.261.261,0,0,0,.257-.265,7.543,7.543,0,0,0-.677-2.991Z" style="fill:#999"/><path d="M6.286,9.707a.994.994,0,0,0,.715.3H9a1,1,0,0,0,1-1v-2A.994.994,0,0,0,9.7,6.3l2.175-2.175,0,0L12.93,3.067l0,0,1.4-1.4a.25.25,0,0,0,0-.354L13.617.608a.25.25,0,0,0-.354,0L11.772,2.1A6.97,6.97,0,0,0,7.948.961,7.3,7.3,0,0,0,2.651,3.3L1.293,1.94a.125.125,0,0,0-.214.086L1,6l3.971-.074a.125.125,0,0,0,.086-.213L3.71,4.363a5.733,5.733,0,0,1,4.238-1.9,5.523,5.523,0,0,1,2.723.739L7.86,6.011H7a1,1,0,0,0-1,1V7.87L3.291,10.58a6,6,0,0,1-.537-2.346.248.248,0,0,0-.244-.245h-1a.261.261,0,0,0-.257.265A7.329,7.329,0,0,0,2.172,11.7L.608,13.263a.25.25,0,0,0,0,.354l.707.707a.25.25,0,0,0,.354,0l1.4-1.4,0,0,1.056-1.056,0,0Z" style="fill:#999"/></svg> | ||||
| After Width: | Height: | Size: 1.1 KiB | 
							
								
								
									
										1
									
								
								data/theme/automatic-updates-on-symbolic.svg
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								data/theme/automatic-updates-on-symbolic.svg
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1 @@ | ||||
| <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><title>EOS_symbolic-icons_v0.1auto-updates_ON</title><rect x="6.001" y="6.011" width="4" height="4" rx="1" ry="1" style="fill:#999"/><path d="M5.057,5.713,3.71,4.362a5.733,5.733,0,0,1,4.238-1.9,5.173,5.173,0,0,1,5.3,5.305.248.248,0,0,0,.244.244h1a.261.261,0,0,0,.257-.265A6.684,6.684,0,0,0,7.948.961,7.3,7.3,0,0,0,2.65,3.3L1.293,1.94a.125.125,0,0,0-.213.086L1,6l3.971-.074A.125.125,0,0,0,5.057,5.713Z" style="fill:#999"/><path d="M11.03,10.074a.125.125,0,0,0-.086.213l1.347,1.35a5.733,5.733,0,0,1-4.238,1.9,5.173,5.173,0,0,1-5.3-5.305.248.248,0,0,0-.244-.245h-1a.261.261,0,0,0-.257.265,6.684,6.684,0,0,0,6.8,6.785,7.3,7.3,0,0,0,5.3-2.339l1.357,1.36a.125.125,0,0,0,.214-.086L15,10Z" style="fill:#999"/></svg> | ||||
| After Width: | Height: | Size: 767 B | 
							
								
								
									
										1
									
								
								data/theme/automatic-updates-scheduled-symbolic.svg
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								data/theme/automatic-updates-scheduled-symbolic.svg
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1 @@ | ||||
| <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><title>EOS_symbolic-icons_v0.1update-scheduled_OUTLINE</title><path d="M7.99,15.054A6.7,6.7,0,0,1,1.06,8,6.7,6.7,0,0,1,7.99.954,6.71,6.71,0,0,1,14.948,8,6.71,6.71,0,0,1,7.99,15.054Zm0-12.6A5.2,5.2,0,0,0,2.56,8a5.2,5.2,0,0,0,5.43,5.55A5.215,5.215,0,0,0,13.448,8,5.216,5.216,0,0,0,7.99,2.454Z" style="fill:#999"/><path d="M9.209,10.443,7.2,8.437a.25.25,0,0,1-.073-.177l0-4.01A.25.25,0,0,1,7.379,4h1.25a.25.25,0,0,1,.25.25l0,3.283a.25.25,0,0,0,.073.177l1.5,1.494a.25.25,0,0,1,0,.354l-.883.884A.25.25,0,0,1,9.209,10.443Z" style="fill:#999"/></svg> | ||||
| After Width: | Height: | Size: 603 B | 
| @@ -9,6 +9,7 @@ | ||||
|     <file>gdm/realmd.js</file> | ||||
|     <file>gdm/util.js</file> | ||||
|  | ||||
|     <file>misc/updateManager.js</file> | ||||
|     <file>misc/config.js</file> | ||||
|     <file>misc/extensionUtils.js</file> | ||||
|     <file>misc/fileUtils.js</file> | ||||
| @@ -116,9 +117,11 @@ | ||||
|     <file>ui/components/networkAgent.js</file> | ||||
|     <file>ui/components/polkitAgent.js</file> | ||||
|     <file>ui/components/telepathyClient.js</file> | ||||
|     <file>ui/components/updates.js</file> | ||||
|     <file>ui/components/keyring.js</file> | ||||
|  | ||||
|     <file>ui/status/accessibility.js</file> | ||||
|     <file>ui/status/automaticUpdates.js</file> | ||||
|     <file>ui/status/brightness.js</file> | ||||
|     <file>ui/status/location.js</file> | ||||
|     <file>ui/status/keyboard.js</file> | ||||
|   | ||||
							
								
								
									
										338
									
								
								js/misc/updateManager.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										338
									
								
								js/misc/updateManager.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,338 @@ | ||||
| // -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*- | ||||
| // | ||||
| // Copyright (C) 2019 Endless Mobile, Inc. | ||||
| // | ||||
| // This is a GNOME Shell component to wrap the interactions over | ||||
| // D-Bus with the Mogwai system daemon. | ||||
| // | ||||
| // Licensed under the GNU General Public License Version 2 | ||||
| // | ||||
| // This program is free software; you can redistribute it and/or | ||||
| // modify it under the terms of the GNU General Public License | ||||
| // as published by the Free Software Foundation; either version 2 | ||||
| // of the License, or (at your option) any later version. | ||||
| // | ||||
| // This program is distributed in the hope that it will be useful, | ||||
| // but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
| // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
| // GNU General Public License for more details. | ||||
| // | ||||
| // You should have received a copy of the GNU General Public License | ||||
| // along with this program; if not, write to the Free Software | ||||
| // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA. | ||||
|  | ||||
| const { Clutter, Gio, GLib, | ||||
|         GObject, Gtk, NM, Shell, St } = imports.gi; | ||||
|  | ||||
| const NM_SETTING_AUTOMATIC_UPDATES_NOTIFICATION_TIME = "connection.automatic-updates-notification-time"; | ||||
| const NM_SETTING_ALLOW_DOWNLOADS = 'connection.allow-downloads'; | ||||
| const NM_SETTING_TARIFF_ENABLED = "connection.tariff-enabled"; | ||||
|  | ||||
| const SchedulerInterface = '\ | ||||
| <node> \ | ||||
|   <interface name="com.endlessm.DownloadManager1.Scheduler"> \ | ||||
|     <property name="ActiveEntryCount" type="u" access="read" /> \ | ||||
|     <property name="EntryCount" type="u" access="read" /> \ | ||||
|   </interface> \ | ||||
| </node>'; | ||||
|  | ||||
| const SchedulerProxy = Gio.DBusProxy.makeProxyWrapper(SchedulerInterface); | ||||
|  | ||||
| let _updateManager = null; | ||||
|  | ||||
| function getUpdateManager() { | ||||
|     if (_updateManager == null) | ||||
|         _updateManager = new UpdateManager(); | ||||
|     return _updateManager; | ||||
| } | ||||
|  | ||||
| var State = { | ||||
|     UNKNOWN: 0, | ||||
|     DISCONNECTED: 1, | ||||
|     DISABLED: 2, | ||||
|     IDLE: 3, | ||||
|     SCHEDULED: 4, | ||||
|     DOWNLOADING: 5 | ||||
| }; | ||||
|  | ||||
| function stateToIconName(state) { | ||||
|     switch (state) { | ||||
|     case State.UNKNOWN: | ||||
|     case State.DISCONNECTED: | ||||
|         return null; | ||||
|  | ||||
|     case State.DISABLED: | ||||
|         return 'resource:///org/gnome/shell/theme/automatic-updates-off-symbolic.svg'; | ||||
|  | ||||
|     case State.IDLE: | ||||
|     case State.DOWNLOADING: | ||||
|         return 'resource:///org/gnome/shell/theme/automatic-updates-on-symbolic.svg'; | ||||
|  | ||||
|     case State.SCHEDULED: | ||||
|         return 'resource:///org/gnome/shell/theme/automatic-updates-scheduled-symbolic.svg'; | ||||
|     } | ||||
|  | ||||
|     return null; | ||||
| } | ||||
|  | ||||
| var UpdateManager = GObject.registerClass ({ | ||||
|     Properties: { | ||||
|         'last-notification-time': GObject.ParamSpec.int('last-notification-time', | ||||
|                                                         'last-notification-time', | ||||
|                                                         'last-notification-time', | ||||
|                                                         GObject.ParamFlags.READWRITE, | ||||
|                                                         null), | ||||
|         'icon': GObject.ParamSpec.object('icon', 'icon', 'icon', | ||||
|                                          GObject.ParamFlags.READABLE, | ||||
|                                          Gio.Icon.$gtype), | ||||
|         'state': GObject.ParamSpec.uint('state', 'state', 'state', | ||||
|                                         GObject.ParamFlags.READABLE, | ||||
|                                         null), | ||||
|     }, | ||||
| }, class UpdateManager extends GObject.Object { | ||||
|     _init() { | ||||
|         super._init(); | ||||
|  | ||||
|         this._activeConnection = null; | ||||
|         this._settingChangedSignalId = 0; | ||||
|         this._updateTimeoutId = 0; | ||||
|  | ||||
|         this._state = State.UNKNOWN; | ||||
|  | ||||
|         NM.Client.new_async(null, this._clientGot.bind(this)); | ||||
|     } | ||||
|  | ||||
|     _clientGot(obj, result) { | ||||
|         this._client = NM.Client.new_finish(result); | ||||
|  | ||||
|         this._client.connect('notify::primary-connection', this._sync.bind(this)); | ||||
|         this._client.connect('notify::state', this._sync.bind(this)); | ||||
|  | ||||
|         // Start retrieving the Mogwai proxy | ||||
|         this._proxy = new SchedulerProxy(Gio.DBus.system, | ||||
|                                          'com.endlessm.MogwaiSchedule1', | ||||
|                                          '/com/endlessm/DownloadManager1', | ||||
|                                           (proxy, error) => { | ||||
|                                               if (error) { | ||||
|                                                   log(error.message); | ||||
|                                                   return; | ||||
|                                               } | ||||
|                                               this._proxy.connect('g-properties-changed', | ||||
|                                                                   this._sync.bind(this)); | ||||
|                                               this._updateStatus(); | ||||
|                                           }); | ||||
|  | ||||
|         this._sync(); | ||||
|     } | ||||
|  | ||||
|     _sync() { | ||||
|         if (!this._client || !this._proxy) | ||||
|             return; | ||||
|  | ||||
|         if (this._updateTimeoutId > 0) { | ||||
|             GLib.source_remove(this._updateTimeoutId); | ||||
|             this._updateTimeoutId = 0; | ||||
|         } | ||||
|  | ||||
|         // Intermediate states (connecting or disconnecting) must not trigger | ||||
|         // any kind of state change. | ||||
|         if (this._client.state == NM.State.CONNECTING || this._client.state == NM.State.DISCONNECTING) | ||||
|             return; | ||||
|  | ||||
|         // Use a timeout to avoid instantly throwing the notification at | ||||
|         // the user's face, and to avoid a series of unecessary updates | ||||
|         // that happen when NetworkManager is still figuring out details. | ||||
|         this._updateTimeoutId = GLib.timeout_add_seconds(GLib.PRIORITY_DEFAULT, | ||||
|                                                          2, | ||||
|                                                          () => { | ||||
|                                                             this._updateStatus(); | ||||
|                                                             this._updateTimeoutId = 0; | ||||
|                                                             return GLib.SOURCE_REMOVE; | ||||
|                                                          }); | ||||
|         GLib.Source.set_name_by_id(this._updateTimeoutId, '[update] updateStatus'); | ||||
|     } | ||||
|  | ||||
|     _updateStatus() { | ||||
|         // Update the current active connection. This will connect to the | ||||
|         // NM.SettingUser signal to sync every time someone updates the | ||||
|         // NM_SETTING_ALLOW_DOWNLOADS setting. | ||||
|         this._updateActiveConnection(); | ||||
|  | ||||
|         let state = this._getState(); | ||||
|         if (state != this._state) { | ||||
|             this._state = state; | ||||
|             this.notify('state'); | ||||
|  | ||||
|             this._updateIcon(); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     _updateActiveConnection() { | ||||
|         let currentActiveConnection = this._getActiveConnection(); | ||||
|  | ||||
|         if (this._activeConnection == currentActiveConnection) | ||||
|             return; | ||||
|  | ||||
|         // Disconnect from the previous active connection | ||||
|         if (this._settingChangedSignalId > 0) { | ||||
|             this._activeConnection.disconnect(this._settingChangedSignalId); | ||||
|             this._settingChangedSignalId = 0; | ||||
|         } | ||||
|  | ||||
|         this._activeConnection = currentActiveConnection; | ||||
|  | ||||
|         // Connect from the current active connection | ||||
|         if (currentActiveConnection) | ||||
|             this._settingChangedSignalId = currentActiveConnection.connect('changed', this._updateStatus.bind(this)); | ||||
|     } | ||||
|  | ||||
|     _ensureUserSetting(connection) { | ||||
|         let userSetting = connection.get_setting(NM.SettingUser.$gtype); | ||||
|         if (!userSetting) { | ||||
|             userSetting = new NM.SettingUser(); | ||||
|             connection.add_setting(userSetting); | ||||
|         } | ||||
|         return userSetting; | ||||
|     } | ||||
|  | ||||
|     _getState() { | ||||
|         if (!this._activeConnection) | ||||
|             return State.DISCONNECTED; | ||||
|  | ||||
|         let userSetting = this._ensureUserSetting(this._activeConnection); | ||||
|  | ||||
|         // We only return true when: | ||||
|         //  * Automatic Updates are on | ||||
|         //  * A schedule was set | ||||
|         //  * Something is being downloaded | ||||
|  | ||||
|         let allowDownloadsValue = userSetting.get_data(NM_SETTING_ALLOW_DOWNLOADS); | ||||
|         if (allowDownloadsValue) { | ||||
|             let allowDownloads = (allowDownloadsValue === '1'); | ||||
|  | ||||
|             if (!allowDownloads) | ||||
|                 return State.DISABLED; | ||||
|         } else { | ||||
|             // Guess the default value from the metered state. Only return | ||||
|             // if it's disabled - if it's not, we want to follow the regular | ||||
|             // code paths and fetch the correct state | ||||
|             let connectionSetting = this._activeConnection.get_setting_connection(); | ||||
|  | ||||
|             if (!connectionSetting) | ||||
|                 return State.DISABLED; | ||||
|  | ||||
|             let metered = connectionSetting.get_metered(); | ||||
|             if (metered == NM.Metered.YES || metered == NM.Metered.GUESS_YES) | ||||
|                 return State.DISABLED; | ||||
|         } | ||||
|  | ||||
|         // Without the proxy, we can't really know the state | ||||
|         if (!this._proxy) | ||||
|             return State.UNKNOWN; | ||||
|  | ||||
|         let scheduleSet = userSetting.get_data(NM_SETTING_TARIFF_ENABLED) === '1'; | ||||
|         if (!scheduleSet) | ||||
|             return State.IDLE; | ||||
|  | ||||
|         let downloading = this._proxy.ActiveEntryCount > 0; | ||||
|         if (downloading) | ||||
|             return State.DOWNLOADING; | ||||
|  | ||||
|         // At this point we're not downloading anything, but something | ||||
|         // might be queued | ||||
|         let downloadsQueued = this._proxy.EntryCount > 0; | ||||
|         if (downloadsQueued) | ||||
|             return State.SCHEDULED; | ||||
|         else | ||||
|             return State.IDLE; | ||||
|     } | ||||
|  | ||||
|     _getActiveConnection() { | ||||
|         let activeConnection = this._client.get_primary_connection(); | ||||
|         return activeConnection ? activeConnection.get_connection() : null; | ||||
|     } | ||||
|  | ||||
|     _updateIcon() { | ||||
|         let state = this._state; | ||||
|         let iconName = stateToIconName(state); | ||||
|  | ||||
|         if (iconName) { | ||||
|             let iconFile = Gio.File.new_for_uri(iconName); | ||||
|             this._icon = new Gio.FileIcon({ file: iconFile }); | ||||
|         } else { | ||||
|             this._icon = null; | ||||
|         } | ||||
|  | ||||
|         this.notify('icon'); | ||||
|     } | ||||
|  | ||||
|     get state() { | ||||
|         return this._state; | ||||
|     } | ||||
|  | ||||
|     get lastNotificationTime() { | ||||
|         let connection = this._getActiveConnection(); | ||||
|         if (!connection) | ||||
|             return -1; | ||||
|  | ||||
|         let userSetting = connection.get_setting(NM.SettingUser.$gtype); | ||||
|         if (!userSetting) | ||||
|             return -1; | ||||
|  | ||||
|         let time = userSetting.get_data(NM_SETTING_AUTOMATIC_UPDATES_NOTIFICATION_TIME); | ||||
|         return time ? parseInt(time) : -1; | ||||
|     } | ||||
|  | ||||
|     set lastNotificationTime(time) { | ||||
|         if (!this._activeConnection) | ||||
|             return; | ||||
|  | ||||
|         let userSetting = this._ensureUserSetting(this._activeConnection); | ||||
|         userSetting.set_data(NM_SETTING_AUTOMATIC_UPDATES_NOTIFICATION_TIME, | ||||
|                              '%s'.format(time)); | ||||
|  | ||||
|         this._activeConnection.commit_changes(true, null); | ||||
|     } | ||||
|  | ||||
|  | ||||
|     get active() { | ||||
|         return this._active; | ||||
|     } | ||||
|  | ||||
|     set active(_active) { | ||||
|         if (this._active == _active) | ||||
|             return; | ||||
|  | ||||
|         this._active = _active; | ||||
|         this.notify('active'); | ||||
|     } | ||||
|  | ||||
|     get icon() { | ||||
|         return this._icon; | ||||
|     } | ||||
|  | ||||
|     toggleAutomaticUpdates() { | ||||
|         if (!this._activeConnection) | ||||
|             return; | ||||
|  | ||||
|         let userSetting = this._ensureUserSetting(this._activeConnection); | ||||
|  | ||||
|         let state = this._getState(); | ||||
|         let value; | ||||
|  | ||||
|         if (state == State.IDLE || | ||||
|             state == State.SCHEDULED || | ||||
|             state == State.DOWNLOADING) { | ||||
|             value = '0'; | ||||
|         } else { | ||||
|             value = '1'; | ||||
|         } | ||||
|  | ||||
|         userSetting.set_data(NM_SETTING_ALLOW_DOWNLOADS, value); | ||||
|  | ||||
|         this._activeConnection.commit_changes_async(true, null, (con, res, data) => { | ||||
|             this._activeConnection.commit_changes_finish(res); | ||||
|             this._updateStatus(); | ||||
|         }); | ||||
|     } | ||||
| }); | ||||
							
								
								
									
										135
									
								
								js/ui/components/updates.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										135
									
								
								js/ui/components/updates.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,135 @@ | ||||
| // -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*- | ||||
| // | ||||
| // Copyright (C) 2018 Endless Mobile, Inc. | ||||
| // | ||||
| // This is a GNOME Shell component to wrap the interactions over | ||||
| // D-Bus with the Mogwai system daemon. | ||||
| // | ||||
| // Licensed under the GNU General Public License Version 2 | ||||
| // | ||||
| // This program is free software; you can redistribute it and/or | ||||
| // modify it under the terms of the GNU General Public License | ||||
| // as published by the Free Software Foundation; either version 2 | ||||
| // of the License, or (at your option) any later version. | ||||
| // | ||||
| // This program is distributed in the hope that it will be useful, | ||||
| // but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
| // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
| // GNU General Public License for more details. | ||||
| // | ||||
| // You should have received a copy of the GNU General Public License | ||||
| // along with this program; if not, write to the Free Software | ||||
| // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA. | ||||
|  | ||||
| const { Gio, GLib, Shell } = imports.gi; | ||||
|  | ||||
| const UpdateManager = imports.misc.updateManager; | ||||
|  | ||||
| const Main = imports.ui.main; | ||||
| const MessageTray = imports.ui.messageTray; | ||||
|  | ||||
| var UpdateComponent = class { | ||||
|     constructor() { | ||||
|         this._notification = null; | ||||
|         this._state = UpdateManager.State.UNKNOWN; | ||||
|  | ||||
|         this._manager = UpdateManager.getUpdateManager(); | ||||
|         this._manager.connect('notify::state', this._updateState.bind(this)); | ||||
|  | ||||
|         this._updateState(); | ||||
|     } | ||||
|  | ||||
|     enable() { | ||||
|     } | ||||
|  | ||||
|     disable() { | ||||
|     } | ||||
|  | ||||
|     _updateState() { | ||||
|         let newState = this._manager.state; | ||||
|  | ||||
|         if (this._state == newState) | ||||
|             return; | ||||
|  | ||||
|         this._updateNotification(newState); | ||||
|         this._state = newState; | ||||
|     } | ||||
|  | ||||
|     _updateNotification(newState) { | ||||
|         // Don't notify when starting up | ||||
|         if (this._manager.state == UpdateManager.State.UNKNOWN) | ||||
|             return; | ||||
|  | ||||
|         let alreadySentNotification = this._manager.lastNotificationTime != -1; | ||||
|  | ||||
|         let wasDisconnected = this._state == UpdateManager.State.DISCONNECTED; | ||||
|         let wasActive = this._state >= UpdateManager.State.IDLE; | ||||
|         let isActive = newState >= UpdateManager.State.IDLE; | ||||
|  | ||||
|         // The criteria to notify about the Automatic Updates setting is: | ||||
|         //   1. If the user was disconnected and connects to a new network; or | ||||
|         //   2. If the user was connected and connects to a network with different status; | ||||
|         if ((wasDisconnected && alreadySentNotification) || (!wasDisconnected && isActive == wasActive)) | ||||
|             return; | ||||
|  | ||||
|         if (this._notification) | ||||
|             this._notification.destroy(); | ||||
|  | ||||
|         if (newState == UpdateManager.State.DISCONNECTED) | ||||
|             return; | ||||
|  | ||||
|         let source = new MessageTray.SystemNotificationSource(); | ||||
|         Main.messageTray.add(source); | ||||
|  | ||||
|         // Figure out the title, subtitle and icon | ||||
|         let title, subtitle, iconFile; | ||||
|  | ||||
|         if (isActive) { | ||||
|             title = _("Automatic updates on"); | ||||
|             subtitle = _("Your connection has unlimited data so automatic updates have been turned on."); | ||||
|             iconFile = UpdateManager.stateToIconName(UpdateManager.State.IDLE); | ||||
|         } else { | ||||
|             title = _("Automatic updates are turned off to save your data"); | ||||
|             subtitle = _("You will need to choose which updates to apply when on this connection."); | ||||
|             iconFile = UpdateManager.stateToIconName(UpdateManager.State.DISABLED); | ||||
|         } | ||||
|  | ||||
|         let gicon = new Gio.FileIcon({ file: Gio.File.new_for_uri(iconFile) }); | ||||
|  | ||||
|         // Create the notification. | ||||
|         // The first time we notify the user for a given connection, | ||||
|         // we set the urgency to critical so that we make sure the | ||||
|         // user understands how we may be changing their settings. | ||||
|         // On subsequent notifications for the given connection, | ||||
|         // for instance if the user regularly switches between | ||||
|         // metered and unmetered connections, we set the urgency | ||||
|         // to normal so as not to be too obtrusive. | ||||
|         this._notification = new MessageTray.Notification(source, title, subtitle, { gicon: gicon }); | ||||
|         this._notification.setUrgency(alreadySentNotification ? | ||||
|                                       MessageTray.Urgency.NORMAL : MessageTray.Urgency.CRITICAL); | ||||
|         this._notification.setTransient(false); | ||||
|  | ||||
|         this._notification.addAction(_("Close"), () => { | ||||
|             this._notification.destroy(); | ||||
|         }); | ||||
|  | ||||
|         this._notification.addAction(_("Change Settings…"), () => { | ||||
|             // FIXME: this requires the Automatic Updates panel in GNOME | ||||
|             // Settings. Going with the Network panel for now… | ||||
|             let app = Shell.AppSystem.get_default().lookup_app('gnome-network-panel.desktop'); | ||||
|             Main.overview.hide(); | ||||
|             app.activate(); | ||||
|         }); | ||||
|  | ||||
|         source.notify(this._notification); | ||||
|  | ||||
|         this._notification.connect('destroy', () => { | ||||
|             this._notification = null; | ||||
|         }); | ||||
|  | ||||
|         // Now that we first detected this connection, mark it as such | ||||
|         this._manager.lastNotificationTime = GLib.get_real_time(); | ||||
|     } | ||||
| }; | ||||
|  | ||||
| var Component = UpdateComponent; | ||||
| @@ -774,10 +774,17 @@ class AggregateMenu extends PanelMenu.Button { | ||||
|         this._nightLight = new imports.ui.status.nightLight.Indicator(); | ||||
|         this._thunderbolt = new imports.ui.status.thunderbolt.Indicator(); | ||||
|  | ||||
|         if (Main.sessionMode.components.includes('updates')) | ||||
|             this._automaticUpdates = new imports.ui.status.automaticUpdates.Indicator(); | ||||
|         else | ||||
|             this._automaticUpdates = null; | ||||
|  | ||||
|         this._indicators.add_child(this._thunderbolt.indicators); | ||||
|         this._indicators.add_child(this._screencast.indicators); | ||||
|         this._indicators.add_child(this._location.indicators); | ||||
|         this._indicators.add_child(this._nightLight.indicators); | ||||
|         if (this._automaticUpdates) | ||||
|             this._indicators.add_child(this._automaticUpdates.indicators); | ||||
|         if (this._network) { | ||||
|             this._indicators.add_child(this._network.indicators); | ||||
|         } | ||||
| @@ -796,6 +803,8 @@ class AggregateMenu extends PanelMenu.Button { | ||||
|         if (this._network) { | ||||
|             this.menu.addMenuItem(this._network.menu); | ||||
|         } | ||||
|         if (this._automaticUpdates) | ||||
|             this.menu.addMenuItem(this._automaticUpdates.menu); | ||||
|         if (this._bluetooth) { | ||||
|             this.menu.addMenuItem(this._bluetooth.menu); | ||||
|         } | ||||
|   | ||||
| @@ -92,9 +92,11 @@ const _modes = { | ||||
|         unlockDialog: imports.ui.unlockDialog.UnlockDialog, | ||||
|         components: Config.HAVE_NETWORKMANAGER ? | ||||
|                     ['networkAgent', 'polkitAgent', 'telepathyClient', | ||||
|                      'keyring', 'autorunManager', 'automountManager'] : | ||||
|                      'keyring', 'autorunManager', 'automountManager', | ||||
|                      'updates'] : | ||||
|                     ['polkitAgent', 'telepathyClient', | ||||
|                      'keyring', 'autorunManager', 'automountManager'], | ||||
|                      'keyring', 'autorunManager', 'automountManager', | ||||
|                      'updates'], | ||||
|  | ||||
|         panel: { | ||||
|             left: ['activities', 'appMenu'], | ||||
|   | ||||
							
								
								
									
										144
									
								
								js/ui/status/automaticUpdates.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										144
									
								
								js/ui/status/automaticUpdates.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,144 @@ | ||||
| // -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*- | ||||
| // | ||||
| // Copyright (C) 2018 Endless Mobile, Inc. | ||||
| // | ||||
| // This is a GNOME Shell component to wrap the interactions over | ||||
| // D-Bus with the Mogwai system daemon. | ||||
| // | ||||
| // Licensed under the GNU General Public License Version 2 | ||||
| // | ||||
| // This program is free software; you can redistribute it and/or | ||||
| // modify it under the terms of the GNU General Public License | ||||
| // as published by the Free Software Foundation; either version 2 | ||||
| // of the License, or (at your option) any later version. | ||||
| // | ||||
| // This program is distributed in the hope that it will be useful, | ||||
| // but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
| // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
| // GNU General Public License for more details. | ||||
| // | ||||
| // You should have received a copy of the GNU General Public License | ||||
| // along with this program; if not, write to the Free Software | ||||
| // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA. | ||||
|  | ||||
| const { Gio, GLib, Shell, St } = imports.gi; | ||||
|  | ||||
| const UpdateManager = imports.misc.updateManager; | ||||
|  | ||||
| const Main = imports.ui.main; | ||||
| const MessageTray = imports.ui.messageTray; | ||||
| const PanelMenu = imports.ui.panelMenu; | ||||
| const PopupMenu = imports.ui.popupMenu; | ||||
|  | ||||
| const NM_SETTING_AUTOMATIC_UPDATES_NOTIFICATION_TIME = "connection.automatic-updates-notification-time"; | ||||
| const NM_SETTING_ALLOW_DOWNLOADS = 'connection.allow-downloads'; | ||||
| const NM_SETTING_TARIFF_ENABLED = "connection.tariff-enabled"; | ||||
|  | ||||
| const SchedulerInterface = '\ | ||||
| <node> \ | ||||
|   <interface name="com.endlessm.DownloadManager1.Scheduler"> \ | ||||
|     <property name="ActiveEntryCount" type="u" access="read" /> \ | ||||
|     <property name="EntryCount" type="u" access="read" /> \ | ||||
|   </interface> \ | ||||
| </node>'; | ||||
|  | ||||
| const SchedulerProxy = Gio.DBusProxy.makeProxyWrapper(SchedulerInterface); | ||||
|  | ||||
| var Indicator = class extends PanelMenu.SystemIndicator { | ||||
|     constructor() { | ||||
|         super(); | ||||
|  | ||||
|         this._indicator = this._addIndicator(); | ||||
|         this._indicator.visible = false; | ||||
|  | ||||
|         this._item = new PopupMenu.PopupSubMenuMenuItem("", true); | ||||
|         this._toggleItem = this._item.menu.addAction("", this._toggleAutomaticUpdates.bind(this)); | ||||
|         this._item.menu.addAction(_("Updates Queue"), () => { | ||||
|             let params = new GLib.Variant('(sava{sv})', [ 'set-mode', [ new GLib.Variant('s', 'updates') ], {} ]); | ||||
|             Gio.DBus.session.call('org.gnome.Software', | ||||
|                                   '/org/gnome/Software', | ||||
|                                   'org.gtk.Actions', | ||||
|                                   'Activate', | ||||
|                                   params, | ||||
|                                   null, | ||||
|                                   Gio.DBusCallFlags.NONE, | ||||
|                                   5000, | ||||
|                                   null, | ||||
|                                   (conn, result) => { | ||||
|                                       try { | ||||
|                                           conn.call_finish(result); | ||||
|                                       } catch (e) { | ||||
|                                           logError(e, 'Failed to start gnome-software'); | ||||
|                                       } | ||||
|                                   }); | ||||
|  | ||||
|         }); | ||||
|         this._item.menu.addSettingsAction(_("Set a Schedule"), 'gnome-updates-panel.desktop'); | ||||
|         this.menu.addMenuItem(this._item); | ||||
|  | ||||
|         this._manager = UpdateManager.getUpdateManager(); | ||||
|         this._manager.connect('notify::state', this._updateState.bind(this)); | ||||
|  | ||||
|         this._updateState(); | ||||
|     } | ||||
|  | ||||
|     _updateState() { | ||||
|         this._updateStatus(); | ||||
|     } | ||||
|  | ||||
|     _sessionUpdated() { | ||||
|         let sensitive = !Main.sessionMode.isLocked && !Main.sessionMode.isGreeter; | ||||
|         this.menu.setSensitive(sensitive); | ||||
|     } | ||||
|  | ||||
|     _updateStatus() { | ||||
|         // Toggle item name | ||||
|         this._updateItem(); | ||||
|  | ||||
|         // Icons | ||||
|         let icon = this._getIcon() | ||||
|  | ||||
|         this._item.icon.gicon = icon; | ||||
|         this._indicator.gicon = icon; | ||||
|  | ||||
|         // Only show the Automatic Updates icon at the bottom bar when it is | ||||
|         // both enabled, and there are updates being downloaded or installed. | ||||
|         this._updateVisibility(); | ||||
|  | ||||
|         // The status label | ||||
|         this._item.label.text = _("Automatic Updates"); | ||||
|     } | ||||
|  | ||||
|     _updateItem() { | ||||
|         let state = this._manager.state; | ||||
|  | ||||
|         if (state == UpdateManager.State.DISABLED) | ||||
|             this._toggleItem.label.text = _("Turn On"); | ||||
|         else | ||||
|             this._toggleItem.label.text = _("Turn Off"); | ||||
|     } | ||||
|  | ||||
|     _toggleAutomaticUpdates() { | ||||
|         this._manager.toggleAutomaticUpdates(); | ||||
|     } | ||||
|  | ||||
|     _getIcon() { | ||||
|         let state = this._manager.state; | ||||
|         let iconName = UpdateManager.stateToIconName(state); | ||||
|  | ||||
|         if (!iconName) | ||||
|             return null; | ||||
|  | ||||
|         let iconFile = Gio.File.new_for_uri(iconName); | ||||
|         let gicon = new Gio.FileIcon({ file: iconFile }); | ||||
|  | ||||
|         return gicon; | ||||
|     } | ||||
|  | ||||
|     _updateVisibility() { | ||||
|         let state = this._manager.state; | ||||
|  | ||||
|         this._item.actor.visible = (state != UpdateManager.State.DISCONNECTED); | ||||
|         this._indicator.visible = (state == UpdateManager.State.DOWNLOADING); | ||||
|     } | ||||
| }; | ||||
| @@ -52,6 +52,7 @@ js/ui/search.js | ||||
| js/ui/shellEntry.js | ||||
| js/ui/shellMountOperation.js | ||||
| js/ui/status/accessibility.js | ||||
| js/ui/status/automaticUpdates.js | ||||
| js/ui/status/bluetooth.js | ||||
| js/ui/status/brightness.js | ||||
| js/ui/status/keyboard.js | ||||
|   | ||||
		Reference in New Issue
	
	Block a user