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 target: LauncherModel
134 property: "applicationManager"
135 value: ApplicationManager
138 Component.onCompleted: {
139 Theme.name = "Ubuntu.Components.Themes.SuruGradient"
140 if (ApplicationManager.count > 0) {
141 ApplicationManager.focusApplication(ApplicationManager.get(0).appId);
143 if (orientationLockEnabled) {
144 orientation = OrientationLock.savedOrientation;
154 objectName: "dashCommunicator"
158 id: physicalKeysFilter
160 onPowerPressed: dialogs.onPowerKeyPressed();
161 onVolumeDownPressed: volumeControl.volumeDown()
162 onVolumeUpPressed: volumeControl.volumeUp()
163 onScreenshotPressed: screenGrabber.capture()
169 enabled: Powerd.status === Powerd.On
173 target: ApplicationManager
174 property: "forceDashActive"
175 value: launcher.shown || launcher.dashSwipe
180 event.accepted = physicalKeysFilter.onKeyPressed(event.key);
184 event.accepted = physicalKeysFilter.onKeyReleased(event.key);
192 height: parent.height
193 visible: !ApplicationManager.empty
196 target: ApplicationManager
198 if (greeter.narrowMode) {
199 if (appId === "dialer-app" && callManager.hasCalls && shell.locked) {
200 // If we are in the middle of a call, make dialer lockedApp and show it.
201 // This can happen if user backs out of dialer back to greeter, then
202 // launches dialer again.
203 greeter.lockedApp = appId;
205 if (greeter.hasLockedApp) {
206 if (appId === greeter.lockedApp) {
207 lockscreen.hide() // show locked app
209 greeter.startUnlock() // show lockscreen if necessary
214 if (LightDM.Greeter.active) {
215 greeter.startUnlock()
220 onFocusedApplicationIdChanged: {
221 if (greeter.hasLockedApp && greeter.lockedApp !== ApplicationManager.focusedApplicationId) {
222 greeter.startUnlock()
224 panel.indicators.hide();
227 onApplicationAdded: {
228 if (greeter.shown && appId != "unity8-dash") {
229 greeter.startUnlock()
231 // If this happens on first boot, we may be in edge
232 // tutorial or wizard while receiving a call. But a call
233 // is more important than wizard so just bail out of those.
234 if (edgeDemo.running) {
235 edgeDemo.hideEdgeDemos();
239 if (greeter.narrowMode && greeter.hasLockedApp && appId === greeter.lockedApp) {
240 lockscreen.hide() // show locked app
247 id: applicationsDisplayLoader
248 objectName: "applicationsDisplayLoader"
251 // When we have a locked app, we only want to show that one app.
252 // FIXME: do this in a less traumatic way. We currently only allow
253 // locked apps in phone mode (see FIXME in Lockscreen component in
254 // this same file). When that changes, we need to do something
255 // nicer here. But this code is currently just to prevent a
256 // theoretical attack where user enters lockedApp mode, then makes
257 // the screen larger (maybe connects to monitor) and tries to enter
259 property bool tabletMode: shell.sideStageEnabled && !greeter.hasLockedApp
260 source: tabletMode ? "Stages/TabletStage.qml" : "Stages/PhoneStage.qml"
263 target: applicationsDisplayLoader.item
264 property: "objectName"
268 target: applicationsDisplayLoader.item
269 property: "dragAreaWidth"
270 value: shell.edgeSize
273 target: applicationsDisplayLoader.item
274 property: "maximizedAppTopMargin"
275 // Not just using panel.panelHeight as that changes depending on the focused app.
276 value: panel.indicators.minimizedPanelHeight + units.dp(2) // dp(2) for orange line
279 target: applicationsDisplayLoader.item
280 property: "interactive"
281 value: edgeDemo.stagesEnabled && !greeter.shown && !lockscreen.shown && panel.indicators.fullyClosed && launcher.progress == 0 && !notifications.useModal
284 target: applicationsDisplayLoader.item
285 property: "spreadEnabled"
286 value: edgeDemo.stagesEnabled && !greeter.hasLockedApp
289 target: applicationsDisplayLoader.item
290 property: "inverseProgress"
291 value: launcher.progress
294 target: applicationsDisplayLoader.item
295 property: "orientation"
296 value: shell.orientation
303 objectName: "inputMethod"
304 anchors { fill: parent; topMargin: panel.panelHeight }
305 z: notifications.useModal || panel.indicators.shown ? overlay.z + 1 : overlay.z - 1
309 target: SurfaceManager
311 if (surface.type == MirSurfaceItem.InputMethod) {
312 inputMethod.surface = surface;
316 onSurfaceDestroyed: {
317 if (inputMethod.surface == surface) {
318 inputMethod.surface = null;
319 surface.parent = null;
321 if (!surface.parent) {
322 // there's no one displaying it. delete it right away
328 target: SessionManager
330 if (!session.parentSession && !session.application) {
331 // nothing is using it. delete it right away
339 objectName: "lockscreen"
341 hides: [launcher, panel.indicators]
344 showAnimation: StandardAnimation { property: "opacity"; to: 1 }
345 hideAnimation: StandardAnimation { property: "opacity"; to: 0 }
349 height: parent.height - panel.panelHeight
350 background: shell.background
351 darkenBackground: 0.4
352 alphaNumeric: AccountsService.passwordDisplayHint === AccountsService.Keyboard
356 property string promptText
357 infoText: promptText !== "" ? i18n.tr("Enter %1").arg(promptText) :
358 alphaNumeric ? i18n.tr("Enter passphrase") :
359 i18n.tr("Enter passcode")
360 errorText: promptText !== "" ? i18n.tr("Sorry, incorrect %1").arg(promptText) :
361 alphaNumeric ? i18n.tr("Sorry, incorrect passphrase") + "\n" +
362 i18n.tr("Please re-enter") :
363 i18n.tr("Sorry, incorrect passcode")
365 // FIXME: We *should* show emergency dialer if there is a SIM present,
366 // regardless of whether the side stage is enabled. But right now,
367 // the assumption is that narrow screens are phones which have SIMs
368 // and wider screens are tablets which don't. When we do allow this
369 // on devices with a side stage and a SIM, work should be done to
370 // ensure that the main stage is disabled while the dialer is present
371 // in the side stage. See the FIXME in the stage loader in this file.
372 showEmergencyCallButton: !shell.sideStageEnabled
374 onEntered: LightDM.Greeter.respond(passphrase);
375 onCancel: greeter.show()
376 onEmergencyCall: startLockedApp("dialer-app")
378 onShownChanged: if (shown) greeter.lockedApp = ""
380 function maybeShow() {
381 if (!shell.forcedUnlock) {
390 if (lockscreen.delayMinutes > 0) {
391 lockscreen.delayMinutes -= 1
392 if (lockscreen.delayMinutes > 0) {
399 Component.onCompleted: {
400 if (greeter.narrowMode) {
401 LightDM.Greeter.authenticate(LightDM.Users.data(0, LightDM.UserRoles.NameRole))
407 target: LightDM.Greeter
409 onShowGreeter: greeter.show()
410 onHideGreeter: greeter.login()
413 shell.enabled = true;
414 if (!LightDM.Greeter.active) {
415 return; // could happen if hideGreeter() comes in before we prompt
417 if (greeter.narrowMode) {
418 lockscreen.promptText = isDefaultPrompt ? "" : text.toLowerCase();
419 lockscreen.maybeShow();
423 onPromptlessChanged: {
424 if (!LightDM.Greeter.active) {
425 return; // could happen if hideGreeter() comes in before we prompt
427 if (greeter.narrowMode) {
428 if (LightDM.Greeter.promptless && LightDM.Greeter.authenticated) {
432 lockscreen.maybeShow();
437 onAuthenticationComplete: {
438 shell.enabled = true;
439 if (LightDM.Greeter.authenticated) {
440 AccountsService.failedLogins = 0
442 // Else only penalize user for a failed login if they actually were
443 // prompted for a password. We do this below after the promptless
446 if (LightDM.Greeter.promptless) {
450 if (LightDM.Greeter.authenticated) {
453 AccountsService.failedLogins++
454 if (maxFailedLogins >= 2) { // require at least a warning
455 if (AccountsService.failedLogins === maxFailedLogins - 1) {
456 var title = lockscreen.alphaNumeric ?
457 i18n.tr("Sorry, incorrect passphrase.") :
458 i18n.tr("Sorry, incorrect passcode.")
459 var text = i18n.tr("This will be your last attempt.") + " " +
460 (lockscreen.alphaNumeric ?
461 i18n.tr("If passphrase is entered incorrectly, your phone will conduct a factory reset and all personal data will be deleted.") :
462 i18n.tr("If passcode is entered incorrectly, your phone will conduct a factory reset and all personal data will be deleted."))
463 lockscreen.showInfoPopup(title, text)
464 } else if (AccountsService.failedLogins >= maxFailedLogins) {
465 SystemImage.factoryReset() // Ouch!
468 if (failedLoginsDelayAttempts > 0 && AccountsService.failedLogins % failedLoginsDelayAttempts == 0) {
469 lockscreen.delayMinutes = failedLoginsDelayMinutes
470 forcedDelayTimer.start()
473 lockscreen.clear(true);
474 if (greeter.narrowMode) {
475 LightDM.Greeter.authenticate(LightDM.Users.data(0, LightDM.UserRoles.NameRole))
482 target: LightDM.Greeter
484 value: greeter.shown || lockscreen.shown || greeter.hasLockedApp
490 opacity: greeterWrapper.showProgress * 0.8
494 // Just a tiny wrapper to adjust greeter's x without messing with its own dragging
496 objectName: "greeterWrapper"
497 x: (greeter.narrowMode && greeter.showProgress > 0) ? launcher.progress : 0
500 height: parent.height - panel.panelHeight
503 enabled: !launcher.dashSwipe
507 property bool fullyShown: showProgress === 1.0
508 onFullyShownChanged: {
509 // Wait until the greeter is completely covering lockscreen before resetting it.
510 if (greeter.narrowMode && fullyShown && !LightDM.Greeter.authenticated) {
512 lockscreen.maybeShow();
516 readonly property real showProgress: MathUtils.clamp((1 - x/width) + greeter.showProgress - 1, 0, 1)
517 onShowProgressChanged: {
518 if (showProgress === 0) {
519 if ((LightDM.Greeter.promptless && LightDM.Greeter.authenticated) || shell.forcedUnlock) {
521 } else if (greeter.narrowMode) {
522 lockscreen.clear(false) // to reset focus if necessary
529 objectName: "greeter"
531 signal sessionStarted() // helpful for tests
533 property string lockedApp: ""
534 property bool hasLockedApp: lockedApp !== ""
537 hides: [launcher, panel.indicators]
539 loadContent: required || lockscreen.required // keeps content in memory for quick show()
543 background: shell.background
546 height: parent.height
548 dragHandleWidth: shell.edgeSize
550 function startUnlock() {
552 if (!LightDM.Greeter.authenticated) {
553 lockscreen.maybeShow()
564 if (LightDM.Greeter.startSessionSync()) {
575 // Disable everything so that user can't swipe greeter or
576 // launcher until we get the next prompt/authenticate, which
577 // will re-enable the shell.
578 shell.enabled = false;
580 if (greeter.narrowMode) {
581 LightDM.Greeter.authenticate(LightDM.Users.data(0, LightDM.UserRoles.NameRole));
585 greeter.lockedApp = "";
586 greeter.forceActiveFocus();
590 Component.onCompleted: {
591 Connectivity.unlockAllModems()
594 onUnlocked: greeter.hide()
596 // Update launcher items for new user
597 var user = LightDM.Users.data(uid, LightDM.UserRoles.NameRole);
598 AccountsService.user = user;
599 LauncherModel.setUser(user);
602 onTease: launcher.tease()
605 target: ApplicationManager
606 property: "suspended"
607 value: (greeter.shown && greeterWrapper.showProgress == 1) || lockscreen.shown
617 if (shell.locked && callManager.hasCalls) {
618 // We just received an incoming call while locked. The
619 // indicator will have already launched dialer-app for us, but
620 // there is a race between "hasCalls" changing and the dialer
621 // starting up. So in case we lose that race, we'll start/
622 // focus the dialer ourselves here too. Even if the indicator
623 // didn't launch the dialer for some reason (or maybe a call
624 // started via some other means), if an active call is
625 // happening, we want to be in the dialer.
626 startLockedApp("dialer-app")
636 if (Powerd.status === Powerd.Off && reason !== Powerd.Proximity &&
637 !callManager.hasCalls && !edgeDemo.running) {
643 function showHome() {
644 if (edgeDemo.running) {
648 if (LightDM.Greeter.active) {
649 greeter.startUnlock()
652 var animate = !LightDM.Greeter.active && !stages.shown
653 dash.setCurrentScope(0, animate, false)
654 ApplicationManager.requestFocusApplication("unity8-dash")
657 function showDash() {
658 if (greeter.hasLockedApp || // just in case user gets here
659 (!greeter.narrowMode && shell.locked)) {
668 if (ApplicationManager.focusedApplicationId != "unity8-dash") {
669 ApplicationManager.requestFocusApplication("unity8-dash")
683 anchors.fill: parent //because this draws indicator menus
686 available: edgeDemo.panelEnabled && (!shell.locked || AccountsService.enableIndicatorsWhileLocked) && !greeter.hasLockedApp
687 contentEnabled: edgeDemo.panelContentEnabled
688 width: parent.width > units.gu(60) ? units.gu(40) : parent.width
690 minimizedPanelHeight: units.gu(3)
691 expandedPanelHeight: units.gu(7)
693 indicatorsModel: visibleIndicators.model
697 id: visibleIndicators
698 // TODO: This should be sourced by device type (eg "desktop", "tablet", "phone"...)
699 Component.onCompleted: initialise(indicatorProfile)
702 greeterShown: greeter.shown || lockscreen.shown
705 property bool topmostApplicationIsFullscreen:
706 ApplicationManager.focusedApplicationId &&
707 ApplicationManager.findApplication(ApplicationManager.focusedApplicationId).fullscreen
709 fullscreenMode: (topmostApplicationIsFullscreen && !LightDM.Greeter.active && launcher.progress == 0)
710 || greeter.hasLockedApp
715 objectName: "launcher"
717 readonly property bool dashSwipe: progress > 0
719 anchors.top: parent.top
720 anchors.bottom: parent.bottom
722 dragAreaWidth: shell.edgeSize
723 available: edgeDemo.launcherEnabled && (!shell.locked || AccountsService.enableLauncherWhileLocked) && !greeter.hasLockedApp
725 onShowDashHome: showHome()
727 onDashSwipeChanged: {
729 dash.setCurrentScope(0, false, true)
732 onLauncherApplicationSelected: {
733 if (greeter.hasLockedApp) {
734 greeter.startUnlock()
736 if (!edgeDemo.running)
737 shell.activateApplication(appId)
741 panel.indicators.hide()
749 background: shell.background
753 id: modalNotificationBackground
755 visible: notifications.useModal && (notifications.state == "narrow")
768 model: NotificationBackend.Model
771 y: topmostIsFullscreen ? 0 : panel.panelHeight
772 height: parent.height - (topmostIsFullscreen ? 0 : panel.panelHeight)
777 when: overlay.width <= units.gu(60)
779 target: notifications
780 anchors.left: parent.left
781 anchors.right: parent.right
786 when: overlay.width > units.gu(60)
788 target: notifications
789 anchors.left: undefined
790 anchors.right: parent.right
792 PropertyChanges { target: notifications; width: units.gu(38) }
803 shutdownFadeOutRectangle.enabled = true;
804 shutdownFadeOutRectangle.visible = true;
805 shutdownFadeOut.start();
811 objectName: "edgeDemo"
813 paused: Powerd.status === Powerd.Off || wizard.active // Saves power
821 target: SessionBroadcast
822 onShowHome: showHome()
826 id: shutdownFadeOutRectangle
833 NumberAnimation on opacity {
838 if (shutdownFadeOutRectangle.enabled && shutdownFadeOutRectangle.visible) {
839 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