on xenial help sign in
Implement edge demos on first boot.

Build-Depends: +dbus-test-runner, +qtbase5-dev-tools. Approved by PS Jenkins bot, Michael Zanetti.

commit69aaebe2cda0800338df9d0d580da9af082a2c93 authorMichael Terry <michael.terry@canonical.com> dateWed 28 Aug 2013, 23:44 (13 years ago) parentc3755eee75661a
38 files changed+1471−55
Components/EdgeDemo.qml+231 Components/EdgeDemoOverlay.qml+256 Greeter/Greeter.qml+6−2 Launcher/Launcher.qml+1 Panel/Indicators.qml+4−2 Shell.qml+24−3 debian/control+2 debian/rules+5 debian/unity8-private.install+5 plugins/AccountsService/50-com.canonical.unity.AccountsService.pkla+6 plugins/AccountsService/AccountsService.cpp+74 plugins/AccountsService/AccountsService.h+44 plugins/AccountsService/CMakeLists.txt+36 plugins/AccountsService/com.canonical.unity.AccountsService.policy+24 plugins/AccountsService/com.canonical.unity.AccountsService.xml+17 plugins/AccountsService/plugin.cpp+35 plugins/AccountsService/plugin.h+34 plugins/AccountsService/qmldir+3 plugins/CMakeLists.txt+4−3 po/unity8.pot+51−43 tests/mocks/AccountsService/AccountsService.cpp+33 tests/mocks/AccountsService/AccountsService.h+38 tests/mocks/AccountsService/CMakeLists.txt+19 tests/mocks/AccountsService/plugin.cpp+36 tests/mocks/AccountsService/plugin.h+35 tests/mocks/AccountsService/qmldir+2 tests/mocks/CMakeLists.txt+1 tests/plugins/AccountsService/AccountsServer.cpp+35 tests/plugins/AccountsService/AccountsServer.h+39 tests/plugins/AccountsService/CMakeLists.txt+40 tests/plugins/AccountsService/PropertiesServer.cpp+45 tests/plugins/AccountsService/PropertiesServer.h+44 tests/plugins/AccountsService/client.cpp+49 tests/plugins/AccountsService/interfaces.xml+20 tests/plugins/AccountsService/server.cpp+46 tests/plugins/CMakeLists.txt+3−2 tests/qmltests/CMakeLists.txt+1 tests/qmltests/Components/tst_EdgeDemoOverlay.qml+123
Components/EdgeDemo.qmlnew file+231
@@ -0,0 +1,231 @@
1+/*
2+ * Copyright (C) 2013 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 AccountsService 0.1
18+import LightDM 0.1 as LightDM
19+import QtQuick 2.0
20+
21+Item {
22+ id: demo
23+
24+ property Item greeter
25+ property Item dash
26+ property Item launcher
27+ property Item indicators
28+ property Item underlay
29+
30+ property bool launcherEnabled: true
31+ property bool dashEnabled: true
32+ property bool panelEnabled: true
33+ property bool panelContentEnabled: true
34+
35+ property bool paused: false
36+
37+ onPausedChanged: {
38+ if (d.rightEdgeDemo) d.rightEdgeDemo.paused = paused
39+ if (d.topEdgeDemo) d.topEdgeDemo.paused = paused
40+ if (d.bottomEdgeDemo) d.bottomEdgeDemo.paused = paused
41+ if (d.leftEdgeDemo) d.leftEdgeDemo.paused = paused
42+ if (d.finalEdgeDemo) d.finalEdgeDemo.paused = paused
43+ }
44+
45+ function hideEdgeDemoInShell() {
46+ var user = LightDM.Users.data(greeter.currentIndex, LightDM.UserRoles.NameRole);
47+ AccountsService.setUserProperty(user, "demo-edges", false);
48+ d.showEdgeDemo = false;
49+ stopDemo();
50+ }
51+
52+ function hideEdgeDemoInGreeter() {
53+ // TODO: AccountsService.setUserProperty("lightdm", "demo-edges", false);
54+ d.showEdgeDemoInGreeter = false;
55+ }
56+
57+ function hideEdgeDemos() {
58+ hideEdgeDemoInGreeter();
59+ hideEdgeDemoInShell();
60+ }
61+
62+ function stopDemo() {
63+ launcherEnabled = true
64+ dashEnabled = true
65+ panelEnabled = true
66+ panelContentEnabled = true
67+ if (d.rightEdgeDemo) d.rightEdgeDemo.destroy()
68+ if (d.topEdgeDemo) d.topEdgeDemo.destroy()
69+ if (d.bottomEdgeDemo) d.bottomEdgeDemo.destroy()
70+ if (d.leftEdgeDemo) d.leftEdgeDemo.destroy()
71+ if (d.finalEdgeDemo) d.finalEdgeDemo.destroy()
72+ }
73+
74+ QtObject {
75+ id: d
76+ property Component overlay
77+ property QtObject rightEdgeDemo
78+ property QtObject topEdgeDemo
79+ property QtObject bottomEdgeDemo
80+ property QtObject leftEdgeDemo
81+ property QtObject finalEdgeDemo
82+ property bool showEdgeDemo: false
83+ property bool showEdgeDemoInGreeter: d.showEdgeDemo // TODO: AccountsService.getUserProperty("lightdm", "demo-edges")
84+
85+ onShowEdgeDemoInGreeterChanged: {
86+ if (!d.overlay && d.showEdgeDemoInGreeter) {
87+ d.overlay = Qt.createComponent("EdgeDemoOverlay.qml")
88+ startRightEdgeDemo()
89+ }
90+ }
91+ }
92+
93+ function startRightEdgeDemo() {
94+ launcherEnabled = false;
95+ demo.dashEnabled = false;
96+ demo.panelEnabled = false;
97+ demo.panelContentEnabled = false;
98+ if (demo.greeter) {
99+ d.rightEdgeDemo = d.overlay.createObject(demo.greeter, {
100+ "edge": "right",
101+ "title": i18n.tr("Right edge"),
102+ "text": i18n.tr("Try swiping from the right edge to unlock the phone"),
103+ "anchors.fill": demo.greeter,
104+ });
105+ }
106+ if (d.rightEdgeDemo) {
107+ d.rightEdgeDemo.onSkip.connect(demo.hideEdgeDemos)
108+ } else {
109+ stopDemo();
110+ }
111+ }
112+
113+ Connections {
114+ target: demo.greeter
115+
116+ function hide() {
117+ if (d.rightEdgeDemo && d.rightEdgeDemo.available) {
118+ d.rightEdgeDemo.hide()
119+ hideEdgeDemoInGreeter()
120+ startTopEdgeDemo()
121+ }
122+ }
123+
124+ onUnlocked: hide()
125+ onShownChanged: if (!greeter.shown) hide()
126+
127+ onSelected: {
128+ var user = LightDM.Users.data(uid, LightDM.UserRoles.NameRole)
129+ d.showEdgeDemo = AccountsService.getUserProperty(user, "demo-edges")
130+ }
131+ }
132+
133+ function startTopEdgeDemo() {
134+ demo.panelEnabled = true;
135+ if (demo.dash && demo.underlay) {
136+ d.topEdgeDemo = d.overlay.createObject(demo.underlay, {
137+ "edge": "top",
138+ "title": i18n.tr("Top edge"),
139+ "text": i18n.tr("Try swiping from the top edge to access the indicators"),
140+ "anchors.fill": demo.dash,
141+ });
142+ }
143+ if (d.topEdgeDemo) {
144+ d.topEdgeDemo.onSkip.connect(demo.hideEdgeDemoInShell)
145+ } else {
146+ stopDemo();
147+ }
148+ }
149+
150+ Connections {
151+ target: demo.indicators
152+ onFullyOpenedChanged: {
153+ if (d.topEdgeDemo && d.topEdgeDemo.available && demo.indicators.fullyOpened) {
154+ d.topEdgeDemo.hideNow()
155+ startBottomEdgeDemo()
156+ }
157+ }
158+ }
159+
160+ function startBottomEdgeDemo() {
161+ if (demo.indicators) {
162+ d.bottomEdgeDemo = d.overlay.createObject(demo.indicators, {
163+ "edge": "bottom",
164+ "title": i18n.tr("Close"),
165+ "text": i18n.tr("Swipe up again to close the settings screen"),
166+ "anchors.fill": demo.indicators,
167+ });
168+ }
169+ if (d.bottomEdgeDemo) {
170+ d.bottomEdgeDemo.onSkip.connect(demo.hideEdgeDemoInShell)
171+ } else {
172+ stopDemo();
173+ }
174+ }
175+
176+ Connections {
177+ target: demo.indicators
178+ onPartiallyOpenedChanged: {
179+ if (d.bottomEdgeDemo && d.bottomEdgeDemo.available && !demo.indicators.partiallyOpened && !demo.indicators.fullyOpened) {
180+ d.bottomEdgeDemo.hideNow()
181+ startLeftEdgeDemo()
182+ }
183+ }
184+ }
185+
186+ function startLeftEdgeDemo() {
187+ demo.panelEnabled = false;
188+ demo.launcherEnabled = true;
189+ if (demo.dash && demo.underlay) {
190+ d.leftEdgeDemo = d.overlay.createObject(demo.underlay, {
191+ "edge": "left",
192+ "title": i18n.tr("Left edge"),
193+ "text": i18n.tr("Swipe from the left to reveal the launcher for quick access to apps"),
194+ "anchors.fill": demo.dash,
195+ });
196+ }
197+ if (d.leftEdgeDemo) {
198+ d.leftEdgeDemo.onSkip.connect(demo.hideEdgeDemoInShell)
199+ } else {
200+ stopDemo();
201+ }
202+ }
203+
204+ Connections {
205+ target: demo.launcher
206+ onStateChanged: {
207+ if (d.leftEdgeDemo && d.leftEdgeDemo.available && launcher.state == "visible") {
208+ d.leftEdgeDemo.hide()
209+ launcher.hide()
210+ startFinalEdgeDemo()
211+ }
212+ }
213+ }
214+
215+ function startFinalEdgeDemo() {
216+ demo.launcherEnabled = false;
217+ if (demo.dash && demo.underlay) {
218+ d.finalEdgeDemo = d.overlay.createObject(demo.underlay, {
219+ "edge": "none",
220+ "title": i18n.tr("Well done"),
221+ "text": i18n.tr("You have now mastered the edge gestures and can start using the phone"),
222+ "anchors.fill": demo.dash,
223+ });
224+ }
225+ if (d.finalEdgeDemo) {
226+ d.finalEdgeDemo.onSkip.connect(demo.hideEdgeDemoInShell)
227+ } else {
228+ stopDemo();
229+ }
230+ }
231+}
Components/EdgeDemoOverlay.qmlnew file+256
@@ -0,0 +1,256 @@
1+/*
2+ * Copyright (C) 2013 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 Powerd 0.1
18+import QtQuick 2.0
19+import QtGraphicalEffects 1.0
20+import Ubuntu.Application 0.1
21+import Ubuntu.Components 0.1
22+
23+Showable {
24+ id: overlay
25+
26+ /*
27+ * Valid values are "left", "right", "top", "bottom", or "none".
28+ */
29+ property string edge: "top"
30+
31+ /*
32+ * This is the header displayed, like "Right edge".
33+ */
34+ property alias title: titleLabel.text
35+
36+ /*
37+ * This is the block of text displayed below the header.
38+ */
39+ property alias text: textLabel.text
40+
41+ /*
42+ * This is the text for the skip button.
43+ */
44+ property alias skipText: skipLabel.text
45+
46+ /*
47+ * Whether this demo is running currently.
48+ */
49+ readonly property bool active: available && visible
50+
51+ /*
52+ * Whether animations are paused.
53+ */
54+ property alias paused: wholeAnimation.paused
55+
56+ /*
57+ * Whether animations are running.
58+ */
59+ readonly property alias running: wholeAnimation.running
60+
61+ signal skip()
62+
63+ function doSkip() {
64+ d.skipOnHide = true;
65+ hide();
66+ }
67+
68+ function hideNow() {
69+ overlay.visible = false;
70+ overlay.available = false;
71+ if (d.skipOnHide) {
72+ overlay.skip();
73+ }
74+ }
75+
76+ showAnimation: StandardAnimation {
77+ property: "opacity"
78+ to: 1
79+ onRunningChanged: if (running) overlay.visible = true
80+ }
81+ hideAnimation: StandardAnimation {
82+ property: "opacity"
83+ to: 0
84+ duration: UbuntuAnimation.BriskDuration
85+ onRunningChanged: if (!running) overlay.hideNow()
86+ }
87+
88+ QtObject {
89+ id: d
90+ property bool skipOnHide: false
91+ property int edgeMargin: units.gu(4)
92+ }
93+
94+ Rectangle {
95+ objectName: "backgroundShade"
96+
97+ anchors.fill: parent
98+ color: "black"
99+ opacity: 0.8
100+ visible: overlay.active
101+
102+ MouseArea {
103+ anchors.fill: parent
104+ enabled: overlay.edge == "none" && overlay.opacity == 1.0
105+ onClicked: overlay.doSkip()
106+ }
107+ }
108+
109+ Item {
110+ id: hintGroup
111+ x: 0
112+ y: 0
113+ width: parent.width
114+ height: parent.height
115+ visible: overlay.active
116+
117+ Column {
118+ id: labelGroup
119+ layer.enabled: true // otherwise the underlining on "Skip intro" jumps
120+ spacing: units.gu(3)
121+
122+ anchors {
123+ margins: d.edgeMargin
124+ left: parent.left
125+ top: overlay.edge == "bottom" ? undefined : parent.top
126+ bottom: overlay.edge == "bottom" ? parent.bottom : undefined
127+ }
128+
129+ Label {
130+ id: titleLabel
131+ fontSize: "x-large"
132+ width: units.gu(25)
133+ wrapMode: Text.WordWrap
134+ }
135+
136+ Label {
137+ id: textLabel
138+ width: units.gu(25)
139+ wrapMode: Text.WordWrap
140+ }
141+
142+ Label {
143+ id: skipLabel
144+ objectName: "skipLabel"
145+ text: i18n.tr("Skip intro")
146+ color: UbuntuColors.orange
147+ fontSize: "small"
148+ font.underline: true
149+
150+ MouseArea {
151+ anchors.fill: parent
152+ onClicked: overlay.doSkip()
153+ }
154+ }
155+ }
156+
157+ LinearGradient {
158+ id: edgeHint
159+ property int size: 1
160+ cached: false
161+ visible: overlay.edge != "none"
162+ gradient: Gradient {
163+ GradientStop {
164+ position: 0.0
165+ color: Qt.hsla(16.0/360.0, 0.83, 0.47, 0.4) // UbuntuColors.orange, but transparent
166+ }
167+ GradientStop {
168+ position: 1.0
169+ color: "transparent"
170+ }
171+ }
172+ anchors.fill: parent
173+ start: {
174+ if (overlay.edge == "right") {
175+ return Qt.point(width, 0);
176+ } else if (overlay.edge == "left") {
177+ return Qt.point(0, 0);
178+ } else if (overlay.edge == "top") {
179+ return Qt.point(0, 0);
180+ } else {
181+ return Qt.point(0, height);
182+ }
183+ }
184+ end: {
185+ if (overlay.edge == "right") {
186+ return Qt.point(width - size, 0);
187+ } else if (overlay.edge == "left") {
188+ return Qt.point(size, 0);
189+ } else if (overlay.edge == "top") {
190+ return Qt.point(0, size);
191+ } else {
192+ return Qt.point(0, height - size);
193+ }
194+ }
195+ }
196+ }
197+
198+ SequentialAnimation {
199+ id: wholeAnimation
200+ objectName: "wholeAnimation"
201+ running: overlay.active
202+
203+ ParallelAnimation {
204+ id: fadeInAnimation
205+
206+ StandardAnimation {
207+ target: labelGroup
208+ property: {
209+ if (overlay.edge == "right" || overlay.edge == "left") {
210+ return "anchors.leftMargin";
211+ } else if (overlay.edge == "bottom") {
212+ return "anchors.bottomMargin";
213+ } else {
214+ return "anchors.topMargin";
215+ }
216+ }
217+ from: {
218+ if (overlay.edge == "right") {
219+ return d.edgeMargin + units.gu(3)
220+ } else {
221+ return d.edgeMargin - units.gu(3)
222+ }
223+ }
224+ to: d.edgeMargin
225+ duration: overlay.edge == "none" ? 0 : UbuntuAnimation.SleepyDuration
226+ }
227+ StandardAnimation {
228+ target: overlay
229+ property: "opacity"
230+ from: 0.0
231+ to: 1.0
232+ duration: UbuntuAnimation.SleepyDuration
233+ }
234+ }
235+
236+ SequentialAnimation {
237+ id: hintAnimation
238+ loops: Animation.Infinite
239+ property string prop: (overlay.edge == "left" || overlay.edge == "right") ? "x" : "y"
240+ property double endVal: units.dp(5) * ((overlay.edge == "left" || overlay.edge == "top") ? 1 : -1)
241+ property double maxGlow: units.dp(20)
242+ property int duration: overlay.edge == "none" ? 0 : UbuntuAnimation.SleepyDuration
243+
244+ ParallelAnimation {
245+ StandardAnimation { target: hintGroup; property: hintAnimation.prop; from: 0; to: hintAnimation.endVal; duration: hintAnimation.duration }
246+ StandardAnimation { target: edgeHint; property: "size"; from: 1; to: hintAnimation.maxGlow; duration: hintAnimation.duration }
247+ }
248+
249+ // Undo the above
250+ ParallelAnimation {
251+ StandardAnimation { target: hintGroup; property: hintAnimation.prop; from: hintAnimation.endVal; to: 0; duration: hintAnimation.duration }
252+ StandardAnimation { target: edgeHint; property: "size"; from: hintAnimation.maxGlow; to: 1; duration: hintAnimation.duration }
253+ }
254+ }
255+ }
256+}
Greeter/Greeter.qml+6−2
@@ -44,6 +44,8 @@ Showable {
4444 readonly property bool rightTeaserPressed: greeterContentLoader.status == Loader.Ready &&
4545 greeterContentLoader.item.rightTeaserPressed
4646
47+ readonly property int currentIndex: greeterContentLoader.currentIndex
48+
4749 signal selected(int uid)
4850 signal unlocked(int uid)
4951
@@ -63,11 +65,13 @@ Showable {
6365
6466 source: required ? "GreeterContent.qml" : ""
6567
66−
6768 Connections {
6869 target: greeterContentLoader.item
6970
70− onSelected: greeter.selected(uid);
71+ onSelected: {
72+ greeter.selected(uid);
73+ greeterContentLoader.currentIndex = uid;
74+ }
7175 onUnlocked: greeter.unlocked(uid);
7276 }
7377 }
Launcher/Launcher.qml+1
@@ -145,6 +145,7 @@ Item {
145145 LauncherPanel {
146146 id: panel
147147 objectName: "launcherPanel"
148+ enabled: root.available
148149 width: root.panelWidth
149150 anchors {
150151 top: parent.top
Panel/Indicators.qml+4−2
@@ -34,6 +34,7 @@ Showable {
3434 property bool fullyOpened: height == openedHeight
3535 property bool partiallyOpened: height > panelHeight && !fullyOpened
3636 property real visualBottom: Math.max(y+height, y+indicatorRow.y+indicatorRow.height)
37+ property bool contentEnabled: true
3738
3839 // TODO: Perhaps we need a animation standard for showing/hiding? Each showable seems to
3940 // use its own values. Need to ask design about this.
@@ -178,6 +179,7 @@ Showable {
178179 indicatorsModel: indicatorsModel
179180 clip: !indicators.fullyOpened
180181 activeHeader: indicators.state == "hint" || indicators.state == "reveal"
182+ enabled: contentEnabled
181183
182184 Connections {
183185 target: indicatorRow
@@ -301,7 +303,7 @@ Showable {
301303 anchors.right: parent.right
302304 height: panelHeight
303305 direction: Direction.Downwards
304− enabled: !indicators.shown
306+ enabled: !indicators.shown && indicators.available
305307 hintDisplacement: pinnedMode ? indicators.hintValue : 0
306308 autoCompleteDragThreshold: maxTotalDragDistance / 2
307309 stretch: true
@@ -318,7 +320,7 @@ Showable {
318320 id: hideDragHandle
319321 anchors.fill: handle
320322 direction: Direction.Upwards
321− enabled: indicators.shown
323+ enabled: indicators.shown && indicators.available
322324 hintDisplacement: indicators.hintValue
323325 autoCompleteDragThreshold: maxTotalDragDistance / 6
324326 stretch: true
Shell.qml+24−3
@@ -195,7 +195,7 @@ FocusScope {
195195 available: !greeter.shown && !lockscreen.shown
196196 hides: [stages, launcher, panel.indicators]
197197 shown: disappearingAnimationProgress !== 1.0
198− enabled: disappearingAnimationProgress === 0.0
198+ enabled: disappearingAnimationProgress === 0.0 && edgeDemo.dashEnabled
199199 // FIXME: unfocus all applications when going back to the dash
200200 onEnabledChanged: {
201201 if (enabled) {
@@ -386,6 +386,7 @@ FocusScope {
386386
387387 width: shell.edgeSize
388388 direction: Direction.Leftwards
389+ enabled: edgeDemo.dashEnabled
389390 property bool haveApps: mainStage.applications.count > 0 || sideStage.applications.count > 0
390391
391392 maxTotalDragDistance: haveApps ? parent.width : parent.width * 0.7
@@ -414,6 +415,7 @@ FocusScope {
414415 Component.onCompleted: {
415416 if (LightDM.Users.count == 1) {
416417 LightDM.Greeter.authenticate(LightDM.Users.data(0, LightDM.UserRoles.NameRole))
418+ greeter.selected(0)
417419 }
418420 }
419421 }
@@ -456,6 +458,7 @@ FocusScope {
456458 // If there are more users, the Greeter will handle that
457459 if (LightDM.Users.count == 1) {
458460 LightDM.Greeter.authenticate(LightDM.Users.data(0, LightDM.UserRoles.NameRole));
461+ greeter.selected(0);
459462 }
460463 greeter.forceActiveFocus();
461464 }
@@ -510,6 +513,13 @@ FocusScope {
510513 } else if (status == Powerd.On) {
511514 powerConnection.setFocused(true);
512515 }
516+
517+ // No reason to chew demo CPU when user isn't watching
518+ if (status == Powerd.Off) {
519+ edgeDemo.paused = true;
520+ } else if (status == Powerd.On) {
521+ edgeDemo.paused = false;
522+ }
513523 }
514524 }
515525
@@ -524,6 +534,8 @@ FocusScope {
524534 indicatorsMenuWidth: parent.width > units.gu(60) ? units.gu(40) : parent.width
525535 indicators {
526536 hides: [launcher]
537+ available: edgeDemo.panelEnabled
538+ contentEnabled: edgeDemo.panelContentEnabled
527539 }
528540 fullscreenMode: shell.fullscreenMode
529541 searchVisible: !greeter.shown && !lockscreen.shown
@@ -540,7 +552,7 @@ FocusScope {
540552 width: parent.width > units.gu(60) ? units.gu(40) : parent.width
541553 height: parent.height
542554
543− available: !greeter.shown && !panel.indicators.shown && !lockscreen.shown
555+ available: !greeter.shown && !panel.indicators.shown && !lockscreen.shown && edgeDemo.dashEnabled
544556 shown: false
545557 showAnimation: StandardAnimation { property: "y"; duration: hud.showableAnimationDuration; to: 0; easing.type: Easing.Linear }
546558 hideAnimation: StandardAnimation { property: "y"; duration: hud.showableAnimationDuration; to: hudRevealer.closedValue; easing.type: Easing.Linear }
@@ -598,7 +610,7 @@ FocusScope {
598610 anchors.bottom: parent.bottom
599611 width: parent.width
600612 dragAreaWidth: shell.edgeSize
601− available: !greeter.shown || greeter.narrowMode
613+ available: (!greeter.shown || greeter.narrowMode) && edgeDemo.launcherEnabled
602614 onDashItemSelected: {
603615 greeter.hide()
604616 // Animate if moving between application and dash
@@ -707,4 +719,13 @@ FocusScope {
707719 rotation: -45
708720 scale: Math.min(parent.width, parent.height) / width
709721 }
722+
723+ EdgeDemo {
724+ id: edgeDemo
725+ greeter: greeter
726+ launcher: launcher
727+ dash: dash
728+ indicators: panel.indicators
729+ underlay: underlay
730+ }
710731}
debian/control+2
@@ -3,6 +3,7 @@ Section: x11
33Priority: optional
44Maintainer: Ubuntu Developers <ubuntu-devel-discuss@lists.ubuntu.com>
55Build-Depends: cmake,
6+ dbus-test-runner,
67 debhelper (>= 9),
78 libdee-qt5-dev (>= 3.2),
89 libgl1-mesa-dev[!armhf] | libgl-dev[!armhf],
@@ -31,6 +32,7 @@ Build-Depends: cmake,
3132 python3:any,
3233 qt5-default,
3334 qtbase5-dev,
35+ qtbase5-dev-tools,
3436 qtbase5-private-dev,
3537 qtdeclarative5-dev,
3638 qtdeclarative5-dev-tools,
debian/rules+5
@@ -16,6 +16,11 @@ override_dh_auto_clean:
1616 dh_auto_clean
1717 rm -rf builddir
1818
19+override_dh_auto_configure:
20+ # Debian defines CMAKE_INSTALL_LOCALSTATEDIR as /usr/var, which is wrong.
21+ # So until Debian bug 719148 is fixed, do it ourselves.
22+ dh_auto_configure -- -DCMAKE_INSTALL_LOCALSTATEDIR="/var"
23+
1924override_dh_install:
2025 # install autopilot tests
2126 cd tests/autopilot; \
debian/unity8-private.install+5
@@ -1,3 +1,4 @@
1+usr/lib/*/unity8/qml/AccountsService
12usr/lib/*/unity8/qml/HudClient
23usr/lib/*/unity8/qml/LightDM
34usr/lib/*/unity8/qml/ListViewWithPageHeader
@@ -5,3 +6,7 @@ usr/lib/*/unity8/qml/Powerd
56usr/lib/*/unity8/qml/Ubuntu
67usr/lib/*/unity8/qml/Unity
78usr/lib/*/unity8/qml/Utils
9+usr/share/accountsservice/interfaces
10+usr/share/dbus-1/interfaces
11+usr/share/polkit-1
12+var/lib/polkit-1
plugins/AccountsService/50-com.canonical.unity.AccountsService.pklanew file+6
@@ -0,0 +1,6 @@
1+[Allow LightDM to set Unity AccountsService fields]
2+Identity=unix-user:lightdm
3+Action=com.canonical.unity.AccountsService.ModifyAnyUser
4+ResultActive=yes
5+ResultInactive=yes
6+ResultAny=yes
plugins/AccountsService/AccountsService.cppnew file+74
@@ -0,0 +1,74 @@
1+/*
2+ * Copyright (C) 2013 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+ * Author: Michael Terry <michael.terry@canonical.com>
17+ */
18+
19+#include "AccountsService.h"
20+#include <QDBusConnection>
21+#include <QDBusConnectionInterface>
22+#include <QDBusMessage>
23+#include <QDBusVariant>
24+
25+AccountsService::AccountsService(QObject* parent)
26+ : QObject(parent),
27+ accounts_manager(NULL),
28+ users()
29+{
30+ auto connection = QDBusConnection::SM_BUSNAME();
31+ auto interface = connection.interface();
32+ interface->startService("org.freedesktop.Accounts");
33+ accounts_manager = new QDBusInterface("org.freedesktop.Accounts",
34+ "/org/freedesktop/Accounts",
35+ "org.freedesktop.Accounts",
36+ connection, this);
37+}
38+
39+QVariant AccountsService::getUserProperty(const QString &user, const QString &property)
40+{
41+ auto iface = getUserInterface(user);
42+ if (iface != nullptr && iface->isValid()) {
43+ auto answer = iface->call("Get", "com.canonical.unity.AccountsService", property);
44+ if (answer.type() == QDBusMessage::ReplyMessage) {
45+ return answer.arguments()[0].value<QDBusVariant>().variant();
46+ }
47+ }
48+ return QVariant();
49+}
50+
51+void AccountsService::setUserProperty(const QString &user, const QString &property, const QVariant &value)
52+{
53+ auto iface = getUserInterface(user);
54+ if (iface != nullptr && iface->isValid()) {
55+ // The value needs to be carefully wrapped
56+ iface->call("Set", "com.canonical.unity.AccountsService", property, QVariant::fromValue(QDBusVariant(value)));
57+ }
58+}
59+
60+QDBusInterface *AccountsService::getUserInterface(const QString &user)
61+{
62+ auto iface = users.value(user);
63+ if (iface == nullptr && accounts_manager->isValid()) {
64+ auto answer = accounts_manager->call("FindUserByName", user);
65+ if (answer.type() == QDBusMessage::ReplyMessage) {
66+ iface = new QDBusInterface("org.freedesktop.Accounts",
67+ answer.arguments()[0].value<QDBusObjectPath>().path(),
68+ "org.freedesktop.DBus.Properties",
69+ accounts_manager->connection(), this);
70+ users.insert(user, iface);
71+ }
72+ }
73+ return iface;
74+}
plugins/AccountsService/AccountsService.hnew file+44
@@ -0,0 +1,44 @@
1+/*
2+ * Copyright (C) 2012,2013 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+ * Authors: Michael Terry <michael.terry@canonical.com>
17+ */
18+
19+#ifndef UNITY_ACCOUNTSSERVICE_H
20+#define UNITY_ACCOUNTSSERVICE_H
21+
22+#include <QDBusInterface>
23+#include <QMap>
24+#include <QObject>
25+#include <QString>
26+
27+class AccountsService: public QObject
28+{
29+ Q_OBJECT
30+
31+public:
32+ explicit AccountsService(QObject *parent = 0);
33+
34+ Q_INVOKABLE QVariant getUserProperty(const QString &user, const QString &property);
35+ Q_INVOKABLE void setUserProperty(const QString &user, const QString &property, const QVariant &value);
36+
37+private:
38+ QDBusInterface *getUserInterface(const QString &user);
39+
40+ QDBusInterface *accounts_manager;
41+ QMap<QString, QDBusInterface *> users;
42+};
43+
44+#endif
plugins/AccountsService/CMakeLists.txtnew file+36
@@ -0,0 +1,36 @@
1+include(Plugins)
2+
3+add_definitions(-DSM_BUSNAME=systemBus)
4+
5+add_library(AccountsService-qml MODULE
6+ AccountsService.cpp
7+ plugin.cpp
8+ )
9+
10+qt5_use_modules(AccountsService-qml DBus Qml)
11+
12+export_qmlfiles(AccountsService AccountsService)
13+export_qmlplugin(AccountsService 0.1 AccountsService TARGETS AccountsService-qml)
14+
15+set(POLKIT_LIB_DIR "${CMAKE_INSTALL_LOCALSTATEDIR}/lib/polkit-1")
16+set(POLKIT_DATA_DIR "${CMAKE_INSTALL_PREFIX}/share/polkit-1")
17+set(DBUS_IFACE_DIR "${CMAKE_INSTALL_PREFIX}/share/dbus-1/interfaces")
18+set(ACCOUNTS_IFACE_DIR "${CMAKE_INSTALL_PREFIX}/share/accountsservice/interfaces")
19+
20+install(FILES com.canonical.unity.AccountsService.xml
21+ DESTINATION "${DBUS_IFACE_DIR}"
22+ )
23+
24+# Create accountsservice symlink for above dbus interface
25+install(CODE "
26+execute_process(COMMAND mkdir -p \"\$ENV{DESTDIR}${ACCOUNTS_IFACE_DIR}\")
27+execute_process(COMMAND ln -sf ../../dbus-1/interfaces/com.canonical.unity.AccountsService.xml \"\$ENV{DESTDIR}${ACCOUNTS_IFACE_DIR}\")
28+ ")
29+
30+install(FILES com.canonical.unity.AccountsService.policy
31+ DESTINATION "${POLKIT_DATA_DIR}/actions"
32+ )
33+
34+install(FILES 50-com.canonical.unity.AccountsService.pkla
35+ DESTINATION "${POLKIT_LIB_DIR}/localauthority/10-vendor.d"
36+ )
plugins/AccountsService/com.canonical.unity.AccountsService.policynew file+24
@@ -0,0 +1,24 @@
1+<?xml version="1.0" encoding="UTF-8"?>
2+
3+<policyconfig>
4+ <action id="com.canonical.unity.AccountsService.ModifyOwnUser">
5+ <description>Set properties of own user</description>
6+ <message>Authentication is required to set one's own unity properties.</message>
7+ <defaults>
8+ <allow_any>yes</allow_any>
9+ <allow_inactive>yes</allow_inactive>
10+ <allow_active>yes</allow_active>
11+ </defaults>
12+ </action>
13+
14+ <action id="com.canonical.unity.AccountsService.ModifyAnyUser">
15+ <description>Set properties of any user</description>
16+ <message>Authentication is required to set another user's unity
17+properties.</message>
18+ <defaults>
19+ <allow_any>no</allow_any>
20+ <allow_inactive>no</allow_inactive>
21+ <allow_active>no</allow_active>
22+ </defaults>
23+ </action>
24+</policyconfig>
plugins/AccountsService/com.canonical.unity.AccountsService.xmlnew file+17
@@ -0,0 +1,17 @@
1+<node>
2+ <interface name="com.canonical.unity.AccountsService">
3+
4+ <annotation name="org.freedesktop.Accounts.VendorExtension" value="true"/>
5+
6+ <annotation name="org.freedesktop.Accounts.Authentication.ChangeOwn"
7+ value="com.canonical.unity.AccountsService.ModifyOwnUser"/>
8+
9+ <annotation name="org.freedesktop.Accounts.Authentication.ChangeAny"
10+ value="com.canonical.unity.AccountsService.ModifyAnyUser"/>
11+
12+ <property name="demo-edges" type="b" access="readwrite">
13+ <annotation name="org.freedesktop.Accounts.DefaultValue" value="true"/>
14+ </property>
15+
16+ </interface>
17+</node>
plugins/AccountsService/plugin.cppnew file+35
@@ -0,0 +1,35 @@
1+/*
2+ * Copyright (C) 2012,2013 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+ * Authors: Michael Terry <michael.terry@canonical.com>
17+ */
18+
19+#include "plugin.h"
20+#include "AccountsService.h"
21+
22+#include <QtQml>
23+
24+static QObject *service_provider(QQmlEngine *engine, QJSEngine *scriptEngine)
25+{
26+ Q_UNUSED(engine)
27+ Q_UNUSED(scriptEngine)
28+ return new AccountsService();
29+}
30+
31+void AccountsServicePlugin::registerTypes(const char *uri)
32+{
33+ Q_ASSERT(uri == QLatin1String("AccountsService"));
34+ qmlRegisterSingletonType<AccountsService>(uri, 0, 1, "AccountsService", service_provider);
35+}
plugins/AccountsService/plugin.hnew file+34
@@ -0,0 +1,34 @@
1+/*
2+ * Copyright (C) 2012,2013 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+ * Authors: Michael Terry <michael.terry@canonical.com>
17+ */
18+
19+#ifndef ACCOUNTSSERVICE_PLUGIN_H
20+#define ACCOUNTSSERVICE_PLUGIN_H
21+
22+#include <QQmlEngine>
23+#include <QQmlExtensionPlugin>
24+
25+class AccountsServicePlugin : public QQmlExtensionPlugin
26+{
27+ Q_OBJECT
28+ Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QQmlExtensionInterface")
29+
30+public:
31+ void registerTypes(const char *uri);
32+};
33+
34+#endif
plugins/AccountsService/qmldirnew file+3
@@ -0,0 +1,3 @@
1+module AccountsService
2+plugin AccountsService-qml
3+typeinfo plugin.qmltypes
plugins/CMakeLists.txt+4−3
@@ -1,7 +1,8 @@
1−add_subdirectory(Ubuntu)
2−add_subdirectory(Utils)
3−add_subdirectory(Unity)
1+add_subdirectory(AccountsService)
42add_subdirectory(HudClient)
53add_subdirectory(LightDM)
64add_subdirectory(ListViewWithPageHeader)
75add_subdirectory(Powerd)
6+add_subdirectory(Ubuntu)
7+add_subdirectory(Unity)
8+add_subdirectory(Utils)
po/unity8.pot+51−43
@@ -8,7 +8,7 @@ msgid ""
88msgstr ""
99"Project-Id-Version: unity8\n"
1010"Report-Msgid-Bugs-To: \n"
11−"POT-Creation-Date: 2013-08-09 12:18+0200\n"
11+"POT-Creation-Date: 2013-08-27 13:36-0400\n"
1212"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
1313"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
1414"Language-Team: LANGUAGE <LL@li.org>\n"
@@ -86,25 +86,69 @@ msgstr ""
8686msgid "YouTube"
8787msgstr ""
8888
89+#: Components/EdgeDemoOverlay.qml:145
90+msgid "Skip intro"
91+msgstr ""
92+
93+#: Components/EdgeDemo.qml:98
94+msgid "Right edge"
95+msgstr ""
96+
97+#: Components/EdgeDemo.qml:99
98+msgid "Try swiping from the right edge to unlock the phone"
99+msgstr ""
100+
101+#: Components/EdgeDemo.qml:135
102+msgid "Top edge"
103+msgstr ""
104+
105+#: Components/EdgeDemo.qml:136
106+msgid "Try swiping from the top edge to access the indicators"
107+msgstr ""
108+
109+#: Components/EdgeDemo.qml:161
110+msgid "Close"
111+msgstr ""
112+
113+#: Components/EdgeDemo.qml:162
114+msgid "Swipe up again to close the settings screen"
115+msgstr ""
116+
117+#: Components/EdgeDemo.qml:189
118+msgid "Left edge"
119+msgstr ""
120+
121+#: Components/EdgeDemo.qml:190
122+msgid "Swipe from the left to reveal the launcher for quick access to apps"
123+msgstr ""
124+
125+#: Components/EdgeDemo.qml:217
126+msgid "Well done"
127+msgstr ""
128+
129+#: Components/EdgeDemo.qml:218
130+msgid "You have now mastered the edge gestures and can start using the phone"
131+msgstr ""
132+
89133#: Components/PageHeader.qml:298
90134msgid "Recent searches"
91135msgstr ""
92136
93137#. TRANSLATORS: Number of persons who rated this app
94−#: Dash/Apps/AppInfo.qml:72
138+#: Dash/Apps/AppInfo.qml:73
95139#, qt-format
96140msgid "(%1)"
97141msgstr ""
98142
99143#. TRANSLATORS: Number of persons who wrote reviews for this app
100−#: Dash/Apps/AppInfo.qml:84 tests/qmltests/Dash/Apps/tst_AppPreview.qml:115
144+#: Dash/Apps/AppInfo.qml:85
101145#, qt-format
102146msgid "%1 review"
103147msgid_plural "%1 reviews"
104148msgstr[0] ""
105149msgstr[1] ""
106150
107−#: Dash/Apps/AppPreview.qml:181
151+#: Dash/Apps/AppPreview.qml:175
108152msgid "Rate this"
109153msgstr ""
110154
@@ -124,42 +168,6 @@ msgstr ""
124168msgid "Comments:"
125169msgstr ""
126170
127−#: Dash/DashApps.qml:65
128−msgid "Running apps"
129−msgstr ""
130−
131−#: Dash/DashApps.qml:68
132−msgid "Frequently used"
133−msgstr ""
134−
135−#: Dash/DashApps.qml:71
136−msgid "Installed"
137−msgstr ""
138−
139−#: Dash/DashApps.qml:74
140−msgid "Available for download"
141−msgstr ""
142−
143−#: Dash/DashApps.qml:168
144−msgid "Apps"
145−msgstr ""
146−
147−#: Dash/DashHome.qml:41
148−msgid "Frequent Apps"
149−msgstr ""
150−
151−#: Dash/DashHome.qml:44
152−msgid "Recent Music"
153−msgstr ""
154−
155−#: Dash/DashHome.qml:47
156−msgid "Videos Popular Online"
157−msgstr ""
158−
159−#: Dash/DashHome.qml:151
160−msgid "Home"
161−msgstr ""
162−
163171#: Dash/DashMusic.qml:27 Dash/DashVideos.qml:28
164172msgid "Featured"
165173msgstr ""
@@ -200,7 +208,7 @@ msgstr ""
200208msgid "Author:"
201209msgstr ""
202210
203−#: Greeter/Lockscreen.qml:133
211+#: Greeter/Lockscreen.qml:134
204212msgid "Emergency Call"
205213msgstr ""
206214
@@ -237,11 +245,11 @@ msgstr ""
237245msgid "Search"
238246msgstr ""
239247
240−#: plugins/Unity/scope.cpp:298
248+#: plugins/Unity/scope.cpp:317
241249msgid "Sorry, there is nothing that matches your search."
242250msgstr ""
243251
244−#: Shell.qml:399
252+#: Shell.qml:429
245253#, qt-format
246254msgid "Please enter %1"
247255msgstr ""
tests/mocks/AccountsService/AccountsService.cppnew file+33
@@ -0,0 +1,33 @@
1+/*
2+ * Copyright (C) 2013 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+ * Author: Michael Terry <michael.terry@canonical.com>
17+ */
18+
19+#include "AccountsService.h"
20+
21+AccountsService::AccountsService(QObject* parent)
22+ : QObject(parent)
23+{
24+}
25+
26+QVariant AccountsService::getUserProperty(const QString &, const QString &)
27+{
28+ return QVariant(false);
29+}
30+
31+void AccountsService::setUserProperty(const QString &, const QString &, const QVariant &)
32+{
33+}
tests/mocks/AccountsService/AccountsService.hnew file+38
@@ -0,0 +1,38 @@
1+/*
2+ * Copyright (C) 2012,2013 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+ * Authors: Gerry Boland <gerry.boland@canonical.com>
17+ * Michael Terry <michael.terry@canonical.com>
18+ */
19+
20+#ifndef UNITY_MOCK_ACCOUNTSSERVICE_H
21+#define UNITY_MOCK_ACCOUNTSSERVICE_H
22+
23+#include <QObject>
24+#include <QString>
25+#include <QVariant>
26+
27+class AccountsService: public QObject
28+{
29+ Q_OBJECT
30+
31+public:
32+ explicit AccountsService(QObject *parent = 0);
33+
34+ Q_INVOKABLE QVariant getUserProperty(const QString &user, const QString &property);
35+ Q_INVOKABLE void setUserProperty(const QString &user, const QString &property, const QVariant &value);
36+};
37+
38+#endif
tests/mocks/AccountsService/CMakeLists.txtnew file+19
@@ -0,0 +1,19 @@
1+add_library(MockAccountsService-qml MODULE
2+ AccountsService.cpp
3+ plugin.cpp
4+ )
5+
6+qt5_use_modules(MockAccountsService-qml DBus Qml)
7+
8+# copy files into build directory for shadow builds
9+file(COPY "${CMAKE_CURRENT_SOURCE_DIR}/qmldir"
10+ DESTINATION ${CMAKE_CURRENT_BINARY_DIR}
11+ )
12+
13+install(TARGETS MockAccountsService-qml
14+ DESTINATION ${SHELL_PRIVATE_LIBDIR}/qml/mocks/AccountsService
15+ )
16+
17+install(FILES qmldir
18+ DESTINATION ${SHELL_PRIVATE_LIBDIR}/qml/mocks/AccountsService
19+ )
tests/mocks/AccountsService/plugin.cppnew file+36
@@ -0,0 +1,36 @@
1+/*
2+ * Copyright (C) 2012,2013 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+ * Authors: Gerry Boland <gerry.boland@canonical.com>
17+ * Michael Terry <michael.terry@canonical.com>
18+ */
19+
20+#include "plugin.h"
21+#include "AccountsService.h"
22+
23+#include <QtQml>
24+
25+static QObject *service_provider(QQmlEngine *engine, QJSEngine *scriptEngine)
26+{
27+ Q_UNUSED(engine)
28+ Q_UNUSED(scriptEngine)
29+ return new AccountsService();
30+}
31+
32+void AccountsServicePlugin::registerTypes(const char *uri)
33+{
34+ Q_ASSERT(uri == QLatin1String("AccountsService"));
35+ qmlRegisterSingletonType<AccountsService>(uri, 0, 1, "AccountsService", service_provider);
36+}
tests/mocks/AccountsService/plugin.hnew file+35
@@ -0,0 +1,35 @@
1+/*
2+ * Copyright (C) 2012,2013 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+ * Authors: Gerry Boland <gerry.boland@canonical.com>
17+ * Michael Terry <michael.terry@canonical.com>
18+ */
19+
20+#ifndef MOCK_ACCOUNTSSERVICE_PLUGIN_H
21+#define MOCK_ACCOUNTSSERVICE_PLUGIN_H
22+
23+#include <QQmlEngine>
24+#include <QQmlExtensionPlugin>
25+
26+class AccountsServicePlugin : public QQmlExtensionPlugin
27+{
28+ Q_OBJECT
29+ Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QQmlExtensionInterface")
30+
31+public:
32+ void registerTypes(const char *uri);
33+};
34+
35+#endif
tests/mocks/AccountsService/qmldirnew file+2
@@ -0,0 +1,2 @@
1+module AccountsService
2+plugin MockAccountsService-qml
tests/mocks/CMakeLists.txt+1
@@ -1,3 +1,4 @@
1+add_subdirectory(AccountsService)
12add_subdirectory(GSettings.1.0)
23add_subdirectory(HudClient)
34add_subdirectory(libusermetrics)
tests/plugins/AccountsService/AccountsServer.cppnew file+35
@@ -0,0 +1,35 @@
1+/*
2+ * Copyright 2013 Canonical Ltd.
3+ *
4+ * This program is free software: you can redistribute it and/or modify it
5+ * under the terms of the GNU General Public License version 3, as published
6+ * by the Free Software Foundation.
7+ *
8+ * This program is distributed in the hope that it will be useful, but
9+ * WITHOUT ANY WARRANTY; without even the implied warranties of
10+ * MERCHANTABILITY, SATISFACTORY QUALITY or FITNESS FOR A PARTICULAR
11+ * PURPOSE. See the GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * version 3 along with this program. If not, see
15+ * <http://www.gnu.org/licenses/>
16+ *
17+ * Authored by: Michael Terry <michael.terry@canonical.com>
18+ */
19+
20+#include "AccountsServer.h"
21+
22+AccountsServer::AccountsServer(QObject *parent)
23+ : QObject(parent)
24+{
25+}
26+
27+QDBusObjectPath AccountsServer::FindUserByName(const QString &user)
28+{
29+ if (user == "testuser") {
30+ return QDBusObjectPath("/user");
31+ } else {
32+ sendErrorReply(QDBusError::InvalidArgs, "Bad user");
33+ return QDBusObjectPath("/");
34+ }
35+}
tests/plugins/AccountsService/AccountsServer.hnew file+39
@@ -0,0 +1,39 @@
1+/*
2+ * Copyright 2013 Canonical Ltd.
3+ *
4+ * This program is free software: you can redistribute it and/or modify it
5+ * under the terms of the GNU General Public License version 3, as published
6+ * by the Free Software Foundation.
7+ *
8+ * This program is distributed in the hope that it will be useful, but
9+ * WITHOUT ANY WARRANTY; without even the implied warranties of
10+ * MERCHANTABILITY, SATISFACTORY QUALITY or FITNESS FOR A PARTICULAR
11+ * PURPOSE. See the GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * version 3 along with this program. If not, see
15+ * <http://www.gnu.org/licenses/>
16+ *
17+ * Authored by: Michael Terry <michael.terry@canonical.com>
18+ */
19+
20+#ifndef UNITY_ACCOUNTSSERVER_H
21+#define UNITY_ACCOUNTSSERVER_H
22+
23+#include <QDBusContext>
24+#include <QDBusObjectPath>
25+#include <QObject>
26+#include <QString>
27+
28+class AccountsServer: public QObject, protected QDBusContext
29+{
30+ Q_OBJECT
31+
32+public:
33+ explicit AccountsServer(QObject *parent = 0);
34+
35+public Q_SLOTS:
36+ QDBusObjectPath FindUserByName(const QString &user);
37+};
38+
39+#endif
tests/plugins/AccountsService/CMakeLists.txtnew file+40
@@ -0,0 +1,40 @@
1+macro(make_dbus_class NAME INTERFACE)
2+ if(${CMAKE_CURRENT_SOURCE_DIR}/interfaces.xml IS_NEWER_THAN ${CMAKE_CURRENT_BINARY_DIR}/${NAME}Adaptor.h)
3+ execute_process(COMMAND qdbusxml2cpp -c ${NAME}Adaptor -a ${CMAKE_CURRENT_BINARY_DIR}/${NAME}Adaptor ${CMAKE_CURRENT_SOURCE_DIR}/interfaces.xml ${INTERFACE})
4+ endif()
5+endmacro(make_dbus_class)
6+
7+make_dbus_class(Accounts org.freedesktop.Accounts)
8+make_dbus_class(Properties org.freedesktop.DBus.Properties)
9+
10+include_directories(
11+ ${CMAKE_CURRENT_BINARY_DIR}
12+ ${CMAKE_SOURCE_DIR}/plugins/AccountsService
13+ )
14+
15+add_definitions(-DSM_BUSNAME=sessionBus)
16+
17+add_executable(mock-server
18+ ${CMAKE_CURRENT_BINARY_DIR}/AccountsAdaptor.cpp
19+ ${CMAKE_CURRENT_BINARY_DIR}/PropertiesAdaptor.cpp
20+ server.cpp
21+ AccountsServer.cpp
22+ PropertiesServer.cpp
23+ )
24+qt5_use_modules(mock-server Core DBus)
25+
26+add_executable(test-accountsservice
27+ ${CMAKE_SOURCE_DIR}/plugins/AccountsService/AccountsService.cpp
28+ client.cpp
29+ )
30+qt5_use_modules(test-accountsservice Core DBus Test)
31+
32+add_test(NAME test-accountsservice
33+ COMMAND dbus-test-runner
34+ --task ${CMAKE_CURRENT_BINARY_DIR}/mock-server
35+ --task-name server
36+ --ignore-return
37+ --task ${CMAKE_CURRENT_BINARY_DIR}/test-accountsservice
38+ --task-name client
39+ --wait-for org.freedesktop.Accounts
40+ )
tests/plugins/AccountsService/PropertiesServer.cppnew file+45
@@ -0,0 +1,45 @@
1+/*
2+ * Copyright 2013 Canonical Ltd.
3+ *
4+ * This program is free software: you can redistribute it and/or modify it
5+ * under the terms of the GNU General Public License version 3, as published
6+ * by the Free Software Foundation.
7+ *
8+ * This program is distributed in the hope that it will be useful, but
9+ * WITHOUT ANY WARRANTY; without even the implied warranties of
10+ * MERCHANTABILITY, SATISFACTORY QUALITY or FITNESS FOR A PARTICULAR
11+ * PURPOSE. See the GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * version 3 along with this program. If not, see
15+ * <http://www.gnu.org/licenses/>
16+ *
17+ * Authored by: Michael Terry <michael.terry@canonical.com>
18+ */
19+
20+#include "PropertiesServer.h"
21+
22+PropertiesServer::PropertiesServer(QObject *parent)
23+ : QObject(parent),
24+ edge_demo(true)
25+{
26+}
27+
28+QDBusVariant PropertiesServer::Get(const QString &interface, const QString &property)
29+{
30+ if (interface == "com.canonical.unity.AccountsService" && property == "demo-edges") {
31+ return QDBusVariant(QVariant(edge_demo));
32+ } else {
33+ sendErrorReply(QDBusError::InvalidArgs, "Bad interface or property");
34+ return QDBusVariant(QVariant());
35+ }
36+}
37+
38+void PropertiesServer::Set(const QString &interface, const QString &property, const QDBusVariant &variant)
39+{
40+ if (interface == "com.canonical.unity.AccountsService" && property == "demo-edges") {
41+ edge_demo = variant.variant().toBool();
42+ } else {
43+ sendErrorReply(QDBusError::InvalidArgs, "Bad interface or property");
44+ }
45+}
tests/plugins/AccountsService/PropertiesServer.hnew file+44
@@ -0,0 +1,44 @@
1+/*
2+ * Copyright 2013 Canonical Ltd.
3+ *
4+ * This program is free software: you can redistribute it and/or modify it
5+ * under the terms of the GNU General Public License version 3, as published
6+ * by the Free Software Foundation.
7+ *
8+ * This program is distributed in the hope that it will be useful, but
9+ * WITHOUT ANY WARRANTY; without even the implied warranties of
10+ * MERCHANTABILITY, SATISFACTORY QUALITY or FITNESS FOR A PARTICULAR
11+ * PURPOSE. See the GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * version 3 along with this program. If not, see
15+ * <http://www.gnu.org/licenses/>
16+ *
17+ * Authored by: Michael Terry <michael.terry@canonical.com>
18+ */
19+
20+#ifndef UNITY_PROPERTIESSERVER_H
21+#define UNITY_PROPERTIESSERVER_H
22+
23+#include "PropertiesServer.h"
24+#include <QDBusContext>
25+#include <QDBusVariant>
26+#include <QObject>
27+#include <QString>
28+
29+class PropertiesServer: public QObject, protected QDBusContext
30+{
31+ Q_OBJECT
32+
33+public:
34+ explicit PropertiesServer(QObject *parent = 0);
35+
36+public Q_SLOTS:
37+ QDBusVariant Get(const QString &interface, const QString &property);
38+ void Set(const QString &interface, const QString &property, const QDBusVariant &variant);
39+
40+private:
41+ bool edge_demo;
42+};
43+
44+#endif
tests/plugins/AccountsService/client.cppnew file+49
@@ -0,0 +1,49 @@
1+/*
2+ * Copyright 2013 Canonical Ltd.
3+ *
4+ * This program is free software: you can redistribute it and/or modify it
5+ * under the terms of the GNU General Public License version 3, as published
6+ * by the Free Software Foundation.
7+ *
8+ * This program is distributed in the hope that it will be useful, but
9+ * WITHOUT ANY WARRANTY; without even the implied warranties of
10+ * MERCHANTABILITY, SATISFACTORY QUALITY or FITNESS FOR A PARTICULAR
11+ * PURPOSE. See the GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * version 3 along with this program. If not, see
15+ * <http://www.gnu.org/licenses/>
16+ *
17+ * Authored by: Michael Terry <michael.terry@canonical.com>
18+ */
19+
20+#include "AccountsService.h"
21+#include <QSignalSpy>
22+#include <QTest>
23+
24+class AccountsServiceTest : public QObject
25+{
26+ Q_OBJECT
27+
28+private Q_SLOTS:
29+
30+ void testInvalids()
31+ {
32+ // Test various invalid calls
33+ AccountsService session;
34+ QCOMPARE(session.getUserProperty("NOPE", "demo-edges"), QVariant());
35+ QCOMPARE(session.getUserProperty("testuser", "NOPE"), QVariant());
36+ }
37+
38+ void testGetSet()
39+ {
40+ AccountsService session;
41+ QCOMPARE(session.getUserProperty("testuser", "demo-edges"), QVariant(true));
42+ session.setUserProperty("testuser", "demo-edges", QVariant(false));
43+ QCOMPARE(session.getUserProperty("testuser", "demo-edges"), QVariant(false));
44+ }
45+};
46+
47+QTEST_MAIN(AccountsServiceTest)
48+
49+#include "client.moc"
tests/plugins/AccountsService/interfaces.xmlnew file+20
@@ -0,0 +1,20 @@
1+<node>
2+ <interface name="org.freedesktop.DBus.Properties">
3+ <method name="Get">
4+ <arg name="interface" type="s" direction="in" />
5+ <arg name="property" type="s" direction="in" />
6+ <arg name="value" type="v" direction="out" />
7+ </method>
8+ <method name="Set">
9+ <arg name="interface" type="s" direction="in" />
10+ <arg name="property" type="s" direction="in" />
11+ <arg name="value" type="v" direction="in" />
12+ </method>
13+ </interface>
14+ <interface name="org.freedesktop.Accounts">
15+ <method name="FindUserByName">
16+ <arg name="user" type="s" direction="in" />
17+ <arg name="path" type="o" direction="out" />
18+ </method>
19+ </interface>
20+</node>
tests/plugins/AccountsService/server.cppnew file+46
@@ -0,0 +1,46 @@
1+/*
2+ * Copyright 2013 Canonical Ltd.
3+ *
4+ * This program is free software: you can redistribute it and/or modify it
5+ * under the terms of the GNU General Public License version 3, as published
6+ * by the Free Software Foundation.
7+ *
8+ * This program is distributed in the hope that it will be useful, but
9+ * WITHOUT ANY WARRANTY; without even the implied warranties of
10+ * MERCHANTABILITY, SATISFACTORY QUALITY or FITNESS FOR A PARTICULAR
11+ * PURPOSE. See the GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * version 3 along with this program. If not, see
15+ * <http://www.gnu.org/licenses/>
16+ *
17+ * Authored by: Michael Terry <michael.terry@canonical.com>
18+ */
19+
20+#include "AccountsAdaptor.h"
21+#include "AccountsServer.h"
22+#include "PropertiesAdaptor.h"
23+#include "PropertiesServer.h"
24+#include <QCoreApplication>
25+
26+int main(int argc, char *argv[])
27+{
28+ QCoreApplication a(argc, argv);
29+
30+ auto accounts = new AccountsServer();
31+ new AccountsAdaptor(accounts);
32+
33+ auto props = new PropertiesServer();
34+ new PropertiesAdaptor(props);
35+
36+ // We use the session bus for testing. The real plugin uses the system bus
37+ auto connection = QDBusConnection::sessionBus();
38+ if (!connection.registerObject("/org/freedesktop/Accounts", accounts))
39+ return 1;
40+ if (!connection.registerObject("/user", props))
41+ return 1;
42+ if (!connection.registerService("org.freedesktop.Accounts"))
43+ return 1;
44+
45+ return a.exec();
46+}
tests/plugins/CMakeLists.txt+3−2
@@ -1,4 +1,5 @@
1−add_subdirectory(Utils)
1+add_subdirectory(AccountsService)
2+add_subdirectory(ListViewWithPageHeader)
23add_subdirectory(Ubuntu)
34add_subdirectory(Unity)
4−add_subdirectory(ListViewWithPageHeader)
5+add_subdirectory(Utils)
tests/qmltests/CMakeLists.txt+1
@@ -30,6 +30,7 @@ add_qml_test(. Shell IMPORT_PATHS ${CMAKE_BINARY_DIR}/plugins ${qmltest_DEFAULT_
3030 ENVIRONMENT "LD_LIBRARY_PATH=${CMAKE_BINARY_DIR}/tests/mocks/LightDM/single")
3131add_manual_qml_test(Components DragHandle IMPORT_PATHS ${CMAKE_BINARY_DIR}/plugins)
3232add_qml_test(Components DraggingArea)
33+add_qml_test(Components EdgeDemoOverlay)
3334add_qml_test(Components FilterGrid IMPORT_PATHS ${qmltest_DEFAULT_IMPORT_PATHS} ${CMAKE_BINARY_DIR}/plugins)
3435add_qml_test(Components ResponsiveFlowView)
3536add_qml_test(Components ResponsiveGridView)
tests/qmltests/Components/tst_EdgeDemoOverlay.qmlnew file+123
@@ -0,0 +1,123 @@
1+/*
2+ * Copyright 2013 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.0
18+import QtTest 1.0
19+import Unity.Test 0.1 as UT
20+import "../../../Components"
21+
22+Item {
23+ id: root
24+ width: boxWidth * 3
25+ height: boxHeight * 2
26+
27+ property int boxWidth: 250
28+ property int boxHeight: 250
29+
30+ EdgeDemoOverlay {
31+ id: top
32+ edge: "top"
33+ title: "Top"
34+ text: "Displayed on top left"
35+ anchors.left: parent.left
36+ anchors.top: parent.top
37+ width: boxWidth
38+ height: boxHeight
39+ }
40+
41+ EdgeDemoOverlay {
42+ id: right
43+ edge: "right"
44+ title: "Right"
45+ text: "Displayed on top right"
46+ anchors.right: parent.right
47+ anchors.top: parent.top
48+ width: boxWidth
49+ height: boxHeight
50+ }
51+
52+ EdgeDemoOverlay {
53+ id: left
54+ edge: "left"
55+ title: "Left"
56+ text: "Displayed on bottom right"
57+ anchors.right: parent.right
58+ anchors.bottom: parent.bottom
59+ width: boxWidth
60+ height: boxHeight
61+ available: false
62+ }
63+
64+ EdgeDemoOverlay {
65+ id: bottom
66+ edge: "bottom"
67+ title: "Bottom"
68+ text: "Displayed on bottom left"
69+ anchors.left: parent.left
70+ anchors.bottom: parent.bottom
71+ width: boxWidth
72+ height: boxHeight
73+ }
74+
75+ EdgeDemoOverlay {
76+ id: none
77+ edge: "none"
78+ title: "None"
79+ text: "Displayed on top middle"
80+ anchors.horizontalCenter: parent.horizontalCenter
81+ anchors.top: parent.top
82+ width: boxWidth
83+ height: boxHeight
84+ }
85+
86+ SignalSpy {
87+ id: signalSpy
88+ }
89+
90+ UT.UnityTestCase {
91+ name: "EdgeDemoOverlay"
92+ when: windowShown
93+
94+ function test_animations() {
95+ compare(right.running, true)
96+
97+ compare(left.running, false)
98+ left.available = true
99+ compare(left.running, true)
100+ }
101+
102+ function test_skip() {
103+ signalSpy.target = bottom
104+ signalSpy.signalName = "skip"
105+ signalSpy.clear()
106+ var bottomSkip = findChild(bottom, "skipLabel")
107+ mousePress(bottomSkip, 1, 1)
108+ mouseRelease(bottomSkip, 1, 1)
109+ signalSpy.wait()
110+ compare(bottom.available, false)
111+
112+ // Test that the 'none' edge skips anywhere
113+ signalSpy.target = none
114+ signalSpy.clear()
115+ var backgroundShade = findChild(none, "backgroundShade")
116+ tryCompare(none, "opacity", 1)
117+ mousePress(backgroundShade, 1, 1)
118+ mouseRelease(backgroundShade, 1, 1)
119+ signalSpy.wait()
120+ compare(none.available, false)
121+ }
122+ }
123+}