diff --git a/CHANGELOG b/CHANGELOG index 0cea38fa2..854659cef 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -6,6 +6,7 @@ KeePassDX(2.9.13) * Better UI #876 * Fix themes and add Purple Dark #889 * Allow OTP with many padding #585 + * Add notes in groups #734 KeePassDX(2.9.12) * Fix OTP token type #863 diff --git a/app/src/main/java/com/kunzisoft/keepass/activities/GroupActivity.kt b/app/src/main/java/com/kunzisoft/keepass/activities/GroupActivity.kt index 48bb26c59..032abd639 100644 --- a/app/src/main/java/com/kunzisoft/keepass/activities/GroupActivity.kt +++ b/app/src/main/java/com/kunzisoft/keepass/activities/GroupActivity.kt @@ -57,13 +57,13 @@ import com.kunzisoft.keepass.database.element.Database import com.kunzisoft.keepass.database.element.Entry import com.kunzisoft.keepass.database.element.Group import com.kunzisoft.keepass.database.element.SortNodeEnum -import com.kunzisoft.keepass.database.element.icon.IconImage import com.kunzisoft.keepass.database.element.node.Node import com.kunzisoft.keepass.database.element.node.NodeId import com.kunzisoft.keepass.database.element.node.Type import com.kunzisoft.keepass.database.search.SearchHelper import com.kunzisoft.keepass.education.GroupActivityEducation import com.kunzisoft.keepass.icons.assignDatabaseIcon +import com.kunzisoft.keepass.model.GroupInfo import com.kunzisoft.keepass.model.RegisterInfo import com.kunzisoft.keepass.model.SearchInfo import com.kunzisoft.keepass.services.DatabaseTaskNotificationService.Companion.ACTION_DATABASE_COPY_NODES_TASK @@ -750,7 +750,7 @@ class GroupActivity : LockingActivity(), when (node.type) { Type.GROUP -> { mOldGroupToUpdate = node as Group - GroupEditDialogFragment.build(mOldGroupToUpdate!!) + GroupEditDialogFragment.build(mOldGroupToUpdate!!.getGroupInfo()) .show(supportFragmentManager, GroupEditDialogFragment.TAG_CREATE_GROUP) } @@ -1036,19 +1036,17 @@ class GroupActivity : LockingActivity(), } } - override fun approveEditGroup(action: GroupEditDialogFragment.EditGroupDialogAction?, - name: String?, - icon: IconImage?) { + override fun approveEditGroup(action: GroupEditDialogFragment.EditGroupDialogAction, + groupInfo: GroupInfo) { - if (name != null && name.isNotEmpty() && icon != null) { + if (groupInfo.name.isNotEmpty()) { when (action) { GroupEditDialogFragment.EditGroupDialogAction.CREATION -> { // If group creation mCurrentGroup?.let { currentGroup -> // Build the group mDatabase?.createGroup()?.let { newGroup -> - newGroup.title = name - newGroup.icon = icon + newGroup.setGroupInfo(groupInfo) // Not really needed here because added in runnable but safe newGroup.parent = currentGroup @@ -1068,9 +1066,7 @@ class GroupActivity : LockingActivity(), // WARNING remove parent and children to keep memory removeParent() removeChildren() - - title = name - this.icon = icon // TODO custom icon #96 + this.setGroupInfo(groupInfo) } } // If group updated save it in the database @@ -1086,9 +1082,8 @@ class GroupActivity : LockingActivity(), } } - override fun cancelEditGroup(action: GroupEditDialogFragment.EditGroupDialogAction?, - name: String?, - icon: IconImage?) { + override fun cancelEditGroup(action: GroupEditDialogFragment.EditGroupDialogAction, + groupInfo: GroupInfo) { // Do nothing here } diff --git a/app/src/main/java/com/kunzisoft/keepass/activities/dialogs/GroupEditDialogFragment.kt b/app/src/main/java/com/kunzisoft/keepass/activities/dialogs/GroupEditDialogFragment.kt index a6b7a95c9..d42e0bbc9 100644 --- a/app/src/main/java/com/kunzisoft/keepass/activities/dialogs/GroupEditDialogFragment.kt +++ b/app/src/main/java/com/kunzisoft/keepass/activities/dialogs/GroupEditDialogFragment.kt @@ -23,34 +23,35 @@ import android.app.Dialog import android.content.Context import android.graphics.Color import android.os.Bundle -import com.google.android.material.textfield.TextInputLayout -import androidx.fragment.app.DialogFragment -import androidx.appcompat.app.AlertDialog +import android.view.View import android.widget.Button import android.widget.ImageView import android.widget.TextView +import androidx.appcompat.app.AlertDialog +import androidx.fragment.app.DialogFragment +import com.google.android.material.textfield.TextInputLayout import com.kunzisoft.keepass.R import com.kunzisoft.keepass.activities.dialogs.GroupEditDialogFragment.EditGroupDialogAction.CREATION import com.kunzisoft.keepass.activities.dialogs.GroupEditDialogFragment.EditGroupDialogAction.UPDATE import com.kunzisoft.keepass.database.element.Database -import com.kunzisoft.keepass.database.element.Group -import com.kunzisoft.keepass.database.element.icon.IconImage import com.kunzisoft.keepass.icons.assignDatabaseIcon +import com.kunzisoft.keepass.model.GroupInfo class GroupEditDialogFragment : DialogFragment(), IconPickerDialogFragment.IconPickerListener { private var mDatabase: Database? = null - private var editGroupListener: EditGroupListener? = null + private var mEditGroupListener: EditGroupListener? = null - private var editGroupDialogAction: EditGroupDialogAction? = null - private var nameGroup: String? = null - private var iconGroup: IconImage? = null + private var mEditGroupDialogAction = EditGroupDialogAction.NONE + private var mGroupInfo = GroupInfo() - private var nameTextLayoutView: TextInputLayout? = null - private var nameTextView: TextView? = null private var iconButtonView: ImageView? = null private var iconColor: Int = 0 + private var nameTextLayoutView: TextInputLayout? = null + private var nameTextView: TextView? = null + private var notesTextLayoutView: TextInputLayout? = null + private var notesTextView: TextView? = null enum class EditGroupDialogAction { CREATION, UPDATE, NONE; @@ -67,7 +68,7 @@ class GroupEditDialogFragment : DialogFragment(), IconPickerDialogFragment.IconP // Verify that the host activity implements the callback interface try { // Instantiate the NoticeDialogListener so we can send events to the host - editGroupListener = context as EditGroupListener + mEditGroupListener = context as EditGroupListener } catch (e: ClassCastException) { // The activity doesn't implement the interface, throw exception throw ClassCastException(context.toString() @@ -76,16 +77,18 @@ class GroupEditDialogFragment : DialogFragment(), IconPickerDialogFragment.IconP } override fun onDetach() { - editGroupListener = null + mEditGroupListener = null super.onDetach() } override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { activity?.let { activity -> val root = activity.layoutInflater.inflate(R.layout.fragment_group_edit, null) + iconButtonView = root?.findViewById(R.id.group_edit_icon_button) nameTextLayoutView = root?.findViewById(R.id.group_edit_name_container) nameTextView = root?.findViewById(R.id.group_edit_name) - iconButtonView = root?.findViewById(R.id.group_edit_icon_button) + notesTextLayoutView = root?.findViewById(R.id.group_edit_note_container) + notesTextView = root?.findViewById(R.id.group_edit_note) // Retrieve the textColor to tint the icon val ta = activity.theme.obtainStyledAttributes(intArrayOf(android.R.attr.textColor)) @@ -94,43 +97,40 @@ class GroupEditDialogFragment : DialogFragment(), IconPickerDialogFragment.IconP // Init elements mDatabase = Database.getInstance() - editGroupDialogAction = EditGroupDialogAction.NONE - nameGroup = "" - iconGroup = mDatabase?.iconFactory?.folderIcon if (savedInstanceState != null && savedInstanceState.containsKey(KEY_ACTION_ID) - && savedInstanceState.containsKey(KEY_NAME) - && savedInstanceState.containsKey(KEY_ICON)) { - editGroupDialogAction = EditGroupDialogAction.getActionFromOrdinal(savedInstanceState.getInt(KEY_ACTION_ID)) - nameGroup = savedInstanceState.getString(KEY_NAME) - iconGroup = savedInstanceState.getParcelable(KEY_ICON) - + && savedInstanceState.containsKey(KEY_GROUP_INFO)) { + mEditGroupDialogAction = EditGroupDialogAction.getActionFromOrdinal(savedInstanceState.getInt(KEY_ACTION_ID)) + mGroupInfo = savedInstanceState.getParcelable(KEY_GROUP_INFO) ?: mGroupInfo } else { arguments?.apply { if (containsKey(KEY_ACTION_ID)) - editGroupDialogAction = EditGroupDialogAction.getActionFromOrdinal(getInt(KEY_ACTION_ID)) - - if (containsKey(KEY_NAME) && containsKey(KEY_ICON)) { - nameGroup = getString(KEY_NAME) - iconGroup = getParcelable(KEY_ICON) + mEditGroupDialogAction = EditGroupDialogAction.getActionFromOrdinal(getInt(KEY_ACTION_ID)) + if (containsKey(KEY_GROUP_INFO)) { + mGroupInfo = getParcelable(KEY_GROUP_INFO) ?: mGroupInfo } } } - // populate the name - nameTextView?.text = nameGroup // populate the icon assignIconView() + // populate the name + nameTextView?.text = mGroupInfo.name + // populate the note + notesTextLayoutView?.visibility = if (mGroupInfo.notes == null) View.GONE else View.VISIBLE + mGroupInfo.notes?.let { + notesTextView?.text = it + } val builder = AlertDialog.Builder(activity) builder.setView(root) .setPositiveButton(android.R.string.ok, null) .setNegativeButton(android.R.string.cancel) { _, _ -> - editGroupListener?.cancelEditGroup( - editGroupDialogAction, - nameTextView?.text?.toString(), - iconGroup) + retrieveGroupInfo() + mEditGroupListener?.cancelEditGroup( + mEditGroupDialogAction, + mGroupInfo) } iconButtonView?.setOnClickListener { _ -> @@ -150,32 +150,40 @@ class GroupEditDialogFragment : DialogFragment(), IconPickerDialogFragment.IconP if (d != null) { val positiveButton = d.getButton(Dialog.BUTTON_POSITIVE) as Button positiveButton.setOnClickListener { + retrieveGroupInfo() if (isValid()) { - editGroupListener?.approveEditGroup( - editGroupDialogAction, - nameTextView?.text?.toString(), - iconGroup) + mEditGroupListener?.approveEditGroup( + mEditGroupDialogAction, + mGroupInfo) d.dismiss() } } } } + private fun retrieveGroupInfo() { + mGroupInfo.name = nameTextView?.text?.toString() ?: "" + // Only if there + val newNotes = notesTextView?.text?.toString() + if (newNotes != null && newNotes.isNotEmpty()) { + mGroupInfo.notes = newNotes + } + } + private fun assignIconView() { - if (mDatabase?.drawFactory != null && iconGroup != null) { - iconButtonView?.assignDatabaseIcon(mDatabase?.drawFactory!!, iconGroup!!, iconColor) + if (mDatabase?.drawFactory != null) { + iconButtonView?.assignDatabaseIcon(mDatabase?.drawFactory!!, mGroupInfo.icon, iconColor) } } override fun iconPicked(bundle: Bundle) { - iconGroup = IconPickerDialogFragment.getIconStandardFromBundle(bundle) + mGroupInfo.icon = IconPickerDialogFragment.getIconStandardFromBundle(bundle) ?: mGroupInfo.icon assignIconView() } override fun onSaveInstanceState(outState: Bundle) { - outState.putInt(KEY_ACTION_ID, editGroupDialogAction!!.ordinal) - outState.putString(KEY_NAME, nameGroup) - outState.putParcelable(KEY_ICON, iconGroup) + outState.putInt(KEY_ACTION_ID, mEditGroupDialogAction.ordinal) + outState.putParcelable(KEY_GROUP_INFO, mGroupInfo) super.onSaveInstanceState(outState) } @@ -188,17 +196,17 @@ class GroupEditDialogFragment : DialogFragment(), IconPickerDialogFragment.IconP } interface EditGroupListener { - fun approveEditGroup(action: EditGroupDialogAction?, name: String?, icon: IconImage?) - fun cancelEditGroup(action: EditGroupDialogAction?, name: String?, icon: IconImage?) + fun approveEditGroup(action: EditGroupDialogAction, + groupInfo: GroupInfo) + fun cancelEditGroup(action: EditGroupDialogAction, + groupInfo: GroupInfo) } companion object { const val TAG_CREATE_GROUP = "TAG_CREATE_GROUP" - - const val KEY_NAME = "KEY_NAME" - const val KEY_ICON = "KEY_ICON" const val KEY_ACTION_ID = "KEY_ACTION_ID" + const val KEY_GROUP_INFO = "KEY_GROUP_INFO" fun build(): GroupEditDialogFragment { val bundle = Bundle() @@ -208,11 +216,10 @@ class GroupEditDialogFragment : DialogFragment(), IconPickerDialogFragment.IconP return fragment } - fun build(group: Group): GroupEditDialogFragment { + fun build(groupInfo: GroupInfo): GroupEditDialogFragment { val bundle = Bundle() - bundle.putString(KEY_NAME, group.title) - bundle.putParcelable(KEY_ICON, group.icon) bundle.putInt(KEY_ACTION_ID, UPDATE.ordinal) + bundle.putParcelable(KEY_GROUP_INFO, groupInfo) val fragment = GroupEditDialogFragment() fragment.arguments = bundle return fragment diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/Group.kt b/app/src/main/java/com/kunzisoft/keepass/database/element/Group.kt index 73ec4a35b..d014e3253 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/element/Group.kt +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/Group.kt @@ -29,6 +29,7 @@ import com.kunzisoft.keepass.database.element.icon.IconImage import com.kunzisoft.keepass.database.element.icon.IconImageStandard import com.kunzisoft.keepass.database.element.node.* import com.kunzisoft.keepass.model.EntryInfo +import com.kunzisoft.keepass.model.GroupInfo import com.kunzisoft.keepass.settings.PreferencesUtil import java.util.* import kotlin.collections.ArrayList @@ -232,6 +233,14 @@ class Group : Node, GroupVersionedInterface { override val isCurrentlyExpires: Boolean get() = groupKDB?.isCurrentlyExpires ?: groupKDBX?.isCurrentlyExpires ?: false + var notes: String? + get() = groupKDBX?.notes + set(value) { + value?.let { + groupKDBX?.notes = it + } + } + override fun getChildGroups(): List { return groupKDB?.getChildGroups()?.map { Group(it) @@ -391,6 +400,26 @@ class Group : Node, GroupVersionedInterface { return groupKDBX?.containsCustomData() ?: false } + /* + ------------ + Converter + ------------ + */ + + fun getGroupInfo(): GroupInfo { + val groupInfo = GroupInfo() + groupInfo.name = title + groupInfo.icon = icon + groupInfo.notes = notes + return groupInfo + } + + fun setGroupInfo(groupInfo: GroupInfo) { + title = groupInfo.name + icon = groupInfo.icon + notes = groupInfo.notes + } + override fun equals(other: Any?): Boolean { if (this === other) return true if (javaClass != other?.javaClass) return false diff --git a/app/src/main/java/com/kunzisoft/keepass/model/GroupInfo.kt b/app/src/main/java/com/kunzisoft/keepass/model/GroupInfo.kt new file mode 100644 index 000000000..c8637d5cf --- /dev/null +++ b/app/src/main/java/com/kunzisoft/keepass/model/GroupInfo.kt @@ -0,0 +1,39 @@ +package com.kunzisoft.keepass.model + +import android.os.Parcel +import android.os.Parcelable +import com.kunzisoft.keepass.database.element.icon.IconImage +import com.kunzisoft.keepass.database.element.icon.IconImageStandard + +class GroupInfo() : Parcelable { + + var name: String = "" + var icon: IconImage = IconImageStandard() + var notes: String? = null + + constructor(parcel: Parcel) : this() { + name = parcel.readString() ?: name + icon = parcel.readParcelable(IconImage::class.java.classLoader) ?: icon + notes = parcel.readString() + } + + override fun writeToParcel(parcel: Parcel, flags: Int) { + parcel.writeString(name) + parcel.writeParcelable(icon, flags) + parcel.writeString(notes) + } + + override fun describeContents(): Int { + return 0 + } + + companion object CREATOR : Parcelable.Creator { + override fun createFromParcel(parcel: Parcel): GroupInfo { + return GroupInfo(parcel) + } + + override fun newArray(size: Int): Array { + return arrayOfNulls(size) + } + } +} \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_group_edit.xml b/app/src/main/res/layout/fragment_group_edit.xml index 2c9c4aaee..b39e8e16e 100644 --- a/app/src/main/res/layout/fragment_group_edit.xml +++ b/app/src/main/res/layout/fragment_group_edit.xml @@ -48,5 +48,19 @@ android:singleLine="true" android:hint="@string/hint_group_name"/> + + + diff --git a/fastlane/metadata/android/en-US/changelogs/57.txt b/fastlane/metadata/android/en-US/changelogs/57.txt index e6511f4b9..9d88cb7ec 100644 --- a/fastlane/metadata/android/en-US/changelogs/57.txt +++ b/fastlane/metadata/android/en-US/changelogs/57.txt @@ -4,4 +4,5 @@ * Scroll and better UI in entry edition screen #876 * Better UI #876 * Fix themes and add Purple Dark #889 - * Allow OTP with many padding #585 \ No newline at end of file + * Allow OTP with many padding #585 + * Add notes in groups #734 \ No newline at end of file diff --git a/fastlane/metadata/android/fr-FR/changelogs/57.txt b/fastlane/metadata/android/fr-FR/changelogs/57.txt index 4e526f49c..34d388658 100644 --- a/fastlane/metadata/android/fr-FR/changelogs/57.txt +++ b/fastlane/metadata/android/fr-FR/changelogs/57.txt @@ -4,4 +4,5 @@ * Défilement et amélioration de l'UI dans l'écran d'édition #876 * Meilleure interface utilisateur #876 * Correction des thèmes et ajout de Pourpre Sombre #889 - * Autoriser l'OTP avec de nombreux rembourrages #585 \ No newline at end of file + * Autoriser l'OTP avec de nombreux rembourrages #585 + * Ajout des notes dans les groupes #734 \ No newline at end of file