Publish Framework tray enhancements and idle fingerprint retry
This commit is contained in:
@@ -0,0 +1,67 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
import QtQml
|
||||
|
||||
Timer {
|
||||
id: retry
|
||||
|
||||
required property QtObject auth
|
||||
required property int fingerprintType
|
||||
property bool enabled: true
|
||||
readonly property bool fingerprintActive: (auth.authenticatorTypes & fingerprintType) !== 0
|
||||
property bool sawInfo: false
|
||||
property bool sawError: false
|
||||
|
||||
interval: 6000
|
||||
|
||||
function reset() {
|
||||
stop();
|
||||
sawInfo = false;
|
||||
sawError = false;
|
||||
}
|
||||
|
||||
onEnabledChanged: {
|
||||
if (!enabled) {
|
||||
reset();
|
||||
}
|
||||
}
|
||||
onFingerprintActiveChanged: {
|
||||
if (fingerprintActive) {
|
||||
reset();
|
||||
} else if (enabled && sawInfo && !sawError) {
|
||||
// An idle conversation ended. Consume the retry until another
|
||||
// conversation actually supplies fingerprint instructions.
|
||||
sawInfo = false;
|
||||
restart();
|
||||
}
|
||||
}
|
||||
onTriggered: {
|
||||
if (enabled && !fingerprintActive) {
|
||||
auth.startAuthenticating();
|
||||
}
|
||||
}
|
||||
|
||||
property Connections authenticationSignals: Connections {
|
||||
target: retry.auth
|
||||
function onNoninteractiveInfo(kind, source) {
|
||||
if (retry.enabled && (kind & retry.fingerprintType)) {
|
||||
retry.sawInfo = true;
|
||||
}
|
||||
}
|
||||
function onNoninteractiveError(kind, source) {
|
||||
if (kind & retry.fingerprintType) {
|
||||
// Do not automatically replenish failed scan attempts.
|
||||
retry.sawError = true;
|
||||
retry.stop();
|
||||
}
|
||||
}
|
||||
function onFailed(kind, source) {
|
||||
if (kind === 0) {
|
||||
retry.reset();
|
||||
}
|
||||
}
|
||||
function onSucceeded() {
|
||||
retry.reset();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -57,7 +57,7 @@
|
||||
onUiVisibleChanged: {
|
||||
if (uiVisible) {
|
||||
Window.window.requestActivate();
|
||||
@@ -135,6 +143,18 @@
|
||||
@@ -135,6 +143,24 @@
|
||||
}
|
||||
authenticator.startAuthenticating();
|
||||
}
|
||||
@@ -72,11 +72,17 @@
|
||||
+ interval: 6000
|
||||
+ running: root.viewVisible
|
||||
+ onTriggered: authenticator.startAuthenticating()
|
||||
+ }
|
||||
+ FedoraToolsFingerprintRetry {
|
||||
+ auth: authenticator
|
||||
+ fingerprintType: ScreenLocker.Authenticator.Fingerprint
|
||||
+ enabled: root.viewVisible && !authenticator.unlocked
|
||||
+ && !graceLockTimer.running && !resumeAuthenticationTimer.running
|
||||
+ }
|
||||
onBlockUIChanged: {
|
||||
if (blockUI) {
|
||||
fadeoutTimer.running = false;
|
||||
@@ -194,6 +214,6 @@
|
||||
@@ -194,6 +220,6 @@
|
||||
WallpaperFader {
|
||||
anchors.fill: parent
|
||||
- state: lockScreenRoot.uiVisible ? "on" : "off"
|
||||
@@ -84,7 +90,7 @@
|
||||
source: wallpaper
|
||||
mainStack: mainStack
|
||||
footer: footer
|
||||
@@ -211,7 +231,7 @@
|
||||
@@ -211,7 +237,7 @@
|
||||
samples: 15
|
||||
spread: 0.2
|
||||
color : Qt.rgba(0, 0, 0, 0.7)
|
||||
@@ -93,6 +99,6 @@
|
||||
Behavior on opacity {
|
||||
OpacityAnimator {
|
||||
duration: Kirigami.Units.veryLongDuration * 2
|
||||
@@ -265 +285 @@
|
||||
@@ -265 +291 @@
|
||||
- lockScreenUiVisible: lockScreenRoot.uiVisible
|
||||
+ lockScreenUiVisible: true
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
Name: plasma-always-show-unlock
|
||||
Version: 0.1.0
|
||||
Release: 10%{?dist}
|
||||
Release: 11%{?dist}
|
||||
Summary: Immediately show the Plasma unlock prompt without requiring input
|
||||
|
||||
License: MIT AND GPL-2.0-or-later
|
||||
@@ -11,10 +11,13 @@ Source2: test-plasma-always-show-unlock
|
||||
Source3: LockScreenUi.qml
|
||||
Source4: LICENSE
|
||||
Source5: README.md
|
||||
Source6: FedoraToolsFingerprintRetry.qml
|
||||
Source7: tst_fingerprintretry.qml
|
||||
|
||||
BuildArch: noarch
|
||||
BuildRequires: bash
|
||||
BuildRequires: patch
|
||||
BuildRequires: qt6-qtdeclarative-devel
|
||||
Requires: bash
|
||||
Requires: patch
|
||||
Requires: plasma-desktop >= 6.7.4
|
||||
@@ -31,6 +34,11 @@ patch after plasma-desktop updates and leaves unknown future layouts unchanged.
|
||||
|
||||
%check
|
||||
bash %{SOURCE2} %{SOURCE0} %{SOURCE1} %{SOURCE3}
|
||||
mkdir -p qml-tests/tests
|
||||
cp %{SOURCE6} qml-tests/
|
||||
cp %{SOURCE7} qml-tests/tests/
|
||||
QT_QPA_PLATFORM=offscreen QT_QUICK_BACKEND=software \
|
||||
qmltestrunner-qt6 -input qml-tests/tests
|
||||
|
||||
%install
|
||||
install -Dpm 0755 %{SOURCE0} \
|
||||
@@ -41,6 +49,15 @@ install -Dpm 0644 %{SOURCE4} \
|
||||
%{buildroot}%{_licensedir}/%{name}/LICENSE
|
||||
install -Dpm 0644 %{SOURCE5} \
|
||||
%{buildroot}%{_docdir}/%{name}/README.md
|
||||
install -Dpm 0644 %{SOURCE6} \
|
||||
%{buildroot}%{_datadir}/plasma/shells/org.kde.plasma.desktop/contents/lockscreen/FedoraToolsFingerprintRetry.qml
|
||||
|
||||
%pre
|
||||
# Revert using the installed helper and patch, before RPM replaces either.
|
||||
# The new patch must be applied to the original QML, not an older patched copy.
|
||||
if [ -x %{_libexecdir}/plasma-always-show-unlock ]; then
|
||||
%{_libexecdir}/plasma-always-show-unlock revert || exit 1
|
||||
fi
|
||||
|
||||
%preun
|
||||
if [ "$1" -eq 0 ]; then
|
||||
@@ -55,8 +72,14 @@ fi
|
||||
%doc %{_docdir}/%{name}/README.md
|
||||
%{_libexecdir}/plasma-always-show-unlock
|
||||
%{_datadir}/plasma-always-show-unlock/LockScreenUi.patch
|
||||
%{_datadir}/plasma/shells/org.kde.plasma.desktop/contents/lockscreen/FedoraToolsFingerprintRetry.qml
|
||||
|
||||
%changelog
|
||||
* Mon Sep 14 2026 fedora-tools contributors - 0.1.0-11
|
||||
- Retry idle fingerprint conversations without polling or restarting fprintd
|
||||
- Preserve failed-scan handling and test timeout recovery in QML
|
||||
- Revert the installed patch before upgrading to a new revision
|
||||
|
||||
* Sat Sep 05 2026 fedora-tools contributors - 0.1.0-10
|
||||
- Clarify the package summary
|
||||
|
||||
|
||||
@@ -37,6 +37,8 @@ run_tool status >/dev/null
|
||||
grep -q 'function onViewVisibleChanged()' "$target"
|
||||
grep -q 'id: resumeAuthenticationTimer' "$target"
|
||||
grep -q 'id: postGraceAuthenticationTimer' "$target"
|
||||
grep -q 'FedoraToolsFingerprintRetry {' "$target"
|
||||
grep -q 'fingerprintType: ScreenLocker.Authenticator.Fingerprint' "$target"
|
||||
grep -q 'interval: 1500' "$target"
|
||||
grep -q 'interval: 6000' "$target"
|
||||
grep -q 'running: root.viewVisible' "$target"
|
||||
|
||||
@@ -0,0 +1,131 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
import QtQml
|
||||
import QtTest
|
||||
import ".."
|
||||
|
||||
TestCase {
|
||||
id: testCase
|
||||
name: "FingerprintRetry"
|
||||
property alias authenticator: auth
|
||||
|
||||
QtObject {
|
||||
id: auth
|
||||
property int authenticatorTypes: 0
|
||||
property int starts: 0
|
||||
signal noninteractiveInfo(int kind, QtObject source)
|
||||
signal noninteractiveError(int kind, QtObject source)
|
||||
signal failed(int kind, QtObject source)
|
||||
signal succeeded()
|
||||
function startAuthenticating() { ++starts; }
|
||||
}
|
||||
|
||||
FedoraToolsFingerprintRetry {
|
||||
id: retry
|
||||
auth: testCase.authenticator
|
||||
fingerprintType: 1
|
||||
}
|
||||
|
||||
function initTestCase() {
|
||||
compare(retry.interval, 6000);
|
||||
compare(retry.repeat, false);
|
||||
}
|
||||
|
||||
function init() {
|
||||
retry.enabled = false;
|
||||
auth.authenticatorTypes = 0;
|
||||
auth.starts = 0;
|
||||
retry.interval = 20;
|
||||
retry.enabled = true;
|
||||
}
|
||||
|
||||
function beginFingerprint() {
|
||||
auth.authenticatorTypes = 1;
|
||||
auth.noninteractiveInfo(1, auth);
|
||||
}
|
||||
|
||||
function test_idleTimeoutRetriesWithoutInput() {
|
||||
beginFingerprint();
|
||||
wait(50);
|
||||
compare(auth.starts, 0); // Never restart a working conversation.
|
||||
auth.authenticatorTypes = 0;
|
||||
verify(retry.running);
|
||||
tryCompare(auth, "starts", 1);
|
||||
wait(60);
|
||||
compare(auth.starts, 1); // No polling while the reader stays unavailable.
|
||||
|
||||
beginFingerprint();
|
||||
auth.authenticatorTypes = 0;
|
||||
tryCompare(auth, "starts", 2); // Later idle timeouts can also recover.
|
||||
}
|
||||
|
||||
function test_noReaderOrNoInstructions() {
|
||||
wait(60);
|
||||
compare(auth.starts, 0);
|
||||
auth.authenticatorTypes = 1;
|
||||
auth.authenticatorTypes = 0;
|
||||
wait(60);
|
||||
compare(auth.starts, 0);
|
||||
}
|
||||
|
||||
function test_failedScansDoNotGetAutomaticNewAttempts() {
|
||||
beginFingerprint();
|
||||
auth.noninteractiveError(1, auth);
|
||||
auth.noninteractiveInfo(1, auth); // Another instruction or timeout message.
|
||||
auth.failed(1, auth);
|
||||
auth.authenticatorTypes = 0;
|
||||
wait(60);
|
||||
compare(auth.starts, 0);
|
||||
}
|
||||
|
||||
function test_unavailableRetryDoesNotLoop() {
|
||||
beginFingerprint();
|
||||
auth.authenticatorTypes = 0;
|
||||
tryCompare(auth, "starts", 1);
|
||||
auth.authenticatorTypes = 1; // PAM marks active before opening the reader.
|
||||
auth.failed(1, auth);
|
||||
auth.authenticatorTypes = 0;
|
||||
wait(60);
|
||||
compare(auth.starts, 1);
|
||||
}
|
||||
|
||||
function test_smartcardDoesNotArmOrCancelFingerprintRetry() {
|
||||
auth.authenticatorTypes = 2;
|
||||
auth.noninteractiveInfo(2, auth);
|
||||
auth.authenticatorTypes = 0;
|
||||
wait(60);
|
||||
compare(auth.starts, 0);
|
||||
|
||||
beginFingerprint();
|
||||
auth.noninteractiveError(2, auth);
|
||||
auth.authenticatorTypes = 2;
|
||||
tryCompare(auth, "starts", 1);
|
||||
}
|
||||
|
||||
function test_cancelPendingRetry_data() {
|
||||
return [ {tag: "manual restart"}, {tag: "success"},
|
||||
{tag: "password failure"}, {tag: "hidden or suspended"} ];
|
||||
}
|
||||
|
||||
function test_cancelPendingRetry(data) {
|
||||
beginFingerprint();
|
||||
auth.authenticatorTypes = 0;
|
||||
verify(retry.running);
|
||||
switch (data.tag) {
|
||||
case "manual restart": auth.authenticatorTypes = 1; break;
|
||||
case "success": auth.succeeded(); break;
|
||||
case "password failure": auth.failed(0, auth); break;
|
||||
case "hidden or suspended": retry.enabled = false; break;
|
||||
}
|
||||
wait(60);
|
||||
compare(auth.starts, 0);
|
||||
}
|
||||
|
||||
function test_passwordFailureBeforeAvailabilityChange() {
|
||||
beginFingerprint();
|
||||
auth.failed(0, auth);
|
||||
auth.authenticatorTypes = 0;
|
||||
wait(60);
|
||||
compare(auth.starts, 0);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user