on xenial help sign in
PhoneStage: ensure you're left in a consistent state after being reset
0bb912bDaniel d'Andrada
qml/Stages/PhoneStage.qml+15−7
@@ -48,17 +48,12 @@ Rectangle {
48 onBeingResizedChanged: {
48 onBeingResizedChanged: {
49 if (beingResized) {
49 if (beingResized) {
50 // Brace yourselves for impact!
50 // Brace yourselves for impact!
51− spreadView.selectedIndex = -1;
51+ priv.reset();
52− spreadView.phase = 0;
53− spreadView.contentX = -spreadView.shift;
54 }
52 }
55 }
53 }
56 onSpreadEnabledChanged: {
54 onSpreadEnabledChanged: {
57 if (!spreadEnabled) {
55 if (!spreadEnabled) {
58− // reset. go back to display the focused app
56+ priv.reset();
59− spreadView.selectedIndex = -1;
60− spreadView.phase = 0;
61− spreadView.contentX = -spreadView.shift;
62 }
57 }
63 }
58 }
64 function updateFocusedAppOrientation() {
59 function updateFocusedAppOrientation() {
@@ -94,6 +89,7 @@ Rectangle {
94 readonly property var mainApp: applicationManager.focusedApplicationId
89 readonly property var mainApp: applicationManager.focusedApplicationId
95 ? applicationManager.findApplication(applicationManager.focusedApplicationId)
90 ? applicationManager.findApplication(applicationManager.focusedApplicationId)
96 : null
91 : null
92+
97 property int mainAppWindowOrientationAngle: 0
93 property int mainAppWindowOrientationAngle: 0
98 readonly property bool orientationChangesEnabled: priv.focusedAppOrientationChangesEnabled
94 readonly property bool orientationChangesEnabled: priv.focusedAppOrientationChangesEnabled
99 && !priv.focusedAppDelegateIsDislocated
95 && !priv.focusedAppDelegateIsDislocated
@@ -216,6 +212,18 @@ Rectangle {
216 // Is more stable than "spreadView.shiftedContentX === 0" as it filters out noise caused by
212 // Is more stable than "spreadView.shiftedContentX === 0" as it filters out noise caused by
217 // Flickable.contentX changing due to resizes.
213 // Flickable.contentX changing due to resizes.
218 property bool fullyShowingFocusedApp: true
214 property bool fullyShowingFocusedApp: true
215+
216+ function reset() {
217+ // The app that's about to go to foreground has to be focused, otherwise
218+ // it would leave us in an inconsistent state.
219+ if (!root.applicationManager.focusedApplicationId && root.applicationManager.count > 0) {
220+ root.applicationManager.focusApplication(root.applicationManager.get(0).appId);
221+ }
222+
223+ spreadView.selectedIndex = -1;
224+ spreadView.phase = 0;
225+ spreadView.contentX = -spreadView.shift;
226+ }
219 }
227 }
220 Timer {
228 Timer {
221 id: fullyShowingFocusedAppUpdateTimer
229 id: fullyShowingFocusedAppUpdateTimer
tests/qmltests/tst_OrientedShell.qml+106−1
@@ -996,6 +996,60 @@ Rectangle {
996 tryCompare(shell, "transformRotationAngle", root.primaryOrientationAngle);
996 tryCompare(shell, "transformRotationAngle", root.primaryOrientationAngle);
997 }
997 }
998
998
999+ /*
1000+ Regression test for https://bugs.launchpad.net/ubuntu/+source/unity8/+bug/1476757
1001+
1002+ Steps:
1003+ 1- have a portrait-only app in foreground (eg unity8-dash)
1004+ 2- launch or switch to some other application
1005+ 3- right-edge swipe to show the apps spread
1006+ 4- swipe up to close the current app (the one from step 2)
1007+ 5- lock the phone (press the power button)
1008+ 6- unlock the phone (press power button again and swipe greeter away)
1009+ * app from step 1 should be on foreground and focused
1010+ 7- rotate phone
1011+
1012+ Expected outcome:
1013+ - The portrait-only application stays put
1014+
1015+ Actual outcome:
1016+ - The portrait-only application rotates freely
1017+ */
1018+ function test_lockPhoneAfterClosingAppInSpreadThenUnlockAndRotate() {
1019+ loadShell("mako");
1020+
1021+ var gmailApp = ApplicationManager.startApplication("gmail-webapp");
1022+ verify(gmailApp);
1023+
1024+ waitUntilAppSurfaceShowsUp("gmail-webapp");
1025+
1026+ performEdgeSwipeToShowAppSpread();
1027+
1028+ swipeToCloseCurrentAppInSpread();
1029+
1030+ // press the power key once
1031+ Powerd.setStatus(Powerd.Off, Powerd.Unknown);
1032+ var greeter = findChild(shell, "greeter");
1033+ tryCompare(greeter, "fullyShown", true);
1034+
1035+ // and a second time to turn the display back on
1036+ Powerd.setStatus(Powerd.On, Powerd.Unknown);
1037+
1038+ swipeAwayGreeter();
1039+
1040+ verify(isAppSurfaceFocused("unity8-dash"))
1041+
1042+ signalSpy.clear();
1043+ signalSpy.target = shell;
1044+ signalSpy.signalName = "widthChanged";
1045+ verify(signalSpy.valid);
1046+
1047+ rotateTo(90);
1048+
1049+ // shell shouldn't have change its orientation at any moment
1050+ compare(signalSpy.count, 0);
1051+ }
1052+
999 // angle - rotation angle in degrees clockwise, relative to the primary orientation.
1053 // angle - rotation angle in degrees clockwise, relative to the primary orientation.
1000 function rotateTo(angle) {
1054 function rotateTo(angle) {
1001 switch (angle) {
1055 switch (angle) {
@@ -1016,6 +1070,12 @@ Rectangle {
1016 }
1070 }
1017
1071
1018 var rotationStates = findInvisibleChild(orientedShell, "rotationStates");
1072 var rotationStates = findInvisibleChild(orientedShell, "rotationStates");
1073+ verify(rotationStates.d);
1074+ verify(rotationStates.d.stateUpdateTimer);
1075+
1076+ // wait for the delayed state update to take place, if any
1077+ tryCompare(rotationStates.d.stateUpdateTimer, "running", false);
1078+
1019 waitUntilTransitionsEnd(rotationStates);
1079 waitUntilTransitionsEnd(rotationStates);
1020 }
1080 }
1021
1081
@@ -1239,7 +1299,6 @@ Rectangle {
1239 var childs = new Array(0);
1299 var childs = new Array(0);
1240 childs.push(obj)
1300 childs.push(obj)
1241 while (childs.length > 0) {
1301 while (childs.length > 0) {
1242− console.log("Checking " + childs[0].objectName);
1243 if (childs[0].objectName === "surfaceItem"
1302 if (childs[0].objectName === "surfaceItem"
1244 && childs[0].surface !== undefined
1303 && childs[0].surface !== undefined
1245 && childs[0].surface === surface) {
1304 && childs[0].surface === surface) {
@@ -1252,5 +1311,51 @@ Rectangle {
1252 }
1311 }
1253 return null;
1312 return null;
1254 }
1313 }
1314+
1315+ function swipeToCloseCurrentAppInSpread() {
1316+ var spreadView = findChild(shell, "spreadView");
1317+ verify(spreadView);
1318+
1319+ var delegateToClose = findChild(spreadView, "appDelegate0");
1320+ verify(delegateToClose);
1321+
1322+ var appIdToClose = ApplicationManager.get(0).appId;;
1323+ var appCountBefore = ApplicationManager.count;
1324+
1325+ // ensure the current app is widely visible by swiping to the right,
1326+ // which will move the app windows accordingly
1327+ touchFlick(shell,
1328+ shell.width * 0.25, shell.width / 2,
1329+ shell.width, shell.width / 2);
1330+
1331+ tryCompare(spreadView, "flicking", false);
1332+ tryCompare(spreadView, "moving", false);
1333+
1334+ // Swipe up close to its left edge, as it is the only area of it guaranteed to be exposed
1335+ // in the spread. Eg: its center could be covered by some other delegate.
1336+ touchFlick(delegateToClose,
1337+ 1, delegateToClose.height / 2,
1338+ 1, - delegateToClose.height / 4);
1339+
1340+ // ensure it got closed
1341+ tryCompare(ApplicationManager, "count", appCountBefore - 1);
1342+ compare(ApplicationManager.findApplication(appIdToClose), null);
1343+ }
1344+
1345+ function isAppSurfaceFocused(appId) {
1346+ var appWindow = findChild(shell, "appWindow_" + appId);
1347+ verify(appWindow);
1348+
1349+ var app = ApplicationManager.findApplication(appId);
1350+ verify(app);
1351+
1352+ var surface = app.session.surface;
1353+ verify(surface);
1354+
1355+ var surfaceItem = findSurfaceItem(appWindow, surface);
1356+ verify(surfaceItem);
1357+
1358+ return surfaceItem.activeFocus;
1359+ }
1255 }
1360 }
1256}
1361}