mirror of
https://github.com/Kunzisoft/KeePassDX.git
synced 2025-12-04 15:49:33 +01:00
Merge tag '2.5.0.0beta24' into develop
2.5.0.0beta24
This commit is contained in:
@@ -55,7 +55,7 @@ You can contribute in different ways to help us on our work.
|
|||||||
|
|
||||||
## F.A.Q.
|
## F.A.Q.
|
||||||
|
|
||||||
Other questions? You can read the [F.A.Q.](https://www.keepassdx.com/FAQ)
|
Other questions? You can read the [F.A.Q.](https://github.com/Kunzisoft/KeePassDX/wiki/F.A.Q.)
|
||||||
|
|
||||||
## Other devices
|
## Other devices
|
||||||
|
|
||||||
|
|||||||
1
_config.yml
Normal file
1
_config.yml
Normal file
@@ -0,0 +1 @@
|
|||||||
|
theme: jekyll-theme-cayman
|
||||||
@@ -163,7 +163,7 @@ class FileDatabaseSelectActivity : StylishActivity(),
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Attach the dialog thread to this activity
|
// Attach the dialog thread to this activity
|
||||||
progressDialogThread = ProgressDialogThread(this) { actionTask, result ->
|
progressDialogThread = ProgressDialogThread(this) { actionTask, _ ->
|
||||||
when (actionTask) {
|
when (actionTask) {
|
||||||
ACTION_DATABASE_CREATE_TASK -> {
|
ACTION_DATABASE_CREATE_TASK -> {
|
||||||
// TODO Check
|
// TODO Check
|
||||||
|
|||||||
@@ -55,7 +55,11 @@ class SetOTPDialogFragment : DialogFragment() {
|
|||||||
private var otpAlgorithmAdapter: ArrayAdapter<TokenCalculator.HashAlgorithm>? = null
|
private var otpAlgorithmAdapter: ArrayAdapter<TokenCalculator.HashAlgorithm>? = null
|
||||||
|
|
||||||
private var mManualEvent = false
|
private var mManualEvent = false
|
||||||
private var touchListener = View.OnTouchListener { _, event ->
|
private var mOnFocusChangeListener = View.OnFocusChangeListener { _, isFocus ->
|
||||||
|
if (!isFocus)
|
||||||
|
mManualEvent = true
|
||||||
|
}
|
||||||
|
private var mOnTouchListener = View.OnTouchListener { _, event ->
|
||||||
when (event.action) {
|
when (event.action) {
|
||||||
MotionEvent.ACTION_DOWN -> {
|
MotionEvent.ACTION_DOWN -> {
|
||||||
mManualEvent = true
|
mManualEvent = true
|
||||||
@@ -117,13 +121,17 @@ class SetOTPDialogFragment : DialogFragment() {
|
|||||||
otpDigitsTextView = root?.findViewById(R.id.setup_otp_digits)
|
otpDigitsTextView = root?.findViewById(R.id.setup_otp_digits)
|
||||||
|
|
||||||
// To fix init element
|
// To fix init element
|
||||||
otpTypeSpinner?.setOnTouchListener(touchListener)
|
// With tab keyboard selection
|
||||||
otpTokenTypeSpinner?.setOnTouchListener(touchListener)
|
otpSecretTextView?.onFocusChangeListener = mOnFocusChangeListener
|
||||||
otpAlgorithmSpinner?.setOnTouchListener(touchListener)
|
// With finger selection
|
||||||
otpSecretTextView?.setOnTouchListener(touchListener)
|
otpTypeSpinner?.setOnTouchListener(mOnTouchListener)
|
||||||
otpPeriodTextView?.setOnTouchListener(touchListener)
|
otpTokenTypeSpinner?.setOnTouchListener(mOnTouchListener)
|
||||||
otpCounterTextView?.setOnTouchListener(touchListener)
|
otpSecretTextView?.setOnTouchListener(mOnTouchListener)
|
||||||
otpDigitsTextView?.setOnTouchListener(touchListener)
|
otpAlgorithmSpinner?.setOnTouchListener(mOnTouchListener)
|
||||||
|
otpPeriodTextView?.setOnTouchListener(mOnTouchListener)
|
||||||
|
otpCounterTextView?.setOnTouchListener(mOnTouchListener)
|
||||||
|
otpDigitsTextView?.setOnTouchListener(mOnTouchListener)
|
||||||
|
|
||||||
|
|
||||||
// HOTP / TOTP Type selection
|
// HOTP / TOTP Type selection
|
||||||
val otpTypeArray = OtpType.values()
|
val otpTypeArray = OtpType.values()
|
||||||
@@ -178,8 +186,8 @@ class SetOTPDialogFragment : DialogFragment() {
|
|||||||
return super.onCreateDialog(savedInstanceState)
|
return super.onCreateDialog(savedInstanceState)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onStart() {
|
override fun onResume() {
|
||||||
super.onStart()
|
super.onResume()
|
||||||
(dialog as AlertDialog).getButton(Dialog.BUTTON_POSITIVE).setOnClickListener {
|
(dialog as AlertDialog).getButton(Dialog.BUTTON_POSITIVE).setOnClickListener {
|
||||||
if (mSecretWellFormed
|
if (mSecretWellFormed
|
||||||
&& mCounterWellFormed
|
&& mCounterWellFormed
|
||||||
@@ -236,16 +244,14 @@ class SetOTPDialogFragment : DialogFragment() {
|
|||||||
// Set secret in OtpElement
|
// Set secret in OtpElement
|
||||||
otpSecretTextView?.addTextChangedListener(object: TextWatcher {
|
otpSecretTextView?.addTextChangedListener(object: TextWatcher {
|
||||||
override fun afterTextChanged(s: Editable?) {
|
override fun afterTextChanged(s: Editable?) {
|
||||||
if (mManualEvent) {
|
s?.toString()?.let { userString ->
|
||||||
s?.toString()?.let { userString ->
|
try {
|
||||||
try {
|
mOtpElement.setBase32Secret(userString)
|
||||||
mOtpElement.setBase32Secret(userString)
|
otpSecretContainer?.error = null
|
||||||
otpSecretContainer?.error = null
|
} catch (exception: Exception) {
|
||||||
} catch (exception: Exception) {
|
otpSecretContainer?.error = getString(R.string.error_otp_secret_key)
|
||||||
otpSecretContainer?.error = getString(R.string.error_otp_secret_key)
|
|
||||||
}
|
|
||||||
mSecretWellFormed = otpSecretContainer?.error == null
|
|
||||||
}
|
}
|
||||||
|
mSecretWellFormed = otpSecretContainer?.error == null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {}
|
override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {}
|
||||||
@@ -339,7 +345,11 @@ class SetOTPDialogFragment : DialogFragment() {
|
|||||||
private fun upgradeParameters() {
|
private fun upgradeParameters() {
|
||||||
otpAlgorithmSpinner?.setSelection(TokenCalculator.HashAlgorithm.values()
|
otpAlgorithmSpinner?.setSelection(TokenCalculator.HashAlgorithm.values()
|
||||||
.indexOf(mOtpElement.algorithm))
|
.indexOf(mOtpElement.algorithm))
|
||||||
otpSecretTextView?.setText(mOtpElement.getBase32Secret())
|
otpSecretTextView?.apply {
|
||||||
|
setText(mOtpElement.getBase32Secret())
|
||||||
|
// Cursor at end
|
||||||
|
setSelection(this.text.length)
|
||||||
|
}
|
||||||
otpCounterTextView?.setText(mOtpElement.counter.toString())
|
otpCounterTextView?.setText(mOtpElement.counter.toString())
|
||||||
otpPeriodTextView?.setText(mOtpElement.period.toString())
|
otpPeriodTextView?.setText(mOtpElement.period.toString())
|
||||||
otpDigitsTextView?.setText(mOtpElement.digits.toString())
|
otpDigitsTextView?.setText(mOtpElement.digits.toString())
|
||||||
|
|||||||
@@ -75,9 +75,10 @@ class OpenFileHelper {
|
|||||||
val intentOpenDocument = Intent(APP_ACTION_OPEN_DOCUMENT).apply {
|
val intentOpenDocument = Intent(APP_ACTION_OPEN_DOCUMENT).apply {
|
||||||
addCategory(Intent.CATEGORY_OPENABLE)
|
addCategory(Intent.CATEGORY_OPENABLE)
|
||||||
type = "*/*"
|
type = "*/*"
|
||||||
flags = Intent.FLAG_GRANT_READ_URI_PERMISSION or
|
flags = Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION or
|
||||||
Intent.FLAG_GRANT_WRITE_URI_PERMISSION or
|
Intent.FLAG_GRANT_PREFIX_URI_PERMISSION or
|
||||||
Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION
|
Intent.FLAG_GRANT_READ_URI_PERMISSION or
|
||||||
|
Intent.FLAG_GRANT_WRITE_URI_PERMISSION
|
||||||
}
|
}
|
||||||
if (fragment != null)
|
if (fragment != null)
|
||||||
fragment?.startActivityForResult(intentOpenDocument, OPEN_DOC)
|
fragment?.startActivityForResult(intentOpenDocument, OPEN_DOC)
|
||||||
@@ -85,10 +86,15 @@ class OpenFileHelper {
|
|||||||
activity?.startActivityForResult(intentOpenDocument, OPEN_DOC)
|
activity?.startActivityForResult(intentOpenDocument, OPEN_DOC)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressLint("InlinedApi")
|
||||||
private fun openActivityWithActionGetContent() {
|
private fun openActivityWithActionGetContent() {
|
||||||
val intentGetContent = Intent(Intent.ACTION_GET_CONTENT).apply {
|
val intentGetContent = Intent(Intent.ACTION_GET_CONTENT).apply {
|
||||||
addCategory(Intent.CATEGORY_OPENABLE)
|
addCategory(Intent.CATEGORY_OPENABLE)
|
||||||
type = "*/*"
|
type = "*/*"
|
||||||
|
flags = Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION or
|
||||||
|
Intent.FLAG_GRANT_PREFIX_URI_PERMISSION or
|
||||||
|
Intent.FLAG_GRANT_READ_URI_PERMISSION or
|
||||||
|
Intent.FLAG_GRANT_WRITE_URI_PERMISSION
|
||||||
}
|
}
|
||||||
if (fragment != null)
|
if (fragment != null)
|
||||||
fragment?.startActivityForResult(intentGetContent, GET_CONTENT)
|
fragment?.startActivityForResult(intentGetContent, GET_CONTENT)
|
||||||
|
|||||||
@@ -62,16 +62,10 @@ class LoadDatabaseInvalidAlgorithmException : LoadDatabaseException {
|
|||||||
|
|
||||||
class LoadDatabaseDuplicateUuidException: LoadDatabaseException {
|
class LoadDatabaseDuplicateUuidException: LoadDatabaseException {
|
||||||
@StringRes
|
@StringRes
|
||||||
override var errorId: Int = R.string.invalid_algorithm
|
override var errorId: Int = R.string.invalid_db_same_uuid
|
||||||
|
|
||||||
constructor(type: Type, uuid: PwNodeId<*>) : super() {
|
constructor(type: Type, uuid: PwNodeId<*>) : super() {
|
||||||
parameters = Array(2) {
|
parameters = arrayOf(type.name, uuid.toString())
|
||||||
when(it) {
|
|
||||||
1 -> type.name
|
|
||||||
2 -> uuid.toString()
|
|
||||||
else -> {""}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
constructor(exception: Throwable) : super(exception)
|
constructor(exception: Throwable) : super(exception)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
package com.kunzisoft.keepass.otp
|
package com.kunzisoft.keepass.otp
|
||||||
|
|
||||||
import com.kunzisoft.keepass.model.OtpModel
|
import com.kunzisoft.keepass.model.OtpModel
|
||||||
import org.apache.commons.codec.DecoderException
|
|
||||||
import org.apache.commons.codec.binary.Base32
|
import org.apache.commons.codec.binary.Base32
|
||||||
import org.apache.commons.codec.binary.Base64
|
import org.apache.commons.codec.binary.Base64
|
||||||
import org.apache.commons.codec.binary.Hex
|
import org.apache.commons.codec.binary.Hex
|
||||||
@@ -68,15 +67,17 @@ data class OtpElement(var otpModel: OtpModel = OtpModel()) {
|
|||||||
|
|
||||||
var counter
|
var counter
|
||||||
get() = otpModel.counter
|
get() = otpModel.counter
|
||||||
|
@Throws(NumberFormatException::class)
|
||||||
set(value) {
|
set(value) {
|
||||||
otpModel.counter = if (value < MIN_HOTP_COUNTER || value > MAX_HOTP_COUNTER) {
|
otpModel.counter = if (value < MIN_HOTP_COUNTER || value > MAX_HOTP_COUNTER) {
|
||||||
TokenCalculator.HOTP_INITIAL_COUNTER
|
TokenCalculator.HOTP_INITIAL_COUNTER
|
||||||
throw NumberFormatException()
|
throw IllegalArgumentException()
|
||||||
} else value
|
} else value
|
||||||
}
|
}
|
||||||
|
|
||||||
var period
|
var period
|
||||||
get() = otpModel.period
|
get() = otpModel.period
|
||||||
|
@Throws(NumberFormatException::class)
|
||||||
set(value) {
|
set(value) {
|
||||||
otpModel.period = if (value < MIN_TOTP_PERIOD || value > MAX_TOTP_PERIOD) {
|
otpModel.period = if (value < MIN_TOTP_PERIOD || value > MAX_TOTP_PERIOD) {
|
||||||
TokenCalculator.TOTP_DEFAULT_PERIOD
|
TokenCalculator.TOTP_DEFAULT_PERIOD
|
||||||
@@ -86,6 +87,7 @@ data class OtpElement(var otpModel: OtpModel = OtpModel()) {
|
|||||||
|
|
||||||
var digits
|
var digits
|
||||||
get() = otpModel.digits
|
get() = otpModel.digits
|
||||||
|
@Throws(NumberFormatException::class)
|
||||||
set(value) {
|
set(value) {
|
||||||
otpModel.digits = if (value < MIN_OTP_DIGITS|| value > MAX_OTP_DIGITS) {
|
otpModel.digits = if (value < MIN_OTP_DIGITS|| value > MAX_OTP_DIGITS) {
|
||||||
TokenCalculator.OTP_DEFAULT_DIGITS
|
TokenCalculator.OTP_DEFAULT_DIGITS
|
||||||
@@ -99,18 +101,20 @@ data class OtpElement(var otpModel: OtpModel = OtpModel()) {
|
|||||||
otpModel.algorithm = value
|
otpModel.algorithm = value
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Throws(IllegalArgumentException::class)
|
||||||
fun setUTF8Secret(secret: String) {
|
fun setUTF8Secret(secret: String) {
|
||||||
if (secret.isNotEmpty())
|
if (secret.isNotEmpty())
|
||||||
otpModel.secret = secret.toByteArray(Charset.forName("UTF-8"))
|
otpModel.secret = secret.toByteArray(Charset.forName("UTF-8"))
|
||||||
else
|
else
|
||||||
throw DecoderException()
|
throw IllegalArgumentException()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Throws(IllegalArgumentException::class)
|
||||||
fun setHexSecret(secret: String) {
|
fun setHexSecret(secret: String) {
|
||||||
if (secret.isNotEmpty())
|
if (secret.isNotEmpty())
|
||||||
otpModel.secret = Hex.decodeHex(secret)
|
otpModel.secret = Hex.decodeHex(secret)
|
||||||
else
|
else
|
||||||
throw DecoderException()
|
throw IllegalArgumentException()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getBase32Secret(): String {
|
fun getBase32Secret(): String {
|
||||||
@@ -119,18 +123,20 @@ data class OtpElement(var otpModel: OtpModel = OtpModel()) {
|
|||||||
} ?: ""
|
} ?: ""
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Throws(IllegalArgumentException::class)
|
||||||
fun setBase32Secret(secret: String) {
|
fun setBase32Secret(secret: String) {
|
||||||
if (secret.isNotEmpty() && checkBase32Secret(secret))
|
if (secret.isNotEmpty() && checkBase32Secret(secret))
|
||||||
otpModel.secret = Base32().decode(secret.toByteArray())
|
otpModel.secret = Base32().decode(secret.toByteArray())
|
||||||
else
|
else
|
||||||
throw DecoderException()
|
throw IllegalArgumentException()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Throws(IllegalArgumentException::class)
|
||||||
fun setBase64Secret(secret: String) {
|
fun setBase64Secret(secret: String) {
|
||||||
if (secret.isNotEmpty() && checkBase64Secret(secret))
|
if (secret.isNotEmpty() && checkBase64Secret(secret))
|
||||||
otpModel.secret = Base64().decode(secret.toByteArray())
|
otpModel.secret = Base64().decode(secret.toByteArray())
|
||||||
else
|
else
|
||||||
throw DecoderException()
|
throw IllegalArgumentException()
|
||||||
}
|
}
|
||||||
|
|
||||||
val token: String
|
val token: String
|
||||||
|
|||||||
@@ -158,29 +158,29 @@ object OtpEntryFields {
|
|||||||
|
|
||||||
val digitsParam = uri.getQueryParameter(DIGITS_URL_PARAM)
|
val digitsParam = uri.getQueryParameter(DIGITS_URL_PARAM)
|
||||||
if (digitsParam != null && digitsParam.isNotEmpty())
|
if (digitsParam != null && digitsParam.isNotEmpty())
|
||||||
otpElement.digits = try {
|
try {
|
||||||
digitsParam.toIntOrNull() ?: OTP_DEFAULT_DIGITS
|
otpElement.digits = digitsParam.toIntOrNull() ?: OTP_DEFAULT_DIGITS
|
||||||
} catch (exception: Exception) {
|
} catch (exception: Exception) {
|
||||||
Log.e(TAG, "Unable to retrieve OTP digits.", exception)
|
Log.e(TAG, "Unable to retrieve OTP digits.", exception)
|
||||||
OTP_DEFAULT_DIGITS
|
otpElement.digits = OTP_DEFAULT_DIGITS
|
||||||
}
|
}
|
||||||
|
|
||||||
val counterParam = uri.getQueryParameter(COUNTER_URL_PARAM)
|
val counterParam = uri.getQueryParameter(COUNTER_URL_PARAM)
|
||||||
if (counterParam != null && counterParam.isNotEmpty())
|
if (counterParam != null && counterParam.isNotEmpty())
|
||||||
otpElement.counter = try {
|
try {
|
||||||
counterParam.toLongOrNull() ?: HOTP_INITIAL_COUNTER
|
otpElement.counter = counterParam.toLongOrNull() ?: HOTP_INITIAL_COUNTER
|
||||||
} catch (exception: Exception) {
|
} catch (exception: Exception) {
|
||||||
Log.e(TAG, "Unable to retrieve HOTP counter.", exception)
|
Log.e(TAG, "Unable to retrieve HOTP counter.", exception)
|
||||||
HOTP_INITIAL_COUNTER
|
otpElement.counter = HOTP_INITIAL_COUNTER
|
||||||
}
|
}
|
||||||
|
|
||||||
val stepParam = uri.getQueryParameter(PERIOD_URL_PARAM)
|
val stepParam = uri.getQueryParameter(PERIOD_URL_PARAM)
|
||||||
if (stepParam != null && stepParam.isNotEmpty())
|
if (stepParam != null && stepParam.isNotEmpty())
|
||||||
otpElement.period = try {
|
try {
|
||||||
stepParam.toIntOrNull() ?: TOTP_DEFAULT_PERIOD
|
otpElement.period = stepParam.toIntOrNull() ?: TOTP_DEFAULT_PERIOD
|
||||||
} catch (exception: Exception) {
|
} catch (exception: Exception) {
|
||||||
Log.e(TAG, "Unable to retrieve TOTP period.", exception)
|
Log.e(TAG, "Unable to retrieve TOTP period.", exception)
|
||||||
TOTP_DEFAULT_PERIOD
|
otpElement.period = TOTP_DEFAULT_PERIOD
|
||||||
}
|
}
|
||||||
|
|
||||||
val algorithmParam = uri.getQueryParameter(ALGORITHM_URL_PARAM)
|
val algorithmParam = uri.getQueryParameter(ALGORITHM_URL_PARAM)
|
||||||
|
|||||||
@@ -20,7 +20,8 @@
|
|||||||
Translations from BoDEAN
|
Translations from BoDEAN
|
||||||
Translations from Matthias Dill
|
Translations from Matthias Dill
|
||||||
Translations from David Ramiro
|
Translations from David Ramiro
|
||||||
--><resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation">
|
-->
|
||||||
|
<resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation">
|
||||||
<string name="about_feedback">Rückmeldung</string>
|
<string name="about_feedback">Rückmeldung</string>
|
||||||
<string name="about_homepage">Webseite</string>
|
<string name="about_homepage">Webseite</string>
|
||||||
<string name="about_description">Android-Implementierung des Passwortmanagers KeePass</string>
|
<string name="about_description">Android-Implementierung des Passwortmanagers KeePass</string>
|
||||||
@@ -398,8 +399,6 @@
|
|||||||
<string name="disable">Deaktivieren</string>
|
<string name="disable">Deaktivieren</string>
|
||||||
<string name="biometric_prompt_store_credential_message">Datenbank-Anmeldeinformationen biometrisch speichern</string>
|
<string name="biometric_prompt_store_credential_message">Datenbank-Anmeldeinformationen biometrisch speichern</string>
|
||||||
<string name="biometric_prompt_extract_credential_message">Datenbank-Anmeldeinformationen mit biometrischen Daten extrahieren</string>
|
<string name="biometric_prompt_extract_credential_message">Datenbank-Anmeldeinformationen mit biometrischen Daten extrahieren</string>
|
||||||
<string name="fingerprint_open_biometric_prompt">Die Eingabeaufforderung für biometrische Daten mittels Klick auf den biometrischen Button öffnen</string>
|
|
||||||
<string name="biometric_auto_open_prompt_title">Biometrische Eingabeaufforderung automatisch öffnen</string>
|
<string name="biometric_auto_open_prompt_title">Biometrische Eingabeaufforderung automatisch öffnen</string>
|
||||||
<string name="fingerprint_auto_open_biometric_prompt">Sie können die Einstellung ändern, um die biometrische Eingabeaufforderung schnell zu öffnen und Schritt 1 nicht wiederholen zu müssen.</string>
|
|
||||||
<string name="biometric_auto_open_prompt_summary">Biometrische Eingabeaufforderung automatisch öffnen, wenn ein biometrischer Schlüssel für eine Datenbank definiert ist</string>
|
<string name="biometric_auto_open_prompt_summary">Biometrische Eingabeaufforderung automatisch öffnen, wenn ein biometrischer Schlüssel für eine Datenbank definiert ist</string>
|
||||||
</resources>
|
</resources>
|
||||||
@@ -10,7 +10,7 @@ buildscript {
|
|||||||
google()
|
google()
|
||||||
}
|
}
|
||||||
dependencies {
|
dependencies {
|
||||||
classpath 'com.android.tools.build:gradle:3.5.1'
|
classpath 'com.android.tools.build:gradle:3.5.2'
|
||||||
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
|
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user