Add undo redo functionality. Move new game to navigation drawer

This commit is contained in:
2023-01-20 19:28:50 +01:00
parent f44b51c075
commit cd39384207
7 changed files with 111 additions and 32 deletions

View File

@@ -73,17 +73,10 @@ abstract class BaseActivity : AppCompatActivity(),
}
private fun setLanguage(language: Language) {
val locale = when (language) {
Language.ENGLISH -> "en"
Language.GERMAN -> "de"
else -> null
}
val currentLocale = AppCompatDelegate.getApplicationLocales()[0].toString()
if (locale != null && locale != currentLocale) {
val newLocale = LocaleListCompat.forLanguageTags(locale)
if (language.value != null && language.value != currentLocale) {
val newLocale = LocaleListCompat.forLanguageTags(language.value)
AppCompatDelegate.setApplicationLocales(newLocale)
}
}

View File

@@ -15,6 +15,7 @@ import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
import androidx.lifecycle.viewmodel.compose.viewModel
import androidx.navigation.NavDestination.Companion.hierarchy
import androidx.navigation.NavGraph.Companion.findStartDestination
import androidx.navigation.NavHostController
@@ -87,7 +88,11 @@ class MainActivity : BaseActivity() {
) {
composable("counter") {
Counter(counterViewModel)
mainViewModel.topBarActions = (listOf(undoAction, newGameAction))
mainViewModel.topBarActions = (listOf(
TopBarAction(Icons.Outlined.Undo, mainViewModel.isUndoActionActive) { mainViewModel.undoLastRound() },
TopBarAction(Icons.Outlined.Redo, mainViewModel.isRedoActionActive) { mainViewModel.redoLastRound() }
))
mainViewModel.topBarIcon = Icons.Outlined.Menu
mainViewModel.topBarTitle = stringResource(R.string.app_name)
mainViewModel.topBarNavigationAction =
@@ -140,10 +145,10 @@ class MainActivity : BaseActivity() {
},
actions = {
actions.forEach {
IconButton(onClick = { it.action() }) {
IconButton(onClick = { it.action() }, enabled = it.isActive) {
Icon(
imageVector = it.imageVector,
contentDescription = null
contentDescription = null,
)
}
}
@@ -167,7 +172,40 @@ class MainActivity : BaseActivity() {
drawerState = drawerState,
drawerContent = {
ModalDrawerSheet {
Spacer(Modifier.height(12.dp))
Spacer(Modifier.height(20.dp))
NavigationDrawerItem(
icon = { Icon(Icons.Outlined.RestartAlt, contentDescription = null) },
colors = NavigationDrawerItemDefaults.colors(
unselectedContainerColor = MaterialTheme.colorScheme.secondaryContainer
),
label = { Text(stringResource(R.string.newGame)) },
selected = false,
onClick = {
scope.launch { drawerState.close() }
mainViewModel.newGame()
navController.navigate("counter") {
// Pop up to the start destination of the graph to
// avoid building up a large stack of destinations
// on the back stack as users select items
popUpTo(navController.graph.findStartDestination().id) {
saveState = true
}
// Avoid multiple copies of the same destination when
// reselecting the same item
launchSingleTop = true
// Restore state when reselecting a previously selected item
restoreState = true
}
},
modifier = Modifier.padding(NavigationDrawerItemDefaults.ItemPadding)
)
Spacer(Modifier.height(20.dp))
Divider()
items.forEach { screen ->
NavigationDrawerItem(
icon = { Icon(screen.icon, contentDescription = null) },
@@ -203,8 +241,4 @@ class MainActivity : BaseActivity() {
object Settings : Screen("settings", Icons.Outlined.Settings, R.string.menu_settings)
}
private val undoAction = TopBarAction(Icons.Outlined.Undo) { mainViewModel.undoLastRound() }
private val newGameAction = TopBarAction(Icons.Outlined.Add) { mainViewModel.newGame() }
}

View File

@@ -2,4 +2,4 @@ package me.zobrist.tichucounter.domain
import androidx.compose.ui.graphics.vector.ImageVector
class TopBarAction(val imageVector: ImageVector, val action: () -> Unit)
class TopBarAction(val imageVector: ImageVector, val isActive: Boolean, val action: () -> Unit)

View File

@@ -2,6 +2,7 @@ package me.zobrist.tichucounter.repository
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.last
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import me.zobrist.tichucounter.data.Game
@@ -57,12 +58,20 @@ class GameRepository @Inject constructor(
}
}
suspend fun revertLastRound() {
suspend fun getLastRound(): Round? {
return try {
withContext(Dispatchers.IO) {
roundDao.getAllForGame(activeGame.uid).last()
}
} catch (_:NoSuchElementException) {
null
}
}
suspend fun deleteLastRound() {
withContext(Dispatchers.IO) {
try {
val lastRound = roundDao.getAllForGame(activeGame.uid).last()
roundDao.delete(lastRound)
} catch (_: NoSuchElementException) {
roundDao.delete(getLastRound()!!)
} catch (_: NullPointerException) {
}
}
}

View File

@@ -2,40 +2,84 @@ package me.zobrist.tichucounter.ui
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Menu
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
import androidx.compose.runtime.*
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.launch
import me.zobrist.tichucounter.data.Round
import me.zobrist.tichucounter.data.RoundDao
import me.zobrist.tichucounter.domain.NavigationAction
import me.zobrist.tichucounter.domain.TopBarAction
import me.zobrist.tichucounter.repository.GameRepository
import java.util.NoSuchElementException
import javax.inject.Inject
@HiltViewModel
class MainViewModel @Inject constructor(private val gameRepository: GameRepository) : ViewModel() {
class MainViewModel @Inject constructor(private val gameRepository: GameRepository, roundDao: RoundDao) : ViewModel() {
private var redoRounds = mutableStateListOf<Round>()
private var expectedRoundCount = 0
var topBarTitle by mutableStateOf("")
var topBarActions by mutableStateOf(emptyList<TopBarAction>())
var topBarIcon by mutableStateOf(Icons.Filled.Menu)
var isUndoActionActive by mutableStateOf(false)
var topBarNavigationAction by mutableStateOf(NavigationAction {})
val isRedoActionActive: Boolean
get() = redoRounds.isNotEmpty()
init {
viewModelScope.launch {
roundDao.getForActiveGame().collect() {
isUndoActionActive = it.isNotEmpty()
if(expectedRoundCount != it.count()) {
redoRounds.clear()
}
expectedRoundCount = it.count()
}
}
}
fun onNavigateClicked() {
topBarNavigationAction.aciton()
}
fun undoLastRound() {
viewModelScope.launch {
gameRepository.revertLastRound()
val round = gameRepository.getLastRound()
if (round != null) {
redoRounds.add(round)
expectedRoundCount--
gameRepository.deleteLastRound()
}
}
}
fun redoLastRound() {
viewModelScope.launch {
try {
val round = redoRounds.last()
redoRounds.remove(round)
expectedRoundCount++
gameRepository.addRoundToActiveGame(round.scoreA, round.scoreB)
}catch (_: NoSuchElementException)
{
}
}
}
fun clearRedoList() {
redoRounds.clear()
}
fun newGame() {
viewModelScope.launch {
redoRounds.clear()
gameRepository.newGame()
}
}

View File

@@ -24,5 +24,6 @@
<string name="submit">Übermitteln</string>
<string name="on">Ein</string>
<string name="off">Aus</string>
<string name="newGame">Neues Spiel</string>
</resources>

View File

@@ -21,8 +21,6 @@
<string name="dark">Dark</string>
<string name="display">Display</string>
<string name="settings">Settings</string>
<string name="nav_header_subtitle">app@zobrist.me</string>
<string name="nav_header_desc">Navigation header</string>
<string name="menu_counter">Counter</string>
<string name="menu_history">History</string>
@@ -30,7 +28,7 @@
<string name="activate">Activate</string>
<string name="delete">Delete</string>
<string name="submit">Submit</string>
<string name="title_activity_main">MainActivity</string>
<string name="on">On</string>
<string name="off">Off</string>
<string name="newGame">New Game</string>
</resources>