2 * Copyright (C) 2013,2014 Canonical, Ltd.
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.
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.
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/>.
18 import Ubuntu.Components 1.3
19 import AccountsService 0.1
20 import Unity.Application 0.1
25 property Item launcher
28 property string usageScenario
30 property bool keyboardVisible
31 property var lastInputTimestamp
33 readonly property bool launcherEnabled: !running
34 || tutorialLeftLoader.shown
35 || tutorialLeftLongLoader.shown
36 readonly property bool spreadEnabled: !running || tutorialRightLoader.shown
37 readonly property bool panelEnabled: !running || tutorialTopLoader.shown
38 readonly property bool running: tutorialLeftLoader.shown
39 || tutorialTopLoader.shown
40 || tutorialRightLoader.shown
41 || tutorialBottomLoader.shown
54 // We allow "" because it is briefly empty on startup, and we don't
55 // want to improperly skip any mobile tutorials.
56 property bool mobileScenario: root.usageScenario === "" ||
57 root.usageScenario === "phone" ||
58 root.usageScenario === "tablet"
60 property var focusedApp: ApplicationManager.focusedApplicationId
61 ? ApplicationManager.findApplication(ApplicationManager.focusedApplicationId)
64 function haveShown(tutorialId) {
65 return AccountsService.demoEdgesCompleted.indexOf(tutorialId) != -1;
68 property bool endPointsFinished: tutorialRightLoader.skipped &&
69 tutorialBottomLoader.skipped
70 onEndPointsFinishedChanged: if (endPointsFinished) root.finish()
74 id: tutorialLeftLoader
75 objectName: "tutorialLeftLoader"
78 readonly property bool skipped: !d.mobileScenario || d.haveShown("left")
79 readonly property bool shown: item && item.shown
80 active: !skipped || (item && item.visible)
81 onSkippedChanged: if (skipped && shown) item.hide()
83 sourceComponent: TutorialLeft {
85 objectName: "tutorialLeft"
87 launcher: root.launcher
88 hides: [launcher, panel.indicators]
91 isReady: !tutorialLeftLoader.skipped && !paused && !keyboardVisible &&
92 !tutorialBottomLoader.shown && !tutorialBottomLoader.mightShow
94 // Use an idle timer here, because when constructed, all our isReady variables will be false.
95 // Qml needs a moment to copy their values from Tutorial.qml. So we idle it and recheck.
99 onTriggered: if (tutorialLeft.isReady && !tutorialLeft.shown) tutorialLeft.show()
102 onIsReadyChanged: if (isReady && !shown) tutorialLeftTimer.start()
103 onFinished: AccountsService.markDemoEdgeCompleted("left")
108 id: tutorialLeftLongLoader
109 objectName: "tutorialLeftLongLoader"
112 readonly property bool skipped: !d.mobileScenario || d.haveShown("left-long")
113 readonly property bool shown: item && item.shown
114 active: !skipped || (item && item.visible)
115 onSkippedChanged: if (skipped && shown) item.hide()
117 sourceComponent: TutorialLeftLong {
119 objectName: "tutorialLeftLong"
121 launcher: root.launcher
122 hides: [launcher, panel.indicators]
125 skipped: tutorialLeftLongLoader.skipped
126 isReady: tutorialLeftLoader.skipped && !skipped && !paused && !keyboardVisible &&
127 !tutorialBottomLoader.shown && !tutorialBottomLoader.mightShow
130 id: tutorialLeftLongTimer
131 objectName: "tutorialLeftLongTimer"
134 if (parent.isReady) {
138 } else if (!parent.skipped) {
144 onIsReadyChanged: if (isReady && !shown) tutorialLeftLongTimer.start()
145 onFinished: AccountsService.markDemoEdgeCompleted("left-long")
150 id: tutorialTopLoader
151 objectName: "tutorialTopLoader"
154 readonly property bool skipped: !d.mobileScenario || d.haveShown("top")
155 readonly property bool shown: item && item.shown
156 active: !skipped || (item && item.visible)
157 onSkippedChanged: if (skipped && shown) item.hide()
159 sourceComponent: TutorialTop {
161 objectName: "tutorialTop"
164 hides: [launcher, panel.indicators]
167 skipped: tutorialTopLoader.skipped
168 isReady: tutorialLeftLongLoader.skipped && !skipped && !paused && !keyboardVisible &&
169 !tutorialBottomLoader.shown && !tutorialBottomLoader.mightShow
171 // We fire 30s after left edge tutorial, with at least 3s of inactivity
174 id: tutorialTopInactivityTimer
175 lastInputTimestamp: root.lastInputTimestamp
181 objectName: "tutorialTopTimer"
183 onTriggered: tutorialTopInactivityTimer.start()
186 onIsReadyChanged: if (isReady && !shown) tutorialTopTimer.start()
187 onFinished: AccountsService.markDemoEdgeCompleted("top")
192 id: tutorialRightLoader
193 objectName: "tutorialRightLoader"
196 readonly property bool skipped: d.haveShown("right")
197 readonly property bool shown: item && item.shown
198 active: !skipped || (item && item.visible)
199 onSkippedChanged: if (skipped && shown) item.hide()
201 sourceComponent: TutorialRight {
203 objectName: "tutorialRight"
206 usageScenario: root.usageScenario
207 hides: [launcher, panel.indicators]
210 skipped: tutorialRightLoader.skipped
211 isReady: tutorialTopLoader.skipped && !skipped && !paused && !keyboardVisible &&
212 !tutorialBottomLoader.shown && !tutorialBottomLoader.mightShow &&
213 ApplicationManager.count >= 3
216 id: tutorialRightInactivityTimer
217 objectName: "tutorialRightInactivityTimer"
218 lastInputTimestamp: root.lastInputTimestamp
224 onFocusedAppChanged: {
225 if (tutorialRight.isReady && !tutorialRight.shown && d.focusedApp
226 && d.focusedApp.state === ApplicationInfoInterface.Starting) {
227 tutorialRight.show();
232 onIsReadyChanged: if (isReady && !shown) tutorialRightInactivityTimer.start()
233 onFinished: AccountsService.markDemoEdgeCompleted("right")
238 id: tutorialBottomLoader
239 objectName: "tutorialBottomLoader"
242 // See TutorialBottom.qml for an explanation of why we only support
244 readonly property var supportedApps: ["address-book-app",
245 "com.ubuntu.calculator_calculator",
248 readonly property bool skipped: {
249 if (!d.mobileScenario) {
252 for (var i = 0; i < supportedApps.length; i++) {
253 if (!d.haveShown("bottom-" + supportedApps[i])) {
259 readonly property bool shown: item && item.shown
260 readonly property bool haveShownFocusedApp: d.focusedApp &&
261 d.haveShown("bottom-" + d.focusedApp.appId)
262 readonly property bool mightShow: !skipped && d.focusedApp &&
263 supportedApps.indexOf(d.focusedApp.appId) !== -1 &&
265 active: !skipped || (item && item.visible)
266 onHaveShownFocusedAppChanged: if (haveShownFocusedApp && shown) hide()
267 onSkippedChanged: if (skipped && shown) item.hide()
269 sourceComponent: TutorialBottom {
271 objectName: "tutorialBottom"
273 hides: [launcher, panel.indicators]
275 usageScenario: root.usageScenario
277 application: d.focusedApp
279 skipped: tutorialBottomLoader.skipped
280 isReady: !tutorialBottomLoader.skipped && !paused && !keyboardVisible &&
281 !tutorialLeftLoader.shown && !tutorialLeftLongLoader.shown &&
282 !tutorialTopLoader.shown && !tutorialRightLoader.shown &&
283 tutorialBottomLoader.mightShow &&
284 d.focusedApp.state === ApplicationInfoInterface.Running
286 onIsReadyChanged: if (isReady && !shown) show()
287 onFinished: AccountsService.markDemoEdgeCompleted("bottom-" + d.focusedApp.appId)