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
58 property bool sideStageEnabled: shell.width >= units.gu(100)
59 readonly property string focusedApplicationId: ApplicationManager.focusedApplicationId
61 property int maxFailedLogins: -1 // disabled by default for now, will enable via settings in future
62 property int failedLoginsDelayAttempts: 7 // number of failed logins
63 property int failedLoginsDelayMinutes: 5 // minutes of forced waiting
65 property int orientation
66 readonly property int deviceOrientationAngle: Screen.angleBetween(Screen.primaryOrientation, Screen.orientation)
67 onDeviceOrientationAngleChanged: {
68 if (!OrientationLock.enabled) {
69 orientation = Screen.orientation;
72 readonly property bool orientationLockEnabled: OrientationLock.enabled
73 onOrientationLockEnabledChanged: {
74 if (orientationLockEnabled) {
75 OrientationLock.savedOrientation = Screen.orientation;
77 orientation = Screen.orientation;
81 function activateApplication(appId) {
82 if (ApplicationManager.findApplication(appId)) {
83 ApplicationManager.requestFocusApplication(appId);
85 var execFlags = shell.sideStageEnabled ? ApplicationManager.NoFlag : ApplicationManager.ForceMainStage;
86 ApplicationManager.startApplication(appId, execFlags);
90 function setFakeActiveForApp(app) {
92 greeter.fakeActiveForApp = app
99 property: "applicationManager"
100 value: ApplicationManager
103 Component.onCompleted: {
104 Theme.name = "Ubuntu.Components.Themes.SuruGradient"
105 if (ApplicationManager.count > 0) {
106 ApplicationManager.focusApplication(ApplicationManager.get(0).appId);
108 if (orientationLockEnabled) {
109 orientation = OrientationLock.savedOrientation;
114 id: backgroundSettings
115 schema.id: "org.gnome.desktop.background"
117 property url gSettingsPicture: backgroundSettings.pictureUri != undefined && backgroundSettings.pictureUri.length > 0 ? backgroundSettings.pictureUri : shell.defaultBackground
118 onGSettingsPictureChanged: {
119 shell.background = gSettingsPicture
128 objectName: "dashCommunicator"
132 // Handle but do not filter out volume keys
133 Keys.onVolumeUpPressed: { volumeControl.volumeUp(); event.accepted = false; }
134 Keys.onVolumeDownPressed: { volumeControl.volumeDown(); event.accepted = false; }
137 if (event.key == Qt.Key_PowerOff || event.key == Qt.Key_PowerDown) {
138 dialogs.onPowerKeyPressed();
139 event.accepted = true;
141 event.accepted = false;
146 if (event.key == Qt.Key_PowerOff || event.key == Qt.Key_PowerDown) {
147 dialogs.onPowerKeyReleased();
148 event.accepted = true;
150 event.accepted = false;
159 height: parent.height
160 visible: !ApplicationManager.empty
163 target: ApplicationManager
165 if (appId === "dialer-app") {
166 // Always let the dialer-app through. Either user asked
167 // for an emergency call or accepted an incoming call.
168 setFakeActiveForApp(appId)
169 } else if (greeter.fakeActiveForApp !== "" && greeter.fakeActiveForApp !== appId) {
176 onFocusedApplicationIdChanged: {
177 if (greeter.fakeActiveForApp !== "" && greeter.fakeActiveForApp !== ApplicationManager.focusedApplicationId) {
180 panel.indicators.hide();
183 onApplicationAdded: {
184 if (greeter.shown && appId != "unity8-dash") {
187 if (appId === "dialer-app") {
188 // Always let the dialer-app through. Either user asked
189 // for an emergency call or accepted an incoming call.
190 setFakeActiveForApp(appId)
197 id: applicationsDisplayLoader
200 source: shell.sideStageEnabled ? "Stages/TabletStage.qml" : "Stages/PhoneStage.qml"
203 target: applicationsDisplayLoader.item
204 property: "objectName"
208 target: applicationsDisplayLoader.item
209 property: "dragAreaWidth"
210 value: shell.edgeSize
213 target: applicationsDisplayLoader.item
214 property: "maximizedAppTopMargin"
215 // Not just using panel.panelHeight as that changes depending on the focused app.
216 value: panel.indicators.panelHeight
219 target: applicationsDisplayLoader.item
220 property: "interactive"
221 value: edgeDemo.stagesEnabled && !greeter.shown && !lockscreen.shown && panel.indicators.fullyClosed && launcher.progress == 0
224 target: applicationsDisplayLoader.item
225 property: "spreadEnabled"
226 value: edgeDemo.stagesEnabled && greeter.fakeActiveForApp === "" // to support emergency dialer hack
229 target: applicationsDisplayLoader.item
230 property: "inverseProgress"
231 value: launcher.progress
234 target: applicationsDisplayLoader.item
235 property: "orientation"
236 value: shell.orientation
243 objectName: "inputMethod"
244 anchors { fill: parent; topMargin: panel.panelHeight }
245 z: notifications.useModal || panel.indicators.shown ? overlay.z + 1 : overlay.z - 1
249 target: SurfaceManager
251 if (surface.type == MirSurfaceItem.InputMethod) {
252 inputMethod.surface = surface;
256 onSurfaceDestroyed: {
257 if (inputMethod.surface == surface) {
258 inputMethod.surface = null;
259 surface.parent = null;
261 if (!surface.parent) {
262 // there's no one displaying it. delete it right away
268 target: SessionManager
270 if (!session.parentSession && !session.application) {
271 // nothing is using it. delete it right away
279 objectName: "lockscreen"
281 hides: [launcher, panel.indicators]
284 showAnimation: StandardAnimation { property: "opacity"; to: 1 }
285 hideAnimation: StandardAnimation { property: "opacity"; to: 0 }
289 height: parent.height - panel.panelHeight
290 background: shell.background
291 alphaNumeric: AccountsService.passwordDisplayHint === AccountsService.Keyboard
295 // FIXME: We *should* show emergency dialer if there is a SIM present,
296 // regardless of whether the side stage is enabled. But right now,
297 // the assumption is that narrow screens are phones which have SIMs
298 // and wider screens are tablets which don't. When we do allow this
299 // on devices with a side stage and a SIM, work should be done to
300 // ensure that the main stage is disabled while the dialer is present
301 // in the side stage.
302 showEmergencyCallButton: !shell.sideStageEnabled
304 onEntered: LightDM.Greeter.respond(passphrase);
305 onCancel: greeter.show()
306 onEmergencyCall: shell.activateApplication("dialer-app") // will automatically enter fake-active mode
308 onShownChanged: if (shown) greeter.fakeActiveForApp = ""
314 if (lockscreen.delayMinutes > 0) {
315 lockscreen.delayMinutes -= 1
316 if (lockscreen.delayMinutes > 0) {
323 Component.onCompleted: {
324 if (greeter.narrowMode) {
325 LightDM.Greeter.authenticate(LightDM.Users.data(0, LightDM.UserRoles.NameRole))
331 target: LightDM.Greeter
333 onShowGreeter: greeter.show()
334 onHideGreeter: greeter.login()
337 if (greeter.narrowMode) {
338 if (isDefaultPrompt) {
339 if (lockscreen.alphaNumeric) {
340 lockscreen.infoText = i18n.tr("Enter passphrase")
341 lockscreen.errorText = i18n.tr("Sorry, incorrect passphrase") + "\n" +
342 i18n.tr("Please re-enter")
344 lockscreen.infoText = i18n.tr("Enter passcode")
345 lockscreen.errorText = i18n.tr("Sorry, incorrect passcode")
348 lockscreen.infoText = i18n.tr("Enter %1").arg(text.toLowerCase())
349 lockscreen.errorText = i18n.tr("Sorry, incorrect %1").arg(text.toLowerCase())
356 onPromptlessChanged: {
357 if (LightDM.Greeter.promptless && LightDM.Greeter.authenticated) {
365 onAuthenticationComplete: {
366 if (LightDM.Greeter.authenticated) {
367 AccountsService.failedLogins = 0
369 // Else only penalize user for a failed login if they actually were
370 // prompted for a password. We do this below after the promptless
373 if (LightDM.Greeter.promptless) {
377 if (LightDM.Greeter.authenticated) {
380 AccountsService.failedLogins++
381 if (maxFailedLogins >= 2) { // require at least a warning
382 if (AccountsService.failedLogins === maxFailedLogins - 1) {
383 var title = lockscreen.alphaNumeric ?
384 i18n.tr("Sorry, incorrect passphrase.") :
385 i18n.tr("Sorry, incorrect passcode.")
386 var text = i18n.tr("This will be your last attempt.") + " " +
387 (lockscreen.alphaNumeric ?
388 i18n.tr("If passphrase is entered incorrectly, your phone will conduct a factory reset and all personal data will be deleted.") :
389 i18n.tr("If passcode is entered incorrectly, your phone will conduct a factory reset and all personal data will be deleted."))
390 lockscreen.showInfoPopup(title, text)
391 } else if (AccountsService.failedLogins >= maxFailedLogins) {
392 SystemImage.factoryReset() // Ouch!
395 if (failedLoginsDelayAttempts > 0 && AccountsService.failedLogins % failedLoginsDelayAttempts == 0) {
396 lockscreen.delayMinutes = failedLoginsDelayMinutes
397 forcedDelayTimer.start()
400 lockscreen.clear(true);
401 if (greeter.narrowMode) {
402 LightDM.Greeter.authenticate(LightDM.Users.data(0, LightDM.UserRoles.NameRole))
409 target: LightDM.Greeter
411 value: greeter.shown || lockscreen.shown || greeter.fakeActiveForApp != ""
417 opacity: greeterWrapper.showProgress * 0.8
421 // Just a tiny wrapper to adjust greeter's x without messing with its own dragging
426 height: parent.height - panel.panelHeight
429 enabled: !launcher.dashSwipe
433 property bool fullyShown: showProgress === 1.0
434 onFullyShownChanged: {
435 // Wait until the greeter is completely covering lockscreen before resetting it.
436 if (fullyShown && !LightDM.Greeter.authenticated) {
442 readonly property real showProgress: MathUtils.clamp((1 - x/width) + greeter.showProgress - 1, 0, 1)
443 onShowProgressChanged: {
444 if (showProgress === 0) {
445 if (LightDM.Greeter.authenticated) {
447 } else if (greeter.narrowMode) {
448 lockscreen.clear(false) // to reset focus if necessary
455 objectName: "greeter"
457 signal sessionStarted() // helpful for tests
459 property string fakeActiveForApp: ""
462 hides: [launcher, panel.indicators]
464 loadContent: required || lockscreen.required // keeps content in memory for quick show()
466 defaultBackground: shell.background
469 height: parent.height
471 dragHandleWidth: shell.edgeSize
475 if (LightDM.Greeter.startSessionSync()) {
486 if (greeter.narrowMode) {
487 LightDM.Greeter.authenticate(LightDM.Users.data(0, LightDM.UserRoles.NameRole));
489 greeter.fakeActiveForApp = "";
490 greeter.forceActiveFocus();
494 /* TODO re-enable when the corresponding changes in the service land (LP: #1361074)
495 Component.onCompleted: {
496 Connectivity.unlockAllModems()
499 onUnlocked: greeter.hide()
501 // Update launcher items for new user
502 var user = LightDM.Users.data(uid, LightDM.UserRoles.NameRole);
503 AccountsService.user = user;
504 LauncherModel.setUser(user);
507 onTease: launcher.tease()
510 target: ApplicationManager
511 property: "suspended"
512 value: greeter.shown && greeterWrapper.showProgress == 1
522 if (Powerd.status === Powerd.Off && !callManager.hasCalls && !edgeDemo.running) {
528 function showHome() {
529 if (edgeDemo.running) {
533 if (LightDM.Greeter.active) {
534 if (!LightDM.Greeter.authenticated) {
540 var animate = !LightDM.Greeter.active && !stages.shown
541 dash.setCurrentScope("clickscope", animate, false)
542 ApplicationManager.requestFocusApplication("unity8-dash")
545 function showDash() {
546 if (greeter.fakeActiveForApp !== "") { // just in case user gets here
555 if (ApplicationManager.focusedApplicationId != "unity8-dash") {
556 ApplicationManager.requestFocusApplication("unity8-dash")
570 anchors.fill: parent //because this draws indicator menus
573 available: edgeDemo.panelEnabled && (!shell.locked || AccountsService.enableIndicatorsWhileLocked) && greeter.fakeActiveForApp === ""
574 contentEnabled: edgeDemo.panelContentEnabled
575 width: parent.width > units.gu(60) ? units.gu(40) : parent.width
576 panelHeight: units.gu(3)
578 // TODO: This should be sourced by device type (eg "desktop", "tablet", "phone"...)
579 profile: LightDM.Greeter.active ? (indicatorProfile + "_greeter") : indicatorProfile
582 property bool topmostApplicationIsFullscreen:
583 ApplicationManager.focusedApplicationId &&
584 ApplicationManager.findApplication(ApplicationManager.focusedApplicationId).fullscreen
586 fullscreenMode: (topmostApplicationIsFullscreen && !LightDM.Greeter.active && launcher.progress == 0)
587 || greeter.fakeActiveForApp !== ""
592 objectName: "launcher"
594 readonly property bool dashSwipe: progress > 0
596 anchors.top: parent.top
597 anchors.bottom: parent.bottom
599 dragAreaWidth: shell.edgeSize
600 available: edgeDemo.launcherEnabled && (!shell.locked || AccountsService.enableLauncherWhileLocked) && greeter.fakeActiveForApp === ""
602 onShowDashHome: showHome()
604 if (ApplicationManager.focusedApplicationId != "unity8-dash") {
608 onDashSwipeChanged: {
610 dash.setCurrentScope("clickscope", false, true)
613 onLauncherApplicationSelected: {
614 if (greeter.fakeActiveForApp !== "") {
617 if (!edgeDemo.running)
618 shell.activateApplication(appId)
622 panel.indicators.hide()
628 id: modalNotificationBackground
630 visible: notifications.useModal && !greeter.shown && (notifications.state == "narrow")
643 model: NotificationBackend.Model
648 height: parent.height - panel.panelHeight
653 when: overlay.width <= units.gu(60)
654 AnchorChanges { target: notifications; anchors.left: parent.left }
658 when: overlay.width > units.gu(60)
659 AnchorChanges { target: notifications; anchors.left: undefined }
660 PropertyChanges { target: notifications; width: units.gu(38) }
671 shutdownFadeOutRectangle.enabled = true;
672 shutdownFadeOutRectangle.visible = true;
673 shutdownFadeOut.start();
678 id: alphaDisclaimerLabel
679 anchors.centerIn: parent
680 visible: ApplicationManager.fake ? ApplicationManager.fake : false
682 text: "EARLY ALPHA\nNOT READY FOR USE"
685 font.weight: Font.Black
686 horizontalAlignment: Text.AlignHCenter
687 verticalAlignment: Text.AlignVCenter
688 fontSizeMode: Text.Fit
690 scale: Math.min(parent.width, parent.height) / width
695 z: alphaDisclaimerLabel.z + 10
696 paused: Powerd.status === Powerd.Off // Saves power
699 indicators: panel.indicators
704 target: SessionBroadcast
705 onShowHome: showHome()
709 id: shutdownFadeOutRectangle
716 NumberAnimation on opacity {
721 if (shutdownFadeOutRectangle.enabled && shutdownFadeOutRectangle.visible) {
722 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