Commit e606551d authored by Devin Lin's avatar Devin Lin 🎨
Browse files

lockscreen: Port to kscreenlocker interface v3 and cleanup

parent 3e22b962
Pipeline #179049 passed with stage
in 1 minute and 9 seconds
/*
SPDX-FileCopyrightText: 2020-2021 Devin Lin <espidev@gmail.com>
SPDX-License-Identifier: GPL-2.0-or-later
* SPDX-FileCopyrightText: 2020-2022 Devin Lin <espidev@gmail.com>
*
* SPDX-License-Identifier: GPL-2.0-or-later
*/
import QtQuick 2.12
......@@ -19,19 +19,20 @@ import org.kde.kirigami 2.12 as Kirigami
Rectangle {
id: keypadRoot
required property var lockScreenState
// 0 - keypad is not shown, 1 - keypad is shown
property double swipeProgress
// slightly translucent background, for key contrast
color: Kirigami.ColorUtils.adjustColor(PlasmaCore.Theme.backgroundColor, {"alpha": 0.9*255})
property string pinLabel: qsTr("Enter PIN")
// colour calculations
property color buttonColor: Qt.lighter(PlasmaCore.Theme.backgroundColor, 1.3)
property color buttonPressedColor: Qt.darker(PlasmaCore.Theme.backgroundColor, 1.08)
property color buttonTextColor: PlasmaCore.Theme.textColor
property color dropShadowColor: Qt.darker(PlasmaCore.Theme.backgroundColor, 1.2)
property color headerBackgroundColor: Qt.lighter(PlasmaCore.Theme.backgroundColor, 1.3)
readonly property color buttonColor: Qt.lighter(PlasmaCore.Theme.backgroundColor, 1.3)
readonly property color buttonPressedColor: Qt.darker(PlasmaCore.Theme.backgroundColor, 1.08)
readonly property color buttonTextColor: PlasmaCore.Theme.textColor
readonly property color dropShadowColor: Qt.darker(PlasmaCore.Theme.backgroundColor, 1.2)
readonly property color headerBackgroundColor: Qt.lighter(PlasmaCore.Theme.backgroundColor, 1.3)
opacity: Math.sin((Math.PI / 2) * swipeProgress + 1.5 * Math.PI) + 1
......@@ -50,31 +51,6 @@ Rectangle {
}
}
signal passwordChanged()
function reset() {
passwordBar.reset();
}
Connections {
target: authenticator
function onSucceeded() {
passwordBar.pinLabel = qsTr("Logging in...");
passwordBar.waitingForAuth = false;
}
function onFailed() {
root.password = "";
passwordBar.pinLabel = qsTr("Wrong PIN");
passwordBar.waitingForAuth = false;
}
function onGraceLockedChanged() {
// try authenticating if it was waiting for grace lock to stop and it has stopped
if (!authenticator.graceLocked && passwordBar.waitingForAuth) {
authenticator.tryUnlock(root.password);
}
}
}
// listen for keyboard events
Keys.onPressed: {
if (event.modifiers === Qt.NoModifier) {
......@@ -107,30 +83,25 @@ Rectangle {
// pin display and bar
PasswordBar {
id: passwordBar
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
color: keypadRoot.headerBackgroundColor
opacity: (Math.sin(2*((Math.PI / 2) * keypadRoot.swipeProgress + 1.5 * Math.PI)) + 1)
lockScreenState: keypadRoot.lockScreenState
keypadOpen: swipeProgress === 1
password: root.password
previewCharIndex: -2
pinLabel: qsTr("Enter PIN")
onPasswordChanged: keypadRoot.passwordChanged()
onChangePassword: root.password = password
Binding {
target: passwordBar
property: "password"
value: root.password
}
}
// actual number keys
ColumnLayout {
visible: opacity > 0
opacity: passwordBar.isPinMode ? 1 : 0
Behavior on opacity {
NumberAnimation {
duration: Kirigami.Units.longDuration
......
......@@ -6,12 +6,14 @@
*/
import QtQuick 2.12
import QtQuick.Controls 1.1
import QtQuick.Layouts 1.1
import QtQuick.Controls 2.15
import QtQuick.Layouts 1.15
import org.kde.plasma.core 2.0 as PlasmaCore
import org.kde.notificationmanager 1.1 as Notifications
import org.kde.kirigami 2.12 as Kirigami
/**
* Lockscreen component that is loaded after the device is locked.
*
......@@ -20,24 +22,17 @@ import org.kde.notificationmanager 1.1 as Notifications
PlasmaCore.ColorScope {
id: root
property string password
property var lockScreenState: LockScreenState {}
property var notifModel: Notifications.WatchedNotificationsModel {}
property bool isWidescreen: root.height < root.width * 0.75
property bool notificationsShown: false
readonly property bool drawerOpen: flickable.openFactor >= 1
function askPassword() {
flickable.goToOpenPosition();
}
colorGroup: PlasmaCore.Theme.ComplementaryColorGroup
anchors.fill: parent
Notifications.WatchedNotificationsModel {
id: notifModel
}
// wallpaper blur
Loader {
anchors.fill: parent
......@@ -55,9 +50,18 @@ PlasmaCore.ColorScope {
anchors.fill: parent
openFactor: flickable.openFactor
notificationsModel: notifModel
notificationsModel: root.notifModel
onPasswordRequested: root.askPassword()
}
Connections {
target: root.lockScreenState
// ensure keypad is opened when password is updated (ex. keyboard)
function onPasswordChanged() {
flickable.goToOpenPosition()
}
}
FlickContainer {
id: flickable
......@@ -67,11 +71,13 @@ PlasmaCore.ColorScope {
keypadHeight: PlasmaCore.Units.gridUnit * 20
// go to closed position when loaded
Component.onCompleted: {
flickable.position = 0;
flickable.goToClosePosition();
}
// update position, and cap it at the keypad height
onPositionChanged: {
if (position > keypadHeight) {
position = keypadHeight;
......@@ -94,10 +100,11 @@ PlasmaCore.ColorScope {
fullHeight: root.height
notificationsModel: notifModel
lockScreenState: root.lockScreenState
notificationsModel: root.notifModel
onNotificationsShownChanged: root.notificationsShown = notificationsShown
onPasswordRequested: root.askPassword()
onPasswordRequested: flickable.goToOpenPosition()
anchors.top: parent.top
anchors.bottom: scrollUpIconLoader.top
......@@ -110,14 +117,16 @@ PlasmaCore.ColorScope {
LockScreenWideScreenContent {
id: tabletComponent
visible: isWidescreen
active: visible
opacity: 1 - flickable.openFactor
notificationsModel: notifModel
lockScreenState: root.lockScreenState
notificationsModel: root.notifModel
onNotificationsShownChanged: root.notificationsShown = notificationsShown
onPasswordRequested: root.askPassword()
onPasswordRequested: flickable.goToOpenPosition()
anchors.topMargin: headerBar.statusBarHeight
anchors.top: parent.top
......@@ -159,11 +168,31 @@ PlasmaCore.ColorScope {
sourceComponent: ColumnLayout {
transform: Translate { y: flickable.keypadHeight - flickable.position }
spacing: PlasmaCore.Units.gridUnit
spacing: 0
// info notification text
Label {
Layout.fillWidth: true
Layout.rightMargin: Kirigami.Units.largeSpacing
Layout.leftMargin: Kirigami.Units.largeSpacing
Layout.bottomMargin: PlasmaCore.Units.smallSpacing * 2
font.pointSize: 9
elide: Text.ElideRight
horizontalAlignment: Text.AlignHCenter
text: root.lockScreenState.info
opacity: (root.lockScreenState.info.length === 0 || flickable.openFactor < 1) ? 0 : 1
color: 'white'
Behavior on opacity {
NumberAnimation { duration: 200 }
}
}
// scroll down icon
PlasmaCore.IconItem {
Layout.alignment: Qt.AlignHCenter
Layout.bottomMargin: PlasmaCore.Units.gridUnit
implicitWidth: PlasmaCore.Units.iconSizes.smallMedium
implicitHeight: PlasmaCore.Units.iconSizes.smallMedium
colorGroup: PlasmaCore.Theme.ComplementaryColorGroup
......@@ -174,10 +203,10 @@ PlasmaCore.ColorScope {
Keypad {
id: keypad
Layout.fillWidth: true
focus: true
lockScreenState: root.lockScreenState
swipeProgress: flickable.openFactor
onPasswordChanged: flickable.goToOpenPosition()
}
}
}
......
......@@ -15,6 +15,7 @@ import org.kde.plasma.private.mobileshell 1.0 as MobileShell
Loader {
id: root
required property var lockScreenState
property var notificationsModel: []
property bool notificationsShown: false
......@@ -67,6 +68,7 @@ Loader {
NotificationsComponent {
id: notificationComponent
lockScreenState: root.lockScreenState
notificationsModel: root.notificationsModel
Layout.fillHeight: true
......
/*
* SPDX-FileCopyrightText: 2022 Devin Lin <espidev@gmail.com>
*
* SPDX-License-Identifier: GPL-2.0-or-later
*/
import QtQml 2.15
import QtQuick 2.15
QtObject {
id: root
// current password being typed
property string password: ""
// whether waiting for authentication after trying password
property bool waitingForAuth: false
property string info: ""
signal reset()
signal unlockSucceeded()
signal unlockFailed()
function tryPassword() {
if (root.password !== '') { // prevent typing lock when password is empty
waitingForAuth = true;
}
authenticator.tryUnlock();
}
function resetPassword() {
password = "";
root.reset();
}
property var connections: Connections {
target: authenticator
function onSucceeded() {
console.log('login succeeded');
root.waitingForAuth = false;
root.unlockSucceeded();
Qt.quit();
}
function onFailed() {
console.log('login failed');
root.waitingForAuth = false;
root.password = "";
root.unlockFailed();
}
function onInfoMessage(msg) {
console.log('info: ' + msg);
root.info += msg + " ";
}
// TODO
function onErrorMessage(msg) {
console.log('error: ' + msg);
}
// TODO
function onPrompt(msg) {
console.log('prompt: ' + msg);
}
function onPromptForSecret(msg) {
console.log('prompt secret: ' + msg);
authenticator.respond(root.password);
authenticator.tryUnlock();
}
}
}
......@@ -15,6 +15,7 @@ import org.kde.plasma.private.mobileshell 1.0 as MobileShell
Loader {
id: root
required property var lockScreenState
property var notificationsModel: []
property bool notificationsShown: false
......@@ -65,6 +66,7 @@ Loader {
NotificationsComponent {
id: notificationComponent
lockScreenState: root.lockScreenState
notificationsModel: root.notificationsModel
Layout.alignment: Qt.AlignVCenter | Qt.AlignHCenter
......
......@@ -14,8 +14,8 @@ import org.kde.notificationmanager 1.1 as Notifications
import org.kde.plasma.private.mobileshell 1.0 as MobileShell
Rectangle {
id: rect
id: root
required property var lockScreenState
property var notificationsModel: []
readonly property bool notificationsShown: notificationsList.hasNotifications
......@@ -30,35 +30,37 @@ Rectangle {
color: "transparent"
clip: true
PlasmaCore.ColorScope {
anchors.fill: parent
anchors.topMargin: rect.topMargin
anchors.bottomMargin: rect.bottomMargin
anchors.leftMargin: rect.leftMargin
anchors.rightMargin: rect.rightMargin
colorGroup: PlasmaCore.Theme.NormalColorGroup
Connections {
target: lockScreenState
Connections {
target: authenticator
function onSucceeded() {
// run pending action if successfully unlocked
if (notificationsList.requestNotificationAction) {
notificationsList.runPendingAction();
notificationsList.requestNotificationAction = false;
}
}
function onFailed() {
function onUnlockSucceeded() {
// run pending action if successfully unlocked
if (notificationsList.requestNotificationAction) {
notificationsList.runPendingAction();
notificationsList.requestNotificationAction = false;
}
}
function onUnlockFailed() {
notificationsList.requestNotificationAction = false;
}
}
PlasmaCore.ColorScope {
anchors.fill: parent
anchors.topMargin: root.topMargin
anchors.bottomMargin: root.bottomMargin
anchors.leftMargin: root.leftMargin
anchors.rightMargin: root.rightMargin
colorGroup: PlasmaCore.Theme.NormalColorGroup
MobileShell.NotificationsWidget {
id: notificationsList
anchors.fill: parent
historyModelType: MobileShell.NotificationsModelType.WatchedNotificationsModel
actionsRequireUnlock: true
historyModel: rect.notificationsModel
historyModel: root.notificationsModel
property bool requestNotificationAction: false
......
/*
* SPDX-FileCopyrightText: 2020-2021 Devin Lin <espidev@gmail.com>
* SPDX-FileCopyrightText: 2020-2022 Devin Lin <espidev@gmail.com>
* SPDX-License-Identifier: GPL-2.0-or-later
*/
......@@ -7,67 +7,77 @@ import QtQuick 2.12
import QtQuick.Controls 2.1
import QtQuick.Layouts 1.1
import QtGraphicalEffects 1.12
import org.kde.plasma.core 2.0 as PlasmaCore
import org.kde.plasma.workspace.keyboardlayout 1.0
import org.kde.kirigami 2.12 as Kirigami
import org.kde.plasma.workspace.keyboardlayout 1.0 as Keyboards
import org.kde.kirigami 2.12 as Kirigami
Rectangle {
id: root
implicitHeight: PlasmaCore.Units.gridUnit * 2.5
required property var lockScreenState
// toggle between pin and password mode
property bool isPinMode: true
property string password
// for displaying temporary number in pin dot display
property int previewCharIndex
property int previewCharIndex: -2
property string pinLabel
property bool keypadOpen
property string pinLabel: qsTr("Enter PIN")
// if waiting for result of auth
property bool waitingForAuth: false
property bool keypadOpen
property color headerTextColor: Kirigami.ColorUtils.adjustColor(PlasmaCore.Theme.textColor, {"alpha": 0.75*255})
property color headerTextInactiveColor: Kirigami.ColorUtils.adjustColor(PlasmaCore.Theme.textColor, {"alpha": 0.4*255})
readonly property color headerTextColor: Kirigami.ColorUtils.adjustColor(PlasmaCore.Theme.textColor, {"alpha": 0.75*255})
readonly property color headerTextInactiveColor: Kirigami.ColorUtils.adjustColor(PlasmaCore.Theme.textColor, {"alpha": 0.4*255})
signal changePassword();
// model for shown dots
// we need to use a listmodel to avoid all delegates from reloading
ListModel {
id: dotDisplayModel
}
// keypad functions
function reset() {
waitingForAuth = false;
root.password = "";
changePassword();
root.pinLabel = qsTr("Enter PIN");
Connections {
target: root.lockScreenState
function onUnlockSucceeded() {
root.pinLabel = qsTr("Logging in...");
}
function onUnlockFailed() {
root.pinLabel = qsTr("Wrong PIN");
}
function onPasswordChanged() {
while (root.lockScreenState.password.length < dotDisplayModel.count) {
dotDisplayModel.remove(dotDisplayModel.count - 1);
}
while (root.lockScreenState.password.length > dotDisplayModel.count) {
dotDisplayModel.append({"char": root.lockScreenState.password.charAt(dotDisplayModel.count)});
}
}
}
// keypad functions
function backspace() {
if (!root.waitingForAuth) {
if (!lockScreenState.waitingForAuth) {
root.previewCharIndex = -2;
root.password = root.password.substr(0, root.password.length - 1);
changePassword();
lockScreenState.password = lockScreenState.password.substr(0, lockScreenState.password.length - 1);
}
}
function clear() {
if (!root.waitingForAuth) {
if (!lockScreenState.waitingForAuth) {
root.previewCharIndex = -2;
root.password = "";
changePassword();
lockScreenState.resetPassword();
}
}
function enter() {
if (root.password !== "") { // prevent typing lock when password is empty
root.waitingForAuth = true;
}
lockScreenState.tryPassword();
// don't try to unlock if there is a timeout (unlock once unlocked)
if (!authenticator.graceLocked) {
authenticator.tryUnlock(root.password);
}
if (keypadOpen && !isPinMode) {
// make sure keyboard doesn't close
openKeyboardTimer.restart();
......@@ -75,13 +85,14 @@ Rectangle {
}
function keyPress(data) {
if (!root.waitingForAuth) {
if (!lockScreenState.waitingForAuth) {
if (root.pinLabel !== qsTr("Enter PIN")) {
root.pinLabel = qsTr("Enter PIN");
}
root.previewCharIndex = root.password.length;
root.password += data
changePassword();
root.previewCharIndex = lockScreenState.password.length;
lockScreenState.password += data
// trigger turning letter into dot later
letterTimer.restart();
......@@ -108,19 +119,6 @@ Rectangle {
}
}
// we need to use a listmodel to avoid all delegates from reloading
ListModel {
id: dotDisplayModel
}
onPasswordChanged: {
while (password.length < dotDisplayModel.count) {
dotDisplayModel.remove(dotDisplayModel.count - 1);
}
while (password.length > dotDisplayModel.count) {
dotDisplayModel.append({"char": password.charAt(dotDisplayModel.count)});
}
}
// hidden textfield so that the virtual keyboard shows up
TextField {
id: textField
......@@ -139,19 +137,22 @@ Rectangle {
property string prevText: ""
Connections {
target: root
function onChangePassword() {
if (textField.text != root.password) {
target: root.lockScreenState
function onPasswordChanged() {
if (textField.text != root.lockScreenState.password) {
textField.externalEdit = true;
textField.text = root.password;
textField.text = root.lockScreenState.password;
}