fix: Complete refactoring of database action

This commit is contained in:
J-Jamet
2025-10-08 15:53:41 +02:00
parent 51c62034df
commit 50b1ac388e
15 changed files with 904 additions and 607 deletions

View File

@@ -217,7 +217,6 @@ class FileDatabaseSelectActivity : DatabaseModeActivity(),
} }
override fun onDatabaseRetrieved(database: ContextualDatabase?) { override fun onDatabaseRetrieved(database: ContextualDatabase?) {
super.onDatabaseRetrieved(database)
if (database != null) { if (database != null) {
launchGroupActivityIfLoaded(database) launchGroupActivityIfLoaded(database)
} }
@@ -228,8 +227,6 @@ class FileDatabaseSelectActivity : DatabaseModeActivity(),
actionTask: String, actionTask: String,
result: ActionRunnable.Result result: ActionRunnable.Result
) { ) {
super.onDatabaseActionFinished(database, actionTask, result)
if (result.isSuccess) { if (result.isSuccess) {
// Update list // Update list
when (actionTask) { when (actionTask) {
@@ -392,7 +389,7 @@ class FileDatabaseSelectActivity : DatabaseModeActivity(),
try { try {
mDatabaseFileUri?.let { databaseUri -> mDatabaseFileUri?.let { databaseUri ->
// Create the new database // Create the new database
createDatabase(databaseUri, mainCredential) mDatabaseViewModel.createDatabase(databaseUri, mainCredential)
} }
} catch (e: Exception) { } catch (e: Exception) {
val error = getString(R.string.error_create_database_file) val error = getString(R.string.error_create_database_file)

View File

@@ -587,7 +587,7 @@ class MainCredentialActivity : DatabaseModeActivity() {
readOnly: Boolean, readOnly: Boolean,
cipherEncryptDatabase: CipherEncryptDatabase?, cipherEncryptDatabase: CipherEncryptDatabase?,
fixDuplicateUUID: Boolean) { fixDuplicateUUID: Boolean) {
loadDatabase( mDatabaseViewModel.loadDatabase(
databaseUri, databaseUri,
mainCredential, mainCredential,
readOnly, readOnly,

View File

@@ -67,7 +67,7 @@ class DatabaseChangedDialogFragment : DatabaseDialogFragment() {
} }
builder.setMessage(stringBuilder) builder.setMessage(stringBuilder)
builder.setPositiveButton(android.R.string.ok) { _, _ -> builder.setPositiveButton(android.R.string.ok) { _, _ ->
actionDatabaseListener?.validateDatabaseChanged() actionDatabaseListener?.onDatabaseChangeValidated()
} }
return builder.create() return builder.create()
} }
@@ -76,7 +76,7 @@ class DatabaseChangedDialogFragment : DatabaseDialogFragment() {
} }
interface ActionDatabaseChangedListener { interface ActionDatabaseChangedListener {
fun validateDatabaseChanged() fun onDatabaseChangeValidated()
} }
companion object { companion object {

View File

@@ -30,13 +30,17 @@ abstract class DatabaseDialogFragment : DialogFragment(), DatabaseRetrieval {
resetAppTimeoutOnTouchOrFocus() resetAppTimeoutOnTouchOrFocus()
onDatabaseRetrieved(uiState.database) onDatabaseRetrieved(uiState.database)
} }
is DatabaseViewModel.UIState.OnDatabaseActionFinished -> {
onDatabaseActionFinished(
uiState.database,
uiState.actionTask,
uiState.result
)
}
else -> {}
} }
} }
} }
mDatabaseViewModel.actionFinished.observe(this) { result ->
onDatabaseActionFinished(result.database, result.actionTask, result.result)
}
} }
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {

View File

@@ -4,7 +4,9 @@ import android.os.Bundle
import android.view.View import android.view.View
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import androidx.fragment.app.activityViewModels import androidx.fragment.app.activityViewModels
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle
import com.kunzisoft.keepass.activities.legacy.DatabaseRetrieval import com.kunzisoft.keepass.activities.legacy.DatabaseRetrieval
import com.kunzisoft.keepass.activities.legacy.resetAppTimeoutWhenViewTouchedOrFocused import com.kunzisoft.keepass.activities.legacy.resetAppTimeoutWhenViewTouchedOrFocused
import com.kunzisoft.keepass.database.ContextualDatabase import com.kunzisoft.keepass.database.ContextualDatabase
@@ -20,21 +22,26 @@ abstract class DatabaseFragment : Fragment(), DatabaseRetrieval {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
lifecycleScope.launch { viewLifecycleOwner.lifecycleScope.launch {
// Initialize the parameters repeatOnLifecycle(Lifecycle.State.STARTED) {
mDatabaseViewModel.uiState.collect { uiState -> mDatabaseViewModel.uiState.collect { uiState ->
when (uiState) { when (uiState) {
is DatabaseViewModel.UIState.Loading -> {} is DatabaseViewModel.UIState.Loading -> {}
is DatabaseViewModel.UIState.OnDatabaseRetrieved -> { is DatabaseViewModel.UIState.OnDatabaseRetrieved -> {
onDatabaseRetrieved(uiState.database) onDatabaseRetrieved(uiState.database)
}
is DatabaseViewModel.UIState.OnDatabaseActionFinished -> {
onDatabaseActionFinished(
uiState.database,
uiState.actionTask,
uiState.result
)
}
else -> {}
} }
} }
} }
} }
mDatabaseViewModel.actionFinished.observe(viewLifecycleOwner) { result ->
onDatabaseActionFinished(result.database, result.actionTask, result.result)
}
} }
protected fun resetAppTimeoutWhenViewFocusedOrChanged(view: View?) { protected fun resetAppTimeoutWhenViewFocusedOrChanged(view: View?) {

View File

@@ -1,58 +1,120 @@
package com.kunzisoft.keepass.activities.legacy package com.kunzisoft.keepass.activities.legacy
import android.net.Uri import android.Manifest
import android.content.pm.PackageManager
import android.os.Build
import android.os.Bundle import android.os.Bundle
import androidx.activity.result.contract.ActivityResultContracts
import androidx.activity.viewModels import androidx.activity.viewModels
import androidx.appcompat.app.AlertDialog
import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle
import com.kunzisoft.keepass.R
import com.kunzisoft.keepass.activities.dialogs.DatabaseChangedDialogFragment
import com.kunzisoft.keepass.activities.dialogs.DatabaseChangedDialogFragment.Companion.DATABASE_CHANGED_DIALOG_TAG
import com.kunzisoft.keepass.activities.stylish.StylishActivity import com.kunzisoft.keepass.activities.stylish.StylishActivity
import com.kunzisoft.keepass.database.ContextualDatabase import com.kunzisoft.keepass.database.ContextualDatabase
import com.kunzisoft.keepass.database.DatabaseTaskProvider import com.kunzisoft.keepass.database.DatabaseTaskProvider.Companion.startDatabaseService
import com.kunzisoft.keepass.database.MainCredential import com.kunzisoft.keepass.database.ProgressMessage
import com.kunzisoft.keepass.model.CipherEncryptDatabase import com.kunzisoft.keepass.model.SnapFileDatabaseInfo
import com.kunzisoft.keepass.tasks.ActionRunnable import com.kunzisoft.keepass.tasks.ActionRunnable
import com.kunzisoft.keepass.utils.getBinaryDir import com.kunzisoft.keepass.tasks.ProgressTaskDialogFragment
import com.kunzisoft.keepass.tasks.ProgressTaskDialogFragment.Companion.PROGRESS_TASK_DIALOG_TAG
import com.kunzisoft.keepass.viewmodels.DatabaseViewModel import com.kunzisoft.keepass.viewmodels.DatabaseViewModel
import kotlinx.coroutines.launch
abstract class DatabaseActivity : StylishActivity(), DatabaseRetrieval { abstract class DatabaseActivity : StylishActivity(), DatabaseRetrieval {
protected val mDatabaseViewModel: DatabaseViewModel by viewModels() protected val mDatabaseViewModel: DatabaseViewModel by viewModels()
protected var mDatabaseTaskProvider: DatabaseTaskProvider? = null protected val mDatabase: ContextualDatabase?
protected var mDatabase: ContextualDatabase? = null get() = mDatabaseViewModel.database
private var progressTaskDialogFragment: ProgressTaskDialogFragment? = null
private var databaseChangedDialogFragment: DatabaseChangedDialogFragment? = null
private val mActionDatabaseListener =
object : DatabaseChangedDialogFragment.ActionDatabaseChangedListener {
override fun onDatabaseChangeValidated() {
mDatabaseViewModel.onDatabaseChangeValidated()
}
}
private val tempServiceParameters = mutableListOf<Pair<Bundle?, String>>()
private val requestPermissionLauncher = registerForActivityResult(
ActivityResultContracts.RequestPermission()
) { _ ->
// Whether or not the user has accepted, the service can be started,
// There just won't be any notification if it's not allowed.
tempServiceParameters.removeFirstOrNull()?.let {
startDatabaseService(it.first, it.second)
}
}
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
lifecycleScope.launch {
repeatOnLifecycle(Lifecycle.State.STARTED) {
mDatabaseViewModel.uiState.collect { uiState ->
when (uiState) {
is DatabaseViewModel.UIState.Loading -> {}
is DatabaseViewModel.UIState.OnDatabaseRetrieved -> {
onDatabaseRetrieved(uiState.database)
}
mDatabaseTaskProvider = DatabaseTaskProvider(this, showDatabaseDialog()) is DatabaseViewModel.UIState.OnDatabaseReloaded -> {
if (finishActivityIfReloadRequested()) {
finish()
}
}
mDatabaseTaskProvider?.onDatabaseRetrieved = { database -> is DatabaseViewModel.UIState.OnDatabaseInfoChanged -> {
val databaseWasReloaded = database?.wasReloaded == true showDatabaseChangedDialog(
if (databaseWasReloaded && finishActivityIfReloadRequested()) { uiState.previousDatabaseInfo,
finish() uiState.newDatabaseInfo,
} else if (mDatabase == null || mDatabase != database || databaseWasReloaded) { uiState.readOnlyDatabase
database?.wasReloaded = false )
onDatabaseRetrieved(database) }
is DatabaseViewModel.UIState.OnDatabaseActionRequested -> {
startDatabasePermissionService(
uiState.bundle,
uiState.actionTask
)
}
is DatabaseViewModel.UIState.OnDatabaseActionStarted -> {
if (showDatabaseDialog())
startDialog(uiState.progressMessage)
}
is DatabaseViewModel.UIState.OnDatabaseActionUpdated -> {
if (showDatabaseDialog())
updateDialog(uiState.progressMessage)
}
is DatabaseViewModel.UIState.OnDatabaseActionStopped -> {
// Remove the progress task
stopDialog()
}
is DatabaseViewModel.UIState.OnDatabaseActionFinished -> {
onDatabaseActionFinished(
uiState.database,
uiState.actionTask,
uiState.result
)
stopDialog()
}
}
}
} }
} }
mDatabaseTaskProvider?.onActionFinish = { database, actionTask, result ->
onDatabaseActionFinished(database, actionTask, result)
}
} }
protected open fun showDatabaseDialog(): Boolean {
return true
}
override fun onDestroy() {
mDatabaseTaskProvider?.destroy()
mDatabaseTaskProvider = null
mDatabase = null
super.onDestroy()
}
override fun onDatabaseRetrieved(database: ContextualDatabase?) { override fun onDatabaseRetrieved(database: ContextualDatabase?) {
mDatabase = database
mDatabaseViewModel.defineDatabase(database)
// optional method implementation // optional method implementation
} }
@@ -61,44 +123,100 @@ abstract class DatabaseActivity : StylishActivity(), DatabaseRetrieval {
actionTask: String, actionTask: String,
result: ActionRunnable.Result result: ActionRunnable.Result
) { ) {
mDatabaseViewModel.onActionFinished(database, actionTask, result)
// optional method implementation // optional method implementation
} }
fun createDatabase( private fun startDatabasePermissionService(bundle: Bundle?, actionTask: String) {
databaseUri: Uri, if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
mainCredential: MainCredential if (ContextCompat.checkSelfPermission(this, Manifest.permission.POST_NOTIFICATIONS)
== PackageManager.PERMISSION_GRANTED
) {
startDatabaseService(bundle, actionTask)
} else if (ActivityCompat.shouldShowRequestPermissionRationale(
this,
Manifest.permission.POST_NOTIFICATIONS
)
) {
// it's not the first time, so the user deliberately chooses not to display the notification
startDatabaseService(bundle, actionTask)
} else {
AlertDialog.Builder(this)
.setMessage(R.string.warning_database_notification_permission)
.setNegativeButton(R.string.later) { _, _ ->
// Refuses the notification, so start the service
startDatabaseService(bundle, actionTask)
}
.setPositiveButton(R.string.ask) { _, _ ->
// Save the temp parameters to ask the permission
tempServiceParameters.add(Pair(bundle, actionTask))
requestPermissionLauncher.launch(Manifest.permission.POST_NOTIFICATIONS)
}.create().show()
}
} else {
startDatabaseService(bundle, actionTask)
}
}
private fun showDatabaseChangedDialog(
previousDatabaseInfo: SnapFileDatabaseInfo,
newDatabaseInfo: SnapFileDatabaseInfo,
readOnlyDatabase: Boolean
) { ) {
mDatabaseTaskProvider?.startDatabaseCreate(databaseUri, mainCredential) lifecycleScope.launch {
if (databaseChangedDialogFragment == null) {
databaseChangedDialogFragment = supportFragmentManager
.findFragmentByTag(DATABASE_CHANGED_DIALOG_TAG) as DatabaseChangedDialogFragment?
databaseChangedDialogFragment?.actionDatabaseListener =
mActionDatabaseListener
}
if (progressTaskDialogFragment == null) {
databaseChangedDialogFragment = DatabaseChangedDialogFragment.getInstance(
previousDatabaseInfo,
newDatabaseInfo,
readOnlyDatabase
)
databaseChangedDialogFragment?.actionDatabaseListener =
mActionDatabaseListener
databaseChangedDialogFragment?.show(
supportFragmentManager,
DATABASE_CHANGED_DIALOG_TAG
)
}
}
} }
fun loadDatabase( private fun startDialog(progressMessage: ProgressMessage) {
databaseUri: Uri, lifecycleScope.launch {
mainCredential: MainCredential, if (progressTaskDialogFragment == null) {
readOnly: Boolean, progressTaskDialogFragment = supportFragmentManager
cipherEncryptDatabase: CipherEncryptDatabase?, .findFragmentByTag(PROGRESS_TASK_DIALOG_TAG) as ProgressTaskDialogFragment?
fixDuplicateUuid: Boolean }
) { if (progressTaskDialogFragment == null) {
mDatabaseTaskProvider?.startDatabaseLoad( progressTaskDialogFragment = ProgressTaskDialogFragment()
databaseUri, progressTaskDialogFragment?.show(
mainCredential, supportFragmentManager,
readOnly, PROGRESS_TASK_DIALOG_TAG
cipherEncryptDatabase, )
fixDuplicateUuid }
) updateDialog(progressMessage)
}
} }
protected fun closeDatabase() { private fun updateDialog(progressMessage: ProgressMessage) {
mDatabase?.clearAndClose(this.getBinaryDir()) progressTaskDialogFragment?.apply {
updateTitle(progressMessage.titleId)
updateMessage(progressMessage.messageId)
updateWarning(progressMessage.warningId)
setCancellable(progressMessage.cancelable)
}
} }
override fun onResume() { private fun stopDialog() {
super.onResume() progressTaskDialogFragment?.dismissAllowingStateLoss()
mDatabaseTaskProvider?.registerProgressTask() progressTaskDialogFragment = null
} }
override fun onPause() { protected open fun showDatabaseDialog(): Boolean {
mDatabaseTaskProvider?.unregisterProgressTask() return true
super.onPause()
} }
} }

View File

@@ -87,80 +87,6 @@ abstract class DatabaseLockActivity : DatabaseModeActivity(),
deleteDatabaseNodes(nodes) deleteDatabaseNodes(nodes)
} }
mDatabaseViewModel.saveDatabase.observe(this) { save ->
mDatabaseTaskProvider?.startDatabaseSave(save)
}
mDatabaseViewModel.mergeDatabase.observe(this) { save ->
mDatabaseTaskProvider?.startDatabaseMerge(save)
}
mDatabaseViewModel.reloadDatabase.observe(this) { fixDuplicateUuid ->
mDatabaseTaskProvider?.askToStartDatabaseReload(mDatabase?.dataModifiedSinceLastLoading != false) {
mDatabaseTaskProvider?.startDatabaseReload(fixDuplicateUuid)
}
}
mDatabaseViewModel.saveName.observe(this) {
mDatabaseTaskProvider?.startDatabaseSaveName(it.oldValue, it.newValue, it.save)
}
mDatabaseViewModel.saveDescription.observe(this) {
mDatabaseTaskProvider?.startDatabaseSaveDescription(it.oldValue, it.newValue, it.save)
}
mDatabaseViewModel.saveDefaultUsername.observe(this) {
mDatabaseTaskProvider?.startDatabaseSaveDefaultUsername(it.oldValue, it.newValue, it.save)
}
mDatabaseViewModel.saveColor.observe(this) {
mDatabaseTaskProvider?.startDatabaseSaveColor(it.oldValue, it.newValue, it.save)
}
mDatabaseViewModel.saveCompression.observe(this) {
mDatabaseTaskProvider?.startDatabaseSaveCompression(it.oldValue, it.newValue, it.save)
}
mDatabaseViewModel.removeUnlinkData.observe(this) {
mDatabaseTaskProvider?.startDatabaseRemoveUnlinkedData(it)
}
mDatabaseViewModel.saveRecycleBin.observe(this) {
mDatabaseTaskProvider?.startDatabaseSaveRecycleBin(it.oldValue, it.newValue, it.save)
}
mDatabaseViewModel.saveTemplatesGroup.observe(this) {
mDatabaseTaskProvider?.startDatabaseSaveTemplatesGroup(it.oldValue, it.newValue, it.save)
}
mDatabaseViewModel.saveMaxHistoryItems.observe(this) {
mDatabaseTaskProvider?.startDatabaseSaveMaxHistoryItems(it.oldValue, it.newValue, it.save)
}
mDatabaseViewModel.saveMaxHistorySize.observe(this) {
mDatabaseTaskProvider?.startDatabaseSaveMaxHistorySize(it.oldValue, it.newValue, it.save)
}
mDatabaseViewModel.saveEncryption.observe(this) {
mDatabaseTaskProvider?.startDatabaseSaveEncryption(it.oldValue, it.newValue, it.save)
}
mDatabaseViewModel.saveKeyDerivation.observe(this) {
mDatabaseTaskProvider?.startDatabaseSaveKeyDerivation(it.oldValue, it.newValue, it.save)
}
mDatabaseViewModel.saveIterations.observe(this) {
mDatabaseTaskProvider?.startDatabaseSaveIterations(it.oldValue, it.newValue, it.save)
}
mDatabaseViewModel.saveMemoryUsage.observe(this) {
mDatabaseTaskProvider?.startDatabaseSaveMemoryUsage(it.oldValue, it.newValue, it.save)
}
mDatabaseViewModel.saveParallelism.observe(this) {
mDatabaseTaskProvider?.startDatabaseSaveParallelism(it.oldValue, it.newValue, it.save)
}
mExitLock = false mExitLock = false
} }
@@ -169,8 +95,6 @@ abstract class DatabaseLockActivity : DatabaseModeActivity(),
} }
override fun onDatabaseRetrieved(database: ContextualDatabase?) { override fun onDatabaseRetrieved(database: ContextualDatabase?) {
super.onDatabaseRetrieved(database)
// End activity if database not loaded // End activity if database not loaded
if (finishActivityIfDatabaseNotLoaded() && (database == null || !database.loaded)) { if (finishActivityIfDatabaseNotLoaded() && (database == null || !database.loaded)) {
finish() finish()
@@ -186,7 +110,6 @@ abstract class DatabaseLockActivity : DatabaseModeActivity(),
if (mTimeoutEnable) { if (mTimeoutEnable) {
if (mLockReceiver == null) { if (mLockReceiver == null) {
mLockReceiver = LockReceiver { mLockReceiver = LockReceiver {
mDatabase = null
closeDatabase(database) closeDatabase(database)
mExitLock = true mExitLock = true
closeOptionsMenu() closeOptionsMenu()
@@ -227,7 +150,6 @@ abstract class DatabaseLockActivity : DatabaseModeActivity(),
actionTask: String, actionTask: String,
result: ActionRunnable.Result result: ActionRunnable.Result
) { ) {
super.onDatabaseActionFinished(database, actionTask, result)
when (actionTask) { when (actionTask) {
DatabaseTaskNotificationService.ACTION_DATABASE_MERGE_TASK, DatabaseTaskNotificationService.ACTION_DATABASE_MERGE_TASK,
DatabaseTaskNotificationService.ACTION_DATABASE_RELOAD_TASK -> { DatabaseTaskNotificationService.ACTION_DATABASE_RELOAD_TASK -> {
@@ -249,24 +171,15 @@ abstract class DatabaseLockActivity : DatabaseModeActivity(),
databaseUri: Uri?, databaseUri: Uri?,
mainCredential: MainCredential mainCredential: MainCredential
) { ) {
assignDatabasePassword(databaseUri, mainCredential) mDatabaseViewModel.assignMainCredential(databaseUri, mainCredential)
} }
private fun assignDatabasePassword( fun assignMainCredential(mainCredential: MainCredential) {
databaseUri: Uri?,
mainCredential: MainCredential
) {
if (databaseUri != null) {
mDatabaseTaskProvider?.startDatabaseAssignCredential(databaseUri, mainCredential)
}
}
fun assignPassword(mainCredential: MainCredential) {
mDatabase?.let { database -> mDatabase?.let { database ->
database.fileUri?.let { databaseUri -> database.fileUri?.let { databaseUri ->
// Show the progress dialog now or after dialog confirmation // Show the progress dialog now or after dialog confirmation
if (database.isValidCredential(mainCredential.toMasterCredential(contentResolver))) { if (database.isValidCredential(mainCredential.toMasterCredential(contentResolver))) {
assignDatabasePassword(databaseUri, mainCredential) mDatabaseViewModel.assignMainCredential(databaseUri, mainCredential)
} else { } else {
PasswordEncodingDialogFragment.getInstance(databaseUri, mainCredential) PasswordEncodingDialogFragment.getInstance(databaseUri, mainCredential)
.show(supportFragmentManager, "passwordEncodingTag") .show(supportFragmentManager, "passwordEncodingTag")
@@ -276,45 +189,51 @@ abstract class DatabaseLockActivity : DatabaseModeActivity(),
} }
fun saveDatabase() { fun saveDatabase() {
mDatabaseTaskProvider?.startDatabaseSave(true) mDatabaseViewModel.saveDatabase(save = true)
} }
fun saveDatabaseTo(uri: Uri) { fun saveDatabaseTo(uri: Uri) {
mDatabaseTaskProvider?.startDatabaseSave(true, uri) mDatabaseViewModel.saveDatabase(save = true, saveToUri = uri)
} }
fun mergeDatabase() { fun mergeDatabase() {
mDatabaseTaskProvider?.startDatabaseMerge(mAutoSaveEnable) mDatabaseViewModel.mergeDatabase(save = mAutoSaveEnable)
} }
fun mergeDatabaseFrom(uri: Uri, mainCredential: MainCredential) { fun mergeDatabaseFrom(uri: Uri, mainCredential: MainCredential) {
mDatabaseTaskProvider?.startDatabaseMerge(mAutoSaveEnable, uri, mainCredential) mDatabaseViewModel.mergeDatabase(mAutoSaveEnable, uri, mainCredential)
} }
fun reloadDatabase() { fun reloadDatabase() {
mDatabaseTaskProvider?.askToStartDatabaseReload(mDatabase?.dataModifiedSinceLastLoading != false) { mDatabaseViewModel.reloadDatabase(fixDuplicateUuid = false)
mDatabaseTaskProvider?.startDatabaseReload(false)
}
} }
fun createEntry(newEntry: Entry, fun createEntry(
parent: Group) { newEntry: Entry,
mDatabaseTaskProvider?.startDatabaseCreateEntry(newEntry, parent, mAutoSaveEnable) parent: Group
) {
mDatabaseViewModel.createEntry(newEntry, parent, mAutoSaveEnable)
} }
fun updateEntry(oldEntry: Entry, fun updateEntry(
entryToUpdate: Entry) { oldEntry: Entry,
mDatabaseTaskProvider?.startDatabaseUpdateEntry(oldEntry, entryToUpdate, mAutoSaveEnable) entryToUpdate: Entry
) {
mDatabaseViewModel.updateEntry(oldEntry, entryToUpdate, mAutoSaveEnable)
} }
fun copyNodes(nodesToCopy: List<Node>, fun copyNodes(
newParent: Group) { nodesToCopy: List<Node>,
mDatabaseTaskProvider?.startDatabaseCopyNodes(nodesToCopy, newParent, mAutoSaveEnable) newParent: Group
) {
mDatabaseViewModel.copyNodes(nodesToCopy, newParent, mAutoSaveEnable)
} }
fun moveNodes(nodesToMove: List<Node>, fun moveNodes(
newParent: Group) { nodesToMove: List<Node>,
mDatabaseTaskProvider?.startDatabaseMoveNodes(nodesToMove, newParent, mAutoSaveEnable) newParent: Group
) {
mDatabaseViewModel.moveNodes(nodesToMove, newParent, mAutoSaveEnable)
} }
private fun eachNodeRecyclable(database: ContextualDatabase, nodes: List<Node>): Boolean { private fun eachNodeRecyclable(database: ContextualDatabase, nodes: List<Node>): Boolean {
@@ -330,6 +249,7 @@ abstract class DatabaseLockActivity : DatabaseModeActivity(),
} }
fun deleteNodes(nodes: List<Node>, recycleBin: Boolean = false) { fun deleteNodes(nodes: List<Node>, recycleBin: Boolean = false) {
// TODO Move in ViewModel
mDatabase?.let { database -> mDatabase?.let { database ->
// If recycle bin enabled, ensure it exists // If recycle bin enabled, ensure it exists
if (database.isRecycleBinEnabled) { if (database.isRecycleBinEnabled) {
@@ -350,11 +270,14 @@ abstract class DatabaseLockActivity : DatabaseModeActivity(),
} }
private fun deleteDatabaseNodes(nodes: List<Node>) { private fun deleteDatabaseNodes(nodes: List<Node>) {
mDatabaseTaskProvider?.startDatabaseDeleteNodes(nodes, mAutoSaveEnable) mDatabaseViewModel.deleteNodes(nodes, mAutoSaveEnable)
} }
fun createGroup(parent: Group, fun createGroup(
groupInfo: GroupInfo?) { parent: Group,
groupInfo: GroupInfo?
) {
// TODO Move in ViewModel
// Build the group // Build the group
mDatabase?.createGroup()?.let { newGroup -> mDatabase?.createGroup()?.let { newGroup ->
groupInfo?.let { info -> groupInfo?.let { info ->
@@ -362,12 +285,15 @@ abstract class DatabaseLockActivity : DatabaseModeActivity(),
} }
// Not really needed here because added in runnable but safe // Not really needed here because added in runnable but safe
newGroup.parent = parent newGroup.parent = parent
mDatabaseTaskProvider?.startDatabaseCreateGroup(newGroup, parent, mAutoSaveEnable) mDatabaseViewModel.createGroup(newGroup, parent, mAutoSaveEnable)
} }
} }
fun updateGroup(oldGroup: Group, fun updateGroup(
groupInfo: GroupInfo) { oldGroup: Group,
groupInfo: GroupInfo
) {
// TODO Move in ViewModel
// If group updated save it in the database // If group updated save it in the database
val updateGroup = Group(oldGroup).let { updateGroup -> val updateGroup = Group(oldGroup).let { updateGroup ->
updateGroup.apply { updateGroup.apply {
@@ -377,18 +303,21 @@ abstract class DatabaseLockActivity : DatabaseModeActivity(),
this.setGroupInfo(groupInfo) this.setGroupInfo(groupInfo)
} }
} }
mDatabaseTaskProvider?.startDatabaseUpdateGroup(oldGroup, updateGroup, mAutoSaveEnable) mDatabaseViewModel.updateGroup(oldGroup, updateGroup, mAutoSaveEnable)
} }
fun restoreEntryHistory(mainEntryId: NodeId<UUID>, fun restoreEntryHistory(
entryHistoryPosition: Int) { mainEntryId: NodeId<UUID>,
mDatabaseTaskProvider entryHistoryPosition: Int
?.startDatabaseRestoreEntryHistory(mainEntryId, entryHistoryPosition, mAutoSaveEnable) ) {
mDatabaseViewModel.restoreEntryHistory(mainEntryId, entryHistoryPosition, mAutoSaveEnable)
} }
fun deleteEntryHistory(mainEntryId: NodeId<UUID>, fun deleteEntryHistory(
entryHistoryPosition: Int) { mainEntryId: NodeId<UUID>,
mDatabaseTaskProvider?.startDatabaseDeleteEntryHistory(mainEntryId, entryHistoryPosition, mAutoSaveEnable) entryHistoryPosition: Int
) {
mDatabaseViewModel.deleteEntryHistory(mainEntryId, entryHistoryPosition, mAutoSaveEnable)
} }
private fun checkRegister() { private fun checkRegister() {

View File

@@ -19,7 +19,6 @@
*/ */
package com.kunzisoft.keepass.database package com.kunzisoft.keepass.database
import android.Manifest
import android.content.BroadcastReceiver import android.content.BroadcastReceiver
import android.content.ComponentName import android.content.ComponentName
import android.content.Context import android.content.Context
@@ -29,23 +28,16 @@ import android.content.Context.BIND_IMPORTANT
import android.content.Intent import android.content.Intent
import android.content.IntentFilter import android.content.IntentFilter
import android.content.ServiceConnection import android.content.ServiceConnection
import android.content.pm.PackageManager
import android.net.Uri import android.net.Uri
import android.os.Build
import android.os.Bundle import android.os.Bundle
import android.os.IBinder import android.os.IBinder
import android.util.Log import android.util.Log
import android.widget.Toast import android.widget.Toast
import androidx.activity.result.contract.ActivityResultContracts
import androidx.appcompat.app.AlertDialog import androidx.appcompat.app.AlertDialog
import androidx.core.app.ActivityCompat.shouldShowRequestPermissionRationale
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import androidx.core.content.ContextCompat.RECEIVER_NOT_EXPORTED import androidx.core.content.ContextCompat.RECEIVER_NOT_EXPORTED
import androidx.fragment.app.FragmentActivity
import androidx.lifecycle.lifecycleScope
import com.kunzisoft.keepass.R import com.kunzisoft.keepass.R
import com.kunzisoft.keepass.activities.dialogs.DatabaseChangedDialogFragment import com.kunzisoft.keepass.activities.dialogs.DatabaseChangedDialogFragment
import com.kunzisoft.keepass.activities.dialogs.DatabaseChangedDialogFragment.Companion.DATABASE_CHANGED_DIALOG_TAG
import com.kunzisoft.keepass.database.crypto.EncryptionAlgorithm import com.kunzisoft.keepass.database.crypto.EncryptionAlgorithm
import com.kunzisoft.keepass.database.crypto.kdf.KdfEngine import com.kunzisoft.keepass.database.crypto.kdf.KdfEngine
import com.kunzisoft.keepass.database.element.Entry import com.kunzisoft.keepass.database.element.Entry
@@ -55,7 +47,6 @@ 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.CipherEncryptDatabase import com.kunzisoft.keepass.model.CipherEncryptDatabase
import com.kunzisoft.keepass.model.SnapFileDatabaseInfo
import com.kunzisoft.keepass.services.DatabaseTaskNotificationService import com.kunzisoft.keepass.services.DatabaseTaskNotificationService
import com.kunzisoft.keepass.services.DatabaseTaskNotificationService.Companion.ACTION_CHALLENGE_RESPONDED import com.kunzisoft.keepass.services.DatabaseTaskNotificationService.Companion.ACTION_CHALLENGE_RESPONDED
import com.kunzisoft.keepass.services.DatabaseTaskNotificationService.Companion.ACTION_DATABASE_ASSIGN_CREDENTIAL_TASK import com.kunzisoft.keepass.services.DatabaseTaskNotificationService.Companion.ACTION_DATABASE_ASSIGN_CREDENTIAL_TASK
@@ -89,13 +80,9 @@ import com.kunzisoft.keepass.services.DatabaseTaskNotificationService.Companion.
import com.kunzisoft.keepass.services.DatabaseTaskNotificationService.Companion.ACTION_DATABASE_UPDATE_RECYCLE_BIN_TASK import com.kunzisoft.keepass.services.DatabaseTaskNotificationService.Companion.ACTION_DATABASE_UPDATE_RECYCLE_BIN_TASK
import com.kunzisoft.keepass.services.DatabaseTaskNotificationService.Companion.ACTION_DATABASE_UPDATE_TEMPLATES_GROUP_TASK import com.kunzisoft.keepass.services.DatabaseTaskNotificationService.Companion.ACTION_DATABASE_UPDATE_TEMPLATES_GROUP_TASK
import com.kunzisoft.keepass.services.DatabaseTaskNotificationService.Companion.getBundleFromListNodes import com.kunzisoft.keepass.services.DatabaseTaskNotificationService.Companion.getBundleFromListNodes
import com.kunzisoft.keepass.tasks.ActionRunnable
import com.kunzisoft.keepass.tasks.ProgressTaskDialogFragment
import com.kunzisoft.keepass.tasks.ProgressTaskDialogFragment.Companion.PROGRESS_TASK_DIALOG_TAG
import com.kunzisoft.keepass.utils.DATABASE_START_TASK_ACTION import com.kunzisoft.keepass.utils.DATABASE_START_TASK_ACTION
import com.kunzisoft.keepass.utils.DATABASE_STOP_TASK_ACTION import com.kunzisoft.keepass.utils.DATABASE_STOP_TASK_ACTION
import com.kunzisoft.keepass.utils.putParcelableList import com.kunzisoft.keepass.utils.putParcelableList
import kotlinx.coroutines.launch
import java.util.UUID import java.util.UUID
/** /**
@@ -103,121 +90,41 @@ import java.util.UUID
* Useful to retrieve a database instance and sending tasks commands * Useful to retrieve a database instance and sending tasks commands
*/ */
class DatabaseTaskProvider( class DatabaseTaskProvider(
private var context: Context, private var context: Context
private var showDialog: Boolean = true
) { ) {
// To show dialog only if context is an activity
private var activity: FragmentActivity? = try {
context as? FragmentActivity?
} catch (_: Exception) {
null
}
var onDatabaseRetrieved: ((database: ContextualDatabase?) -> Unit)? = null var onDatabaseRetrieved: ((database: ContextualDatabase?) -> Unit)? = null
var onActionFinish: (( var onStartActionRequested: ((bundle: Bundle?, actionTask: String) -> Unit)? = null
database: ContextualDatabase, var actionTaskListener: DatabaseTaskNotificationService.ActionTaskListener? = null
actionTask: String, var databaseInfoListener: DatabaseTaskNotificationService.DatabaseInfoListener? = null
result: ActionRunnable.Result
) -> Unit)? = null
private var intentDatabaseTask: Intent = Intent(
context.applicationContext,
DatabaseTaskNotificationService::class.java
)
private var databaseTaskBroadcastReceiver: BroadcastReceiver? = null private var databaseTaskBroadcastReceiver: BroadcastReceiver? = null
private var mBinder: DatabaseTaskNotificationService.ActionTaskBinder? = null private var mBinder: DatabaseTaskNotificationService.ActionTaskBinder? = null
private var serviceConnection: ServiceConnection? = null private var serviceConnection: ServiceConnection? = null
private var progressTaskDialogFragment: ProgressTaskDialogFragment? = null private var intentDatabaseTask: Intent = Intent(
private var databaseChangedDialogFragment: DatabaseChangedDialogFragment? = null context.applicationContext,
DatabaseTaskNotificationService::class.java
)
fun destroy() { fun destroy() {
this.activity = null
this.onDatabaseRetrieved = null this.onDatabaseRetrieved = null
this.onActionFinish = null
this.databaseTaskBroadcastReceiver = null this.databaseTaskBroadcastReceiver = null
this.mBinder = null this.mBinder = null
this.serviceConnection = null this.serviceConnection = null
this.progressTaskDialogFragment = null
this.databaseChangedDialogFragment = null
}
private val actionTaskListener = object : DatabaseTaskNotificationService.ActionTaskListener {
override fun onActionStarted(
database: ContextualDatabase,
progressMessage: ProgressMessage
) {
if (showDialog)
startDialog(progressMessage)
}
override fun onActionUpdated(
database: ContextualDatabase,
progressMessage: ProgressMessage
) {
if (showDialog)
updateDialog(progressMessage)
}
override fun onActionStopped(
database: ContextualDatabase
) {
// Remove the progress task
stopDialog()
}
override fun onActionFinished(
database: ContextualDatabase,
actionTask: String,
result: ActionRunnable.Result
) {
onActionFinish?.invoke(database, actionTask, result)
onActionStopped(database)
}
} }
private val mActionDatabaseListener = private val mActionDatabaseListener =
object : DatabaseChangedDialogFragment.ActionDatabaseChangedListener { object : DatabaseChangedDialogFragment.ActionDatabaseChangedListener {
override fun validateDatabaseChanged() { override fun onDatabaseChangeValidated() {
mBinder?.getService()?.saveDatabaseInfo() mBinder?.getService()?.saveDatabaseInfo()
} }
} }
private var databaseInfoListener = object : fun onDatabaseChangeValidated() {
DatabaseTaskNotificationService.DatabaseInfoListener { mBinder?.getService()?.saveDatabaseInfo()
override fun onDatabaseInfoChanged(
previousDatabaseInfo: SnapFileDatabaseInfo,
newDatabaseInfo: SnapFileDatabaseInfo,
readOnlyDatabase: Boolean
) {
activity?.let { activity ->
activity.lifecycleScope.launch {
if (databaseChangedDialogFragment == null) {
databaseChangedDialogFragment = activity.supportFragmentManager
.findFragmentByTag(DATABASE_CHANGED_DIALOG_TAG) as DatabaseChangedDialogFragment?
databaseChangedDialogFragment?.actionDatabaseListener =
mActionDatabaseListener
}
if (progressTaskDialogFragment == null) {
databaseChangedDialogFragment = DatabaseChangedDialogFragment.getInstance(
previousDatabaseInfo,
newDatabaseInfo,
readOnlyDatabase
)
databaseChangedDialogFragment?.actionDatabaseListener =
mActionDatabaseListener
databaseChangedDialogFragment?.show(
activity.supportFragmentManager,
DATABASE_CHANGED_DIALOG_TAG
)
}
}
}
}
} }
private var databaseListener = object : DatabaseTaskNotificationService.DatabaseListener { private var databaseListener = object : DatabaseTaskNotificationService.DatabaseListener {
@@ -226,49 +133,16 @@ class DatabaseTaskProvider(
} }
} }
private fun startDialog(progressMessage: ProgressMessage) {
activity?.let { activity ->
activity.lifecycleScope.launch {
if (progressTaskDialogFragment == null) {
progressTaskDialogFragment = activity.supportFragmentManager
.findFragmentByTag(PROGRESS_TASK_DIALOG_TAG) as ProgressTaskDialogFragment?
}
if (progressTaskDialogFragment == null) {
progressTaskDialogFragment = ProgressTaskDialogFragment()
progressTaskDialogFragment?.show(
activity.supportFragmentManager,
PROGRESS_TASK_DIALOG_TAG
)
}
updateDialog(progressMessage)
}
}
}
private fun updateDialog(progressMessage: ProgressMessage) {
progressTaskDialogFragment?.apply {
updateTitle(progressMessage.titleId)
updateMessage(progressMessage.messageId)
updateWarning(progressMessage.warningId)
setCancellable(progressMessage.cancelable)
}
}
private fun stopDialog() {
progressTaskDialogFragment?.dismissAllowingStateLoss()
progressTaskDialogFragment = null
}
private fun initServiceConnection() { private fun initServiceConnection() {
stopDialog() actionTaskListener?.onActionStopped()
if (serviceConnection == null) { if (serviceConnection == null) {
serviceConnection = object : ServiceConnection { serviceConnection = object : ServiceConnection {
override fun onBindingDied(name: ComponentName?) { override fun onBindingDied(name: ComponentName?) {
stopDialog() actionTaskListener?.onActionStopped()
} }
override fun onNullBinding(name: ComponentName?) { override fun onNullBinding(name: ComponentName?) {
stopDialog() actionTaskListener?.onActionStopped()
} }
override fun onServiceConnected(name: ComponentName?, serviceBinder: IBinder?) { override fun onServiceConnected(name: ComponentName?, serviceBinder: IBinder?) {
@@ -291,13 +165,21 @@ class DatabaseTaskProvider(
private fun addServiceListeners(service: DatabaseTaskNotificationService.ActionTaskBinder?) { private fun addServiceListeners(service: DatabaseTaskNotificationService.ActionTaskBinder?) {
service?.addDatabaseListener(databaseListener) service?.addDatabaseListener(databaseListener)
service?.addDatabaseFileInfoListener(databaseInfoListener) databaseInfoListener?.let { infoListener ->
service?.addActionTaskListener(actionTaskListener) service?.addDatabaseFileInfoListener(infoListener)
}
actionTaskListener?.let { taskListener ->
service?.addActionTaskListener(taskListener)
}
} }
private fun removeServiceListeners(service: DatabaseTaskNotificationService.ActionTaskBinder?) { private fun removeServiceListeners(service: DatabaseTaskNotificationService.ActionTaskBinder?) {
service?.removeActionTaskListener(actionTaskListener) actionTaskListener?.let { taskListener ->
service?.removeDatabaseFileInfoListener(databaseInfoListener) service?.removeActionTaskListener(taskListener)
}
databaseInfoListener?.let { infoListener ->
service?.removeDatabaseFileInfoListener(infoListener)
}
service?.removeDatabaseListener(databaseListener) service?.removeDatabaseListener(databaseListener)
} }
@@ -369,58 +251,9 @@ class DatabaseTaskProvider(
} }
} }
private val tempServiceParameters = mutableListOf<Pair<Bundle?, String>>()
private val requestPermissionLauncher = activity?.registerForActivityResult(
ActivityResultContracts.RequestPermission()
) { _ ->
// Whether or not the user has accepted, the service can be started,
// There just won't be any notification if it's not allowed.
tempServiceParameters.removeFirstOrNull()?.let {
startService(it.first, it.second)
}
}
private fun start(bundle: Bundle? = null, actionTask: String) { private fun start(bundle: Bundle? = null, actionTask: String) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { onStartActionRequested?.invoke(bundle, actionTask) ?: run {
val contextActivity = activity context.startDatabaseService(bundle, actionTask)
if (ContextCompat.checkSelfPermission(context, Manifest.permission.POST_NOTIFICATIONS)
== PackageManager.PERMISSION_GRANTED
) {
startService(bundle, actionTask)
} else if (contextActivity != null && shouldShowRequestPermissionRationale(
contextActivity,
Manifest.permission.POST_NOTIFICATIONS
)
) {
// it's not the first time, so the user deliberately chooses not to display the notification
startService(bundle, actionTask)
} else {
AlertDialog.Builder(context)
.setMessage(R.string.warning_database_notification_permission)
.setNegativeButton(R.string.later) { _, _ ->
// Refuses the notification, so start the service
startService(bundle, actionTask)
}
.setPositiveButton(R.string.ask) { _, _ ->
// Save the temp parameters to ask the permission
tempServiceParameters.add(Pair(bundle, actionTask))
requestPermissionLauncher?.launch(Manifest.permission.POST_NOTIFICATIONS)
}.create().show()
}
} else {
startService(bundle, actionTask)
}
}
private fun startService(bundle: Bundle? = null, actionTask: String) {
try {
if (bundle != null)
intentDatabaseTask.putExtras(bundle)
intentDatabaseTask.action = actionTask
context.startService(intentDatabaseTask)
} catch (e: Exception) {
Log.e(TAG, "Unable to perform database action", e)
Toast.makeText(context, R.string.error_start_database_action, Toast.LENGTH_LONG).show()
} }
} }
@@ -843,5 +676,21 @@ class DatabaseTaskProvider(
companion object { companion object {
private val TAG = DatabaseTaskProvider::class.java.name private val TAG = DatabaseTaskProvider::class.java.name
fun Context.startDatabaseService(bundle: Bundle? = null, actionTask: String) {
try {
val intentDatabaseTask = Intent(
applicationContext,
DatabaseTaskNotificationService::class.java
)
if (bundle != null)
intentDatabaseTask.putExtras(bundle)
intentDatabaseTask.action = actionTask
startService(intentDatabaseTask)
} catch (e: Exception) {
Log.e(TAG, "Unable to perform database action", e)
Toast.makeText(this, R.string.error_start_database_action, Toast.LENGTH_LONG).show()
}
}
} }
} }

View File

@@ -27,10 +27,10 @@ class HardwareKeyActivity: DatabaseModeActivity(){
if (result.resultCode == RESULT_OK) { if (result.resultCode == RESULT_OK) {
val challengeResponse: ByteArray? = result.data?.getByteArrayExtra(HARDWARE_KEY_RESPONSE_KEY) val challengeResponse: ByteArray? = result.data?.getByteArrayExtra(HARDWARE_KEY_RESPONSE_KEY)
Log.d(TAG, "Response form challenge") Log.d(TAG, "Response form challenge")
mDatabaseTaskProvider?.startChallengeResponded(challengeResponse ?: ByteArray(0)) mDatabaseViewModel.onChallengeResponded(challengeResponse)
} else { } else {
Log.e(TAG, "Response from challenge error") Log.e(TAG, "Response from challenge error")
mDatabaseTaskProvider?.startChallengeResponded(ByteArray(0)) mDatabaseViewModel.onChallengeResponded(null)
} }
finish() finish()
} }
@@ -49,13 +49,11 @@ class HardwareKeyActivity: DatabaseModeActivity(){
} }
override fun onDatabaseRetrieved(database: ContextualDatabase?) { override fun onDatabaseRetrieved(database: ContextualDatabase?) {
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, true) { if (isHardwareKeyAvailable(this, hardwareKey, true) {
mDatabaseTaskProvider?.startChallengeResponded(ByteArray(0)) mDatabaseViewModel.onChallengeResponded(null)
}) { }) {
when (hardwareKey) { when (hardwareKey) {
/* /*

View File

@@ -176,7 +176,7 @@ open class DatabaseTaskNotificationService : LockNotificationService(), Progress
progressMessage: ProgressMessage progressMessage: ProgressMessage
) )
fun onActionStopped( fun onActionStopped(
database: ContextualDatabase database: ContextualDatabase? = null
) )
fun onActionFinished( fun onActionFinished(
database: ContextualDatabase, database: ContextualDatabase,

View File

@@ -60,6 +60,7 @@ class MainPreferenceFragment : PreferenceFragmentCompat() {
is DatabaseViewModel.UIState.OnDatabaseRetrieved -> { is DatabaseViewModel.UIState.OnDatabaseRetrieved -> {
checkDatabaseLoaded(uiState.database?.loaded == true) checkDatabaseLoaded(uiState.database?.loaded == true)
} }
else -> {}
} }
} }
} }

View File

@@ -21,7 +21,12 @@ package com.kunzisoft.keepass.settings
import android.os.Bundle import android.os.Bundle
import android.util.Log import android.util.Log
import android.view.* import android.view.LayoutInflater
import android.view.Menu
import android.view.MenuInflater
import android.view.MenuItem
import android.view.View
import android.view.ViewGroup
import androidx.core.graphics.toColorInt import androidx.core.graphics.toColorInt
import androidx.core.view.MenuProvider import androidx.core.view.MenuProvider
import androidx.fragment.app.DialogFragment import androidx.fragment.app.DialogFragment
@@ -40,10 +45,29 @@ import com.kunzisoft.keepass.database.crypto.EncryptionAlgorithm
import com.kunzisoft.keepass.database.crypto.kdf.KdfEngine import com.kunzisoft.keepass.database.crypto.kdf.KdfEngine
import com.kunzisoft.keepass.database.element.Group import com.kunzisoft.keepass.database.element.Group
import com.kunzisoft.keepass.database.element.database.CompressionAlgorithm import com.kunzisoft.keepass.database.element.database.CompressionAlgorithm
import com.kunzisoft.keepass.database.helper.* import com.kunzisoft.keepass.database.helper.getLocalizedName
import com.kunzisoft.keepass.services.DatabaseTaskNotificationService import com.kunzisoft.keepass.services.DatabaseTaskNotificationService
import com.kunzisoft.keepass.settings.preference.* import com.kunzisoft.keepass.settings.preference.DialogColorPreference
import com.kunzisoft.keepass.settings.preferencedialogfragment.* import com.kunzisoft.keepass.settings.preference.DialogListExplanationPreference
import com.kunzisoft.keepass.settings.preference.InputKdfNumberPreference
import com.kunzisoft.keepass.settings.preference.InputKdfSizePreference
import com.kunzisoft.keepass.settings.preference.InputNumberPreference
import com.kunzisoft.keepass.settings.preference.InputTextPreference
import com.kunzisoft.keepass.settings.preferencedialogfragment.DatabaseColorPreferenceDialogFragmentCompat
import com.kunzisoft.keepass.settings.preferencedialogfragment.DatabaseDataCompressionPreferenceDialogFragmentCompat
import com.kunzisoft.keepass.settings.preferencedialogfragment.DatabaseDefaultUsernamePreferenceDialogFragmentCompat
import com.kunzisoft.keepass.settings.preferencedialogfragment.DatabaseDescriptionPreferenceDialogFragmentCompat
import com.kunzisoft.keepass.settings.preferencedialogfragment.DatabaseEncryptionAlgorithmPreferenceDialogFragmentCompat
import com.kunzisoft.keepass.settings.preferencedialogfragment.DatabaseKeyDerivationPreferenceDialogFragmentCompat
import com.kunzisoft.keepass.settings.preferencedialogfragment.DatabaseMaxHistoryItemsPreferenceDialogFragmentCompat
import com.kunzisoft.keepass.settings.preferencedialogfragment.DatabaseMaxHistorySizePreferenceDialogFragmentCompat
import com.kunzisoft.keepass.settings.preferencedialogfragment.DatabaseMemoryUsagePreferenceDialogFragmentCompat
import com.kunzisoft.keepass.settings.preferencedialogfragment.DatabaseNamePreferenceDialogFragmentCompat
import com.kunzisoft.keepass.settings.preferencedialogfragment.DatabaseParallelismPreferenceDialogFragmentCompat
import com.kunzisoft.keepass.settings.preferencedialogfragment.DatabaseRecycleBinGroupPreferenceDialogFragmentCompat
import com.kunzisoft.keepass.settings.preferencedialogfragment.DatabaseRemoveUnlinkedDataPreferenceDialogFragmentCompat
import com.kunzisoft.keepass.settings.preferencedialogfragment.DatabaseRoundsPreferenceDialogFragmentCompat
import com.kunzisoft.keepass.settings.preferencedialogfragment.DatabaseTemplatesGroupPreferenceDialogFragmentCompat
import com.kunzisoft.keepass.tasks.ActionRunnable import com.kunzisoft.keepass.tasks.ActionRunnable
import com.kunzisoft.keepass.utils.getParcelableCompat import com.kunzisoft.keepass.utils.getParcelableCompat
import com.kunzisoft.keepass.utils.getSerializableCompat import com.kunzisoft.keepass.utils.getSerializableCompat
@@ -131,13 +155,17 @@ class NestedDatabaseSettingsFragment : NestedSettingsFragment(), DatabaseRetriev
) )
onDatabaseRetrieved(uiState.database) onDatabaseRetrieved(uiState.database)
} }
is DatabaseViewModel.UIState.OnDatabaseActionFinished -> {
onDatabaseActionFinished(
uiState.database,
uiState.actionTask,
uiState.result
)
}
else -> {}
} }
} }
} }
mDatabaseViewModel.actionFinished.observe(viewLifecycleOwner) {
onDatabaseActionFinished(it.database, it.actionTask, it.result)
}
} }
override fun onCreateScreenPreference(screen: Screen, savedInstanceState: Bundle?, rootKey: String?) { override fun onCreateScreenPreference(screen: Screen, savedInstanceState: Bundle?, rootKey: String?) {

View File

@@ -192,7 +192,7 @@ open class SettingsActivity
} }
override fun onAssignKeyDialogPositiveClick(mainCredential: MainCredential) { override fun onAssignKeyDialogPositiveClick(mainCredential: MainCredential) {
assignPassword(mainCredential) assignMainCredential(mainCredential)
} }
override fun onAssignKeyDialogNegativeClick(mainCredential: MainCredential) {} override fun onAssignKeyDialogNegativeClick(mainCredential: MainCredential) {}

View File

@@ -59,6 +59,14 @@ abstract class DatabaseSavePreferenceDialogFragmentCompat
is DatabaseViewModel.UIState.OnDatabaseRetrieved -> { is DatabaseViewModel.UIState.OnDatabaseRetrieved -> {
onDatabaseRetrieved(uiState.database) onDatabaseRetrieved(uiState.database)
} }
is DatabaseViewModel.UIState.OnDatabaseActionFinished -> {
onDatabaseActionFinished(
uiState.database,
uiState.actionTask,
uiState.result
)
}
else -> {}
} }
} }
} }
@@ -81,8 +89,10 @@ abstract class DatabaseSavePreferenceDialogFragmentCompat
// To inherit to save element in database // To inherit to save element in database
} }
protected fun saveColor(oldColor: Int?, protected fun saveColor(
newColor: Int?) { oldColor: Int?,
newColor: Int?
) {
val oldColorString = if (oldColor != null) val oldColorString = if (oldColor != null)
ChromaUtil.getFormattedColorString(oldColor, false) ChromaUtil.getFormattedColorString(oldColor, false)
else else
@@ -91,77 +101,158 @@ abstract class DatabaseSavePreferenceDialogFragmentCompat
ChromaUtil.getFormattedColorString(newColor, false) ChromaUtil.getFormattedColorString(newColor, false)
else else
"" ""
mDatabaseViewModel.saveColor(oldColorString, newColorString, mDatabaseAutoSaveEnable) mDatabaseViewModel.saveColor(
oldColorString,
newColorString,
mDatabaseAutoSaveEnable
)
} }
protected fun saveCompression(oldCompression: CompressionAlgorithm, protected fun saveCompression(
newCompression: CompressionAlgorithm oldCompression: CompressionAlgorithm,
newCompression: CompressionAlgorithm
) { ) {
mDatabaseViewModel.saveCompression(oldCompression, newCompression, mDatabaseAutoSaveEnable) mDatabaseViewModel.saveCompression(
oldCompression,
newCompression,
mDatabaseAutoSaveEnable
)
} }
protected fun saveDefaultUsername(oldUsername: String, protected fun saveDefaultUsername(
newUsername: String) { oldUsername: String,
mDatabaseViewModel.saveDefaultUsername(oldUsername, newUsername, mDatabaseAutoSaveEnable) newUsername: String
) {
mDatabaseViewModel.saveDefaultUsername(
oldUsername,
newUsername,
mDatabaseAutoSaveEnable
)
} }
protected fun saveDescription(oldDescription: String, protected fun saveDescription(
newDescription: String) { oldDescription: String,
mDatabaseViewModel.saveDescription(oldDescription, newDescription, mDatabaseAutoSaveEnable) newDescription: String
) {
mDatabaseViewModel.saveDescription(
oldDescription,
newDescription,
mDatabaseAutoSaveEnable
)
} }
protected fun saveEncryption(oldEncryption: EncryptionAlgorithm, protected fun saveEncryption(
newEncryptionAlgorithm: EncryptionAlgorithm) { oldEncryption: EncryptionAlgorithm,
mDatabaseViewModel.saveEncryption(oldEncryption, newEncryptionAlgorithm, mDatabaseAutoSaveEnable) newEncryptionAlgorithm: EncryptionAlgorithm
) {
mDatabaseViewModel.saveEncryption(
oldEncryption,
newEncryptionAlgorithm,
mDatabaseAutoSaveEnable
)
} }
protected fun saveKeyDerivation(oldKeyDerivation: KdfEngine, protected fun saveKeyDerivation(
newKeyDerivation: KdfEngine) { oldKeyDerivation: KdfEngine,
mDatabaseViewModel.saveKeyDerivation(oldKeyDerivation, newKeyDerivation, mDatabaseAutoSaveEnable) newKeyDerivation: KdfEngine
) {
mDatabaseViewModel.saveKeyDerivation(
oldKeyDerivation,
newKeyDerivation,
mDatabaseAutoSaveEnable
)
} }
protected fun saveName(oldName: String, protected fun saveName(
newName: String) { oldName: String,
mDatabaseViewModel.saveName(oldName, newName, mDatabaseAutoSaveEnable) newName: String
) {
mDatabaseViewModel.saveName(
oldName,
newName,
mDatabaseAutoSaveEnable
)
} }
protected fun saveRecycleBin(oldGroup: Group?, protected fun saveRecycleBin(
newGroup: Group?) { oldGroup: Group?,
mDatabaseViewModel.saveRecycleBin(oldGroup, newGroup, mDatabaseAutoSaveEnable) newGroup: Group?
) {
mDatabaseViewModel.saveRecycleBin(
oldGroup,
newGroup,
mDatabaseAutoSaveEnable
)
} }
protected fun removeUnlinkedData() { protected fun removeUnlinkedData() {
mDatabaseViewModel.removeUnlinkedData(mDatabaseAutoSaveEnable) mDatabaseViewModel.removeUnlinkedData(mDatabaseAutoSaveEnable)
} }
protected fun saveTemplatesGroup(oldGroup: Group?, protected fun saveTemplatesGroup(
newGroup: Group?) { oldGroup: Group?,
mDatabaseViewModel.saveTemplatesGroup(oldGroup, newGroup, mDatabaseAutoSaveEnable) newGroup: Group?
) {
mDatabaseViewModel.saveTemplatesGroup(
oldGroup,
newGroup,
mDatabaseAutoSaveEnable
)
} }
protected fun saveMaxHistoryItems(oldNumber: Int, protected fun saveMaxHistoryItems(
newNumber: Int) { oldNumber: Int,
mDatabaseViewModel.saveMaxHistoryItems(oldNumber, newNumber, mDatabaseAutoSaveEnable) newNumber: Int
) {
mDatabaseViewModel.saveMaxHistoryItems(
oldNumber,
newNumber,
mDatabaseAutoSaveEnable
)
} }
protected fun saveMaxHistorySize(oldNumber: Long, protected fun saveMaxHistorySize(
newNumber: Long) { oldNumber: Long,
mDatabaseViewModel.saveMaxHistorySize(oldNumber, newNumber, mDatabaseAutoSaveEnable) newNumber: Long
) {
mDatabaseViewModel.saveMaxHistorySize(
oldNumber,
newNumber,
mDatabaseAutoSaveEnable
)
} }
protected fun saveMemoryUsage(oldNumber: Long, protected fun saveMemoryUsage(
newNumber: Long) { oldNumber: Long,
mDatabaseViewModel.saveMemoryUsage(oldNumber, newNumber, mDatabaseAutoSaveEnable) newNumber: Long
) {
mDatabaseViewModel.saveMemoryUsage(
oldNumber,
newNumber,
mDatabaseAutoSaveEnable
)
} }
protected fun saveParallelism(oldNumber: Long, protected fun saveParallelism(
newNumber: Long) { oldNumber: Long,
mDatabaseViewModel.saveParallelism(oldNumber, newNumber, mDatabaseAutoSaveEnable) newNumber: Long
) {
mDatabaseViewModel.saveParallelism(
oldNumber,
newNumber,
mDatabaseAutoSaveEnable
)
} }
protected fun saveIterations(oldNumber: Long, protected fun saveIterations(
newNumber: Long) { oldNumber: Long,
mDatabaseViewModel.saveIterations(oldNumber, newNumber, mDatabaseAutoSaveEnable) newNumber: Long
) {
mDatabaseViewModel.saveIterations(
oldNumber,
newNumber,
mDatabaseAutoSaveEnable
)
} }
companion object { companion object {

View File

@@ -1,17 +1,30 @@
package com.kunzisoft.keepass.viewmodels package com.kunzisoft.keepass.viewmodels
import androidx.lifecycle.LiveData import android.app.Application
import androidx.lifecycle.ViewModel import android.net.Uri
import android.os.Bundle
import androidx.lifecycle.AndroidViewModel
import com.kunzisoft.keepass.database.ContextualDatabase import com.kunzisoft.keepass.database.ContextualDatabase
import com.kunzisoft.keepass.database.DatabaseTaskProvider
import com.kunzisoft.keepass.database.MainCredential
import com.kunzisoft.keepass.database.ProgressMessage
import com.kunzisoft.keepass.database.crypto.EncryptionAlgorithm import com.kunzisoft.keepass.database.crypto.EncryptionAlgorithm
import com.kunzisoft.keepass.database.crypto.kdf.KdfEngine import com.kunzisoft.keepass.database.crypto.kdf.KdfEngine
import com.kunzisoft.keepass.database.element.Entry
import com.kunzisoft.keepass.database.element.Group import com.kunzisoft.keepass.database.element.Group
import com.kunzisoft.keepass.database.element.database.CompressionAlgorithm import com.kunzisoft.keepass.database.element.database.CompressionAlgorithm
import com.kunzisoft.keepass.database.element.node.Node
import com.kunzisoft.keepass.database.element.node.NodeId
import com.kunzisoft.keepass.model.CipherEncryptDatabase
import com.kunzisoft.keepass.model.SnapFileDatabaseInfo
import com.kunzisoft.keepass.services.DatabaseTaskNotificationService
import com.kunzisoft.keepass.tasks.ActionRunnable import com.kunzisoft.keepass.tasks.ActionRunnable
import com.kunzisoft.keepass.utils.getBinaryDir
import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.StateFlow
import java.util.UUID
class DatabaseViewModel: ViewModel() { class DatabaseViewModel(application: Application): AndroidViewModel(application) {
var database: ContextualDatabase? = null var database: ContextualDatabase? = null
private set private set
@@ -19,174 +32,438 @@ class DatabaseViewModel: ViewModel() {
private val mUiState = MutableStateFlow<UIState>(UIState.Loading) private val mUiState = MutableStateFlow<UIState>(UIState.Loading)
val uiState: StateFlow<UIState> = mUiState val uiState: StateFlow<UIState> = mUiState
val actionFinished : LiveData<ActionResult> get() = _actionFinished private var mDatabaseTaskProvider: DatabaseTaskProvider = DatabaseTaskProvider(
private val _actionFinished = SingleLiveEvent<ActionResult>() context = application
)
val saveDatabase : LiveData<Boolean> get() = _saveDatabase init {
private val _saveDatabase = SingleLiveEvent<Boolean>() mDatabaseTaskProvider.onDatabaseRetrieved = { databaseRetrieved ->
val databaseWasReloaded = databaseRetrieved?.wasReloaded == true
if (databaseWasReloaded) {
mUiState.value = UIState.OnDatabaseReloaded
}
if (database == null || database != databaseRetrieved || databaseWasReloaded) {
databaseRetrieved?.wasReloaded = false
defineDatabase(databaseRetrieved)
}
}
mDatabaseTaskProvider.onStartActionRequested = { bundle, actionTask ->
mUiState.value = UIState.OnDatabaseActionRequested(bundle, actionTask)
}
mDatabaseTaskProvider.databaseInfoListener = object : DatabaseTaskNotificationService.DatabaseInfoListener {
override fun onDatabaseInfoChanged(
previousDatabaseInfo: SnapFileDatabaseInfo,
newDatabaseInfo: SnapFileDatabaseInfo,
readOnlyDatabase: Boolean
) {
mUiState.value = UIState.OnDatabaseInfoChanged(
previousDatabaseInfo,
newDatabaseInfo,
readOnlyDatabase
)
}
}
mDatabaseTaskProvider.actionTaskListener = object : DatabaseTaskNotificationService.ActionTaskListener {
override fun onActionStarted(
database: ContextualDatabase,
progressMessage: ProgressMessage
) {
mUiState.value = UIState.OnDatabaseActionStarted(database, progressMessage)
}
val mergeDatabase : LiveData<Boolean> get() = _mergeDatabase override fun onActionUpdated(
private val _mergeDatabase = SingleLiveEvent<Boolean>() database: ContextualDatabase,
progressMessage: ProgressMessage
) {
mUiState.value = UIState.OnDatabaseActionUpdated(database, progressMessage)
}
val reloadDatabase : LiveData<Boolean> get() = _reloadDatabase override fun onActionStopped(database: ContextualDatabase?) {
private val _reloadDatabase = SingleLiveEvent<Boolean>() mUiState.value = UIState.OnDatabaseActionStopped(database)
}
val saveName : LiveData<SuperString> get() = _saveName override fun onActionFinished(
private val _saveName = SingleLiveEvent<SuperString>() database: ContextualDatabase,
actionTask: String,
result: ActionRunnable.Result
) {
mUiState.value = UIState.OnDatabaseActionFinished(database, actionTask, result)
}
}
val saveDescription : LiveData<SuperString> get() = _saveDescription mDatabaseTaskProvider.registerProgressTask()
private val _saveDescription = SingleLiveEvent<SuperString>() }
val saveDefaultUsername : LiveData<SuperString> get() = _saveDefaultUsername /*
private val _saveDefaultUsername = SingleLiveEvent<SuperString>() * Main database actions
*/
val saveColor : LiveData<SuperString> get() = _saveColor private fun defineDatabase(database: ContextualDatabase?) {
private val _saveColor = SingleLiveEvent<SuperString>()
val saveCompression : LiveData<SuperCompression> get() = _saveCompression
private val _saveCompression = SingleLiveEvent<SuperCompression>()
val removeUnlinkData : LiveData<Boolean> get() = _removeUnlinkData
private val _removeUnlinkData = SingleLiveEvent<Boolean>()
val saveRecycleBin : LiveData<SuperGroup> get() = _saveRecycleBin
private val _saveRecycleBin = SingleLiveEvent<SuperGroup>()
val saveTemplatesGroup : LiveData<SuperGroup> get() = _saveTemplatesGroup
private val _saveTemplatesGroup = SingleLiveEvent<SuperGroup>()
val saveMaxHistoryItems : LiveData<SuperInt> get() = _saveMaxHistoryItems
private val _saveMaxHistoryItems = SingleLiveEvent<SuperInt>()
val saveMaxHistorySize : LiveData<SuperLong> get() = _saveMaxHistorySize
private val _saveMaxHistorySize = SingleLiveEvent<SuperLong>()
val saveEncryption : LiveData<SuperEncryption> get() = _saveEncryption
private val _saveEncryption = SingleLiveEvent<SuperEncryption>()
val saveKeyDerivation : LiveData<SuperKeyDerivation> get() = _saveKeyDerivation
private val _saveKeyDerivation = SingleLiveEvent<SuperKeyDerivation>()
val saveIterations : LiveData<SuperLong> get() = _saveIterations
private val _saveIterations = SingleLiveEvent<SuperLong>()
val saveMemoryUsage : LiveData<SuperLong> get() = _saveMemoryUsage
private val _saveMemoryUsage = SingleLiveEvent<SuperLong>()
val saveParallelism : LiveData<SuperLong> get() = _saveParallelism
private val _saveParallelism = SingleLiveEvent<SuperLong>()
fun defineDatabase(database: ContextualDatabase?) {
this.database = database this.database = database
this.mUiState.value = UIState.OnDatabaseRetrieved(database) this.mUiState.value = UIState.OnDatabaseRetrieved(database)
} }
fun onActionFinished(database: ContextualDatabase, fun loadDatabase(
actionTask: String, databaseUri: Uri,
result: ActionRunnable.Result) { mainCredential: MainCredential,
this._actionFinished.value = ActionResult(database, actionTask, result) readOnly: Boolean,
cipherEncryptDatabase: CipherEncryptDatabase?,
fixDuplicateUuid: Boolean
) {
mDatabaseTaskProvider.startDatabaseLoad(
databaseUri,
mainCredential,
readOnly,
cipherEncryptDatabase,
fixDuplicateUuid
)
} }
fun saveDatabase(save: Boolean) { fun createDatabase(
_saveDatabase.value = save databaseUri: Uri,
mainCredential: MainCredential
) {
mDatabaseTaskProvider.startDatabaseCreate(databaseUri, mainCredential)
} }
fun mergeDatabase(save: Boolean) { fun assignMainCredential(
_mergeDatabase.value = save databaseUri: Uri?,
mainCredential: MainCredential
) {
if (databaseUri != null) {
mDatabaseTaskProvider.startDatabaseAssignCredential(databaseUri, mainCredential)
}
}
fun saveDatabase(save: Boolean, saveToUri: Uri? = null) {
mDatabaseTaskProvider.startDatabaseSave(save, saveToUri)
}
fun mergeDatabase(
save: Boolean,
fromDatabaseUri: Uri? = null,
mainCredential: MainCredential? = null
) {
mDatabaseTaskProvider.startDatabaseMerge(save, fromDatabaseUri, mainCredential)
} }
fun reloadDatabase(fixDuplicateUuid: Boolean) { fun reloadDatabase(fixDuplicateUuid: Boolean) {
_reloadDatabase.value = fixDuplicateUuid mDatabaseTaskProvider.askToStartDatabaseReload(
conditionToAsk = database?.dataModifiedSinceLastLoading != false
) {
mDatabaseTaskProvider.startDatabaseReload(fixDuplicateUuid)
}
} }
fun saveName(oldValue: String, fun closeDatabase() {
newValue: String, database?.clearAndClose(getApplication<Application>().getBinaryDir())
save: Boolean) {
_saveName.value = SuperString(oldValue, newValue, save)
} }
fun saveDescription(oldValue: String, fun onDatabaseChangeValidated() {
newValue: String, mDatabaseTaskProvider.onDatabaseChangeValidated()
save: Boolean) {
_saveDescription.value = SuperString(oldValue, newValue, save)
} }
fun saveDefaultUsername(oldValue: String, /*
newValue: String, * Nodes actions
save: Boolean) { */
_saveDefaultUsername.value = SuperString(oldValue, newValue, save)
fun createEntry(
newEntry: Entry,
parent: Group,
save: Boolean
) {
mDatabaseTaskProvider.startDatabaseCreateEntry(
newEntry,
parent,
save
)
} }
fun saveColor(oldValue: String, fun updateEntry(
newValue: String, oldEntry: Entry,
save: Boolean) { entryToUpdate: Entry,
_saveColor.value = SuperString(oldValue, newValue, save) save: Boolean
) {
mDatabaseTaskProvider.startDatabaseUpdateEntry(
oldEntry,
entryToUpdate,
save
)
} }
fun saveCompression(oldValue: CompressionAlgorithm, fun restoreEntryHistory(
newValue: CompressionAlgorithm, mainEntryId: NodeId<UUID>,
save: Boolean) { entryHistoryPosition: Int,
_saveCompression.value = SuperCompression(oldValue, newValue, save) save: Boolean
) {
mDatabaseTaskProvider.startDatabaseRestoreEntryHistory(
mainEntryId,
entryHistoryPosition,
save
)
}
fun deleteEntryHistory(
mainEntryId: NodeId<UUID>,
entryHistoryPosition: Int,
save: Boolean
) {
mDatabaseTaskProvider.startDatabaseDeleteEntryHistory(
mainEntryId,
entryHistoryPosition,
save
)
}
fun createGroup(
newGroup: Group,
parent: Group,
save: Boolean
) {
mDatabaseTaskProvider.startDatabaseCreateGroup(
newGroup,
parent,
save
)
}
fun updateGroup(
oldGroup: Group,
groupToUpdate: Group,
save: Boolean
) {
mDatabaseTaskProvider.startDatabaseUpdateGroup(
oldGroup,
groupToUpdate,
save
)
}
fun copyNodes(
nodesToCopy: List<Node>,
newParent: Group,
save: Boolean
) {
mDatabaseTaskProvider.startDatabaseCopyNodes(
nodesToCopy,
newParent,
save
)
}
fun moveNodes(
nodesToMove: List<Node>,
newParent: Group,
save: Boolean
) {
mDatabaseTaskProvider.startDatabaseMoveNodes(
nodesToMove,
newParent,
save
)
}
fun deleteNodes(
nodes: List<Node>,
save: Boolean
) {
mDatabaseTaskProvider.startDatabaseDeleteNodes(
nodes,
save
)
}
/*
* Settings actions
*/
fun saveName(
oldValue: String,
newValue: String,
save: Boolean
) {
mDatabaseTaskProvider.startDatabaseSaveName(
oldValue,
newValue,
save
)
}
fun saveDescription(
oldValue: String,
newValue: String,
save: Boolean
) {
mDatabaseTaskProvider.startDatabaseSaveDescription(
oldValue,
newValue,
save
)
}
fun saveDefaultUsername(
oldValue: String,
newValue: String,
save: Boolean
) {
mDatabaseTaskProvider.startDatabaseSaveDefaultUsername(
oldValue,
newValue,
save
)
}
fun saveColor(
oldValue: String,
newValue: String,
save: Boolean
) {
mDatabaseTaskProvider.startDatabaseSaveColor(
oldValue,
newValue,
save
)
}
fun saveCompression(
oldValue: CompressionAlgorithm,
newValue: CompressionAlgorithm,
save: Boolean
) {
mDatabaseTaskProvider.startDatabaseSaveCompression(
oldValue,
newValue,
save
)
} }
fun removeUnlinkedData(save: Boolean) { fun removeUnlinkedData(save: Boolean) {
_removeUnlinkData.value = save mDatabaseTaskProvider.startDatabaseRemoveUnlinkedData(save)
} }
fun saveRecycleBin(oldValue: Group?, fun saveRecycleBin(
newValue: Group?, oldValue: Group?,
save: Boolean) { newValue: Group?,
_saveRecycleBin.value = SuperGroup(oldValue, newValue, save) save: Boolean
) {
mDatabaseTaskProvider.startDatabaseSaveRecycleBin(
oldValue,
newValue,
save
)
} }
fun saveTemplatesGroup(oldValue: Group?, fun saveTemplatesGroup(
newValue: Group?, oldValue: Group?,
save: Boolean) { newValue: Group?,
_saveTemplatesGroup.value = SuperGroup(oldValue, newValue, save) save: Boolean
) {
mDatabaseTaskProvider.startDatabaseSaveTemplatesGroup(
oldValue,
newValue,
save
)
} }
fun saveMaxHistoryItems(oldValue: Int, fun saveMaxHistoryItems(
newValue: Int, oldValue: Int,
save: Boolean) { newValue: Int,
_saveMaxHistoryItems.value = SuperInt(oldValue, newValue, save) save: Boolean
) {
mDatabaseTaskProvider.startDatabaseSaveMaxHistoryItems(
oldValue,
newValue,
save
)
} }
fun saveMaxHistorySize(oldValue: Long, fun saveMaxHistorySize(
newValue: Long, oldValue: Long,
save: Boolean) { newValue: Long,
_saveMaxHistorySize.value = SuperLong(oldValue, newValue, save) save: Boolean
) {
mDatabaseTaskProvider.startDatabaseSaveMaxHistorySize(
oldValue,
newValue,
save
)
} }
fun saveEncryption(oldValue: EncryptionAlgorithm, fun saveEncryption(
newValue: EncryptionAlgorithm, oldValue: EncryptionAlgorithm,
save: Boolean) { newValue: EncryptionAlgorithm,
_saveEncryption.value = SuperEncryption(oldValue, newValue, save) save: Boolean
) {
mDatabaseTaskProvider.startDatabaseSaveEncryption(
oldValue,
newValue,
save
)
} }
fun saveKeyDerivation(oldValue: KdfEngine, fun saveKeyDerivation(
newValue: KdfEngine, oldValue: KdfEngine,
save: Boolean) { newValue: KdfEngine,
_saveKeyDerivation.value = SuperKeyDerivation(oldValue, newValue, save) save: Boolean
) {
mDatabaseTaskProvider.startDatabaseSaveKeyDerivation(
oldValue,
newValue,
save
)
} }
fun saveIterations(oldValue: Long, fun saveIterations(
newValue: Long, oldValue: Long,
save: Boolean) { newValue: Long,
_saveIterations.value = SuperLong(oldValue, newValue, save) save: Boolean
) {
mDatabaseTaskProvider.startDatabaseSaveIterations(
oldValue,
newValue,
save
)
} }
fun saveMemoryUsage(oldValue: Long, fun saveMemoryUsage(
newValue: Long, oldValue: Long,
save: Boolean) { newValue: Long,
_saveMemoryUsage.value = SuperLong(oldValue, newValue, save) save: Boolean
) {
mDatabaseTaskProvider.startDatabaseSaveMemoryUsage(
oldValue,
newValue,
save
)
} }
fun saveParallelism(oldValue: Long, fun saveParallelism(
newValue: Long, oldValue: Long,
save: Boolean) { newValue: Long,
_saveParallelism.value = SuperLong(oldValue, newValue, save) save: Boolean
) {
mDatabaseTaskProvider.startDatabaseSaveParallelism(
oldValue,
newValue,
save
)
}
/*
* Hardware Key
*/
fun onChallengeResponded(challengeResponse: ByteArray?) {
mDatabaseTaskProvider.startChallengeResponded(
challengeResponse ?: ByteArray(0)
)
}
override fun onCleared() {
super.onCleared()
mDatabaseTaskProvider.unregisterProgressTask()
mDatabaseTaskProvider.destroy()
} }
sealed class UIState { sealed class UIState {
@@ -194,33 +471,31 @@ class DatabaseViewModel: ViewModel() {
data class OnDatabaseRetrieved( data class OnDatabaseRetrieved(
val database: ContextualDatabase? val database: ContextualDatabase?
): UIState() ): UIState()
object OnDatabaseReloaded: UIState()
data class OnDatabaseActionRequested(
val bundle: Bundle? = null,
val actionTask: String
): UIState()
data class OnDatabaseInfoChanged(
val previousDatabaseInfo: SnapFileDatabaseInfo,
val newDatabaseInfo: SnapFileDatabaseInfo,
val readOnlyDatabase: Boolean
): UIState()
data class OnDatabaseActionStarted(
val database: ContextualDatabase,
val progressMessage: ProgressMessage
): UIState()
data class OnDatabaseActionUpdated(
val database: ContextualDatabase,
val progressMessage: ProgressMessage
): UIState()
data class OnDatabaseActionStopped(
val database: ContextualDatabase?
): UIState()
data class OnDatabaseActionFinished(
val database: ContextualDatabase,
val actionTask: String,
val result: ActionRunnable.Result
): UIState()
} }
data class ActionResult(val database: ContextualDatabase,
val actionTask: String,
val result: ActionRunnable.Result)
data class SuperString(val oldValue: String,
val newValue: String,
val save: Boolean)
data class SuperInt(val oldValue: Int,
val newValue: Int,
val save: Boolean)
data class SuperLong(val oldValue: Long,
val newValue: Long,
val save: Boolean)
data class SuperMerge(val fixDuplicateUuid: Boolean,
val save: Boolean)
data class SuperCompression(val oldValue: CompressionAlgorithm,
val newValue: CompressionAlgorithm,
val save: Boolean)
data class SuperEncryption(val oldValue: EncryptionAlgorithm,
val newValue: EncryptionAlgorithm,
val save: Boolean)
data class SuperKeyDerivation(val oldValue: KdfEngine,
val newValue: KdfEngine,
val save: Boolean)
data class SuperGroup(val oldValue: Group?,
val newValue: Group?,
val save: Boolean)
} }