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: physicalKeysMapper
160 onVolumeDownPressed: volumeControl.volumeDown()
161 onVolumeUpPressed: volumeControl.volumeUp()
162 onScreenshotPressed: screenGrabber.capture()
168 enabled: Powerd.status === Powerd.On
172 target: ApplicationManager
173 property: "forceDashActive"
174 value: launcher.shown || launcher.dashSwipe
179 physicalKeysMapper.onKeyPressed(event.key);
180 if (event.key == Qt.Key_PowerOff || event.key == Qt.Key_PowerDown) {
181 event.accepted = true;
183 event.accepted = false;
188 physicalKeysMapper.onKeyReleased(event.key);
189 if (event.key = Qt.Key_PowerOff || event.key == Qt.Key_PowerDown) {
190 event.accepted = true;
192 event.accepted = false;
201 height: parent.height
202 visible: !ApplicationManager.empty
205 target: ApplicationManager
207 if (greeter.narrowMode) {
208 if (appId === "dialer-app" && callManager.hasCalls && shell.locked) {
209 // If we are in the middle of a call, make dialer lockedApp and show it.
210 // This can happen if user backs out of dialer back to greeter, then
211 // launches dialer again.
212 greeter.lockedApp = appId;
214 if (greeter.hasLockedApp) {
215 if (appId === greeter.lockedApp) {
216 lockscreen.hide() // show locked app
218 greeter.startUnlock() // show lockscreen if necessary
223 if (LightDM.Greeter.active) {
224 greeter.startUnlock()
229 onFocusedApplicationIdChanged: {
230 if (greeter.hasLockedApp && greeter.lockedApp !== ApplicationManager.focusedApplicationId) {
231 greeter.startUnlock()
233 panel.indicators.hide();
236 onApplicationAdded: {
237 if (greeter.shown && appId != "unity8-dash") {
238 greeter.startUnlock()
240 // If this happens on first boot, we may be in edge
241 // tutorial or wizard while receiving a call. But a call
242 // is more important than wizard so just bail out of those.
243 if (edgeDemo.running) {
244 edgeDemo.hideEdgeDemos();
248 if (greeter.narrowMode && greeter.hasLockedApp && appId === greeter.lockedApp) {
249 lockscreen.hide() // show locked app
256 id: applicationsDisplayLoader
257 objectName: "applicationsDisplayLoader"
260 // When we have a locked app, we only want to show that one app.
261 // FIXME: do this in a less traumatic way. We currently only allow
262 // locked apps in phone mode (see FIXME in Lockscreen component in
263 // this same file). When that changes, we need to do something
264 // nicer here. But this code is currently just to prevent a
265 // theoretical attack where user enters lockedApp mode, then makes
266 // the screen larger (maybe connects to monitor) and tries to enter
268 property bool tabletMode: shell.sideStageEnabled && !greeter.hasLockedApp
269 source: tabletMode ? "Stages/TabletStage.qml" : "Stages/PhoneStage.qml"
272 target: applicationsDisplayLoader.item
273 property: "objectName"
277 target: applicationsDisplayLoader.item
278 property: "dragAreaWidth"
279 value: shell.edgeSize
282 target: applicationsDisplayLoader.item
283 property: "maximizedAppTopMargin"
284 // Not just using panel.panelHeight as that changes depending on the focused app.
285 value: panel.indicators.minimizedPanelHeight + units.dp(2) // dp(2) for orange line
288 target: applicationsDisplayLoader.item
289 property: "interactive"
290 value: edgeDemo.stagesEnabled && !greeter.shown && !lockscreen.shown && panel.indicators.fullyClosed && launcher.progress == 0 && !notifications.useModal
293 target: applicationsDisplayLoader.item
294 property: "spreadEnabled"
295 value: edgeDemo.stagesEnabled && !greeter.hasLockedApp
298 target: applicationsDisplayLoader.item
299 property: "inverseProgress"
300 value: launcher.progress
303 target: applicationsDisplayLoader.item
304 property: "orientation"
305 value: shell.orientation
312 objectName: "inputMethod"
313 anchors { fill: parent; topMargin: panel.panelHeight }
314 z: notifications.useModal || panel.indicators.shown ? overlay.z + 1 : overlay.z - 1
318 target: SurfaceManager
320 if (surface.type == MirSurfaceItem.InputMethod) {
321 inputMethod.surface = surface;
325 onSurfaceDestroyed: {
326 if (inputMethod.surface == surface) {
327 inputMethod.surface = null;
328 surface.parent = null;
330 if (!surface.parent) {
331 // there's no one displaying it. delete it right away
337 target: SessionManager
339 if (!session.parentSession && !session.application) {
340 // nothing is using it. delete it right away
348 objectName: "lockscreen"
350 hides: [launcher, panel.indicators]
353 showAnimation: StandardAnimation { property: "opacity"; to: 1 }
354 hideAnimation: StandardAnimation { property: "opacity"; to: 0 }
358 height: parent.height - panel.panelHeight
359 background: shell.background
360 darkenBackground: 0.4
361 alphaNumeric: AccountsService.passwordDisplayHint === AccountsService.Keyboard
365 property string promptText
366 infoText: promptText !== "" ? i18n.tr("Enter %1").arg(promptText) :
367 alphaNumeric ? i18n.tr("Enter passphrase") :
368 i18n.tr("Enter passcode")
369 errorText: promptText !== "" ? i18n.tr("Sorry, incorrect %1").arg(promptText) :
370 alphaNumeric ? i18n.tr("Sorry, incorrect passphrase") + "\n" +
371 i18n.tr("Please re-enter") :
372 i18n.tr("Sorry, incorrect passcode")
374 // FIXME: We *should* show emergency dialer if there is a SIM present,
375 // regardless of whether the side stage is enabled. But right now,
376 // the assumption is that narrow screens are phones which have SIMs
377 // and wider screens are tablets which don't. When we do allow this
378 // on devices with a side stage and a SIM, work should be done to
379 // ensure that the main stage is disabled while the dialer is present
380 // in the side stage. See the FIXME in the stage loader in this file.
381 showEmergencyCallButton: !shell.sideStageEnabled
383 onEntered: LightDM.Greeter.respond(passphrase);
384 onCancel: greeter.show()
385 onEmergencyCall: startLockedApp("dialer-app")
387 onShownChanged: if (shown) greeter.lockedApp = ""
389 function maybeShow() {
390 if (!shell.forcedUnlock) {
399 if (lockscreen.delayMinutes > 0) {
400 lockscreen.delayMinutes -= 1
401 if (lockscreen.delayMinutes > 0) {
408 Component.onCompleted: {
409 if (greeter.narrowMode) {
410 LightDM.Greeter.authenticate(LightDM.Users.data(0, LightDM.UserRoles.NameRole))
416 target: LightDM.Greeter
418 onShowGreeter: greeter.show()
419 onHideGreeter: greeter.login()
422 shell.enabled = true;
423 if (!LightDM.Greeter.active) {
424 return; // could happen if hideGreeter() comes in before we prompt
426 if (greeter.narrowMode) {
427 lockscreen.promptText = isDefaultPrompt ? "" : text.toLowerCase();
428 lockscreen.maybeShow();
432 onPromptlessChanged: {
433 if (!LightDM.Greeter.active) {
434 return; // could happen if hideGreeter() comes in before we prompt
436 if (greeter.narrowMode) {
437 if (LightDM.Greeter.promptless && LightDM.Greeter.authenticated) {
441 lockscreen.maybeShow();
446 onAuthenticationComplete: {
447 shell.enabled = true;
448 if (LightDM.Greeter.authenticated) {
449 AccountsService.failedLogins = 0
451 // Else only penalize user for a failed login if they actually were
452 // prompted for a password. We do this below after the promptless
455 if (LightDM.Greeter.promptless) {
459 if (LightDM.Greeter.authenticated) {
462 AccountsService.failedLogins++
463 if (maxFailedLogins >= 2) { // require at least a warning
464 if (AccountsService.failedLogins === maxFailedLogins - 1) {
465 var title = lockscreen.alphaNumeric ?
466 i18n.tr("Sorry, incorrect passphrase.") :
467 i18n.tr("Sorry, incorrect passcode.")
468 var text = i18n.tr("This will be your last attempt.") + " " +
469 (lockscreen.alphaNumeric ?
470 i18n.tr("If passphrase is entered incorrectly, your phone will conduct a factory reset and all personal data will be deleted.") :
471 i18n.tr("If passcode is entered incorrectly, your phone will conduct a factory reset and all personal data will be deleted."))
472 lockscreen.showInfoPopup(title, text)
473 } else if (AccountsService.failedLogins >= maxFailedLogins) {
474 SystemImage.factoryReset() // Ouch!
477 if (failedLoginsDelayAttempts > 0 && AccountsService.failedLogins % failedLoginsDelayAttempts == 0) {
478 lockscreen.delayMinutes = failedLoginsDelayMinutes
479 forcedDelayTimer.start()
482 lockscreen.clear(true);
483 if (greeter.narrowMode) {
484 LightDM.Greeter.authenticate(LightDM.Users.data(0, LightDM.UserRoles.NameRole))
491 target: LightDM.Greeter
493 value: greeter.shown || lockscreen.shown || greeter.hasLockedApp
499 opacity: greeterWrapper.showProgress * 0.8
503 // Just a tiny wrapper to adjust greeter's x without messing with its own dragging
505 objectName: "greeterWrapper"
506 x: (greeter.narrowMode && greeter.showProgress > 0) ? launcher.progress : 0
509 height: parent.height - panel.panelHeight
512 enabled: !launcher.dashSwipe
516 property bool fullyShown: showProgress === 1.0
517 onFullyShownChanged: {
518 // Wait until the greeter is completely covering lockscreen before resetting it.
519 if (greeter.narrowMode && fullyShown && !LightDM.Greeter.authenticated) {
521 lockscreen.maybeShow();
525 readonly property real showProgress: MathUtils.clamp((1 - x/width) + greeter.showProgress - 1, 0, 1)
526 onShowProgressChanged: {
527 if (showProgress === 0) {
528 if ((LightDM.Greeter.promptless && LightDM.Greeter.authenticated) || shell.forcedUnlock) {
530 } else if (greeter.narrowMode) {
531 lockscreen.clear(false) // to reset focus if necessary
538 objectName: "greeter"
540 signal sessionStarted() // helpful for tests
542 property string lockedApp: ""
543 property bool hasLockedApp: lockedApp !== ""
546 hides: [launcher, panel.indicators]
548 loadContent: required || lockscreen.required // keeps content in memory for quick show()
552 background: shell.background
555 height: parent.height
557 dragHandleWidth: shell.edgeSize
559 function startUnlock() {
561 if (!LightDM.Greeter.authenticated) {
562 lockscreen.maybeShow()
573 if (LightDM.Greeter.startSessionSync()) {
584 // Disable everything so that user can't swipe greeter or
585 // launcher until we get the next prompt/authenticate, which
586 // will re-enable the shell.
587 shell.enabled = false;
589 if (greeter.narrowMode) {
590 LightDM.Greeter.authenticate(LightDM.Users.data(0, LightDM.UserRoles.NameRole));
594 greeter.lockedApp = "";
595 greeter.forceActiveFocus();
599 Component.onCompleted: {
600 Connectivity.unlockAllModems()
603 onUnlocked: greeter.hide()
605 // Update launcher items for new user
606 var user = LightDM.Users.data(uid, LightDM.UserRoles.NameRole);
607 AccountsService.user = user;
608 LauncherModel.setUser(user);
611 onTease: launcher.tease()
614 target: ApplicationManager
615 property: "suspended"
616 value: (greeter.shown && greeterWrapper.showProgress == 1) || lockscreen.shown
626 if (shell.locked && callManager.hasCalls) {
627 // We just received an incoming call while locked. The
628 // indicator will have already launched dialer-app for us, but
629 // there is a race between "hasCalls" changing and the dialer
630 // starting up. So in case we lose that race, we'll start/
631 // focus the dialer ourselves here too. Even if the indicator
632 // didn't launch the dialer for some reason (or maybe a call
633 // started via some other means), if an active call is
634 // happening, we want to be in the dialer.
635 startLockedApp("dialer-app")
645 if (Powerd.status === Powerd.Off && reason !== Powerd.Proximity &&
646 !callManager.hasCalls && !edgeDemo.running) {
652 function showHome() {
653 if (edgeDemo.running) {
657 if (LightDM.Greeter.active) {
658 greeter.startUnlock()
661 var animate = !LightDM.Greeter.active && !stages.shown
662 dash.setCurrentScope(0, animate, false)
663 ApplicationManager.requestFocusApplication("unity8-dash")
666 function showDash() {
667 if (greeter.hasLockedApp || // just in case user gets here
668 (!greeter.narrowMode && shell.locked)) {
677 if (ApplicationManager.focusedApplicationId != "unity8-dash") {
678 ApplicationManager.requestFocusApplication("unity8-dash")
692 anchors.fill: parent //because this draws indicator menus
695 available: edgeDemo.panelEnabled && (!shell.locked || AccountsService.enableIndicatorsWhileLocked) && !greeter.hasLockedApp
696 contentEnabled: edgeDemo.panelContentEnabled
697 width: parent.width > units.gu(60) ? units.gu(40) : parent.width
699 minimizedPanelHeight: units.gu(3)
700 expandedPanelHeight: units.gu(7)
702 indicatorsModel: visibleIndicators.model
706 id: visibleIndicators
707 // TODO: This should be sourced by device type (eg "desktop", "tablet", "phone"...)
708 Component.onCompleted: initialise(indicatorProfile)
711 greeterShown: greeter.shown || lockscreen.shown
714 property bool topmostApplicationIsFullscreen:
715 ApplicationManager.focusedApplicationId &&
716 ApplicationManager.findApplication(ApplicationManager.focusedApplicationId).fullscreen
718 fullscreenMode: (topmostApplicationIsFullscreen && !LightDM.Greeter.active && launcher.progress == 0)
719 || greeter.hasLockedApp
724 objectName: "launcher"
726 readonly property bool dashSwipe: progress > 0
728 anchors.top: parent.top
729 anchors.bottom: parent.bottom
731 dragAreaWidth: shell.edgeSize
732 available: edgeDemo.launcherEnabled && (!shell.locked || AccountsService.enableLauncherWhileLocked) && !greeter.hasLockedApp
734 onShowDashHome: showHome()
736 onDashSwipeChanged: {
738 dash.setCurrentScope(0, false, true)
741 onLauncherApplicationSelected: {
742 if (greeter.hasLockedApp) {
743 greeter.startUnlock()
745 if (!edgeDemo.running)
746 shell.activateApplication(appId)
750 panel.indicators.hide()
758 background: shell.background
762 id: modalNotificationBackground
764 visible: notifications.useModal && (notifications.state == "narrow")
777 model: NotificationBackend.Model
780 y: topmostIsFullscreen ? 0 : panel.panelHeight
781 height: parent.height - (topmostIsFullscreen ? 0 : panel.panelHeight)
786 when: overlay.width <= units.gu(60)
788 target: notifications
789 anchors.left: parent.left
790 anchors.right: parent.right
795 when: overlay.width > units.gu(60)
797 target: notifications
798 anchors.left: undefined
799 anchors.right: parent.right
801 PropertyChanges { target: notifications; width: units.gu(38) }
812 shutdownFadeOutRectangle.enabled = true;
813 shutdownFadeOutRectangle.visible = true;
814 shutdownFadeOut.start();
818 target: physicalKeysMapper
819 onPowerKeyLongPress: dialogs.showPowerDialog();
825 objectName: "edgeDemo"
827 paused: Powerd.status === Powerd.Off || wizard.active // Saves power
835 target: SessionBroadcast
836 onShowHome: showHome()
840 id: shutdownFadeOutRectangle
847 NumberAnimation on opacity {
852 if (shutdownFadeOutRectangle.enabled && shutdownFadeOutRectangle.visible) {
853 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