mirror of
https://github.com/Kunzisoft/KeePassDX.git
synced 2025-12-04 15:49:33 +01:00
Show a dialog when a database file info changes #794
This commit is contained in:
@@ -0,0 +1,92 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2020 Jeremy Jamet / Kunzisoft.
|
||||||
|
*
|
||||||
|
* This file is part of KeePassDX.
|
||||||
|
*
|
||||||
|
* KeePassDX is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* KeePassDX is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with KeePassDX. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
package com.kunzisoft.keepass.activities.dialogs
|
||||||
|
|
||||||
|
import android.app.Dialog
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.text.SpannableStringBuilder
|
||||||
|
import androidx.appcompat.app.AlertDialog
|
||||||
|
import androidx.fragment.app.DialogFragment
|
||||||
|
import com.kunzisoft.keepass.R
|
||||||
|
import com.kunzisoft.keepass.model.SnapFileDatabaseInfo
|
||||||
|
|
||||||
|
|
||||||
|
class DatabaseChangedDialogFragment : DialogFragment() {
|
||||||
|
|
||||||
|
var actionDatabaseListener: ActionDatabaseChangedListener? = null
|
||||||
|
|
||||||
|
override fun onPause() {
|
||||||
|
super.onPause()
|
||||||
|
actionDatabaseListener = null
|
||||||
|
this.dismiss()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
|
||||||
|
activity?.let { activity ->
|
||||||
|
|
||||||
|
val oldSnapFileDatabaseInfo: SnapFileDatabaseInfo? = arguments?.getParcelable(OLD_FILE_DATABASE_INFO)
|
||||||
|
val newSnapFileDatabaseInfo: SnapFileDatabaseInfo? = arguments?.getParcelable(NEW_FILE_DATABASE_INFO)
|
||||||
|
|
||||||
|
if (oldSnapFileDatabaseInfo != null && newSnapFileDatabaseInfo != null) {
|
||||||
|
// Use the Builder class for convenient dialog construction
|
||||||
|
val builder = AlertDialog.Builder(activity)
|
||||||
|
|
||||||
|
val stringBuilder = SpannableStringBuilder()
|
||||||
|
if (newSnapFileDatabaseInfo.exists) {
|
||||||
|
stringBuilder.append(getString(R.string.warning_database_info_changed))
|
||||||
|
stringBuilder.append("\n\n" +oldSnapFileDatabaseInfo.toString(activity)
|
||||||
|
+ "\n→\n" +
|
||||||
|
newSnapFileDatabaseInfo.toString(activity) + "\n\n")
|
||||||
|
stringBuilder.append(getString(R.string.warning_database_info_changed_options))
|
||||||
|
} else {
|
||||||
|
stringBuilder.append(getString(R.string.warning_database_revoked))
|
||||||
|
}
|
||||||
|
builder.setMessage(stringBuilder)
|
||||||
|
builder.setPositiveButton(android.R.string.ok) { _, _ ->
|
||||||
|
actionDatabaseListener?.validateDatabaseChanged()
|
||||||
|
}
|
||||||
|
return builder.create()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return super.onCreateDialog(savedInstanceState)
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ActionDatabaseChangedListener {
|
||||||
|
fun validateDatabaseChanged()
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
|
||||||
|
const val DATABASE_CHANGED_DIALOG_TAG = "databaseChangedDialogFragment"
|
||||||
|
private const val OLD_FILE_DATABASE_INFO = "OLD_FILE_DATABASE_INFO"
|
||||||
|
private const val NEW_FILE_DATABASE_INFO = "NEW_FILE_DATABASE_INFO"
|
||||||
|
|
||||||
|
fun getInstance(oldSnapFileDatabaseInfo: SnapFileDatabaseInfo,
|
||||||
|
newSnapFileDatabaseInfo: SnapFileDatabaseInfo)
|
||||||
|
: DatabaseChangedDialogFragment {
|
||||||
|
val fragment = DatabaseChangedDialogFragment()
|
||||||
|
fragment.arguments = Bundle().apply {
|
||||||
|
putParcelable(OLD_FILE_DATABASE_INFO, oldSnapFileDatabaseInfo)
|
||||||
|
putParcelable(NEW_FILE_DATABASE_INFO, newSnapFileDatabaseInfo)
|
||||||
|
}
|
||||||
|
return fragment
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -25,8 +25,9 @@ import android.content.Context.BIND_NOT_FOREGROUND
|
|||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.os.IBinder
|
import android.os.IBinder
|
||||||
import android.widget.Toast
|
|
||||||
import androidx.fragment.app.FragmentActivity
|
import androidx.fragment.app.FragmentActivity
|
||||||
|
import com.kunzisoft.keepass.activities.dialogs.DatabaseChangedDialogFragment
|
||||||
|
import com.kunzisoft.keepass.activities.dialogs.DatabaseChangedDialogFragment.Companion.DATABASE_CHANGED_DIALOG_TAG
|
||||||
import com.kunzisoft.keepass.app.database.CipherDatabaseEntity
|
import com.kunzisoft.keepass.app.database.CipherDatabaseEntity
|
||||||
import com.kunzisoft.keepass.crypto.keyDerivation.KdfEngine
|
import com.kunzisoft.keepass.crypto.keyDerivation.KdfEngine
|
||||||
import com.kunzisoft.keepass.database.element.Entry
|
import com.kunzisoft.keepass.database.element.Entry
|
||||||
@@ -36,6 +37,7 @@ import com.kunzisoft.keepass.database.element.node.Node
|
|||||||
import com.kunzisoft.keepass.database.element.node.NodeId
|
import com.kunzisoft.keepass.database.element.node.NodeId
|
||||||
import com.kunzisoft.keepass.database.element.node.Type
|
import com.kunzisoft.keepass.database.element.node.Type
|
||||||
import com.kunzisoft.keepass.database.element.security.EncryptionAlgorithm
|
import com.kunzisoft.keepass.database.element.security.EncryptionAlgorithm
|
||||||
|
import com.kunzisoft.keepass.model.SnapFileDatabaseInfo
|
||||||
import com.kunzisoft.keepass.notifications.DatabaseTaskNotificationService
|
import com.kunzisoft.keepass.notifications.DatabaseTaskNotificationService
|
||||||
import com.kunzisoft.keepass.notifications.DatabaseTaskNotificationService.Companion.ACTION_DATABASE_ASSIGN_PASSWORD_TASK
|
import com.kunzisoft.keepass.notifications.DatabaseTaskNotificationService.Companion.ACTION_DATABASE_ASSIGN_PASSWORD_TASK
|
||||||
import com.kunzisoft.keepass.notifications.DatabaseTaskNotificationService.Companion.ACTION_DATABASE_COPY_NODES_TASK
|
import com.kunzisoft.keepass.notifications.DatabaseTaskNotificationService.Companion.ACTION_DATABASE_COPY_NODES_TASK
|
||||||
@@ -85,6 +87,7 @@ class ProgressDatabaseTaskProvider(private val activity: FragmentActivity) {
|
|||||||
private var serviceConnection: ServiceConnection? = null
|
private var serviceConnection: ServiceConnection? = null
|
||||||
|
|
||||||
private var progressTaskDialogFragment: ProgressTaskDialogFragment? = null
|
private var progressTaskDialogFragment: ProgressTaskDialogFragment? = null
|
||||||
|
private var databaseChangedDialogFragment: DatabaseChangedDialogFragment? = null
|
||||||
|
|
||||||
private val actionTaskListener = object: DatabaseTaskNotificationService.ActionTaskListener {
|
private val actionTaskListener = object: DatabaseTaskNotificationService.ActionTaskListener {
|
||||||
override fun onStartAction(titleId: Int?, messageId: Int?, warningId: Int?) {
|
override fun onStartAction(titleId: Int?, messageId: Int?, warningId: Int?) {
|
||||||
@@ -102,11 +105,24 @@ class ProgressDatabaseTaskProvider(private val activity: FragmentActivity) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private val mActionDatabaseListener = object: DatabaseChangedDialogFragment.ActionDatabaseChangedListener {
|
||||||
|
override fun validateDatabaseChanged() {
|
||||||
|
mBinder?.getService()?.saveDatabaseInfo()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private var databaseInfoListener = object: DatabaseTaskNotificationService.DatabaseInfoListener {
|
private var databaseInfoListener = object: DatabaseTaskNotificationService.DatabaseInfoListener {
|
||||||
override fun onDatabaseInfoChanged(previousDatabaseInfo: DatabaseTaskNotificationService.SnapFileDatabaseInfo,
|
override fun onDatabaseInfoChanged(previousDatabaseInfo: SnapFileDatabaseInfo,
|
||||||
newDatabaseInfo: DatabaseTaskNotificationService.SnapFileDatabaseInfo) {
|
newDatabaseInfo: SnapFileDatabaseInfo) {
|
||||||
activity.runOnUiThread {
|
if (databaseChangedDialogFragment == null) {
|
||||||
Toast.makeText(activity, "Database changed $previousDatabaseInfo to $newDatabaseInfo", Toast.LENGTH_SHORT).show()
|
databaseChangedDialogFragment = activity.supportFragmentManager
|
||||||
|
.findFragmentByTag(DATABASE_CHANGED_DIALOG_TAG) as DatabaseChangedDialogFragment?
|
||||||
|
databaseChangedDialogFragment?.actionDatabaseListener = mActionDatabaseListener
|
||||||
|
}
|
||||||
|
if (progressTaskDialogFragment == null) {
|
||||||
|
databaseChangedDialogFragment = DatabaseChangedDialogFragment.getInstance(previousDatabaseInfo, newDatabaseInfo)
|
||||||
|
databaseChangedDialogFragment?.actionDatabaseListener = mActionDatabaseListener
|
||||||
|
databaseChangedDialogFragment?.show(activity.supportFragmentManager, DATABASE_CHANGED_DIALOG_TAG)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -219,6 +235,7 @@ class ProgressDatabaseTaskProvider(private val activity: FragmentActivity) {
|
|||||||
fun unregisterProgressTask() {
|
fun unregisterProgressTask() {
|
||||||
stopDialog()
|
stopDialog()
|
||||||
|
|
||||||
|
mBinder?.removeDatabaseFileInfoListener(databaseInfoListener)
|
||||||
mBinder?.removeActionTaskListener(actionTaskListener)
|
mBinder?.removeActionTaskListener(actionTaskListener)
|
||||||
mBinder = null
|
mBinder = null
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,62 @@
|
|||||||
|
package com.kunzisoft.keepass.model
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.net.Uri
|
||||||
|
import android.os.Parcel
|
||||||
|
import android.os.Parcelable
|
||||||
|
import android.text.format.Formatter
|
||||||
|
import com.kunzisoft.keepass.viewmodels.FileDatabaseInfo
|
||||||
|
import java.text.DateFormat
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Utility data class to get FileDatabaseInfo at a `t` time
|
||||||
|
*/
|
||||||
|
data class SnapFileDatabaseInfo(var fileUri: Uri?,
|
||||||
|
var exists: Boolean,
|
||||||
|
var lastModification: Long?,
|
||||||
|
var size: Long?): Parcelable {
|
||||||
|
|
||||||
|
constructor(parcel: Parcel) : this(
|
||||||
|
parcel.readParcelable(Uri::class.java.classLoader),
|
||||||
|
parcel.readByte() != 0.toByte(),
|
||||||
|
parcel.readValue(Long::class.java.classLoader) as? Long,
|
||||||
|
parcel.readValue(Long::class.java.classLoader) as? Long) {
|
||||||
|
}
|
||||||
|
|
||||||
|
fun toString(context: Context): String {
|
||||||
|
val lastModificationString = DateFormat.getDateTimeInstance()
|
||||||
|
.format(Date(lastModification ?: 0))
|
||||||
|
return "$lastModificationString, " +
|
||||||
|
Formatter.formatFileSize(context, size ?: 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun writeToParcel(parcel: Parcel, flags: Int) {
|
||||||
|
parcel.writeParcelable(fileUri, flags)
|
||||||
|
parcel.writeByte(if (exists) 1 else 0)
|
||||||
|
parcel.writeValue(lastModification)
|
||||||
|
parcel.writeValue(size)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun describeContents(): Int {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object CREATOR : Parcelable.Creator<SnapFileDatabaseInfo> {
|
||||||
|
override fun createFromParcel(parcel: Parcel): SnapFileDatabaseInfo {
|
||||||
|
return SnapFileDatabaseInfo(parcel)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun newArray(size: Int): Array<SnapFileDatabaseInfo?> {
|
||||||
|
return arrayOfNulls(size)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun fromFileDatabaseInfo(fileDatabaseInfo: FileDatabaseInfo): SnapFileDatabaseInfo {
|
||||||
|
return SnapFileDatabaseInfo(
|
||||||
|
fileDatabaseInfo.fileUri,
|
||||||
|
fileDatabaseInfo.exists,
|
||||||
|
fileDatabaseInfo.getLastModification(),
|
||||||
|
fileDatabaseInfo.getSize())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -39,6 +39,7 @@ import com.kunzisoft.keepass.database.element.database.CompressionAlgorithm
|
|||||||
import com.kunzisoft.keepass.database.element.node.Node
|
import com.kunzisoft.keepass.database.element.node.Node
|
||||||
import com.kunzisoft.keepass.database.element.node.NodeId
|
import com.kunzisoft.keepass.database.element.node.NodeId
|
||||||
import com.kunzisoft.keepass.database.element.node.Type
|
import com.kunzisoft.keepass.database.element.node.Type
|
||||||
|
import com.kunzisoft.keepass.model.SnapFileDatabaseInfo
|
||||||
import com.kunzisoft.keepass.tasks.ActionRunnable
|
import com.kunzisoft.keepass.tasks.ActionRunnable
|
||||||
import com.kunzisoft.keepass.tasks.ProgressTaskUpdater
|
import com.kunzisoft.keepass.tasks.ProgressTaskUpdater
|
||||||
import com.kunzisoft.keepass.timeout.TimeoutHelper
|
import com.kunzisoft.keepass.timeout.TimeoutHelper
|
||||||
@@ -48,7 +49,6 @@ import com.kunzisoft.keepass.utils.LOCK_ACTION
|
|||||||
import com.kunzisoft.keepass.utils.closeDatabase
|
import com.kunzisoft.keepass.utils.closeDatabase
|
||||||
import com.kunzisoft.keepass.viewmodels.FileDatabaseInfo
|
import com.kunzisoft.keepass.viewmodels.FileDatabaseInfo
|
||||||
import kotlinx.coroutines.*
|
import kotlinx.coroutines.*
|
||||||
import java.text.DateFormat
|
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import java.util.concurrent.atomic.AtomicBoolean
|
import java.util.concurrent.atomic.AtomicBoolean
|
||||||
import kotlin.collections.ArrayList
|
import kotlin.collections.ArrayList
|
||||||
@@ -107,34 +107,6 @@ open class DatabaseTaskNotificationService : LockNotificationService(), Progress
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Utility data class to get FileDatabaseInfo at a `t` time
|
|
||||||
*/
|
|
||||||
data class SnapFileDatabaseInfo(var fileUri: Uri?,
|
|
||||||
var exists: Boolean,
|
|
||||||
var lastModification: Long?,
|
|
||||||
var size: Long?) {
|
|
||||||
|
|
||||||
override fun toString(): String {
|
|
||||||
val lastModificationString = DateFormat.getDateTimeInstance()
|
|
||||||
.format(Date(lastModification ?: 0))
|
|
||||||
return "SnapFileDatabaseInfo(fileUri=${fileUri?.host}, " +
|
|
||||||
"exists=$exists, " +
|
|
||||||
"lastModification=$lastModificationString, " +
|
|
||||||
"size=$size)"
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
fun fromFileDatabaseInfo(fileDatabaseInfo: FileDatabaseInfo): SnapFileDatabaseInfo {
|
|
||||||
return SnapFileDatabaseInfo(
|
|
||||||
fileDatabaseInfo.fileUri,
|
|
||||||
fileDatabaseInfo.exists,
|
|
||||||
fileDatabaseInfo.getLastModification(),
|
|
||||||
fileDatabaseInfo.getSize())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
interface ActionTaskListener {
|
interface ActionTaskListener {
|
||||||
fun onStartAction(titleId: Int?, messageId: Int?, warningId: Int?)
|
fun onStartAction(titleId: Int?, messageId: Int?, warningId: Int?)
|
||||||
fun onUpdateAction(titleId: Int?, messageId: Int?, warningId: Int?)
|
fun onUpdateAction(titleId: Int?, messageId: Int?, warningId: Int?)
|
||||||
@@ -178,6 +150,13 @@ open class DatabaseTaskNotificationService : LockNotificationService(), Progress
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun saveDatabaseInfo() {
|
||||||
|
mDatabase.fileUri?.let {
|
||||||
|
mSnapFileDatabaseInfo = SnapFileDatabaseInfo.fromFileDatabaseInfo(
|
||||||
|
FileDatabaseInfo(applicationContext, it))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
override fun onBind(intent: Intent): IBinder? {
|
override fun onBind(intent: Intent): IBinder? {
|
||||||
super.onBind(intent)
|
super.onBind(intent)
|
||||||
return mActionTaskBinder
|
return mActionTaskBinder
|
||||||
@@ -259,11 +238,8 @@ open class DatabaseTaskNotificationService : LockNotificationService(), Progress
|
|||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
removeIntentData(intent)
|
removeIntentData(intent)
|
||||||
// Save the current database info
|
// Save the database info after performing action
|
||||||
mDatabase.fileUri?.let {
|
saveDatabaseInfo()
|
||||||
mSnapFileDatabaseInfo = SnapFileDatabaseInfo.fromFileDatabaseInfo(
|
|
||||||
FileDatabaseInfo(applicationContext, it))
|
|
||||||
}
|
|
||||||
TimeoutHelper.releaseTemporarilyDisableTimeout()
|
TimeoutHelper.releaseTemporarilyDisableTimeout()
|
||||||
if (TimeoutHelper.checkTimeAndLockIfTimeout(this@DatabaseTaskNotificationService)) {
|
if (TimeoutHelper.checkTimeAndLockIfTimeout(this@DatabaseTaskNotificationService)) {
|
||||||
if (!mDatabase.loaded) {
|
if (!mDatabase.loaded) {
|
||||||
|
|||||||
@@ -272,6 +272,9 @@
|
|||||||
<string name="warning_sure_remove_data">Remove this data anyway?</string>
|
<string name="warning_sure_remove_data">Remove this data anyway?</string>
|
||||||
<string name="warning_empty_keyfile">It is not recommended to add an empty keyfile.</string>
|
<string name="warning_empty_keyfile">It is not recommended to add an empty keyfile.</string>
|
||||||
<string name="warning_empty_keyfile_explanation">The content of the keyfile should never be changed, and in the best case, should contain randomly generated data.</string>
|
<string name="warning_empty_keyfile_explanation">The content of the keyfile should never be changed, and in the best case, should contain randomly generated data.</string>
|
||||||
|
<string name="warning_database_info_changed">The information contained in your database file has been modified outside the app.</string>
|
||||||
|
<string name="warning_database_info_changed_options">Overwrite the external modifications by saving the database or close it and re-enter your main credentials to reload the information with the latest changes.</string>
|
||||||
|
<string name="warning_database_revoked">Access to the file revoked by the file manager, close the database and reopen it from its location.</string>
|
||||||
<string name="version_label">Version %1$s</string>
|
<string name="version_label">Version %1$s</string>
|
||||||
<string name="build_label">Build %1$s</string>
|
<string name="build_label">Build %1$s</string>
|
||||||
<string name="configure_biometric">No biometric or device credential is enrolled.</string>
|
<string name="configure_biometric">No biometric or device credential is enrolled.</string>
|
||||||
|
|||||||
Reference in New Issue
Block a user