fretfret

learn the notes on a guitar from the comfort of your android phone
Log | Files | Refs

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:
M.idea/deploymentTargetSelector.xml | 4++--
Mapp/src/main/java/com/example/fretboardtrainer/MainActivity.kt | 125+++++++++++++++++++++++++++++++++++++++++++++++++------------------------------
Mapp/src/test/java/com/example/fretboardtrainer/ExampleUnitTest.kt | 10----------
Mgradle/libs.versions.toml | 2+-
Mgradle/wrapper/gradle-wrapper.properties | 2+-
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