mirror of
https://github.com/Kunzisoft/KeePassDX.git
synced 2025-12-04 15:49:33 +01:00
Merge branch 'feature/Loading_Database' into develop
This commit is contained in:
@@ -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)
|
||||
|
||||
@@ -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,53 +249,50 @@ 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 ->
|
||||
mNewEntry?.let { newEntry ->
|
||||
|
||||
// WARNING Add the parent previously deleted
|
||||
newEntry.parent = mEntry?.parent
|
||||
// Build info
|
||||
newEntry.lastAccessTime = PwDate()
|
||||
newEntry.lastModificationTime = PwDate()
|
||||
// WARNING Add the parent previously deleted
|
||||
newEntry.parent = mEntry?.parent
|
||||
// Build info
|
||||
newEntry.lastAccessTime = PwDate()
|
||||
newEntry.lastModificationTime = PwDate()
|
||||
|
||||
populateEntryWithViews(newEntry)
|
||||
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()
|
||||
}
|
||||
// Open a progress dialog and save entry
|
||||
if (mIsNew) {
|
||||
mParent?.let { parent ->
|
||||
progressDialogThread?.startDatabaseCreateEntry(
|
||||
newEntry,
|
||||
parent,
|
||||
!mReadOnly
|
||||
)
|
||||
}
|
||||
if (mIsNew) {
|
||||
mParent?.let { parent ->
|
||||
actionRunnable = AddEntryRunnable(this@EntryEditActivity,
|
||||
database,
|
||||
newEntry,
|
||||
parent,
|
||||
afterActionNodeFinishRunnable,
|
||||
!mReadOnly)
|
||||
}
|
||||
|
||||
} else {
|
||||
mEntry?.let { oldEntry ->
|
||||
actionRunnable = UpdateEntryRunnable(this@EntryEditActivity,
|
||||
database,
|
||||
oldEntry,
|
||||
newEntry,
|
||||
afterActionNodeFinishRunnable,
|
||||
!mReadOnly)
|
||||
}
|
||||
}
|
||||
actionRunnable?.let { runnable ->
|
||||
ProgressDialogSaveDatabaseThread(this@EntryEditActivity) { runnable }.start()
|
||||
} else {
|
||||
mEntry?.let { oldEntry ->
|
||||
progressDialogThread?.startDatabaseUpdateEntry(
|
||||
oldEntry,
|
||||
newEntry,
|
||||
!mReadOnly
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
|
||||
progressDialogThread?.registerProgressTask()
|
||||
}
|
||||
|
||||
override fun onPause() {
|
||||
progressDialogThread?.unregisterProgressTask()
|
||||
|
||||
super.onPause()
|
||||
}
|
||||
|
||||
override fun onCreateOptionsMenu(menu: Menu): Boolean {
|
||||
super.onCreateOptionsMenu(menu)
|
||||
|
||||
|
||||
@@ -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,
|
||||
databaseUri,
|
||||
Database.getInstance(),
|
||||
masterPasswordChecked,
|
||||
masterPassword,
|
||||
keyFileChecked,
|
||||
keyFile,
|
||||
true, // TODO get readonly
|
||||
LaunchGroupActivityFinish(databaseUri, keyFile)
|
||||
)
|
||||
},
|
||||
R.string.progress_create)
|
||||
.start()
|
||||
progressDialogThread?.startDatabaseCreate(
|
||||
databaseUri,
|
||||
masterPasswordChecked,
|
||||
masterPassword,
|
||||
keyFileChecked,
|
||||
keyFile
|
||||
)
|
||||
}
|
||||
} 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?) {
|
||||
|
||||
@@ -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(),
|
||||
nodes,
|
||||
newParent,
|
||||
AfterAddNodeRunnable(),
|
||||
!mReadOnly)
|
||||
}.start()
|
||||
progressDialogThread?.startDatabaseCopyNodes(
|
||||
nodes,
|
||||
newParent,
|
||||
!mReadOnly
|
||||
)
|
||||
}
|
||||
}
|
||||
ListNodesFragment.PasteMode.PASTE_FROM_MOVE -> {
|
||||
// Move
|
||||
mCurrentGroup?.let { newParent ->
|
||||
ProgressDialogSaveDatabaseThread(this) {
|
||||
MoveNodesRunnable(
|
||||
this,
|
||||
Database.getInstance(),
|
||||
nodes,
|
||||
newParent,
|
||||
AfterAddNodeRunnable(),
|
||||
!mReadOnly)
|
||||
}.start()
|
||||
progressDialogThread?.startDatabaseMoveNodes(
|
||||
nodes,
|
||||
newParent,
|
||||
!mReadOnly
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -516,14 +573,10 @@ class GroupActivity : LockingActivity(),
|
||||
}
|
||||
|
||||
override fun onDeleteMenuClick(nodes: List<NodeVersioned>): Boolean {
|
||||
ProgressDialogSaveDatabaseThread(this) {
|
||||
DeleteNodesRunnable(
|
||||
this,
|
||||
Database.getInstance(),
|
||||
nodes,
|
||||
AfterDeleteNodeRunnable(),
|
||||
!mReadOnly)
|
||||
}.start()
|
||||
progressDialogThread?.startDatabaseDeleteNodes(
|
||||
nodes,
|
||||
!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,104 +734,41 @@ 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 -> {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
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,
|
||||
databaseUri,
|
||||
password,
|
||||
keyFile,
|
||||
this@PasswordActivity.contentResolver,
|
||||
this@PasswordActivity.filesDir,
|
||||
SearchDbHelper(PreferencesUtil.omitBackup(this@PasswordActivity)),
|
||||
fixDuplicateUUID,
|
||||
progressTaskUpdater,
|
||||
onFinishLoadDatabase)
|
||||
},
|
||||
R.string.loading_database).start()
|
||||
readOnly: Boolean,
|
||||
cipherDatabaseEntity: CipherDatabaseEntity?,
|
||||
fixDuplicateUUID: Boolean) {
|
||||
progressDialogThread?.startDatabaseLoad(
|
||||
databaseUri,
|
||||
password,
|
||||
keyFile,
|
||||
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
|
||||
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -111,30 +111,40 @@ class AdvancedUnlockedManager(var context: FragmentActivity,
|
||||
override fun onAuthenticationError(
|
||||
errorCode: Int,
|
||||
errString: CharSequence) {
|
||||
Log.e(TAG, "Biometric authentication error. Code : $errorCode Error : $errString")
|
||||
setAdvancedUnlockedMessageView(errString.toString())
|
||||
context.runOnUiThread {
|
||||
Log.e(TAG, "Biometric authentication error. Code : $errorCode Error : $errString")
|
||||
setAdvancedUnlockedMessageView(errString.toString())
|
||||
}
|
||||
}
|
||||
|
||||
override fun onAuthenticationFailed() {
|
||||
Log.e(TAG, "Biometric authentication failed, biometric not recognized")
|
||||
setAdvancedUnlockedMessageView(R.string.biometric_not_recognized)
|
||||
context.runOnUiThread {
|
||||
Log.e(TAG, "Biometric authentication failed, biometric not recognized")
|
||||
setAdvancedUnlockedMessageView(R.string.biometric_not_recognized)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onAuthenticationSucceeded(result: BiometricPrompt.AuthenticationResult) {
|
||||
when (biometricMode) {
|
||||
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())
|
||||
}
|
||||
Mode.OPEN -> {
|
||||
// retrieve the encrypted value from preferences
|
||||
cipherDatabaseAction.getCipherDatabase(databaseFileUri) {
|
||||
it?.encryptedValue?.let { value ->
|
||||
biometricUnlockDatabaseHelper?.decryptData(value)
|
||||
context.runOnUiThread {
|
||||
when (biometricMode) {
|
||||
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())
|
||||
}
|
||||
Mode.OPEN -> {
|
||||
// retrieve the encrypted value from preferences
|
||||
cipherDatabaseAction.getCipherDatabase(databaseFileUri) {
|
||||
it?.encryptedValue?.let { value ->
|
||||
biometricUnlockDatabaseHelper?.decryptData(value)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -19,36 +19,65 @@
|
||||
*/
|
||||
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)
|
||||
finishRun(true)
|
||||
|
||||
// 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)
|
||||
|
||||
@@ -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)
|
||||
@@ -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
|
||||
|
||||
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
|
||||
|
||||
private var intentDatabaseTask:Intent = Intent(activity, DatabaseTaskNotificationService::class.java)
|
||||
|
||||
init {
|
||||
actionRunnableAsyncTask = ActionRunnableAsyncTask(progressTaskDialogFragment,
|
||||
{
|
||||
activity.runOnUiThread {
|
||||
intentDatabaseTask.putExtra(DATABASE_TASK_TITLE_KEY, titleId)
|
||||
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)
|
||||
}
|
||||
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
|
||||
|
||||
|
||||
private class ActionRunnableAsyncTask(private val progressTaskUpdater: ProgressTaskUpdater,
|
||||
private val onPreExecute: () -> Unit,
|
||||
private val onPostExecute: (result: ActionRunnable.Result) -> Unit)
|
||||
: AsyncTask<((ProgressTaskUpdater?)-> ActionRunnable), Void, ActionRunnable.Result>() {
|
||||
class ProgressDialogThread(private val activity: FragmentActivity,
|
||||
var onActionFinish: (actionTask: String,
|
||||
result: ActionRunnable.Result) -> Unit) {
|
||||
|
||||
override fun onPreExecute() {
|
||||
super.onPreExecute()
|
||||
onPreExecute.invoke()
|
||||
private var intentDatabaseTask = Intent(activity, DatabaseTaskNotificationService::class.java)
|
||||
|
||||
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 doInBackground(vararg actionRunnables: ((ProgressTaskUpdater?)-> ActionRunnable)?): ActionRunnable.Result {
|
||||
var resultTask = ActionRunnable.Result(false)
|
||||
actionRunnables.forEach {
|
||||
it?.invoke(progressTaskUpdater)?.apply {
|
||||
run()
|
||||
resultTask = result
|
||||
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
|
||||
}
|
||||
}
|
||||
return resultTask
|
||||
}
|
||||
|
||||
override fun onPostExecute(result: ActionRunnable.Result) {
|
||||
super.onPostExecute(result)
|
||||
onPostExecute.invoke(result)
|
||||
}
|
||||
}
|
||||
|
||||
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 {
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
----
|
||||
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)
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
, ACTION_DATABASE_ASSIGN_PASSWORD_TASK)
|
||||
}
|
||||
|
||||
/*
|
||||
----
|
||||
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)
|
||||
}
|
||||
}
|
||||
@@ -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 {
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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>()
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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))))
|
||||
}
|
||||
}
|
||||
@@ -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(
|
||||
|
||||
@@ -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()
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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()
|
||||
|
||||
|
||||
@@ -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 = ""
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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?) {
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -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"
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
dialog.show(activity.supportFragmentManager, PROGRESS_TASK_DIALOG_TAG)
|
||||
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()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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"
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user