From ac615560ae47d553949ac2b225ae66b0d0e1704f Mon Sep 17 00:00:00 2001 From: Fabian Zobrist Date: Tue, 4 Jul 2023 23:23:39 +0200 Subject: [PATCH] Add setting and a button to change between modes. --- .../me/zobrist/tichucounter/MainActivity.kt | 3 +- .../tichucounter/domain/SettingsAdapter.kt | 63 ++++++++++++--- .../tichucounter/ui/counter/CounterView.kt | 76 +++++++++++++++++-- .../ui/counter/CounterViewModel.kt | 34 ++++++++- .../tichucounter/ui/counter/RoundGraphView.kt | 2 +- .../tichucounter/ui/counter/RoundListView.kt | 2 +- 6 files changed, 154 insertions(+), 26 deletions(-) diff --git a/app/src/main/java/me/zobrist/tichucounter/MainActivity.kt b/app/src/main/java/me/zobrist/tichucounter/MainActivity.kt index 0aacbf2..a395eda 100644 --- a/app/src/main/java/me/zobrist/tichucounter/MainActivity.kt +++ b/app/src/main/java/me/zobrist/tichucounter/MainActivity.kt @@ -8,7 +8,6 @@ import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatDelegate import androidx.compose.foundation.layout.* import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.filled.* import androidx.compose.material.icons.outlined.* import androidx.compose.material3.* import androidx.compose.runtime.* @@ -37,7 +36,7 @@ import me.zobrist.tichucounter.ui.settings.SettingsViewModel import javax.inject.Inject @AndroidEntryPoint -class MainActivity : AppCompatActivity(), ISettingsChangeListener { +class MainActivity : AppCompatActivity(), ISystemSettingsListener { @Inject lateinit var settingsAdapter: SettingsAdapter diff --git a/app/src/main/java/me/zobrist/tichucounter/domain/SettingsAdapter.kt b/app/src/main/java/me/zobrist/tichucounter/domain/SettingsAdapter.kt index 452e678..3f09c7e 100644 --- a/app/src/main/java/me/zobrist/tichucounter/domain/SettingsAdapter.kt +++ b/app/src/main/java/me/zobrist/tichucounter/domain/SettingsAdapter.kt @@ -14,19 +14,26 @@ enum class Language(val value: LocaleListCompat) { GERMAN(LocaleListCompat.forLanguageTags("de")) } +enum class CounterMode{ LIST, GRAPH } + enum class KeepScreenOn(val value: Boolean) { ON(true), OFF(false) } -interface ISettingsChangeListener { +interface ISystemSettingsListener { fun onLanguageChanged(language: Language) fun onThemeChanged(theme: Theme) fun onScreenOnChanged(keepOn: KeepScreenOn) } +interface IDisplaySettingsListener { + fun onCounterModeChanged(counterMode: CounterMode) +} + @Singleton class SettingsAdapter @Inject constructor(@ApplicationContext private val context: Context) { private val sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context) - private var listenerList = mutableListOf() + private var systemSettingsListeners = mutableListOf() + private var displaySettingsListeners = mutableListOf() var language: Language private set @@ -37,37 +44,57 @@ class SettingsAdapter @Inject constructor(@ApplicationContext private val contex var keepScreenOn: KeepScreenOn private set + var counterMode: CounterMode + private set + init { language = try { enumValueOf(sharedPreferences.getString(Language::class.simpleName, null)!!) - } catch (_: NullPointerException) { + } catch (_: Exception) { Language.DEFAULT } theme = try { enumValueOf(sharedPreferences.getString(Theme::class.simpleName, null)!!) - } catch (_: java.lang.Exception) { + } catch (_: Exception) { Theme.DEFAULT } keepScreenOn = try { enumValueOf(sharedPreferences.getString(KeepScreenOn::class.simpleName, null)!!) - } catch (_: java.lang.Exception) { + } catch (_: Exception) { KeepScreenOn.OFF } + + counterMode = try { + enumValueOf(sharedPreferences.getString(CounterMode::class.simpleName, null)!!) + } catch (_: Exception) { + CounterMode.LIST + } } - fun registerOnChangeListener(listener: ISettingsChangeListener) { - listenerList.add(listener) + fun registerOnChangeListener(listener: IDisplaySettingsListener) { + displaySettingsListeners.add(listener) + + listener.onCounterModeChanged(counterMode) + } + fun unregisterOnChangeListener(listener: IDisplaySettingsListener?) { + if (listener != null) { + displaySettingsListeners.remove(listener) + } + } + + fun registerOnChangeListener(listener: ISystemSettingsListener) { + systemSettingsListeners.add(listener) listener.onThemeChanged(theme) listener.onLanguageChanged(language) listener.onScreenOnChanged(keepScreenOn) } - fun unregisterOnChangeListener(listener: ISettingsChangeListener?) { + fun unregisterOnChangeListener(listener: ISystemSettingsListener?) { if (listener != null) { - listenerList.remove(listener) + systemSettingsListeners.remove(listener) } } @@ -89,6 +116,12 @@ class SettingsAdapter @Inject constructor(@ApplicationContext private val contex notifyListeners(setting) } + fun setCounterMode(counterMode: CounterMode) { + this.counterMode = counterMode + updatePreference(CounterMode::class.simpleName, counterMode.name) + notifyListeners(counterMode) + } + private fun updatePreference(name: String?, value: String) { val editor = sharedPreferences.edit() editor.putString(name, value) @@ -96,21 +129,27 @@ class SettingsAdapter @Inject constructor(@ApplicationContext private val contex } private fun notifyListeners(language: Language) { - listenerList.forEach { + systemSettingsListeners.forEach { it.onLanguageChanged(language) } } private fun notifyListeners(theme: Theme) { - listenerList.forEach { + systemSettingsListeners.forEach { it.onThemeChanged(theme) } } private fun notifyListeners(keepScreenOn: KeepScreenOn) { - listenerList.forEach { + systemSettingsListeners.forEach { it.onScreenOnChanged(keepScreenOn) } } + private fun notifyListeners(counterMode: CounterMode) { + displaySettingsListeners.forEach { + it.onCounterModeChanged(counterMode) + } + } + } \ 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 7476fe6..b4e4956 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 @@ -1,19 +1,37 @@ package me.zobrist.tichucounter.ui.counter import android.content.res.Configuration +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.fillMaxHeight +import androidx.compose.foundation.layout.padding +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.outlined.ArrowDropDown +import androidx.compose.material.icons.outlined.Keyboard +import androidx.compose.material.icons.outlined.ShowChart +import androidx.compose.material.icons.outlined.TableChart +import androidx.compose.material.icons.outlined.TableRows +import androidx.compose.material.icons.outlined.TableView +import androidx.compose.material3.FloatingActionButton +import androidx.compose.material3.FloatingActionButtonElevation +import androidx.compose.material3.Icon import androidx.compose.material3.Surface import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue +import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.focus.FocusRequester +import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.platform.LocalConfiguration import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp import me.zobrist.tichucounter.data.entity.Round +import me.zobrist.tichucounter.domain.CounterMode import me.zobrist.tichucounter.ui.AppTheme @@ -50,10 +68,12 @@ fun Landscape(viewModel: ICounterViewModel) { viewModel.totalScoreB ) - RoundGraphView( - viewModel.roundScoreList, - Modifier.weight(1f) - ) + RoundView( + Modifier.weight(1f), + viewModel.counterMode, + viewModel.roundScoreList) + { viewModel.changeCounterMode() } + } if (!viewModel.keyboardHidden) { Column(Modifier.weight(1f)) { @@ -81,10 +101,11 @@ fun Portrait(viewModel: ICounterViewModel) { viewModel.totalScoreB ) - RoundGraphView( - viewModel.roundScoreList, - Modifier.weight(1f) - ) + RoundView( + Modifier.weight(1f), + viewModel.counterMode, + viewModel.roundScoreList) + { viewModel.changeCounterMode() } if (!viewModel.keyboardHidden) { KeyBoardView(viewModel = viewModel) @@ -92,6 +113,41 @@ fun Portrait(viewModel: ICounterViewModel) { } } +@Composable +private fun RoundView(modifier: Modifier = Modifier, + counterMode: CounterMode, + rounds: List, + changeCounterMode: () -> Unit) { + Box(modifier) { + if(counterMode == CounterMode.LIST) + { + RoundListView( + rounds, + Modifier.fillMaxHeight() + ) + } + else + { + RoundGraphView( + rounds, + Modifier.fillMaxHeight() + ) + } + FloatingActionButton( + modifier = Modifier.align(Alignment.BottomStart).padding(15.dp), + onClick = { changeCounterMode() }) { + Icon(getIcon(counterMode), null) + } + } +} + +private fun getIcon(counterMode: CounterMode): ImageVector { + return when(counterMode) + { + CounterMode.LIST -> Icons.Outlined.ShowChart + CounterMode.GRAPH -> Icons.Outlined.TableRows + } +} @Preview(name = "Light Mode") @Preview(name = "Dark Mode", uiMode = Configuration.UI_MODE_NIGHT_YES, showBackground = true) @@ -123,6 +179,7 @@ internal class PreviewViewModel : ICounterViewModel { listOf("TeamA", "asdffd", "TeamB", "really really long Team Name that is way too long") override val teamNameSuggestionsB: List = listOf("TeamA", "asdffd", "TeamB", "really really long Team Name that is way too long") + override val counterMode: CounterMode = CounterMode.GRAPH override fun focusLastInput() { } @@ -155,6 +212,9 @@ internal class PreviewViewModel : ICounterViewModel { override fun updateNameB(value: String) { } + override fun changeCounterMode() { + } + override fun updateFocusStateA(state: Boolean) { } 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 751d186..708161d 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 @@ -11,6 +11,9 @@ import kotlinx.coroutines.Job import kotlinx.coroutines.delay import kotlinx.coroutines.launch import me.zobrist.tichucounter.data.entity.Round +import me.zobrist.tichucounter.domain.CounterMode +import me.zobrist.tichucounter.domain.IDisplaySettingsListener +import me.zobrist.tichucounter.domain.SettingsAdapter import me.zobrist.tichucounter.domain.Tichu import me.zobrist.tichucounter.domain.digitCount import me.zobrist.tichucounter.domain.getTotalPoints @@ -57,16 +60,19 @@ interface ICounterViewModel : IKeyBoardViewModel { val teamNameB: String val teamNameSuggestionsA: List val teamNameSuggestionsB: List + val counterMode: CounterMode fun updateNameA(value: String) fun updateNameB(value: String) + fun changeCounterMode() } @HiltViewModel class CounterViewModel @Inject constructor( - private val gameRepository: GameRepository + private val gameRepository: GameRepository, + private val settingsAdapter: SettingsAdapter ) : - ViewModel(), ICounterViewModel { + ViewModel(), ICounterViewModel, IDisplaySettingsListener { override var roundScoreList by mutableStateOf(emptyList()) private set @@ -113,6 +119,17 @@ class CounterViewModel @Inject constructor( override var teamNameSuggestionsB by mutableStateOf(listOf()) private set + override var counterMode by mutableStateOf(CounterMode.LIST) + private set + + init { + settingsAdapter.registerOnChangeListener(this) + } + + override fun onCleared() { + settingsAdapter.unregisterOnChangeListener(this) + } + override var activeValue: String get() { return if (isBFocused) { @@ -289,6 +306,15 @@ class CounterViewModel @Inject constructor( } } + override fun changeCounterMode() { + val nextMode = when(settingsAdapter.counterMode) + { + CounterMode.LIST -> CounterMode.GRAPH + CounterMode.GRAPH -> CounterMode.LIST + } + settingsAdapter.setCounterMode(nextMode) + } + override fun updateFocusStateA(state: Boolean) { isAFocused = state if (state) { @@ -362,4 +388,8 @@ class CounterViewModel @Inject constructor( return filtered.sorted().sortedBy { it.length }.take(10) } + + override fun onCounterModeChanged(counterMode: CounterMode) { + this.counterMode = counterMode + } } \ No newline at end of file diff --git a/app/src/main/java/me/zobrist/tichucounter/ui/counter/RoundGraphView.kt b/app/src/main/java/me/zobrist/tichucounter/ui/counter/RoundGraphView.kt index 43d5a80..b24c52b 100644 --- a/app/src/main/java/me/zobrist/tichucounter/ui/counter/RoundGraphView.kt +++ b/app/src/main/java/me/zobrist/tichucounter/ui/counter/RoundGraphView.kt @@ -40,7 +40,7 @@ import kotlin.math.roundToInt @Composable -fun RoundGraphView(rounds: List, modifier: Modifier) { +fun RoundGraphView(rounds: List, modifier: Modifier = Modifier) { val points = getPoints(rounds) diff --git a/app/src/main/java/me/zobrist/tichucounter/ui/counter/RoundListView.kt b/app/src/main/java/me/zobrist/tichucounter/ui/counter/RoundListView.kt index e9d3dfb..97a656e 100644 --- a/app/src/main/java/me/zobrist/tichucounter/ui/counter/RoundListView.kt +++ b/app/src/main/java/me/zobrist/tichucounter/ui/counter/RoundListView.kt @@ -21,7 +21,7 @@ import me.zobrist.tichucounter.data.entity.Round import me.zobrist.tichucounter.ui.AppTheme @Composable -fun RoundListView(rounds: List, modifier: Modifier) { +fun RoundListView(rounds: List, modifier: Modifier = Modifier) { val lazyListState = rememberLazyListState() val scope = rememberCoroutineScope()