FilterProfilesFragment: Add rename and delete buttons + undo function

This commit is contained in:
Johan von Forstner
2021-01-03 10:45:56 +01:00
parent 595e6e9a8f
commit 8bf33c7384
8 changed files with 183 additions and 58 deletions

View File

@@ -1,16 +1,23 @@
package net.vonforst.evmap.adapter
import android.annotation.SuppressLint
import android.view.MotionEvent
import android.view.animation.AccelerateInterpolator
import androidx.recyclerview.widget.ItemTouchHelper
import net.vonforst.evmap.R
import net.vonforst.evmap.databinding.ItemFilterProfileBinding
import net.vonforst.evmap.storage.FilterProfile
class FilterProfilesAdapter(val dragHelper: ItemTouchHelper) : DataBindingAdapter<FilterProfile>() {
class FilterProfilesAdapter(
val dragHelper: ItemTouchHelper,
val onDelete: (FilterProfile) -> Unit,
val onRename: (FilterProfile) -> Unit
) : DataBindingAdapter<FilterProfile>() {
init {
setHasStableIds(true)
}
@SuppressLint("ClickableViewAccessibility")
override fun bind(
holder: ViewHolder<FilterProfile>,
item: FilterProfile
@@ -24,6 +31,20 @@ class FilterProfilesAdapter(val dragHelper: ItemTouchHelper) : DataBindingAdapte
}
false
}
binding.foreground.translationX = 0f
binding.btnDelete.setOnClickListener {
binding.foreground.animate()
.translationX(binding.foreground.width.toFloat())
.setDuration(250)
.setInterpolator(AccelerateInterpolator())
.withEndAction {
onDelete(item)
}
.start()
}
binding.btnRename.setOnClickListener {
onRename(item)
}
}
override fun getItemId(position: Int): Long {

View File

@@ -1,14 +1,7 @@
package net.vonforst.evmap.fragment
import android.content.Context
import android.content.DialogInterface
import android.os.Bundle
import android.view.*
import android.view.inputmethod.EditorInfo
import android.view.inputmethod.InputMethodManager
import android.widget.EditText
import android.widget.FrameLayout
import androidx.appcompat.app.AlertDialog
import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.widget.Toolbar
import androidx.databinding.DataBindingUtil
@@ -24,6 +17,7 @@ import net.vonforst.evmap.MapsActivity
import net.vonforst.evmap.R
import net.vonforst.evmap.adapter.FiltersAdapter
import net.vonforst.evmap.databinding.FragmentFilterBinding
import net.vonforst.evmap.ui.showEditTextDialog
import net.vonforst.evmap.viewmodel.FilterViewModel
import net.vonforst.evmap.viewmodel.viewModelFactory
@@ -96,54 +90,22 @@ class FilterFragment : Fragment() {
true
}
R.id.menu_save_profile -> {
val container = FrameLayout(requireContext())
container.setPadding(
(16 * resources.displayMetrics.density).toInt(), 0,
(16 * resources.displayMetrics.density).toInt(), 0
)
val input = EditText(requireContext())
input.isSingleLine = true
vm.filterProfile.value?.let { profile ->
input.setText(profile.name)
}
container.addView(input)
val dialog = AlertDialog.Builder(requireContext())
.setTitle(R.string.save_as_profile)
.setMessage(R.string.save_profile_enter_name)
.setView(container)
.setPositiveButton(R.string.ok) { di, button ->
lifecycleScope.launch {
vm.saveAsProfile(input.text.toString())
findNavController().popBackStack()
}
showEditTextDialog(requireContext()) { dialog, input ->
vm.filterProfile.value?.let { profile ->
input.setText(profile.name)
}
.setNegativeButton(R.string.cancel) { di, button ->
}.show()
// move dialog to top
val attrs = dialog.window?.attributes?.apply {
gravity = Gravity.TOP
}
dialog.window?.attributes = attrs
// focus and show keyboard
input.requestFocus()
input.postDelayed({
val imm =
requireContext().getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
imm.showSoftInput(input, InputMethodManager.SHOW_IMPLICIT)
}, 100)
input.setOnEditorActionListener { _, actionId, event ->
if (actionId == EditorInfo.IME_ACTION_DONE) {
val text = input.text
if (text != null) {
dialog.getButton(DialogInterface.BUTTON_POSITIVE).performClick()
return@setOnEditorActionListener true
dialog.setTitle(R.string.save_as_profile)
.setMessage(R.string.save_profile_enter_name)
.setPositiveButton(R.string.ok) { di, button ->
lifecycleScope.launch {
vm.saveAsProfile(input.text.toString())
findNavController().popBackStack()
}
}
.setNegativeButton(R.string.cancel) { di, button ->
}
}
false
}
true
}

View File

@@ -11,18 +11,23 @@ import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.widget.Toolbar
import androidx.fragment.app.Fragment
import androidx.fragment.app.viewModels
import androidx.lifecycle.lifecycleScope
import androidx.navigation.fragment.findNavController
import androidx.navigation.ui.setupWithNavController
import androidx.recyclerview.widget.DividerItemDecoration
import androidx.recyclerview.widget.ItemTouchHelper
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.google.android.material.snackbar.Snackbar
import kotlinx.coroutines.launch
import net.vonforst.evmap.MapsActivity
import net.vonforst.evmap.R
import net.vonforst.evmap.adapter.DataBindingAdapter
import net.vonforst.evmap.adapter.FilterProfilesAdapter
import net.vonforst.evmap.databinding.FragmentFilterProfilesBinding
import net.vonforst.evmap.databinding.ItemFilterProfileBinding
import net.vonforst.evmap.storage.FilterProfile
import net.vonforst.evmap.ui.showEditTextDialog
import net.vonforst.evmap.viewmodel.FilterProfilesViewModel
import net.vonforst.evmap.viewmodel.viewModelFactory
@@ -34,6 +39,7 @@ class FilterProfilesFragment : Fragment() {
FilterProfilesViewModel(requireActivity().application)
}
})
private var deleteSnackbar: Snackbar? = null
override fun onCreateView(
inflater: LayoutInflater,
@@ -86,7 +92,8 @@ class FilterProfilesFragment : Fragment() {
}
override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) {
vm.delete(viewHolder.itemId)
val fp = vm.filterProfiles.value?.find { it.id == viewHolder.itemId }
fp?.let { delete(it) }
}
override fun onSelectedChanged(viewHolder: RecyclerView.ViewHolder?, actionState: Int) {
@@ -166,7 +173,24 @@ class FilterProfilesFragment : Fragment() {
}
})
val adapter = FilterProfilesAdapter(touchHelper)
val adapter = FilterProfilesAdapter(touchHelper, onDelete = { fp ->
delete(fp)
}, onRename = { fp ->
showEditTextDialog(requireContext()) { dialog, input ->
input.setText(fp.name)
dialog.setTitle(R.string.rename)
.setMessage(R.string.save_profile_enter_name)
.setPositiveButton(R.string.ok) { di, button ->
lifecycleScope.launch {
vm.insert(fp.copy(name = input.text.toString()))
}
}
.setNegativeButton(R.string.cancel) { di, button ->
}
}
})
binding.filterProfilesList.apply {
this.adapter = adapter
layoutManager =
@@ -184,4 +208,21 @@ class FilterProfilesFragment : Fragment() {
findNavController().popBackStack()
}
}
fun delete(fp: FilterProfile) {
vm.delete(fp.id)
deleteSnackbar?.dismiss()
view?.let {
val snackbar = Snackbar.make(
it,
getString(R.string.deleted_filterprofile, fp.name),
Snackbar.LENGTH_LONG
).setAction(R.string.undo) {
vm.insert(fp.copy(id = 0))
}
deleteSnackbar = snackbar
snackbar.show()
}
}
}

View File

@@ -0,0 +1,63 @@
package net.vonforst.evmap.ui
import android.content.Context
import android.content.DialogInterface
import android.view.Gravity
import android.view.View
import android.view.inputmethod.EditorInfo
import android.view.inputmethod.InputMethodManager
import android.widget.EditText
import android.widget.FrameLayout
import androidx.appcompat.app.AlertDialog
private fun dialogEditText(ctx: Context): Pair<View, EditText> {
val container = FrameLayout(ctx)
container.setPadding(
(16 * ctx.resources.displayMetrics.density).toInt(), 0,
(16 * ctx.resources.displayMetrics.density).toInt(), 0
)
val input = EditText(ctx)
input.isSingleLine = true
container.addView(input)
return container to input
}
fun showEditTextDialog(
ctx: Context,
customize: (AlertDialog.Builder, EditText) -> Unit
): AlertDialog {
val (container, input) = dialogEditText(ctx)
val dialogBuilder = AlertDialog.Builder(ctx)
.setView(container)
customize(dialogBuilder, input)
val dialog = dialogBuilder.show()
// move dialog to top
val attrs = dialog.window?.attributes?.apply {
gravity = Gravity.TOP
}
dialog.window?.attributes = attrs
// focus and show keyboard
input.requestFocus()
input.postDelayed({
val imm =
ctx.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
imm.showSoftInput(input, InputMethodManager.SHOW_IMPLICIT)
}, 100)
input.setOnEditorActionListener { _, actionId, _ ->
if (actionId == EditorInfo.IME_ACTION_DONE) {
val text = input.text
val button = dialog.getButton(DialogInterface.BUTTON_POSITIVE)
if (text != null && button != null) {
button.performClick()
return@setOnEditorActionListener true
}
}
false
}
return dialog
}

View File

@@ -27,6 +27,12 @@ class FilterProfilesViewModel(application: Application) : AndroidViewModel(appli
}
}
fun insert(item: FilterProfile) {
viewModelScope.launch {
db.filterProfileDao().insert(item)
}
}
fun reorderProfiles(list: List<FilterProfile>) {
viewModelScope.launch {
db.filterProfileDao().update(*list.toTypedArray())