2 * Copyright (C) 2013 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.SystemImage 0.1
26 import Ubuntu.Telephony 0.1 as Telephony
27 import Unity.Connectivity 0.1
28 import Unity.Launcher 0.1
30 import LightDM 0.1 as LightDM
32 import SessionBroadcast 0.1
37 import "Notifications"
39 import Unity.Notifications 1.0 as NotificationBackend
40 import Unity.Session 0.1
41 import Unity.DashCommunicator 0.1
46 // this is only here to select the width / height of the window if not running fullscreen
47 property bool tablet: false
48 width: tablet ? units.gu(160) : applicationArguments.hasGeometry() ? applicationArguments.width() : units.gu(40)
49 height: tablet ? units.gu(100) : applicationArguments.hasGeometry() ? applicationArguments.height() : units.gu(71)
51 property real edgeSize: units.gu(2)
52 property url defaultBackground: Qt.resolvedUrl(shell.width >= units.gu(60) ? "graphics/tablet_background.jpg" : "graphics/phone_background.jpg")
53 property url background
54 readonly property real panelHeight: panel.panelHeight
56 readonly property bool locked: LightDM.Greeter.active && !LightDM.Greeter.authenticated
58 property bool sideStageEnabled: shell.width >= units.gu(100)
59 readonly property string focusedApplicationId: ApplicationManager.focusedApplicationId
61 property int maxFailedLogins: -1 // disabled by default for now, will enable via settings in future
62 property int failedLoginsDelayAttempts: 7 // number of failed logins
63 property int failedLoginsDelaySeconds: 5 * 60 // seconds of forced waiting
65 property int orientation
66 readonly property int deviceOrientationAngle: Screen.angleBetween(Screen.primaryOrientation, Screen.orientation)
67 onDeviceOrientationAngleChanged: {
68 if (!OrientationLock.enabled) {
69 orientation = Screen.orientation;
72 readonly property bool orientationLockEnabled: OrientationLock.enabled
73 onOrientationLockEnabledChanged: {
74 if (orientationLockEnabled) {
75 OrientationLock.savedOrientation = Screen.orientation;
77 orientation = Screen.orientation;
81 function activateApplication(appId) {
82 if (ApplicationManager.findApplication(appId)) {
83 ApplicationManager.requestFocusApplication(appId);
85 var execFlags = shell.sideStageEnabled ? ApplicationManager.NoFlag : ApplicationManager.ForceMainStage;
86 ApplicationManager.startApplication(appId, execFlags);
90 function setFakeActiveForApp(app) {
92 greeter.fakeActiveForApp = app
99 property: "applicationManager"
100 value: ApplicationManager
103 Component.onCompleted: {
104 Theme.name = "Ubuntu.Components.Themes.SuruGradient"
105 if (ApplicationManager.count > 0) {
106 ApplicationManager.focusApplication(ApplicationManager.get(0).appId);
108 if (orientationLockEnabled) {
109 orientation = OrientationLock.savedOrientation;
114 id: backgroundSettings
115 schema.id: "org.gnome.desktop.background"
117 property url gSettingsPicture: backgroundSettings.pictureUri != undefined && backgroundSettings.pictureUri.length > 0 ? backgroundSettings.pictureUri : shell.defaultBackground
118 onGSettingsPictureChanged: {
119 shell.background = gSettingsPicture
128 objectName: "dashCommunicator"
132 // Handle but do not filter out volume keys
133 Keys.onVolumeUpPressed: { volumeControl.volumeUp(); event.accepted = false; }
134 Keys.onVolumeDownPressed: { volumeControl.volumeDown(); event.accepted = false; }
137 if (event.key == Qt.Key_PowerOff || event.key == Qt.Key_PowerDown) {
138 dialogs.onPowerKeyPressed();
139 event.accepted = true;
141 event.accepted = false;
146 if (event.key == Qt.Key_PowerOff || event.key == Qt.Key_PowerDown) {
147 dialogs.onPowerKeyReleased();
148 event.accepted = true;
150 event.accepted = false;
159 height: parent.height
160 visible: !ApplicationManager.empty
163 target: ApplicationManager
165 if (appId === "dialer-app") {
166 // Always let the dialer-app through. Either user asked
167 // for an emergency call or accepted an incoming call.
168 setFakeActiveForApp(appId)
169 } else if (greeter.fakeActiveForApp !== "" && greeter.fakeActiveForApp !== appId) {
176 onFocusedApplicationIdChanged: {
177 if (greeter.fakeActiveForApp !== "" && greeter.fakeActiveForApp !== ApplicationManager.focusedApplicationId) {
180 panel.indicators.hide();
183 onApplicationAdded: {
184 if (greeter.shown && appId != "unity8-dash") {
187 if (appId === "dialer-app") {
188 // Always let the dialer-app through. Either user asked
189 // for an emergency call or accepted an incoming call.
190 setFakeActiveForApp(appId)
197 id: applicationsDisplayLoader
200 source: shell.sideStageEnabled ? "Stages/TabletStage.qml" : "Stages/PhoneStage.qml"
203 target: applicationsDisplayLoader.item
204 property: "objectName"
208 target: applicationsDisplayLoader.item
209 property: "dragAreaWidth"
210 value: shell.edgeSize
213 target: applicationsDisplayLoader.item
214 property: "maximizedAppTopMargin"
215 // Not just using panel.panelHeight as that changes depending on the focused app.
216 value: panel.indicators.panelHeight
219 target: applicationsDisplayLoader.item
220 property: "interactive"
221 value: edgeDemo.stagesEnabled && !greeter.shown && !lockscreen.shown && panel.indicators.fullyClosed && launcher.progress == 0
224 target: applicationsDisplayLoader.item
225 property: "spreadEnabled"
226 value: edgeDemo.stagesEnabled && greeter.fakeActiveForApp === "" // to support emergency dialer hack
229 target: applicationsDisplayLoader.item
230 property: "inverseProgress"
231 value: launcher.progress
234 target: applicationsDisplayLoader.item
235 property: "orientation"
236 value: shell.orientation
243 objectName: "inputMethod"
244 anchors { fill: parent; topMargin: panel.panelHeight }
245 z: notifications.useModal || panel.indicators.shown ? overlay.z + 1 : overlay.z - 1
249 target: SurfaceManager
251 if (surface.type == MirSurfaceItem.InputMethod) {
252 inputMethod.surface = surface;
256 onSurfaceDestroyed: {
257 if (inputMethod.surface == surface) {
258 inputMethod.surface = null;
259 surface.parent = null;
261 if (!surface.parent) {
262 // there's no one displaying it. delete it right away
268 target: SessionManager
270 if (!session.parentSession && !session.application) {
271 // nothing is using it. delete it right away
279 objectName: "lockscreen"
281 readonly property int backgroundTopMargin: -panel.panelHeight
283 hides: [launcher, panel.indicators]
286 showAnimation: StandardAnimation { property: "opacity"; to: 1 }
287 hideAnimation: StandardAnimation { property: "opacity"; to: 0 }
291 height: parent.height - panel.panelHeight
292 background: shell.background
293 alphaNumeric: AccountsService.passwordDisplayHint === AccountsService.Keyboard
297 onEntered: LightDM.Greeter.respond(passphrase);
298 onCancel: greeter.show()
299 onEmergencyCall: shell.activateApplication("dialer-app") // will automatically enter fake-active mode
301 onShownChanged: if (shown) greeter.fakeActiveForApp = ""
303 Component.onCompleted: {
304 if (greeter.narrowMode) {
305 LightDM.Greeter.authenticate(LightDM.Users.data(0, LightDM.UserRoles.NameRole))
311 target: LightDM.Greeter
313 onShowGreeter: greeter.show()
314 onHideGreeter: greeter.login()
317 if (greeter.narrowMode) {
318 if (isDefaultPrompt) {
319 if (lockscreen.alphaNumeric) {
320 lockscreen.infoText = i18n.tr("Enter your passphrase")
321 lockscreen.errorText = i18n.tr("Sorry, incorrect passphrase")
323 lockscreen.infoText = i18n.tr("Enter your passcode")
324 lockscreen.errorText = i18n.tr("Sorry, incorrect passcode")
327 lockscreen.infoText = i18n.tr("Enter your %1").arg(text.toLowerCase())
328 lockscreen.errorText = i18n.tr("Sorry, incorrect %1").arg(text.toLowerCase())
335 onPromptlessChanged: {
336 if (LightDM.Greeter.promptless && LightDM.Greeter.authenticated) {
344 onAuthenticationComplete: {
345 if (LightDM.Greeter.authenticated) {
346 AccountsService.failedLogins = 0
348 // Else only penalize user for a failed login if they actually were
349 // prompted for a password. We do this below after the promptless
352 if (LightDM.Greeter.promptless) {
356 if (LightDM.Greeter.authenticated) {
359 AccountsService.failedLogins++
360 if (maxFailedLogins >= 2) { // require at least a warning
361 if (AccountsService.failedLogins === maxFailedLogins - 1) {
362 var title = lockscreen.alphaNumeric ?
363 i18n.tr("Sorry, incorrect passphrase.") :
364 i18n.tr("Sorry, incorrect passcode.")
365 var text = i18n.tr("This will be your last attempt.") + " " +
366 (lockscreen.alphaNumeric ?
367 i18n.tr("If passphrase is entered incorrectly, your phone will conduct a factory reset and all personal data will be deleted.") :
368 i18n.tr("If passcode is entered incorrectly, your phone will conduct a factory reset and all personal data will be deleted."))
369 lockscreen.showInfoPopup(title, text)
370 } else if (AccountsService.failedLogins >= maxFailedLogins) {
371 SystemImage.factoryReset() // Ouch!
374 if (failedLoginsDelayAttempts > 0 && AccountsService.failedLogins % failedLoginsDelayAttempts == 0) {
375 lockscreen.forceDelay(failedLoginsDelaySeconds * 1000)
378 lockscreen.clear(true);
379 if (greeter.narrowMode) {
380 LightDM.Greeter.authenticate(LightDM.Users.data(0, LightDM.UserRoles.NameRole))
387 target: LightDM.Greeter
389 value: greeter.shown || lockscreen.shown || greeter.fakeActiveForApp != ""
395 opacity: greeterWrapper.showProgress * 0.8
399 // Just a tiny wrapper to adjust greeter's x without messing with its own dragging
404 height: parent.height - panel.panelHeight
407 enabled: !launcher.dashSwipe
411 property bool fullyShown: showProgress === 1.0
412 onFullyShownChanged: {
413 // Wait until the greeter is completely covering lockscreen before resetting it.
414 if (fullyShown && !LightDM.Greeter.authenticated) {
420 readonly property real showProgress: MathUtils.clamp((1 - x/width) + greeter.showProgress - 1, 0, 1)
421 onShowProgressChanged: if (LightDM.Greeter.authenticated && showProgress === 0) greeter.login()
425 objectName: "greeter"
427 signal sessionStarted() // helpful for tests
429 property string fakeActiveForApp: ""
432 hides: [launcher, panel.indicators]
434 loadContent: required || lockscreen.required // keeps content in memory for quick show()
436 defaultBackground: shell.background
439 height: parent.height
441 dragHandleWidth: shell.edgeSize
445 if (LightDM.Greeter.startSessionSync()) {
456 if (greeter.narrowMode) {
457 LightDM.Greeter.authenticate(LightDM.Users.data(0, LightDM.UserRoles.NameRole));
459 greeter.fakeActiveForApp = "";
460 greeter.forceActiveFocus();
464 /* TODO re-enable when the corresponding changes in the service land (LP: #1361074)
465 Component.onCompleted: {
466 Connectivity.unlockAllModems()
469 onUnlocked: greeter.hide()
471 // Update launcher items for new user
472 var user = LightDM.Users.data(uid, LightDM.UserRoles.NameRole);
473 AccountsService.user = user;
474 LauncherModel.setUser(user);
477 onTease: launcher.tease()
480 target: ApplicationManager
481 property: "suspended"
482 value: greeter.shown && greeterWrapper.showProgress == 1
492 if (Powerd.status === Powerd.Off && !callManager.hasCalls && !edgeDemo.running) {
498 function showHome() {
499 if (edgeDemo.running) {
503 if (LightDM.Greeter.active) {
504 if (!LightDM.Greeter.authenticated) {
510 var animate = !LightDM.Greeter.active && !stages.shown
511 dash.setCurrentScope("clickscope", animate, false)
512 ApplicationManager.requestFocusApplication("unity8-dash")
515 function showDash() {
524 ApplicationManager.requestFocusApplication("unity8-dash")
537 anchors.fill: parent //because this draws indicator menus
540 available: edgeDemo.panelEnabled && (!shell.locked || AccountsService.enableIndicatorsWhileLocked) && greeter.fakeActiveForApp === ""
541 contentEnabled: edgeDemo.panelContentEnabled
542 width: parent.width > units.gu(60) ? units.gu(40) : parent.width
543 panelHeight: units.gu(3)
546 property bool topmostApplicationIsFullscreen:
547 ApplicationManager.focusedApplicationId &&
548 ApplicationManager.findApplication(ApplicationManager.focusedApplicationId).fullscreen
550 fullscreenMode: (topmostApplicationIsFullscreen && !LightDM.Greeter.active && launcher.progress == 0)
551 || greeter.fakeActiveForApp !== ""
556 objectName: "launcher"
558 readonly property bool dashSwipe: progress > 0
560 anchors.top: parent.top
561 anchors.bottom: parent.bottom
563 dragAreaWidth: shell.edgeSize
564 available: edgeDemo.launcherEnabled && (!shell.locked || AccountsService.enableLauncherWhileLocked) && greeter.fakeActiveForApp === ""
566 onShowDashHome: showHome()
568 onDashSwipeChanged: {
570 dash.setCurrentScope("clickscope", false, true)
573 onLauncherApplicationSelected: {
574 if (greeter.fakeActiveForApp !== "") {
577 if (!edgeDemo.running)
578 shell.activateApplication(appId)
582 panel.indicators.hide()
588 id: modalNotificationBackground
590 visible: notifications.useModal && !greeter.shown && (notifications.state == "narrow")
603 model: NotificationBackend.Model
608 height: parent.height - panel.panelHeight
613 when: overlay.width <= units.gu(60)
614 AnchorChanges { target: notifications; anchors.left: parent.left }
618 when: overlay.width > units.gu(60)
619 AnchorChanges { target: notifications; anchors.left: undefined }
620 PropertyChanges { target: notifications; width: units.gu(38) }
631 shutdownFadeOutRectangle.enabled = true;
632 shutdownFadeOutRectangle.visible = true;
633 shutdownFadeOut.start();
638 id: alphaDisclaimerLabel
639 anchors.centerIn: parent
640 visible: ApplicationManager.fake ? ApplicationManager.fake : false
642 text: "EARLY ALPHA\nNOT READY FOR USE"
645 font.weight: Font.Black
646 horizontalAlignment: Text.AlignHCenter
647 verticalAlignment: Text.AlignVCenter
648 fontSizeMode: Text.Fit
650 scale: Math.min(parent.width, parent.height) / width
655 z: alphaDisclaimerLabel.z + 10
656 paused: Powerd.status === Powerd.Off // Saves power
659 indicators: panel.indicators
664 target: SessionBroadcast
665 onShowHome: showHome()
669 id: shutdownFadeOutRectangle
676 NumberAnimation on opacity {
681 if (shutdownFadeOutRectangle.enabled && shutdownFadeOutRectangle.visible) {
682 DBusUnitySessionService.Shutdown();