Merge pull request 'feature/swipe-actions' (#44) from feature/swipe-actions into develop
All checks were successful
Build Android / build (push) Successful in 7m26s
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:
@@ -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'
|
||||||
|
|||||||
@@ -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 =
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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, {}, {}, {})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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(
|
||||||
|
|||||||
@@ -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>
|
||||||
@@ -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>
|
||||||
@@ -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
|
||||||
|
|||||||
Reference in New Issue
Block a user