diff --git a/app/src/main/java/me/zobrist/tichucounter/data/GameDao.kt b/app/src/main/java/me/zobrist/tichucounter/data/GameDao.kt index 634b3cb..1f8cd32 100644 --- a/app/src/main/java/me/zobrist/tichucounter/data/GameDao.kt +++ b/app/src/main/java/me/zobrist/tichucounter/data/GameDao.kt @@ -35,4 +35,7 @@ interface GameDao : DaoBase { @Query("UPDATE game SET active = 0 WHERE uid is not :gameId;") fun setOthersInactive(gameId: Long) + @Query("SELECT names FROM (SELECT nameA AS names FROM game UNION ALL SELECT nameB AS names FROM game) GROUP BY names") + fun getDistinctTeamNames(): Flow> + } \ No newline at end of file diff --git a/app/src/main/java/me/zobrist/tichucounter/repository/GameRepository.kt b/app/src/main/java/me/zobrist/tichucounter/repository/GameRepository.kt index 78e5bb3..78e1fd3 100644 --- a/app/src/main/java/me/zobrist/tichucounter/repository/GameRepository.kt +++ b/app/src/main/java/me/zobrist/tichucounter/repository/GameRepository.kt @@ -2,10 +2,7 @@ package me.zobrist.tichucounter.repository import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.filter -import kotlinx.coroutines.flow.map -import kotlinx.coroutines.flow.take +import kotlinx.coroutines.flow.* import kotlinx.coroutines.launch import kotlinx.coroutines.withContext import me.zobrist.tichucounter.data.GameDao @@ -23,6 +20,8 @@ class GameRepository @Inject constructor( private var activeGame: Game = Game(true, "TeamA", "TeamB", Date(), Date()) + private var distinctTeamNames: List = listOf() + init { CoroutineScope(Dispatchers.IO).launch { gameDao.getActiveAsFlow().collect { @@ -130,4 +129,8 @@ class GameRepository @Inject constructor( fun getAllWithRoundFlow(): Flow> { return gameDao.getGamesWithRounds() } + + fun getDistinctTeamNames(): Flow> { + return gameDao.getDistinctTeamNames() + } } \ No newline at end of file diff --git a/app/src/main/java/me/zobrist/tichucounter/ui/composables/TypeaheadTextField.kt b/app/src/main/java/me/zobrist/tichucounter/ui/composables/TypeaheadTextField.kt new file mode 100644 index 0000000..7772d39 --- /dev/null +++ b/app/src/main/java/me/zobrist/tichucounter/ui/composables/TypeaheadTextField.kt @@ -0,0 +1,90 @@ +package me.zobrist.tichucounter.ui.composables + +import androidx.compose.foundation.layout.width +import androidx.compose.foundation.text.KeyboardActions +import androidx.compose.foundation.text.KeyboardOptions +import androidx.compose.material3.* +import androidx.compose.runtime.* +import androidx.compose.ui.ExperimentalComposeUiApi +import androidx.compose.ui.Modifier +import androidx.compose.ui.focus.onFocusChanged +import androidx.compose.ui.input.key.Key +import androidx.compose.ui.input.key.key +import androidx.compose.ui.input.key.onKeyEvent +import androidx.compose.ui.layout.onSizeChanged +import androidx.compose.ui.platform.LocalDensity +import androidx.compose.ui.platform.LocalFocusManager +import androidx.compose.ui.text.TextStyle +import androidx.compose.ui.text.input.ImeAction + +@OptIn(ExperimentalMaterial3Api::class, ExperimentalComposeUiApi::class) +@Composable +fun TypeaheadTextField( + value: String, + items: List, + onValueChange: (String) -> Unit, + modifier: Modifier, + colors: TextFieldColors, + textStyle: TextStyle +) { + + var isFocused by remember { mutableStateOf(false) } + val focusManager = LocalFocusManager.current + + ExposedDropdownMenuBox( + expanded = isFocused, + modifier = modifier, + onExpandedChange = {} + ) { + + var dropDownWidth by remember { mutableStateOf(0) } + + TextField( + value = value, + textStyle = textStyle, + onValueChange = { + onValueChange(it) + }, + singleLine = true, + keyboardOptions = KeyboardOptions( + imeAction = ImeAction.Done, + ), + keyboardActions = KeyboardActions( + onDone = { + focusManager.clearFocus() + } + ), + modifier = Modifier + .menuAnchor() + .onFocusChanged { + isFocused = it.isFocused + } + .onSizeChanged { dropDownWidth = it.width } + .onKeyEvent { event -> + if (event.key == Key.Back || event.key == Key.Enter) { + focusManager.clearFocus() + true + } + false + }, + colors = colors + ) + ExposedDropdownMenu( + expanded = isFocused, + modifier = Modifier + .width(with(LocalDensity.current){dropDownWidth.toDp()}), + onDismissRequest = { } + ) { + val filtered = items.filter { it.contains(value) && it.isNotBlank() } + filtered.forEach { + DropdownMenuItem( + onClick = { + onValueChange(it) + focusManager.clearFocus() + }, + text = { Text(it) }, + ) + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/me/zobrist/tichucounter/ui/counter/CounterView.kt b/app/src/main/java/me/zobrist/tichucounter/ui/counter/CounterView.kt index a9b0497..8496b43 100644 --- a/app/src/main/java/me/zobrist/tichucounter/ui/counter/CounterView.kt +++ b/app/src/main/java/me/zobrist/tichucounter/ui/counter/CounterView.kt @@ -35,6 +35,7 @@ fun Landscape(viewModel: ICounterViewModel) { TeamNamesView( viewModel.teamNameA, viewModel.teamNameB, + viewModel.distinctTeamNames, { viewModel.updateNameA(it) }, { viewModel.updateNameB(it) } ) @@ -64,6 +65,7 @@ fun Portrait(viewModel: ICounterViewModel) { TeamNamesView( viewModel.teamNameA, viewModel.teamNameB, + viewModel.distinctTeamNames, { viewModel.updateNameA(it) }, { viewModel.updateNameB(it) } ) @@ -111,6 +113,7 @@ internal class PreviewViewModel : ICounterViewModel { override var activeValue: String = currentScoreA override var inactiveValue: String = currentScoreB override var keyboardHidden: Boolean = false + override val distinctTeamNames: List = listOf("TeamA", "asdffd", "TeamB", "really really long Team Name that is way too long") override fun focusLastInput() { } diff --git a/app/src/main/java/me/zobrist/tichucounter/ui/counter/CounterViewModel.kt b/app/src/main/java/me/zobrist/tichucounter/ui/counter/CounterViewModel.kt index 81bed74..fbd6642 100644 --- a/app/src/main/java/me/zobrist/tichucounter/ui/counter/CounterViewModel.kt +++ b/app/src/main/java/me/zobrist/tichucounter/ui/counter/CounterViewModel.kt @@ -1,6 +1,7 @@ package me.zobrist.tichucounter.ui.counter import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateListOf import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.setValue import androidx.compose.ui.focus.FocusRequester @@ -9,6 +10,8 @@ import androidx.lifecycle.viewModelScope import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.Job import kotlinx.coroutines.delay +import kotlinx.coroutines.flow.collect +import kotlinx.coroutines.flow.take import kotlinx.coroutines.launch import me.zobrist.tichucounter.data.entity.Round import me.zobrist.tichucounter.domain.Tichu @@ -31,6 +34,7 @@ interface IKeyBoardViewModel { val activeValue: String val inactiveValue: String val keyboardHidden: Boolean + val distinctTeamNames: List fun focusLastInput() fun updateOtherScore() @@ -105,6 +109,9 @@ class CounterViewModel @Inject constructor( override var keyboardHidden by mutableStateOf(false) private set + override var distinctTeamNames by mutableStateOf(listOf()) + private set + override var activeValue: String get() { return if (isBFocused) { @@ -160,6 +167,12 @@ class CounterViewModel @Inject constructor( } } } + + viewModelScope.launch { + gameRepository.getDistinctTeamNames().collect() { + distinctTeamNames = it + } + } } override fun focusLastInput() { diff --git a/app/src/main/java/me/zobrist/tichucounter/ui/counter/TeamNamesView.kt b/app/src/main/java/me/zobrist/tichucounter/ui/counter/TeamNamesView.kt index 596c861..58037ef 100644 --- a/app/src/main/java/me/zobrist/tichucounter/ui/counter/TeamNamesView.kt +++ b/app/src/main/java/me/zobrist/tichucounter/ui/counter/TeamNamesView.kt @@ -4,18 +4,22 @@ import android.content.res.Configuration import androidx.compose.foundation.layout.* import androidx.compose.material3.* -import androidx.compose.runtime.Composable +import androidx.compose.runtime.* import androidx.compose.ui.Modifier +import androidx.compose.ui.focus.onFocusChanged +import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import me.zobrist.tichucounter.ui.AppTheme +import me.zobrist.tichucounter.ui.composables.TypeaheadTextField @OptIn(ExperimentalMaterial3Api::class) @Composable fun TeamNamesView( nameA: String, nameB: String, + nameSuggestions: List, updateA: (String) -> Unit, updateB: (String) -> Unit ) { @@ -25,23 +29,26 @@ fun TeamNamesView( ) Row { - TextField( + + TypeaheadTextField( value = nameA, - textStyle = LocalTextStyle.current.copy(textAlign = TextAlign.Center), + items = nameSuggestions, onValueChange = { updateA(it) }, - singleLine = true, modifier = Modifier.weight(1f), - colors = color + colors = color, + textStyle = LocalTextStyle.current.copy(textAlign = TextAlign.Center) ) - TextField( + TypeaheadTextField( value = nameB, - textStyle = LocalTextStyle.current.copy(textAlign = TextAlign.Center), + items = nameSuggestions, onValueChange = { updateB(it) }, - singleLine = true, modifier = Modifier.weight(1f), - colors = color + colors = color, + textStyle = LocalTextStyle.current.copy(textAlign = TextAlign.Center) ) + + } } @@ -50,6 +57,6 @@ fun TeamNamesView( @Composable private fun TeamNamesViewPreview() { AppTheme { - TeamNamesView("TeamA", "TeamB", {}, {}) + TeamNamesView("TeamA", "TeamB", listOf("Test1", "Test3"), {}, {}) } }