lockscreen: Port to kscreenlocker interface v3 and cleanup

This commit is contained in:
Devin Lin 2022-05-20 23:41:11 -04:00
parent 3e22b962f2
commit e606551d7b
8 changed files with 223 additions and 139 deletions

View file

@ -1,7 +1,7 @@
/* /*
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 * SPDX-License-Identifier: GPL-2.0-or-later
*/ */
import QtQuick 2.12 import QtQuick 2.12
@ -19,19 +19,20 @@ import org.kde.kirigami 2.12 as Kirigami
Rectangle { Rectangle {
id: keypadRoot id: keypadRoot
required property var lockScreenState
// 0 - keypad is not shown, 1 - keypad is shown // 0 - keypad is not shown, 1 - keypad is shown
property double swipeProgress property double swipeProgress
// slightly translucent background, for key contrast // slightly translucent background, for key contrast
color: Kirigami.ColorUtils.adjustColor(PlasmaCore.Theme.backgroundColor, {"alpha": 0.9*255}) color: Kirigami.ColorUtils.adjustColor(PlasmaCore.Theme.backgroundColor, {"alpha": 0.9*255})
property string pinLabel: qsTr("Enter PIN")
// colour calculations // colour calculations
property color buttonColor: Qt.lighter(PlasmaCore.Theme.backgroundColor, 1.3) readonly property color buttonColor: Qt.lighter(PlasmaCore.Theme.backgroundColor, 1.3)
property color buttonPressedColor: Qt.darker(PlasmaCore.Theme.backgroundColor, 1.08) readonly property color buttonPressedColor: Qt.darker(PlasmaCore.Theme.backgroundColor, 1.08)
property color buttonTextColor: PlasmaCore.Theme.textColor readonly property color buttonTextColor: PlasmaCore.Theme.textColor
property color dropShadowColor: Qt.darker(PlasmaCore.Theme.backgroundColor, 1.2) readonly property color dropShadowColor: Qt.darker(PlasmaCore.Theme.backgroundColor, 1.2)
property color headerBackgroundColor: Qt.lighter(PlasmaCore.Theme.backgroundColor, 1.3) readonly property color headerBackgroundColor: Qt.lighter(PlasmaCore.Theme.backgroundColor, 1.3)
opacity: Math.sin((Math.PI / 2) * swipeProgress + 1.5 * Math.PI) + 1 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 // listen for keyboard events
Keys.onPressed: { Keys.onPressed: {
if (event.modifiers === Qt.NoModifier) { if (event.modifiers === Qt.NoModifier) {
@ -107,30 +83,25 @@ Rectangle {
// pin display and bar // pin display and bar
PasswordBar { PasswordBar {
id: passwordBar id: passwordBar
anchors.top: parent.top anchors.top: parent.top
anchors.left: parent.left anchors.left: parent.left
anchors.right: parent.right anchors.right: parent.right
color: keypadRoot.headerBackgroundColor color: keypadRoot.headerBackgroundColor
opacity: (Math.sin(2*((Math.PI / 2) * keypadRoot.swipeProgress + 1.5 * Math.PI)) + 1) opacity: (Math.sin(2*((Math.PI / 2) * keypadRoot.swipeProgress + 1.5 * Math.PI)) + 1)
lockScreenState: keypadRoot.lockScreenState
keypadOpen: swipeProgress === 1 keypadOpen: swipeProgress === 1
password: root.password
previewCharIndex: -2 previewCharIndex: -2
pinLabel: qsTr("Enter PIN")
onPasswordChanged: keypadRoot.passwordChanged()
onChangePassword: root.password = password
Binding {
target: passwordBar
property: "password"
value: root.password
}
} }
// actual number keys // actual number keys
ColumnLayout { ColumnLayout {
visible: opacity > 0 visible: opacity > 0
opacity: passwordBar.isPinMode ? 1 : 0 opacity: passwordBar.isPinMode ? 1 : 0
Behavior on opacity { Behavior on opacity {
NumberAnimation { NumberAnimation {
duration: Kirigami.Units.longDuration duration: Kirigami.Units.longDuration

View file

@ -6,12 +6,14 @@
*/ */
import QtQuick 2.12 import QtQuick 2.12
import QtQuick.Controls 1.1 import QtQuick.Controls 2.15
import QtQuick.Layouts 1.1 import QtQuick.Layouts 1.15
import org.kde.plasma.core 2.0 as PlasmaCore import org.kde.plasma.core 2.0 as PlasmaCore
import org.kde.notificationmanager 1.1 as Notifications 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. * Lockscreen component that is loaded after the device is locked.
* *
@ -20,24 +22,17 @@ import org.kde.notificationmanager 1.1 as Notifications
PlasmaCore.ColorScope { PlasmaCore.ColorScope {
id: root 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 isWidescreen: root.height < root.width * 0.75
property bool notificationsShown: false property bool notificationsShown: false
readonly property bool drawerOpen: flickable.openFactor >= 1 readonly property bool drawerOpen: flickable.openFactor >= 1
function askPassword() {
flickable.goToOpenPosition();
}
colorGroup: PlasmaCore.Theme.ComplementaryColorGroup colorGroup: PlasmaCore.Theme.ComplementaryColorGroup
anchors.fill: parent anchors.fill: parent
Notifications.WatchedNotificationsModel {
id: notifModel
}
// wallpaper blur // wallpaper blur
Loader { Loader {
anchors.fill: parent anchors.fill: parent
@ -55,9 +50,18 @@ PlasmaCore.ColorScope {
anchors.fill: parent anchors.fill: parent
openFactor: flickable.openFactor openFactor: flickable.openFactor
notificationsModel: notifModel notificationsModel: root.notifModel
onPasswordRequested: root.askPassword() onPasswordRequested: root.askPassword()
} }
Connections {
target: root.lockScreenState
// ensure keypad is opened when password is updated (ex. keyboard)
function onPasswordChanged() {
flickable.goToOpenPosition()
}
}
FlickContainer { FlickContainer {
id: flickable id: flickable
@ -67,11 +71,13 @@ PlasmaCore.ColorScope {
keypadHeight: PlasmaCore.Units.gridUnit * 20 keypadHeight: PlasmaCore.Units.gridUnit * 20
// go to closed position when loaded
Component.onCompleted: { Component.onCompleted: {
flickable.position = 0; flickable.position = 0;
flickable.goToClosePosition(); flickable.goToClosePosition();
} }
// update position, and cap it at the keypad height
onPositionChanged: { onPositionChanged: {
if (position > keypadHeight) { if (position > keypadHeight) {
position = keypadHeight; position = keypadHeight;
@ -94,10 +100,11 @@ PlasmaCore.ColorScope {
fullHeight: root.height fullHeight: root.height
notificationsModel: notifModel lockScreenState: root.lockScreenState
notificationsModel: root.notifModel
onNotificationsShownChanged: root.notificationsShown = notificationsShown onNotificationsShownChanged: root.notificationsShown = notificationsShown
onPasswordRequested: root.askPassword() onPasswordRequested: flickable.goToOpenPosition()
anchors.top: parent.top anchors.top: parent.top
anchors.bottom: scrollUpIconLoader.top anchors.bottom: scrollUpIconLoader.top
@ -110,14 +117,16 @@ PlasmaCore.ColorScope {
LockScreenWideScreenContent { LockScreenWideScreenContent {
id: tabletComponent id: tabletComponent
visible: isWidescreen visible: isWidescreen
active: visible active: visible
opacity: 1 - flickable.openFactor opacity: 1 - flickable.openFactor
notificationsModel: notifModel lockScreenState: root.lockScreenState
notificationsModel: root.notifModel
onNotificationsShownChanged: root.notificationsShown = notificationsShown onNotificationsShownChanged: root.notificationsShown = notificationsShown
onPasswordRequested: root.askPassword() onPasswordRequested: flickable.goToOpenPosition()
anchors.topMargin: headerBar.statusBarHeight anchors.topMargin: headerBar.statusBarHeight
anchors.top: parent.top anchors.top: parent.top
@ -159,11 +168,31 @@ PlasmaCore.ColorScope {
sourceComponent: ColumnLayout { sourceComponent: ColumnLayout {
transform: Translate { y: flickable.keypadHeight - flickable.position } 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 // scroll down icon
PlasmaCore.IconItem { PlasmaCore.IconItem {
Layout.alignment: Qt.AlignHCenter Layout.alignment: Qt.AlignHCenter
Layout.bottomMargin: PlasmaCore.Units.gridUnit
implicitWidth: PlasmaCore.Units.iconSizes.smallMedium implicitWidth: PlasmaCore.Units.iconSizes.smallMedium
implicitHeight: PlasmaCore.Units.iconSizes.smallMedium implicitHeight: PlasmaCore.Units.iconSizes.smallMedium
colorGroup: PlasmaCore.Theme.ComplementaryColorGroup colorGroup: PlasmaCore.Theme.ComplementaryColorGroup
@ -174,10 +203,10 @@ PlasmaCore.ColorScope {
Keypad { Keypad {
id: keypad id: keypad
Layout.fillWidth: true Layout.fillWidth: true
focus: true focus: true
lockScreenState: root.lockScreenState
swipeProgress: flickable.openFactor swipeProgress: flickable.openFactor
onPasswordChanged: flickable.goToOpenPosition()
} }
} }
} }

View file

@ -15,6 +15,7 @@ import org.kde.plasma.private.mobileshell 1.0 as MobileShell
Loader { Loader {
id: root id: root
required property var lockScreenState
property var notificationsModel: [] property var notificationsModel: []
property bool notificationsShown: false property bool notificationsShown: false
@ -67,6 +68,7 @@ Loader {
NotificationsComponent { NotificationsComponent {
id: notificationComponent id: notificationComponent
lockScreenState: root.lockScreenState
notificationsModel: root.notificationsModel notificationsModel: root.notificationsModel
Layout.fillHeight: true Layout.fillHeight: true

View file

@ -0,0 +1,76 @@
/*
* 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();
}
}
}

View file

@ -15,6 +15,7 @@ import org.kde.plasma.private.mobileshell 1.0 as MobileShell
Loader { Loader {
id: root id: root
required property var lockScreenState
property var notificationsModel: [] property var notificationsModel: []
property bool notificationsShown: false property bool notificationsShown: false
@ -65,6 +66,7 @@ Loader {
NotificationsComponent { NotificationsComponent {
id: notificationComponent id: notificationComponent
lockScreenState: root.lockScreenState
notificationsModel: root.notificationsModel notificationsModel: root.notificationsModel
Layout.alignment: Qt.AlignVCenter | Qt.AlignHCenter Layout.alignment: Qt.AlignVCenter | Qt.AlignHCenter

View file

@ -14,8 +14,8 @@ import org.kde.notificationmanager 1.1 as Notifications
import org.kde.plasma.private.mobileshell 1.0 as MobileShell import org.kde.plasma.private.mobileshell 1.0 as MobileShell
Rectangle { Rectangle {
id: rect id: root
required property var lockScreenState
property var notificationsModel: [] property var notificationsModel: []
readonly property bool notificationsShown: notificationsList.hasNotifications readonly property bool notificationsShown: notificationsList.hasNotifications
@ -30,35 +30,37 @@ Rectangle {
color: "transparent" color: "transparent"
clip: true clip: true
PlasmaCore.ColorScope { Connections {
anchors.fill: parent target: lockScreenState
anchors.topMargin: rect.topMargin
anchors.bottomMargin: rect.bottomMargin
anchors.leftMargin: rect.leftMargin
anchors.rightMargin: rect.rightMargin
colorGroup: PlasmaCore.Theme.NormalColorGroup
Connections { function onUnlockSucceeded() {
target: authenticator // run pending action if successfully unlocked
function onSucceeded() { if (notificationsList.requestNotificationAction) {
// run pending action if successfully unlocked notificationsList.runPendingAction();
if (notificationsList.requestNotificationAction) {
notificationsList.runPendingAction();
notificationsList.requestNotificationAction = false;
}
}
function onFailed() {
notificationsList.requestNotificationAction = false; 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 { MobileShell.NotificationsWidget {
id: notificationsList id: notificationsList
anchors.fill: parent anchors.fill: parent
historyModelType: MobileShell.NotificationsModelType.WatchedNotificationsModel historyModelType: MobileShell.NotificationsModelType.WatchedNotificationsModel
actionsRequireUnlock: true actionsRequireUnlock: true
historyModel: rect.notificationsModel historyModel: root.notificationsModel
property bool requestNotificationAction: false property bool requestNotificationAction: false

View file

@ -1,5 +1,5 @@
/* /*
* 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 * SPDX-License-Identifier: GPL-2.0-or-later
*/ */
@ -7,67 +7,77 @@ import QtQuick 2.12
import QtQuick.Controls 2.1 import QtQuick.Controls 2.1
import QtQuick.Layouts 1.1 import QtQuick.Layouts 1.1
import QtGraphicalEffects 1.12 import QtGraphicalEffects 1.12
import org.kde.plasma.core 2.0 as PlasmaCore import org.kde.plasma.core 2.0 as PlasmaCore
import org.kde.plasma.workspace.keyboardlayout 1.0 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.plasma.workspace.keyboardlayout 1.0 as Keyboards
import org.kde.kirigami 2.12 as Kirigami
Rectangle { Rectangle {
id: root id: root
implicitHeight: PlasmaCore.Units.gridUnit * 2.5 implicitHeight: PlasmaCore.Units.gridUnit * 2.5
required property var lockScreenState
// toggle between pin and password mode // toggle between pin and password mode
property bool isPinMode: true property bool isPinMode: true
property string password
// for displaying temporary number in pin dot display // for displaying temporary number in pin dot display
property int previewCharIndex property int previewCharIndex: -2
property string pinLabel: qsTr("Enter PIN")
property string pinLabel
property bool keypadOpen property bool keypadOpen
// if waiting for result of auth readonly property color headerTextColor: Kirigami.ColorUtils.adjustColor(PlasmaCore.Theme.textColor, {"alpha": 0.75*255})
property bool waitingForAuth: false readonly property color headerTextInactiveColor: Kirigami.ColorUtils.adjustColor(PlasmaCore.Theme.textColor, {"alpha": 0.4*255})
property color headerTextColor: Kirigami.ColorUtils.adjustColor(PlasmaCore.Theme.textColor, {"alpha": 0.75*255}) // model for shown dots
property color headerTextInactiveColor: Kirigami.ColorUtils.adjustColor(PlasmaCore.Theme.textColor, {"alpha": 0.4*255}) // we need to use a listmodel to avoid all delegates from reloading
ListModel {
signal changePassword(); 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() { function backspace() {
if (!root.waitingForAuth) { if (!lockScreenState.waitingForAuth) {
root.previewCharIndex = -2; root.previewCharIndex = -2;
root.password = root.password.substr(0, root.password.length - 1); lockScreenState.password = lockScreenState.password.substr(0, lockScreenState.password.length - 1);
changePassword();
} }
} }
function clear() { function clear() {
if (!root.waitingForAuth) { if (!lockScreenState.waitingForAuth) {
root.previewCharIndex = -2; root.previewCharIndex = -2;
root.password = ""; lockScreenState.resetPassword();
changePassword();
} }
} }
function enter() { function enter() {
if (root.password !== "") { // prevent typing lock when password is empty lockScreenState.tryPassword();
root.waitingForAuth = true;
}
// don't try to unlock if there is a timeout (unlock once unlocked)
if (!authenticator.graceLocked) {
authenticator.tryUnlock(root.password);
}
if (keypadOpen && !isPinMode) { if (keypadOpen && !isPinMode) {
// make sure keyboard doesn't close // make sure keyboard doesn't close
openKeyboardTimer.restart(); openKeyboardTimer.restart();
@ -75,13 +85,14 @@ Rectangle {
} }
function keyPress(data) { function keyPress(data) {
if (!root.waitingForAuth) { if (!lockScreenState.waitingForAuth) {
if (root.pinLabel !== qsTr("Enter PIN")) { if (root.pinLabel !== qsTr("Enter PIN")) {
root.pinLabel = qsTr("Enter PIN"); root.pinLabel = qsTr("Enter PIN");
} }
root.previewCharIndex = root.password.length;
root.password += data root.previewCharIndex = lockScreenState.password.length;
changePassword(); lockScreenState.password += data
// trigger turning letter into dot later // trigger turning letter into dot later
letterTimer.restart(); 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 // hidden textfield so that the virtual keyboard shows up
TextField { TextField {
id: textField id: textField
@ -139,19 +137,22 @@ Rectangle {
property string prevText: "" property string prevText: ""
Connections { Connections {
target: root target: root.lockScreenState
function onChangePassword() {
if (textField.text != root.password) { function onPasswordChanged() {
if (textField.text != root.lockScreenState.password) {
textField.externalEdit = true; textField.externalEdit = true;
textField.text = root.password; textField.text = root.lockScreenState.password;
} }
} }
} }
onEditingFinished: { onEditingFinished: {
if (textField.focus) { if (textField.focus) {
root.enter(); root.enter();
} }
} }
onTextChanged: { onTextChanged: {
if (!externalEdit) { if (!externalEdit) {
if (prevText.length > text.length) { // backspace if (prevText.length > text.length) { // backspace
@ -194,7 +195,7 @@ Rectangle {
// label ("wrong pin", "enter pin") // label ("wrong pin", "enter pin")
Label { Label {
opacity: password.length === 0 ? 1 : 0 opacity: root.lockScreenState.password.length === 0 ? 1 : 0
anchors.centerIn: parent anchors.centerIn: parent
text: root.pinLabel text: root.pinLabel
font.pointSize: 12 font.pointSize: 12
@ -255,7 +256,7 @@ Rectangle {
scale: 0 scale: 0
anchors.fill: parent anchors.fill: parent
radius: width radius: width
color: root.waitingForAuth ? root.headerTextInactiveColor : root.headerTextColor // dim when waiting for auth color: lockScreenState.waitingForAuth ? root.headerTextInactiveColor : root.headerTextColor // dim when waiting for auth
PropertyAnimation { PropertyAnimation {
id: dotAnimation id: dotAnimation
@ -269,7 +270,7 @@ Rectangle {
id: charLabel id: charLabel
scale: 0 scale: 0
anchors.centerIn: parent anchors.centerIn: parent
color: root.waitingForAuth ? root.headerTextInactiveColor : root.headerTextColor // dim when waiting for auth color: lockScreenState.waitingForAuth ? root.headerTextInactiveColor : root.headerTextColor // dim when waiting for auth
text: model.char text: model.char
font.pointSize: 12 font.pointSize: 12

View file

@ -128,4 +128,5 @@ X-KDE-PluginInfo-License=GPLv2+
X-KDE-PluginInfo-Name=org.kde.plasma.phone X-KDE-PluginInfo-Name=org.kde.plasma.phone
X-KDE-PluginInfo-Version=0.1 X-KDE-PluginInfo-Version=0.1
X-KDE-PluginInfo-Website= X-KDE-PluginInfo-Website=
X-Plasma-APIVersion=2
X-Plasma-MainScript=defaults X-Plasma-MainScript=defaults