mirror of
https://github.com/LucasVbr/Queezer.git
synced 2026-05-13 17:11:55 +00:00
refactor: Use data classes for Settings + Game
This commit is contained in:
@@ -51,6 +51,7 @@ dependencies {
|
||||
implementation(libs.androidx.navigation.compose)
|
||||
implementation(libs.retrofit)
|
||||
implementation(libs.converter.gson)
|
||||
implementation("io.coil-kt:coil-compose:2.4.0")
|
||||
testImplementation(libs.junit)
|
||||
androidTestImplementation(libs.androidx.junit)
|
||||
androidTestImplementation(libs.androidx.espresso.core)
|
||||
|
||||
@@ -1,61 +0,0 @@
|
||||
package fr.univpau.queezer
|
||||
|
||||
import android.util.Log
|
||||
import androidx.compose.runtime.MutableState
|
||||
import com.google.gson.Gson
|
||||
import com.google.gson.JsonObject
|
||||
import com.google.gson.reflect.TypeToken
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
import java.net.HttpURLConnection
|
||||
import java.net.URL
|
||||
|
||||
data class Track(
|
||||
val title: String,
|
||||
val artist: Artist,
|
||||
val preview: String,
|
||||
val album: Album
|
||||
)
|
||||
|
||||
data class Artist(
|
||||
val name: String
|
||||
)
|
||||
|
||||
data class Album(
|
||||
val cover: String
|
||||
)
|
||||
|
||||
|
||||
suspend fun fetchTracks(apiUrl: String): List<Track> {
|
||||
return withContext(Dispatchers.IO) {
|
||||
try {
|
||||
val url = URL(apiUrl)
|
||||
val connection = url.openConnection() as HttpURLConnection
|
||||
connection.requestMethod = "GET"
|
||||
|
||||
if (connection.responseCode == HttpURLConnection.HTTP_OK) {
|
||||
val response = connection.inputStream.bufferedReader().use { it.readText() }
|
||||
val json = Gson().fromJson(response, JsonObject::class.java)
|
||||
val tracksJson = json["tracks"].asJsonObject["data"].toString()
|
||||
val trackListType = object : TypeToken<List<Track>>() {}.type
|
||||
Gson().fromJson<List<Track>>(tracksJson, trackListType)
|
||||
} else {
|
||||
emptyList()
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
emptyList()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun displayTracks(apiUrl: String) {
|
||||
val tracks = fetchTracks(apiUrl)
|
||||
|
||||
tracks?.forEach { track ->
|
||||
Log.d("Track", track.title)
|
||||
Log.d("Artist", track.artist.name)
|
||||
Log.d("Preview", track.preview)
|
||||
Log.d("Album", track.album.cover)
|
||||
}
|
||||
}
|
||||
@@ -1,151 +0,0 @@
|
||||
package fr.univpau.queezer
|
||||
|
||||
import android.os.CountDownTimer
|
||||
import android.util.Log
|
||||
import android.widget.Toast
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
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.text.KeyboardOptions
|
||||
import androidx.compose.material3.Button
|
||||
import androidx.compose.material3.Text
|
||||
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.setValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.text.input.KeyboardType
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import androidx.navigation.NavHostController
|
||||
|
||||
@Composable
|
||||
fun GameScreen(navController: NavHostController) {
|
||||
val context = LocalContext.current
|
||||
val loadedSettings = loadSettings(context)
|
||||
|
||||
var selectedGameMode by remember { mutableStateOf(loadedSettings.gameMode) }
|
||||
val numberOfTitles by remember { mutableIntStateOf(loadedSettings.numberOfTitles.toInt()) }
|
||||
val playlistUrl by remember { mutableStateOf(loadedSettings.playlistUrl) }
|
||||
var tracks by remember { mutableStateOf(emptyList<Track>()) }
|
||||
|
||||
LaunchedEffect(playlistUrl) {
|
||||
tracks = fetchTracks(playlistUrl)
|
||||
if (tracks.isEmpty()) {
|
||||
// Affiche un message d'erreur en toast
|
||||
Toast.makeText(context, "Impossible de charger les titres, veuillez vérifier la validité de l'URL.", Toast.LENGTH_SHORT).show()
|
||||
|
||||
// Retourn à l'écran d'accueil
|
||||
navController.popBackStack()
|
||||
}
|
||||
|
||||
tracks = tracks.shuffled() // On mélange les titres
|
||||
Log.i("Tracks", tracks.toString())
|
||||
}
|
||||
|
||||
val score = remember { mutableIntStateOf(0) }
|
||||
val remainingTitles = remember { mutableIntStateOf(numberOfTitles) } // Exemple avec 5 titres restants
|
||||
val userInput = remember { mutableStateOf("") }
|
||||
// val albumCover: Painter = painterResource(id = R.drawable.album_cover) // Remplacez par une ressource valide d'album
|
||||
val isCoverVisible = remember { mutableStateOf(false) }
|
||||
val totalTime = 30000L // 30 secondes
|
||||
var timeLeft by remember { mutableStateOf(totalTime / 1000) }
|
||||
|
||||
var currentTrackIndex by remember { mutableIntStateOf(0) }
|
||||
|
||||
// Timer de 30 secondes
|
||||
LaunchedEffect(Unit) {
|
||||
object : CountDownTimer(totalTime, 1000) { // Tick toutes les secondes
|
||||
override fun onTick(millisUntilFinished: Long) {
|
||||
timeLeft = millisUntilFinished / 1000 // Mettre à jour en secondes
|
||||
}
|
||||
|
||||
override fun onFinish() {
|
||||
timeLeft = 0 // Compte à rebours terminé
|
||||
currentTrackIndex += 1 // Passer à la chanson suivante
|
||||
}
|
||||
}.start()
|
||||
}
|
||||
|
||||
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.padding(16.dp),
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
verticalArrangement = Arrangement.spacedBy(16.dp)
|
||||
) {
|
||||
// Score
|
||||
Text("Score : ${score.intValue}", fontSize = 24.sp)
|
||||
|
||||
// Nombre de titres restants
|
||||
Text("Titres restants : ${remainingTitles.intValue}", fontSize = 20.sp)
|
||||
|
||||
// Timer
|
||||
Text("Temps restant : $timeLeft s", fontSize = 20.sp)
|
||||
|
||||
// Affichage de la couverture de l'album
|
||||
if (isCoverVisible.value) {
|
||||
// Image(painter = albumCover, contentDescription = "Cover", modifier = Modifier.fillMaxWidth())
|
||||
} else {
|
||||
Text("Couverture cachée", fontSize = 18.sp)
|
||||
}
|
||||
|
||||
// Champ de texte pour entrer la proposition
|
||||
TextField(
|
||||
value = userInput.value,
|
||||
onValueChange = { userInput.value = it },
|
||||
label = { Text("Titre / Artiste") },
|
||||
keyboardOptions = KeyboardOptions.Default.copy(keyboardType = KeyboardType.Text),
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
)
|
||||
|
||||
// Bouton Valider la réponse
|
||||
Button(
|
||||
onClick = {
|
||||
// Logique pour valider la réponse, en vérifiant la casse et en ajustant le score
|
||||
val correctAnswer = "Titre Correct" // Exemple, il faut remplacer par la bonne réponse
|
||||
if (userInput.value.trim().equals(correctAnswer, ignoreCase = true)) {
|
||||
score.value += 1 // Ajouter 10 points pour une bonne réponse
|
||||
}
|
||||
remainingTitles.value -= 1
|
||||
userInput.value = "" // Réinitialiser le champ de texte
|
||||
// Réinitialiser ou ajuster le timer si nécessaire
|
||||
}
|
||||
) {
|
||||
Text("Valider")
|
||||
}
|
||||
|
||||
// Bouton Passer
|
||||
Row(modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceBetween) {
|
||||
Button(
|
||||
onClick = {
|
||||
// Logique pour passer la chanson
|
||||
remainingTitles.value -= 1
|
||||
// Vous pouvez réinitialiser le timer, ou passer à la chanson suivante
|
||||
},
|
||||
) {
|
||||
Text("Passer")
|
||||
}
|
||||
|
||||
// Bouton Abandonner
|
||||
Button(
|
||||
onClick = {
|
||||
// Logique pour abandonner, peut-être retour à l'écran d'accueil
|
||||
navController.popBackStack()
|
||||
},
|
||||
) {
|
||||
Text("Abandonner")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4,13 +4,12 @@ import android.os.Bundle
|
||||
import androidx.activity.ComponentActivity
|
||||
import androidx.activity.compose.setContent
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.navigation.compose.NavHost
|
||||
import androidx.navigation.compose.composable
|
||||
import androidx.navigation.compose.rememberNavController
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import kotlinx.coroutines.launch
|
||||
import fr.univpau.queezer.screen.GameScreen
|
||||
import fr.univpau.queezer.screen.HomeScreen
|
||||
import fr.univpau.queezer.screen.SettingsScreen
|
||||
|
||||
class MainActivity : ComponentActivity() {
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
@@ -27,14 +26,8 @@ fun QueezerApp() {
|
||||
val navController = rememberNavController()
|
||||
|
||||
NavHost(navController = navController, startDestination = "home") {
|
||||
composable("home") {
|
||||
HomeScreen(navController)
|
||||
}
|
||||
composable("game") {
|
||||
GameScreen(navController)
|
||||
}
|
||||
composable("settings") {
|
||||
SettingsScreen(navController)
|
||||
}
|
||||
composable("home") { HomeScreen(navController) }
|
||||
composable("game") { GameScreen(navController) }
|
||||
composable("settings") { SettingsScreen(navController) }
|
||||
}
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
package fr.univpau.queezer
|
||||
|
||||
import android.content.Context
|
||||
import android.content.SharedPreferences
|
||||
|
||||
val gameModes = listOf("Titre Uniquement", "Artiste Uniquement", "Titre et Artiste")
|
||||
|
||||
fun saveSettings(context: Context, gameMode: String, numberOfTitles: String, playlistUrl: String) {
|
||||
val sharedPreferences: SharedPreferences =
|
||||
context.getSharedPreferences("AppSettings", Context.MODE_PRIVATE)
|
||||
with(sharedPreferences.edit()) {
|
||||
putString("gameMode", gameMode)
|
||||
putString("numberOfTitles", numberOfTitles)
|
||||
putString("playlistUrl", playlistUrl)
|
||||
apply()
|
||||
}
|
||||
}
|
||||
|
||||
fun loadSettings(context: Context): Settings {
|
||||
val sharedPreferences: SharedPreferences =
|
||||
context.getSharedPreferences("AppSettings", Context.MODE_PRIVATE)
|
||||
val gameMode = sharedPreferences.getString("gameMode", gameModes[0]) ?: gameModes[0]
|
||||
val numberOfTitles = sharedPreferences.getString("numberOfTitles", "30") ?: "30"
|
||||
val playlistUrl = sharedPreferences.getString("playlistUrl", "") ?: ""
|
||||
return Settings(gameMode, numberOfTitles, playlistUrl)
|
||||
}
|
||||
|
||||
data class Settings(val gameMode: String, val numberOfTitles: String, val playlistUrl: String)
|
||||
@@ -0,0 +1,5 @@
|
||||
package fr.univpau.queezer.data
|
||||
|
||||
data class Album(
|
||||
val cover: String
|
||||
)
|
||||
@@ -0,0 +1,5 @@
|
||||
package fr.univpau.queezer.data
|
||||
|
||||
data class Artist(
|
||||
val name: String
|
||||
)
|
||||
@@ -0,0 +1,10 @@
|
||||
package fr.univpau.queezer.data
|
||||
|
||||
import java.sql.Date
|
||||
|
||||
data class Game(
|
||||
val settings: Settings,
|
||||
val tracks: List<Track>,
|
||||
val score: Int,
|
||||
val date: Date
|
||||
)
|
||||
@@ -0,0 +1,7 @@
|
||||
package fr.univpau.queezer.data
|
||||
|
||||
enum class GameMode {
|
||||
TITLE,
|
||||
ARTIST,
|
||||
ALL
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
package fr.univpau.queezer.data
|
||||
|
||||
import fr.univpau.queezer.R
|
||||
|
||||
data class Settings(
|
||||
var gameMode: GameMode = GameMode.TITLE,
|
||||
var numberOfTitles: Int? = 5,
|
||||
var playlistUrl: String = "https://api.deezer.com/playlist/13279914183",
|
||||
) {
|
||||
fun validate(context: android.content.Context) {
|
||||
if (playlistUrl.isEmpty()) {
|
||||
throw IllegalArgumentException(context.resources.getString(R.string.error_playlist_url_empty))
|
||||
}
|
||||
|
||||
if (!playlistUrl.startsWith("https://api.deezer.com/playlist/")) {
|
||||
throw IllegalArgumentException(context.resources.getString(R.string.error_playlist_url_invalid))
|
||||
}
|
||||
|
||||
if (numberOfTitles == null) {
|
||||
throw IllegalArgumentException(context.resources.getString(R.string.error_tracks_count_empty))
|
||||
}
|
||||
|
||||
if (numberOfTitles!! <= 0) {
|
||||
throw IllegalArgumentException(context.resources.getString(R.string.error_tracks_count_negative))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
package fr.univpau.queezer.data
|
||||
|
||||
data class Track(
|
||||
val title: String,
|
||||
val artist: Artist,
|
||||
val preview: String,
|
||||
val album: Album
|
||||
)
|
||||
@@ -0,0 +1,31 @@
|
||||
package fr.univpau.queezer.manager
|
||||
|
||||
import android.media.MediaPlayer
|
||||
|
||||
class AudioManager {
|
||||
private var mediaPlayer: MediaPlayer = MediaPlayer()
|
||||
|
||||
fun play(url: String) {
|
||||
mediaPlayer.apply {
|
||||
setDataSource(url)
|
||||
prepare()
|
||||
start()
|
||||
}
|
||||
}
|
||||
|
||||
fun stop() {
|
||||
mediaPlayer.release()
|
||||
}
|
||||
|
||||
fun pause() {
|
||||
mediaPlayer.pause()
|
||||
}
|
||||
|
||||
fun resume() {
|
||||
mediaPlayer.start()
|
||||
}
|
||||
|
||||
fun isPlaying(): Boolean {
|
||||
return mediaPlayer.isPlaying
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
package fr.univpau.queezer.manager
|
||||
|
||||
import android.os.CountDownTimer
|
||||
|
||||
class CountdownManager (val duration: Long, val onFinish: () -> Unit) {
|
||||
|
||||
var timeLeft = duration;
|
||||
var interval = 1000L;
|
||||
var timer: CountDownTimer? = null
|
||||
|
||||
private fun create() {
|
||||
timer = object : CountDownTimer(duration, interval) {
|
||||
override fun onTick(millisUntilFinished: Long) {
|
||||
timeLeft = millisUntilFinished / 1000;
|
||||
}
|
||||
override fun onFinish() { onFinish() }
|
||||
}
|
||||
}
|
||||
|
||||
fun start() {
|
||||
create()
|
||||
timer?.start()
|
||||
}
|
||||
|
||||
fun stop() {
|
||||
timer?.cancel()
|
||||
timer = null
|
||||
}
|
||||
|
||||
fun restart() {
|
||||
stop()
|
||||
start()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
package fr.univpau.queezer.manager
|
||||
|
||||
import com.google.gson.Gson
|
||||
import com.google.gson.JsonObject
|
||||
import com.google.gson.reflect.TypeToken
|
||||
import fr.univpau.queezer.data.Settings
|
||||
import fr.univpau.queezer.data.Track
|
||||
import java.net.HttpURLConnection
|
||||
import java.net.URL
|
||||
|
||||
class GameManager(var settings: Settings) {
|
||||
|
||||
val audioManager = AudioManager()
|
||||
val countDownManager = CountdownManager(30000L, onFinish = ::nextTrack)
|
||||
|
||||
var tracks: List<Track> = mutableListOf()
|
||||
|
||||
var currentTrackIndex: Int = 0;
|
||||
var score = 0
|
||||
|
||||
suspend fun loadTracks() {
|
||||
val url = URL(settings.playlistUrl)
|
||||
val connection = url.openConnection() as HttpURLConnection
|
||||
connection.requestMethod = "GET"
|
||||
|
||||
if (connection.responseCode != HttpURLConnection.HTTP_OK) {
|
||||
throw Exception("Failed to load tracks")
|
||||
}
|
||||
|
||||
val response = connection.inputStream.bufferedReader().use { it.readText() }
|
||||
val json = Gson().fromJson(response, JsonObject::class.java)
|
||||
val tracksJson = json["tracks"].asJsonObject["data"].toString()
|
||||
|
||||
// Assurez-vous d'utiliser un TypeToken explicite pour une liste de Track
|
||||
tracks = Gson().fromJson(tracksJson, object : TypeToken<List<Track>>() {}.type)
|
||||
}
|
||||
|
||||
|
||||
fun nextTrack() {
|
||||
// Stop the current track
|
||||
audioManager.stop()
|
||||
|
||||
// Play the next track
|
||||
if (currentTrackIndex >= tracks.size - 1) {
|
||||
return
|
||||
}
|
||||
|
||||
currentTrackIndex++
|
||||
audioManager.play(getCurrentTrack().preview)
|
||||
|
||||
// Restart the countdown
|
||||
countDownManager.restart()
|
||||
}
|
||||
|
||||
fun getCurrentTrack(): Track {
|
||||
if (tracks.isEmpty()) {
|
||||
throw IllegalStateException("La liste des pistes est vide")
|
||||
}
|
||||
return tracks[currentTrackIndex]
|
||||
}
|
||||
|
||||
|
||||
fun start() {
|
||||
if (tracks.isEmpty()) {
|
||||
throw IllegalStateException("Aucune piste n'a été trouvée dans la playlist")
|
||||
}
|
||||
countDownManager.start()
|
||||
audioManager.play(tracks[currentTrackIndex].preview)
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
package fr.univpau.queezer.manager
|
||||
|
||||
import android.content.Context
|
||||
import com.google.gson.Gson
|
||||
import fr.univpau.queezer.data.Settings
|
||||
|
||||
fun saveSettings(context: Context, settings: Settings) {
|
||||
|
||||
val sharedPreferences = context.getSharedPreferences("app_preferences", Context.MODE_PRIVATE)
|
||||
val editor = sharedPreferences.edit()
|
||||
|
||||
// Sérialiser l'objet Settings en JSON
|
||||
val json = Gson().toJson(settings)
|
||||
|
||||
// Sauvegarder le JSON dans les SharedPreferences
|
||||
editor.putString("settings", json)
|
||||
editor.apply()
|
||||
}
|
||||
|
||||
fun loadSettings(context: Context): Settings {
|
||||
val sharedPreferences = context.getSharedPreferences("app_preferences", Context.MODE_PRIVATE)
|
||||
|
||||
// Récupérer le JSON depuis SharedPreferences
|
||||
val json = sharedPreferences.getString("settings", null)
|
||||
|
||||
// Si le JSON n'est pas null, le convertir en objet Settings
|
||||
return if (json != null) {
|
||||
Gson().fromJson(json, Settings::class.java)
|
||||
} else {
|
||||
Settings()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,104 @@
|
||||
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.Row
|
||||
import androidx.compose.foundation.layout.aspectRatio
|
||||
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.width
|
||||
import androidx.compose.foundation.text.KeyboardOptions
|
||||
import androidx.compose.material3.Button
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TextField
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.blur
|
||||
import androidx.compose.ui.layout.ContentScale
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.text.input.KeyboardType
|
||||
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.Settings
|
||||
import fr.univpau.queezer.manager.loadSettings
|
||||
import coil.compose.AsyncImage
|
||||
|
||||
@Composable
|
||||
fun GameScreen(navController: NavHostController) {
|
||||
val context = LocalContext.current
|
||||
val settings: Settings = loadSettings(context)
|
||||
|
||||
// val gameManager: GameManager = remember { GameManager(settings) }
|
||||
|
||||
// LaunchedEffect(gameManager) {
|
||||
// gameManager.loadTracks()
|
||||
// gameManager.start()
|
||||
// }
|
||||
|
||||
// État de l'utilisateur et des éléments du jeu
|
||||
val userInput = remember { mutableStateOf("") }
|
||||
|
||||
// Affichage de l'interface
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.padding(16.dp),
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
verticalArrangement = Arrangement.spacedBy(16.dp)
|
||||
) {
|
||||
|
||||
Text("Score : 0", fontSize = 24.sp)
|
||||
|
||||
Text("Temps restant : 30sec", fontSize = 20.sp)
|
||||
|
||||
// Affiche une image a partir d'une url
|
||||
AsyncImage(
|
||||
model = "https://api.deezer.com/album/382921287/image",
|
||||
contentDescription = "Image from URL",
|
||||
modifier = Modifier
|
||||
.width(200.dp)
|
||||
.height(200.dp)
|
||||
.blur(30.dp)
|
||||
,
|
||||
contentScale = ContentScale.Crop
|
||||
)
|
||||
|
||||
Text("Titre : Légende Vivante", fontSize = 20.sp)
|
||||
Text("Artiste : Lorenzo", fontSize = 20.sp)
|
||||
|
||||
// Champ de texte pour entrer la proposition
|
||||
TextField(
|
||||
value = userInput.value,
|
||||
onValueChange = { userInput.value = it },
|
||||
label = { Text("Titre / Artiste") },
|
||||
keyboardOptions = KeyboardOptions.Default.copy(keyboardType = KeyboardType.Text),
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
)
|
||||
|
||||
// Bouton Valider la réponse
|
||||
Button(
|
||||
onClick = {
|
||||
userInput.value = "" // Réinitialiser le champ de texte
|
||||
}
|
||||
) { Text(context.resources.getString(R.string.submit)) }
|
||||
|
||||
Row(modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceBetween) {
|
||||
Button(
|
||||
onClick = {
|
||||
userInput.value = "" // Réinitialiser le champ de texte
|
||||
},
|
||||
) { Text(context.resources.getString(R.string.skip)) }
|
||||
|
||||
Button(onClick = { navController.popBackStack() })
|
||||
{ Text(context.resources.getString(R.string.give_up)) }
|
||||
}
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
package fr.univpau.queezer
|
||||
package fr.univpau.queezer.screen
|
||||
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
+37
-26
@@ -1,5 +1,6 @@
|
||||
package fr.univpau.queezer
|
||||
package fr.univpau.queezer.screen
|
||||
|
||||
import android.widget.Toast
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
@@ -13,10 +14,8 @@ import androidx.compose.material3.RadioButton
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TextField
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
@@ -24,15 +23,16 @@ import androidx.compose.ui.text.input.KeyboardType
|
||||
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.GameMode
|
||||
import fr.univpau.queezer.manager.loadSettings
|
||||
import fr.univpau.queezer.manager.saveSettings
|
||||
import java.net.URL
|
||||
|
||||
@Composable
|
||||
fun SettingsScreen(navController: NavHostController) {
|
||||
val context = LocalContext.current
|
||||
val loadedSettings = loadSettings(context)
|
||||
|
||||
var selectedGameMode by remember { mutableStateOf(loadedSettings.gameMode) }
|
||||
var numberOfTitles by remember { mutableStateOf(loadedSettings.numberOfTitles) }
|
||||
var playlistUrl by remember { mutableStateOf(loadedSettings.playlistUrl) }
|
||||
val settings = remember { mutableStateOf(loadSettings(context)) }
|
||||
|
||||
Column(
|
||||
modifier = Modifier
|
||||
@@ -43,7 +43,7 @@ fun SettingsScreen(navController: NavHostController) {
|
||||
) {
|
||||
// Titre des paramètres
|
||||
Text(
|
||||
text = "Paramètres",
|
||||
text = context.resources.getString(R.string.settings),
|
||||
fontSize = 32.sp,
|
||||
modifier = Modifier.padding(bottom = 32.dp)
|
||||
)
|
||||
@@ -54,10 +54,12 @@ fun SettingsScreen(navController: NavHostController) {
|
||||
horizontalArrangement = Arrangement.SpaceBetween
|
||||
) {
|
||||
TextField(
|
||||
value = playlistUrl,
|
||||
onValueChange = { playlistUrl = it },
|
||||
label = { Text("URL de la playlist") },
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
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),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -67,11 +69,13 @@ fun SettingsScreen(navController: NavHostController) {
|
||||
horizontalArrangement = Arrangement.SpaceBetween
|
||||
) {
|
||||
TextField(
|
||||
value = numberOfTitles,
|
||||
onValueChange = { numberOfTitles = it },
|
||||
label = { Text("Nombre de titres dans une partie") },
|
||||
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),
|
||||
modifier = Modifier.fillMaxWidth(1f)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -82,16 +86,20 @@ fun SettingsScreen(navController: NavHostController) {
|
||||
) {
|
||||
Column {
|
||||
Text(text = "Mode de jeu", fontSize = 18.sp)
|
||||
gameModes.forEach { option ->
|
||||
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 = selectedGameMode == option,
|
||||
onClick = { selectedGameMode = option }
|
||||
selected = settings.value.gameMode.ordinal == index,
|
||||
onClick = {
|
||||
settings.value =
|
||||
settings.value.copy(gameMode = GameMode.entries[index])
|
||||
}
|
||||
)
|
||||
Text(text = option)
|
||||
Text(text = label)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -104,17 +112,20 @@ fun SettingsScreen(navController: NavHostController) {
|
||||
) {
|
||||
OutlinedButton(
|
||||
onClick = { navController.popBackStack() },
|
||||
) {
|
||||
Text("Retour")
|
||||
}
|
||||
) { Text(context.resources.getString(R.string.back)) }
|
||||
|
||||
Button(
|
||||
onClick = {
|
||||
saveSettings(context, selectedGameMode, numberOfTitles, playlistUrl)
|
||||
try {
|
||||
settings.value.validate(context)
|
||||
saveSettings(context, settings.value)
|
||||
navController.popBackStack()
|
||||
} catch (e: Exception) {
|
||||
Toast.makeText(context, e.message, Toast.LENGTH_LONG).show()
|
||||
}
|
||||
},
|
||||
) {
|
||||
Text("Valider")
|
||||
Text(context.resources.getString(R.string.submit))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,3 +1,32 @@
|
||||
<resources>
|
||||
<string name="app_name">Queezer</string>
|
||||
|
||||
<string name="quick_play">Partie rapide</string>
|
||||
<string name="custom_play">Partie personnalisée</string>
|
||||
<string name="settings">Paramètre</string>
|
||||
<string name="score">Score</string>
|
||||
|
||||
<!-- Settings -->
|
||||
<string name="playlist_url_label">URL de la playlist</string>
|
||||
<string name="playlist_url_hint">https://api.deezer.com/playlist/...</string>
|
||||
<string name="error_playlist_url_empty">L\'URL de la playlist ne peut pas être vide</string>
|
||||
<string name="error_playlist_url_invalid">L\'URL de la playlist est invalide</string>
|
||||
|
||||
<string name="tracks_count_label">Nombre de titres dans une partie</string>
|
||||
<string name="tracks_count_hint">10</string>
|
||||
<string name="error_tracks_count_empty">Le nombre de titres ne peut pas être vide</string>
|
||||
<string name="error_tracks_count_negative">Le nombre de titres doit être supérieur à 0</string>
|
||||
|
||||
<string name="game_mode_label">Mode de jeu</string>
|
||||
<string-array name="game_modes">
|
||||
<item>Titres</item>
|
||||
<item>Artistes</item>
|
||||
<item>Tout</item>
|
||||
</string-array>
|
||||
|
||||
<string name="back">Retour</string>
|
||||
<string name="skip">Passer</string>
|
||||
<string name="give_up">Abandonner</string>
|
||||
<string name="submit">Valider</string>
|
||||
|
||||
</resources>
|
||||
Reference in New Issue
Block a user