mirror of
https://github.com/Kunzisoft/KeePassDX.git
synced 2025-12-04 15:49:33 +01:00
Add & edit custom icon name #976
This commit is contained in:
@@ -1,5 +1,6 @@
|
|||||||
KeePassDX(3.1.0)
|
KeePassDX(3.1.0)
|
||||||
* Change default Argon2 parameters #1098
|
* Change default Argon2 parameters #1098
|
||||||
|
* Add & edit custom icon name #976
|
||||||
|
|
||||||
KeePassDX(3.0.2)
|
KeePassDX(3.0.2)
|
||||||
* Samsung DeX mode #1114 #245 (Thx @chenxiaolong)
|
* Samsung DeX mode #1114 #245 (Thx @chenxiaolong)
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ import androidx.coordinatorlayout.widget.CoordinatorLayout
|
|||||||
import androidx.fragment.app.commit
|
import androidx.fragment.app.commit
|
||||||
import com.google.android.material.snackbar.Snackbar
|
import com.google.android.material.snackbar.Snackbar
|
||||||
import com.kunzisoft.keepass.R
|
import com.kunzisoft.keepass.R
|
||||||
|
import com.kunzisoft.keepass.activities.dialogs.IconEditDialogFragment
|
||||||
import com.kunzisoft.keepass.activities.fragments.IconPickerFragment
|
import com.kunzisoft.keepass.activities.fragments.IconPickerFragment
|
||||||
import com.kunzisoft.keepass.activities.helpers.ExternalFileHelper
|
import com.kunzisoft.keepass.activities.helpers.ExternalFileHelper
|
||||||
import com.kunzisoft.keepass.activities.helpers.setOpenDocumentClickListener
|
import com.kunzisoft.keepass.activities.helpers.setOpenDocumentClickListener
|
||||||
@@ -139,6 +140,16 @@ class IconPickerActivity : DatabaseLockActivity() {
|
|||||||
}
|
}
|
||||||
uploadButton.isEnabled = true
|
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? {
|
override fun viewToInvalidateTimeout(): View? {
|
||||||
@@ -197,6 +208,10 @@ class IconPickerActivity : DatabaseLockActivity() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun onPrepareOptionsMenu(menu: Menu?): Boolean {
|
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 {
|
menu?.findItem(R.id.menu_delete)?.apply {
|
||||||
isEnabled = mCustomIconsSelectionMode
|
isEnabled = mCustomIconsSelectionMode
|
||||||
isVisible = isEnabled
|
isVisible = isEnabled
|
||||||
@@ -213,6 +228,9 @@ class IconPickerActivity : DatabaseLockActivity() {
|
|||||||
onBackPressed()
|
onBackPressed()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
R.id.menu_edit -> {
|
||||||
|
updateCustomIcon(mIconsSelected[0])
|
||||||
|
}
|
||||||
R.id.menu_delete -> {
|
R.id.menu_delete -> {
|
||||||
mIconsSelected.forEach { iconToRemove ->
|
mIconsSelected.forEach { iconToRemove ->
|
||||||
removeCustomIcon(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) {
|
private fun removeCustomIcon(iconImageCustom: IconImageCustom) {
|
||||||
uploadButton.isEnabled = false
|
uploadButton.isEnabled = false
|
||||||
iconPickerViewModel.deselectAllCustomIcons()
|
iconPickerViewModel.deselectAllCustomIcons()
|
||||||
|
|||||||
@@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -55,8 +55,10 @@ class IconCustomFragment : IconFragment<IconImageCustom>() {
|
|||||||
iconCustomAdded?.iconCustom?.let { icon ->
|
iconCustomAdded?.iconCustom?.let { icon ->
|
||||||
iconPickerAdapter.addIcon(icon)
|
iconPickerAdapter.addIcon(icon)
|
||||||
iconCustomAdded.iconCustom = null
|
iconCustomAdded.iconCustom = null
|
||||||
|
try {
|
||||||
|
iconsGridView.smoothScrollToPosition(iconPickerAdapter.lastPosition)
|
||||||
|
} catch (ignore: Exception) {}
|
||||||
}
|
}
|
||||||
iconsGridView.smoothScrollToPosition(iconPickerAdapter.lastPosition)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
iconPickerViewModel.customIconRemoved.observe(viewLifecycleOwner) { iconCustomRemoved ->
|
iconPickerViewModel.customIconRemoved.observe(viewLifecycleOwner) { iconCustomRemoved ->
|
||||||
@@ -67,6 +69,14 @@ class IconCustomFragment : IconFragment<IconImageCustom>() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
iconPickerViewModel.customIconUpdated.observe(viewLifecycleOwner) { iconCustomUpdated ->
|
||||||
|
if (!iconCustomUpdated.error) {
|
||||||
|
iconCustomUpdated?.iconCustom?.let { icon ->
|
||||||
|
iconPickerAdapter.updateIcon(icon)
|
||||||
|
iconCustomUpdated.iconCustom = null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onIconClickListener(icon: IconImageCustom) {
|
override fun onIconClickListener(icon: IconImageCustom) {
|
||||||
|
|||||||
@@ -147,6 +147,10 @@ class Database {
|
|||||||
iconsManager.removeCustomIcon(binaryCache, customIcon.uuid)
|
iconsManager.removeCustomIcon(binaryCache, customIcon.uuid)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun updateCustomIcon(customIcon: IconImageCustom) {
|
||||||
|
iconsManager.getIcon(customIcon.uuid).updateWith(customIcon)
|
||||||
|
}
|
||||||
|
|
||||||
fun getTemplates(templateCreation: Boolean): List<Template> {
|
fun getTemplates(templateCreation: Boolean): List<Template> {
|
||||||
return mDatabaseKDBX?.getTemplates(templateCreation) ?: listOf()
|
return mDatabaseKDBX?.getTemplates(templateCreation) ?: listOf()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -31,6 +31,10 @@ class CustomIconPool(private val binaryCache: BinaryCache) : BinaryPool<UUID>(bi
|
|||||||
return newUUID
|
return newUUID
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun getCustomIcon(key: UUID): IconImageCustom? {
|
||||||
|
return customIcons[key]
|
||||||
|
}
|
||||||
|
|
||||||
fun any(predicate: (IconImageCustom)-> Boolean): Boolean {
|
fun any(predicate: (IconImageCustom)-> Boolean): Boolean {
|
||||||
return customIcons.any { predicate(it.value) }
|
return customIcons.any { predicate(it.value) }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -32,6 +32,16 @@ class IconImageCustom : IconImageDraw {
|
|||||||
var name: String = ""
|
var name: String = ""
|
||||||
var lastModificationTime: DateInstant? = null
|
var lastModificationTime: DateInstant? = null
|
||||||
|
|
||||||
|
fun updateWith(icon: IconImageCustom) {
|
||||||
|
this.name = icon.name
|
||||||
|
this.lastModificationTime = icon.lastModificationTime
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(copy: IconImageCustom) {
|
||||||
|
this.uuid = copy.uuid
|
||||||
|
updateWith(copy)
|
||||||
|
}
|
||||||
|
|
||||||
constructor(name: String = "", lastModificationTime: DateInstant? = null) {
|
constructor(name: String = "", lastModificationTime: DateInstant? = null) {
|
||||||
this.uuid = DatabaseVersioned.UUID_ZERO
|
this.uuid = DatabaseVersioned.UUID_ZERO
|
||||||
this.name = name
|
this.name = name
|
||||||
|
|||||||
@@ -65,7 +65,7 @@ class IconsManager(binaryCache: BinaryCache) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun getIcon(iconUuid: UUID): IconImageCustom {
|
fun getIcon(iconUuid: UUID): IconImageCustom {
|
||||||
return IconImageCustom(iconUuid)
|
return customCache.getCustomIcon(iconUuid) ?: IconImageCustom(iconUuid)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun isCustomIconBinaryDuplicate(binaryData: BinaryData): Boolean {
|
fun isCustomIconBinaryDuplicate(binaryData: BinaryData): Boolean {
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package com.kunzisoft.keepass.viewmodels
|
|||||||
|
|
||||||
import android.os.Parcel
|
import android.os.Parcel
|
||||||
import android.os.Parcelable
|
import android.os.Parcelable
|
||||||
|
import androidx.lifecycle.LiveData
|
||||||
import androidx.lifecycle.MutableLiveData
|
import androidx.lifecycle.MutableLiveData
|
||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
import com.kunzisoft.keepass.database.element.icon.IconImageCustom
|
import com.kunzisoft.keepass.database.element.icon.IconImageCustom
|
||||||
@@ -30,6 +31,10 @@ class IconPickerViewModel: ViewModel() {
|
|||||||
MutableLiveData<IconCustomState>()
|
MutableLiveData<IconCustomState>()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val customIconUpdated : MutableLiveData<IconCustomState> by lazy {
|
||||||
|
MutableLiveData<IconCustomState>()
|
||||||
|
}
|
||||||
|
|
||||||
fun pickStandardIcon(icon: IconImageStandard) {
|
fun pickStandardIcon(icon: IconImageStandard) {
|
||||||
standardIconPicked.value = icon
|
standardIconPicked.value = icon
|
||||||
}
|
}
|
||||||
@@ -54,6 +59,10 @@ class IconPickerViewModel: ViewModel() {
|
|||||||
customIconRemoved.value = customIcon
|
customIconRemoved.value = customIcon
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun updateCustomIcon(customIcon: IconCustomState) {
|
||||||
|
customIconUpdated.value = customIcon
|
||||||
|
}
|
||||||
|
|
||||||
data class IconCustomState(var iconCustom: IconImageCustom? = null,
|
data class IconCustomState(var iconCustom: IconImageCustom? = null,
|
||||||
var error: Boolean = true,
|
var error: Boolean = true,
|
||||||
var errorStringId: Int = -1,
|
var errorStringId: Int = -1,
|
||||||
|
|||||||
56
app/src/main/res/layout/fragment_icon_edit.xml
Normal file
56
app/src/main/res/layout/fragment_icon_edit.xml
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
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 <http://www.gnu.org/licenses/>.
|
||||||
|
-->
|
||||||
|
<androidx.core.widget.NestedScrollView
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent">
|
||||||
|
<LinearLayout
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:padding="@dimen/default_margin"
|
||||||
|
android:importantForAutofill="noExcludeDescendants"
|
||||||
|
tools:targetApi="o">
|
||||||
|
<androidx.appcompat.widget.AppCompatImageView
|
||||||
|
android:id="@+id/icon_edit_image"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="center"
|
||||||
|
android:layout_marginRight="@dimen/default_margin"
|
||||||
|
android:layout_marginEnd="@dimen/default_margin"
|
||||||
|
android:src="@drawable/ic_blank_32dp"/>
|
||||||
|
<com.google.android.material.textfield.TextInputLayout
|
||||||
|
android:id="@+id/icon_edit_name_container"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content">
|
||||||
|
<com.google.android.material.textfield.TextInputEditText
|
||||||
|
android:id="@+id/icon_edit_name"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginLeft="4dp"
|
||||||
|
android:layout_marginStart="4dp"
|
||||||
|
android:inputType="text"
|
||||||
|
android:maxLines="1"
|
||||||
|
android:singleLine="true"
|
||||||
|
android:hint="@string/hint_icon_name"/>
|
||||||
|
</com.google.android.material.textfield.TextInputLayout>
|
||||||
|
</LinearLayout>
|
||||||
|
</androidx.core.widget.NestedScrollView>
|
||||||
@@ -19,6 +19,12 @@
|
|||||||
-->
|
-->
|
||||||
<menu xmlns:android="http://schemas.android.com/apk/res/android"
|
<menu xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||||
|
<item android:id="@+id/menu_edit"
|
||||||
|
android:icon="@drawable/ic_mode_edit_white_24dp"
|
||||||
|
android:title="@string/menu_edit"
|
||||||
|
android:orderInCategory="5"
|
||||||
|
app:iconTint="?attr/colorControlNormal"
|
||||||
|
app:showAsAction="ifRoom" />
|
||||||
<item android:id="@+id/menu_delete"
|
<item android:id="@+id/menu_delete"
|
||||||
android:icon="@drawable/ic_delete_forever_white_24dp"
|
android:icon="@drawable/ic_delete_forever_white_24dp"
|
||||||
android:title="@string/menu_delete"
|
android:title="@string/menu_delete"
|
||||||
|
|||||||
@@ -182,6 +182,7 @@
|
|||||||
<string name="hint_conf_pass">Confirm password</string>
|
<string name="hint_conf_pass">Confirm password</string>
|
||||||
<string name="hint_generated_password">Generated password</string>
|
<string name="hint_generated_password">Generated password</string>
|
||||||
<string name="hint_group_name">Group name</string>
|
<string name="hint_group_name">Group name</string>
|
||||||
|
<string name="hint_icon_name">Icon name</string>
|
||||||
<string name="hint_keyfile">Keyfile</string>
|
<string name="hint_keyfile">Keyfile</string>
|
||||||
<string name="hint_length">Length</string>
|
<string name="hint_length">Length</string>
|
||||||
<string name="hint_pass">Password</string>
|
<string name="hint_pass">Password</string>
|
||||||
|
|||||||
@@ -1 +1,2 @@
|
|||||||
* Change default Argon2 parameters #1098
|
* Change default Argon2 parameters #1098
|
||||||
|
* Add & edit custom icon name #976
|
||||||
@@ -1 +1,2 @@
|
|||||||
* Changement des paramètres Argon2 par défaut #1098
|
* Changement des paramètres Argon2 par défaut #1098
|
||||||
|
* Ajout & édition du nom d'icone customisé #976
|
||||||
Reference in New Issue
Block a user