commit d5e0150dad4d44133476b2e43675b593cc610210
parent e3f7c2d5ba5e1016185c1bc8d44e0fa0e1df8b59
Author: massi <mdsiboldi@gmail.com>
Date: Thu, 11 Jul 2024 13:09:34 -0700
save to bytearray, change settings toggle behavior
Diffstat:
5 files changed, 81 insertions(+), 62 deletions(-)
diff --git a/.idea/deploymentTargetSelector.xml b/.idea/deploymentTargetSelector.xml
@@ -4,10 +4,10 @@
<selectionStates>
<SelectionState runConfigName="app">
<option name="selectionMode" value="DROPDOWN" />
- <DropdownSelection timestamp="2024-07-03T09:18:11.113954846Z">
+ <DropdownSelection timestamp="2024-07-03T09:21:41.354400312Z">
<Target type="DEFAULT_BOOT">
<handle>
- <DeviceId pluginId="PhysicalDevice" identifier="serial=1C161FDF600FF6" />
+ <DeviceId pluginId="LocalEmulator" identifier="path=/home/me/.android/avd/Medium_Tablet_API_VanillaIceCream.avd" />
</handle>
</Target>
</DropdownSelection>
diff --git a/app/src/main/java/com/example/fretboardtrainer/MainActivity.kt b/app/src/main/java/com/example/fretboardtrainer/MainActivity.kt
@@ -4,6 +4,7 @@ import android.app.Application
import android.content.Context
import android.content.pm.ActivityInfo
import android.os.Bundle
+import android.util.Log
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.activity.enableEdgeToEdge
@@ -44,8 +45,8 @@ import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.dp
import androidx.datastore.core.DataStore
import androidx.datastore.preferences.core.Preferences
+import androidx.datastore.preferences.core.byteArrayPreferencesKey
import androidx.datastore.preferences.core.edit
-import androidx.datastore.preferences.core.intPreferencesKey
import androidx.datastore.preferences.preferencesDataStore
import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.viewModelScope
@@ -63,29 +64,22 @@ val STRING_COUNT = 6;
val Context.dataStore: DataStore<Preferences> by preferencesDataStore(name = "settings")
-fun serializeBoolList(l: List<Boolean>): Int {
- var num: Int = 0;
- for (b in l) {
- num = num shl 1
- if (b) {
- num = num or 1
+fun encodeBoolList(l: List<Boolean>): ByteArray {
+ return ByteArray(l.size) {
+ when (l[it]) {
+ true -> 1.toByte()
+ else -> 0.toByte()
}
}
- return num
}
-fun deserializeBoolList(num: Int, len: Int): List<Boolean> {
- var l = len - 1
- var n = num
- val list = MutableList<Boolean>(len) { false }
- while (l >= 0) {
- if ((n and 1) == 1) {
- list[l] = true
+fun decodeBoolList(arr: ByteArray): List<Boolean> {
+ return arr.toList().map { n ->
+ when (n) {
+ 1.toByte() -> true
+ else -> false
}
- n = n shr 1
- l--
}
- return list
}
fun weightForFret(fretIdx: Int): Float {
@@ -127,8 +121,9 @@ data class FretfretState(
val lastNote: List<Int>? = null,
)
-val STRING_KEY = intPreferencesKey("string_key")
-val FRET_KEY = intPreferencesKey("fret_key")
+val STRING_KEY = byteArrayPreferencesKey("string_key")
+val FRET_KEY = byteArrayPreferencesKey("fret_key")
+
class FretfretViewModel(application: Application) : AndroidViewModel(application) {
private val _uiState = MutableStateFlow(FretfretState())
@@ -136,32 +131,46 @@ class FretfretViewModel(application: Application) : AndroidViewModel(application
init {
viewModelScope.launch {
- _uiState.update { currentState ->
- currentState.copy(
- stringSettings = application.dataStore.data.map { prefs ->
- when (prefs[STRING_KEY]) {
- is Int -> deserializeBoolList(prefs[STRING_KEY]!!, STRING_COUNT)
- else -> List(STRING_COUNT) { true }
- }
- }.first(),
- fretSettings = application.dataStore.data.map { prefs ->
- when (prefs[FRET_KEY]) {
- is Int -> deserializeBoolList(prefs[FRET_KEY]!!, FRET_COUNT)
- else -> List(FRET_COUNT) { true }
- }
- }.first(),
+ try {
+ _uiState.update { currentState ->
+ currentState.copy(
+ stringSettings = application.dataStore.data.map { prefs ->
+ when (prefs[STRING_KEY]) {
+ is ByteArray -> decodeBoolList(prefs[STRING_KEY]!!)
+ else -> List(STRING_COUNT) { true }
+ }
+ }.first(),
+ fretSettings = application.dataStore.data.map { prefs ->
+ when (prefs[FRET_KEY]) {
+ is ByteArray -> decodeBoolList(prefs[FRET_KEY]!!)
+ else -> List(FRET_COUNT) { true }
+ }
+ }.first(),
+ )
+ }
+ updateNote()
+ } catch (e: Exception) {
+ Log.w(
+ "WARN",
+ "Error while reading settings. Starting fresh."
)
}
}
}
fun toggleString(idx: Int) {
+
_uiState.update { currentState ->
val res = currentState.stringSettings.toMutableList()
res[idx] = !res[idx]
- currentState.copy(
- stringSettings = res.toList()
- )
+ if (!res.any { it }) {
+ currentState
+ }
+ else {
+ currentState.copy(
+ stringSettings = res.toList()
+ )
+ }
}
viewModelScope.launch { updateSettings() }
}
@@ -170,9 +179,13 @@ class FretfretViewModel(application: Application) : AndroidViewModel(application
_uiState.update { currentState ->
val res = currentState.fretSettings.toMutableList()
res[idx] = !res[idx]
- currentState.copy(
- fretSettings = res.toList()
- )
+ if (!res.any { it }) {
+ currentState
+ } else {
+ currentState.copy(
+ fretSettings = res.toList()
+ )
+ }
}
viewModelScope.launch { updateSettings() }
}
@@ -208,18 +221,29 @@ class FretfretViewModel(application: Application) : AndroidViewModel(application
private suspend fun updateSettings() {
getApplication<Application>().dataStore.edit { settings ->
- settings[STRING_KEY] = serializeBoolList(uiState.value.stringSettings);
- settings[FRET_KEY] = serializeBoolList(uiState.value.fretSettings);
+ settings[STRING_KEY] = encodeBoolList(uiState.value.stringSettings);
+ settings[FRET_KEY] = encodeBoolList(uiState.value.fretSettings);
}
}
+ private fun isValidNote(note: List<Int>?): Boolean {
+ return note != null && uiState.value.stringSettings[note[0]] &&
+ uiState.value.fretSettings[note[1]]
+ }
+
fun toggleSettings() {
val show = !uiState.value.showSettings
_uiState.update { currentState ->
// TODO: keep note if updated settings allow it?
+ var note = currentState.note
+ var lastNote = currentState.lastNote
+ if (!show && !isValidNote(note)) {
+ lastNote = note
+ note = pickNote()
+ }
currentState.copy(
- lastNote = null,
- note = if (show) null else pickNote(),
+ lastNote = lastNote,
+ note = note,
showSettings = show
)
}
@@ -303,15 +327,20 @@ fun Fretfret(viewModel: FretfretViewModel = viewModel()) {
verticalAlignment = Alignment.CenterVertically
) {
Row {
- Text("last answer: ")
- Text(uiState.lastNote?.let { noteName(it) }
- ?: "you're just getting started! B)")
+ if (uiState.lastNote != null) {
+ Text("last answer: ")
+ Text(noteName(uiState.lastNote!!))
+ }
}
Button(
onClick = { viewModel.updateNote() },
shape = RoundedCornerShape(5.dp)
) {
- Text("next")
+ if (uiState.lastNote != null) {
+ Text("next")
+ } else {
+ Text("start")
+ }
}
}
}
diff --git a/app/src/test/java/com/example/fretboardtrainer/ExampleUnitTest.kt b/app/src/test/java/com/example/fretboardtrainer/ExampleUnitTest.kt
@@ -14,14 +14,4 @@ class ExampleUnitTest {
fun addition_isCorrect() {
assertEquals(4, 2 + 2)
}
-
- @Test
- fun serializes_settings() {
- assertEquals(0b1010, serializeBoolList(listOf(true, false, true, false)))
- }
-
- @Test
- fun deserializes_settings() {
- assertEquals(listOf(true, false, true, false), deserializeBoolList(0b1010, 4))
- }
}
\ No newline at end of file
diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml
@@ -1,5 +1,5 @@
[versions]
-agp = "8.4.1"
+agp = "8.5.0"
kotlin = "1.9.0"
coreKtx = "1.13.1"
junit = "4.13.2"
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
@@ -1,6 +1,6 @@
#Tue Jun 25 12:14:45 PDT 2024
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-8.6-bin.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists