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
41import Unity.Notifications 1.0 as NotificationBackend
42import Unity.Session 0.1
43import Unity.DashCommunicator 0.1
44import Unity.Indicators 0.1 as Indicators
49 // Disable everything so that user can't swipe greeter or launcher until
50 // we get first prompt/authenticate, which will re-enable the shell.
53 // this is only here to select the width / height of the window if not running fullscreen
54 property bool tablet: false
55 width: tablet ? units.gu(160) : applicationArguments.hasGeometry() ? applicationArguments.width() : units.gu(40)
56 height: tablet ? units.gu(100) : applicationArguments.hasGeometry() ? applicationArguments.height() : units.gu(71)
58 property real edgeSize: units.gu(2)
59 property url defaultBackground: Qt.resolvedUrl(shell.width >= units.gu(60) ? "graphics/tablet_background.jpg" : "graphics/phone_background.jpg")
60 property url background: asImageTester.status == Image.Ready ? asImageTester.source
61 : gsImageTester.status == Image.Ready ? gsImageTester.source : defaultBackground
62 readonly property real panelHeight: panel.panelHeight
64 readonly property bool locked: LightDM.Greeter.active && !LightDM.Greeter.authenticated && !forcedUnlock
65 readonly property alias hasLockedApp: greeter.hasLockedApp
66 readonly property bool forcedUnlock: tutorial.running
67 onForcedUnlockChanged: if (forcedUnlock) lockscreen.hide()
69 property bool sideStageEnabled: shell.width >= units.gu(100)
70 readonly property string focusedApplicationId: ApplicationManager.focusedApplicationId
72 property int maxFailedLogins: -1 // disabled by default for now, will enable via settings in future
73 property int failedLoginsDelayAttempts: 7 // number of failed logins
74 property int failedLoginsDelayMinutes: 5 // minutes of forced waiting
76 property int orientation
77 readonly property int deviceOrientationAngle: Screen.angleBetween(Screen.primaryOrientation, Screen.orientation)
78 onDeviceOrientationAngleChanged: {
79 if (!OrientationLock.enabled) {
80 orientation = Screen.orientation;
83 readonly property bool orientationLockEnabled: OrientationLock.enabled
84 onOrientationLockEnabledChanged: {
85 if (orientationLockEnabled) {
86 OrientationLock.savedOrientation = Screen.orientation;
88 orientation = Screen.orientation;
92 function activateApplication(appId) {
93 if (ApplicationManager.findApplication(appId)) {
94 ApplicationManager.requestFocusApplication(appId);
96 var execFlags = shell.sideStageEnabled ? ApplicationManager.NoFlag : ApplicationManager.ForceMainStage;
97 ApplicationManager.startApplication(appId, execFlags);
101 function startLockedApp(app) {
103 greeter.lockedApp = app;
105 shell.activateApplication(app);
108 // This is a dummy image to detect if the custom AS set wallpaper loads successfully.
111 source: AccountsService.backgroundFile != undefined && AccountsService.backgroundFile.length > 0 ? AccountsService.backgroundFile : ""
119 id: backgroundSettings
120 schema.id: "org.gnome.desktop.background"
123 // This is a dummy image to detect if the custom GSettings set wallpaper loads successfully.
126 source: backgroundSettings.pictureUri != undefined && backgroundSettings.pictureUri.length > 0 ? backgroundSettings.pictureUri : ""
134 id: usageModeSettings
135 schema.id: "com.canonical.Unity8"
139 target: LauncherModel
140 property: "applicationManager"
141 value: ApplicationManager
144 Component.onCompleted: {
145 Theme.name = "Ubuntu.Components.Themes.SuruGradient"
146 if (ApplicationManager.count > 0) {
147 ApplicationManager.focusApplication(ApplicationManager.get(0).appId);
149 if (orientationLockEnabled) {
150 orientation = OrientationLock.savedOrientation;
160 objectName: "dashCommunicator"
166 enabled: Powerd.status === Powerd.On
170 target: ApplicationManager
171 property: "forceDashActive"
172 value: launcher.shown || launcher.dashSwipe
177 onVolumeDownPressed: volumeControl.volumeDown()
178 onVolumeUpPressed: volumeControl.volumeUp()
179 onBothVolumeKeysPressed: screenGrabber.capture()
184 // Nokia earpieces give TogglePlayPause, while the iPhone's earpiece gives Play
185 if (event.key == Qt.Key_MediaTogglePlayPause || event.key == Qt.Key_MediaPlay) {
186 event.accepted = callManager.handleMediaKey(false);
187 } else if (event.key == Qt.Key_PowerOff || event.key == Qt.Key_PowerDown) {
188 // FIXME: We only consider power key presses if the screen is
189 // on because of bugs 1410830/1409003. The theory is that when
190 // those bugs are encountered, there is a >2s delay between the
191 // power press event and the power release event, which causes
192 // the shutdown dialog to appear on resume. So to avoid that
193 // symptom while we investigate the root cause, we simply won't
194 // initiate any dialogs when the screen is off.
195 if (Powerd.status === Powerd.On) {
196 dialogs.onPowerKeyPressed();
198 event.accepted = true;
200 volumeKeyFilter.onKeyPressed(event.key);
201 event.accepted = false;
206 if (event.key == Qt.Key_PowerOff || event.key == Qt.Key_PowerDown) {
207 dialogs.onPowerKeyReleased();
208 event.accepted = true;
210 volumeKeyFilter.onKeyReleased(event.key);
211 event.accepted = false;
220 height: parent.height
221 visible: !ApplicationManager.empty
224 target: ApplicationManager
226 if (greeter.narrowMode) {
227 if (appId === "dialer-app" && callManager.hasCalls && shell.locked) {
228 // If we are in the middle of a call, make dialer lockedApp and show it.
229 // This can happen if user backs out of dialer back to greeter, then
230 // launches dialer again.
231 greeter.lockedApp = appId;
233 if (greeter.hasLockedApp) {
234 if (appId === greeter.lockedApp) {
235 lockscreen.hide() // show locked app
237 greeter.startUnlock() // show lockscreen if necessary
242 if (LightDM.Greeter.active) {
243 greeter.startUnlock()
248 onFocusedApplicationIdChanged: {
249 if (greeter.hasLockedApp && greeter.lockedApp !== ApplicationManager.focusedApplicationId) {
250 greeter.startUnlock()
252 panel.indicators.hide();
255 onApplicationAdded: {
256 if (appId != "unity8-dash") {
258 greeter.startUnlock();
261 // If this happens on first boot, we may be in edge
262 // tutorial or wizard while receiving a call. But a call
263 // is more important than wizard so just bail out of those.
264 if (tutorial.running) {
269 if (greeter.narrowMode && greeter.hasLockedApp && appId === greeter.lockedApp) {
270 lockscreen.hide() // show locked app
277 id: applicationsDisplayLoader
278 objectName: "applicationsDisplayLoader"
281 // When we have a locked app, we only want to show that one app.
282 // FIXME: do this in a less traumatic way. We currently only allow
283 // locked apps in phone mode (see FIXME in Lockscreen component in
284 // this same file). When that changes, we need to do something
285 // nicer here. But this code is currently just to prevent a
286 // theoretical attack where user enters lockedApp mode, then makes
287 // the screen larger (maybe connects to monitor) and tries to enter
289 property bool tabletMode: shell.sideStageEnabled && !greeter.hasLockedApp
290 source: usageModeSettings.usageMode === "Windowed" ? "Stages/DesktopStage.qml"
291 : tabletMode ? "Stages/TabletStage.qml" : "Stages/PhoneStage.qml"
294 target: applicationsDisplayLoader.item
295 property: "objectName"
299 target: applicationsDisplayLoader.item
300 property: "dragAreaWidth"
301 value: shell.edgeSize
304 target: applicationsDisplayLoader.item
305 property: "maximizedAppTopMargin"
306 // Not just using panel.panelHeight as that changes depending on the focused app.
307 value: panel.indicators.minimizedPanelHeight + units.dp(2) // dp(2) for orange line
310 target: applicationsDisplayLoader.item
311 property: "interactive"
312 value: tutorial.stagesEnabled && !greeter.shown && !lockscreen.shown && panel.indicators.fullyClosed && launcher.progress == 0 && !notifications.useModal
315 target: applicationsDisplayLoader.item
316 property: "spreadEnabled"
317 value: tutorial.stagesEnabled && !greeter.hasLockedApp
320 target: applicationsDisplayLoader.item
321 property: "inverseProgress"
322 value: launcher.progress
325 target: applicationsDisplayLoader.item
326 property: "orientation"
327 value: shell.orientation
330 target: applicationsDisplayLoader.item
331 property: "background"
332 value: shell.background
339 objectName: "inputMethod"
340 anchors { fill: parent; topMargin: panel.panelHeight }
341 z: notifications.useModal || panel.indicators.shown || wizard.active ? overlay.z + 1 : overlay.z - 1
345 target: SurfaceManager
347 if (surface.type == MirSurfaceItem.InputMethod) {
348 inputMethod.surface = surface;
352 onSurfaceDestroyed: {
353 if (inputMethod.surface == surface) {
354 inputMethod.surface = null;
355 surface.parent = null;
357 if (!surface.parent) {
358 // there's no one displaying it. delete it right away
364 target: SessionManager
366 if (!session.parentSession && !session.application) {
367 // nothing is using it. delete it right away
375 objectName: "lockscreen"
377 hides: [launcher, panel.indicators]
380 showAnimation: StandardAnimation { property: "opacity"; to: 1 }
381 hideAnimation: StandardAnimation { property: "opacity"; to: 0 }
385 height: parent.height - panel.panelHeight
386 background: shell.background
387 darkenBackground: 0.4
388 alphaNumeric: AccountsService.passwordDisplayHint === AccountsService.Keyboard
392 property string promptText
393 infoText: promptText !== "" ? i18n.tr("Enter %1").arg(promptText) :
394 alphaNumeric ? i18n.tr("Enter passphrase") :
395 i18n.tr("Enter passcode")
396 errorText: promptText !== "" ? i18n.tr("Sorry, incorrect %1").arg(promptText) :
397 alphaNumeric ? i18n.tr("Sorry, incorrect passphrase") + "\n" +
398 i18n.tr("Please re-enter") :
399 i18n.tr("Sorry, incorrect passcode")
401 // FIXME: We *should* show emergency dialer if there is a SIM present,
402 // regardless of whether the side stage is enabled. But right now,
403 // the assumption is that narrow screens are phones which have SIMs
404 // and wider screens are tablets which don't. When we do allow this
405 // on devices with a side stage and a SIM, work should be done to
406 // ensure that the main stage is disabled while the dialer is present
407 // in the side stage. See the FIXME in the stage loader in this file.
408 showEmergencyCallButton: !shell.sideStageEnabled
410 onEntered: LightDM.Greeter.respond(passphrase);
411 onCancel: greeter.show()
412 onEmergencyCall: startLockedApp("dialer-app")
414 onShownChanged: if (shown) greeter.lockedApp = ""
416 function maybeShow() {
417 if (!shell.forcedUnlock) {
426 if (lockscreen.delayMinutes > 0) {
427 lockscreen.delayMinutes -= 1
428 if (lockscreen.delayMinutes > 0) {
435 Component.onCompleted: {
436 if (greeter.narrowMode) {
437 LightDM.Greeter.authenticate(LightDM.Users.data(0, LightDM.UserRoles.NameRole))
443 target: LightDM.Greeter
445 onShowGreeter: greeter.show()
446 onHideGreeter: greeter.login()
449 shell.enabled = true;
450 if (!LightDM.Greeter.active) {
451 return; // could happen if hideGreeter() comes in before we prompt
453 if (greeter.narrowMode) {
454 lockscreen.promptText = isDefaultPrompt ? "" : text.toLowerCase();
455 lockscreen.maybeShow();
459 onPromptlessChanged: {
460 if (!LightDM.Greeter.active) {
461 return; // could happen if hideGreeter() comes in before we prompt
463 if (greeter.narrowMode) {
464 if (LightDM.Greeter.promptless && LightDM.Greeter.authenticated) {
468 lockscreen.maybeShow();
473 onAuthenticationComplete: {
474 shell.enabled = true;
475 if (LightDM.Greeter.authenticated) {
476 AccountsService.failedLogins = 0
478 // Else only penalize user for a failed login if they actually were
479 // prompted for a password. We do this below after the promptless
482 if (LightDM.Greeter.promptless) {
486 if (LightDM.Greeter.authenticated) {
489 AccountsService.failedLogins++
490 if (maxFailedLogins >= 2) { // require at least a warning
491 if (AccountsService.failedLogins === maxFailedLogins - 1) {
492 var title = lockscreen.alphaNumeric ?
493 i18n.tr("Sorry, incorrect passphrase.") :
494 i18n.tr("Sorry, incorrect passcode.")
495 var text = i18n.tr("This will be your last attempt.") + " " +
496 (lockscreen.alphaNumeric ?
497 i18n.tr("If passphrase is entered incorrectly, your phone will conduct a factory reset and all personal data will be deleted.") :
498 i18n.tr("If passcode is entered incorrectly, your phone will conduct a factory reset and all personal data will be deleted."))
499 lockscreen.showInfoPopup(title, text)
500 } else if (AccountsService.failedLogins >= maxFailedLogins) {
501 SystemImage.factoryReset() // Ouch!
504 if (failedLoginsDelayAttempts > 0 && AccountsService.failedLogins % failedLoginsDelayAttempts == 0) {
505 lockscreen.delayMinutes = failedLoginsDelayMinutes
506 forcedDelayTimer.start()
509 lockscreen.clear(true);
510 if (greeter.narrowMode) {
511 LightDM.Greeter.authenticate(LightDM.Users.data(0, LightDM.UserRoles.NameRole))
518 target: LightDM.Greeter
520 value: greeter.shown || lockscreen.shown || greeter.hasLockedApp
526 opacity: greeterWrapper.showProgress * 0.8
530 // Just a tiny wrapper to adjust greeter's x without messing with its own dragging
532 objectName: "greeterWrapper"
533 x: (greeter.narrowMode && greeter.showProgress > 0) ? launcher.progress : 0
536 height: parent.height - panel.panelHeight
539 enabled: !launcher.dashSwipe
543 property bool fullyShown: showProgress === 1.0
544 onFullyShownChanged: {
545 // Wait until the greeter is completely covering lockscreen before resetting it.
546 if (greeter.narrowMode && fullyShown && !LightDM.Greeter.authenticated) {
548 lockscreen.maybeShow();
552 readonly property real showProgress: MathUtils.clamp((1 - x/width) + greeter.showProgress - 1, 0, 1)
553 onShowProgressChanged: {
554 if (showProgress === 0) {
555 if ((LightDM.Greeter.promptless && LightDM.Greeter.authenticated) || shell.forcedUnlock) {
557 } else if (greeter.narrowMode) {
558 lockscreen.clear(false) // to reset focus if necessary
565 objectName: "greeter"
567 signal sessionStarted() // helpful for tests
569 property string lockedApp: ""
570 property bool hasLockedApp: lockedApp !== ""
572 hides: [launcher, panel.indicators]
573 loadContent: required || lockscreen.required // keeps content in memory for quick show()
577 background: shell.background
580 height: parent.height
583 // avoid overlapping with Launcher's edge drag area
584 // FIXME: Fix TouchRegistry & friends and remove this workaround
585 // Issue involves launcher's DDA getting disabled on a long
587 dragHandleLeftMargin: launcher.available ? launcher.dragAreaWidth + 1 : 0
589 function startUnlock() {
591 if (!LightDM.Greeter.authenticated) {
592 lockscreen.maybeShow()
603 if (LightDM.Greeter.startSessionSync()) {
613 // See powerConnection for why this is useful
614 id: showGreeterDelayed
623 // Disable everything so that user can't swipe greeter or
624 // launcher until we get the next prompt/authenticate, which
625 // will re-enable the shell.
626 shell.enabled = false;
628 if (greeter.narrowMode) {
629 LightDM.Greeter.authenticate(LightDM.Users.data(0, LightDM.UserRoles.NameRole));
633 greeter.lockedApp = "";
634 greeter.forceActiveFocus();
638 Component.onCompleted: {
639 Connectivity.unlockAllModems()
642 onUnlocked: greeter.hide()
644 // Update launcher items for new user
645 var user = LightDM.Users.data(uid, LightDM.UserRoles.NameRole);
646 AccountsService.user = user;
647 LauncherModel.setUser(user);
651 if (!tutorial.running) {
656 if (dragging && !tutorial.running) {
662 target: ApplicationManager
663 property: "suspended"
664 value: (greeter.shown && greeterWrapper.showProgress == 1) || lockscreen.shown
674 if (shell.locked && callManager.hasCalls && greeter.lockedApp !== "dialer-app") {
675 // We just received an incoming call while locked. The
676 // indicator will have already launched dialer-app for us, but
677 // there is a race between "hasCalls" changing and the dialer
678 // starting up. So in case we lose that race, we'll start/
679 // focus the dialer ourselves here too. Even if the indicator
680 // didn't launch the dialer for some reason (or maybe a call
681 // started via some other means), if an active call is
682 // happening, we want to be in the dialer.
683 startLockedApp("dialer-app")
693 if (Powerd.status === Powerd.Off && reason !== Powerd.Proximity &&
694 !callManager.hasCalls && !tutorial.running) {
695 // We don't want to simply call greeter.showNow() here, because
696 // that will take too long. Qt will delay button event
697 // handling until the greeter is done loading and may think the
698 // user held down the power button the whole time, leading to a
699 // power dialog being shown. Instead, delay showing the
700 // greeter until we've finished handling the event. We could
701 // make the greeter load asynchronously instead, but that
702 // introduces a whole host of timing issues, especially with
703 // its animations. So this is simpler.
704 showGreeterDelayed.start();
709 function showHome() {
710 if (tutorial.running) {
714 if (LightDM.Greeter.active) {
715 greeter.startUnlock()
718 var animate = !LightDM.Greeter.active && !stages.shown
719 dash.setCurrentScope(0, animate, false)
720 ApplicationManager.requestFocusApplication("unity8-dash")
723 function showDash() {
724 if (greeter.hasLockedApp || // just in case user gets here
725 (!greeter.narrowMode && shell.locked)) {
734 if (ApplicationManager.focusedApplicationId != "unity8-dash") {
735 ApplicationManager.requestFocusApplication("unity8-dash")
749 anchors.fill: parent //because this draws indicator menus
752 available: tutorial.panelEnabled && (!shell.locked || AccountsService.enableIndicatorsWhileLocked) && !greeter.hasLockedApp
753 contentEnabled: tutorial.panelContentEnabled
754 width: parent.width > units.gu(60) ? units.gu(40) : parent.width
756 minimizedPanelHeight: units.gu(3)
757 expandedPanelHeight: units.gu(7)
759 indicatorsModel: Indicators.IndicatorsModel {
760 Component.onCompleted: load(indicatorProfile);
764 greeterShown: greeter.shown || lockscreen.shown
767 property bool topmostApplicationIsFullscreen:
768 ApplicationManager.focusedApplicationId &&
769 ApplicationManager.findApplication(ApplicationManager.focusedApplicationId).fullscreen
771 fullscreenMode: (topmostApplicationIsFullscreen && !LightDM.Greeter.active && launcher.progress == 0)
772 || greeter.hasLockedApp
777 objectName: "launcher"
779 readonly property bool dashSwipe: progress > 0
781 anchors.top: parent.top
782 anchors.topMargin: inverted ? 0 : panel.panelHeight
783 anchors.bottom: parent.bottom
785 dragAreaWidth: shell.edgeSize
786 available: tutorial.launcherEnabled && (!shell.locked || AccountsService.enableLauncherWhileLocked) && !greeter.hasLockedApp
787 inverted: usageModeSettings.usageMode === "Staged"
788 shadeBackground: !tutorial.running
790 onShowDashHome: showHome()
792 onDashSwipeChanged: {
794 dash.setCurrentScope(0, false, true)
797 onLauncherApplicationSelected: {
798 if (greeter.hasLockedApp) {
799 greeter.startUnlock()
801 if (!tutorial.running)
802 shell.activateApplication(appId)
806 panel.indicators.hide()
814 background: shell.background
818 id: modalNotificationBackground
820 visible: notifications.useModal && (notifications.state == "narrow")
833 model: NotificationBackend.Model
836 y: topmostIsFullscreen ? 0 : panel.panelHeight
837 height: parent.height - (topmostIsFullscreen ? 0 : panel.panelHeight)
842 when: overlay.width <= units.gu(60)
844 target: notifications
845 anchors.left: parent.left
846 anchors.right: parent.right
851 when: overlay.width > units.gu(60)
853 target: notifications
854 anchors.left: undefined
855 anchors.right: parent.right
857 PropertyChanges { target: notifications; width: units.gu(38) }
868 shutdownFadeOutRectangle.enabled = true;
869 shutdownFadeOutRectangle.visible = true;
870 shutdownFadeOut.start();
876 objectName: "tutorial"
877 active: AccountsService.demoEdges
878 paused: LightDM.Greeter.active
884 onFinished: AccountsService.demoEdges = false
888 target: SessionBroadcast
889 onShowHome: showHome()
893 id: shutdownFadeOutRectangle
894 z: screenGrabber.z + 10
900 NumberAnimation on opacity {
905 if (shutdownFadeOutRectangle.enabled && shutdownFadeOutRectangle.visible) {
906 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