File indexing completed on 2024-06-02 05:44:06
0001 /* 0002 Copyright 2020 Devin Lin <espidev@gmail.com> 0003 0004 This library is free software; you can redistribute it and/or 0005 modify it under the terms of the GNU Lesser General Public 0006 License as published by the Free Software Foundation; either 0007 version 2.1 of the License, or (at your option) version 3, or any 0008 later version accepted by the membership of KDE e.V. (or its 0009 successor approved by the membership of KDE e.V.), which shall 0010 act as a proxy defined in Section 6 of version 3 of the license. 0011 0012 This library is distributed in the hope that it will be useful, 0013 but WITHOUT ANY WARRANTY; without even the implied warranty of 0014 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 0015 Lesser General Public License for more details. 0016 0017 You should have received a copy of the GNU Lesser General Public 0018 License along with this library. If not, see <http://www.gnu.org/licenses/>. 0019 */ 0020 0021 #include "fingerprintmodel.h" 0022 #include "fprintdevice.h" 0023 0024 FingerprintModel::FingerprintModel(QObject *parent) 0025 : QObject(parent) 0026 , m_managerDbusInterface(new NetReactivatedFprintManagerInterface(QStringLiteral("net.reactivated.Fprint"), 0027 QStringLiteral("/net/reactivated/Fprint/Manager"), 0028 QDBusConnection::systemBus(), 0029 this)) 0030 { 0031 auto reply = m_managerDbusInterface->GetDefaultDevice(); 0032 reply.waitForFinished(); 0033 0034 if (reply.isError()) { 0035 qDebug() << reply.error().message(); 0036 setCurrentError(reply.error().message()); 0037 return; 0038 } 0039 0040 QDBusObjectPath path = reply.value(); 0041 m_device = new FprintDevice(path, this); 0042 0043 connect(m_device, &FprintDevice::enrollCompleted, this, &FingerprintModel::handleEnrollCompleted); 0044 connect(m_device, &FprintDevice::enrollStagePassed, this, &FingerprintModel::handleEnrollStagePassed); 0045 connect(m_device, &FprintDevice::enrollRetryStage, this, &FingerprintModel::handleEnrollRetryStage); 0046 connect(m_device, &FprintDevice::enrollFailed, this, &FingerprintModel::handleEnrollFailed); 0047 } 0048 0049 FingerprintModel::~FingerprintModel() 0050 { 0051 if (m_device) { // just in case device is claimed 0052 m_device->stopEnrolling(); 0053 m_device->release(); 0054 } 0055 } 0056 0057 FprintDevice::ScanType FingerprintModel::scanType() 0058 { 0059 return !m_device ? FprintDevice::Press : m_device->scanType(); 0060 } 0061 0062 QString FingerprintModel::currentError() 0063 { 0064 return m_currentError; 0065 } 0066 0067 void FingerprintModel::setCurrentError(QString error) 0068 { 0069 if (m_currentError != error) { 0070 m_currentError = error; 0071 Q_EMIT currentErrorChanged(); 0072 } 0073 } 0074 0075 QString FingerprintModel::enrollFeedback() 0076 { 0077 return m_enrollFeedback; 0078 } 0079 0080 void FingerprintModel::setEnrollFeedback(QString feedback) 0081 { 0082 m_enrollFeedback = feedback; 0083 Q_EMIT enrollFeedbackChanged(); 0084 } 0085 0086 bool FingerprintModel::currentlyEnrolling() 0087 { 0088 return m_currentlyEnrolling; 0089 } 0090 0091 bool FingerprintModel::deviceFound() 0092 { 0093 return m_device != nullptr; 0094 } 0095 0096 double FingerprintModel::enrollProgress() 0097 { 0098 if (!deviceFound()) { 0099 return 0; 0100 } 0101 return (m_device->numOfEnrollStages() == 0) ? 1 : ((double)m_enrollStage) / m_device->numOfEnrollStages(); 0102 } 0103 0104 void FingerprintModel::setEnrollStage(int stage) 0105 { 0106 m_enrollStage = stage; 0107 Q_EMIT enrollProgressChanged(); 0108 } 0109 0110 FingerprintModel::DialogState FingerprintModel::dialogState() 0111 { 0112 return m_dialogState; 0113 } 0114 0115 void FingerprintModel::setDialogState(DialogState dialogState) 0116 { 0117 m_dialogState = dialogState; 0118 Q_EMIT dialogStateChanged(); 0119 } 0120 0121 void FingerprintModel::switchUser(QString username) 0122 { 0123 m_username = username; 0124 0125 if (deviceFound()) { 0126 stopEnrolling(); // stop enrolling if ongoing 0127 m_device->release(); // release from old user 0128 0129 Q_EMIT enrolledFingerprintsChanged(); 0130 } 0131 } 0132 0133 bool FingerprintModel::claimDevice() 0134 { 0135 if (!deviceFound()) { 0136 return false; 0137 } 0138 0139 QDBusError error = m_device->claim(m_username); 0140 if (error.isValid() && error.name() != "net.reactivated.Fprint.Error.AlreadyInUse") { 0141 qDebug() << "error claiming:" << error.message(); 0142 setCurrentError(error.message()); 0143 return false; 0144 } 0145 return true; 0146 } 0147 0148 void FingerprintModel::startEnrolling(QString finger) 0149 { 0150 if (!deviceFound()) { 0151 setCurrentError(i18n("No fingerprint device found.")); 0152 setDialogState(DialogState::FingerprintList); 0153 return; 0154 } 0155 0156 setEnrollStage(0); 0157 setEnrollFeedback({}); 0158 0159 // claim device for user 0160 if (!claimDevice()) { 0161 setDialogState(DialogState::FingerprintList); 0162 return; 0163 } 0164 0165 QDBusError error = m_device->startEnrolling(finger); 0166 if (error.isValid()) { 0167 qDebug() << "error start enrolling:" << error.message(); 0168 setCurrentError(error.message()); 0169 m_device->release(); 0170 setDialogState(DialogState::FingerprintList); 0171 return; 0172 } 0173 0174 m_currentlyEnrolling = true; 0175 Q_EMIT currentlyEnrollingChanged(); 0176 0177 setDialogState(DialogState::Enrolling); 0178 } 0179 0180 void FingerprintModel::stopEnrolling() 0181 { 0182 setDialogState(DialogState::FingerprintList); 0183 if (m_currentlyEnrolling) { 0184 m_currentlyEnrolling = false; 0185 Q_EMIT currentlyEnrollingChanged(); 0186 0187 QDBusError error = m_device->stopEnrolling(); 0188 if (error.isValid()) { 0189 qDebug() << "error stop enrolling:" << error.message(); 0190 setCurrentError(error.message()); 0191 return; 0192 } 0193 m_device->release(); 0194 } 0195 } 0196 0197 void FingerprintModel::deleteFingerprint(QString finger) 0198 { 0199 // claim for user 0200 if (!claimDevice()) { 0201 return; 0202 } 0203 0204 QDBusError error = m_device->deleteEnrolledFinger(finger); 0205 if (error.isValid()) { 0206 qDebug() << "error deleting fingerprint:" << error.message(); 0207 setCurrentError(error.message()); 0208 } 0209 0210 // release from user 0211 error = m_device->release(); 0212 if (error.isValid()) { 0213 qDebug() << "error releasing:" << error.message(); 0214 setCurrentError(error.message()); 0215 } 0216 0217 Q_EMIT enrolledFingerprintsChanged(); 0218 } 0219 0220 void FingerprintModel::clearFingerprints() 0221 { 0222 // claim for user 0223 if (!claimDevice()) { 0224 return; 0225 } 0226 0227 QDBusError error = m_device->deleteEnrolledFingers(); 0228 if (error.isValid()) { 0229 qDebug() << "error clearing fingerprints:" << error.message(); 0230 setCurrentError(error.message()); 0231 } 0232 0233 // release from user 0234 error = m_device->release(); 0235 if (error.isValid()) { 0236 qDebug() << "error releasing:" << error.message(); 0237 setCurrentError(error.message()); 0238 } 0239 0240 Q_EMIT enrolledFingerprintsChanged(); 0241 } 0242 0243 QStringList FingerprintModel::enrolledFingerprintsRaw() 0244 { 0245 if (deviceFound()) { 0246 QDBusPendingReply<QStringList> reply = m_device->listEnrolledFingers(m_username); 0247 reply.waitForFinished(); 0248 if (reply.isError()) { 0249 // ignore net.reactivated.Fprint.Error.NoEnrolledPrints, as it shows up when there are no fingerprints 0250 if (reply.error().name() != "net.reactivated.Fprint.Error.NoEnrolledPrints") { 0251 qDebug() << "error listing enrolled fingers:" << reply.error().message(); 0252 setCurrentError(reply.error().message()); 0253 } 0254 return QStringList(); 0255 } 0256 return reply.value(); 0257 } else { 0258 setCurrentError(i18n("No fingerprint device found.")); 0259 setDialogState(DialogState::FingerprintList); 0260 return QStringList(); 0261 } 0262 } 0263 0264 QVariantList FingerprintModel::enrolledFingerprints() 0265 { 0266 // convert fingers list to qlist of Finger objects 0267 QVariantList fingers; 0268 for (QString &finger : enrolledFingerprintsRaw()) { 0269 for (Finger *storedFinger : FINGERS) { 0270 if (storedFinger->internalName() == finger) { 0271 fingers.append(QVariant::fromValue(storedFinger)); 0272 break; 0273 } 0274 } 0275 } 0276 return fingers; 0277 } 0278 0279 QVariantList FingerprintModel::availableFingersToEnroll() 0280 { 0281 QVariantList list; 0282 QStringList enrolled = enrolledFingerprintsRaw(); 0283 0284 // add fingerprints to list that are not in the enrolled list 0285 for (Finger *finger : FINGERS) { 0286 if (!enrolledFingerprintsRaw().contains(finger->internalName())) { 0287 list.append(QVariant::fromValue(finger)); 0288 } 0289 } 0290 return list; 0291 } 0292 0293 QVariantList FingerprintModel::unavailableFingersToEnroll() 0294 { 0295 QVariantList list; 0296 QStringList enrolled = enrolledFingerprintsRaw(); 0297 0298 // add fingerprints to list that are in the enrolled list 0299 for (Finger *finger : FINGERS) { 0300 if (enrolledFingerprintsRaw().contains(finger->internalName())) { 0301 list.append(QVariant::fromValue(finger)); 0302 } 0303 } 0304 return list; 0305 } 0306 0307 void FingerprintModel::handleEnrollCompleted() 0308 { 0309 setEnrollStage(m_device->numOfEnrollStages()); 0310 setEnrollFeedback({}); 0311 Q_EMIT enrolledFingerprintsChanged(); 0312 Q_EMIT scanComplete(); 0313 0314 // stopEnrolling not called, as it is triggered only when the "complete" button is pressed 0315 // (only change dialog state change after button is pressed) 0316 setDialogState(DialogState::EnrollComplete); 0317 } 0318 0319 void FingerprintModel::handleEnrollStagePassed() 0320 { 0321 setEnrollStage(m_enrollStage + 1); 0322 setEnrollFeedback({}); 0323 Q_EMIT scanSuccess(); 0324 qDebug() << "fingerprint enroll stage pass:" << enrollProgress(); 0325 } 0326 0327 void FingerprintModel::handleEnrollRetryStage(QString feedback) 0328 { 0329 Q_EMIT scanFailure(); 0330 if (feedback == "enroll-retry-scan") { 0331 setEnrollFeedback(i18n("Retry scanning your finger.")); 0332 } else if (feedback == "enroll-swipe-too-short") { 0333 setEnrollFeedback(i18n("Swipe too short. Try again.")); 0334 } else if (feedback == "enroll-finger-not-centered") { 0335 setEnrollFeedback(i18n("Finger not centered on the reader. Try again.")); 0336 } else if (feedback == "enroll-remove-and-retry") { 0337 setEnrollFeedback(i18n("Remove your finger from the reader, and try again.")); 0338 } 0339 qDebug() << "fingerprint enroll stage fail:" << feedback; 0340 } 0341 0342 void FingerprintModel::handleEnrollFailed(QString error) 0343 { 0344 if (error == "enroll-failed") { 0345 setCurrentError(i18n("Fingerprint enrollment has failed.")); 0346 stopEnrolling(); 0347 } else if (error == "enroll-data-full") { 0348 setCurrentError(i18n("There is no space left for this device, delete other fingerprints to continue.")); 0349 stopEnrolling(); 0350 } else if (error == "enroll-disconnected") { 0351 setCurrentError(i18n("The device was disconnected.")); 0352 m_currentlyEnrolling = false; 0353 Q_EMIT currentlyEnrollingChanged(); 0354 setDialogState(DialogState::FingerprintList); 0355 } else if (error == "enroll-unknown-error") { 0356 setCurrentError(i18n("An unknown error has occurred.")); 0357 stopEnrolling(); 0358 } 0359 }