on xenial help sign in
Apps update now
commit098f1cd09c053402128a6d0b884408280d01a206 authorDalton Durst <me@daltondurst.com> dateSat 14 Jul 2018, 16:45 (8 years ago) parent0a5bf19
4 files changed+412−22
qml/Wizard/Pages/78-app-update.qml+43−19 qml/Wizard/Pages/DownloadHandler.qml+204 qml/Wizard/Pages/Global.qml+145 qml/Wizard/Pages/UpdateDelegate.qml+20−3
qml/Wizard/Pages/78-app-update.qml+43−19
@@ -55,29 +55,57 @@ LocalComponents.Page {
5555 UpdateManager.check(UpdateManager.CheckClickIgnoreVersion);
5656 }
5757
58+ DownloadHandler {
59+ id: downloadHandler
60+ updateModel: UpdateManager.model
61+ }
62+
5863 Flickable {
5964 id: scrollWidget
6065 anchors {
61− top: parent.top
62− left: parent.left
63− right: parent.right
64− bottom: parent.bottom
66+ fill: content
67+ leftMargin: parent.leftMargin
68+ rightMargin: parent.rightMargin
6569 }
6670 clip: true
67− contentHeight: content.height
68− boundsBehavior: (contentHeight > parent.height) ?
71+ contentHeight: scrollWidgetContent.height
72+ boundsBehavior: (contentHeight > scrollWidgetContent.height) ?
6973 Flickable.DragAndOvershootBounds :
7074 Flickable.StopAtBounds
7175 flickableDirection: Flickable.VerticalFlick
7276
7377 Column {
74− id: content
75− anchors { left: parent.left; right: parent.right }
78+ id: scrollWidgetContent
79+ anchors {
80+ left: parent.left
81+ right: parent.right
82+ }
83+
84+ Global {
85+ id: glob
86+ objectName: "global"
87+ anchors { left: parent.left; right: parent.right }
88+
89+ height: hidden ? 0 : units.gu(8)
90+ clip: true
91+ status: UpdateManager.status
92+ batchMode: appUpdatePage.batchMode
93+ updatesCount: appUpdatePage.updatesCount
94+ online: appUpdatePage.online
95+ onStop: UpdateManager.cancel()
96+
97+ onRequestInstall: {
98+ install();
99+ }
100+ onInstall: {
101+ appUpdatePage.batchMode = true
102+ postClickBatchHandler.target = appUpdatePage;
103+ }
104+ }
76105
77106 AppUpdateList {
78107 id: appUpdateList
79108 objectName: "appUpdateList"
80− anchors { left: parent.left; right: parent.right }
81109
82110 height: hidden ? 0 : units.gu(8)
83111 clip: true
@@ -88,15 +116,11 @@ LocalComponents.Page {
88116 onStop: UpdateManager.cancel()
89117
90118 onRequestInstall: {
91− install();
119+ install();
92120 }
93121 onInstall: {
94122 appUpdatePage.batchMode = true
95− if (requireRestart) {
96− postAllBatchHandler.target = appUpdatePage;
97− } else {
98− postClickBatchHandler.target = appUpdatePage;
99− }
123+ postClickBatchHandler.target = appUpdatePage;
100124 }
101125 }
102126
@@ -127,7 +151,7 @@ LocalComponents.Page {
127151 } else if (s === UpdateManager.StatusIdle && updatesCount === 0) {
128152 return i18n.tr("Software is up to date");
129153 } else if (s === UpdateManager.StatusServerError ||
130− s === UpdateManager.StatusNetworkError) {
154+ s === UpdateManager.StatusNetworkError) {
131155 return i18n.tr("The update server is not responding. Try again later.");
132156 }
133157 return "";
@@ -174,7 +198,7 @@ LocalComponents.Page {
174198 /* This creates a new signed URL with which we can
175199 retry the download. See onSignedUrlChanged. */
176200 UpdateManager.retry(model.identifier,
177− model.revision);
201+ model.revision);
178202 }
179203
180204 onSignedUrlChanged: {
@@ -207,8 +231,8 @@ LocalComponents.Page {
207231 }
208232 }
209233 }
210− } // Column inside flickable.
211− } // Flickable
234+ } // Column inside flickable inside column.
235+ } // Flickable inside Column
212236
213237 Connections {
214238 id: postClickBatchHandler
qml/Wizard/Pages/DownloadHandler.qmlnew file+204
@@ -0,0 +1,204 @@
1+/*
2+ * This file is part of system-settings
3+ *
4+ * Copyright (C) 2016 Canonical Ltd.
5+ *
6+ * This program is free software: you can redistribute it and/or modify it
7+ * under the terms of the GNU General Public License version 3, as published
8+ * by the Free Software Foundation.
9+ *
10+ * This program is distributed in the hope that it will be useful, but
11+ * WITHOUT ANY WARRANTY; without even the implied warranties of
12+ * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
13+ * PURPOSE. See the GNU General Public License for more details.
14+ *
15+ * You should have received a copy of the GNU General Public License along
16+ * with this program. If not, see <http://www.gnu.org/licenses/>.
17+ *
18+ */
19+import QtQuick 2.4
20+import Ubuntu.DownloadManager 1.2
21+
22+Item {
23+ id: root
24+ property var updateModel
25+ property alias downloads: downloadManager.downloads
26+ property var udm: DownloadManager {
27+ id: downloadManager
28+ onDownloadFinished: {
29+ updateModel.setInstalled(download.metadata.custom.identifier,
30+ download.metadata.custom.revision);
31+ }
32+ onDownloadPaused: {
33+ updateModel.pauseUpdate(download.metadata.custom.identifier,
34+ download.metadata.custom.revision)
35+ }
36+ onDownloadResumed: {
37+ updateModel.resumeUpdate(download.metadata.custom.identifier,
38+ download.metadata.custom.revision)
39+ }
40+ onDownloadCanceled: {
41+ updateModel.cancelUpdate(download.metadata.custom.identifier,
42+ download.metadata.custom.revision)
43+ }
44+ onErrorFound: {
45+ updateModel.setError(download.metadata.custom.identifier,
46+ download.metadata.custom.revision,
47+ download.errorMessage)
48+ }
49+ onDownloadsChanged: restoreDownloads()
50+ Component.onCompleted: restoreDownloads()
51+ }
52+
53+ function downloadAll() {
54+
55+ }
56+
57+ function restoreDownloads() {
58+ var dl;
59+ for (var i = 0; i<downloads.length; i++) {
60+ dl = downloads[i];
61+ if (!dl._bound) {
62+ /* We only bind those signals here, that the UDM does not
63+ receive, i.e. processing and progressChanged. */
64+ dl.progressChanged.connect(onDownloadProgress.bind(dl));
65+ dl.processing.connect(onDownloadProcessing.bind(dl));
66+ dl._bound = true;
67+ }
68+ }
69+ }
70+
71+ function resumeDownload(click) {
72+ var download = getDownloadFor(click);
73+ if (download && !download.downloading && !download.isCompleted) {
74+ download.resume();
75+ }
76+ }
77+
78+ function pauseDownload(click) {
79+ var download = getDownloadFor(click);
80+ if (download && download.downloading) {
81+ download.pause();
82+ }
83+ }
84+
85+ // Return download for a click update.
86+ function getDownloadFor(click) {
87+ var cust;
88+ var dl;
89+
90+ for (var i = 0; i<downloads.length; i++) {
91+ dl = downloads[i];
92+ if (dl.errorMessage || dl.isCompleted) {
93+ // Ignore failed and completed downloads.
94+ continue;
95+ }
96+
97+ cust = downloads[i].metadata.custom;
98+ if (cust.identifier === click.identifier && cust.revision === click.revision) {
99+ return downloads[i];
100+ }
101+ }
102+ return null;
103+ }
104+
105+ function hasExistingDownload(click) {
106+ var existingDownload = getDownloadFor(click);
107+ return (existingDownload !== null &&
108+ !existingDownload.errorMessage &&
109+ !existingDownload.isCompleted);
110+ }
111+
112+ function retryDownload(click) {
113+ return createDownload(click, true);
114+ }
115+
116+ function createDownload(click, useSignedUrl) {
117+ if (hasExistingDownload(click)) {
118+ return null;
119+ }
120+ var downloadUrl = click.downloadUrl;
121+ var metadata = {
122+ "command": click.command,
123+ "title": click.title,
124+ "showInIndicator": false
125+ };
126+ var metadataObj = mdt.createObject(root, metadata);
127+ metadataObj.custom = {
128+ "identifier": click.identifier,
129+ "package-name": click.identifier,
130+ "revision": click.revision,
131+ };
132+
133+ var hdrs = {}
134+ if (useSignedUrl) {
135+ if (!click.signedDownloadUrl) {
136+ console.warn('The signed download URL was empty.');
137+ }
138+ downloadUrl = click.signedDownloadUrl;
139+ } else {
140+ // If we're using an unsigned URL, we need to provide this header.
141+ hdrs["X-Click-Token"] = click.token;
142+ }
143+
144+ var singleDownloadObj = sdl.createObject(root, {
145+ "autoStart": true,
146+ "hash": click.downloadHash,
147+ "algorithm": "sha512",
148+ "headers": hdrs,
149+ "metadata": metadataObj,
150+ "revision": click.revision,
151+ "identifier": click.identifier
152+ });
153+ singleDownloadObj.download(downloadUrl);
154+ return singleDownloadObj;
155+ }
156+
157+ function onDownloadProgress(progress) {
158+ updateModel.setProgress(this.metadata.custom.identifier,
159+ this.metadata.custom.revision,
160+ this.progress);
161+ }
162+
163+ function onDownloadProcessing() {
164+ updateModel.processUpdate(this.metadata.custom.identifier,
165+ this.metadata.custom.revision);
166+ }
167+
168+ /* If a update's model has a downloadId, check if UDM knows it. If not,
169+ treat this as a failure. Workaround for lp:1603770. */
170+ function assertDownloadExist(click) {
171+ if (!getDownloadFor(click)) {
172+ updateModel.setError(
173+ click.identifier, click.revision,
174+ i18n.tr("Installation failed")
175+ );
176+ }
177+ }
178+
179+ Component {
180+ id: sdl
181+ SingleDownload {
182+ id: download
183+ objectName: "singleDownload"
184+ property bool _bound: true
185+
186+ onDownloadIdChanged: {
187+ updateModel.queueUpdate(metadata.custom.identifier,
188+ metadata.custom.revision,
189+ downloadId);
190+ }
191+ onStarted: {
192+ updateModel.startUpdate(metadata.custom.identifier,
193+ metadata.custom.revision);
194+ }
195+ onProgressChanged: onDownloadProgress.call(download)
196+ onProcessing: onDownloadProcessing.call(download)
197+ }
198+ }
199+
200+ Component {
201+ id: mdt
202+ Metadata {}
203+ }
204+}
qml/Wizard/Pages/Global.qmlnew file+145
@@ -0,0 +1,145 @@
1+/*
2+ * This file is part of system-settings
3+ *
4+ * Copyright (C) 2016 Canonical Ltd.
5+ *
6+ * This program is free software: you can redistribute it and/or modify it
7+ * under the terms of the GNU General Public License version 3, as published
8+ * by the Free Software Foundation.
9+ *
10+ * This program is distributed in the hope that it will be useful, but
11+ * WITHOUT ANY WARRANTY; without even the implied warranties of
12+ * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
13+ * PURPOSE. See the GNU General Public License for more details.
14+ *
15+ * You should have received a copy of the GNU General Public License along
16+ * with this program. If not, see <http://www.gnu.org/licenses/>.
17+ */
18+
19+import QtQuick 2.4
20+import QtQuick.Layouts 1.1
21+import Ubuntu.Components 1.3
22+import Ubuntu.Components.ListItems 1.3 as ListItems
23+import Ubuntu.SystemSettings.Update 1.0
24+
25+Item {
26+ id: g
27+
28+ property bool batchMode
29+ property int status // A UpdateManager::Status
30+ property bool requireRestart: false
31+ property int updatesCount: 0
32+ property bool online: false
33+
34+ property bool hidden: {
35+ switch (status) {
36+ case UpdateManager.StatusNetworkError:
37+ case UpdateManager.StatusServerError:
38+ return true;
39+ }
40+ return !online ||
41+ (updatesCount <= 1 && status === UpdateManager.StatusIdle) ||
42+ batchMode
43+ }
44+
45+ signal stop()
46+ signal requestInstall()
47+ signal install()
48+
49+ Behavior on height {
50+ UbuntuNumberAnimation {}
51+ }
52+
53+ RowLayout {
54+ id: checking
55+ spacing: units.gu(2)
56+ anchors {
57+ fill: parent
58+ margins: units.gu(2)
59+ }
60+
61+ Behavior on opacity {
62+ UbuntuNumberAnimation {}
63+ }
64+
65+ opacity: visible ? 1 : 0
66+ visible: {
67+ switch (g.status) {
68+ case UpdateManager.StatusCheckingClickUpdates:
69+ case UpdateManager.StatusCheckingImageUpdates:
70+ case UpdateManager.StatusCheckingAllUpdates:
71+ return true;
72+ }
73+ return false;
74+ }
75+
76+ ActivityIndicator {
77+ running: parent.visible
78+ }
79+
80+ Label {
81+ objectName: "checkLabel"
82+ Layout.fillWidth: true
83+ elide: Text.ElideRight
84+ text: i18n.tr("Checking for updates…")
85+ }
86+
87+ Button {
88+ objectName: "updatesGlobalStopButton"
89+ text: i18n.tr("Stop")
90+ onClicked: g.stop()
91+ Layout.alignment: Qt.AlignRight | Qt.AlignVCenter
92+ }
93+ }
94+
95+ RowLayout {
96+ id: install
97+ anchors {
98+ fill: parent
99+ margins: units.gu(2)
100+ }
101+ spacing: units.gu(2)
102+
103+ Behavior on opacity {
104+ UbuntuNumberAnimation {}
105+ }
106+
107+ opacity: visible ? 1 : 0
108+ visible: {
109+ var canInstall = g.status === UpdateManager.StatusIdle;
110+ return canInstall && updatesCount > 1;
111+ }
112+
113+ Label {
114+ objectName: "updatesGlobalInfoLabel"
115+ // // TRANSLATORS: %1 is number of software updates available.
116+ text: i18n.tr("%1 update available",
117+ "%1 updates available",
118+ updatesCount).arg(updatesCount)
119+ Layout.fillWidth: true
120+ }
121+
122+ Button {
123+ objectName: "updatesGlobalInstallButton"
124+ text: {
125+ if (g.requireRestart === true) {
126+ return i18n.tr("Update all…");
127+ } else {
128+ return i18n.tr("Update all");
129+ }
130+ }
131+ onClicked: g.requestInstall()
132+ color: theme.palette.normal.positive
133+ strokeColor: "transparent"
134+ }
135+ }
136+
137+ ListItems.ThinDivider {
138+ visible: !g.hidden
139+ anchors {
140+ left: parent.left
141+ right: parent.right
142+ bottom: parent.bottom
143+ }
144+ }
145+}
qml/Wizard/Pages/UpdateDelegate.qml+20−3
@@ -243,16 +243,33 @@ ListItem {
243243 fontSize: "small"
244244
245245 text: {
246+
247+ function humanFileSize(bytes, si) {
248+ var thresh = si ? 1000 : 1024;
249+ if(Math.abs(bytes) < thresh) {
250+ return bytes + ' B';
251+ }
252+ var units = si
253+ ? ['kB','MB','GB','TB','PB','EB','ZB','YB']
254+ : ['KiB','MiB','GiB','TiB','PiB','EiB','ZiB','YiB'];
255+ var u = -1;
256+ do {
257+ bytes /= thresh;
258+ ++u;
259+ } while(Math.abs(bytes) >= thresh && u < units.length - 1);
260+ return bytes.toFixed(1)+' '+units[u];
261+ }
262+
246263 switch (updateState) {
247264 case Update.StateAvailable:
248− return Utilities.formatSize(size);
265+ return humanFileSize(size, 1000);
249266
250267 case Update.StateDownloading:
251268 case Update.StateDownloadingAutomatically:
252269 case Update.StateDownloadPaused:
253270 case Update.StateAutomaticDownloadPaused:
254− var down = Utilities.formatSize((size / 100) * progress);
255− var left = Utilities.formatSize(size);
271+ var down = humanFileSize((size / 100) * progress, 1000);
272+ var left = humanFileSize(size, 1000);
256273 if (progress > 100) {
257274 return left;
258275 } else {