on xenial help sign in
Make Tutorial start faster; block functionality (#279)

These changes make the Tutorial far tighter. It starts faster, demonstrates what it needs to, then gets out of the way. This fixes the most common complaint about the Tutorial, that it started too slowly. It also puts the steps in (what I believe) is a more logical order. First Indicators, then the Launcher, then Drawer, then Spread after the user opens an app. The Launcher is disabled until its tutorial appears, likewise for the Drawer. Fixes https://github.com/ubports/ubuntu-touch/issues/746

commit1d2d928dbb996c24f573953bd85e5c037549ce2c authorDalton Durst <dalton@ubports.com> dateThu 20 Feb 2020, 15:34 (7 years ago) parent1b356b0
4 files changed+173−151
qml/Shell.qml+2−2 qml/Tutorial/Tutorial.qml+1 qml/Tutorial/TutorialContent.qml+39−32 tests/qmltests/Tutorial/tst_Tutorial.qml+131−117
qml/Shell.qml+2−2
@@ -580,7 +580,7 @@ StyledItem {
580580 panelWidth: units.gu(settings.launcherWidth)
581581 lockedVisible: (lockedByUser || shell.atDesktop) && lockAllowed
582582 topPanelHeight: panel.panelHeight
583− drawerEnabled: !greeter.active
583+ drawerEnabled: !greeter.active && tutorial.launcherLongSwipeEnabled
584584 privateMode: greeter.active
585585 background: wallpaperResolver.background
586586
@@ -593,7 +593,7 @@ StyledItem {
593593
594594 // The Launcher should absolutely not be locked visible under some
595595 // conditions
596− readonly property bool lockAllowed: !collidingWithPanel && !panel.fullscreenMode && !wizard.active
596+ readonly property bool lockAllowed: !collidingWithPanel && !panel.fullscreenMode && !wizard.active && !tutorial.demonstrateLauncher
597597
598598 onShowDashHome: showHome()
599599 onLauncherApplicationSelected: {
qml/Tutorial/Tutorial.qml+1
@@ -36,6 +36,7 @@ Item {
3636 property bool delayed // don't show new tutorials
3737 property int lastInputTimestamp
3838
39+ readonly property bool demonstrateLauncher: loader.item ? loader.item.demonstrateLauncher : false
3940 readonly property bool launcherEnabled: loader.item ? loader.item.launcherEnabled : true
4041 readonly property bool launcherLongSwipeEnabled: loader.item ? loader.item.launcherLongSwipeEnabled : true
4142 readonly property bool spreadEnabled: loader.item ? loader.item.spreadEnabled : true
qml/Tutorial/TutorialContent.qml+39−32
@@ -30,11 +30,14 @@ Item {
3030 property bool delayed: true // same
3131 property var lastInputTimestamp
3232
33+ readonly property bool demonstrateLauncher: !tutorialLeftLoader.skipped
3334 readonly property bool launcherEnabled: !running
3435 || tutorialLeftLoader.shown
3536 || tutorialLeftLongLoader.shown
3637 readonly property bool launcherLongSwipeEnabled: tutorialLeftLongLoader.shown
3738 || tutorialLeftLongLoader.skipped
39+ || paused
40+ || delayed
3841 readonly property bool spreadEnabled: !running || tutorialRightLoader.shown
3942 readonly property bool panelEnabled: !running || tutorialTopLoader.shown
4043 readonly property bool running: tutorialLeftLoader.shown
@@ -68,69 +71,73 @@ Item {
6871 }
6972
7073 Loader {
71− id: tutorialLeftLoader
72− objectName: "tutorialLeftLoader"
74+ id: tutorialTopLoader
75+ objectName: "tutorialTopLoader"
7376 anchors.fill: parent
7477
75− readonly property bool skipped: !d.mobileScenario || d.haveShown("left")
78+ readonly property bool skipped: !d.mobileScenario || d.haveShown("top")
7679 readonly property bool shown: item && item.shown
7780 active: !skipped || (item && item.visible)
7881 onSkippedChanged: if (skipped && shown) item.hide()
7982
80− sourceComponent: TutorialLeft {
81− id: tutorialLeft
82− objectName: "tutorialLeft"
83+ sourceComponent: TutorialTop {
84+ id: tutorialTop
85+ objectName: "tutorialTop"
8386 anchors.fill: parent
84− launcher: root.launcher
87+ panel: root.panel
8588 hides: [launcher, panel.indicators]
8689 paused: root.paused
8790
88− isReady: !tutorialLeftLoader.skipped && !paused && !delayed
91+ skipped: tutorialTopLoader.skipped
92+ isReady: !skipped && !paused && !delayed
8993
9094 InactivityTimer {
91− id: tutorialLeftTimer
92− objectName: "tutorialLeftTimer"
93− interval: 20000
95+ id: tutorialTopTimer
96+ objectName: "tutorialTopTimer"
97+ interval: 1000
9498 lastInputTimestamp: root.lastInputTimestamp
9599 page: parent
96100 }
97101
98− onIsReadyChanged: if (isReady && !shown) tutorialLeftTimer.start()
99− onFinished: AccountsService.markDemoEdgeCompleted("left")
102+ onIsReadyChanged: if (isReady && !shown) tutorialTopTimer.start()
103+ onFinished: AccountsService.markDemoEdgeCompleted("top")
100104 }
101105 }
102106
103107 Loader {
104− id: tutorialTopLoader
105− objectName: "tutorialTopLoader"
108+ id: tutorialLeftLoader
109+ objectName: "tutorialLeftLoader"
106110 anchors.fill: parent
107111
108− readonly property bool skipped: !d.mobileScenario || d.haveShown("top")
112+ readonly property bool skipped: !d.mobileScenario || d.haveShown("left")
109113 readonly property bool shown: item && item.shown
110114 active: !skipped || (item && item.visible)
111115 onSkippedChanged: if (skipped && shown) item.hide()
112116
113− sourceComponent: TutorialTop {
114− id: tutorialTop
115− objectName: "tutorialTop"
117+ sourceComponent: TutorialLeft {
118+ id: tutorialLeft
119+ objectName: "tutorialLeft"
116120 anchors.fill: parent
117− panel: root.panel
121+ launcher: root.launcher
118122 hides: [launcher, panel.indicators]
119123 paused: root.paused
120124
121− skipped: tutorialTopLoader.skipped
122− isReady: tutorialLeftLoader.skipped && !skipped && !paused && !delayed
125+ isReady: !tutorialLeftLoader.skipped && tutorialTopLoader.skipped && !paused && !delayed
123126
124127 InactivityTimer {
125− id: tutorialTopTimer
126− objectName: "tutorialTopTimer"
127− interval: 60000
128+ id: tutorialLeftTimer
129+ objectName: "tutorialLeftTimer"
130+ interval: 1000
128131 lastInputTimestamp: root.lastInputTimestamp
129132 page: parent
130133 }
131134
132− onIsReadyChanged: if (isReady && !shown) tutorialTopTimer.start()
133− onFinished: AccountsService.markDemoEdgeCompleted("top")
135+ onIsReadyChanged: {
136+ if (isReady && !shown) {
137+ tutorialLeftTimer.start();
138+ }
139+ }
140+ onFinished: AccountsService.markDemoEdgeCompleted("left")
134141 }
135142 }
136143
@@ -153,12 +160,12 @@ Item {
153160 paused: root.paused
154161
155162 skipped: tutorialLeftLongLoader.skipped
156− isReady: tutorialTopLoader.skipped && !skipped && !paused && !delayed
163+ isReady: tutorialLeftLoader.skipped && !skipped && !paused && !delayed
157164
158165 InactivityTimer {
159166 id: tutorialLeftLongTimer
160167 objectName: "tutorialLeftLongTimer"
161− interval: 5000
168+ interval: 1000
162169 lastInputTimestamp: root.lastInputTimestamp
163170 page: parent
164171 }
@@ -188,13 +195,13 @@ Item {
188195 paused: root.paused
189196
190197 skipped: tutorialRightLoader.skipped
191− isReady: tutorialTopLoader.skipped && !skipped && !paused && !delayed &&
192− ApplicationManager.count >= 3
198+ isReady: tutorialLeftLongLoader.skipped && !skipped && !paused && !delayed &&
199+ ApplicationManager.count >= 1
193200
194201 InactivityTimer {
195202 id: tutorialRightTimer
196203 objectName: "tutorialRightTimer"
197− interval: 10000
204+ interval: 1000
198205 lastInputTimestamp: root.lastInputTimestamp
199206 page: parent
200207 }
tests/qmltests/Tutorial/tst_Tutorial.qml+131−117
@@ -267,10 +267,10 @@ Rectangle {
267267 function init() {
268268 prepareShell();
269269
270− var tutorialLeft = findChild(shell, "tutorialLeft");
271− var tutorialLeftTimer = findInvisibleChild(tutorialLeft, "tutorialLeftTimer");
272− tutorialLeftTimer.interval = 0;
273− tryCompare(tutorialLeft, "opacity", 1);
270+ var tutorialTop = findChild(shell, "tutorialTop");
271+ var tutorialTopTimer = findInvisibleChild(tutorialTop, "tutorialTopTimer");
272+ tutorialTopTimer.interval = 0;
273+ tryCompare(tutorialTop, "opacity", 1);
274274 }
275275
276276 function cleanup() {
@@ -308,7 +308,6 @@ Rectangle {
308308 tryCompare(shell, "waitingOnGreeter", false); // reset by greeter when ready
309309
310310 WindowStateStorage.clear();
311− //topLevelSurfaceList.inputMethodSurface.setState(Mir.MinimizedState);
312311 callManager.foregroundCall = null;
313312 AccountsService.demoEdges = false;
314313 AccountsService.demoEdgesCompleted = [];
@@ -317,8 +316,6 @@ Rectangle {
317316 LightDM.Greeter.hideGreeter();
318317
319318 stage = findChild(shell, "stage"); // from StageTestCase
320−
321− startApplication("unity8-dash");
322319 }
323320
324321 function loadShell(state) {
@@ -334,37 +331,25 @@ Rectangle {
334331 tryCompareFunction(function() { return topLevelSurfaceList.inputMethodSurface !== null }, true);
335332 }
336333
337− function swipeAwayGreeter() {
338− var coverPage = findChild(shell, "coverPage");
339− tryCompare(coverPage, "showProgress", 1);
340−
341− touchFlick(shell, halfWidth, halfHeight, shell.width, halfHeight);
342−
343− // wait until the animation has finished
344− var greeter = findChild(shell, "greeter");
345− tryCompare(greeter, "required", false);
346− waitForRendering(greeter);
347− }
348−
349− function openTutorialTop() {
334+ function openTutorialLeft() {
335+ var tutorialTopLoader = findChild(shell, "tutorialTopLoader");
350336 var tutorialLeftLoader = findChild(shell, "tutorialLeftLoader");
351− var tutorialTop = findChild(shell, "tutorialTop");
352− var tutorialTopTimer = findInvisibleChild(tutorialTop, "tutorialTopTimer");
337+ var tutorialLeft = findInvisibleChild(tutorialLeftLoader, "tutorialLeft");
338+ var tutorialLeftTimer = findInvisibleChild(tutorialLeft, "tutorialLeftTimer");
353339
354− tutorialTopTimer.interval = 1;
355− AccountsService.demoEdgesCompleted = ["left"];
340+ tutorialLeftTimer.interval = 1;
341+ AccountsService.demoEdgesCompleted = ["top"];
356342
357− tryCompare(tutorialLeftLoader, "active", false);
358− tryCompare(tutorialTop, "shown", true);
359− tryCompare(tutorialTop, "opacity", 1);
343+ tryCompare(tutorialTopLoader, "active", false);
344+ tryCompare(tutorialLeft, "shown", true);
345+ tryCompare(tutorialLeft, "opacity", 1);
360346 }
361347
362348 function openTutorialLeftLong() {
363349 var tutorialLeftLong = findChild(shell, "tutorialLeftLong");
364350 var tutorialLeftLongTimer = findChild(tutorialLeftLong, "tutorialLeftLongTimer");
365351
366− AccountsService.demoEdgesCompleted = ["left", "top"];
367− ApplicationManager.startApplication("gallery-app");
352+ AccountsService.demoEdgesCompleted = ["top", "left"];
368353
369354 tutorialLeftLongTimer.interval = 1;
370355 tryCompare(tutorialLeftLong, "shown", true);
@@ -372,21 +357,29 @@ Rectangle {
372357 }
373358
374359 function openTutorialRight() {
375− var tutorialLeftLoader = findChild(shell, "tutorialLeftLoader");
360+ var tutorialLeftLongLoader = findChild(shell, "tutorialLeftLongLoader");
376361 var tutorialRight = findChild(shell, "tutorialRight");
377362 var tutorialRightTimer = findChild(tutorialRight, "tutorialRightTimer");
378363
379− AccountsService.demoEdgesCompleted = ["left", "top", "left-long"];
380− ApplicationManager.startApplication("gallery-app");
364+ AccountsService.demoEdgesCompleted = ["top", "left", "left-long"];
381365 ApplicationManager.startApplication("facebook-webapp");
382− ApplicationManager.startApplication("camera-app");
383366
384− tryCompare(tutorialLeftLoader, "active", false);
367+ tryCompare(tutorialLeftLongLoader, "active", false);
385368 tutorialRightTimer.interval = 1;
386369 tryCompare(tutorialRight, "shown", true);
387370 tryCompare(tutorialRight, "opacity", 1);
388371 }
389372
373+ function longLeftSwipe() {
374+ // Perform a long enough swipe to cause the Drawer to appear
375+ var launcher = findChild(shell, "launcher");
376+ var startX = launcher.dragAreaWidth/2;
377+ var startY = launcher.height/2;
378+ touchFlick(launcher,
379+ startX, startY,
380+ startX+units.gu(35), startY);
381+ }
382+
390383 function test_tutorialLeftEdges() {
391384 var tutorial = findChild(shell, "tutorial");
392385 var tutorialLeft = findChild(tutorial, "tutorialLeft");
@@ -394,6 +387,8 @@ Rectangle {
394387 var stage = findChild(shell, "stage");
395388 var panel = findChild(shell, "panel");
396389
390+ openTutorialLeft();
391+
397392 verify(tutorial.running);
398393 verify(tutorial.launcherEnabled);
399394 verify(!tutorial.spreadEnabled);
@@ -409,10 +404,12 @@ Rectangle {
409404 var tutorialLeftLoader = findChild(tutorial, "tutorialLeftLoader");
410405 var launcher = findChild(shell, "launcher");
411406
407+ openTutorialLeft();
408+
412409 touchFlick(shell, 0, halfHeight, halfWidth, halfHeight);
413410
414411 tryCompare(tutorialLeftLoader, "shown", false);
415− tryCompare(AccountsService, "demoEdgesCompleted", ["left"]);
412+ tryCompare(AccountsService, "demoEdgesCompleted", ["top", "left"]);
416413 tryCompare(launcher, "state", "visible");
417414 }
418415
@@ -424,6 +421,8 @@ Rectangle {
424421 var tutorialLeftLoader = findChild(shell, "tutorialLeftLoader");
425422 var launcher = findChild(shell, "launcher");
426423
424+ openTutorialLeft();
425+
427426 // Confirm fade during drag
428427 touchFlick(shell, 0, halfHeight, launcher.panelWidth * 0.4, halfHeight, true, false);
429428 // compare opacity with a bound rather than hard 0.6 because progress doesn't
@@ -445,55 +444,65 @@ Rectangle {
445444 touchFlick(shell, 0, halfHeight, launcher.panelWidth * 0.9, halfHeight);
446445
447446 tryCompare(tutorialLeftLoader, "shown", false);
448− tryCompare(AccountsService, "demoEdgesCompleted", ["left"]);
447+ tryCompare(AccountsService, "demoEdgesCompleted", ["top", "left"]);
449448 tryCompare(launcher, "state", "visible");
450449 }
451450
452451 function test_tutorialLeftLongFinish() {
453− AccountsService.demoEdgesCompleted = ["left", "top"];
454−
455− var tutorialLeftLongTimer = findInvisibleChild(shell, "tutorialLeftLongTimer");
456− tutorialLeftLongTimer.interval = 1;
457−
458− ApplicationManager.startApplication("gallery-app");
459−
460452 var tutorialLeftLongLoader = findChild(shell, "tutorialLeftLongLoader");
453+ openTutorialLeftLong();
454+
461455 tryCompare(tutorialLeftLongLoader, "shown", true);
462456
463457 touchFlick(shell, 0, halfHeight, shell.width, halfHeight);
464458
465459 tryCompare(tutorialLeftLongLoader, "shown", false);
466− tryCompare(AccountsService, "demoEdgesCompleted", ["left", "top", "left-long"]);
460+ tryCompare(AccountsService, "demoEdgesCompleted", ["top", "left", "left-long"]);
467461 }
468462
469463 function test_tutorialLeftAutoSkipped() {
470464 // Test that we skip the tutorial if user uses left edge themselves
465+ var launcher = findChild(shell, "launcher");
471466
467+ AccountsService.demoEdgesCompleted = ["top"]
472468 var tutorialLeft = findChild(shell, "tutorialLeft");
473− LightDM.Greeter.showGreeter();
469+ launcher.show();
474470 tryCompare(tutorialLeft, "visible", false);
475− compare(AccountsService.demoEdgesCompleted, []);
471+ compare(AccountsService.demoEdgesCompleted, ["top"]);
476472
477473 touchFlick(shell, 0, halfHeight, halfWidth, halfHeight);
478− tryCompare(AccountsService, "demoEdgesCompleted", ["left"]);
474+ tryCompare(AccountsService, "demoEdgesCompleted", ["top", "left"]);
479475 }
480476
481477 function test_tutorialLongLeftSwipeDisabled() {
482478 // Test that a long left swipe is disabled until we get to or pass
483479 // the long left tutorial.
484480 var tutorial = findChild(shell, "tutorial");
481+ var launcher = findChild(shell, "launcher");
482+ var drawer = findChild(launcher, "drawer");
485483
486− verify(!tutorial.launcherLongSwipeEnabled);
484+ // Waiting for top tutorial, the Launcher should be unavailable
485+ longLeftSwipe();
486+ tryCompare(launcher, "state", "");
487487
488− openTutorialLeftLong();
489− verify(tutorial.launcherLongSwipeEnabled);
488+ // Swipe the left edge far enough to open the Drawer. Only the
489+ // Launcher should appear.
490+ openTutorialLeft();
491+ longLeftSwipe();
492+ tryCompare(launcher, "state", "visible");
490493
491− openTutorialRight();
492− verify(tutorial.launcherLongSwipeEnabled);
494+ // Once the Drawer tutorial is open, the Drawer should appear on
495+ // a long swipe.
496+ openTutorialLeftLong();
497+ longLeftSwipe();
498+ tryCompare(launcher, "state", "drawer");
499+ launcher.hide();
493500
501+ // That should continue after the Tutorial is no more
494502 AccountsService.demoEdges = false;
495503 tryCompare(tutorial, "running", false);
496− verify(tutorial.launcherLongSwipeEnabled);
504+ longLeftSwipe();
505+ tryCompare(launcher, "state", "drawer");
497506 }
498507
499508 function test_tutorialTopEdges() {
@@ -503,8 +512,6 @@ Rectangle {
503512 var stage = findChild(shell, "stage");
504513 var panel = findChild(shell, "panel");
505514
506− openTutorialTop();
507−
508515 tryCompare(tutorial, "running", true);
509516 verify(!tutorial.launcherEnabled);
510517 verify(!tutorial.spreadEnabled);
@@ -520,11 +527,10 @@ Rectangle {
520527 var tutorialTopLoader = findChild(tutorial, "tutorialTopLoader");
521528 var panel = findChild(shell, "panel");
522529
523− openTutorialTop();
524530 touchFlick(shell, halfWidth, 0, halfWidth, shell.height);
525531
526532 tryCompare(tutorialTopLoader, "shown", false);
527− tryCompare(AccountsService, "demoEdgesCompleted", ["left", "top"]);
533+ tryCompare(AccountsService, "demoEdgesCompleted", ["top"]);
528534 tryCompare(panel.indicators, "fullyOpened", true);
529535 }
530536
@@ -533,7 +539,6 @@ Rectangle {
533539 var tutorialTop = findChild(tutorial, "tutorialTop");
534540 var panel = findChild(shell, "panel");
535541
536− openTutorialTop();
537542 touchFlick(shell, halfWidth, 0, halfWidth, shell.height * 0.4, true, false);
538543 // compare opacity with a bound rather than hard 0.6 because progress doesn't
539544 // always match the drag perfectly (takes a moment for drag to kick in)
@@ -545,19 +550,6 @@ Rectangle {
545550 compare(tutorialTop.shown, true);
546551 }
547552
548− function test_tutorialTopAutoSkipped() {
549− // Test that we skip the tutorial if user uses top edge themselves
550−
551− var tutorialLeftLoader = findChild(shell, "tutorialLeftLoader");
552− var tutorialTopLoader = findChild(shell, "tutorialTopLoader");
553− AccountsService.demoEdgesCompleted = ["left"];
554− tryCompare(tutorialLeftLoader, "active", false);
555− verify(!tutorialTopLoader.shown);
556−
557− touchFlick(shell, halfWidth, 0, halfWidth, shell.height);
558− tryCompare(AccountsService, "demoEdgesCompleted", ["left", "top"]);
559− }
560−
561553 function test_tutorialRightEdges() {
562554 var tutorial = findChild(shell, "tutorial");
563555 var tutorialRight = findChild(tutorial, "tutorialRight");
@@ -581,7 +573,7 @@ Rectangle {
581573 openTutorialRight();
582574 touchFlick(shell, shell.width, halfHeight, 0, halfHeight);
583575
584− tryCompare(AccountsService, "demoEdgesCompleted", ["left", "top", "left-long", "right"]);
576+ tryCompare(AccountsService, "demoEdgesCompleted", ["top", "left", "left-long", "right"]);
585577 tryCompare(AccountsService, "demoEdges", false);
586578 }
587579
@@ -608,13 +600,10 @@ Rectangle {
608600 var tutorialRightLoader = findChild(shell, "tutorialRightLoader");
609601 var tutorialRightTimer = findInvisibleChild(tutorialRightLoader, "tutorialRightTimer");
610602
611− openTutorialTop();
612603 ApplicationManager.startApplication("gallery-app");
613− ApplicationManager.startApplication("facebook-webapp");
614− ApplicationManager.startApplication("camera-app");
615− tryCompare(ApplicationManager, "count", 4);
604+ tryCompare(ApplicationManager, "count", 1);
616605
617− AccountsService.demoEdgesCompleted = ["left", "top", "left-long"];
606+ AccountsService.demoEdgesCompleted = ["top", "left", "left-long"];
618607 verify(tutorialRightTimer.running);
619608 verify(!tutorialRightLoader.shown);
620609 tutorialRightTimer.interval = 1;
@@ -624,18 +613,30 @@ Rectangle {
624613 function test_tutorialRightAutoSkipped() {
625614 // Test that we skip the tutorial if user uses right edge themselves
626615
627− var tutorialLeftLoader = findChild(shell, "tutorialLeftLoader");
628− AccountsService.demoEdgesCompleted = ["left"];
629− tryCompare(tutorialLeftLoader, "active", false);
616+ var tutorialLeftLongLoader = findChild(shell, "tutorialLeftLongLoader");
617+ var tutorialRight = findChild(shell, "tutorialRight");
618+ var tutorialRightTimer = findChild(tutorialRight, "tutorialRightTimer");
619+
620+ tutorialRightTimer.interval = 9999999;
621+
622+ ApplicationManager.startApplication("facebook-webapp");
623+ tryCompare(ApplicationManager, "count", 1);
624+
625+ AccountsService.demoEdgesCompleted = ["top", "left", "left-long"];
626+ tryCompare(tutorialLeftLongLoader, "skipped", true);
627+ verify(tutorialRightTimer.running);
628+ tryCompare(tutorialRight, "isReady", true);
629+
630+ // If the app isn't fully started, it may interrupt our attempt to swipe
631+ wait(1000);
630632
631633 touchFlick(shell, shell.width, halfHeight, 0, halfHeight);
632− tryCompare(AccountsService, "demoEdgesCompleted", ["left", "right"]);
634+ tryCompare(AccountsService, "demoEdgesCompleted", ["top", "left", "left-long", "right"]);
633635 }
634636
635637 function test_activeCallInterruptsTutorial() {
636638 var tutorialLeft = findChild(shell, "tutorialLeft");
637− verify(tutorialLeft.shown);
638− verify(!tutorialLeft.paused);
639+ openTutorialLeft();
639640
640641 callManager.foregroundCall = phoneCall;
641642 verify(!tutorialLeft.shown);
@@ -648,18 +649,18 @@ Rectangle {
648649 }
649650
650651 function test_greeterInterruptsTutorial() {
651− var tutorialLeft = findChild(shell, "tutorialLeft");
652− verify(tutorialLeft.shown);
653− verify(!tutorialLeft.paused);
652+ var tutorialTop = findChild(shell, "tutorialTop");
653+ verify(tutorialTop.shown);
654+ verify(!tutorialTop.paused);
654655
655656 LightDM.Greeter.showGreeter();
656− verify(!tutorialLeft.shown);
657− verify(tutorialLeft.paused);
658− tryCompare(tutorialLeft, "visible", false);
657+ verify(!tutorialTop.shown);
658+ verify(tutorialTop.paused);
659+ tryCompare(tutorialTop, "visible", false);
659660
660661 LightDM.Greeter.hideGreeter();
661− tryCompare(tutorialLeft, "shown", true);
662− verify(!tutorialLeft.paused);
662+ tryCompare(tutorialTop, "shown", true);
663+ verify(!tutorialTop.paused);
663664 }
664665
665666 function test_interruptionChecksReadyStateWhenDone() {
@@ -682,7 +683,7 @@ Rectangle {
682683 verify(!tutorialRight.isReady);
683684 verify(!tutorialRight.shown);
684685 verify(!tutorialRight.paused);
685− compare(AccountsService.demoEdgesCompleted, ["left", "top", "left-long"]);
686+ compare(AccountsService.demoEdgesCompleted, ["top", "left", "left-long"]);
686687 }
687688
688689 function test_desktopOnlyShowsTutorialRight() {
@@ -701,8 +702,6 @@ Rectangle {
701702 var tutorialRightTimer = findInvisibleChild(tutorialRightLoader, "tutorialRightTimer");
702703 tutorialRightTimer.interval = 1;
703704
704− ApplicationManager.startApplication("dialer-app");
705− ApplicationManager.startApplication("camera-app");
706705 ApplicationManager.startApplication("facebook-webapp");
707706 tryCompare(tutorialRightLoader.item, "isReady", true);
708707
@@ -715,8 +714,6 @@ Rectangle {
715714 var tutorialRightLoader = findChild(shell, "tutorialRightLoader");
716715 var tutorialRightTimer = findInvisibleChild(tutorialRightLoader, "tutorialRightTimer");
717716 tutorialRightTimer.interval = 1;
718− ApplicationManager.startApplication("dialer-app");
719− ApplicationManager.startApplication("camera-app");
720717 ApplicationManager.startApplication("facebook-webapp");
721718 tryCompare(tutorialRightLoader, "shown", true);
722719
@@ -729,8 +726,8 @@ Rectangle {
729726 }
730727
731728 function test_oskDoesNotHideTutorial() {
732− var tutorialLeftLoader = findChild(shell, "tutorialLeftLoader");
733− verify(tutorialLeftLoader.shown);
729+ var tutorialTopLoader = findChild(shell, "tutorialTopLoader");
730+ verify(tutorialTopLoader.shown);
734731
735732 ensureInputMethodSurface();
736733 var surface = topLevelSurfaceList.inputMethodSurface;
@@ -739,7 +736,7 @@ Rectangle {
739736 var inputMethod = findInvisibleChild(shell, "inputMethod");
740737 tryCompare(inputMethod, "visible", true);
741738
742− verify(tutorialLeftLoader.shown);
739+ verify(tutorialTopLoader.shown);
743740 }
744741
745742 function test_oskDelaysTutorial() {
@@ -775,41 +772,58 @@ Rectangle {
775772 }
776773
777774 function test_delayedTutorial() {
775+ var launcher = findChild(shell, "launcher");
778776 var tutorial = findChild(shell, "tutorial");
779− var tutorialTop = findChild(tutorial, "tutorialTop");
780− var tutorialTopTimer = findChild(tutorialTop, "tutorialTopTimer");
777+ var tutorialLeft = findChild(tutorial, "tutorialLeft");
778+ var tutorialLeftTimer = findChild(tutorial, "tutorialLeftTimer");
779+ // Since we'll be using the Launcher and Drawer, the left tutorial
780+ // will be skipped entirely and we'll show the left long one.
781+ var tutorialLeftLong = findChild(tutorial, "tutorialLeftLong");
782+ var tutorialLeftLongTimer = findChild(tutorialLeftLong, "tutorialLeftLongTimer");
781783
782− // Get top tutorial ready
783− AccountsService.demoEdgesCompleted = ["left"];
784− verify(tutorialTop.isReady);
785− verify(tutorialTopTimer.running);
784+ // Don't need these to start before we're ready
785+ tutorialLeftTimer.interval = 999999;
786+ tutorialLeftLongTimer.interval = 1;
787+
788+ // Get left tutorial ready to be skipped
789+ AccountsService.demoEdgesCompleted = ["top"];
790+ tryCompare(tutorialLeft, "isReady", true);
791+ tryCompare(tutorialLeftTimer, "running", true);
786792
787793 // Confirm that we become unready when delayed, but timer still goes
788794 tutorial.delayed = true;
789− verify(!tutorialTop.isReady);
790− verify(tutorialTopTimer.running);
795+ tryCompare(tutorialLeft, "isReady", false);
796+ tryCompare(tutorialLeftTimer, "running", true);
791797
792798 // Confirm that we don't open the tutorial when delayed
793− tutorialTopTimer.interval = 1;
794− wait(5);
795− verify(!tutorialTop.shown);
799+ tutorialLeftTimer.interval = 1;
800+ wait(500);
801+ verify(!tutorialLeft.shown);
802+
803+ // Confirm the Launcher and Drawer can be opened when delayed
804+ tryCompare(tutorial, "launcherEnabled", true);
805+ tryCompare(tutorial, "launcherLongSwipeEnabled", true);
806+ longLeftSwipe();
807+ tryCompare(launcher, "state", "drawer");
808+ launcher.hide();
796809
797810 // Confirm we go back to normal when undelayed
798811 tutorial.delayed = false;
799− tryCompare(tutorialTop, "shown", true);
812+ tryCompare(tutorialLeftLong, "shown", true);
800813 }
801814
802815 function test_accountsServiceSettings() {
803− var tutorialLeftLoader = findChild(shell, "tutorialLeftLoader");
804− verify(tutorialLeftLoader.shown);
816+ var tutorial = findChild(shell, "tutorial");
817+ var tutorialTopLoader = findChild(tutorial, "tutorialTopLoader");
818+ verify(tutorialTopLoader.shown);
805819
806820 AccountsService.demoEdges = false;
807− verify(findChild(shell, "tutorialLeftLoader") == null);
821+ verify(findChild(tutorial, "tutorialTopLoader", 1) == null);
808822
809823 AccountsService.demoEdges = true;
810− tutorialLeftLoader = findChild(shell, "tutorialLeftLoader");
811− var tutorialLeftTimer = findChild(tutorialLeftLoader, "tutorialLeftTimer");
812− verify(tutorialLeftTimer.running);
824+ tutorialTopLoader = findChild(shell, "tutorialTopLoader");
825+ var tutorialTopTimer = findChild(tutorialTopLoader, "tutorialTopTimer");
826+ verify(tutorialTopTimer.running);
813827 }
814828 }
815829}