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/>.
18 import AccountsService 0.1
20 import Unity.Application 0.1
21 import Ubuntu.Components 0.1
22 import Ubuntu.Components.Popups 1.0
23 import Ubuntu.Gestures 0.1
24 import Ubuntu.SystemImage 0.1
25 import Ubuntu.Telephony 0.1 as Telephony
26 import Unity.Connectivity 0.1
27 import Unity.Launcher 0.1
29 import LightDM 0.1 as LightDM
31 import SessionBroadcast 0.1
36 import "Notifications"
38 import Unity.Notifications 1.0 as NotificationBackend
39 import Unity.Session 0.1
40 import Unity.DashCommunicator 0.1
45 // this is only here to select the width / height of the window if not running fullscreen
46 property bool tablet: false
47 width: tablet ? units.gu(160) : applicationArguments.hasGeometry() ? applicationArguments.width() : units.gu(40)
48 height: tablet ? units.gu(100) : applicationArguments.hasGeometry() ? applicationArguments.height() : units.gu(71)
50 property real edgeSize: units.gu(2)
51 property url defaultBackground: Qt.resolvedUrl(shell.width >= units.gu(60) ? "graphics/tablet_background.jpg" : "graphics/phone_background.jpg")
52 property url background
53 readonly property real panelHeight: panel.panelHeight
55 readonly property bool locked: LightDM.Greeter.active && !LightDM.Greeter.authenticated
57 property bool sideStageEnabled: shell.width >= units.gu(100)
58 readonly property string focusedApplicationId: ApplicationManager.focusedApplicationId
60 property int maxFailedLogins: -1 // disabled by default for now, will enable via settings in future
61 property int failedLoginsDelayAttempts: 7 // number of failed logins
62 property int failedLoginsDelaySeconds: 5 * 60 // seconds of forced waiting
64 function activateApplication(appId) {
65 if (ApplicationManager.findApplication(appId)) {
66 ApplicationManager.requestFocusApplication(appId);
68 var execFlags = shell.sideStageEnabled ? ApplicationManager.NoFlag : ApplicationManager.ForceMainStage;
69 ApplicationManager.startApplication(appId, execFlags);
73 function setFakeActiveForApp(app) {
75 greeter.fakeActiveForApp = app
82 property: "applicationManager"
83 value: ApplicationManager
86 Component.onCompleted: {
87 Theme.name = "Ubuntu.Components.Themes.SuruGradient"
91 id: backgroundSettings
92 schema.id: "org.gnome.desktop.background"
94 property url gSettingsPicture: backgroundSettings.pictureUri != undefined && backgroundSettings.pictureUri.length > 0 ? backgroundSettings.pictureUri : shell.defaultBackground
95 onGSettingsPictureChanged: {
96 shell.background = gSettingsPicture
105 objectName: "dashCommunicator"
109 // Handle but do not filter out volume keys
110 Keys.onVolumeUpPressed: { volumeControl.volumeUp(); event.accepted = false; }
111 Keys.onVolumeDownPressed: { volumeControl.volumeDown(); event.accepted = false; }
114 if (event.key == Qt.Key_PowerOff || event.key == Qt.Key_PowerDown) {
115 dialogs.onPowerKeyPressed();
116 event.accepted = true;
118 event.accepted = false;
123 if (event.key == Qt.Key_PowerOff || event.key == Qt.Key_PowerDown) {
124 dialogs.onPowerKeyReleased();
125 event.accepted = true;
127 event.accepted = false;
136 height: parent.height
137 visible: !ApplicationManager.empty
140 target: ApplicationManager
142 if (appId === "dialer-app") {
143 // Always let the dialer-app through. Either user asked
144 // for an emergency call or accepted an incoming call.
145 setFakeActiveForApp(appId)
146 } else if (greeter.fakeActiveForApp !== "" && greeter.fakeActiveForApp !== appId) {
153 onFocusedApplicationIdChanged: {
154 if (greeter.fakeActiveForApp !== "" && greeter.fakeActiveForApp !== ApplicationManager.focusedApplicationId) {
157 panel.indicators.hide();
160 onApplicationAdded: {
161 if (greeter.shown && appId != "unity8-dash") {
164 if (appId === "dialer-app") {
165 // Always let the dialer-app through. Either user asked
166 // for an emergency call or accepted an incoming call.
167 setFakeActiveForApp(appId)
174 id: applicationsDisplayLoader
177 source: shell.sideStageEnabled ? "Stages/TabletStage.qml" : "Stages/PhoneStage.qml"
180 target: applicationsDisplayLoader.item
181 property: "objectName"
185 target: applicationsDisplayLoader.item
186 property: "dragAreaWidth"
187 value: shell.edgeSize
190 target: applicationsDisplayLoader.item
191 property: "maximizedAppTopMargin"
192 // Not just using panel.panelHeight as that changes depending on the focused app.
193 value: panel.indicators.panelHeight
196 target: applicationsDisplayLoader.item
197 property: "interactive"
198 value: edgeDemo.stagesEnabled && !greeter.shown && !lockscreen.shown && panel.indicators.fullyClosed && launcher.progress == 0
201 target: applicationsDisplayLoader.item
202 property: "spreadEnabled"
203 value: edgeDemo.stagesEnabled && greeter.fakeActiveForApp === "" // to support emergency dialer hack
206 target: applicationsDisplayLoader.item
207 property: "inverseProgress"
208 value: launcher.progress
215 objectName: "inputMethod"
216 anchors { fill: parent; topMargin: panel.panelHeight }
217 z: notifications.useModal || panel.indicators.shown ? overlay.z + 1 : overlay.z - 1
221 target: SurfaceManager
223 if (surface.type == MirSurfaceItem.InputMethod) {
224 inputMethod.surface = surface;
228 onSurfaceDestroyed: {
229 if (inputMethod.surface == surface) {
230 inputMethod.surface = null;
231 surface.parent = null;
233 if (!surface.parent) {
234 // there's no one displaying it. delete it right away
240 target: SessionManager
242 if (!session.parentSession && !session.application) {
243 // nothing is using it. delete it right away
251 objectName: "lockscreen"
253 readonly property int backgroundTopMargin: -panel.panelHeight
255 hides: [launcher, panel.indicators]
258 showAnimation: StandardAnimation { property: "opacity"; to: 1 }
259 hideAnimation: StandardAnimation { property: "opacity"; to: 0 }
263 height: parent.height - panel.panelHeight
264 background: shell.background
265 alphaNumeric: AccountsService.passwordDisplayHint === AccountsService.Keyboard
269 onEntered: LightDM.Greeter.respond(passphrase);
270 onCancel: greeter.show()
271 onEmergencyCall: shell.activateApplication("dialer-app") // will automatically enter fake-active mode
273 onShownChanged: if (shown) greeter.fakeActiveForApp = ""
275 Component.onCompleted: {
276 if (greeter.narrowMode) {
277 LightDM.Greeter.authenticate(LightDM.Users.data(0, LightDM.UserRoles.NameRole))
283 target: LightDM.Greeter
285 onShowGreeter: greeter.show()
286 onHideGreeter: greeter.login()
289 if (greeter.narrowMode) {
290 if (isDefaultPrompt) {
291 if (lockscreen.alphaNumeric) {
292 lockscreen.infoText = i18n.tr("Enter your passphrase")
293 lockscreen.errorText = i18n.tr("Sorry, incorrect passphrase")
295 lockscreen.infoText = i18n.tr("Enter your passcode")
296 lockscreen.errorText = i18n.tr("Sorry, incorrect passcode")
299 lockscreen.infoText = i18n.tr("Enter your %1").arg(text.toLowerCase())
300 lockscreen.errorText = i18n.tr("Sorry, incorrect %1").arg(text.toLowerCase())
307 onPromptlessChanged: {
308 if (LightDM.Greeter.promptless && LightDM.Greeter.authenticated) {
316 onAuthenticationComplete: {
317 if (LightDM.Greeter.authenticated) {
318 AccountsService.failedLogins = 0
320 // Else only penalize user for a failed login if they actually were
321 // prompted for a password. We do this below after the promptless
324 if (LightDM.Greeter.promptless) {
328 if (LightDM.Greeter.authenticated) {
331 AccountsService.failedLogins++
332 if (maxFailedLogins >= 2) { // require at least a warning
333 if (AccountsService.failedLogins === maxFailedLogins - 1) {
334 var title = lockscreen.alphaNumeric ?
335 i18n.tr("Sorry, incorrect passphrase.") :
336 i18n.tr("Sorry, incorrect passcode.")
337 var text = i18n.tr("This will be your last attempt.") + " " +
338 (lockscreen.alphaNumeric ?
339 i18n.tr("If passphrase is entered incorrectly, your phone will conduct a factory reset and all personal data will be deleted.") :
340 i18n.tr("If passcode is entered incorrectly, your phone will conduct a factory reset and all personal data will be deleted."))
341 lockscreen.showInfoPopup(title, text)
342 } else if (AccountsService.failedLogins >= maxFailedLogins) {
343 SystemImage.factoryReset() // Ouch!
346 if (failedLoginsDelayAttempts > 0 && AccountsService.failedLogins % failedLoginsDelayAttempts == 0) {
347 lockscreen.forceDelay(failedLoginsDelaySeconds * 1000)
350 lockscreen.clear(true);
351 if (greeter.narrowMode) {
352 LightDM.Greeter.authenticate(LightDM.Users.data(0, LightDM.UserRoles.NameRole))
359 target: LightDM.Greeter
361 value: greeter.shown || lockscreen.shown || greeter.fakeActiveForApp != ""
367 opacity: greeterWrapper.showProgress * 0.8
371 // Just a tiny wrapper to adjust greeter's x without messing with its own dragging
376 height: parent.height - panel.panelHeight
379 enabled: !launcher.dashSwipe
383 property bool fullyShown: showProgress === 1.0
384 onFullyShownChanged: {
385 // Wait until the greeter is completely covering lockscreen before resetting it.
386 if (fullyShown && !LightDM.Greeter.authenticated) {
392 readonly property real showProgress: MathUtils.clamp((1 - x/width) + greeter.showProgress - 1, 0, 1)
393 onShowProgressChanged: if (LightDM.Greeter.authenticated && showProgress === 0) greeter.login()
397 objectName: "greeter"
399 signal sessionStarted() // helpful for tests
401 property string fakeActiveForApp: ""
404 hides: [launcher, panel.indicators]
406 loadContent: required || lockscreen.required // keeps content in memory for quick show()
408 defaultBackground: shell.background
411 height: parent.height
413 dragHandleWidth: shell.edgeSize
417 if (LightDM.Greeter.startSessionSync()) {
428 if (greeter.narrowMode) {
429 LightDM.Greeter.authenticate(LightDM.Users.data(0, LightDM.UserRoles.NameRole));
431 greeter.fakeActiveForApp = "";
432 greeter.forceActiveFocus();
436 /* TODO re-enable when the corresponding changes in the service land (LP: #1361074)
437 Component.onCompleted: {
438 Connectivity.unlockAllModems()
441 onUnlocked: greeter.hide()
443 // Update launcher items for new user
444 var user = LightDM.Users.data(uid, LightDM.UserRoles.NameRole);
445 AccountsService.user = user;
446 LauncherModel.setUser(user);
449 onTease: launcher.tease()
452 target: ApplicationManager
453 property: "suspended"
454 value: greeter.shown && greeterWrapper.showProgress == 1
464 if (Powerd.status === Powerd.Off && !callManager.hasCalls && !edgeDemo.running) {
470 function showHome() {
471 if (edgeDemo.running) {
475 if (LightDM.Greeter.active) {
476 if (!LightDM.Greeter.authenticated) {
482 var animate = !LightDM.Greeter.active && !stages.shown
483 dash.setCurrentScope("clickscope", animate, false)
484 ApplicationManager.requestFocusApplication("unity8-dash")
487 function showDash() {
496 ApplicationManager.requestFocusApplication("unity8-dash")
509 anchors.fill: parent //because this draws indicator menus
512 available: edgeDemo.panelEnabled && (!shell.locked || AccountsService.enableIndicatorsWhileLocked) && greeter.fakeActiveForApp === ""
513 contentEnabled: edgeDemo.panelContentEnabled
514 width: parent.width > units.gu(60) ? units.gu(40) : parent.width
515 panelHeight: units.gu(3)
518 property bool topmostApplicationIsFullscreen:
519 ApplicationManager.focusedApplicationId &&
520 ApplicationManager.findApplication(ApplicationManager.focusedApplicationId).fullscreen
522 fullscreenMode: (topmostApplicationIsFullscreen && !LightDM.Greeter.active && launcher.progress == 0)
523 || greeter.fakeActiveForApp !== ""
528 objectName: "launcher"
530 readonly property bool dashSwipe: progress > 0
532 anchors.top: parent.top
533 anchors.bottom: parent.bottom
535 dragAreaWidth: shell.edgeSize
536 available: edgeDemo.launcherEnabled && (!shell.locked || AccountsService.enableLauncherWhileLocked) && greeter.fakeActiveForApp === ""
538 onShowDashHome: showHome()
540 onDashSwipeChanged: {
542 dash.setCurrentScope("clickscope", false, true)
545 onLauncherApplicationSelected: {
546 if (greeter.fakeActiveForApp !== "") {
549 if (!edgeDemo.running)
550 shell.activateApplication(appId)
554 panel.indicators.hide()
560 id: modalNotificationBackground
562 visible: notifications.useModal && !greeter.shown && (notifications.state == "narrow")
575 model: NotificationBackend.Model
580 height: parent.height - panel.panelHeight
585 when: overlay.width <= units.gu(60)
586 AnchorChanges { target: notifications; anchors.left: parent.left }
590 when: overlay.width > units.gu(60)
591 AnchorChanges { target: notifications; anchors.left: undefined }
592 PropertyChanges { target: notifications; width: units.gu(38) }
603 shutdownFadeOutRectangle.enabled = true;
604 shutdownFadeOutRectangle.visible = true;
605 shutdownFadeOut.start();
610 id: alphaDisclaimerLabel
611 anchors.centerIn: parent
612 visible: ApplicationManager.fake ? ApplicationManager.fake : false
614 text: "EARLY ALPHA\nNOT READY FOR USE"
617 font.weight: Font.Black
618 horizontalAlignment: Text.AlignHCenter
619 verticalAlignment: Text.AlignVCenter
620 fontSizeMode: Text.Fit
622 scale: Math.min(parent.width, parent.height) / width
627 z: alphaDisclaimerLabel.z + 10
628 paused: Powerd.status === Powerd.Off // Saves power
631 indicators: panel.indicators
636 target: SessionBroadcast
637 onShowHome: showHome()
641 id: shutdownFadeOutRectangle
648 NumberAnimation on opacity {
653 if (shutdownFadeOutRectangle.enabled && shutdownFadeOutRectangle.visible) {
654 DBusUnitySessionService.Shutdown();