Move temp entry in fragment

This commit is contained in:
J-Jamet
2020-09-15 16:50:04 +02:00
parent d157fea9be
commit 07af9f36b2
6 changed files with 131 additions and 134 deletions

View File

@@ -309,7 +309,7 @@ class EntryActivity : LockingActivity() {
entryContentsView?.assignNotes(entry.notes) entryContentsView?.assignNotes(entry.notes)
// Assign custom fields // Assign custom fields
if (entry.allowCustomFields()) { if (mDatabase?.allowEntryCustomFields() == true) {
entryContentsView?.clearExtraFields() entryContentsView?.clearExtraFields()
for ((label, value) in entry.customFields) { for ((label, value) in entry.customFields) {
val allowCopyProtectedField = !value.isProtected || allowCopyPasswordAndProtectedFields val allowCopyProtectedField = !value.isProtected || allowCopyPasswordAndProtectedFields

View File

@@ -45,11 +45,13 @@ import com.kunzisoft.keepass.activities.lock.LockingActivity
import com.kunzisoft.keepass.database.element.* import com.kunzisoft.keepass.database.element.*
import com.kunzisoft.keepass.database.element.icon.IconImage import com.kunzisoft.keepass.database.element.icon.IconImage
import com.kunzisoft.keepass.database.element.icon.IconImageStandard import com.kunzisoft.keepass.database.element.icon.IconImageStandard
import com.kunzisoft.keepass.database.element.node.Node
import com.kunzisoft.keepass.database.element.node.NodeId import com.kunzisoft.keepass.database.element.node.NodeId
import com.kunzisoft.keepass.education.EntryEditActivityEducation import com.kunzisoft.keepass.education.EntryEditActivityEducation
import com.kunzisoft.keepass.model.* import com.kunzisoft.keepass.model.*
import com.kunzisoft.keepass.notifications.AttachmentFileNotificationService import com.kunzisoft.keepass.notifications.AttachmentFileNotificationService
import com.kunzisoft.keepass.notifications.ClipboardEntryNotificationService import com.kunzisoft.keepass.notifications.ClipboardEntryNotificationService
import com.kunzisoft.keepass.notifications.DatabaseTaskNotificationService
import com.kunzisoft.keepass.notifications.DatabaseTaskNotificationService.Companion.ACTION_DATABASE_CREATE_ENTRY_TASK 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.notifications.DatabaseTaskNotificationService.Companion.ACTION_DATABASE_UPDATE_ENTRY_TASK
import com.kunzisoft.keepass.notifications.KeyboardEntryNotificationService import com.kunzisoft.keepass.notifications.KeyboardEntryNotificationService
@@ -82,8 +84,6 @@ class EntryEditActivity : LockingActivity(),
// Refs of an entry and group in database, are not modifiable // Refs of an entry and group in database, are not modifiable
private var mEntry: Entry? = null private var mEntry: Entry? = null
private var mParent: Group? = null private var mParent: Group? = null
// New or copy of mEntry in the database to be modifiable
private var mNewEntry: Entry? = null
private var mIsNew: Boolean = false private var mIsNew: Boolean = false
// Views // Views
@@ -132,6 +132,8 @@ class EntryEditActivity : LockingActivity(),
// Likely the app has been killed exit the activity // Likely the app has been killed exit the activity
mDatabase = Database.getInstance() mDatabase = Database.getInstance()
var tempEntry: Entry? = null
// Entry is retrieve, it's an entry to update // Entry is retrieve, it's an entry to update
intent.getParcelableExtra<NodeId<UUID>>(KEY_ENTRY)?.let { intent.getParcelableExtra<NodeId<UUID>>(KEY_ENTRY)?.let {
mIsNew = false mIsNew = false
@@ -148,14 +150,11 @@ class EntryEditActivity : LockingActivity(),
} }
} }
// Create the new entry from the current one mEntry?.let { entry ->
if (savedInstanceState?.containsKey(KEY_NEW_ENTRY) != true) { // Create a copy to modify
mEntry?.let { entry -> tempEntry = Entry(entry).also { newEntry ->
// Create a copy to modify // WARNING Remove the parent to keep memory with parcelable
mNewEntry = Entry(entry).also { newEntry -> newEntry.removeParent()
// WARNING Remove the parent to keep memory with parcelable
newEntry.removeParent()
}
} }
} }
} }
@@ -163,52 +162,32 @@ class EntryEditActivity : LockingActivity(),
// Parent is retrieve, it's a new entry to create // Parent is retrieve, it's a new entry to create
intent.getParcelableExtra<NodeId<*>>(KEY_PARENT)?.let { intent.getParcelableExtra<NodeId<*>>(KEY_PARENT)?.let {
mIsNew = true mIsNew = true
// Create an empty new entry
if (savedInstanceState?.containsKey(KEY_NEW_ENTRY) != true) {
mNewEntry = mDatabase?.createEntry()
}
mParent = mDatabase?.getGroupById(it) mParent = mDatabase?.getGroupById(it)
// Add the default icon from parent if not a folder // Add the default icon from parent if not a folder
val parentIcon = mParent?.icon val parentIcon = mParent?.icon
if (parentIcon != null if (parentIcon != null
&& parentIcon.iconId != IconImage.UNKNOWN_ID && parentIcon.iconId != IconImage.UNKNOWN_ID
&& parentIcon.iconId != IconImageStandard.FOLDER) { && parentIcon.iconId != IconImageStandard.FOLDER) {
temporarilySaveAndShowSelectedIcon(parentIcon) tempEntry?.icon = parentIcon
} else {
mDatabase?.drawFactory?.let { iconFactory ->
entryEditContentsFragment?.setDefaultIcon(iconFactory)
}
} }
tempEntry = mDatabase?.createEntry()
} }
// Retrieve the new entry after an orientation change // Build fragment to manage entry modification
if (savedInstanceState?.containsKey(KEY_NEW_ENTRY) == true) {
mNewEntry = savedInstanceState.getParcelable(KEY_NEW_ENTRY)
}
if (savedInstanceState?.containsKey(TEMP_ATTACHMENTS) == true) {
mTempAttachments = savedInstanceState.getParcelableArrayList(TEMP_ATTACHMENTS) ?: mTempAttachments
}
// Close the activity if entry or parent can't be retrieve
if (mNewEntry == null || mParent == null) {
finish()
return
}
entryEditContentsFragment = supportFragmentManager.findFragmentByTag("entry_edit_contents") as? EntryEditContentsFragment? entryEditContentsFragment = supportFragmentManager.findFragmentByTag("entry_edit_contents") as? EntryEditContentsFragment?
if (entryEditContentsFragment == null) { if (entryEditContentsFragment == null) {
entryEditContentsFragment = EntryEditContentsFragment() entryEditContentsFragment = EntryEditContentsFragment()
supportFragmentManager.beginTransaction() }
.replace(R.id.entry_edit_contents, entryEditContentsFragment!!, "entry_edit_contents") supportFragmentManager.beginTransaction()
.commit() .replace(R.id.entry_edit_contents, entryEditContentsFragment!!, "entry_edit_contents")
.commit()
mDatabase?.let { database ->
entryEditContentsFragment?.setDatabase(database)
}
tempEntry?.let {
entryEditContentsFragment?.setEntry(it, mIsNew)
} }
entryEditContentsFragment?.apply { entryEditContentsFragment?.apply {
mDatabase?.let { database ->
mNewEntry?.let { newEntry ->
entryEditContentsFragment?.setEntry(database, newEntry, mIsNew)
}
}
applyFontVisibilityToFields(PreferencesUtil.fieldFontIsInVisibility(this@EntryEditActivity)) applyFontVisibilityToFields(PreferencesUtil.fieldFontIsInVisibility(this@EntryEditActivity))
setOnDateClickListener = View.OnClickListener { setOnDateClickListener = View.OnClickListener {
expiresDate.date.let { expiresDate -> expiresDate.date.let { expiresDate ->
@@ -235,6 +214,11 @@ class EntryEditActivity : LockingActivity(),
} }
} }
// Retrieve temp attachments in case of deletion
if (savedInstanceState?.containsKey(TEMP_ATTACHMENTS) == true) {
mTempAttachments = savedInstanceState.getParcelableArrayList(TEMP_ATTACHMENTS) ?: mTempAttachments
}
// Assign title // Assign title
title = if (mIsNew) getString(R.string.add_entry) else getString(R.string.edit_entry) title = if (mIsNew) getString(R.string.add_entry) else getString(R.string.edit_entry)
@@ -244,7 +228,7 @@ class EntryEditActivity : LockingActivity(),
menuInflater.inflate(R.menu.entry_edit, menu) menuInflater.inflate(R.menu.entry_edit, menu)
menu.findItem(R.id.menu_add_field).apply { menu.findItem(R.id.menu_add_field).apply {
val allowCustomField = mNewEntry?.allowCustomFields() == true val allowCustomField = mDatabase?.allowEntryCustomFields() == true
isEnabled = allowCustomField isEnabled = allowCustomField
isVisible = allowCustomField isVisible = allowCustomField
} }
@@ -296,8 +280,22 @@ class EntryEditActivity : LockingActivity(),
when (actionTask) { when (actionTask) {
ACTION_DATABASE_CREATE_ENTRY_TASK, ACTION_DATABASE_CREATE_ENTRY_TASK,
ACTION_DATABASE_UPDATE_ENTRY_TASK -> { ACTION_DATABASE_UPDATE_ENTRY_TASK -> {
if (result.isSuccess) try {
finish() if (result.isSuccess) {
var newNodes: List<Node> = ArrayList()
result.data?.getBundle(DatabaseTaskNotificationService.NEW_NODES_KEY)?.let { newNodesBundle ->
mDatabase?.let { database ->
newNodes = DatabaseTaskNotificationService.getListNodesFromBundle(database, newNodesBundle)
}
}
if (newNodes.size == 1) {
mEntry = newNodes[0] as Entry?
finish()
}
}
} catch (e: Exception) {
Log.e(TAG, "Unable to retrieve entry after database action", e)
}
} }
} }
coordinatorLayout?.showActionError(result) coordinatorLayout?.showActionError(result)
@@ -366,13 +364,6 @@ class EntryEditActivity : LockingActivity(),
super.onPause() super.onPause()
} }
private fun temporarilySaveAndShowSelectedIcon(icon: IconImage) {
mNewEntry?.icon = icon
mDatabase?.drawFactory?.let { iconDrawFactory ->
entryEditContentsFragment?.setIcon(iconDrawFactory, icon)
}
}
/** /**
* Open the password generator fragment * Open the password generator fragment
*/ */
@@ -483,47 +474,40 @@ class EntryEditActivity : LockingActivity(),
* Saves the new entry or update an existing entry in the database * Saves the new entry or update an existing entry in the database
*/ */
private fun saveEntry() { private fun saveEntry() {
// Launch a validation and show the error if present // Get the temp entry
if (entryEditContentsFragment?.isValid() == true) { entryEditContentsFragment?.getEntry()?.let { newEntry ->
// Clone the entry
mNewEntry?.let { newEntry ->
// WARNING Add the parent previously deleted // WARNING Add the parent previously deleted
newEntry.parent = mEntry?.parent newEntry.parent = mEntry?.parent
// Build info // Build info
newEntry.lastAccessTime = DateInstant() newEntry.lastAccessTime = DateInstant()
newEntry.lastModificationTime = DateInstant() newEntry.lastModificationTime = DateInstant()
mDatabase?.let { database -> // Delete temp attachment if not used
entryEditContentsFragment?.populateEntryWithViews(database, newEntry) mTempAttachments.forEach {
} mDatabase?.binaryPool?.let { binaryPool ->
if (!newEntry.getAttachments(binaryPool).contains(it)) {
// Delete temp attachment if not used mDatabase?.removeAttachmentIfNotUsed(it)
mTempAttachments.forEach {
mDatabase?.binaryPool?.let { binaryPool ->
if (!newEntry.getAttachments(binaryPool).contains(it)) {
mDatabase?.removeAttachmentIfNotUsed(it)
}
} }
} }
}
// Open a progress dialog and save entry // Open a progress dialog and save entry
if (mIsNew) { if (mIsNew) {
mParent?.let { parent -> mParent?.let { parent ->
mProgressDatabaseTaskProvider?.startDatabaseCreateEntry( mProgressDatabaseTaskProvider?.startDatabaseCreateEntry(
newEntry, newEntry,
parent, parent,
!mReadOnly && mAutoSaveEnable !mReadOnly && mAutoSaveEnable
) )
} }
} else { } else {
mEntry?.let { oldEntry -> mEntry?.let { oldEntry ->
mProgressDatabaseTaskProvider?.startDatabaseUpdateEntry( mProgressDatabaseTaskProvider?.startDatabaseUpdateEntry(
oldEntry, oldEntry,
newEntry, newEntry,
!mReadOnly && mAutoSaveEnable !mReadOnly && mAutoSaveEnable
) )
}
} }
} }
} }
@@ -559,8 +543,9 @@ class EntryEditActivity : LockingActivity(),
) )
if (!generatePasswordEducationPerformed) { if (!generatePasswordEducationPerformed) {
val addNewFieldView: View? = entryEditAddToolBar?.findViewById(R.id.menu_add_field) val addNewFieldView: View? = entryEditAddToolBar?.findViewById(R.id.menu_add_field)
val addNewFieldEducationPerformed = mNewEntry != null val addNewFieldEducationPerformed = // TODO mNewEntry != null
&& mNewEntry!!.allowCustomFields() && addNewFieldView != null // && mNewEntry!!.allowCustomFields()
addNewFieldView != null
&& addNewFieldView.visibility == View.VISIBLE && addNewFieldView.visibility == View.VISIBLE
&& entryEditActivityEducation.checkAndPerformedEntryNewFieldEducation( && entryEditActivityEducation.checkAndPerformedEntryNewFieldEducation(
addNewFieldView, addNewFieldView,
@@ -629,7 +614,7 @@ class EntryEditActivity : LockingActivity(),
override fun iconPicked(bundle: Bundle) { override fun iconPicked(bundle: Bundle) {
IconPickerDialogFragment.getIconStandardFromBundle(bundle)?.let { icon -> IconPickerDialogFragment.getIconStandardFromBundle(bundle)?.let { icon ->
temporarilySaveAndShowSelectedIcon(icon) entryEditContentsFragment?.icon = icon
} }
} }
@@ -667,12 +652,6 @@ class EntryEditActivity : LockingActivity(),
} }
override fun onSaveInstanceState(outState: Bundle) { override fun onSaveInstanceState(outState: Bundle) {
mNewEntry?.let { newEntry ->
mDatabase?.let { database ->
entryEditContentsFragment?.populateEntryWithViews(database, newEntry)
}
outState.putParcelable(KEY_NEW_ENTRY, newEntry)
}
outState.putParcelableArrayList(TEMP_ATTACHMENTS, mTempAttachments) outState.putParcelableArrayList(TEMP_ATTACHMENTS, mTempAttachments)
@@ -705,10 +684,10 @@ class EntryEditActivity : LockingActivity(),
override fun finish() { override fun finish() {
// Assign entry callback as a result in all case // Assign entry callback as a result in all case
try { try {
mNewEntry?.let { mEntry?.let { entry ->
val bundle = Bundle() val bundle = Bundle()
val intentEntry = Intent() val intentEntry = Intent()
bundle.putParcelable(ADD_OR_UPDATE_ENTRY_KEY, mNewEntry) bundle.putParcelable(ADD_OR_UPDATE_ENTRY_KEY, entry)
intentEntry.putExtras(bundle) intentEntry.putExtras(bundle)
if (mIsNew) { if (mIsNew) {
setResult(ADD_ENTRY_RESULT_CODE, intentEntry) setResult(ADD_ENTRY_RESULT_CODE, intentEntry)
@@ -732,7 +711,6 @@ class EntryEditActivity : LockingActivity(),
const val KEY_PARENT = "parent" const val KEY_PARENT = "parent"
// SaveInstanceState // SaveInstanceState
const val KEY_NEW_ENTRY = "new_entry"
const val TEMP_ATTACHMENTS = "TEMP_ATTACHMENTS" const val TEMP_ATTACHMENTS = "TEMP_ATTACHMENTS"
// Keys for callback // Keys for callback

View File

@@ -43,9 +43,8 @@ import com.kunzisoft.keepass.database.element.Database
import com.kunzisoft.keepass.database.element.DateInstant import com.kunzisoft.keepass.database.element.DateInstant
import com.kunzisoft.keepass.database.element.Entry import com.kunzisoft.keepass.database.element.Entry
import com.kunzisoft.keepass.database.element.icon.IconImage import com.kunzisoft.keepass.database.element.icon.IconImage
import com.kunzisoft.keepass.icons.IconDrawableFactory import com.kunzisoft.keepass.database.element.icon.IconImageStandard
import com.kunzisoft.keepass.icons.assignDatabaseIcon import com.kunzisoft.keepass.icons.assignDatabaseIcon
import com.kunzisoft.keepass.icons.assignDefaultDatabaseIcon
import com.kunzisoft.keepass.model.EntryAttachmentState import com.kunzisoft.keepass.model.EntryAttachmentState
import com.kunzisoft.keepass.model.Field import com.kunzisoft.keepass.model.Field
import com.kunzisoft.keepass.model.StreamDirection import com.kunzisoft.keepass.model.StreamDirection
@@ -86,6 +85,7 @@ class EntryEditContentsFragment: StylishFragment() {
var setOnEditCustomField: ((Field) -> Unit)? = null var setOnEditCustomField: ((Field) -> Unit)? = null
var setOnRemoveAttachment: ((Attachment) -> Unit)? = null var setOnRemoveAttachment: ((Attachment) -> Unit)? = null
// Elements to modify the current entry
private var mDatabase: Database? = null private var mDatabase: Database? = null
private var mEntry: Entry? = null private var mEntry: Entry? = null
private var mIsNewEntry = true private var mIsNewEntry = true
@@ -148,6 +148,11 @@ class EntryEditContentsFragment: StylishFragment() {
iconColor = taIconColor?.getColor(0, Color.WHITE) ?: Color.WHITE iconColor = taIconColor?.getColor(0, Color.WHITE) ?: Color.WHITE
taIconColor?.recycle() taIconColor?.recycle()
// Retrieve the new entry after an orientation change
if (savedInstanceState?.containsKey(KEY_TEMP_ENTRY) == true) {
mEntry = savedInstanceState.getParcelable(KEY_TEMP_ENTRY)
}
mDatabase?.let { database -> mDatabase?.let { database ->
mEntry?.let { entry -> mEntry?.let { entry ->
populateViewsWithEntry(database, entry, mIsNewEntry) populateViewsWithEntry(database, entry, mIsNewEntry)
@@ -157,8 +162,20 @@ class EntryEditContentsFragment: StylishFragment() {
return rootView return rootView
} }
fun setEntry(database: Database, entry: Entry, isNewEntry: Boolean) { fun setDatabase(database: Database) {
mDatabase = database mDatabase = database
}
fun getEntry(): Entry? {
mDatabase?.let { database ->
mEntry?.let { entry ->
populateEntryWithViews(database, entry)
}
}
return mEntry
}
fun setEntry(entry: Entry, isNewEntry: Boolean) {
mEntry = entry mEntry = entry
mIsNewEntry = isNewEntry mIsNewEntry = isNewEntry
} }
@@ -168,7 +185,7 @@ class EntryEditContentsFragment: StylishFragment() {
database.stopManageEntry(entry) database.stopManageEntry(entry)
// Set info in view // Set info in view
setIcon(database.drawFactory, entry.icon) icon = entry.icon
title = entry.title title = entry.title
username = if (isNewEntry && entry.username.isEmpty()) username = if (isNewEntry && entry.username.isEmpty())
database.defaultUsername database.defaultUsername
@@ -191,7 +208,7 @@ class EntryEditContentsFragment: StylishFragment() {
} }
} }
fun populateEntryWithViews(database: Database, newEntry: Entry) { private fun populateEntryWithViews(database: Database, newEntry: Entry) {
database.startManageEntry(newEntry) database.startManageEntry(newEntry)
@@ -236,13 +253,16 @@ class EntryEditContentsFragment: StylishFragment() {
entryTitleView.applyFontVisibility() entryTitleView.applyFontVisibility()
} }
fun setDefaultIcon(iconFactory: IconDrawableFactory) { var icon: IconImage
entryIconView.assignDefaultDatabaseIcon(iconFactory, iconColor) get() {
} return mEntry?.icon ?: IconImageStandard()
}
fun setIcon(iconFactory: IconDrawableFactory, icon: IconImage) { set(value) {
entryIconView.assignDatabaseIcon(iconFactory, icon, iconColor) mEntry?.icon = value
} mDatabase?.drawFactory?.let { drawFactory ->
entryIconView.assignDatabaseIcon(drawFactory, value, iconColor)
}
}
var username: String var username: String
get() { get() {
@@ -492,14 +512,19 @@ class EntryEditContentsFragment: StylishFragment() {
}, 250) }, 250)
} }
/** override fun onSaveInstanceState(outState: Bundle) {
* Validate or not the entry form mEntry?.let { entry ->
* mDatabase?.let { database ->
* @return ErrorValidation An error with a message or a validation without message populateEntryWithViews(database, entry)
*/ }
fun isValid(): Boolean { outState.putParcelable(KEY_TEMP_ENTRY, entry)
// TODO }
return true
super.onSaveInstanceState(outState)
}
companion object {
const val KEY_TEMP_ENTRY = "KEY_TEMP_ENTRY"
} }
} }

View File

@@ -834,6 +834,13 @@ class Database {
} }
} }
/**
* @return true if database allows custom field
*/
fun allowEntryCustomFields(): Boolean {
return mDatabaseKDBX != null
}
/** /**
* Remove oldest history for each entry if more than max items or max memory * Remove oldest history for each entry if more than max items or max memory
*/ */

View File

@@ -290,14 +290,6 @@ class Entry : Node, EntryVersionedInterface<Group> {
val customFields: HashMap<String, ProtectedString> val customFields: HashMap<String, ProtectedString>
get() = entryKDBX?.customFields ?: HashMap() get() = entryKDBX?.customFields ?: HashMap()
/**
* To redefine if version of entry allow custom field,
* @return true if entry allows custom field
*/
fun allowCustomFields(): Boolean {
return entryKDBX?.allowCustomFields() ?: false
}
fun removeAllFields() { fun removeAllFields() {
entryKDBX?.removeAllFields() entryKDBX?.removeAllFields()
} }

View File

@@ -38,7 +38,6 @@ import com.kunzisoft.keepass.utils.ParcelableUtil
import com.kunzisoft.keepass.utils.UnsignedLong import com.kunzisoft.keepass.utils.UnsignedLong
import java.util.* import java.util.*
import kotlin.collections.ArrayList import kotlin.collections.ArrayList
import kotlin.collections.HashSet
import kotlin.collections.LinkedHashMap import kotlin.collections.LinkedHashMap
class EntryKDBX : EntryVersioned<UUID, UUID, GroupKDBX, EntryKDBX>, NodeKDBXInterface { class EntryKDBX : EntryVersioned<UUID, UUID, GroupKDBX, EntryKDBX>, NodeKDBXInterface {
@@ -272,10 +271,6 @@ class EntryKDBX : EntryVersioned<UUID, UUID, GroupKDBX, EntryKDBX>, NodeKDBXInte
return field return field
} }
fun allowCustomFields(): Boolean {
return true
}
fun removeAllFields() { fun removeAllFields() {
fields.clear() fields.clear()
} }