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"
160 enabled: Powerd.status === Powerd.On
164 target: ApplicationManager
165 property: "forceDashActive"
166 value: launcher.shown || launcher.dashSwipe
171 onVolumeDownPressed: volumeControl.volumeDown()
172 onVolumeUpPressed: volumeControl.volumeUp()
173 onBothVolumeKeysPressed: screenGrabber.capture()
178 if (event.key == Qt.Key_PowerOff || event.key == Qt.Key_PowerDown) {
179 dialogs.onPowerKeyPressed();
180 event.accepted = true;
182 volumeKeyFilter.onKeyPressed(event.key);
183 event.accepted = false;
188 if (event.key == Qt.Key_PowerOff || event.key == Qt.Key_PowerDown) {
189 dialogs.onPowerKeyReleased();
190 event.accepted = true;
192 volumeKeyFilter.onKeyReleased(event.key);
193 event.accepted = false;
202 height: parent.height
203 visible: !ApplicationManager.empty
206 target: ApplicationManager
208 if (greeter.narrowMode) {
209 if (appId === "dialer-app" && callManager.hasCalls && shell.locked) {
210 // If we are in the middle of a call, make dialer lockedApp and show it.
211 // This can happen if user backs out of dialer back to greeter, then
212 // launches dialer again.
213 greeter.lockedApp = appId;
215 if (greeter.hasLockedApp) {
216 if (appId === greeter.lockedApp) {
217 lockscreen.hide() // show locked app
219 greeter.startUnlock() // show lockscreen if necessary
224 if (LightDM.Greeter.active) {
225 greeter.startUnlock()
230 onFocusedApplicationIdChanged: {
231 if (greeter.hasLockedApp && greeter.lockedApp !== ApplicationManager.focusedApplicationId) {
232 greeter.startUnlock()
234 panel.indicators.hide();
237 onApplicationAdded: {
238 if (greeter.shown && appId != "unity8-dash") {
239 greeter.startUnlock()
241 // If this happens on first boot, we may be in edge
242 // tutorial or wizard while receiving a call. But a call
243 // is more important than wizard so just bail out of those.
244 if (edgeDemo.running) {
245 edgeDemo.hideEdgeDemos();
249 if (greeter.narrowMode && greeter.hasLockedApp && appId === greeter.lockedApp) {
250 lockscreen.hide() // show locked app
257 id: applicationsDisplayLoader
258 objectName: "applicationsDisplayLoader"
261 // When we have a locked app, we only want to show that one app.
262 // FIXME: do this in a less traumatic way. We currently only allow
263 // locked apps in phone mode (see FIXME in Lockscreen component in
264 // this same file). When that changes, we need to do something
265 // nicer here. But this code is currently just to prevent a
266 // theoretical attack where user enters lockedApp mode, then makes
267 // the screen larger (maybe connects to monitor) and tries to enter
269 property bool tabletMode: shell.sideStageEnabled && !greeter.hasLockedApp
270 source: tabletMode ? "Stages/TabletStage.qml" : "Stages/PhoneStage.qml"
273 target: applicationsDisplayLoader.item
274 property: "objectName"
278 target: applicationsDisplayLoader.item
279 property: "dragAreaWidth"
280 value: shell.edgeSize
283 target: applicationsDisplayLoader.item
284 property: "maximizedAppTopMargin"
285 // Not just using panel.panelHeight as that changes depending on the focused app.
286 value: panel.indicators.minimizedPanelHeight + units.dp(2) // dp(2) for orange line
289 target: applicationsDisplayLoader.item
290 property: "interactive"
291 value: edgeDemo.stagesEnabled && !greeter.shown && !lockscreen.shown && panel.indicators.fullyClosed && launcher.progress == 0 && !notifications.useModal
294 target: applicationsDisplayLoader.item
295 property: "spreadEnabled"
296 value: edgeDemo.stagesEnabled && !greeter.hasLockedApp
299 target: applicationsDisplayLoader.item
300 property: "inverseProgress"
301 value: launcher.progress
304 target: applicationsDisplayLoader.item
305 property: "orientation"
306 value: shell.orientation
313 objectName: "inputMethod"
314 anchors { fill: parent; topMargin: panel.panelHeight }
315 z: notifications.useModal || panel.indicators.shown ? overlay.z + 1 : overlay.z - 1
319 target: SurfaceManager
321 if (surface.type == MirSurfaceItem.InputMethod) {
322 inputMethod.surface = surface;
326 onSurfaceDestroyed: {
327 if (inputMethod.surface == surface) {
328 inputMethod.surface = null;
329 surface.parent = null;
331 if (!surface.parent) {
332 // there's no one displaying it. delete it right away
338 target: SessionManager
340 if (!session.parentSession && !session.application) {
341 // nothing is using it. delete it right away
349 objectName: "lockscreen"
351 hides: [launcher, panel.indicators]
354 showAnimation: StandardAnimation { property: "opacity"; to: 1 }
355 hideAnimation: StandardAnimation { property: "opacity"; to: 0 }
359 height: parent.height - panel.panelHeight
360 background: shell.background
361 darkenBackground: 0.4
362 alphaNumeric: AccountsService.passwordDisplayHint === AccountsService.Keyboard
366 property string promptText
367 infoText: promptText !== "" ? i18n.tr("Enter %1").arg(promptText) :
368 alphaNumeric ? i18n.tr("Enter passphrase") :
369 i18n.tr("Enter passcode")
370 errorText: promptText !== "" ? i18n.tr("Sorry, incorrect %1").arg(promptText) :
371 alphaNumeric ? i18n.tr("Sorry, incorrect passphrase") + "\n" +
372 i18n.tr("Please re-enter") :
373 i18n.tr("Sorry, incorrect passcode")
375 // FIXME: We *should* show emergency dialer if there is a SIM present,
376 // regardless of whether the side stage is enabled. But right now,
377 // the assumption is that narrow screens are phones which have SIMs
378 // and wider screens are tablets which don't. When we do allow this
379 // on devices with a side stage and a SIM, work should be done to
380 // ensure that the main stage is disabled while the dialer is present
381 // in the side stage. See the FIXME in the stage loader in this file.
382 showEmergencyCallButton: !shell.sideStageEnabled
384 onEntered: LightDM.Greeter.respond(passphrase);
385 onCancel: greeter.show()
386 onEmergencyCall: startLockedApp("dialer-app")
388 onShownChanged: if (shown) greeter.lockedApp = ""
390 function maybeShow() {
391 if (!shell.forcedUnlock) {
400 if (lockscreen.delayMinutes > 0) {
401 lockscreen.delayMinutes -= 1
402 if (lockscreen.delayMinutes > 0) {
409 Component.onCompleted: {
410 if (greeter.narrowMode) {
411 LightDM.Greeter.authenticate(LightDM.Users.data(0, LightDM.UserRoles.NameRole))
417 target: LightDM.Greeter
419 onShowGreeter: greeter.show()
420 onHideGreeter: greeter.login()
423 shell.enabled = true;
424 if (!LightDM.Greeter.active) {
425 return; // could happen if hideGreeter() comes in before we prompt
427 if (greeter.narrowMode) {
428 lockscreen.promptText = isDefaultPrompt ? "" : text.toLowerCase();
429 lockscreen.maybeShow();
433 onPromptlessChanged: {
434 if (!LightDM.Greeter.active) {
435 return; // could happen if hideGreeter() comes in before we prompt
437 if (greeter.narrowMode) {
438 if (LightDM.Greeter.promptless && LightDM.Greeter.authenticated) {
442 lockscreen.maybeShow();
447 onAuthenticationComplete: {
448 shell.enabled = true;
449 if (LightDM.Greeter.authenticated) {
450 AccountsService.failedLogins = 0
452 // Else only penalize user for a failed login if they actually were
453 // prompted for a password. We do this below after the promptless
456 if (LightDM.Greeter.promptless) {
460 if (LightDM.Greeter.authenticated) {
463 AccountsService.failedLogins++
464 if (maxFailedLogins >= 2) { // require at least a warning
465 if (AccountsService.failedLogins === maxFailedLogins - 1) {
466 var title = lockscreen.alphaNumeric ?
467 i18n.tr("Sorry, incorrect passphrase.") :
468 i18n.tr("Sorry, incorrect passcode.")
469 var text = i18n.tr("This will be your last attempt.") + " " +
470 (lockscreen.alphaNumeric ?
471 i18n.tr("If passphrase is entered incorrectly, your phone will conduct a factory reset and all personal data will be deleted.") :
472 i18n.tr("If passcode is entered incorrectly, your phone will conduct a factory reset and all personal data will be deleted."))
473 lockscreen.showInfoPopup(title, text)
474 } else if (AccountsService.failedLogins >= maxFailedLogins) {
475 SystemImage.factoryReset() // Ouch!
478 if (failedLoginsDelayAttempts > 0 && AccountsService.failedLogins % failedLoginsDelayAttempts == 0) {
479 lockscreen.delayMinutes = failedLoginsDelayMinutes
480 forcedDelayTimer.start()
483 lockscreen.clear(true);
484 if (greeter.narrowMode) {
485 LightDM.Greeter.authenticate(LightDM.Users.data(0, LightDM.UserRoles.NameRole))
492 target: LightDM.Greeter
494 value: greeter.shown || lockscreen.shown || greeter.hasLockedApp
500 opacity: greeterWrapper.showProgress * 0.8
504 // Just a tiny wrapper to adjust greeter's x without messing with its own dragging
506 objectName: "greeterWrapper"
507 x: (greeter.narrowMode && greeter.showProgress > 0) ? launcher.progress : 0
510 height: parent.height - panel.panelHeight
513 enabled: !launcher.dashSwipe
517 property bool fullyShown: showProgress === 1.0
518 onFullyShownChanged: {
519 // Wait until the greeter is completely covering lockscreen before resetting it.
520 if (greeter.narrowMode && fullyShown && !LightDM.Greeter.authenticated) {
522 lockscreen.maybeShow();
526 readonly property real showProgress: MathUtils.clamp((1 - x/width) + greeter.showProgress - 1, 0, 1)
527 onShowProgressChanged: {
528 if (showProgress === 0) {
529 if ((LightDM.Greeter.promptless && LightDM.Greeter.authenticated) || shell.forcedUnlock) {
531 } else if (greeter.narrowMode) {
532 lockscreen.clear(false) // to reset focus if necessary
539 objectName: "greeter"
541 signal sessionStarted() // helpful for tests
543 property string lockedApp: ""
544 property bool hasLockedApp: lockedApp !== ""
548 background: shell.background
551 height: parent.height
554 // avoid overlapping with Launcher's edge drag area
555 // FIXME: Fix TouchRegistry & friends and remove this workaround
556 // Issue involves launcher's DDA getting disabled on a long
558 dragHandleLeftMargin: launcher.dragAreaWidth + 1
560 function startUnlock() {
562 if (!LightDM.Greeter.authenticated) {
563 lockscreen.maybeShow()
574 if (LightDM.Greeter.startSessionSync()) {
586 panel.indicators.hide();
588 // Disable everything so that user can't swipe greeter or
589 // launcher until we get the next prompt/authenticate, which
590 // will re-enable the shell.
591 shell.enabled = false;
593 if (greeter.narrowMode) {
594 LightDM.Greeter.authenticate(LightDM.Users.data(0, LightDM.UserRoles.NameRole));
598 greeter.lockedApp = "";
599 greeter.forceActiveFocus();
603 Component.onCompleted: {
604 Connectivity.unlockAllModems()
607 onUnlocked: greeter.hide()
609 // Update launcher items for new user
610 var user = LightDM.Users.data(uid, LightDM.UserRoles.NameRole);
611 AccountsService.user = user;
612 LauncherModel.setUser(user);
615 onTapped: launcher.tease()
618 target: ApplicationManager
619 property: "suspended"
620 value: (greeter.shown && greeterWrapper.showProgress == 1) || lockscreen.shown
630 if (shell.locked && callManager.hasCalls) {
631 // We just received an incoming call while locked. The
632 // indicator will have already launched dialer-app for us, but
633 // there is a race between "hasCalls" changing and the dialer
634 // starting up. So in case we lose that race, we'll start/
635 // focus the dialer ourselves here too. Even if the indicator
636 // didn't launch the dialer for some reason (or maybe a call
637 // started via some other means), if an active call is
638 // happening, we want to be in the dialer.
639 startLockedApp("dialer-app")
649 if (Powerd.status === Powerd.Off && reason !== Powerd.Proximity &&
650 !callManager.hasCalls && !edgeDemo.running) {
656 function showHome() {
657 if (edgeDemo.running) {
661 if (LightDM.Greeter.active) {
662 greeter.startUnlock()
665 var animate = !LightDM.Greeter.active && !stages.shown
666 dash.setCurrentScope(0, animate, false)
667 ApplicationManager.requestFocusApplication("unity8-dash")
670 function showDash() {
671 if (greeter.hasLockedApp || // just in case user gets here
672 (!greeter.narrowMode && shell.locked)) {
681 if (ApplicationManager.focusedApplicationId != "unity8-dash") {
682 ApplicationManager.requestFocusApplication("unity8-dash")
696 anchors.fill: parent //because this draws indicator menus
699 available: edgeDemo.panelEnabled && (!shell.locked || AccountsService.enableIndicatorsWhileLocked) && !greeter.hasLockedApp
700 contentEnabled: edgeDemo.panelContentEnabled
701 width: parent.width > units.gu(60) ? units.gu(40) : parent.width
703 minimizedPanelHeight: units.gu(3)
704 expandedPanelHeight: units.gu(7)
706 indicatorsModel: visibleIndicators.model
710 id: visibleIndicators
711 // TODO: This should be sourced by device type (eg "desktop", "tablet", "phone"...)
712 Component.onCompleted: initialise(indicatorProfile)
715 greeterShown: greeter.shown || lockscreen.shown
718 property bool topmostApplicationIsFullscreen:
719 ApplicationManager.focusedApplicationId &&
720 ApplicationManager.findApplication(ApplicationManager.focusedApplicationId).fullscreen
722 fullscreenMode: (topmostApplicationIsFullscreen && !LightDM.Greeter.active && launcher.progress == 0)
723 || greeter.hasLockedApp
728 objectName: "launcher"
730 readonly property bool dashSwipe: progress > 0
732 anchors.top: parent.top
733 anchors.bottom: parent.bottom
735 dragAreaWidth: shell.edgeSize
736 available: edgeDemo.launcherEnabled && (!shell.locked || AccountsService.enableLauncherWhileLocked) && !greeter.hasLockedApp
738 onShowDashHome: showHome()
740 onDashSwipeChanged: {
742 dash.setCurrentScope(0, false, true)
745 onLauncherApplicationSelected: {
746 if (greeter.hasLockedApp) {
747 greeter.startUnlock()
749 if (!edgeDemo.running)
750 shell.activateApplication(appId)
754 panel.indicators.hide()
762 background: shell.background
766 id: modalNotificationBackground
768 visible: notifications.useModal && (notifications.state == "narrow")
781 model: NotificationBackend.Model
784 y: topmostIsFullscreen ? 0 : panel.panelHeight
785 height: parent.height - (topmostIsFullscreen ? 0 : panel.panelHeight)
790 when: overlay.width <= units.gu(60)
792 target: notifications
793 anchors.left: parent.left
794 anchors.right: parent.right
799 when: overlay.width > units.gu(60)
801 target: notifications
802 anchors.left: undefined
803 anchors.right: parent.right
805 PropertyChanges { target: notifications; width: units.gu(38) }
816 shutdownFadeOutRectangle.enabled = true;
817 shutdownFadeOutRectangle.visible = true;
818 shutdownFadeOut.start();
824 objectName: "edgeDemo"
826 paused: Powerd.status === Powerd.Off || wizard.active // Saves power
834 target: SessionBroadcast
835 onShowHome: showHome()
839 id: shutdownFadeOutRectangle
846 NumberAnimation on opacity {
851 if (shutdownFadeOutRectangle.enabled && shutdownFadeOutRectangle.visible) {
852 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