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/>.
18import QtQuick.Window 2.0
19import AccountsService 0.1
21import Unity.Application 0.1
22import Ubuntu.Components 0.1
23import Ubuntu.Components.Popups 1.0
24import Ubuntu.Gestures 0.1
25import Ubuntu.SystemImage 0.1
26import Ubuntu.Telephony 0.1 as Telephony
27import Unity.Connectivity 0.1
28import Unity.Launcher 0.1
30import LightDM 0.1 as LightDM
32import SessionBroadcast 0.1
39import "Panel/Indicators"
41import Unity.Notifications 1.0 as NotificationBackend
42import Unity.Session 0.1
43import Unity.DashCommunicator 0.1
48 // Disable everything so that user can't swipe greeter or launcher until
49 // we get first prompt/authenticate, which will re-enable the shell.
52 // this is only here to select the width / height of the window if not running fullscreen
53 property bool tablet: false
54 width: tablet ? units.gu(160) : applicationArguments.hasGeometry() ? applicationArguments.width() : units.gu(40)
55 height: tablet ? units.gu(100) : applicationArguments.hasGeometry() ? applicationArguments.height() : units.gu(71)
57 property real edgeSize: units.gu(2)
58 property url defaultBackground: Qt.resolvedUrl(shell.width >= units.gu(60) ? "graphics/tablet_background.jpg" : "graphics/phone_background.jpg")
59 property url background: asImageTester.status == Image.Ready ? asImageTester.source
60 : gsImageTester.status == Image.Ready ? gsImageTester.source : defaultBackground
61 readonly property real panelHeight: panel.panelHeight
63 readonly property bool locked: LightDM.Greeter.active && !LightDM.Greeter.authenticated && !forcedUnlock
64 readonly property alias hasLockedApp: greeter.hasLockedApp
65 readonly property bool forcedUnlock: edgeDemo.running
66 onForcedUnlockChanged: if (forcedUnlock) lockscreen.hide()
68 property bool sideStageEnabled: shell.width >= units.gu(100)
69 readonly property string focusedApplicationId: ApplicationManager.focusedApplicationId
71 property int maxFailedLogins: -1 // disabled by default for now, will enable via settings in future
72 property int failedLoginsDelayAttempts: 7 // number of failed logins
73 property int failedLoginsDelayMinutes: 5 // minutes of forced waiting
75 property int orientation
76 readonly property int deviceOrientationAngle: Screen.angleBetween(Screen.primaryOrientation, Screen.orientation)
77 onDeviceOrientationAngleChanged: {
78 if (!OrientationLock.enabled) {
79 orientation = Screen.orientation;
82 readonly property bool orientationLockEnabled: OrientationLock.enabled
83 onOrientationLockEnabledChanged: {
84 if (orientationLockEnabled) {
85 OrientationLock.savedOrientation = Screen.orientation;
87 orientation = Screen.orientation;
91 function activateApplication(appId) {
92 if (ApplicationManager.findApplication(appId)) {
93 ApplicationManager.requestFocusApplication(appId);
95 var execFlags = shell.sideStageEnabled ? ApplicationManager.NoFlag : ApplicationManager.ForceMainStage;
96 ApplicationManager.startApplication(appId, execFlags);
100 function startLockedApp(app) {
102 greeter.lockedApp = app;
104 shell.activateApplication(app);
107 // This is a dummy image to detect if the custom AS set wallpaper loads successfully.
110 source: AccountsService.backgroundFile != undefined && AccountsService.backgroundFile.length > 0 ? AccountsService.backgroundFile : ""
118 id: backgroundSettings
119 schema.id: "org.gnome.desktop.background"
122 // This is a dummy image to detect if the custom GSettings set wallpaper loads successfully.
125 source: backgroundSettings.pictureUri != undefined && backgroundSettings.pictureUri.length > 0 ? backgroundSettings.pictureUri : ""
133 id: usageModeSettings
134 schema.id: "com.canonical.Unity8"
138 target: LauncherModel
139 property: "applicationManager"
140 value: ApplicationManager
143 Component.onCompleted: {
144 Theme.name = "Ubuntu.Components.Themes.SuruGradient"
145 if (ApplicationManager.count > 0) {
146 ApplicationManager.focusApplication(ApplicationManager.get(0).appId);
148 if (orientationLockEnabled) {
149 orientation = OrientationLock.savedOrientation;
159 objectName: "dashCommunicator"
165 enabled: Powerd.status === Powerd.On
169 target: ApplicationManager
170 property: "forceDashActive"
171 value: launcher.shown || launcher.dashSwipe
176 onVolumeDownPressed: volumeControl.volumeDown()
177 onVolumeUpPressed: volumeControl.volumeUp()
178 onBothVolumeKeysPressed: screenGrabber.capture()
183 if (event.key == Qt.Key_PowerOff || event.key == Qt.Key_PowerDown) {
184 dialogs.onPowerKeyPressed();
185 event.accepted = true;
187 volumeKeyFilter.onKeyPressed(event.key);
188 event.accepted = false;
193 if (event.key == Qt.Key_PowerOff || event.key == Qt.Key_PowerDown) {
194 dialogs.onPowerKeyReleased();
195 event.accepted = true;
197 volumeKeyFilter.onKeyReleased(event.key);
198 event.accepted = false;
207 height: parent.height
208 visible: !ApplicationManager.empty
211 target: ApplicationManager
213 if (greeter.narrowMode) {
214 if (appId === "dialer-app" && callManager.hasCalls && shell.locked) {
215 // If we are in the middle of a call, make dialer lockedApp and show it.
216 // This can happen if user backs out of dialer back to greeter, then
217 // launches dialer again.
218 greeter.lockedApp = appId;
220 if (greeter.hasLockedApp) {
221 if (appId === greeter.lockedApp) {
222 lockscreen.hide() // show locked app
224 greeter.startUnlock() // show lockscreen if necessary
229 if (LightDM.Greeter.active) {
230 greeter.startUnlock()
235 onFocusedApplicationIdChanged: {
236 if (greeter.hasLockedApp && greeter.lockedApp !== ApplicationManager.focusedApplicationId) {
237 greeter.startUnlock()
239 panel.indicators.hide();
242 onApplicationAdded: {
243 if (greeter.shown && appId != "unity8-dash") {
244 greeter.startUnlock()
246 // If this happens on first boot, we may be in edge
247 // tutorial or wizard while receiving a call. But a call
248 // is more important than wizard so just bail out of those.
249 if (edgeDemo.running) {
250 edgeDemo.hideEdgeDemos();
254 if (greeter.narrowMode && greeter.hasLockedApp && appId === greeter.lockedApp) {
255 lockscreen.hide() // show locked app
262 id: applicationsDisplayLoader
263 objectName: "applicationsDisplayLoader"
266 // When we have a locked app, we only want to show that one app.
267 // FIXME: do this in a less traumatic way. We currently only allow
268 // locked apps in phone mode (see FIXME in Lockscreen component in
269 // this same file). When that changes, we need to do something
270 // nicer here. But this code is currently just to prevent a
271 // theoretical attack where user enters lockedApp mode, then makes
272 // the screen larger (maybe connects to monitor) and tries to enter
274 property bool tabletMode: shell.sideStageEnabled && !greeter.hasLockedApp
275 source: usageModeSettings.usageMode === "Windowed" ? "Stages/DesktopStage.qml"
276 : tabletMode ? "Stages/TabletStage.qml" : "Stages/PhoneStage.qml"
279 target: applicationsDisplayLoader.item
280 property: "objectName"
284 target: applicationsDisplayLoader.item
285 property: "dragAreaWidth"
286 value: shell.edgeSize
289 target: applicationsDisplayLoader.item
290 property: "maximizedAppTopMargin"
291 // Not just using panel.panelHeight as that changes depending on the focused app.
292 value: panel.indicators.minimizedPanelHeight + units.dp(2) // dp(2) for orange line
295 target: applicationsDisplayLoader.item
296 property: "interactive"
297 value: edgeDemo.stagesEnabled && !greeter.shown && !lockscreen.shown && panel.indicators.fullyClosed && launcher.progress == 0 && !notifications.useModal
300 target: applicationsDisplayLoader.item
301 property: "spreadEnabled"
302 value: edgeDemo.stagesEnabled && !greeter.hasLockedApp
305 target: applicationsDisplayLoader.item
306 property: "inverseProgress"
307 value: launcher.progress
310 target: applicationsDisplayLoader.item
311 property: "orientation"
312 value: shell.orientation
315 target: applicationsDisplayLoader.item
316 property: "background"
317 value: shell.background
324 objectName: "inputMethod"
325 anchors { fill: parent; topMargin: panel.panelHeight }
326 z: notifications.useModal || panel.indicators.shown ? overlay.z + 1 : overlay.z - 1
330 target: SurfaceManager
332 if (surface.type == MirSurfaceItem.InputMethod) {
333 inputMethod.surface = surface;
337 onSurfaceDestroyed: {
338 if (inputMethod.surface == surface) {
339 inputMethod.surface = null;
340 surface.parent = null;
342 if (!surface.parent) {
343 // there's no one displaying it. delete it right away
349 target: SessionManager
351 if (!session.parentSession && !session.application) {
352 // nothing is using it. delete it right away
360 objectName: "lockscreen"
362 hides: [launcher, panel.indicators]
365 showAnimation: StandardAnimation { property: "opacity"; to: 1 }
366 hideAnimation: StandardAnimation { property: "opacity"; to: 0 }
370 height: parent.height - panel.panelHeight
371 background: shell.background
372 darkenBackground: 0.4
373 alphaNumeric: AccountsService.passwordDisplayHint === AccountsService.Keyboard
377 property string promptText
378 infoText: promptText !== "" ? i18n.tr("Enter %1").arg(promptText) :
379 alphaNumeric ? i18n.tr("Enter passphrase") :
380 i18n.tr("Enter passcode")
381 errorText: promptText !== "" ? i18n.tr("Sorry, incorrect %1").arg(promptText) :
382 alphaNumeric ? i18n.tr("Sorry, incorrect passphrase") + "\n" +
383 i18n.tr("Please re-enter") :
384 i18n.tr("Sorry, incorrect passcode")
386 // FIXME: We *should* show emergency dialer if there is a SIM present,
387 // regardless of whether the side stage is enabled. But right now,
388 // the assumption is that narrow screens are phones which have SIMs
389 // and wider screens are tablets which don't. When we do allow this
390 // on devices with a side stage and a SIM, work should be done to
391 // ensure that the main stage is disabled while the dialer is present
392 // in the side stage. See the FIXME in the stage loader in this file.
393 showEmergencyCallButton: !shell.sideStageEnabled
395 onEntered: LightDM.Greeter.respond(passphrase);
396 onCancel: greeter.show()
397 onEmergencyCall: startLockedApp("dialer-app")
399 onShownChanged: if (shown) greeter.lockedApp = ""
401 function maybeShow() {
402 if (!shell.forcedUnlock) {
411 if (lockscreen.delayMinutes > 0) {
412 lockscreen.delayMinutes -= 1
413 if (lockscreen.delayMinutes > 0) {
420 Component.onCompleted: {
421 if (greeter.narrowMode) {
422 LightDM.Greeter.authenticate(LightDM.Users.data(0, LightDM.UserRoles.NameRole))
428 target: LightDM.Greeter
430 onShowGreeter: greeter.show()
431 onHideGreeter: greeter.login()
434 shell.enabled = true;
435 if (!LightDM.Greeter.active) {
436 return; // could happen if hideGreeter() comes in before we prompt
438 if (greeter.narrowMode) {
439 lockscreen.promptText = isDefaultPrompt ? "" : text.toLowerCase();
440 lockscreen.maybeShow();
444 onPromptlessChanged: {
445 if (!LightDM.Greeter.active) {
446 return; // could happen if hideGreeter() comes in before we prompt
448 if (greeter.narrowMode) {
449 if (LightDM.Greeter.promptless && LightDM.Greeter.authenticated) {
453 lockscreen.maybeShow();
458 onAuthenticationComplete: {
459 shell.enabled = true;
460 if (LightDM.Greeter.authenticated) {
461 AccountsService.failedLogins = 0
463 // Else only penalize user for a failed login if they actually were
464 // prompted for a password. We do this below after the promptless
467 if (LightDM.Greeter.promptless) {
471 if (LightDM.Greeter.authenticated) {
474 AccountsService.failedLogins++
475 if (maxFailedLogins >= 2) { // require at least a warning
476 if (AccountsService.failedLogins === maxFailedLogins - 1) {
477 var title = lockscreen.alphaNumeric ?
478 i18n.tr("Sorry, incorrect passphrase.") :
479 i18n.tr("Sorry, incorrect passcode.")
480 var text = i18n.tr("This will be your last attempt.") + " " +
481 (lockscreen.alphaNumeric ?
482 i18n.tr("If passphrase is entered incorrectly, your phone will conduct a factory reset and all personal data will be deleted.") :
483 i18n.tr("If passcode is entered incorrectly, your phone will conduct a factory reset and all personal data will be deleted."))
484 lockscreen.showInfoPopup(title, text)
485 } else if (AccountsService.failedLogins >= maxFailedLogins) {
486 SystemImage.factoryReset() // Ouch!
489 if (failedLoginsDelayAttempts > 0 && AccountsService.failedLogins % failedLoginsDelayAttempts == 0) {
490 lockscreen.delayMinutes = failedLoginsDelayMinutes
491 forcedDelayTimer.start()
494 lockscreen.clear(true);
495 if (greeter.narrowMode) {
496 LightDM.Greeter.authenticate(LightDM.Users.data(0, LightDM.UserRoles.NameRole))
503 target: LightDM.Greeter
505 value: greeter.shown || lockscreen.shown || greeter.hasLockedApp
511 opacity: greeterWrapper.showProgress * 0.8
515 // Just a tiny wrapper to adjust greeter's x without messing with its own dragging
517 objectName: "greeterWrapper"
518 x: (greeter.narrowMode && greeter.showProgress > 0) ? launcher.progress : 0
521 height: parent.height - panel.panelHeight
524 enabled: !launcher.dashSwipe
528 property bool fullyShown: showProgress === 1.0
529 onFullyShownChanged: {
530 // Wait until the greeter is completely covering lockscreen before resetting it.
531 if (greeter.narrowMode && fullyShown && !LightDM.Greeter.authenticated) {
533 lockscreen.maybeShow();
537 readonly property real showProgress: MathUtils.clamp((1 - x/width) + greeter.showProgress - 1, 0, 1)
538 onShowProgressChanged: {
539 if (showProgress === 0) {
540 if ((LightDM.Greeter.promptless && LightDM.Greeter.authenticated) || shell.forcedUnlock) {
542 } else if (greeter.narrowMode) {
543 lockscreen.clear(false) // to reset focus if necessary
550 objectName: "greeter"
552 signal sessionStarted() // helpful for tests
554 property string lockedApp: ""
555 property bool hasLockedApp: lockedApp !== ""
557 hides: [launcher, panel.indicators]
558 loadContent: required || lockscreen.required // keeps content in memory for quick show()
562 background: shell.background
565 height: parent.height
568 // avoid overlapping with Launcher's edge drag area
569 // FIXME: Fix TouchRegistry & friends and remove this workaround
570 // Issue involves launcher's DDA getting disabled on a long
572 dragHandleLeftMargin: launcher.available ? launcher.dragAreaWidth + 1 : 0
574 function startUnlock() {
576 if (!LightDM.Greeter.authenticated) {
577 lockscreen.maybeShow()
588 if (LightDM.Greeter.startSessionSync()) {
599 // Disable everything so that user can't swipe greeter or
600 // launcher until we get the next prompt/authenticate, which
601 // will re-enable the shell.
602 shell.enabled = false;
604 if (greeter.narrowMode) {
605 LightDM.Greeter.authenticate(LightDM.Users.data(0, LightDM.UserRoles.NameRole));
609 greeter.lockedApp = "";
610 greeter.forceActiveFocus();
614 Component.onCompleted: {
615 Connectivity.unlockAllModems()
618 onUnlocked: greeter.hide()
620 // Update launcher items for new user
621 var user = LightDM.Users.data(uid, LightDM.UserRoles.NameRole);
622 AccountsService.user = user;
623 LauncherModel.setUser(user);
626 onTapped: launcher.tease()
634 target: ApplicationManager
635 property: "suspended"
636 value: (greeter.shown && greeterWrapper.showProgress == 1) || lockscreen.shown
646 if (shell.locked && callManager.hasCalls) {
647 // We just received an incoming call while locked. The
648 // indicator will have already launched dialer-app for us, but
649 // there is a race between "hasCalls" changing and the dialer
650 // starting up. So in case we lose that race, we'll start/
651 // focus the dialer ourselves here too. Even if the indicator
652 // didn't launch the dialer for some reason (or maybe a call
653 // started via some other means), if an active call is
654 // happening, we want to be in the dialer.
655 startLockedApp("dialer-app")
665 if (Powerd.status === Powerd.Off && reason !== Powerd.Proximity &&
666 !callManager.hasCalls && !edgeDemo.running) {
672 function showHome() {
673 if (edgeDemo.running) {
677 if (LightDM.Greeter.active) {
678 greeter.startUnlock()
681 var animate = !LightDM.Greeter.active && !stages.shown
682 dash.setCurrentScope(0, animate, false)
683 ApplicationManager.requestFocusApplication("unity8-dash")
686 function showDash() {
687 if (greeter.hasLockedApp || // just in case user gets here
688 (!greeter.narrowMode && shell.locked)) {
697 if (ApplicationManager.focusedApplicationId != "unity8-dash") {
698 ApplicationManager.requestFocusApplication("unity8-dash")
712 anchors.fill: parent //because this draws indicator menus
715 available: edgeDemo.panelEnabled && (!shell.locked || AccountsService.enableIndicatorsWhileLocked) && !greeter.hasLockedApp
716 contentEnabled: edgeDemo.panelContentEnabled
717 width: parent.width > units.gu(60) ? units.gu(40) : parent.width
719 minimizedPanelHeight: units.gu(3)
720 expandedPanelHeight: units.gu(7)
722 indicatorsModel: visibleIndicators.model
726 id: visibleIndicators
727 // TODO: This should be sourced by device type (eg "desktop", "tablet", "phone"...)
728 Component.onCompleted: initialise(indicatorProfile)
731 greeterShown: greeter.shown || lockscreen.shown
734 property bool topmostApplicationIsFullscreen:
735 ApplicationManager.focusedApplicationId &&
736 ApplicationManager.findApplication(ApplicationManager.focusedApplicationId).fullscreen
738 fullscreenMode: (topmostApplicationIsFullscreen && !LightDM.Greeter.active && launcher.progress == 0)
739 || greeter.hasLockedApp
744 objectName: "launcher"
746 readonly property bool dashSwipe: progress > 0
748 anchors.top: parent.top
749 anchors.topMargin: inverted ? 0 : panel.panelHeight
750 anchors.bottom: parent.bottom
752 dragAreaWidth: shell.edgeSize
753 available: edgeDemo.launcherEnabled && (!shell.locked || AccountsService.enableLauncherWhileLocked) && !greeter.hasLockedApp
754 inverted: usageModeSettings.usageMode === "Staged"
756 onShowDashHome: showHome()
758 onDashSwipeChanged: {
760 dash.setCurrentScope(0, false, true)
763 onLauncherApplicationSelected: {
764 if (greeter.hasLockedApp) {
765 greeter.startUnlock()
767 if (!edgeDemo.running)
768 shell.activateApplication(appId)
772 panel.indicators.hide()
780 background: shell.background
784 id: modalNotificationBackground
786 visible: notifications.useModal && (notifications.state == "narrow")
799 model: NotificationBackend.Model
802 y: topmostIsFullscreen ? 0 : panel.panelHeight
803 height: parent.height - (topmostIsFullscreen ? 0 : panel.panelHeight)
808 when: overlay.width <= units.gu(60)
810 target: notifications
811 anchors.left: parent.left
812 anchors.right: parent.right
817 when: overlay.width > units.gu(60)
819 target: notifications
820 anchors.left: undefined
821 anchors.right: parent.right
823 PropertyChanges { target: notifications; width: units.gu(38) }
834 shutdownFadeOutRectangle.enabled = true;
835 shutdownFadeOutRectangle.visible = true;
836 shutdownFadeOut.start();
842 objectName: "edgeDemo"
844 paused: Powerd.status === Powerd.Off || wizard.active // Saves power
852 target: SessionBroadcast
853 onShowHome: showHome()
857 id: shutdownFadeOutRectangle
864 NumberAnimation on opacity {
869 if (shutdownFadeOutRectangle.enabled && shutdownFadeOutRectangle.visible) {
870 DBusUnitySessionService.Shutdown();
keysesc to close
- j k
- move down, up
- enter
- open
- esc
- back, or up a level
- 1 2 3 4
- readme, files, commits, refs
- /
- filter
- y
- copy the clone command
- c
- compare the chosen commit with another (or shift-click both)
- ?
- show or hide this list