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 && !forcedUnlock
58 readonly property alias hasLockedApp: greeter.hasLockedApp
59 readonly property bool forcedUnlock: edgeDemo.running
60 onForcedUnlockChanged: if (forcedUnlock) lockscreen.hide()
62 property bool sideStageEnabled: shell.width >= units.gu(100)
63 readonly property string focusedApplicationId: ApplicationManager.focusedApplicationId
65 property int maxFailedLogins: -1 // disabled by default for now, will enable via settings in future
66 property int failedLoginsDelayAttempts: 7 // number of failed logins
67 property int failedLoginsDelayMinutes: 5 // minutes of forced waiting
69 property int orientation
70 readonly property int deviceOrientationAngle: Screen.angleBetween(Screen.primaryOrientation, Screen.orientation)
71 onDeviceOrientationAngleChanged: {
72 if (!OrientationLock.enabled) {
73 orientation = Screen.orientation;
76 readonly property bool orientationLockEnabled: OrientationLock.enabled
77 onOrientationLockEnabledChanged: {
78 if (orientationLockEnabled) {
79 OrientationLock.savedOrientation = Screen.orientation;
81 orientation = Screen.orientation;
85 function activateApplication(appId) {
86 if (ApplicationManager.findApplication(appId)) {
87 ApplicationManager.requestFocusApplication(appId);
89 var execFlags = shell.sideStageEnabled ? ApplicationManager.NoFlag : ApplicationManager.ForceMainStage;
90 ApplicationManager.startApplication(appId, execFlags);
94 function startLockedApp(app) {
96 greeter.lockedApp = app;
98 shell.activateApplication(app);
102 target: LauncherModel
103 property: "applicationManager"
104 value: ApplicationManager
107 Component.onCompleted: {
108 Theme.name = "Ubuntu.Components.Themes.SuruGradient"
109 if (ApplicationManager.count > 0) {
110 ApplicationManager.focusApplication(ApplicationManager.get(0).appId);
112 if (orientationLockEnabled) {
113 orientation = OrientationLock.savedOrientation;
118 id: backgroundSettings
119 schema.id: "org.gnome.desktop.background"
121 property url gSettingsPicture: backgroundSettings.pictureUri != undefined && backgroundSettings.pictureUri.length > 0 ? backgroundSettings.pictureUri : shell.defaultBackground
122 onGSettingsPictureChanged: {
123 shell.background = gSettingsPicture
132 objectName: "dashCommunicator"
138 enabled: Powerd.status === Powerd.On
142 target: ApplicationManager
143 property: "forceDashActive"
144 value: launcher.shown || launcher.dashSwipe
149 onVolumeDownPressed: volumeControl.volumeDown()
150 onVolumeUpPressed: volumeControl.volumeUp()
151 onBothVolumeKeysPressed: screenGrabber.capture()
156 if (event.key == Qt.Key_PowerOff || event.key == Qt.Key_PowerDown) {
157 dialogs.onPowerKeyPressed();
158 event.accepted = true;
160 volumeKeyFilter.onKeyPressed(event.key);
161 event.accepted = false;
166 if (event.key == Qt.Key_PowerOff || event.key == Qt.Key_PowerDown) {
167 dialogs.onPowerKeyReleased();
168 event.accepted = true;
170 volumeKeyFilter.onKeyReleased(event.key);
171 event.accepted = false;
180 height: parent.height
181 visible: !ApplicationManager.empty
184 target: ApplicationManager
186 if (greeter.narrowMode) {
187 if (appId === "dialer-app" && callManager.hasCalls && shell.locked) {
188 // If we are in the middle of a call, make dialer lockedApp and show it.
189 // This can happen if user backs out of dialer back to greeter, then
190 // launches dialer again.
191 greeter.lockedApp = appId;
193 if (greeter.hasLockedApp) {
194 if (appId === greeter.lockedApp) {
195 lockscreen.hide() // show locked app
197 greeter.startUnlock() // show lockscreen if necessary
202 if (LightDM.Greeter.active) {
203 greeter.startUnlock()
208 onFocusedApplicationIdChanged: {
209 if (greeter.hasLockedApp && greeter.lockedApp !== ApplicationManager.focusedApplicationId) {
210 greeter.startUnlock()
212 panel.indicators.hide();
215 onApplicationAdded: {
216 if (greeter.shown && appId != "unity8-dash") {
217 greeter.startUnlock()
219 if (greeter.narrowMode && greeter.hasLockedApp && appId === greeter.lockedApp) {
220 lockscreen.hide() // show locked app
227 id: applicationsDisplayLoader
228 objectName: "applicationsDisplayLoader"
231 // When we have a locked app, we only want to show that one app.
232 // FIXME: do this in a less traumatic way. We currently only allow
233 // locked apps in phone mode (see FIXME in Lockscreen component in
234 // this same file). When that changes, we need to do something
235 // nicer here. But this code is currently just to prevent a
236 // theoretical attack where user enters lockedApp mode, then makes
237 // the screen larger (maybe connects to monitor) and tries to enter
239 property bool tabletMode: shell.sideStageEnabled && !greeter.hasLockedApp
240 source: tabletMode ? "Stages/TabletStage.qml" : "Stages/PhoneStage.qml"
243 target: applicationsDisplayLoader.item
244 property: "objectName"
248 target: applicationsDisplayLoader.item
249 property: "dragAreaWidth"
250 value: shell.edgeSize
253 target: applicationsDisplayLoader.item
254 property: "maximizedAppTopMargin"
255 // Not just using panel.panelHeight as that changes depending on the focused app.
256 value: panel.indicators.minimizedPanelHeight + units.dp(2) // dp(2) for orange line
259 target: applicationsDisplayLoader.item
260 property: "interactive"
261 value: edgeDemo.stagesEnabled && !greeter.shown && !lockscreen.shown && panel.indicators.fullyClosed && launcher.progress == 0 && !notifications.useModal
264 target: applicationsDisplayLoader.item
265 property: "spreadEnabled"
266 value: edgeDemo.stagesEnabled && !greeter.hasLockedApp
269 target: applicationsDisplayLoader.item
270 property: "inverseProgress"
271 value: launcher.progress
274 target: applicationsDisplayLoader.item
275 property: "orientation"
276 value: shell.orientation
283 objectName: "inputMethod"
284 anchors { fill: parent; topMargin: panel.panelHeight }
285 z: notifications.useModal || panel.indicators.shown ? overlay.z + 1 : overlay.z - 1
289 target: SurfaceManager
291 if (surface.type == MirSurfaceItem.InputMethod) {
292 inputMethod.surface = surface;
296 onSurfaceDestroyed: {
297 if (inputMethod.surface == surface) {
298 inputMethod.surface = null;
299 surface.parent = null;
301 if (!surface.parent) {
302 // there's no one displaying it. delete it right away
308 target: SessionManager
310 if (!session.parentSession && !session.application) {
311 // nothing is using it. delete it right away
319 objectName: "lockscreen"
321 hides: [launcher, panel.indicators]
324 showAnimation: StandardAnimation { property: "opacity"; to: 1 }
325 hideAnimation: StandardAnimation { property: "opacity"; to: 0 }
329 height: parent.height - panel.panelHeight
330 background: shell.background
331 alphaNumeric: AccountsService.passwordDisplayHint === AccountsService.Keyboard
335 // FIXME: We *should* show emergency dialer if there is a SIM present,
336 // regardless of whether the side stage is enabled. But right now,
337 // the assumption is that narrow screens are phones which have SIMs
338 // and wider screens are tablets which don't. When we do allow this
339 // on devices with a side stage and a SIM, work should be done to
340 // ensure that the main stage is disabled while the dialer is present
341 // in the side stage. See the FIXME in the stage loader in this file.
342 showEmergencyCallButton: !shell.sideStageEnabled
344 onEntered: LightDM.Greeter.respond(passphrase);
345 onCancel: greeter.show()
346 onEmergencyCall: startLockedApp("dialer-app")
348 onShownChanged: if (shown) greeter.lockedApp = ""
350 function maybeShow() {
351 if (!shell.forcedUnlock) {
360 if (lockscreen.delayMinutes > 0) {
361 lockscreen.delayMinutes -= 1
362 if (lockscreen.delayMinutes > 0) {
369 Component.onCompleted: {
370 if (greeter.narrowMode) {
371 LightDM.Greeter.authenticate(LightDM.Users.data(0, LightDM.UserRoles.NameRole))
377 target: LightDM.Greeter
379 onShowGreeter: greeter.show()
380 onHideGreeter: greeter.login()
383 shell.enabled = true;
384 if (!LightDM.Greeter.active) {
385 return; // could happen if hideGreeter() comes in before we prompt
387 if (greeter.narrowMode) {
388 if (isDefaultPrompt) {
389 if (lockscreen.alphaNumeric) {
390 lockscreen.infoText = i18n.tr("Enter passphrase")
391 lockscreen.errorText = i18n.tr("Sorry, incorrect passphrase") + "\n" +
392 i18n.tr("Please re-enter")
394 lockscreen.infoText = i18n.tr("Enter passcode")
395 lockscreen.errorText = i18n.tr("Sorry, incorrect passcode")
398 lockscreen.infoText = i18n.tr("Enter %1").arg(text.toLowerCase())
399 lockscreen.errorText = i18n.tr("Sorry, incorrect %1").arg(text.toLowerCase())
402 lockscreen.maybeShow();
406 onPromptlessChanged: {
407 if (!LightDM.Greeter.active) {
408 return; // could happen if hideGreeter() comes in before we prompt
410 if (greeter.narrowMode) {
411 if (LightDM.Greeter.promptless && LightDM.Greeter.authenticated) {
415 lockscreen.maybeShow();
420 onAuthenticationComplete: {
421 shell.enabled = true;
422 if (LightDM.Greeter.authenticated) {
423 AccountsService.failedLogins = 0
425 // Else only penalize user for a failed login if they actually were
426 // prompted for a password. We do this below after the promptless
429 if (LightDM.Greeter.promptless) {
433 if (LightDM.Greeter.authenticated) {
436 AccountsService.failedLogins++
437 if (maxFailedLogins >= 2) { // require at least a warning
438 if (AccountsService.failedLogins === maxFailedLogins - 1) {
439 var title = lockscreen.alphaNumeric ?
440 i18n.tr("Sorry, incorrect passphrase.") :
441 i18n.tr("Sorry, incorrect passcode.")
442 var text = i18n.tr("This will be your last attempt.") + " " +
443 (lockscreen.alphaNumeric ?
444 i18n.tr("If passphrase is entered incorrectly, your phone will conduct a factory reset and all personal data will be deleted.") :
445 i18n.tr("If passcode is entered incorrectly, your phone will conduct a factory reset and all personal data will be deleted."))
446 lockscreen.showInfoPopup(title, text)
447 } else if (AccountsService.failedLogins >= maxFailedLogins) {
448 SystemImage.factoryReset() // Ouch!
451 if (failedLoginsDelayAttempts > 0 && AccountsService.failedLogins % failedLoginsDelayAttempts == 0) {
452 lockscreen.delayMinutes = failedLoginsDelayMinutes
453 forcedDelayTimer.start()
456 lockscreen.clear(true);
457 if (greeter.narrowMode) {
458 LightDM.Greeter.authenticate(LightDM.Users.data(0, LightDM.UserRoles.NameRole))
465 target: LightDM.Greeter
467 value: greeter.shown || lockscreen.shown || greeter.hasLockedApp
473 opacity: greeterWrapper.showProgress * 0.8
477 // Just a tiny wrapper to adjust greeter's x without messing with its own dragging
479 objectName: "greeterWrapper"
480 x: greeter.narrowMode ? launcher.progress : 0
483 height: parent.height - panel.panelHeight
486 enabled: !launcher.dashSwipe
490 property bool fullyShown: showProgress === 1.0
491 onFullyShownChanged: {
492 // Wait until the greeter is completely covering lockscreen before resetting it.
493 if (greeter.narrowMode && fullyShown && !LightDM.Greeter.authenticated) {
495 lockscreen.maybeShow();
499 readonly property real showProgress: MathUtils.clamp((1 - x/width) + greeter.showProgress - 1, 0, 1)
500 onShowProgressChanged: {
501 if (showProgress === 0) {
502 if ((LightDM.Greeter.promptless && LightDM.Greeter.authenticated) || shell.forcedUnlock) {
504 } else if (greeter.narrowMode) {
505 lockscreen.clear(false) // to reset focus if necessary
512 objectName: "greeter"
514 signal sessionStarted() // helpful for tests
516 property string lockedApp: ""
517 property bool hasLockedApp: lockedApp !== ""
520 hides: [launcher, panel.indicators]
522 loadContent: required || lockscreen.required // keeps content in memory for quick show()
526 defaultBackground: shell.background
529 height: parent.height
531 dragHandleWidth: shell.edgeSize
533 function startUnlock() {
535 if (!LightDM.Greeter.authenticated) {
536 lockscreen.maybeShow()
547 if (LightDM.Greeter.startSessionSync()) {
558 // Disable everything so that user can't swipe greeter or
559 // launcher until we get first prompt/authenticate, which
560 // will re-enable the shell.
561 shell.enabled = false;
563 if (greeter.narrowMode) {
564 LightDM.Greeter.authenticate(LightDM.Users.data(0, LightDM.UserRoles.NameRole));
568 greeter.lockedApp = "";
569 greeter.forceActiveFocus();
573 Component.onCompleted: {
574 Connectivity.unlockAllModems()
577 onUnlocked: greeter.hide()
579 // Update launcher items for new user
580 var user = LightDM.Users.data(uid, LightDM.UserRoles.NameRole);
581 AccountsService.user = user;
582 LauncherModel.setUser(user);
585 onTease: launcher.tease()
588 target: ApplicationManager
589 property: "suspended"
590 value: greeter.shown && greeterWrapper.showProgress == 1
600 if (shell.locked && callManager.hasCalls) {
601 // We just received an incoming call while locked. The
602 // indicator will have already launched dialer-app for us, but
603 // there is a race between "hasCalls" changing and the dialer
604 // starting up. So in case we lose that race, we'll start/
605 // focus the dialer ourselves here too. Even if the indicator
606 // didn't launch the dialer for some reason (or maybe a call
607 // started via some other means), if an active call is
608 // happening, we want to be in the dialer.
609 startLockedApp("dialer-app")
619 if (Powerd.status === Powerd.Off && reason !== Powerd.Proximity &&
620 !callManager.hasCalls && !edgeDemo.running) {
626 function showHome() {
627 if (edgeDemo.running) {
631 if (LightDM.Greeter.active) {
632 greeter.startUnlock()
635 var animate = !LightDM.Greeter.active && !stages.shown
636 dash.setCurrentScope(0, animate, false)
637 ApplicationManager.requestFocusApplication("unity8-dash")
640 function showDash() {
641 if (greeter.hasLockedApp || // just in case user gets here
642 (!greeter.narrowMode && shell.locked)) {
651 if (ApplicationManager.focusedApplicationId != "unity8-dash") {
652 ApplicationManager.requestFocusApplication("unity8-dash")
666 anchors.fill: parent //because this draws indicator menus
669 available: edgeDemo.panelEnabled && (!shell.locked || AccountsService.enableIndicatorsWhileLocked) && !greeter.hasLockedApp
670 contentEnabled: edgeDemo.panelContentEnabled
671 width: parent.width > units.gu(60) ? units.gu(40) : parent.width
673 minimizedPanelHeight: units.gu(3)
674 expandedPanelHeight: units.gu(7)
676 indicatorsModel: visibleIndicators.model
680 id: visibleIndicators
681 // TODO: This should be sourced by device type (eg "desktop", "tablet", "phone"...)
682 Component.onCompleted: initialise(indicatorProfile)
685 greeterShown: greeter.shown || lockscreen.shown
688 property bool topmostApplicationIsFullscreen:
689 ApplicationManager.focusedApplicationId &&
690 ApplicationManager.findApplication(ApplicationManager.focusedApplicationId).fullscreen
692 fullscreenMode: (topmostApplicationIsFullscreen && !LightDM.Greeter.active && launcher.progress == 0)
693 || greeter.hasLockedApp
698 objectName: "launcher"
700 readonly property bool dashSwipe: progress > 0
702 anchors.top: parent.top
703 anchors.bottom: parent.bottom
705 dragAreaWidth: shell.edgeSize
706 available: edgeDemo.launcherEnabled && (!shell.locked || AccountsService.enableLauncherWhileLocked) && !greeter.hasLockedApp
708 onShowDashHome: showHome()
710 onDashSwipeChanged: {
712 dash.setCurrentScope(0, false, true)
715 onLauncherApplicationSelected: {
716 if (greeter.hasLockedApp) {
717 greeter.startUnlock()
719 if (!edgeDemo.running)
720 shell.activateApplication(appId)
724 panel.indicators.hide()
730 id: modalNotificationBackground
732 visible: notifications.useModal && (notifications.state == "narrow")
745 model: NotificationBackend.Model
748 y: topmostIsFullscreen ? 0 : panel.panelHeight
750 height: parent.height - (topmostIsFullscreen ? 0 : panel.panelHeight)
755 when: overlay.width <= units.gu(60)
756 AnchorChanges { target: notifications; anchors.left: parent.left }
760 when: overlay.width > units.gu(60)
761 AnchorChanges { target: notifications; anchors.left: undefined }
762 PropertyChanges { target: notifications; width: units.gu(38) }
773 shutdownFadeOutRectangle.enabled = true;
774 shutdownFadeOutRectangle.visible = true;
775 shutdownFadeOut.start();
780 id: alphaDisclaimerLabel
781 anchors.centerIn: parent
782 visible: ApplicationManager.fake ? ApplicationManager.fake : false
784 text: "EARLY ALPHA\nNOT READY FOR USE"
787 font.weight: Font.Black
788 horizontalAlignment: Text.AlignHCenter
789 verticalAlignment: Text.AlignVCenter
790 fontSizeMode: Text.Fit
792 scale: Math.min(parent.width, parent.height) / width
797 objectName: "edgeDemo"
798 z: alphaDisclaimerLabel.z + 10
799 paused: Powerd.status === Powerd.Off // Saves power
807 target: SessionBroadcast
808 onShowHome: showHome()
812 id: shutdownFadeOutRectangle
819 NumberAnimation on opacity {
824 if (shutdownFadeOutRectangle.enabled && shutdownFadeOutRectangle.visible) {
825 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