From f0fdd4a5373e6153e80cf8545d27d665149bf1c2 Mon Sep 17 00:00:00 2001 From: J-Jamet Date: Tue, 28 Sep 2021 18:12:49 +0200 Subject: [PATCH] Add & edit custom icon name #976 --- CHANGELOG | 1 + .../keepass/activities/IconPickerActivity.kt | 23 ++++ .../dialogs/IconEditDialogFragment.kt | 126 ++++++++++++++++++ .../fragments/IconCustomFragment.kt | 12 +- .../keepass/database/element/Database.kt | 4 + .../database/element/binary/CustomIconPool.kt | 4 + .../database/element/icon/IconImageCustom.kt | 10 ++ .../database/element/icon/IconsManager.kt | 2 +- .../keepass/viewmodels/IconPickerViewModel.kt | 9 ++ .../main/res/layout/fragment_icon_edit.xml | 56 ++++++++ app/src/main/res/menu/icon.xml | 6 + app/src/main/res/values/strings.xml | 1 + .../metadata/android/en-US/changelogs/90.txt | 3 +- .../metadata/android/fr-FR/changelogs/90.txt | 3 +- 14 files changed, 256 insertions(+), 4 deletions(-) create mode 100644 app/src/main/java/com/kunzisoft/keepass/activities/dialogs/IconEditDialogFragment.kt create mode 100644 app/src/main/res/layout/fragment_icon_edit.xml diff --git a/CHANGELOG b/CHANGELOG index b6842b7c6..dc7174086 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,5 +1,6 @@ KeePassDX(3.1.0) * Change default Argon2 parameters #1098 + * Add & edit custom icon name #976 KeePassDX(3.0.2) * Samsung DeX mode #1114 #245 (Thx @chenxiaolong) diff --git a/app/src/main/java/com/kunzisoft/keepass/activities/IconPickerActivity.kt b/app/src/main/java/com/kunzisoft/keepass/activities/IconPickerActivity.kt index 93635b1a3..ff9e7694e 100644 --- a/app/src/main/java/com/kunzisoft/keepass/activities/IconPickerActivity.kt +++ b/app/src/main/java/com/kunzisoft/keepass/activities/IconPickerActivity.kt @@ -33,6 +33,7 @@ import androidx.coordinatorlayout.widget.CoordinatorLayout import androidx.fragment.app.commit import com.google.android.material.snackbar.Snackbar import com.kunzisoft.keepass.R +import com.kunzisoft.keepass.activities.dialogs.IconEditDialogFragment import com.kunzisoft.keepass.activities.fragments.IconPickerFragment import com.kunzisoft.keepass.activities.helpers.ExternalFileHelper import com.kunzisoft.keepass.activities.helpers.setOpenDocumentClickListener @@ -139,6 +140,16 @@ class IconPickerActivity : DatabaseLockActivity() { } uploadButton.isEnabled = true } + iconPickerViewModel.customIconUpdated.observe(this) { iconCustomUpdated -> + if (iconCustomUpdated.error && !iconCustomUpdated.errorConsumed) { + Snackbar.make(coordinatorLayout, iconCustomUpdated.errorStringId, Snackbar.LENGTH_LONG).asError().show() + iconCustomUpdated.errorConsumed = true + } + iconCustomUpdated.iconCustom?.let { + mDatabase?.updateCustomIcon(it) + } + iconPickerViewModel.deselectAllCustomIcons() + } } override fun viewToInvalidateTimeout(): View? { @@ -197,6 +208,10 @@ class IconPickerActivity : DatabaseLockActivity() { } override fun onPrepareOptionsMenu(menu: Menu?): Boolean { + menu?.findItem(R.id.menu_edit)?.apply { + isEnabled = mIconsSelected.size == 1 + isVisible = isEnabled + } menu?.findItem(R.id.menu_delete)?.apply { isEnabled = mCustomIconsSelectionMode isVisible = isEnabled @@ -213,6 +228,9 @@ class IconPickerActivity : DatabaseLockActivity() { onBackPressed() } } + R.id.menu_edit -> { + updateCustomIcon(mIconsSelected[0]) + } R.id.menu_delete -> { mIconsSelected.forEach { iconToRemove -> removeCustomIcon(iconToRemove) @@ -277,6 +295,11 @@ class IconPickerActivity : DatabaseLockActivity() { } } + private fun updateCustomIcon(iconImageCustom: IconImageCustom) { + IconEditDialogFragment.update(iconImageCustom) + .show(supportFragmentManager, IconEditDialogFragment.TAG_UPDATE_ICON) + } + private fun removeCustomIcon(iconImageCustom: IconImageCustom) { uploadButton.isEnabled = false iconPickerViewModel.deselectAllCustomIcons() diff --git a/app/src/main/java/com/kunzisoft/keepass/activities/dialogs/IconEditDialogFragment.kt b/app/src/main/java/com/kunzisoft/keepass/activities/dialogs/IconEditDialogFragment.kt new file mode 100644 index 000000000..f43fa4391 --- /dev/null +++ b/app/src/main/java/com/kunzisoft/keepass/activities/dialogs/IconEditDialogFragment.kt @@ -0,0 +1,126 @@ +/* + * Copyright 2021 Jeremy Jamet / Kunzisoft. + * + * This file is part of KeePassDX. + * + * KeePassDX is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * KeePassDX is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with KeePassDX. If not, see . + * + */ +package com.kunzisoft.keepass.activities.dialogs + +import android.app.Dialog +import android.os.Bundle +import android.widget.ImageView +import android.widget.TextView +import androidx.appcompat.app.AlertDialog +import androidx.fragment.app.activityViewModels +import com.google.android.material.textfield.TextInputLayout +import com.kunzisoft.keepass.R +import com.kunzisoft.keepass.database.element.Database +import com.kunzisoft.keepass.database.element.DateInstant +import com.kunzisoft.keepass.database.element.icon.IconImage +import com.kunzisoft.keepass.database.element.icon.IconImageCustom +import com.kunzisoft.keepass.viewmodels.IconPickerViewModel + +class IconEditDialogFragment : DatabaseDialogFragment() { + + private val mIconPickerViewModel: IconPickerViewModel by activityViewModels() + + private var mPopulateIconMethod: ((ImageView, IconImage) -> Unit)? = null + private lateinit var iconView: ImageView + private lateinit var nameTextLayoutView: TextInputLayout + private lateinit var nameTextView: TextView + + private var mCustomIcon: IconImageCustom? = null + + override fun onDatabaseRetrieved(database: Database?) { + super.onDatabaseRetrieved(database) + mPopulateIconMethod = { imageView, icon -> + database?.iconDrawableFactory?.assignDatabaseIcon(imageView, icon) + } + mCustomIcon?.let { customIcon -> + populateViewsWithCustomIcon(customIcon) + } + } + + override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { + activity?.let { activity -> + val root = activity.layoutInflater.inflate(R.layout.fragment_icon_edit, null) + iconView = root.findViewById(R.id.icon_edit_image) + nameTextLayoutView = root.findViewById(R.id.icon_edit_name_container) + nameTextView = root.findViewById(R.id.icon_edit_name) + + if (savedInstanceState != null + && savedInstanceState.containsKey(KEY_CUSTOM_ICON_ID)) { + mCustomIcon = savedInstanceState.getParcelable(KEY_CUSTOM_ICON_ID) ?: mCustomIcon + } else { + arguments?.apply { + if (containsKey(KEY_CUSTOM_ICON_ID)) { + mCustomIcon = getParcelable(KEY_CUSTOM_ICON_ID) ?: mCustomIcon + } + } + } + + val builder = AlertDialog.Builder(activity) + builder.setView(root) + .setPositiveButton(android.R.string.ok) { _, _ -> + retrieveIconInfoFromViews() + mCustomIcon?.let { customIcon -> + mIconPickerViewModel.updateCustomIcon( + IconPickerViewModel.IconCustomState(customIcon, false) + ) + } + } + .setNegativeButton(android.R.string.cancel) { _, _ -> + // Do nothing + mIconPickerViewModel.updateCustomIcon( + IconPickerViewModel.IconCustomState(null, false) + ) + } + + return builder.create() + } + return super.onCreateDialog(savedInstanceState) + } + + private fun populateViewsWithCustomIcon(customIcon: IconImageCustom) { + mPopulateIconMethod?.invoke(iconView, customIcon.getIconImageToDraw()) + nameTextView.text = customIcon.name + } + + private fun retrieveIconInfoFromViews() { + mCustomIcon?.name = nameTextView.text.toString() + mCustomIcon?.lastModificationTime = DateInstant() + } + + override fun onSaveInstanceState(outState: Bundle) { + retrieveIconInfoFromViews() + outState.putParcelable(KEY_CUSTOM_ICON_ID, mCustomIcon) + super.onSaveInstanceState(outState) + } + + companion object { + + const val TAG_UPDATE_ICON = "TAG_UPDATE_ICON" + const val KEY_CUSTOM_ICON_ID = "KEY_CUSTOM_ICON_ID" + + fun update(customIcon: IconImageCustom): IconEditDialogFragment { + val bundle = Bundle() + bundle.putParcelable(KEY_CUSTOM_ICON_ID, IconImageCustom(customIcon)) + val fragment = IconEditDialogFragment() + fragment.arguments = bundle + return fragment + } + } +} diff --git a/app/src/main/java/com/kunzisoft/keepass/activities/fragments/IconCustomFragment.kt b/app/src/main/java/com/kunzisoft/keepass/activities/fragments/IconCustomFragment.kt index f1f9ed87c..e764e429a 100644 --- a/app/src/main/java/com/kunzisoft/keepass/activities/fragments/IconCustomFragment.kt +++ b/app/src/main/java/com/kunzisoft/keepass/activities/fragments/IconCustomFragment.kt @@ -55,8 +55,10 @@ class IconCustomFragment : IconFragment() { iconCustomAdded?.iconCustom?.let { icon -> iconPickerAdapter.addIcon(icon) iconCustomAdded.iconCustom = null + try { + iconsGridView.smoothScrollToPosition(iconPickerAdapter.lastPosition) + } catch (ignore: Exception) {} } - iconsGridView.smoothScrollToPosition(iconPickerAdapter.lastPosition) } } iconPickerViewModel.customIconRemoved.observe(viewLifecycleOwner) { iconCustomRemoved -> @@ -67,6 +69,14 @@ class IconCustomFragment : IconFragment() { } } } + iconPickerViewModel.customIconUpdated.observe(viewLifecycleOwner) { iconCustomUpdated -> + if (!iconCustomUpdated.error) { + iconCustomUpdated?.iconCustom?.let { icon -> + iconPickerAdapter.updateIcon(icon) + iconCustomUpdated.iconCustom = null + } + } + } } override fun onIconClickListener(icon: IconImageCustom) { diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/Database.kt b/app/src/main/java/com/kunzisoft/keepass/database/element/Database.kt index 1d960f64d..0f96505e5 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/element/Database.kt +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/Database.kt @@ -147,6 +147,10 @@ class Database { iconsManager.removeCustomIcon(binaryCache, customIcon.uuid) } + fun updateCustomIcon(customIcon: IconImageCustom) { + iconsManager.getIcon(customIcon.uuid).updateWith(customIcon) + } + fun getTemplates(templateCreation: Boolean): List