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"
41import Unity.Notifications 1.0 as NotificationBackend
42import Unity.Session 0.1
43import Unity.DashCommunicator 0.1
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"
163 id: physicalKeysMapper
165 onVolumeDownPressed: volumeControl.volumeDown()
166 onVolumeUpPressed: volumeControl.volumeUp()
167 onScreenshotPressed: screenGrabber.capture()
173 enabled: Powerd.status === Powerd.On
177 target: ApplicationManager
178 property: "forceDashActive"
179 value: launcher.shown || launcher.dashSwipe
184 physicalKeysMapper.onKeyPressed(event.key);
185 if (event.key == Qt.Key_PowerOff || event.key == Qt.Key_PowerDown) {
186 event.accepted = true;
188 event.accepted = false;
193 physicalKeysMapper.onKeyReleased(event.key);
194 if (event.key = Qt.Key_PowerOff || event.key == Qt.Key_PowerDown) {
195 event.accepted = true;
197 event.accepted = false;
206 height: parent.height
207 visible: !ApplicationManager.empty
210 target: ApplicationManager
212 if (greeter.narrowMode) {
213 if (appId === "dialer-app" && callManager.hasCalls && shell.locked) {
214 // If we are in the middle of a call, make dialer lockedApp and show it.
215 // This can happen if user backs out of dialer back to greeter, then
216 // launches dialer again.
217 greeter.lockedApp = appId;
219 if (greeter.hasLockedApp) {
220 if (appId === greeter.lockedApp) {
221 lockscreen.hide() // show locked app
223 greeter.startUnlock() // show lockscreen if necessary
228 if (LightDM.Greeter.active) {
229 greeter.startUnlock()
234 onFocusedApplicationIdChanged: {
235 if (greeter.hasLockedApp && greeter.lockedApp !== ApplicationManager.focusedApplicationId) {
236 greeter.startUnlock()
238 panel.indicators.hide();
241 onApplicationAdded: {
242 if (greeter.shown && appId != "unity8-dash") {
243 greeter.startUnlock()
245 // If this happens on first boot, we may be in edge
246 // tutorial or wizard while receiving a call. But a call
247 // is more important than wizard so just bail out of those.
248 if (edgeDemo.running) {
249 edgeDemo.hideEdgeDemos();
253 if (greeter.narrowMode && greeter.hasLockedApp && appId === greeter.lockedApp) {
254 lockscreen.hide() // show locked app
261 id: applicationsDisplayLoader
262 objectName: "applicationsDisplayLoader"
265 // When we have a locked app, we only want to show that one app.
266 // FIXME: do this in a less traumatic way. We currently only allow
267 // locked apps in phone mode (see FIXME in Lockscreen component in
268 // this same file). When that changes, we need to do something
269 // nicer here. But this code is currently just to prevent a
270 // theoretical attack where user enters lockedApp mode, then makes
271 // the screen larger (maybe connects to monitor) and tries to enter
273 property bool tabletMode: shell.sideStageEnabled && !greeter.hasLockedApp
274 source: usageModeSettings.usageMode === "Windowed" ? "Stages/DesktopStage.qml"
275 : tabletMode ? "Stages/TabletStage.qml" : "Stages/PhoneStage.qml"
278 target: applicationsDisplayLoader.item
279 property: "objectName"
283 target: applicationsDisplayLoader.item
284 property: "dragAreaWidth"
285 value: shell.edgeSize
288 target: applicationsDisplayLoader.item
289 property: "maximizedAppTopMargin"
290 // Not just using panel.panelHeight as that changes depending on the focused app.
291 value: panel.indicators.minimizedPanelHeight + units.dp(2) // dp(2) for orange line
294 target: applicationsDisplayLoader.item
295 property: "interactive"
296 value: edgeDemo.stagesEnabled && !greeter.shown && !lockscreen.shown && panel.indicators.fullyClosed && launcher.progress == 0 && !notifications.useModal
299 target: applicationsDisplayLoader.item
300 property: "spreadEnabled"
301 value: edgeDemo.stagesEnabled && !greeter.hasLockedApp
304 target: applicationsDisplayLoader.item
305 property: "inverseProgress"
306 value: launcher.progress
309 target: applicationsDisplayLoader.item
310 property: "orientation"
311 value: shell.orientation
314 target: applicationsDisplayLoader.item
315 property: "background"
316 value: shell.background
323 objectName: "inputMethod"
324 anchors { fill: parent; topMargin: panel.panelHeight }
325 z: notifications.useModal || panel.indicators.shown ? overlay.z + 1 : overlay.z - 1
329 target: SurfaceManager
331 if (surface.type == MirSurfaceItem.InputMethod) {
332 inputMethod.surface = surface;
336 onSurfaceDestroyed: {
337 if (inputMethod.surface == surface) {
338 inputMethod.surface = null;
339 surface.parent = null;
341 if (!surface.parent) {
342 // there's no one displaying it. delete it right away
348 target: SessionManager
350 if (!session.parentSession && !session.application) {
351 // nothing is using it. delete it right away
359 objectName: "lockscreen"
361 hides: [launcher, panel.indicators]
364 showAnimation: StandardAnimation { property: "opacity"; to: 1 }
365 hideAnimation: StandardAnimation { property: "opacity"; to: 0 }
369 height: parent.height - panel.panelHeight
370 background: shell.background
371 darkenBackground: 0.4
372 alphaNumeric: AccountsService.passwordDisplayHint === AccountsService.Keyboard
376 property string promptText
377 infoText: promptText !== "" ? i18n.tr("Enter %1").arg(promptText) :
378 alphaNumeric ? i18n.tr("Enter passphrase") :
379 i18n.tr("Enter passcode")
380 errorText: promptText !== "" ? i18n.tr("Sorry, incorrect %1").arg(promptText) :
381 alphaNumeric ? i18n.tr("Sorry, incorrect passphrase") + "\n" +
382 i18n.tr("Please re-enter") :
383 i18n.tr("Sorry, incorrect passcode")
385 // FIXME: We *should* show emergency dialer if there is a SIM present,
386 // regardless of whether the side stage is enabled. But right now,
387 // the assumption is that narrow screens are phones which have SIMs
388 // and wider screens are tablets which don't. When we do allow this
389 // on devices with a side stage and a SIM, work should be done to
390 // ensure that the main stage is disabled while the dialer is present
391 // in the side stage. See the FIXME in the stage loader in this file.
392 showEmergencyCallButton: !shell.sideStageEnabled
394 onEntered: LightDM.Greeter.respond(passphrase);
395 onCancel: greeter.show()
396 onEmergencyCall: startLockedApp("dialer-app")
398 onShownChanged: if (shown) greeter.lockedApp = ""
400 function maybeShow() {
401 if (!shell.forcedUnlock) {
410 if (lockscreen.delayMinutes > 0) {
411 lockscreen.delayMinutes -= 1
412 if (lockscreen.delayMinutes > 0) {
419 Component.onCompleted: {
420 if (greeter.narrowMode) {
421 LightDM.Greeter.authenticate(LightDM.Users.data(0, LightDM.UserRoles.NameRole))
427 target: LightDM.Greeter
429 onShowGreeter: greeter.show()
430 onHideGreeter: greeter.login()
433 shell.enabled = true;
434 if (!LightDM.Greeter.active) {
435 return; // could happen if hideGreeter() comes in before we prompt
437 if (greeter.narrowMode) {
438 lockscreen.promptText = isDefaultPrompt ? "" : text.toLowerCase();
439 lockscreen.maybeShow();
443 onPromptlessChanged: {
444 if (!LightDM.Greeter.active) {
445 return; // could happen if hideGreeter() comes in before we prompt
447 if (greeter.narrowMode) {
448 if (LightDM.Greeter.promptless && LightDM.Greeter.authenticated) {
452 lockscreen.maybeShow();
457 onAuthenticationComplete: {
458 shell.enabled = true;
459 if (LightDM.Greeter.authenticated) {
460 AccountsService.failedLogins = 0
462 // Else only penalize user for a failed login if they actually were
463 // prompted for a password. We do this below after the promptless
466 if (LightDM.Greeter.promptless) {
470 if (LightDM.Greeter.authenticated) {
473 AccountsService.failedLogins++
474 if (maxFailedLogins >= 2) { // require at least a warning
475 if (AccountsService.failedLogins === maxFailedLogins - 1) {
476 var title = lockscreen.alphaNumeric ?
477 i18n.tr("Sorry, incorrect passphrase.") :
478 i18n.tr("Sorry, incorrect passcode.")
479 var text = i18n.tr("This will be your last attempt.") + " " +
480 (lockscreen.alphaNumeric ?
481 i18n.tr("If passphrase is entered incorrectly, your phone will conduct a factory reset and all personal data will be deleted.") :
482 i18n.tr("If passcode is entered incorrectly, your phone will conduct a factory reset and all personal data will be deleted."))
483 lockscreen.showInfoPopup(title, text)
484 } else if (AccountsService.failedLogins >= maxFailedLogins) {
485 SystemImage.factoryReset() // Ouch!
488 if (failedLoginsDelayAttempts > 0 && AccountsService.failedLogins % failedLoginsDelayAttempts == 0) {
489 lockscreen.delayMinutes = failedLoginsDelayMinutes
490 forcedDelayTimer.start()
493 lockscreen.clear(true);
494 if (greeter.narrowMode) {
495 LightDM.Greeter.authenticate(LightDM.Users.data(0, LightDM.UserRoles.NameRole))
502 target: LightDM.Greeter
504 value: greeter.shown || lockscreen.shown || greeter.hasLockedApp
510 opacity: greeterWrapper.showProgress * 0.8
514 // Just a tiny wrapper to adjust greeter's x without messing with its own dragging
516 objectName: "greeterWrapper"
517 x: (greeter.narrowMode && greeter.showProgress > 0) ? launcher.progress : 0
520 height: parent.height - panel.panelHeight
523 enabled: !launcher.dashSwipe
527 property bool fullyShown: showProgress === 1.0
528 onFullyShownChanged: {
529 // Wait until the greeter is completely covering lockscreen before resetting it.
530 if (greeter.narrowMode && fullyShown && !LightDM.Greeter.authenticated) {
532 lockscreen.maybeShow();
536 readonly property real showProgress: MathUtils.clamp((1 - x/width) + greeter.showProgress - 1, 0, 1)
537 onShowProgressChanged: {
538 if (showProgress === 0) {
539 if ((LightDM.Greeter.promptless && LightDM.Greeter.authenticated) || shell.forcedUnlock) {
541 } else if (greeter.narrowMode) {
542 lockscreen.clear(false) // to reset focus if necessary
549 objectName: "greeter"
551 signal sessionStarted() // helpful for tests
553 property string lockedApp: ""
554 property bool hasLockedApp: lockedApp !== ""
556 hides: [launcher, panel.indicators]
557 loadContent: required || lockscreen.required // keeps content in memory for quick show()
561 background: shell.background
564 height: parent.height
567 // avoid overlapping with Launcher's edge drag area
568 // FIXME: Fix TouchRegistry & friends and remove this workaround
569 // Issue involves launcher's DDA getting disabled on a long
571 dragHandleLeftMargin: launcher.available ? launcher.dragAreaWidth + 1 : 0
573 function startUnlock() {
575 if (!LightDM.Greeter.authenticated) {
576 lockscreen.maybeShow()
587 if (LightDM.Greeter.startSessionSync()) {
598 // Disable everything so that user can't swipe greeter or
599 // launcher until we get the next prompt/authenticate, which
600 // will re-enable the shell.
601 shell.enabled = false;
603 if (greeter.narrowMode) {
604 LightDM.Greeter.authenticate(LightDM.Users.data(0, LightDM.UserRoles.NameRole));
608 greeter.lockedApp = "";
609 greeter.forceActiveFocus();
613 Component.onCompleted: {
614 Connectivity.unlockAllModems()
617 onUnlocked: greeter.hide()
619 // Update launcher items for new user
620 var user = LightDM.Users.data(uid, LightDM.UserRoles.NameRole);
621 AccountsService.user = user;
622 LauncherModel.setUser(user);
625 onTapped: launcher.tease()
633 target: ApplicationManager
634 property: "suspended"
635 value: (greeter.shown && greeterWrapper.showProgress == 1) || lockscreen.shown
645 if (shell.locked && callManager.hasCalls) {
646 // We just received an incoming call while locked. The
647 // indicator will have already launched dialer-app for us, but
648 // there is a race between "hasCalls" changing and the dialer
649 // starting up. So in case we lose that race, we'll start/
650 // focus the dialer ourselves here too. Even if the indicator
651 // didn't launch the dialer for some reason (or maybe a call
652 // started via some other means), if an active call is
653 // happening, we want to be in the dialer.
654 startLockedApp("dialer-app")
664 if (Powerd.status === Powerd.Off && reason !== Powerd.Proximity &&
665 !callManager.hasCalls && !edgeDemo.running) {
671 function showHome() {
672 if (edgeDemo.running) {
676 if (LightDM.Greeter.active) {
677 greeter.startUnlock()
680 var animate = !LightDM.Greeter.active && !stages.shown
681 dash.setCurrentScope(0, animate, false)
682 ApplicationManager.requestFocusApplication("unity8-dash")
685 function showDash() {
686 if (greeter.hasLockedApp || // just in case user gets here
687 (!greeter.narrowMode && shell.locked)) {
696 if (ApplicationManager.focusedApplicationId != "unity8-dash") {
697 ApplicationManager.requestFocusApplication("unity8-dash")
711 anchors.fill: parent //because this draws indicator menus
714 available: edgeDemo.panelEnabled && (!shell.locked || AccountsService.enableIndicatorsWhileLocked) && !greeter.hasLockedApp
715 contentEnabled: edgeDemo.panelContentEnabled
716 width: parent.width > units.gu(60) ? units.gu(40) : parent.width
718 minimizedPanelHeight: units.gu(3)
719 expandedPanelHeight: units.gu(7)
721 indicatorsModel: visibleIndicators.model
725 id: visibleIndicators
726 // TODO: This should be sourced by device type (eg "desktop", "tablet", "phone"...)
727 Component.onCompleted: initialise(indicatorProfile)
730 greeterShown: greeter.shown || lockscreen.shown
733 property bool topmostApplicationIsFullscreen:
734 ApplicationManager.focusedApplicationId &&
735 ApplicationManager.findApplication(ApplicationManager.focusedApplicationId).fullscreen
737 fullscreenMode: (topmostApplicationIsFullscreen && !LightDM.Greeter.active && launcher.progress == 0)
738 || greeter.hasLockedApp
743 objectName: "launcher"
745 readonly property bool dashSwipe: progress > 0
747 anchors.top: parent.top
748 anchors.topMargin: inverted ? 0 : panel.panelHeight
749 anchors.bottom: parent.bottom
751 dragAreaWidth: shell.edgeSize
752 available: edgeDemo.launcherEnabled && (!shell.locked || AccountsService.enableLauncherWhileLocked) && !greeter.hasLockedApp
753 inverted: usageModeSettings.usageMode === "Staged"
755 onShowDashHome: showHome()
757 onDashSwipeChanged: {
759 dash.setCurrentScope(0, false, true)
762 onLauncherApplicationSelected: {
763 if (greeter.hasLockedApp) {
764 greeter.startUnlock()
766 if (!edgeDemo.running)
767 shell.activateApplication(appId)
771 panel.indicators.hide()
779 background: shell.background
783 id: modalNotificationBackground
785 visible: notifications.useModal && (notifications.state == "narrow")
798 model: NotificationBackend.Model
801 y: topmostIsFullscreen ? 0 : panel.panelHeight
802 height: parent.height - (topmostIsFullscreen ? 0 : panel.panelHeight)
807 when: overlay.width <= units.gu(60)
809 target: notifications
810 anchors.left: parent.left
811 anchors.right: parent.right
816 when: overlay.width > units.gu(60)
818 target: notifications
819 anchors.left: undefined
820 anchors.right: parent.right
822 PropertyChanges { target: notifications; width: units.gu(38) }
833 shutdownFadeOutRectangle.enabled = true;
834 shutdownFadeOutRectangle.visible = true;
835 shutdownFadeOut.start();
839 target: physicalKeysMapper
840 onPowerKeyLongPress: dialogs.showPowerDialog();
846 objectName: "edgeDemo"
848 paused: Powerd.status === Powerd.Off || wizard.active // Saves power
856 target: SessionBroadcast
857 onShowHome: showHome()
861 id: shutdownFadeOutRectangle
868 NumberAnimation on opacity {
873 if (shutdownFadeOutRectangle.enabled && shutdownFadeOutRectangle.visible) {
874 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