haunt / commit
Color picker + fix purple clustering
.../main/java/net/fuzzyworld/haunt/MainActivity.kt | 1 + .../net/fuzzyworld/haunt/debug/DebugViewModel.kt | 11 +++- .../net/fuzzyworld/haunt/debug/PointsAdapter.kt | 61 ++++++++++++++++++++++ 3 files changed, 71 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/net/fuzzyworld/haunt/MainActivity.kt b/app/src/main/java/net/fuzzyworld/haunt/MainActivity.kt index f031591..df5a0db 100644 --- a/app/src/main/java/net/fuzzyworld/haunt/MainActivity.kt +++ b/app/src/main/java/net/fuzzyworld/haunt/MainActivity.kt @@ -25,6 +25,7 @@ class MainActivity : AppCompatActivity() { private val adapter = PointsAdapter( onRename = { id, name -> vm.renamePoint(id, name) }, onDelete = { id -> vm.deletePoint(id) }, + onRecolor = { id, color -> vm.recolorPoint(id, color) }, ) private val requestPermission = registerForActivityResult( diff --git a/app/src/main/java/net/fuzzyworld/haunt/debug/DebugViewModel.kt b/app/src/main/java/net/fuzzyworld/haunt/debug/DebugViewModel.kt index 2cf3fc5..680f00f 100644 --- a/app/src/main/java/net/fuzzyworld/haunt/debug/DebugViewModel.kt +++ b/app/src/main/java/net/fuzzyworld/haunt/debug/DebugViewModel.kt @@ -102,8 +102,15 @@ class DebugViewModel(application: Application) : AndroidViewModel(application) { store.save(_points.value) } + fun recolorPoint(id: String, color: Int) { + _points.value = _points.value.map { if (it.id == id) it.copy(color = color) else it } + store.save(_points.value) + } + + // Golden-angle stepping guarantees maximum hue separation between successive colors + private var nextHue = (0..360).random().toFloat() private fun randomColor(): Int { - val hue = (Math.random() * 360).toFloat() - return Color.HSVToColor(floatArrayOf(hue, 0.70f, 0.80f)) + nextHue = (nextHue + 137.5f) % 360f + return Color.HSVToColor(floatArrayOf(nextHue, 0.72f, 0.82f)) } } diff --git a/app/src/main/java/net/fuzzyworld/haunt/debug/PointsAdapter.kt b/app/src/main/java/net/fuzzyworld/haunt/debug/PointsAdapter.kt index a9cf5dd..bbaaba4 100644 --- a/app/src/main/java/net/fuzzyworld/haunt/debug/PointsAdapter.kt +++ b/app/src/main/java/net/fuzzyworld/haunt/debug/PointsAdapter.kt @@ -1,13 +1,34 @@ package net.fuzzyworld.haunt.debug +import android.graphics.Color +import android.graphics.drawable.GradientDrawable import android.view.LayoutInflater +import android.view.View import android.view.ViewGroup +import android.widget.GridLayout +import androidx.appcompat.app.AlertDialog import androidx.recyclerview.widget.RecyclerView import net.fuzzyworld.haunt.databinding.ItemDebugPointBinding +private val PRESET_COLORS = listOf( + Color.HSVToColor(floatArrayOf(0f, 0.75f, 0.90f)), // red + Color.HSVToColor(floatArrayOf(25f, 0.85f, 0.90f)), // orange + Color.HSVToColor(floatArrayOf(50f, 0.80f, 0.85f)), // yellow + Color.HSVToColor(floatArrayOf(90f, 0.70f, 0.80f)), // lime + Color.HSVToColor(floatArrayOf(130f, 0.70f, 0.75f)), // green + Color.HSVToColor(floatArrayOf(165f, 0.70f, 0.78f)), // teal + Color.HSVToColor(floatArrayOf(195f, 0.75f, 0.85f)), // cyan + Color.HSVToColor(floatArrayOf(215f, 0.75f, 0.90f)), // sky + Color.HSVToColor(floatArrayOf(240f, 0.70f, 0.90f)), // blue + Color.HSVToColor(floatArrayOf(270f, 0.70f, 0.85f)), // purple + Color.HSVToColor(floatArrayOf(310f, 0.70f, 0.85f)), // magenta + Color.HSVToColor(floatArrayOf(340f, 0.75f, 0.90f)), // pink +) + class PointsAdapter( private val onRename: (id: String, newName: String) -> Unit, private val onDelete: (id: String) -> Unit, + private val onRecolor: (id: String, color: Int) -> Unit, ) : RecyclerView.Adapter<PointsAdapter.ViewHolder>() { private var points: List<DebugPoint> = emptyList() @@ -36,6 +57,7 @@ class PointsAdapter( if (!hasFocus) onRename(point.id, b.nameEdit.text.toString().trim().ifEmpty { point.name }) } b.deleteButton.setOnClickListener { onDelete(point.id) } + b.colorSwatch.setOnClickListener { showColorPicker(point) } val x = axisBssids.getOrNull(0)?.let { point.readings[it] } val y = axisBssids.getOrNull(1)?.let { point.readings[it] } @@ -45,5 +67,44 @@ class PointsAdapter( else -> "" } } + + private fun showColorPicker(point: DebugPoint) { + val ctx = b.root.context + val dp = ctx.resources.displayMetrics.density + val chipSize = (48 * dp).toInt() + val chipMargin = (8 * dp).toInt() + + val grid = GridLayout(ctx).apply { + columnCount = 4 + setPadding(chipMargin, chipMargin, chipMargin, chipMargin) + } + + var dialog: AlertDialog? = null + PRESET_COLORS.forEach { color -> + val chip = View(ctx).apply { + background = GradientDrawable().apply { + shape = GradientDrawable.OVAL + setColor(color) + if (color == point.color) setStroke((3 * dp).toInt(), Color.BLACK) + } + setOnClickListener { + onRecolor(point.id, color) + dialog?.dismiss() + } + } + val lp = GridLayout.LayoutParams().apply { + width = chipSize; height = chipSize + setMargins(chipMargin, chipMargin, chipMargin, chipMargin) + } + grid.addView(chip, lp) + } + + dialog = AlertDialog.Builder(ctx) + .setTitle("Pick a color") + .setView(grid) + .setNegativeButton("Cancel", null) + .create() + dialog.show() + } } }