Merge branch 'feature/Loading_Database' into develop

This commit is contained in:
J-Jamet
2019-10-21 11:09:37 +02:00
63 changed files with 1997 additions and 955 deletions

View File

@@ -60,6 +60,8 @@ class EntryActivity : LockingHideActivity() {
private var entryContentsView: EntryContentsView? = null
private var toolbar: Toolbar? = null
private var mDatabase: Database? = null
private var mEntry: EntryVersioned? = null
private var mIsHistory: Boolean = false
@@ -80,34 +82,11 @@ class EntryActivity : LockingHideActivity() {
supportActionBar?.setDisplayHomeAsUpEnabled(true)
supportActionBar?.setDisplayShowHomeEnabled(true)
val currentDatabase = Database.getInstance()
mReadOnly = currentDatabase.isReadOnly || mReadOnly
mDatabase = Database.getInstance()
mReadOnly = mDatabase!!.isReadOnly || mReadOnly
mShowPassword = !PreferencesUtil.isPasswordMask(this)
// Get Entry from UUID
try {
val keyEntry: PwNodeId<UUID> = intent.getParcelableExtra(KEY_ENTRY)
mEntry = currentDatabase.getEntryById(keyEntry)
} catch (e: ClassCastException) {
Log.e(TAG, "Unable to retrieve the entry key")
}
val historyPosition = intent.getIntExtra(KEY_ENTRY_HISTORY_POSITION, -1)
if (historyPosition >= 0) {
mIsHistory = true
mEntry = mEntry?.getHistory()?.get(historyPosition)
}
if (mEntry == null) {
Toast.makeText(this, R.string.entry_not_found, Toast.LENGTH_LONG).show()
finish()
return
}
// Update last access time.
mEntry?.touch(modified = false, touchParents = false)
// Retrieve the textColor to tint the icon
val taIconColor = theme.obtainStyledAttributes(intArrayOf(R.attr.colorAccent))
iconColor = taIconColor.getColor(0, Color.BLACK)
@@ -131,6 +110,29 @@ class EntryActivity : LockingHideActivity() {
override fun onResume() {
super.onResume()
// Get Entry from UUID
try {
val keyEntry: PwNodeId<UUID> = intent.getParcelableExtra(KEY_ENTRY)
mEntry = mDatabase?.getEntryById(keyEntry)
} catch (e: ClassCastException) {
Log.e(TAG, "Unable to retrieve the entry key")
}
val historyPosition = intent.getIntExtra(KEY_ENTRY_HISTORY_POSITION, -1)
if (historyPosition >= 0) {
mIsHistory = true
mEntry = mEntry?.getHistory()?.get(historyPosition)
}
if (mEntry == null) {
Toast.makeText(this, R.string.entry_not_found, Toast.LENGTH_LONG).show()
finish()
return
}
// Update last access time.
mEntry?.touch(modified = false, touchParents = false)
mEntry?.let { entry ->
// Fill data in resume to update from EntryEditActivity
fillEntryDataInContentsView(entry)

View File

@@ -22,22 +22,22 @@ import android.app.Activity
import android.content.Intent
import android.os.Bundle
import android.os.Handler
import androidx.appcompat.widget.Toolbar
import android.util.Log
import android.view.Menu
import android.view.MenuItem
import android.view.View
import android.widget.ScrollView
import androidx.appcompat.widget.Toolbar
import com.kunzisoft.keepass.R
import com.kunzisoft.keepass.activities.dialogs.GeneratePasswordDialogFragment
import com.kunzisoft.keepass.activities.dialogs.IconPickerDialogFragment
import com.kunzisoft.keepass.activities.lock.LockingHideActivity
import com.kunzisoft.keepass.database.action.ProgressDialogSaveDatabaseThread
import com.kunzisoft.keepass.database.action.node.*
import com.kunzisoft.keepass.database.action.ProgressDialogThread
import com.kunzisoft.keepass.database.element.*
import com.kunzisoft.keepass.education.EntryEditActivityEducation
import com.kunzisoft.keepass.notifications.DatabaseTaskNotificationService.Companion.ACTION_DATABASE_CREATE_ENTRY_TASK
import com.kunzisoft.keepass.notifications.DatabaseTaskNotificationService.Companion.ACTION_DATABASE_UPDATE_ENTRY_TASK
import com.kunzisoft.keepass.settings.PreferencesUtil
import com.kunzisoft.keepass.tasks.ActionRunnable
import com.kunzisoft.keepass.timeout.TimeoutHelper
import com.kunzisoft.keepass.utils.MenuUtil
import com.kunzisoft.keepass.view.EntryEditContentsView
@@ -58,11 +58,12 @@ class EntryEditActivity : LockingHideActivity(),
// Views
private var scrollView: ScrollView? = null
private var entryEditContentsView: EntryEditContentsView? = null
private var saveView: View? = null
// Dialog thread
private var progressDialogThread: ProgressDialogThread? = null
// Education
private var entryEditActivityEducation: EntryEditActivityEducation? = null
@@ -110,7 +111,7 @@ class EntryEditActivity : LockingHideActivity(),
// Create a copy to modify
mNewEntry = EntryVersioned(entry).also { newEntry ->
// WARNING Remove the parent to keep memory with parcelable
newEntry.parent = null
newEntry.removeParent()
}
}
}
@@ -162,6 +163,17 @@ class EntryEditActivity : LockingHideActivity(),
// Verify the education views
entryEditActivityEducation = EntryEditActivityEducation(this)
// Create progress dialog
progressDialogThread = ProgressDialogThread(this) { actionTask, result ->
when (actionTask) {
ACTION_DATABASE_CREATE_ENTRY_TASK,
ACTION_DATABASE_UPDATE_ENTRY_TASK -> {
if (result.isSuccess)
finish()
}
}
}
}
private fun populateViewsWithEntry(newEntry: EntryVersioned) {
@@ -237,7 +249,6 @@ class EntryEditActivity : LockingHideActivity(),
// Launch a validation and show the error if present
if (entryEditContentsView?.isValid() == true) {
// Clone the entry
mDatabase?.let { database ->
mNewEntry?.let { newEntry ->
// WARNING Add the parent previously deleted
@@ -249,41 +260,39 @@ class EntryEditActivity : LockingHideActivity(),
populateEntryWithViews(newEntry)
// Open a progress dialog and save entry
var actionRunnable: ActionRunnable? = null
val afterActionNodeFinishRunnable = object : AfterActionNodeFinishRunnable() {
override fun onActionNodeFinish(actionNodeValues: ActionNodeValues) {
if (actionNodeValues.result.isSuccess)
finish()
}
}
if (mIsNew) {
mParent?.let { parent ->
actionRunnable = AddEntryRunnable(this@EntryEditActivity,
database,
progressDialogThread?.startDatabaseCreateEntry(
newEntry,
parent,
afterActionNodeFinishRunnable,
!mReadOnly)
!mReadOnly
)
}
} else {
mEntry?.let { oldEntry ->
actionRunnable = UpdateEntryRunnable(this@EntryEditActivity,
database,
progressDialogThread?.startDatabaseUpdateEntry(
oldEntry,
newEntry,
afterActionNodeFinishRunnable,
!mReadOnly)
}
}
actionRunnable?.let { runnable ->
ProgressDialogSaveDatabaseThread(this@EntryEditActivity) { runnable }.start()
!mReadOnly
)
}
}
}
}
}
override fun onResume() {
super.onResume()
progressDialogThread?.registerProgressTask()
}
override fun onPause() {
progressDialogThread?.unregisterProgressTask()
super.onPause()
}
override fun onCreateOptionsMenu(menu: Menu): Boolean {
super.onCreateOptionsMenu(menu)

View File

@@ -29,17 +29,17 @@ import android.os.Bundle
import android.os.Environment
import android.os.Handler
import android.preference.PreferenceManager
import androidx.annotation.RequiresApi
import com.google.android.material.snackbar.Snackbar
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import androidx.recyclerview.widget.SimpleItemAnimator
import androidx.appcompat.widget.Toolbar
import android.util.Log
import android.view.Menu
import android.view.MenuItem
import android.view.View
import android.widget.TextView
import androidx.annotation.RequiresApi
import androidx.appcompat.widget.Toolbar
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import androidx.recyclerview.widget.SimpleItemAnimator
import com.google.android.material.snackbar.Snackbar
import com.kunzisoft.keepass.R
import com.kunzisoft.keepass.activities.dialogs.AssignMasterKeyDialogFragment
import com.kunzisoft.keepass.activities.dialogs.BrowserDialogFragment
@@ -49,11 +49,10 @@ import com.kunzisoft.keepass.activities.stylish.StylishActivity
import com.kunzisoft.keepass.adapters.FileDatabaseHistoryAdapter
import com.kunzisoft.keepass.app.database.FileDatabaseHistoryAction
import com.kunzisoft.keepass.autofill.AutofillHelper
import com.kunzisoft.keepass.database.action.CreateDatabaseRunnable
import com.kunzisoft.keepass.database.action.ProgressDialogThread
import com.kunzisoft.keepass.database.element.Database
import com.kunzisoft.keepass.education.FileDatabaseSelectActivityEducation
import com.kunzisoft.keepass.tasks.ActionRunnable
import com.kunzisoft.keepass.notifications.DatabaseTaskNotificationService.Companion.ACTION_DATABASE_CREATE_TASK
import com.kunzisoft.keepass.utils.MenuUtil
import com.kunzisoft.keepass.utils.UriUtil
import com.kunzisoft.keepass.view.asError
@@ -77,6 +76,8 @@ class FileDatabaseSelectActivity : StylishActivity(),
private var mOpenFileHelper: OpenFileHelper? = null
private var progressDialogThread: ProgressDialogThread? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
@@ -160,6 +161,18 @@ class FileDatabaseSelectActivity : StylishActivity(),
&& savedInstanceState.containsKey(EXTRA_DATABASE_URI)) {
mDatabaseFileUri = savedInstanceState.getParcelable(EXTRA_DATABASE_URI)
}
// Attach the dialog thread to this activity
progressDialogThread = ProgressDialogThread(this) { actionTask, result ->
when (actionTask) {
ACTION_DATABASE_CREATE_TASK -> {
// TODO Check
// mAdapterDatabaseHistory?.notifyDataSetChanged()
// updateFileListVisibility()
GroupActivity.launch(this)
}
}
}
}
/**
@@ -220,6 +233,23 @@ class FileDatabaseSelectActivity : StylishActivity(),
})
}
private fun launchGroupActivity(readOnly: Boolean) {
EntrySelectionHelper.doEntrySelectionAction(intent,
{
GroupActivity.launch(this@FileDatabaseSelectActivity, readOnly)
},
{
GroupActivity.launchForKeyboardSelection(this@FileDatabaseSelectActivity, readOnly)
// Do not keep history
finish()
},
{ assistStructure ->
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
GroupActivity.launchForAutofillResult(this@FileDatabaseSelectActivity, assistStructure, readOnly)
}
})
}
private fun launchPasswordActivityWithPath(databaseUri: Uri) {
launchPasswordActivity(databaseUri, null)
// Delete flickering for kitkat <=
@@ -247,6 +277,11 @@ class FileDatabaseSelectActivity : StylishActivity(),
}
override fun onResume() {
val database = Database.getInstance()
if (database.loaded) {
launchGroupActivity(database.isReadOnly)
}
super.onResume()
updateExternalStorageWarning()
@@ -259,6 +294,16 @@ class FileDatabaseSelectActivity : StylishActivity(),
mAdapterDatabaseHistory?.notifyDataSetChanged()
}
}
// Register progress task
progressDialogThread?.registerProgressTask()
}
override fun onPause() {
// Unregister progress task
progressDialogThread?.unregisterProgressTask()
super.onPause()
}
override fun onSaveInstanceState(outState: Bundle) {
@@ -284,21 +329,13 @@ class FileDatabaseSelectActivity : StylishActivity(),
mDatabaseFileUri?.let { databaseUri ->
// Create the new database
ProgressDialogThread(this@FileDatabaseSelectActivity,
{
CreateDatabaseRunnable(this@FileDatabaseSelectActivity,
progressDialogThread?.startDatabaseCreate(
databaseUri,
Database.getInstance(),
masterPasswordChecked,
masterPassword,
keyFileChecked,
keyFile,
true, // TODO get readonly
LaunchGroupActivityFinish(databaseUri, keyFile)
keyFile
)
},
R.string.progress_create)
.start()
}
} catch (e: Exception) {
val error = getString(R.string.error_create_database_file)
@@ -307,28 +344,6 @@ class FileDatabaseSelectActivity : StylishActivity(),
}
}
private inner class LaunchGroupActivityFinish(private val databaseFileUri: Uri,
private val keyFileUri: Uri?) : ActionRunnable() {
override fun run() {
finishRun(true)
}
override fun onFinishRun(result: Result) {
runOnUiThread {
if (result.isSuccess) {
// Add database to recent files
mFileDatabaseHistoryAction?.addOrUpdateDatabaseUri(databaseFileUri, keyFileUri)
mAdapterDatabaseHistory?.notifyDataSetChanged()
updateFileListVisibility()
GroupActivity.launch(this@FileDatabaseSelectActivity)
} else {
Log.e(TAG, "Unable to open the database")
}
}
}
}
override fun onAssignKeyDialogNegativeClick(
masterPasswordChecked: Boolean, masterPassword: String?,
keyFileChecked: Boolean, keyFile: Uri?) {

View File

@@ -50,12 +50,19 @@ import com.kunzisoft.keepass.activities.lock.LockingActivity
import com.kunzisoft.keepass.adapters.SearchEntryCursorAdapter
import com.kunzisoft.keepass.autofill.AutofillHelper
import com.kunzisoft.keepass.database.SortNodeEnum
import com.kunzisoft.keepass.database.action.ProgressDialogSaveDatabaseThread
import com.kunzisoft.keepass.database.action.node.*
import com.kunzisoft.keepass.database.action.ProgressDialogThread
import com.kunzisoft.keepass.database.element.*
import com.kunzisoft.keepass.education.GroupActivityEducation
import com.kunzisoft.keepass.icons.assignDatabaseIcon
import com.kunzisoft.keepass.magikeyboard.MagikIME
import com.kunzisoft.keepass.notifications.DatabaseTaskNotificationService.Companion.ACTION_DATABASE_COPY_NODES_TASK
import com.kunzisoft.keepass.notifications.DatabaseTaskNotificationService.Companion.ACTION_DATABASE_CREATE_GROUP_TASK
import com.kunzisoft.keepass.notifications.DatabaseTaskNotificationService.Companion.ACTION_DATABASE_DELETE_NODES_TASK
import com.kunzisoft.keepass.notifications.DatabaseTaskNotificationService.Companion.ACTION_DATABASE_MOVE_NODES_TASK
import com.kunzisoft.keepass.notifications.DatabaseTaskNotificationService.Companion.ACTION_DATABASE_UPDATE_GROUP_TASK
import com.kunzisoft.keepass.notifications.DatabaseTaskNotificationService.Companion.NEW_NODES_KEY
import com.kunzisoft.keepass.notifications.DatabaseTaskNotificationService.Companion.OLD_NODES_KEY
import com.kunzisoft.keepass.notifications.DatabaseTaskNotificationService.Companion.getListNodesFromBundle
import com.kunzisoft.keepass.settings.PreferencesUtil
import com.kunzisoft.keepass.timeout.TimeoutHelper
import com.kunzisoft.keepass.utils.MenuUtil
@@ -85,6 +92,8 @@ class GroupActivity : LockingActivity(),
private var mListNodesFragment: ListNodesFragment? = null
private var mCurrentGroupIsASearch: Boolean = false
private var progressDialogThread: ProgressDialogThread? = null
// Nodes
private var mRootGroup: GroupVersioned? = null
private var mCurrentGroup: GroupVersioned? = null
@@ -188,9 +197,64 @@ class GroupActivity : LockingActivity(),
}
})
// Search suggestion
mDatabase?.let { database ->
// Search suggestion
mSearchSuggestionAdapter = SearchEntryCursorAdapter(this, database)
// Init dialog thread
progressDialogThread = ProgressDialogThread(this) { actionTask, result ->
var oldNodes: List<NodeVersioned> = ArrayList()
result.data?.getBundle(OLD_NODES_KEY)?.let { oldNodesBundle ->
oldNodes = getListNodesFromBundle(database, oldNodesBundle)
}
var newNodes: List<NodeVersioned> = ArrayList()
result.data?.getBundle(NEW_NODES_KEY)?.let { newNodesBundle ->
newNodes = getListNodesFromBundle(database, newNodesBundle)
}
when (actionTask) {
ACTION_DATABASE_UPDATE_GROUP_TASK -> {
if (result.isSuccess) {
mListNodesFragment?.updateNodes(oldNodes, newNodes)
}
}
ACTION_DATABASE_CREATE_GROUP_TASK,
ACTION_DATABASE_COPY_NODES_TASK,
ACTION_DATABASE_MOVE_NODES_TASK -> {
if (result.isSuccess) {
mListNodesFragment?.addNodes(newNodes)
}
}
ACTION_DATABASE_DELETE_NODES_TASK -> {
if (result.isSuccess) {
// Rebuild all the list the avoid bug when delete node from db sort
if (PreferencesUtil.getListSort(this@GroupActivity) == SortNodeEnum.DB) {
mListNodesFragment?.rebuildList()
} else {
// Use the old Nodes / entries unchanged with the old parent
mListNodesFragment?.removeNodes(oldNodes)
}
// Add trash in views list if it doesn't exists
if (database.isRecycleBinEnabled) {
val recycleBin = database.recycleBin
if (mCurrentGroup != null && recycleBin != null
&& mCurrentGroup!!.parent == null
&& mCurrentGroup != recycleBin) {
if (mListNodesFragment?.contains(recycleBin) == true)
mListNodesFragment?.updateNode(recycleBin)
else
mListNodesFragment?.addNode(recycleBin)
}
}
}
}
}
finishNodeAction()
}
}
Log.i(TAG, "Finished creating tree")
@@ -487,28 +551,21 @@ class GroupActivity : LockingActivity(),
ListNodesFragment.PasteMode.PASTE_FROM_COPY -> {
// Copy
mCurrentGroup?.let { newParent ->
ProgressDialogSaveDatabaseThread(this) {
CopyNodesRunnable(this,
Database.getInstance(),
progressDialogThread?.startDatabaseCopyNodes(
nodes,
newParent,
AfterAddNodeRunnable(),
!mReadOnly)
}.start()
!mReadOnly
)
}
}
ListNodesFragment.PasteMode.PASTE_FROM_MOVE -> {
// Move
mCurrentGroup?.let { newParent ->
ProgressDialogSaveDatabaseThread(this) {
MoveNodesRunnable(
this,
Database.getInstance(),
progressDialogThread?.startDatabaseMoveNodes(
nodes,
newParent,
AfterAddNodeRunnable(),
!mReadOnly)
}.start()
!mReadOnly
)
}
}
}
@@ -516,14 +573,10 @@ class GroupActivity : LockingActivity(),
}
override fun onDeleteMenuClick(nodes: List<NodeVersioned>): Boolean {
ProgressDialogSaveDatabaseThread(this) {
DeleteNodesRunnable(
this,
Database.getInstance(),
progressDialogThread?.startDatabaseDeleteNodes(
nodes,
AfterDeleteNodeRunnable(),
!mReadOnly)
}.start()
!mReadOnly
)
return true
}
@@ -533,9 +586,13 @@ class GroupActivity : LockingActivity(),
assignGroupViewElements()
// Refresh suggestions to change preferences
mSearchSuggestionAdapter?.reInit(this)
progressDialogThread?.registerProgressTask()
}
override fun onPause() {
progressDialogThread?.unregisterProgressTask()
super.onPause()
finishNodeAction()
@@ -670,7 +727,6 @@ class GroupActivity : LockingActivity(),
override fun approveEditGroup(action: GroupEditDialogFragment.EditGroupDialogAction?,
name: String?,
icon: PwIcon?) {
val database = Database.getInstance()
if (name != null && name.isNotEmpty() && icon != null) {
when (action) {
@@ -678,43 +734,34 @@ class GroupActivity : LockingActivity(),
// If group creation
mCurrentGroup?.let { currentGroup ->
// Build the group
database.createGroup()?.let { newGroup ->
mDatabase?.createGroup()?.let { newGroup ->
newGroup.title = name
newGroup.icon = icon
// Not really needed here because added in runnable but safe
newGroup.parent = currentGroup
// If group created save it in the database
ProgressDialogSaveDatabaseThread(this) {
AddGroupRunnable(this,
Database.getInstance(),
newGroup,
currentGroup,
AfterAddNodeRunnable(),
!mReadOnly)
}.start()
progressDialogThread?.startDatabaseCreateGroup(
newGroup, currentGroup, !mReadOnly)
}
}
}
GroupEditDialogFragment.EditGroupDialogAction.UPDATE ->
GroupEditDialogFragment.EditGroupDialogAction.UPDATE -> {
// If update add new elements
mOldGroupToUpdate?.let { oldGroupToUpdate ->
GroupVersioned(oldGroupToUpdate).let { updateGroup ->
updateGroup.title = name
// TODO custom icon
updateGroup.icon = icon
updateGroup.apply {
// WARNING remove parent and children to keep memory
removeParent()
removeChildren()
mListNodesFragment?.removeNode(oldGroupToUpdate)
title = name
this.icon = icon // TODO custom icon
}
// If group updated save it in the database
ProgressDialogSaveDatabaseThread(this) {
UpdateGroupRunnable(this,
Database.getInstance(),
oldGroupToUpdate,
updateGroup,
AfterUpdateNodeRunnable(),
!mReadOnly)
}.start()
progressDialogThread?.startDatabaseUpdateGroup(
oldGroupToUpdate, updateGroup, !mReadOnly)
}
}
}
else -> {}
@@ -722,60 +769,6 @@ class GroupActivity : LockingActivity(),
}
}
internal inner class AfterAddNodeRunnable : AfterActionNodeFinishRunnable() {
override fun onActionNodeFinish(actionNodeValues: ActionNodeValues) {
runOnUiThread {
if (actionNodeValues.result.isSuccess) {
mListNodesFragment?.addNodes(actionNodeValues.newNodes)
}
finishNodeAction()
}
}
}
internal inner class AfterUpdateNodeRunnable : AfterActionNodeFinishRunnable() {
override fun onActionNodeFinish(actionNodeValues: ActionNodeValues) {
runOnUiThread {
if (actionNodeValues.result.isSuccess) {
mListNodesFragment?.updateNodes(actionNodeValues.oldNodes, actionNodeValues.newNodes)
}
finishNodeAction()
}
}
}
internal inner class AfterDeleteNodeRunnable : AfterActionNodeFinishRunnable() {
override fun onActionNodeFinish(actionNodeValues: ActionNodeValues) {
runOnUiThread {
if (actionNodeValues.result.isSuccess) {
// Rebuold all the list the avoid bug when delete node from db sort
if (PreferencesUtil.getListSort(this@GroupActivity) == SortNodeEnum.DB) {
mListNodesFragment?.rebuildList()
} else {
// Use the old Nodes / entries unchanged with the old parent
mListNodesFragment?.removeNodes(actionNodeValues.oldNodes)
}
// Add trash in views list if it doesn't exists
val database = Database.getInstance()
if (database.isRecycleBinEnabled) {
val recycleBin = database.recycleBin
if (mCurrentGroup != null && recycleBin != null
&& mCurrentGroup!!.parent == null
&& mCurrentGroup != recycleBin) {
if (mListNodesFragment?.contains(recycleBin) == true)
mListNodesFragment?.updateNode(recycleBin)
else
mListNodesFragment?.addNode(recycleBin)
}
}
}
finishNodeAction()
}
}
}
override fun cancelEditGroup(action: GroupEditDialogFragment.EditGroupDialogAction?,
name: String?,
icon: PwIcon?) {
@@ -885,10 +878,14 @@ class GroupActivity : LockingActivity(),
private const val SEARCH_FRAGMENT_TAG = "SEARCH_FRAGMENT_TAG"
private const val OLD_GROUP_TO_UPDATE_KEY = "OLD_GROUP_TO_UPDATE_KEY"
private fun buildAndLaunchIntent(activity: Activity, group: GroupVersioned?, readOnly: Boolean,
private fun buildAndLaunchIntent(context: Context, group: GroupVersioned?, readOnly: Boolean,
intentBuildLauncher: (Intent) -> Unit) {
if (TimeoutHelper.checkTimeAndLockIfTimeout(activity)) {
val intent = Intent(activity, GroupActivity::class.java)
val checkTime = if (context is Activity)
TimeoutHelper.checkTimeAndLockIfTimeout(context)
else
TimeoutHelper.checkTime(context)
if (checkTime) {
val intent = Intent(context, GroupActivity::class.java)
if (group != null) {
intent.putExtra(GROUP_ID_KEY, group.nodeId)
}
@@ -904,10 +901,10 @@ class GroupActivity : LockingActivity(),
*/
@JvmOverloads
fun launch(activity: Activity, readOnly: Boolean = PreferencesUtil.enableReadOnlyDatabase(activity)) {
TimeoutHelper.recordTime(activity)
buildAndLaunchIntent(activity, null, readOnly) { intent ->
activity.startActivity(intent)
fun launch(context: Context, readOnly: Boolean = PreferencesUtil.enableReadOnlyDatabase(context)) {
TimeoutHelper.recordTime(context)
buildAndLaunchIntent(context, null, readOnly) { intent ->
context.startActivity(intent)
}
}
@@ -918,10 +915,10 @@ class GroupActivity : LockingActivity(),
*/
// TODO implement pre search to directly open the direct group
fun launchForKeyboarSelection(activity: Activity, readOnly: Boolean) {
TimeoutHelper.recordTime(activity)
buildAndLaunchIntent(activity, null, readOnly) { intent ->
EntrySelectionHelper.startActivityForEntrySelection(activity, intent)
fun launchForKeyboardSelection(context: Context, readOnly: Boolean) {
TimeoutHelper.recordTime(context)
buildAndLaunchIntent(context, null, readOnly) { intent ->
EntrySelectionHelper.startActivityForEntrySelection(context, intent)
}
}

View File

@@ -22,7 +22,6 @@ package com.kunzisoft.keepass.activities
import android.app.Activity
import android.app.assist.AssistStructure
import android.app.backup.BackupManager
import android.content.DialogInterface
import android.content.Intent
import android.content.SharedPreferences
import android.net.Uri
@@ -30,9 +29,6 @@ import android.os.Build
import android.os.Bundle
import android.os.Handler
import android.preference.PreferenceManager
import androidx.annotation.RequiresApi
import com.google.android.material.snackbar.Snackbar
import androidx.appcompat.widget.Toolbar
import android.text.Editable
import android.text.TextWatcher
import android.util.Log
@@ -41,31 +37,38 @@ import android.view.Menu
import android.view.MenuItem
import android.view.View
import android.view.inputmethod.EditorInfo.IME_ACTION_DONE
import android.widget.*
import android.widget.Button
import android.widget.CompoundButton
import android.widget.EditText
import android.widget.TextView
import androidx.annotation.RequiresApi
import androidx.appcompat.widget.Toolbar
import androidx.biometric.BiometricManager
import com.google.android.material.snackbar.Snackbar
import com.kunzisoft.keepass.R
import com.kunzisoft.keepass.activities.dialogs.DuplicateUuidDialog
import com.kunzisoft.keepass.activities.dialogs.FingerPrintExplanationDialog
import com.kunzisoft.keepass.activities.dialogs.PasswordEncodingDialogFragment
import com.kunzisoft.keepass.activities.helpers.EntrySelectionHelper
import com.kunzisoft.keepass.activities.helpers.OpenFileHelper
import com.kunzisoft.keepass.activities.helpers.ReadOnlyHelper
import com.kunzisoft.keepass.activities.lock.LockingActivity
import com.kunzisoft.keepass.activities.stylish.StylishActivity
import com.kunzisoft.keepass.app.database.CipherDatabaseAction
import com.kunzisoft.keepass.app.database.CipherDatabaseEntity
import com.kunzisoft.keepass.utils.FileDatabaseInfo
import com.kunzisoft.keepass.app.database.FileDatabaseHistoryAction
import com.kunzisoft.keepass.autofill.AutofillHelper
import com.kunzisoft.keepass.database.action.LoadDatabaseRunnable
import com.kunzisoft.keepass.biometric.AdvancedUnlockedManager
import com.kunzisoft.keepass.database.action.ProgressDialogThread
import com.kunzisoft.keepass.database.element.Database
import com.kunzisoft.keepass.education.PasswordActivityEducation
import com.kunzisoft.keepass.biometric.AdvancedUnlockedManager
import com.kunzisoft.keepass.database.exception.LoadDatabaseDuplicateUuidException
import com.kunzisoft.keepass.database.search.SearchDbHelper
import com.kunzisoft.keepass.education.PasswordActivityEducation
import com.kunzisoft.keepass.notifications.DatabaseTaskNotificationService.Companion.ACTION_DATABASE_LOAD_TASK
import com.kunzisoft.keepass.notifications.DatabaseTaskNotificationService.Companion.CIPHER_ENTITY_KEY
import com.kunzisoft.keepass.notifications.DatabaseTaskNotificationService.Companion.DATABASE_URI_KEY
import com.kunzisoft.keepass.notifications.DatabaseTaskNotificationService.Companion.KEY_FILE_KEY
import com.kunzisoft.keepass.notifications.DatabaseTaskNotificationService.Companion.MASTER_PASSWORD_KEY
import com.kunzisoft.keepass.notifications.DatabaseTaskNotificationService.Companion.READ_ONLY_KEY
import com.kunzisoft.keepass.settings.PreferencesUtil
import com.kunzisoft.keepass.tasks.ActionRunnable
import com.kunzisoft.keepass.utils.FileDatabaseInfo
import com.kunzisoft.keepass.utils.MenuUtil
import com.kunzisoft.keepass.utils.UriUtil
import com.kunzisoft.keepass.view.AdvancedUnlockInfoView
@@ -99,6 +102,8 @@ class PasswordActivity : StylishActivity() {
private var readOnly: Boolean = false
private var progressDialogThread: ProgressDialogThread? = null
private var advancedUnlockedManager: AdvancedUnlockedManager? = null
override fun onCreate(savedInstanceState: Bundle?) {
@@ -158,6 +163,88 @@ class PasswordActivity : StylishActivity() {
enableButtonOnCheckedChangeListener = CompoundButton.OnCheckedChangeListener { _, _ ->
enableOrNotTheConfirmationButton()
}
progressDialogThread = ProgressDialogThread(this) { actionTask, result ->
when (actionTask) {
ACTION_DATABASE_LOAD_TASK -> {
// Recheck fingerprint if error
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
if (PreferencesUtil.isBiometricUnlockEnable(this@PasswordActivity)) {
// Stay with the same mode and init it
advancedUnlockedManager?.initBiometricMode()
}
}
var databaseUri: Uri? = null
var masterPassword: String? = null
var keyFileUri: Uri? = null
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 ->
// Remove the password in view in all cases
removePassword()
if (result.isSuccess) {
launchGroupActivity()
} else {
var resultError = ""
val resultException = result.exception
val resultMessage = result.message
if (resultException != null) {
resultError = resultException.getLocalizedMessage(resources)
if (resultException is LoadDatabaseDuplicateUuidException)
showLoadDatabaseDuplicateUuidMessage {
showProgressDialogAndLoadDatabase(
databaseFileUri,
masterPassword,
keyFileUri,
readOnly,
cipherEntity,
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()
}
}
}
}
}
}
private fun launchGroupActivity() {
EntrySelectionHelper.doEntrySelectionAction(intent,
{
GroupActivity.launch(this@PasswordActivity, readOnly)
},
{
GroupActivity.launchForKeyboardSelection(this@PasswordActivity, readOnly)
// Do not keep history
finish()
},
{ assistStructure ->
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
GroupActivity.launchForAutofillResult(this@PasswordActivity, assistStructure, readOnly)
}
})
}
private val onEditorActionListener = object : TextView.OnEditorActionListener {
@@ -171,6 +258,9 @@ class PasswordActivity : StylishActivity() {
}
override fun onResume() {
if (Database.getInstance().loaded)
launchGroupActivity()
// If the database isn't accessible make sure to clear the password field, if it
// was saved in the instance state
if (Database.getInstance().loaded) {
@@ -180,6 +270,8 @@ class PasswordActivity : StylishActivity() {
// For check shutdown
super.onResume()
progressDialogThread?.registerProgressTask()
initUriFromIntent()
}
@@ -372,6 +464,9 @@ class PasswordActivity : StylishActivity() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
advancedUnlockedManager?.pause()
}
progressDialogThread?.unregisterProgressTask()
super.onPause()
}
@@ -416,111 +511,36 @@ class PasswordActivity : StylishActivity() {
keyFileUri: Uri?,
cipherDatabaseEntity: CipherDatabaseEntity? = null) {
runOnUiThread {
if (PreferencesUtil.deletePasswordAfterConnexionAttempt(this)) {
removePassword()
}
}
// Clear before we load
val database = Database.getInstance()
database.closeAndClear(applicationContext.filesDir)
databaseFileUri?.let { databaseUri ->
val onFinishLoadDatabase = object: ActionRunnable() {
override fun onFinishRun(result: Result) {
runOnUiThread {
// Recheck fingerprint if error
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
if (PreferencesUtil.isBiometricUnlockEnable(this@PasswordActivity)) {
// Stay with the same mode and init it
advancedUnlockedManager?.initBiometricMode()
}
}
if (result.isSuccess) {
// Save keyFile in app database
if (mRememberKeyFile) {
mDatabaseFileUri?.let { databaseUri ->
saveKeyFileData(databaseUri, mDatabaseKeyFileUri)
}
}
// Remove the password in view in all cases
removePassword()
// Register the biometric
if (cipherDatabaseEntity != null) {
CipherDatabaseAction.getInstance(this@PasswordActivity)
.addOrUpdateCipherDatabase(cipherDatabaseEntity) {
checkAndLaunchGroupActivity(database, password, keyFileUri)
}
} else {
checkAndLaunchGroupActivity(database, password, keyFileUri)
}
} else {
var resultError = ""
val resultException = result.exception
val resultMessage = result.message
if (resultException != null) {
resultError = resultException.getLocalizedMessage(resources)
if (resultException is LoadDatabaseDuplicateUuidException)
showLoadDatabaseDuplicateUuidMessage {
showProgressDialogAndLoadDatabase(database,
databaseUri,
password,
keyFileUri,
true,
this)
}
}
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 the progress dialog and load the database
showProgressDialogAndLoadDatabase(database,
showProgressDialogAndLoadDatabase(
databaseUri,
password,
keyFileUri,
false,
onFinishLoadDatabase)
readOnly,
cipherDatabaseEntity,
false)
}
}
private fun showProgressDialogAndLoadDatabase(database: Database,
databaseUri: Uri,
private fun showProgressDialogAndLoadDatabase(databaseUri: Uri,
password: String?,
keyFile: Uri?,
fixDuplicateUUID: Boolean,
onFinishLoadDatabase: ActionRunnable) {
ProgressDialogThread(this,
{ progressTaskUpdater ->
LoadDatabaseRunnable(
database,
readOnly: Boolean,
cipherDatabaseEntity: CipherDatabaseEntity?,
fixDuplicateUUID: Boolean) {
progressDialogThread?.startDatabaseLoad(
databaseUri,
password,
keyFile,
this@PasswordActivity.contentResolver,
this@PasswordActivity.filesDir,
SearchDbHelper(PreferencesUtil.omitBackup(this@PasswordActivity)),
fixDuplicateUUID,
progressTaskUpdater,
onFinishLoadDatabase)
},
R.string.loading_database).start()
readOnly,
cipherDatabaseEntity,
fixDuplicateUUID
)
}
private fun showLoadDatabaseDuplicateUuidMessage(loadDatabaseWithFix: (() -> Unit)? = null) {
@@ -529,44 +549,6 @@ class PasswordActivity : StylishActivity() {
}.show(supportFragmentManager, "duplicateUUIDDialog")
}
private fun saveKeyFileData(databaseUri: Uri, keyUri: Uri?) {
var keyFileUri = keyUri
if (!mRememberKeyFile) {
keyFileUri = null
}
FileDatabaseHistoryAction.getInstance(this).addOrUpdateDatabaseUri(databaseUri, keyFileUri)
}
private fun checkAndLaunchGroupActivity(database: Database, password: String?, keyFileUri: Uri?) {
if (database.validatePasswordEncoding(password, keyFileUri != null)) {
launchGroupActivity()
} else {
PasswordEncodingDialogFragment().apply {
positiveButtonClickListener = DialogInterface.OnClickListener { _, _ ->
launchGroupActivity()
}
show(supportFragmentManager, "passwordEncodingTag")
}
}
}
private fun launchGroupActivity() {
EntrySelectionHelper.doEntrySelectionAction(intent,
{
GroupActivity.launch(this@PasswordActivity, readOnly)
},
{
GroupActivity.launchForKeyboarSelection(this@PasswordActivity, readOnly)
// Do not keep history
finish()
},
{ assistStructure ->
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
GroupActivity.launchForAutofillResult(this@PasswordActivity, assistStructure, readOnly)
}
})
}
// To fix multiple view education
private var performedEductionInProgress = false

View File

@@ -1,7 +1,7 @@
package com.kunzisoft.keepass.activities.helpers
import android.app.Activity
import android.app.assist.AssistStructure
import android.content.Context
import android.content.Intent
import android.os.Build
import com.kunzisoft.keepass.autofill.AutofillHelper
@@ -11,10 +11,10 @@ object EntrySelectionHelper {
private const val EXTRA_ENTRY_SELECTION_MODE = "com.kunzisoft.keepass.extra.ENTRY_SELECTION_MODE"
private const val DEFAULT_ENTRY_SELECTION_MODE = false
fun startActivityForEntrySelection(activity: Activity, intent: Intent) {
fun startActivityForEntrySelection(context: Context, intent: Intent) {
addEntrySelectionModeExtraInIntent(intent)
// only to avoid visible flickering when redirecting
activity.startActivity(intent)
context.startActivity(intent)
}
fun addEntrySelectionModeExtraInIntent(intent: Intent) {

View File

@@ -142,6 +142,7 @@ class NodeAdapter
Log.e(TAG, "Can't add node elements to the list", e)
Toast.makeText(context, "Can't add node elements to the list : " + e.message, Toast.LENGTH_LONG).show()
}
notifyDataSetChanged()
}
fun contains(node: NodeVersioned): Boolean {

View File

@@ -1,5 +1,7 @@
package com.kunzisoft.keepass.app.database
import android.os.Parcel
import android.os.Parcelable
import androidx.room.ColumnInfo
import androidx.room.Entity
import androidx.room.PrimaryKey
@@ -15,7 +17,33 @@ data class CipherDatabaseEntity(
@ColumnInfo(name = "specs_parameters")
var specParameters: String
) {
): Parcelable {
constructor(parcel: Parcel) : this(
parcel.readString()!!,
parcel.readString()!!,
parcel.readString()!!)
override fun writeToParcel(parcel: Parcel, flags: Int) {
parcel.writeString(databaseUri)
parcel.writeString(encryptedValue)
parcel.writeString(specParameters)
}
override fun describeContents(): Int {
return 0
}
companion object CREATOR : Parcelable.Creator<CipherDatabaseEntity> {
override fun createFromParcel(parcel: Parcel): CipherDatabaseEntity {
return CipherDatabaseEntity(parcel)
}
override fun newArray(size: Int): Array<CipherDatabaseEntity?> {
return arrayOfNulls(size)
}
}
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (javaClass != other?.javaClass) return false

View File

@@ -111,21 +111,30 @@ class AdvancedUnlockedManager(var context: FragmentActivity,
override fun onAuthenticationError(
errorCode: Int,
errString: CharSequence) {
context.runOnUiThread {
Log.e(TAG, "Biometric authentication error. Code : $errorCode Error : $errString")
setAdvancedUnlockedMessageView(errString.toString())
}
}
override fun onAuthenticationFailed() {
context.runOnUiThread {
Log.e(TAG, "Biometric authentication failed, biometric not recognized")
setAdvancedUnlockedMessageView(R.string.biometric_not_recognized)
}
}
override fun onAuthenticationSucceeded(result: BiometricPrompt.AuthenticationResult) {
context.runOnUiThread {
when (biometricMode) {
Mode.UNAVAILABLE -> {}
Mode.PAUSE -> {}
Mode.NOT_CONFIGURED -> {}
Mode.WAIT_CREDENTIAL -> {}
Mode.UNAVAILABLE -> {
}
Mode.PAUSE -> {
}
Mode.NOT_CONFIGURED -> {
}
Mode.WAIT_CREDENTIAL -> {
}
Mode.STORE -> {
// newly store the entered password in encrypted way
biometricUnlockDatabaseHelper?.encryptData(passwordView?.text.toString())
@@ -141,6 +150,7 @@ class AdvancedUnlockedManager(var context: FragmentActivity,
}
}
}
}
private fun initNotAvailable() {
showFingerPrintViews(false)

View File

@@ -22,9 +22,11 @@ package com.kunzisoft.keepass.crypto.keyDerivation
import com.kunzisoft.keepass.database.ObjectNameResource
import java.io.IOException
import java.io.Serializable
import java.util.UUID
abstract class KdfEngine : ObjectNameResource {
// TODO Parcelable
abstract class KdfEngine : ObjectNameResource, Serializable {
var uuid: UUID? = null

View File

@@ -19,37 +19,66 @@
*/
package com.kunzisoft.keepass.database.action
import android.content.ContentResolver
import android.content.Context
import android.net.Uri
import com.kunzisoft.keepass.app.database.CipherDatabaseAction
import com.kunzisoft.keepass.app.database.CipherDatabaseEntity
import com.kunzisoft.keepass.app.database.FileDatabaseHistoryAction
import com.kunzisoft.keepass.database.element.Database
import com.kunzisoft.keepass.database.exception.LoadDatabaseException
import com.kunzisoft.keepass.database.search.SearchDbHelper
import com.kunzisoft.keepass.settings.PreferencesUtil
import com.kunzisoft.keepass.tasks.ActionRunnable
import com.kunzisoft.keepass.tasks.ProgressTaskUpdater
import java.io.File
class LoadDatabaseRunnable(private val mDatabase: Database,
class LoadDatabaseRunnable(private val context: Context,
private val mDatabase: Database,
private val mUri: Uri,
private val mPass: String?,
private val mKey: Uri?,
private val contentResolver: ContentResolver,
private val cacheDirectory: File,
private val mSearchHelper: SearchDbHelper,
private val mReadonly: Boolean,
private val mCipherEntity: CipherDatabaseEntity?,
private val mOmitBackup: Boolean,
private val mFixDuplicateUUID: Boolean,
private val progressTaskUpdater: ProgressTaskUpdater?,
nestedAction: ActionRunnable)
: ActionRunnable(nestedAction, executeNestedActionIfResultFalse = true) {
actionFinishRunnable: ActionRunnable?)
: ActionRunnable(actionFinishRunnable, executeNestedActionIfResultFalse = true) {
private val cacheDirectory = context.applicationContext.filesDir
override fun run() {
try {
// Clear before we load
mDatabase.closeAndClear(cacheDirectory)
mDatabase.loadData(mUri, mPass, mKey,
contentResolver,
mReadonly,
context.contentResolver,
cacheDirectory,
mSearchHelper,
mOmitBackup,
mFixDuplicateUUID,
progressTaskUpdater)
// Save keyFile in app database
val rememberKeyFile = PreferencesUtil.rememberKeyFiles(context)
if (rememberKeyFile) {
var keyUri = mKey
if (!rememberKeyFile) {
keyUri = null
}
FileDatabaseHistoryAction.getInstance(context)
.addOrUpdateDatabaseUri(mUri, keyUri)
}
// Register the biometric
mCipherEntity?.let { cipherDatabaseEntity ->
CipherDatabaseAction.getInstance(context)
.addOrUpdateCipherDatabase(cipherDatabaseEntity) {
finishRun(true)
}
} ?: run {
finishRun(true)
}
}
catch (e: LoadDatabaseException) {
finishRun(false, e)
}

View File

@@ -1,14 +0,0 @@
package com.kunzisoft.keepass.database.action
import androidx.fragment.app.FragmentActivity
import com.kunzisoft.keepass.R
import com.kunzisoft.keepass.tasks.ActionRunnable
import com.kunzisoft.keepass.tasks.ProgressTaskUpdater
class ProgressDialogSaveDatabaseThread(activity: FragmentActivity,
actionRunnable: (ProgressTaskUpdater?)-> ActionRunnable)
: ProgressDialogThread(activity,
actionRunnable,
R.string.saving_database,
null,
R.string.do_not_kill_app)

View File

@@ -1,86 +1,456 @@
package com.kunzisoft.keepass.database.action
import android.content.Intent
import android.os.AsyncTask
import android.content.*
import android.content.Context.BIND_ABOVE_CLIENT
import android.content.Context.BIND_NOT_FOREGROUND
import android.net.Uri
import android.os.Build
import androidx.annotation.StringRes
import android.os.Bundle
import android.os.IBinder
import androidx.fragment.app.FragmentActivity
import com.kunzisoft.keepass.app.database.CipherDatabaseEntity
import com.kunzisoft.keepass.crypto.keyDerivation.KdfEngine
import com.kunzisoft.keepass.database.element.*
import com.kunzisoft.keepass.notifications.DatabaseTaskNotificationService
import com.kunzisoft.keepass.notifications.DatabaseTaskNotificationService.Companion.DATABASE_TASK_TITLE_KEY
import com.kunzisoft.keepass.notifications.DatabaseTaskNotificationService.Companion.ACTION_DATABASE_ASSIGN_PASSWORD_TASK
import com.kunzisoft.keepass.notifications.DatabaseTaskNotificationService.Companion.ACTION_DATABASE_COPY_NODES_TASK
import com.kunzisoft.keepass.notifications.DatabaseTaskNotificationService.Companion.ACTION_DATABASE_CREATE_ENTRY_TASK
import com.kunzisoft.keepass.notifications.DatabaseTaskNotificationService.Companion.ACTION_DATABASE_CREATE_GROUP_TASK
import com.kunzisoft.keepass.notifications.DatabaseTaskNotificationService.Companion.ACTION_DATABASE_CREATE_TASK
import com.kunzisoft.keepass.notifications.DatabaseTaskNotificationService.Companion.ACTION_DATABASE_DELETE_NODES_TASK
import com.kunzisoft.keepass.notifications.DatabaseTaskNotificationService.Companion.ACTION_DATABASE_LOAD_TASK
import com.kunzisoft.keepass.notifications.DatabaseTaskNotificationService.Companion.ACTION_DATABASE_MOVE_NODES_TASK
import com.kunzisoft.keepass.notifications.DatabaseTaskNotificationService.Companion.ACTION_DATABASE_SAVE_COLOR_TASK
import com.kunzisoft.keepass.notifications.DatabaseTaskNotificationService.Companion.ACTION_DATABASE_SAVE_COMPRESSION_TASK
import com.kunzisoft.keepass.notifications.DatabaseTaskNotificationService.Companion.ACTION_DATABASE_SAVE_DEFAULT_USERNAME_TASK
import com.kunzisoft.keepass.notifications.DatabaseTaskNotificationService.Companion.ACTION_DATABASE_SAVE_DESCRIPTION_TASK
import com.kunzisoft.keepass.notifications.DatabaseTaskNotificationService.Companion.ACTION_DATABASE_SAVE_ENCRYPTION_TASK
import com.kunzisoft.keepass.notifications.DatabaseTaskNotificationService.Companion.ACTION_DATABASE_SAVE_ITERATIONS_TASK
import com.kunzisoft.keepass.notifications.DatabaseTaskNotificationService.Companion.ACTION_DATABASE_SAVE_KEY_DERIVATION_TASK
import com.kunzisoft.keepass.notifications.DatabaseTaskNotificationService.Companion.ACTION_DATABASE_SAVE_MAX_HISTORY_ITEMS_TASK
import com.kunzisoft.keepass.notifications.DatabaseTaskNotificationService.Companion.ACTION_DATABASE_SAVE_MAX_HISTORY_SIZE_TASK
import com.kunzisoft.keepass.notifications.DatabaseTaskNotificationService.Companion.ACTION_DATABASE_SAVE_MEMORY_USAGE_TASK
import com.kunzisoft.keepass.notifications.DatabaseTaskNotificationService.Companion.ACTION_DATABASE_SAVE_NAME_TASK
import com.kunzisoft.keepass.notifications.DatabaseTaskNotificationService.Companion.ACTION_DATABASE_SAVE_PARALLELISM_TASK
import com.kunzisoft.keepass.notifications.DatabaseTaskNotificationService.Companion.ACTION_DATABASE_UPDATE_ENTRY_TASK
import com.kunzisoft.keepass.notifications.DatabaseTaskNotificationService.Companion.ACTION_DATABASE_UPDATE_GROUP_TASK
import com.kunzisoft.keepass.notifications.DatabaseTaskNotificationService.Companion.getBundleFromListNodes
import com.kunzisoft.keepass.tasks.ActionRunnable
import com.kunzisoft.keepass.tasks.ProgressTaskDialogFragment
import com.kunzisoft.keepass.tasks.ProgressTaskUpdater
import com.kunzisoft.keepass.tasks.ProgressTaskDialogFragment.Companion.retrieveProgressDialog
import com.kunzisoft.keepass.timeout.TimeoutHelper
import com.kunzisoft.keepass.utils.DATABASE_START_TASK_ACTION
import com.kunzisoft.keepass.utils.DATABASE_STOP_TASK_ACTION
import java.util.*
import kotlin.collections.ArrayList
open class ProgressDialogThread(private val activity: FragmentActivity,
private val actionRunnable: (ProgressTaskUpdater?)-> ActionRunnable,
@StringRes private val titleId: Int,
@StringRes private val messageId: Int? = null,
@StringRes private val warningId: Int? = null) {
private val progressTaskDialogFragment = ProgressTaskDialogFragment.build(
titleId,
messageId,
warningId)
private var actionRunnableAsyncTask: ActionRunnableAsyncTask? = null
var actionFinishInUIThread: ActionRunnable? = null
class ProgressDialogThread(private val activity: FragmentActivity,
var onActionFinish: (actionTask: String,
result: ActionRunnable.Result) -> Unit) {
private var intentDatabaseTask:Intent = Intent(activity, DatabaseTaskNotificationService::class.java)
private var intentDatabaseTask = Intent(activity, DatabaseTaskNotificationService::class.java)
init {
actionRunnableAsyncTask = ActionRunnableAsyncTask(progressTaskDialogFragment,
{
private var databaseTaskBroadcastReceiver: BroadcastReceiver? = null
private var mBinder: DatabaseTaskNotificationService.ActionTaskBinder? = null
private var serviceConnection: ServiceConnection? = null
private val actionTaskListener = object: DatabaseTaskNotificationService.ActionTaskListener {
override fun onStartAction(titleId: Int?, messageId: Int?, warningId: Int?) {
TimeoutHelper.temporarilyDisableTimeout()
startOrUpdateDialog(titleId, messageId, warningId)
}
override fun onUpdateAction(titleId: Int?, messageId: Int?, warningId: Int?) {
TimeoutHelper.temporarilyDisableTimeout()
startOrUpdateDialog(titleId, messageId, warningId)
}
override fun onStopAction(actionTask: String, result: ActionRunnable.Result) {
onActionFinish.invoke(actionTask, result)
// Remove the progress task
ProgressTaskDialogFragment.stop(activity)
TimeoutHelper.releaseTemporarilyDisableTimeoutAndLockIfTimeout(activity)
}
}
private fun startOrUpdateDialog(titleId: Int?, messageId: Int?, warningId: Int?) {
var progressTaskDialogFragment = retrieveProgressDialog(activity)
if (progressTaskDialogFragment == null) {
progressTaskDialogFragment = ProgressTaskDialogFragment.build()
ProgressTaskDialogFragment.start(activity, progressTaskDialogFragment)
}
progressTaskDialogFragment.apply {
titleId?.let {
updateTitle(it)
}
messageId?.let {
updateMessage(it)
}
warningId?.let {
updateWarning(it)
}
}
}
private fun initServiceConnection() {
if (serviceConnection == null) {
serviceConnection = object : ServiceConnection {
override fun onServiceConnected(name: ComponentName?, serviceBinder: IBinder?) {
mBinder = (serviceBinder as DatabaseTaskNotificationService.ActionTaskBinder).apply {
addActionTaskListener(actionTaskListener)
getService().checkAction()
}
}
override fun onServiceDisconnected(name: ComponentName?) {
mBinder?.removeActionTaskListener(actionTaskListener)
mBinder = null
}
}
}
}
private fun bindService() {
initServiceConnection()
serviceConnection?.let {
activity.bindService(intentDatabaseTask, it, BIND_NOT_FOREGROUND or BIND_ABOVE_CLIENT)
}
}
/**
* Unbind the service and assign null to the service connection to check if already unbind or not
*/
private fun unBindService() {
serviceConnection?.let {
activity.unbindService(it)
}
serviceConnection = null
}
fun registerProgressTask() {
ProgressTaskDialogFragment.stop(activity)
// Register a database task receiver to stop loading dialog when service finish the task
databaseTaskBroadcastReceiver = object : BroadcastReceiver() {
override fun onReceive(context: Context?, intent: Intent?) {
activity.runOnUiThread {
intentDatabaseTask.putExtra(DATABASE_TASK_TITLE_KEY, titleId)
when (intent?.action) {
DATABASE_START_TASK_ACTION -> {
// Bind to the service when is starting
bindService()
}
DATABASE_STOP_TASK_ACTION -> {
unBindService()
}
}
}
}
}
activity.registerReceiver(databaseTaskBroadcastReceiver,
IntentFilter().apply {
addAction(DATABASE_START_TASK_ACTION)
addAction(DATABASE_STOP_TASK_ACTION)
}
)
// Check if a service is currently running else do nothing
bindService()
}
fun unregisterProgressTask() {
ProgressTaskDialogFragment.stop(activity)
mBinder?.removeActionTaskListener(actionTaskListener)
mBinder = null
unBindService()
activity.unregisterReceiver(databaseTaskBroadcastReceiver)
}
private fun start(bundle: Bundle? = null, actionTask: String) {
activity.stopService(intentDatabaseTask)
if (bundle != null)
intentDatabaseTask.putExtras(bundle)
activity.runOnUiThread {
intentDatabaseTask.action = actionTask
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
activity.startForegroundService(intentDatabaseTask)
} else {
activity.startService(intentDatabaseTask)
}
TimeoutHelper.temporarilyDisableTimeout()
// Show the dialog
ProgressTaskDialogFragment.start(activity, progressTaskDialogFragment)
}
}, { result ->
activity.runOnUiThread {
actionFinishInUIThread?.onFinishRun(result)
// Remove the progress task
ProgressTaskDialogFragment.stop(activity)
TimeoutHelper.releaseTemporarilyDisableTimeoutAndLockIfTimeout(activity)
activity.stopService(intentDatabaseTask)
}
})
}
fun start() {
actionRunnableAsyncTask?.execute(actionRunnable)
/*
----
Main methods
----
*/
fun startDatabaseCreate(databaseUri: Uri,
masterPasswordChecked: Boolean,
masterPassword: String?,
keyFileChecked: Boolean,
keyFile: Uri?) {
start(Bundle().apply {
putParcelable(DatabaseTaskNotificationService.DATABASE_URI_KEY, databaseUri)
putBoolean(DatabaseTaskNotificationService.MASTER_PASSWORD_CHECKED_KEY, masterPasswordChecked)
putString(DatabaseTaskNotificationService.MASTER_PASSWORD_KEY, masterPassword)
putBoolean(DatabaseTaskNotificationService.KEY_FILE_CHECKED_KEY, keyFileChecked)
putParcelable(DatabaseTaskNotificationService.KEY_FILE_KEY, keyFile)
}
, ACTION_DATABASE_CREATE_TASK)
}
private class ActionRunnableAsyncTask(private val progressTaskUpdater: ProgressTaskUpdater,
private val onPreExecute: () -> Unit,
private val onPostExecute: (result: ActionRunnable.Result) -> Unit)
: AsyncTask<((ProgressTaskUpdater?)-> ActionRunnable), Void, ActionRunnable.Result>() {
override fun onPreExecute() {
super.onPreExecute()
onPreExecute.invoke()
fun startDatabaseLoad(databaseUri: Uri,
masterPassword: String?,
keyFile: Uri?,
readOnly: Boolean,
cipherEntity: CipherDatabaseEntity?,
fixDuplicateUuid: Boolean) {
start(Bundle().apply {
putParcelable(DatabaseTaskNotificationService.DATABASE_URI_KEY, databaseUri)
putString(DatabaseTaskNotificationService.MASTER_PASSWORD_KEY, masterPassword)
putParcelable(DatabaseTaskNotificationService.KEY_FILE_KEY, keyFile)
putBoolean(DatabaseTaskNotificationService.READ_ONLY_KEY, readOnly)
putParcelable(DatabaseTaskNotificationService.CIPHER_ENTITY_KEY, cipherEntity)
putBoolean(DatabaseTaskNotificationService.FIX_DUPLICATE_UUID_KEY, fixDuplicateUuid)
}
, ACTION_DATABASE_LOAD_TASK)
}
override fun doInBackground(vararg actionRunnables: ((ProgressTaskUpdater?)-> ActionRunnable)?): ActionRunnable.Result {
var resultTask = ActionRunnable.Result(false)
actionRunnables.forEach {
it?.invoke(progressTaskUpdater)?.apply {
run()
resultTask = result
fun startDatabaseAssignPassword(masterPasswordChecked: Boolean,
masterPassword: String?,
keyFileChecked: Boolean,
keyFile: Uri?) {
start(Bundle().apply {
putBoolean(DatabaseTaskNotificationService.MASTER_PASSWORD_CHECKED_KEY, masterPasswordChecked)
putString(DatabaseTaskNotificationService.MASTER_PASSWORD_KEY, masterPassword)
putBoolean(DatabaseTaskNotificationService.KEY_FILE_CHECKED_KEY, keyFileChecked)
putParcelable(DatabaseTaskNotificationService.KEY_FILE_KEY, keyFile)
}
}
return resultTask
, ACTION_DATABASE_ASSIGN_PASSWORD_TASK)
}
override fun onPostExecute(result: ActionRunnable.Result) {
super.onPostExecute(result)
onPostExecute.invoke(result)
/*
----
Nodes Actions
----
*/
fun startDatabaseCreateGroup(newGroup: GroupVersioned,
parent: GroupVersioned,
save: Boolean) {
start(Bundle().apply {
putParcelable(DatabaseTaskNotificationService.GROUP_KEY, newGroup)
putParcelable(DatabaseTaskNotificationService.PARENT_ID_KEY, parent.nodeId)
putBoolean(DatabaseTaskNotificationService.SAVE_DATABASE_KEY, save)
}
, ACTION_DATABASE_CREATE_GROUP_TASK)
}
fun startDatabaseUpdateGroup(oldGroup: GroupVersioned,
groupToUpdate: GroupVersioned,
save: Boolean) {
start(Bundle().apply {
putParcelable(DatabaseTaskNotificationService.GROUP_ID_KEY, oldGroup.nodeId)
putParcelable(DatabaseTaskNotificationService.GROUP_KEY, groupToUpdate)
putBoolean(DatabaseTaskNotificationService.SAVE_DATABASE_KEY, save)
}
, ACTION_DATABASE_UPDATE_GROUP_TASK)
}
fun startDatabaseCreateEntry(newEntry: EntryVersioned,
parent: GroupVersioned,
save: Boolean) {
start(Bundle().apply {
putParcelable(DatabaseTaskNotificationService.ENTRY_KEY, newEntry)
putParcelable(DatabaseTaskNotificationService.PARENT_ID_KEY, parent.nodeId)
putBoolean(DatabaseTaskNotificationService.SAVE_DATABASE_KEY, save)
}
, ACTION_DATABASE_CREATE_ENTRY_TASK)
}
fun startDatabaseUpdateEntry(oldEntry: EntryVersioned,
entryToUpdate: EntryVersioned,
save: Boolean) {
start(Bundle().apply {
putParcelable(DatabaseTaskNotificationService.ENTRY_ID_KEY, oldEntry.nodeId)
putParcelable(DatabaseTaskNotificationService.ENTRY_KEY, entryToUpdate)
putBoolean(DatabaseTaskNotificationService.SAVE_DATABASE_KEY, save)
}
, ACTION_DATABASE_UPDATE_ENTRY_TASK)
}
private fun startDatabaseActionListNodes(actionTask: String,
nodesPaste: List<NodeVersioned>,
newParent: GroupVersioned?,
save: Boolean) {
val groupsIdToCopy = ArrayList<PwNodeId<*>>()
val entriesIdToCopy = ArrayList<PwNodeId<UUID>>()
nodesPaste.forEach { nodeVersioned ->
when (nodeVersioned.type) {
Type.GROUP -> {
(nodeVersioned as GroupVersioned).nodeId?.let { groupId ->
groupsIdToCopy.add(groupId)
}
}
Type.ENTRY -> {
entriesIdToCopy.add((nodeVersioned as EntryVersioned).nodeId)
}
}
}
val newParentId = newParent?.nodeId
start(Bundle().apply {
putAll(getBundleFromListNodes(nodesPaste))
putParcelableArrayList(DatabaseTaskNotificationService.GROUPS_ID_KEY, groupsIdToCopy)
putParcelableArrayList(DatabaseTaskNotificationService.ENTRIES_ID_KEY, entriesIdToCopy)
if (newParentId != null)
putParcelable(DatabaseTaskNotificationService.PARENT_ID_KEY, newParentId)
putBoolean(DatabaseTaskNotificationService.SAVE_DATABASE_KEY, save)
}
, actionTask)
}
fun startDatabaseCopyNodes(nodesToCopy: List<NodeVersioned>,
newParent: GroupVersioned,
save: Boolean) {
startDatabaseActionListNodes(ACTION_DATABASE_COPY_NODES_TASK, nodesToCopy, newParent, save)
}
fun startDatabaseMoveNodes(nodesToMove: List<NodeVersioned>,
newParent: GroupVersioned,
save: Boolean) {
startDatabaseActionListNodes(ACTION_DATABASE_MOVE_NODES_TASK, nodesToMove, newParent, save)
}
fun startDatabaseDeleteNodes(nodesToDelete: List<NodeVersioned>,
save: Boolean) {
startDatabaseActionListNodes(ACTION_DATABASE_DELETE_NODES_TASK, nodesToDelete, null, save)
}
/*
-----------------
Main Settings
-----------------
*/
fun startDatabaseSaveName(oldName: String,
newName: String) {
start(Bundle().apply {
putString(DatabaseTaskNotificationService.OLD_ELEMENT_KEY, oldName)
putString(DatabaseTaskNotificationService.NEW_ELEMENT_KEY, newName)
}
, ACTION_DATABASE_SAVE_NAME_TASK)
}
fun startDatabaseSaveDescription(oldDescription: String,
newDescription: String) {
start(Bundle().apply {
putString(DatabaseTaskNotificationService.OLD_ELEMENT_KEY, oldDescription)
putString(DatabaseTaskNotificationService.NEW_ELEMENT_KEY, newDescription)
}
, ACTION_DATABASE_SAVE_DESCRIPTION_TASK)
}
fun startDatabaseSaveDefaultUsername(oldDefaultUsername: String,
newDefaultUsername: String) {
start(Bundle().apply {
putString(DatabaseTaskNotificationService.OLD_ELEMENT_KEY, oldDefaultUsername)
putString(DatabaseTaskNotificationService.NEW_ELEMENT_KEY, newDefaultUsername)
}
, ACTION_DATABASE_SAVE_DEFAULT_USERNAME_TASK)
}
fun startDatabaseSaveColor(oldColor: String,
newColor: String) {
start(Bundle().apply {
putString(DatabaseTaskNotificationService.OLD_ELEMENT_KEY, oldColor)
putString(DatabaseTaskNotificationService.NEW_ELEMENT_KEY, newColor)
}
, ACTION_DATABASE_SAVE_COLOR_TASK)
}
fun startDatabaseSaveCompression(oldCompression: PwCompressionAlgorithm,
newCompression: PwCompressionAlgorithm) {
start(Bundle().apply {
putSerializable(DatabaseTaskNotificationService.OLD_ELEMENT_KEY, oldCompression)
putSerializable(DatabaseTaskNotificationService.NEW_ELEMENT_KEY, newCompression)
}
, ACTION_DATABASE_SAVE_COMPRESSION_TASK)
}
fun startDatabaseSaveMaxHistoryItems(oldMaxHistoryItems: Int,
newMaxHistoryItems: Int) {
start(Bundle().apply {
putInt(DatabaseTaskNotificationService.OLD_ELEMENT_KEY, oldMaxHistoryItems)
putInt(DatabaseTaskNotificationService.NEW_ELEMENT_KEY, newMaxHistoryItems)
}
, ACTION_DATABASE_SAVE_MAX_HISTORY_ITEMS_TASK)
}
fun startDatabaseSaveMaxHistorySize(oldMaxHistorySize: Long,
newMaxHistorySize: Long) {
start(Bundle().apply {
putLong(DatabaseTaskNotificationService.OLD_ELEMENT_KEY, oldMaxHistorySize)
putLong(DatabaseTaskNotificationService.NEW_ELEMENT_KEY, newMaxHistorySize)
}
, ACTION_DATABASE_SAVE_MAX_HISTORY_SIZE_TASK)
}
/*
-------------------
Security Settings
-------------------
*/
fun startDatabaseSaveEncryption(oldEncryption: PwEncryptionAlgorithm,
newEncryption: PwEncryptionAlgorithm) {
start(Bundle().apply {
putSerializable(DatabaseTaskNotificationService.OLD_ELEMENT_KEY, oldEncryption)
putSerializable(DatabaseTaskNotificationService.NEW_ELEMENT_KEY, newEncryption)
}
, ACTION_DATABASE_SAVE_ENCRYPTION_TASK)
}
fun startDatabaseSaveKeyDerivation(oldKeyDerivation: KdfEngine,
newKeyDerivation: KdfEngine) {
start(Bundle().apply {
putSerializable(DatabaseTaskNotificationService.OLD_ELEMENT_KEY, oldKeyDerivation)
putSerializable(DatabaseTaskNotificationService.NEW_ELEMENT_KEY, newKeyDerivation)
}
, ACTION_DATABASE_SAVE_KEY_DERIVATION_TASK)
}
fun startDatabaseSaveIterations(oldIterations: Long,
newIterations: Long) {
start(Bundle().apply {
putLong(DatabaseTaskNotificationService.OLD_ELEMENT_KEY, oldIterations)
putLong(DatabaseTaskNotificationService.NEW_ELEMENT_KEY, newIterations)
}
, ACTION_DATABASE_SAVE_ITERATIONS_TASK)
}
fun startDatabaseSaveMemoryUsage(oldMemoryUsage: Long,
newMemoryUsage: Long) {
start(Bundle().apply {
putLong(DatabaseTaskNotificationService.OLD_ELEMENT_KEY, oldMemoryUsage)
putLong(DatabaseTaskNotificationService.NEW_ELEMENT_KEY, newMemoryUsage)
}
, ACTION_DATABASE_SAVE_MEMORY_USAGE_TASK)
}
fun startDatabaseSaveParallelism(oldParallelism: Int,
newParallelism: Int) {
start(Bundle().apply {
putInt(DatabaseTaskNotificationService.OLD_ELEMENT_KEY, oldParallelism)
putInt(DatabaseTaskNotificationService.NEW_ELEMENT_KEY, newParallelism)
}
, ACTION_DATABASE_SAVE_PARALLELISM_TASK)
}
}

View File

@@ -30,7 +30,6 @@ abstract class SaveDatabaseRunnable(protected var context: Context,
private val save: Boolean,
nestedAction: ActionRunnable? = null) : ActionRunnable(nestedAction) {
// TODO Service to prevent background thread kill
override fun run() {
if (save) {
try {

View File

@@ -1,12 +1,12 @@
package com.kunzisoft.keepass.database.action.node
import androidx.fragment.app.FragmentActivity
import android.content.Context
import android.util.Log
import com.kunzisoft.keepass.database.action.SaveDatabaseRunnable
import com.kunzisoft.keepass.database.element.Database
abstract class ActionNodeDatabaseRunnable(
context: FragmentActivity,
context: Context,
database: Database,
private val callbackRunnable: AfterActionNodeFinishRunnable?,
save: Boolean)

View File

@@ -19,19 +19,19 @@
*/
package com.kunzisoft.keepass.database.action.node
import androidx.fragment.app.FragmentActivity
import android.content.Context
import com.kunzisoft.keepass.database.element.Database
import com.kunzisoft.keepass.database.element.EntryVersioned
import com.kunzisoft.keepass.database.element.GroupVersioned
import com.kunzisoft.keepass.database.element.NodeVersioned
class AddEntryRunnable constructor(
context: FragmentActivity,
context: Context,
database: Database,
private val mNewEntry: EntryVersioned,
private val mParent: GroupVersioned,
finishRunnable: AfterActionNodeFinishRunnable?,
save: Boolean)
save: Boolean,
finishRunnable: AfterActionNodeFinishRunnable?)
: ActionNodeDatabaseRunnable(context, database, finishRunnable, save) {
override fun nodeAction() {

View File

@@ -19,18 +19,18 @@
*/
package com.kunzisoft.keepass.database.action.node
import androidx.fragment.app.FragmentActivity
import android.content.Context
import com.kunzisoft.keepass.database.element.Database
import com.kunzisoft.keepass.database.element.GroupVersioned
import com.kunzisoft.keepass.database.element.NodeVersioned
class AddGroupRunnable constructor(
context: FragmentActivity,
context: Context,
database: Database,
private val mNewGroup: GroupVersioned,
private val mParent: GroupVersioned,
afterAddNodeRunnable: AfterActionNodeFinishRunnable?,
save: Boolean)
save: Boolean,
afterAddNodeRunnable: AfterActionNodeFinishRunnable?)
: ActionNodeDatabaseRunnable(context, database, afterAddNodeRunnable, save) {
override fun nodeAction() {

View File

@@ -19,18 +19,18 @@
*/
package com.kunzisoft.keepass.database.action.node
import android.content.Context
import android.util.Log
import androidx.fragment.app.FragmentActivity
import com.kunzisoft.keepass.R
import com.kunzisoft.keepass.database.element.*
class CopyNodesRunnable constructor(
context: FragmentActivity,
context: Context,
database: Database,
private val mNodesToCopy: List<NodeVersioned>,
private val mNewParent: GroupVersioned,
afterAddNodeRunnable: AfterActionNodeFinishRunnable?,
save: Boolean)
save: Boolean,
afterAddNodeRunnable: AfterActionNodeFinishRunnable?)
: ActionNodeDatabaseRunnable(context, database, afterAddNodeRunnable, save) {
private var mEntriesCopied = ArrayList<EntryVersioned>()

View File

@@ -19,14 +19,16 @@
*/
package com.kunzisoft.keepass.database.action.node
import androidx.fragment.app.FragmentActivity
import android.content.Context
import com.kunzisoft.keepass.database.element.*
class DeleteNodesRunnable(context: FragmentActivity,
class DeleteNodesRunnable(context: Context,
database: Database,
private val mNodesToDelete: List<NodeVersioned>,
finish: AfterActionNodeFinishRunnable,
save: Boolean) : ActionNodeDatabaseRunnable(context, database, finish, save) {
save: Boolean,
finish: AfterActionNodeFinishRunnable)
: ActionNodeDatabaseRunnable(context, database, finish, save) {
private var mParent: GroupVersioned? = null
private var mCanRecycle: Boolean = false

View File

@@ -19,18 +19,18 @@
*/
package com.kunzisoft.keepass.database.action.node
import androidx.fragment.app.FragmentActivity
import android.content.Context
import android.util.Log
import com.kunzisoft.keepass.R
import com.kunzisoft.keepass.database.element.*
class MoveNodesRunnable constructor(
context: FragmentActivity,
context: Context,
database: Database,
private val mNodesToMove: List<NodeVersioned>,
private val mNewParent: GroupVersioned,
afterAddNodeRunnable: AfterActionNodeFinishRunnable?,
save: Boolean)
save: Boolean,
afterAddNodeRunnable: AfterActionNodeFinishRunnable?)
: ActionNodeDatabaseRunnable(context, database, afterAddNodeRunnable, save) {
private var mOldParent: GroupVersioned? = null

View File

@@ -19,48 +19,50 @@
*/
package com.kunzisoft.keepass.database.action.node
import androidx.fragment.app.FragmentActivity
import android.content.Context
import com.kunzisoft.keepass.database.element.Database
import com.kunzisoft.keepass.database.element.EntryVersioned
import com.kunzisoft.keepass.database.element.NodeVersioned
class UpdateEntryRunnable constructor(
context: FragmentActivity,
context: Context,
database: Database,
private val mOldEntry: EntryVersioned,
private val mNewEntry: EntryVersioned,
finishRunnable: AfterActionNodeFinishRunnable?,
save: Boolean)
save: Boolean,
finishRunnable: AfterActionNodeFinishRunnable?)
: ActionNodeDatabaseRunnable(context, database, finishRunnable, save) {
// Keep backup of original values in case save fails
private var mBackupEntryHistory: EntryVersioned? = null
private var mBackupEntryHistory: EntryVersioned = EntryVersioned(mOldEntry)
override fun nodeAction() {
mNewEntry.touch(modified = true, touchParents = true)
// WARNING : Re attribute parent removed in entry edit activity to save memory
mNewEntry.addParentFrom(mOldEntry)
mBackupEntryHistory = EntryVersioned(mOldEntry)
// Create an entry history (an entry history don't have history)
mNewEntry.addEntryToHistory(EntryVersioned(mOldEntry, copyHistory = false))
database.removeOldestHistory(mNewEntry)
// Update entry with new values
mOldEntry.updateWith(mNewEntry)
mNewEntry.touch(modified = true, touchParents = true)
// Create an entry history (an entry history don't have history)
mOldEntry.addEntryToHistory(EntryVersioned(mBackupEntryHistory, copyHistory = false))
database.removeOldestHistory(mOldEntry)
// Only change data in index
database.updateEntry(mOldEntry)
}
override fun nodeFinish(result: Result): ActionNodeValues {
if (!result.isSuccess) {
mOldEntry.updateWith(mBackupEntryHistory)
// If we fail to save, back out changes to global structure
mBackupEntryHistory?.let {
mOldEntry.updateWith(it)
}
database.updateEntry(mOldEntry)
}
val oldNodesReturn = ArrayList<NodeVersioned>()
oldNodesReturn.add(mOldEntry)
oldNodesReturn.add(mBackupEntryHistory)
val newNodesReturn = ArrayList<NodeVersioned>()
newNodesReturn.add(mNewEntry)
newNodesReturn.add(mOldEntry)
return ActionNodeValues(result, oldNodesReturn, newNodesReturn)
}
}

View File

@@ -19,39 +19,47 @@
*/
package com.kunzisoft.keepass.database.action.node
import androidx.fragment.app.FragmentActivity
import android.content.Context
import com.kunzisoft.keepass.database.element.Database
import com.kunzisoft.keepass.database.element.GroupVersioned
import com.kunzisoft.keepass.database.element.NodeVersioned
class UpdateGroupRunnable constructor(
context: FragmentActivity,
context: Context,
database: Database,
private val mOldGroup: GroupVersioned,
private val mNewGroup: GroupVersioned,
finishRunnable: AfterActionNodeFinishRunnable?,
save: Boolean)
save: Boolean,
finishRunnable: AfterActionNodeFinishRunnable?)
: ActionNodeDatabaseRunnable(context, database, finishRunnable, save) {
// Keep backup of original values in case save fails
private val mBackupGroup: GroupVersioned = GroupVersioned(mOldGroup)
override fun nodeAction() {
// WARNING : Re attribute parent and children removed in group activity to save memory
mNewGroup.addParentFrom(mOldGroup)
mNewGroup.addChildrenFrom(mOldGroup)
// Update group with new values
mOldGroup.touch(modified = true, touchParents = true)
mOldGroup.updateWith(mNewGroup)
mOldGroup.touch(modified = true, touchParents = true)
// Only change data in index
database.updateGroup(mOldGroup)
}
override fun nodeFinish(result: Result): ActionNodeValues {
if (!result.isSuccess) {
// If we fail to save, back out changes to global structure
mOldGroup.updateWith(mBackupGroup)
database.updateGroup(mOldGroup)
}
val oldNodesReturn = ArrayList<NodeVersioned>()
oldNodesReturn.add(mOldGroup)
oldNodesReturn.add(mBackupGroup)
val newNodesReturn = ArrayList<NodeVersioned>()
newNodesReturn.add(mNewGroup)
newNodesReturn.add(mOldGroup)
return ActionNodeValues(result, oldNodesReturn, newNodesReturn)
}
}

View File

@@ -2,12 +2,11 @@ package com.kunzisoft.keepass.database.cursor
import android.database.MatrixCursor
import android.provider.BaseColumns
import com.kunzisoft.keepass.database.element.PwEntry
import com.kunzisoft.keepass.database.element.PwIconFactory
import com.kunzisoft.keepass.database.element.PwNodeId
import com.kunzisoft.keepass.database.element.*
import java.util.UUID
abstract class EntryCursor<PwEntryV : PwEntry<*, *>> : MatrixCursor(arrayOf(
abstract class EntryCursor<EntryId, PwEntryV : PwEntry<*, EntryId, *, *>> : MatrixCursor(arrayOf(
_ID,
COLUMN_INDEX_UUID_MOST_SIGNIFICANT_BITS,
COLUMN_INDEX_UUID_LEAST_SIGNIFICANT_BITS,
@@ -25,10 +24,10 @@ abstract class EntryCursor<PwEntryV : PwEntry<*, *>> : MatrixCursor(arrayOf(
abstract fun addEntry(entry: PwEntryV)
abstract fun getPwNodeId(): PwNodeId<EntryId>
open fun populateEntry(pwEntry: PwEntryV, iconFactory: PwIconFactory) {
pwEntry.nodeId = PwNodeIdUUID(
UUID(getLong(getColumnIndex(COLUMN_INDEX_UUID_MOST_SIGNIFICANT_BITS)),
getLong(getColumnIndex(COLUMN_INDEX_UUID_LEAST_SIGNIFICANT_BITS))))
pwEntry.nodeId = getPwNodeId()
pwEntry.title = getString(getColumnIndex(COLUMN_INDEX_TITLE))
val iconStandard = iconFactory.getIcon(getInt(getColumnIndex(COLUMN_INDEX_ICON_STANDARD)))
@@ -53,5 +52,4 @@ abstract class EntryCursor<PwEntryV : PwEntry<*, *>> : MatrixCursor(arrayOf(
const val COLUMN_INDEX_URL = "URL"
const val COLUMN_INDEX_NOTES = "notes"
}
}

View File

@@ -0,0 +1,15 @@
package com.kunzisoft.keepass.database.cursor
import com.kunzisoft.keepass.database.element.PwEntry
import com.kunzisoft.keepass.database.element.PwNodeId
import com.kunzisoft.keepass.database.element.PwNodeIdUUID
import java.util.*
abstract class EntryCursorUUID<EntryV: PwEntry<*, UUID, *, *>>: EntryCursor<UUID, EntryV>() {
override fun getPwNodeId(): PwNodeId<UUID> {
return PwNodeIdUUID(
UUID(getLong(getColumnIndex(COLUMN_INDEX_UUID_MOST_SIGNIFICANT_BITS)),
getLong(getColumnIndex(COLUMN_INDEX_UUID_LEAST_SIGNIFICANT_BITS))))
}
}

View File

@@ -3,7 +3,7 @@ package com.kunzisoft.keepass.database.cursor
import com.kunzisoft.keepass.database.element.PwDatabase
import com.kunzisoft.keepass.database.element.PwEntryV3
class EntryCursorV3 : EntryCursor<PwEntryV3>() {
class EntryCursorV3 : EntryCursorUUID<PwEntryV3>() {
override fun addEntry(entry: PwEntryV3) {
addRow(arrayOf(

View File

@@ -5,7 +5,7 @@ import com.kunzisoft.keepass.database.element.PwIconFactory
import java.util.UUID
class EntryCursorV4 : EntryCursor<PwEntryV4>() {
class EntryCursorV4 : EntryCursorUUID<PwEntryV4>() {
private val extraFieldCursor: ExtraFieldCursor = ExtraFieldCursor()

View File

@@ -288,14 +288,15 @@ class Database {
@Throws(LoadDatabaseException::class)
fun loadData(uri: Uri, password: String?, keyfile: Uri?,
readOnly: Boolean,
contentResolver: ContentResolver,
cacheDirectory: File,
searchHelper: SearchDbHelper,
omitBackup: Boolean,
fixDuplicateUUID: Boolean,
progressTaskUpdater: ProgressTaskUpdater?) {
mUri = uri
isReadOnly = false
isReadOnly = readOnly
if (uri.scheme == "file") {
val file = File(uri.path!!)
isReadOnly = !file.canWrite()
@@ -359,7 +360,7 @@ class Database {
else -> throw LoadDatabaseSignatureException()
}
this.mSearchHelper = searchHelper
this.mSearchHelper = SearchDbHelper(omitBackup)
loaded = true
}
@@ -488,7 +489,7 @@ class Database {
// In all cases, delete all the files in the temp dir
try {
FileUtils.cleanDirectory(filesDirectory)
} catch (e: IOException) {
} catch (e: Exception) {
Log.e(TAG, "Unable to clear the directory cache.", e)
}
@@ -576,6 +577,15 @@ class Database {
entry.afterAssignNewParent()
}
fun updateEntry(entry: EntryVersioned) {
entry.pwEntryV3?.let { entryV3 ->
pwDatabaseV3?.updateEntry(entryV3)
}
entry.pwEntryV4?.let { entryV4 ->
pwDatabaseV4?.updateEntry(entryV4)
}
}
fun removeEntryFrom(entry: EntryVersioned, parent: GroupVersioned) {
entry.pwEntryV3?.let { entryV3 ->
pwDatabaseV3?.removeEntryFrom(entryV3, parent.pwGroupV3)
@@ -596,6 +606,15 @@ class Database {
group.afterAssignNewParent()
}
fun updateGroup(group: GroupVersioned) {
group.pwGroupV3?.let { groupV3 ->
pwDatabaseV3?.updateGroup(groupV3)
}
group.pwGroupV4?.let { groupV4 ->
pwDatabaseV4?.updateGroup(groupV4)
}
}
fun removeGroupFrom(group: GroupVersioned, parent: GroupVersioned) {
group.pwGroupV3?.let { groupV3 ->
pwDatabaseV3?.removeGroupFrom(groupV3, parent.pwGroupV3)

View File

@@ -61,7 +61,7 @@ class EntryVersioned : NodeVersioned, PwEntryInterface<GroupVersioned> {
dest.writeParcelable(pwEntryV4, flags)
}
var nodeId: PwNodeId<UUID>
override var nodeId: PwNodeId<UUID>
get() = pwEntryV4?.nodeId ?: pwEntryV3?.nodeId ?: PwNodeIdUUID()
set(value) {
pwEntryV3?.nodeId = value

View File

@@ -70,7 +70,7 @@ class GroupVersioned : NodeVersioned, PwGroupInterface<GroupVersioned, EntryVers
dest.writeParcelable(pwGroupV4, flags)
}
val nodeId: PwNodeId<*>?
override val nodeId: PwNodeId<*>?
get() = pwGroupV4?.nodeId ?: pwGroupV3?.nodeId
override var title: String
@@ -114,6 +114,38 @@ class GroupVersioned : NodeVersioned, PwGroupInterface<GroupVersioned, EntryVers
pwGroupV4?.afterAssignNewParent()
}
fun addChildrenFrom(group: GroupVersioned) {
group.pwGroupV3?.getChildEntries()?.forEach { entryToAdd ->
pwGroupV3?.addChildEntry(entryToAdd)
}
group.pwGroupV3?.getChildGroups()?.forEach { groupToAdd ->
pwGroupV3?.addChildGroup(groupToAdd)
}
group.pwGroupV4?.getChildEntries()?.forEach { entryToAdd ->
pwGroupV4?.addChildEntry(entryToAdd)
}
group.pwGroupV4?.getChildGroups()?.forEach { groupToAdd ->
pwGroupV4?.addChildGroup(groupToAdd)
}
}
fun removeChildren() {
pwGroupV3?.getChildEntries()?.forEach { entryToRemove ->
pwGroupV3?.removeChildEntry(entryToRemove)
}
pwGroupV3?.getChildGroups()?.forEach { groupToRemove ->
pwGroupV3?.removeChildGroup(groupToRemove)
}
pwGroupV4?.getChildEntries()?.forEach { entryToRemove ->
pwGroupV4?.removeChildEntry(entryToRemove)
}
pwGroupV4?.getChildGroups()?.forEach { groupToRemove ->
pwGroupV4?.removeChildGroup(groupToRemove)
}
}
override fun touch(modified: Boolean, touchParents: Boolean) {
pwGroupV3?.touch(modified, touchParents)
pwGroupV4?.touch(modified, touchParents)

View File

@@ -2,16 +2,26 @@ package com.kunzisoft.keepass.database.element
interface NodeVersioned: PwNodeInterface<GroupVersioned> {
val nodeId: PwNodeId<*>?
val nodePositionInParent: Int
get() {
parent?.getChildren(true)?.let { children ->
children.forEachIndexed { index, nodeVersioned ->
if (nodeVersioned == this)
if (nodeVersioned.nodeId == this.nodeId)
return index
}
}
return -1
}
fun addParentFrom(node: NodeVersioned) {
parent = node.parent
}
fun removeParent() {
parent = null
}
}
/**

View File

@@ -30,8 +30,9 @@ import java.util.*
abstract class PwDatabase<
GroupId,
Group : PwGroup<GroupId, Group, Entry>,
Entry : PwEntry<Group, Entry>
EntryId,
Group : PwGroup<GroupId, EntryId, Group, Entry>,
Entry : PwEntry<GroupId, EntryId, Group, Entry>
> {
// Algorithm used to encrypt the database
@@ -51,7 +52,7 @@ abstract class PwDatabase<
var changeDuplicateId = false
private var groupIndexes = LinkedHashMap<PwNodeId<GroupId>, Group>()
private var entryIndexes = LinkedHashMap<PwNodeId<UUID>, Entry>()
private var entryIndexes = LinkedHashMap<PwNodeId<EntryId>, Entry>()
abstract val version: String
@@ -193,7 +194,7 @@ abstract class PwDatabase<
abstract fun newGroupId(): PwNodeId<GroupId>
abstract fun newEntryId(): PwNodeId<UUID>
abstract fun newEntryId(): PwNodeId<EntryId>
abstract fun createGroup(): Group
@@ -253,6 +254,13 @@ abstract class PwDatabase<
}
}
fun updateGroupIndex(group: Group) {
val groupId = group.nodeId
if (groupIndexes.containsKey(groupId)) {
groupIndexes[groupId] = group
}
}
fun removeGroupIndex(group: Group) {
this.groupIndexes.remove(group.nodeId)
}
@@ -267,7 +275,7 @@ abstract class PwDatabase<
}
}
fun isEntryIdUsed(id: PwNodeId<UUID>): Boolean {
fun isEntryIdUsed(id: PwNodeId<EntryId>): Boolean {
return entryIndexes.containsKey(id)
}
@@ -275,7 +283,7 @@ abstract class PwDatabase<
return entryIndexes.values
}
fun getEntryById(id: PwNodeId<UUID>): Entry? {
fun getEntryById(id: PwNodeId<EntryId>): Entry? {
return this.entryIndexes[id]
}
@@ -295,6 +303,13 @@ abstract class PwDatabase<
}
}
fun updateEntryIndex(entry: Entry) {
val entryId = entry.nodeId
if (entryIndexes.containsKey(entryId)) {
entryIndexes[entryId] = entry
}
}
fun removeEntryIndex(entry: Entry) {
this.entryIndexes.remove(entry.nodeId)
}
@@ -325,6 +340,10 @@ abstract class PwDatabase<
addGroupIndex(newGroup)
}
fun updateGroup(group: Group) {
updateGroupIndex(group)
}
fun removeGroupFrom(groupToRemove: Group, parent: Group?) {
// Remove tree from parent tree
parent?.removeChildGroup(groupToRemove)
@@ -338,6 +357,10 @@ abstract class PwDatabase<
addEntryIndex(newEntry)
}
fun updateEntry(entry: Entry) {
updateEntryIndex(entry)
}
open fun removeEntryFrom(entryToRemove: Entry, parent: Group?) {
// Remove entry from parent
parent?.removeChildEntry(entryToRemove)

View File

@@ -28,8 +28,10 @@ import java.io.InputStream
import java.security.DigestOutputStream
import java.security.MessageDigest
import java.security.NoSuchAlgorithmException
import java.util.*
import kotlin.collections.ArrayList
class PwDatabaseV3 : PwDatabase<Int, PwGroupV3, PwEntryV3>() {
class PwDatabaseV3 : PwDatabase<Int, UUID, PwGroupV3, PwEntryV3>() {
private var numKeyEncRounds: Int = 0

View File

@@ -41,7 +41,7 @@ import javax.xml.parsers.DocumentBuilderFactory
import javax.xml.parsers.ParserConfigurationException
class PwDatabaseV4 : PwDatabase<UUID, PwGroupV4, PwEntryV4> {
class PwDatabaseV4 : PwDatabase<UUID, UUID, PwGroupV4, PwEntryV4> {
var hmacKey: ByteArray? = null
private set

View File

@@ -5,10 +5,12 @@ import java.util.*
abstract class PwEntry
<
ParentGroup: PwGroupInterface<ParentGroup, Entry>,
Entry: PwEntryInterface<ParentGroup>
GroupId,
EntryId,
ParentGroup: PwGroup<GroupId, EntryId, ParentGroup, Entry>,
Entry: PwEntry<GroupId, EntryId, ParentGroup, Entry>
>
: PwNode<UUID, ParentGroup, Entry>, PwEntryInterface<ParentGroup> {
: PwNode<EntryId, ParentGroup, Entry>, PwEntryInterface<ParentGroup> {
constructor() : super()

View File

@@ -48,7 +48,7 @@ import java.util.UUID
* @author Dominik Reichl <dominik.reichl></dominik.reichl>@t-online.de>
* @author Jeremy Jamet <jeremy.jamet></jeremy.jamet>@kunzisoft.com>
*/
class PwEntryV3 : PwEntry<PwGroupV3, PwEntryV3>, PwNodeV3Interface {
class PwEntryV3 : PwEntry<Int, UUID, PwGroupV3, PwEntryV3>, PwNodeV3Interface {
/** A string describing what is in pBinaryData */
var binaryDesc = ""

View File

@@ -26,7 +26,7 @@ import com.kunzisoft.keepass.database.element.security.ProtectedString
import com.kunzisoft.keepass.utils.MemoryUtil
import java.util.*
class PwEntryV4 : PwEntry<PwGroupV4, PwEntryV4>, PwNodeV4Interface {
class PwEntryV4 : PwEntry<UUID, UUID, PwGroupV4, PwEntryV4>, PwNodeV4Interface {
// To decode each field not parcelable
@Transient

View File

@@ -4,11 +4,12 @@ import android.os.Parcel
abstract class PwGroup
<
Id,
Group: PwGroupInterface<Group, Entry>,
Entry: PwEntryInterface<Group>
GroupId,
EntryId,
Group: PwGroup<GroupId, EntryId, Group, Entry>,
Entry: PwEntry<GroupId, EntryId, Group, Entry>
>
: PwNode<Id, Group, Entry>, PwGroupInterface<Group, Entry> {
: PwNode<GroupId, Group, Entry>, PwGroupInterface<Group, Entry> {
private var titleGroup = ""
@Transient
@@ -27,7 +28,7 @@ abstract class PwGroup
dest.writeString(titleGroup)
}
protected fun updateWith(source: PwGroup<Id, Group, Entry>) {
protected fun updateWith(source: PwGroup<GroupId, EntryId, Group, Entry>) {
super.updateWith(source)
titleGroup = source.titleGroup
childGroups.clear()

View File

@@ -22,8 +22,9 @@ package com.kunzisoft.keepass.database.element
import android.os.Parcel
import android.os.Parcelable
import java.util.*
class PwGroupV3 : PwGroup<Int, PwGroupV3, PwEntryV3>, PwNodeV3Interface {
class PwGroupV3 : PwGroup<Int, UUID, PwGroupV3, PwEntryV3>, PwNodeV3Interface {
var level = 0 // short
/** Used by KeePass internally, don't use */

View File

@@ -25,7 +25,7 @@ import android.os.Parcelable
import java.util.HashMap
import java.util.UUID
class PwGroupV4 : PwGroup<UUID, PwGroupV4, PwEntryV4>, PwNodeV4Interface {
class PwGroupV4 : PwGroup<UUID, UUID, PwGroupV4, PwEntryV4>, PwNodeV4Interface {
// TODO Encapsulate
override var icon: PwIcon

View File

@@ -31,4 +31,17 @@ abstract class PwNodeId<Id> : Parcelable {
override fun describeContents(): Int {
return 0
}
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (other !is PwNodeId<*>) return false
if (id != other.id) return false
return true
}
override fun hashCode(): Int {
return id?.hashCode() ?: 0
}
}

View File

@@ -24,7 +24,7 @@ import com.kunzisoft.keepass.database.exception.LoadDatabaseException
import com.kunzisoft.keepass.tasks.ProgressTaskUpdater
import java.io.InputStream
abstract class Importer<PwDb : PwDatabase<*, *, *>> {
abstract class Importer<PwDb : PwDatabase<*, *, *, *>> {
/**
* Load a versioned database file, return contents in a new PwDatabase.

View File

@@ -12,7 +12,7 @@ class KeyboardLauncherActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
if (Database.getInstance().loaded && TimeoutHelper.checkTime(this))
GroupActivity.launchForKeyboarSelection(this, PreferencesUtil.enableReadOnlyDatabase(this))
GroupActivity.launchForKeyboardSelection(this, PreferencesUtil.enableReadOnlyDatabase(this))
else {
// Pass extra to get entry
FileDatabaseSelectActivity.launchForKeyboardSelection(this)

View File

@@ -1,20 +1,154 @@
package com.kunzisoft.keepass.notifications
import android.content.Intent
import android.net.Uri
import android.os.AsyncTask
import android.os.Binder
import android.os.Bundle
import android.os.IBinder
import android.util.Log
import com.kunzisoft.keepass.R
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.CreateDatabaseRunnable
import com.kunzisoft.keepass.database.action.LoadDatabaseRunnable
import com.kunzisoft.keepass.database.action.SaveDatabaseActionRunnable
import com.kunzisoft.keepass.database.action.node.*
import com.kunzisoft.keepass.database.element.*
import com.kunzisoft.keepass.settings.PreferencesUtil
import com.kunzisoft.keepass.tasks.ActionRunnable
import com.kunzisoft.keepass.tasks.ProgressTaskUpdater
import com.kunzisoft.keepass.utils.DATABASE_START_TASK_ACTION
import com.kunzisoft.keepass.utils.DATABASE_STOP_TASK_ACTION
import java.util.*
import kotlin.collections.ArrayList
class DatabaseTaskNotificationService : NotificationService() {
class DatabaseTaskNotificationService : NotificationService(), ProgressTaskUpdater {
override var notificationId = 532
override var notificationId: Int = 575
private var actionRunnableAsyncTask: ActionRunnableAsyncTask? = null
private var mActionTaskBinder = ActionTaskBinder()
private var mActionTaskListeners = LinkedList<ActionTaskListener>()
private var mTitleId: Int? = null
private var mMessageId: Int? = null
private var mWarningId: Int? = null
inner class ActionTaskBinder: Binder() {
fun getService(): DatabaseTaskNotificationService = this@DatabaseTaskNotificationService
fun addActionTaskListener(actionTaskListener: ActionTaskListener) {
mActionTaskListeners.add(actionTaskListener)
}
fun removeActionTaskListener(actionTaskListener: ActionTaskListener) {
mActionTaskListeners.remove(actionTaskListener)
}
}
interface ActionTaskListener {
fun onStartAction(titleId: Int?, messageId: Int?, warningId: Int?)
fun onUpdateAction(titleId: Int?, messageId: Int?, warningId: Int?)
fun onStopAction(actionTask: String, result: ActionRunnable.Result)
}
fun checkAction() {
mActionTaskListeners.forEach { actionTaskListener ->
actionTaskListener.onUpdateAction(mTitleId, mMessageId, mWarningId)
}
}
override fun onBind(intent: Intent): IBinder? {
return mActionTaskBinder
}
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
if (intent == null) {
Log.w(TAG, "null intent")
} else {
newNotification(intent.getIntExtra(DATABASE_TASK_TITLE_KEY, R.string.saving_database))
if (intent == null) return START_REDELIVER_INTENT
val intentAction = intent.action
val titleId: Int = when (intentAction) {
ACTION_DATABASE_CREATE_TASK -> R.string.creating_database
ACTION_DATABASE_LOAD_TASK -> R.string.loading_database
else -> R.string.saving_database
}
return START_NOT_STICKY
val messageId: Int? = when (intentAction) {
ACTION_DATABASE_LOAD_TASK -> null
else -> null
}
val warningId: Int? =
if (intentAction == ACTION_DATABASE_LOAD_TASK)
null
else
R.string.do_not_kill_app
val actionRunnable: ActionRunnable? = when (intentAction) {
ACTION_DATABASE_CREATE_TASK -> buildDatabaseCreateActionTask(intent)
ACTION_DATABASE_LOAD_TASK -> buildDatabaseLoadActionTask(intent)
ACTION_DATABASE_ASSIGN_PASSWORD_TASK -> buildDatabaseAssignPasswordActionTask(intent)
ACTION_DATABASE_CREATE_GROUP_TASK -> buildDatabaseCreateGroupActionTask(intent)
ACTION_DATABASE_UPDATE_GROUP_TASK -> buildDatabaseUpdateGroupActionTask(intent)
ACTION_DATABASE_CREATE_ENTRY_TASK -> buildDatabaseCreateEntryActionTask(intent)
ACTION_DATABASE_UPDATE_ENTRY_TASK -> buildDatabaseUpdateEntryActionTask(intent)
ACTION_DATABASE_COPY_NODES_TASK -> buildDatabaseCopyNodesActionTask(intent)
ACTION_DATABASE_MOVE_NODES_TASK -> buildDatabaseMoveNodesActionTask(intent)
ACTION_DATABASE_DELETE_NODES_TASK -> buildDatabaseDeleteNodesActionTask(intent)
ACTION_DATABASE_SAVE_NAME_TASK,
ACTION_DATABASE_SAVE_DESCRIPTION_TASK,
ACTION_DATABASE_SAVE_DEFAULT_USERNAME_TASK,
ACTION_DATABASE_SAVE_COLOR_TASK,
ACTION_DATABASE_SAVE_COMPRESSION_TASK,
ACTION_DATABASE_SAVE_MAX_HISTORY_ITEMS_TASK,
ACTION_DATABASE_SAVE_MAX_HISTORY_SIZE_TASK,
ACTION_DATABASE_SAVE_ENCRYPTION_TASK,
ACTION_DATABASE_SAVE_KEY_DERIVATION_TASK,
ACTION_DATABASE_SAVE_MEMORY_USAGE_TASK,
ACTION_DATABASE_SAVE_PARALLELISM_TASK,
ACTION_DATABASE_SAVE_ITERATIONS_TASK -> buildDatabaseSaveElementActionTask(intent)
else -> null
}
actionRunnable?.let { actionRunnableNotNull ->
// Assign elements for updates
mTitleId = titleId
mMessageId = messageId
mWarningId = warningId
// Create the notification
newNotification(intent.getIntExtra(DATABASE_TASK_TITLE_KEY, titleId))
// Build and launch the action
actionRunnableAsyncTask = ActionRunnableAsyncTask(this,
{
sendBroadcast(Intent(DATABASE_START_TASK_ACTION).apply {
putExtra(DATABASE_TASK_TITLE_KEY, titleId)
putExtra(DATABASE_TASK_MESSAGE_KEY, messageId)
putExtra(DATABASE_TASK_WARNING_KEY, warningId)
})
mActionTaskListeners.forEach { actionTaskListener ->
actionTaskListener.onStartAction(titleId, messageId, warningId)
}
}, { result ->
mActionTaskListeners.forEach { actionTaskListener ->
actionTaskListener.onStopAction(intentAction!!, result)
}
sendBroadcast(Intent(DATABASE_STOP_TASK_ACTION))
stopSelf()
}
)
actionRunnableAsyncTask?.execute({ actionRunnableNotNull })
}
return START_REDELIVER_INTENT
}
private fun newNotification(title: Int) {
@@ -27,11 +161,385 @@ class DatabaseTaskNotificationService : NotificationService() {
startForeground(notificationId, builder.build())
}
override fun updateMessage(resId: Int) {
mMessageId = resId
mActionTaskListeners.forEach { actionTaskListener ->
actionTaskListener.onUpdateAction(mTitleId, mMessageId, mWarningId)
}
}
private fun buildDatabaseCreateActionTask(intent: Intent): ActionRunnable? {
if (intent.hasExtra(DATABASE_URI_KEY)
&& intent.hasExtra(MASTER_PASSWORD_CHECKED_KEY)
&& intent.hasExtra(MASTER_PASSWORD_KEY)
&& intent.hasExtra(KEY_FILE_CHECKED_KEY)
&& intent.hasExtra(KEY_FILE_KEY)
) {
val databaseUri: Uri = intent.getParcelableExtra(DATABASE_URI_KEY)
val keyFileUri: Uri? = intent.getParcelableExtra(KEY_FILE_KEY)
return CreateDatabaseRunnable(this,
databaseUri,
Database.getInstance(),
intent.getBooleanExtra(MASTER_PASSWORD_CHECKED_KEY, false),
intent.getStringExtra(MASTER_PASSWORD_KEY),
intent.getBooleanExtra(KEY_FILE_CHECKED_KEY, false),
keyFileUri,
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 {
return null
}
}
private fun buildDatabaseLoadActionTask(intent: Intent): ActionRunnable? {
if (intent.hasExtra(DATABASE_URI_KEY)
&& intent.hasExtra(MASTER_PASSWORD_KEY)
&& intent.hasExtra(KEY_FILE_KEY)
&& intent.hasExtra(READ_ONLY_KEY)
&& intent.hasExtra(CIPHER_ENTITY_KEY)
&& intent.hasExtra(FIX_DUPLICATE_UUID_KEY)
) {
val database = Database.getInstance()
val databaseUri: Uri = intent.getParcelableExtra(DATABASE_URI_KEY)
val masterPassword: String? = intent.getStringExtra(MASTER_PASSWORD_KEY)
val keyFileUri: Uri? = intent.getParcelableExtra(KEY_FILE_KEY)
val readOnly: Boolean = intent.getBooleanExtra(READ_ONLY_KEY, true)
val cipherEntity: CipherDatabaseEntity? = intent.getParcelableExtra(CIPHER_ENTITY_KEY)
return LoadDatabaseRunnable(
this,
database,
databaseUri,
masterPassword,
keyFileUri,
readOnly,
cipherEntity,
PreferencesUtil.omitBackup(this),
intent.getBooleanExtra(FIX_DUPLICATE_UUID_KEY, false),
this,
object: ActionRunnable() {
override fun onFinishRun(result: Result) {
result.data = Bundle().apply {
putParcelable(DATABASE_URI_KEY, databaseUri)
putString(MASTER_PASSWORD_KEY, masterPassword)
putParcelable(KEY_FILE_KEY, keyFileUri)
putBoolean(READ_ONLY_KEY, readOnly)
putParcelable(CIPHER_ENTITY_KEY, cipherEntity)
}
}
})
} else {
return null
}
}
private fun buildDatabaseAssignPasswordActionTask(intent: Intent): ActionRunnable? {
return if (intent.hasExtra(MASTER_PASSWORD_CHECKED_KEY)
&& intent.hasExtra(MASTER_PASSWORD_KEY)
&& intent.hasExtra(KEY_FILE_CHECKED_KEY)
&& intent.hasExtra(KEY_FILE_KEY)
) {
AssignPasswordInDatabaseRunnable(this,
Database.getInstance(),
intent.getBooleanExtra(MASTER_PASSWORD_CHECKED_KEY, false),
intent.getStringExtra(MASTER_PASSWORD_KEY),
intent.getBooleanExtra(KEY_FILE_CHECKED_KEY, false),
intent.getParcelableExtra(KEY_FILE_KEY),
true)
} else {
null
}
}
private inner class AfterActionNodeRunnable : AfterActionNodeFinishRunnable() {
override fun onActionNodeFinish(actionNodeValues: ActionNodeValues) {
// TODO Encapsulate
val bundle = actionNodeValues.result.data ?: Bundle()
bundle.putBundle(OLD_NODES_KEY, getBundleFromListNodes(actionNodeValues.oldNodes))
bundle.putBundle(NEW_NODES_KEY, getBundleFromListNodes(actionNodeValues.newNodes))
actionNodeValues.result.data = bundle
}
}
private fun buildDatabaseCreateGroupActionTask(intent: Intent): ActionRunnable? {
return if (intent.hasExtra(GROUP_KEY)
&& intent.hasExtra(PARENT_ID_KEY)
&& intent.hasExtra(SAVE_DATABASE_KEY)
) {
val database = Database.getInstance()
database.getGroupById(intent.getParcelableExtra(PARENT_ID_KEY))?.let { parent ->
AddGroupRunnable(this,
database,
intent.getParcelableExtra(GROUP_KEY),
parent,
intent.getBooleanExtra(SAVE_DATABASE_KEY, false),
AfterActionNodeRunnable())
}
} else {
null
}
}
private fun buildDatabaseUpdateGroupActionTask(intent: Intent): ActionRunnable? {
return if (intent.hasExtra(GROUP_ID_KEY)
&& intent.hasExtra(GROUP_KEY)
&& intent.hasExtra(SAVE_DATABASE_KEY)
) {
val database = Database.getInstance()
database.getGroupById(intent.getParcelableExtra(GROUP_ID_KEY))?.let { oldGroup ->
val newGroup: GroupVersioned = intent.getParcelableExtra(GROUP_KEY)
UpdateGroupRunnable(this,
database,
oldGroup,
newGroup,
intent.getBooleanExtra(SAVE_DATABASE_KEY, false),
AfterActionNodeRunnable())
}
} else {
null
}
}
private fun buildDatabaseCreateEntryActionTask(intent: Intent): ActionRunnable? {
return if (intent.hasExtra(ENTRY_KEY)
&& intent.hasExtra(PARENT_ID_KEY)
&& intent.hasExtra(SAVE_DATABASE_KEY)
) {
val database = Database.getInstance()
database.getGroupById(intent.getParcelableExtra(PARENT_ID_KEY))?.let { parent ->
AddEntryRunnable(this,
database,
intent.getParcelableExtra(ENTRY_KEY),
parent,
intent.getBooleanExtra(SAVE_DATABASE_KEY, false),
AfterActionNodeRunnable())
}
} else {
null
}
}
private fun buildDatabaseUpdateEntryActionTask(intent: Intent): ActionRunnable? {
return if (intent.hasExtra(ENTRY_ID_KEY)
&& intent.hasExtra(ENTRY_KEY)
&& intent.hasExtra(SAVE_DATABASE_KEY)
) {
val database = Database.getInstance()
database.getEntryById(intent.getParcelableExtra(ENTRY_ID_KEY))?.let { oldEntry ->
val newEntry: EntryVersioned = intent.getParcelableExtra(ENTRY_KEY)
UpdateEntryRunnable(this,
database,
oldEntry,
newEntry,
intent.getBooleanExtra(SAVE_DATABASE_KEY, false),
AfterActionNodeRunnable())
}
} else {
null
}
}
private fun buildDatabaseCopyNodesActionTask(intent: Intent): ActionRunnable? {
return if (intent.hasExtra(GROUPS_ID_KEY)
&& intent.hasExtra(ENTRIES_ID_KEY)
&& intent.hasExtra(PARENT_ID_KEY)
&& intent.hasExtra(SAVE_DATABASE_KEY)
) {
val database = Database.getInstance()
database.getGroupById(intent.getParcelableExtra(PARENT_ID_KEY))?.let { newParent ->
CopyNodesRunnable(this,
database,
getListNodesFromBundle(database, intent.extras!!),
newParent,
intent.getBooleanExtra(SAVE_DATABASE_KEY, false),
AfterActionNodeRunnable())
}
} else {
null
}
}
private fun buildDatabaseMoveNodesActionTask(intent: Intent): ActionRunnable? {
return if (intent.hasExtra(GROUPS_ID_KEY)
&& intent.hasExtra(ENTRIES_ID_KEY)
&& intent.hasExtra(PARENT_ID_KEY)
&& intent.hasExtra(SAVE_DATABASE_KEY)
) {
val database = Database.getInstance()
database.getGroupById(intent.getParcelableExtra(PARENT_ID_KEY))?.let { newParent ->
MoveNodesRunnable(this,
database,
getListNodesFromBundle(database, intent.extras!!),
newParent,
intent.getBooleanExtra(SAVE_DATABASE_KEY, false),
AfterActionNodeRunnable())
}
} else {
null
}
}
private fun buildDatabaseDeleteNodesActionTask(intent: Intent): ActionRunnable? {
return if (intent.hasExtra(GROUPS_ID_KEY)
&& intent.hasExtra(ENTRIES_ID_KEY)
&& intent.hasExtra(SAVE_DATABASE_KEY)
) {
val database = Database.getInstance()
DeleteNodesRunnable(this,
database,
getListNodesFromBundle(database, intent.extras!!),
intent.getBooleanExtra(SAVE_DATABASE_KEY, false),
AfterActionNodeRunnable())
} else {
null
}
}
private fun buildDatabaseSaveElementActionTask(intent: Intent): ActionRunnable? {
return SaveDatabaseActionRunnable(this,
Database.getInstance(),
true,
object: ActionRunnable() {
override fun onFinishRun(result: Result) {
result.data = intent.extras
}
})
}
private class ActionRunnableAsyncTask(private val progressTaskUpdater: ProgressTaskUpdater,
private val onPreExecute: () -> Unit,
private val onPostExecute: (result: ActionRunnable.Result) -> Unit)
: AsyncTask<((ProgressTaskUpdater?) -> ActionRunnable), Void, ActionRunnable.Result>() {
override fun onPreExecute() {
super.onPreExecute()
onPreExecute.invoke()
}
override fun doInBackground(vararg actionRunnables: ((ProgressTaskUpdater?)-> ActionRunnable)?): ActionRunnable.Result {
var resultTask = ActionRunnable.Result(false)
actionRunnables.forEach {
it?.invoke(progressTaskUpdater)?.apply {
run()
resultTask = result
}
}
return resultTask
}
override fun onPostExecute(result: ActionRunnable.Result) {
super.onPostExecute(result)
onPostExecute.invoke(result)
}
}
companion object {
private val TAG = DatabaseTaskNotificationService::class.java.name
const val DATABASE_TASK_TITLE_KEY = "DatabaseTaskTitle"
const val DATABASE_TASK_TITLE_KEY = "DATABASE_TASK_TITLE_KEY"
const val DATABASE_TASK_MESSAGE_KEY = "DATABASE_TASK_MESSAGE_KEY"
const val DATABASE_TASK_WARNING_KEY = "DATABASE_TASK_WARNING_KEY"
const val ACTION_DATABASE_CREATE_TASK = "ACTION_DATABASE_CREATE_TASK"
const val ACTION_DATABASE_LOAD_TASK = "ACTION_DATABASE_LOAD_TASK"
const val ACTION_DATABASE_ASSIGN_PASSWORD_TASK = "ACTION_DATABASE_ASSIGN_PASSWORD_TASK"
const val ACTION_DATABASE_CREATE_GROUP_TASK = "ACTION_DATABASE_CREATE_GROUP_TASK"
const val ACTION_DATABASE_UPDATE_GROUP_TASK = "ACTION_DATABASE_UPDATE_GROUP_TASK"
const val ACTION_DATABASE_CREATE_ENTRY_TASK = "ACTION_DATABASE_CREATE_ENTRY_TASK"
const val ACTION_DATABASE_UPDATE_ENTRY_TASK = "ACTION_DATABASE_UPDATE_ENTRY_TASK"
const val ACTION_DATABASE_COPY_NODES_TASK = "ACTION_DATABASE_COPY_NODES_TASK"
const val ACTION_DATABASE_MOVE_NODES_TASK = "ACTION_DATABASE_MOVE_NODES_TASK"
const val ACTION_DATABASE_DELETE_NODES_TASK = "ACTION_DATABASE_DELETE_NODES_TASK"
const val ACTION_DATABASE_SAVE_NAME_TASK = "ACTION_DATABASE_SAVE_NAME_TASK"
const val ACTION_DATABASE_SAVE_DESCRIPTION_TASK = "ACTION_DATABASE_SAVE_DESCRIPTION_TASK"
const val ACTION_DATABASE_SAVE_DEFAULT_USERNAME_TASK = "ACTION_DATABASE_SAVE_DEFAULT_USERNAME_TASK"
const val ACTION_DATABASE_SAVE_COLOR_TASK = "ACTION_DATABASE_SAVE_COLOR_TASK"
const val ACTION_DATABASE_SAVE_COMPRESSION_TASK = "ACTION_DATABASE_SAVE_COMPRESSION_TASK"
const val ACTION_DATABASE_SAVE_MAX_HISTORY_ITEMS_TASK = "ACTION_DATABASE_SAVE_MAX_HISTORY_ITEMS_TASK"
const val ACTION_DATABASE_SAVE_MAX_HISTORY_SIZE_TASK = "ACTION_DATABASE_SAVE_MAX_HISTORY_SIZE_TASK"
const val ACTION_DATABASE_SAVE_ENCRYPTION_TASK = "ACTION_DATABASE_SAVE_ENCRYPTION_TASK"
const val ACTION_DATABASE_SAVE_KEY_DERIVATION_TASK = "ACTION_DATABASE_SAVE_KEY_DERIVATION_TASK"
const val ACTION_DATABASE_SAVE_MEMORY_USAGE_TASK = "ACTION_DATABASE_SAVE_MEMORY_USAGE_TASK"
const val ACTION_DATABASE_SAVE_PARALLELISM_TASK = "ACTION_DATABASE_SAVE_PARALLELISM_TASK"
const val ACTION_DATABASE_SAVE_ITERATIONS_TASK = "ACTION_DATABASE_SAVE_ITERATIONS_TASK"
const val DATABASE_URI_KEY = "DATABASE_URI_KEY"
const val MASTER_PASSWORD_CHECKED_KEY = "MASTER_PASSWORD_CHECKED_KEY"
const val MASTER_PASSWORD_KEY = "MASTER_PASSWORD_KEY"
const val KEY_FILE_CHECKED_KEY = "KEY_FILE_CHECKED_KEY"
const val KEY_FILE_KEY = "KEY_FILE_KEY"
const val READ_ONLY_KEY = "READ_ONLY_KEY"
const val CIPHER_ENTITY_KEY = "CIPHER_ENTITY_KEY"
const val FIX_DUPLICATE_UUID_KEY = "FIX_DUPLICATE_UUID_KEY"
const val GROUP_KEY = "GROUP_KEY"
const val ENTRY_KEY = "ENTRY_KEY"
const val GROUP_ID_KEY = "GROUP_ID_KEY"
const val ENTRY_ID_KEY = "ENTRY_ID_KEY"
const val GROUPS_ID_KEY = "GROUPS_ID_KEY"
const val ENTRIES_ID_KEY = "ENTRIES_ID_KEY"
const val PARENT_ID_KEY = "PARENT_ID_KEY"
const val SAVE_DATABASE_KEY = "SAVE_DATABASE_KEY"
const val OLD_NODES_KEY = "OLD_NODES_KEY"
const val NEW_NODES_KEY = "NEW_NODES_KEY"
const val OLD_ELEMENT_KEY = "OLD_ELEMENT_KEY" // Warning type of this thing change every time
const val NEW_ELEMENT_KEY = "NEW_ELEMENT_KEY" // Warning type of this thing change every time
fun getListNodesFromBundle(database: Database, bundle: Bundle): List<NodeVersioned> {
val nodesAction = ArrayList<NodeVersioned>()
bundle.getParcelableArrayList<PwNodeId<*>>(GROUPS_ID_KEY)?.forEach {
database.getGroupById(it)?.let { groupRetrieve ->
nodesAction.add(groupRetrieve)
}
}
bundle.getParcelableArrayList<PwNodeId<UUID>>(ENTRIES_ID_KEY)?.forEach {
database.getEntryById(it)?.let { entryRetrieve ->
nodesAction.add(entryRetrieve)
}
}
return nodesAction
}
fun getBundleFromListNodes(nodes: List<NodeVersioned>): Bundle {
val groupsIdToCopy = ArrayList<PwNodeId<*>>()
val entriesIdToCopy = ArrayList<PwNodeId<UUID>>()
nodes.forEach { nodeVersioned ->
when (nodeVersioned.type) {
Type.GROUP -> {
(nodeVersioned as GroupVersioned).nodeId?.let { groupId ->
groupsIdToCopy.add(groupId)
}
}
Type.ENTRY -> {
entriesIdToCopy.add((nodeVersioned as EntryVersioned).nodeId)
}
}
}
return Bundle().apply {
putParcelableArrayList(GROUPS_ID_KEY, groupsIdToCopy)
putParcelableArrayList(ENTRIES_ID_KEY, entriesIdToCopy)
}
}
}
}

View File

@@ -47,13 +47,30 @@ import com.kunzisoft.keepass.activities.stylish.Stylish
import com.kunzisoft.keepass.app.database.CipherDatabaseAction
import com.kunzisoft.keepass.app.database.FileDatabaseHistoryAction
import com.kunzisoft.keepass.biometric.BiometricUnlockDatabaseHelper
import com.kunzisoft.keepass.crypto.keyDerivation.KdfEngine
import com.kunzisoft.keepass.database.element.Database
import com.kunzisoft.keepass.database.element.PwCompressionAlgorithm
import com.kunzisoft.keepass.database.element.PwEncryptionAlgorithm
import com.kunzisoft.keepass.education.Education
import com.kunzisoft.keepass.icons.IconPackChooser
import com.kunzisoft.keepass.notifications.DatabaseTaskNotificationService.Companion.ACTION_DATABASE_SAVE_COLOR_TASK
import com.kunzisoft.keepass.notifications.DatabaseTaskNotificationService.Companion.ACTION_DATABASE_SAVE_COMPRESSION_TASK
import com.kunzisoft.keepass.notifications.DatabaseTaskNotificationService.Companion.ACTION_DATABASE_SAVE_DEFAULT_USERNAME_TASK
import com.kunzisoft.keepass.notifications.DatabaseTaskNotificationService.Companion.ACTION_DATABASE_SAVE_DESCRIPTION_TASK
import com.kunzisoft.keepass.notifications.DatabaseTaskNotificationService.Companion.ACTION_DATABASE_SAVE_ENCRYPTION_TASK
import com.kunzisoft.keepass.notifications.DatabaseTaskNotificationService.Companion.ACTION_DATABASE_SAVE_ITERATIONS_TASK
import com.kunzisoft.keepass.notifications.DatabaseTaskNotificationService.Companion.ACTION_DATABASE_SAVE_KEY_DERIVATION_TASK
import com.kunzisoft.keepass.notifications.DatabaseTaskNotificationService.Companion.ACTION_DATABASE_SAVE_MAX_HISTORY_ITEMS_TASK
import com.kunzisoft.keepass.notifications.DatabaseTaskNotificationService.Companion.ACTION_DATABASE_SAVE_MAX_HISTORY_SIZE_TASK
import com.kunzisoft.keepass.notifications.DatabaseTaskNotificationService.Companion.ACTION_DATABASE_SAVE_MEMORY_USAGE_TASK
import com.kunzisoft.keepass.notifications.DatabaseTaskNotificationService.Companion.ACTION_DATABASE_SAVE_NAME_TASK
import com.kunzisoft.keepass.notifications.DatabaseTaskNotificationService.Companion.ACTION_DATABASE_SAVE_PARALLELISM_TASK
import com.kunzisoft.keepass.notifications.DatabaseTaskNotificationService.Companion.NEW_ELEMENT_KEY
import com.kunzisoft.keepass.notifications.DatabaseTaskNotificationService.Companion.OLD_ELEMENT_KEY
import com.kunzisoft.keepass.settings.preference.*
import com.kunzisoft.keepass.settings.preference.DialogColorPreference.Companion.DISABLE_COLOR
import com.kunzisoft.keepass.settings.preferencedialogfragment.*
import com.kunzisoft.keepass.tasks.ActionRunnable
class NestedSettingsFragment : PreferenceFragmentCompat(), Preference.OnPreferenceClickListener {
@@ -62,7 +79,15 @@ class NestedSettingsFragment : PreferenceFragmentCompat(), Preference.OnPreferen
private var mCount = 0
private var dbNamePref: InputTextPreference? = null
private var dbDescriptionPref: InputTextPreference? = null
private var dbDefaultUsername: InputTextPreference? = null
private var dbCustomColorPref: DialogColorPreference? = null
private var dbDataCompressionPref: Preference? = null
private var dbMaxHistoryItemsPref: InputNumberPreference? = null
private var dbMaxHistorySizePref: InputNumberPreference? = null
private var mEncryptionAlgorithmPref: DialogListExplanationPreference? = null
private var mKeyDerivationPref: DialogListExplanationPreference? = null
private var mRoundPref: InputKdfNumberPreference? = null
private var mMemoryPref: InputKdfNumberPreference? = null
private var mParallelismPref: InputKdfNumberPreference? = null
@@ -349,7 +374,7 @@ class NestedSettingsFragment : PreferenceFragmentCompat(), Preference.OnPreferen
val dbGeneralPrefCategory: PreferenceCategory? = findPreference(getString(R.string.database_category_general_key))
// Database name
val dbNamePref: InputTextPreference? = findPreference(getString(R.string.database_name_key))
dbNamePref = findPreference(getString(R.string.database_name_key))
if (mDatabase.allowName) {
dbNamePref?.summary = mDatabase.name
} else {
@@ -357,7 +382,7 @@ class NestedSettingsFragment : PreferenceFragmentCompat(), Preference.OnPreferen
}
// Database description
val dbDescriptionPref: InputTextPreference? = findPreference(getString(R.string.database_description_key))
dbDescriptionPref = findPreference(getString(R.string.database_description_key))
if (mDatabase.allowDescription) {
dbDescriptionPref?.summary = mDatabase.description
} else {
@@ -365,7 +390,7 @@ class NestedSettingsFragment : PreferenceFragmentCompat(), Preference.OnPreferen
}
// Database default username
val dbDefaultUsername: InputTextPreference? = findPreference(getString(R.string.database_default_username_key))
dbDefaultUsername = findPreference(getString(R.string.database_default_username_key))
if (mDatabase.allowDefaultUsername) {
dbDefaultUsername?.summary = mDatabase.defaultUsername
} else {
@@ -397,9 +422,9 @@ class NestedSettingsFragment : PreferenceFragmentCompat(), Preference.OnPreferen
val dbCompressionPrefCategory: PreferenceCategory? = findPreference(getString(R.string.database_category_compression_key))
// Database compression
val databaseDataCompressionPref = findPreference<Preference>(getString(R.string.database_data_compression_key))
dbDataCompressionPref = findPreference(getString(R.string.database_data_compression_key))
if (mDatabase.allowDataCompression) {
databaseDataCompressionPref?.summary = (mDatabase.compressionAlgorithm
dbDataCompressionPref?.summary = (mDatabase.compressionAlgorithm
?: PwCompressionAlgorithm.None).getName(resources)
} else {
dbCompressionPrefCategory?.isVisible = false
@@ -421,12 +446,14 @@ class NestedSettingsFragment : PreferenceFragmentCompat(), Preference.OnPreferen
?.isVisible = mDatabase.manageHistory == true
// Max history items
findPreference<InputNumberPreference>(getString(R.string.max_history_items_key))
?.summary = mDatabase.historyMaxItems.toString()
dbMaxHistoryItemsPref = findPreference<InputNumberPreference>(getString(R.string.max_history_items_key))?.apply {
summary = mDatabase.historyMaxItems.toString()
}
// Max history size
findPreference<InputNumberPreference>(getString(R.string.max_history_size_key))
?.summary = mDatabase.historyMaxSize.toString()
dbMaxHistorySizePref = findPreference<InputNumberPreference>(getString(R.string.max_history_size_key))?.apply {
summary = mDatabase.historyMaxSize.toString()
}
} else {
Log.e(javaClass.name, "Database isn't ready")
@@ -438,24 +465,29 @@ class NestedSettingsFragment : PreferenceFragmentCompat(), Preference.OnPreferen
if (mDatabase.loaded) {
// Encryption Algorithm
findPreference<DialogListExplanationPreference>(getString(R.string.encryption_algorithm_key))
?.summary = mDatabase.getEncryptionAlgorithmName(resources)
mEncryptionAlgorithmPref = findPreference<DialogListExplanationPreference>(getString(R.string.encryption_algorithm_key))?.apply {
summary = mDatabase.getEncryptionAlgorithmName(resources)
}
// Key derivation function
findPreference<DialogListExplanationPreference>(getString(R.string.key_derivation_function_key))
?.summary = mDatabase.getKeyDerivationName(resources)
mKeyDerivationPref = findPreference<DialogListExplanationPreference>(getString(R.string.key_derivation_function_key))?.apply {
summary = mDatabase.getKeyDerivationName(resources)
}
// Round encryption
mRoundPref = findPreference(getString(R.string.transform_rounds_key))
mRoundPref?.summary = mDatabase.numberKeyEncryptionRounds.toString()
mRoundPref = findPreference<InputKdfNumberPreference>(getString(R.string.transform_rounds_key))?.apply {
summary = mDatabase.numberKeyEncryptionRounds.toString()
}
// Memory Usage
mMemoryPref = findPreference(getString(R.string.memory_usage_key))
mMemoryPref?.summary = mDatabase.memoryUsage.toString()
mMemoryPref = findPreference<InputKdfNumberPreference>(getString(R.string.memory_usage_key))?.apply {
summary = mDatabase.memoryUsage.toString()
}
// Parallelism
mParallelismPref = findPreference(getString(R.string.parallelism_key))
mParallelismPref?.summary = mDatabase.parallelism.toString()
mParallelismPref = findPreference<InputKdfNumberPreference>(getString(R.string.parallelism_key))?.apply {
summary = mDatabase.parallelism.toString()
}
} else {
Log.e(javaClass.name, "Database isn't ready")
}
@@ -551,6 +583,178 @@ class NestedSettingsFragment : PreferenceFragmentCompat(), Preference.OnPreferen
return view
}
fun onProgressDialogThreadResult(actionTask: String,
result: ActionRunnable.Result) {
result.data?.let { data ->
if (data.containsKey(OLD_ELEMENT_KEY)
&& data.containsKey(NEW_ELEMENT_KEY)) {
when (actionTask) {
/*
--------
Main preferences
--------
*/
ACTION_DATABASE_SAVE_NAME_TASK -> {
val oldName = data.getString(OLD_ELEMENT_KEY)!!
val newName = data.getString(NEW_ELEMENT_KEY)!!
val nameToShow =
if (result.isSuccess) {
newName
} else {
mDatabase.name = oldName
oldName
}
dbNamePref?.summary = nameToShow
}
ACTION_DATABASE_SAVE_DESCRIPTION_TASK -> {
val oldDescription = data.getString(OLD_ELEMENT_KEY)!!
val newDescription = data.getString(NEW_ELEMENT_KEY)!!
val descriptionToShow =
if (result.isSuccess) {
newDescription
} else {
mDatabase.description = oldDescription
oldDescription
}
dbDescriptionPref?.summary = descriptionToShow
}
ACTION_DATABASE_SAVE_DEFAULT_USERNAME_TASK -> {
val oldDefaultUsername = data.getString(OLD_ELEMENT_KEY)!!
val newDefaultUsername = data.getString(NEW_ELEMENT_KEY)!!
val defaultUsernameToShow =
if (result.isSuccess) {
newDefaultUsername
} else {
mDatabase.defaultUsername = oldDefaultUsername
oldDefaultUsername
}
dbDefaultUsername?.summary = defaultUsernameToShow
}
ACTION_DATABASE_SAVE_COLOR_TASK -> {
val oldColor = data.getString(OLD_ELEMENT_KEY)!!
val newColor = data.getString(NEW_ELEMENT_KEY)!!
val defaultColorToShow =
if (result.isSuccess) {
newColor
} else {
mDatabase.customColor = oldColor
oldColor
}
dbCustomColorPref?.summary = defaultColorToShow
}
ACTION_DATABASE_SAVE_COMPRESSION_TASK -> {
val oldCompression = data.getSerializable(OLD_ELEMENT_KEY) as PwCompressionAlgorithm
val newCompression = data.getSerializable(NEW_ELEMENT_KEY) as PwCompressionAlgorithm
val algorithmToShow =
if (result.isSuccess) {
newCompression
} else {
mDatabase.compressionAlgorithm = oldCompression
oldCompression
}
dbDataCompressionPref?.summary = algorithmToShow.getName(resources)
}
ACTION_DATABASE_SAVE_MAX_HISTORY_ITEMS_TASK -> {
val oldMaxHistoryItems = data.getInt(OLD_ELEMENT_KEY)
val newMaxHistoryItems = data.getInt(NEW_ELEMENT_KEY)
val maxHistoryItemsToShow =
if (result.isSuccess) {
newMaxHistoryItems
} else {
mDatabase.historyMaxItems = oldMaxHistoryItems
oldMaxHistoryItems
}
dbMaxHistoryItemsPref?.summary = maxHistoryItemsToShow.toString()
}
ACTION_DATABASE_SAVE_MAX_HISTORY_SIZE_TASK -> {
val oldMaxHistorySize = data.getLong(OLD_ELEMENT_KEY)
val newMaxHistorySize = data.getLong(NEW_ELEMENT_KEY)
val maxHistorySizeToShow =
if (result.isSuccess) {
newMaxHistorySize
} else {
mDatabase.historyMaxSize = oldMaxHistorySize
oldMaxHistorySize
}
dbMaxHistorySizePref?.summary = maxHistorySizeToShow.toString()
}
/*
--------
Security
--------
*/
ACTION_DATABASE_SAVE_ENCRYPTION_TASK -> {
val oldEncryption = data.getSerializable(OLD_ELEMENT_KEY) as PwEncryptionAlgorithm
val newEncryption = data.getSerializable(NEW_ELEMENT_KEY) as PwEncryptionAlgorithm
val algorithmToShow =
if (result.isSuccess) {
newEncryption
} else {
mDatabase.encryptionAlgorithm = oldEncryption
oldEncryption
}
mEncryptionAlgorithmPref?.summary = algorithmToShow.getName(resources)
}
ACTION_DATABASE_SAVE_KEY_DERIVATION_TASK -> {
val oldKeyDerivationEngine = data.getSerializable(OLD_ELEMENT_KEY) as KdfEngine
val newKeyDerivationEngine = data.getSerializable(NEW_ELEMENT_KEY) as KdfEngine
val kdfEngineToShow =
if (result.isSuccess) {
newKeyDerivationEngine
} else {
mDatabase.kdfEngine = oldKeyDerivationEngine
oldKeyDerivationEngine
}
mKeyDerivationPref?.summary = kdfEngineToShow.getName(resources)
mRoundPref?.summary = kdfEngineToShow.defaultKeyRounds.toString()
// Disable memory and parallelism if not available
mMemoryPref?.summary = kdfEngineToShow.defaultMemoryUsage.toString()
mParallelismPref?.summary = kdfEngineToShow.defaultParallelism.toString()
}
ACTION_DATABASE_SAVE_ITERATIONS_TASK -> {
val oldIterations = data.getLong(OLD_ELEMENT_KEY)
val newIterations = data.getLong(NEW_ELEMENT_KEY)
val roundsToShow =
if (result.isSuccess) {
newIterations
} else {
mDatabase.numberKeyEncryptionRounds = oldIterations
oldIterations
}
mRoundPref?.summary = roundsToShow.toString()
}
ACTION_DATABASE_SAVE_MEMORY_USAGE_TASK -> {
val oldMemoryUsage = data.getLong(OLD_ELEMENT_KEY)
val newMemoryUsage = data.getLong(NEW_ELEMENT_KEY)
val memoryToShow =
if (result.isSuccess) {
newMemoryUsage
} else {
mDatabase.memoryUsage = oldMemoryUsage
oldMemoryUsage
}
mMemoryPref?.summary = memoryToShow.toString()
}
ACTION_DATABASE_SAVE_PARALLELISM_TASK -> {
val oldParallelism = data.getInt(OLD_ELEMENT_KEY)
val newParallelism = data.getInt(NEW_ELEMENT_KEY)
val parallelismToShow =
if (result.isSuccess) {
newParallelism
} else {
mDatabase.parallelism = oldParallelism
oldParallelism
}
mParallelismPref?.summary = parallelismToShow.toString()
}
}
}
}
}
override fun onDisplayPreferenceDialog(preference: Preference?) {
var otherDialogFragment = false
@@ -559,6 +763,7 @@ class NestedSettingsFragment : PreferenceFragmentCompat(), Preference.OnPreferen
preference?.let { preference ->
var dialogFragment: DialogFragment? = null
when {
// Main Preferences
preference.key == getString(R.string.database_name_key) -> {
dialogFragment = DatabaseNamePreferenceDialogFragmentCompat.newInstance(preference.key)
}
@@ -582,6 +787,8 @@ class NestedSettingsFragment : PreferenceFragmentCompat(), Preference.OnPreferen
preference.key == getString(R.string.max_history_size_key) -> {
dialogFragment = MaxHistorySizePreferenceDialogFragmentCompat.newInstance(preference.key)
}
// Security
preference.key == getString(R.string.encryption_algorithm_key) -> {
dialogFragment = DatabaseEncryptionAlgorithmPreferenceDialogFragmentCompat.newInstance(preference.key)
}

View File

@@ -25,16 +25,15 @@ import android.content.DialogInterface
import android.content.Intent
import android.net.Uri
import android.os.Bundle
import androidx.fragment.app.Fragment
import androidx.appcompat.widget.Toolbar
import android.view.MenuItem
import androidx.appcompat.widget.Toolbar
import androidx.fragment.app.Fragment
import com.kunzisoft.keepass.R
import com.kunzisoft.keepass.activities.dialogs.AssignMasterKeyDialogFragment
import com.kunzisoft.keepass.activities.dialogs.PasswordEncodingDialogFragment
import com.kunzisoft.keepass.activities.helpers.ReadOnlyHelper
import com.kunzisoft.keepass.activities.lock.LockingActivity
import com.kunzisoft.keepass.database.action.AssignPasswordInDatabaseRunnable
import com.kunzisoft.keepass.database.action.ProgressDialogSaveDatabaseThread
import com.kunzisoft.keepass.database.action.ProgressDialogThread
import com.kunzisoft.keepass.database.element.Database
import com.kunzisoft.keepass.timeout.TimeoutHelper
@@ -47,6 +46,8 @@ open class SettingsActivity
private var toolbar: Toolbar? = null
var progressDialogThread: ProgressDialogThread? = null
companion object {
private const val TAG_NESTED = "TAG_NESTED"
@@ -88,6 +89,26 @@ open class SettingsActivity
}
backupManager = BackupManager(this)
progressDialogThread = ProgressDialogThread(this) { actionTask, result ->
// Call result in fragment
(supportFragmentManager
.findFragmentByTag(TAG_NESTED) as NestedSettingsFragment?)
?.onProgressDialogThreadResult(actionTask, result)
}
}
override fun onResume() {
super.onResume()
progressDialogThread?.registerProgressTask()
}
override fun onPause() {
progressDialogThread?.unregisterProgressTask()
super.onPause()
}
override fun onOptionsItemSelected(item: MenuItem): Boolean {
@@ -103,24 +124,28 @@ open class SettingsActivity
super.onStop()
}
override fun onAssignKeyDialogPositiveClick(masterPasswordChecked: Boolean, masterPassword: String?, keyFileChecked: Boolean, keyFile: Uri?) {
override fun onAssignKeyDialogPositiveClick(masterPasswordChecked: Boolean,
masterPassword: String?,
keyFileChecked: Boolean,
keyFile: Uri?) {
Database.getInstance().let { database ->
val progressDialogThread = ProgressDialogSaveDatabaseThread(this) {
AssignPasswordInDatabaseRunnable(this,
database,
// Show the progress dialog now or after dialog confirmation
if (database.validatePasswordEncoding(masterPassword, keyFileChecked)) {
progressDialogThread?.startDatabaseAssignPassword(
masterPasswordChecked,
masterPassword,
keyFileChecked,
keyFile,
true)
}
// Show the progress dialog now or after dialog confirmation
if (database.validatePasswordEncoding(masterPassword, keyFileChecked)) {
progressDialogThread.start()
keyFile
)
} else {
PasswordEncodingDialogFragment().apply {
positiveButtonClickListener = DialogInterface.OnClickListener { _, _ ->
progressDialogThread.start()
progressDialogThread?.startDatabaseAssignPassword(
masterPasswordChecked,
masterPassword,
keyFileChecked,
keyFile
)
}
show(supportFragmentManager, "passwordEncodingTag")
}
@@ -128,7 +153,10 @@ open class SettingsActivity
}
}
override fun onAssignKeyDialogNegativeClick(masterPasswordChecked: Boolean, masterPassword: String?, keyFileChecked: Boolean, keyFile: Uri?) {
override fun onAssignKeyDialogNegativeClick(masterPasswordChecked: Boolean,
masterPassword: String?,
keyFileChecked: Boolean,
keyFile: Uri?) {
}

View File

@@ -36,7 +36,6 @@ import com.kunzisoft.androidclearchroma.colormode.ColorMode
import com.kunzisoft.androidclearchroma.fragment.ChromaColorFragment
import com.kunzisoft.androidclearchroma.fragment.ChromaColorFragment.*
import com.kunzisoft.keepass.R
import com.kunzisoft.keepass.tasks.ActionRunnable
import java.lang.Exception
class DatabaseColorPreferenceDialogFragmentCompat : DatabaseSavePreferenceDialogFragmentCompat() {
@@ -88,16 +87,15 @@ class DatabaseColorPreferenceDialogFragmentCompat : DatabaseSavePreferenceDialog
}
val oldColor = database.customColor
database.customColor = newColor
actionInUIThreadAfterSaveDatabase = AfterColorSave(newColor, oldColor)
progressDialogThread?.startDatabaseSaveColor(oldColor, newColor)
}
super.onDialogClosed(true)
onDialogClosed(true)
dismiss()
}
alertDialogBuilder.setNegativeButton(android.R.string.cancel) { _, _ ->
super.onDialogClosed(false)
onDialogClosed(false)
dismiss()
}
@@ -112,6 +110,10 @@ class DatabaseColorPreferenceDialogFragmentCompat : DatabaseSavePreferenceDialog
return dialog
}
override fun onDialogClosed(positiveResult: Boolean) {
// Nothing here
}
/**
* Set new dimensions to dialog
* @param ad dialog
@@ -134,22 +136,6 @@ class DatabaseColorPreferenceDialogFragmentCompat : DatabaseSavePreferenceDialog
return rootView
}
private inner class AfterColorSave(private val mNewColor: String,
private val mOldColor: String)
: ActionRunnable() {
override fun onFinishRun(result: Result) {
val defaultColorToShow =
if (result.isSuccess) {
mNewColor
} else {
database?.customColor = mOldColor
mOldColor
}
preference.summary = defaultColorToShow
}
}
companion object {
private const val TAG_FRAGMENT_COLORS = "TAG_FRAGMENT_COLORS"

View File

@@ -26,7 +26,6 @@ import androidx.recyclerview.widget.RecyclerView
import com.kunzisoft.keepass.R
import com.kunzisoft.keepass.database.element.PwCompressionAlgorithm
import com.kunzisoft.keepass.settings.preferencedialogfragment.adapter.ListRadioItemAdapter
import com.kunzisoft.keepass.tasks.ActionRunnable
class DatabaseDataCompressionPreferenceDialogFragmentCompat
: DatabaseSavePreferenceDialogFragmentCompat(),
@@ -60,39 +59,21 @@ class DatabaseDataCompressionPreferenceDialogFragmentCompat
if (positiveResult) {
database?.let { database ->
if (compressionSelected != null) {
val newAlgorithm = compressionSelected
val oldAlgorithm = database.compressionAlgorithm
database.compressionAlgorithm = newAlgorithm
val newCompression = compressionSelected
val oldCompression = database.compressionAlgorithm
database.compressionAlgorithm = newCompression
if (oldAlgorithm != null && newAlgorithm != null)
actionInUIThreadAfterSaveDatabase = AfterDescriptionSave(newAlgorithm, oldAlgorithm)
if (oldCompression != null && newCompression != null)
progressDialogThread?.startDatabaseSaveCompression(oldCompression, newCompression)
}
}
}
super.onDialogClosed(positiveResult)
}
override fun onItemSelected(item: PwCompressionAlgorithm) {
this.compressionSelected = item
}
private inner class AfterDescriptionSave(private val mNewAlgorithm: PwCompressionAlgorithm,
private val mOldAlgorithm: PwCompressionAlgorithm)
: ActionRunnable() {
override fun onFinishRun(result: Result) {
val algorithmToShow =
if (result.isSuccess) {
mNewAlgorithm
} else {
database?.compressionAlgorithm = mOldAlgorithm
mOldAlgorithm
}
preference.summary = algorithmToShow.getName(settingsResources)
}
}
companion object {
fun newInstance(key: String): DatabaseDataCompressionPreferenceDialogFragmentCompat {

View File

@@ -21,7 +21,6 @@ package com.kunzisoft.keepass.settings.preferencedialogfragment
import android.os.Bundle
import android.view.View
import com.kunzisoft.keepass.tasks.ActionRunnable
class DatabaseDefaultUsernamePreferenceDialogFragmentCompat : DatabaseSavePreferenceDialogFragmentCompat() {
@@ -38,27 +37,9 @@ class DatabaseDefaultUsernamePreferenceDialogFragmentCompat : DatabaseSavePrefer
val oldDefaultUsername = database.defaultUsername
database.defaultUsername = newDefaultUsername
actionInUIThreadAfterSaveDatabase = AfterDefaultUsernameSave(newDefaultUsername, oldDefaultUsername)
progressDialogThread?.startDatabaseSaveDefaultUsername(oldDefaultUsername, newDefaultUsername)
}
}
super.onDialogClosed(positiveResult)
}
private inner class AfterDefaultUsernameSave(private val mNewDefaultUsername: String,
private val mOldDefaultUsername: String)
: ActionRunnable() {
override fun onFinishRun(result: Result) {
val defaultUsernameToShow =
if (result.isSuccess) {
mNewDefaultUsername
} else {
database?.defaultUsername = mOldDefaultUsername
mOldDefaultUsername
}
preference.summary = defaultUsernameToShow
}
}
companion object {

View File

@@ -21,7 +21,6 @@ package com.kunzisoft.keepass.settings.preferencedialogfragment
import android.os.Bundle
import android.view.View
import com.kunzisoft.keepass.tasks.ActionRunnable
class DatabaseDescriptionPreferenceDialogFragmentCompat : DatabaseSavePreferenceDialogFragmentCompat() {
@@ -38,27 +37,9 @@ class DatabaseDescriptionPreferenceDialogFragmentCompat : DatabaseSavePreference
val oldDescription = database.description
database.description = newDescription
actionInUIThreadAfterSaveDatabase = AfterDescriptionSave(newDescription, oldDescription)
progressDialogThread?.startDatabaseSaveDescription(oldDescription, newDescription)
}
}
super.onDialogClosed(positiveResult)
}
private inner class AfterDescriptionSave(private val mNewDescription: String,
private val mOldDescription: String)
: ActionRunnable() {
override fun onFinishRun(result: Result) {
val descriptionToShow =
if (result.isSuccess) {
mNewDescription
} else {
database?.description = mOldDescription
mOldDescription
}
preference.summary = descriptionToShow
}
}
companion object {

View File

@@ -26,7 +26,6 @@ import android.view.View
import com.kunzisoft.keepass.R
import com.kunzisoft.keepass.database.element.PwEncryptionAlgorithm
import com.kunzisoft.keepass.settings.preferencedialogfragment.adapter.ListRadioItemAdapter
import com.kunzisoft.keepass.tasks.ActionRunnable
class DatabaseEncryptionAlgorithmPreferenceDialogFragmentCompat
: DatabaseSavePreferenceDialogFragmentCompat(),
@@ -66,35 +65,17 @@ class DatabaseEncryptionAlgorithmPreferenceDialogFragmentCompat
database.encryptionAlgorithm = newAlgorithm
if (oldAlgorithm != null && newAlgorithm != null)
actionInUIThreadAfterSaveDatabase = AfterDescriptionSave(newAlgorithm, oldAlgorithm)
progressDialogThread?.startDatabaseSaveEncryption(oldAlgorithm, newAlgorithm)
}
}
}
}
super.onDialogClosed(positiveResult)
}
override fun onItemSelected(item: PwEncryptionAlgorithm) {
this.algorithmSelected = item
}
private inner class AfterDescriptionSave(private val mNewAlgorithm: PwEncryptionAlgorithm,
private val mOldAlgorithm: PwEncryptionAlgorithm)
: ActionRunnable() {
override fun onFinishRun(result: Result) {
val algorithmToShow =
if (result.isSuccess) {
mNewAlgorithm
} else {
database?.encryptionAlgorithm = mOldAlgorithm
mOldAlgorithm
}
preference.summary = algorithmToShow.getName(settingsResources)
}
}
companion object {
fun newInstance(key: String): DatabaseEncryptionAlgorithmPreferenceDialogFragmentCompat {

View File

@@ -27,7 +27,6 @@ import android.view.View
import com.kunzisoft.keepass.R
import com.kunzisoft.keepass.crypto.keyDerivation.KdfEngine
import com.kunzisoft.keepass.settings.preferencedialogfragment.adapter.ListRadioItemAdapter
import com.kunzisoft.keepass.tasks.ActionRunnable
class DatabaseKeyDerivationPreferenceDialogFragmentCompat
: DatabaseSavePreferenceDialogFragmentCompat(),
@@ -67,13 +66,11 @@ class DatabaseKeyDerivationPreferenceDialogFragmentCompat
val oldKdfEngine = database.kdfEngine
if (newKdfEngine != null && oldKdfEngine != null) {
database.kdfEngine = newKdfEngine
actionInUIThreadAfterSaveDatabase = AfterDescriptionSave(newKdfEngine, oldKdfEngine)
progressDialogThread?.startDatabaseSaveKeyDerivation(oldKdfEngine, newKdfEngine)
}
}
}
}
super.onDialogClosed(positiveResult)
}
fun setRoundPreference(preference: Preference?) {
@@ -92,27 +89,6 @@ class DatabaseKeyDerivationPreferenceDialogFragmentCompat
kdfEngineSelected = item
}
private inner class AfterDescriptionSave(private val mNewKdfEngine: KdfEngine,
private val mOldKdfEngine: KdfEngine)
: ActionRunnable() {
override fun onFinishRun(result: Result) {
val kdfEngineToShow =
if (result.isSuccess) {
mNewKdfEngine
} else {
database?.kdfEngine = mOldKdfEngine
mOldKdfEngine
}
preference.summary = kdfEngineToShow.getName(settingsResources)
roundPreference?.summary = kdfEngineToShow.defaultKeyRounds.toString()
// Disable memory and parallelism if not available
memoryPreference?.summary = kdfEngineToShow.defaultMemoryUsage.toString()
parallelismPreference?.summary = kdfEngineToShow.defaultParallelism.toString()
}
}
companion object {
fun newInstance(key: String): DatabaseKeyDerivationPreferenceDialogFragmentCompat {

View File

@@ -21,7 +21,6 @@ package com.kunzisoft.keepass.settings.preferencedialogfragment
import android.os.Bundle
import android.view.View
import com.kunzisoft.keepass.tasks.ActionRunnable
class DatabaseNamePreferenceDialogFragmentCompat : DatabaseSavePreferenceDialogFragmentCompat() {
@@ -38,27 +37,9 @@ class DatabaseNamePreferenceDialogFragmentCompat : DatabaseSavePreferenceDialogF
val oldName = database.name
database.name = newName
actionInUIThreadAfterSaveDatabase = AfterNameSave(newName, oldName)
progressDialogThread?.startDatabaseSaveName(oldName, newName)
}
}
super.onDialogClosed(positiveResult)
}
private inner class AfterNameSave(private val mNewName: String,
private val mOldName: String)
: ActionRunnable() {
override fun onFinishRun(result: Result) {
val nameToShow =
if (result.isSuccess) {
mNewName
} else {
database?.name = mOldName
mOldName
}
preference.summary = nameToShow
}
}
companion object {

View File

@@ -19,23 +19,17 @@
*/
package com.kunzisoft.keepass.settings.preferencedialogfragment
import android.content.res.Resources
import android.content.Context
import android.os.Bundle
import android.util.Log
import android.view.View
import android.widget.Toast
import com.kunzisoft.keepass.database.action.ProgressDialogSaveDatabaseThread
import com.kunzisoft.keepass.database.action.SaveDatabaseActionRunnable
import com.kunzisoft.keepass.database.action.ProgressDialogThread
import com.kunzisoft.keepass.database.element.Database
import com.kunzisoft.keepass.tasks.ActionRunnable
import com.kunzisoft.keepass.settings.SettingsActivity
abstract class DatabaseSavePreferenceDialogFragmentCompat : InputPreferenceDialogFragmentCompat() {
protected var database: Database? = null
var actionInUIThreadAfterSaveDatabase: ActionRunnable? = null
protected lateinit var settingsResources: Resources
protected var progressDialogThread: ProgressDialogThread? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
@@ -43,40 +37,15 @@ abstract class DatabaseSavePreferenceDialogFragmentCompat : InputPreferenceDialo
this.database = Database.getInstance()
}
override fun onBindDialogView(view: View) {
super.onBindDialogView(view)
activity?.resources?.let { settingsResources = it }
}
override fun onDialogClosed(positiveResult: Boolean) {
if (positiveResult) {
activity?.let { notNullActivity ->
database?.let { notNullDatabase ->
ProgressDialogSaveDatabaseThread(notNullActivity) {
SaveDatabaseActionRunnable(
notNullActivity,
notNullDatabase,
true)
}.apply {
actionFinishInUIThread = object:ActionRunnable() {
override fun onFinishRun(result: Result) {
if (!result.isSuccess) {
Log.e(TAG, result.message)
Toast.makeText(notNullActivity, result.message, Toast.LENGTH_SHORT).show()
}
actionInUIThreadAfterSaveDatabase?.onFinishRun(result)
}
}
start()
}
}
}
override fun onAttach(context: Context) {
super.onAttach(context)
// Attach dialog thread to start action
if (context is SettingsActivity) {
progressDialogThread = context.progressDialogThread
}
}
companion object {
private const val TAG = "DbSavePrefDialog"
}
}

View File

@@ -22,7 +22,6 @@ package com.kunzisoft.keepass.settings.preferencedialogfragment
import android.os.Bundle
import android.view.View
import com.kunzisoft.keepass.R
import com.kunzisoft.keepass.tasks.ActionRunnable
class MaxHistoryItemsPreferenceDialogFragmentCompat : DatabaseSavePreferenceDialogFragmentCompat() {
@@ -57,27 +56,9 @@ class MaxHistoryItemsPreferenceDialogFragmentCompat : DatabaseSavePreferenceDial
val oldMaxHistoryItems = database.historyMaxItems
database.historyMaxItems = maxHistoryItems
actionInUIThreadAfterSaveDatabase = AfterMaxHistoryItemsSave(maxHistoryItems, oldMaxHistoryItems)
progressDialogThread?.startDatabaseSaveMaxHistoryItems(oldMaxHistoryItems, maxHistoryItems)
}
}
super.onDialogClosed(positiveResult)
}
private inner class AfterMaxHistoryItemsSave(private val mNewMaxHistoryItems: Int,
private val mOldMaxHistoryItems: Int)
: ActionRunnable() {
override fun onFinishRun(result: Result) {
val maxHistoryItemsToShow =
if (result.isSuccess) {
mNewMaxHistoryItems
} else {
database?.historyMaxItems = mOldMaxHistoryItems
mOldMaxHistoryItems
}
preference.summary = maxHistoryItemsToShow.toString()
}
}
companion object {

View File

@@ -22,7 +22,6 @@ package com.kunzisoft.keepass.settings.preferencedialogfragment
import android.os.Bundle
import android.view.View
import com.kunzisoft.keepass.R
import com.kunzisoft.keepass.tasks.ActionRunnable
class MaxHistorySizePreferenceDialogFragmentCompat : DatabaseSavePreferenceDialogFragmentCompat() {
@@ -57,27 +56,9 @@ class MaxHistorySizePreferenceDialogFragmentCompat : DatabaseSavePreferenceDialo
val oldMaxHistorySize = database.historyMaxSize
database.historyMaxSize = maxHistorySize
actionInUIThreadAfterSaveDatabase = AfterMaxHistorySizeSave(maxHistorySize, oldMaxHistorySize)
progressDialogThread?.startDatabaseSaveMaxHistorySize(oldMaxHistorySize, maxHistorySize)
}
}
super.onDialogClosed(positiveResult)
}
private inner class AfterMaxHistorySizeSave(private val mNewMaxHistorySize: Long,
private val mOldMaxHistorySize: Long)
: ActionRunnable() {
override fun onFinishRun(result: Result) {
val maxHistorySizeToShow =
if (result.isSuccess) {
mNewMaxHistorySize
} else {
database?.historyMaxSize = mOldMaxHistorySize
mOldMaxHistorySize
}
preference.summary = maxHistorySizeToShow.toString()
}
}
companion object {

View File

@@ -22,7 +22,6 @@ package com.kunzisoft.keepass.settings.preferencedialogfragment
import android.os.Bundle
import android.view.View
import com.kunzisoft.keepass.R
import com.kunzisoft.keepass.tasks.ActionRunnable
class MemoryUsagePreferenceDialogFragmentCompat : DatabaseSavePreferenceDialogFragmentCompat() {
@@ -49,27 +48,9 @@ class MemoryUsagePreferenceDialogFragmentCompat : DatabaseSavePreferenceDialogFr
val oldMemoryUsage = database.memoryUsage
database.memoryUsage = memoryUsage
actionInUIThreadAfterSaveDatabase = AfterMemorySave(memoryUsage, oldMemoryUsage)
progressDialogThread?.startDatabaseSaveMemoryUsage(oldMemoryUsage, memoryUsage)
}
}
super.onDialogClosed(positiveResult)
}
private inner class AfterMemorySave(private val mNewMemory: Long,
private val mOldMemory: Long)
: ActionRunnable() {
override fun onFinishRun(result: Result) {
val memoryToShow =
if (result.isSuccess) {
mNewMemory
} else {
database?.memoryUsage = mOldMemory
mOldMemory
}
preference.summary = memoryToShow.toString()
}
}
companion object {

View File

@@ -22,7 +22,6 @@ package com.kunzisoft.keepass.settings.preferencedialogfragment
import android.os.Bundle
import android.view.View
import com.kunzisoft.keepass.R
import com.kunzisoft.keepass.tasks.ActionRunnable
class ParallelismPreferenceDialogFragmentCompat : DatabaseSavePreferenceDialogFragmentCompat() {
@@ -49,27 +48,9 @@ class ParallelismPreferenceDialogFragmentCompat : DatabaseSavePreferenceDialogFr
val oldParallelism = database.parallelism
database.parallelism = parallelism
actionInUIThreadAfterSaveDatabase = AfterParallelismSave(parallelism, oldParallelism)
progressDialogThread?.startDatabaseSaveParallelism(oldParallelism, parallelism)
}
}
super.onDialogClosed(positiveResult)
}
private inner class AfterParallelismSave(private val mNewParallelism: Int,
private val mOldParallelism: Int)
: ActionRunnable() {
override fun onFinishRun(result: Result) {
val parallelismToShow =
if (result.isSuccess) {
mNewParallelism
} else {
database?.parallelism = mOldParallelism
mOldParallelism
}
preference.summary = parallelismToShow.toString()
}
}
companion object {

View File

@@ -23,7 +23,6 @@ import android.os.Bundle
import android.view.View
import android.widget.Toast
import com.kunzisoft.keepass.R
import com.kunzisoft.keepass.tasks.ActionRunnable
class RoundsPreferenceDialogFragmentCompat : DatabaseSavePreferenceDialogFragmentCompat() {
@@ -55,26 +54,9 @@ class RoundsPreferenceDialogFragmentCompat : DatabaseSavePreferenceDialogFragmen
database.numberKeyEncryptionRounds = Long.MAX_VALUE
}
actionInUIThreadAfterSaveDatabase = AfterRoundSave(rounds, oldRounds)
progressDialogThread?.startDatabaseSaveIterations(oldRounds, rounds)
}
}
super.onDialogClosed(positiveResult)
}
private inner class AfterRoundSave(private val mNewRounds: Long,
private val mOldRounds: Long) : ActionRunnable() {
override fun onFinishRun(result: Result) {
val roundsToShow =
if (result.isSuccess) {
mNewRounds
} else {
database?.numberKeyEncryptionRounds = mOldRounds
mOldRounds
}
preference.summary = roundsToShow.toString()
}
}
companion object {

View File

@@ -106,5 +106,29 @@ abstract class ActionRunnable(private var nestedActionRunnable: ActionRunnable?
data class Result(var isSuccess: Boolean = true,
var message: String? = 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))
}
}
}
}

View File

@@ -21,7 +21,6 @@ package com.kunzisoft.keepass.tasks
import android.annotation.SuppressLint
import android.app.Dialog
import android.content.DialogInterface
import android.os.Bundle
import androidx.annotation.StringRes
import androidx.fragment.app.DialogFragment
@@ -31,8 +30,6 @@ import android.view.View
import android.widget.ProgressBar
import android.widget.TextView
import com.kunzisoft.keepass.R
import com.kunzisoft.keepass.view.lockScreenOrientation
import com.kunzisoft.keepass.view.unlockScreenOrientation
open class ProgressTaskDialogFragment : DialogFragment(), ProgressTaskUpdater {
@@ -77,11 +74,6 @@ open class ProgressTaskDialogFragment : DialogFragment(), ProgressTaskUpdater {
return super.onCreateDialog(savedInstanceState)
}
override fun onDismiss(dialog: DialogInterface) {
activity?.unlockScreenOrientation()
super.onDismiss(dialog)
}
fun setTitle(@StringRes titleId: Int) {
this.title = titleId
}
@@ -116,36 +108,27 @@ open class ProgressTaskDialogFragment : DialogFragment(), ProgressTaskUpdater {
private const val PROGRESS_TASK_DIALOG_TAG = "progressDialogFragment"
private const val UNDEFINED = -1
const val UNDEFINED = -1
fun build(@StringRes titleId: Int,
@StringRes messageId: Int? = null,
@StringRes warningId: Int? = null): ProgressTaskDialogFragment {
// Create an instance of the dialog fragment and show it
val dialog = ProgressTaskDialogFragment()
dialog.updateTitle(titleId)
messageId?.let {
dialog.updateMessage(it)
}
warningId?.let {
dialog.updateWarning(it)
}
return dialog
fun build(): ProgressTaskDialogFragment {
// Create an instance of the dialog fragment
return ProgressTaskDialogFragment()
}
fun start(activity: FragmentActivity,
dialog: ProgressTaskDialogFragment) {
activity.lockScreenOrientation()
activity.runOnUiThread {
dialog.show(activity.supportFragmentManager, PROGRESS_TASK_DIALOG_TAG)
}
}
fun retrieveProgressDialog(activity: FragmentActivity): ProgressTaskDialogFragment? {
return activity.supportFragmentManager
.findFragmentByTag(PROGRESS_TASK_DIALOG_TAG) as ProgressTaskDialogFragment?
}
fun stop(activity: FragmentActivity) {
val fragmentTask = activity.supportFragmentManager.findFragmentByTag(PROGRESS_TASK_DIALOG_TAG)
if (fragmentTask != null) {
val loadingDatabaseDialog = fragmentTask as ProgressTaskDialogFragment
loadingDatabaseDialog.dismissAllowingStateLoss()
activity.unlockScreenOrientation()
}
retrieveProgressDialog(activity)?.dismissAllowingStateLoss()
}
}
}

View File

@@ -1,5 +1,9 @@
package com.kunzisoft.keepass.utils
const val DATABASE_START_TASK_ACTION = "com.kunzisoft.keepass.DATABASE_START_TASK_ACTION"
const val DATABASE_STOP_TASK_ACTION = "com.kunzisoft.keepass.DATABASE_STOP_TASK_ACTION"
const val LOCK_ACTION = "com.kunzisoft.keepass.LOCK"
const val REMOVE_ENTRY_MAGIKEYBOARD_ACTION = "com.kunzisoft.keepass.REMOVE_ENTRY_MAGIKEYBOARD"

View File

@@ -49,19 +49,6 @@ fun Snackbar.asError(): Snackbar {
return this
}
fun Activity.lockScreenOrientation() {
val currentOrientation = resources.configuration.orientation
requestedOrientation = if (currentOrientation == Configuration.ORIENTATION_PORTRAIT) {
ActivityInfo.SCREEN_ORIENTATION_PORTRAIT
} else {
ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE
}
}
fun Activity.unlockScreenOrientation() {
requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED
}
fun Toolbar.collapse(animate: Boolean = true) {
val recordBarHeight = layoutParams.height
val slideAnimator = ValueAnimator.ofInt(height, 0)

View File

@@ -138,6 +138,7 @@
<string name="list_groups_show_number_entries_summary">Show the number of entries in a group</string>
<string name="list_size_title">Size of list items</string>
<string name="list_size_summary">Text size in the element list</string>
<string name="creating_database">Creating database…</string>
<string name="loading_database">Loading database…</string>
<string name="lowercase">Lower-case</string>
<string name="maskpass_title">Hide passwords</string>