2 * Copyright (C) 2013-2015 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 QtQuick.Window 2.0
19 import AccountsService 0.1
21 import Unity.Application 0.1
22 import Ubuntu.Components 0.1
23 import Ubuntu.Components.Popups 1.0
24 import Ubuntu.Gestures 0.1
25 import Ubuntu.Telephony 0.1 as Telephony
26 import Unity.Connectivity 0.1
27 import Unity.Launcher 0.1
28 import GlobalShortcut 1.0 // has to be before Utils, because of WindowKeysFilter
31 import SessionBroadcast 0.1
36 import "Notifications"
40 import Unity.Notifications 1.0 as NotificationBackend
41 import Unity.Session 0.1
42 import Unity.DashCommunicator 0.1
43 import Unity.Indicators 0.1 as Indicators
49 // to be set from outside
50 property int orientationAngle: 0
51 property int orientation
52 property int primaryOrientation
53 property int nativeOrientation
54 property real nativeWidth
55 property real nativeHeight
56 property alias indicatorAreaShowProgress: panel.indicatorAreaShowProgress
57 property bool beingResized
58 property string usageScenario: "phone" // supported values: "phone", "tablet" or "desktop"
59 property string mode: "full-greeter"
60 function updateFocusedAppOrientation() {
61 applicationsDisplayLoader.item.updateFocusedAppOrientation();
63 function updateFocusedAppOrientationAnimated() {
64 applicationsDisplayLoader.item.updateFocusedAppOrientationAnimated();
67 // to be read from outside
68 readonly property int mainAppWindowOrientationAngle:
69 applicationsDisplayLoader.item ? applicationsDisplayLoader.item.mainAppWindowOrientationAngle : 0
71 readonly property bool orientationChangesEnabled: panel.indicators.fullyClosed
72 && (applicationsDisplayLoader.item && applicationsDisplayLoader.item.orientationChangesEnabled)
73 && (!greeter || !greeter.animating)
75 readonly property bool showingGreeter: greeter && greeter.shown
77 property bool startingUp: true
78 Timer { id: finishStartUpTimer; interval: 500; onTriggered: startingUp = false }
80 property int supportedOrientations: {
82 // Ensure we don't rotate during start up
83 return Qt.PrimaryOrientation;
84 } else if (greeter && greeter.shown) {
85 return Qt.PrimaryOrientation;
87 return mainApp.supportedOrientations;
90 return Qt.PortraitOrientation
91 | Qt.LandscapeOrientation
92 | Qt.InvertedPortraitOrientation
93 | Qt.InvertedLandscapeOrientation;
97 // For autopilot consumption
98 readonly property string focusedApplicationId: ApplicationManager.focusedApplicationId
100 // internal props from here onwards
101 readonly property var mainApp:
102 applicationsDisplayLoader.item ? applicationsDisplayLoader.item.mainApp : null
104 // Disable everything while greeter is waiting, so that the user can't swipe
105 // the greeter or launcher until we know whether the session is locked.
106 enabled: greeter && !greeter.waiting
108 property real edgeSize: units.gu(2)
109 property url defaultBackground: Qt.resolvedUrl(shell.width >= units.gu(60) ? "graphics/tablet_background.jpg" : "graphics/phone_background.jpg")
110 property url background: asImageTester.status == Image.Ready ? asImageTester.source
111 : gsImageTester.status == Image.Ready ? gsImageTester.source : defaultBackground
113 readonly property alias greeter: greeterLoader.item
115 function activateApplication(appId) {
116 if (ApplicationManager.findApplication(appId)) {
117 ApplicationManager.requestFocusApplication(appId);
119 var execFlags = shell.usageScenario === "phone" ? ApplicationManager.ForceMainStage
120 : ApplicationManager.NoFlag;
121 ApplicationManager.startApplication(appId, execFlags);
125 function startLockedApp(app) {
126 if (greeter.locked) {
127 greeter.lockedApp = app;
129 shell.activateApplication(app);
132 // This is a dummy image to detect if the custom AS set wallpaper loads successfully.
135 source: AccountsService.backgroundFile != undefined && AccountsService.backgroundFile.length > 0 ? AccountsService.backgroundFile : ""
143 id: backgroundSettings
144 schema.id: "org.gnome.desktop.background"
147 // This is a dummy image to detect if the custom GSettings set wallpaper loads successfully.
150 source: backgroundSettings.pictureUri && backgroundSettings.pictureUri.length > 0 ? backgroundSettings.pictureUri : ""
158 target: LauncherModel
159 property: "applicationManager"
160 value: ApplicationManager
163 Component.onCompleted: {
164 Theme.name = "Ubuntu.Components.Themes.SuruGradient"
165 if (ApplicationManager.count > 0) {
166 ApplicationManager.focusApplication(ApplicationManager.get(0).appId);
168 finishStartUpTimer.start();
171 LightDM{id: lightDM} // Provide backend access
174 indicators: panel.indicators
179 objectName: "dashCommunicator"
183 id: physicalKeysMapper
184 objectName: "physicalKeysMapper"
186 onPowerKeyLongPressed: dialogs.showPowerDialog();
187 onVolumeDownTriggered: volumeControl.volumeDown();
188 onVolumeUpTriggered: volumeControl.volumeUp();
189 onScreenshotTriggered: screenGrabber.capture();
198 // dummy shortcut to force creation of GlobalShortcutRegistry before WindowKeyFilter
202 Keys.onPressed: physicalKeysMapper.onKeyPressed(event);
203 Keys.onReleased: physicalKeysMapper.onKeyReleased(event);
207 onActivated: { launcher.fadeOut(); shell.showHome(); }
214 height: parent.height
217 target: ApplicationManager
219 // This signal is also fired when we try to focus the current app
220 // again. We rely on this!
221 onFocusedApplicationIdChanged: {
222 var appId = ApplicationManager.focusedApplicationId;
224 if (tutorial.running && appId != "" && appId != "unity8-dash") {
225 // If this happens on first boot, we may be in edge
226 // tutorial or wizard while receiving a call. But a call
227 // is more important than wizard so just bail out of those.
232 if (appId === "dialer-app" && callManager.hasCalls && greeter.locked) {
233 // If we are in the middle of a call, make dialer lockedApp and show it.
234 // This can happen if user backs out of dialer back to greeter, then
235 // launches dialer again.
236 greeter.lockedApp = appId;
238 greeter.notifyAppFocused(appId);
240 panel.indicators.hide();
243 onApplicationAdded: {
249 id: applicationsDisplayLoader
250 objectName: "applicationsDisplayLoader"
253 // When we have a locked app, we only want to show that one app.
254 // FIXME: do this in a less traumatic way. We currently only allow
255 // locked apps in phone mode (see FIXME in Lockscreen component in
256 // this same file). When that changes, we need to do something
257 // nicer here. But this code is currently just to prevent a
258 // theoretical attack where user enters lockedApp mode, then makes
259 // the screen larger (maybe connects to monitor) and tries to enter
262 property string usageScenario: shell.usageScenario === "phone" || greeter.hasLockedApp
264 : shell.usageScenario
266 if(shell.mode === "greeter") {
267 return "Stages/ShimStage.qml"
268 } else if (applicationsDisplayLoader.usageScenario === "phone") {
269 return "Stages/PhoneStage.qml";
270 } else if (applicationsDisplayLoader.usageScenario === "tablet") {
271 return "Stages/TabletStage.qml";
273 return "Stages/DesktopStage.qml";
277 property bool interactive: tutorial.spreadEnabled
278 && (!greeter || !greeter.shown)
279 && panel.indicators.fullyClosed
280 && launcher.progress == 0
281 && !notifications.useModal
283 onInteractiveChanged: { if (interactive) { focus = true; } }
286 target: applicationsDisplayLoader.item
287 property: "objectName"
291 target: applicationsDisplayLoader.item
292 property: "dragAreaWidth"
293 value: shell.edgeSize
296 target: applicationsDisplayLoader.item
297 property: "maximizedAppTopMargin"
298 // Not just using panel.panelHeight as that changes depending on the focused app.
299 value: panel.indicators.minimizedPanelHeight + units.dp(2) // dp(2) for orange line
302 target: applicationsDisplayLoader.item
303 property: "interactive"
304 value: applicationsDisplayLoader.interactive
307 target: applicationsDisplayLoader.item
308 property: "spreadEnabled"
309 value: tutorial.spreadEnabled && (!greeter || (!greeter.hasLockedApp && !greeter.shown))
312 target: applicationsDisplayLoader.item
313 property: "inverseProgress"
314 value: greeter && greeter.locked ? 0 : launcher.progress
317 target: applicationsDisplayLoader.item
318 property: "shellOrientationAngle"
319 value: shell.orientationAngle
322 target: applicationsDisplayLoader.item
323 property: "shellOrientation"
324 value: shell.orientation
327 target: applicationsDisplayLoader.item
328 property: "background"
329 value: shell.background
332 target: applicationsDisplayLoader.item
333 property: "shellPrimaryOrientation"
334 value: shell.primaryOrientation
337 target: applicationsDisplayLoader.item
338 property: "nativeOrientation"
339 value: shell.nativeOrientation
342 target: applicationsDisplayLoader.item
343 property: "nativeWidth"
344 value: shell.nativeWidth
347 target: applicationsDisplayLoader.item
348 property: "nativeHeight"
349 value: shell.nativeHeight
352 target: applicationsDisplayLoader.item
353 property: "beingResized"
354 value: shell.beingResized
357 target: applicationsDisplayLoader.item
358 property: "keepDashRunning"
359 value: launcher.shown || launcher.dashSwipe
362 target: applicationsDisplayLoader.item
363 property: "suspended"
367 target: applicationsDisplayLoader.item
368 property: "altTabPressed"
369 value: physicalKeysMapper.altTabPressed
375 objectName: "tutorial"
378 // EdgeDragAreas don't work with mice. So to avoid trapping the user,
379 // we skip the tutorial on the Desktop to avoid using them. The
380 // Desktop doesn't use the same spread design anyway. The tutorial is
381 // all a bit of a placeholder on non-phone form factors right now.
382 // When the design team gives us more guidance, we can do something
384 active: usageScenario != "desktop" && AccountsService.demoEdges
386 paused: LightDM.Greeter.active
389 edgeSize: shell.edgeSize
392 AccountsService.demoEdges = false;
393 active = false; // for immediate response / if AS is having problems
400 objectName: "inputMethod"
401 anchors { fill: parent; topMargin: panel.panelHeight }
402 z: notifications.useModal || panel.indicators.shown || wizard.active ? overlay.z + 1 : overlay.z - 1
406 target: SessionManager
408 if (!session.parentSession && !session.application) {
409 // nothing is using it. delete it right away
418 anchors.topMargin: panel.panelHeight
419 sourceComponent: shell.mode != "shell" ? integratedGreeter :
420 Qt.createComponent(Qt.resolvedUrl("Greeter/ShimGreeter.qml"));
422 item.objectName = "greeter"
427 id: integratedGreeter
430 hides: [launcher, panel.indicators]
431 tabletMode: shell.usageScenario != "phone"
432 launcherOffset: launcher.progress
433 forcedUnlock: tutorial.running
434 background: shell.background
436 // avoid overlapping with Launcher's edge drag area
437 // FIXME: Fix TouchRegistry & friends and remove this workaround
438 // Issue involves launcher's DDA getting disabled on a long
440 dragHandleLeftMargin: launcher.available ? launcher.dragAreaWidth + 1 : 0
447 if (!tutorial.running) {
452 onEmergencyCall: startLockedApp("dialer-app")
457 // See powerConnection for why this is useful
458 id: showGreeterDelayed
470 if (greeter.locked && callManager.hasCalls && greeter.lockedApp !== "dialer-app") {
471 // We just received an incoming call while locked. The
472 // indicator will have already launched dialer-app for us, but
473 // there is a race between "hasCalls" changing and the dialer
474 // starting up. So in case we lose that race, we'll start/
475 // focus the dialer ourselves here too. Even if the indicator
476 // didn't launch the dialer for some reason (or maybe a call
477 // started via some other means), if an active call is
478 // happening, we want to be in the dialer.
479 startLockedApp("dialer-app")
489 if (Powerd.status === Powerd.Off && reason !== Powerd.Proximity &&
490 !callManager.hasCalls && !tutorial.running) {
491 // We don't want to simply call greeter.showNow() here, because
492 // that will take too long. Qt will delay button event
493 // handling until the greeter is done loading and may think the
494 // user held down the power button the whole time, leading to a
495 // power dialog being shown. Instead, delay showing the
496 // greeter until we've finished handling the event. We could
497 // make the greeter load asynchronously instead, but that
498 // introduces a whole host of timing issues, especially with
499 // its animations. So this is simpler.
500 showGreeterDelayed.start();
505 function showHome() {
506 if (tutorial.running) {
510 greeter.notifyAboutToFocusApp("unity8-dash");
512 var animate = !lightDM.greeter.active && !stages.shown
513 dash.setCurrentScope(0, animate, false)
514 ApplicationManager.requestFocusApplication("unity8-dash")
517 function showDash() {
518 if (greeter.notifyShowingDashFromDrag()) {
522 if (!greeter.locked && ApplicationManager.focusedApplicationId != "unity8-dash") {
523 ApplicationManager.requestFocusApplication("unity8-dash")
537 anchors.fill: parent //because this draws indicator menus
540 available: tutorial.panelEnabled
541 && ((!greeter || !greeter.locked) || AccountsService.enableIndicatorsWhileLocked)
542 && (!greeter || !greeter.hasLockedApp)
543 contentEnabled: tutorial.panelContentEnabled
544 width: parent.width > units.gu(60) ? units.gu(40) : parent.width
546 minimizedPanelHeight: units.gu(3)
547 expandedPanelHeight: units.gu(7)
549 indicatorsModel: Indicators.IndicatorsModel {
550 // tablet and phone both use the same profile
551 profile: shell.usageScenario === "desktop" ? "desktop" : "phone"
552 Component.onCompleted: load();
557 greeterShown: greeter.shown
560 property bool topmostApplicationIsFullscreen:
561 ApplicationManager.focusedApplicationId &&
562 ApplicationManager.findApplication(ApplicationManager.focusedApplicationId).fullscreen
564 fullscreenMode: (topmostApplicationIsFullscreen && !lightDM.greeter.active && launcher.progress == 0)
565 || greeter.hasLockedApp
570 objectName: "launcher"
572 readonly property bool dashSwipe: progress > 0
574 anchors.top: parent.top
575 anchors.topMargin: inverted ? 0 : panel.panelHeight
576 anchors.bottom: parent.bottom
578 dragAreaWidth: shell.edgeSize
579 available: tutorial.launcherEnabled
580 && (!greeter.locked || AccountsService.enableLauncherWhileLocked)
581 && !greeter.hasLockedApp
582 inverted: shell.usageScenario !== "desktop"
583 shadeBackground: !tutorial.running
585 onShowDashHome: showHome()
587 onDashSwipeChanged: {
589 dash.setCurrentScope(0, false, true)
592 onLauncherApplicationSelected: {
593 if (!tutorial.running) {
594 greeter.notifyAboutToFocusApp(appId);
595 shell.activateApplication(appId)
600 panel.indicators.hide()
609 background: shell.background
611 function unlockWhenDoneWithWizard() {
613 Connectivity.unlockAllModems();
617 Component.onCompleted: unlockWhenDoneWithWizard()
618 onActiveChanged: unlockWhenDoneWithWizard()
622 id: modalNotificationBackground
624 visible: notifications.useModal
637 model: NotificationBackend.Model
640 y: topmostIsFullscreen ? 0 : panel.panelHeight
641 height: parent.height - (topmostIsFullscreen ? 0 : panel.panelHeight)
646 when: overlay.width <= units.gu(60)
648 target: notifications
649 anchors.left: parent.left
650 anchors.right: parent.right
655 when: overlay.width > units.gu(60)
657 target: notifications
658 anchors.left: undefined
659 anchors.right: parent.right
661 PropertyChanges { target: notifications; width: units.gu(38) }
669 objectName: "dialogs"
672 usageScenario: shell.usageScenario
674 shutdownFadeOutRectangle.enabled = true;
675 shutdownFadeOutRectangle.visible = true;
676 shutdownFadeOut.start();
681 target: SessionBroadcast
682 onShowHome: showHome()
686 id: shutdownFadeOutRectangle
687 z: screenGrabber.z + 10
693 NumberAnimation on opacity {
698 if (shutdownFadeOutRectangle.enabled && shutdownFadeOutRectangle.visible) {
699 DBusUnitySessionService.shutdown();