Better Action runnable implementation

This commit is contained in:
J-Jamet
2019-10-30 14:49:40 +01:00
parent e088f4a4ad
commit 8c0d7ab9ed
17 changed files with 199 additions and 292 deletions

View File

@@ -430,7 +430,7 @@ class EntryActivity : LockingHideActivity() {
TODO Slowdown when add entry as result TODO Slowdown when add entry as result
Intent intent = new Intent(); Intent intent = new Intent();
intent.putExtra(EntryEditActivity.ADD_OR_UPDATE_ENTRY_KEY, mEntry); intent.putExtra(EntryEditActivity.ADD_OR_UPDATE_ENTRY_KEY, mEntry);
setResult(EntryEditActivity.UPDATE_ENTRY_RESULT_CODE, intent); onFinish(EntryEditActivity.UPDATE_ENTRY_RESULT_CODE, intent);
*/ */
super.finish() super.finish()
} }

View File

@@ -175,35 +175,38 @@ class PasswordActivity : StylishActivity() {
} }
} }
var databaseUri: Uri? = null // Remove the password in view in all cases
var masterPassword: String? = null removePassword()
var keyFileUri: Uri? = null
var readOnly = true
var cipherEntity: CipherDatabaseEntity? = null
result.data?.let { resultData -> if (result.isSuccess) {
databaseUri = resultData.getParcelable(DATABASE_URI_KEY) launchGroupActivity()
masterPassword = resultData.getString(MASTER_PASSWORD_KEY) } else {
keyFileUri = resultData.getParcelable(KEY_FILE_KEY) var resultError = ""
readOnly = resultData.getBoolean(READ_ONLY_KEY) val resultException = result.exception
cipherEntity = resultData.getParcelable(CIPHER_ENTITY_KEY) val resultMessage = result.message
}
databaseUri?.let { databaseFileUri -> if (resultException != null) {
// Remove the password in view in all cases resultError = resultException.getLocalizedMessage(resources)
removePassword()
if (result.isSuccess) { // Relaunch loading if we need to fix UUID
launchGroupActivity() if (resultException is LoadDatabaseDuplicateUuidException) {
} else { showLoadDatabaseDuplicateUuidMessage {
var resultError = ""
val resultException = result.exception
val resultMessage = result.message
if (resultException != null) { var databaseUri: Uri? = null
resultError = resultException.getLocalizedMessage(resources) var masterPassword: String? = null
if (resultException is LoadDatabaseDuplicateUuidException) var keyFileUri: Uri? = null
showLoadDatabaseDuplicateUuidMessage { var readOnly = true
var cipherEntity: CipherDatabaseEntity? = null
result.data?.let { resultData ->
databaseUri = resultData.getParcelable(DATABASE_URI_KEY)
masterPassword = resultData.getString(MASTER_PASSWORD_KEY)
keyFileUri = resultData.getParcelable(KEY_FILE_KEY)
readOnly = resultData.getBoolean(READ_ONLY_KEY)
cipherEntity = resultData.getParcelable(CIPHER_ENTITY_KEY)
}
databaseUri?.let { databaseFileUri ->
showProgressDialogAndLoadDatabase( showProgressDialogAndLoadDatabase(
databaseFileUri, databaseFileUri,
masterPassword, masterPassword,
@@ -212,18 +215,18 @@ class PasswordActivity : StylishActivity() {
cipherEntity, cipherEntity,
true) true)
} }
}
} }
if (resultMessage != null && resultMessage.isNotEmpty()) {
resultError = "$resultError $resultMessage"
}
Log.e(TAG, resultError, resultException)
Snackbar.make(activity_password_coordinator_layout,
resultError,
Snackbar.LENGTH_LONG).asError().show()
} }
// Show error message
if (resultMessage != null && resultMessage.isNotEmpty()) {
resultError = "$resultError $resultMessage"
}
Log.e(TAG, resultError, resultException)
Snackbar.make(activity_password_coordinator_layout,
resultError,
Snackbar.LENGTH_LONG).asError().show()
} }
} }
} }

View File

@@ -22,22 +22,20 @@ package com.kunzisoft.keepass.database.action
import android.content.Context import android.content.Context
import android.net.Uri import android.net.Uri
import com.kunzisoft.keepass.database.element.Database import com.kunzisoft.keepass.database.element.Database
import com.kunzisoft.keepass.tasks.ActionRunnable
import com.kunzisoft.keepass.utils.UriUtil import com.kunzisoft.keepass.utils.UriUtil
open class AssignPasswordInDatabaseRunnable @JvmOverloads constructor( open class AssignPasswordInDatabaseRunnable (
context: Context, context: Context,
database: Database, database: Database,
withMasterPassword: Boolean, withMasterPassword: Boolean,
masterPassword: String?, masterPassword: String?,
withKeyFile: Boolean, withKeyFile: Boolean,
keyFile: Uri?, keyFile: Uri?,
save: Boolean, save: Boolean)
actionRunnable: ActionRunnable? = null) : SaveDatabaseRunnable(context, database, save) {
: SaveDatabaseRunnable(context, database, save, actionRunnable) {
private var mMasterPassword: String? = null private var mMasterPassword: String? = null
private var mKeyFile: Uri? = null protected var mKeyFile: Uri? = null
private var mBackupKey: ByteArray? = null private var mBackupKey: ByteArray? = null
@@ -48,7 +46,7 @@ open class AssignPasswordInDatabaseRunnable @JvmOverloads constructor(
this.mKeyFile = keyFile this.mKeyFile = keyFile
} }
override fun run() { override fun onStartRun() {
// Set key // Set key
try { try {
// TODO move master key methods // TODO move master key methods
@@ -57,17 +55,17 @@ open class AssignPasswordInDatabaseRunnable @JvmOverloads constructor(
val uriInputStream = UriUtil.getUriInputStream(context.contentResolver, mKeyFile) val uriInputStream = UriUtil.getUriInputStream(context.contentResolver, mKeyFile)
database.retrieveMasterKey(mMasterPassword, uriInputStream) database.retrieveMasterKey(mMasterPassword, uriInputStream)
// To save the database
super.run()
finishRun(true)
} catch (e: Exception) { } catch (e: Exception) {
erase(mBackupKey) erase(mBackupKey)
finishRun(false, e.message) setError(e.message)
} }
super.onStartRun()
} }
override fun onFinishRun(result: Result) { override fun onFinishRun() {
super.onFinishRun()
if (!result.isSuccess) { if (!result.isSuccess) {
// Erase the current master key // Erase the current master key
erase(database.masterKey) erase(database.masterKey)
@@ -75,8 +73,6 @@ open class AssignPasswordInDatabaseRunnable @JvmOverloads constructor(
database.masterKey = it database.masterKey = it
} }
} }
super.onFinishRun(result)
} }
/** /**

View File

@@ -21,8 +21,9 @@ package com.kunzisoft.keepass.database.action
import android.content.Context import android.content.Context
import android.net.Uri import android.net.Uri
import android.util.Log
import com.kunzisoft.keepass.app.database.FileDatabaseHistoryAction
import com.kunzisoft.keepass.database.element.Database import com.kunzisoft.keepass.database.element.Database
import com.kunzisoft.keepass.tasks.ActionRunnable
class CreateDatabaseRunnable(context: Context, class CreateDatabaseRunnable(context: Context,
private val mDatabaseUri: Uri, private val mDatabaseUri: Uri,
@@ -31,28 +32,34 @@ class CreateDatabaseRunnable(context: Context,
masterPassword: String?, masterPassword: String?,
withKeyFile: Boolean, withKeyFile: Boolean,
keyFile: Uri?, keyFile: Uri?,
save: Boolean, save: Boolean)
actionRunnable: ActionRunnable? = null) : AssignPasswordInDatabaseRunnable(context, mDatabase, withMasterPassword, masterPassword, withKeyFile, keyFile, save) {
: AssignPasswordInDatabaseRunnable(context, mDatabase, withMasterPassword, masterPassword, withKeyFile, keyFile, save, actionRunnable) {
override fun run() { override fun onStartRun() {
try { try {
// Create new database record // Create new database record
mDatabase.apply { mDatabase.apply {
createData(mDatabaseUri) createData(mDatabaseUri)
// Set Database state // Set Database state
loaded = true loaded = true
// Commit changes
super.run()
} }
finishRun(true)
} catch (e: Exception) { } catch (e: Exception) {
mDatabase.closeAndClear() mDatabase.closeAndClear()
finishRun(false, e.message) setError(e.message)
} }
super.onStartRun()
} }
override fun onFinishRun(result: Result) {} override fun onFinishRun() {
super.onFinishRun()
if (result.isSuccess) {
// Add database to recent files
FileDatabaseHistoryAction.getInstance(context.applicationContext)
.addOrUpdateDatabaseUri(mDatabaseUri, mKeyFile)
} else {
Log.e("CreateDatabaseRunnable", "Unable to create the database")
}
}
} }

View File

@@ -26,6 +26,7 @@ import com.kunzisoft.keepass.app.database.CipherDatabaseAction
import com.kunzisoft.keepass.app.database.CipherDatabaseEntity import com.kunzisoft.keepass.app.database.CipherDatabaseEntity
import com.kunzisoft.keepass.app.database.FileDatabaseHistoryAction import com.kunzisoft.keepass.app.database.FileDatabaseHistoryAction
import com.kunzisoft.keepass.database.element.Database import com.kunzisoft.keepass.database.element.Database
import com.kunzisoft.keepass.database.exception.LoadDatabaseDuplicateUuidException
import com.kunzisoft.keepass.database.exception.LoadDatabaseException import com.kunzisoft.keepass.database.exception.LoadDatabaseException
import com.kunzisoft.keepass.notifications.DatabaseOpenNotificationService import com.kunzisoft.keepass.notifications.DatabaseOpenNotificationService
import com.kunzisoft.keepass.settings.PreferencesUtil import com.kunzisoft.keepass.settings.PreferencesUtil
@@ -42,16 +43,18 @@ class LoadDatabaseRunnable(private val context: Context,
private val mOmitBackup: Boolean, private val mOmitBackup: Boolean,
private val mFixDuplicateUUID: Boolean, private val mFixDuplicateUUID: Boolean,
private val progressTaskUpdater: ProgressTaskUpdater?, private val progressTaskUpdater: ProgressTaskUpdater?,
private val mOnFinish: ((Result) -> Unit)?) private val mDuplicateUuidAction: ((Result) -> Unit)?)
: ActionRunnable(null, executeNestedActionIfResultFalse = true) { : ActionRunnable() {
private val cacheDirectory = context.applicationContext.filesDir private val cacheDirectory = context.applicationContext.filesDir
override fun run() { override fun onStartRun() {
try { // Clear before we load
// Clear before we load mDatabase.closeAndClear(cacheDirectory)
mDatabase.closeAndClear(cacheDirectory) }
override fun onActionRun() {
try {
mDatabase.loadData(mUri, mPass, mKey, mDatabase.loadData(mUri, mPass, mKey,
mReadonly, mReadonly,
context.contentResolver, context.contentResolver,
@@ -59,7 +62,18 @@ class LoadDatabaseRunnable(private val context: Context,
mOmitBackup, mOmitBackup,
mFixDuplicateUUID, mFixDuplicateUUID,
progressTaskUpdater) progressTaskUpdater)
}
catch (e: LoadDatabaseDuplicateUuidException) {
mDuplicateUuidAction?.invoke(result)
setError(e)
}
catch (e: LoadDatabaseException) {
setError(e)
}
}
override fun onFinishRun() {
if (result.isSuccess) {
// Save keyFile in app database // Save keyFile in app database
val rememberKeyFile = PreferencesUtil.rememberKeyFiles(context) val rememberKeyFile = PreferencesUtil.rememberKeyFiles(context)
if (rememberKeyFile) { if (rememberKeyFile) {
@@ -71,28 +85,15 @@ class LoadDatabaseRunnable(private val context: Context,
.addOrUpdateDatabaseUri(mUri, keyUri) .addOrUpdateDatabaseUri(mUri, keyUri)
} }
mOnFinish?.invoke(result)
// Register the biometric // Register the biometric
mCipherEntity?.let { cipherDatabaseEntity -> mCipherEntity?.let { cipherDatabaseEntity ->
CipherDatabaseAction.getInstance(context) CipherDatabaseAction.getInstance(context)
.addOrUpdateCipherDatabase(cipherDatabaseEntity) { .addOrUpdateCipherDatabase(cipherDatabaseEntity) // return value not called
finishRun(true)
}
} ?: run {
finishRun(true)
} }
}
catch (e: LoadDatabaseException) {
finishRun(false, e)
}
// Start the opening notification // Start the opening notification
context.startService(Intent(context, DatabaseOpenNotificationService::class.java)) context.startService(Intent(context, DatabaseOpenNotificationService::class.java))
} } else {
override fun onFinishRun(result: Result) {
if (!result.isSuccess) {
mDatabase.closeAndClear(cacheDirectory) mDatabase.closeAndClear(cacheDirectory)
} }
} }

View File

@@ -25,39 +25,29 @@ import com.kunzisoft.keepass.database.exception.DatabaseOutputException
import com.kunzisoft.keepass.tasks.ActionRunnable import com.kunzisoft.keepass.tasks.ActionRunnable
import java.io.IOException import java.io.IOException
abstract class SaveDatabaseRunnable(protected var context: Context, open class SaveDatabaseRunnable(protected var context: Context,
protected var database: Database, protected var database: Database,
protected var saveDatabase: Boolean, private var saveDatabase: Boolean)
nestedAction: ActionRunnable? = null) : ActionRunnable() {
: ActionRunnable(nestedAction) {
override fun run() { var mAfterSaveDatabase: ((Result) -> Unit)? = null
if (saveDatabase) {
override fun onStartRun() {}
override fun onActionRun() {
if (saveDatabase && result.isSuccess) {
try { try {
database.saveData(context.contentResolver) database.saveData(context.contentResolver)
} catch (e: IOException) { } catch (e: IOException) {
finishRun(false, e.message) setError(e.message)
} catch (e: DatabaseOutputException) { } catch (e: DatabaseOutputException) {
finishRun(false, e.message) setError(e.message)
} }
} }
// Need to call super.run() in child class
} }
override fun onFinishRun(result: Result) { override fun onFinishRun() {
// Need to call super.onFinishRun(result) in child class // Need to call super.onFinishRun() in child class
} mAfterSaveDatabase?.invoke(result)
}
class SaveDatabaseActionRunnable(context: Context,
database: Database,
save: Boolean,
nestedAction: ActionRunnable? = null)
: SaveDatabaseRunnable(context, database, save, nestedAction) {
override fun run() {
super.run()
finishRun(true)
} }
} }

View File

@@ -3,44 +3,33 @@ package com.kunzisoft.keepass.database.action.node
import android.content.Context import android.content.Context
import com.kunzisoft.keepass.database.action.SaveDatabaseRunnable import com.kunzisoft.keepass.database.action.SaveDatabaseRunnable
import com.kunzisoft.keepass.database.element.Database import com.kunzisoft.keepass.database.element.Database
import com.kunzisoft.keepass.database.exception.LoadDatabaseException
abstract class ActionNodeDatabaseRunnable( abstract class ActionNodeDatabaseRunnable(
context: Context, context: Context,
database: Database, database: Database,
private val callbackRunnable: AfterActionNodeFinishRunnable?, private val afterActionNodesFinish: AfterActionNodesFinish?,
save: Boolean) save: Boolean)
: SaveDatabaseRunnable(context, database, save) { : SaveDatabaseRunnable(context, database, save) {
/** /**
* Function do to a node action, don't implements run() if used this * Function do to a node action
*/ */
abstract fun nodeAction() abstract fun nodeAction()
protected fun saveDatabaseAndFinish() { override fun onStartRun() {
if (result.isSuccess) {
super.run()
finishRun(true)
}
}
protected fun throwErrorAndFinish(throwable: LoadDatabaseException) {
finishRun(false, throwable)
}
override fun run() {
nodeAction() nodeAction()
super.onStartRun()
} }
/** /**
* Function do get the finish node action, don't implements onFinishRun() if used this * Function do get the finish node action
*/ */
abstract fun nodeFinish(result: Result): ActionNodeValues abstract fun nodeFinish(): ActionNodesValues
override fun onFinishRun(result: Result) { override fun onFinishRun() {
callbackRunnable?.apply { super.onFinishRun()
onActionNodeFinish(nodeFinish(result)) afterActionNodesFinish?.apply {
onActionNodesFinish(result, nodeFinish())
} }
super.onFinishRun(result)
} }
} }

View File

@@ -31,17 +31,16 @@ class AddEntryRunnable constructor(
private val mNewEntry: EntryVersioned, private val mNewEntry: EntryVersioned,
private val mParent: GroupVersioned, private val mParent: GroupVersioned,
save: Boolean, save: Boolean,
finishRunnable: AfterActionNodeFinishRunnable?) afterActionNodesFinish: AfterActionNodesFinish?)
: ActionNodeDatabaseRunnable(context, database, finishRunnable, save) { : ActionNodeDatabaseRunnable(context, database, afterActionNodesFinish, save) {
override fun nodeAction() { override fun nodeAction() {
mNewEntry.touch(modified = true, touchParents = true) mNewEntry.touch(modified = true, touchParents = true)
mParent.touch(modified = true, touchParents = true) mParent.touch(modified = true, touchParents = true)
database.addEntryTo(mNewEntry, mParent) database.addEntryTo(mNewEntry, mParent)
saveDatabaseAndFinish()
} }
override fun nodeFinish(result: Result): ActionNodeValues { override fun nodeFinish(): ActionNodesValues {
if (!result.isSuccess) { if (!result.isSuccess) {
mNewEntry.parent?.let { mNewEntry.parent?.let {
database.removeEntryFrom(mNewEntry, it) database.removeEntryFrom(mNewEntry, it)
@@ -51,6 +50,6 @@ class AddEntryRunnable constructor(
val oldNodesReturn = ArrayList<NodeVersioned>() val oldNodesReturn = ArrayList<NodeVersioned>()
val newNodesReturn = ArrayList<NodeVersioned>() val newNodesReturn = ArrayList<NodeVersioned>()
newNodesReturn.add(mNewEntry) newNodesReturn.add(mNewEntry)
return ActionNodeValues(result, oldNodesReturn, newNodesReturn) return ActionNodesValues(oldNodesReturn, newNodesReturn)
} }
} }

View File

@@ -30,17 +30,16 @@ class AddGroupRunnable constructor(
private val mNewGroup: GroupVersioned, private val mNewGroup: GroupVersioned,
private val mParent: GroupVersioned, private val mParent: GroupVersioned,
save: Boolean, save: Boolean,
afterAddNodeRunnable: AfterActionNodeFinishRunnable?) afterActionNodesFinish: AfterActionNodesFinish?)
: ActionNodeDatabaseRunnable(context, database, afterAddNodeRunnable, save) { : ActionNodeDatabaseRunnable(context, database, afterActionNodesFinish, save) {
override fun nodeAction() { override fun nodeAction() {
mNewGroup.touch(modified = true, touchParents = true) mNewGroup.touch(modified = true, touchParents = true)
mParent.touch(modified = true, touchParents = true) mParent.touch(modified = true, touchParents = true)
database.addGroupTo(mNewGroup, mParent) database.addGroupTo(mNewGroup, mParent)
saveDatabaseAndFinish()
} }
override fun nodeFinish(result: Result): ActionNodeValues { override fun nodeFinish(): ActionNodesValues {
if (!result.isSuccess) { if (!result.isSuccess) {
database.removeGroupFrom(mNewGroup, mParent) database.removeGroupFrom(mNewGroup, mParent)
} }
@@ -48,6 +47,6 @@ class AddGroupRunnable constructor(
val oldNodesReturn = ArrayList<NodeVersioned>() val oldNodesReturn = ArrayList<NodeVersioned>()
val newNodesReturn = ArrayList<NodeVersioned>() val newNodesReturn = ArrayList<NodeVersioned>()
newNodesReturn.add(mNewGroup) newNodesReturn.add(mNewGroup)
return ActionNodeValues(result, oldNodesReturn, newNodesReturn) return ActionNodesValues(oldNodesReturn, newNodesReturn)
} }
} }

View File

@@ -30,8 +30,8 @@ import com.kunzisoft.keepass.tasks.ActionRunnable
* - Move : @param oldNodes empty, @param newNodes NodesToMove * - Move : @param oldNodes empty, @param newNodes NodesToMove
* - Update : @param oldNodes NodesToUpdate, @param newNodes NodesUpdated * - Update : @param oldNodes NodesToUpdate, @param newNodes NodesUpdated
*/ */
class ActionNodeValues(val result: ActionRunnable.Result, val oldNodes: List<NodeVersioned>, val newNodes: List<NodeVersioned>) class ActionNodesValues(val oldNodes: List<NodeVersioned>, val newNodes: List<NodeVersioned>)
abstract class AfterActionNodeFinishRunnable { abstract class AfterActionNodesFinish {
abstract fun onActionNodeFinish(actionNodeValues: ActionNodeValues) abstract fun onActionNodesFinish(result: ActionRunnable.Result, actionNodesValues: ActionNodesValues)
} }

View File

@@ -32,20 +32,18 @@ class CopyNodesRunnable constructor(
private val mNodesToCopy: List<NodeVersioned>, private val mNodesToCopy: List<NodeVersioned>,
private val mNewParent: GroupVersioned, private val mNewParent: GroupVersioned,
save: Boolean, save: Boolean,
afterAddNodeRunnable: AfterActionNodeFinishRunnable?) afterActionNodesFinish: AfterActionNodesFinish?)
: ActionNodeDatabaseRunnable(context, database, afterAddNodeRunnable, save) { : ActionNodeDatabaseRunnable(context, database, afterActionNodesFinish, save) {
private var mEntriesCopied = ArrayList<EntryVersioned>() private var mEntriesCopied = ArrayList<EntryVersioned>()
override fun nodeAction() { override fun nodeAction() {
var error: LoadDatabaseException? = null
foreachNode@ for(currentNode in mNodesToCopy) { foreachNode@ for(currentNode in mNodesToCopy) {
when (currentNode.type) { when (currentNode.type) {
Type.GROUP -> { Type.GROUP -> {
Log.e(TAG, "Copy not allowed for group")// Only finish thread Log.e(TAG, "Copy not allowed for group")// Only finish thread
error = CopyDatabaseGroupException() setError(CopyDatabaseGroupException())
break@foreachNode break@foreachNode
} }
Type.ENTRY -> { Type.ENTRY -> {
@@ -60,24 +58,20 @@ class CopyNodesRunnable constructor(
mEntriesCopied.add(entryCopied) mEntriesCopied.add(entryCopied)
} else { } else {
Log.e(TAG, "Unable to create a copy of the entry") Log.e(TAG, "Unable to create a copy of the entry")
error = CopyDatabaseEntryException() setError(CopyDatabaseEntryException())
break@foreachNode break@foreachNode
} }
} else { } else {
// Only finish thread // Only finish thread
error = CopyDatabaseEntryException() setError(CopyDatabaseEntryException())
break@foreachNode break@foreachNode
} }
} }
} }
} }
if (error != null)
throwErrorAndFinish(error)
else
saveDatabaseAndFinish()
} }
override fun nodeFinish(result: Result): ActionNodeValues { override fun nodeFinish(): ActionNodesValues {
if (!result.isSuccess) { if (!result.isSuccess) {
// If we fail to save, try to delete the copy // If we fail to save, try to delete the copy
mEntriesCopied.forEach { mEntriesCopied.forEach {
@@ -88,7 +82,7 @@ class CopyNodesRunnable constructor(
} }
} }
} }
return ActionNodeValues(result, mNodesToCopy, mEntriesCopied) return ActionNodesValues(mNodesToCopy, mEntriesCopied)
} }
companion object { companion object {

View File

@@ -26,8 +26,8 @@ class DeleteNodesRunnable(context: Context,
database: Database, database: Database,
private val mNodesToDelete: List<NodeVersioned>, private val mNodesToDelete: List<NodeVersioned>,
save: Boolean, save: Boolean,
finish: AfterActionNodeFinishRunnable) afterActionNodesFinish: AfterActionNodesFinish)
: ActionNodeDatabaseRunnable(context, database, finish, save) { : ActionNodeDatabaseRunnable(context, database, afterActionNodesFinish, save) {
private var mParent: GroupVersioned? = null private var mParent: GroupVersioned? = null
private var mCanRecycle: Boolean = false private var mCanRecycle: Boolean = false
@@ -65,10 +65,9 @@ class DeleteNodesRunnable(context: Context,
} }
} }
} }
saveDatabaseAndFinish()
} }
override fun nodeFinish(result: Result): ActionNodeValues { override fun nodeFinish(): ActionNodesValues {
if (!result.isSuccess) { if (!result.isSuccess) {
if (mCanRecycle) { if (mCanRecycle) {
mParent?.let { mParent?.let {
@@ -92,6 +91,6 @@ class DeleteNodesRunnable(context: Context,
// Return a copy of unchanged nodes as old param // Return a copy of unchanged nodes as old param
// and nodes deleted or moved in recycle bin as new param // and nodes deleted or moved in recycle bin as new param
return ActionNodeValues(result, mNodesToDeleteBackup, mNodesToDelete) return ActionNodesValues(mNodesToDeleteBackup, mNodesToDelete)
} }
} }

View File

@@ -32,14 +32,13 @@ class MoveNodesRunnable constructor(
private val mNodesToMove: List<NodeVersioned>, private val mNodesToMove: List<NodeVersioned>,
private val mNewParent: GroupVersioned, private val mNewParent: GroupVersioned,
save: Boolean, save: Boolean,
afterAddNodeRunnable: AfterActionNodeFinishRunnable?) afterActionNodesFinish: AfterActionNodesFinish?)
: ActionNodeDatabaseRunnable(context, database, afterAddNodeRunnable, save) { : ActionNodeDatabaseRunnable(context, database, afterActionNodesFinish, save) {
private var mOldParent: GroupVersioned? = null private var mOldParent: GroupVersioned? = null
override fun nodeAction() { override fun nodeAction() {
var error: LoadDatabaseException? = null
foreachNode@ for(nodeToMove in mNodesToMove) { foreachNode@ for(nodeToMove in mNodesToMove) {
// Move node in new parent // Move node in new parent
mOldParent = nodeToMove.parent mOldParent = nodeToMove.parent
@@ -54,7 +53,7 @@ class MoveNodesRunnable constructor(
database.moveGroupTo(groupToMove, mNewParent) database.moveGroupTo(groupToMove, mNewParent)
} else { } else {
// Only finish thread // Only finish thread
error = MoveDatabaseGroupException() setError(MoveDatabaseGroupException())
break@foreachNode break@foreachNode
} }
} }
@@ -68,19 +67,15 @@ class MoveNodesRunnable constructor(
database.moveEntryTo(entryToMove, mNewParent) database.moveEntryTo(entryToMove, mNewParent)
} else { } else {
// Only finish thread // Only finish thread
error = MoveDatabaseEntryException() setError(MoveDatabaseEntryException())
break@foreachNode break@foreachNode
} }
} }
} }
} }
if (error != null)
throwErrorAndFinish(error)
else
saveDatabaseAndFinish()
} }
override fun nodeFinish(result: Result): ActionNodeValues { override fun nodeFinish(): ActionNodesValues {
if (!result.isSuccess) { if (!result.isSuccess) {
try { try {
mNodesToMove.forEach { nodeToMove -> mNodesToMove.forEach { nodeToMove ->
@@ -97,7 +92,7 @@ class MoveNodesRunnable constructor(
Log.i(TAG, "Unable to replace the node") Log.i(TAG, "Unable to replace the node")
} }
} }
return ActionNodeValues(result, ArrayList(), mNodesToMove) return ActionNodesValues(ArrayList(), mNodesToMove)
} }
companion object { companion object {

View File

@@ -30,8 +30,8 @@ class UpdateEntryRunnable constructor(
private val mOldEntry: EntryVersioned, private val mOldEntry: EntryVersioned,
private val mNewEntry: EntryVersioned, private val mNewEntry: EntryVersioned,
save: Boolean, save: Boolean,
finishRunnable: AfterActionNodeFinishRunnable?) afterActionNodesFinish: AfterActionNodesFinish?)
: ActionNodeDatabaseRunnable(context, database, finishRunnable, save) { : ActionNodeDatabaseRunnable(context, database, afterActionNodesFinish, save) {
// Keep backup of original values in case save fails // Keep backup of original values in case save fails
private var mBackupEntryHistory: EntryVersioned = EntryVersioned(mOldEntry) private var mBackupEntryHistory: EntryVersioned = EntryVersioned(mOldEntry)
@@ -50,11 +50,9 @@ class UpdateEntryRunnable constructor(
// Only change data in index // Only change data in index
database.updateEntry(mOldEntry) database.updateEntry(mOldEntry)
saveDatabaseAndFinish()
} }
override fun nodeFinish(result: Result): ActionNodeValues { override fun nodeFinish(): ActionNodesValues {
if (!result.isSuccess) { if (!result.isSuccess) {
mOldEntry.updateWith(mBackupEntryHistory) mOldEntry.updateWith(mBackupEntryHistory)
// If we fail to save, back out changes to global structure // If we fail to save, back out changes to global structure
@@ -65,6 +63,6 @@ class UpdateEntryRunnable constructor(
oldNodesReturn.add(mBackupEntryHistory) oldNodesReturn.add(mBackupEntryHistory)
val newNodesReturn = ArrayList<NodeVersioned>() val newNodesReturn = ArrayList<NodeVersioned>()
newNodesReturn.add(mOldEntry) newNodesReturn.add(mOldEntry)
return ActionNodeValues(result, oldNodesReturn, newNodesReturn) return ActionNodesValues(oldNodesReturn, newNodesReturn)
} }
} }

View File

@@ -30,8 +30,8 @@ class UpdateGroupRunnable constructor(
private val mOldGroup: GroupVersioned, private val mOldGroup: GroupVersioned,
private val mNewGroup: GroupVersioned, private val mNewGroup: GroupVersioned,
save: Boolean, save: Boolean,
finishRunnable: AfterActionNodeFinishRunnable?) afterActionNodesFinish: AfterActionNodesFinish?)
: ActionNodeDatabaseRunnable(context, database, finishRunnable, save) { : ActionNodeDatabaseRunnable(context, database, afterActionNodesFinish, save) {
// Keep backup of original values in case save fails // Keep backup of original values in case save fails
private val mBackupGroup: GroupVersioned = GroupVersioned(mOldGroup) private val mBackupGroup: GroupVersioned = GroupVersioned(mOldGroup)
@@ -47,11 +47,9 @@ class UpdateGroupRunnable constructor(
// Only change data in index // Only change data in index
database.updateGroup(mOldGroup) database.updateGroup(mOldGroup)
saveDatabaseAndFinish()
} }
override fun nodeFinish(result: Result): ActionNodeValues { override fun nodeFinish(): ActionNodesValues {
if (!result.isSuccess) { if (!result.isSuccess) {
// If we fail to save, back out changes to global structure // If we fail to save, back out changes to global structure
mOldGroup.updateWith(mBackupGroup) mOldGroup.updateWith(mBackupGroup)
@@ -62,6 +60,6 @@ class UpdateGroupRunnable constructor(
oldNodesReturn.add(mBackupGroup) oldNodesReturn.add(mBackupGroup)
val newNodesReturn = ArrayList<NodeVersioned>() val newNodesReturn = ArrayList<NodeVersioned>()
newNodesReturn.add(mOldGroup) newNodesReturn.add(mOldGroup)
return ActionNodeValues(result, oldNodesReturn, newNodesReturn) return ActionNodesValues(oldNodesReturn, newNodesReturn)
} }
} }

View File

@@ -6,14 +6,12 @@ import android.os.AsyncTask
import android.os.Binder import android.os.Binder
import android.os.Bundle import android.os.Bundle
import android.os.IBinder import android.os.IBinder
import android.util.Log
import com.kunzisoft.keepass.R import com.kunzisoft.keepass.R
import com.kunzisoft.keepass.app.database.CipherDatabaseEntity import com.kunzisoft.keepass.app.database.CipherDatabaseEntity
import com.kunzisoft.keepass.app.database.FileDatabaseHistoryAction
import com.kunzisoft.keepass.database.action.AssignPasswordInDatabaseRunnable import com.kunzisoft.keepass.database.action.AssignPasswordInDatabaseRunnable
import com.kunzisoft.keepass.database.action.CreateDatabaseRunnable import com.kunzisoft.keepass.database.action.CreateDatabaseRunnable
import com.kunzisoft.keepass.database.action.LoadDatabaseRunnable import com.kunzisoft.keepass.database.action.LoadDatabaseRunnable
import com.kunzisoft.keepass.database.action.SaveDatabaseActionRunnable import com.kunzisoft.keepass.database.action.SaveDatabaseRunnable
import com.kunzisoft.keepass.database.action.node.* import com.kunzisoft.keepass.database.action.node.*
import com.kunzisoft.keepass.database.element.* import com.kunzisoft.keepass.database.element.*
import com.kunzisoft.keepass.settings.PreferencesUtil import com.kunzisoft.keepass.settings.PreferencesUtil
@@ -185,22 +183,7 @@ class DatabaseTaskNotificationService : NotificationService(), ProgressTaskUpdat
intent.getStringExtra(MASTER_PASSWORD_KEY), intent.getStringExtra(MASTER_PASSWORD_KEY),
intent.getBooleanExtra(KEY_FILE_CHECKED_KEY, false), intent.getBooleanExtra(KEY_FILE_CHECKED_KEY, false),
keyFileUri, keyFileUri,
true, // TODO get readonly true // TODO get readonly
object: ActionRunnable() {
override fun run() {
finishRun(true)
}
override fun onFinishRun(result: Result) {
if (result.isSuccess) {
// Add database to recent files
FileDatabaseHistoryAction.getInstance(applicationContext)
.addOrUpdateDatabaseUri(databaseUri, keyFileUri)
} else {
Log.e(TAG, "Unable to create the database")
}
}
}
) )
} else { } else {
return null return null
@@ -235,6 +218,7 @@ class DatabaseTaskNotificationService : NotificationService(), ProgressTaskUpdat
intent.getBooleanExtra(FIX_DUPLICATE_UUID_KEY, false), intent.getBooleanExtra(FIX_DUPLICATE_UUID_KEY, false),
this this
) { result -> ) { result ->
// Add each info to reload database after thrown duplicate UUID exception
result.data = Bundle().apply { result.data = Bundle().apply {
putParcelable(DATABASE_URI_KEY, databaseUri) putParcelable(DATABASE_URI_KEY, databaseUri)
putString(MASTER_PASSWORD_KEY, masterPassword) putString(MASTER_PASSWORD_KEY, masterPassword)
@@ -266,13 +250,13 @@ class DatabaseTaskNotificationService : NotificationService(), ProgressTaskUpdat
} }
} }
private inner class AfterActionNodeRunnable : AfterActionNodeFinishRunnable() { private inner class AfterActionNodesRunnable : AfterActionNodesFinish() {
override fun onActionNodeFinish(actionNodeValues: ActionNodeValues) { override fun onActionNodesFinish(result: ActionRunnable.Result,
// TODO Encapsulate actionNodesValues: ActionNodesValues) {
val bundle = actionNodeValues.result.data ?: Bundle() val bundle = result.data ?: Bundle()
bundle.putBundle(OLD_NODES_KEY, getBundleFromListNodes(actionNodeValues.oldNodes)) bundle.putBundle(OLD_NODES_KEY, getBundleFromListNodes(actionNodesValues.oldNodes))
bundle.putBundle(NEW_NODES_KEY, getBundleFromListNodes(actionNodeValues.newNodes)) bundle.putBundle(NEW_NODES_KEY, getBundleFromListNodes(actionNodesValues.newNodes))
actionNodeValues.result.data = bundle result.data = bundle
} }
} }
@@ -288,7 +272,7 @@ class DatabaseTaskNotificationService : NotificationService(), ProgressTaskUpdat
intent.getParcelableExtra(GROUP_KEY), intent.getParcelableExtra(GROUP_KEY),
parent, parent,
intent.getBooleanExtra(SAVE_DATABASE_KEY, false), intent.getBooleanExtra(SAVE_DATABASE_KEY, false),
AfterActionNodeRunnable()) AfterActionNodesRunnable())
} }
} else { } else {
null null
@@ -308,7 +292,7 @@ class DatabaseTaskNotificationService : NotificationService(), ProgressTaskUpdat
oldGroup, oldGroup,
newGroup, newGroup,
intent.getBooleanExtra(SAVE_DATABASE_KEY, false), intent.getBooleanExtra(SAVE_DATABASE_KEY, false),
AfterActionNodeRunnable()) AfterActionNodesRunnable())
} }
} else { } else {
null null
@@ -327,7 +311,7 @@ class DatabaseTaskNotificationService : NotificationService(), ProgressTaskUpdat
intent.getParcelableExtra(ENTRY_KEY), intent.getParcelableExtra(ENTRY_KEY),
parent, parent,
intent.getBooleanExtra(SAVE_DATABASE_KEY, false), intent.getBooleanExtra(SAVE_DATABASE_KEY, false),
AfterActionNodeRunnable()) AfterActionNodesRunnable())
} }
} else { } else {
null null
@@ -347,7 +331,7 @@ class DatabaseTaskNotificationService : NotificationService(), ProgressTaskUpdat
oldEntry, oldEntry,
newEntry, newEntry,
intent.getBooleanExtra(SAVE_DATABASE_KEY, false), intent.getBooleanExtra(SAVE_DATABASE_KEY, false),
AfterActionNodeRunnable()) AfterActionNodesRunnable())
} }
} else { } else {
null null
@@ -367,7 +351,7 @@ class DatabaseTaskNotificationService : NotificationService(), ProgressTaskUpdat
getListNodesFromBundle(database, intent.extras!!), getListNodesFromBundle(database, intent.extras!!),
newParent, newParent,
intent.getBooleanExtra(SAVE_DATABASE_KEY, false), intent.getBooleanExtra(SAVE_DATABASE_KEY, false),
AfterActionNodeRunnable()) AfterActionNodesRunnable())
} }
} else { } else {
@@ -388,7 +372,7 @@ class DatabaseTaskNotificationService : NotificationService(), ProgressTaskUpdat
getListNodesFromBundle(database, intent.extras!!), getListNodesFromBundle(database, intent.extras!!),
newParent, newParent,
intent.getBooleanExtra(SAVE_DATABASE_KEY, false), intent.getBooleanExtra(SAVE_DATABASE_KEY, false),
AfterActionNodeRunnable()) AfterActionNodesRunnable())
} }
} else { } else {
@@ -406,7 +390,7 @@ class DatabaseTaskNotificationService : NotificationService(), ProgressTaskUpdat
database, database,
getListNodesFromBundle(database, intent.extras!!), getListNodesFromBundle(database, intent.extras!!),
intent.getBooleanExtra(SAVE_DATABASE_KEY, false), intent.getBooleanExtra(SAVE_DATABASE_KEY, false),
AfterActionNodeRunnable()) AfterActionNodesRunnable())
} else { } else {
null null
@@ -414,14 +398,14 @@ class DatabaseTaskNotificationService : NotificationService(), ProgressTaskUpdat
} }
private fun buildDatabaseSaveElementActionTask(intent: Intent): ActionRunnable? { private fun buildDatabaseSaveElementActionTask(intent: Intent): ActionRunnable? {
return SaveDatabaseActionRunnable(this, return SaveDatabaseRunnable(this,
Database.getInstance(), Database.getInstance(),
true, true
object: ActionRunnable() { ).apply {
override fun onFinishRun(result: Result) { mAfterSaveDatabase = { result ->
result.data = intent.extras result.data = intent.extras
} }
}) }
} }
private class ActionRunnableAsyncTask(private val progressTaskUpdater: ProgressTaskUpdater, private class ActionRunnableAsyncTask(private val progressTaskUpdater: ProgressTaskUpdater,

View File

@@ -25,58 +25,37 @@ import com.kunzisoft.keepass.database.exception.LoadDatabaseException
/** /**
* Callback after a task is completed. * Callback after a task is completed.
*/ */
abstract class ActionRunnable(private var nestedActionRunnable: ActionRunnable? = null, abstract class ActionRunnable: Runnable {
private var executeNestedActionIfResultFalse: Boolean = false)
: Runnable {
var result: Result = Result() var result: Result = Result()
private fun execute() {
nestedActionRunnable?.let {
// Pass on result on call finish
it.result = result
it.run()
}
onFinishRun(result)
}
override fun run() { override fun run() {
execute() onStartRun()
onActionRun()
onFinishRun()
} }
/** abstract fun onStartRun()
* If [success] or [executeNestedActionIfResultFalse] true,
* launch the nested action runnable if exists and finish,
* else directly finish
*/
protected fun finishRun(isSuccess: Boolean,
message: String? = null) {
finishRun(isSuccess, null, message)
}
/** abstract fun onActionRun()
* If [success] or [executeNestedActionIfResultFalse] true,
* launch the nested action runnable if exists and finish,
* else directly finish
*/
protected fun finishRun(isSuccess: Boolean,
exception: LoadDatabaseException?,
message: String? = null) {
result.isSuccess = isSuccess
result.exception = exception
result.message = message
if (isSuccess || executeNestedActionIfResultFalse) {
execute()
}
else
onFinishRun(result)
}
/** /**
* Method called when the action is finished * Method called when the action is finished
* @param result 'true' if success action, 'false' elsewhere, with message
*/ */
abstract fun onFinishRun(result: Result) abstract fun onFinishRun()
protected fun setError(message: String? = null) {
setError(null, message)
}
protected fun setError(exception: LoadDatabaseException?,
message: String? = null) {
result.isSuccess = false
result.exception = exception
result.message = message
}
/** /**
* Class to manage result from ActionRunnable * Class to manage result from ActionRunnable
@@ -84,29 +63,5 @@ abstract class ActionRunnable(private var nestedActionRunnable: ActionRunnable?
data class Result(var isSuccess: Boolean = true, data class Result(var isSuccess: Boolean = true,
var message: String? = null, var message: String? = null,
var exception: LoadDatabaseException? = null, var exception: LoadDatabaseException? = null,
var data: Bundle? = null) { var data: Bundle? = null)
fun toBundle(): Bundle {
return Bundle().apply {
putBoolean(IS_SUCCESS_KEY, isSuccess)
putString(MESSAGE_KEY, message)
putSerializable(EXCEPTION_KEY, exception)
putBundle(DATA_KEY, data)
}
}
companion object {
private const val IS_SUCCESS_KEY = "IS_SUCCESS_KEY"
private const val MESSAGE_KEY = "MESSAGE_KEY"
private const val EXCEPTION_KEY = "EXCEPTION_KEY"
private const val DATA_KEY = "DATA_KEY"
fun fromBundle(bundle: Bundle): Result {
return Result(bundle.getBoolean(IS_SUCCESS_KEY),
bundle.getString(MESSAGE_KEY),
bundle.getSerializable(EXCEPTION_KEY) as LoadDatabaseException?,
bundle.getBundle(DATA_KEY))
}
}
}
} }