feat: Save locally all parties
@@ -11,6 +11,9 @@
|
|||||||
<SelectionState runConfigName="MainActivity">
|
<SelectionState runConfigName="MainActivity">
|
||||||
<option name="selectionMode" value="DROPDOWN" />
|
<option name="selectionMode" value="DROPDOWN" />
|
||||||
</SelectionState>
|
</SelectionState>
|
||||||
|
<SelectionState runConfigName="Queezer">
|
||||||
|
<option name="selectionMode" value="DROPDOWN" />
|
||||||
|
</SelectionState>
|
||||||
</selectionStates>
|
</selectionStates>
|
||||||
</component>
|
</component>
|
||||||
</project>
|
</project>
|
||||||
@@ -2,6 +2,7 @@ plugins {
|
|||||||
alias(libs.plugins.android.application)
|
alias(libs.plugins.android.application)
|
||||||
alias(libs.plugins.kotlin.android)
|
alias(libs.plugins.kotlin.android)
|
||||||
alias(libs.plugins.kotlin.compose)
|
alias(libs.plugins.kotlin.compose)
|
||||||
|
id("kotlin-kapt")
|
||||||
}
|
}
|
||||||
|
|
||||||
android {
|
android {
|
||||||
@@ -39,7 +40,24 @@ android {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
apply(
|
||||||
|
plugin = "kotlin-kapt"
|
||||||
|
)
|
||||||
|
|
||||||
dependencies {
|
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.core.ktx)
|
||||||
implementation(libs.androidx.lifecycle.runtime.ktx)
|
implementation(libs.androidx.lifecycle.runtime.ktx)
|
||||||
implementation(libs.androidx.activity.compose)
|
implementation(libs.androidx.activity.compose)
|
||||||
@@ -51,7 +69,7 @@ dependencies {
|
|||||||
implementation(libs.androidx.navigation.compose)
|
implementation(libs.androidx.navigation.compose)
|
||||||
implementation(libs.retrofit)
|
implementation(libs.retrofit)
|
||||||
implementation(libs.converter.gson)
|
implementation(libs.converter.gson)
|
||||||
implementation("io.coil-kt:coil-compose:2.4.0")
|
implementation(libs.coil.compose)
|
||||||
testImplementation(libs.junit)
|
testImplementation(libs.junit)
|
||||||
androidTestImplementation(libs.androidx.junit)
|
androidTestImplementation(libs.androidx.junit)
|
||||||
androidTestImplementation(libs.androidx.espresso.core)
|
androidTestImplementation(libs.androidx.espresso.core)
|
||||||
|
|||||||
@@ -10,7 +10,6 @@
|
|||||||
android:fullBackupContent="@xml/backup_rules"
|
android:fullBackupContent="@xml/backup_rules"
|
||||||
android:icon="@mipmap/ic_launcher"
|
android:icon="@mipmap/ic_launcher"
|
||||||
android:label="@string/app_name"
|
android:label="@string/app_name"
|
||||||
android:roundIcon="@mipmap/ic_launcher_round"
|
|
||||||
android:supportsRtl="true"
|
android:supportsRtl="true"
|
||||||
android:theme="@style/Theme.Queezer"
|
android:theme="@style/Theme.Queezer"
|
||||||
tools:targetApi="31">
|
tools:targetApi="31">
|
||||||
|
|||||||
|
After Width: | Height: | Size: 16 KiB |
@@ -1,6 +1,7 @@
|
|||||||
package fr.univpau.queezer
|
package fr.univpau.queezer
|
||||||
|
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
|
import android.util.Log
|
||||||
import androidx.activity.ComponentActivity
|
import androidx.activity.ComponentActivity
|
||||||
import androidx.activity.compose.setContent
|
import androidx.activity.compose.setContent
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
@@ -9,25 +10,31 @@ import androidx.navigation.compose.composable
|
|||||||
import androidx.navigation.compose.rememberNavController
|
import androidx.navigation.compose.rememberNavController
|
||||||
import fr.univpau.queezer.screen.GameScreen
|
import fr.univpau.queezer.screen.GameScreen
|
||||||
import fr.univpau.queezer.screen.HomeScreen
|
import fr.univpau.queezer.screen.HomeScreen
|
||||||
|
import fr.univpau.queezer.screen.ScoreScreen
|
||||||
import fr.univpau.queezer.screen.SettingsScreen
|
import fr.univpau.queezer.screen.SettingsScreen
|
||||||
|
import fr.univpau.queezer.service.DatabaseService
|
||||||
|
import fr.univpau.queezer.viewmodel.GameViewModel
|
||||||
|
|
||||||
class MainActivity : ComponentActivity() {
|
class MainActivity : ComponentActivity() {
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
|
val database: DatabaseService by lazy { DatabaseService.getDatabase(this) }
|
||||||
|
|
||||||
setContent {
|
setContent {
|
||||||
QueezerApp()
|
QueezerApp(database)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun QueezerApp() {
|
fun QueezerApp(database: DatabaseService) {
|
||||||
val navController = rememberNavController()
|
val navController = rememberNavController()
|
||||||
|
val gameViewModel = GameViewModel(database.gameDao())
|
||||||
|
|
||||||
NavHost(navController = navController, startDestination = "home") {
|
NavHost(navController = navController, startDestination = "home") {
|
||||||
composable("home") { HomeScreen(navController) }
|
composable("home") { HomeScreen(navController) }
|
||||||
composable("game") { GameScreen(navController) }
|
composable("game") { GameScreen(navController, database) }
|
||||||
composable("settings") { SettingsScreen(navController) }
|
composable("settings") { SettingsScreen(navController) }
|
||||||
|
composable("score") { ScoreScreen(navController, gameViewModel) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,10 +1,14 @@
|
|||||||
package fr.univpau.queezer.data
|
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(
|
data class Game(
|
||||||
val settings: Settings,
|
@PrimaryKey(autoGenerate = true) val id: Int,
|
||||||
val tracks: List<Track>,
|
val settings: Settings = Settings(),
|
||||||
val score: Int,
|
val playlist: Playlist = Playlist(),
|
||||||
val date: Date
|
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
|
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 timeLeft = duration / 1000;
|
||||||
var interval = 1000L;
|
var interval = 1000L;
|
||||||
@@ -12,8 +12,11 @@ class CountdownManager (val duration: Long, val onFinish: () -> Unit) {
|
|||||||
timer = object : CountDownTimer(duration, interval) {
|
timer = object : CountDownTimer(duration, interval) {
|
||||||
override fun onTick(millisUntilFinished: Long) {
|
override fun onTick(millisUntilFinished: Long) {
|
||||||
timeLeft = millisUntilFinished / 1000;
|
timeLeft = millisUntilFinished / 1000;
|
||||||
|
onTickTimer()
|
||||||
|
}
|
||||||
|
override fun onFinish() {
|
||||||
|
onFinishTimer()
|
||||||
}
|
}
|
||||||
override fun onFinish() { onFinish() }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,29 +1,42 @@
|
|||||||
package fr.univpau.queezer.manager
|
package fr.univpau.queezer.manager
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
import android.media.MediaPlayer
|
import android.media.MediaPlayer
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import fr.univpau.queezer.data.Answer
|
import fr.univpau.queezer.data.Answer
|
||||||
|
import fr.univpau.queezer.data.Game
|
||||||
import fr.univpau.queezer.data.GameMode
|
import fr.univpau.queezer.data.GameMode
|
||||||
|
import fr.univpau.queezer.data.Playlist
|
||||||
import fr.univpau.queezer.data.Settings
|
import fr.univpau.queezer.data.Settings
|
||||||
import fr.univpau.queezer.data.Track
|
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
|
import java.util.Locale
|
||||||
|
|
||||||
class GameManager() {
|
class GameManager() {
|
||||||
|
private lateinit var databaseService: DatabaseService
|
||||||
private var mediaPlayer: MediaPlayer = MediaPlayer()
|
private var mediaPlayer: MediaPlayer = MediaPlayer()
|
||||||
val countDownManager = CountdownManager(30000L, onFinish = ::nextTrack)
|
var countDownManager = CountdownManager(30000L, {}, {})
|
||||||
|
|
||||||
var settings: Settings = Settings()
|
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 currentTrackIndex: Int = 0;
|
||||||
var score = 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.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) {
|
if (settings.gameMode == GameMode.TITLE) {
|
||||||
track.title.answer = Answer.INCORRECT
|
track.title.answer = Answer.INCORRECT
|
||||||
}
|
}
|
||||||
@@ -74,7 +87,7 @@ class GameManager() {
|
|||||||
fun formatString(input: String): String {
|
fun formatString(input: String): String {
|
||||||
return input
|
return input
|
||||||
.trim()
|
.trim()
|
||||||
.toLowerCase(Locale.ROOT)
|
.lowercase(Locale.ROOT)
|
||||||
.removeSurrounding("(", ")")
|
.removeSurrounding("(", ")")
|
||||||
.removeSurrounding("[", "]")
|
.removeSurrounding("[", "]")
|
||||||
.removeSurrounding("{", "}")
|
.removeSurrounding("{", "}")
|
||||||
@@ -84,46 +97,65 @@ class GameManager() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun getCurrentTrack(): Track? {
|
fun getCurrentTrack(): Track? {
|
||||||
if (tracks.isEmpty()) return null;
|
if (playlist.tracks.isEmpty()) return null;
|
||||||
return tracks[currentTrackIndex]
|
return playlist.tracks[currentTrackIndex]
|
||||||
}
|
}
|
||||||
|
|
||||||
fun stop() {
|
fun stop() {
|
||||||
mediaPlayer.release()
|
mediaPlayer.release()
|
||||||
// countDownManager.stop()
|
countDownManager.stop()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun nextTrack() {
|
fun nextTrack() {
|
||||||
mediaPlayer.release()
|
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
|
return; // TODO vérifier avant meme de lancer la partie si le nombre de titres est suffisant
|
||||||
}
|
}
|
||||||
|
|
||||||
if (currentTrackIndex >= settings.numberOfTitles!! - 1) {
|
if (currentTrackIndex >= settings.numberOfTitles!! - 1) {
|
||||||
|
gameFinished = true
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
currentTrackIndex++
|
currentTrackIndex++
|
||||||
|
|
||||||
mediaPlayer = MediaPlayer().apply {
|
mediaPlayer = MediaPlayer().apply {
|
||||||
setDataSource(tracks[currentTrackIndex].preview)
|
setDataSource(playlist.tracks[currentTrackIndex].preview)
|
||||||
prepare()
|
prepare()
|
||||||
start()
|
start()
|
||||||
}
|
}
|
||||||
|
|
||||||
// countDownManager.restart()
|
countDownManager.restart()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
fun start() {
|
fun start() {
|
||||||
// countDownManager.start()
|
countDownManager.start()
|
||||||
Log.i("GameManager", "Next track: ${tracks[currentTrackIndex].preview}")
|
Log.i("GameManager", "Next track: ${playlist.tracks[currentTrackIndex].preview}")
|
||||||
mediaPlayer = MediaPlayer().apply {
|
mediaPlayer = MediaPlayer().apply {
|
||||||
setDataSource(tracks[currentTrackIndex].preview)
|
setDataSource(playlist.tracks[currentTrackIndex].preview)
|
||||||
prepare()
|
prepare()
|
||||||
start()
|
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
|
package fr.univpau.queezer.manager
|
||||||
|
|
||||||
import fr.univpau.queezer.data.Input
|
import fr.univpau.queezer.data.Input
|
||||||
|
import fr.univpau.queezer.data.Playlist
|
||||||
import fr.univpau.queezer.data.Track
|
import fr.univpau.queezer.data.Track
|
||||||
|
import fr.univpau.queezer.service.PlaylistResponse
|
||||||
import fr.univpau.queezer.service.createDeezerApiService
|
import fr.univpau.queezer.service.createDeezerApiService
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.withContext
|
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> {
|
suspend fun fetchAndFormatPlaylist(apiUrl: String): List<Track> {
|
||||||
val deezerApiService = createDeezerApiService()
|
val deezerApiService = createDeezerApiService()
|
||||||
|
|
||||||
|
|||||||
@@ -18,8 +18,10 @@ import androidx.compose.material3.TextField
|
|||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.LaunchedEffect
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
|
import androidx.compose.runtime.mutableIntStateOf
|
||||||
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
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
@@ -35,22 +37,25 @@ import fr.univpau.queezer.data.Settings
|
|||||||
import fr.univpau.queezer.manager.loadSettings
|
import fr.univpau.queezer.manager.loadSettings
|
||||||
import coil.compose.AsyncImage
|
import coil.compose.AsyncImage
|
||||||
import fr.univpau.queezer.data.Answer
|
import fr.univpau.queezer.data.Answer
|
||||||
|
import fr.univpau.queezer.data.Playlist
|
||||||
import fr.univpau.queezer.manager.GameManager
|
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
|
@Composable
|
||||||
fun GameScreen(navController: NavHostController) {
|
fun GameScreen(navController: NavHostController, database: DatabaseService) {
|
||||||
|
val coroutineScope = rememberCoroutineScope()
|
||||||
val context = LocalContext.current
|
val context = LocalContext.current
|
||||||
val settings: Settings = loadSettings(context)
|
val settings: Settings = loadSettings(context)
|
||||||
|
|
||||||
var gameManager by remember { mutableStateOf(GameManager(settings, emptyList())) }
|
var gameManager by remember { mutableStateOf(GameManager(settings, Playlist(), {}, database)) }
|
||||||
var currentTrack by remember { mutableStateOf(gameManager.getCurrentTrack()) }
|
var countdown by remember { mutableIntStateOf(30) }
|
||||||
|
|
||||||
LaunchedEffect(settings.playlistUrl) {
|
LaunchedEffect(settings.playlistUrl) {
|
||||||
val tracks = fetchAndFormatPlaylist(settings.playlistUrl).shuffled()
|
val playlist = fetchPlaylist(settings.playlistUrl)
|
||||||
gameManager = GameManager(settings, tracks)
|
gameManager = GameManager(settings, playlist ?: Playlist(), { countdown = gameManager.countDownManager.timeLeft.toInt() }, database)
|
||||||
|
|
||||||
currentTrack = gameManager.getCurrentTrack()
|
|
||||||
gameManager.start()
|
gameManager.start()
|
||||||
Log.i("GameScreen", gameManager.getCurrentTrack().toString())
|
Log.i("GameScreen", gameManager.getCurrentTrack().toString())
|
||||||
}
|
}
|
||||||
@@ -67,21 +72,32 @@ fun GameScreen(navController: NavHostController) {
|
|||||||
horizontalAlignment = Alignment.CenterHorizontally,
|
horizontalAlignment = Alignment.CenterHorizontally,
|
||||||
verticalArrangement = Arrangement.spacedBy(16.dp)
|
verticalArrangement = Arrangement.spacedBy(16.dp)
|
||||||
) {
|
) {
|
||||||
if (currentTrack == null) {
|
if (gameManager.getCurrentTrack() == null) {
|
||||||
CircularProgressIndicator(
|
CircularProgressIndicator(
|
||||||
modifier = Modifier.width(64.dp),
|
modifier = Modifier.width(64.dp),
|
||||||
color = MaterialTheme.colorScheme.secondary,
|
color = MaterialTheme.colorScheme.secondary,
|
||||||
trackColor = MaterialTheme.colorScheme.surfaceVariant,
|
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 {
|
} else {
|
||||||
Text("Score : ${gameManager.score}", fontSize = 24.sp)
|
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
|
if (gameManager.getCurrentTrack()!!.title.answer == Answer.CORRECT && gameManager.getCurrentTrack()!!.artist.answer == Answer.UNKNOWN
|
||||||
|| currentTrack!!.title.answer == Answer.UNKNOWN && currentTrack!!.artist.answer == Answer.CORRECT
|
|| gameManager.getCurrentTrack()!!.title.answer == Answer.UNKNOWN && gameManager.getCurrentTrack()!!.artist.answer == Answer.CORRECT
|
||||||
|| currentTrack!!.title.answer == Answer.CORRECT && currentTrack!!.artist.answer == Answer.CORRECT) {
|
|| gameManager.getCurrentTrack()!!.title.answer == Answer.CORRECT && gameManager.getCurrentTrack()!!.artist.answer == Answer.CORRECT) {
|
||||||
AsyncImage(
|
AsyncImage(
|
||||||
model = currentTrack!!.album,
|
model = gameManager.getCurrentTrack()!!.album,
|
||||||
contentDescription = "Image from URL",
|
contentDescription = "Image from URL",
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.width(200.dp)
|
.width(200.dp)
|
||||||
@@ -91,7 +107,7 @@ fun GameScreen(navController: NavHostController) {
|
|||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
AsyncImage(
|
AsyncImage(
|
||||||
model = currentTrack!!.album,
|
model = gameManager.getCurrentTrack()!!.album,
|
||||||
contentDescription = "Image from URL",
|
contentDescription = "Image from URL",
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.width(200.dp)
|
.width(200.dp)
|
||||||
@@ -103,14 +119,14 @@ fun GameScreen(navController: NavHostController) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Row {
|
Row {
|
||||||
if (currentTrack!!.title.answer == Answer.CORRECT || currentTrack!!.title.answer == Answer.UNKNOWN) {
|
if (gameManager.getCurrentTrack()!!.title.answer == Answer.CORRECT || gameManager.getCurrentTrack()!!.title.answer == Answer.UNKNOWN) {
|
||||||
Text("Titre : ${currentTrack!!.title.value}", fontSize = 20.sp)
|
Text("Titre : ${gameManager.getCurrentTrack()!!.title.value}", fontSize = 20.sp)
|
||||||
} else {
|
} else {
|
||||||
TextField(
|
TextField(
|
||||||
value = titleInput.value,
|
value = titleInput.value,
|
||||||
onValueChange = {
|
onValueChange = {
|
||||||
titleInput.value = it;
|
titleInput.value = it;
|
||||||
gameManager.checkTitleAnswer(currentTrack, titleInput.value)
|
gameManager.checkTitleAnswer(gameManager.getCurrentTrack(), titleInput.value)
|
||||||
},
|
},
|
||||||
label = { Text("Titre") },
|
label = { Text("Titre") },
|
||||||
keyboardOptions = KeyboardOptions.Default.copy(keyboardType = KeyboardType.Text),
|
keyboardOptions = KeyboardOptions.Default.copy(keyboardType = KeyboardType.Text),
|
||||||
@@ -120,12 +136,12 @@ fun GameScreen(navController: NavHostController) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Row {
|
Row {
|
||||||
if (currentTrack!!.artist.answer == Answer.CORRECT || currentTrack!!.artist.answer == Answer.UNKNOWN) {
|
if (gameManager.getCurrentTrack()!!.artist.answer == Answer.CORRECT || gameManager.getCurrentTrack()!!.artist.answer == Answer.UNKNOWN) {
|
||||||
Text("Artiste : ${currentTrack!!.artist.value}", fontSize = 20.sp)
|
Text("Artiste : ${gameManager.getCurrentTrack()!!.artist.value}", fontSize = 20.sp)
|
||||||
} else {
|
} else {
|
||||||
TextField(
|
TextField(
|
||||||
value = artistInput.value,
|
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") },
|
label = { Text("Artiste") },
|
||||||
keyboardOptions = KeyboardOptions.Default.copy(keyboardType = KeyboardType.Text),
|
keyboardOptions = KeyboardOptions.Default.copy(keyboardType = KeyboardType.Text),
|
||||||
modifier = Modifier.fillMaxWidth()
|
modifier = Modifier.fillMaxWidth()
|
||||||
@@ -139,7 +155,6 @@ fun GameScreen(navController: NavHostController) {
|
|||||||
titleInput.value = ""
|
titleInput.value = ""
|
||||||
artistInput.value = ""
|
artistInput.value = ""
|
||||||
gameManager.nextTrack()
|
gameManager.nextTrack()
|
||||||
currentTrack = gameManager.getCurrentTrack()
|
|
||||||
},
|
},
|
||||||
) { Text(context.resources.getString(R.string.skip)) }
|
) { Text(context.resources.getString(R.string.skip)) }
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
package fr.univpau.queezer.screen
|
package fr.univpau.queezer.screen
|
||||||
|
|
||||||
|
import androidx.compose.foundation.Image
|
||||||
import androidx.compose.foundation.layout.Arrangement
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
import androidx.compose.foundation.layout.Spacer
|
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.fillMaxWidth
|
||||||
import androidx.compose.foundation.layout.height
|
import androidx.compose.foundation.layout.height
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.foundation.layout.size
|
||||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||||
import androidx.compose.material3.Button
|
import androidx.compose.material3.Button
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
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.text.font.FontWeight
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.compose.ui.unit.sp
|
import androidx.compose.ui.unit.sp
|
||||||
import androidx.navigation.NavHostController
|
import androidx.navigation.NavHostController
|
||||||
|
import fr.univpau.queezer.R
|
||||||
import fr.univpau.queezer.ui.theme.Purple40
|
import fr.univpau.queezer.ui.theme.Purple40
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
@@ -28,15 +33,14 @@ fun HomeScreen(navController: NavHostController) {
|
|||||||
horizontalAlignment = Alignment.CenterHorizontally,
|
horizontalAlignment = Alignment.CenterHorizontally,
|
||||||
verticalArrangement = Arrangement.Center
|
verticalArrangement = Arrangement.Center
|
||||||
) {
|
) {
|
||||||
// Logo et Titre
|
// Logo foreground et background
|
||||||
// Image(
|
Image(
|
||||||
// painter = painterResource(id = R.drawable.logo),
|
painter = painterResource(id = R.mipmap.ic_launcher),
|
||||||
// contentDescription = "Logo Queezer",
|
contentDescription = "Logo Queezer",
|
||||||
// modifier = Modifier
|
modifier = Modifier.size(140.dp),
|
||||||
// .size(120.dp)
|
contentScale = ContentScale.Crop
|
||||||
// .padding(16.dp),
|
)
|
||||||
// contentScale = ContentScale.Crop
|
|
||||||
// )
|
|
||||||
Text(
|
Text(
|
||||||
text = "Queezer",
|
text = "Queezer",
|
||||||
fontSize = 32.sp,
|
fontSize = 32.sp,
|
||||||
@@ -78,7 +82,7 @@ fun HomeScreen(navController: NavHostController) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Button(
|
Button(
|
||||||
onClick = { /* TODO: Scores */ },
|
onClick = { navController.navigate("score") },
|
||||||
shape = RoundedCornerShape(8.dp),
|
shape = RoundedCornerShape(8.dp),
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxWidth()
|
.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.data.GameMode
|
||||||
import fr.univpau.queezer.manager.loadSettings
|
import fr.univpau.queezer.manager.loadSettings
|
||||||
import fr.univpau.queezer.manager.saveSettings
|
import fr.univpau.queezer.manager.saveSettings
|
||||||
import java.net.URL
|
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun SettingsScreen(navController: NavHostController) {
|
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
|
import retrofit2.http.Url
|
||||||
|
|
||||||
data class PlaylistResponse(
|
data class PlaylistResponse(
|
||||||
|
val title: String,
|
||||||
val tracks: TrackList
|
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="give_up">Abandonner</string>
|
||||||
<string name="submit">Valider</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>
|
</resources>
|
||||||
@@ -1,5 +1,7 @@
|
|||||||
[versions]
|
[versions]
|
||||||
agp = "8.7.3"
|
agp = "8.7.3"
|
||||||
|
annotationsJava5 = "15.0"
|
||||||
|
coilCompose = "2.4.0"
|
||||||
converterGson = "2.9.0"
|
converterGson = "2.9.0"
|
||||||
kotlin = "2.0.0"
|
kotlin = "2.0.0"
|
||||||
coreKtx = "1.13.1"
|
coreKtx = "1.13.1"
|
||||||
@@ -10,12 +12,28 @@ lifecycleRuntimeKtx = "2.8.6"
|
|||||||
activityCompose = "1.9.3"
|
activityCompose = "1.9.3"
|
||||||
composeBom = "2024.04.01"
|
composeBom = "2024.04.01"
|
||||||
navigationCompose = "2.8.4"
|
navigationCompose = "2.8.4"
|
||||||
media3Exoplayer = "1.5.0"
|
|
||||||
retrofit = "2.9.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]
|
[libraries]
|
||||||
androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" }
|
androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" }
|
||||||
androidx-navigation-compose = { module = "androidx.navigation:navigation-compose", version.ref = "navigationCompose" }
|
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" }
|
converter-gson = { module = "com.squareup.retrofit2:converter-gson", version.ref = "converterGson" }
|
||||||
junit = { group = "junit", name = "junit", version.ref = "junit" }
|
junit = { group = "junit", name = "junit", version.ref = "junit" }
|
||||||
androidx-junit = { group = "androidx.test.ext", name = "junit", version.ref = "junitVersion" }
|
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-manifest = { group = "androidx.compose.ui", name = "ui-test-manifest" }
|
||||||
androidx-ui-test-junit4 = { group = "androidx.compose.ui", name = "ui-test-junit4" }
|
androidx-ui-test-junit4 = { group = "androidx.compose.ui", name = "ui-test-junit4" }
|
||||||
androidx-material3 = { group = "androidx.compose.material3", name = "material3" }
|
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" }
|
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]
|
[plugins]
|
||||||
android-application = { id = "com.android.application", version.ref = "agp" }
|
android-application = { id = "com.android.application", version.ref = "agp" }
|
||||||
|
|||||||