iOS and Android Updates!

This commit is contained in:
2026-01-02 22:23:51 -07:00
parent 61a81d4d91
commit cde4b41ade
13 changed files with 1283 additions and 327 deletions

1
android/.idea/.name generated Normal file
View File

@@ -0,0 +1 @@
MagicCounter

View File

@@ -1,4 +1,3 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ExternalStorageConfigurationManager" enabled="true" />
<component name="ProjectRootManager" version="2" languageLevel="JDK_17" project-jdk-name="jbr-21" project-jdk-type="JavaSDK">

1
android/.idea/vcs.xml generated
View File

@@ -2,5 +2,6 @@
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="" vcs="Git" />
<mapping directory="$PROJECT_DIR$/.." vcs="Git" />
</component>
</project>

View File

@@ -15,8 +15,8 @@ android {
applicationId = "com.atridad.magiccounter"
minSdk = 31
targetSdk = 36
versionCode = 3
versionName = "1.3.0"
versionCode = 4
versionName = "1.4.0"
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
}

View File

@@ -65,7 +65,7 @@ fun MagicCounterApp() {
val settingsVm: AppSettingsViewModel = viewModel()
val theme = settingsVm.themeMode.collectAsState()
val historyState = settingsVm.matchHistory.collectAsState()
MagicCounterTheme(themeMode = theme.value) {
@@ -81,7 +81,7 @@ fun MagicCounterApp() {
}
},
actions = {
IconButton(onClick = { screenStack.add(Screen.Settings) }) {
Icon(Icons.Default.Settings, contentDescription = "App settings")
}
@@ -142,7 +142,7 @@ fun MagicCounterApp() {
screenStack.removeAt(screenStack.lastIndex)
return@SetupScreen
}
// Create and persist a new MatchRecord
val newId = java.util.UUID.randomUUID().toString()
val now = System.currentTimeMillis()
@@ -175,6 +175,8 @@ fun MagicCounterApp() {
.padding(paddingValues)
.fillMaxSize(),
state = stateForGame,
matchName = record?.name ?: "Game",
startedAtEpochMs = record?.startedAtEpochMs ?: System.currentTimeMillis(),
onProgress = { updated ->
val current = historyState.value
val idx = current.indexOfFirst { it.id == id }
@@ -233,7 +235,7 @@ fun MagicCounterApp() {
onSelect = { settingsVm.setTheme(it) }
)
}
}
}
@@ -242,7 +244,7 @@ fun MagicCounterApp() {
@Composable
private fun SettingsContent(modifier: Modifier, current: ThemeMode, onSelect: (ThemeMode) -> Unit) {
Column(
modifier = modifier.padding(24.dp),
modifier = modifier.padding(24.dp),
verticalArrangement = Arrangement.spacedBy(24.dp)
) {
Text(
@@ -250,7 +252,7 @@ private fun SettingsContent(modifier: Modifier, current: ThemeMode, onSelect: (T
style = MaterialTheme.typography.headlineSmall,
color = MaterialTheme.colorScheme.primary
)
Column(verticalArrangement = Arrangement.spacedBy(16.dp)) {
Text(
"Theme",
@@ -291,17 +293,17 @@ private fun HomeScreen(
contentAlignment = Alignment.Center
) {
Column(
horizontalAlignment = Alignment.CenterHorizontally,
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.spacedBy(16.dp)
) {
Icon(
Icons.Default.History,
Icons.Default.History,
contentDescription = "Empty history",
modifier = Modifier.size(64.dp),
tint = MaterialTheme.colorScheme.onSurfaceVariant
)
Text(
"No games yet",
"No games yet",
style = MaterialTheme.typography.headlineSmall,
color = MaterialTheme.colorScheme.onSurfaceVariant
)
@@ -332,13 +334,13 @@ private fun HomeScreen(
onClick = { onResume(activeGame) }
) {
Row(
modifier = Modifier.fillMaxWidth().padding(16.dp),
modifier = Modifier.fillMaxWidth().padding(16.dp),
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically
) {
Column(modifier = Modifier.weight(1f)) {
Text(
"Active Game",
"Active Game",
style = MaterialTheme.typography.titleMedium,
color = MaterialTheme.colorScheme.onPrimary
)
@@ -382,23 +384,29 @@ private fun HomeScreen(
onClick = { onResume(rec) }
) {
Row(
modifier = Modifier.fillMaxWidth().padding(16.dp),
modifier = Modifier.fillMaxWidth().padding(16.dp),
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically
) {
Column(modifier = Modifier.weight(1f)) {
Text(
rec.name,
rec.name,
style = MaterialTheme.typography.bodyLarge,
color = MaterialTheme.colorScheme.onSurfaceVariant
)
val status = if (rec.state.stopped) "Stopped" else "Finished"
val winner = rec.winnerPlayerId?.let { " • Winner: Player ${it + 1}" } ?: ""
val statusText = if (winner.isNotEmpty()) "$status$winner" else status
val winnerId = rec.winnerPlayerId ?: rec.state.winnerPlayerId
val winnerName = winnerId?.let { wId ->
rec.state.players.find { it.id == wId }?.name
}
val statusText = when {
winnerName != null -> "Winner: $winnerName"
rec.state.stopped -> "Stopped"
else -> "Finished"
}
Text(
statusText,
style = MaterialTheme.typography.bodyMedium,
color = MaterialTheme.colorScheme.onSurfaceVariant.copy(alpha = 0.7f)
color = if (winnerName != null) Color(0xFF2E7D32) else MaterialTheme.colorScheme.onSurfaceVariant.copy(alpha = 0.7f)
)
}
Row(horizontalArrangement = Arrangement.spacedBy(8.dp)) {
@@ -409,12 +417,12 @@ private fun HomeScreen(
containerColor = MaterialTheme.colorScheme.secondary,
contentColor = MaterialTheme.colorScheme.onSecondary
)
) {
) {
Icon(
Icons.Default.Visibility,
Icons.Default.Visibility,
contentDescription = "View match",
modifier = Modifier.size(20.dp)
)
)
}
androidx.compose.material3.FilledTonalIconButton(
onClick = { pendingDeleteId = rec.id },
@@ -423,12 +431,12 @@ private fun HomeScreen(
containerColor = MaterialTheme.colorScheme.error,
contentColor = MaterialTheme.colorScheme.onError
)
) {
) {
Icon(
Icons.Default.Delete,
Icons.Default.Delete,
contentDescription = "Delete",
modifier = Modifier.size(20.dp)
)
)
}
}
}
@@ -461,14 +469,14 @@ private fun GameDuration(
color: Color
) {
var duration by remember { mutableStateOf("") }
LaunchedEffect(startTime) {
while (true) {
val elapsed = System.currentTimeMillis() - startTime
val seconds = (elapsed / 1000).toInt()
val minutes = seconds / 60
val remainingSeconds = seconds % 60
duration = when {
minutes > 0 -> "Duration: ${minutes}m ${remainingSeconds}s"
else -> "${remainingSeconds}s"
@@ -476,12 +484,10 @@ private fun GameDuration(
delay(1000)
}
}
Text(
text = duration,
style = MaterialTheme.typography.bodyMedium,
color = color.copy(alpha = 0.8f)
)
}

View File

@@ -21,9 +21,8 @@ data class GameState(
val startingLife: Int,
val trackPoison: Boolean,
val trackCommanderDamage: Boolean,
val stopped: Boolean = false
val stopped: Boolean = false,
val winnerPlayerId: Int? = null
)
fun defaultPlayerName(index: Int): String = "Player ${index + 1}"

View File

@@ -201,6 +201,25 @@ object CustomIcons {
close()
}.build()
fun Droplet(color: Color = Color.Black): ImageVector = ImageVector.Builder(
name = "Droplet",
defaultWidth = 24.dp,
defaultHeight = 24.dp,
viewportWidth = 24f,
viewportHeight = 24f
).path(
fill = SolidColor(color)
) {
moveTo(12f, 2.69f)
lineTo(17.66f, 8.35f)
curveTo(19.1f, 9.79f, 20f, 11.79f, 20f, 14f)
curveTo(20f, 18.42f, 16.42f, 22f, 12f, 22f)
curveTo(7.58f, 22f, 4f, 18.42f, 4f, 14f)
curveTo(4f, 11.79f, 4.9f, 9.79f, 6.34f, 8.35f)
lineTo(12f, 2.69f)
close()
}.build()
fun Sword(color: Color = Color.Black): ImageVector = ImageVector.Builder(
name = "Sword",
defaultWidth = 24.dp,

View File

@@ -1,23 +1,23 @@
[versions]
agp = "8.12.3"
kotlin = "2.0.21"
coreKtx = "1.10.1"
kotlin = "2.3.0"
coreKtx = "1.17.0"
junit = "4.13.2"
junitVersion = "1.1.5"
espressoCore = "3.5.1"
appcompat = "1.6.1"
material = "1.10.0"
constraintlayout = "2.1.4"
lifecycleLivedataKtx = "2.6.1"
lifecycleViewmodelKtx = "2.6.1"
navigationFragmentKtx = "2.6.0"
navigationUiKtx = "2.6.0"
composeBom = "2024.10.01"
activityCompose = "1.9.2"
lifecycleRuntimeCompose = "2.8.6"
lifecycleViewmodelCompose = "2.8.6"
datastore = "1.1.1"
serialization = "1.7.3"
junitVersion = "1.3.0"
espressoCore = "3.7.0"
appcompat = "1.7.1"
material = "1.13.0"
constraintlayout = "2.2.1"
lifecycleLivedataKtx = "2.10.0"
lifecycleViewmodelKtx = "2.10.0"
navigationFragmentKtx = "2.9.6"
navigationUiKtx = "2.9.6"
composeBom = "2025.12.01"
activityCompose = "1.12.2"
lifecycleRuntimeCompose = "2.10.0"
lifecycleViewmodelCompose = "2.10.0"
datastore = "1.2.0"
serialization = "1.9.0"
[libraries]
androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" }