mirror of
https://github.com/Kunzisoft/KeePassDX.git
synced 2025-12-04 15:49:33 +01:00
Compare commits
14 Commits
3.5.0beta0
...
3.5.1
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
428efd2bb6 | ||
|
|
512b4cd16e | ||
|
|
4d648b77b9 | ||
|
|
02f6caf4dd | ||
|
|
2b23649674 | ||
|
|
e7cce21c51 | ||
|
|
128ce5657e | ||
|
|
861911ad63 | ||
|
|
9093c65235 | ||
|
|
e6ab8f82ff | ||
|
|
77ff1850f3 | ||
|
|
e985bd2a20 | ||
|
|
a6cd02d146 | ||
|
|
eb51f6712b |
@@ -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
|
||||||
|
|||||||
@@ -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.
|
||||||
|
|
||||||
|
|||||||
@@ -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"
|
||||||
|
|||||||
@@ -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"
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|
||||||
|
|||||||
@@ -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()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
1
fastlane/metadata/android/en-US/changelogs/119.txt
Normal file
1
fastlane/metadata/android/en-US/changelogs/119.txt
Normal file
@@ -0,0 +1 @@
|
|||||||
|
* Fix action dialog with YubiKey challenge-response #1506
|
||||||
@@ -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)
|
||||||
1
fastlane/metadata/android/fr-FR/changelogs/119.txt
Normal file
1
fastlane/metadata/android/fr-FR/changelogs/119.txt
Normal file
@@ -0,0 +1 @@
|
|||||||
|
* Correction de la fenêtre d'action pour la Yubikey #1506
|
||||||
Reference in New Issue
Block a user