Compare commits

...

14 Commits

Author SHA1 Message Date
J-Jamet
428efd2bb6 fix: CHANGELOG 2023-03-26 14:53:27 +02:00
J-Jamet
512b4cd16e Merge tag '3.5.1' into develop
3.5.1
2023-03-26 14:48:43 +02:00
J-Jamet
4d648b77b9 Merge branch 'release/3.5.1' 2023-03-26 14:48:32 +02:00
J-Jamet
02f6caf4dd fix: Loading dialog with Yubikey #1506 2023-03-26 14:46:11 +02:00
J-Jamet
2b23649674 feat: upgrade to 3.5.1 2023-03-26 14:44:20 +02:00
J-Jamet
e7cce21c51 Merge tag '3.5.0' into develop
3.5.0
2023-01-26 23:25:50 +01:00
J-Jamet
128ce5657e Merge branch 'release/3.5.0' 2023-01-26 23:25:40 +01:00
J-Jamet
861911ad63 fix: update fastlane changelogs 2023-01-26 23:21:10 +01:00
J-Jamet
9093c65235 feat: upgrade to 3.5.0 2023-01-26 23:15:02 +01:00
J-Jamet
e6ab8f82ff fix: url to download driver 2023-01-26 23:01:54 +01:00
J-Jamet
77ff1850f3 fix: url to download driver 2023-01-26 23:01:26 +01:00
J-Jamet
e985bd2a20 fix: dialog to download driver 2023-01-26 22:29:01 +01:00
J-Jamet
a6cd02d146 feat: new year 2023-01-22 21:30:37 +01:00
J-Jamet
eb51f6712b fix: changelog length 2023-01-22 21:12:43 +01:00
15 changed files with 199 additions and 114 deletions

View File

@@ -1,3 +1,6 @@
KeePassDX(3.5.1)
* Fix action dialog with YubiKey challenge-response #1506
KeePassDX(3.5.0) KeePassDX(3.5.0)
* Support YubiKey challenge-response #8 #137 * Support YubiKey challenge-response #8 #137
* Better exception management during database save #1346 * Better exception management during database save #1346

View File

@@ -74,7 +74,7 @@ Other questions? You can read the [FAQ](https://github.com/Kunzisoft/KeePassDX/w
## License ## License
Copyright © 2022 Jeremy Jamet / [Kunzisoft](https://www.kunzisoft.com). Copyright © 2023 Jeremy Jamet / [Kunzisoft](https://www.kunzisoft.com).
This file is part of KeePassDX. This file is part of KeePassDX.

View File

@@ -12,8 +12,8 @@ android {
applicationId "com.kunzisoft.keepass" applicationId "com.kunzisoft.keepass"
minSdkVersion 15 minSdkVersion 15
targetSdkVersion 32 targetSdkVersion 32
versionCode = 117 versionCode = 119
versionName = "3.5.0Beta03" versionName = "3.5.1"
multiDexEnabled true multiDexEnabled true
testApplicationId = "com.kunzisoft.keepass.tests" testApplicationId = "com.kunzisoft.keepass.tests"

View File

@@ -157,7 +157,8 @@
<activity <activity
android:name="com.kunzisoft.keepass.settings.AutofillSettingsActivity" /> android:name="com.kunzisoft.keepass.settings.AutofillSettingsActivity" />
<activity <activity
android:name="com.kunzisoft.keepass.hardware.HardwareKeyActivity" /> android:name="com.kunzisoft.keepass.hardware.HardwareKeyActivity"
android:theme="@style/Theme.Transparent" />
<activity <activity
android:name="com.kunzisoft.keepass.activities.EntrySelectionLauncherActivity" android:name="com.kunzisoft.keepass.activities.EntrySelectionLauncherActivity"
android:theme="@style/Theme.Transparent" android:theme="@style/Theme.Transparent"

View File

@@ -20,7 +20,7 @@ abstract class DatabaseActivity: StylishActivity(), DatabaseRetrieval {
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
mDatabaseTaskProvider = DatabaseTaskProvider(this) mDatabaseTaskProvider = DatabaseTaskProvider(this, showDatabaseDialog())
mDatabaseTaskProvider?.onDatabaseRetrieved = { database -> mDatabaseTaskProvider?.onDatabaseRetrieved = { database ->
val databaseWasReloaded = database?.wasReloaded == true val databaseWasReloaded = database?.wasReloaded == true
@@ -36,6 +36,10 @@ abstract class DatabaseActivity: StylishActivity(), DatabaseRetrieval {
} }
} }
protected open fun showDatabaseDialog(): Boolean {
return true
}
override fun onDestroy() { override fun onDestroy() {
mDatabaseTaskProvider?.destroy() mDatabaseTaskProvider?.destroy()
mDatabaseTaskProvider = null mDatabaseTaskProvider = null

View File

@@ -90,7 +90,8 @@ import java.util.*
* Utility class to connect an activity or a service to the DatabaseTaskNotificationService, * Utility class to connect an activity or a service to the DatabaseTaskNotificationService,
* Useful to retrieve a database instance and sending tasks commands * Useful to retrieve a database instance and sending tasks commands
*/ */
class DatabaseTaskProvider(private var context: Context) { class DatabaseTaskProvider(private var context: Context,
private var showDialog: Boolean = true) {
// To show dialog only if context is an activity // To show dialog only if context is an activity
private var activity: FragmentActivity? = try { context as? FragmentActivity? } private var activity: FragmentActivity? = try { context as? FragmentActivity? }
@@ -127,23 +128,29 @@ class DatabaseTaskProvider(private var context: Context) {
} }
private val actionTaskListener = object: DatabaseTaskNotificationService.ActionTaskListener { private val actionTaskListener = object: DatabaseTaskNotificationService.ActionTaskListener {
override fun onStartAction(database: Database, override fun onActionStarted(database: Database,
progressMessage: ProgressMessage) { progressMessage: ProgressMessage) {
startDialog(progressMessage) if (showDialog)
startDialog(progressMessage)
} }
override fun onUpdateAction(database: Database, override fun onActionUpdated(database: Database,
progressMessage: ProgressMessage) { progressMessage: ProgressMessage) {
updateDialog(progressMessage) if (showDialog)
updateDialog(progressMessage)
} }
override fun onStopAction(database: Database, override fun onActionStopped(database: Database) {
actionTask: String,
result: ActionRunnable.Result) {
onActionFinish?.invoke(database, actionTask, result)
// Remove the progress task // Remove the progress task
stopDialog() stopDialog()
} }
override fun onActionFinished(database: Database,
actionTask: String,
result: ActionRunnable.Result) {
onActionFinish?.invoke(database, actionTask, result)
onActionStopped(database)
}
} }
private val mActionDatabaseListener = object: DatabaseChangedDialogFragment.ActionDatabaseChangedListener { private val mActionDatabaseListener = object: DatabaseChangedDialogFragment.ActionDatabaseChangedListener {
@@ -222,6 +229,16 @@ class DatabaseTaskProvider(private var context: Context) {
private fun initServiceConnection() { private fun initServiceConnection() {
if (serviceConnection == null) { if (serviceConnection == null) {
serviceConnection = object : ServiceConnection { serviceConnection = object : ServiceConnection {
override fun onBindingDied(name: ComponentName?) {
stopDialog()
super.onBindingDied(name)
}
override fun onNullBinding(name: ComponentName?) {
stopDialog()
super.onNullBinding(name)
}
override fun onServiceConnected(name: ComponentName?, serviceBinder: IBinder?) { override fun onServiceConnected(name: ComponentName?, serviceBinder: IBinder?) {
mBinder = (serviceBinder as DatabaseTaskNotificationService.ActionTaskBinder?)?.apply { mBinder = (serviceBinder as DatabaseTaskNotificationService.ActionTaskBinder?)?.apply {
addServiceListeners(this) addServiceListeners(this)
@@ -269,8 +286,6 @@ class DatabaseTaskProvider(private var context: Context) {
} }
fun registerProgressTask() { fun registerProgressTask() {
stopDialog()
// Register a database task receiver to stop loading dialog when service finish the task // Register a database task receiver to stop loading dialog when service finish the task
databaseTaskBroadcastReceiver = object : BroadcastReceiver() { databaseTaskBroadcastReceiver = object : BroadcastReceiver() {
override fun onReceive(context: Context?, intent: Intent?) { override fun onReceive(context: Context?, intent: Intent?) {
@@ -281,7 +296,6 @@ class DatabaseTaskProvider(private var context: Context) {
} }
DATABASE_STOP_TASK_ACTION -> { DATABASE_STOP_TASK_ACTION -> {
// Remove the progress task // Remove the progress task
stopDialog()
unBindService() unBindService()
} }
} }
@@ -299,8 +313,6 @@ class DatabaseTaskProvider(private var context: Context) {
} }
fun unregisterProgressTask() { fun unregisterProgressTask() {
stopDialog()
removeServiceListeners(mBinder) removeServiceListeners(mBinder)
mBinder = null mBinder = null

View File

@@ -2,6 +2,7 @@ package com.kunzisoft.keepass.hardware
import android.app.Activity import android.app.Activity
import android.content.Context import android.content.Context
import android.content.DialogInterface
import android.content.Intent import android.content.Intent
import android.content.Intent.FLAG_ACTIVITY_NEW_TASK import android.content.Intent.FLAG_ACTIVITY_NEW_TASK
import android.util.Log import android.util.Log
@@ -10,6 +11,7 @@ import androidx.activity.result.ActivityResultCallback
import androidx.activity.result.ActivityResultLauncher import androidx.activity.result.ActivityResultLauncher
import androidx.activity.result.contract.ActivityResultContracts import androidx.activity.result.contract.ActivityResultContracts
import androidx.appcompat.app.AlertDialog import androidx.appcompat.app.AlertDialog
import com.kunzisoft.keepass.BuildConfig
import com.kunzisoft.keepass.R import com.kunzisoft.keepass.R
import com.kunzisoft.keepass.activities.legacy.DatabaseModeActivity import com.kunzisoft.keepass.activities.legacy.DatabaseModeActivity
import com.kunzisoft.keepass.database.element.Database import com.kunzisoft.keepass.database.element.Database
@@ -39,13 +41,23 @@ class HardwareKeyActivity: DatabaseModeActivity(){
resultCallback resultCallback
) )
override fun applyCustomStyle(): Boolean {
return false
}
override fun showDatabaseDialog(): Boolean {
return false
}
override fun onDatabaseRetrieved(database: Database?) { override fun onDatabaseRetrieved(database: Database?) {
super.onDatabaseRetrieved(database) super.onDatabaseRetrieved(database)
val hardwareKey = HardwareKey.getHardwareKeyFromString( val hardwareKey = HardwareKey.getHardwareKeyFromString(
intent.getStringExtra(DATA_HARDWARE_KEY) intent.getStringExtra(DATA_HARDWARE_KEY)
) )
if (isHardwareKeyAvailable(this, hardwareKey)) { if (isHardwareKeyAvailable(this, hardwareKey, true) {
mDatabaseTaskProvider?.startChallengeResponded(ByteArray(0))
}) {
when (hardwareKey) { when (hardwareKey) {
/* /*
HardwareKey.FIDO2_SECRET -> { HardwareKey.FIDO2_SECRET -> {
@@ -60,8 +72,6 @@ class HardwareKeyActivity: DatabaseModeActivity(){
finish() finish()
} }
} }
} else {
finish()
} }
} }
@@ -106,7 +116,8 @@ class HardwareKeyActivity: DatabaseModeActivity(){
fun isHardwareKeyAvailable( fun isHardwareKeyAvailable(
context: Context, context: Context,
hardwareKey: HardwareKey?, hardwareKey: HardwareKey?,
showDialog: Boolean = true showDialog: Boolean = true,
onDialogDismissed: DialogInterface.OnDismissListener? = null
): Boolean { ): Boolean {
if (hardwareKey == null) if (hardwareKey == null)
return false return false
@@ -125,8 +136,12 @@ class HardwareKeyActivity: DatabaseModeActivity(){
val yubikeyDriverAvailable = val yubikeyDriverAvailable =
Intent(YUBIKEY_CHALLENGE_RESPONSE_INTENT) Intent(YUBIKEY_CHALLENGE_RESPONSE_INTENT)
.resolveActivity(context.packageManager) != null .resolveActivity(context.packageManager) != null
if (showDialog && !yubikeyDriverAvailable) if (showDialog && !yubikeyDriverAvailable
showHardwareKeyDriverNeeded(context, hardwareKey) && context is Activity)
showHardwareKeyDriverNeeded(context, hardwareKey) {
onDialogDismissed?.onDismiss(it)
context.finish()
}
yubikeyDriverAvailable yubikeyDriverAvailable
} }
} }
@@ -134,7 +149,8 @@ class HardwareKeyActivity: DatabaseModeActivity(){
private fun showHardwareKeyDriverNeeded( private fun showHardwareKeyDriverNeeded(
context: Context, context: Context,
hardwareKey: HardwareKey hardwareKey: HardwareKey,
onDialogDismissed: DialogInterface.OnDismissListener
) { ) {
val builder = AlertDialog.Builder(context) val builder = AlertDialog.Builder(context)
builder builder
@@ -142,9 +158,14 @@ class HardwareKeyActivity: DatabaseModeActivity(){
context.getString(R.string.error_driver_required, hardwareKey.toString()) context.getString(R.string.error_driver_required, hardwareKey.toString())
) )
.setPositiveButton(R.string.download) { _, _ -> .setPositiveButton(R.string.download) { _, _ ->
UriUtil.openExternalApp(context, context.getString(R.string.key_driver_app_id)) UriUtil.openExternalApp(
context,
context.getString(R.string.key_driver_app_id),
context.getString(R.string.key_driver_url)
)
} }
.setNegativeButton(android.R.string.cancel) { _, _ -> } .setNegativeButton(android.R.string.cancel) { _, _ -> }
.setOnDismissListener(onDialogDismissed)
builder.create().show() builder.create().show()
} }
} }

View File

@@ -79,7 +79,7 @@ open class DatabaseTaskNotificationService : LockNotificationService(), Progress
// Channel to connect asynchronously a response // Channel to connect asynchronously a response
private var mResponseChallengeChannel: Channel<ByteArray?>? = null private var mResponseChallengeChannel: Channel<ByteArray?>? = null
private var mActionRunning = false private var mActionRunning = 0
private var mTaskRemovedRequested = false private var mTaskRemovedRequested = false
private var mSaveState = false private var mSaveState = false
@@ -135,20 +135,14 @@ open class DatabaseTaskNotificationService : LockNotificationService(), Progress
} }
interface ActionTaskListener { interface ActionTaskListener {
fun onStartAction(database: Database, fun onActionStarted(database: Database,
progressMessage: ProgressMessage) progressMessage: ProgressMessage)
fun onUpdateAction(database: Database, fun onActionUpdated(database: Database,
progressMessage: ProgressMessage) progressMessage: ProgressMessage)
fun onStopAction(database: Database, fun onActionStopped(database: Database)
actionTask: String, fun onActionFinished(database: Database,
result: ActionRunnable.Result) actionTask: String,
} result: ActionRunnable.Result)
interface RequestChallengeListener {
fun onChallengeResponseRequested(
hardwareKey: HardwareKey,
seed: ByteArray?
)
} }
fun checkDatabase() { fun checkDatabase() {
@@ -211,16 +205,24 @@ open class DatabaseTaskNotificationService : LockNotificationService(), Progress
} }
/** /**
* Force to call [ActionTaskListener.onStartAction] if the action is still running * Force to call [ActionTaskListener.onActionStarted] if the action is still running
* or [ActionTaskListener.onActionStopped] if the action is no longer running
*/ */
fun checkAction() { fun checkAction() {
mDatabase?.let { database -> mDatabase?.let { database ->
if (mActionRunning) { // Check if action / sub-action is running
if (mActionRunning > 0) {
mActionTaskListeners.forEach { actionTaskListener -> mActionTaskListeners.forEach { actionTaskListener ->
actionTaskListener.onStartAction( actionTaskListener.onActionStarted(
database, mProgressMessage database, mProgressMessage
) )
} }
} else {
mActionTaskListeners.forEach { actionTaskListener ->
actionTaskListener.onActionStopped(
database
)
}
} }
} }
} }
@@ -330,81 +332,104 @@ open class DatabaseTaskNotificationService : LockNotificationService(), Progress
else -> null else -> null
} }
// Sub action is an action in another action, don't perform pre and post action
val isMainAction = intentAction != ACTION_CHALLENGE_RESPONDED
// Build and launch the action // Build and launch the action
if (actionRunnable != null) { if (actionRunnable != null) {
mainScope.launch { mainScope.launch {
executeAction(this@DatabaseTaskNotificationService, executeAction(this@DatabaseTaskNotificationService,
{ {
TimeoutHelper.temporarilyDisableTimeout() mActionRunning++
if (isMainAction) {
TimeoutHelper.temporarilyDisableTimeout()
mActionRunning = true sendBroadcast(Intent(DATABASE_START_TASK_ACTION).apply {
putExtra(DATABASE_TASK_TITLE_KEY, mProgressMessage.titleId)
putExtra(DATABASE_TASK_MESSAGE_KEY, mProgressMessage.messageId)
putExtra(DATABASE_TASK_WARNING_KEY, mProgressMessage.warningId)
})
sendBroadcast(Intent(DATABASE_START_TASK_ACTION).apply { mActionTaskListeners.forEach { actionTaskListener ->
putExtra(DATABASE_TASK_TITLE_KEY, mProgressMessage.titleId) actionTaskListener.onActionStarted(
putExtra(DATABASE_TASK_MESSAGE_KEY, mProgressMessage.messageId) database,
putExtra(DATABASE_TASK_WARNING_KEY, mProgressMessage.warningId) mProgressMessage
}) )
}
mActionTaskListeners.forEach { actionTaskListener ->
actionTaskListener.onStartAction(
database, mProgressMessage
)
} }
}, },
{ {
actionRunnable actionRunnable
}, },
{ result -> { result ->
try { if (isMainAction) {
mActionTaskListeners.forEach { actionTaskListener -> try {
mTaskRemovedRequested = false mActionTaskListeners.forEach { actionTaskListener ->
actionTaskListener.onStopAction(database, intentAction!!, result) mTaskRemovedRequested = false
} actionTaskListener.onActionFinished(
} finally { database,
// Save the database info before performing action intentAction!!,
when (intentAction) { result
ACTION_DATABASE_LOAD_TASK, )
ACTION_DATABASE_MERGE_TASK,
ACTION_DATABASE_RELOAD_TASK -> {
saveDatabaseInfo()
} }
} } finally {
val save = !database.isReadOnly // Save the database info before performing action
&& (intentAction == ACTION_DATABASE_SAVE when (intentAction) {
|| intent?.getBooleanExtra(SAVE_DATABASE_KEY, false) == true) ACTION_DATABASE_LOAD_TASK,
// Save the database info after performing save action ACTION_DATABASE_MERGE_TASK,
if (save) { ACTION_DATABASE_RELOAD_TASK -> {
database.fileUri?.let { saveDatabaseInfo()
val newSnapFileDatabaseInfo = SnapFileDatabaseInfo.fromFileDatabaseInfo(
FileDatabaseInfo(applicationContext, it))
mLastLocalSaveTime = System.currentTimeMillis()
mSnapFileDatabaseInfo = newSnapFileDatabaseInfo
}
}
removeIntentData(intent)
TimeoutHelper.releaseTemporarilyDisableTimeout()
// Stop service after save if user remove task
if (save && mTaskRemovedRequested) {
actionOnLock()
} else if (TimeoutHelper.checkTimeAndLockIfTimeout(this@DatabaseTaskNotificationService)) {
if (!database.loaded) {
stopSelf()
} else {
// Restart the service to open lock notification
try {
startService(Intent(applicationContext,
DatabaseTaskNotificationService::class.java))
} catch (e: IllegalStateException) {
Log.w(TAG, "Cannot restart the database task service", e)
} }
} }
val save = !database.isReadOnly
&& (intentAction == ACTION_DATABASE_SAVE
|| intent?.getBooleanExtra(
SAVE_DATABASE_KEY,
false
) == true)
// Save the database info after performing save action
if (save) {
database.fileUri?.let {
val newSnapFileDatabaseInfo =
SnapFileDatabaseInfo.fromFileDatabaseInfo(
FileDatabaseInfo(applicationContext, it)
)
mLastLocalSaveTime = System.currentTimeMillis()
mSnapFileDatabaseInfo = newSnapFileDatabaseInfo
}
}
removeIntentData(intent)
TimeoutHelper.releaseTemporarilyDisableTimeout()
// Stop service after save if user remove task
if (save && mTaskRemovedRequested) {
actionOnLock()
} else if (TimeoutHelper.checkTimeAndLockIfTimeout(this@DatabaseTaskNotificationService)) {
if (!database.loaded) {
stopSelf()
} else {
// Restart the service to open lock notification
try {
startService(
Intent(
applicationContext,
DatabaseTaskNotificationService::class.java
)
)
} catch (e: IllegalStateException) {
Log.w(
TAG,
"Cannot restart the database task service",
e
)
}
}
}
mTaskRemovedRequested = false
} }
mTaskRemovedRequested = false
sendBroadcast(Intent(DATABASE_STOP_TASK_ACTION))
} }
mActionRunning--
sendBroadcast(Intent(DATABASE_STOP_TASK_ACTION))
mActionRunning = false
} }
) )
} }
@@ -577,7 +602,7 @@ open class DatabaseTaskNotificationService : LockNotificationService(), Progress
private fun notifyProgressMessage() { private fun notifyProgressMessage() {
mDatabase?.let { database -> mDatabase?.let { database ->
mActionTaskListeners.forEach { actionTaskListener -> mActionTaskListeners.forEach { actionTaskListener ->
actionTaskListener.onUpdateAction( actionTaskListener.onActionUpdated(
database, mProgressMessage database, mProgressMessage
) )
} }

View File

@@ -28,6 +28,7 @@ import android.os.Build
import android.util.Log import android.util.Log
import android.widget.Toast import android.widget.Toast
import androidx.documentfile.provider.DocumentFile import androidx.documentfile.provider.DocumentFile
import com.kunzisoft.keepass.BuildConfig
import com.kunzisoft.keepass.R import com.kunzisoft.keepass.R
import com.kunzisoft.keepass.app.database.FileDatabaseHistoryAction import com.kunzisoft.keepass.app.database.FileDatabaseHistoryAction
import com.kunzisoft.keepass.education.Education import com.kunzisoft.keepass.education.Education
@@ -289,21 +290,35 @@ object UriUtil {
return false return false
} }
fun openExternalApp(context: Context, packageName: String) { fun openExternalApp(context: Context, packageName: String, sourcesURL: String? = null) {
var launchIntent: Intent? = null var launchIntent: Intent? = null
try { try {
launchIntent = context.packageManager.getLaunchIntentForPackage(packageName)?.apply { launchIntent = context.packageManager.getLaunchIntentForPackage(packageName)?.apply {
addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
} }
} catch (ignored: Exception) { } catch (ignored: Exception) { }
}
try { try {
if (launchIntent == null) { if (launchIntent == null) {
// TODO F-Droid
context.startActivity( context.startActivity(
Intent(Intent.ACTION_VIEW) Intent(Intent.ACTION_VIEW)
.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
.setData(Uri.parse(context.getString(R.string.play_store_url, packageName))) .setData(
Uri.parse(
if (sourcesURL != null
&& !BuildConfig.CLOSED_STORE
) {
sourcesURL
} else {
context.getString(
if (BuildConfig.CLOSED_STORE)
R.string.play_store_url
else
R.string.f_droid_url,
packageName
)
}
)
)
) )
} else { } else {
context.startActivity(launchIntent) context.startActivity(launchIntent)

View File

@@ -31,8 +31,10 @@
<string name="app_name_part3" translatable="false">Pro</string> <string name="app_name_part3" translatable="false">Pro</string>
<string name="play_store_url" translatable="false">https://play.google.com/store/apps/details?id=%1$s</string> <string name="play_store_url" translatable="false">https://play.google.com/store/apps/details?id=%1$s</string>
<string name="f_droid_url" translatable="false">https://f-droid.org/en/packages/%1$s</string>
<string name="keepro_app_id" translatable="false">com.kunzisoft.keepass.pro</string> <string name="keepro_app_id" translatable="false">com.kunzisoft.keepass.pro</string>
<string name="key_driver_app_id" translatable="false">com.kunzisoft.hardware.key</string> <string name="key_driver_app_id" translatable="false">com.kunzisoft.hardware.key</string>
<string name="key_driver_url" translatable="false">https://gitlab.com/kunzisoft/android-hardware-key-driver/-/releases/</string>
<string name="contribution_url" translatable="false">https://www.keepassdx.com/#donation</string> <string name="contribution_url" translatable="false">https://www.keepassdx.com/#donation</string>
<string name="homepage_url" translatable="false">https://www.keepassdx.com</string> <string name="homepage_url" translatable="false">https://www.keepassdx.com</string>
<string name="external_icon_url" translatable="false">https://www.keepassdx.com/#icons</string> <string name="external_icon_url" translatable="false">https://www.keepassdx.com/#icons</string>

View File

@@ -565,7 +565,7 @@
</style> </style>
<!-- Transparent --> <!-- Transparent -->
<style name="Theme.Transparent" parent="Theme.MaterialComponents"> <style name="Theme.Transparent" parent="Theme.Material3.DynamicColors.Dark">
<item name="android:windowAnimationStyle">@null</item> <item name="android:windowAnimationStyle">@null</item>
<item name="android:windowIsTranslucent">true</item> <item name="android:windowIsTranslucent">true</item>
<item name="android:windowBackground">@android:color/transparent</item> <item name="android:windowBackground">@android:color/transparent</item>

View File

@@ -0,0 +1 @@
* Fix action dialog with YubiKey challenge-response #1506

View File

@@ -1,8 +1,8 @@
* Support du YubiKey challenge-response #8 #137 * Support du YubiKey challenge-response #8 #137
* Meilleure gestion des exceptions durant une sauvegarde #1346 * Meilleure gestion des exceptions de sauvegarde #1346
* Ajout du "Mode Capture d'Ecran" dans les paramètres #459 #1377 #1354 (Thx @GianpaMX) * Ajout du "Mode Capture d'Ecran" dans les paramètres #459 #1377 #1354 (Thx @GianpaMX)
* Masquage du texte sensible dans le presse-papiers #1386 * Masquage du texte sensible du presse-papiers #1386
* Correction du bouton des pièces jointes #1401 * Correction du bouton des pièces jointes #1401
* Ajout de l'icone monochrome #1403 #1404 (Thx @Sandelinos) * Ajout de l'icone monochrome #1403 #1404 (Thx @Sandelinos)
* Correction du verrouillage avec le bouton arrière #1412 #1414 (Thx @ryg-git) * Correction du verrouillage avec bouton arrière #1412 #1414 (Thx @ryg-git)
* Compatibilité de Vanadium #1447 (Thx @flawedworld) * Compatibilité de Vanadium #1447 (Thx @flawedworld)

View File

@@ -0,0 +1 @@
* Correction de la fenêtre d'action pour la Yubikey #1506