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 // this is only here to select the width / height of the window if not running fullscreen
49 property bool tablet: false
50 width: tablet ? units.gu(160) : applicationArguments.hasGeometry() ? applicationArguments.width() : units.gu(40)
51 height: tablet ? units.gu(100) : applicationArguments.hasGeometry() ? applicationArguments.height() : units.gu(71)
53 property real edgeSize: units.gu(2)
54 property url defaultBackground: Qt.resolvedUrl(shell.width >= units.gu(60) ? "graphics/tablet_background.jpg" : "graphics/phone_background.jpg")
55 property url background: asImageTester.status == Image.Ready ? asImageTester.source
56 : gsImageTester.status == Image.Ready ? gsImageTester.source : defaultBackground
57 readonly property real panelHeight: panel.panelHeight
59 readonly property bool locked: LightDM.Greeter.active && !LightDM.Greeter.authenticated && !forcedUnlock
60 readonly property alias hasLockedApp: greeter.hasLockedApp
61 readonly property bool forcedUnlock: edgeDemo.running
62 onForcedUnlockChanged: if (forcedUnlock) lockscreen.hide()
64 property bool sideStageEnabled: shell.width >= units.gu(100)
65 readonly property string focusedApplicationId: ApplicationManager.focusedApplicationId
67 property int maxFailedLogins: -1 // disabled by default for now, will enable via settings in future
68 property int failedLoginsDelayAttempts: 7 // number of failed logins
69 property int failedLoginsDelayMinutes: 5 // minutes of forced waiting
71 property int orientation
72 readonly property int deviceOrientationAngle: Screen.angleBetween(Screen.primaryOrientation, Screen.orientation)
73 onDeviceOrientationAngleChanged: {
74 if (!OrientationLock.enabled) {
75 orientation = Screen.orientation;
78 readonly property bool orientationLockEnabled: OrientationLock.enabled
79 onOrientationLockEnabledChanged: {
80 if (orientationLockEnabled) {
81 OrientationLock.savedOrientation = Screen.orientation;
83 orientation = Screen.orientation;
87 function activateApplication(appId) {
88 if (ApplicationManager.findApplication(appId)) {
89 ApplicationManager.requestFocusApplication(appId);
91 var execFlags = shell.sideStageEnabled ? ApplicationManager.NoFlag : ApplicationManager.ForceMainStage;
92 ApplicationManager.startApplication(appId, execFlags);
96 function startLockedApp(app) {
98 greeter.lockedApp = app;
100 shell.activateApplication(app);
103 // This is a dummy image to detect if the custom AS set wallpaper loads successfully.
106 source: AccountsService.backgroundFile != undefined && AccountsService.backgroundFile.length > 0 ? AccountsService.backgroundFile : ""
114 id: backgroundSettings
115 schema.id: "org.gnome.desktop.background"
118 // This is a dummy image to detect if the custom GSettings set wallpaper loads successfully.
121 source: backgroundSettings.pictureUri != undefined && backgroundSettings.pictureUri.length > 0 ? backgroundSettings.pictureUri : ""
129 target: LauncherModel
130 property: "applicationManager"
131 value: ApplicationManager
134 Component.onCompleted: {
135 Theme.name = "Ubuntu.Components.Themes.SuruGradient"
136 if (ApplicationManager.count > 0) {
137 ApplicationManager.focusApplication(ApplicationManager.get(0).appId);
139 if (orientationLockEnabled) {
140 orientation = OrientationLock.savedOrientation;
150 objectName: "dashCommunicator"
156 enabled: Powerd.status === Powerd.On
160 target: ApplicationManager
161 property: "forceDashActive"
162 value: launcher.shown || launcher.dashSwipe
167 onVolumeDownPressed: volumeControl.volumeDown()
168 onVolumeUpPressed: volumeControl.volumeUp()
169 onBothVolumeKeysPressed: screenGrabber.capture()
174 if (event.key == Qt.Key_PowerOff || event.key == Qt.Key_PowerDown) {
175 dialogs.onPowerKeyPressed();
176 event.accepted = true;
178 volumeKeyFilter.onKeyPressed(event.key);
179 event.accepted = false;
184 if (event.key == Qt.Key_PowerOff || event.key == Qt.Key_PowerDown) {
185 dialogs.onPowerKeyReleased();
186 event.accepted = true;
188 volumeKeyFilter.onKeyReleased(event.key);
189 event.accepted = false;
198 height: parent.height
199 visible: !ApplicationManager.empty
202 target: ApplicationManager
204 if (greeter.narrowMode) {
205 if (appId === "dialer-app" && callManager.hasCalls && shell.locked) {
206 // If we are in the middle of a call, make dialer lockedApp and show it.
207 // This can happen if user backs out of dialer back to greeter, then
208 // launches dialer again.
209 greeter.lockedApp = appId;
211 if (greeter.hasLockedApp) {
212 if (appId === greeter.lockedApp) {
213 lockscreen.hide() // show locked app
215 greeter.startUnlock() // show lockscreen if necessary
220 if (LightDM.Greeter.active) {
221 greeter.startUnlock()
226 onFocusedApplicationIdChanged: {
227 if (greeter.hasLockedApp && greeter.lockedApp !== ApplicationManager.focusedApplicationId) {
228 greeter.startUnlock()
230 panel.indicators.hide();
233 onApplicationAdded: {
234 if (greeter.shown && appId != "unity8-dash") {
235 greeter.startUnlock()
237 // If this happens on first boot, we may be in edge
238 // tutorial or wizard while receiving a call. But a call
239 // is more important than wizard so just bail out of those.
240 if (edgeDemo.running) {
241 edgeDemo.hideEdgeDemos();
245 if (greeter.narrowMode && greeter.hasLockedApp && appId === greeter.lockedApp) {
246 lockscreen.hide() // show locked app
253 id: applicationsDisplayLoader
254 objectName: "applicationsDisplayLoader"
257 // When we have a locked app, we only want to show that one app.
258 // FIXME: do this in a less traumatic way. We currently only allow
259 // locked apps in phone mode (see FIXME in Lockscreen component in
260 // this same file). When that changes, we need to do something
261 // nicer here. But this code is currently just to prevent a
262 // theoretical attack where user enters lockedApp mode, then makes
263 // the screen larger (maybe connects to monitor) and tries to enter
265 property bool tabletMode: shell.sideStageEnabled && !greeter.hasLockedApp
266 source: tabletMode ? "Stages/TabletStage.qml" : "Stages/PhoneStage.qml"
269 target: applicationsDisplayLoader.item
270 property: "objectName"
274 target: applicationsDisplayLoader.item
275 property: "dragAreaWidth"
276 value: shell.edgeSize
279 target: applicationsDisplayLoader.item
280 property: "maximizedAppTopMargin"
281 // Not just using panel.panelHeight as that changes depending on the focused app.
282 value: panel.indicators.minimizedPanelHeight + units.dp(2) // dp(2) for orange line
285 target: applicationsDisplayLoader.item
286 property: "interactive"
287 value: edgeDemo.stagesEnabled && !greeter.shown && !lockscreen.shown && panel.indicators.fullyClosed && launcher.progress == 0 && !notifications.useModal
290 target: applicationsDisplayLoader.item
291 property: "spreadEnabled"
292 value: edgeDemo.stagesEnabled && !greeter.hasLockedApp
295 target: applicationsDisplayLoader.item
296 property: "inverseProgress"
297 value: launcher.progress
300 target: applicationsDisplayLoader.item
301 property: "orientation"
302 value: shell.orientation
309 objectName: "inputMethod"
310 anchors { fill: parent; topMargin: panel.panelHeight }
311 z: notifications.useModal || panel.indicators.shown ? overlay.z + 1 : overlay.z - 1
315 target: SurfaceManager
317 if (surface.type == MirSurfaceItem.InputMethod) {
318 inputMethod.surface = surface;
322 onSurfaceDestroyed: {
323 if (inputMethod.surface == surface) {
324 inputMethod.surface = null;
325 surface.parent = null;
327 if (!surface.parent) {
328 // there's no one displaying it. delete it right away
334 target: SessionManager
336 if (!session.parentSession && !session.application) {
337 // nothing is using it. delete it right away
345 objectName: "lockscreen"
347 hides: [launcher, panel.indicators]
350 showAnimation: StandardAnimation { property: "opacity"; to: 1 }
351 hideAnimation: StandardAnimation { property: "opacity"; to: 0 }
355 height: parent.height - panel.panelHeight
356 background: shell.background
357 darkenBackground: 0.4
358 alphaNumeric: AccountsService.passwordDisplayHint === AccountsService.Keyboard
362 property string promptText
363 infoText: promptText !== "" ? i18n.tr("Enter %1").arg(promptText) :
364 alphaNumeric ? i18n.tr("Enter passphrase") :
365 i18n.tr("Enter passcode")
366 errorText: promptText !== "" ? i18n.tr("Sorry, incorrect %1").arg(promptText) :
367 alphaNumeric ? i18n.tr("Sorry, incorrect passphrase") + "\n" +
368 i18n.tr("Please re-enter") :
369 i18n.tr("Sorry, incorrect passcode")
371 // FIXME: We *should* show emergency dialer if there is a SIM present,
372 // regardless of whether the side stage is enabled. But right now,
373 // the assumption is that narrow screens are phones which have SIMs
374 // and wider screens are tablets which don't. When we do allow this
375 // on devices with a side stage and a SIM, work should be done to
376 // ensure that the main stage is disabled while the dialer is present
377 // in the side stage. See the FIXME in the stage loader in this file.
378 showEmergencyCallButton: !shell.sideStageEnabled
380 onEntered: LightDM.Greeter.respond(passphrase);
381 onCancel: greeter.show()
382 onEmergencyCall: startLockedApp("dialer-app")
384 onShownChanged: if (shown) greeter.lockedApp = ""
386 function maybeShow() {
387 if (!shell.forcedUnlock) {
396 if (lockscreen.delayMinutes > 0) {
397 lockscreen.delayMinutes -= 1
398 if (lockscreen.delayMinutes > 0) {
405 Component.onCompleted: {
406 if (greeter.narrowMode) {
407 LightDM.Greeter.authenticate(LightDM.Users.data(0, LightDM.UserRoles.NameRole))
413 target: LightDM.Greeter
415 onShowGreeter: greeter.show()
416 onHideGreeter: greeter.login()
419 shell.enabled = true;
420 if (!LightDM.Greeter.active) {
421 return; // could happen if hideGreeter() comes in before we prompt
423 if (greeter.narrowMode) {
424 lockscreen.promptText = isDefaultPrompt ? "" : text.toLowerCase();
425 lockscreen.maybeShow();
429 onPromptlessChanged: {
430 if (!LightDM.Greeter.active) {
431 return; // could happen if hideGreeter() comes in before we prompt
433 if (greeter.narrowMode) {
434 if (LightDM.Greeter.promptless && LightDM.Greeter.authenticated) {
438 lockscreen.maybeShow();
443 onAuthenticationComplete: {
444 shell.enabled = true;
445 if (LightDM.Greeter.authenticated) {
446 AccountsService.failedLogins = 0
448 // Else only penalize user for a failed login if they actually were
449 // prompted for a password. We do this below after the promptless
452 if (LightDM.Greeter.promptless) {
456 if (LightDM.Greeter.authenticated) {
459 AccountsService.failedLogins++
460 if (maxFailedLogins >= 2) { // require at least a warning
461 if (AccountsService.failedLogins === maxFailedLogins - 1) {
462 var title = lockscreen.alphaNumeric ?
463 i18n.tr("Sorry, incorrect passphrase.") :
464 i18n.tr("Sorry, incorrect passcode.")
465 var text = i18n.tr("This will be your last attempt.") + " " +
466 (lockscreen.alphaNumeric ?
467 i18n.tr("If passphrase is entered incorrectly, your phone will conduct a factory reset and all personal data will be deleted.") :
468 i18n.tr("If passcode is entered incorrectly, your phone will conduct a factory reset and all personal data will be deleted."))
469 lockscreen.showInfoPopup(title, text)
470 } else if (AccountsService.failedLogins >= maxFailedLogins) {
471 SystemImage.factoryReset() // Ouch!
474 if (failedLoginsDelayAttempts > 0 && AccountsService.failedLogins % failedLoginsDelayAttempts == 0) {
475 lockscreen.delayMinutes = failedLoginsDelayMinutes
476 forcedDelayTimer.start()
479 lockscreen.clear(true);
480 if (greeter.narrowMode) {
481 LightDM.Greeter.authenticate(LightDM.Users.data(0, LightDM.UserRoles.NameRole))
488 target: LightDM.Greeter
490 value: greeter.shown || lockscreen.shown || greeter.hasLockedApp
496 opacity: greeterWrapper.showProgress * 0.8
500 // Just a tiny wrapper to adjust greeter's x without messing with its own dragging
502 objectName: "greeterWrapper"
503 x: greeter.narrowMode ? launcher.progress : 0
506 height: parent.height - panel.panelHeight
509 enabled: !launcher.dashSwipe
513 property bool fullyShown: showProgress === 1.0
514 onFullyShownChanged: {
515 // Wait until the greeter is completely covering lockscreen before resetting it.
516 if (greeter.narrowMode && fullyShown && !LightDM.Greeter.authenticated) {
518 lockscreen.maybeShow();
522 readonly property real showProgress: MathUtils.clamp((1 - x/width) + greeter.showProgress - 1, 0, 1)
523 onShowProgressChanged: {
524 if (showProgress === 0) {
525 if ((LightDM.Greeter.promptless && LightDM.Greeter.authenticated) || shell.forcedUnlock) {
527 } else if (greeter.narrowMode) {
528 lockscreen.clear(false) // to reset focus if necessary
535 objectName: "greeter"
537 property string lockedApp: ""
538 property bool hasLockedApp: lockedApp !== ""
541 hides: [launcher, panel.indicators]
543 loadContent: required || lockscreen.required // keeps content in memory for quick show()
547 background: shell.background
550 height: parent.height
552 dragHandleWidth: shell.edgeSize
560 function startUnlock() {
562 if (!LightDM.Greeter.authenticated) {
563 lockscreen.maybeShow()
574 // Disable everything so that user can't swipe greeter or
575 // launcher until we get first prompt/authenticate, which
576 // will re-enable the shell.
577 shell.enabled = false;
579 if (greeter.narrowMode) {
580 LightDM.Greeter.authenticate(LightDM.Users.data(0, LightDM.UserRoles.NameRole));
584 greeter.lockedApp = "";
585 greeter.forceActiveFocus();
589 Component.onCompleted: {
590 Connectivity.unlockAllModems()
593 onUnlocked: greeter.hide()
595 // Update launcher items for new user
596 var user = LightDM.Users.data(uid, LightDM.UserRoles.NameRole);
597 AccountsService.user = user;
598 LauncherModel.setUser(user);
601 onTease: launcher.tease()
604 target: ApplicationManager
605 property: "suspended"
606 value: (greeter.shown && greeterWrapper.showProgress == 1) || lockscreen.shown
616 if (shell.locked && callManager.hasCalls) {
617 // We just received an incoming call while locked. The
618 // indicator will have already launched dialer-app for us, but
619 // there is a race between "hasCalls" changing and the dialer
620 // starting up. So in case we lose that race, we'll start/
621 // focus the dialer ourselves here too. Even if the indicator
622 // didn't launch the dialer for some reason (or maybe a call
623 // started via some other means), if an active call is
624 // happening, we want to be in the dialer.
625 startLockedApp("dialer-app")
635 if (Powerd.status === Powerd.Off && reason !== Powerd.Proximity &&
636 !callManager.hasCalls && !edgeDemo.running) {
642 function showHome() {
643 if (edgeDemo.running) {
647 if (LightDM.Greeter.active) {
648 greeter.startUnlock()
651 var animate = !LightDM.Greeter.active && !stages.shown
652 dash.setCurrentScope(0, animate, false)
653 ApplicationManager.requestFocusApplication("unity8-dash")
656 function showDash() {
657 if (greeter.hasLockedApp || // just in case user gets here
658 (!greeter.narrowMode && shell.locked)) {
667 if (ApplicationManager.focusedApplicationId != "unity8-dash") {
668 ApplicationManager.requestFocusApplication("unity8-dash")
682 anchors.fill: parent //because this draws indicator menus
685 available: edgeDemo.panelEnabled && (!shell.locked || AccountsService.enableIndicatorsWhileLocked) && !greeter.hasLockedApp
686 contentEnabled: edgeDemo.panelContentEnabled
687 width: parent.width > units.gu(60) ? units.gu(40) : parent.width
689 minimizedPanelHeight: units.gu(3)
690 expandedPanelHeight: units.gu(7)
692 indicatorsModel: visibleIndicators.model
696 id: visibleIndicators
697 // TODO: This should be sourced by device type (eg "desktop", "tablet", "phone"...)
698 Component.onCompleted: initialise(indicatorProfile)
701 greeterShown: greeter.shown || lockscreen.shown
704 property bool topmostApplicationIsFullscreen:
705 ApplicationManager.focusedApplicationId &&
706 ApplicationManager.findApplication(ApplicationManager.focusedApplicationId).fullscreen
708 fullscreenMode: (topmostApplicationIsFullscreen && !LightDM.Greeter.active && launcher.progress == 0)
709 || greeter.hasLockedApp
714 objectName: "launcher"
716 readonly property bool dashSwipe: progress > 0
718 anchors.top: parent.top
719 anchors.bottom: parent.bottom
721 dragAreaWidth: shell.edgeSize
722 available: edgeDemo.launcherEnabled && (!shell.locked || AccountsService.enableLauncherWhileLocked) && !greeter.hasLockedApp
724 onShowDashHome: showHome()
726 onDashSwipeChanged: {
728 dash.setCurrentScope(0, false, true)
731 onLauncherApplicationSelected: {
732 if (greeter.hasLockedApp) {
733 greeter.startUnlock()
735 if (!edgeDemo.running)
736 shell.activateApplication(appId)
740 panel.indicators.hide()
748 background: shell.background
752 id: modalNotificationBackground
754 visible: notifications.useModal && (notifications.state == "narrow")
767 model: NotificationBackend.Model
770 y: topmostIsFullscreen ? 0 : panel.panelHeight
772 height: parent.height - (topmostIsFullscreen ? 0 : panel.panelHeight)
777 when: overlay.width <= units.gu(60)
778 AnchorChanges { target: notifications; anchors.left: parent.left }
782 when: overlay.width > units.gu(60)
783 AnchorChanges { target: notifications; anchors.left: undefined }
784 PropertyChanges { target: notifications; width: units.gu(38) }
795 shutdownFadeOutRectangle.enabled = true;
796 shutdownFadeOutRectangle.visible = true;
797 shutdownFadeOut.start();
803 objectName: "edgeDemo"
805 paused: Powerd.status === Powerd.Off || wizard.active // Saves power
813 target: SessionBroadcast
814 onShowHome: showHome()
818 id: shutdownFadeOutRectangle
825 NumberAnimation on opacity {
830 if (shutdownFadeOutRectangle.enabled && shutdownFadeOutRectangle.visible) {
831 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