on xenial help sign in
Forward-port Wizard fixes from older Unity (#105)
7f172ecDalton Durst
plugins/Wizard/CMakeLists.txt+1
@@ -10,6 +10,7 @@ add_library(Wizard-qml MODULE
10 timezonemodel.cpp
10 timezonemodel.cpp
11 Status.cpp
11 Status.cpp
12 keyboardLayoutsModel.cpp
12 keyboardLayoutsModel.cpp
13+ Changelog.cpp
13)
14)
14
15
15qt5_use_modules(Wizard-qml DBus Qml)
16qt5_use_modules(Wizard-qml DBus Qml)
plugins/Wizard/Changelog.cppnew file+58
@@ -0,0 +1,58 @@
1+/*
2+ * Copyright (C) 2018 The UBports project
3+ * Written by: Marius Gripsgard <marius@ubports.com>
4+ *
5+ * This program is free software: you can redistribute it and/or modify it
6+ * under the terms of the GNU General Public License version 3, as published
7+ * by the Free Software Foundation.
8+ *
9+ * This program is distributed in the hope that it will be useful, but
10+ * WITHOUT ANY WARRANTY; without even the implied warranties of
11+ * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
12+ * PURPOSE. See the GNU General Public License for more details.
13+ *
14+ * You should have received a copy of the GNU General Public License along
15+ * with this program. If not, see <http://www.gnu.org/licenses/>.
16+ */
17+
18+#include "Changelog.h"
19+
20+#include <QDir>
21+#include <QFile>
22+#include <QTextStream>
23+
24+Changelog::Changelog()
25+ : QObject()
26+{
27+ readChangelog();
28+ if(QFile::exists(changelogPath()))
29+ m_fsWatcher.addPath(changelogPath());
30+ connect(&m_fsWatcher, &QFileSystemWatcher::fileChanged, this, &Changelog::watcherFileChanged);
31+}
32+
33+QString Changelog::changelogPath()
34+{
35+ return "/usr/share/ubports/changelogs/current";
36+}
37+
38+QString Changelog::text() const
39+{
40+ return m_text;
41+}
42+
43+void Changelog::readChangelog()
44+{
45+ if(!QFile::exists(changelogPath()))
46+ return;
47+ QFile f(changelogPath());
48+ if (!f.open(QFile::ReadOnly | QFile::Text)) return;
49+ QTextStream in(&f);
50+ m_text = in.readAll();
51+ Q_EMIT textChanged();
52+}
53+
54+void Changelog::watcherFileChanged()
55+{
56+ readChangelog();
57+ m_fsWatcher.removePath(changelogPath());
58+}
plugins/Wizard/Changelog.hnew file+52
@@ -0,0 +1,52 @@
1+/*
2+ * Copyright (C) 2018 The UBports project
3+ * Written by: Marius Gripsgard <marius@ubports.com>
4+ *
5+ * This program is free software: you can redistribute it and/or modify it
6+ * under the terms of the GNU General Public License version 3, as published
7+ * by the Free Software Foundation.
8+ *
9+ * This program is distributed in the hope that it will be useful, but
10+ * WITHOUT ANY WARRANTY; without even the implied warranties of
11+ * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
12+ * PURPOSE. See the GNU General Public License for more details.
13+ *
14+ * You should have received a copy of the GNU General Public License along
15+ * with this program. If not, see <http://www.gnu.org/licenses/>.
16+ */
17+
18+#ifndef WIZARD_CHANGELOG_H
19+#define WIZARD_CHANGELOG_H
20+
21+#include <QFileSystemWatcher>
22+#include <QObject>
23+#include <QString>
24+
25+class Changelog : public QObject
26+{
27+ Q_OBJECT
28+ Q_PROPERTY(QString text READ text NOTIFY textChanged)
29+
30+public:
31+ Changelog();
32+ ~Changelog() = default;
33+
34+ QString text() const;
35+
36+Q_SIGNALS:
37+ void textChanged();
38+
39+private Q_SLOTS:
40+ void watcherFileChanged();
41+
42+private:
43+ Q_DISABLE_COPY(Changelog)
44+
45+ static QString changelogPath();
46+ void readChangelog();
47+
48+ QFileSystemWatcher m_fsWatcher;
49+ QString m_text;
50+};
51+
52+#endif
plugins/Wizard/PageList.cpp+6−5
@@ -46,9 +46,7 @@ PageList::PageList(QObject *parent)
46 QSet<QString> disabledPages;
46 QSet<QString> disabledPages;
47 QStringList dataDirs;
47 QStringList dataDirs;
48
48
49− if (getenv("WIZARD_TESTING") != nullptr) {
49+ if (!isRunningInstalled() && getenv("WIZARD_TESTING") == nullptr) {
50− dataDirs = QStandardPaths::standardLocations(QStandardPaths::GenericDataLocation);
51− } else if (!isRunningInstalled()) {
52 dataDirs << qmlDirectory();
50 dataDirs << qmlDirectory();
53 } else {
51 } else {
54 dataDirs = shellDataDirs();
52 dataDirs = shellDataDirs();
@@ -73,8 +71,11 @@ PageList::PageList(QObject *parent)
73 // If there was a system update installed, skip until the last page to just greet the user
71 // If there was a system update installed, skip until the last page to just greet the user
74 QSettings settings;
72 QSettings settings;
75 if (settings.value(QStringLiteral("Wizard/SkipUntilFinishedPage")).toBool()) {
73 if (settings.value(QStringLiteral("Wizard/SkipUntilFinishedPage")).toBool()) {
76− while (m_pages.count() > 1) {
74+ const QString lastPage = m_pages.lastKey();
77− m_pages.erase(m_pages.begin());
75+ Q_FOREACH(const QString &page, m_pages.keys()) {
76+ if (Q_UNLIKELY(page != lastPage)) {
77+ m_pages.remove(page);
78+ }
78 }
79 }
79
80
80 // ... and reset it again for the next run
81 // ... and reset it again for the next run
plugins/Wizard/System.cpp+68−3
@@ -1,4 +1,5 @@
1/*
1/*
2+ * Copyright (C) 2018 The UBports project
2 * Copyright (C) 2014-2016 Canonical Ltd.
3 * Copyright (C) 2014-2016 Canonical Ltd.
3 *
4 *
4 * This program is free software: you can redistribute it and/or modify it
5 * This program is free software: you can redistribute it and/or modify it
@@ -46,29 +47,93 @@ QString System::wizardEnabledPath()
46 return QDir::home().filePath(QStringLiteral(".config/ubuntu-system-settings/wizard-has-run"));
47 return QDir::home().filePath(QStringLiteral(".config/ubuntu-system-settings/wizard-has-run"));
47}
48}
48
49
50+QString System::currentFrameworkPath()
51+{
52+ QFileInfo f("/usr/share/click/frameworks/current");
53+ return f.canonicalFilePath();
54+}
55+
56+/*
57+wizardEnabled and isUpdate logic
58+
59+if wizard-has-run does NOT exist == is new install
60+if wizard-has-run exists but does NOT match current framework == is update
61+if wizard-has-run exists but does match current framework == show no wizard
62+*/
63+
64+bool System::wizardPathExists() {
65+ return QFile::exists(wizardEnabledPath());
66+}
67+
49bool System::wizardEnabled() const
68bool System::wizardEnabled() const
50{
69{
70+ if (!wizardPathExists()) {
71+ return true;
72+ }
73+ return isUpdate();
74+}
75+
76+QString System::readCurrentFramework()
77+{
78+ QFile f(currentFrameworkPath());
79+ if (!f.open(QFile::ReadOnly | QFile::Text)) return "";
80+ QTextStream in(&f);
81+ return in.readAll();
82+}
83+
84+QString System::readWizardEnabled()
85+{
51− return !QFile::exists(wizardEnabledPath());
86+ QFile f(wizardEnabledPath());
87+ if (!f.open(QFile::ReadOnly | QFile::Text)) return "";
88+ QTextStream in(&f);
89+ return in.readAll();
90+}
91+
92+QString System::version() const
93+{
94+ return readCurrentFramework();
95+}
96+
97+bool System::isUpdate() const
98+{
99+ if (!wizardPathExists()) {
100+ return false;
101+ }
102+
103+ return readCurrentFramework() != readWizardEnabled();
52}
104}
53
105
54void System::setWizardEnabled(bool enabled)
106void System::setWizardEnabled(bool enabled)
55{
107{
56− if (wizardEnabled() == enabled)
108+ if (wizardEnabled() == enabled && !isUpdate())
57 return;
109 return;
58
110
59 if (enabled) {
111 if (enabled) {
60 QFile::remove(wizardEnabledPath());
112 QFile::remove(wizardEnabledPath());
61 } else {
113 } else {
62 QDir(wizardEnabledPath()).mkpath(QStringLiteral(".."));
114 QDir(wizardEnabledPath()).mkpath(QStringLiteral(".."));
115+ if (QFile::exists(wizardEnabledPath())) {
116+ QFile::remove(wizardEnabledPath());
117+ }
118+ // For special cases check if wizardEnabledPath is a folder
119+ if (QDir(wizardEnabledPath()).exists()) {
120+ QDir(wizardEnabledPath()).removeRecursively();
121+ }
63− QFile(wizardEnabledPath()).open(QIODevice::WriteOnly);
122+ if (!QFile::copy(currentFrameworkPath(), wizardEnabledPath())) {
123+ // Make en empty file if framework does not exist
124+ QFile f(wizardEnabledPath());
125+ f.open(QFile::WriteOnly);
126+ }
64 m_fsWatcher.addPath(wizardEnabledPath());
127 m_fsWatcher.addPath(wizardEnabledPath());
65 Q_EMIT wizardEnabledChanged();
128 Q_EMIT wizardEnabledChanged();
129+ Q_EMIT isUpdateChanged();
66 }
130 }
67}
131}
68
132
69void System::watcherFileChanged()
133void System::watcherFileChanged()
70{
134{
71 Q_EMIT wizardEnabledChanged();
135 Q_EMIT wizardEnabledChanged();
136+ Q_EMIT isUpdateChanged();
72 m_fsWatcher.removePath(wizardEnabledPath());
137 m_fsWatcher.removePath(wizardEnabledPath());
73}
138}
74
139
plugins/Wizard/System.h+17
@@ -1,4 +1,5 @@
1/*
1/*
2+ * Copyright (C) 2018 The UBports project
2 * Copyright (C) 2014-2016 Canonical Ltd.
3 * Copyright (C) 2014-2016 Canonical Ltd.
3 *
4 *
4 * This program is free software: you can redistribute it and/or modify it
5 * This program is free software: you can redistribute it and/or modify it
@@ -18,6 +19,7 @@
18#define WIZARD_SYSTEM_H
19#define WIZARD_SYSTEM_H
19
20
20#include <QFileSystemWatcher>
21#include <QFileSystemWatcher>
22+#include <QVersionNumber>
21#include <QObject>
23#include <QObject>
22#include <QString>
24#include <QString>
23
25
@@ -25,12 +27,21 @@ class System : public QObject
25{
27{
26 Q_OBJECT
28 Q_OBJECT
27 Q_PROPERTY(bool wizardEnabled READ wizardEnabled WRITE setWizardEnabled NOTIFY wizardEnabledChanged)
29 Q_PROPERTY(bool wizardEnabled READ wizardEnabled WRITE setWizardEnabled NOTIFY wizardEnabledChanged)
30+ Q_PROPERTY(QString version READ version NOTIFY versionChanged)
31+ Q_PROPERTY(bool isUpdate READ isUpdate NOTIFY isUpdateChanged)
28
32
29public:
33public:
30 System();
34 System();
31 ~System() = default;
35 ~System() = default;
32
36
37+ /**
38+ * Checks whether the wizard is enabled for first-run or otherwise.
39+ */
33 bool wizardEnabled() const;
40 bool wizardEnabled() const;
41+
42+ QString version() const;
43+ bool isUpdate() const;
44+
34 void setWizardEnabled(bool enabled);
45 void setWizardEnabled(bool enabled);
35
46
36public Q_SLOTS:
47public Q_SLOTS:
@@ -42,6 +53,8 @@ public Q_SLOTS:
42
53
43Q_SIGNALS:
54Q_SIGNALS:
44 void wizardEnabledChanged();
55 void wizardEnabledChanged();
56+ void versionChanged();
57+ void isUpdateChanged();
45
58
46private Q_SLOTS:
59private Q_SLOTS:
47 void watcherFileChanged();
60 void watcherFileChanged();
@@ -50,7 +63,11 @@ private:
50 Q_DISABLE_COPY(System)
63 Q_DISABLE_COPY(System)
51
64
52 static QString wizardEnabledPath();
65 static QString wizardEnabledPath();
66+ static QString currentFrameworkPath();
53 static void setSessionVariable(const QString &variable, const QString &value);
67 static void setSessionVariable(const QString &variable, const QString &value);
68+ static QString readCurrentFramework();
69+ static QString readWizardEnabled();
70+ static bool wizardPathExists();
54
71
55 QFileSystemWatcher m_fsWatcher;
72 QFileSystemWatcher m_fsWatcher;
56};
73};
plugins/Wizard/plugin.cpp+2
@@ -20,6 +20,7 @@
20#include "timezonemodel.h"
20#include "timezonemodel.h"
21#include "LocalePlugin.h"
21#include "LocalePlugin.h"
22#include "Status.h"
22#include "Status.h"
23+#include "Changelog.h"
23#include "keyboardLayoutsModel.h"
24#include "keyboardLayoutsModel.h"
24
25
25#include <QtQml/qqml.h>
26#include <QtQml/qqml.h>
@@ -30,6 +31,7 @@ void WizardPlugin::registerTypes(const char *uri)
30 qmlRegisterType<PageList>(uri, 0, 1, "PageList");
31 qmlRegisterType<PageList>(uri, 0, 1, "PageList");
31 qmlRegisterSingletonType<System>(uri, 0, 1, "System", [](QQmlEngine*, QJSEngine*) -> QObject* { return new System; });
32 qmlRegisterSingletonType<System>(uri, 0, 1, "System", [](QQmlEngine*, QJSEngine*) -> QObject* { return new System; });
32 qmlRegisterSingletonType<Status>(uri, 0, 1, "Status", [](QQmlEngine*, QJSEngine*) -> QObject* { return new Status; });
33 qmlRegisterSingletonType<Status>(uri, 0, 1, "Status", [](QQmlEngine*, QJSEngine*) -> QObject* { return new Status; });
34+ qmlRegisterSingletonType<Changelog>(uri, 0, 1, "Changelog", [](QQmlEngine*, QJSEngine*) -> QObject* { return new Changelog; });
33 qmlRegisterType<TimeZoneLocationModel>(uri, 0, 1, "TimeZoneModel");
35 qmlRegisterType<TimeZoneLocationModel>(uri, 0, 1, "TimeZoneModel");
34 qmlRegisterType<KeyboardLayoutsModel>(uri, 0, 1, "KeyboardLayoutsModel");
36 qmlRegisterType<KeyboardLayoutsModel>(uri, 0, 1, "KeyboardLayoutsModel");
35 qmlRegisterType<LocalePlugin>(uri, 0, 1, "LocalePlugin");
37 qmlRegisterType<LocalePlugin>(uri, 0, 1, "LocalePlugin");
plugins/Wizard/timezonemodel.h+1−1
@@ -27,6 +27,7 @@ class TimeZoneLocationModel: public QAbstractListModel
27 Q_PROPERTY(bool listUpdating READ listUpdating NOTIFY listUpdatingChanged)
27 Q_PROPERTY(bool listUpdating READ listUpdating NOTIFY listUpdatingChanged)
28 Q_PROPERTY(QString filter READ filter WRITE setFilter NOTIFY filterChanged)
28 Q_PROPERTY(QString filter READ filter WRITE setFilter NOTIFY filterChanged)
29 Q_PROPERTY(QString country READ country WRITE setCountry NOTIFY countryChanged)
29 Q_PROPERTY(QString country READ country WRITE setCountry NOTIFY countryChanged)
30+ Q_ENUMS(Roles)
30
31
31public:
32public:
32 explicit TimeZoneLocationModel(QObject *parent = nullptr);
33 explicit TimeZoneLocationModel(QObject *parent = nullptr);
@@ -41,7 +42,6 @@ public:
41 LatitudeRole,
42 LatitudeRole,
42 LongitudeRole
43 LongitudeRole
43 };
44 };
44− Q_ENUM(Roles)
45
45
46 int rowCount(const QModelIndex &parent = QModelIndex()) const override;
46 int rowCount(const QModelIndex &parent = QModelIndex()) const override;
47 QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
47 QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
qml/Wizard/Components/InputMethod.qml+9−9
@@ -21,19 +21,19 @@ import Ubuntu.Components 0.1
21// TODO: try to share this code with that from the unity8 shell
21// TODO: try to share this code with that from the unity8 shell
22
22
23Item {
23Item {
24− id: root
24+ id: inputMethodRoot
25
25
26 Connections {
26 Connections {
27 target: SurfaceManager
27 target: SurfaceManager
28 onSurfaceCreated: {
28 onSurfaceCreated: {
29 if (surface.type == MirSurfaceItem.InputMethod) {
29 if (surface.type == MirSurfaceItem.InputMethod) {
30− root.surface = surface;
30+ inputMethodRoot.surface = surface;
31 }
31 }
32 }
32 }
33
33
34 onSurfaceDestroyed: {
34 onSurfaceDestroyed: {
35− if (root.surface == surface) {
35+ if (inputMethodRoot.surface == surface) {
36− root.surface = null;
36+ inputMethodRoot.surface = null;
37 surface.parent = null;
37 surface.parent = null;
38 }
38 }
39 if (!surface.parent) {
39 if (!surface.parent) {
@@ -71,7 +71,7 @@ Item {
71 visible: false
71 visible: false
72 // half-way down because the vkb occupies only the lower half of the surface
72 // half-way down because the vkb occupies only the lower half of the surface
73 // TODO: consider keyboard rotation
73 // TODO: consider keyboard rotation
74− y: root.parent.height / 2.0
74+ y: inputMethodRoot.parent.height / 2.0
75 }
75 }
76 }
76 }
77 ]
77 ]
@@ -89,17 +89,17 @@ Item {
89 ignoreUnknownSignals: true // don't wanna spam the log when surface is null
89 ignoreUnknownSignals: true // don't wanna spam the log when surface is null
90 onStateChanged: {
90 onStateChanged: {
91 if (state == MirSurfaceItem.Minimized) {
91 if (state == MirSurfaceItem.Minimized) {
92− root.hide();
92+ inputMethodRoot.hide();
93 } else if (state == MirSurfaceItem.Maximized) {
93 } else if (state == MirSurfaceItem.Maximized) {
94− root.show();
94+ inputMethodRoot.show();
95 }
95 }
96 }
96 }
97 }
97 }
98
98
99 onSurfaceChanged: {
99 onSurfaceChanged: {
100 if (surface) {
100 if (surface) {
101− surface.parent = root;
101+ surface.parent = inputMethodRoot;
102− surface.anchors.fill = root;
102+ surface.anchors.fill = inputMethodRoot;
103 }
103 }
104 }
104 }
105
105
qml/Wizard/Page.qml+15
@@ -1,4 +1,5 @@
1/*
1/*
2+ * Copyright (C) 2018 The UBports project
2 * Copyright (C) 2013-2016 Canonical, Ltd.
3 * Copyright (C) 2013-2016 Canonical, Ltd.
3 *
4 *
4 * This program is free software; you can redistribute it and/or modify
5 * This program is free software; you can redistribute it and/or modify
@@ -50,6 +51,10 @@ Item {
50 property bool skipValid: true
51 property bool skipValid: true
51 property bool skip: false
52 property bool skip: false
52
53
54+ // Marks if should be only shown if update or install
55+ property bool onlyOnUpdate: false
56+ property bool onlyOnInstall: false
57+
53 property bool hasBackButton: true
58 property bool hasBackButton: true
54 property string backButtonText: i18n.ctr("Button: Go back one page in the Wizard", "Back")
59 property string backButtonText: i18n.ctr("Button: Go back one page in the Wizard", "Back")
55 property bool customBack: false
60 property bool customBack: false
@@ -59,6 +64,10 @@ Item {
59 property bool lastPage: false
64 property bool lastPage: false
60 property bool buttonBarVisible: true
65 property bool buttonBarVisible: true
61
66
67+ // Item that will receive input focus when the page is in front
68+ // May be useful to open the keyboard automatically for a text field
69+ property Item focusItem
70+
62 property string title: ""
71 property string title: ""
63
72
64 signal backClicked()
73 signal backClicked()
@@ -66,6 +75,12 @@ Item {
66 visible: false
75 visible: false
67 anchors.fill: parent
76 anchors.fill: parent
68
77
78+ onVisibleChanged: {
79+ if (visible && focusItem) {
80+ focusItem.forceActiveFocus();
81+ }
82+ }
83+
69 Timer {
84 Timer {
70 id: indicatorTimer
85 id: indicatorTimer
71 interval: 1000
86 interval: 1000
qml/Wizard/Pages.qml+90−16
@@ -1,4 +1,5 @@
1/*
1/*
2+ * Copyright (C) 2018 The UBports project
2 * Copyright (C) 2013-2016 Canonical, Ltd.
3 * Copyright (C) 2013-2016 Canonical, Ltd.
3 *
4 *
4 * This program is free software; you can redistribute it and/or modify
5 * This program is free software; you can redistribute it and/or modify
@@ -18,7 +19,8 @@ import QtQuick 2.4
18import MeeGo.QOfono 0.2
19import MeeGo.QOfono 0.2
19import Ubuntu.Components 1.3
20import Ubuntu.Components 1.3
20import Ubuntu.SystemSettings.SecurityPrivacy 1.0
21import Ubuntu.SystemSettings.SecurityPrivacy 1.0
21−import Ubuntu.SystemSettings.Diagnostics 1.0
22+import Ubuntu.SystemSettings.Update 1.0
23+import Ubuntu.Connectivity 1.0
22import Wizard 0.1
24import Wizard 0.1
23import "../Components"
25import "../Components"
24
26
@@ -35,6 +37,10 @@ StyledItem {
35 property string password: ""
37 property string password: ""
36
38
37 property bool seenSIMPage: false // we want to see the SIM page at most once
39 property bool seenSIMPage: false // we want to see the SIM page at most once
40+ readonly property bool connected: NetworkingStatus.online
41+
42+ property bool checkedForUpdates: false
43+ property bool updateDownloaded: false
38
44
39 property alias modemManager: modemManager
45 property alias modemManager: modemManager
40 property alias simManager0: simManager0
46 property alias simManager0: simManager0
@@ -49,11 +55,6 @@ StyledItem {
49 objectName: "securityPrivacy"
55 objectName: "securityPrivacy"
50 }
56 }
51
57
52− UbuntuDiagnostics {
53− id: diagnostics
54− objectName: "diagnostics"
55− }
56−
57 OfonoManager { // need it here for the language and country detection
58 OfonoManager { // need it here for the language and country detection
58 id: modemManager
59 id: modemManager
59 readonly property bool gotSimCard: available && ((simManager0.ready && simManager0.present) || (simManager1.ready && simManager1.present))
60 readonly property bool gotSimCard: available && ((simManager0.ready && simManager0.present) || (simManager1.ready && simManager1.present))
@@ -75,6 +76,14 @@ StyledItem {
75 modemPath: modemManager.modems.length >= 2 ? modemManager.modems[1] : ""
76 modemPath: modemManager.modems.length >= 2 ? modemManager.modems[1] : ""
76 }
77 }
77
78
79+ function checkForUpdatesIfConnected() {
80+ if (connected && !checkedForUpdates) {
81+ console.info("Wizard: Checking for system-image update");
82+ SystemImage.checkForUpdate();
83+ checkedForUpdates = true;
84+ }
85+ }
86+
78 function quitWizard() {
87 function quitWizard() {
79 pageStack.currentPage.enabled = false;
88 pageStack.currentPage.enabled = false;
80
89
@@ -84,7 +93,7 @@ StyledItem {
84 // Ignore (but log) any errors, since we're past where the user set
93 // Ignore (but log) any errors, since we're past where the user set
85 // the method. Worst case, we just leave the user with a swipe
94 // the method. Worst case, we just leave the user with a swipe
86 // security method and they fix it in the system settings.
95 // security method and they fix it in the system settings.
87− console.log("Error setting security method:", errorMsg)
96+ console.log("Wizard: Error setting security method:", errorMsg)
88 }
97 }
89 }
98 }
90
99
@@ -105,6 +114,59 @@ StyledItem {
105 id: pageList
114 id: pageList
106 }
115 }
107
116
117+ ActivityIndicator {
118+ id: pagesSpinner
119+ anchors.centerIn: parent
120+ z: 100
121+ running: false
122+ visible: running
123+
124+ NumberAnimation on opacity {
125+ id: fadeInAnimation
126+ from: 0
127+ to: 1
128+ duration: 200
129+ }
130+
131+ onVisibleChanged: {
132+ if (visible) {
133+ opacity = 0;
134+ fadeInAnimation.start();
135+ }
136+ }
137+ }
138+
139+ onConnectedChanged: {
140+ checkForUpdatesIfConnected();
141+ }
142+
143+ Connections {
144+ target: SystemImage
145+ onUpdateDownloaded: {
146+ console.info("Wizard: A system-image update has been downloaded!")
147+ root.updateDownloaded = true;
148+ }
149+ onCheckingForUpdatesChanged: {
150+ if (!SystemImage.checkingForUpdates) {
151+ console.info("Wizard: Update check finished")
152+ if (SystemImage.updateAvailable) {
153+ console.info("Wizard: A system-image update is available!")
154+ } else {
155+ console.info("Wizard: No update found.")
156+ }
157+ }
158+ }
159+ }
160+
161+ Timer {
162+ id: impatientLoadingTimer
163+ interval: 1700
164+ onTriggered: {
165+ console.warn("Wizard: Impatient timer going off. Fix the wizard, it's too slow at skipping pages.")
166+ pagesSpinner.running = true;
167+ }
168+ }
169+
108 PageStack {
170 PageStack {
109 id: pageStack
171 id: pageStack
110 objectName: "pageStack"
172 objectName: "pageStack"
@@ -139,18 +201,21 @@ StyledItem {
139
201
140 function load(path) {
202 function load(path) {
141 if (currentPage) {
203 if (currentPage) {
142− currentPage.enabled = false
204+ currentPage.enabled = false;
143 }
205 }
144
206
145 // First load it invisible, check that we should actually use
207 // First load it invisible, check that we should actually use
146 // this page, and either skip it or continue.
208 // this page, and either skip it or continue.
147− push(path, {"opacity": 0, "enabled": false})
209+ push(path, {"opacity": 0, "enabled": false});
148
210
149 timeout.restart();
211 timeout.restart();
212+ impatientLoadingTimer.start();
213+
214+ console.info("Wizard: Loading page " + currentPage.objectName);
150
215
151 // Check for immediate skip or not. We may have to wait for
216 // Check for immediate skip or not. We may have to wait for
152 // skipValid to be assigned (see Connections object below)
217 // skipValid to be assigned (see Connections object below)
153− checkSkip()
218+ checkSkip();
154
219
155 var isPrimaryPage = !currentPage.customTitle;
220 var isPrimaryPage = !currentPage.customTitle;
156 if (isPrimaryPage) {
221 if (isPrimaryPage) {
@@ -162,13 +227,19 @@ StyledItem {
162
227
163 function checkSkip() {
228 function checkSkip() {
164 if (!currentPage) { // may have had a parse error
229 if (!currentPage) { // may have had a parse error
230+ console.warn("Wizard: page skipped due to possible parse error.");
165− next()
231+ next();
166 } else if (currentPage.skipValid) {
232 } else if (currentPage.skipValid) {
167 if (currentPage.skip) {
233 if (currentPage.skip) {
168− next()
234+ next();
235+ } else if ((currentPage.onlyOnUpdate && !wizard.isUpdate) ||
236+ (currentPage.onlyOnInstall && wizard.isUpdate)) {
237+ next();
169 } else {
238 } else {
239+ impatientLoadingTimer.stop()
240+ pagesSpinner.running = false;
170− currentPage.opacity = 1
241+ currentPage.opacity = 1;
171− currentPage.enabled = true
242+ currentPage.enabled = true;
172 timeout.stop();
243 timeout.stop();
173 }
244 }
174 }
245 }
@@ -179,7 +250,7 @@ StyledItem {
179 objectName: "timeout"
250 objectName: "timeout"
180 interval: 2000 // wizard pages shouldn't take long
251 interval: 2000 // wizard pages shouldn't take long
181 onTriggered: {
252 onTriggered: {
182− console.warn("Wizard page " + pageStack.currentPage.objectName + " skipped due to taking too long!!!");
253+ console.warn("Wizard: Page " + pageStack.currentPage.objectName + " skipped due to taking too long!!!");
183 pageStack.currentPage.skip = true;
254 pageStack.currentPage.skip = true;
184 pageStack.currentPage.skipValid = true;
255 pageStack.currentPage.skipValid = true;
185 }
256 }
@@ -190,6 +261,9 @@ StyledItem {
190 onSkipValidChanged: pageStack.checkSkip()
261 onSkipValidChanged: pageStack.checkSkip()
191 }
262 }
192
263
193− Component.onCompleted: next()
264+ Component.onCompleted: {
265+ checkForUpdatesIfConnected();
266+ next();
267+ }
194 }
268 }
195}
269}
qml/Wizard/Pages/10-welcome-update.qmlnew file+137
@@ -0,0 +1,137 @@
1+/*
2+ * Copyright (C) 2018 The UBports project
3+ *
4+ * Written by: Marius Gripsgard <marius@ubports.com>
5+ *
6+ * This program is free software; you can redistribute it and/or modify
7+ * it under the terms of the GNU General Public License as published by
8+ * the Free Software Foundation; version 3.
9+ *
10+ * This program is distributed in the hope that it will be useful,
11+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
12+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13+ * GNU General Public License for more details.
14+ *
15+ * You should have received a copy of the GNU General Public License
16+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
17+ */
18+
19+import QtQuick 2.4
20+import Ubuntu.Components 1.3
21+import Wizard 0.1
22+import ".." as LocalComponents
23+
24+LocalComponents.Page {
25+ objectName: "welcomeUpdate"
26+
27+ hasBackButton: false
28+ customTitle: true
29+ buttonBarVisible: false
30+ onlyOnUpdate: true
31+
32+ Component.onCompleted: {
33+ state = "reanchored";
34+ }
35+
36+ states: State {
37+ name: "reanchored"
38+ AnchorChanges { target: bgImage; anchors.top: parent.top; anchors.bottom: parent.bottom }
39+ AnchorChanges { target: column;
40+ anchors.verticalCenter: parent.verticalCenter;
41+ anchors.top: undefined
42+ }
43+ }
44+
45+ SequentialAnimation {
46+ id: splashAnimation
47+ PauseAnimation { duration: UbuntuAnimation.BriskDuration }
48+ SmoothedAnimation {
49+ target: bgImage
50+ property: "height"
51+ to: units.gu(16)
52+ duration: UbuntuAnimation.BriskDuration
53+ }
54+ NumberAnimation {
55+ target: bgImage
56+ property: 'opacity'
57+ from: 1
58+ to: 0
59+ }
60+ }
61+
62+ Image {
63+ id: bgImage
64+ source: wideMode ? "data/Desktop_splash_screen_bkg.png" : "data/Phone_splash_screen_bkg.png"
65+ scale: Image.PreserveAspectFit
66+ anchors.left: parent.left
67+ anchors.right: parent.right
68+ anchors.bottom: parent.top // outside to let it slide down
69+ visible: opacity > 0
70+ }
71+
72+ Item {
73+ id: column
74+ anchors.leftMargin: leftMargin
75+ anchors.rightMargin: rightMargin
76+ anchors.left: parent.left
77+ anchors.right: parent.right
78+ anchors.top: parent.bottom // outside to let it slide in
79+ height: childrenRect.height
80+ visible: opacity > 0
81+
82+ Label {
83+ id: welcomeLabel
84+ anchors.left: parent.left
85+ anchors.right: parent.right
86+ horizontalAlignment: Text.AlignHCenter
87+ wrapMode: Text.Wrap
88+ fontSize: "x-large"
89+ font.weight: Font.Light
90+ lineHeight: 1.2
91+ text: i18n.tr("Welcome to ") + System.version.split("Base-Version: ")[1]
92+ color: whiteColor
93+ }
94+
95+ Label {
96+ id: welcomeText
97+ anchors.left: parent.left
98+ anchors.right: parent.right
99+ anchors.top: welcomeLabel.bottom
100+ anchors.topMargin: units.gu(2)
101+ horizontalAlignment: Text.AlignHCenter
102+ wrapMode: Text.Wrap
103+ fontSize: "large"
104+ font.weight: Font.Light
105+ lineHeight: 1.2
106+ text: i18n.tr("We will make sure your device is ready to use ") + System.version.split("Base-Version: ")[1]
107+ color: whiteColor
108+ }
109+
110+ Rectangle {
111+ anchors {
112+ top: welcomeText.bottom
113+ horizontalCenter: parent.horizontalCenter
114+ topMargin: units.gu(4)
115+ }
116+ color: "transparent"
117+ border.width: units.dp(1)
118+ border.color: whiteColor
119+ radius: units.dp(4)
120+ width: buttonLabel.paintedWidth + units.gu(6)
121+ height: buttonLabel.paintedHeight + units.gu(1.8)
122+
123+ Label {
124+ id: buttonLabel
125+ color: whiteColor
126+ text: i18n.tr("Next")
127+ fontSize: "medium"
128+ anchors.centerIn: parent
129+ }
130+ AbstractButton {
131+ objectName: "nextButton"
132+ anchors.fill: parent
133+ onClicked: pageStack.next();
134+ }
135+ }
136+ }
137+}
qml/Wizard/Pages/10-welcome.qml+2
@@ -1,4 +1,5 @@
1/*
1/*
2+ * Copyright (C) 2018 The UBports project
2 * Copyright (C) 2013-2016 Canonical, Ltd.
3 * Copyright (C) 2013-2016 Canonical, Ltd.
3 *
4 *
4 * This program is free software; you can redistribute it and/or modify
5 * This program is free software; you can redistribute it and/or modify
@@ -27,6 +28,7 @@ LocalComponents.Page {
27
28
28 title: i18n.tr("Language")
29 title: i18n.tr("Language")
29 forwardButtonSourceComponent: forwardButton
30 forwardButtonSourceComponent: forwardButton
31+ onlyOnInstall: true
30
32
31 UbuntuLanguagePlugin {
33 UbuntuLanguagePlugin {
32 id: plugin
34 id: plugin
qml/Wizard/Pages/11-changelog.qmlnew file+95
@@ -0,0 +1,95 @@
1+/*
2+ * Copyright (C) 2018 The UBports project
3+ *
4+ * Written by: Dalton Durst <dalton@ubports.com>
5+ * Marius Gripsgard <marius@ubports.com>
6+ *
7+ * This program is free software; you can redistribute it and/or modify
8+ * it under the terms of the GNU General Public License as published by
9+ * the Free Software Foundation; version 3.
10+ *
11+ * This program is distributed in the hope that it will be useful,
12+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
13+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14+ * GNU General Public License for more details.
15+ *
16+ * You should have received a copy of the GNU General Public License
17+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
18+ */
19+
20+import QtQuick 2.4
21+import Ubuntu.Components 1.3
22+import AccountsService 0.1
23+import Wizard 0.1
24+import ".." as LocalComponents
25+
26+LocalComponents.Page {
27+ objectName: "changelogPage"
28+ title: i18n.tr("What's new")
29+ id: changelogPage
30+
31+ // See skipTimer below for information about this hack
32+ property bool loading: false
33+
34+ forwardButtonSourceComponent: forwardButton
35+ onlyOnUpdate: true
36+
37+ ScrollView {
38+ id: scroll
39+
40+ anchors {
41+ fill: content
42+ leftMargin: wideMode ? parent.leftMargin : staticMargin
43+ rightMargin: wideMode ? parent.rightMargin : staticMargin
44+ }
45+
46+ Column {
47+ id: column
48+
49+ width: scroll.width
50+
51+ // Make it appear that the text is hiding behind the header
52+ Item {
53+ height: staticMargin
54+ width: units.gu(1)
55+ }
56+
57+ Label {
58+ anchors {
59+ // Keep the scroll bar from interfering with text
60+ rightMargin: units.gu(1)
61+ }
62+ id: changelogText
63+ width: parent.width
64+ wrapMode: Text.WordWrap
65+ textSize: Label.Medium
66+ text: Changelog.text
67+ }
68+ }
69+ }
70+
71+ Component {
72+ id: forwardButton
73+ LocalComponents.StackButton {
74+ text: loading ? i18n.tr("Loading...") : i18n.tr("Next")
75+ onClicked: {
76+ changelogPage.loading = true;
77+ skipTimer.restart();
78+ }
79+ }
80+ }
81+
82+ // A horrible hack to make sure the UI refreshes before actually skipping
83+ // Without this, people press the Next button multiple times and skip
84+ // multiple pages at once.
85+ Timer {
86+ id: skipTimer
87+ interval: 100
88+ repeat: false
89+ running: false
90+ onTriggered: {
91+ changelogPage.loading = false;
92+ pageStack.next();
93+ }
94+ }
95+}
qml/Wizard/Pages/30-wifi.qml+3−2
@@ -20,6 +20,7 @@ import QMenuModel 0.1 as QMenuModel
20import Ubuntu.Components 1.3
20import Ubuntu.Components 1.3
21import Wizard 0.1
21import Wizard 0.1
22import Ubuntu.Connectivity 1.0
22import Ubuntu.Connectivity 1.0
23+import Ubuntu.SystemSettings.Update 1.0
23import ".." as LocalComponents
24import ".." as LocalComponents
24import "../../Components"
25import "../../Components"
25
26
@@ -30,7 +31,7 @@ LocalComponents.Page {
30 title: i18n.tr("Connect to Wi‑Fi")
31 title: i18n.tr("Connect to Wi‑Fi")
31 forwardButtonSourceComponent: forwardButton
32 forwardButtonSourceComponent: forwardButton
32
33
33− readonly property bool connected: Connectivity.online
34+ skip: root.connected
34
35
35 function getExtendedProperty(object, propertyName, defaultValue) {
36 function getExtendedProperty(object, propertyName, defaultValue) {
36 if (object && object.hasOwnProperty(propertyName)) {
37 if (object && object.hasOwnProperty(propertyName)) {
@@ -203,7 +204,7 @@ LocalComponents.Page {
203 Component {
204 Component {
204 id: forwardButton
205 id: forwardButton
205 LocalComponents.StackButton {
206 LocalComponents.StackButton {
206− text: (connected || listview.count === 0) ? i18n.tr("Next") : i18n.tr("Skip")
207+ text: (root.connected || listview.count === 0) ? i18n.tr("Next") : i18n.tr("Skip")
207 onClicked: {
208 onClicked: {
208 pageStack.next();
209 pageStack.next();
209 }
210 }
qml/Wizard/Pages/40-location.qmldeleted−290
@@ -1,290 +0,0 @@
1−/*
2− * Copyright (C) 2013-2016 Canonical, Ltd.
3− *
4− * This program is free software; you can redistribute it and/or modify
5− * it under the terms of the GNU General Public License as published by
6− * the Free Software Foundation; version 3.
7− *
8− * This program is distributed in the hope that it will be useful,
9− * but WITHOUT ANY WARRANTY; without even the implied warranty of
10− * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11− * GNU General Public License for more details.
12− *
13− * You should have received a copy of the GNU General Public License
14− * along with this program. If not, see <http://www.gnu.org/licenses/>.
15− */
16−
17−import QtQuick 2.4
18−import AccountsService 0.1
19−import QMenuModel 0.1 as QMenuModel
20−import Qt.labs.folderlistmodel 2.1
21−import Ubuntu.Components 1.3
22−import ".." as LocalComponents
23−
24−LocalComponents.Page {
25− objectName: "locationPage"
26−
27− title: i18n.tr("Location Services")
28− forwardButtonSourceComponent: forwardButton
29−
30− readonly property bool pathSet: AccountsService.hereLicensePathValid
31− skipValid: pathSet && (AccountsService.hereLicensePath === "" || termsModel.count > 0)
32− skip: skipValid && (AccountsService.hereLicensePath === "" || termsModel.count === 2) // no files but . and ..
33−
34− FolderListModel {
35− id: termsModel
36− folder: AccountsService.hereLicensePath
37− nameFilters: ["*.html"]
38− showOnlyReadable: true
39− showDotAndDotDot: true // so that count == 0 means we're not done scanning yet
40− }
41−
42− QMenuModel.QDBusActionGroup {
43− id: locationActionGroup
44− objectName: "locationActionGroup"
45− busType: QMenuModel.DBus.SessionBus
46− busName: "com.canonical.indicator.location"
47− objectPath: "/com/canonical/indicator/location"
48− property variant location: action("location-detection-enabled")
49− property variant gps: action("gps-detection-enabled")
50− Component.onCompleted: start()
51− }
52−
53− Item {
54− id: column
55− anchors.fill: content
56− anchors.topMargin: customMargin
57− anchors.leftMargin: wideMode ? parent.leftMargin : 0
58− anchors.rightMargin: wideMode ? parent.rightMargin : 0
59−
60− Item {
61− id: hereCheckGroup
62− anchors {
63− left: parent.left
64− right: parent.right
65− top: parent.top
66− }
67− height: hereCheckLabel.height + hereTermsLabel.height
68−
69− Label {
70− id: hereCheckLabel
71− objectName: "hereCheckLabel"
72− anchors {
73− left: parent.left
74− right: hereTermsCheck.left
75− top: parent.top
76− leftMargin: column.anchors.leftMargin == 0 ? staticMargin : 0
77− rightMargin: units.gu(2)
78− }
79− lineHeight: 1.2
80− wrapMode: Text.WordWrap
81− color: textColor
82− font.weight: checked ? Font.Normal : Font.Light
83− text: i18n.tr("Use GPS, Wi-Fi hotspots and mobile network anonymously to detect location (recommended)")
84− property bool checked: true
85− }
86−
87− Label {
88− id: hereTermsLabel
89− objectName: "hereTermsLabel"
90− anchors {
91− left: parent.left
92− right: hereTermsCheck.left
93− top: hereCheckLabel.bottom
94− topMargin: units.gu(1)
95− leftMargin: column.anchors.leftMargin == 0 ? staticMargin : 0
96− rightMargin: units.gu(2)
97− }
98−
99− wrapMode: Text.WordWrap
100− color: textColor
101− font.weight: Font.Light
102− fontSize: "small"
103− lineHeight: 1.2
104− linkColor: UbuntuColors.orange
105− text: i18n.tr("By selecting this option you agree to the Nokia HERE %1.")
106− .arg("<a href='#'>" + i18n.ctr("part of: Nokia HERE terms and conditions", "terms and conditions") + "</a>")
107− Mouse.forwardTo: hereCheckArea // clicking on the link also selects this group
108− Mouse.priority: Mouse.AfterItem
109− onLinkActivated: {
110− pageStack.load(Qt.resolvedUrl("here-terms.qml"));
111− }
112− }
113−
114− Image {
115− id: hereTermsCheck
116− fillMode: Image.PreserveAspectFit
117− height: units.gu(1.5)
118− source: "data/Tick@30.png"
119− opacity: hereCheckLabel.checked ? 1 : 0
120− anchors.right: parent.right
121− anchors.verticalCenter: hereCheckLabel.verticalCenter
122− anchors.rightMargin: column.anchors.rightMargin == 0 ? staticMargin : 0
123− }
124−
125− MouseArea {
126− id: hereCheckArea
127− anchors.fill: parent
128− z: hereCheckGroup.z - 1 // so that the link can be activated
129− onClicked: {
130− if (!hereCheckLabel.checked) {
131− hereCheckLabel.checked = true;
132− gpsCheckLabel.checked = false;
133− nopeCheckLabel.checked = false;
134− }
135− }
136− }
137− }
138−
139− Rectangle {
140− id: divider
141− anchors.left: parent.left
142− anchors.right: parent.right
143− anchors.top: hereCheckGroup.bottom
144− anchors.topMargin: units.gu(3)
145− height: units.dp(1)
146− color: dividerColor
147− }
148−
149− Label {
150− id: gpsCheckLabel
151− objectName: "gpsCheckLabel"
152− anchors {
153− left: parent.left
154− right: parent.right
155− top: divider.bottom
156− topMargin: units.gu(3)
157− leftMargin: column.anchors.leftMargin == 0 ? staticMargin : 0
158− rightMargin: units.gu(2)
159− }
160− text: i18n.tr("GPS only")
161− wrapMode: Text.WordWrap
162− color: textColor
163− font.weight: checked ? Font.Normal : Font.Light
164− width: content.width
165− property bool checked: false
166−
167− MouseArea {
168− anchors.fill: parent
169− onClicked: {
170− if (!gpsCheckLabel.checked) {
171− hereCheckLabel.checked = false;
172− gpsCheckLabel.checked = true;
173− nopeCheckLabel.checked = false;
174− }
175− }
176− }
177− }
178−
179− Image {
180− fillMode: Image.PreserveAspectFit
181− height: units.gu(1.5)
182− source: "data/Tick@30.png"
183− opacity: gpsCheckLabel.checked ? 1 : 0
184− anchors.right: parent.right
185− anchors.verticalCenter: gpsCheckLabel.verticalCenter
186− anchors.rightMargin: column.anchors.rightMargin == 0 ? staticMargin : 0
187− }
188−
189− Rectangle {
190− id: divider2
191− anchors.left: parent.left
192− anchors.right: parent.right
193− anchors.top: gpsCheckLabel.bottom
194− anchors.topMargin: units.gu(3)
195− height: units.dp(1)
196− color: dividerColor
197− }
198−
199− Label {
200− id: nopeCheckLabel
201− objectName: "nopeCheckLabel"
202− anchors {
203− left: parent.left
204− right: parent.right
205− top: divider2.bottom
206− topMargin: units.gu(3)
207− leftMargin: column.anchors.leftMargin == 0 ? staticMargin : 0
208− rightMargin: units.gu(2)
209− }
210− wrapMode: Text.WordWrap
211− color: textColor
212− font.weight: checked ? Font.Normal : Font.Light
213− width: content.width
214− text: i18n.tr("Don't use my location")
215− property bool checked: false
216−
217− MouseArea {
218− anchors.fill: parent
219− onClicked: {
220− if (!nopeCheckLabel.checked) {
221− hereCheckLabel.checked = false;
222− gpsCheckLabel.checked = false;
223− nopeCheckLabel.checked = true;
224− }
225− }
226− }
227− }
228−
229− Image {
230− fillMode: Image.PreserveAspectFit
231− height: units.gu(1.5)
232− source: "data/Tick@30.png"
233− opacity: nopeCheckLabel.checked ? 1 : 0
234− anchors.right: parent.right
235− anchors.verticalCenter: nopeCheckLabel.verticalCenter
236− anchors.rightMargin: column.anchors.rightMargin == 0 ? staticMargin : 0
237− }
238−
239− Rectangle {
240− id: divider3
241− anchors.left: parent.left
242− anchors.right: parent.right
243− anchors.top: nopeCheckLabel.bottom
244− anchors.topMargin: units.gu(3)
245− height: units.dp(1)
246− color: dividerColor
247− }
248−
249− Label {
250− id: infoLabel
251− anchors {
252− left: parent.left
253− right: parent.right
254− top: divider3.bottom
255− topMargin: units.gu(4)
256− leftMargin: column.anchors.leftMargin == 0 ? staticMargin : 0
257− rightMargin: column.anchors.rightMargin == 0 ? staticMargin : 0
258− }
259− wrapMode: Text.Wrap
260− text: i18n.tr("You can change it later in System Settings.")
261− color: textColor
262− fontSize: "small"
263− font.weight: Font.Light
264− width: content.width
265− }
266− }
267−
268− Component {
269− id: forwardButton
270− LocalComponents.StackButton {
271− text: i18n.tr("Next")
272− onClicked: {
273− var locationOn = gpsCheckLabel.checked || hereCheckLabel.checked;
274− var gpsOn = gpsCheckLabel.checked || hereCheckLabel.checked;
275− var hereOn = hereCheckLabel.checked;
276−
277− // location service doesn't currently listen to updateState
278− // requests, so we activate the actions if needed.
279− if (locationActionGroup.location.state != locationOn) {
280− locationActionGroup.location.activate();
281− }
282− if (locationActionGroup.gps.state != gpsOn) {
283− locationActionGroup.gps.activate();
284− }
285− AccountsService.hereEnabled = hereOn;
286− pageStack.next();
287− }
288− }
289− }
290−}
qml/Wizard/Pages/50-timezone.qml+2−1
@@ -28,6 +28,7 @@ LocalComponents.Page {
28 objectName: "tzPage"
28 objectName: "tzPage"
29
29
30 title: i18n.tr("Time Zone")
30 title: i18n.tr("Time Zone")
31+ focusItem: searchField
31 forwardButtonSourceComponent: forwardButton
32 forwardButtonSourceComponent: forwardButton
32
33
33 property string selectedTimeZone: ""
34 property string selectedTimeZone: ""
@@ -36,6 +37,7 @@ LocalComponents.Page {
36
37
37 // for testing
38 // for testing
38 readonly property alias tdModule: timeDatePanel
39 readonly property alias tdModule: timeDatePanel
40+ onlyOnInstall: true
39
41
40 function highlightTimezone(offset) {
42 function highlightTimezone(offset) {
41 highlightImage.source = "data/timezonemap/timezone_" + offset + ".png";
43 highlightImage.source = "data/timezonemap/timezone_" + offset + ".png";
@@ -90,7 +92,6 @@ LocalComponents.Page {
90 }
92 }
91
93
92 resetViews();
94 resetViews();
93− searchField.forceActiveFocus();
94 }
95 }
95 }
96 }
96
97
qml/Wizard/Pages/60-account.qml+12−10
@@ -22,12 +22,18 @@ import ".." as LocalComponents
22LocalComponents.Page {
22LocalComponents.Page {
23 objectName: "accountPage"
23 objectName: "accountPage"
24 title: i18n.tr("Personalize Your Device")
24 title: i18n.tr("Personalize Your Device")
25+ focusItem: nameInput
25
26
26 forwardButtonSourceComponent: forwardButton
27 forwardButtonSourceComponent: forwardButton
28+ onlyOnInstall: true
27
29
28− QtObject {
29− id: d
30− readonly property string validName: nameInput.text.trim()
30+ readonly property string validName: nameInput.text.trim()
31+
32+ function saveNameAndGoNext() {
33+ if (validName) {
34+ AccountsService.realName = validName;
35+ }
36+ pageStack.next();
31 }
37 }
32
38
33 Column {
39 Column {
@@ -56,19 +62,15 @@ LocalComponents.Page {
56 anchors.left: parent.left
62 anchors.left: parent.left
57 anchors.right: parent.right
63 anchors.right: parent.right
58 inputMethodHints: Qt.ImhNoPredictiveText
64 inputMethodHints: Qt.ImhNoPredictiveText
65+ onAccepted: saveNameAndGoNext()
59 }
66 }
60 }
67 }
61
68
62 Component {
69 Component {
63 id: forwardButton
70 id: forwardButton
64 LocalComponents.StackButton {
71 LocalComponents.StackButton {
65− text: d.validName ? i18n.tr("Next") : i18n.tr("Skip")
72+ text: validName ? i18n.tr("Next") : i18n.tr("Skip")
66− onClicked: {
73+ onClicked: saveNameAndGoNext()
67− if (d.validName) {
68− AccountsService.realName = d.validName;
69− }
70− pageStack.next();
71− }
72 }
74 }
73 }
75 }
74}
76}
qml/Wizard/Pages/70-passwd-type.qml+1
@@ -44,6 +44,7 @@ LocalComponents.Page {
44 // or this isn't the first time the wizard has been run, etc). We can't
44 // or this isn't the first time the wizard has been run, etc). We can't
45 // properly set the password again, so let's not pretend we can.
45 // properly set the password again, so let's not pretend we can.
46 skip: securityPrivacy.securityType !== UbuntuSecurityPrivacyPanel.Swipe
46 skip: securityPrivacy.securityType !== UbuntuSecurityPrivacyPanel.Swipe
47+ onlyOnInstall: true
47
48
48 function indexToMethod(index) {
49 function indexToMethod(index) {
49 if (index === 0/* || index === 1*/)
50 if (index === 0/* || index === 1*/)
qml/Wizard/Pages/75-report-check.qmldeleted−91
@@ -1,91 +0,0 @@
1−/*
2− * Copyright (C) 2015-2016 Canonical, Ltd.
3− *
4− * This program is free software; you can redistribute it and/or modify
5− * it under the terms of the GNU General Public License as published by
6− * the Free Software Foundation; version 3.
7− *
8− * This program is distributed in the hope that it will be useful,
9− * but WITHOUT ANY WARRANTY; without even the implied warranty of
10− * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11− * GNU General Public License for more details.
12− *
13− * You should have received a copy of the GNU General Public License
14− * along with this program. If not, see <http://www.gnu.org/licenses/>.
15− */
16−
17−import QtQuick 2.4
18−import Ubuntu.Components 1.3
19−import Ubuntu.Web 0.2
20−import ".." as LocalComponents
21−
22−LocalComponents.Page {
23− id: reportingPage
24− objectName: "reportingPage"
25−
26− title: webview.visible ? i18n.tr("Privacy Policy") : i18n.tr("Help Us Improve")
27− forwardButtonSourceComponent: !webview.visible ? forwardButton : null
28− customBack: true
29− customTitle: webview.visible
30−
31− skip: !diagnostics.reportCrashes // skip the page when the system is configured not to report crashes
32−
33− onBackClicked: {
34− if (webview.visible) {
35− webview.visible = false;
36− } else {
37− pageStack.prev();
38− }
39− }
40−
41− Column {
42− id: column
43− anchors {
44− fill: content
45− leftMargin: leftMargin
46− rightMargin: rightMargin
47− topMargin: customMargin
48− }
49−
50− LocalComponents.CheckableSetting {
51− anchors {
52− left: parent.left
53− right: parent.right
54− }
55−
56− id: reportCheck
57− objectName: "reportCheck"
58− showDivider: false
59− text: i18n.tr("Improve system performance by sending us crashes and error reports.") + "<br><br><a href='#'>" +
60− i18n.tr("Privacy policy") + "</a>"
61− checked: diagnostics.reportCrashes
62− visible: !webview.visible
63− onLinkActivated: {
64− webview.url = "http://www.ubuntu.com/legal/terms-and-policies/privacy-policy";
65− webview.visible = true;
66− }
67− }
68−
69− WebView {
70− id: webview
71− objectName: "webview"
72− anchors.left: parent.left
73− anchors.right: parent.right
74− anchors.leftMargin: -leftMargin
75− anchors.rightMargin: -rightMargin
76− height: parent.height
77− visible: false
78− }
79− }
80−
81− Component {
82− id: forwardButton
83− LocalComponents.StackButton {
84− text: i18n.tr("Next")
85− onClicked: {
86− diagnostics.setReportCrashes(reportCheck.checked);
87− pageStack.next();
88− }
89− }
90− }
91−}
qml/Wizard/Pages/76-app-update.qmlnew file+241
@@ -0,0 +1,241 @@
1+/*
2+ * Copyright (C) 2018 The UBports project
3+ *
4+ * Written by: Dalton Durst <dalton@ubports.com>
5+ * Marius Gripsgard <marius@ubports.com>
6+ *
7+ * This program is free software: you can redistribute it and/or modify it
8+ * under the terms of the GNU General Public License version 3, as published
9+ * by the Free Software Foundation.
10+ *
11+ * This program is distributed in the hope that it will be useful, but
12+ * WITHOUT ANY WARRANTY; without even the implied warranties of
13+ * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
14+ * PURPOSE. See the GNU General Public License for more details.
15+ *
16+ * You should have received a copy of the GNU General Public License along
17+ * with this program. If not, see <http://www.gnu.org/licenses/>.
18+ */
19+
20+import QMenuModel 0.1
21+import QtQuick 2.4
22+import QtQuick.Layouts 1.1
23+import Ubuntu.Components 1.3
24+import Ubuntu.Components.ListItems 1.3 as ListItem
25+import Ubuntu.Components.Popups 1.3
26+import Ubuntu.SystemSettings.Update 1.0
27+import Wizard 0.1
28+import ".." as LocalComponents
29+
30+LocalComponents.Page {
31+ id: appUpdatePage
32+ objectName: "appUpdatePage"
33+
34+ title: i18n.tr("Update Apps")
35+ forwardButtonSourceComponent: forwardButton
36+
37+ property bool batchMode: false
38+
39+ onlyOnUpdate: true
40+
41+ property int updatesCount: {
42+ var count = 0;
43+ count += clickRepeater.count;
44+ return count;
45+ }
46+
47+ function check(force) {
48+ UpdateManager.check(UpdateManager.CheckClickIgnoreVersion);
49+ }
50+
51+ DownloadHandler {
52+ id: downloadHandler
53+ updateModel: UpdateManager.model
54+ }
55+
56+ Flickable {
57+ id: scrollWidget
58+ anchors {
59+ fill: content
60+ leftMargin: parent.leftMargin
61+ rightMargin: parent.rightMargin
62+ }
63+ clip: true
64+ contentHeight: scrollWidgetContent.height
65+ boundsBehavior: (contentHeight > scrollWidgetContent.height) ?
66+ Flickable.DragAndOvershootBounds :
67+ Flickable.StopAtBounds
68+ flickableDirection: Flickable.VerticalFlick
69+
70+ Column {
71+ id: scrollWidgetContent
72+ anchors {
73+ left: parent.left
74+ right: parent.right
75+ }
76+
77+ GlobalUpdateControls {
78+ id: g
79+ objectName: "global"
80+ anchors { left: parent.left; right: parent.right }
81+
82+ height: hidden ? 0 : units.gu(8)
83+ clip: true
84+ status: UpdateManager.status
85+ batchMode: appUpdatePage.batchMode
86+ updatesCount: appUpdatePage.updatesCount
87+ online: root.connected
88+ onStop: UpdateManager.cancel()
89+
90+ onRequestInstall: {
91+ install();
92+ }
93+ onInstall: {
94+ appUpdatePage.batchMode = true
95+ postClickBatchHandler.target = appUpdatePage;
96+ }
97+ }
98+
99+ Rectangle {
100+ id: overlay
101+ objectName: "overlay"
102+ anchors {
103+ left: parent.left
104+ leftMargin: units.gu(2)
105+ right: parent.right
106+ rightMargin: units.gu(2)
107+ }
108+ visible: placeholder.text
109+ color: theme.palette.normal.background
110+ height: units.gu(10)
111+
112+ Label {
113+ id: placeholder
114+ objectName: "overlayText"
115+ anchors.fill: parent
116+ verticalAlignment: Text.AlignVCenter
117+ horizontalAlignment: Text.AlignHCenter
118+ wrapMode: Text.WordWrap
119+ text: {
120+ var s = UpdateManager.status;
121+ if (!root.connected) {
122+ return i18n.tr("This device is not connected to the internet.") + " " +
123+ i18n.tr("Use the OpenStore app to check for updates once connected.");
124+ } else if (s === UpdateManager.StatusIdle && updatesCount === 0) {
125+ return i18n.tr("Software is up to date");
126+ } else if (s === UpdateManager.StatusServerError ||
127+ s === UpdateManager.StatusNetworkError) {
128+ return i18n.tr("The update server is not responding.") + " " +
129+ i18n.tr("Use the OpenStore app to check for updates once connected.");
130+ }
131+ return "";
132+ }
133+ }
134+ }
135+
136+ Column {
137+ id: clickUpdatesCol
138+ objectName: "clickUpdates"
139+ anchors { left: parent.left; right: parent.right }
140+ visible: {
141+ var s = UpdateManager.status;
142+ var haveUpdates = clickRepeater.count > 0;
143+ switch (s) {
144+ case UpdateManager.StatusCheckingImageUpdates:
145+ case UpdateManager.StatusIdle:
146+ return haveUpdates && root.connected;
147+ }
148+ return false;
149+ }
150+
151+ Repeater {
152+ id: clickRepeater
153+ model: UpdateManager.clickUpdates
154+
155+ delegate: ClickUpdateDelegate {
156+ objectName: "clickUpdatesDelegate" + index
157+ width: clickUpdatesCol.width
158+ updateState: model.updateState
159+ progress: model.progress
160+ version: remoteVersion
161+ size: model.size
162+ name: title
163+ iconUrl: model.iconUrl
164+ kind: model.kind
165+ error: model.error
166+ signedUrl: signedDownloadUrl
167+
168+ onInstall: downloadHandler.createDownload(model);
169+ onPause: downloadHandler.pauseDownload(model)
170+ onResume: downloadHandler.resumeDownload(model)
171+ onRetry: {
172+ /* This creates a new signed URL with which we can
173+ retry the download. See onSignedUrlChanged. */
174+ UpdateManager.retry(model.identifier,
175+ model.revision);
176+ }
177+
178+ onSignedUrlChanged: {
179+ // If we have a signedUrl, user intend to retry.
180+ if (signedUrl) {
181+ downloadHandler.retryDownload(model);
182+ }
183+ }
184+
185+ Connections {
186+ target: g
187+ onInstall: install()
188+ }
189+
190+ /* If we a downloadId, we expect UDM to restore it
191+ after some time. Workaround for lp:1603770. */
192+ Timer {
193+ id: downloadTimeout
194+ interval: 30000
195+ running: true
196+ onTriggered: {
197+ var s = updateState;
198+ if (model.downloadId
199+ || s === Update.StateQueuedForDownload
200+ || s === Update.StateDownloading) {
201+ downloadHandler.assertDownloadExist(model);
202+ }
203+ }
204+ }
205+ }
206+ }
207+ }
208+ } // Column inside flickable
209+ } // Flickable
210+
211+ Connections {
212+ id: postClickBatchHandler
213+ ignoreUnknownSignals: true
214+ target: null
215+ onUpdatesCountChanged: {
216+ if (target.updatesCount === 0) {
217+ appUpdatePage.batchMode = false;
218+ target = null;
219+ }
220+ }
221+ }
222+
223+ Component.onCompleted: check()
224+
225+ Component {
226+ id: forwardButton
227+ LocalComponents.StackButton {
228+ text: {
229+ var s = UpdateManager.status;
230+ if (s === UpdateManager.StatusIdle && updatesCount === 0) {
231+ return i18n.tr("Next");
232+ } else if (s === UpdateManager.StatusCheckingClickUpdates) {
233+ return i18n.tr("Please wait...")
234+ } else {
235+ return i18n.tr("Skip");
236+ }
237+ }
238+ onClicked: pageStack.next()
239+ }
240+ }
241+}
qml/Wizard/Pages/78-firmware-update.qml.disablednew file+246
@@ -0,0 +1,246 @@
1+/*
2+ * Copyright (C) 2018 The UBports project
3+ *
4+ * Written by: Marius Gripsgard <marius@ubports.com>
5+ *
6+ * This program is free software; you can redistribute it and/or modify
7+ * it under the terms of the GNU General Public License as published by
8+ * the Free Software Foundation; version 3.
9+ *
10+ * This program is distributed in the hope that it will be useful,
11+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
12+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13+ * GNU General Public License for more details.
14+ *
15+ * You should have received a copy of the GNU General Public License
16+ * along 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.SystemSettings.Update 1.0
23+import Ubuntu.Connectivity 1.0
24+import Wizard 0.1
25+import ".." as LocalComponents
26+
27+LocalComponents.Page {
28+ id: firmwareUpdatePage
29+ objectName: "firmwareUpdatePage"
30+
31+ title: i18n.tr("Firmware Update")
32+ forwardButtonSourceComponent: forwardButton
33+
34+ skip: !SystemImage.supportsFirmwareUpdate()
35+
36+ property bool online: root.connected
37+ property bool hasUpdate: false
38+ property bool isUpdating: false
39+ property bool isChecking: true
40+ property var partitions: ""
41+
42+ function check() {
43+ if (!SystemImage.supportsFirmwareUpdate()) {
44+ return;
45+ }
46+ firmwareUpdatePage.isChecking = true;
47+ SystemImage.checkForFirmwareUpdate();
48+ }
49+
50+ function flash() {
51+ if (!SystemImage.supportsFirmwareUpdate()) {
52+ return pageStack.next();
53+ }
54+ firmwareUpdatePage.isUpdating = true;
55+ SystemImage.updateFirmware();
56+ }
57+
58+ Connections {
59+ target: SystemImage
60+ onCheckForFirmwareUpdateDone: {
61+ var updateobj = JSON.parse(updateObj);
62+ if (Array.isArray(updateobj) && updateobj.length > 0) {
63+ for (var u in updateobj) {
64+ console.log(u);
65+ if (firmwareUpdatePage.partitions === "") {
66+ firmwareUpdatePage.partitions += updateobj[u].file;
67+ } else {
68+ firmwareUpdatePage.partitions += ", " + updateobj[u].file;
69+ }
70+ }
71+ firmwareUpdatePage.hasUpdate = true;
72+ }
73+ console.log(updateobj);
74+ firmwareUpdatePage.isChecking = false;
75+ }
76+ onUpdateFirmwareDone: {
77+ var updateobj = JSON.parse(updateObj);
78+ if (Array.isArray(updateobj) && updateobj.length === 0) {
79+ // This means success!
80+ firmwareUpdatePage.hasUpdate = false;
81+ firmwareUpdatePage.partitions = "";
82+ System.skipUntilFinishedPage();
83+ SystemImage.reboot();
84+ return;
85+ }
86+ console.log(updateobj);
87+ firmwareUpdatePage.isUpdating = false;
88+ }
89+ }
90+
91+ Column {
92+ id: column
93+ anchors {
94+ fill: content
95+ leftMargin: firmwareUpdatePage.leftMargin
96+ rightMargin: firmwareUpdatePage.rightMargin
97+ topMargin: firmwareUpdatePage.customMargin
98+ }
99+ spacing: units.gu(3)
100+ opacity: spinner.visible ? 0.5 : 1
101+ Behavior on opacity {
102+ UbuntuNumberAnimation {}
103+ }
104+
105+ Label {
106+ visible: !firmwareUpdatePage.isChecking
107+ anchors.horizontalCenter: parent.horizontalCenter
108+ font.weight: Font.Light
109+ wrapMode: Text.Wrap
110+ textSize: Label.Medium
111+ text: firmwareUpdatePage.hasUpdate ? i18n.tr("There is a firmware update available!")
112+ : online ? i18n.tr("Firmware is up to date!")
113+ : i18n.tr("Please connect to the Internet to check for updates.")
114+
115+ }
116+
117+ GridLayout {
118+ rows: 3
119+ columns: 2
120+ rowSpacing: units.gu(1)
121+ columnSpacing: units.gu(2)
122+ anchors.horizontalCenter: parent.horizontalCenter
123+ visible: hasUpdate
124+
125+ Icon {
126+ Layout.rowSpan: 3
127+ Layout.alignment: Qt.AlignVCenter | Qt.AlignHCenter
128+ width: units.gu(5)
129+ height: width
130+ name: "security-alert"
131+ }
132+
133+ Label {
134+ font.weight: Font.Normal
135+ textSize: Label.Medium
136+ text: i18n.tr("Firmware Update")
137+ }
138+
139+ Label {
140+ font.weight: Font.Light
141+ wrapMode: Text.WordWrap
142+ fontSize: "small"
143+ text: firmwareUpdatePage.partitions
144+ }
145+
146+ Label {
147+ font.weight: Font.Light
148+ fontSize: "small"
149+ text: i18n.tr("The device will automatically restart after installing is done.")
150+ }
151+ }
152+
153+ Rectangle {
154+ anchors.left: parent.left
155+ anchors.leftMargin: 3
156+ anchors.horizontalCenter: parent.horizontalCenter
157+ color: theme.palette.normal.foreground
158+ radius: units.dp(4)
159+ width: buttonLabel.paintedWidth + units.gu(3)
160+ height: buttonLabel.paintedHeight + units.gu(1.8)
161+ visible: hasUpdate
162+
163+ Label {
164+ id: buttonLabel
165+ text: i18n.tr("Install and restart now")
166+ font.weight: Font.Light
167+ anchors.horizontalCenter: parent.horizontalCenter
168+ anchors.centerIn: parent
169+ }
170+
171+ AbstractButton {
172+ id: button
173+ objectName: "installButton"
174+ anchors.fill: parent
175+ anchors.horizontalCenter: parent.horizontalCenter
176+ onClicked: {
177+ firmwareUpdatePage.flash()
178+ }
179+ }
180+
181+ transformOrigin: Item.Top
182+ scale: button.pressed ? 0.98 : 1.0
183+ Behavior on scale {
184+ ScaleAnimator {
185+ duration: UbuntuAnimation.SnapDuration
186+ easing.type: Easing.Linear
187+ }
188+ }
189+ }
190+ }
191+
192+ Column {
193+ id: spinner
194+ anchors.centerIn: firmwareUpdatePage
195+ visible: firmwareUpdatePage.isChecking || firmwareUpdatePage.isUpdating
196+ spacing: units.gu(1)
197+
198+ ActivityIndicator {
199+ anchors.horizontalCenter: parent.horizontalCenter
200+ running: parent.visible
201+ }
202+
203+ Label {
204+ wrapMode: Text.Wrap
205+ width: firmwareUpdatePage.width - units.gu(3)
206+ fontSize: "small"
207+ text: i18n.tr("Downloading and Flashing firmware updates, this could take a few minutes...")
208+ visible: firmwareUpdatePage.isUpdating
209+ }
210+
211+ Label {
212+ wrapMode: Text.Wrap
213+ fontSize: "small"
214+ text: i18n.tr("Checking for firmware update")
215+ visible: firmwareUpdatePage.isChecking
216+ }
217+
218+ Component.onCompleted: {
219+ if (online) {
220+ firmwareUpdatePage.check();
221+ }
222+ else {
223+ firmwareUpdatePage.isChecking = false;
224+ }
225+ }
226+ }
227+
228+ Connections {
229+ target: NetworkingStatus
230+ onOnlineChanged: {
231+ if (online) {
232+ firmwareUpdatePage.check();
233+ } else {
234+ firmwareUpdatePage.isChecking = false;
235+ }
236+ }
237+ }
238+
239+ Component {
240+ id: forwardButton
241+ LocalComponents.StackButton {
242+ text: (!firmwareUpdatePage.hasUpdate && !spinner.visible) ? i18n.tr("Next") : i18n.tr("Skip");
243+ onClicked: pageStack.next();
244+ }
245+ }
246+}
qml/Wizard/Pages/79-system-update.qmlnew file+148
@@ -0,0 +1,148 @@
1+/*
2+ * Copyright (C) 2016 Canonical, Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+import QtQuick 2.4
18+import QtQuick.Layouts 1.1
19+import Ubuntu.Components 1.3
20+import Ubuntu.SystemSettings.Update 1.0
21+import Wizard 0.1
22+import ".." as LocalComponents
23+
24+LocalComponents.Page {
25+ id: systemUpdatePage
26+ objectName: "systemUpdatePage"
27+
28+ title: i18n.tr("Update Device")
29+ forwardButtonSourceComponent: forwardButton
30+
31+ skip: !root.updateDownloaded // skip the page when the system update is not ready to install
32+
33+ Column {
34+ id: column
35+ anchors {
36+ fill: content
37+ leftMargin: systemUpdatePage.leftMargin
38+ rightMargin: systemUpdatePage.rightMargin
39+ topMargin: systemUpdatePage.customMargin
40+ }
41+ spacing: units.gu(3)
42+ opacity: spinner.running ? 0.5 : 1
43+ Behavior on opacity {
44+ UbuntuNumberAnimation {}
45+ }
46+
47+ Label {
48+ anchors.left: parent.left
49+ anchors.right: parent.right
50+ anchors.leftMargin: column.anchors.leftMargin == 0 ? staticMargin : 0
51+ font.weight: Font.Light
52+ color: textColor
53+ wrapMode: Text.Wrap
54+ fontSize: "small"
55+ text: i18n.tr("There is a system update available and ready to install. Afterwards, the device will automatically restart.")
56+ }
57+
58+ GridLayout {
59+ rows: 3
60+ columns: 2
61+ rowSpacing: units.gu(1)
62+ columnSpacing: units.gu(2)
63+
64+ Image {
65+ Layout.rowSpan: 3
66+ Layout.alignment: Qt.AlignVCenter | Qt.AlignHCenter
67+ sourceSize: Qt.size(units.gu(3), units.gu(3))
68+ fillMode: Image.PreserveAspectFit
69+ source: "image://theme/distributor-logo"
70+ }
71+
72+ Label {
73+ color: textColor
74+ font.weight: Font.Normal
75+ fontSize: "small"
76+ text: i18n.ctr("string identifying name of the update", "Ubuntu Touch system")
77+ }
78+
79+ Label {
80+ font.weight: Font.Light
81+ fontSize: "small"
82+ color: textColor
83+ text: i18n.ctr("version of the system update", "Version %1").arg(SystemImage.targetBuildNumber)
84+ }
85+ }
86+
87+ Label {
88+ anchors.left: parent.left
89+ anchors.right: parent.right
90+ anchors.leftMargin: column.anchors.leftMargin == 0 ? staticMargin : 0
91+ font.weight: Font.Light
92+ color: textColor
93+ wrapMode: Text.Wrap
94+ fontSize: "small"
95+ text: i18n.tr("This could take a few minutes...")
96+ }
97+
98+ Rectangle {
99+ anchors.left: parent.left
100+ anchors.leftMargin: column.anchors.leftMargin == 0 ? staticMargin : 0
101+ color: theme.palette.normal.foreground
102+ radius: units.dp(4)
103+ width: buttonLabel.paintedWidth + units.gu(3)
104+ height: buttonLabel.paintedHeight + units.gu(1.8)
105+
106+ Label {
107+ id: buttonLabel
108+ color: textColor
109+ text: i18n.tr("Install and restart now")
110+ font.weight: Font.Light
111+ anchors.centerIn: parent
112+ }
113+
114+ AbstractButton {
115+ id: button
116+ objectName: "installButton"
117+ anchors.fill: parent
118+ onClicked: {
119+ System.skipUntilFinishedPage();
120+ SystemImage.applyUpdate();
121+ }
122+ }
123+
124+ transformOrigin: Item.Top
125+ scale: button.pressed ? 0.98 : 1.0
126+ Behavior on scale {
127+ ScaleAnimator {
128+ duration: UbuntuAnimation.SnapDuration
129+ easing.type: Easing.Linear
130+ }
131+ }
132+ }
133+ }
134+
135+ ActivityIndicator {
136+ id: spinner
137+ anchors.centerIn: systemUpdatePage
138+ visible: running
139+ }
140+
141+ Component {
142+ id: forwardButton
143+ LocalComponents.StackButton {
144+ text: i18n.tr("Skip")
145+ onClicked: pageStack.next()
146+ }
147+ }
148+}
qml/Wizard/Pages/80-finished.qml+4−2
@@ -1,4 +1,5 @@
1/*
1/*
2+ * Copyright (C) 2018 The UBports project
2 * Copyright (C) 2013-2016 Canonical, Ltd.
3 * Copyright (C) 2013-2016 Canonical, Ltd.
3 *
4 *
4 * This program is free software; you can redistribute it and/or modify
5 * This program is free software; you can redistribute it and/or modify
@@ -16,6 +17,7 @@
16
17
17import QtQuick 2.4
18import QtQuick 2.4
18import Ubuntu.Components 1.3
19import Ubuntu.Components 1.3
20+import Wizard 0.1
19import ".." as LocalComponents
21import ".." as LocalComponents
20
22
21LocalComponents.Page {
23LocalComponents.Page {
@@ -86,7 +88,7 @@ LocalComponents.Page {
86 fontSize: "x-large"
88 fontSize: "x-large"
87 font.weight: Font.Light
89 font.weight: Font.Light
88 lineHeight: 1.2
90 lineHeight: 1.2
89− text: i18n.tr("Welcome to Ubuntu")
91+ text: (System.isUpdate) ? i18n.tr("Welcome Back") : i18n.tr("Welcome to Ubuntu")
90 color: whiteColor
92 color: whiteColor
91 }
93 }
92
94
@@ -121,7 +123,7 @@ LocalComponents.Page {
121 Label {
123 Label {
122 id: buttonLabel
124 id: buttonLabel
123 color: whiteColor
125 color: whiteColor
124− text: i18n.tr("Get Started")
126+ text: (System.isUpdate) ? i18n.tr("Continue") : i18n.tr("Get Started")
125 fontSize: "medium"
127 fontSize: "medium"
126 anchors.centerIn: parent
128 anchors.centerIn: parent
127 }
129 }
qml/Wizard/Pages/ClickUpdateDelegate.qmlnew file+26
@@ -0,0 +1,26 @@
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.SystemSettings.Update 1.0
21+
22+UpdateDelegate {
23+ id: update
24+
25+ property string signedUrl
26+}
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: downloadHandler
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/GlobalUpdateControls.qmlnew file+138
@@ -0,0 +1,138 @@
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+
88+ RowLayout {
89+ id: install
90+ anchors {
91+ fill: parent
92+ margins: units.gu(2)
93+ }
94+ spacing: units.gu(2)
95+
96+ Behavior on opacity {
97+ UbuntuNumberAnimation {}
98+ }
99+
100+ opacity: visible ? 1 : 0
101+ visible: {
102+ var canInstall = g.status === UpdateManager.StatusIdle;
103+ return canInstall && updatesCount > 1;
104+ }
105+
106+ Label {
107+ objectName: "updatesGlobalInfoLabel"
108+ // // TRANSLATORS: %1 is number of software updates available.
109+ text: i18n.tr("%1 update available",
110+ "%1 updates available",
111+ updatesCount).arg(updatesCount)
112+ Layout.fillWidth: true
113+ }
114+
115+ Button {
116+ objectName: "updatesGlobalInstallButton"
117+ text: {
118+ if (g.requireRestart === true) {
119+ return i18n.tr("Update all…");
120+ } else {
121+ return i18n.tr("Update all");
122+ }
123+ }
124+ onClicked: g.requestInstall()
125+ color: theme.palette.normal.positive
126+ strokeColor: "transparent"
127+ }
128+ }
129+
130+ ListItems.ThinDivider {
131+ visible: !g.hidden
132+ anchors {
133+ left: parent.left
134+ right: parent.right
135+ bottom: parent.bottom
136+ }
137+ }
138+}
qml/Wizard/Pages/UpdateDelegate.qmlnew file+389
@@ -0,0 +1,389 @@
1+/*
2+ * This file is part of system-settings
3+ *
4+ * Copyright (C) 2016 Canonical Ltd.
5+ *
6+ * Contact: Jonas G. Drange <jonas.drange@canonical.com>
7+ *
8+ * This program is free software: you can redistribute it and/or modify it
9+ * under the terms of the GNU General Public License version 3, as published
10+ * by the Free Software Foundation.
11+ *
12+ * This program is distributed in the hope that it will be useful, but
13+ * WITHOUT ANY WARRANTY; without even the implied warranties of
14+ * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
15+ * PURPOSE. See the GNU General Public License for more details.
16+ *
17+ * You should have received a copy of the GNU General Public License along
18+ * with this program. If not, see <http://www.gnu.org/licenses/>.
19+ *
20+ */
21+
22+import QtQuick 2.4
23+import QtQuick.Layouts 1.1
24+import Ubuntu.Components 1.3
25+import Ubuntu.Components.ListItems 1.3 as ListItems
26+import Ubuntu.Components.Themes 1.3
27+import Ubuntu.SystemSettings.Update 1.0
28+
29+ListItem {
30+ id: update
31+
32+ property int updateState // This is an Update::State
33+ property int kind // This is an Update::Kind
34+ property real size
35+ property string version
36+ property string downloadId
37+ property date updatedAt
38+ property bool launchable: false
39+
40+ property alias name: nameLabel.text
41+ property alias error: errorElementDetail.text
42+ property alias iconUrl: icon.source
43+ property alias progress: progressBar.value
44+
45+ signal retry()
46+ signal download()
47+ signal pause()
48+ signal resume()
49+ signal install()
50+ signal launch()
51+
52+ height: layout.height + (divider.visible ? divider.height : 0)
53+ Behavior on height {
54+ animation: UbuntuNumberAnimation {}
55+ }
56+
57+ SlotsLayout {
58+ id: layout
59+ mainSlot: ColumnLayout {
60+ spacing: units.gu(1)
61+ // Width the parent, minus the icon and some padding
62+ width: parent.width - (icon.width + (layout.padding.top * 3))
63+
64+ RowLayout {
65+ spacing: units.gu(2)
66+
67+ Label {
68+ id: nameLabel
69+ verticalAlignment: Text.AlignVCenter
70+ elide: Text.ElideMiddle
71+ Layout.fillWidth: true
72+ Layout.fillHeight: true
73+ }
74+
75+ Button {
76+ id: button
77+ objectName: "updateButton"
78+ Layout.alignment: Qt.AlignRight | Qt.AlignVCenter
79+ visible: {
80+ switch (updateState) {
81+ case Update.StateInstalled:
82+ return launchable;
83+ default:
84+ return true;
85+ }
86+ }
87+ enabled: {
88+ switch(updateState) {
89+ case Update.StateAvailable:
90+ case Update.StateDownloading:
91+ case Update.StateDownloadingAutomatically:
92+ case Update.StateDownloadPaused:
93+ case Update.StateAutomaticDownloadPaused:
94+ case Update.StateInstallPaused:
95+ case Update.StateDownloaded:
96+ case Update.StateFailed:
97+ return true;
98+
99+ // Enabled if installed and a click app (can launch).
100+ case Update.StateInstalled:
101+ return kind === Update.KindClick;
102+
103+ case Update.StateInstalling:
104+ case Update.StateInstallingAutomatically:
105+ case Update.StateUnavailable:
106+ case Update.StateInstallFinished:
107+ case Update.StateQueuedForDownload:
108+ case Update.StateUnknown:
109+ default:
110+ return false;
111+ }
112+ }
113+ text: {
114+ switch(updateState) {
115+ case Update.StateUnknown:
116+ case Update.StateUnavailable:
117+ case Update.StateFailed:
118+ return i18n.tr("Retry");
119+
120+ case Update.StateAvailable:
121+ case Update.StateQueuedForDownload:
122+ if (update.kind === Update.KindClick) {
123+ return i18n.tr("Update");
124+ } else {
125+ return i18n.tr("Download");
126+ }
127+
128+ case Update.StateDownloadPaused:
129+ case Update.StateAutomaticDownloadPaused:
130+ case Update.StateInstallPaused:
131+ return i18n.tr("Resume");
132+
133+ case Update.StateDownloading:
134+ case Update.StateDownloadingAutomatically:
135+ case Update.StateInstalling:
136+ case Update.StateInstallingAutomatically:
137+ case Update.StateInstallFinished:
138+ return i18n.tr("Pause");
139+
140+ case Update.StateDownloaded:
141+ if (kind === Update.KindImage) {
142+ return i18n.tr("Install…");
143+ } else {
144+ return i18n.tr("Install");
145+ }
146+
147+ case Update.StateInstalled:
148+ return i18n.tr("Open");
149+
150+ default:
151+ console.error("Unknown update state", updateState);
152+ }
153+ }
154+
155+ onClicked: {
156+ switch (updateState) {
157+
158+ // Retries.
159+ case Update.StateUnknown:
160+ case Update.StateUnavailable:
161+ case Update.StateFailed:
162+ update.retry();
163+ break;
164+
165+ case Update.StateDownloadPaused:
166+ case Update.StateAutomaticDownloadPaused:
167+ case Update.StateInstallPaused:
168+ update.resume();
169+ break;
170+
171+ case Update.StateAvailable:
172+ if (kind === Update.KindClick) {
173+ update.install();
174+ } else {
175+ update.download();
176+ }
177+ break;
178+
179+ case Update.StateDownloaded:
180+ update.install();
181+ break;
182+
183+ case Update.StateDownloading:
184+ case Update.StateDownloadingAutomatically:
185+ case Update.StateInstalling:
186+ case Update.StateInstallingAutomatically:
187+ update.pause();
188+ break;
189+
190+ case Update.StateInstalled:
191+ update.launch();
192+ break;
193+ }
194+ }
195+ } // Button
196+ } // Name/button RowLayout
197+
198+ RowLayout {
199+ spacing: units.gu(2)
200+
201+ Label {
202+ id: downloadLabel
203+ objectName: "updateDownloadLabel"
204+
205+ Layout.alignment: Qt.AlignRight | Qt.AlignVCenter
206+
207+ visible: text !== ""
208+
209+ fontSize: "small"
210+ text: {
211+ switch (updateState) {
212+
213+ case Update.StateInstalling:
214+ case Update.StateInstallingAutomatically:
215+ case Update.StateInstallPaused:
216+ return i18n.tr("Installing");
217+
218+ case Update.StateInstallPaused:
219+ case Update.StateDownloadPaused:
220+ return i18n.tr("Paused");
221+
222+ case Update.StateQueuedForDownload:
223+ return i18n.tr("Waiting to download");
224+
225+ case Update.StateDownloading:
226+ return i18n.tr("Downloading");
227+
228+ default:
229+ return "";
230+ }
231+ }
232+ }
233+
234+ Label {
235+ id: statusLabel
236+ objectName: "updateStatusLabel"
237+
238+ visible: text !== ""
239+ Layout.alignment: Qt.AlignRight | Qt.AlignVCenter
240+ Layout.fillWidth: true
241+
242+ horizontalAlignment: Text.AlignRight
243+ fontSize: "small"
244+
245+ 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+
263+ switch (updateState) {
264+ case Update.StateAvailable:
265+ return humanFileSize(size, 1000);
266+
267+ case Update.StateDownloading:
268+ case Update.StateDownloadingAutomatically:
269+ case Update.StateDownloadPaused:
270+ case Update.StateAutomaticDownloadPaused:
271+ var down = humanFileSize((size / 100) * progress, 1000);
272+ var left = humanFileSize(size, 1000);
273+ if (progress > 100) {
274+ return left;
275+ } else {
276+ /* TRANSLATORS: %1 is the human readable amount
277+ of bytes downloaded, and %2 is the total to be
278+ downloaded. */
279+ return i18n.tr("%1 of %2").arg(down).arg(left);
280+ }
281+
282+ case Update.StateDownloaded:
283+ return i18n.tr("Downloaded");
284+
285+ case Update.StateInstallFinished:
286+ return i18n.tr("Installed");
287+
288+ case Update.StateInstalled:
289+ /* TRANSLATORS: %1 is the date at which this
290+ update was applied. */
291+ return i18n.tr("Updated %1").arg(
292+ updatedAt.toLocaleDateString(Qt.locale(), "d MMMM")
293+ );
294+ default:
295+ return "";
296+ }
297+ }
298+
299+ /* Seems AlignRight causes a rendering issue that can be
300+ fixed by doing an explicit doLayout. */
301+ Component.onCompleted: doLayout()
302+ }
303+ }
304+
305+ Column {
306+ id: error
307+ objectName: "updateError"
308+ spacing: units.gu(1)
309+ height: childrenRect.height
310+ Layout.fillWidth: true
311+ visible: errorElementDetail.text
312+
313+ Label {
314+ id: errorElementTitle
315+ text: i18n.tr("Update failed")
316+ color: UbuntuColors.red
317+ }
318+
319+ Label {
320+ id: errorElementDetail
321+ anchors { left: parent.left; right: parent.right }
322+ fontSize: "small"
323+ wrapMode: Text.WrapAnywhere
324+ }
325+ } // Error column
326+
327+ ProgressBar {
328+ id: progressBar
329+ objectName: "updateProgressbar"
330+
331+ visible: {
332+ switch (updateState) {
333+ case Update.StateQueuedForDownload:
334+ case Update.StateDownloading:
335+ case Update.StateDownloadPaused:
336+ case Update.StateInstalling:
337+ case Update.StateInstallPaused:
338+ return true;
339+
340+ default:
341+ return false;
342+ }
343+ }
344+ Layout.maximumHeight: units.gu(0.5)
345+ Layout.fillWidth: true
346+ indeterminate: update.progress < 0 || update.progress > 100
347+ minimumValue: 0
348+ maximumValue: 100
349+ showProgressPercentage: false
350+
351+ Behavior on value {
352+ animation: UbuntuNumberAnimation {}
353+ }
354+ } // Progress bar
355+ } // Layout for the rest of the stuff
356+
357+ Item {
358+ SlotsLayout.position: SlotsLayout.Leading;
359+ width: units.gu(4)
360+ height: width
361+
362+ Image {
363+ id: icon
364+ visible: kind === Update.KindImage && !fallback.visible
365+ anchors.fill: parent
366+ asynchronous: true
367+ smooth: true
368+ mipmap: true
369+ }
370+
371+ UbuntuShape {
372+ id: shape
373+ visible: kind !== Update.KindImage && !fallback.visible
374+ anchors.fill: parent
375+ source: icon
376+ }
377+
378+ Image {
379+ id: fallback
380+ visible: icon.status === Image.Error
381+ source : Qt.resolvedUrl("/usr/share/icons/suru/apps/scalable/ubuntu-logo-symbolic.svg")
382+ anchors.fill: parent
383+ asynchronous: true
384+ smooth: true
385+ mipmap: true
386+ }
387+ }
388+ }
389+}
qml/Wizard/Pages/passcode-desktop.qml+14−5
@@ -29,10 +29,17 @@ LocalComponents.Page {
29 id: passwdSetPage
29 id: passwdSetPage
30 objectName: "passcodeDesktopPage"
30 objectName: "passcodeDesktopPage"
31 title: i18n.tr("Lock Screen Passcode")
31 title: i18n.tr("Lock Screen Passcode")
32+ focusItem: passwordField
32 forwardButtonSourceComponent: forwardButton
33 forwardButtonSourceComponent: forwardButton
33
34
34 readonly property alias password: passwordField.text
35 readonly property alias password: passwordField.text
35 readonly property alias password2: password2Field.text
36 readonly property alias password2: password2Field.text
37+ readonly property bool passwordsMatching: password != "" && password == password2
38+
39+ function savePasswordAndGoNext() {
40+ root.password = password;
41+ pageStack.next();
42+ }
36
43
37 Flickable {
44 Flickable {
38 id: column
45 id: column
@@ -105,6 +112,11 @@ LocalComponents.Page {
105 inputMethodHints: Qt.ImhDigitsOnly
112 inputMethodHints: Qt.ImhDigitsOnly
106 validator: RegExpValidator { regExp: /^\d{4}$/ }
113 validator: RegExpValidator { regExp: /^\d{4}$/ }
107 maximumLength: 4
114 maximumLength: 4
115+ onAccepted: {
116+ if (passwordsMatching) {
117+ savePasswordAndGoNext();
118+ }
119+ }
108 onActiveFocusChanged: {
120 onActiveFocusChanged: {
109 if (activeFocus) {
121 if (activeFocus) {
110 column.contentY = y
122 column.contentY = y
@@ -140,11 +152,8 @@ LocalComponents.Page {
140 id: forwardButton
152 id: forwardButton
141 LocalComponents.StackButton {
153 LocalComponents.StackButton {
142 text: i18n.tr("Next")
154 text: i18n.tr("Next")
143− enabled: password != "" && password == password2
155+ enabled: passwordsMatching
144− onClicked: {
156+ onClicked: savePasswordAndGoNext()
145− root.password = password;
146− pageStack.next();
147− }
148 }
157 }
149 }
158 }
150}
159}
qml/Wizard/Pages/password-set.qml+12−4
@@ -29,12 +29,18 @@ LocalComponents.Page {
29 id: passwdSetPage
29 id: passwdSetPage
30 objectName: "passwdSetPage"
30 objectName: "passwdSetPage"
31 title: i18n.tr("Lock Screen Password")
31 title: i18n.tr("Lock Screen Password")
32+ focusItem: passwordField
32 forwardButtonSourceComponent: forwardButton
33 forwardButtonSourceComponent: forwardButton
33
34
34 readonly property alias password: passwordField.text
35 readonly property alias password: passwordField.text
35 readonly property alias password2: password2Field.text
36 readonly property alias password2: password2Field.text
36 readonly property bool passwordsMatching: password == password2 && password.trim().length > 7
37 readonly property bool passwordsMatching: password == password2 && password.trim().length > 7
37
38
39+ function savePasswordAndGoNext() {
40+ root.password = password;
41+ pageStack.next();
42+ }
43+
38 Flickable {
44 Flickable {
39 id: column
45 id: column
40 clip: true
46 clip: true
@@ -114,6 +120,11 @@ LocalComponents.Page {
114 id: password2Field
120 id: password2Field
115 objectName: "password2Field"
121 objectName: "password2Field"
116 echoMode: TextInput.Password
122 echoMode: TextInput.Password
123+ onAccepted: {
124+ if (passwordsMatching) {
125+ savePasswordAndGoNext();
126+ }
127+ }
117 onActiveFocusChanged: {
128 onActiveFocusChanged: {
118 if (activeFocus) {
129 if (activeFocus) {
119 column.contentY = pass2Label.y
130 column.contentY = pass2Label.y
@@ -141,10 +152,7 @@ LocalComponents.Page {
141 LocalComponents.StackButton {
152 LocalComponents.StackButton {
142 text: i18n.tr("Next")
153 text: i18n.tr("Next")
143 enabled: passwordsMatching
154 enabled: passwordsMatching
144− onClicked: {
155+ onClicked: savePasswordAndGoNext()
145− root.password = password;
146− pageStack.next();
147− }
148 }
156 }
149 }
157 }
150}
158}
qml/Wizard/Wizard.qml+5−2
@@ -1,4 +1,5 @@
1/*
1/*
2+ * Copyright (C) 2018 The UBports project
2 * Copyright (C) 2013, 2015 Canonical, Ltd.
3 * Copyright (C) 2013, 2015 Canonical, Ltd.
3 *
4 *
4 * This program is free software; you can redistribute it and/or modify
5 * This program is free software; you can redistribute it and/or modify
@@ -20,7 +21,7 @@ import Wizard 0.1
20import "../Components"
21import "../Components"
21
22
22Showable {
23Showable {
23− id: root
24+ id: wizard
24
25
25 // This is a bool instead of an alias because Loader classes like to emit
26 // This is a bool instead of an alias because Loader classes like to emit
26 // changed signals for 'active' during startup even if they aren't actually
27 // changed signals for 'active' during startup even if they aren't actually
@@ -29,6 +30,8 @@ Showable {
29 // expected behavior of no emission on startup.
30 // expected behavior of no emission on startup.
30 readonly property bool active: loader.active
31 readonly property bool active: loader.active
31
32
33+ property bool isUpdate: System.isUpdate
34+
32 // The wizard should be run at a later time if, for example, unity8 is
35 // The wizard should be run at a later time if, for example, unity8 is
33 // in greeter mode
36 // in greeter mode
34 property bool deferred: false
37 property bool deferred: false
@@ -49,7 +52,7 @@ Showable {
49
52
50 Connections {
53 Connections {
51 target: loader.item
54 target: loader.item
52− onQuit: root.hide()
55+ onQuit: wizard.hide()
53 }
56 }
54 }
57 }
55}
58}
tests/qmltests/Wizard/tst_Wizard.qml+4−13
@@ -168,16 +168,13 @@ Item {
168 return stack.currentPage;
168 return stack.currentPage;
169 }
169 }
170
170
171− function goToPage(name, skipSim, skipLocation, skipReporting) {
171+ function goToPage(name, skipSim, skipLocation) {
172 if (skipSim === undefined) {
172 if (skipSim === undefined) {
173 skipSim = false;
173 skipSim = false;
174 }
174 }
175 if (skipLocation === undefined) {
175 if (skipLocation === undefined) {
176 skipLocation = false;
176 skipLocation = false;
177 }
177 }
178− if (skipReporting === undefined) {
179− skipReporting = false;
180− }
181
178
182 var page = waitForPage("languagePage");
179 var page = waitForPage("languagePage");
183 if (name === page.objectName) return page;
180 if (name === page.objectName) return page;
@@ -222,12 +219,6 @@ Item {
222 if (name === page.objectName) return page;
219 if (name === page.objectName) return page;
223 tap(findChild(page, "forwardButton"));
220 tap(findChild(page, "forwardButton"));
224
221
225− if (!skipReporting) {
226− page = waitForPage("reportingPage");
227− if (name === page.objectName) return page;
228− tap(findChild(page, "forwardButton"));
229− }
230−
231 page = waitForPage("finishedPage");
222 page = waitForPage("finishedPage");
232 if (name === page.objectName) return page;
223 if (name === page.objectName) return page;
233 waitUntilTransitionsEnd(page);
224 waitUntilTransitionsEnd(page);
@@ -334,7 +325,7 @@ Item {
334 enterPasscode("1111");
325 enterPasscode("1111");
335
326
336 // now finish up
327 // now finish up
337− page = waitForPage("reportingPage");
328+ page = waitForPage("systemUpdatePage");
338 tap(findChild(page, "forwardButton"));
329 tap(findChild(page, "forwardButton"));
339 page = waitForPage("finishedPage");
330 page = waitForPage("finishedPage");
340 waitUntilTransitionsEnd(page);
331 waitUntilTransitionsEnd(page);
@@ -374,7 +365,7 @@ Item {
374 tap(continueButton);
365 tap(continueButton);
375
366
376 // now finish up
367 // now finish up
377− page = waitForPage("reportingPage");
368+ page = waitForPage("systemUpdatePage");
378 tap(findChild(page, "forwardButton"));
369 tap(findChild(page, "forwardButton"));
379 page = waitForPage("finishedPage");
370 page = waitForPage("finishedPage");
380 waitUntilTransitionsEnd(page);
371 waitUntilTransitionsEnd(page);
@@ -392,7 +383,7 @@ Item {
392
383
393 // now finish up
384 // now finish up
394 tap(findChild(page, "forwardButton"));
385 tap(findChild(page, "forwardButton"));
395− page = waitForPage("reportingPage");
386+ page = waitForPage("systemUpdatePage");
396 tap(findChild(page, "forwardButton"));
387 tap(findChild(page, "forwardButton"));
397 page = waitForPage("finishedPage");
388 page = waitForPage("finishedPage");
398 waitUntilTransitionsEnd(page);
389 waitUntilTransitionsEnd(page);