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 Unity.Notifications 1.0 as NotificationBackend
40import Unity.Session 0.1
41import Unity.DashCommunicator 0.1
46 // this is only here to select the width / height of the window if not running fullscreen
47 property bool tablet: false
48 width: tablet ? units.gu(160) : applicationArguments.hasGeometry() ? applicationArguments.width() : units.gu(40)
49 height: tablet ? units.gu(100) : applicationArguments.hasGeometry() ? applicationArguments.height() : units.gu(71)
51 property real edgeSize: units.gu(2)
52 property url defaultBackground: Qt.resolvedUrl(shell.width >= units.gu(60) ? "graphics/tablet_background.jpg" : "graphics/phone_background.jpg")
53 property url background
54 readonly property real panelHeight: panel.panelHeight
56 readonly property bool locked: LightDM.Greeter.active && !LightDM.Greeter.authenticated && !forcedUnlock
57 readonly property bool forcedUnlock: edgeDemo.running
58 onForcedUnlockChanged: if (forcedUnlock) lockscreen.hide()
60 property bool sideStageEnabled: shell.width >= units.gu(100)
61 readonly property string focusedApplicationId: ApplicationManager.focusedApplicationId
63 property int maxFailedLogins: -1 // disabled by default for now, will enable via settings in future
64 property int failedLoginsDelayAttempts: 7 // number of failed logins
65 property int failedLoginsDelayMinutes: 5 // minutes of forced waiting
67 property int orientation
68 readonly property int deviceOrientationAngle: Screen.angleBetween(Screen.primaryOrientation, Screen.orientation)
69 onDeviceOrientationAngleChanged: {
70 if (!OrientationLock.enabled) {
71 orientation = Screen.orientation;
74 readonly property bool orientationLockEnabled: OrientationLock.enabled
75 onOrientationLockEnabledChanged: {
76 if (orientationLockEnabled) {
77 OrientationLock.savedOrientation = Screen.orientation;
79 orientation = Screen.orientation;
83 function activateApplication(appId) {
84 if (ApplicationManager.findApplication(appId)) {
85 ApplicationManager.requestFocusApplication(appId);
87 var execFlags = shell.sideStageEnabled ? ApplicationManager.NoFlag : ApplicationManager.ForceMainStage;
88 ApplicationManager.startApplication(appId, execFlags);
92 function setLockedApp(app) {
94 greeter.lockedApp = app
100 target: LauncherModel
101 property: "applicationManager"
102 value: ApplicationManager
105 Component.onCompleted: {
106 Theme.name = "Ubuntu.Components.Themes.SuruGradient"
107 if (ApplicationManager.count > 0) {
108 ApplicationManager.focusApplication(ApplicationManager.get(0).appId);
110 if (orientationLockEnabled) {
111 orientation = OrientationLock.savedOrientation;
116 id: backgroundSettings
117 schema.id: "org.gnome.desktop.background"
119 property url gSettingsPicture: backgroundSettings.pictureUri != undefined && backgroundSettings.pictureUri.length > 0 ? backgroundSettings.pictureUri : shell.defaultBackground
120 onGSettingsPictureChanged: {
121 shell.background = gSettingsPicture
130 objectName: "dashCommunicator"
134 // Handle but do not filter out volume keys
135 Keys.onVolumeUpPressed: { volumeControl.volumeUp(); event.accepted = false; }
136 Keys.onVolumeDownPressed: { volumeControl.volumeDown(); event.accepted = false; }
139 if (event.key == Qt.Key_PowerOff || event.key == Qt.Key_PowerDown) {
140 dialogs.onPowerKeyPressed();
141 event.accepted = true;
143 event.accepted = false;
148 if (event.key == Qt.Key_PowerOff || event.key == Qt.Key_PowerDown) {
149 dialogs.onPowerKeyReleased();
150 event.accepted = true;
152 event.accepted = false;
161 height: parent.height
162 visible: !ApplicationManager.empty
165 target: ApplicationManager
167 if (greeter.narrowMode) {
168 if (appId === "dialer-app") {
169 // Always let the dialer-app through. Either user asked
170 // for an emergency call or accepted an incoming call.
172 } else if (greeter.hasLockedApp && greeter.lockedApp !== appId) {
173 greeter.startUnlock()
178 if (LightDM.Greeter.active) {
179 greeter.startUnlock()
184 onFocusedApplicationIdChanged: {
185 if (greeter.hasLockedApp && greeter.lockedApp !== ApplicationManager.focusedApplicationId) {
186 greeter.startUnlock()
188 panel.indicators.hide();
191 onApplicationAdded: {
192 if (greeter.shown && appId != "unity8-dash") {
193 greeter.startUnlock()
195 if (greeter.narrowMode && appId === "dialer-app") {
196 // Always let the dialer-app through. Either user asked
197 // for an emergency call or accepted an incoming call.
205 id: applicationsDisplayLoader
206 objectName: "applicationsDisplayLoader"
209 // When we have a locked app, we only want to show that one app
210 property bool tabletMode: shell.sideStageEnabled && !greeter.hasLockedApp
211 source: tabletMode ? "Stages/TabletStage.qml" : "Stages/PhoneStage.qml"
214 target: applicationsDisplayLoader.item
215 property: "objectName"
219 target: applicationsDisplayLoader.item
220 property: "dragAreaWidth"
221 value: shell.edgeSize
224 target: applicationsDisplayLoader.item
225 property: "maximizedAppTopMargin"
226 // Not just using panel.panelHeight as that changes depending on the focused app.
227 value: panel.indicators.panelHeight
230 target: applicationsDisplayLoader.item
231 property: "interactive"
232 value: edgeDemo.stagesEnabled && !greeter.shown && !lockscreen.shown && panel.indicators.fullyClosed && launcher.progress == 0
235 target: applicationsDisplayLoader.item
236 property: "spreadEnabled"
237 value: edgeDemo.stagesEnabled && !greeter.hasLockedApp
240 target: applicationsDisplayLoader.item
241 property: "inverseProgress"
242 value: launcher.progress
245 target: applicationsDisplayLoader.item
246 property: "orientation"
247 value: shell.orientation
254 objectName: "inputMethod"
255 anchors { fill: parent; topMargin: panel.panelHeight }
256 z: notifications.useModal || panel.indicators.shown ? overlay.z + 1 : overlay.z - 1
260 target: SurfaceManager
262 if (surface.type == MirSurfaceItem.InputMethod) {
263 inputMethod.surface = surface;
267 onSurfaceDestroyed: {
268 if (inputMethod.surface == surface) {
269 inputMethod.surface = null;
270 surface.parent = null;
272 if (!surface.parent) {
273 // there's no one displaying it. delete it right away
279 target: SessionManager
281 if (!session.parentSession && !session.application) {
282 // nothing is using it. delete it right away
290 objectName: "lockscreen"
292 hides: [launcher, panel.indicators]
295 showAnimation: StandardAnimation { property: "opacity"; to: 1 }
296 hideAnimation: StandardAnimation { property: "opacity"; to: 0 }
300 height: parent.height - panel.panelHeight
301 background: shell.background
302 alphaNumeric: AccountsService.passwordDisplayHint === AccountsService.Keyboard
306 // FIXME: We *should* show emergency dialer if there is a SIM present,
307 // regardless of whether the side stage is enabled. But right now,
308 // the assumption is that narrow screens are phones which have SIMs
309 // and wider screens are tablets which don't. When we do allow this
310 // on devices with a side stage and a SIM, work should be done to
311 // ensure that the main stage is disabled while the dialer is present
312 // in the side stage.
313 showEmergencyCallButton: !shell.sideStageEnabled
315 onEntered: LightDM.Greeter.respond(passphrase);
316 onCancel: greeter.show()
317 onEmergencyCall: shell.activateApplication("dialer-app") // will automatically enter locked-app mode
319 onShownChanged: if (shown) greeter.lockedApp = ""
321 function maybeShow() {
322 if (!shell.forcedUnlock) {
331 if (lockscreen.delayMinutes > 0) {
332 lockscreen.delayMinutes -= 1
333 if (lockscreen.delayMinutes > 0) {
340 Component.onCompleted: {
341 if (greeter.narrowMode) {
342 LightDM.Greeter.authenticate(LightDM.Users.data(0, LightDM.UserRoles.NameRole))
348 target: LightDM.Greeter
350 onShowGreeter: greeter.show()
351 onHideGreeter: greeter.login()
354 if (greeter.narrowMode) {
355 if (isDefaultPrompt) {
356 if (lockscreen.alphaNumeric) {
357 lockscreen.infoText = i18n.tr("Enter passphrase")
358 lockscreen.errorText = i18n.tr("Sorry, incorrect passphrase") + "\n" +
359 i18n.tr("Please re-enter")
361 lockscreen.infoText = i18n.tr("Enter passcode")
362 lockscreen.errorText = i18n.tr("Sorry, incorrect passcode")
365 lockscreen.infoText = i18n.tr("Enter %1").arg(text.toLowerCase())
366 lockscreen.errorText = i18n.tr("Sorry, incorrect %1").arg(text.toLowerCase())
369 lockscreen.maybeShow();
373 onPromptlessChanged: {
374 if (greeter.narrowMode) {
375 if (LightDM.Greeter.promptless && LightDM.Greeter.authenticated) {
379 lockscreen.maybeShow();
384 onAuthenticationComplete: {
385 if (LightDM.Greeter.authenticated) {
386 AccountsService.failedLogins = 0
388 // Else only penalize user for a failed login if they actually were
389 // prompted for a password. We do this below after the promptless
392 if (LightDM.Greeter.promptless) {
396 if (LightDM.Greeter.authenticated) {
399 AccountsService.failedLogins++
400 if (maxFailedLogins >= 2) { // require at least a warning
401 if (AccountsService.failedLogins === maxFailedLogins - 1) {
402 var title = lockscreen.alphaNumeric ?
403 i18n.tr("Sorry, incorrect passphrase.") :
404 i18n.tr("Sorry, incorrect passcode.")
405 var text = i18n.tr("This will be your last attempt.") + " " +
406 (lockscreen.alphaNumeric ?
407 i18n.tr("If passphrase is entered incorrectly, your phone will conduct a factory reset and all personal data will be deleted.") :
408 i18n.tr("If passcode is entered incorrectly, your phone will conduct a factory reset and all personal data will be deleted."))
409 lockscreen.showInfoPopup(title, text)
410 } else if (AccountsService.failedLogins >= maxFailedLogins) {
411 SystemImage.factoryReset() // Ouch!
414 if (failedLoginsDelayAttempts > 0 && AccountsService.failedLogins % failedLoginsDelayAttempts == 0) {
415 lockscreen.delayMinutes = failedLoginsDelayMinutes
416 forcedDelayTimer.start()
419 lockscreen.clear(true);
420 if (greeter.narrowMode) {
421 LightDM.Greeter.authenticate(LightDM.Users.data(0, LightDM.UserRoles.NameRole))
428 target: LightDM.Greeter
430 value: greeter.shown || lockscreen.shown || greeter.hasLockedApp
436 opacity: greeterWrapper.showProgress * 0.8
440 // Just a tiny wrapper to adjust greeter's x without messing with its own dragging
442 x: greeter.narrowMode ? launcher.progress : 0
445 height: parent.height - panel.panelHeight
448 enabled: !launcher.dashSwipe
452 property bool fullyShown: showProgress === 1.0
453 onFullyShownChanged: {
454 // Wait until the greeter is completely covering lockscreen before resetting it.
455 if (greeter.narrowMode && fullyShown && !LightDM.Greeter.authenticated) {
457 lockscreen.maybeShow();
461 readonly property real showProgress: MathUtils.clamp((1 - x/width) + greeter.showProgress - 1, 0, 1)
462 onShowProgressChanged: {
463 if (showProgress === 0) {
464 if ((LightDM.Greeter.promptless && LightDM.Greeter.authenticated) || shell.forcedUnlock) {
466 } else if (greeter.narrowMode) {
467 lockscreen.clear(false) // to reset focus if necessary
474 objectName: "greeter"
476 signal sessionStarted() // helpful for tests
478 property string lockedApp: ""
479 property bool hasLockedApp: lockedApp !== ""
482 hides: [launcher, panel.indicators]
484 loadContent: required || lockscreen.required // keeps content in memory for quick show()
488 defaultBackground: shell.background
491 height: parent.height
493 dragHandleWidth: shell.edgeSize
495 function startUnlock() {
497 if (!LightDM.Greeter.authenticated) {
498 lockscreen.maybeShow()
509 if (LightDM.Greeter.startSessionSync()) {
520 if (greeter.narrowMode) {
521 LightDM.Greeter.authenticate(LightDM.Users.data(0, LightDM.UserRoles.NameRole));
525 greeter.lockedApp = "";
526 greeter.forceActiveFocus();
530 /* TODO re-enable when the corresponding changes in the service land (LP: #1361074)
531 Component.onCompleted: {
532 Connectivity.unlockAllModems()
535 onUnlocked: greeter.hide()
537 // Update launcher items for new user
538 var user = LightDM.Users.data(uid, LightDM.UserRoles.NameRole);
539 AccountsService.user = user;
540 LauncherModel.setUser(user);
543 onTease: launcher.tease()
546 target: ApplicationManager
547 property: "suspended"
548 value: greeter.shown && greeterWrapper.showProgress == 1
558 if (Powerd.status === Powerd.Off && !callManager.hasCalls && !edgeDemo.running) {
564 function showHome() {
565 if (edgeDemo.running) {
569 if (LightDM.Greeter.active) {
570 greeter.startUnlock()
573 var animate = !LightDM.Greeter.active && !stages.shown
574 dash.setCurrentScope("clickscope", animate, false)
575 ApplicationManager.requestFocusApplication("unity8-dash")
578 function showDash() {
579 if (greeter.hasLockedApp) { // just in case user gets here
588 if (ApplicationManager.focusedApplicationId != "unity8-dash") {
589 ApplicationManager.requestFocusApplication("unity8-dash")
603 anchors.fill: parent //because this draws indicator menus
606 available: edgeDemo.panelEnabled && (!shell.locked || AccountsService.enableIndicatorsWhileLocked) && !greeter.hasLockedApp
607 contentEnabled: edgeDemo.panelContentEnabled
608 width: parent.width > units.gu(60) ? units.gu(40) : parent.width
609 panelHeight: units.gu(3)
612 property bool topmostApplicationIsFullscreen:
613 ApplicationManager.focusedApplicationId &&
614 ApplicationManager.findApplication(ApplicationManager.focusedApplicationId).fullscreen
616 fullscreenMode: (topmostApplicationIsFullscreen && !LightDM.Greeter.active && launcher.progress == 0)
617 || greeter.hasLockedApp
622 objectName: "launcher"
624 readonly property bool dashSwipe: progress > 0
626 anchors.top: parent.top
627 anchors.bottom: parent.bottom
629 dragAreaWidth: shell.edgeSize
630 available: edgeDemo.launcherEnabled && (!shell.locked || AccountsService.enableLauncherWhileLocked) && !greeter.hasLockedApp
632 onShowDashHome: showHome()
634 onDashSwipeChanged: {
636 dash.setCurrentScope("clickscope", false, true)
639 onLauncherApplicationSelected: {
640 if (greeter.hasLockedApp) {
641 greeter.startUnlock()
643 if (!edgeDemo.running)
644 shell.activateApplication(appId)
648 panel.indicators.hide()
654 id: modalNotificationBackground
656 visible: notifications.useModal && !greeter.shown && (notifications.state == "narrow")
669 model: NotificationBackend.Model
674 height: parent.height - panel.panelHeight
679 when: overlay.width <= units.gu(60)
680 AnchorChanges { target: notifications; anchors.left: parent.left }
684 when: overlay.width > units.gu(60)
685 AnchorChanges { target: notifications; anchors.left: undefined }
686 PropertyChanges { target: notifications; width: units.gu(38) }
697 shutdownFadeOutRectangle.enabled = true;
698 shutdownFadeOutRectangle.visible = true;
699 shutdownFadeOut.start();
704 id: alphaDisclaimerLabel
705 anchors.centerIn: parent
706 visible: ApplicationManager.fake ? ApplicationManager.fake : false
708 text: "EARLY ALPHA\nNOT READY FOR USE"
711 font.weight: Font.Black
712 horizontalAlignment: Text.AlignHCenter
713 verticalAlignment: Text.AlignVCenter
714 fontSizeMode: Text.Fit
716 scale: Math.min(parent.width, parent.height) / width
721 objectName: "edgeDemo"
722 z: alphaDisclaimerLabel.z + 10
723 paused: Powerd.status === Powerd.Off // Saves power
726 indicators: panel.indicators
731 target: SessionBroadcast
732 onShowHome: showHome()
736 id: shutdownFadeOutRectangle
743 NumberAnimation on opacity {
748 if (shutdownFadeOutRectangle.enabled && shutdownFadeOutRectangle.visible) {
749 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