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"
40import Unity.Notifications 1.0 as NotificationBackend
41import Unity.Session 0.1
42import Unity.DashCommunicator 0.1
47 // this is only here to select the width / height of the window if not running fullscreen
48 property bool tablet: false
49 width: tablet ? units.gu(160) : applicationArguments.hasGeometry() ? applicationArguments.width() : units.gu(40)
50 height: tablet ? units.gu(100) : applicationArguments.hasGeometry() ? applicationArguments.height() : units.gu(71)
52 property real edgeSize: units.gu(2)
53 property url defaultBackground: Qt.resolvedUrl(shell.width >= units.gu(60) ? "graphics/tablet_background.jpg" : "graphics/phone_background.jpg")
54 property url background
55 readonly property real panelHeight: panel.panelHeight
57 readonly property bool locked: LightDM.Greeter.active && !LightDM.Greeter.authenticated
59 property bool sideStageEnabled: shell.width >= units.gu(100)
60 readonly property string focusedApplicationId: ApplicationManager.focusedApplicationId
62 property int maxFailedLogins: -1 // disabled by default for now, will enable via settings in future
63 property int failedLoginsDelayAttempts: 7 // number of failed logins
64 property int failedLoginsDelayMinutes: 5 // minutes of forced waiting
66 property int orientation
67 readonly property int deviceOrientationAngle: Screen.angleBetween(Screen.primaryOrientation, Screen.orientation)
68 onDeviceOrientationAngleChanged: {
69 if (!OrientationLock.enabled) {
70 orientation = Screen.orientation;
73 readonly property bool orientationLockEnabled: OrientationLock.enabled
74 onOrientationLockEnabledChanged: {
75 if (orientationLockEnabled) {
76 OrientationLock.savedOrientation = Screen.orientation;
78 orientation = Screen.orientation;
82 function activateApplication(appId) {
83 if (ApplicationManager.findApplication(appId)) {
84 ApplicationManager.requestFocusApplication(appId);
86 var execFlags = shell.sideStageEnabled ? ApplicationManager.NoFlag : ApplicationManager.ForceMainStage;
87 ApplicationManager.startApplication(appId, execFlags);
91 function setLockedApp(app) {
93 greeter.lockedApp = app
100 property: "applicationManager"
101 value: ApplicationManager
104 Component.onCompleted: {
105 Theme.name = "Ubuntu.Components.Themes.SuruGradient"
106 if (ApplicationManager.count > 0) {
107 ApplicationManager.focusApplication(ApplicationManager.get(0).appId);
109 if (orientationLockEnabled) {
110 orientation = OrientationLock.savedOrientation;
115 id: backgroundSettings
116 schema.id: "org.gnome.desktop.background"
118 property url gSettingsPicture: backgroundSettings.pictureUri != undefined && backgroundSettings.pictureUri.length > 0 ? backgroundSettings.pictureUri : shell.defaultBackground
119 onGSettingsPictureChanged: {
120 shell.background = gSettingsPicture
129 objectName: "dashCommunicator"
133 target: ApplicationManager
134 property: "forceDashActive"
135 value: launcher.shown || launcher.dashSwipe
140 // Handle but do not filter out volume keys
141 Keys.onVolumeUpPressed: { volumeControl.volumeUp(); event.accepted = false; }
142 Keys.onVolumeDownPressed: { volumeControl.volumeDown(); event.accepted = false; }
145 if (event.key == Qt.Key_PowerOff || event.key == Qt.Key_PowerDown) {
146 dialogs.onPowerKeyPressed();
147 event.accepted = true;
149 event.accepted = false;
154 if (event.key == Qt.Key_PowerOff || event.key == Qt.Key_PowerDown) {
155 dialogs.onPowerKeyReleased();
156 event.accepted = true;
158 event.accepted = false;
167 height: parent.height
168 visible: !ApplicationManager.empty
171 target: ApplicationManager
173 if (greeter.narrowMode) {
174 if (appId === "dialer-app") {
175 // Always let the dialer-app through. Either user asked
176 // for an emergency call or accepted an incoming call.
178 } else if (greeter.hasLockedApp && greeter.lockedApp !== appId) {
179 greeter.startUnlock()
183 if (LightDM.Greeter.active) {
184 greeter.startUnlock()
189 onFocusedApplicationIdChanged: {
190 if (greeter.hasLockedApp && greeter.lockedApp !== ApplicationManager.focusedApplicationId) {
191 greeter.startUnlock()
193 panel.indicators.hide();
196 onApplicationAdded: {
197 if (greeter.shown && appId != "unity8-dash") {
198 greeter.startUnlock()
200 if (greeter.narrowMode && appId === "dialer-app") {
201 // Always let the dialer-app through. Either user asked
202 // for an emergency call or accepted an incoming call.
210 id: applicationsDisplayLoader
211 objectName: "applicationsDisplayLoader"
214 // When we have a locked app, we only want to show that one app.
215 // FIXME: do this in a less traumatic way. We currently only allow
216 // locked apps in phone mode (see FIXME in Lockscreen component in
217 // this same file). When that changes, we need to do something
218 // nicer here. But this code is currently just to prevent a
219 // theoretical attack where user enters lockedApp mode, then makes
220 // the screen larger (maybe connects to monitor) and tries to enter
222 property bool tabletMode: shell.sideStageEnabled && !greeter.hasLockedApp
223 source: tabletMode ? "Stages/TabletStage.qml" : "Stages/PhoneStage.qml"
226 target: applicationsDisplayLoader.item
227 property: "objectName"
231 target: applicationsDisplayLoader.item
232 property: "dragAreaWidth"
233 value: shell.edgeSize
236 target: applicationsDisplayLoader.item
237 property: "maximizedAppTopMargin"
238 // Not just using panel.panelHeight as that changes depending on the focused app.
239 value: panel.indicators.minimizedPanelHeight + units.dp(2) // dp(2) for orange line
242 target: applicationsDisplayLoader.item
243 property: "interactive"
244 value: edgeDemo.stagesEnabled && !greeter.shown && !lockscreen.shown && panel.indicators.fullyClosed && launcher.progress == 0
247 target: applicationsDisplayLoader.item
248 property: "spreadEnabled"
249 value: edgeDemo.stagesEnabled && !greeter.hasLockedApp
252 target: applicationsDisplayLoader.item
253 property: "inverseProgress"
254 value: launcher.progress
257 target: applicationsDisplayLoader.item
258 property: "orientation"
259 value: shell.orientation
266 objectName: "inputMethod"
267 anchors { fill: parent; topMargin: panel.panelHeight }
268 z: notifications.useModal || panel.indicators.shown ? overlay.z + 1 : overlay.z - 1
272 target: SurfaceManager
274 if (surface.type == MirSurfaceItem.InputMethod) {
275 inputMethod.surface = surface;
279 onSurfaceDestroyed: {
280 if (inputMethod.surface == surface) {
281 inputMethod.surface = null;
282 surface.parent = null;
284 if (!surface.parent) {
285 // there's no one displaying it. delete it right away
291 target: SessionManager
293 if (!session.parentSession && !session.application) {
294 // nothing is using it. delete it right away
302 objectName: "lockscreen"
304 hides: [launcher, panel.indicators]
307 showAnimation: StandardAnimation { property: "opacity"; to: 1 }
308 hideAnimation: StandardAnimation { property: "opacity"; to: 0 }
312 height: parent.height - panel.panelHeight
313 background: shell.background
314 alphaNumeric: AccountsService.passwordDisplayHint === AccountsService.Keyboard
318 // FIXME: We *should* show emergency dialer if there is a SIM present,
319 // regardless of whether the side stage is enabled. But right now,
320 // the assumption is that narrow screens are phones which have SIMs
321 // and wider screens are tablets which don't. When we do allow this
322 // on devices with a side stage and a SIM, work should be done to
323 // ensure that the main stage is disabled while the dialer is present
324 // in the side stage. See the FIXME in the stage loader in this file.
325 showEmergencyCallButton: !shell.sideStageEnabled
327 onEntered: LightDM.Greeter.respond(passphrase);
328 onCancel: greeter.show()
329 onEmergencyCall: shell.activateApplication("dialer-app") // will automatically enter locked-app mode
331 onShownChanged: if (shown) greeter.lockedApp = ""
337 if (lockscreen.delayMinutes > 0) {
338 lockscreen.delayMinutes -= 1
339 if (lockscreen.delayMinutes > 0) {
346 Component.onCompleted: {
347 if (greeter.narrowMode) {
348 LightDM.Greeter.authenticate(LightDM.Users.data(0, LightDM.UserRoles.NameRole))
354 target: LightDM.Greeter
356 onShowGreeter: greeter.show()
357 onHideGreeter: greeter.login()
360 if (greeter.narrowMode) {
361 if (isDefaultPrompt) {
362 if (lockscreen.alphaNumeric) {
363 lockscreen.infoText = i18n.tr("Enter passphrase")
364 lockscreen.errorText = i18n.tr("Sorry, incorrect passphrase") + "\n" +
365 i18n.tr("Please re-enter")
367 lockscreen.infoText = i18n.tr("Enter passcode")
368 lockscreen.errorText = i18n.tr("Sorry, incorrect passcode")
371 lockscreen.infoText = i18n.tr("Enter %1").arg(text.toLowerCase())
372 lockscreen.errorText = i18n.tr("Sorry, incorrect %1").arg(text.toLowerCase())
379 onPromptlessChanged: {
380 if (greeter.narrowMode) {
381 if (LightDM.Greeter.promptless && LightDM.Greeter.authenticated) {
390 onAuthenticationComplete: {
391 if (LightDM.Greeter.authenticated) {
392 AccountsService.failedLogins = 0
394 // Else only penalize user for a failed login if they actually were
395 // prompted for a password. We do this below after the promptless
398 if (LightDM.Greeter.promptless) {
402 if (LightDM.Greeter.authenticated) {
405 AccountsService.failedLogins++
406 if (maxFailedLogins >= 2) { // require at least a warning
407 if (AccountsService.failedLogins === maxFailedLogins - 1) {
408 var title = lockscreen.alphaNumeric ?
409 i18n.tr("Sorry, incorrect passphrase.") :
410 i18n.tr("Sorry, incorrect passcode.")
411 var text = i18n.tr("This will be your last attempt.") + " " +
412 (lockscreen.alphaNumeric ?
413 i18n.tr("If passphrase is entered incorrectly, your phone will conduct a factory reset and all personal data will be deleted.") :
414 i18n.tr("If passcode is entered incorrectly, your phone will conduct a factory reset and all personal data will be deleted."))
415 lockscreen.showInfoPopup(title, text)
416 } else if (AccountsService.failedLogins >= maxFailedLogins) {
417 SystemImage.factoryReset() // Ouch!
420 if (failedLoginsDelayAttempts > 0 && AccountsService.failedLogins % failedLoginsDelayAttempts == 0) {
421 lockscreen.delayMinutes = failedLoginsDelayMinutes
422 forcedDelayTimer.start()
425 lockscreen.clear(true);
426 if (greeter.narrowMode) {
427 LightDM.Greeter.authenticate(LightDM.Users.data(0, LightDM.UserRoles.NameRole))
434 target: LightDM.Greeter
436 value: greeter.shown || lockscreen.shown || greeter.hasLockedApp
442 opacity: greeterWrapper.showProgress * 0.8
446 // Just a tiny wrapper to adjust greeter's x without messing with its own dragging
448 objectName: "greeterWrapper"
449 x: greeter.narrowMode ? launcher.progress : 0
452 height: parent.height - panel.panelHeight
455 enabled: !launcher.dashSwipe
459 property bool fullyShown: showProgress === 1.0
460 onFullyShownChanged: {
461 // Wait until the greeter is completely covering lockscreen before resetting it.
462 if (greeter.narrowMode && fullyShown && !LightDM.Greeter.authenticated) {
468 readonly property real showProgress: MathUtils.clamp((1 - x/width) + greeter.showProgress - 1, 0, 1)
469 onShowProgressChanged: {
470 if (showProgress === 0) {
471 if (LightDM.Greeter.promptless && LightDM.Greeter.authenticated) {
473 } else if (greeter.narrowMode) {
474 lockscreen.clear(false) // to reset focus if necessary
481 objectName: "greeter"
483 signal sessionStarted() // helpful for tests
485 property string lockedApp: ""
486 property bool hasLockedApp: lockedApp !== ""
489 hides: [launcher, panel.indicators]
491 loadContent: required || lockscreen.required // keeps content in memory for quick show()
495 defaultBackground: shell.background
498 height: parent.height
500 dragHandleWidth: shell.edgeSize
502 function startUnlock() {
504 if (!LightDM.Greeter.authenticated) {
516 if (LightDM.Greeter.startSessionSync()) {
527 if (greeter.narrowMode) {
528 LightDM.Greeter.authenticate(LightDM.Users.data(0, LightDM.UserRoles.NameRole));
532 greeter.lockedApp = "";
533 greeter.forceActiveFocus();
537 /* TODO re-enable when the corresponding changes in the service land (LP: #1361074)
538 Component.onCompleted: {
539 Connectivity.unlockAllModems()
542 onUnlocked: greeter.hide()
544 // Update launcher items for new user
545 var user = LightDM.Users.data(uid, LightDM.UserRoles.NameRole);
546 AccountsService.user = user;
547 LauncherModel.setUser(user);
550 onTease: launcher.tease()
553 target: ApplicationManager
554 property: "suspended"
555 value: greeter.shown && greeterWrapper.showProgress == 1
565 if (Powerd.status === Powerd.Off && !callManager.hasCalls && !edgeDemo.running) {
571 function showHome() {
572 if (edgeDemo.running) {
576 if (LightDM.Greeter.active) {
577 greeter.startUnlock()
580 var animate = !LightDM.Greeter.active && !stages.shown
581 dash.setCurrentScope("clickscope", animate, false)
582 ApplicationManager.requestFocusApplication("unity8-dash")
585 function showDash() {
586 if (greeter.hasLockedApp || // just in case user gets here
587 (!greeter.narrowMode && shell.locked)) {
596 if (ApplicationManager.focusedApplicationId != "unity8-dash") {
597 ApplicationManager.requestFocusApplication("unity8-dash")
611 anchors.fill: parent //because this draws indicator menus
614 available: edgeDemo.panelEnabled && (!shell.locked || AccountsService.enableIndicatorsWhileLocked) && !greeter.hasLockedApp
615 contentEnabled: edgeDemo.panelContentEnabled
616 width: parent.width > units.gu(60) ? units.gu(40) : parent.width
618 minimizedPanelHeight: units.gu(3)
619 expandedPanelHeight: units.gu(7)
621 indicatorsModel: visibleIndicators.model
625 id: visibleIndicators
626 // TODO: This should be sourced by device type (eg "desktop", "tablet", "phone"...)
627 Component.onCompleted: initialise(indicatorProfile)
630 property bool topmostApplicationIsFullscreen:
631 ApplicationManager.focusedApplicationId &&
632 ApplicationManager.findApplication(ApplicationManager.focusedApplicationId).fullscreen
634 fullscreenMode: (topmostApplicationIsFullscreen && !LightDM.Greeter.active && launcher.progress == 0)
635 || greeter.hasLockedApp
640 objectName: "launcher"
642 readonly property bool dashSwipe: progress > 0
644 anchors.top: parent.top
645 anchors.bottom: parent.bottom
647 dragAreaWidth: shell.edgeSize
648 available: edgeDemo.launcherEnabled && (!shell.locked || AccountsService.enableLauncherWhileLocked) && !greeter.hasLockedApp
650 onShowDashHome: showHome()
652 onDashSwipeChanged: {
654 dash.setCurrentScope("clickscope", false, true)
657 onLauncherApplicationSelected: {
658 if (greeter.hasLockedApp) {
659 greeter.startUnlock()
661 if (!edgeDemo.running)
662 shell.activateApplication(appId)
666 panel.indicators.hide()
672 id: modalNotificationBackground
674 visible: notifications.useModal && !greeter.shown && (notifications.state == "narrow")
687 model: NotificationBackend.Model
692 height: parent.height - panel.panelHeight
697 when: overlay.width <= units.gu(60)
698 AnchorChanges { target: notifications; anchors.left: parent.left }
702 when: overlay.width > units.gu(60)
703 AnchorChanges { target: notifications; anchors.left: undefined }
704 PropertyChanges { target: notifications; width: units.gu(38) }
715 shutdownFadeOutRectangle.enabled = true;
716 shutdownFadeOutRectangle.visible = true;
717 shutdownFadeOut.start();
722 id: alphaDisclaimerLabel
723 anchors.centerIn: parent
724 visible: ApplicationManager.fake ? ApplicationManager.fake : false
726 text: "EARLY ALPHA\nNOT READY FOR USE"
729 font.weight: Font.Black
730 horizontalAlignment: Text.AlignHCenter
731 verticalAlignment: Text.AlignVCenter
732 fontSizeMode: Text.Fit
734 scale: Math.min(parent.width, parent.height) / width
739 z: alphaDisclaimerLabel.z + 10
740 paused: Powerd.status === Powerd.Off // Saves power
743 indicators: panel.indicators
748 target: SessionBroadcast
749 onShowHome: showHome()
753 id: shutdownFadeOutRectangle
760 NumberAnimation on opacity {
765 if (shutdownFadeOutRectangle.enabled && shutdownFadeOutRectangle.visible) {
766 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