Merge pull request 'feature/swipe-actions' (#44) from feature/swipe-actions into develop
All checks were successful
Build Android / build (push) Successful in 7m26s

Reviewed-on: #44
This commit was merged in pull request #44.
This commit is contained in:
2023-08-25 18:19:44 +02:00
10 changed files with 351 additions and 165 deletions

View File

@@ -24,7 +24,7 @@ versionProperties.load(new FileInputStream(versionPropertiesFile))
android { android {
compileSdkVersion 33 compileSdk 33
defaultConfig { defaultConfig {
applicationId "me.zobrist.tichucounter" applicationId "me.zobrist.tichucounter"
@@ -63,12 +63,11 @@ android {
} }
buildFeatures { buildFeatures {
viewBinding = true
compose = true compose = true
} }
composeOptions { composeOptions {
kotlinCompilerExtensionVersion = "1.4.7" kotlinCompilerExtensionVersion = "1.4.8"
} }
compileOptions { compileOptions {
@@ -91,7 +90,7 @@ dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version" implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
implementation 'androidx.core:core-ktx:1.10.1' implementation 'androidx.core:core-ktx:1.10.1'
implementation 'androidx.appcompat:appcompat:1.6.1' implementation 'androidx.appcompat:appcompat:1.6.1'
implementation "androidx.compose.material3:material3:1.1.0" implementation "androidx.compose.material3:material3:1.1.1"
implementation 'com.google.android.play:core-ktx:1.8.1' implementation 'com.google.android.play:core-ktx:1.8.1'
implementation 'com.google.android.play:core-ktx:1.8.1' implementation 'com.google.android.play:core-ktx:1.8.1'
implementation 'com.google.code.gson:gson:2.9.0' implementation 'com.google.code.gson:gson:2.9.0'

View File

@@ -6,12 +6,32 @@ import androidx.activity.compose.setContent
import androidx.activity.viewModels import androidx.activity.viewModels
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.app.AppCompatDelegate import androidx.appcompat.app.AppCompatDelegate
import androidx.compose.foundation.layout.* import androidx.compose.foundation.layout.padding
import androidx.compose.material.icons.Icons import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.* import androidx.compose.material.icons.outlined.Calculate
import androidx.compose.material.icons.outlined.* import androidx.compose.material.icons.outlined.Info
import androidx.compose.material3.* import androidx.compose.material.icons.outlined.Keyboard
import androidx.compose.runtime.* import androidx.compose.material.icons.outlined.List
import androidx.compose.material.icons.outlined.MoreVert
import androidx.compose.material.icons.outlined.Redo
import androidx.compose.material.icons.outlined.Settings
import androidx.compose.material.icons.outlined.Undo
import androidx.compose.material3.DrawerState
import androidx.compose.material3.DrawerValue
import androidx.compose.material3.FloatingActionButton
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.ModalNavigationDrawer
import androidx.compose.material3.Scaffold
import androidx.compose.material3.SnackbarHost
import androidx.compose.material3.SnackbarHostState
import androidx.compose.material3.rememberDrawerState
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.navigation.NavHostController import androidx.navigation.NavHostController
@@ -22,12 +42,23 @@ import com.google.accompanist.systemuicontroller.rememberSystemUiController
import dagger.hilt.android.AndroidEntryPoint import dagger.hilt.android.AndroidEntryPoint
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import me.zobrist.tichucounter.domain.* import me.zobrist.tichucounter.domain.DrawerItem
import me.zobrist.tichucounter.domain.ISettingsChangeListener
import me.zobrist.tichucounter.domain.KeepScreenOn
import me.zobrist.tichucounter.domain.Language
import me.zobrist.tichucounter.domain.Route
import me.zobrist.tichucounter.domain.SettingsAdapter
import me.zobrist.tichucounter.domain.Theme
import me.zobrist.tichucounter.domain.TopBarAction
import me.zobrist.tichucounter.domain.TopBarState
import me.zobrist.tichucounter.domain.composable
import me.zobrist.tichucounter.domain.navigate
import me.zobrist.tichucounter.ui.AppTheme import me.zobrist.tichucounter.ui.AppTheme
import me.zobrist.tichucounter.ui.MainViewModel import me.zobrist.tichucounter.ui.MainViewModel
import me.zobrist.tichucounter.ui.about.AboutView import me.zobrist.tichucounter.ui.about.AboutView
import me.zobrist.tichucounter.ui.composables.DropDownMenu import me.zobrist.tichucounter.ui.composables.DropDownMenu
import me.zobrist.tichucounter.ui.counter.* import me.zobrist.tichucounter.ui.counter.Counter
import me.zobrist.tichucounter.ui.counter.CounterViewModel
import me.zobrist.tichucounter.ui.history.HistoryList import me.zobrist.tichucounter.ui.history.HistoryList
import me.zobrist.tichucounter.ui.history.HistoryViewModel import me.zobrist.tichucounter.ui.history.HistoryViewModel
import me.zobrist.tichucounter.ui.layout.DrawerContent import me.zobrist.tichucounter.ui.layout.DrawerContent
@@ -151,8 +182,11 @@ class MainActivity : AppCompatActivity(), ISettingsChangeListener {
) { ) {
var topBarState by remember { mutableStateOf(TopBarState()) } var topBarState by remember { mutableStateOf(TopBarState()) }
var snackbarHostState by remember { mutableStateOf(SnackbarHostState()) }
val scope = rememberCoroutineScope()
Scaffold( Scaffold(
snackbarHost = { SnackbarHost(snackbarHostState) },
floatingActionButton = { floatingActionButton = {
if (showFab) { if (showFab) {
FloatingActionButton( FloatingActionButton(
@@ -216,7 +250,10 @@ class MainActivity : AppCompatActivity(), ISettingsChangeListener {
topBarState = topBarState =
TopBarState(title = stringResource(R.string.menu_history)) { scope.launch { drawerState.open() } } TopBarState(title = stringResource(R.string.menu_history)) { scope.launch { drawerState.open() } }
HistoryList(historyViewModel) { navController.navigate(Route.COUNTER) } HistoryList(
historyViewModel,
snackbarHostState
) { navController.navigate(Route.COUNTER) }
} }
composable(Route.SETTINGS) { composable(Route.SETTINGS) {
topBarState = topBarState =

View File

@@ -1,17 +1,40 @@
package me.zobrist.tichucounter.ui.counter package me.zobrist.tichucounter.ui.counter
import android.content.res.Configuration import android.content.res.Configuration
import androidx.compose.animation.core.* import androidx.compose.animation.core.RepeatMode
import androidx.compose.animation.core.animateFloat
import androidx.compose.animation.core.infiniteRepeatable
import androidx.compose.animation.core.rememberInfiniteTransition
import androidx.compose.animation.core.tween
import androidx.compose.foundation.interaction.MutableInteractionSource import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.interaction.collectIsPressedAsState import androidx.compose.foundation.interaction.collectIsPressedAsState
import androidx.compose.foundation.layout.* import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.IntrinsicSize
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.layout.wrapContentWidth
import androidx.compose.material.icons.Icons import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.outlined.Backspace import androidx.compose.material.icons.outlined.Backspace
import androidx.compose.material.icons.outlined.Check import androidx.compose.material.icons.outlined.Check
import androidx.compose.material.icons.outlined.KeyboardHide import androidx.compose.material.icons.outlined.KeyboardHide
import androidx.compose.material.icons.outlined.SwapHoriz import androidx.compose.material.icons.outlined.SwapHoriz
import androidx.compose.material3.* import androidx.compose.material3.Divider
import androidx.compose.runtime.* import androidx.compose.material3.ElevatedButton
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.LocalTextStyle
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.material3.TextField
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.focus.FocusRequester import androidx.compose.ui.focus.FocusRequester

View File

@@ -1,9 +1,12 @@
package me.zobrist.tichucounter.ui.counter package me.zobrist.tichucounter.ui.counter
import android.content.res.Configuration import android.content.res.Configuration
import androidx.compose.foundation.layout.* import androidx.compose.foundation.layout.Row
import androidx.compose.material3.* import androidx.compose.material3.LocalTextStyle
import androidx.compose.runtime.* import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.TextFieldDefaults
import androidx.compose.material3.surfaceColorAtElevation
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.Preview

View File

@@ -1,44 +1,68 @@
package me.zobrist.tichucounter.ui.history package me.zobrist.tichucounter.ui.history
import androidx.compose.foundation.clickable import androidx.compose.animation.animateColorAsState
import androidx.compose.animation.core.animateFloatAsState
import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.aspectRatio
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.width import androidx.compose.foundation.layout.requiredHeight
import androidx.compose.foundation.layout.wrapContentSize import androidx.compose.foundation.layout.requiredWidth
import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.LazyListState
import androidx.compose.foundation.lazy.items import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.material.icons.Icons import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.outlined.Delete
import androidx.compose.material.icons.outlined.DeleteForever import androidx.compose.material.icons.outlined.DeleteForever
import androidx.compose.material.icons.outlined.MoreVert import androidx.compose.material.icons.outlined.RestartAlt
import androidx.compose.material3.AlertDialog import androidx.compose.material3.AlertDialog
import androidx.compose.material3.Badge
import androidx.compose.material3.Button import androidx.compose.material3.Button
import androidx.compose.material3.Card import androidx.compose.material3.Card
import androidx.compose.material3.CardDefaults import androidx.compose.material3.DismissDirection
import androidx.compose.material3.DismissValue
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Icon import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.SnackbarDuration
import androidx.compose.material3.SnackbarHostState
import androidx.compose.material3.SnackbarResult
import androidx.compose.material3.SwipeToDismiss
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.material3.TextButton import androidx.compose.material3.TextButton
import androidx.compose.material3.rememberDismissState
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.setValue import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Alignment.Companion.CenterVertically import androidx.compose.ui.Alignment.Companion.CenterVertically
import androidx.compose.ui.Alignment.Companion.TopEnd
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.scale
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import kotlinx.coroutines.launch
import me.zobrist.tichucounter.R import me.zobrist.tichucounter.R
import me.zobrist.tichucounter.data.GameWithScores import me.zobrist.tichucounter.data.GameWithScores
import me.zobrist.tichucounter.data.entity.Game import me.zobrist.tichucounter.data.entity.Game
import me.zobrist.tichucounter.data.entity.Round import me.zobrist.tichucounter.data.entity.Round
import me.zobrist.tichucounter.domain.getTotalPoints import me.zobrist.tichucounter.domain.getTotalPoints
import me.zobrist.tichucounter.ui.composables.DropDownMenu
import java.text.DateFormat import java.text.DateFormat
import java.util.Date import java.util.Date
import java.util.Locale import java.util.Locale
@@ -47,11 +71,14 @@ import java.util.Locale
@Composable @Composable
fun HistoryList( fun HistoryList(
viewModel: HistoryViewModel, viewModel: HistoryViewModel,
navigateToCalculator: () -> Unit snackbarHostState: SnackbarHostState,
navigateToCalculator: () -> Unit,
) { ) {
val scope = rememberCoroutineScope()
val lazyListState = rememberLazyListState()
var showDeleteDialog by remember { mutableStateOf(false) } var showDeleteDialog by remember { mutableStateOf(false) }
DeleteConfirmDialog(showDeleteDialog) { DeleteConfirmDialog(showDeleteDialog) {
showDeleteDialog = false showDeleteDialog = false
if (it) { if (it) {
@@ -59,14 +86,48 @@ fun HistoryList(
} }
} }
val deletedMessage = stringResource(id = R.string.delete_success)
val deletedActionLabel = stringResource(id = R.string.undo_question)
val activatedMessage = stringResource(id = R.string.activated_success)
val activatedActionLabel = stringResource(id = R.string.to_calculator_question)
HistoryList( HistoryList(
viewModel.gameAndHistory, games = viewModel.gameAndHistory,
{ onOpenClicked = {
viewModel.activateGame(it) scope.launch {
navigateToCalculator() viewModel.activateGame(it)
lazyListState.animateScrollToItem(0)
val result = snackbarHostState.showSnackbar(
message = activatedMessage,
actionLabel = activatedActionLabel,
duration = SnackbarDuration.Short
)
if (result == SnackbarResult.ActionPerformed) {
navigateToCalculator()
}
}
}, },
{ viewModel.deleteGame(it) }, onDeleteClicked = {
{ showDeleteDialog = true }, scope.launch {
viewModel.markToDelete(it)
val result = snackbarHostState.showSnackbar(
message = deletedMessage,
actionLabel = deletedActionLabel,
duration = SnackbarDuration.Short
)
if (result == SnackbarResult.Dismissed) {
viewModel.deleteGame(it)
} else {
viewModel.unmarkToDelete(it)
}
}
},
onDeleteAllClicked = { showDeleteDialog = true },
lazyListState = lazyListState
) )
} }
@@ -78,14 +139,12 @@ fun DeleteConfirmDialog(show: Boolean = true, onExecuted: (Boolean) -> Unit = {}
AlertDialog( AlertDialog(
onDismissRequest = { onExecuted(false) }, onDismissRequest = { onExecuted(false) },
dismissButton = { dismissButton = {
TextButton({ onExecuted(false) }) TextButton({ onExecuted(false) }) {
{
Text(stringResource(R.string.cancel)) Text(stringResource(R.string.cancel))
} }
}, },
confirmButton = { confirmButton = {
TextButton({ onExecuted(true) }) TextButton({ onExecuted(true) }) {
{
Text(stringResource(R.string.ok)) Text(stringResource(R.string.ok))
} }
}, },
@@ -95,133 +154,178 @@ fun DeleteConfirmDialog(show: Boolean = true, onExecuted: (Boolean) -> Unit = {}
} }
} }
@OptIn(ExperimentalFoundationApi::class)
@Composable @Composable
fun HistoryList( fun HistoryList(
games: List<GameWithScores>, games: List<GameWithScores>,
onOpenClicked: (GameId: Long) -> Unit, onOpenClicked: (gameId: Long) -> Unit,
onDeleteClicked: (GameId: Long) -> Unit, onDeleteClicked: (gameId: Long) -> Unit,
onDeleteAllClicked: () -> Unit onDeleteAllClicked: () -> Unit,
lazyListState: LazyListState = LazyListState(),
) { ) {
Row { Row {
LazyColumn { LazyColumn(state = lazyListState) {
item { items(
Text( items = games,
modifier = Modifier.padding(start = 10.dp, end = 10.dp), key = { it.hashCode() }) {
text = stringResource(R.string.active), if (it.game.active) {
style = MaterialTheme.typography.headlineSmall HistoryListItem(
) it,
} Modifier.animateItemPlacement()
items(games.filter { it.game.active }) { )
HistoryListItem(it, onOpenClicked, onDeleteClicked) } else {
} DismissibleHistoryListItem(
it,
if (games.count() > 1) { Modifier.animateItemPlacement(),
item { onOpenClicked,
Text( onDeleteClicked
modifier = Modifier.padding(start = 10.dp, end = 10.dp, top = 10.dp),
text = stringResource(R.string.inactive),
style = MaterialTheme.typography.headlineSmall
) )
} }
}
items(games.filter { !it.game.active }) { item {
HistoryListItem(it, onOpenClicked, onDeleteClicked) Button(enabled = games.count() > 1,
} modifier = Modifier
.padding(start = 4.dp, end = 4.dp, top = 10.dp)
item { .align(CenterVertically)
Button( .fillMaxWidth()
enabled = games.count() > 1, .animateItemPlacement(),
modifier = Modifier onClick = { onDeleteAllClicked() }) {
.padding(start = 4.dp, end = 4.dp, top = 10.dp) Icon(imageVector = Icons.Outlined.DeleteForever, contentDescription = null)
.align(CenterVertically) Text(text = stringResource(id = R.string.deleteAll))
.fillMaxWidth(),
onClick = { onDeleteAllClicked() }) {
Icon(imageVector = Icons.Outlined.DeleteForever, contentDescription = null)
Text(text = stringResource(id = R.string.deleteAll))
}
} }
} }
} }
} }
} }
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun DismissibleHistoryListItem(
game: GameWithScores,
modifier: Modifier = Modifier,
onOpenClicked: (gameId: Long) -> Unit,
onDeleteClicked: (gameId: Long) -> Unit,
) {
val density = LocalDensity.current
val dismissState =
rememberDismissState(positionalThreshold = { with(density) { 100.dp.toPx() } },
confirmValueChange = {
if (it == DismissValue.DismissedToStart) {
onDeleteClicked(game.game.uid)
}
if (it == DismissValue.DismissedToEnd) {
onOpenClicked(game.game.uid)
}
true
})
val directions = if (game.game.active) {
setOf()
} else {
setOf(DismissDirection.EndToStart, DismissDirection.StartToEnd)
}
SwipeToDismiss(
modifier = modifier,
state = dismissState,
directions = directions,
background = {
val direction = dismissState.dismissDirection ?: return@SwipeToDismiss
val color by animateColorAsState(
when (dismissState.targetValue) {
DismissValue.DismissedToStart -> Color.Red
else -> MaterialTheme.colorScheme.background
}, label = ""
)
val alignment = when (direction) {
DismissDirection.StartToEnd -> Alignment.CenterStart
DismissDirection.EndToStart -> Alignment.CenterEnd
}
val icon = when (direction) {
DismissDirection.StartToEnd -> Icons.Outlined.RestartAlt
DismissDirection.EndToStart -> Icons.Outlined.Delete
}
val text = when (direction) {
DismissDirection.StartToEnd -> stringResource(id = R.string.continue_play)
DismissDirection.EndToStart -> stringResource(id = R.string.delete)
}
val scale by animateFloatAsState(
if (dismissState.targetValue == DismissValue.Default) 0.75f else 1f, label = ""
)
Box(
Modifier
.fillMaxSize()
.background(color)
.padding(horizontal = 20.dp),
contentAlignment = alignment
) {
Column(
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally
) {
Icon(
icon, contentDescription = null, modifier = Modifier.scale(scale)
)
Text(text = text)
}
}
}, dismissContent = {
HistoryListItem(game = game)
})
}
@OptIn(ExperimentalMaterial3Api::class)
@Composable @Composable
fun HistoryListItem( fun HistoryListItem(
game: GameWithScores, game: GameWithScores, modifier: Modifier = Modifier
onOpenClicked: (GameId: Long) -> Unit,
onDeleteClicked: (GameId: Long) -> Unit
) { ) {
val format = val format =
DateFormat.getDateTimeInstance(DateFormat.MEDIUM, DateFormat.SHORT, Locale.getDefault()) DateFormat.getDateTimeInstance(DateFormat.MEDIUM, DateFormat.SHORT, Locale.getDefault())
val cardColor = if (game.game.active) {
CardDefaults.cardColors(containerColor = MaterialTheme.colorScheme.secondaryContainer)
} else {
CardDefaults.cardColors()
}
val totalScores = game.getTotalPoints() val totalScores = game.getTotalPoints()
Card( Card(
modifier = Modifier modifier = modifier
.fillMaxWidth() .fillMaxWidth()
.padding(all = 4.dp) .padding(all = 4.dp)
.clickable { onOpenClicked(game.game.uid) },
colors = cardColor
) { ) {
Row( Row(
Modifier Modifier.padding(all = 12.dp)
.padding(all = 12.dp)
) { ) {
Column(Modifier.weight(4f)) { Box( modifier = modifier.fillMaxSize()) {
Text( Column {
text = game.game.nameA + " vs " + game.game.nameB, Text(
maxLines = 1, text = game.game.nameA + " vs " + game.game.nameB,
overflow = TextOverflow.Ellipsis, maxLines = 1,
style = MaterialTheme.typography.headlineSmall overflow = TextOverflow.Ellipsis,
) style = MaterialTheme.typography.headlineSmall
Text(
text = totalScores.first.toString() + " : " + totalScores.second.toString(),
style = MaterialTheme.typography.bodyLarge
)
Spacer(modifier = Modifier.padding(5.dp))
Text(
text = format.format(game.game.modified),
style = MaterialTheme.typography.labelSmall
)
}
Column(
Modifier
.wrapContentSize()
.width(40.dp)
) {
if (!game.game.active) {
var expanded by remember { mutableStateOf(false) }
Icon(
modifier = Modifier
.padding(start = 20.dp, bottom = 20.dp)
.clickable { expanded = true },
imageVector = Icons.Outlined.MoreVert,
contentDescription = null
) )
Text(
text = totalScores.first.toString() + " : " + totalScores.second.toString(),
DropDownMenu( style = MaterialTheme.typography.bodyLarge
mapOf("delete" to R.string.delete), )
"", Spacer(modifier = Modifier.padding(5.dp))
expanded, Text(
) { text = format.format(game.game.modified),
expanded = false style = MaterialTheme.typography.labelSmall
it?.let { )
when (it) { }
"delete" -> onDeleteClicked(game.game.uid) if (game.game.active) {
} Badge(
} modifier = Modifier.align(TopEnd),
contentColor = MaterialTheme.colorScheme.onPrimary,
containerColor = MaterialTheme.colorScheme.primary) {
Text(
text = stringResource(id = R.string.active),
style = MaterialTheme.typography.labelSmall
)
} }
} }
} }
@@ -234,25 +338,16 @@ fun HistoryListItem(
private fun HistoryListPreview() { private fun HistoryListPreview() {
val tempData = listOf( val tempData = listOf(
GameWithScores( GameWithScores(
Game(true, "abc", "def", Date(), Date()), Game(true, "abcsdf sdaf asdf sdf ", "defsadf asdf sadf ", Date(), Date()), listOf(Round(1, 550, 500))
listOf(Round(1, 550, 500)) ), GameWithScores(
), Game(false, "ADTH", "dogfg", Date(), Date()), listOf(Round(2, 20, 60))
GameWithScores( ), GameWithScores(
Game(false, "ADTH", "dogfg", Date(), Date()), Game(false, "TeamA3 langer Name", "TeamB3", Date(), Date()), listOf(Round(3, 30, 70))
listOf(Round(2, 20, 60)) ), GameWithScores(
), Game(false, "TeamA4", "TeamB4", Date(), Date()), listOf(Round(4, 40, 80))
GameWithScores( ), GameWithScores(
Game(false, "TeamA3 langer Name", "TeamB3", Date(), Date()), Game(false, "TeamA5", "TeamB5", Date(), Date()), listOf(Round(5, 50, 90))
listOf(Round(3, 30, 70))
),
GameWithScores(
Game(false, "TeamA4", "TeamB4", Date(), Date()),
listOf(Round(4, 40, 80))
),
GameWithScores(
Game(false, "TeamA5", "TeamB5", Date(), Date()),
listOf(Round(5, 50, 90))
) )
) )
HistoryList(tempData, {}, {}) {} HistoryList(tempData, {}, {}, {})
} }

View File

@@ -20,16 +20,27 @@ class HistoryViewModel @Inject constructor(
var gameAndHistory by mutableStateOf(emptyList<GameWithScores>()) var gameAndHistory by mutableStateOf(emptyList<GameWithScores>())
private set private set
private var fullList: List<GameWithScores> = emptyList()
init { init {
viewModelScope.launch { viewModelScope.launch {
gameRepository.getAllWithRoundFlow().collect { games -> gameRepository.getAllWithRoundFlow().collect { games ->
gameAndHistory = fullList =
games.sortedBy { it.game.modified }.sortedBy { it.game.active }.reversed() games.sortedBy { it.game.modified }.sortedBy { it.game.active }.reversed()
gameAndHistory = fullList
} }
} }
} }
fun markToDelete(gameId: Long) {
gameAndHistory = fullList.filter { it.game.uid != gameId }
}
fun unmarkToDelete(gameId: Long) {
gameAndHistory = fullList
}
fun deleteGame(gameId: Long) { fun deleteGame(gameId: Long) {
viewModelScope.launch { viewModelScope.launch {
gameRepository.deleteGame(gameId) gameRepository.deleteGame(gameId)

View File

@@ -1,20 +1,28 @@
package me.zobrist.tichucounter.ui.layout package me.zobrist.tichucounter.ui.layout
import android.content.res.Configuration import android.content.res.Configuration
import androidx.compose.foundation.layout.* import androidx.compose.foundation.layout.padding
import androidx.compose.material.icons.Icons import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.* import androidx.compose.material.icons.outlined.Calculate
import androidx.compose.material.icons.outlined.* import androidx.compose.material.icons.outlined.List
import androidx.compose.material3.* import androidx.compose.material.icons.outlined.Settings
import androidx.compose.runtime.* import androidx.compose.material3.Divider
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.ModalDrawerSheet
import androidx.compose.material3.NavigationDrawerItem
import androidx.compose.material3.NavigationDrawerItemDefaults
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import me.zobrist.tichucounter.R import me.zobrist.tichucounter.R
import me.zobrist.tichucounter.domain.* import me.zobrist.tichucounter.domain.DrawerItem
import me.zobrist.tichucounter.domain.Route
import me.zobrist.tichucounter.ui.AppTheme import me.zobrist.tichucounter.ui.AppTheme
import me.zobrist.tichucounter.ui.counter.*
@Composable @Composable
fun DrawerContent( fun DrawerContent(

View File

@@ -24,5 +24,10 @@
<string name="menu_counter">Counter</string> <string name="menu_counter">Counter</string>
<string name="menu_about">About</string> <string name="menu_about">About</string>
<string name="contact_us">Schreib uns</string> <string name="contact_us">Schreib uns</string>
<string name="continue_play">Weiterspielen</string>
<string name="delete_success">Spiel gelöscht.</string>
<string name="undo_question">RÜCKGÄNGIG</string>
<string name="activated_success">Spiel aktiviert.</string>
<string name="to_calculator_question">WEITERSPIELEN</string>
</resources> </resources>

View File

@@ -17,7 +17,7 @@
<string name="off">Off</string> <string name="off">Off</string>
<string name="newGame">New Game</string> <string name="newGame">New Game</string>
<string name="delete_inactive_title">Delete history</string> <string name="delete_inactive_title">Delete history</string>
<string name="delete_inactive_text">You really want to delete the the history? This action can\'t be undone.</string> <string name="delete_inactive_text">You really want to delete the history? This action can\'t be undone.</string>
<string name="cancel">Cancel</string> <string name="cancel">Cancel</string>
<string name="ok">OK</string> <string name="ok">OK</string>
<string name="delete">Delete</string> <string name="delete">Delete</string>
@@ -28,4 +28,9 @@
<string name="menu_about">About</string> <string name="menu_about">About</string>
<string name="contact_us">Contact us</string> <string name="contact_us">Contact us</string>
<string name="play_store" translatable="false">Play Store</string> <string name="play_store" translatable="false">Play Store</string>
<string name="continue_play">Continue</string>
<string name="delete_success">Game deleted.</string>
<string name="undo_question">UNDO</string>
<string name="activated_success">Game activated.</string>
<string name="to_calculator_question">CONTINUE PLAYING</string>
</resources> </resources>

View File

@@ -1,12 +1,12 @@
// Top-level build file where you can add configuration options common to all sub-projects/modules. // Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript { buildscript {
ext.kotlin_version = "1.8.21" ext.kotlin_version = "1.8.22"
repositories { repositories {
google() google()
mavenCentral() mavenCentral()
} }
dependencies { dependencies {
classpath 'com.android.tools.build:gradle:8.0.2' classpath 'com.android.tools.build:gradle:8.1.0'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
// NOTE: Do not place your application dependencies here; they belong // NOTE: Do not place your application dependencies here; they belong