mirror of
https://github.com/LucasVbr/Queezer.git
synced 2026-07-09 14:17:45 +00:00
feat: Save locally all parties
This commit is contained in:
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user