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: true
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: asImageTester.status == Image.Ready ? asImageTester.source
55 : gsImageTester.status == Image.Ready ? gsImageTester.source : defaultBackground
56 readonly property real panelHeight: panel.panelHeight
58 readonly property bool locked: LightDM.Greeter.active && !LightDM.Greeter.authenticated && !forcedUnlock
59 readonly property alias hasLockedApp: greeter.hasLockedApp
60 readonly property bool forcedUnlock: edgeDemo.running
61 onForcedUnlockChanged: if (forcedUnlock) lockscreen.hide()
63 property bool sideStageEnabled: shell.width >= units.gu(100)
64 readonly property string focusedApplicationId: ApplicationManager.focusedApplicationId
66 property int maxFailedLogins: -1 // disabled by default for now, will enable via settings in future
67 property int failedLoginsDelayAttempts: 7 // number of failed logins
68 property int failedLoginsDelayMinutes: 5 // minutes of forced waiting
70 property int orientation
71 readonly property int deviceOrientationAngle: Screen.angleBetween(Screen.primaryOrientation, Screen.orientation)
72 onDeviceOrientationAngleChanged: {
73 if (!OrientationLock.enabled) {
74 orientation = Screen.orientation;
77 readonly property bool orientationLockEnabled: OrientationLock.enabled
78 onOrientationLockEnabledChanged: {
79 if (orientationLockEnabled) {
80 OrientationLock.savedOrientation = Screen.orientation;
82 orientation = Screen.orientation;
86 function activateApplication(appId) {
87 if (ApplicationManager.findApplication(appId)) {
88 ApplicationManager.requestFocusApplication(appId);
90 var execFlags = shell.sideStageEnabled ? ApplicationManager.NoFlag : ApplicationManager.ForceMainStage;
91 ApplicationManager.startApplication(appId, execFlags);
95 function startLockedApp(app) {
97 greeter.lockedApp = app;
99 shell.activateApplication(app);
102 // This is a dummy image to detect if the custom AS set wallpaper loads successfully.
105 source: AccountsService.backgroundFile != undefined && AccountsService.backgroundFile.length > 0 ? AccountsService.backgroundFile : ""
113 id: backgroundSettings
114 schema.id: "org.gnome.desktop.background"
117 // This is a dummy image to detect if the custom GSettings set wallpaper loads successfully.
120 source: backgroundSettings.pictureUri != undefined && backgroundSettings.pictureUri.length > 0 ? backgroundSettings.pictureUri : ""
128 id: usageModeSettings
129 schema.id: "com.canonical.Unity8"
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 (greeter.narrowMode && greeter.hasLockedApp && appId === greeter.lockedApp) {
242 lockscreen.hide() // show locked app
249 id: applicationsDisplayLoader
250 objectName: "applicationsDisplayLoader"
253 // When we have a locked app, we only want to show that one app.
254 // FIXME: do this in a less traumatic way. We currently only allow
255 // locked apps in phone mode (see FIXME in Lockscreen component in
256 // this same file). When that changes, we need to do something
257 // nicer here. But this code is currently just to prevent a
258 // theoretical attack where user enters lockedApp mode, then makes
259 // the screen larger (maybe connects to monitor) and tries to enter
261 property bool tabletMode: shell.sideStageEnabled && !greeter.hasLockedApp
262 source: usageModeSettings.usageMode === "Windowed" ? "Stages/DesktopStage.qml"
263 : tabletMode ? "Stages/TabletStage.qml" : "Stages/PhoneStage.qml"
266 target: applicationsDisplayLoader.item
267 property: "objectName"
271 target: applicationsDisplayLoader.item
272 property: "dragAreaWidth"
273 value: shell.edgeSize
276 target: applicationsDisplayLoader.item
277 property: "maximizedAppTopMargin"
278 // Not just using panel.panelHeight as that changes depending on the focused app.
279 value: panel.indicators.minimizedPanelHeight + units.dp(2) // dp(2) for orange line
282 target: applicationsDisplayLoader.item
283 property: "interactive"
284 value: edgeDemo.stagesEnabled && !greeter.shown && !lockscreen.shown && panel.indicators.fullyClosed && launcher.progress == 0 && !notifications.useModal
287 target: applicationsDisplayLoader.item
288 property: "spreadEnabled"
289 value: edgeDemo.stagesEnabled && !greeter.hasLockedApp
292 target: applicationsDisplayLoader.item
293 property: "inverseProgress"
294 value: launcher.progress
297 target: applicationsDisplayLoader.item
298 property: "orientation"
299 value: shell.orientation
302 target: applicationsDisplayLoader.item
303 property: "background"
304 value: shell.background
311 objectName: "inputMethod"
312 anchors { fill: parent; topMargin: panel.panelHeight }
313 z: notifications.useModal || panel.indicators.shown ? overlay.z + 1 : overlay.z - 1
317 target: SurfaceManager
319 if (surface.type == MirSurfaceItem.InputMethod) {
320 inputMethod.surface = surface;
324 onSurfaceDestroyed: {
325 if (inputMethod.surface == surface) {
326 inputMethod.surface = null;
327 surface.parent = null;
329 if (!surface.parent) {
330 // there's no one displaying it. delete it right away
336 target: SessionManager
338 if (!session.parentSession && !session.application) {
339 // nothing is using it. delete it right away
347 objectName: "lockscreen"
349 hides: [launcher, panel.indicators]
352 showAnimation: StandardAnimation { property: "opacity"; to: 1 }
353 hideAnimation: StandardAnimation { property: "opacity"; to: 0 }
357 height: parent.height - panel.panelHeight
358 background: shell.background
359 darkenBackground: 0.4
360 alphaNumeric: AccountsService.passwordDisplayHint === AccountsService.Keyboard
364 // FIXME: We *should* show emergency dialer if there is a SIM present,
365 // regardless of whether the side stage is enabled. But right now,
366 // the assumption is that narrow screens are phones which have SIMs
367 // and wider screens are tablets which don't. When we do allow this
368 // on devices with a side stage and a SIM, work should be done to
369 // ensure that the main stage is disabled while the dialer is present
370 // in the side stage. See the FIXME in the stage loader in this file.
371 showEmergencyCallButton: !shell.sideStageEnabled
373 onEntered: LightDM.Greeter.respond(passphrase);
374 onCancel: greeter.show()
375 onEmergencyCall: startLockedApp("dialer-app")
377 onShownChanged: if (shown) greeter.lockedApp = ""
379 function maybeShow() {
380 if (!shell.forcedUnlock) {
389 if (lockscreen.delayMinutes > 0) {
390 lockscreen.delayMinutes -= 1
391 if (lockscreen.delayMinutes > 0) {
398 Component.onCompleted: {
399 if (greeter.narrowMode) {
400 LightDM.Greeter.authenticate(LightDM.Users.data(0, LightDM.UserRoles.NameRole))
406 target: LightDM.Greeter
408 onShowGreeter: greeter.show()
409 onHideGreeter: greeter.login()
412 shell.enabled = true;
413 if (!LightDM.Greeter.active) {
414 return; // could happen if hideGreeter() comes in before we prompt
416 if (greeter.narrowMode) {
417 if (isDefaultPrompt) {
418 if (lockscreen.alphaNumeric) {
419 lockscreen.infoText = i18n.tr("Enter passphrase")
420 lockscreen.errorText = i18n.tr("Sorry, incorrect passphrase") + "\n" +
421 i18n.tr("Please re-enter")
423 lockscreen.infoText = i18n.tr("Enter passcode")
424 lockscreen.errorText = i18n.tr("Sorry, incorrect passcode")
427 lockscreen.infoText = i18n.tr("Enter %1").arg(text.toLowerCase())
428 lockscreen.errorText = i18n.tr("Sorry, incorrect %1").arg(text.toLowerCase())
431 lockscreen.maybeShow();
435 onPromptlessChanged: {
436 if (!LightDM.Greeter.active) {
437 return; // could happen if hideGreeter() comes in before we prompt
439 if (greeter.narrowMode) {
440 if (LightDM.Greeter.promptless && LightDM.Greeter.authenticated) {
444 lockscreen.maybeShow();
449 onAuthenticationComplete: {
450 shell.enabled = true;
451 if (LightDM.Greeter.authenticated) {
452 AccountsService.failedLogins = 0
454 // Else only penalize user for a failed login if they actually were
455 // prompted for a password. We do this below after the promptless
458 if (LightDM.Greeter.promptless) {
462 if (LightDM.Greeter.authenticated) {
465 AccountsService.failedLogins++
466 if (maxFailedLogins >= 2) { // require at least a warning
467 if (AccountsService.failedLogins === maxFailedLogins - 1) {
468 var title = lockscreen.alphaNumeric ?
469 i18n.tr("Sorry, incorrect passphrase.") :
470 i18n.tr("Sorry, incorrect passcode.")
471 var text = i18n.tr("This will be your last attempt.") + " " +
472 (lockscreen.alphaNumeric ?
473 i18n.tr("If passphrase is entered incorrectly, your phone will conduct a factory reset and all personal data will be deleted.") :
474 i18n.tr("If passcode is entered incorrectly, your phone will conduct a factory reset and all personal data will be deleted."))
475 lockscreen.showInfoPopup(title, text)
476 } else if (AccountsService.failedLogins >= maxFailedLogins) {
477 SystemImage.factoryReset() // Ouch!
480 if (failedLoginsDelayAttempts > 0 && AccountsService.failedLogins % failedLoginsDelayAttempts == 0) {
481 lockscreen.delayMinutes = failedLoginsDelayMinutes
482 forcedDelayTimer.start()
485 lockscreen.clear(true);
486 if (greeter.narrowMode) {
487 LightDM.Greeter.authenticate(LightDM.Users.data(0, LightDM.UserRoles.NameRole))
494 target: LightDM.Greeter
496 value: greeter.shown || lockscreen.shown || greeter.hasLockedApp
502 opacity: greeterWrapper.showProgress * 0.8
506 // Just a tiny wrapper to adjust greeter's x without messing with its own dragging
508 objectName: "greeterWrapper"
509 x: greeter.narrowMode ? launcher.progress : 0
512 height: parent.height - panel.panelHeight
515 enabled: !launcher.dashSwipe
519 property bool fullyShown: showProgress === 1.0
520 onFullyShownChanged: {
521 // Wait until the greeter is completely covering lockscreen before resetting it.
522 if (greeter.narrowMode && fullyShown && !LightDM.Greeter.authenticated) {
524 lockscreen.maybeShow();
528 readonly property real showProgress: MathUtils.clamp((1 - x/width) + greeter.showProgress - 1, 0, 1)
529 onShowProgressChanged: {
530 if (showProgress === 0) {
531 if ((LightDM.Greeter.promptless && LightDM.Greeter.authenticated) || shell.forcedUnlock) {
533 } else if (greeter.narrowMode) {
534 lockscreen.clear(false) // to reset focus if necessary
541 objectName: "greeter"
543 signal sessionStarted() // helpful for tests
545 property string lockedApp: ""
546 property bool hasLockedApp: lockedApp !== ""
549 hides: [launcher, panel.indicators]
551 loadContent: required || lockscreen.required // keeps content in memory for quick show()
555 background: shell.background
558 height: parent.height
560 dragHandleWidth: shell.edgeSize
562 function startUnlock() {
564 if (!LightDM.Greeter.authenticated) {
565 lockscreen.maybeShow()
576 if (LightDM.Greeter.startSessionSync()) {
587 // Disable everything so that user can't swipe greeter or
588 // launcher until we get first prompt/authenticate, which
589 // will re-enable the shell.
590 shell.enabled = false;
592 if (greeter.narrowMode) {
593 LightDM.Greeter.authenticate(LightDM.Users.data(0, LightDM.UserRoles.NameRole));
597 greeter.lockedApp = "";
598 greeter.forceActiveFocus();
602 Component.onCompleted: {
603 Connectivity.unlockAllModems()
606 onUnlocked: greeter.hide()
608 // Update launcher items for new user
609 var user = LightDM.Users.data(uid, LightDM.UserRoles.NameRole);
610 AccountsService.user = user;
611 LauncherModel.setUser(user);
614 onTease: launcher.tease()
617 target: ApplicationManager
618 property: "suspended"
619 value: (greeter.shown && greeterWrapper.showProgress == 1) || lockscreen.shown
629 if (shell.locked && callManager.hasCalls) {
630 // We just received an incoming call while locked. The
631 // indicator will have already launched dialer-app for us, but
632 // there is a race between "hasCalls" changing and the dialer
633 // starting up. So in case we lose that race, we'll start/
634 // focus the dialer ourselves here too. Even if the indicator
635 // didn't launch the dialer for some reason (or maybe a call
636 // started via some other means), if an active call is
637 // happening, we want to be in the dialer.
638 startLockedApp("dialer-app")
648 if (Powerd.status === Powerd.Off && reason !== Powerd.Proximity &&
649 !callManager.hasCalls && !edgeDemo.running) {
655 function showHome() {
656 if (edgeDemo.running) {
660 if (LightDM.Greeter.active) {
661 greeter.startUnlock()
664 var animate = !LightDM.Greeter.active && !stages.shown
665 dash.setCurrentScope(0, animate, false)
666 ApplicationManager.requestFocusApplication("unity8-dash")
669 function showDash() {
670 if (greeter.hasLockedApp || // just in case user gets here
671 (!greeter.narrowMode && shell.locked)) {
680 if (ApplicationManager.focusedApplicationId != "unity8-dash") {
681 ApplicationManager.requestFocusApplication("unity8-dash")
695 anchors.fill: parent //because this draws indicator menus
698 available: edgeDemo.panelEnabled && (!shell.locked || AccountsService.enableIndicatorsWhileLocked) && !greeter.hasLockedApp
699 contentEnabled: edgeDemo.panelContentEnabled
700 width: parent.width > units.gu(60) ? units.gu(40) : parent.width
702 minimizedPanelHeight: units.gu(3)
703 expandedPanelHeight: units.gu(7)
705 indicatorsModel: visibleIndicators.model
709 id: visibleIndicators
710 // TODO: This should be sourced by device type (eg "desktop", "tablet", "phone"...)
711 Component.onCompleted: initialise(indicatorProfile)
714 greeterShown: greeter.shown || lockscreen.shown
717 property bool topmostApplicationIsFullscreen:
718 ApplicationManager.focusedApplicationId &&
719 ApplicationManager.findApplication(ApplicationManager.focusedApplicationId).fullscreen
721 fullscreenMode: (topmostApplicationIsFullscreen && !LightDM.Greeter.active && launcher.progress == 0)
722 || greeter.hasLockedApp
727 objectName: "launcher"
729 readonly property bool dashSwipe: progress > 0
731 anchors.top: parent.top
732 anchors.topMargin: inverted ? 0 : panel.panelHeight
733 anchors.bottom: parent.bottom
735 dragAreaWidth: shell.edgeSize
736 available: edgeDemo.launcherEnabled && (!shell.locked || AccountsService.enableLauncherWhileLocked) && !greeter.hasLockedApp
737 inverted: usageModeSettings.usageMode === "Staged"
739 onShowDashHome: showHome()
741 onDashSwipeChanged: {
743 dash.setCurrentScope(0, false, true)
746 onLauncherApplicationSelected: {
747 if (greeter.hasLockedApp) {
748 greeter.startUnlock()
750 if (!edgeDemo.running)
751 shell.activateApplication(appId)
755 panel.indicators.hide()
761 id: modalNotificationBackground
763 visible: notifications.useModal && (notifications.state == "narrow")
776 model: NotificationBackend.Model
779 y: topmostIsFullscreen ? 0 : panel.panelHeight
781 height: parent.height - (topmostIsFullscreen ? 0 : panel.panelHeight)
786 when: overlay.width <= units.gu(60)
787 AnchorChanges { target: notifications; anchors.left: parent.left }
791 when: overlay.width > units.gu(60)
792 AnchorChanges { target: notifications; anchors.left: undefined }
793 PropertyChanges { target: notifications; width: units.gu(38) }
804 shutdownFadeOutRectangle.enabled = true;
805 shutdownFadeOutRectangle.visible = true;
806 shutdownFadeOut.start();
812 objectName: "edgeDemo"
814 paused: Powerd.status === Powerd.Off // Saves power
822 target: SessionBroadcast
823 onShowHome: showHome()
827 id: shutdownFadeOutRectangle
834 NumberAnimation on opacity {
839 if (shutdownFadeOutRectangle.enabled && shutdownFadeOutRectangle.visible) {
840 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