Compare commits

...

2 Commits

Author SHA1 Message Date
fgerber
411bd5c4de Adapt group selection UI
- Enable group deletion (including mapped countries)
- If only one group exists, no need to go through the dialog
2024-03-05 18:57:48 +01:00
fgerber
872f31746e Create default group and delete group (WIP) 2024-03-04 22:35:27 +01:00
13 changed files with 133 additions and 43 deletions

View File

@ -88,10 +88,15 @@ class GeolocListAdapter(
fun addListeners(el: Pair<GeoLoc, Boolean>, expandLambda: () -> Boolean) { fun addListeners(el: Pair<GeoLoc, Boolean>, expandLambda: () -> Boolean) {
textView.setOnClickListener { expandLambda() } textView.setOnClickListener { expandLambda() }
checkBox.setOnClickListener { checkBox.setOnClickListener {
val dialogFragment = EditPlaceColorFragment(this)
selected_geoloc = el.first selected_geoloc = el.first
selected_group = null if (groups!!.size() != 1) {
dialogFragment.show(ctx.supportFragmentManager, "AddColorDialogFragment") val dialogFragment = EditPlaceColorFragment(this)
selected_group = null
dialogFragment.show(ctx.supportFragmentManager, "AddColorDialogFragment")
} else {
selected_group = groups!!.getUniqueEntry()!!
onColorDialogDismiss(false)
}
} }
} }

View File

@ -7,12 +7,15 @@ import android.widget.Button
import androidx.fragment.app.DialogFragment import androidx.fragment.app.DialogFragment
import androidx.fragment.app.FragmentActivity import androidx.fragment.app.FragmentActivity
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import net.helcel.beendroid.R import net.helcel.beendroid.R
import net.helcel.beendroid.activity.fragment.EditGroupAddFragment import net.helcel.beendroid.activity.fragment.EditGroupAddFragment
import net.helcel.beendroid.helper.Groups import net.helcel.beendroid.helper.Groups
import net.helcel.beendroid.helper.getContrastColor import net.helcel.beendroid.helper.getContrastColor
import net.helcel.beendroid.helper.groups import net.helcel.beendroid.helper.groups
import net.helcel.beendroid.helper.saveData
import net.helcel.beendroid.helper.selected_group import net.helcel.beendroid.helper.selected_group
import net.helcel.beendroid.helper.visits
class GroupListAdapter(private val activity: FragmentActivity, private val selectDialog: DialogFragment?) : RecyclerView.Adapter<GroupListAdapter.GroupViewHolder>() { class GroupListAdapter(private val activity: FragmentActivity, private val selectDialog: DialogFragment?) : RecyclerView.Adapter<GroupListAdapter.GroupViewHolder>() {
@ -29,7 +32,7 @@ class GroupListAdapter(private val activity: FragmentActivity, private val selec
return groups!!.size() return groups!!.size()
} }
class GroupViewHolder(itemView: View, private val activity: FragmentActivity, private val selectDialog: DialogFragment?) : RecyclerView.ViewHolder(itemView) { inner class GroupViewHolder(itemView: View, private val activity: FragmentActivity, private val selectDialog: DialogFragment?) : RecyclerView.ViewHolder(itemView) {
private val color: Button = itemView.findViewById(R.id.group_color) private val color: Button = itemView.findViewById(R.id.group_color)
fun bind(entry: Pair<Int, Groups.Group>) { fun bind(entry: Pair<Int, Groups.Group>) {
@ -37,7 +40,7 @@ class GroupListAdapter(private val activity: FragmentActivity, private val selec
color.setBackgroundColor(entry.second.color.color) color.setBackgroundColor(entry.second.color.color)
color.setTextColor(getContrastColor(entry.second.color.color)) color.setTextColor(getContrastColor(entry.second.color.color))
color.setOnClickListener { color.setOnClickListener {
if(selectDialog==null) { if (selectDialog == null) {
val dialogFragment = EditGroupAddFragment(entry.first) { val dialogFragment = EditGroupAddFragment(entry.first) {
val newEntry = groups!!.getGroupFromKey(entry.first)!! val newEntry = groups!!.getGroupFromKey(entry.first)!!
color.text = newEntry.name color.text = newEntry.name
@ -48,11 +51,32 @@ class GroupListAdapter(private val activity: FragmentActivity, private val selec
activity.supportFragmentManager, activity.supportFragmentManager,
"AddColorDialogFragment" "AddColorDialogFragment"
) )
}else{ } else {
selected_group = entry.second selected_group = entry.second
selectDialog.dismiss() selectDialog.dismiss()
} }
} }
color.setOnLongClickListener {
if (selectDialog == null) {
MaterialAlertDialogBuilder(activity)
.setMessage(R.string.delete_group)
.setPositiveButton(android.R.string.ok) { _, _ ->
// Remove all countries belonging to that group
val key = entry.first
visits!!.deleteVisited(key)
// Delete the group
val pos = groups!!.findGroupPos(key)
groups!!.deleteGroup(key)
saveData()
this@GroupListAdapter.notifyItemRemoved(pos)
}
.setNegativeButton(android.R.string.cancel) { _, _ -> }
.show()
}
true
}
} }
} }
} }

View File

@ -8,8 +8,8 @@ import net.helcel.beendroid.activity.fragment.EditGroupFragment
import net.helcel.beendroid.activity.fragment.EditPlaceFragment import net.helcel.beendroid.activity.fragment.EditPlaceFragment
private val tabArray = arrayOf( private val tabArray = arrayOf(
"Places",
"Groups", "Groups",
"Places"
) )
class ViewPagerAdapter (fragmentManager: FragmentManager, lifecycle: Lifecycle) : class ViewPagerAdapter (fragmentManager: FragmentManager, lifecycle: Lifecycle) :
FragmentStateAdapter(fragmentManager, lifecycle) { FragmentStateAdapter(fragmentManager, lifecycle) {
@ -24,10 +24,10 @@ class ViewPagerAdapter (fragmentManager: FragmentManager, lifecycle: Lifecycle)
override fun createFragment(position: Int): Fragment { override fun createFragment(position: Int): Fragment {
when (position) { when (position) {
0 -> return EditGroupFragment() 0 -> return EditPlaceFragment()
1 -> return EditPlaceFragment() 1 -> return EditGroupFragment()
} }
return EditGroupFragment() return EditPlaceFragment()
} }
} }

View File

@ -23,15 +23,16 @@ import net.helcel.beendroid.helper.saveData
import java.lang.Exception import java.lang.Exception
class EditGroupAddFragment(private val key: Int =0, val onAddCb : (Int)->Unit) : DialogFragment() { class EditGroupAddFragment(private val key: Int = 0, val onAddCb: (Int) -> Unit) :
DialogFragment() {
private lateinit var colorNameEditText: TextInputEditText private lateinit var colorNameEditText: TextInputEditText
private lateinit var colorEditText: TextInputEditText private lateinit var colorEditText: TextInputEditText
private lateinit var colorView : View private lateinit var colorView: View
private lateinit var colorEditR : Slider private lateinit var colorEditR: Slider
private lateinit var colorEditG : Slider private lateinit var colorEditG: Slider
private lateinit var colorEditB : Slider private lateinit var colorEditB: Slider
private val grp = groups!!.getGroupFromKey(key) private val grp = groups!!.getGroupFromKey(key)
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
@ -48,54 +49,68 @@ class EditGroupAddFragment(private val key: Int =0, val onAddCb : (Int)->Unit) :
colorEditG = view.findViewById(R.id.colorG) colorEditG = view.findViewById(R.id.colorG)
colorEditB = view.findViewById(R.id.colorB) colorEditB = view.findViewById(R.id.colorB)
setupSlider(colorEditR,(grp?.color?.color?.red ?: 0)/ 255F) setupSlider(colorEditR, (grp?.color?.color?.red ?: 0) / 255F)
setupSlider(colorEditG,(grp?.color?.color?.green ?: 0)/ 255F) setupSlider(colorEditG, (grp?.color?.color?.green ?: 0) / 255F)
setupSlider(colorEditB,(grp?.color?.color?.blue ?: 0)/ 255F) setupSlider(colorEditB, (grp?.color?.color?.blue ?: 0) / 255F)
setupText(colorEditText,grp) setupText(colorEditText, grp)
colorView.background = ColorDrawable(grp?.color?.color ?: 0) colorView.background = ColorDrawable(grp?.color?.color ?: 0)
colorNameEditText.setText(grp?.name ?: "") colorNameEditText.setText(grp?.name ?: "")
builder.setView(view) builder.setView(view)
.setPositiveButton("Ok") { _: DialogInterface?, _: Int -> .setPositiveButton(android.R.string.ok) { _: DialogInterface?, _: Int ->
val name = colorNameEditText.text.toString() val name = colorNameEditText.text.toString()
val color = colorEditText.text.toString() val color = colorEditText.text.toString()
val key = (if (key!=0) key else groups!!.genKey()) val key = (if (key != 0) key else groups!!.genKey())
groups!!.setGroup(key,name, ColorDrawable(Color.parseColor("#$color"))) groups!!.setGroup(key, name, ColorDrawable(Color.parseColor("#$color")))
saveData() saveData()
onAddCb(key) onAddCb(key)
} }
.setNegativeButton( .setNegativeButton(android.R.string.cancel) { dialog: DialogInterface, _: Int ->
"Cancel" dialog.cancel()
) { dialog: DialogInterface, _: Int -> dialog.cancel() } }
return builder.create() return builder.create()
} }
private fun setupText(s: TextInputEditText, grp: Groups.Group?) { private fun setupText(s: TextInputEditText, grp: Groups.Group?) {
s.setText( colorToHex6(ColorDrawable(grp?.color?.color ?: 0)).substring(1)) s.setText(colorToHex6(ColorDrawable(grp?.color?.color ?: 0)).substring(1))
s.addTextChangedListener(EditTextListener(colorEditR, colorEditG, colorEditB, colorEditText, colorView)) s.addTextChangedListener(
EditTextListener(
colorEditR,
colorEditG,
colorEditB,
colorEditText,
colorView
)
)
} }
private fun setupSlider(s: Slider, v: Float){ private fun setupSlider(s: Slider, v: Float) {
s.valueFrom = 0F s.valueFrom = 0F
s.valueTo = 1F s.valueTo = 1F
s.value = v s.value = v
s.addOnChangeListener(SliderOnChangeListener( colorEditR, colorEditG, colorEditB,colorEditText, colorView)) s.addOnChangeListener(
SliderOnChangeListener(
colorEditR,
colorEditG,
colorEditB,
colorEditText,
colorView
)
)
} }
} }
private class EditTextListener( private class EditTextListener(
private val colorEditR: Slider, private val colorEditR: Slider,
private val colorEditG: Slider, private val colorEditG: Slider,
private val colorEditB: Slider, private val colorEditB: Slider,
private val colorEditText: TextInputEditText, private val colorEditText: TextInputEditText,
private val colorView: View private val colorView: View
): TextWatcher { ) : TextWatcher {
override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) { override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {
@ -105,10 +120,10 @@ private class EditTextListener(
} }
override fun afterTextChanged(s: Editable?) { override fun afterTextChanged(s: Editable?) {
val col : Color val col: Color
try{ try {
col = Color.valueOf(Color.parseColor("#${colorEditText.text}")) col = Color.valueOf(Color.parseColor("#${colorEditText.text}"))
}catch (e:Exception){ } catch (e: Exception) {
return return
} }
@ -126,9 +141,10 @@ private class SliderOnChangeListener(
private val colorEditB: Slider, private val colorEditB: Slider,
private val colorEditText: TextInputEditText, private val colorEditText: TextInputEditText,
private val colorView: View private val colorView: View
): Slider.OnChangeListener { ) : Slider.OnChangeListener {
override fun onValueChange(slider: Slider, value: Float, fromUser: Boolean) { override fun onValueChange(slider: Slider, value: Float, fromUser: Boolean) {
val rgb = ColorDrawable(Color.argb(1F,colorEditR.value, colorEditG.value, colorEditB.value)) val rgb =
ColorDrawable(Color.argb(1F, colorEditR.value, colorEditG.value, colorEditB.value))
colorEditText.setText(colorToHex6(rgb).substring(1)) colorEditText.setText(colorToHex6(rgb).substring(1))
colorView.background = rgb colorView.background = rgb
} }

View File

@ -2,7 +2,10 @@ package net.helcel.beendroid.helper
import android.content.Context import android.content.Context
import android.content.SharedPreferences import android.content.SharedPreferences
import android.graphics.drawable.ColorDrawable
import androidx.core.content.ContextCompat
import androidx.preference.PreferenceManager import androidx.preference.PreferenceManager
import net.helcel.beendroid.R
import net.helcel.beendroid.countries.GeoLoc import net.helcel.beendroid.countries.GeoLoc
var visits : Visits? = null var visits : Visits? = null
@ -24,6 +27,13 @@ fun loadData(ctx: Context, id:Int) {
groups = if(!groupsString.isNullOrEmpty()) groupsSerial.readFrom(groupsString.byteInputStream()) else groupsSerial.defaultValue groups = if(!groupsString.isNullOrEmpty()) groupsSerial.readFrom(groupsString.byteInputStream()) else groupsSerial.defaultValue
visits = if(!visitsString.isNullOrEmpty()) visitsSerial.readFrom(visitsString.byteInputStream()) else visitsSerial.defaultValue visits = if(!visitsString.isNullOrEmpty()) visitsSerial.readFrom(visitsString.byteInputStream()) else visitsSerial.defaultValue
// Add default group "Visited" with app's color if there is no group already
if (groups!!.size() == 0) {
groups!!.setGroup(1, "Visited", ColorDrawable(ContextCompat.getColor(ctx, R.color.blue)))
saveData()
}
} }
fun saveData() { fun saveData() {

View File

@ -18,6 +18,10 @@ class Groups(val id: Int, private val grps: HashMap<Int,Group>) {
grps[key] = Group(key,name,col) grps[key] = Group(key,name,col)
} }
fun deleteGroup(key: Int) {
grps.remove(key)
}
fun getGroupFromKey(key: Int): Group? { fun getGroupFromKey(key: Int): Group? {
return grps.getOrDefault(key,null) return grps.getOrDefault(key,null)
} }
@ -32,6 +36,15 @@ class Groups(val id: Int, private val grps: HashMap<Int,Group>) {
return grps.size return grps.size
} }
fun getUniqueEntry(): Group? {
assert(size() == 1)
return if (grps.size == 1) {
grps[grps.keys.first()]
} else {
null
}
}
fun getGroupFromPos(pos: Int): Pair<Int,Group> { fun getGroupFromPos(pos: Int): Pair<Int,Group> {
val key = grps.keys.toList()[pos] val key = grps.keys.toList()[pos]
return Pair(key,getGroupFromKey(key)!!) return Pair(key,getGroupFromKey(key)!!)

View File

@ -15,6 +15,15 @@ class Visits(val id: Int, private val locs: HashMap<String,Int>) {
locs[key.code] = b locs[key.code] = b
} }
fun deleteVisited(key: Int) {
val keysToDelete = locs
.filter { it.value == key }
.map { it.key }
keysToDelete.forEach {
locs.remove(it)
}
}
fun getVisited(key: GeoLoc): Int { fun getVisited(key: GeoLoc): Int {
return locs.getOrDefault(key.code,0) return locs.getOrDefault(key.code,0)
} }

View File

@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="?attr/colorOnBackground"
android:pathData="M19,13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z" />
</vector>

View File

@ -5,6 +5,6 @@
android:viewportWidth="960" android:viewportWidth="960"
android:viewportHeight="960"> android:viewportHeight="960">
<path <path
android:fillColor="?attr/background" android:fillColor="@color/blue"
android:pathData="M0,0h960v960h-960z" /> android:pathData="M0,0h960v960h-960z" />
</vector> </vector>

View File

@ -5,7 +5,7 @@
android:viewportHeight="1600"> android:viewportHeight="1600">
<path <path
android:fillColor="@color/blue" android:fillColor="@color/white"
android:fillType="nonZero" android:fillType="nonZero"
android:pathData="m800,1200q-83,0 -156,-31.5Q571,1137 517,1083 463,1029 431.5,956 400,883 400,800 400,717 431.5,644 463,571 517,517 571,463 644,431.5 717,400 800,400q83,0 156,31.5 73,31.5 127,85.5 54,54 85.5,127 31.5,73 31.5,156 0,83 -31.5,156 -31.5,73 -85.5,127 -54,54 -127,85.5 -73,31.5 -156,31.5zM800,1120q134,0 227,-93 93,-93 93,-227 0,-7 -0.5,-14.5 -0.5,-7.5 -0.5,-12.5 -5,29 -27,48 -22,19 -52,19L960,840Q927,840 903.5,816.5 880,793 880,760L880,720L720,720v-80q0,-33 23.5,-56.5Q767,560 800,560h40v0q0,-23 12.5,-40.5Q865,502 883,491q-20,-5 -40.5,-8 -20.5,-3 -42.5,-3 -134,0 -227,93 -93,93 -93,227 0,0 0,0 0,0 0,0h200q66,0 113,47 47,47 47,113v40L720,1000v110q20,5 39.5,7.5 19.5,2.5 40.5,2.5z" android:pathData="m800,1200q-83,0 -156,-31.5Q571,1137 517,1083 463,1029 431.5,956 400,883 400,800 400,717 431.5,644 463,571 517,517 571,463 644,431.5 717,400 800,400q83,0 156,31.5 73,31.5 127,85.5 54,54 85.5,127 31.5,73 31.5,156 0,83 -31.5,156 -31.5,73 -85.5,127 -54,54 -127,85.5 -73,31.5 -156,31.5zM800,1120q134,0 227,-93 93,-93 93,-227 0,-7 -0.5,-14.5 -0.5,-7.5 -0.5,-12.5 -5,29 -27,48 -22,19 -52,19L960,840Q927,840 903.5,816.5 880,793 880,760L880,720L720,720v-80q0,-33 23.5,-56.5Q767,560 800,560h40v0q0,-23 12.5,-40.5Q865,502 883,491q-20,-5 -40.5,-8 -20.5,-3 -42.5,-3 -134,0 -227,93 -93,93 -93,227 0,0 0,0 0,0 0,0h200q66,0 113,47 47,47 47,113v40L720,1000v110q20,5 39.5,7.5 19.5,2.5 40.5,2.5z"
android:strokeWidth="1" android:strokeWidth="1"

View File

@ -8,20 +8,23 @@
android:id="@+id/list" android:id="@+id/list"
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginTop="16dp"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" /> app:layout_constraintTop_toTopOf="parent" />
<Button <com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="@+id/add_group" android:id="@+id/add_group"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginBottom="32dp" android:layout_marginBottom="32dp"
android:layout_marginEnd="32dp"
android:layout_weight="1" android:layout_weight="1"
android:text="@string/group_add" android:text="@string/group_add"
android:src="@drawable/add"
android:backgroundTint="@color/blue"
app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent" />
app:layout_constraintStart_toStartOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout> </androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -26,7 +26,7 @@
app:layout_constraintEnd_toStartOf="@+id/colorR" app:layout_constraintEnd_toStartOf="@+id/colorR"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" app:layout_constraintTop_toTopOf="parent"
android:background="?attr/background"/> android:background="@color/black"/>
<com.google.android.material.slider.Slider <com.google.android.material.slider.Slider

View File

@ -17,6 +17,7 @@
<string name="beendroid_repo">Project repository: https://git.helcel.net/helcel/beendroid\n Feel free to report issues or contribute to the project.</string> <string name="beendroid_repo">Project repository: https://git.helcel.net/helcel/beendroid\n Feel free to report issues or contribute to the project.</string>
<string name="foss_licenses">Free and open source dependencies and licenses</string> <string name="foss_licenses">Free and open source dependencies and licenses</string>
<string name="about_beendroid">About the BeenDroid application</string> <string name="about_beendroid">About the BeenDroid application</string>
<string name="delete_group">Are your sure you want to delete this group and remove all its country mappings?</string>
<string name="group_add">Add</string> <string name="group_add">Add</string>
<string name="logo">Logo</string> <string name="logo">Logo</string>
<string name="name">Name</string> <string name="name">Name</string>