feat: Save locally all parties
@@ -11,6 +11,9 @@
|
||||
<SelectionState runConfigName="MainActivity">
|
||||
<option name="selectionMode" value="DROPDOWN" />
|
||||
</SelectionState>
|
||||
<SelectionState runConfigName="Queezer">
|
||||
<option name="selectionMode" value="DROPDOWN" />
|
||||
</SelectionState>
|
||||
</selectionStates>
|
||||
</component>
|
||||
</project>
|
||||
@@ -2,6 +2,7 @@ plugins {
|
||||
alias(libs.plugins.android.application)
|
||||
alias(libs.plugins.kotlin.android)
|
||||
alias(libs.plugins.kotlin.compose)
|
||||
id("kotlin-kapt")
|
||||
}
|
||||
|
||||
android {
|
||||
@@ -39,7 +40,24 @@ android {
|
||||
}
|
||||
}
|
||||
|
||||
apply(
|
||||
plugin = "kotlin-kapt"
|
||||
)
|
||||
|
||||
dependencies {
|
||||
implementation(libs.androidx.runtime.livedata)
|
||||
val roomVersion = "2.5.2"
|
||||
|
||||
|
||||
//annotationProcessor(libs.androidx.room.compiler)
|
||||
implementation("androidx.room:room-common:${roomVersion}")
|
||||
kapt("androidx.room:room-compiler:${roomVersion}")
|
||||
implementation("androidx.room:room-runtime:${roomVersion}")
|
||||
implementation("androidx.room:room-ktx:${roomVersion}")
|
||||
implementation("androidx.room:room-paging:${roomVersion}")
|
||||
//implementation(libs.androidx.room.ktx)
|
||||
|
||||
|
||||
implementation(libs.androidx.core.ktx)
|
||||
implementation(libs.androidx.lifecycle.runtime.ktx)
|
||||
implementation(libs.androidx.activity.compose)
|
||||
@@ -51,7 +69,7 @@ dependencies {
|
||||
implementation(libs.androidx.navigation.compose)
|
||||
implementation(libs.retrofit)
|
||||
implementation(libs.converter.gson)
|
||||
implementation("io.coil-kt:coil-compose:2.4.0")
|
||||
implementation(libs.coil.compose)
|
||||
testImplementation(libs.junit)
|
||||
androidTestImplementation(libs.androidx.junit)
|
||||
androidTestImplementation(libs.androidx.espresso.core)
|
||||
|
||||
@@ -10,7 +10,6 @@
|
||||
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.Queezer"
|
||||
tools:targetApi="31">
|
||||
|
||||
|
After Width: | Height: | Size: 16 KiB |
@@ -1,6 +1,7 @@
|
||||
package fr.univpau.queezer
|
||||
|
||||
import android.os.Bundle
|
||||
import android.util.Log
|
||||
import androidx.activity.ComponentActivity
|
||||
import androidx.activity.compose.setContent
|
||||
import androidx.compose.runtime.Composable
|
||||
@@ -9,25 +10,31 @@ import androidx.navigation.compose.composable
|
||||
import androidx.navigation.compose.rememberNavController
|
||||
import fr.univpau.queezer.screen.GameScreen
|
||||
import fr.univpau.queezer.screen.HomeScreen
|
||||
import fr.univpau.queezer.screen.ScoreScreen
|
||||
import fr.univpau.queezer.screen.SettingsScreen
|
||||
import fr.univpau.queezer.service.DatabaseService
|
||||
import fr.univpau.queezer.viewmodel.GameViewModel
|
||||
|
||||
class MainActivity : ComponentActivity() {
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
val database: DatabaseService by lazy { DatabaseService.getDatabase(this) }
|
||||
|
||||
setContent {
|
||||
QueezerApp()
|
||||
QueezerApp(database)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun QueezerApp() {
|
||||
fun QueezerApp(database: DatabaseService) {
|
||||
val navController = rememberNavController()
|
||||
val gameViewModel = GameViewModel(database.gameDao())
|
||||
|
||||
NavHost(navController = navController, startDestination = "home") {
|
||||
composable("home") { HomeScreen(navController) }
|
||||
composable("game") { GameScreen(navController) }
|
||||
composable("game") { GameScreen(navController, database) }
|
||||
composable("settings") { SettingsScreen(navController) }
|
||||
composable("score") { ScoreScreen(navController, gameViewModel) }
|
||||
}
|
||||
}
|
||||
@@ -1,10 +1,14 @@
|
||||
package fr.univpau.queezer.data
|
||||
|
||||
import java.sql.Date
|
||||
import androidx.room.Entity
|
||||
import androidx.room.PrimaryKey
|
||||
import java.util.Date
|
||||
|
||||
@Entity(tableName = "game")
|
||||
data class Game(
|
||||
val settings: Settings,
|
||||
val tracks: List<Track>,
|
||||
val score: Int,
|
||||
val date: Date
|
||||
@PrimaryKey(autoGenerate = true) val id: Int,
|
||||
val settings: Settings = Settings(),
|
||||
val playlist: Playlist = Playlist(),
|
||||
val score: Int = 0,
|
||||
val date: Date = Date()
|
||||
)
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
package fr.univpau.queezer.data
|
||||
|
||||
import androidx.lifecycle.LiveData
|
||||
import androidx.room.*
|
||||
|
||||
@Dao
|
||||
interface GameDao {
|
||||
|
||||
@Insert
|
||||
fun insert(game: Game)
|
||||
|
||||
@Query("SELECT * FROM game")
|
||||
fun getAll(): LiveData<List<Game>>
|
||||
|
||||
@Delete
|
||||
fun delete(game: Game)
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
package fr.univpau.queezer.data
|
||||
|
||||
data class Playlist(
|
||||
val title: String = "",
|
||||
val tracks: List<Track> = emptyList()
|
||||
)
|
||||
@@ -2,7 +2,7 @@ package fr.univpau.queezer.manager
|
||||
|
||||
import android.os.CountDownTimer
|
||||
|
||||
class CountdownManager (val duration: Long, val onFinish: () -> Unit) {
|
||||
class CountdownManager (val duration: Long, val onTickTimer: () -> Unit, val onFinishTimer: () -> Unit) {
|
||||
|
||||
var timeLeft = duration / 1000;
|
||||
var interval = 1000L;
|
||||
@@ -12,8 +12,11 @@ class CountdownManager (val duration: Long, val onFinish: () -> Unit) {
|
||||
timer = object : CountDownTimer(duration, interval) {
|
||||
override fun onTick(millisUntilFinished: Long) {
|
||||
timeLeft = millisUntilFinished / 1000;
|
||||
onTickTimer()
|
||||
}
|
||||
override fun onFinish() {
|
||||
onFinishTimer()
|
||||
}
|
||||
override fun onFinish() { onFinish() }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,29 +1,42 @@
|
||||
package fr.univpau.queezer.manager
|
||||
|
||||
import android.content.Context
|
||||
import android.media.MediaPlayer
|
||||
import android.util.Log
|
||||
import fr.univpau.queezer.data.Answer
|
||||
import fr.univpau.queezer.data.Game
|
||||
import fr.univpau.queezer.data.GameMode
|
||||
import fr.univpau.queezer.data.Playlist
|
||||
import fr.univpau.queezer.data.Settings
|
||||
import fr.univpau.queezer.data.Track
|
||||
import fr.univpau.queezer.service.DatabaseService
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import java.util.Date
|
||||
import java.util.Locale
|
||||
|
||||
class GameManager() {
|
||||
|
||||
private lateinit var databaseService: DatabaseService
|
||||
private var mediaPlayer: MediaPlayer = MediaPlayer()
|
||||
val countDownManager = CountdownManager(30000L, onFinish = ::nextTrack)
|
||||
var countDownManager = CountdownManager(30000L, {}, {})
|
||||
|
||||
var settings: Settings = Settings()
|
||||
var tracks: List<Track> = emptyList()
|
||||
// var tracks: List<Track> = emptyList()
|
||||
var playlist: Playlist = Playlist()
|
||||
var gameFinished : Boolean = false;
|
||||
|
||||
var currentTrackIndex: Int = 0;
|
||||
var score = 0
|
||||
|
||||
constructor(settings: Settings, tracks: List<Track>) : this() {
|
||||
constructor(settings: Settings, playlist: Playlist, onTick: () -> Unit, databaseService: DatabaseService) : this() {
|
||||
this.databaseService = databaseService
|
||||
this.settings = settings
|
||||
this.tracks = tracks
|
||||
this.playlist = playlist
|
||||
|
||||
for (track in tracks) {
|
||||
this.countDownManager = CountdownManager(30000L, onTickTimer = onTick, onFinishTimer = { nextTrack() })
|
||||
|
||||
for (track in playlist.tracks) {
|
||||
if (settings.gameMode == GameMode.TITLE) {
|
||||
track.title.answer = Answer.INCORRECT
|
||||
}
|
||||
@@ -74,7 +87,7 @@ class GameManager() {
|
||||
fun formatString(input: String): String {
|
||||
return input
|
||||
.trim()
|
||||
.toLowerCase(Locale.ROOT)
|
||||
.lowercase(Locale.ROOT)
|
||||
.removeSurrounding("(", ")")
|
||||
.removeSurrounding("[", "]")
|
||||
.removeSurrounding("{", "}")
|
||||
@@ -84,46 +97,65 @@ class GameManager() {
|
||||
}
|
||||
|
||||
fun getCurrentTrack(): Track? {
|
||||
if (tracks.isEmpty()) return null;
|
||||
return tracks[currentTrackIndex]
|
||||
if (playlist.tracks.isEmpty()) return null;
|
||||
return playlist.tracks[currentTrackIndex]
|
||||
}
|
||||
|
||||
fun stop() {
|
||||
mediaPlayer.release()
|
||||
// countDownManager.stop()
|
||||
countDownManager.stop()
|
||||
}
|
||||
|
||||
fun nextTrack() {
|
||||
mediaPlayer.release()
|
||||
|
||||
|
||||
if (currentTrackIndex >= tracks.size - 1) {
|
||||
if (currentTrackIndex >= playlist.tracks.size - 1) {
|
||||
return; // TODO vérifier avant meme de lancer la partie si le nombre de titres est suffisant
|
||||
}
|
||||
|
||||
if (currentTrackIndex >= settings.numberOfTitles!! - 1) {
|
||||
gameFinished = true
|
||||
return;
|
||||
}
|
||||
|
||||
currentTrackIndex++
|
||||
|
||||
mediaPlayer = MediaPlayer().apply {
|
||||
setDataSource(tracks[currentTrackIndex].preview)
|
||||
setDataSource(playlist.tracks[currentTrackIndex].preview)
|
||||
prepare()
|
||||
start()
|
||||
}
|
||||
|
||||
// countDownManager.restart()
|
||||
countDownManager.restart()
|
||||
}
|
||||
|
||||
|
||||
fun start() {
|
||||
// countDownManager.start()
|
||||
Log.i("GameManager", "Next track: ${tracks[currentTrackIndex].preview}")
|
||||
countDownManager.start()
|
||||
Log.i("GameManager", "Next track: ${playlist.tracks[currentTrackIndex].preview}")
|
||||
mediaPlayer = MediaPlayer().apply {
|
||||
setDataSource(tracks[currentTrackIndex].preview)
|
||||
setDataSource(playlist.tracks[currentTrackIndex].preview)
|
||||
prepare()
|
||||
start()
|
||||
}
|
||||
}
|
||||
|
||||
fun save(context: Context) {
|
||||
// TODO Sauvegarder tout le jeu en base de donnée locale (Room) dans un objet Game
|
||||
|
||||
// Créer un objet Game
|
||||
val game = Game(
|
||||
id = 0,
|
||||
settings = settings,
|
||||
playlist = playlist,
|
||||
score = score,
|
||||
date = Date()
|
||||
)
|
||||
|
||||
// Sauvegarder le jeu avec Room
|
||||
CoroutineScope(Dispatchers.IO).launch {
|
||||
databaseService.gameDao().insert(game)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,11 +1,38 @@
|
||||
package fr.univpau.queezer.manager
|
||||
|
||||
import fr.univpau.queezer.data.Input
|
||||
import fr.univpau.queezer.data.Playlist
|
||||
import fr.univpau.queezer.data.Track
|
||||
import fr.univpau.queezer.service.PlaylistResponse
|
||||
import fr.univpau.queezer.service.createDeezerApiService
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
|
||||
suspend fun fetchPlaylist(apiUrl: String) : Playlist? {
|
||||
val deezerApiService = createDeezerApiService()
|
||||
|
||||
return withContext(Dispatchers.IO) {
|
||||
try {
|
||||
val playlistResponse : PlaylistResponse = deezerApiService.getPlaylist(apiUrl)
|
||||
|
||||
Playlist(
|
||||
title = playlistResponse.title,
|
||||
tracks = playlistResponse.tracks.data.map { track ->
|
||||
Track(
|
||||
preview = track.preview,
|
||||
album = track.album.cover,
|
||||
title = Input(value = track.title),
|
||||
artist = Input(value = track.artist.name)
|
||||
)
|
||||
}.shuffled()
|
||||
)
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun fetchAndFormatPlaylist(apiUrl: String): List<Track> {
|
||||
val deezerApiService = createDeezerApiService()
|
||||
|
||||
|
||||
@@ -18,8 +18,10 @@ import androidx.compose.material3.TextField
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableIntStateOf
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
@@ -35,22 +37,25 @@ import fr.univpau.queezer.data.Settings
|
||||
import fr.univpau.queezer.manager.loadSettings
|
||||
import coil.compose.AsyncImage
|
||||
import fr.univpau.queezer.data.Answer
|
||||
import fr.univpau.queezer.data.Playlist
|
||||
import fr.univpau.queezer.manager.GameManager
|
||||
import fr.univpau.queezer.manager.fetchAndFormatPlaylist
|
||||
import fr.univpau.queezer.manager.fetchPlaylist
|
||||
import fr.univpau.queezer.service.DatabaseService
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
@Composable
|
||||
fun GameScreen(navController: NavHostController) {
|
||||
fun GameScreen(navController: NavHostController, database: DatabaseService) {
|
||||
val coroutineScope = rememberCoroutineScope()
|
||||
val context = LocalContext.current
|
||||
val settings: Settings = loadSettings(context)
|
||||
|
||||
var gameManager by remember { mutableStateOf(GameManager(settings, emptyList())) }
|
||||
var currentTrack by remember { mutableStateOf(gameManager.getCurrentTrack()) }
|
||||
var gameManager by remember { mutableStateOf(GameManager(settings, Playlist(), {}, database)) }
|
||||
var countdown by remember { mutableIntStateOf(30) }
|
||||
|
||||
LaunchedEffect(settings.playlistUrl) {
|
||||
val tracks = fetchAndFormatPlaylist(settings.playlistUrl).shuffled()
|
||||
gameManager = GameManager(settings, tracks)
|
||||
val playlist = fetchPlaylist(settings.playlistUrl)
|
||||
gameManager = GameManager(settings, playlist ?: Playlist(), { countdown = gameManager.countDownManager.timeLeft.toInt() }, database)
|
||||
|
||||
currentTrack = gameManager.getCurrentTrack()
|
||||
gameManager.start()
|
||||
Log.i("GameScreen", gameManager.getCurrentTrack().toString())
|
||||
}
|
||||
@@ -67,21 +72,32 @@ fun GameScreen(navController: NavHostController) {
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
verticalArrangement = Arrangement.spacedBy(16.dp)
|
||||
) {
|
||||
if (currentTrack == null) {
|
||||
if (gameManager.getCurrentTrack() == null) {
|
||||
CircularProgressIndicator(
|
||||
modifier = Modifier.width(64.dp),
|
||||
color = MaterialTheme.colorScheme.secondary,
|
||||
trackColor = MaterialTheme.colorScheme.surfaceVariant,
|
||||
)
|
||||
} else if (gameManager.gameFinished) {
|
||||
Text("Partie terminée !", fontSize = 24.sp)
|
||||
Text("Score : ${gameManager.score}", fontSize = 20.sp)
|
||||
Button(onClick = {
|
||||
|
||||
coroutineScope.launch {
|
||||
gameManager.save(context)
|
||||
gameManager.stop()
|
||||
navController.popBackStack()
|
||||
}
|
||||
}) { Text(context.resources.getString(R.string.back)) }
|
||||
} else {
|
||||
Text("Score : ${gameManager.score}", fontSize = 24.sp)
|
||||
Text("Temps restant : ${gameManager.countDownManager.timeLeft}sec", fontSize = 20.sp)
|
||||
Text("Temps restant : ${countdown}sec", fontSize = 20.sp)
|
||||
|
||||
if (currentTrack!!.title.answer == Answer.CORRECT && currentTrack!!.artist.answer == Answer.UNKNOWN
|
||||
|| currentTrack!!.title.answer == Answer.UNKNOWN && currentTrack!!.artist.answer == Answer.CORRECT
|
||||
|| currentTrack!!.title.answer == Answer.CORRECT && currentTrack!!.artist.answer == Answer.CORRECT) {
|
||||
if (gameManager.getCurrentTrack()!!.title.answer == Answer.CORRECT && gameManager.getCurrentTrack()!!.artist.answer == Answer.UNKNOWN
|
||||
|| gameManager.getCurrentTrack()!!.title.answer == Answer.UNKNOWN && gameManager.getCurrentTrack()!!.artist.answer == Answer.CORRECT
|
||||
|| gameManager.getCurrentTrack()!!.title.answer == Answer.CORRECT && gameManager.getCurrentTrack()!!.artist.answer == Answer.CORRECT) {
|
||||
AsyncImage(
|
||||
model = currentTrack!!.album,
|
||||
model = gameManager.getCurrentTrack()!!.album,
|
||||
contentDescription = "Image from URL",
|
||||
modifier = Modifier
|
||||
.width(200.dp)
|
||||
@@ -91,7 +107,7 @@ fun GameScreen(navController: NavHostController) {
|
||||
)
|
||||
} else {
|
||||
AsyncImage(
|
||||
model = currentTrack!!.album,
|
||||
model = gameManager.getCurrentTrack()!!.album,
|
||||
contentDescription = "Image from URL",
|
||||
modifier = Modifier
|
||||
.width(200.dp)
|
||||
@@ -103,14 +119,14 @@ fun GameScreen(navController: NavHostController) {
|
||||
}
|
||||
|
||||
Row {
|
||||
if (currentTrack!!.title.answer == Answer.CORRECT || currentTrack!!.title.answer == Answer.UNKNOWN) {
|
||||
Text("Titre : ${currentTrack!!.title.value}", fontSize = 20.sp)
|
||||
if (gameManager.getCurrentTrack()!!.title.answer == Answer.CORRECT || gameManager.getCurrentTrack()!!.title.answer == Answer.UNKNOWN) {
|
||||
Text("Titre : ${gameManager.getCurrentTrack()!!.title.value}", fontSize = 20.sp)
|
||||
} else {
|
||||
TextField(
|
||||
value = titleInput.value,
|
||||
onValueChange = {
|
||||
titleInput.value = it;
|
||||
gameManager.checkTitleAnswer(currentTrack, titleInput.value)
|
||||
gameManager.checkTitleAnswer(gameManager.getCurrentTrack(), titleInput.value)
|
||||
},
|
||||
label = { Text("Titre") },
|
||||
keyboardOptions = KeyboardOptions.Default.copy(keyboardType = KeyboardType.Text),
|
||||
@@ -120,12 +136,12 @@ fun GameScreen(navController: NavHostController) {
|
||||
}
|
||||
|
||||
Row {
|
||||
if (currentTrack!!.artist.answer == Answer.CORRECT || currentTrack!!.artist.answer == Answer.UNKNOWN) {
|
||||
Text("Artiste : ${currentTrack!!.artist.value}", fontSize = 20.sp)
|
||||
if (gameManager.getCurrentTrack()!!.artist.answer == Answer.CORRECT || gameManager.getCurrentTrack()!!.artist.answer == Answer.UNKNOWN) {
|
||||
Text("Artiste : ${gameManager.getCurrentTrack()!!.artist.value}", fontSize = 20.sp)
|
||||
} else {
|
||||
TextField(
|
||||
value = artistInput.value,
|
||||
onValueChange = { artistInput.value = it; gameManager.checkArtistAnswer(currentTrack, artistInput.value) },
|
||||
onValueChange = { artistInput.value = it; gameManager.checkArtistAnswer(gameManager.getCurrentTrack(), artistInput.value) },
|
||||
label = { Text("Artiste") },
|
||||
keyboardOptions = KeyboardOptions.Default.copy(keyboardType = KeyboardType.Text),
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
@@ -139,7 +155,6 @@ fun GameScreen(navController: NavHostController) {
|
||||
titleInput.value = ""
|
||||
artistInput.value = ""
|
||||
gameManager.nextTrack()
|
||||
currentTrack = gameManager.getCurrentTrack()
|
||||
},
|
||||
) { Text(context.resources.getString(R.string.skip)) }
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package fr.univpau.queezer.screen
|
||||
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
@@ -7,16 +8,20 @@ import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material3.Button
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.layout.ContentScale
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import androidx.navigation.NavHostController
|
||||
import fr.univpau.queezer.R
|
||||
import fr.univpau.queezer.ui.theme.Purple40
|
||||
|
||||
@Composable
|
||||
@@ -28,15 +33,14 @@ fun HomeScreen(navController: NavHostController) {
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
verticalArrangement = Arrangement.Center
|
||||
) {
|
||||
// Logo et Titre
|
||||
// Image(
|
||||
// painter = painterResource(id = R.drawable.logo),
|
||||
// contentDescription = "Logo Queezer",
|
||||
// modifier = Modifier
|
||||
// .size(120.dp)
|
||||
// .padding(16.dp),
|
||||
// contentScale = ContentScale.Crop
|
||||
// )
|
||||
// Logo foreground et background
|
||||
Image(
|
||||
painter = painterResource(id = R.mipmap.ic_launcher),
|
||||
contentDescription = "Logo Queezer",
|
||||
modifier = Modifier.size(140.dp),
|
||||
contentScale = ContentScale.Crop
|
||||
)
|
||||
|
||||
Text(
|
||||
text = "Queezer",
|
||||
fontSize = 32.sp,
|
||||
@@ -78,7 +82,7 @@ fun HomeScreen(navController: NavHostController) {
|
||||
}
|
||||
|
||||
Button(
|
||||
onClick = { /* TODO: Scores */ },
|
||||
onClick = { navController.navigate("score") },
|
||||
shape = RoundedCornerShape(8.dp),
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
|
||||
@@ -0,0 +1,164 @@
|
||||
package fr.univpau.queezer.screen
|
||||
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material3.Button
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.livedata.observeAsState
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import androidx.navigation.NavHostController
|
||||
import fr.univpau.queezer.R
|
||||
import fr.univpau.queezer.data.Game
|
||||
import fr.univpau.queezer.viewmodel.GameViewModel
|
||||
|
||||
@Composable
|
||||
fun ScoreScreen(navController: NavHostController, gameViewModel: GameViewModel) {
|
||||
val context = LocalContext.current
|
||||
val gameList : List<Game>? = gameViewModel.games.observeAsState().value
|
||||
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.padding(16.dp),
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
verticalArrangement = Arrangement.spacedBy(16.dp)
|
||||
) {
|
||||
// Titre des paramètres
|
||||
Text(
|
||||
text = context.resources.getString(R.string.score),
|
||||
fontSize = 32.sp,
|
||||
modifier = Modifier.padding(bottom = 32.dp)
|
||||
)
|
||||
|
||||
// Nombre de parties jouées
|
||||
Row(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
horizontalArrangement = Arrangement.SpaceBetween
|
||||
) {
|
||||
Text(
|
||||
text = context.resources.getString(R.string.games_played),
|
||||
fontSize = 24.sp,
|
||||
)
|
||||
if (gameList?.isNotEmpty() == true) {
|
||||
Text(
|
||||
text = gameList.size.toString(),
|
||||
fontSize = 24.sp,
|
||||
)
|
||||
} else {
|
||||
Text(
|
||||
text = "0",
|
||||
fontSize = 24.sp,
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// pourcentage de réussite moyen
|
||||
Row(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
horizontalArrangement = Arrangement.SpaceBetween
|
||||
) {
|
||||
Text(
|
||||
text = context.resources.getString(R.string.average_success_rate),
|
||||
fontSize = 24.sp,
|
||||
)
|
||||
if (gameList?.isNotEmpty() == true) {
|
||||
Text(
|
||||
text = (gameList.sumOf { it.score }.div(gameList.size).toString()) + "%",
|
||||
fontSize = 24.sp,
|
||||
)
|
||||
} else {
|
||||
Text(
|
||||
text = "0%",
|
||||
fontSize = 24.sp,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// filtres
|
||||
// - date
|
||||
// - mode de jeu –titre/artiste/les deux
|
||||
// - nombre de titres
|
||||
Row(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
horizontalArrangement = Arrangement.SpaceBetween
|
||||
) {
|
||||
Text(
|
||||
text = context.resources.getString(R.string.filters),
|
||||
fontSize = 24.sp,
|
||||
)
|
||||
|
||||
Row(
|
||||
horizontalArrangement = Arrangement.SpaceBetween
|
||||
){
|
||||
Button(
|
||||
onClick = { /* TODO: Filtre par date */ },
|
||||
shape = RoundedCornerShape(8.dp),
|
||||
modifier = Modifier
|
||||
.padding(vertical = 8.dp)
|
||||
) {
|
||||
Text(context.resources.getString(R.string.date))
|
||||
}
|
||||
|
||||
Button(
|
||||
onClick = { /* TODO: Filtre par date */ },
|
||||
shape = RoundedCornerShape(8.dp),
|
||||
modifier = Modifier
|
||||
.padding(vertical = 8.dp)
|
||||
) {
|
||||
Text("Mode de jeu")
|
||||
}
|
||||
|
||||
Button(
|
||||
onClick = { /* TODO: Filtre par date */ },
|
||||
shape = RoundedCornerShape(8.dp),
|
||||
modifier = Modifier
|
||||
.padding(vertical = 8.dp)
|
||||
) {
|
||||
Text("Nombre de titres")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Historique des parties (score, nom)
|
||||
// - cliquer sur une partie pour voir les détails
|
||||
|
||||
gameList?.forEach {
|
||||
Row(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
horizontalArrangement = Arrangement.SpaceBetween
|
||||
) {
|
||||
Text(
|
||||
text = it.playlist.title,
|
||||
fontSize = 24.sp,
|
||||
)
|
||||
Text(
|
||||
text = it.score.toString(),
|
||||
fontSize = 24.sp,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
Button(
|
||||
onClick = { navController.navigate("home") },
|
||||
shape = RoundedCornerShape(8.dp),
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(vertical = 8.dp)
|
||||
) {
|
||||
Text(context.resources.getString(R.string.back))
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -27,7 +27,6 @@ import fr.univpau.queezer.R
|
||||
import fr.univpau.queezer.data.GameMode
|
||||
import fr.univpau.queezer.manager.loadSettings
|
||||
import fr.univpau.queezer.manager.saveSettings
|
||||
import java.net.URL
|
||||
|
||||
@Composable
|
||||
fun SettingsScreen(navController: NavHostController) {
|
||||
|
||||
@@ -0,0 +1,66 @@
|
||||
package fr.univpau.queezer.service
|
||||
|
||||
import androidx.room.TypeConverter
|
||||
import com.google.gson.Gson
|
||||
import com.google.gson.reflect.TypeToken
|
||||
import fr.univpau.queezer.data.Answer
|
||||
import fr.univpau.queezer.data.Playlist
|
||||
import fr.univpau.queezer.data.Settings
|
||||
import fr.univpau.queezer.data.Track
|
||||
import java.util.Date
|
||||
|
||||
class Converters {
|
||||
|
||||
private val gson = Gson()
|
||||
|
||||
@TypeConverter
|
||||
fun fromSettings(settings: Settings): String {
|
||||
return gson.toJson(settings)
|
||||
}
|
||||
|
||||
@TypeConverter
|
||||
fun toSettings(data: String): Settings {
|
||||
return gson.fromJson(data, Settings::class.java)
|
||||
}
|
||||
|
||||
@TypeConverter
|
||||
fun fromTrackList(tracks: List<Track>): String {
|
||||
return gson.toJson(tracks)
|
||||
}
|
||||
|
||||
@TypeConverter
|
||||
fun toTrackList(data: String): List<Track> {
|
||||
val listType = object : TypeToken<List<Track>>() {}.type
|
||||
return gson.fromJson(data, listType)
|
||||
}
|
||||
|
||||
@TypeConverter
|
||||
fun fromAnswer(answer: Answer): String {
|
||||
return answer.name
|
||||
}
|
||||
|
||||
@TypeConverter
|
||||
fun toAnswer(data: String): Answer {
|
||||
return Answer.valueOf(data)
|
||||
}
|
||||
|
||||
@TypeConverter
|
||||
fun fromTimestamp(value: Long?): Date? {
|
||||
return value?.let { Date(it) }
|
||||
}
|
||||
|
||||
@TypeConverter
|
||||
fun dateToTimestamp(date: Date?): Long? {
|
||||
return date?.time
|
||||
}
|
||||
|
||||
@TypeConverter
|
||||
fun fromPlaylist(playlist: Playlist): String {
|
||||
return gson.toJson(playlist)
|
||||
}
|
||||
|
||||
@TypeConverter
|
||||
fun toPlaylist(data: String): Playlist {
|
||||
return gson.fromJson(data, Playlist::class.java)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
package fr.univpau.queezer.service
|
||||
|
||||
import android.content.Context
|
||||
import androidx.room.Database
|
||||
import androidx.room.Room
|
||||
import androidx.room.RoomDatabase
|
||||
import androidx.room.TypeConverters
|
||||
import fr.univpau.queezer.data.GameDao
|
||||
import fr.univpau.queezer.data.Game
|
||||
|
||||
@Database(entities = [Game::class], version = 2, exportSchema = false)
|
||||
@TypeConverters(Converters::class)
|
||||
abstract class DatabaseService : RoomDatabase() {
|
||||
|
||||
abstract fun gameDao(): GameDao
|
||||
|
||||
companion object {
|
||||
@Volatile
|
||||
private var INSTANCE: DatabaseService? = null
|
||||
|
||||
fun getDatabase(context: Context): DatabaseService {
|
||||
return INSTANCE ?: synchronized(this) {
|
||||
val instance = Room.databaseBuilder(
|
||||
context.applicationContext,
|
||||
DatabaseService::class.java,
|
||||
"app_notes_database"
|
||||
).build()
|
||||
INSTANCE = instance
|
||||
|
||||
return instance
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -6,6 +6,7 @@ import retrofit2.http.GET
|
||||
import retrofit2.http.Url
|
||||
|
||||
data class PlaylistResponse(
|
||||
val title: String,
|
||||
val tracks: TrackList
|
||||
)
|
||||
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
package fr.univpau.queezer.viewmodel
|
||||
|
||||
import android.content.Context
|
||||
import android.util.Log
|
||||
import androidx.lifecycle.LiveData
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import fr.univpau.queezer.data.GameDao
|
||||
import fr.univpau.queezer.data.Game
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
class GameViewModel(private val gameDao: GameDao) : ViewModel() {
|
||||
|
||||
val games: LiveData<List<Game>> = gameDao.getAll()
|
||||
|
||||
fun addGame(context: Context, game: Game) {
|
||||
viewModelScope.launch {
|
||||
try {
|
||||
game.settings.validate(context)
|
||||
gameDao.insert(game)
|
||||
} catch (e: IllegalArgumentException) {
|
||||
Log.e("GameViewModel", "Error adding game: ${e.message}")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun deleteGame(game: Game) {
|
||||
viewModelScope.launch {
|
||||
gameDao.delete(game)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,170 +0,0 @@
|
||||
<?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">
|
||||
<path
|
||||
android:fillColor="#3DDC84"
|
||||
android:pathData="M0,0h108v108h-108z" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M9,0L9,108"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M19,0L19,108"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M29,0L29,108"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M39,0L39,108"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M49,0L49,108"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M59,0L59,108"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M69,0L69,108"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M79,0L79,108"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M89,0L89,108"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M99,0L99,108"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,9L108,9"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,19L108,19"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,29L108,29"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,39L108,39"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,49L108,49"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,59L108,59"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,69L108,69"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,79L108,79"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,89L108,89"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,99L108,99"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M19,29L89,29"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M19,39L89,39"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M19,49L89,49"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M19,59L89,59"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M19,69L89,69"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M19,79L89,79"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M29,19L29,89"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M39,19L39,89"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M49,19L49,89"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M59,19L59,89"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M69,19L69,89"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M79,19L79,89"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
</vector>
|
||||
@@ -1,30 +0,0 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:aapt="http://schemas.android.com/aapt"
|
||||
android:width="108dp"
|
||||
android:height="108dp"
|
||||
android:viewportWidth="108"
|
||||
android:viewportHeight="108">
|
||||
<path android:pathData="M31,63.928c0,0 6.4,-11 12.1,-13.1c7.2,-2.6 26,-1.4 26,-1.4l38.1,38.1L107,108.928l-32,-1L31,63.928z">
|
||||
<aapt:attr name="android:fillColor">
|
||||
<gradient
|
||||
android:endX="85.84757"
|
||||
android:endY="92.4963"
|
||||
android:startX="42.9492"
|
||||
android:startY="49.59793"
|
||||
android:type="linear">
|
||||
<item
|
||||
android:color="#44000000"
|
||||
android:offset="0.0" />
|
||||
<item
|
||||
android:color="#00000000"
|
||||
android:offset="1.0" />
|
||||
</gradient>
|
||||
</aapt:attr>
|
||||
</path>
|
||||
<path
|
||||
android:fillColor="#FFFFFF"
|
||||
android:fillType="nonZero"
|
||||
android:pathData="M65.3,45.828l3.8,-6.6c0.2,-0.4 0.1,-0.9 -0.3,-1.1c-0.4,-0.2 -0.9,-0.1 -1.1,0.3l-3.9,6.7c-6.3,-2.8 -13.4,-2.8 -19.7,0l-3.9,-6.7c-0.2,-0.4 -0.7,-0.5 -1.1,-0.3C38.8,38.328 38.7,38.828 38.9,39.228l3.8,6.6C36.2,49.428 31.7,56.028 31,63.928h46C76.3,56.028 71.8,49.428 65.3,45.828zM43.4,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2c-0.3,-0.7 -0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C45.3,56.528 44.5,57.328 43.4,57.328L43.4,57.328zM64.6,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2s-0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C66.5,56.528 65.6,57.328 64.6,57.328L64.6,57.328z"
|
||||
android:strokeWidth="1"
|
||||
android:strokeColor="#00000000" />
|
||||
</vector>
|
||||
@@ -1,6 +0,0 @@
|
||||
<?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>
|
||||
@@ -1,6 +0,0 @@
|
||||
<?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>
|
||||
|
After Width: | Height: | Size: 5.7 KiB |
|
Before Width: | Height: | Size: 1.4 KiB |
|
Before Width: | Height: | Size: 2.8 KiB |
|
After Width: | Height: | Size: 3.0 KiB |
|
Before Width: | Height: | Size: 982 B |
|
Before Width: | Height: | Size: 1.7 KiB |
|
After Width: | Height: | Size: 9.3 KiB |
|
Before Width: | Height: | Size: 1.9 KiB |
|
Before Width: | Height: | Size: 3.8 KiB |
|
After Width: | Height: | Size: 19 KiB |
|
Before Width: | Height: | Size: 2.8 KiB |
|
Before Width: | Height: | Size: 5.8 KiB |
|
After Width: | Height: | Size: 33 KiB |
|
Before Width: | Height: | Size: 3.8 KiB |
|
Before Width: | Height: | Size: 7.6 KiB |
@@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<color name="ic_launcher_background">#D13DDC</color>
|
||||
</resources>
|
||||
@@ -29,4 +29,9 @@
|
||||
<string name="give_up">Abandonner</string>
|
||||
<string name="submit">Valider</string>
|
||||
|
||||
<string name="games_played">Nombre de parties jouées</string>
|
||||
<string name="average_success_rate">Taux de réussite moyen</string>
|
||||
<string name="filters">Filtrer par</string>
|
||||
<string name="date">Date</string>
|
||||
|
||||
</resources>
|
||||
@@ -1,5 +1,7 @@
|
||||
[versions]
|
||||
agp = "8.7.3"
|
||||
annotationsJava5 = "15.0"
|
||||
coilCompose = "2.4.0"
|
||||
converterGson = "2.9.0"
|
||||
kotlin = "2.0.0"
|
||||
coreKtx = "1.13.1"
|
||||
@@ -10,12 +12,28 @@ lifecycleRuntimeKtx = "2.8.6"
|
||||
activityCompose = "1.9.3"
|
||||
composeBom = "2024.04.01"
|
||||
navigationCompose = "2.8.4"
|
||||
media3Exoplayer = "1.5.0"
|
||||
retrofit = "2.9.0"
|
||||
roomCommon = "2.6.1"
|
||||
roomCompiler = "2.6.1"
|
||||
roomCompilerVersion = "2.5.0"
|
||||
roomKtx = "2.6.1"
|
||||
roomPaging = "2.6.1"
|
||||
roomRuntime = "2.6.1"
|
||||
roomRuntimeVersion = "2.5.0"
|
||||
runtimeLivedata = "1.7.6"
|
||||
|
||||
[libraries]
|
||||
androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" }
|
||||
androidx-navigation-compose = { module = "androidx.navigation:navigation-compose", version.ref = "navigationCompose" }
|
||||
androidx-room-compiler = { module = "androidx.room:room-compiler", version.ref = "roomCompiler" }
|
||||
androidx-room-compiler-v250 = { module = "androidx.room:room-compiler", version.ref = "roomCompilerVersion" }
|
||||
androidx-room-paging = { module = "androidx.room:room-paging", version.ref = "roomPaging" }
|
||||
androidx-room-room-compiler = { module = "androidx.room:room-compiler" }
|
||||
androidx-room-room-compiler2 = { module = "androidx.room:room-compiler" }
|
||||
androidx-room-runtime = { module = "androidx.room:room-runtime", version.ref = "roomRuntime" }
|
||||
androidx-room-runtime-v250 = { module = "androidx.room:room-runtime", version.ref = "roomRuntimeVersion" }
|
||||
annotations-java5 = { module = "org.jetbrains:annotations-java5", version.ref = "annotationsJava5" }
|
||||
coil-compose = { module = "io.coil-kt:coil-compose", version.ref = "coilCompose" }
|
||||
converter-gson = { module = "com.squareup.retrofit2:converter-gson", version.ref = "converterGson" }
|
||||
junit = { group = "junit", name = "junit", version.ref = "junit" }
|
||||
androidx-junit = { group = "androidx.test.ext", name = "junit", version.ref = "junitVersion" }
|
||||
@@ -30,8 +48,12 @@ androidx-ui-tooling-preview = { group = "androidx.compose.ui", name = "ui-toolin
|
||||
androidx-ui-test-manifest = { group = "androidx.compose.ui", name = "ui-test-manifest" }
|
||||
androidx-ui-test-junit4 = { group = "androidx.compose.ui", name = "ui-test-junit4" }
|
||||
androidx-material3 = { group = "androidx.compose.material3", name = "material3" }
|
||||
androidx-media3-exoplayer = { group = "androidx.media3", name = "media3-exoplayer", version.ref = "media3Exoplayer" }
|
||||
retrofit = { module = "com.squareup.retrofit2:retrofit", version.ref = "retrofit" }
|
||||
androidx-room-common = { group = "androidx.room", name = "room-common", version.ref = "roomCommon" }
|
||||
androidx-room-ktx = { group = "androidx.room", name = "room-ktx", version.ref = "roomKtx" }
|
||||
room-compiler = { module = "androidx.room:room-compiler" }
|
||||
room-ktx = { module = "androidx.room:room-ktx" }
|
||||
androidx-runtime-livedata = { group = "androidx.compose.runtime", name = "runtime-livedata", version.ref = "runtimeLivedata" }
|
||||
|
||||
[plugins]
|
||||
android-application = { id = "com.android.application", version.ref = "agp" }
|
||||
|
||||