This commit is contained in:
mac
2026-03-09 20:23:57 -04:00
commit 7a2c09314d
49 changed files with 1403 additions and 0 deletions

1
app/.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
/build

58
app/build.gradle.kts Normal file
View File

@@ -0,0 +1,58 @@
plugins {
alias(libs.plugins.android.application)
alias(libs.plugins.kotlin.compose)
}
android {
namespace = "com.example.playninescore"
compileSdk {
version = release(36) {
minorApiLevel = 1
}
}
defaultConfig {
applicationId = "com.example.playninescore"
minSdk = 24
targetSdk = 36
versionCode = 1
versionName = "1.0"
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
isMinifyEnabled = false
proguardFiles(
getDefaultProguardFile("proguard-android-optimize.txt"),
"proguard-rules.pro"
)
}
}
compileOptions {
sourceCompatibility = JavaVersion.VERSION_11
targetCompatibility = JavaVersion.VERSION_11
}
buildFeatures {
compose = true
}
}
dependencies {
implementation(libs.androidx.core.ktx)
implementation(libs.androidx.lifecycle.runtime.ktx)
implementation(libs.androidx.activity.compose)
implementation(platform(libs.androidx.compose.bom))
implementation(libs.androidx.compose.ui)
implementation(libs.androidx.compose.ui.graphics)
implementation(libs.androidx.compose.ui.tooling.preview)
implementation(libs.androidx.compose.material3)
testImplementation(libs.junit)
androidTestImplementation(libs.androidx.junit)
androidTestImplementation(libs.androidx.espresso.core)
androidTestImplementation(platform(libs.androidx.compose.bom))
androidTestImplementation(libs.androidx.compose.ui.test.junit4)
debugImplementation(libs.androidx.compose.ui.tooling)
debugImplementation(libs.androidx.compose.ui.test.manifest)
}

21
app/proguard-rules.pro vendored Normal file
View File

@@ -0,0 +1,21 @@
# Add project specific ProGuard rules here.
# You can control the set of applied configuration files using the
# proguardFiles setting in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable
# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile

View File

@@ -0,0 +1,24 @@
package com.example.playninescore
import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.ext.junit.runners.AndroidJUnit4
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.Assert.*
/**
* Instrumented test, which will execute on an Android device.
*
* See [testing documentation](http://d.android.com/tools/testing).
*/
@RunWith(AndroidJUnit4::class)
class ExampleInstrumentedTest {
@Test
fun useAppContext() {
// Context of the app under test.
val appContext = InstrumentationRegistry.getInstrumentation().targetContext
assertEquals("com.example.playninescore", appContext.packageName)
}
}

View File

@@ -0,0 +1,27 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<application
android:allowBackup="true"
android:dataExtractionRules="@xml/data_extraction_rules"
android:fullBackupContent="@xml/backup_rules"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.PlayNineScore">
<activity
android:name=".MainActivity"
android:exported="true"
android:label="@string/app_name"
android:theme="@style/Theme.PlayNineScore">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>

View File

@@ -0,0 +1,438 @@
package com.example.playninescore
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.activity.enableEdgeToEdge
import androidx.compose.foundation.Canvas
import androidx.compose.foundation.background
import androidx.compose.foundation.border
import androidx.compose.foundation.horizontalScroll
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.itemsIndexed
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.text.BasicTextField
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.foundation.verticalScroll
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Add
import androidx.compose.material.icons.filled.Delete
import androidx.compose.material.icons.filled.Refresh
import androidx.compose.material.icons.filled.Star
import androidx.compose.material3.*
import androidx.compose.runtime.*
import androidx.compose.runtime.getValue
import androidx.compose.runtime.setValue
import androidx.compose.runtime.saveable.Saver
import androidx.compose.runtime.saveable.listSaver
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.focus.onFocusChanged
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.geometry.Size
import androidx.compose.ui.graphics.Brush
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.Path
import androidx.compose.ui.graphics.TileMode
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.input.KeyboardType
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import com.example.playninescore.ui.theme.PlayNineScoreTheme
// Thème de couleurs Mini Golf
val GolfGreen = Color(0xFF2E7D32)
val FairwayGreen = Color(0xFF4CAF50)
val FairwayGreenDark = Color(0xFF388E3C)
val GrassLight = Color(0xFFE8F5E9)
val FlagRed = Color(0xFFD32F2F)
val GolfBallWhite = Color(0xFFF5F5F5)
data class Player(
val name: String = "",
val scores: List<String?> = List(9) { null }
)
val PlayerListSaver: Saver<List<Player>, Any> = listSaver(
save = { list ->
list.map { player ->
listOf(player.name) + player.scores.map { it ?: "" }
}
},
restore = { restored ->
val list = restored as? List<*>
list?.map { item ->
val data = item as List<*>
Player(
name = data[0] as String,
scores = data.drop(1).map { val s = it as String; if (s.isEmpty()) null else s }
)
} ?: emptyList()
}
)
@Composable
fun FairwayBackground(content: @Composable () -> Unit) {
Box(modifier = Modifier.fillMaxSize()) {
// Dessin des bandes du fairway
Canvas(modifier = Modifier.fillMaxSize()) {
val stripeWidth = 80.dp.toPx()
var x = 0f
var index = 0
while (x < size.width) {
drawRect(
color = if (index % 2 == 0) FairwayGreen else FairwayGreenDark,
topLeft = Offset(x, 0f),
size = Size(stripeWidth, size.height)
)
x += stripeWidth
index++
}
// Texture d'herbe légère par-dessus
drawRect(
brush = Brush.verticalGradient(
colors = listOf(Color.Black.copy(alpha = 0.05f), Color.Transparent, Color.Black.copy(alpha = 0.05f))
)
)
}
content()
}
}
@Composable
fun GolfBallImage(modifier: Modifier = Modifier) {
Canvas(modifier = modifier.size(60.dp)) {
// Ombre portée
drawOval(
color = Color.Black.copy(alpha = 0.2f),
topLeft = Offset(size.width * 0.1f, size.height * 0.75f),
size = Size(size.width * 0.8f, size.height * 0.25f)
)
// Corps de la balle
drawCircle(color = Color.White)
drawCircle(color = Color.LightGray, radius = size.minDimension / 2f, style = androidx.compose.ui.graphics.drawscope.Stroke(width = 1.dp.toPx()))
// Petites alvéoles (dimples)
val rows = 5
val cols = 5
for (i in 1 until rows) {
for (j in 1 until cols) {
drawCircle(
color = Color.LightGray.copy(alpha = 0.3f),
radius = 2.dp.toPx(),
center = Offset(size.width * (i/rows.toFloat()), size.height * (j/cols.toFloat()))
)
}
}
}
}
@Composable
fun GolfFlagImage(modifier: Modifier = Modifier) {
Canvas(modifier = modifier.size(40.dp)) {
val poleWidth = 2.dp.toPx()
// Le trou
drawOval(Color.Black.copy(alpha = 0.3f), Offset(0f, size.height * 0.8f), Size(size.width, size.height * 0.2f))
// Le mât
drawRect(Color.DarkGray, Offset(size.width/2 - poleWidth/2, 10f), Size(poleWidth, size.height * 0.85f))
// Le drapeau
val path = Path().apply {
moveTo(size.width/2, 10f)
lineTo(size.width * 0.9f, size.height * 0.25f)
lineTo(size.width/2, size.height * 0.4f)
close()
}
drawPath(path, FlagRed)
}
}
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
enableEdgeToEdge()
setContent {
PlayNineScoreTheme {
FairwayBackground {
PlayNineApp()
}
}
}
}
}
@Composable
fun PlayNineApp() {
var players by rememberSaveable(stateSaver = PlayerListSaver) {
mutableStateOf(listOf<Player>())
}
var gameStarted by rememberSaveable { mutableStateOf(false) }
Scaffold(
modifier = Modifier.fillMaxSize(),
containerColor = Color.Transparent
) { innerPadding ->
val modifier = Modifier.padding(innerPadding)
if (!gameStarted) {
SetupScreen(
players = players,
onPlayersChange = { players = it },
onStartGame = { if (players.isNotEmpty()) gameStarted = true },
modifier = modifier
)
} else {
ScoreScreen(
players = players,
onScoresChange = { players = it },
onReset = {
players = emptyList()
gameStarted = false
},
modifier = modifier
)
}
}
}
@Composable
fun SetupScreen(
players: List<Player>,
onPlayersChange: (List<Player>) -> Unit,
onStartGame: () -> Unit,
modifier: Modifier = Modifier
) {
var newPlayerName by remember { mutableStateOf("") }
Column(
modifier = modifier.fillMaxSize().padding(24.dp),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center
) {
GolfBallImage()
Spacer(modifier = Modifier.height(16.dp))
Text(
"PLAY NINE",
fontSize = 38.sp,
fontWeight = FontWeight.Black,
color = Color.White,
style = TextStyle(
shadow = androidx.compose.ui.graphics.Shadow(Color.Black, Offset(2f, 2f), 4f)
)
)
Text("Le compteur de score ultime", fontSize = 14.sp, color = Color.White.copy(alpha = 0.9f))
Spacer(modifier = Modifier.height(32.dp))
Card(
modifier = Modifier.fillMaxWidth(),
shape = RoundedCornerShape(16.dp),
colors = CardDefaults.cardColors(containerColor = Color.White.copy(alpha = 0.95f)),
elevation = CardDefaults.cardElevation(8.dp)
) {
Row(
modifier = Modifier.padding(8.dp),
verticalAlignment = Alignment.CenterVertically
) {
TextField(
value = newPlayerName,
onValueChange = { newPlayerName = it },
placeholder = { Text("Nom du joueur...") },
colors = TextFieldDefaults.colors(
focusedContainerColor = Color.Transparent,
unfocusedContainerColor = Color.Transparent,
focusedIndicatorColor = GolfGreen
),
modifier = Modifier.weight(1f),
singleLine = true
)
IconButton(
onClick = {
if (newPlayerName.isNotBlank() && players.size < 6) {
onPlayersChange(players + Player(name = newPlayerName))
newPlayerName = ""
}
},
colors = IconButtonDefaults.iconButtonColors(contentColor = GolfGreen)
) {
Icon(Icons.Default.Add, contentDescription = "Ajouter")
}
}
}
Spacer(modifier = Modifier.height(24.dp))
Box(modifier = Modifier.weight(1f, fill = false).padding(vertical = 8.dp)) {
LazyColumn(
modifier = Modifier.fillMaxWidth(),
horizontalAlignment = Alignment.CenterHorizontally
) {
itemsIndexed(players) { index, player ->
Card(
modifier = Modifier.fillMaxWidth().padding(vertical = 4.dp),
shape = RoundedCornerShape(12.dp),
colors = CardDefaults.cardColors(containerColor = Color.White.copy(alpha = 0.85f))
) {
ListItem(
headlineContent = { Text(player.name, fontWeight = FontWeight.Bold, color = GolfGreen) },
leadingContent = {
Box(Modifier.size(32.dp).background(GolfGreen, CircleShape), contentAlignment = Alignment.Center) {
Text("${index + 1}", color = Color.White, fontSize = 12.sp)
}
},
trailingContent = {
IconButton(onClick = {
val list = players.toMutableList()
list.removeAt(index)
onPlayersChange(list)
}) {
Icon(Icons.Default.Delete, contentDescription = "Supprimer", tint = FlagRed)
}
},
colors = ListItemDefaults.colors(containerColor = Color.Transparent)
)
}
}
}
}
Spacer(modifier = Modifier.height(16.dp))
Button(
onClick = onStartGame,
modifier = Modifier.fillMaxWidth().height(60.dp),
enabled = players.isNotEmpty(),
colors = ButtonDefaults.buttonColors(containerColor = GolfGreen),
shape = RoundedCornerShape(16.dp),
elevation = ButtonDefaults.buttonElevation(4.dp)
) {
Text("ENTRER SUR LE GREEN", fontSize = 18.sp, fontWeight = FontWeight.Black)
}
}
}
@Composable
fun ScoreScreen(
players: List<Player>,
onScoresChange: (List<Player>) -> Unit,
onReset: () -> Unit,
modifier: Modifier = Modifier
) {
val scrollState = rememberScrollState()
val holeW = 60.dp
val playerW = 100.dp
Column(
modifier = modifier.fillMaxSize().padding(12.dp),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.SpaceBetween
) {
// En-tête
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically
) {
Row(verticalAlignment = Alignment.CenterVertically) {
GolfFlagImage(Modifier.size(35.dp))
Spacer(Modifier.width(8.dp))
Text(
"Scorecard",
fontSize = 28.sp,
fontWeight = FontWeight.Black,
color = Color.White,
style = TextStyle(
shadow = androidx.compose.ui.graphics.Shadow(Color.Black, Offset(2f, 2f), 4f)
)
)
}
IconButton(
onClick = onReset,
colors = IconButtonDefaults.iconButtonColors(containerColor = Color.White.copy(alpha = 0.2f))
) {
Icon(Icons.Default.Refresh, contentDescription = "Reset", tint = Color.White)
}
}
// Centre de l'écran : La Card de score
Box(
modifier = Modifier.weight(1f).fillMaxWidth(),
contentAlignment = Alignment.Center
) {
Card(
modifier = Modifier.wrapContentHeight().fillMaxWidth(),
shape = RoundedCornerShape(16.dp),
colors = CardDefaults.cardColors(containerColor = Color.White.copy(alpha = 0.95f)),
elevation = CardDefaults.cardElevation(8.dp)
) {
Box(modifier = Modifier.horizontalScroll(scrollState)) {
Column {
// Header du tableau
Row(modifier = Modifier.background(GolfGreen).padding(vertical = 10.dp)) {
Text("Trou", Modifier.width(holeW), color = Color.White, textAlign = TextAlign.Center, fontWeight = FontWeight.Black)
players.forEach { player ->
Text(player.name, Modifier.width(playerW), color = Color.White, textAlign = TextAlign.Center, fontWeight = FontWeight.Black, maxLines = 1)
}
}
// Contenu (Scrollable verticalement seulement si nécessaire)
val verticalScrollState = rememberScrollState()
Column(modifier = Modifier.verticalScroll(verticalScrollState)) {
for (hIdx in 0 until 9) {
val isEven = hIdx % 2 == 0
Row(
modifier = Modifier.background(if (isEven) Color.Transparent else GolfGreen.copy(alpha = 0.05f)),
verticalAlignment = Alignment.CenterVertically
) {
Text("${hIdx + 1}", Modifier.width(holeW).padding(vertical = 16.dp), textAlign = TextAlign.Center, fontWeight = FontWeight.ExtraBold, color = GolfGreen)
players.forEachIndexed { pIdx, player ->
var isFocused by remember { mutableStateOf(false) }
Box(
modifier = Modifier.width(playerW).height(55.dp)
.background(if (isFocused) FairwayGreen.copy(alpha = 0.2f) else Color.Transparent)
.border(0.5.dp, Color.LightGray.copy(alpha = 0.3f)),
contentAlignment = Alignment.Center
) {
BasicTextField(
value = player.scores[hIdx] ?: "",
onValueChange = { nv ->
if (nv.isEmpty() || nv == "-" || nv.toIntOrNull() != null) {
val upP = players.toMutableList()
val upS = player.scores.toMutableList()
upS[hIdx] = nv
upP[pIdx] = player.copy(scores = upS)
onScoresChange(upP)
}
},
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number),
textStyle = TextStyle(textAlign = TextAlign.Center, fontSize = 20.sp, fontWeight = FontWeight.Bold, color = if(isFocused) GolfGreen else Color.DarkGray),
modifier = Modifier.fillMaxWidth().onFocusChanged { isFocused = it.isFocused },
singleLine = true
)
}
}
}
}
// Total Row
Row(modifier = Modifier.background(FairwayGreen).padding(vertical = 14.dp)) {
Text("TOTAL", Modifier.width(holeW), color = Color.White, textAlign = TextAlign.Center, fontWeight = FontWeight.Black)
players.forEach { player ->
val total = player.scores.sumOf { it?.toIntOrNull() ?: 0 }
Text("$total", Modifier.width(playerW), color = Color.White, textAlign = TextAlign.Center, fontWeight = FontWeight.Black, fontSize = 22.sp)
}
}
}
}
}
}
}
}
}

View File

@@ -0,0 +1,11 @@
package com.example.playninescore.ui.theme
import androidx.compose.ui.graphics.Color
val Purple80 = Color(0xFFD0BCFF)
val PurpleGrey80 = Color(0xFFCCC2DC)
val Pink80 = Color(0xFFEFB8C8)
val Purple40 = Color(0xFF6650a4)
val PurpleGrey40 = Color(0xFF625b71)
val Pink40 = Color(0xFF7D5260)

View File

@@ -0,0 +1,58 @@
package com.example.playninescore.ui.theme
import android.app.Activity
import android.os.Build
import androidx.compose.foundation.isSystemInDarkTheme
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.darkColorScheme
import androidx.compose.material3.dynamicDarkColorScheme
import androidx.compose.material3.dynamicLightColorScheme
import androidx.compose.material3.lightColorScheme
import androidx.compose.runtime.Composable
import androidx.compose.ui.platform.LocalContext
private val DarkColorScheme = darkColorScheme(
primary = Purple80,
secondary = PurpleGrey80,
tertiary = Pink80
)
private val LightColorScheme = lightColorScheme(
primary = Purple40,
secondary = PurpleGrey40,
tertiary = Pink40
/* Other default colors to override
background = Color(0xFFFFFBFE),
surface = Color(0xFFFFFBFE),
onPrimary = Color.White,
onSecondary = Color.White,
onTertiary = Color.White,
onBackground = Color(0xFF1C1B1F),
onSurface = Color(0xFF1C1B1F),
*/
)
@Composable
fun PlayNineScoreTheme(
darkTheme: Boolean = isSystemInDarkTheme(),
// Dynamic color is available on Android 12+
dynamicColor: Boolean = true,
content: @Composable () -> Unit
) {
val colorScheme = when {
dynamicColor && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> {
val context = LocalContext.current
if (darkTheme) dynamicDarkColorScheme(context) else dynamicLightColorScheme(context)
}
darkTheme -> DarkColorScheme
else -> LightColorScheme
}
MaterialTheme(
colorScheme = colorScheme,
typography = Typography,
content = content
)
}

View File

@@ -0,0 +1,34 @@
package com.example.playninescore.ui.theme
import androidx.compose.material3.Typography
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.font.FontFamily
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.sp
// Set of Material typography styles to start with
val Typography = Typography(
bodyLarge = TextStyle(
fontFamily = FontFamily.Default,
fontWeight = FontWeight.Normal,
fontSize = 16.sp,
lineHeight = 24.sp,
letterSpacing = 0.5.sp
)
/* Other default text styles to override
titleLarge = TextStyle(
fontFamily = FontFamily.Default,
fontWeight = FontWeight.Normal,
fontSize = 22.sp,
lineHeight = 28.sp,
letterSpacing = 0.sp
),
labelSmall = TextStyle(
fontFamily = FontFamily.Default,
fontWeight = FontWeight.Medium,
fontSize = 11.sp,
lineHeight = 16.sp,
letterSpacing = 0.5.sp
)
*/
)

View File

@@ -0,0 +1,15 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="108dp"
android:height="108dp"
android:viewportWidth="108"
android:viewportHeight="108">
<!-- Fond vert type fairway -->
<path
android:fillColor="#4CAF50"
android:pathData="M0,0h108v108h-108z" />
<!-- Bandes de tonte -->
<path
android:fillColor="#388E3C"
android:pathData="M0,0h27v108h-27z M54,0h27v108h-27z" />
</vector>

View File

@@ -0,0 +1,43 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="108dp"
android:height="108dp"
android:viewportWidth="108"
android:viewportHeight="108">
<!-- Ombre légère de la balle -->
<path
android:fillColor="#22000000"
android:pathData="M54,58m-36,0a36,36 0,1 1,72 0a36,36 0,1 1,-72 0" />
<!-- Corps de la balle de golf (blanc) -->
<path
android:fillColor="#FFFFFF"
android:pathData="M54,54m-34,0a34,34 0,1 1,68 0a34,34 0,1 1,-68 0" />
<!-- Bordure légère -->
<path
android:strokeColor="#E0E0E0"
android:strokeWidth="0.5"
android:pathData="M54,54m-34,0a34,34 0,1 1,68 0a34,34 0,1 1,-68 0" />
<!-- Alvéoles (dimples) -->
<group android:translateX="54" android:translateY="54">
<!-- Centre -->
<path android:fillColor="#EEEEEE" android:pathData="M0,0m-3,0a3,3 0,1 1,6 0a3,3 0,1 1,-6 0" />
<!-- Cercle intérieur -->
<path android:fillColor="#EEEEEE" android:pathData="M-12,-8m-3,0a3,3 0,1 1,6 0a3,3 0,1 1,-6 0" />
<path android:fillColor="#EEEEEE" android:pathData="M12,-8m-3,0a3,3 0,1 1,6 0a3,3 0,1 1,-6 0" />
<path android:fillColor="#EEEEEE" android:pathData="M0,14m-3,0a3,3 0,1 1,6 0a3,3 0,1 1,-6 0" />
<!-- Cercle extérieur -->
<path android:fillColor="#EEEEEE" android:pathData="M-22,0m-3,0a3,3 0,1 1,6 0a3,3 0,1 1,-6 0" />
<path android:fillColor="#EEEEEE" android:pathData="M22,0m-3,0a3,3 0,1 1,6 0a3,3 0,1 1,-6 0" />
<path android:fillColor="#EEEEEE" android:pathData="M-14,20m-3,0a3,3 0,1 1,6 0a3,3 0,1 1,-6 0" />
<path android:fillColor="#EEEEEE" android:pathData="M14,20m-3,0a3,3 0,1 1,6 0a3,3 0,1 1,-6 0" />
<path android:fillColor="#EEEEEE" android:pathData="M0,-22m-3,0a3,3 0,1 1,6 0a3,3 0,1 1,-6 0" />
<path android:fillColor="#EEEEEE" android:pathData="M-18,-18m-3,0a3,3 0,1 1,6 0a3,3 0,1 1,-6 0" />
<path android:fillColor="#EEEEEE" android:pathData="M18,-18m-3,0a3,3 0,1 1,6 0a3,3 0,1 1,-6 0" />
</group>
</vector>

View File

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@drawable/ic_launcher_background" />
<foreground android:drawable="@drawable/ic_launcher_foreground" />
<monochrome android:drawable="@drawable/ic_launcher_foreground" />
</adaptive-icon>

View File

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@drawable/ic_launcher_background" />
<foreground android:drawable="@drawable/ic_launcher_foreground" />
<monochrome android:drawable="@drawable/ic_launcher_foreground" />
</adaptive-icon>

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 982 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.6 KiB

View File

@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="purple_200">#FFBB86FC</color>
<color name="purple_500">#FF6200EE</color>
<color name="purple_700">#FF3700B3</color>
<color name="teal_200">#FF03DAC5</color>
<color name="teal_700">#FF018786</color>
<color name="black">#FF000000</color>
<color name="white">#FFFFFFFF</color>
</resources>

View File

@@ -0,0 +1,3 @@
<resources>
<string name="app_name">Play nine score</string>
</resources>

View File

@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<style name="Theme.PlayNineScore" parent="android:Theme.Material.Light.NoActionBar" />
</resources>

View File

@@ -0,0 +1,13 @@
<?xml version="1.0" encoding="utf-8"?><!--
Sample backup rules file; uncomment and customize as necessary.
See https://developer.android.com/guide/topics/data/autobackup
for details.
Note: This file is ignored for devices older than API 31
See https://developer.android.com/about/versions/12/backup-restore
-->
<full-backup-content>
<!--
<include domain="sharedpref" path="."/>
<exclude domain="sharedpref" path="device.xml"/>
-->
</full-backup-content>

View File

@@ -0,0 +1,19 @@
<?xml version="1.0" encoding="utf-8"?><!--
Sample data extraction rules file; uncomment and customize as necessary.
See https://developer.android.com/about/versions/12/backup-restore#xml-changes
for details.
-->
<data-extraction-rules>
<cloud-backup>
<!-- TODO: Use <include> and <exclude> to control what is backed up.
<include .../>
<exclude .../>
-->
</cloud-backup>
<!--
<device-transfer>
<include .../>
<exclude .../>
</device-transfer>
-->
</data-extraction-rules>

View File

@@ -0,0 +1,17 @@
package com.example.playninescore
import org.junit.Test
import org.junit.Assert.*
/**
* Example local unit test, which will execute on the development machine (host).
*
* See [testing documentation](http://d.android.com/tools/testing).
*/
class ExampleUnitTest {
@Test
fun addition_isCorrect() {
assertEquals(4, 2 + 2)
}
}