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
40import Unity.Notifications 1.0 as NotificationBackend
41import Unity.Session 0.1
42import Unity.DashCommunicator 0.1
43import Unity.Indicators 0.1 as Indicators
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 id: usageModeSettings
134 schema.id: "com.canonical.Unity8"
138 target: LauncherModel
139 property: "applicationManager"
140 value: ApplicationManager
143 Component.onCompleted: {
144 Theme.name = "Ubuntu.Components.Themes.SuruGradient"
145 if (ApplicationManager.count > 0) {
146 ApplicationManager.focusApplication(ApplicationManager.get(0).appId);
148 if (orientationLockEnabled) {
149 orientation = OrientationLock.savedOrientation;
159 objectName: "dashCommunicator"
165 enabled: Powerd.status === Powerd.On
169 target: ApplicationManager
170 property: "forceDashActive"
171 value: launcher.shown || launcher.dashSwipe
176 onVolumeDownPressed: volumeControl.volumeDown()
177 onVolumeUpPressed: volumeControl.volumeUp()
178 onBothVolumeKeysPressed: screenGrabber.capture()
183 // Nokia earpieces give TogglePlayPause, while the iPhone's earpiece gives Play
184 if (event.key == Qt.Key_MediaTogglePlayPause || event.key == Qt.Key_MediaPlay) {
185 event.accepted = callManager.handleMediaKey(false);
186 } else if (event.key == Qt.Key_PowerOff || event.key == Qt.Key_PowerDown) {
187 // FIXME: We only consider power key presses if the screen is
188 // on because of bugs 1410830/1409003. The theory is that when
189 // those bugs are encountered, there is a >2s delay between the
190 // power press event and the power release event, which causes
191 // the shutdown dialog to appear on resume. So to avoid that
192 // symptom while we investigate the root cause, we simply won't
193 // initiate any dialogs when the screen is off.
194 if (Powerd.status === Powerd.On) {
195 dialogs.onPowerKeyPressed();
197 event.accepted = true;
199 volumeKeyFilter.onKeyPressed(event.key);
200 event.accepted = false;
205 if (event.key == Qt.Key_PowerOff || event.key == Qt.Key_PowerDown) {
206 dialogs.onPowerKeyReleased();
207 event.accepted = true;
209 volumeKeyFilter.onKeyReleased(event.key);
210 event.accepted = false;
219 height: parent.height
220 visible: !ApplicationManager.empty
223 target: ApplicationManager
225 if (greeter.narrowMode) {
226 if (appId === "dialer-app" && callManager.hasCalls && shell.locked) {
227 // If we are in the middle of a call, make dialer lockedApp and show it.
228 // This can happen if user backs out of dialer back to greeter, then
229 // launches dialer again.
230 greeter.lockedApp = appId;
232 if (greeter.hasLockedApp) {
233 if (appId === greeter.lockedApp) {
234 lockscreen.hide() // show locked app
236 greeter.startUnlock() // show lockscreen if necessary
241 if (LightDM.Greeter.active) {
242 greeter.startUnlock()
247 onFocusedApplicationIdChanged: {
248 if (greeter.hasLockedApp && greeter.lockedApp !== ApplicationManager.focusedApplicationId) {
249 greeter.startUnlock()
251 panel.indicators.hide();
254 onApplicationAdded: {
255 if (greeter.shown && appId != "unity8-dash") {
256 greeter.startUnlock()
258 // If this happens on first boot, we may be in edge
259 // tutorial or wizard while receiving a call. But a call
260 // is more important than wizard so just bail out of those.
261 if (edgeDemo.running) {
262 edgeDemo.hideEdgeDemos();
266 if (greeter.narrowMode && greeter.hasLockedApp && appId === greeter.lockedApp) {
267 lockscreen.hide() // show locked app
274 id: applicationsDisplayLoader
275 objectName: "applicationsDisplayLoader"
278 // When we have a locked app, we only want to show that one app.
279 // FIXME: do this in a less traumatic way. We currently only allow
280 // locked apps in phone mode (see FIXME in Lockscreen component in
281 // this same file). When that changes, we need to do something
282 // nicer here. But this code is currently just to prevent a
283 // theoretical attack where user enters lockedApp mode, then makes
284 // the screen larger (maybe connects to monitor) and tries to enter
286 property bool tabletMode: shell.sideStageEnabled && !greeter.hasLockedApp
287 source: usageModeSettings.usageMode === "Windowed" ? "Stages/DesktopStage.qml"
288 : tabletMode ? "Stages/TabletStage.qml" : "Stages/PhoneStage.qml"
291 target: applicationsDisplayLoader.item
292 property: "objectName"
296 target: applicationsDisplayLoader.item
297 property: "dragAreaWidth"
298 value: shell.edgeSize
301 target: applicationsDisplayLoader.item
302 property: "maximizedAppTopMargin"
303 // Not just using panel.panelHeight as that changes depending on the focused app.
304 value: panel.indicators.minimizedPanelHeight + units.dp(2) // dp(2) for orange line
307 target: applicationsDisplayLoader.item
308 property: "interactive"
309 value: edgeDemo.stagesEnabled && !greeter.shown && !lockscreen.shown && panel.indicators.fullyClosed && launcher.progress == 0 && !notifications.useModal
312 target: applicationsDisplayLoader.item
313 property: "spreadEnabled"
314 value: edgeDemo.stagesEnabled && !greeter.hasLockedApp
317 target: applicationsDisplayLoader.item
318 property: "inverseProgress"
319 value: launcher.progress
322 target: applicationsDisplayLoader.item
323 property: "orientation"
324 value: shell.orientation
327 target: applicationsDisplayLoader.item
328 property: "background"
329 value: shell.background
336 objectName: "inputMethod"
337 anchors { fill: parent; topMargin: panel.panelHeight }
338 z: notifications.useModal || panel.indicators.shown || wizard.active ? overlay.z + 1 : overlay.z - 1
342 target: SurfaceManager
344 if (surface.type == MirSurfaceItem.InputMethod) {
345 inputMethod.surface = surface;
349 onSurfaceDestroyed: {
350 if (inputMethod.surface == surface) {
351 inputMethod.surface = null;
352 surface.parent = null;
354 if (!surface.parent) {
355 // there's no one displaying it. delete it right away
361 target: SessionManager
363 if (!session.parentSession && !session.application) {
364 // nothing is using it. delete it right away
372 objectName: "lockscreen"
374 hides: [launcher, panel.indicators]
377 showAnimation: StandardAnimation { property: "opacity"; to: 1 }
378 hideAnimation: StandardAnimation { property: "opacity"; to: 0 }
382 height: parent.height - panel.panelHeight
383 background: shell.background
384 darkenBackground: 0.4
385 alphaNumeric: AccountsService.passwordDisplayHint === AccountsService.Keyboard
389 property string promptText
390 infoText: promptText !== "" ? i18n.tr("Enter %1").arg(promptText) :
391 alphaNumeric ? i18n.tr("Enter passphrase") :
392 i18n.tr("Enter passcode")
393 errorText: promptText !== "" ? i18n.tr("Sorry, incorrect %1").arg(promptText) :
394 alphaNumeric ? i18n.tr("Sorry, incorrect passphrase") + "\n" +
395 i18n.tr("Please re-enter") :
396 i18n.tr("Sorry, incorrect passcode")
398 // FIXME: We *should* show emergency dialer if there is a SIM present,
399 // regardless of whether the side stage is enabled. But right now,
400 // the assumption is that narrow screens are phones which have SIMs
401 // and wider screens are tablets which don't. When we do allow this
402 // on devices with a side stage and a SIM, work should be done to
403 // ensure that the main stage is disabled while the dialer is present
404 // in the side stage. See the FIXME in the stage loader in this file.
405 showEmergencyCallButton: !shell.sideStageEnabled
407 onEntered: LightDM.Greeter.respond(passphrase);
408 onCancel: greeter.show()
409 onEmergencyCall: startLockedApp("dialer-app")
411 onShownChanged: if (shown) greeter.lockedApp = ""
413 function maybeShow() {
414 if (!shell.forcedUnlock && !shown) {
423 if (lockscreen.delayMinutes > 0) {
424 lockscreen.delayMinutes -= 1
425 if (lockscreen.delayMinutes > 0) {
432 Component.onCompleted: {
433 if (greeter.narrowMode) {
434 LightDM.Greeter.authenticate(LightDM.Users.data(0, LightDM.UserRoles.NameRole))
440 target: LightDM.Greeter
442 onShowGreeter: greeter.show()
443 onHideGreeter: greeter.login()
446 shell.enabled = true;
447 if (!LightDM.Greeter.active) {
448 return; // could happen if hideGreeter() comes in before we prompt
450 if (greeter.narrowMode) {
451 lockscreen.promptText = isDefaultPrompt ? "" : text.toLowerCase();
452 lockscreen.maybeShow();
456 onPromptlessChanged: {
457 if (!LightDM.Greeter.active) {
458 return; // could happen if hideGreeter() comes in before we prompt
460 if (greeter.narrowMode) {
461 if (LightDM.Greeter.promptless && LightDM.Greeter.authenticated) {
465 lockscreen.maybeShow();
470 onAuthenticationComplete: {
471 shell.enabled = true;
472 if (LightDM.Greeter.authenticated) {
473 AccountsService.failedLogins = 0
475 // Else only penalize user for a failed login if they actually were
476 // prompted for a password. We do this below after the promptless
479 if (LightDM.Greeter.promptless) {
483 if (LightDM.Greeter.authenticated) {
486 AccountsService.failedLogins++
487 if (maxFailedLogins >= 2) { // require at least a warning
488 if (AccountsService.failedLogins === maxFailedLogins - 1) {
489 var title = lockscreen.alphaNumeric ?
490 i18n.tr("Sorry, incorrect passphrase.") :
491 i18n.tr("Sorry, incorrect passcode.")
492 var text = i18n.tr("This will be your last attempt.") + " " +
493 (lockscreen.alphaNumeric ?
494 i18n.tr("If passphrase is entered incorrectly, your phone will conduct a factory reset and all personal data will be deleted.") :
495 i18n.tr("If passcode is entered incorrectly, your phone will conduct a factory reset and all personal data will be deleted."))
496 lockscreen.showInfoPopup(title, text)
497 } else if (AccountsService.failedLogins >= maxFailedLogins) {
498 SystemImage.factoryReset() // Ouch!
501 if (failedLoginsDelayAttempts > 0 && AccountsService.failedLogins % failedLoginsDelayAttempts == 0) {
502 lockscreen.delayMinutes = failedLoginsDelayMinutes
503 forcedDelayTimer.start()
506 lockscreen.clear(true);
507 if (greeter.narrowMode) {
508 LightDM.Greeter.authenticate(LightDM.Users.data(0, LightDM.UserRoles.NameRole))
515 target: LightDM.Greeter
517 value: greeter.shown || lockscreen.shown || greeter.hasLockedApp
523 opacity: greeterWrapper.showProgress * 0.8
527 // Just a tiny wrapper to adjust greeter's x without messing with its own dragging
529 objectName: "greeterWrapper"
530 x: (greeter.narrowMode && greeter.showProgress > 0) ? launcher.progress : 0
533 height: parent.height - panel.panelHeight
536 enabled: !launcher.dashSwipe
540 property bool fullyShown: showProgress === 1.0
541 onFullyShownChanged: {
542 // Wait until the greeter is completely covering lockscreen before resetting it.
543 if (greeter.narrowMode && fullyShown && !LightDM.Greeter.authenticated) {
545 lockscreen.maybeShow();
549 readonly property real showProgress: MathUtils.clamp((1 - x/width) + greeter.showProgress - 1, 0, 1)
550 onShowProgressChanged: {
551 if (showProgress === 0) {
552 if ((LightDM.Greeter.promptless && LightDM.Greeter.authenticated) || shell.forcedUnlock) {
554 } else if (greeter.narrowMode) {
555 lockscreen.clear(false) // to reset focus if necessary
562 objectName: "greeter"
564 signal sessionStarted() // helpful for tests
566 property string lockedApp: ""
567 property bool hasLockedApp: lockedApp !== ""
569 hides: [launcher, panel.indicators]
570 loadContent: required || lockscreen.required // keeps content in memory for quick show()
574 background: shell.background
577 height: parent.height
580 // avoid overlapping with Launcher's edge drag area
581 // FIXME: Fix TouchRegistry & friends and remove this workaround
582 // Issue involves launcher's DDA getting disabled on a long
584 dragHandleLeftMargin: launcher.available ? launcher.dragAreaWidth + 1 : 0
586 function startUnlock() {
588 if (!LightDM.Greeter.authenticated) {
589 lockscreen.maybeShow()
600 if (LightDM.Greeter.startSessionSync()) {
610 // See powerConnection for why this is useful
611 id: showGreeterDelayed
620 // Disable everything so that user can't swipe greeter or
621 // launcher until we get the next prompt/authenticate, which
622 // will re-enable the shell.
623 shell.enabled = false;
625 if (greeter.narrowMode) {
626 LightDM.Greeter.authenticate(LightDM.Users.data(0, LightDM.UserRoles.NameRole));
630 greeter.lockedApp = "";
631 greeter.forceActiveFocus();
635 Component.onCompleted: {
636 Connectivity.unlockAllModems()
639 onUnlocked: greeter.hide()
641 // Update launcher items for new user
642 var user = LightDM.Users.data(uid, LightDM.UserRoles.NameRole);
643 AccountsService.user = user;
644 LauncherModel.setUser(user);
647 onTapped: launcher.tease()
655 target: ApplicationManager
656 property: "suspended"
657 value: (greeter.shown && greeterWrapper.showProgress == 1) || lockscreen.shown
667 if (shell.locked && callManager.hasCalls && greeter.lockedApp !== "dialer-app") {
668 // We just received an incoming call while locked. The
669 // indicator will have already launched dialer-app for us, but
670 // there is a race between "hasCalls" changing and the dialer
671 // starting up. So in case we lose that race, we'll start/
672 // focus the dialer ourselves here too. Even if the indicator
673 // didn't launch the dialer for some reason (or maybe a call
674 // started via some other means), if an active call is
675 // happening, we want to be in the dialer.
676 startLockedApp("dialer-app")
686 if (Powerd.status === Powerd.Off && reason !== Powerd.Proximity &&
687 !callManager.hasCalls && !edgeDemo.running) {
688 // We don't want to simply call greeter.showNow() here, because
689 // that will take too long. Qt will delay button event
690 // handling until the greeter is done loading and may think the
691 // user held down the power button the whole time, leading to a
692 // power dialog being shown. Instead, delay showing the
693 // greeter until we've finished handling the event. We could
694 // make the greeter load asynchronously instead, but that
695 // introduces a whole host of timing issues, especially with
696 // its animations. So this is simpler.
697 showGreeterDelayed.start();
702 function showHome() {
703 if (edgeDemo.running) {
707 if (LightDM.Greeter.active) {
708 greeter.startUnlock()
711 var animate = !LightDM.Greeter.active && !stages.shown
712 dash.setCurrentScope(0, animate, false)
713 ApplicationManager.requestFocusApplication("unity8-dash")
716 function showDash() {
717 if (greeter.hasLockedApp || // just in case user gets here
718 (!greeter.narrowMode && shell.locked)) {
727 if (ApplicationManager.focusedApplicationId != "unity8-dash") {
728 ApplicationManager.requestFocusApplication("unity8-dash")
742 anchors.fill: parent //because this draws indicator menus
745 available: edgeDemo.panelEnabled && (!shell.locked || AccountsService.enableIndicatorsWhileLocked) && !greeter.hasLockedApp
746 contentEnabled: edgeDemo.panelContentEnabled
747 width: parent.width > units.gu(60) ? units.gu(40) : parent.width
749 minimizedPanelHeight: units.gu(3)
750 expandedPanelHeight: units.gu(7)
752 indicatorsModel: Indicators.IndicatorsModel {
753 Component.onCompleted: load(indicatorProfile);
757 greeterShown: greeter.shown || lockscreen.shown
760 property bool topmostApplicationIsFullscreen:
761 ApplicationManager.focusedApplicationId &&
762 ApplicationManager.findApplication(ApplicationManager.focusedApplicationId).fullscreen
764 fullscreenMode: (topmostApplicationIsFullscreen && !LightDM.Greeter.active && launcher.progress == 0)
765 || greeter.hasLockedApp
770 objectName: "launcher"
772 readonly property bool dashSwipe: progress > 0
774 anchors.top: parent.top
775 anchors.topMargin: inverted ? 0 : panel.panelHeight
776 anchors.bottom: parent.bottom
778 dragAreaWidth: shell.edgeSize
779 available: edgeDemo.launcherEnabled && (!shell.locked || AccountsService.enableLauncherWhileLocked) && !greeter.hasLockedApp
780 inverted: usageModeSettings.usageMode === "Staged"
782 onShowDashHome: showHome()
784 onDashSwipeChanged: {
786 dash.setCurrentScope(0, false, true)
789 onLauncherApplicationSelected: {
790 if (greeter.hasLockedApp) {
791 greeter.startUnlock()
793 if (!edgeDemo.running)
794 shell.activateApplication(appId)
798 panel.indicators.hide()
806 background: shell.background
810 id: modalNotificationBackground
812 visible: notifications.useModal && (notifications.state == "narrow")
825 model: NotificationBackend.Model
828 y: topmostIsFullscreen ? 0 : panel.panelHeight
829 height: parent.height - (topmostIsFullscreen ? 0 : panel.panelHeight)
834 when: overlay.width <= units.gu(60)
836 target: notifications
837 anchors.left: parent.left
838 anchors.right: parent.right
843 when: overlay.width > units.gu(60)
845 target: notifications
846 anchors.left: undefined
847 anchors.right: parent.right
849 PropertyChanges { target: notifications; width: units.gu(38) }
860 shutdownFadeOutRectangle.enabled = true;
861 shutdownFadeOutRectangle.visible = true;
862 shutdownFadeOut.start();
868 objectName: "edgeDemo"
870 paused: Powerd.status === Powerd.Off || wizard.active // Saves power
878 target: SessionBroadcast
879 onShowHome: showHome()
883 id: shutdownFadeOutRectangle
890 NumberAnimation on opacity {
895 if (shutdownFadeOutRectangle.enabled && shutdownFadeOutRectangle.visible) {
896 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