Add notes in groups #734

This commit is contained in:
J-Jamet
2021-02-11 16:05:58 +01:00
parent 3efe43c0fe
commit 79dbb942f9
8 changed files with 155 additions and 68 deletions

View File

@@ -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

View File

@@ -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
}

View File

@@ -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

View File

@@ -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<Group, Entry> {
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<Group> {
return groupKDB?.getChildGroups()?.map {
Group(it)
@@ -391,6 +400,26 @@ class Group : Node, GroupVersionedInterface<Group, Entry> {
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

View File

@@ -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<GroupInfo> {
override fun createFromParcel(parcel: Parcel): GroupInfo {
return GroupInfo(parcel)
}
override fun newArray(size: Int): Array<GroupInfo?> {
return arrayOfNulls(size)
}
}
}

View File

@@ -48,5 +48,19 @@
android:singleLine="true"
android:hint="@string/hint_group_name"/>
</com.google.android.material.textfield.TextInputLayout>
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/group_edit_note_container"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/group_edit_note"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="4dp"
android:layout_marginStart="4dp"
android:inputType="text"
android:maxLines="5"
android:hint="@string/entry_notes"/>
</com.google.android.material.textfield.TextInputLayout>
</LinearLayout>

View File

@@ -5,3 +5,4 @@
* Better UI #876
* Fix themes and add Purple Dark #889
* Allow OTP with many padding #585
* Add notes in groups #734

View File

@@ -5,3 +5,4 @@
* Meilleure interface utilisateur #876
* Correction des thèmes et ajout de Pourpre Sombre #889
* Autoriser l'OTP avec de nombreux rembourrages #585
* Ajout des notes dans les groupes #734