mirror of
https://github.com/LucasVbr/Queezer.git
synced 2026-07-09 06:07:45 +00:00
feat: Update UI
This commit is contained in:
@@ -7,6 +7,7 @@ data class Settings(
|
|||||||
var numberOfTitles: Int? = 5,
|
var numberOfTitles: Int? = 5,
|
||||||
var playlistUrl: String = "https://api.deezer.com/playlist/13279914183",
|
var playlistUrl: String = "https://api.deezer.com/playlist/13279914183",
|
||||||
) {
|
) {
|
||||||
|
|
||||||
fun validate(context: android.content.Context) {
|
fun validate(context: android.content.Context) {
|
||||||
if (playlistUrl.isEmpty()) {
|
if (playlistUrl.isEmpty()) {
|
||||||
throw IllegalArgumentException(context.resources.getString(R.string.error_playlist_url_empty))
|
throw IllegalArgumentException(context.resources.getString(R.string.error_playlist_url_empty))
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
package fr.univpau.queezer.manager
|
package fr.univpau.queezer.manager
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
import android.os.CountDownTimer
|
import android.os.CountDownTimer
|
||||||
|
|
||||||
class CountdownManager (val duration: Long, val onTickTimer: () -> Unit, val onFinishTimer: () -> Unit) {
|
class CountdownManager (val duration: Long, val onTickTimer: () -> Unit, val onFinishTimer: () -> Unit) {
|
||||||
|
|||||||
@@ -3,6 +3,10 @@ package fr.univpau.queezer.manager
|
|||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.media.MediaPlayer
|
import android.media.MediaPlayer
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
|
import androidx.compose.runtime.getValue
|
||||||
|
import androidx.compose.runtime.mutableIntStateOf
|
||||||
|
import androidx.compose.runtime.mutableStateOf
|
||||||
|
import androidx.compose.runtime.setValue
|
||||||
import fr.univpau.queezer.data.Answer
|
import fr.univpau.queezer.data.Answer
|
||||||
import fr.univpau.queezer.data.Game
|
import fr.univpau.queezer.data.Game
|
||||||
import fr.univpau.queezer.data.GameMode
|
import fr.univpau.queezer.data.GameMode
|
||||||
@@ -18,23 +22,23 @@ import java.util.Locale
|
|||||||
|
|
||||||
class GameManager() {
|
class GameManager() {
|
||||||
private lateinit var databaseService: DatabaseService
|
private lateinit var databaseService: DatabaseService
|
||||||
|
|
||||||
private var mediaPlayer: MediaPlayer = MediaPlayer()
|
private var mediaPlayer: MediaPlayer = MediaPlayer()
|
||||||
var countDownManager = CountdownManager(30000L, {}, {})
|
var countDownManager = CountdownManager(30000L, {}, {})
|
||||||
|
|
||||||
var settings: Settings = Settings()
|
var settings: Settings = Settings()
|
||||||
// var tracks: List<Track> = emptyList()
|
|
||||||
var playlist: Playlist = Playlist()
|
var playlist: Playlist = Playlist()
|
||||||
var gameFinished : Boolean = false;
|
|
||||||
|
|
||||||
var currentTrackIndex: Int = 0;
|
var gameFinished by mutableStateOf(false)
|
||||||
var score = 0
|
var currentTrackIndex by mutableIntStateOf(0)
|
||||||
|
var score by mutableIntStateOf(0)
|
||||||
|
|
||||||
constructor(settings: Settings, playlist: Playlist, onTick: () -> Unit, databaseService: DatabaseService) : this() {
|
constructor(settings: Settings, playlist: Playlist, onTickTrack: () -> Unit, onFinishTrack: () -> Unit, databaseService: DatabaseService) : this() {
|
||||||
this.databaseService = databaseService
|
|
||||||
this.settings = settings
|
this.settings = settings
|
||||||
|
this.databaseService = databaseService
|
||||||
this.playlist = Playlist(playlist.title, playlist.tracks.subList(0, settings.numberOfTitles!!))
|
this.playlist = Playlist(playlist.title, playlist.tracks.subList(0, settings.numberOfTitles!!))
|
||||||
|
|
||||||
this.countDownManager = CountdownManager(30000L, onTickTimer = onTick, onFinishTimer = { nextTrack() })
|
this.countDownManager = CountdownManager(duration = 30000L, onTickTimer = onTickTrack, onFinishTimer = { onFinishTrack() })
|
||||||
|
|
||||||
for (track in playlist.tracks) {
|
for (track in playlist.tracks) {
|
||||||
if (settings.gameMode == GameMode.TITLE) {
|
if (settings.gameMode == GameMode.TITLE) {
|
||||||
@@ -53,11 +57,8 @@ class GameManager() {
|
|||||||
fun checkTitleAnswer(currentTrack: Track?, answerTitle: String) {
|
fun checkTitleAnswer(currentTrack: Track?, answerTitle: String) {
|
||||||
if (currentTrack == null) return;
|
if (currentTrack == null) return;
|
||||||
|
|
||||||
Log.i("GameManager", "Current track: ${currentTrack.title.value}")
|
val simpleTitleAnswer = formatString(answerTitle)
|
||||||
Log.i("GameManager", "Answer: $answerTitle")
|
val simpleTitle = formatString(currentTrack.title.value)
|
||||||
|
|
||||||
var simpleTitleAnswer = formatString(answerTitle)
|
|
||||||
var simpleTitle = formatString(currentTrack.title.value)
|
|
||||||
|
|
||||||
if (simpleTitle.equals(simpleTitleAnswer, ignoreCase = true)) {
|
if (simpleTitle.equals(simpleTitleAnswer, ignoreCase = true)) {
|
||||||
currentTrack.title.answer = Answer.CORRECT
|
currentTrack.title.answer = Answer.CORRECT
|
||||||
@@ -70,11 +71,8 @@ class GameManager() {
|
|||||||
fun checkArtistAnswer(currentTrack: Track?, answerArtist: String) {
|
fun checkArtistAnswer(currentTrack: Track?, answerArtist: String) {
|
||||||
if (currentTrack == null) return;
|
if (currentTrack == null) return;
|
||||||
|
|
||||||
Log.i("GameManager", "Current track: ${currentTrack.artist.value}")
|
val simpleArtistAnswer = formatString(answerArtist)
|
||||||
Log.i("GameManager", "Answer: $answerArtist")
|
val simpleArtist = formatString(currentTrack.artist.value)
|
||||||
|
|
||||||
var simpleArtistAnswer = formatString(answerArtist)
|
|
||||||
var simpleArtist = formatString(currentTrack.artist.value)
|
|
||||||
|
|
||||||
if (simpleArtist.equals(simpleArtistAnswer, ignoreCase = true)) {
|
if (simpleArtist.equals(simpleArtistAnswer, ignoreCase = true)) {
|
||||||
currentTrack.artist.answer = Answer.CORRECT
|
currentTrack.artist.answer = Answer.CORRECT
|
||||||
@@ -84,7 +82,7 @@ class GameManager() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun formatString(input: String): String {
|
private fun formatString(input: String): String {
|
||||||
return input
|
return input
|
||||||
.trim()
|
.trim()
|
||||||
.lowercase(Locale.ROOT)
|
.lowercase(Locale.ROOT)
|
||||||
@@ -109,8 +107,11 @@ class GameManager() {
|
|||||||
fun nextTrack() {
|
fun nextTrack() {
|
||||||
mediaPlayer.release()
|
mediaPlayer.release()
|
||||||
|
|
||||||
|
Log.i("GameManager", "Current track index: $currentTrackIndex")
|
||||||
|
Log.i("GameManager", "Playlist size: ${playlist.tracks.size}")
|
||||||
|
|
||||||
if (currentTrackIndex + 1 >= playlist.tracks.size) {
|
if (currentTrackIndex + 1 >= playlist.tracks.size) {
|
||||||
gameFinished = true
|
this.gameFinished = true
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -128,7 +129,6 @@ class GameManager() {
|
|||||||
|
|
||||||
fun start() {
|
fun start() {
|
||||||
countDownManager.start()
|
countDownManager.start()
|
||||||
Log.i("GameManager", "Next track: ${playlist.tracks[currentTrackIndex].preview}")
|
|
||||||
mediaPlayer = MediaPlayer().apply {
|
mediaPlayer = MediaPlayer().apply {
|
||||||
setDataSource(playlist.tracks[currentTrackIndex].preview)
|
setDataSource(playlist.tracks[currentTrackIndex].preview)
|
||||||
prepare()
|
prepare()
|
||||||
@@ -137,8 +137,6 @@ class GameManager() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun save(context: Context) {
|
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
|
// Créer un objet Game
|
||||||
val game = Game(
|
val game = Game(
|
||||||
id = 0,
|
id = 0,
|
||||||
@@ -153,4 +151,9 @@ class GameManager() {
|
|||||||
databaseService.gameDao().insert(game)
|
databaseService.gameDao().insert(game)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun pause() {
|
||||||
|
mediaPlayer.pause()
|
||||||
|
countDownManager.stop()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -31,28 +31,4 @@ suspend fun fetchPlaylist(apiUrl: String) : Playlist? {
|
|||||||
null;
|
null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
suspend fun fetchAndFormatPlaylist(apiUrl: String): List<Track> {
|
|
||||||
val deezerApiService = createDeezerApiService()
|
|
||||||
|
|
||||||
return withContext(Dispatchers.IO) {
|
|
||||||
try {
|
|
||||||
// Récupérer la réponse de la playlist
|
|
||||||
val playlist = deezerApiService.getPlaylist(apiUrl)
|
|
||||||
|
|
||||||
// Transformer les données en liste de `Track`
|
|
||||||
playlist.tracks.data.map { track ->
|
|
||||||
Track(
|
|
||||||
preview = track.preview,
|
|
||||||
album = track.album.cover,
|
|
||||||
title = Input(value = track.title),
|
|
||||||
artist = Input(value = track.artist.name)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
} catch (e: Exception) {
|
|
||||||
e.printStackTrace()
|
|
||||||
emptyList()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -14,7 +14,7 @@ import androidx.compose.ui.platform.LocalContext
|
|||||||
private val DarkColorScheme = darkColorScheme(
|
private val DarkColorScheme = darkColorScheme(
|
||||||
primary = Purple80,
|
primary = Purple80,
|
||||||
secondary = PurpleGrey80,
|
secondary = PurpleGrey80,
|
||||||
tertiary = Pink80
|
tertiary = Pink80,
|
||||||
)
|
)
|
||||||
|
|
||||||
private val LightColorScheme = lightColorScheme(
|
private val LightColorScheme = lightColorScheme(
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import androidx.compose.material3.MaterialTheme
|
|||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.graphics.Color
|
||||||
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
|
||||||
@@ -16,44 +17,25 @@ import fr.univpau.queezer.data.Track
|
|||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun TrackCardItem(track: Track) {
|
fun TrackCardItem(track: Track) {
|
||||||
val titleColor = when (track.title.answer) {
|
Card(modifier = Modifier.fillMaxWidth()) {
|
||||||
Answer.CORRECT -> {
|
Column(modifier = Modifier.padding(16.dp)) {
|
||||||
MaterialTheme.colorScheme.primary
|
|
||||||
}
|
|
||||||
Answer.INCORRECT -> {
|
|
||||||
MaterialTheme.colorScheme.error
|
|
||||||
}
|
|
||||||
else -> {
|
|
||||||
MaterialTheme.colorScheme.onSurface
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
val artistColor = when (track.artist.answer) {
|
|
||||||
Answer.CORRECT -> {
|
|
||||||
MaterialTheme.colorScheme.primary
|
|
||||||
}
|
|
||||||
Answer.INCORRECT -> {
|
|
||||||
MaterialTheme.colorScheme.error
|
|
||||||
}
|
|
||||||
else -> {
|
|
||||||
MaterialTheme.colorScheme.onSurface
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Card(
|
|
||||||
modifier = Modifier.fillMaxWidth()
|
|
||||||
) {
|
|
||||||
Column(
|
|
||||||
modifier = Modifier.padding(16.dp)
|
|
||||||
) {
|
|
||||||
Text(
|
Text(
|
||||||
text = track.title.value,
|
text = track.title.value,
|
||||||
fontSize = 14.sp,
|
fontSize = 14.sp,
|
||||||
fontWeight = FontWeight.SemiBold,
|
fontWeight = FontWeight.SemiBold,
|
||||||
color = titleColor
|
color = getColor(track.title.answer)
|
||||||
)
|
)
|
||||||
|
|
||||||
Text(text = track.artist.value, color = artistColor)
|
Text(text = track.artist.value, color = getColor(track.artist.answer))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun getColor(answer: Answer): Color {
|
||||||
|
return when (answer) {
|
||||||
|
Answer.CORRECT -> Color(0xFF4CAF50)
|
||||||
|
Answer.INCORRECT -> MaterialTheme.colorScheme.error
|
||||||
|
else -> MaterialTheme.colorScheme.onSurface
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
package fr.univpau.queezer.view.screens
|
package fr.univpau.queezer.view.screens
|
||||||
|
|
||||||
import android.util.Log
|
import android.content.Context
|
||||||
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.Row
|
import androidx.compose.foundation.layout.Row
|
||||||
@@ -9,14 +9,19 @@ 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.width
|
import androidx.compose.foundation.layout.width
|
||||||
import androidx.compose.foundation.text.KeyboardOptions
|
|
||||||
import androidx.compose.material3.Button
|
import androidx.compose.material3.Button
|
||||||
|
import androidx.compose.material3.CenterAlignedTopAppBar
|
||||||
import androidx.compose.material3.CircularProgressIndicator
|
import androidx.compose.material3.CircularProgressIndicator
|
||||||
|
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
|
import androidx.compose.material3.OutlinedButton
|
||||||
|
import androidx.compose.material3.Scaffold
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.material3.TextField
|
import androidx.compose.material3.TextField
|
||||||
|
import androidx.compose.material3.TopAppBarDefaults
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.LaunchedEffect
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
|
import androidx.compose.runtime.MutableState
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.runtime.mutableIntStateOf
|
import androidx.compose.runtime.mutableIntStateOf
|
||||||
import androidx.compose.runtime.mutableStateOf
|
import androidx.compose.runtime.mutableStateOf
|
||||||
@@ -28,7 +33,7 @@ import androidx.compose.ui.Modifier
|
|||||||
import androidx.compose.ui.draw.blur
|
import androidx.compose.ui.draw.blur
|
||||||
import androidx.compose.ui.layout.ContentScale
|
import androidx.compose.ui.layout.ContentScale
|
||||||
import androidx.compose.ui.platform.LocalContext
|
import androidx.compose.ui.platform.LocalContext
|
||||||
import androidx.compose.ui.text.input.KeyboardType
|
import androidx.compose.ui.text.style.TextOverflow
|
||||||
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
|
||||||
@@ -41,7 +46,7 @@ import fr.univpau.queezer.data.Playlist
|
|||||||
import fr.univpau.queezer.manager.GameManager
|
import fr.univpau.queezer.manager.GameManager
|
||||||
import fr.univpau.queezer.manager.fetchPlaylist
|
import fr.univpau.queezer.manager.fetchPlaylist
|
||||||
import fr.univpau.queezer.service.DatabaseService
|
import fr.univpau.queezer.service.DatabaseService
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun GameScreen(navController: NavHostController, database: DatabaseService) {
|
fun GameScreen(navController: NavHostController, database: DatabaseService) {
|
||||||
@@ -52,118 +57,213 @@ fun GameScreen(navController: NavHostController, database: DatabaseService) {
|
|||||||
var gameManager by remember { mutableStateOf(GameManager()) }
|
var gameManager by remember { mutableStateOf(GameManager()) }
|
||||||
var countdown by remember { mutableIntStateOf(30) }
|
var countdown by remember { mutableIntStateOf(30) }
|
||||||
|
|
||||||
LaunchedEffect(settings.playlistUrl) {
|
|
||||||
val playlist = fetchPlaylist(settings.playlistUrl)
|
|
||||||
gameManager = GameManager(settings, playlist ?: Playlist(), { countdown = gameManager.countDownManager.timeLeft.toInt() }, database)
|
|
||||||
|
|
||||||
gameManager.start()
|
|
||||||
Log.i("GameScreen", gameManager.getCurrentTrack().toString())
|
|
||||||
}
|
|
||||||
|
|
||||||
// État de l'utilisateur et des éléments du jeu
|
|
||||||
val titleInput = remember { mutableStateOf("") }
|
val titleInput = remember { mutableStateOf("") }
|
||||||
val artistInput = remember { mutableStateOf("") }
|
val artistInput = remember { mutableStateOf("") }
|
||||||
|
|
||||||
// Affichage de l'interface
|
LaunchedEffect(settings.playlistUrl) {
|
||||||
Column(
|
val playlist = fetchPlaylist(settings.playlistUrl)
|
||||||
modifier = Modifier
|
|
||||||
.fillMaxSize()
|
|
||||||
.padding(16.dp),
|
|
||||||
horizontalAlignment = Alignment.CenterHorizontally,
|
|
||||||
verticalArrangement = Arrangement.spacedBy(16.dp)
|
|
||||||
) {
|
|
||||||
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 = GameManager(
|
||||||
gameManager.save(context)
|
settings = settings,
|
||||||
|
playlist = playlist ?: Playlist(),
|
||||||
|
onTickTrack = {
|
||||||
|
countdown = gameManager.countDownManager.timeLeft.toInt()
|
||||||
|
},
|
||||||
|
onFinishTrack = {
|
||||||
|
titleInput.value = ""
|
||||||
|
artistInput.value = ""
|
||||||
|
|
||||||
|
gameManager.nextTrack()
|
||||||
|
},
|
||||||
|
databaseService = database
|
||||||
|
)
|
||||||
|
|
||||||
|
gameManager.start()
|
||||||
|
}
|
||||||
|
|
||||||
|
if (gameManager.getCurrentTrack() == null) {
|
||||||
|
LoadingScreen()
|
||||||
|
} else if (gameManager.gameFinished) {
|
||||||
|
FinishScreen(gameManager, context, navController)
|
||||||
|
} else {
|
||||||
|
InGameScreen(
|
||||||
|
gameManager,
|
||||||
|
context,
|
||||||
|
coroutineScope,
|
||||||
|
navController,
|
||||||
|
titleInput,
|
||||||
|
artistInput,
|
||||||
|
countdown
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
|
@Composable
|
||||||
|
fun InGameScreen(
|
||||||
|
gameManager: GameManager,
|
||||||
|
context: Context,
|
||||||
|
coroutineScope: CoroutineScope,
|
||||||
|
navController: NavHostController,
|
||||||
|
titleInput: MutableState<String>,
|
||||||
|
artistInput: MutableState<String>,
|
||||||
|
countdown: Int
|
||||||
|
) {
|
||||||
|
Scaffold(
|
||||||
|
topBar = {
|
||||||
|
CenterAlignedTopAppBar(
|
||||||
|
colors = TopAppBarDefaults.centerAlignedTopAppBarColors(
|
||||||
|
containerColor = MaterialTheme.colorScheme.primaryContainer,
|
||||||
|
titleContentColor = MaterialTheme.colorScheme.primary,
|
||||||
|
),
|
||||||
|
title = {
|
||||||
|
Text(
|
||||||
|
text = "${countdown}sec",
|
||||||
|
maxLines = 1,
|
||||||
|
overflow = TextOverflow.Ellipsis
|
||||||
|
)
|
||||||
|
},
|
||||||
|
navigationIcon = { Text("${gameManager.currentTrackIndex + 1}/${gameManager.playlist.tracks.size}") },
|
||||||
|
actions = { Text("${gameManager.score}pts") }
|
||||||
|
)
|
||||||
|
},
|
||||||
|
bottomBar = {
|
||||||
|
Row(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.padding(16.dp),
|
||||||
|
horizontalArrangement = Arrangement.SpaceBetween
|
||||||
|
) {
|
||||||
|
OutlinedButton(onClick = {
|
||||||
gameManager.stop()
|
gameManager.stop()
|
||||||
navController.popBackStack()
|
navController.popBackStack()
|
||||||
}
|
})
|
||||||
}) { Text(context.resources.getString(R.string.back)) }
|
{ Text(context.resources.getString(R.string.give_up)) }
|
||||||
} else {
|
|
||||||
Text("Score : ${gameManager.score}", fontSize = 24.sp)
|
|
||||||
Text("Temps restant : ${countdown}sec", fontSize = 20.sp)
|
|
||||||
|
|
||||||
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 = gameManager.getCurrentTrack()!!.album,
|
|
||||||
contentDescription = "Image from URL",
|
|
||||||
modifier = Modifier
|
|
||||||
.width(200.dp)
|
|
||||||
.height(200.dp)
|
|
||||||
,
|
|
||||||
contentScale = ContentScale.Crop
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
AsyncImage(
|
|
||||||
model = gameManager.getCurrentTrack()!!.album,
|
|
||||||
contentDescription = "Image from URL",
|
|
||||||
modifier = Modifier
|
|
||||||
.width(200.dp)
|
|
||||||
.height(200.dp)
|
|
||||||
.blur(50.dp)
|
|
||||||
,
|
|
||||||
contentScale = ContentScale.Crop
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
Row {
|
|
||||||
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(gameManager.getCurrentTrack(), titleInput.value)
|
|
||||||
},
|
|
||||||
label = { Text("Titre") },
|
|
||||||
keyboardOptions = KeyboardOptions.Default.copy(keyboardType = KeyboardType.Text),
|
|
||||||
modifier = Modifier.fillMaxWidth()
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Row {
|
|
||||||
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(gameManager.getCurrentTrack(), artistInput.value) },
|
|
||||||
label = { Text("Artiste") },
|
|
||||||
keyboardOptions = KeyboardOptions.Default.copy(keyboardType = KeyboardType.Text),
|
|
||||||
modifier = Modifier.fillMaxWidth()
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Row(modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceBetween) {
|
|
||||||
Button(
|
Button(
|
||||||
onClick = {
|
onClick = {
|
||||||
titleInput.value = ""
|
titleInput.value = ""
|
||||||
artistInput.value = ""
|
artistInput.value = ""
|
||||||
gameManager.nextTrack()
|
gameManager.nextTrack()
|
||||||
},
|
},
|
||||||
) { Text(context.resources.getString(R.string.skip)) }
|
) { Text(context.resources.getString(R.string.next)) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
) { innerPadding ->
|
||||||
|
Column(
|
||||||
|
modifier = Modifier
|
||||||
|
.padding(innerPadding),
|
||||||
|
verticalArrangement = Arrangement.spacedBy(16.dp),
|
||||||
|
) {
|
||||||
|
|
||||||
Button(onClick = {
|
Column(
|
||||||
gameManager.stop()
|
modifier = Modifier
|
||||||
navController.popBackStack()
|
.fillMaxWidth()
|
||||||
})
|
.padding(16.dp),
|
||||||
{ Text(context.resources.getString(R.string.give_up)) }
|
horizontalAlignment = Alignment.CenterHorizontally,
|
||||||
|
verticalArrangement = Arrangement.spacedBy(16.dp)
|
||||||
|
) {
|
||||||
|
|
||||||
|
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 = gameManager.getCurrentTrack()!!.album,
|
||||||
|
contentDescription = "Image from URL",
|
||||||
|
modifier = Modifier
|
||||||
|
.width(200.dp)
|
||||||
|
.height(200.dp),
|
||||||
|
contentScale = ContentScale.Crop
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
AsyncImage(
|
||||||
|
model = gameManager.getCurrentTrack()!!.album,
|
||||||
|
contentDescription = "Image from URL",
|
||||||
|
modifier = Modifier
|
||||||
|
.width(200.dp)
|
||||||
|
.height(200.dp)
|
||||||
|
.blur(50.dp),
|
||||||
|
contentScale = ContentScale.Crop
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
Row {
|
||||||
|
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(
|
||||||
|
gameManager.getCurrentTrack(),
|
||||||
|
titleInput.value
|
||||||
|
)
|
||||||
|
},
|
||||||
|
label = { Text("Titre") },
|
||||||
|
modifier = Modifier.fillMaxWidth()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Row {
|
||||||
|
if (gameManager.getCurrentTrack()!!.artist.answer == Answer.CORRECT || gameManager.getCurrentTrack()!!.artist.answer == Answer.UNKNOWN) {
|
||||||
|
Text(
|
||||||
|
gameManager.getCurrentTrack()!!.artist.value,
|
||||||
|
fontSize = 20.sp
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
TextField(
|
||||||
|
value = artistInput.value,
|
||||||
|
onValueChange = {
|
||||||
|
artistInput.value = it;
|
||||||
|
gameManager.checkArtistAnswer(
|
||||||
|
gameManager.getCurrentTrack(),
|
||||||
|
artistInput.value
|
||||||
|
)
|
||||||
|
},
|
||||||
|
label = { Text("Artiste") },
|
||||||
|
modifier = Modifier.fillMaxWidth()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun LoadingScreen() {
|
||||||
|
Column(
|
||||||
|
modifier = Modifier.fillMaxSize(),
|
||||||
|
verticalArrangement = Arrangement.Center,
|
||||||
|
horizontalAlignment = Alignment.CenterHorizontally
|
||||||
|
) {
|
||||||
|
CircularProgressIndicator(
|
||||||
|
modifier = Modifier.width(64.dp),
|
||||||
|
color = MaterialTheme.colorScheme.secondary,
|
||||||
|
trackColor = MaterialTheme.colorScheme.surfaceVariant,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun FinishScreen(gameManager: GameManager, context: Context, navController: NavHostController) {
|
||||||
|
Column(
|
||||||
|
modifier = Modifier.fillMaxSize(),
|
||||||
|
verticalArrangement = Arrangement.Center,
|
||||||
|
horizontalAlignment = Alignment.CenterHorizontally
|
||||||
|
) {
|
||||||
|
Text("Partie terminée !", fontSize = 24.sp)
|
||||||
|
Text("Score : ${gameManager.score}", fontSize = 20.sp)
|
||||||
|
Button(onClick = {
|
||||||
|
gameManager.save(context)
|
||||||
|
gameManager.stop()
|
||||||
|
navController.popBackStack()
|
||||||
|
}) { Text(context.resources.getString(R.string.back)) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -47,7 +47,6 @@ fun ScoreScreen(navController: NavHostController, gameViewModel: GameViewModel)
|
|||||||
val averageSuccessRate = games.sumOf { it.score }.div(max(games.size, 1))
|
val averageSuccessRate = games.sumOf { it.score }.div(max(games.size, 1))
|
||||||
|
|
||||||
val filteredGames = filterGames(filter.value, games)
|
val filteredGames = filterGames(filter.value, games)
|
||||||
Log.i("ScoreScreen", "filteredGames: $filteredGames")
|
|
||||||
|
|
||||||
Scaffold(
|
Scaffold(
|
||||||
topBar = {
|
topBar = {
|
||||||
@@ -79,7 +78,6 @@ fun ScoreScreen(navController: NavHostController, gameViewModel: GameViewModel)
|
|||||||
.padding(innerPadding),
|
.padding(innerPadding),
|
||||||
verticalArrangement = Arrangement.spacedBy(16.dp),
|
verticalArrangement = Arrangement.spacedBy(16.dp),
|
||||||
) {
|
) {
|
||||||
|
|
||||||
Row(
|
Row(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
@@ -100,146 +98,16 @@ fun ScoreScreen(navController: NavHostController, gameViewModel: GameViewModel)
|
|||||||
|
|
||||||
// Todo add filters
|
// Todo add filters
|
||||||
|
|
||||||
GameCardItemList(filteredGames)
|
if (filteredGames.isEmpty()) {
|
||||||
|
Column (
|
||||||
|
modifier = Modifier.fillMaxWidth(),
|
||||||
|
horizontalAlignment = Alignment.CenterHorizontally
|
||||||
|
) {
|
||||||
|
Text(text = "Aucune partie trouvée... (。•́︿•̀。)", fontSize = 16.sp, color = MaterialTheme.colorScheme.onSurface)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
GameCardItemList(filteredGames)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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,
|
|
||||||
// )
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// if (gameList !== null) {
|
|
||||||
// GameList(gameList)
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// Button(
|
|
||||||
// onClick = { navController.navigate("home") },
|
|
||||||
// shape = RoundedCornerShape(8.dp),
|
|
||||||
// modifier = Modifier
|
|
||||||
// .fillMaxWidth()
|
|
||||||
// .padding(vertical = 8.dp)
|
|
||||||
// ) {
|
|
||||||
// Text(context.resources.getString(R.string.back))
|
|
||||||
// }
|
|
||||||
|
|
||||||
//}
|
|
||||||
}
|
}
|
||||||
@@ -7,19 +7,30 @@ import androidx.compose.foundation.layout.Row
|
|||||||
import androidx.compose.foundation.layout.fillMaxSize
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
import androidx.compose.foundation.layout.fillMaxWidth
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.foundation.selection.selectable
|
||||||
import androidx.compose.foundation.text.KeyboardOptions
|
import androidx.compose.foundation.text.KeyboardOptions
|
||||||
|
import androidx.compose.material.icons.Icons
|
||||||
|
import androidx.compose.material.icons.automirrored.filled.ArrowBack
|
||||||
import androidx.compose.material3.Button
|
import androidx.compose.material3.Button
|
||||||
import androidx.compose.material3.OutlinedButton
|
import androidx.compose.material3.CenterAlignedTopAppBar
|
||||||
|
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||||
|
import androidx.compose.material3.Icon
|
||||||
|
import androidx.compose.material3.IconButton
|
||||||
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.material3.RadioButton
|
import androidx.compose.material3.RadioButton
|
||||||
|
import androidx.compose.material3.Scaffold
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.material3.TextField
|
import androidx.compose.material3.TextField
|
||||||
|
import androidx.compose.material3.TopAppBarDefaults
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.mutableStateOf
|
import androidx.compose.runtime.mutableStateOf
|
||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.platform.LocalContext
|
import androidx.compose.ui.platform.LocalContext
|
||||||
|
import androidx.compose.ui.semantics.Role
|
||||||
import androidx.compose.ui.text.input.KeyboardType
|
import androidx.compose.ui.text.input.KeyboardType
|
||||||
|
import androidx.compose.ui.text.style.TextOverflow
|
||||||
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
|
||||||
@@ -28,104 +39,120 @@ 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
|
||||||
|
|
||||||
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
@Composable
|
@Composable
|
||||||
fun SettingsScreen(navController: NavHostController) {
|
fun SettingsScreen(navController: NavHostController) {
|
||||||
val context = LocalContext.current
|
val context = LocalContext.current
|
||||||
val settings = remember { mutableStateOf(loadSettings(context)) }
|
val settings = remember { mutableStateOf(loadSettings(context)) }
|
||||||
|
|
||||||
Column(
|
Scaffold(
|
||||||
modifier = Modifier
|
topBar = {
|
||||||
.fillMaxSize()
|
CenterAlignedTopAppBar(
|
||||||
.padding(16.dp),
|
colors = TopAppBarDefaults.centerAlignedTopAppBarColors(
|
||||||
horizontalAlignment = Alignment.CenterHorizontally,
|
containerColor = MaterialTheme.colorScheme.primaryContainer,
|
||||||
verticalArrangement = Arrangement.spacedBy(16.dp)
|
titleContentColor = MaterialTheme.colorScheme.primary,
|
||||||
) {
|
),
|
||||||
// Titre des paramètres
|
title = {
|
||||||
Text(
|
Text(
|
||||||
text = context.resources.getString(R.string.settings),
|
text = context.resources.getString(R.string.settings),
|
||||||
fontSize = 32.sp,
|
maxLines = 1,
|
||||||
modifier = Modifier.padding(bottom = 32.dp)
|
overflow = TextOverflow.Ellipsis
|
||||||
)
|
)
|
||||||
|
|
||||||
// Paramètre : Playlist à utiliser
|
|
||||||
Row(
|
|
||||||
modifier = Modifier.fillMaxWidth(),
|
|
||||||
horizontalArrangement = Arrangement.SpaceBetween
|
|
||||||
) {
|
|
||||||
TextField(
|
|
||||||
label = { Text(context.resources.getString(R.string.playlist_url_label)) },
|
|
||||||
placeholder = { Text(context.resources.getString(R.string.playlist_url_hint)) },
|
|
||||||
modifier = Modifier.fillMaxWidth(),
|
|
||||||
value = settings.value.playlistUrl,
|
|
||||||
onValueChange = { settings.value = settings.value.copy(playlistUrl = it) },
|
|
||||||
keyboardOptions = KeyboardOptions.Default.copy(keyboardType = KeyboardType.Uri),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Paramètre : Nombre de titres dans une partie
|
|
||||||
Row(
|
|
||||||
modifier = Modifier.fillMaxWidth(),
|
|
||||||
horizontalArrangement = Arrangement.SpaceBetween
|
|
||||||
) {
|
|
||||||
TextField(
|
|
||||||
label = { Text(context.resources.getString(R.string.tracks_count_label)) },
|
|
||||||
modifier = Modifier.fillMaxWidth(1f),
|
|
||||||
value = settings.value.numberOfTitles?.toString() ?: "",
|
|
||||||
onValueChange = {
|
|
||||||
settings.value = settings.value.copy(numberOfTitles = it.toIntOrNull())
|
|
||||||
},
|
},
|
||||||
keyboardOptions = KeyboardOptions.Default.copy(keyboardType = KeyboardType.Number),
|
navigationIcon = {
|
||||||
)
|
IconButton(onClick = { navController.popBackStack() }) {
|
||||||
}
|
Icon(
|
||||||
|
imageVector = Icons.AutoMirrored.Filled.ArrowBack,
|
||||||
// Paramètre : Mode de jeu
|
contentDescription = context.resources.getString(R.string.back)
|
||||||
Row(
|
|
||||||
modifier = Modifier.fillMaxWidth(),
|
|
||||||
horizontalArrangement = Arrangement.SpaceBetween
|
|
||||||
) {
|
|
||||||
Column {
|
|
||||||
Text(text = "Mode de jeu", fontSize = 18.sp)
|
|
||||||
val gameModes = context.resources.getStringArray(R.array.game_modes)
|
|
||||||
gameModes.forEachIndexed { index, label ->
|
|
||||||
Row(
|
|
||||||
verticalAlignment = Alignment.CenterVertically,
|
|
||||||
modifier = Modifier.padding(end = 16.dp)
|
|
||||||
) {
|
|
||||||
RadioButton(
|
|
||||||
selected = settings.value.gameMode.ordinal == index,
|
|
||||||
onClick = {
|
|
||||||
settings.value =
|
|
||||||
settings.value.copy(gameMode = GameMode.entries[index])
|
|
||||||
}
|
|
||||||
)
|
)
|
||||||
Text(text = label)
|
}
|
||||||
|
},
|
||||||
|
)
|
||||||
|
},
|
||||||
|
bottomBar = {
|
||||||
|
Row(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.padding(16.dp),
|
||||||
|
horizontalArrangement = Arrangement.SpaceBetween
|
||||||
|
) {
|
||||||
|
Button(
|
||||||
|
onClick = {
|
||||||
|
try {
|
||||||
|
settings.value.validate(context)
|
||||||
|
saveSettings(context, settings.value)
|
||||||
|
navController.popBackStack()
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Toast.makeText(context, e.message, Toast.LENGTH_LONG).show()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
modifier = Modifier.fillMaxWidth()
|
||||||
|
) { Text(context.resources.getString(R.string.submit)) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
) { innerPadding ->
|
||||||
|
Column(
|
||||||
|
modifier = Modifier
|
||||||
|
.padding(innerPadding),
|
||||||
|
verticalArrangement = Arrangement.spacedBy(16.dp),
|
||||||
|
) {
|
||||||
|
|
||||||
|
Column(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxSize()
|
||||||
|
.padding(16.dp),
|
||||||
|
horizontalAlignment = Alignment.CenterHorizontally,
|
||||||
|
verticalArrangement = Arrangement.spacedBy(16.dp)
|
||||||
|
) {
|
||||||
|
TextField(
|
||||||
|
label = { Text(context.resources.getString(R.string.playlist_url_label)) },
|
||||||
|
placeholder = { Text(context.resources.getString(R.string.playlist_url_hint)) },
|
||||||
|
modifier = Modifier.fillMaxWidth(),
|
||||||
|
value = settings.value.playlistUrl,
|
||||||
|
onValueChange = { settings.value = settings.value.copy(playlistUrl = it) },
|
||||||
|
keyboardOptions = KeyboardOptions.Default.copy(keyboardType = KeyboardType.Uri),
|
||||||
|
)
|
||||||
|
|
||||||
|
TextField(
|
||||||
|
label = { Text(context.resources.getString(R.string.tracks_count_label)) },
|
||||||
|
modifier = Modifier.fillMaxWidth(1f),
|
||||||
|
value = settings.value.numberOfTitles?.toString() ?: "",
|
||||||
|
onValueChange = {
|
||||||
|
settings.value = settings.value.copy(numberOfTitles = it.toIntOrNull())
|
||||||
|
},
|
||||||
|
keyboardOptions = KeyboardOptions.Default.copy(keyboardType = KeyboardType.Number),
|
||||||
|
)
|
||||||
|
|
||||||
|
Column (
|
||||||
|
modifier = Modifier.fillMaxWidth(),
|
||||||
|
) {
|
||||||
|
Text(text = "Mode de jeu", fontSize = 18.sp)
|
||||||
|
val gameModes = context.resources.getStringArray(R.array.game_modes)
|
||||||
|
gameModes.forEachIndexed { index, label ->
|
||||||
|
Row(
|
||||||
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
|
modifier = Modifier.selectable(
|
||||||
|
selected = (settings.value.gameMode.ordinal == index),
|
||||||
|
onClick = {
|
||||||
|
settings.value =
|
||||||
|
settings.value.copy(gameMode = GameMode.entries[index])
|
||||||
|
},
|
||||||
|
role = Role.RadioButton
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
RadioButton(
|
||||||
|
selected = settings.value.gameMode.ordinal == index,
|
||||||
|
onClick = {
|
||||||
|
settings.value =
|
||||||
|
settings.value.copy(gameMode = GameMode.entries[index])
|
||||||
|
}
|
||||||
|
)
|
||||||
|
Text(text = label)
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Bouton pour revenir à l'écran d'accueil
|
|
||||||
Row(
|
|
||||||
modifier = Modifier.fillMaxWidth(),
|
|
||||||
horizontalArrangement = Arrangement.SpaceBetween
|
|
||||||
) {
|
|
||||||
OutlinedButton(
|
|
||||||
onClick = { navController.popBackStack() },
|
|
||||||
) { Text(context.resources.getString(R.string.back)) }
|
|
||||||
|
|
||||||
Button(
|
|
||||||
onClick = {
|
|
||||||
try {
|
|
||||||
settings.value.validate(context)
|
|
||||||
saveSettings(context, settings.value)
|
|
||||||
navController.popBackStack()
|
|
||||||
} catch (e: Exception) {
|
|
||||||
Toast.makeText(context, e.message, Toast.LENGTH_LONG).show()
|
|
||||||
}
|
|
||||||
},
|
|
||||||
) {
|
|
||||||
Text(context.resources.getString(R.string.submit))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,35 @@
|
|||||||
|
package fr.univpau.queezer.viewmodel
|
||||||
|
|
||||||
|
import androidx.lifecycle.ViewModel
|
||||||
|
import androidx.lifecycle.viewModelScope
|
||||||
|
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.launch
|
||||||
|
|
||||||
|
class PlaylistViewModel(private val url: String) : ViewModel() {
|
||||||
|
|
||||||
|
var playlist: Playlist? = null
|
||||||
|
|
||||||
|
init {
|
||||||
|
val deezerApiService = createDeezerApiService()
|
||||||
|
|
||||||
|
viewModelScope.launch {
|
||||||
|
val playlistResponse : PlaylistResponse = deezerApiService.getPlaylist(url)
|
||||||
|
|
||||||
|
playlist = 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()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -25,7 +25,7 @@
|
|||||||
</string-array>
|
</string-array>
|
||||||
|
|
||||||
<string name="back">Retour</string>
|
<string name="back">Retour</string>
|
||||||
<string name="skip">Passer</string>
|
<string name="next">Suivant</string>
|
||||||
<string name="give_up">Abandonner</string>
|
<string name="give_up">Abandonner</string>
|
||||||
<string name="submit">Valider</string>
|
<string name="submit">Valider</string>
|
||||||
|
|
||||||
@@ -33,5 +33,7 @@
|
|||||||
<string name="average_success_rate">Taux de réussite moyen</string>
|
<string name="average_success_rate">Taux de réussite moyen</string>
|
||||||
<string name="filters">Filtrer par</string>
|
<string name="filters">Filtrer par</string>
|
||||||
<string name="date">Date</string>
|
<string name="date">Date</string>
|
||||||
|
<string name="error_playlist_not_found">La playlist n\'as pas été trouvée</string>
|
||||||
|
<string name="error_tracks_count_too_high">Le nombre de titres et trop grand</string>
|
||||||
|
|
||||||
</resources>
|
</resources>
|
||||||
Reference in New Issue
Block a user