diff --git a/app/src/main/java/com/kunzisoft/keepass/activities/FileDatabaseSelectActivity.kt b/app/src/main/java/com/kunzisoft/keepass/activities/FileDatabaseSelectActivity.kt index 03fea2da7..103156dd9 100644 --- a/app/src/main/java/com/kunzisoft/keepass/activities/FileDatabaseSelectActivity.kt +++ b/app/src/main/java/com/kunzisoft/keepass/activities/FileDatabaseSelectActivity.kt @@ -217,7 +217,6 @@ class FileDatabaseSelectActivity : DatabaseModeActivity(), } override fun onDatabaseRetrieved(database: ContextualDatabase?) { - super.onDatabaseRetrieved(database) if (database != null) { launchGroupActivityIfLoaded(database) } @@ -228,8 +227,6 @@ class FileDatabaseSelectActivity : DatabaseModeActivity(), actionTask: String, result: ActionRunnable.Result ) { - super.onDatabaseActionFinished(database, actionTask, result) - if (result.isSuccess) { // Update list when (actionTask) { @@ -392,7 +389,7 @@ class FileDatabaseSelectActivity : DatabaseModeActivity(), try { mDatabaseFileUri?.let { databaseUri -> // Create the new database - createDatabase(databaseUri, mainCredential) + mDatabaseViewModel.createDatabase(databaseUri, mainCredential) } } catch (e: Exception) { val error = getString(R.string.error_create_database_file) diff --git a/app/src/main/java/com/kunzisoft/keepass/activities/MainCredentialActivity.kt b/app/src/main/java/com/kunzisoft/keepass/activities/MainCredentialActivity.kt index 33883d363..32e6bd042 100644 --- a/app/src/main/java/com/kunzisoft/keepass/activities/MainCredentialActivity.kt +++ b/app/src/main/java/com/kunzisoft/keepass/activities/MainCredentialActivity.kt @@ -587,7 +587,7 @@ class MainCredentialActivity : DatabaseModeActivity() { readOnly: Boolean, cipherEncryptDatabase: CipherEncryptDatabase?, fixDuplicateUUID: Boolean) { - loadDatabase( + mDatabaseViewModel.loadDatabase( databaseUri, mainCredential, readOnly, diff --git a/app/src/main/java/com/kunzisoft/keepass/activities/dialogs/DatabaseChangedDialogFragment.kt b/app/src/main/java/com/kunzisoft/keepass/activities/dialogs/DatabaseChangedDialogFragment.kt index ae4f5a75b..8b2bf24da 100644 --- a/app/src/main/java/com/kunzisoft/keepass/activities/dialogs/DatabaseChangedDialogFragment.kt +++ b/app/src/main/java/com/kunzisoft/keepass/activities/dialogs/DatabaseChangedDialogFragment.kt @@ -67,7 +67,7 @@ class DatabaseChangedDialogFragment : DatabaseDialogFragment() { } builder.setMessage(stringBuilder) builder.setPositiveButton(android.R.string.ok) { _, _ -> - actionDatabaseListener?.validateDatabaseChanged() + actionDatabaseListener?.onDatabaseChangeValidated() } return builder.create() } @@ -76,7 +76,7 @@ class DatabaseChangedDialogFragment : DatabaseDialogFragment() { } interface ActionDatabaseChangedListener { - fun validateDatabaseChanged() + fun onDatabaseChangeValidated() } companion object { diff --git a/app/src/main/java/com/kunzisoft/keepass/activities/dialogs/DatabaseDialogFragment.kt b/app/src/main/java/com/kunzisoft/keepass/activities/dialogs/DatabaseDialogFragment.kt index b74572873..abfd26ce7 100644 --- a/app/src/main/java/com/kunzisoft/keepass/activities/dialogs/DatabaseDialogFragment.kt +++ b/app/src/main/java/com/kunzisoft/keepass/activities/dialogs/DatabaseDialogFragment.kt @@ -30,13 +30,17 @@ abstract class DatabaseDialogFragment : DialogFragment(), DatabaseRetrieval { resetAppTimeoutOnTouchOrFocus() 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?) { diff --git a/app/src/main/java/com/kunzisoft/keepass/activities/fragments/DatabaseFragment.kt b/app/src/main/java/com/kunzisoft/keepass/activities/fragments/DatabaseFragment.kt index ecd00b34a..a1f6738b6 100644 --- a/app/src/main/java/com/kunzisoft/keepass/activities/fragments/DatabaseFragment.kt +++ b/app/src/main/java/com/kunzisoft/keepass/activities/fragments/DatabaseFragment.kt @@ -4,7 +4,9 @@ import android.os.Bundle import android.view.View import androidx.fragment.app.Fragment import androidx.fragment.app.activityViewModels +import androidx.lifecycle.Lifecycle import androidx.lifecycle.lifecycleScope +import androidx.lifecycle.repeatOnLifecycle import com.kunzisoft.keepass.activities.legacy.DatabaseRetrieval import com.kunzisoft.keepass.activities.legacy.resetAppTimeoutWhenViewTouchedOrFocused import com.kunzisoft.keepass.database.ContextualDatabase @@ -20,21 +22,26 @@ abstract class DatabaseFragment : Fragment(), DatabaseRetrieval { override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) - lifecycleScope.launch { - // Initialize the parameters - mDatabaseViewModel.uiState.collect { uiState -> - when (uiState) { - is DatabaseViewModel.UIState.Loading -> {} - is DatabaseViewModel.UIState.OnDatabaseRetrieved -> { - onDatabaseRetrieved(uiState.database) + viewLifecycleOwner.lifecycleScope.launch { + repeatOnLifecycle(Lifecycle.State.STARTED) { + mDatabaseViewModel.uiState.collect { uiState -> + when (uiState) { + is DatabaseViewModel.UIState.Loading -> {} + is DatabaseViewModel.UIState.OnDatabaseRetrieved -> { + 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?) { diff --git a/app/src/main/java/com/kunzisoft/keepass/activities/legacy/DatabaseActivity.kt b/app/src/main/java/com/kunzisoft/keepass/activities/legacy/DatabaseActivity.kt index 3f11a3e19..868c933d1 100644 --- a/app/src/main/java/com/kunzisoft/keepass/activities/legacy/DatabaseActivity.kt +++ b/app/src/main/java/com/kunzisoft/keepass/activities/legacy/DatabaseActivity.kt @@ -1,58 +1,120 @@ 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 androidx.activity.result.contract.ActivityResultContracts 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.database.ContextualDatabase -import com.kunzisoft.keepass.database.DatabaseTaskProvider -import com.kunzisoft.keepass.database.MainCredential -import com.kunzisoft.keepass.model.CipherEncryptDatabase +import com.kunzisoft.keepass.database.DatabaseTaskProvider.Companion.startDatabaseService +import com.kunzisoft.keepass.database.ProgressMessage +import com.kunzisoft.keepass.model.SnapFileDatabaseInfo 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 kotlinx.coroutines.launch abstract class DatabaseActivity : StylishActivity(), DatabaseRetrieval { protected val mDatabaseViewModel: DatabaseViewModel by viewModels() - protected var mDatabaseTaskProvider: DatabaseTaskProvider? = null - protected var mDatabase: ContextualDatabase? = null + protected val mDatabase: ContextualDatabase? + 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>() + 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?) { 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 -> - val databaseWasReloaded = database?.wasReloaded == true - if (databaseWasReloaded && finishActivityIfReloadRequested()) { - finish() - } else if (mDatabase == null || mDatabase != database || databaseWasReloaded) { - database?.wasReloaded = false - onDatabaseRetrieved(database) + is DatabaseViewModel.UIState.OnDatabaseInfoChanged -> { + showDatabaseChangedDialog( + uiState.previousDatabaseInfo, + uiState.newDatabaseInfo, + uiState.readOnlyDatabase + ) + } + + 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?) { - mDatabase = database - mDatabaseViewModel.defineDatabase(database) // optional method implementation } @@ -61,44 +123,100 @@ abstract class DatabaseActivity : StylishActivity(), DatabaseRetrieval { actionTask: String, result: ActionRunnable.Result ) { - mDatabaseViewModel.onActionFinished(database, actionTask, result) // optional method implementation } - fun createDatabase( - databaseUri: Uri, - mainCredential: MainCredential + private fun startDatabasePermissionService(bundle: Bundle?, actionTask: String) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { + 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( - databaseUri: Uri, - mainCredential: MainCredential, - readOnly: Boolean, - cipherEncryptDatabase: CipherEncryptDatabase?, - fixDuplicateUuid: Boolean - ) { - mDatabaseTaskProvider?.startDatabaseLoad( - databaseUri, - mainCredential, - readOnly, - cipherEncryptDatabase, - fixDuplicateUuid - ) + private fun startDialog(progressMessage: ProgressMessage) { + lifecycleScope.launch { + if (progressTaskDialogFragment == null) { + progressTaskDialogFragment = supportFragmentManager + .findFragmentByTag(PROGRESS_TASK_DIALOG_TAG) as ProgressTaskDialogFragment? + } + if (progressTaskDialogFragment == null) { + progressTaskDialogFragment = ProgressTaskDialogFragment() + progressTaskDialogFragment?.show( + supportFragmentManager, + PROGRESS_TASK_DIALOG_TAG + ) + } + updateDialog(progressMessage) + } } - protected fun closeDatabase() { - mDatabase?.clearAndClose(this.getBinaryDir()) + private fun updateDialog(progressMessage: ProgressMessage) { + progressTaskDialogFragment?.apply { + updateTitle(progressMessage.titleId) + updateMessage(progressMessage.messageId) + updateWarning(progressMessage.warningId) + setCancellable(progressMessage.cancelable) + } } - override fun onResume() { - super.onResume() - mDatabaseTaskProvider?.registerProgressTask() + private fun stopDialog() { + progressTaskDialogFragment?.dismissAllowingStateLoss() + progressTaskDialogFragment = null } - - override fun onPause() { - mDatabaseTaskProvider?.unregisterProgressTask() - super.onPause() + + protected open fun showDatabaseDialog(): Boolean { + return true } } \ No newline at end of file diff --git a/app/src/main/java/com/kunzisoft/keepass/activities/legacy/DatabaseLockActivity.kt b/app/src/main/java/com/kunzisoft/keepass/activities/legacy/DatabaseLockActivity.kt index 70d0ec1c4..ad339681a 100644 --- a/app/src/main/java/com/kunzisoft/keepass/activities/legacy/DatabaseLockActivity.kt +++ b/app/src/main/java/com/kunzisoft/keepass/activities/legacy/DatabaseLockActivity.kt @@ -87,80 +87,6 @@ abstract class DatabaseLockActivity : DatabaseModeActivity(), 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 } @@ -169,8 +95,6 @@ abstract class DatabaseLockActivity : DatabaseModeActivity(), } override fun onDatabaseRetrieved(database: ContextualDatabase?) { - super.onDatabaseRetrieved(database) - // End activity if database not loaded if (finishActivityIfDatabaseNotLoaded() && (database == null || !database.loaded)) { finish() @@ -186,7 +110,6 @@ abstract class DatabaseLockActivity : DatabaseModeActivity(), if (mTimeoutEnable) { if (mLockReceiver == null) { mLockReceiver = LockReceiver { - mDatabase = null closeDatabase(database) mExitLock = true closeOptionsMenu() @@ -227,7 +150,6 @@ abstract class DatabaseLockActivity : DatabaseModeActivity(), actionTask: String, result: ActionRunnable.Result ) { - super.onDatabaseActionFinished(database, actionTask, result) when (actionTask) { DatabaseTaskNotificationService.ACTION_DATABASE_MERGE_TASK, DatabaseTaskNotificationService.ACTION_DATABASE_RELOAD_TASK -> { @@ -249,24 +171,15 @@ abstract class DatabaseLockActivity : DatabaseModeActivity(), databaseUri: Uri?, mainCredential: MainCredential ) { - assignDatabasePassword(databaseUri, mainCredential) + mDatabaseViewModel.assignMainCredential(databaseUri, mainCredential) } - private fun assignDatabasePassword( - databaseUri: Uri?, - mainCredential: MainCredential - ) { - if (databaseUri != null) { - mDatabaseTaskProvider?.startDatabaseAssignCredential(databaseUri, mainCredential) - } - } - - fun assignPassword(mainCredential: MainCredential) { + fun assignMainCredential(mainCredential: MainCredential) { mDatabase?.let { database -> database.fileUri?.let { databaseUri -> // Show the progress dialog now or after dialog confirmation if (database.isValidCredential(mainCredential.toMasterCredential(contentResolver))) { - assignDatabasePassword(databaseUri, mainCredential) + mDatabaseViewModel.assignMainCredential(databaseUri, mainCredential) } else { PasswordEncodingDialogFragment.getInstance(databaseUri, mainCredential) .show(supportFragmentManager, "passwordEncodingTag") @@ -276,45 +189,51 @@ abstract class DatabaseLockActivity : DatabaseModeActivity(), } fun saveDatabase() { - mDatabaseTaskProvider?.startDatabaseSave(true) + mDatabaseViewModel.saveDatabase(save = true) } fun saveDatabaseTo(uri: Uri) { - mDatabaseTaskProvider?.startDatabaseSave(true, uri) + mDatabaseViewModel.saveDatabase(save = true, saveToUri = uri) } fun mergeDatabase() { - mDatabaseTaskProvider?.startDatabaseMerge(mAutoSaveEnable) + mDatabaseViewModel.mergeDatabase(save = mAutoSaveEnable) } fun mergeDatabaseFrom(uri: Uri, mainCredential: MainCredential) { - mDatabaseTaskProvider?.startDatabaseMerge(mAutoSaveEnable, uri, mainCredential) + mDatabaseViewModel.mergeDatabase(mAutoSaveEnable, uri, mainCredential) } fun reloadDatabase() { - mDatabaseTaskProvider?.askToStartDatabaseReload(mDatabase?.dataModifiedSinceLastLoading != false) { - mDatabaseTaskProvider?.startDatabaseReload(false) - } + mDatabaseViewModel.reloadDatabase(fixDuplicateUuid = false) } - fun createEntry(newEntry: Entry, - parent: Group) { - mDatabaseTaskProvider?.startDatabaseCreateEntry(newEntry, parent, mAutoSaveEnable) + fun createEntry( + newEntry: Entry, + parent: Group + ) { + mDatabaseViewModel.createEntry(newEntry, parent, mAutoSaveEnable) } - fun updateEntry(oldEntry: Entry, - entryToUpdate: Entry) { - mDatabaseTaskProvider?.startDatabaseUpdateEntry(oldEntry, entryToUpdate, mAutoSaveEnable) + fun updateEntry( + oldEntry: Entry, + entryToUpdate: Entry + ) { + mDatabaseViewModel.updateEntry(oldEntry, entryToUpdate, mAutoSaveEnable) } - fun copyNodes(nodesToCopy: List, - newParent: Group) { - mDatabaseTaskProvider?.startDatabaseCopyNodes(nodesToCopy, newParent, mAutoSaveEnable) + fun copyNodes( + nodesToCopy: List, + newParent: Group + ) { + mDatabaseViewModel.copyNodes(nodesToCopy, newParent, mAutoSaveEnable) } - fun moveNodes(nodesToMove: List, - newParent: Group) { - mDatabaseTaskProvider?.startDatabaseMoveNodes(nodesToMove, newParent, mAutoSaveEnable) + fun moveNodes( + nodesToMove: List, + newParent: Group + ) { + mDatabaseViewModel.moveNodes(nodesToMove, newParent, mAutoSaveEnable) } private fun eachNodeRecyclable(database: ContextualDatabase, nodes: List): Boolean { @@ -330,6 +249,7 @@ abstract class DatabaseLockActivity : DatabaseModeActivity(), } fun deleteNodes(nodes: List, recycleBin: Boolean = false) { + // TODO Move in ViewModel mDatabase?.let { database -> // If recycle bin enabled, ensure it exists if (database.isRecycleBinEnabled) { @@ -350,11 +270,14 @@ abstract class DatabaseLockActivity : DatabaseModeActivity(), } private fun deleteDatabaseNodes(nodes: List) { - mDatabaseTaskProvider?.startDatabaseDeleteNodes(nodes, mAutoSaveEnable) + mDatabaseViewModel.deleteNodes(nodes, mAutoSaveEnable) } - fun createGroup(parent: Group, - groupInfo: GroupInfo?) { + fun createGroup( + parent: Group, + groupInfo: GroupInfo? + ) { + // TODO Move in ViewModel // Build the group mDatabase?.createGroup()?.let { newGroup -> groupInfo?.let { info -> @@ -362,12 +285,15 @@ abstract class DatabaseLockActivity : DatabaseModeActivity(), } // Not really needed here because added in runnable but safe newGroup.parent = parent - mDatabaseTaskProvider?.startDatabaseCreateGroup(newGroup, parent, mAutoSaveEnable) + mDatabaseViewModel.createGroup(newGroup, parent, mAutoSaveEnable) } } - fun updateGroup(oldGroup: Group, - groupInfo: GroupInfo) { + fun updateGroup( + oldGroup: Group, + groupInfo: GroupInfo + ) { + // TODO Move in ViewModel // If group updated save it in the database val updateGroup = Group(oldGroup).let { updateGroup -> updateGroup.apply { @@ -377,18 +303,21 @@ abstract class DatabaseLockActivity : DatabaseModeActivity(), this.setGroupInfo(groupInfo) } } - mDatabaseTaskProvider?.startDatabaseUpdateGroup(oldGroup, updateGroup, mAutoSaveEnable) + mDatabaseViewModel.updateGroup(oldGroup, updateGroup, mAutoSaveEnable) } - fun restoreEntryHistory(mainEntryId: NodeId, - entryHistoryPosition: Int) { - mDatabaseTaskProvider - ?.startDatabaseRestoreEntryHistory(mainEntryId, entryHistoryPosition, mAutoSaveEnable) + fun restoreEntryHistory( + mainEntryId: NodeId, + entryHistoryPosition: Int + ) { + mDatabaseViewModel.restoreEntryHistory(mainEntryId, entryHistoryPosition, mAutoSaveEnable) } - fun deleteEntryHistory(mainEntryId: NodeId, - entryHistoryPosition: Int) { - mDatabaseTaskProvider?.startDatabaseDeleteEntryHistory(mainEntryId, entryHistoryPosition, mAutoSaveEnable) + fun deleteEntryHistory( + mainEntryId: NodeId, + entryHistoryPosition: Int + ) { + mDatabaseViewModel.deleteEntryHistory(mainEntryId, entryHistoryPosition, mAutoSaveEnable) } private fun checkRegister() { diff --git a/app/src/main/java/com/kunzisoft/keepass/database/DatabaseTaskProvider.kt b/app/src/main/java/com/kunzisoft/keepass/database/DatabaseTaskProvider.kt index 99133c0aa..ae2c54760 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/DatabaseTaskProvider.kt +++ b/app/src/main/java/com/kunzisoft/keepass/database/DatabaseTaskProvider.kt @@ -19,7 +19,6 @@ */ package com.kunzisoft.keepass.database -import android.Manifest import android.content.BroadcastReceiver import android.content.ComponentName import android.content.Context @@ -29,23 +28,16 @@ import android.content.Context.BIND_IMPORTANT import android.content.Intent import android.content.IntentFilter import android.content.ServiceConnection -import android.content.pm.PackageManager import android.net.Uri -import android.os.Build import android.os.Bundle import android.os.IBinder import android.util.Log import android.widget.Toast -import androidx.activity.result.contract.ActivityResultContracts import androidx.appcompat.app.AlertDialog -import androidx.core.app.ActivityCompat.shouldShowRequestPermissionRationale import androidx.core.content.ContextCompat 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.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.kdf.KdfEngine 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.Type import com.kunzisoft.keepass.model.CipherEncryptDatabase -import com.kunzisoft.keepass.model.SnapFileDatabaseInfo import com.kunzisoft.keepass.services.DatabaseTaskNotificationService import com.kunzisoft.keepass.services.DatabaseTaskNotificationService.Companion.ACTION_CHALLENGE_RESPONDED 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_TEMPLATES_GROUP_TASK 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_STOP_TASK_ACTION import com.kunzisoft.keepass.utils.putParcelableList -import kotlinx.coroutines.launch import java.util.UUID /** @@ -103,121 +90,41 @@ import java.util.UUID * Useful to retrieve a database instance and sending tasks commands */ class DatabaseTaskProvider( - private var context: Context, - private var showDialog: Boolean = true + private var context: Context ) { - // 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 onActionFinish: (( - database: ContextualDatabase, - actionTask: String, - result: ActionRunnable.Result - ) -> Unit)? = null - - private var intentDatabaseTask: Intent = Intent( - context.applicationContext, - DatabaseTaskNotificationService::class.java - ) + var onStartActionRequested: ((bundle: Bundle?, actionTask: String) -> Unit)? = null + var actionTaskListener: DatabaseTaskNotificationService.ActionTaskListener? = null + var databaseInfoListener: DatabaseTaskNotificationService.DatabaseInfoListener? = null private var databaseTaskBroadcastReceiver: BroadcastReceiver? = null private var mBinder: DatabaseTaskNotificationService.ActionTaskBinder? = null private var serviceConnection: ServiceConnection? = null - private var progressTaskDialogFragment: ProgressTaskDialogFragment? = null - private var databaseChangedDialogFragment: DatabaseChangedDialogFragment? = null + private var intentDatabaseTask: Intent = Intent( + context.applicationContext, + DatabaseTaskNotificationService::class.java + ) fun destroy() { - this.activity = null this.onDatabaseRetrieved = null - this.onActionFinish = null this.databaseTaskBroadcastReceiver = null this.mBinder = 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 = object : DatabaseChangedDialogFragment.ActionDatabaseChangedListener { - override fun validateDatabaseChanged() { + override fun onDatabaseChangeValidated() { mBinder?.getService()?.saveDatabaseInfo() } } - private var databaseInfoListener = object : - DatabaseTaskNotificationService.DatabaseInfoListener { - 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 - ) - } - } - } - } + fun onDatabaseChangeValidated() { + mBinder?.getService()?.saveDatabaseInfo() } 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() { - stopDialog() + actionTaskListener?.onActionStopped() if (serviceConnection == null) { serviceConnection = object : ServiceConnection { override fun onBindingDied(name: ComponentName?) { - stopDialog() + actionTaskListener?.onActionStopped() } override fun onNullBinding(name: ComponentName?) { - stopDialog() + actionTaskListener?.onActionStopped() } override fun onServiceConnected(name: ComponentName?, serviceBinder: IBinder?) { @@ -291,13 +165,21 @@ class DatabaseTaskProvider( private fun addServiceListeners(service: DatabaseTaskNotificationService.ActionTaskBinder?) { service?.addDatabaseListener(databaseListener) - service?.addDatabaseFileInfoListener(databaseInfoListener) - service?.addActionTaskListener(actionTaskListener) + databaseInfoListener?.let { infoListener -> + service?.addDatabaseFileInfoListener(infoListener) + } + actionTaskListener?.let { taskListener -> + service?.addActionTaskListener(taskListener) + } } private fun removeServiceListeners(service: DatabaseTaskNotificationService.ActionTaskBinder?) { - service?.removeActionTaskListener(actionTaskListener) - service?.removeDatabaseFileInfoListener(databaseInfoListener) + actionTaskListener?.let { taskListener -> + service?.removeActionTaskListener(taskListener) + } + databaseInfoListener?.let { infoListener -> + service?.removeDatabaseFileInfoListener(infoListener) + } service?.removeDatabaseListener(databaseListener) } @@ -369,58 +251,9 @@ class DatabaseTaskProvider( } } - private val tempServiceParameters = mutableListOf>() - 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) { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { - val contextActivity = activity - 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() + onStartActionRequested?.invoke(bundle, actionTask) ?: run { + context.startDatabaseService(bundle, actionTask) } } @@ -843,5 +676,21 @@ class DatabaseTaskProvider( companion object { 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() + } + } } } diff --git a/app/src/main/java/com/kunzisoft/keepass/hardware/HardwareKeyActivity.kt b/app/src/main/java/com/kunzisoft/keepass/hardware/HardwareKeyActivity.kt index 1e19504c3..0966cacf9 100644 --- a/app/src/main/java/com/kunzisoft/keepass/hardware/HardwareKeyActivity.kt +++ b/app/src/main/java/com/kunzisoft/keepass/hardware/HardwareKeyActivity.kt @@ -27,10 +27,10 @@ class HardwareKeyActivity: DatabaseModeActivity(){ if (result.resultCode == RESULT_OK) { val challengeResponse: ByteArray? = result.data?.getByteArrayExtra(HARDWARE_KEY_RESPONSE_KEY) Log.d(TAG, "Response form challenge") - mDatabaseTaskProvider?.startChallengeResponded(challengeResponse ?: ByteArray(0)) + mDatabaseViewModel.onChallengeResponded(challengeResponse) } else { Log.e(TAG, "Response from challenge error") - mDatabaseTaskProvider?.startChallengeResponded(ByteArray(0)) + mDatabaseViewModel.onChallengeResponded(null) } finish() } @@ -49,13 +49,11 @@ class HardwareKeyActivity: DatabaseModeActivity(){ } override fun onDatabaseRetrieved(database: ContextualDatabase?) { - super.onDatabaseRetrieved(database) - val hardwareKey = HardwareKey.getHardwareKeyFromString( intent.getStringExtra(DATA_HARDWARE_KEY) ) if (isHardwareKeyAvailable(this, hardwareKey, true) { - mDatabaseTaskProvider?.startChallengeResponded(ByteArray(0)) + mDatabaseViewModel.onChallengeResponded(null) }) { when (hardwareKey) { /* diff --git a/app/src/main/java/com/kunzisoft/keepass/services/DatabaseTaskNotificationService.kt b/app/src/main/java/com/kunzisoft/keepass/services/DatabaseTaskNotificationService.kt index 45bb994cc..98b5cdc26 100644 --- a/app/src/main/java/com/kunzisoft/keepass/services/DatabaseTaskNotificationService.kt +++ b/app/src/main/java/com/kunzisoft/keepass/services/DatabaseTaskNotificationService.kt @@ -176,7 +176,7 @@ open class DatabaseTaskNotificationService : LockNotificationService(), Progress progressMessage: ProgressMessage ) fun onActionStopped( - database: ContextualDatabase + database: ContextualDatabase? = null ) fun onActionFinished( database: ContextualDatabase, diff --git a/app/src/main/java/com/kunzisoft/keepass/settings/MainPreferenceFragment.kt b/app/src/main/java/com/kunzisoft/keepass/settings/MainPreferenceFragment.kt index 020bfb398..8121eb1e4 100644 --- a/app/src/main/java/com/kunzisoft/keepass/settings/MainPreferenceFragment.kt +++ b/app/src/main/java/com/kunzisoft/keepass/settings/MainPreferenceFragment.kt @@ -60,6 +60,7 @@ class MainPreferenceFragment : PreferenceFragmentCompat() { is DatabaseViewModel.UIState.OnDatabaseRetrieved -> { checkDatabaseLoaded(uiState.database?.loaded == true) } + else -> {} } } } diff --git a/app/src/main/java/com/kunzisoft/keepass/settings/NestedDatabaseSettingsFragment.kt b/app/src/main/java/com/kunzisoft/keepass/settings/NestedDatabaseSettingsFragment.kt index 47a986135..afeb53df2 100644 --- a/app/src/main/java/com/kunzisoft/keepass/settings/NestedDatabaseSettingsFragment.kt +++ b/app/src/main/java/com/kunzisoft/keepass/settings/NestedDatabaseSettingsFragment.kt @@ -21,7 +21,12 @@ package com.kunzisoft.keepass.settings import android.os.Bundle 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.view.MenuProvider 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.element.Group 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.settings.preference.* -import com.kunzisoft.keepass.settings.preferencedialogfragment.* +import com.kunzisoft.keepass.settings.preference.DialogColorPreference +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.utils.getParcelableCompat import com.kunzisoft.keepass.utils.getSerializableCompat @@ -131,13 +155,17 @@ class NestedDatabaseSettingsFragment : NestedSettingsFragment(), DatabaseRetriev ) 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?) { diff --git a/app/src/main/java/com/kunzisoft/keepass/settings/SettingsActivity.kt b/app/src/main/java/com/kunzisoft/keepass/settings/SettingsActivity.kt index 7c996aac3..475ba4eeb 100644 --- a/app/src/main/java/com/kunzisoft/keepass/settings/SettingsActivity.kt +++ b/app/src/main/java/com/kunzisoft/keepass/settings/SettingsActivity.kt @@ -192,7 +192,7 @@ open class SettingsActivity } override fun onAssignKeyDialogPositiveClick(mainCredential: MainCredential) { - assignPassword(mainCredential) + assignMainCredential(mainCredential) } override fun onAssignKeyDialogNegativeClick(mainCredential: MainCredential) {} diff --git a/app/src/main/java/com/kunzisoft/keepass/settings/preferencedialogfragment/DatabaseSavePreferenceDialogFragmentCompat.kt b/app/src/main/java/com/kunzisoft/keepass/settings/preferencedialogfragment/DatabaseSavePreferenceDialogFragmentCompat.kt index 59b57037e..a83fdf3aa 100644 --- a/app/src/main/java/com/kunzisoft/keepass/settings/preferencedialogfragment/DatabaseSavePreferenceDialogFragmentCompat.kt +++ b/app/src/main/java/com/kunzisoft/keepass/settings/preferencedialogfragment/DatabaseSavePreferenceDialogFragmentCompat.kt @@ -59,6 +59,14 @@ abstract class DatabaseSavePreferenceDialogFragmentCompat is DatabaseViewModel.UIState.OnDatabaseRetrieved -> { 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 } - protected fun saveColor(oldColor: Int?, - newColor: Int?) { + protected fun saveColor( + oldColor: Int?, + newColor: Int? + ) { val oldColorString = if (oldColor != null) ChromaUtil.getFormattedColorString(oldColor, false) else @@ -91,77 +101,158 @@ abstract class DatabaseSavePreferenceDialogFragmentCompat ChromaUtil.getFormattedColorString(newColor, false) else "" - mDatabaseViewModel.saveColor(oldColorString, newColorString, mDatabaseAutoSaveEnable) + mDatabaseViewModel.saveColor( + oldColorString, + newColorString, + mDatabaseAutoSaveEnable + ) } - protected fun saveCompression(oldCompression: CompressionAlgorithm, - newCompression: CompressionAlgorithm + protected fun saveCompression( + oldCompression: CompressionAlgorithm, + newCompression: CompressionAlgorithm ) { - mDatabaseViewModel.saveCompression(oldCompression, newCompression, mDatabaseAutoSaveEnable) + mDatabaseViewModel.saveCompression( + oldCompression, + newCompression, + mDatabaseAutoSaveEnable + ) } - protected fun saveDefaultUsername(oldUsername: String, - newUsername: String) { - mDatabaseViewModel.saveDefaultUsername(oldUsername, newUsername, mDatabaseAutoSaveEnable) + protected fun saveDefaultUsername( + oldUsername: String, + newUsername: String + ) { + mDatabaseViewModel.saveDefaultUsername( + oldUsername, + newUsername, + mDatabaseAutoSaveEnable + ) } - protected fun saveDescription(oldDescription: String, - newDescription: String) { - mDatabaseViewModel.saveDescription(oldDescription, newDescription, mDatabaseAutoSaveEnable) + protected fun saveDescription( + oldDescription: String, + newDescription: String + ) { + mDatabaseViewModel.saveDescription( + oldDescription, + newDescription, + mDatabaseAutoSaveEnable + ) } - protected fun saveEncryption(oldEncryption: EncryptionAlgorithm, - newEncryptionAlgorithm: EncryptionAlgorithm) { - mDatabaseViewModel.saveEncryption(oldEncryption, newEncryptionAlgorithm, mDatabaseAutoSaveEnable) + protected fun saveEncryption( + oldEncryption: EncryptionAlgorithm, + newEncryptionAlgorithm: EncryptionAlgorithm + ) { + mDatabaseViewModel.saveEncryption( + oldEncryption, + newEncryptionAlgorithm, + mDatabaseAutoSaveEnable + ) } - protected fun saveKeyDerivation(oldKeyDerivation: KdfEngine, - newKeyDerivation: KdfEngine) { - mDatabaseViewModel.saveKeyDerivation(oldKeyDerivation, newKeyDerivation, mDatabaseAutoSaveEnable) + protected fun saveKeyDerivation( + oldKeyDerivation: KdfEngine, + newKeyDerivation: KdfEngine + ) { + mDatabaseViewModel.saveKeyDerivation( + oldKeyDerivation, + newKeyDerivation, + mDatabaseAutoSaveEnable + ) } - protected fun saveName(oldName: String, - newName: String) { - mDatabaseViewModel.saveName(oldName, newName, mDatabaseAutoSaveEnable) + protected fun saveName( + oldName: String, + newName: String + ) { + mDatabaseViewModel.saveName( + oldName, + newName, + mDatabaseAutoSaveEnable + ) } - protected fun saveRecycleBin(oldGroup: Group?, - newGroup: Group?) { - mDatabaseViewModel.saveRecycleBin(oldGroup, newGroup, mDatabaseAutoSaveEnable) + protected fun saveRecycleBin( + oldGroup: Group?, + newGroup: Group? + ) { + mDatabaseViewModel.saveRecycleBin( + oldGroup, + newGroup, + mDatabaseAutoSaveEnable + ) } protected fun removeUnlinkedData() { mDatabaseViewModel.removeUnlinkedData(mDatabaseAutoSaveEnable) } - protected fun saveTemplatesGroup(oldGroup: Group?, - newGroup: Group?) { - mDatabaseViewModel.saveTemplatesGroup(oldGroup, newGroup, mDatabaseAutoSaveEnable) + protected fun saveTemplatesGroup( + oldGroup: Group?, + newGroup: Group? + ) { + mDatabaseViewModel.saveTemplatesGroup( + oldGroup, + newGroup, + mDatabaseAutoSaveEnable + ) } - protected fun saveMaxHistoryItems(oldNumber: Int, - newNumber: Int) { - mDatabaseViewModel.saveMaxHistoryItems(oldNumber, newNumber, mDatabaseAutoSaveEnable) + protected fun saveMaxHistoryItems( + oldNumber: Int, + newNumber: Int + ) { + mDatabaseViewModel.saveMaxHistoryItems( + oldNumber, + newNumber, + mDatabaseAutoSaveEnable + ) } - protected fun saveMaxHistorySize(oldNumber: Long, - newNumber: Long) { - mDatabaseViewModel.saveMaxHistorySize(oldNumber, newNumber, mDatabaseAutoSaveEnable) + protected fun saveMaxHistorySize( + oldNumber: Long, + newNumber: Long + ) { + mDatabaseViewModel.saveMaxHistorySize( + oldNumber, + newNumber, + mDatabaseAutoSaveEnable + ) } - protected fun saveMemoryUsage(oldNumber: Long, - newNumber: Long) { - mDatabaseViewModel.saveMemoryUsage(oldNumber, newNumber, mDatabaseAutoSaveEnable) + protected fun saveMemoryUsage( + oldNumber: Long, + newNumber: Long + ) { + mDatabaseViewModel.saveMemoryUsage( + oldNumber, + newNumber, + mDatabaseAutoSaveEnable + ) } - protected fun saveParallelism(oldNumber: Long, - newNumber: Long) { - mDatabaseViewModel.saveParallelism(oldNumber, newNumber, mDatabaseAutoSaveEnable) + protected fun saveParallelism( + oldNumber: Long, + newNumber: Long + ) { + mDatabaseViewModel.saveParallelism( + oldNumber, + newNumber, + mDatabaseAutoSaveEnable + ) } - protected fun saveIterations(oldNumber: Long, - newNumber: Long) { - mDatabaseViewModel.saveIterations(oldNumber, newNumber, mDatabaseAutoSaveEnable) + protected fun saveIterations( + oldNumber: Long, + newNumber: Long + ) { + mDatabaseViewModel.saveIterations( + oldNumber, + newNumber, + mDatabaseAutoSaveEnable + ) } companion object { diff --git a/app/src/main/java/com/kunzisoft/keepass/viewmodels/DatabaseViewModel.kt b/app/src/main/java/com/kunzisoft/keepass/viewmodels/DatabaseViewModel.kt index ca9277d6b..b8fdd5807 100644 --- a/app/src/main/java/com/kunzisoft/keepass/viewmodels/DatabaseViewModel.kt +++ b/app/src/main/java/com/kunzisoft/keepass/viewmodels/DatabaseViewModel.kt @@ -1,17 +1,30 @@ package com.kunzisoft.keepass.viewmodels -import androidx.lifecycle.LiveData -import androidx.lifecycle.ViewModel +import android.app.Application +import android.net.Uri +import android.os.Bundle +import androidx.lifecycle.AndroidViewModel 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.kdf.KdfEngine +import com.kunzisoft.keepass.database.element.Entry import com.kunzisoft.keepass.database.element.Group 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.utils.getBinaryDir import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow +import java.util.UUID -class DatabaseViewModel: ViewModel() { +class DatabaseViewModel(application: Application): AndroidViewModel(application) { var database: ContextualDatabase? = null private set @@ -19,174 +32,438 @@ class DatabaseViewModel: ViewModel() { private val mUiState = MutableStateFlow(UIState.Loading) val uiState: StateFlow = mUiState - val actionFinished : LiveData get() = _actionFinished - private val _actionFinished = SingleLiveEvent() + private var mDatabaseTaskProvider: DatabaseTaskProvider = DatabaseTaskProvider( + context = application + ) - val saveDatabase : LiveData get() = _saveDatabase - private val _saveDatabase = SingleLiveEvent() + init { + 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 get() = _mergeDatabase - private val _mergeDatabase = SingleLiveEvent() + override fun onActionUpdated( + database: ContextualDatabase, + progressMessage: ProgressMessage + ) { + mUiState.value = UIState.OnDatabaseActionUpdated(database, progressMessage) + } - val reloadDatabase : LiveData get() = _reloadDatabase - private val _reloadDatabase = SingleLiveEvent() + override fun onActionStopped(database: ContextualDatabase?) { + mUiState.value = UIState.OnDatabaseActionStopped(database) + } - val saveName : LiveData get() = _saveName - private val _saveName = SingleLiveEvent() + override fun onActionFinished( + database: ContextualDatabase, + actionTask: String, + result: ActionRunnable.Result + ) { + mUiState.value = UIState.OnDatabaseActionFinished(database, actionTask, result) + } + } - val saveDescription : LiveData get() = _saveDescription - private val _saveDescription = SingleLiveEvent() + mDatabaseTaskProvider.registerProgressTask() + } - val saveDefaultUsername : LiveData get() = _saveDefaultUsername - private val _saveDefaultUsername = SingleLiveEvent() + /* + * Main database actions + */ - val saveColor : LiveData get() = _saveColor - private val _saveColor = SingleLiveEvent() - - val saveCompression : LiveData get() = _saveCompression - private val _saveCompression = SingleLiveEvent() - - val removeUnlinkData : LiveData get() = _removeUnlinkData - private val _removeUnlinkData = SingleLiveEvent() - - val saveRecycleBin : LiveData get() = _saveRecycleBin - private val _saveRecycleBin = SingleLiveEvent() - - val saveTemplatesGroup : LiveData get() = _saveTemplatesGroup - private val _saveTemplatesGroup = SingleLiveEvent() - - val saveMaxHistoryItems : LiveData get() = _saveMaxHistoryItems - private val _saveMaxHistoryItems = SingleLiveEvent() - - val saveMaxHistorySize : LiveData get() = _saveMaxHistorySize - private val _saveMaxHistorySize = SingleLiveEvent() - - val saveEncryption : LiveData get() = _saveEncryption - private val _saveEncryption = SingleLiveEvent() - - val saveKeyDerivation : LiveData get() = _saveKeyDerivation - private val _saveKeyDerivation = SingleLiveEvent() - - val saveIterations : LiveData get() = _saveIterations - private val _saveIterations = SingleLiveEvent() - - val saveMemoryUsage : LiveData get() = _saveMemoryUsage - private val _saveMemoryUsage = SingleLiveEvent() - - val saveParallelism : LiveData get() = _saveParallelism - private val _saveParallelism = SingleLiveEvent() - - - fun defineDatabase(database: ContextualDatabase?) { + private fun defineDatabase(database: ContextualDatabase?) { this.database = database this.mUiState.value = UIState.OnDatabaseRetrieved(database) } - fun onActionFinished(database: ContextualDatabase, - actionTask: String, - result: ActionRunnable.Result) { - this._actionFinished.value = ActionResult(database, actionTask, result) + fun loadDatabase( + databaseUri: Uri, + mainCredential: MainCredential, + readOnly: Boolean, + cipherEncryptDatabase: CipherEncryptDatabase?, + fixDuplicateUuid: Boolean + ) { + mDatabaseTaskProvider.startDatabaseLoad( + databaseUri, + mainCredential, + readOnly, + cipherEncryptDatabase, + fixDuplicateUuid + ) } - fun saveDatabase(save: Boolean) { - _saveDatabase.value = save + fun createDatabase( + databaseUri: Uri, + mainCredential: MainCredential + ) { + mDatabaseTaskProvider.startDatabaseCreate(databaseUri, mainCredential) } - fun mergeDatabase(save: Boolean) { - _mergeDatabase.value = save + fun assignMainCredential( + 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) { - _reloadDatabase.value = fixDuplicateUuid + mDatabaseTaskProvider.askToStartDatabaseReload( + conditionToAsk = database?.dataModifiedSinceLastLoading != false + ) { + mDatabaseTaskProvider.startDatabaseReload(fixDuplicateUuid) + } } - fun saveName(oldValue: String, - newValue: String, - save: Boolean) { - _saveName.value = SuperString(oldValue, newValue, save) + fun closeDatabase() { + database?.clearAndClose(getApplication().getBinaryDir()) } - fun saveDescription(oldValue: String, - newValue: String, - save: Boolean) { - _saveDescription.value = SuperString(oldValue, newValue, save) + fun onDatabaseChangeValidated() { + mDatabaseTaskProvider.onDatabaseChangeValidated() } - fun saveDefaultUsername(oldValue: String, - newValue: String, - save: Boolean) { - _saveDefaultUsername.value = SuperString(oldValue, newValue, save) + /* + * Nodes actions + */ + + fun createEntry( + newEntry: Entry, + parent: Group, + save: Boolean + ) { + mDatabaseTaskProvider.startDatabaseCreateEntry( + newEntry, + parent, + save + ) } - fun saveColor(oldValue: String, - newValue: String, - save: Boolean) { - _saveColor.value = SuperString(oldValue, newValue, save) + fun updateEntry( + oldEntry: Entry, + entryToUpdate: Entry, + save: Boolean + ) { + mDatabaseTaskProvider.startDatabaseUpdateEntry( + oldEntry, + entryToUpdate, + save + ) } - fun saveCompression(oldValue: CompressionAlgorithm, - newValue: CompressionAlgorithm, - save: Boolean) { - _saveCompression.value = SuperCompression(oldValue, newValue, save) + fun restoreEntryHistory( + mainEntryId: NodeId, + entryHistoryPosition: Int, + save: Boolean + ) { + mDatabaseTaskProvider.startDatabaseRestoreEntryHistory( + mainEntryId, + entryHistoryPosition, + save + ) + } + + fun deleteEntryHistory( + mainEntryId: NodeId, + 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, + newParent: Group, + save: Boolean + ) { + mDatabaseTaskProvider.startDatabaseCopyNodes( + nodesToCopy, + newParent, + save + ) + } + + fun moveNodes( + nodesToMove: List, + newParent: Group, + save: Boolean + ) { + mDatabaseTaskProvider.startDatabaseMoveNodes( + nodesToMove, + newParent, + save + ) + } + + fun deleteNodes( + nodes: List, + 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) { - _removeUnlinkData.value = save + mDatabaseTaskProvider.startDatabaseRemoveUnlinkedData(save) } - fun saveRecycleBin(oldValue: Group?, - newValue: Group?, - save: Boolean) { - _saveRecycleBin.value = SuperGroup(oldValue, newValue, save) + fun saveRecycleBin( + oldValue: Group?, + newValue: Group?, + save: Boolean + ) { + mDatabaseTaskProvider.startDatabaseSaveRecycleBin( + oldValue, + newValue, + save + ) } - fun saveTemplatesGroup(oldValue: Group?, - newValue: Group?, - save: Boolean) { - _saveTemplatesGroup.value = SuperGroup(oldValue, newValue, save) + fun saveTemplatesGroup( + oldValue: Group?, + newValue: Group?, + save: Boolean + ) { + mDatabaseTaskProvider.startDatabaseSaveTemplatesGroup( + oldValue, + newValue, + save + ) } - fun saveMaxHistoryItems(oldValue: Int, - newValue: Int, - save: Boolean) { - _saveMaxHistoryItems.value = SuperInt(oldValue, newValue, save) + fun saveMaxHistoryItems( + oldValue: Int, + newValue: Int, + save: Boolean + ) { + mDatabaseTaskProvider.startDatabaseSaveMaxHistoryItems( + oldValue, + newValue, + save + ) } - fun saveMaxHistorySize(oldValue: Long, - newValue: Long, - save: Boolean) { - _saveMaxHistorySize.value = SuperLong(oldValue, newValue, save) + fun saveMaxHistorySize( + oldValue: Long, + newValue: Long, + save: Boolean + ) { + mDatabaseTaskProvider.startDatabaseSaveMaxHistorySize( + oldValue, + newValue, + save + ) } - fun saveEncryption(oldValue: EncryptionAlgorithm, - newValue: EncryptionAlgorithm, - save: Boolean) { - _saveEncryption.value = SuperEncryption(oldValue, newValue, save) + fun saveEncryption( + oldValue: EncryptionAlgorithm, + newValue: EncryptionAlgorithm, + save: Boolean + ) { + mDatabaseTaskProvider.startDatabaseSaveEncryption( + oldValue, + newValue, + save + ) } - fun saveKeyDerivation(oldValue: KdfEngine, - newValue: KdfEngine, - save: Boolean) { - _saveKeyDerivation.value = SuperKeyDerivation(oldValue, newValue, save) + fun saveKeyDerivation( + oldValue: KdfEngine, + newValue: KdfEngine, + save: Boolean + ) { + mDatabaseTaskProvider.startDatabaseSaveKeyDerivation( + oldValue, + newValue, + save + ) } - fun saveIterations(oldValue: Long, - newValue: Long, - save: Boolean) { - _saveIterations.value = SuperLong(oldValue, newValue, save) + fun saveIterations( + oldValue: Long, + newValue: Long, + save: Boolean + ) { + mDatabaseTaskProvider.startDatabaseSaveIterations( + oldValue, + newValue, + save + ) } - fun saveMemoryUsage(oldValue: Long, - newValue: Long, - save: Boolean) { - _saveMemoryUsage.value = SuperLong(oldValue, newValue, save) + fun saveMemoryUsage( + oldValue: Long, + newValue: Long, + save: Boolean + ) { + mDatabaseTaskProvider.startDatabaseSaveMemoryUsage( + oldValue, + newValue, + save + ) } - fun saveParallelism(oldValue: Long, - newValue: Long, - save: Boolean) { - _saveParallelism.value = SuperLong(oldValue, newValue, save) + fun saveParallelism( + oldValue: Long, + newValue: Long, + 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 { @@ -194,33 +471,31 @@ class DatabaseViewModel: ViewModel() { data class OnDatabaseRetrieved( val database: ContextualDatabase? ): 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) - }