mirror of
https://github.com/Kunzisoft/KeePassDX.git
synced 2025-12-04 15:49:33 +01:00
Remove custom icons
This commit is contained in:
@@ -20,8 +20,11 @@
|
||||
package com.kunzisoft.keepass.activities
|
||||
|
||||
import android.app.Activity
|
||||
import android.content.ContentResolver
|
||||
import android.content.Intent
|
||||
import android.net.Uri
|
||||
import android.os.Bundle
|
||||
import android.view.Menu
|
||||
import android.view.MenuItem
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
@@ -39,10 +42,13 @@ import com.kunzisoft.keepass.database.element.Database
|
||||
import com.kunzisoft.keepass.database.element.icon.IconImage
|
||||
import com.kunzisoft.keepass.database.element.icon.IconImageCustom
|
||||
import com.kunzisoft.keepass.settings.PreferencesUtil
|
||||
import com.kunzisoft.keepass.tasks.BinaryStreamManager
|
||||
import com.kunzisoft.keepass.utils.UriUtil
|
||||
import com.kunzisoft.keepass.view.asError
|
||||
import com.kunzisoft.keepass.view.updateLockPaddingLeft
|
||||
import com.kunzisoft.keepass.viewmodels.IconPickerViewModel
|
||||
import kotlinx.coroutines.*
|
||||
import java.io.File
|
||||
|
||||
|
||||
class IconPickerActivity : LockingActivity() {
|
||||
@@ -54,7 +60,11 @@ class IconPickerActivity : LockingActivity() {
|
||||
|
||||
private var mIconImage: IconImage = IconImage()
|
||||
|
||||
private val mainScope = CoroutineScope(Dispatchers.Main)
|
||||
|
||||
private val iconPickerViewModel: IconPickerViewModel by viewModels()
|
||||
private var mCustomIconsSelectionMode = false
|
||||
private var mIconsSelected: List<IconImageCustom> = ArrayList()
|
||||
|
||||
private var mDatabase: Database? = null
|
||||
|
||||
@@ -68,7 +78,6 @@ class IconPickerActivity : LockingActivity() {
|
||||
mDatabase = Database.getInstance()
|
||||
|
||||
toolbar = findViewById(R.id.toolbar)
|
||||
toolbar.title = getString(R.string.about)
|
||||
setSupportActionBar(toolbar)
|
||||
supportActionBar?.setDisplayHomeAsUpEnabled(true)
|
||||
supportActionBar?.setDisplayShowHomeEnabled(true)
|
||||
@@ -118,7 +127,7 @@ class IconPickerActivity : LockingActivity() {
|
||||
|
||||
mSelectFileHelper = SelectFileHelper(this)
|
||||
|
||||
iconPickerViewModel.iconStandardSelected.observe(this) { iconStandard ->
|
||||
iconPickerViewModel.standardIconPicked.observe(this) { iconStandard ->
|
||||
mIconImage.standard = iconStandard
|
||||
// Remove the custom icon if a standard one is selected
|
||||
mIconImage.custom = IconImageCustom()
|
||||
@@ -127,7 +136,7 @@ class IconPickerActivity : LockingActivity() {
|
||||
})
|
||||
finish()
|
||||
}
|
||||
iconPickerViewModel.iconCustomSelected.observe(this) { iconCustom ->
|
||||
iconPickerViewModel.customIconPicked.observe(this) { iconCustom ->
|
||||
// Keep the standard icon if a custom one is selected
|
||||
mIconImage.custom = iconCustom
|
||||
setResult(Activity.RESULT_OK, Intent().apply {
|
||||
@@ -135,12 +144,32 @@ class IconPickerActivity : LockingActivity() {
|
||||
})
|
||||
finish()
|
||||
}
|
||||
iconPickerViewModel.iconCustomAdded.observe(this) { iconCustomAdded ->
|
||||
iconPickerViewModel.customIconsSelected.observe(this) { iconsSelected ->
|
||||
mIconsSelected = iconsSelected
|
||||
if (iconsSelected.isEmpty()) {
|
||||
mCustomIconsSelectionMode = false
|
||||
supportActionBar?.setDisplayShowTitleEnabled(false)
|
||||
toolbar.title = ""
|
||||
} else {
|
||||
mCustomIconsSelectionMode = true
|
||||
supportActionBar?.setDisplayShowTitleEnabled(true)
|
||||
toolbar.title = iconsSelected.size.toString()
|
||||
}
|
||||
invalidateOptionsMenu()
|
||||
}
|
||||
iconPickerViewModel.customIconAdded.observe(this) { iconCustomAdded ->
|
||||
if (iconCustomAdded.error) {
|
||||
Snackbar.make(coordinatorLayout, R.string.error_upload_file, Snackbar.LENGTH_LONG).asError().show()
|
||||
}
|
||||
uploadButton.isEnabled = true
|
||||
}
|
||||
iconPickerViewModel.customIconRemoved.observe(this) { iconCustomRemoved ->
|
||||
if (iconCustomRemoved.error) {
|
||||
Snackbar.make(coordinatorLayout, R.string.error_remove_file, Snackbar.LENGTH_LONG).asError().show()
|
||||
}
|
||||
uploadButton.isEnabled = true
|
||||
iconPickerViewModel.deselectAllCustomIcons()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onSaveInstanceState(outState: Bundle) {
|
||||
@@ -163,16 +192,72 @@ class IconPickerActivity : LockingActivity() {
|
||||
toolbar.updateLockPaddingLeft()
|
||||
}
|
||||
|
||||
override fun onCreateOptionsMenu(menu: Menu?): Boolean {
|
||||
super.onCreateOptionsMenu(menu)
|
||||
|
||||
if (mCustomIconsSelectionMode) {
|
||||
menuInflater.inflate(R.menu.icon, menu)
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
||||
when (item.itemId) {
|
||||
android.R.id.home -> {
|
||||
if (mCustomIconsSelectionMode) {
|
||||
iconPickerViewModel.deselectAllCustomIcons()
|
||||
} else {
|
||||
onBackPressed()
|
||||
}
|
||||
}
|
||||
R.id.menu_delete -> {
|
||||
mIconsSelected.forEach { iconToRemove ->
|
||||
removeCustomIcon(iconToRemove)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return super.onOptionsItemSelected(item)
|
||||
}
|
||||
|
||||
private fun addCustomIcon(contentResolver: ContentResolver,
|
||||
iconDir: File,
|
||||
iconToUploadUri: Uri) {
|
||||
uploadButton.isEnabled = false
|
||||
mainScope.launch {
|
||||
withContext(Dispatchers.IO) {
|
||||
// on Progress with thread
|
||||
val asyncResult: Deferred<IconImageCustom?> = async {
|
||||
mDatabase?.buildNewCustomIcon(iconDir)?.let { customIcon ->
|
||||
BinaryStreamManager.resizeBitmapAndStoreDataInBinaryFile(contentResolver,
|
||||
iconToUploadUri, customIcon.binaryFile)
|
||||
customIcon
|
||||
}
|
||||
}
|
||||
withContext(Dispatchers.Main) {
|
||||
asyncResult.await()?.let { customIcon ->
|
||||
var error = false
|
||||
if (customIcon.binaryFile.length <= 0) {
|
||||
mDatabase?.removeCustomIcon(customIcon)
|
||||
error = true
|
||||
}
|
||||
iconPickerViewModel.addCustomIcon(
|
||||
IconPickerViewModel.IconCustomState(customIcon, error)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun removeCustomIcon(iconImageCustom: IconImageCustom) {
|
||||
uploadButton.isEnabled = false
|
||||
mDatabase?.removeCustomIcon(iconImageCustom)
|
||||
iconPickerViewModel.removeCustomIcon(
|
||||
IconPickerViewModel.IconCustomState(iconImageCustom, false)
|
||||
)
|
||||
}
|
||||
|
||||
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
|
||||
super.onActivityResult(requestCode, resultCode, data)
|
||||
|
||||
@@ -182,13 +267,10 @@ class IconPickerActivity : LockingActivity() {
|
||||
if (documentFile.length() > MAX_ICON_SIZE) {
|
||||
Snackbar.make(coordinatorLayout, R.string.error_file_to_big, Snackbar.LENGTH_LONG).asError().show()
|
||||
} else {
|
||||
mDatabase?.let { database ->
|
||||
iconPickerViewModel.addCustomIcon(database,
|
||||
addCustomIcon(
|
||||
contentResolver,
|
||||
UriUtil.getBinaryDir(this),
|
||||
iconToUploadUri)
|
||||
uploadButton.isEnabled = false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,7 +22,6 @@ package com.kunzisoft.keepass.activities.fragments
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import com.kunzisoft.keepass.R
|
||||
import com.kunzisoft.keepass.adapters.IconAdapter
|
||||
import com.kunzisoft.keepass.database.element.Database
|
||||
import com.kunzisoft.keepass.database.element.icon.IconImageCustom
|
||||
|
||||
@@ -40,17 +39,36 @@ class IconCustomFragment : IconFragment<IconImageCustom>() {
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
|
||||
iconPickerViewModel.iconCustomAdded.observe(viewLifecycleOwner) { iconCustom ->
|
||||
iconPickerViewModel.customIconAdded.observe(viewLifecycleOwner) { iconCustom ->
|
||||
if (!iconCustom.error) {
|
||||
iconAdapter.addIcon(iconCustom.iconCustom)
|
||||
iconsGridView.smoothScrollToPosition(iconAdapter.lastPosition)
|
||||
iconPickerAdapter.addIcon(iconCustom.iconCustom)
|
||||
iconsGridView.smoothScrollToPosition(iconPickerAdapter.lastPosition)
|
||||
}
|
||||
}
|
||||
iconPickerViewModel.customIconsSelected.observe(viewLifecycleOwner) { customIconsSelected ->
|
||||
if (customIconsSelected.isEmpty()) {
|
||||
iconPickerAdapter.deselectAllIcons()
|
||||
}
|
||||
}
|
||||
iconPickerViewModel.customIconRemoved.observe(viewLifecycleOwner) { customIconRemoved ->
|
||||
iconPickerAdapter.removeIcon(customIconRemoved.iconCustom)
|
||||
}
|
||||
}
|
||||
|
||||
iconAdapter.iconPickerListener = object : IconAdapter.IconPickerListener<IconImageCustom> {
|
||||
override fun iconPicked(icon: IconImageCustom) {
|
||||
iconPickerViewModel.selectIconCustom(icon)
|
||||
override fun onIconClickListener(icon: IconImageCustom) {
|
||||
if (iconActionSelectionMode) {
|
||||
// Same long click behavior after each single click
|
||||
onIconLongClickListener(icon)
|
||||
} else {
|
||||
iconPickerViewModel.pickCustomIcon(icon)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onIconLongClickListener(icon: IconImageCustom) {
|
||||
// Select or deselect item if already selected
|
||||
icon.selected = !icon.selected
|
||||
iconPickerAdapter.updateIcon(icon)
|
||||
iconActionSelectionMode = iconPickerAdapter.containsAnySelectedIcon()
|
||||
iconPickerViewModel.selectCustomIcons(iconPickerAdapter.getSelectedIcons())
|
||||
}
|
||||
}
|
||||
@@ -29,15 +29,17 @@ import androidx.fragment.app.activityViewModels
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.kunzisoft.keepass.R
|
||||
import com.kunzisoft.keepass.activities.stylish.StylishFragment
|
||||
import com.kunzisoft.keepass.adapters.IconAdapter
|
||||
import com.kunzisoft.keepass.adapters.IconPickerAdapter
|
||||
import com.kunzisoft.keepass.database.element.Database
|
||||
import com.kunzisoft.keepass.database.element.icon.IconImageDraw
|
||||
import com.kunzisoft.keepass.viewmodels.IconPickerViewModel
|
||||
|
||||
abstract class IconFragment<T: IconImageDraw> : StylishFragment() {
|
||||
abstract class IconFragment<T: IconImageDraw> : StylishFragment(),
|
||||
IconPickerAdapter.IconPickerListener<T> {
|
||||
|
||||
protected lateinit var iconsGridView: RecyclerView
|
||||
protected lateinit var iconAdapter: IconAdapter<T>
|
||||
protected lateinit var iconPickerAdapter: IconPickerAdapter<T>
|
||||
protected var iconActionSelectionMode = false
|
||||
|
||||
protected val database = Database.getInstance()
|
||||
|
||||
@@ -55,11 +57,11 @@ abstract class IconFragment<T: IconImageDraw> : StylishFragment() {
|
||||
val tintColor = ta?.getColor(0, Color.BLACK) ?: Color.BLACK
|
||||
ta?.recycle()
|
||||
|
||||
iconAdapter = IconAdapter<T>(context, tintColor).apply {
|
||||
iconPickerAdapter = IconPickerAdapter<T>(context, tintColor).apply {
|
||||
iconDrawableFactory = database.iconDrawableFactory
|
||||
}
|
||||
|
||||
iconAdapter.setList(defineIconList(database))
|
||||
iconPickerAdapter.setList(defineIconList(database))
|
||||
}
|
||||
|
||||
override fun onCreateView(inflater: LayoutInflater,
|
||||
@@ -67,7 +69,17 @@ abstract class IconFragment<T: IconImageDraw> : StylishFragment() {
|
||||
savedInstanceState: Bundle?): View {
|
||||
val root = inflater.inflate(retrieveMainLayoutId(), container, false)
|
||||
iconsGridView = root.findViewById(R.id.icons_grid_view)
|
||||
iconsGridView.adapter = iconAdapter
|
||||
iconsGridView.adapter = iconPickerAdapter
|
||||
return root
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
|
||||
iconPickerAdapter.iconPickerListener = this
|
||||
}
|
||||
|
||||
fun onIconDeleteClicked() {
|
||||
iconActionSelectionMode = false
|
||||
}
|
||||
}
|
||||
@@ -53,7 +53,7 @@ class IconPickerFragment : StylishFragment() {
|
||||
remove(ICON_TAB_ARG)
|
||||
}
|
||||
|
||||
iconPickerViewModel.iconCustomAdded.observe(viewLifecycleOwner) { _ ->
|
||||
iconPickerViewModel.customIconAdded.observe(viewLifecycleOwner) { _ ->
|
||||
viewPager.currentItem = 1
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,10 +19,7 @@
|
||||
*/
|
||||
package com.kunzisoft.keepass.activities.fragments
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import com.kunzisoft.keepass.R
|
||||
import com.kunzisoft.keepass.adapters.IconAdapter
|
||||
import com.kunzisoft.keepass.database.element.Database
|
||||
import com.kunzisoft.keepass.database.element.icon.IconImageStandard
|
||||
|
||||
@@ -37,13 +34,9 @@ class IconStandardFragment : IconFragment<IconImageStandard>() {
|
||||
return database.getStandardIconList()
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
override fun onIconLongClickListener(icon: IconImageStandard) {
|
||||
iconPickerViewModel.pickStandardIcon(icon)
|
||||
}
|
||||
|
||||
iconAdapter.iconPickerListener = object : IconAdapter.IconPickerListener<IconImageStandard> {
|
||||
override fun iconPicked(icon: IconImageStandard) {
|
||||
iconPickerViewModel.selectIconStandard(icon)
|
||||
}
|
||||
}
|
||||
}
|
||||
override fun onIconClickListener(icon: IconImageStandard) {}
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
package com.kunzisoft.keepass.adapters
|
||||
|
||||
import android.content.Context
|
||||
import android.graphics.Color
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
@@ -10,8 +11,8 @@ import com.kunzisoft.keepass.R
|
||||
import com.kunzisoft.keepass.database.element.icon.IconImageDraw
|
||||
import com.kunzisoft.keepass.icons.IconDrawableFactory
|
||||
|
||||
class IconAdapter<I: IconImageDraw>(val context: Context, val tintIcon: Int)
|
||||
: RecyclerView.Adapter<IconAdapter<I>.CustomIconViewHolder>() {
|
||||
class IconPickerAdapter<I: IconImageDraw>(val context: Context, private val tintIcon: Int)
|
||||
: RecyclerView.Adapter<IconPickerAdapter<I>.CustomIconViewHolder>() {
|
||||
|
||||
private val inflater: LayoutInflater = LayoutInflater.from(context)
|
||||
|
||||
@@ -23,6 +24,10 @@ class IconAdapter<I: IconImageDraw>(val context: Context, val tintIcon: Int)
|
||||
val lastPosition: Int
|
||||
get() = iconList.lastIndex
|
||||
|
||||
fun containsIcon(icon: I): Boolean {
|
||||
return iconList.contains(icon)
|
||||
}
|
||||
|
||||
fun addIcon(icon: I) {
|
||||
if (!iconList.contains(icon)) {
|
||||
iconList.add(icon)
|
||||
@@ -30,6 +35,38 @@ class IconAdapter<I: IconImageDraw>(val context: Context, val tintIcon: Int)
|
||||
}
|
||||
}
|
||||
|
||||
fun updateIcon(icon: I) {
|
||||
if (iconList.contains(icon)) {
|
||||
iconList[iconList.indexOf(icon)] = icon
|
||||
notifyItemChanged(iconList.indexOf(icon))
|
||||
}
|
||||
}
|
||||
|
||||
fun removeIcon(icon: I) {
|
||||
if (iconList.contains(icon)) {
|
||||
val position = iconList.indexOf(icon)
|
||||
iconList.remove(icon)
|
||||
notifyItemRemoved(position)
|
||||
}
|
||||
}
|
||||
|
||||
fun containsAnySelectedIcon(): Boolean {
|
||||
return iconList.firstOrNull { it.selected } != null
|
||||
}
|
||||
|
||||
fun deselectAllIcons() {
|
||||
iconList.forEachIndexed { index, icon ->
|
||||
if (icon.selected) {
|
||||
icon.selected = false
|
||||
notifyItemChanged(index)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun getSelectedIcons(): List<I> {
|
||||
return iconList.filter { it.selected }
|
||||
}
|
||||
|
||||
fun setList(icons: List<I>) {
|
||||
iconList.clear()
|
||||
icons.forEach { iconImage ->
|
||||
@@ -46,7 +83,14 @@ class IconAdapter<I: IconImageDraw>(val context: Context, val tintIcon: Int)
|
||||
override fun onBindViewHolder(holder: CustomIconViewHolder, position: Int) {
|
||||
val icon = iconList[position]
|
||||
iconDrawableFactory?.assignDatabaseIcon(holder.iconImageView, icon, tintIcon)
|
||||
holder.itemView.setOnClickListener { iconPickerListener?.iconPicked(icon) }
|
||||
holder.itemView.setBackgroundColor(if (icon.selected) Color.RED else Color.TRANSPARENT)
|
||||
holder.itemView.setOnClickListener {
|
||||
iconPickerListener?.onIconClickListener(icon)
|
||||
}
|
||||
holder.itemView.setOnLongClickListener {
|
||||
iconPickerListener?.onIconLongClickListener(icon)
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
override fun getItemCount(): Int {
|
||||
@@ -54,7 +98,8 @@ class IconAdapter<I: IconImageDraw>(val context: Context, val tintIcon: Int)
|
||||
}
|
||||
|
||||
interface IconPickerListener<I: IconImageDraw> {
|
||||
fun iconPicked(icon: I)
|
||||
fun onIconClickListener(icon: I)
|
||||
fun onIconLongClickListener(icon: I)
|
||||
}
|
||||
|
||||
inner class CustomIconViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
|
||||
@@ -121,8 +121,9 @@ class Database {
|
||||
return mDatabaseKDBX?.buildNewCustomIcon(cacheDirectory)
|
||||
}
|
||||
|
||||
fun removeCustomIcon(iconUUID: UUID) {
|
||||
iconsManager.removeCustomIcon(iconUUID)
|
||||
fun removeCustomIcon(customIcon: IconImageCustom) {
|
||||
iconDrawableFactory.clearFromCache(customIcon)
|
||||
iconsManager.removeCustomIcon(customIcon.uuid)
|
||||
}
|
||||
|
||||
val allowName: Boolean
|
||||
|
||||
@@ -22,7 +22,7 @@ package com.kunzisoft.keepass.database.element.icon
|
||||
import android.os.Parcel
|
||||
import android.os.Parcelable
|
||||
|
||||
class IconImage() : Parcelable, IconImageDraw {
|
||||
class IconImage() : IconImageDraw(), Parcelable {
|
||||
|
||||
var standard: IconImageStandard = IconImageStandard()
|
||||
var custom: IconImageCustom = IconImageCustom()
|
||||
|
||||
@@ -1,8 +1,29 @@
|
||||
/*
|
||||
* 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.database.element.icon
|
||||
|
||||
interface IconImageDraw {
|
||||
abstract class IconImageDraw {
|
||||
|
||||
var selected = false
|
||||
/**
|
||||
* Only to retrieve an icon image to Draw, to not use as object to manipulate
|
||||
*/
|
||||
fun getIconImageToDraw(): IconImage
|
||||
abstract fun getIconImageToDraw(): IconImage
|
||||
}
|
||||
@@ -208,6 +208,13 @@ class IconDrawableFactory(private val retrieveCipherKey : () -> Database.LoadedK
|
||||
getIconDrawable(resources, icon.custom)
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear a specific icon from the cache
|
||||
*/
|
||||
fun clearFromCache(icon: IconImageCustom) {
|
||||
customIconMap.remove(icon.uuid)
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear the cache of icons
|
||||
*/
|
||||
|
||||
@@ -1,69 +1,57 @@
|
||||
package com.kunzisoft.keepass.viewmodels
|
||||
|
||||
import android.content.ContentResolver
|
||||
import android.net.Uri
|
||||
import android.os.Parcel
|
||||
import android.os.Parcelable
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import androidx.lifecycle.ViewModel
|
||||
import com.kunzisoft.keepass.database.element.Database
|
||||
import com.kunzisoft.keepass.database.element.icon.IconImageCustom
|
||||
import com.kunzisoft.keepass.database.element.icon.IconImageStandard
|
||||
import com.kunzisoft.keepass.tasks.BinaryStreamManager.resizeBitmapAndStoreDataInBinaryFile
|
||||
import kotlinx.coroutines.*
|
||||
import java.io.File
|
||||
|
||||
|
||||
class IconPickerViewModel: ViewModel() {
|
||||
|
||||
private val mainScope = CoroutineScope(Dispatchers.Main)
|
||||
|
||||
val iconStandardSelected: MutableLiveData<IconImageStandard> by lazy {
|
||||
val standardIconPicked: MutableLiveData<IconImageStandard> by lazy {
|
||||
MutableLiveData<IconImageStandard>()
|
||||
}
|
||||
|
||||
val iconCustomSelected: MutableLiveData<IconImageCustom> by lazy {
|
||||
val customIconPicked: MutableLiveData<IconImageCustom> by lazy {
|
||||
MutableLiveData<IconImageCustom>()
|
||||
}
|
||||
|
||||
val iconCustomAdded: MutableLiveData<IconCustomState> by lazy {
|
||||
val customIconsSelected: MutableLiveData<List<IconImageCustom>> by lazy {
|
||||
MutableLiveData<List<IconImageCustom>>()
|
||||
}
|
||||
|
||||
val customIconAdded: MutableLiveData<IconCustomState> by lazy {
|
||||
MutableLiveData<IconCustomState>()
|
||||
}
|
||||
|
||||
fun selectIconStandard(icon: IconImageStandard) {
|
||||
iconStandardSelected.value = icon
|
||||
val customIconRemoved: MutableLiveData<IconCustomState> by lazy {
|
||||
MutableLiveData<IconCustomState>()
|
||||
}
|
||||
|
||||
fun selectIconCustom(icon: IconImageCustom) {
|
||||
iconCustomSelected.value = icon
|
||||
fun pickStandardIcon(icon: IconImageStandard) {
|
||||
standardIconPicked.value = icon
|
||||
}
|
||||
|
||||
fun addCustomIcon(database: Database,
|
||||
contentResolver: ContentResolver,
|
||||
iconDir: File,
|
||||
iconToUploadUri: Uri) {
|
||||
mainScope.launch {
|
||||
withContext(Dispatchers.IO) {
|
||||
// on Progress with thread
|
||||
val asyncResult: Deferred<IconImageCustom?> = async {
|
||||
database.buildNewCustomIcon(iconDir)?.let { customIcon ->
|
||||
resizeBitmapAndStoreDataInBinaryFile(contentResolver,
|
||||
iconToUploadUri, customIcon.binaryFile)
|
||||
customIcon
|
||||
}
|
||||
}
|
||||
withContext(Dispatchers.Main) {
|
||||
asyncResult.await()?.let { customIcon ->
|
||||
var error = false
|
||||
if (customIcon.binaryFile.length <= 0) {
|
||||
database.removeCustomIcon(customIcon.uuid)
|
||||
error = true
|
||||
}
|
||||
iconCustomAdded.value = IconCustomState(customIcon, error)
|
||||
fun pickCustomIcon(icon: IconImageCustom) {
|
||||
customIconPicked.value = icon
|
||||
}
|
||||
|
||||
fun selectCustomIcons(icons: List<IconImageCustom>) {
|
||||
customIconsSelected.value = icons
|
||||
}
|
||||
|
||||
fun deselectAllCustomIcons() {
|
||||
customIconsSelected.value = listOf()
|
||||
}
|
||||
|
||||
fun addCustomIcon(customIcon: IconCustomState) {
|
||||
customIconAdded.value = customIcon
|
||||
}
|
||||
|
||||
fun removeCustomIcon(customIcon: IconCustomState) {
|
||||
customIconRemoved.value = customIcon
|
||||
}
|
||||
|
||||
data class IconCustomState(val iconCustom: IconImageCustom, val error: Boolean): Parcelable {
|
||||
|
||||
27
app/src/main/res/menu/icon.xml
Normal file
27
app/src/main/res/menu/icon.xml
Normal file
@@ -0,0 +1,27 @@
|
||||
<?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/>.
|
||||
-->
|
||||
<menu xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||
<item android:id="@+id/menu_delete"
|
||||
android:icon="@drawable/ic_delete_forever_white_24dp"
|
||||
android:title="@string/menu_delete"
|
||||
android:orderInCategory="10"
|
||||
app:showAsAction="ifRoom" />
|
||||
</menu>
|
||||
@@ -140,6 +140,7 @@
|
||||
<string name="error_rebuild_list">Unable to properly rebuild the list.</string>
|
||||
<string name="error_file_to_big">The file you are trying to upload is too big.</string>
|
||||
<string name="error_upload_file">An error occurred while uploading the file data.</string>
|
||||
<string name="error_remove_file">An error occurred while removing the file data.</string>
|
||||
<string name="field_name">Field name</string>
|
||||
<string name="field_value">Field value</string>
|
||||
<string name="file_not_found_content">Could not find file. Try reopening it from your file browser.</string>
|
||||
|
||||
Reference in New Issue
Block a user