You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
346 lines
10 KiB
346 lines
10 KiB
/* |
|
Copyright 2020 Devin Lin <espidev@gmail.com> |
|
|
|
This library is free software; you can redistribute it and/or |
|
modify it under the terms of the GNU Lesser General Public |
|
License as published by the Free Software Foundation; either |
|
version 2.1 of the License, or (at your option) version 3, or any |
|
later version accepted by the membership of KDE e.V. (or its |
|
successor approved by the membership of KDE e.V.), which shall |
|
act as a proxy defined in Section 6 of version 3 of the license. |
|
|
|
This library is distributed in the hope that it will be useful, |
|
but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
|
Lesser General Public License for more details. |
|
|
|
You should have received a copy of the GNU Lesser General Public |
|
License along with this library. If not, see <http://www.gnu.org/licenses/>. |
|
*/ |
|
|
|
#include <KLocalizedString> |
|
|
|
#include "fingerprintmodel.h" |
|
|
|
#include "fprint_device_interface.h" |
|
#include "fprint_manager_interface.h" |
|
|
|
FingerprintModel::FingerprintModel(QObject* parent) |
|
: QObject(parent) |
|
, m_managerDbusInterface(new NetReactivatedFprintManagerInterface(QStringLiteral("net.reactivated.Fprint"), QStringLiteral("/net/reactivated/Fprint/Manager"), QDBusConnection::systemBus(), this)) |
|
{ |
|
auto reply = m_managerDbusInterface->GetDefaultDevice(); |
|
reply.waitForFinished(); |
|
|
|
if (reply.isError()) { |
|
qDebug() << reply.error().message(); |
|
setCurrentError(reply.error().message()); |
|
return; |
|
} |
|
|
|
QDBusObjectPath path = reply.value(); |
|
m_device = new FprintDevice(path, this); |
|
|
|
connect(m_device, &FprintDevice::enrollCompleted, this, &FingerprintModel::handleEnrollCompleted); |
|
connect(m_device, &FprintDevice::enrollStagePassed, this, &FingerprintModel::handleEnrollStagePassed); |
|
connect(m_device, &FprintDevice::enrollRetryStage, this, &FingerprintModel::handleEnrollRetryStage); |
|
connect(m_device, &FprintDevice::enrollFailed, this, &FingerprintModel::handleEnrollFailed); |
|
} |
|
|
|
FingerprintModel::~FingerprintModel() |
|
{ |
|
if (m_device) { // just in case device is claimed |
|
m_device->stopEnrolling(); |
|
m_device->release(); |
|
} |
|
} |
|
|
|
QString FingerprintModel::scanType() |
|
{ |
|
return !m_device ? "" : m_device->scanType(); |
|
} |
|
|
|
QString FingerprintModel::currentError() |
|
{ |
|
return m_currentError; |
|
} |
|
|
|
void FingerprintModel::setCurrentError(QString error) |
|
{ |
|
if (m_currentError != error) { |
|
m_currentError = error; |
|
Q_EMIT currentErrorChanged(); |
|
} |
|
} |
|
|
|
QString FingerprintModel::enrollFeedback() |
|
{ |
|
return m_enrollFeedback; |
|
} |
|
|
|
void FingerprintModel::setEnrollFeedback(QString feedback) |
|
{ |
|
m_enrollFeedback = feedback; |
|
Q_EMIT enrollFeedbackChanged(); |
|
} |
|
|
|
bool FingerprintModel::currentlyEnrolling() |
|
{ |
|
return m_currentlyEnrolling; |
|
} |
|
|
|
bool FingerprintModel::deviceFound() |
|
{ |
|
return m_device != nullptr; |
|
} |
|
|
|
double FingerprintModel::enrollProgress() |
|
{ |
|
if (!deviceFound()) { |
|
return 0; |
|
} |
|
return (m_device->numOfEnrollStages() == 0) ? 1 : ((double) m_enrollStage) / m_device->numOfEnrollStages(); |
|
} |
|
|
|
void FingerprintModel::setEnrollStage(int stage) |
|
{ |
|
m_enrollStage = stage; |
|
Q_EMIT enrollProgressChanged(); |
|
} |
|
|
|
FingerprintModel::DialogState FingerprintModel::dialogState() |
|
{ |
|
return m_dialogState; |
|
} |
|
|
|
void FingerprintModel::setDialogState(DialogState dialogState) |
|
{ |
|
m_dialogState = dialogState; |
|
Q_EMIT dialogStateChanged(); |
|
} |
|
|
|
void FingerprintModel::switchUser(QString username) |
|
{ |
|
m_username = username; |
|
|
|
if (deviceFound()) { |
|
stopEnrolling(); // stop enrolling if ongoing |
|
m_device->release(); // release from old user |
|
|
|
Q_EMIT enrolledFingerprintsChanged(); |
|
} |
|
} |
|
|
|
bool FingerprintModel::claimDevice() |
|
{ |
|
if (!deviceFound()) { |
|
return false; |
|
} |
|
|
|
QDBusError error = m_device->claim(m_username); |
|
if (error.isValid() && error.name() != "net.reactivated.Fprint.Error.AlreadyInUse") { |
|
qDebug() << "error claiming:" << error.message(); |
|
setCurrentError(error.message()); |
|
return false; |
|
} |
|
return true; |
|
} |
|
|
|
void FingerprintModel::startEnrolling(QString finger) |
|
{ |
|
if (!deviceFound()) { |
|
setCurrentError(i18n("No fingerprint device found.")); |
|
setDialogState(DialogState::FingerprintList); |
|
return; |
|
} |
|
|
|
setEnrollStage(0); |
|
setEnrollFeedback({}); |
|
|
|
// claim device for user |
|
if (!claimDevice()) { |
|
setDialogState(DialogState::FingerprintList); |
|
return; |
|
} |
|
|
|
QDBusError error = m_device->startEnrolling(finger); |
|
if (error.isValid()) { |
|
qDebug() << "error start enrolling:" << error.message(); |
|
setCurrentError(error.message()); |
|
m_device->release(); |
|
setDialogState(DialogState::FingerprintList); |
|
return; |
|
} |
|
|
|
m_currentlyEnrolling = true; |
|
Q_EMIT currentlyEnrollingChanged(); |
|
|
|
setDialogState(DialogState::Enrolling); |
|
} |
|
|
|
void FingerprintModel::stopEnrolling() |
|
{ |
|
setDialogState(DialogState::FingerprintList); |
|
if (m_currentlyEnrolling) { |
|
m_currentlyEnrolling = false; |
|
Q_EMIT currentlyEnrollingChanged(); |
|
|
|
QDBusError error = m_device->stopEnrolling(); |
|
if (error.isValid()) { |
|
qDebug() << "error stop enrolling:" << error.message(); |
|
setCurrentError(error.message()); |
|
return; |
|
} |
|
m_device->release(); |
|
} |
|
} |
|
|
|
void FingerprintModel::deleteFingerprint(QString finger) |
|
{ |
|
// claim for user |
|
if (!claimDevice()) { |
|
return; |
|
} |
|
|
|
QDBusError error = m_device->deleteEnrolledFinger(finger); |
|
if (error.isValid()) { |
|
qDebug() << "error deleting fingerprint:" << error.message(); |
|
setCurrentError(error.message()); |
|
} |
|
|
|
// release from user |
|
error = m_device->release(); |
|
if (error.isValid()) { |
|
qDebug() << "error releasing:" << error.message(); |
|
setCurrentError(error.message()); |
|
} |
|
|
|
Q_EMIT enrolledFingerprintsChanged(); |
|
} |
|
|
|
void FingerprintModel::clearFingerprints() |
|
{ |
|
// claim for user |
|
if (!claimDevice()) { |
|
return; |
|
} |
|
|
|
QDBusError error = m_device->deleteEnrolledFingers(); |
|
if (error.isValid()) { |
|
qDebug() << "error clearing fingerprints:" << error.message(); |
|
setCurrentError(error.message()); |
|
} |
|
|
|
// release from user |
|
error = m_device->release(); |
|
if (error.isValid()) { |
|
qDebug() << "error releasing:" << error.message(); |
|
setCurrentError(error.message()); |
|
} |
|
|
|
Q_EMIT enrolledFingerprintsChanged(); |
|
} |
|
|
|
QStringList FingerprintModel::enrolledFingerprintsRaw() |
|
{ |
|
if (deviceFound()) { |
|
QDBusPendingReply<QStringList> reply = m_device->listEnrolledFingers(m_username); |
|
reply.waitForFinished(); |
|
if (reply.isError()) { |
|
// ignore net.reactivated.Fprint.Error.NoEnrolledPrints, as it shows up when there are no fingerprints |
|
if (reply.error().name() != "net.reactivated.Fprint.Error.NoEnrolledPrints") { |
|
qDebug() << "error listing enrolled fingers:" << reply.error().message(); |
|
setCurrentError(reply.error().message()); |
|
} |
|
return QStringList(); |
|
} |
|
return reply.value(); |
|
} else { |
|
setCurrentError(i18n("No fingerprint device found.")); |
|
setDialogState(DialogState::FingerprintList); |
|
return QStringList(); |
|
} |
|
} |
|
|
|
QVariantList FingerprintModel::enrolledFingerprints() |
|
{ |
|
// convert fingers list to qlist of Finger objects |
|
QVariantList fingers; |
|
for (QString &finger : enrolledFingerprintsRaw()) { |
|
for (Finger *storedFinger : FINGERS) { |
|
if (storedFinger->internalName() == finger) { |
|
fingers.append(QVariant::fromValue(storedFinger)); |
|
break; |
|
} |
|
} |
|
} |
|
return fingers; |
|
} |
|
|
|
QVariantList FingerprintModel::availableFingersToEnroll() |
|
{ |
|
QVariantList list; |
|
QStringList enrolled = enrolledFingerprintsRaw(); |
|
|
|
// add fingerprints to list that are not in the enrolled list |
|
for (Finger *finger : FINGERS) { |
|
if (!enrolledFingerprintsRaw().contains(finger->internalName())) { |
|
list.append(QVariant::fromValue(finger)); |
|
} |
|
} |
|
return list; |
|
} |
|
|
|
void FingerprintModel::handleEnrollCompleted() |
|
{ |
|
setEnrollStage(m_device->numOfEnrollStages()); |
|
setEnrollFeedback({}); |
|
Q_EMIT enrolledFingerprintsChanged(); |
|
Q_EMIT scanComplete(); |
|
|
|
// stopEnrolling not called, as it is triggered only when the "complete" button is pressed |
|
// (only change dialog state change after button is pressed) |
|
setDialogState(DialogState::EnrollComplete); |
|
} |
|
|
|
void FingerprintModel::handleEnrollStagePassed() |
|
{ |
|
setEnrollStage(m_enrollStage + 1); |
|
setEnrollFeedback({}); |
|
Q_EMIT scanSuccess(); |
|
qDebug() << "fingerprint enroll stage pass:" << enrollProgress(); |
|
} |
|
|
|
void FingerprintModel::handleEnrollRetryStage(QString feedback) |
|
{ |
|
Q_EMIT scanFailure(); |
|
if (feedback == "enroll-retry-scan") { |
|
setEnrollFeedback(i18n("Retry scanning your finger.")); |
|
} else if (feedback == "enroll-swipe-too-short") { |
|
setEnrollFeedback(i18n("Swipe too short. Try again.")); |
|
} else if (feedback == "enroll-finger-not-centered") { |
|
setEnrollFeedback(i18n("Finger not centered on the reader. Try again.")); |
|
} else if (feedback == "enroll-remove-and-retry") { |
|
setEnrollFeedback(i18n("Remove your finger from the reader, and try again.")); |
|
} |
|
qDebug() << "fingerprint enroll stage fail:" << feedback; |
|
} |
|
|
|
void FingerprintModel::handleEnrollFailed(QString error) |
|
{ |
|
if (error == "enroll-failed") { |
|
setCurrentError(i18n("Fingerprint enrollment has failed.")); |
|
stopEnrolling(); |
|
} else if (error == "enroll-data-full") { |
|
setCurrentError(i18n("There is no space left for this device, delete other fingerprints to continue.")); |
|
stopEnrolling(); |
|
} else if (error == "enroll-disconnected") { |
|
setCurrentError(i18n("The device was disconnected.")); |
|
m_currentlyEnrolling = false; |
|
Q_EMIT currentlyEnrollingChanged(); |
|
setDialogState(DialogState::FingerprintList); |
|
} else if (error == "enroll-unknown-error") { |
|
setCurrentError(i18n("An unknown error has occurred.")); |
|
stopEnrolling(); |
|
} |
|
}
|
|
|