commit 5a592f3e434a1c9455a076043405cd05479a41f5 Author: Lucàs <86352901+LucasVbr@users.noreply.github.com> Date: Thu Jan 2 14:44:39 2025 +0100 Initial commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..aa724b7 --- /dev/null +++ b/.gitignore @@ -0,0 +1,15 @@ +*.iml +.gradle +/local.properties +/.idea/caches +/.idea/libraries +/.idea/modules.xml +/.idea/workspace.xml +/.idea/navEditor.xml +/.idea/assetWizardSettings.xml +.DS_Store +/build +/captures +.externalNativeBuild +.cxx +local.properties diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..26d3352 --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,3 @@ +# Default ignored files +/shelf/ +/workspace.xml diff --git a/.idea/compiler.xml b/.idea/compiler.xml new file mode 100644 index 0000000..b86273d --- /dev/null +++ b/.idea/compiler.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/.idea/deploymentTargetSelector.xml b/.idea/deploymentTargetSelector.xml new file mode 100644 index 0000000..d603dda --- /dev/null +++ b/.idea/deploymentTargetSelector.xml @@ -0,0 +1,16 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/gradle.xml b/.idea/gradle.xml new file mode 100644 index 0000000..7b3006b --- /dev/null +++ b/.idea/gradle.xml @@ -0,0 +1,20 @@ + + + + + + + \ No newline at end of file diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml new file mode 100644 index 0000000..cde3e19 --- /dev/null +++ b/.idea/inspectionProfiles/Project_Default.xml @@ -0,0 +1,57 @@ + + + + \ No newline at end of file diff --git a/.idea/kotlinc.xml b/.idea/kotlinc.xml new file mode 100644 index 0000000..6d0ee1c --- /dev/null +++ b/.idea/kotlinc.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/.idea/migrations.xml b/.idea/migrations.xml new file mode 100644 index 0000000..f8051a6 --- /dev/null +++ b/.idea/migrations.xml @@ -0,0 +1,10 @@ + + + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 0000000..74dd639 --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,10 @@ + + + + + + + + + \ No newline at end of file diff --git a/.idea/runConfigurations.xml b/.idea/runConfigurations.xml new file mode 100644 index 0000000..16660f1 --- /dev/null +++ b/.idea/runConfigurations.xml @@ -0,0 +1,17 @@ + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..d843f34 --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..650487e --- /dev/null +++ b/README.md @@ -0,0 +1,21 @@ +# Queezer + +## Roadmap + +- [X] Page d'accueil +- [X] Page des Paramètres + - [X] URL de l'API + - [X] Nombre de questions + - [X] Mode de jeu + - [X] Annuler les modifications + - [X] Sauvegarder les modifications +- [ ] Partie Rapide + - [ ] Lancer une musique + - [ ] Prendre en compte les réponses + - [ ] Afficher les scores + - [ ] Boucle de jeu, toutes les 30 secondes change de titre +- [ ] Partie Personnalisée + - [ ] Afficher une page de paramètres +- [ ] Page des Scores + +- [ ] Désactiver la rotation de l'écran diff --git a/app/.gitignore b/app/.gitignore new file mode 100644 index 0000000..42afabf --- /dev/null +++ b/app/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/app/build.gradle.kts b/app/build.gradle.kts new file mode 100644 index 0000000..7e97d2a --- /dev/null +++ b/app/build.gradle.kts @@ -0,0 +1,61 @@ +plugins { + alias(libs.plugins.android.application) + alias(libs.plugins.kotlin.android) + alias(libs.plugins.kotlin.compose) +} + +android { + namespace = "fr.univpau.queezer" + compileSdk = 34 + + defaultConfig { + applicationId = "fr.univpau.queezer" + minSdk = 29 + targetSdk = 34 + versionCode = 1 + versionName = "1.0" + + testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" + } + + buildTypes { + release { + isMinifyEnabled = false + proguardFiles( + getDefaultProguardFile("proguard-android-optimize.txt"), + "proguard-rules.pro" + ) + } + } + compileOptions { + sourceCompatibility = JavaVersion.VERSION_11 + targetCompatibility = JavaVersion.VERSION_11 + } + kotlinOptions { + jvmTarget = "11" + } + buildFeatures { + compose = true + } +} + +dependencies { + implementation(libs.androidx.core.ktx) + implementation(libs.androidx.lifecycle.runtime.ktx) + implementation(libs.androidx.activity.compose) + implementation(platform(libs.androidx.compose.bom)) + implementation(libs.androidx.ui) + implementation(libs.androidx.ui.graphics) + implementation(libs.androidx.ui.tooling.preview) + implementation(libs.androidx.material3) + implementation(libs.androidx.navigation.compose) + implementation(libs.retrofit) + implementation(libs.converter.gson) + testImplementation(libs.junit) + androidTestImplementation(libs.androidx.junit) + androidTestImplementation(libs.androidx.espresso.core) + androidTestImplementation(platform(libs.androidx.compose.bom)) + androidTestImplementation(libs.androidx.ui.test.junit4) + debugImplementation(libs.androidx.ui.tooling) + debugImplementation(libs.androidx.ui.test.manifest) +} \ No newline at end of file diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro new file mode 100644 index 0000000..481bb43 --- /dev/null +++ b/app/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile \ No newline at end of file diff --git a/app/src/androidTest/java/fr/univpau/queezer/ExampleInstrumentedTest.kt b/app/src/androidTest/java/fr/univpau/queezer/ExampleInstrumentedTest.kt new file mode 100644 index 0000000..6eb7681 --- /dev/null +++ b/app/src/androidTest/java/fr/univpau/queezer/ExampleInstrumentedTest.kt @@ -0,0 +1,24 @@ +package fr.univpau.queezer + +import androidx.test.platform.app.InstrumentationRegistry +import androidx.test.ext.junit.runners.AndroidJUnit4 + +import org.junit.Test +import org.junit.runner.RunWith + +import org.junit.Assert.* + +/** + * Instrumented test, which will execute on an Android device. + * + * See [testing documentation](http://d.android.com/tools/testing). + */ +@RunWith(AndroidJUnit4::class) +class ExampleInstrumentedTest { + @Test + fun useAppContext() { + // Context of the app under test. + val appContext = InstrumentationRegistry.getInstrumentation().targetContext + assertEquals("fr.univpau.queezer", appContext.packageName) + } +} \ No newline at end of file diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml new file mode 100644 index 0000000..31b2726 --- /dev/null +++ b/app/src/main/AndroidManifest.xml @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/java/fr/univpau/queezer/Fetcher.kt b/app/src/main/java/fr/univpau/queezer/Fetcher.kt new file mode 100644 index 0000000..52635f0 --- /dev/null +++ b/app/src/main/java/fr/univpau/queezer/Fetcher.kt @@ -0,0 +1,61 @@ +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): MutableState> { + 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>() {}.type + Gson().fromJson>(tracksJson, trackListType) + } else { + null + } + } catch (e: Exception) { + e.printStackTrace() + null + } + } +} + +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) + } +} diff --git a/app/src/main/java/fr/univpau/queezer/GameScreen.kt b/app/src/main/java/fr/univpau/queezer/GameScreen.kt new file mode 100644 index 0000000..8225865 --- /dev/null +++ b/app/src/main/java/fr/univpau/queezer/GameScreen.kt @@ -0,0 +1,148 @@ +package fr.univpau.queezer + +import android.os.CountDownTimer +import android.util.Log +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.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.DisposableEffect +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableIntStateOf +import androidx.compose.runtime.mutableLongStateOf +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.graphics.painter.Painter +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.text.input.KeyboardType +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import androidx.navigation.NavHostController +import androidx.navigation.compose.rememberNavController +import androidx.compose.ui.res.painterResource +import kotlinx.coroutines.runBlocking + +@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 = remember { mutableStateOf(emptyList()) } + + LaunchedEffect(playlistUrl) { + tracks = fetchTracks(playlistUrl) + } + + 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") + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/fr/univpau/queezer/HomeScreen.kt b/app/src/main/java/fr/univpau/queezer/HomeScreen.kt new file mode 100644 index 0000000..a518b12 --- /dev/null +++ b/app/src/main/java/fr/univpau/queezer/HomeScreen.kt @@ -0,0 +1,90 @@ +package fr.univpau.queezer + +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Spacer +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.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.text.font.FontWeight +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import androidx.navigation.NavHostController +import fr.univpau.queezer.ui.theme.Purple40 + +@Composable +fun HomeScreen(navController: NavHostController) { + Column( + modifier = Modifier + .fillMaxSize() + .padding(16.dp), + 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 +// ) + Text( + text = "Queezer", + fontSize = 32.sp, + fontWeight = FontWeight.Bold, + color = Purple40 + ) + + Spacer(modifier = Modifier.height(24.dp)) + + // Boutons + Button( + onClick = { navController.navigate("game") }, + shape = RoundedCornerShape(8.dp), + modifier = Modifier + .fillMaxWidth() + .padding(vertical = 8.dp) + ) { + Text("Partie rapide") + } + + Button( + onClick = { /* TODO: Partie personnalisée */ }, + shape = RoundedCornerShape(8.dp), + modifier = Modifier + .fillMaxWidth() + .padding(vertical = 8.dp) + ) { + Text("Partie personnalisée") + } + + Button( + onClick = { navController.navigate("settings") }, + shape = RoundedCornerShape(8.dp), + modifier = Modifier + .fillMaxWidth() + .padding(vertical = 8.dp) + ) { + Text("Paramètres") + } + + Button( + onClick = { /* TODO: Scores */ }, + shape = RoundedCornerShape(8.dp), + modifier = Modifier + .fillMaxWidth() + .padding(vertical = 8.dp) + ) { + Text("Scores") + } + } +} \ No newline at end of file diff --git a/app/src/main/java/fr/univpau/queezer/MainActivity.kt b/app/src/main/java/fr/univpau/queezer/MainActivity.kt new file mode 100644 index 0000000..034d5b7 --- /dev/null +++ b/app/src/main/java/fr/univpau/queezer/MainActivity.kt @@ -0,0 +1,40 @@ +package fr.univpau.queezer + +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 + +class MainActivity : ComponentActivity() { + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + + setContent { + QueezerApp() + } + } +} + +@Composable +fun QueezerApp() { + val navController = rememberNavController() + + NavHost(navController = navController, startDestination = "home") { + composable("home") { + HomeScreen(navController) + } + composable("game") { + GameScreen(navController) + } + composable("settings") { + SettingsScreen(navController) + } + } +} \ No newline at end of file diff --git a/app/src/main/java/fr/univpau/queezer/SettingsScreen.kt b/app/src/main/java/fr/univpau/queezer/SettingsScreen.kt new file mode 100644 index 0000000..fb1ee36 --- /dev/null +++ b/app/src/main/java/fr/univpau/queezer/SettingsScreen.kt @@ -0,0 +1,121 @@ +package fr.univpau.queezer + +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.OutlinedButton +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 +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 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) } + + Column( + modifier = Modifier + .fillMaxSize() + .padding(16.dp), + horizontalAlignment = Alignment.CenterHorizontally, + verticalArrangement = Arrangement.spacedBy(16.dp) + ) { + // Titre des paramètres + Text( + text = "Paramètres", + fontSize = 32.sp, + modifier = Modifier.padding(bottom = 32.dp) + ) + + // Paramètre : Playlist à utiliser + Row( + modifier = Modifier.fillMaxWidth(), + horizontalArrangement = Arrangement.SpaceBetween + ) { + TextField( + value = playlistUrl, + onValueChange = { playlistUrl = it }, + label = { Text("URL de la playlist") }, + modifier = Modifier.fillMaxWidth() + ) + } + + // Paramètre : Nombre de titres dans une partie + Row( + modifier = Modifier.fillMaxWidth(), + horizontalArrangement = Arrangement.SpaceBetween + ) { + TextField( + value = numberOfTitles, + onValueChange = { numberOfTitles = it }, + label = { Text("Nombre de titres dans une partie") }, + keyboardOptions = KeyboardOptions.Default.copy(keyboardType = KeyboardType.Number), + modifier = Modifier.fillMaxWidth(1f) + ) + } + + // Paramètre : Mode de jeu + Row( + modifier = Modifier.fillMaxWidth(), + horizontalArrangement = Arrangement.SpaceBetween + ) { + Column { + Text(text = "Mode de jeu", fontSize = 18.sp) + gameModes.forEach { option -> + Row( + verticalAlignment = Alignment.CenterVertically, + modifier = Modifier.padding(end = 16.dp) + ) { + RadioButton( + selected = selectedGameMode == option, + onClick = { selectedGameMode = option } + ) + Text(text = option) + } + } + } + } + + // Bouton pour revenir à l'écran d'accueil + Row( + modifier = Modifier.fillMaxWidth(), + horizontalArrangement = Arrangement.SpaceBetween + ) { + OutlinedButton( + onClick = { navController.popBackStack() }, + ) { + Text("Retour") + } + + Button( + onClick = { + saveSettings(context, selectedGameMode, numberOfTitles, playlistUrl) + navController.popBackStack() + }, + ) { + Text("Valider") + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/fr/univpau/queezer/SharedPreferences.kt b/app/src/main/java/fr/univpau/queezer/SharedPreferences.kt new file mode 100644 index 0000000..80b46ed --- /dev/null +++ b/app/src/main/java/fr/univpau/queezer/SharedPreferences.kt @@ -0,0 +1,28 @@ +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) diff --git a/app/src/main/java/fr/univpau/queezer/ui/theme/Color.kt b/app/src/main/java/fr/univpau/queezer/ui/theme/Color.kt new file mode 100644 index 0000000..a5d3da2 --- /dev/null +++ b/app/src/main/java/fr/univpau/queezer/ui/theme/Color.kt @@ -0,0 +1,11 @@ +package fr.univpau.queezer.ui.theme + +import androidx.compose.ui.graphics.Color + +val Purple80 = Color(0xFFD0BCFF) +val PurpleGrey80 = Color(0xFFCCC2DC) +val Pink80 = Color(0xFFEFB8C8) + +val Purple40 = Color(0xFF6650a4) +val PurpleGrey40 = Color(0xFF625b71) +val Pink40 = Color(0xFF7D5260) \ No newline at end of file diff --git a/app/src/main/java/fr/univpau/queezer/ui/theme/Theme.kt b/app/src/main/java/fr/univpau/queezer/ui/theme/Theme.kt new file mode 100644 index 0000000..c4a70bc --- /dev/null +++ b/app/src/main/java/fr/univpau/queezer/ui/theme/Theme.kt @@ -0,0 +1,58 @@ +package fr.univpau.queezer.ui.theme + +import android.app.Activity +import android.os.Build +import androidx.compose.foundation.isSystemInDarkTheme +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.darkColorScheme +import androidx.compose.material3.dynamicDarkColorScheme +import androidx.compose.material3.dynamicLightColorScheme +import androidx.compose.material3.lightColorScheme +import androidx.compose.runtime.Composable +import androidx.compose.ui.platform.LocalContext + +private val DarkColorScheme = darkColorScheme( + primary = Purple80, + secondary = PurpleGrey80, + tertiary = Pink80 +) + +private val LightColorScheme = lightColorScheme( + primary = Purple40, + secondary = PurpleGrey40, + tertiary = Pink40 + + /* Other default colors to override + background = Color(0xFFFFFBFE), + surface = Color(0xFFFFFBFE), + onPrimary = Color.White, + onSecondary = Color.White, + onTertiary = Color.White, + onBackground = Color(0xFF1C1B1F), + onSurface = Color(0xFF1C1B1F), + */ +) + +@Composable +fun QueezerTheme( + darkTheme: Boolean = isSystemInDarkTheme(), + // Dynamic color is available on Android 12+ + dynamicColor: Boolean = true, + content: @Composable () -> Unit +) { + val colorScheme = when { + dynamicColor && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> { + val context = LocalContext.current + if (darkTheme) dynamicDarkColorScheme(context) else dynamicLightColorScheme(context) + } + + darkTheme -> DarkColorScheme + else -> LightColorScheme + } + + MaterialTheme( + colorScheme = colorScheme, + typography = Typography, + content = content + ) +} \ No newline at end of file diff --git a/app/src/main/java/fr/univpau/queezer/ui/theme/Type.kt b/app/src/main/java/fr/univpau/queezer/ui/theme/Type.kt new file mode 100644 index 0000000..aac8943 --- /dev/null +++ b/app/src/main/java/fr/univpau/queezer/ui/theme/Type.kt @@ -0,0 +1,34 @@ +package fr.univpau.queezer.ui.theme + +import androidx.compose.material3.Typography +import androidx.compose.ui.text.TextStyle +import androidx.compose.ui.text.font.FontFamily +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.unit.sp + +// Set of Material typography styles to start with +val Typography = Typography( + bodyLarge = TextStyle( + fontFamily = FontFamily.Default, + fontWeight = FontWeight.Normal, + fontSize = 16.sp, + lineHeight = 24.sp, + letterSpacing = 0.5.sp + ) + /* Other default text styles to override + titleLarge = TextStyle( + fontFamily = FontFamily.Default, + fontWeight = FontWeight.Normal, + fontSize = 22.sp, + lineHeight = 28.sp, + letterSpacing = 0.sp + ), + labelSmall = TextStyle( + fontFamily = FontFamily.Default, + fontWeight = FontWeight.Medium, + fontSize = 11.sp, + lineHeight = 16.sp, + letterSpacing = 0.5.sp + ) + */ +) \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_launcher_background.xml b/app/src/main/res/drawable/ic_launcher_background.xml new file mode 100644 index 0000000..07d5da9 --- /dev/null +++ b/app/src/main/res/drawable/ic_launcher_background.xml @@ -0,0 +1,170 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/drawable/ic_launcher_foreground.xml b/app/src/main/res/drawable/ic_launcher_foreground.xml new file mode 100644 index 0000000..2b068d1 --- /dev/null +++ b/app/src/main/res/drawable/ic_launcher_foreground.xml @@ -0,0 +1,30 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/mipmap-anydpi/ic_launcher.xml b/app/src/main/res/mipmap-anydpi/ic_launcher.xml new file mode 100644 index 0000000..6f3b755 --- /dev/null +++ b/app/src/main/res/mipmap-anydpi/ic_launcher.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/mipmap-anydpi/ic_launcher_round.xml b/app/src/main/res/mipmap-anydpi/ic_launcher_round.xml new file mode 100644 index 0000000..6f3b755 --- /dev/null +++ b/app/src/main/res/mipmap-anydpi/ic_launcher_round.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher.webp b/app/src/main/res/mipmap-hdpi/ic_launcher.webp new file mode 100644 index 0000000..c209e78 Binary files /dev/null and b/app/src/main/res/mipmap-hdpi/ic_launcher.webp differ diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp b/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp new file mode 100644 index 0000000..b2dfe3d Binary files /dev/null and b/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp differ diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher.webp b/app/src/main/res/mipmap-mdpi/ic_launcher.webp new file mode 100644 index 0000000..4f0f1d6 Binary files /dev/null and b/app/src/main/res/mipmap-mdpi/ic_launcher.webp differ diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp b/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp new file mode 100644 index 0000000..62b611d Binary files /dev/null and b/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp differ diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher.webp b/app/src/main/res/mipmap-xhdpi/ic_launcher.webp new file mode 100644 index 0000000..948a307 Binary files /dev/null and b/app/src/main/res/mipmap-xhdpi/ic_launcher.webp differ diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp b/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp new file mode 100644 index 0000000..1b9a695 Binary files /dev/null and b/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp differ diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp b/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp new file mode 100644 index 0000000..28d4b77 Binary files /dev/null and b/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp differ diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp b/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp new file mode 100644 index 0000000..9287f50 Binary files /dev/null and b/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp differ diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp b/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp new file mode 100644 index 0000000..aa7d642 Binary files /dev/null and b/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp differ diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp new file mode 100644 index 0000000..9126ae3 Binary files /dev/null and b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp differ diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml new file mode 100644 index 0000000..f8c6127 --- /dev/null +++ b/app/src/main/res/values/colors.xml @@ -0,0 +1,10 @@ + + + #FFBB86FC + #FF6200EE + #FF3700B3 + #FF03DAC5 + #FF018786 + #FF000000 + #FFFFFFFF + \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml new file mode 100644 index 0000000..61e9015 --- /dev/null +++ b/app/src/main/res/values/strings.xml @@ -0,0 +1,3 @@ + + Queezer + \ No newline at end of file diff --git a/app/src/main/res/values/themes.xml b/app/src/main/res/values/themes.xml new file mode 100644 index 0000000..69a036b --- /dev/null +++ b/app/src/main/res/values/themes.xml @@ -0,0 +1,5 @@ + + + +