mirror of
https://github.com/Kunzisoft/KeePassDX.git
synced 2025-12-04 15:49:33 +01:00
@@ -30,6 +30,7 @@ import android.support.annotation.RequiresApi;
|
||||
import android.support.v4.hardware.fingerprint.FingerprintManagerCompat;
|
||||
import android.support.v4.os.CancellationSignal;
|
||||
import android.util.Base64;
|
||||
import android.util.Log;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.security.KeyStore;
|
||||
@@ -47,7 +48,9 @@ import javax.crypto.spec.IvParameterSpec;
|
||||
@RequiresApi(api = Build.VERSION_CODES.M)
|
||||
public class FingerPrintHelper {
|
||||
|
||||
private static final String FINGERPRINT_KEYSTORE_KEY = "example-key";
|
||||
private static final String TAG = FingerPrintHelper.class.getName();
|
||||
|
||||
private static final String FINGERPRINT_KEYSTORE_KEY = "com.kunzisoft.keepass.fingerprint.key";
|
||||
|
||||
private FingerprintManagerCompat fingerprintManager;
|
||||
private KeyStore keyStore = null;
|
||||
@@ -65,8 +68,7 @@ public class FingerPrintHelper {
|
||||
this.authenticationCallback = authenticationCallback;
|
||||
}
|
||||
|
||||
public void startListening() {
|
||||
|
||||
public synchronized void startListening() {
|
||||
// starts listening for fingerprints with the initialised crypto object
|
||||
cancellationSignal = new CancellationSignal();
|
||||
fingerprintManager.authenticate(
|
||||
@@ -77,7 +79,7 @@ public class FingerPrintHelper {
|
||||
null);
|
||||
}
|
||||
|
||||
public void stopListening() {
|
||||
public synchronized void stopListening() {
|
||||
if (!isFingerprintInitialized(false)) {
|
||||
return;
|
||||
}
|
||||
@@ -113,6 +115,7 @@ public class FingerPrintHelper {
|
||||
this.cryptoObject = new FingerprintManagerCompat.CryptoObject(cipher);
|
||||
setInitOk(true);
|
||||
} catch (final Exception e) {
|
||||
Log.e(TAG, "Unable to initialize the keystore", e);
|
||||
setInitOk(false);
|
||||
fingerPrintCallback.onFingerPrintException(e);
|
||||
}
|
||||
@@ -142,6 +145,8 @@ public class FingerPrintHelper {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
stopListening();
|
||||
|
||||
createNewKeyIfNeeded(false); // no need to keep deleting existing keys
|
||||
keyStore.load(null);
|
||||
final SecretKey key = (SecretKey) keyStore.getKey(FINGERPRINT_KEYSTORE_KEY, null);
|
||||
@@ -149,10 +154,13 @@ public class FingerPrintHelper {
|
||||
|
||||
startListening();
|
||||
} catch (final UnrecoverableKeyException unrecoverableKeyException) {
|
||||
Log.e(TAG, "Unable to initialize encrypt data", unrecoverableKeyException);
|
||||
deleteEntryKey();
|
||||
} catch (final KeyPermanentlyInvalidatedException invalidKeyException) {
|
||||
Log.e(TAG, "Unable to initialize encrypt data", invalidKeyException);
|
||||
fingerPrintCallback.onInvalidKeyException(invalidKeyException);
|
||||
} catch (final Exception e) {
|
||||
Log.e(TAG, "Unable to initialize encrypt data", e);
|
||||
fingerPrintCallback.onFingerPrintException(e);
|
||||
}
|
||||
}
|
||||
@@ -164,14 +172,15 @@ public class FingerPrintHelper {
|
||||
try {
|
||||
// actual do encryption here
|
||||
byte[] encrypted = cipher.doFinal(value.getBytes());
|
||||
final String encryptedValue = Base64.encodeToString(encrypted, Base64.DEFAULT);
|
||||
final String encryptedValue = Base64.encodeToString(encrypted, Base64.NO_WRAP);
|
||||
|
||||
// passes updated iv spec on to callback so this can be stored for decryption
|
||||
final IvParameterSpec spec = cipher.getParameters().getParameterSpec(IvParameterSpec.class);
|
||||
final String ivSpecValue = Base64.encodeToString(spec.getIV(), Base64.DEFAULT);
|
||||
final String ivSpecValue = Base64.encodeToString(spec.getIV(), Base64.NO_WRAP);
|
||||
fingerPrintCallback.handleEncryptedResult(encryptedValue, ivSpecValue);
|
||||
|
||||
} catch (final Exception e) {
|
||||
Log.e(TAG, "Unable to encrypt data", e);
|
||||
fingerPrintCallback.onFingerPrintException(e);
|
||||
}
|
||||
}
|
||||
@@ -181,21 +190,26 @@ public class FingerPrintHelper {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
stopListening();
|
||||
|
||||
createNewKeyIfNeeded(false);
|
||||
keyStore.load(null);
|
||||
final SecretKey key = (SecretKey) keyStore.getKey(FINGERPRINT_KEYSTORE_KEY, null);
|
||||
|
||||
// important to restore spec here that was used for decryption
|
||||
final byte[] iv = Base64.decode(ivSpecValue, Base64.DEFAULT);
|
||||
final byte[] iv = Base64.decode(ivSpecValue, Base64.NO_WRAP);
|
||||
final IvParameterSpec spec = new IvParameterSpec(iv);
|
||||
cipher.init(Cipher.DECRYPT_MODE, key, spec);
|
||||
|
||||
startListening();
|
||||
} catch (final KeyPermanentlyInvalidatedException invalidKeyException) {
|
||||
fingerPrintCallback.onInvalidKeyException(invalidKeyException);
|
||||
} catch (final UnrecoverableKeyException unrecoverableKeyException) {
|
||||
Log.e(TAG, "Unable to initialize decrypt data", unrecoverableKeyException);
|
||||
deleteEntryKey();
|
||||
} catch (final KeyPermanentlyInvalidatedException invalidKeyException) {
|
||||
Log.e(TAG, "Unable to initialize decrypt data", invalidKeyException);
|
||||
fingerPrintCallback.onInvalidKeyException(invalidKeyException);
|
||||
} catch (final Exception e) {
|
||||
Log.e(TAG, "Unable to initialize decrypt data", e);
|
||||
fingerPrintCallback.onFingerPrintException(e);
|
||||
}
|
||||
}
|
||||
@@ -206,15 +220,17 @@ public class FingerPrintHelper {
|
||||
}
|
||||
try {
|
||||
// actual decryption here
|
||||
final byte[] encrypted = Base64.decode(encryptedValue, Base64.DEFAULT);
|
||||
final byte[] encrypted = Base64.decode(encryptedValue, Base64.NO_WRAP);
|
||||
byte[] decrypted = cipher.doFinal(encrypted);
|
||||
final String decryptedString = new String(decrypted);
|
||||
|
||||
//final String encryptedString = Base64.encodeToString(encrypted, 0 /* flags */);
|
||||
fingerPrintCallback.handleDecryptedResult(decryptedString);
|
||||
} catch (final BadPaddingException badPaddingException) {
|
||||
Log.e(TAG, "Unable to decrypt data", badPaddingException);
|
||||
fingerPrintCallback.onInvalidKeyException(badPaddingException);
|
||||
} catch (final Exception e) {
|
||||
Log.e(TAG, "Unable to decrypt data", e);
|
||||
fingerPrintCallback.onFingerPrintException(e);
|
||||
}
|
||||
}
|
||||
@@ -249,6 +265,7 @@ public class FingerPrintHelper {
|
||||
keyGenerator.generateKey();
|
||||
}
|
||||
} catch (final Exception e) {
|
||||
Log.e(TAG, "Unable to create a key in keystore", e);
|
||||
fingerPrintCallback.onFingerPrintException(e);
|
||||
}
|
||||
}
|
||||
@@ -262,6 +279,7 @@ public class FingerPrintHelper {
|
||||
| NoSuchAlgorithmException
|
||||
| IOException
|
||||
| NullPointerException e) {
|
||||
Log.e(TAG, "Unable to delete entry key in keystore", e);
|
||||
if (fingerPrintCallback != null)
|
||||
fingerPrintCallback.onFingerPrintException(e);
|
||||
}
|
||||
|
||||
@@ -89,6 +89,8 @@ import static com.keepassdroid.fingerprint.FingerPrintHelper.Mode.STORE_MODE;
|
||||
public class PasswordActivity extends StylishActivity
|
||||
implements FingerPrintHelper.FingerPrintCallback, UriIntentInitTaskCallback {
|
||||
|
||||
private static final String TAG = PasswordActivity.class.getName();
|
||||
|
||||
public static final String KEY_DEFAULT_FILENAME = "defaultFileName";
|
||||
|
||||
private static final String KEY_PASSWORD = "password";
|
||||
@@ -440,7 +442,7 @@ public class PasswordActivity extends StylishActivity
|
||||
if ( !fingerprintMustBeConfigured ) {
|
||||
final boolean validInput = s.length() > 0;
|
||||
// encrypt or decrypt mode based on how much input or not
|
||||
setFingerPrintTextView(validInput ? R.string.store_with_fingerprint : R.string.scanning_fingerprint);
|
||||
setFingerPrintView(validInput ? R.string.store_with_fingerprint : R.string.scanning_fingerprint);
|
||||
if (validInput)
|
||||
toggleFingerprintMode(STORE_MODE);
|
||||
else
|
||||
@@ -455,22 +457,30 @@ public class PasswordActivity extends StylishActivity
|
||||
public void onAuthenticationError(
|
||||
final int errorCode,
|
||||
final CharSequence errString) {
|
||||
Log.i(getClass().getName(), errString.toString());
|
||||
switch (errorCode) {
|
||||
case 5:
|
||||
Log.i(TAG, "Fingerprint authentication error. Code : " + errorCode + " Error : " + errString);
|
||||
break;
|
||||
default:
|
||||
Log.e(TAG, "Fingerprint authentication error. Code : " + errorCode + " Error : " + errString);
|
||||
setFingerPrintView(errString.toString(), true);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAuthenticationHelp(
|
||||
final int helpCode,
|
||||
final CharSequence helpString) {
|
||||
Log.w(TAG, "Fingerprint authentication help. Code : " + helpCode + " Help : " + helpString);
|
||||
showError(helpString);
|
||||
reInitWithSameFingerprintMode();
|
||||
setFingerPrintView(helpString.toString(), true);
|
||||
fingerprintTextView.setText(helpString);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAuthenticationFailed() {
|
||||
Log.e(TAG, "Fingerprint authentication failed, fingerprint not recognized");
|
||||
showError(R.string.fingerprint_not_recognized);
|
||||
reInitWithSameFingerprintMode();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -504,7 +514,7 @@ public class PasswordActivity extends StylishActivity
|
||||
|
||||
@RequiresApi(api = Build.VERSION_CODES.M)
|
||||
private void initEncryptData() {
|
||||
setFingerPrintTextView(R.string.store_with_fingerprint);
|
||||
setFingerPrintView(R.string.store_with_fingerprint);
|
||||
fingerPrintMode = STORE_MODE;
|
||||
if (fingerPrintHelper != null)
|
||||
fingerPrintHelper.initEncryptData();
|
||||
@@ -512,7 +522,7 @@ public class PasswordActivity extends StylishActivity
|
||||
|
||||
@RequiresApi(api = Build.VERSION_CODES.M)
|
||||
private void initDecryptData() {
|
||||
setFingerPrintTextView(R.string.scanning_fingerprint);
|
||||
setFingerPrintView(R.string.scanning_fingerprint);
|
||||
fingerPrintMode = OPEN_MODE;
|
||||
if (fingerPrintHelper != null) {
|
||||
final String ivSpecValue = prefsNoBackup.getString(getPreferenceKeyIvSpec(), null);
|
||||
@@ -525,12 +535,12 @@ public class PasswordActivity extends StylishActivity
|
||||
private synchronized void toggleFingerprintMode(final FingerPrintHelper.Mode newMode) {
|
||||
if( !newMode.equals(fingerPrintMode) ) {
|
||||
fingerPrintMode = newMode;
|
||||
reInitWithSameFingerprintMode();
|
||||
reInitWithFingerprintMode();
|
||||
}
|
||||
}
|
||||
|
||||
@RequiresApi(api = Build.VERSION_CODES.M)
|
||||
private synchronized void reInitWithSameFingerprintMode() {
|
||||
private synchronized void reInitWithFingerprintMode() {
|
||||
switch (fingerPrintMode) {
|
||||
case STORE_MODE:
|
||||
initEncryptData();
|
||||
@@ -545,8 +555,6 @@ public class PasswordActivity extends StylishActivity
|
||||
|
||||
@Override
|
||||
protected void onPause() {
|
||||
super.onPause();
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||
if (fingerPrintAnimatedVector != null) {
|
||||
fingerPrintAnimatedVector.stopScan();
|
||||
@@ -557,18 +565,33 @@ public class PasswordActivity extends StylishActivity
|
||||
fingerPrintHelper.stopListening();
|
||||
}
|
||||
}
|
||||
super.onPause();
|
||||
}
|
||||
|
||||
private void setFingerPrintVisibility(final int vis) {
|
||||
runOnUiThread(() -> fingerprintContainerView.setVisibility(vis));
|
||||
}
|
||||
|
||||
private void setFingerPrintTextView(final int textId) {
|
||||
runOnUiThread(() -> fingerprintTextView.setText(textId));
|
||||
private void setFingerPrintView(final int textId) {
|
||||
setFingerPrintView(textId, false);
|
||||
}
|
||||
|
||||
private void setFingerPrintAlphaImageView(final float alpha) {
|
||||
runOnUiThread(() -> fingerprintContainerView.setAlpha(alpha));
|
||||
private void setFingerPrintView(final CharSequence text) {
|
||||
setFingerPrintView(text, false);
|
||||
}
|
||||
|
||||
private void setFingerPrintView(final int textId, boolean lock) {
|
||||
setFingerPrintView(getString(textId), lock);
|
||||
}
|
||||
|
||||
private void setFingerPrintView(final CharSequence text, boolean lock) {
|
||||
runOnUiThread(() -> {
|
||||
if (lock) {
|
||||
fingerprintContainerView.setAlpha(0.6f);
|
||||
} else
|
||||
fingerprintContainerView.setAlpha(1f);
|
||||
fingerprintTextView.setText(text);
|
||||
});
|
||||
}
|
||||
|
||||
@RequiresApi(api = Build.VERSION_CODES.M)
|
||||
@@ -589,18 +612,16 @@ public class PasswordActivity extends StylishActivity
|
||||
setFingerPrintVisibility(View.VISIBLE);
|
||||
|
||||
if (!fingerPrintHelper.hasEnrolledFingerprints()) {
|
||||
setFingerPrintAlphaImageView(0.6f);
|
||||
// This happens when no fingerprints are registered. Listening won't start
|
||||
setFingerPrintTextView(R.string.configure_fingerprint);
|
||||
setFingerPrintView(R.string.configure_fingerprint, true);
|
||||
}
|
||||
// finally fingerprint available and configured so we can use it
|
||||
else {
|
||||
fingerprintMustBeConfigured = false;
|
||||
setFingerPrintAlphaImageView(1f);
|
||||
|
||||
// fingerprint available but no stored password found yet for this DB so show info don't listen
|
||||
if (!prefsNoBackup.contains(getPreferenceKeyValue())) {
|
||||
setFingerPrintTextView(R.string.no_password_stored);
|
||||
setFingerPrintView(R.string.no_password_stored);
|
||||
// listen for encryption
|
||||
initEncryptData();
|
||||
}
|
||||
@@ -616,7 +637,7 @@ public class PasswordActivity extends StylishActivity
|
||||
invalidateOptionsMenu();
|
||||
}
|
||||
|
||||
private void removePrefsNoBackupKeys() {
|
||||
private void removePrefsNoBackupKey() {
|
||||
prefsNoBackup.edit()
|
||||
.remove(getPreferenceKeyValue())
|
||||
.remove(getPreferenceKeyIvSpec())
|
||||
@@ -632,7 +653,7 @@ public class PasswordActivity extends StylishActivity
|
||||
.putString(getPreferenceKeyIvSpec(), ivSpec)
|
||||
.apply();
|
||||
verifyAllViewsAndLoadDatabase();
|
||||
setFingerPrintTextView(R.string.encrypted_value_stored);
|
||||
setFingerPrintView(R.string.encrypted_value_stored);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -644,22 +665,27 @@ public class PasswordActivity extends StylishActivity
|
||||
@RequiresApi(api = Build.VERSION_CODES.M)
|
||||
@Override
|
||||
public void onInvalidKeyException(Exception e) {
|
||||
showError(R.string.fingerprint_invalid_key);
|
||||
removePrefsNoBackupKeys();
|
||||
e.printStackTrace();
|
||||
reInitWithSameFingerprintMode(); // restarts listening
|
||||
showError(getString(R.string.fingerprint_invalid_key));
|
||||
deleteEntryKey();
|
||||
}
|
||||
|
||||
@RequiresApi(api = Build.VERSION_CODES.M)
|
||||
@Override
|
||||
public void onFingerPrintException(Exception e) {
|
||||
showError(R.string.fingerprint_error);
|
||||
e.printStackTrace();
|
||||
reInitWithSameFingerprintMode();
|
||||
showError(getString(R.string.fingerprint_error, e.getMessage()));
|
||||
setFingerPrintView(e.getLocalizedMessage(), true);
|
||||
}
|
||||
|
||||
@RequiresApi(api = Build.VERSION_CODES.M)
|
||||
private void deleteEntryKey() {
|
||||
fingerPrintHelper.deleteEntryKey();
|
||||
removePrefsNoBackupKey();
|
||||
fingerPrintMode = NOT_CONFIGURED_MODE;
|
||||
checkFingerprintAvailability();
|
||||
}
|
||||
|
||||
private void showError(final int messageId) {
|
||||
runOnUiThread(() -> Toast.makeText(getApplicationContext(), messageId, Toast.LENGTH_SHORT).show());
|
||||
showError(getString(messageId));
|
||||
}
|
||||
|
||||
private void showError(final CharSequence message) {
|
||||
@@ -753,10 +779,7 @@ public class PasswordActivity extends StylishActivity
|
||||
break;
|
||||
case R.id.menu_fingerprint_remove_key:
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||
fingerPrintHelper.deleteEntryKey();
|
||||
removePrefsNoBackupKeys();
|
||||
fingerPrintMode = NOT_CONFIGURED_MODE;
|
||||
checkFingerprintAvailability();
|
||||
deleteEntryKey();
|
||||
}
|
||||
break;
|
||||
default:
|
||||
@@ -793,11 +816,8 @@ public class PasswordActivity extends StylishActivity
|
||||
|
||||
// Recheck fingerprint if error
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||
//setEmptyViews();
|
||||
//mMessage = getString(R.string.fingerprint_error) + " : " + mMessage;
|
||||
// TODO Change fingerprint message
|
||||
// Stay with the same mode
|
||||
reInitWithSameFingerprintMode();
|
||||
reInitWithFingerprintMode();
|
||||
}
|
||||
|
||||
if (db.passwordEncodingError) {
|
||||
|
||||
@@ -131,16 +131,13 @@ public class NestedSettingsFragment extends PreferenceFragmentCompat
|
||||
if (!fingerprintSupported) {
|
||||
// False if under Marshmallow
|
||||
fingerprintEnablePreference.setChecked(false);
|
||||
fingerprintEnablePreference.setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() {
|
||||
@Override
|
||||
public boolean onPreferenceClick(Preference preference) {
|
||||
FragmentManager fragmentManager = getFragmentManager();
|
||||
assert fragmentManager != null;
|
||||
((SwitchPreference) preference).setChecked(false);
|
||||
UnavailableFeatureDialogFragment.getInstance(Build.VERSION_CODES.M)
|
||||
.show(getFragmentManager(), "unavailableFeatureDialog");
|
||||
return false;
|
||||
}
|
||||
fingerprintEnablePreference.setOnPreferenceClickListener(preference -> {
|
||||
FragmentManager fragmentManager = getFragmentManager();
|
||||
assert fragmentManager != null;
|
||||
((SwitchPreference) preference).setChecked(false);
|
||||
UnavailableFeatureDialogFragment.getInstance(Build.VERSION_CODES.M)
|
||||
.show(getFragmentManager(), "unavailableFeatureDialog");
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
@@ -148,45 +145,39 @@ public class NestedSettingsFragment extends PreferenceFragmentCompat
|
||||
if (!fingerprintSupported) {
|
||||
deleteKeysFingerprints.setEnabled(false);
|
||||
} else {
|
||||
deleteKeysFingerprints.setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() {
|
||||
@Override
|
||||
public boolean onPreferenceClick(Preference preference) {
|
||||
new AlertDialog.Builder(getContext())
|
||||
.setMessage(getResources().getString(R.string.fingerprint_delete_all_warning))
|
||||
.setIcon(getResources().getDrawable(
|
||||
android.R.drawable.ic_dialog_alert))
|
||||
.setPositiveButton(
|
||||
getResources().getString(android.R.string.yes),
|
||||
new DialogInterface.OnClickListener() {
|
||||
@RequiresApi(api = Build.VERSION_CODES.M)
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog,
|
||||
int which) {
|
||||
FingerPrintHelper.deleteEntryKeyInKeystoreForFingerprints(
|
||||
getContext(),
|
||||
new FingerPrintHelper.FingerPrintErrorCallback() {
|
||||
@Override
|
||||
public void onInvalidKeyException(Exception e) {
|
||||
}
|
||||
deleteKeysFingerprints.setOnPreferenceClickListener(preference -> {
|
||||
new AlertDialog.Builder(getContext())
|
||||
.setMessage(getResources().getString(R.string.fingerprint_delete_all_warning))
|
||||
.setIcon(getResources().getDrawable(
|
||||
android.R.drawable.ic_dialog_alert))
|
||||
.setPositiveButton(
|
||||
getResources().getString(android.R.string.yes),
|
||||
new DialogInterface.OnClickListener() {
|
||||
@RequiresApi(api = Build.VERSION_CODES.M)
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog,
|
||||
int which) {
|
||||
FingerPrintHelper.deleteEntryKeyInKeystoreForFingerprints(
|
||||
getContext(),
|
||||
new FingerPrintHelper.FingerPrintErrorCallback() {
|
||||
@Override
|
||||
public void onInvalidKeyException(Exception e) {}
|
||||
|
||||
@Override
|
||||
public void onFingerPrintException(Exception e) {
|
||||
Toast.makeText(getContext(), R.string.fingerprint_error, Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
});
|
||||
PreferencesUtil.deleteAllValuesFromNoBackupPreferences(getContext());
|
||||
}
|
||||
})
|
||||
.setNegativeButton(
|
||||
getResources().getString(android.R.string.no),
|
||||
new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog,
|
||||
int which) {
|
||||
}
|
||||
}).show();
|
||||
return false;
|
||||
}
|
||||
@Override
|
||||
public void onFingerPrintException(Exception e) {
|
||||
Toast.makeText(getContext(),
|
||||
getString(R.string.fingerprint_error, e.getLocalizedMessage()),
|
||||
Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
});
|
||||
PreferencesUtil.deleteAllValuesFromNoBackupPreferences(getContext());
|
||||
}
|
||||
})
|
||||
.setNegativeButton(
|
||||
getResources().getString(android.R.string.no),
|
||||
(dialog, which) -> {
|
||||
}).show();
|
||||
return false;
|
||||
});
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -188,9 +188,9 @@
|
||||
<string name="configure_fingerprint">Reconnaissance d\'empreinte digitale non configuré pour cet appareil</string>
|
||||
<string name="scanning_fingerprint">Attente d\'une reconnaissance d\'empreinte digitale</string>
|
||||
<string name="encrypted_value_stored">Mot de passe encrypté stocké</string>
|
||||
<string name="fingerprint_invalid_key">Problème de clé invalide</string>
|
||||
<string name="fingerprint_invalid_key">Problème de clé invalide. Restaurez votre mot de passe.</string>
|
||||
<string name="fingerprint_not_recognized">Empreinte digitale non reconnue</string>
|
||||
<string name="fingerprint_error">Problème d\'empreinte digitale</string>
|
||||
<string name="fingerprint_error">Problème d\'empreinte digitale : %1$s</string>
|
||||
<string name="store_with_fingerprint">Utiliser l\'empreinte digitale pour stocker le mot de passe</string>
|
||||
<string name="no_password_stored">Pas de mot de passe encore stocké pour cette base de données</string>
|
||||
<string name="history">Historique</string>
|
||||
|
||||
@@ -161,8 +161,8 @@
|
||||
<string name="configure_fingerprint">Az ujjlenyomat használat nincs még beállítva az eszközön</string>
|
||||
<string name="scanning_fingerprint">Várakozás az ujjelnyomat megadására</string>
|
||||
<string name="encrypted_value_stored">Titkosított jelszó elmentve</string>
|
||||
<string name="fingerprint_invalid_key">Érvénytelen kulcs</string>
|
||||
<string name="fingerprint_error">Érvénytelen ujjlenyomat</string>
|
||||
<string name="fingerprint_invalid_key">Érvénytelen kulcs. Helyezze vissza jelszavát.</string>
|
||||
<string name="fingerprint_error">Érvénytelen ujjlenyomat : %1$s</string>
|
||||
<string name="store_with_fingerprint">Használjon ujjlenyomatot a jelszó elmentéséhez</string>
|
||||
<string name="no_password_stored">Nincs még jelszó beállítva ehhez az adatbázishoz</string>
|
||||
|
||||
|
||||
@@ -188,9 +188,9 @@
|
||||
<string name="configure_fingerprint">Fingerprint supported but not configured for device</string>
|
||||
<string name="scanning_fingerprint">Listening for fingerprints</string>
|
||||
<string name="encrypted_value_stored">Encrypted password stored</string>
|
||||
<string name="fingerprint_invalid_key">Invalid Key problem</string>
|
||||
<string name="fingerprint_invalid_key">Invalid fingerprint key problem. Restore your password.</string>
|
||||
<string name="fingerprint_not_recognized">Fingerprint not recognized</string>
|
||||
<string name="fingerprint_error">Fingerprint problem</string>
|
||||
<string name="fingerprint_error">Fingerprint problem : %1$s</string>
|
||||
<string name="store_with_fingerprint">Use fingerprint to store this password</string>
|
||||
<string name="no_password_stored">No password stored yet for this database</string>
|
||||
<string name="history">History</string>
|
||||
|
||||
Reference in New Issue
Block a user