mirror of
https://github.com/LucasVbr/Queezer.git
synced 2026-05-13 17:11:55 +00:00
fix: Custom Party + Success rate
This commit is contained in:
@@ -46,6 +46,7 @@ apply(
|
||||
|
||||
dependencies {
|
||||
implementation(libs.androidx.runtime.livedata)
|
||||
implementation(libs.material)
|
||||
val roomVersion = "2.5.2"
|
||||
|
||||
|
||||
|
||||
@@ -1,23 +1,32 @@
|
||||
package fr.univpau.queezer.data
|
||||
|
||||
import android.util.Log
|
||||
|
||||
data class Filter(
|
||||
val date: DateFilter = DateFilter.DESCENDING,
|
||||
val mode : List<GameMode> = listOf(GameMode.TITLE, GameMode.ARTIST, GameMode.ALL),
|
||||
val nbTitle : Int? = null,
|
||||
var dateOrderIsAscending: Boolean = true,
|
||||
|
||||
val mode : Map<GameMode, Boolean> = mapOf(
|
||||
GameMode.TITLE to true,
|
||||
GameMode.ARTIST to true,
|
||||
GameMode.ALL to true
|
||||
),
|
||||
|
||||
val nbTitleIsAscending : Boolean = true,
|
||||
)
|
||||
|
||||
fun filterGames(filter: Filter, games: List<Game>): List<Game> {
|
||||
return games.filter { game ->
|
||||
filter.mode.contains(game.settings.gameMode) && (filter.nbTitle == null || game.playlist.tracks.size == filter.nbTitle)
|
||||
}.sortedBy { game ->
|
||||
when (filter.date) {
|
||||
DateFilter.ASCENDING -> game.date.time
|
||||
DateFilter.DESCENDING -> -game.date.time
|
||||
}
|
||||
}
|
||||
}
|
||||
Log.i("Filter", "Filtering games with $filter")
|
||||
|
||||
enum class DateFilter {
|
||||
ASCENDING,
|
||||
DESCENDING
|
||||
return games
|
||||
.filter { game ->
|
||||
// Filtrer uniquement par les modes de jeu activés
|
||||
filter.mode.filter { it.value }.keys.contains(game.settings.gameMode)
|
||||
}
|
||||
.sortedWith(compareBy<Game> { game ->
|
||||
// Tri par date
|
||||
if (filter.dateOrderIsAscending) -game.date.time else game.date.time
|
||||
}.thenBy { game ->
|
||||
// Tri par nombre de titres
|
||||
if (filter.nbTitleIsAscending) game.settings.numberOfTitles ?: 0 else -(game.settings.numberOfTitles ?: 0)
|
||||
})
|
||||
}
|
||||
@@ -11,4 +11,4 @@ data class Game(
|
||||
val playlist: Playlist = Playlist(),
|
||||
val score: Int = 0,
|
||||
val date: Date = Date()
|
||||
)
|
||||
)
|
||||
@@ -1,10 +1,13 @@
|
||||
package fr.univpau.queezer.view.components
|
||||
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.material3.Badge
|
||||
import androidx.compose.material3.BadgedBox
|
||||
import androidx.compose.material3.Card
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
@@ -15,9 +18,11 @@ import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import fr.univpau.queezer.R
|
||||
import fr.univpau.queezer.data.Game
|
||||
import fr.univpau.queezer.data.GameMode
|
||||
import java.text.SimpleDateFormat
|
||||
@@ -26,7 +31,8 @@ import java.util.Locale
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
fun GameCardItem(game: Game) {
|
||||
val formatter = SimpleDateFormat("dd MMMM yyyy", Locale.getDefault())
|
||||
val context = LocalContext.current
|
||||
val formatter = SimpleDateFormat("dd MMMM yyyy - HH:mm", Locale.getDefault())
|
||||
val showBottomSheet = remember { mutableStateOf(false) }
|
||||
val coroutineScope = rememberCoroutineScope()
|
||||
|
||||
@@ -36,6 +42,8 @@ fun GameCardItem(game: Game) {
|
||||
(game.settings.numberOfTitles ?: 1)
|
||||
}
|
||||
|
||||
val gameModesLabels = context.resources.getStringArray(R.array.game_modes)
|
||||
|
||||
Card(
|
||||
onClick = { showBottomSheet.value = true },
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
@@ -43,14 +51,29 @@ fun GameCardItem(game: Game) {
|
||||
Column(
|
||||
modifier = Modifier.padding(16.dp)
|
||||
) {
|
||||
Text(
|
||||
text = game.playlist.title,
|
||||
fontSize = 14.sp,
|
||||
fontWeight = FontWeight.SemiBold
|
||||
)
|
||||
Text(text = formatter.format(game.date))
|
||||
Row(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
horizontalArrangement = Arrangement.SpaceBetween
|
||||
) {
|
||||
Text(
|
||||
text = game.playlist.title,
|
||||
fontSize = 14.sp,
|
||||
fontWeight = FontWeight.SemiBold
|
||||
)
|
||||
|
||||
Text(text = "${game.score}/$maxScore")
|
||||
Badge(
|
||||
containerColor = MaterialTheme.colorScheme.primary,
|
||||
content = { Text("${game.score}/${maxScore}") }
|
||||
)
|
||||
}
|
||||
|
||||
Row(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
horizontalArrangement = Arrangement.SpaceBetween
|
||||
) {
|
||||
Text(text = formatter.format(game.date))
|
||||
Text(text = "Mode: ${gameModesLabels[game.settings.gameMode.ordinal]}")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -76,14 +99,24 @@ fun GameCardItem(game: Game) {
|
||||
text = game.playlist.title,
|
||||
style = MaterialTheme.typography.titleMedium
|
||||
)
|
||||
Text(
|
||||
text = "${game.score}/$maxScore",
|
||||
style = MaterialTheme.typography.titleMedium
|
||||
Badge(
|
||||
containerColor = MaterialTheme.colorScheme.primary,
|
||||
content = {
|
||||
Text(
|
||||
text = "${game.score}/${maxScore}pts",
|
||||
style = MaterialTheme.typography.titleMedium
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
Text(
|
||||
text = formatter.format(game.date),
|
||||
)
|
||||
|
||||
Row(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
horizontalArrangement = Arrangement.SpaceBetween
|
||||
) {
|
||||
Text(formatter.format(game.date))
|
||||
Text("Mode: ${gameModesLabels[game.settings.gameMode.ordinal]}")
|
||||
}
|
||||
|
||||
TrackCardItemList(game.playlist.tracks)
|
||||
}
|
||||
|
||||
@@ -3,13 +3,21 @@ package fr.univpau.queezer.view.screens
|
||||
import android.util.Log
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.ExperimentalLayoutApi
|
||||
import androidx.compose.foundation.layout.FlowRow
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.automirrored.filled.ArrowBack
|
||||
import androidx.compose.material.icons.filled.Check
|
||||
import androidx.compose.material.icons.filled.KeyboardArrowDown
|
||||
import androidx.compose.material.icons.filled.KeyboardArrowUp
|
||||
import androidx.compose.material3.CenterAlignedTopAppBar
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.FilterChip
|
||||
import androidx.compose.material3.FilterChipDefaults
|
||||
import androidx.compose.material3.HorizontalDivider
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.IconButton
|
||||
@@ -18,9 +26,12 @@ import androidx.compose.material3.Scaffold
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TopAppBarDefaults
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.derivedStateOf
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.livedata.observeAsState
|
||||
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
|
||||
@@ -31,22 +42,23 @@ import androidx.navigation.NavHostController
|
||||
import fr.univpau.queezer.R
|
||||
import fr.univpau.queezer.data.Filter
|
||||
import fr.univpau.queezer.data.Game
|
||||
import fr.univpau.queezer.data.GameMode
|
||||
import fr.univpau.queezer.data.filterGames
|
||||
import fr.univpau.queezer.view.components.GameCardItemList
|
||||
import fr.univpau.queezer.viewmodel.GameViewModel
|
||||
import kotlin.math.max
|
||||
import java.util.Locale
|
||||
import kotlin.math.round
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@OptIn(ExperimentalMaterial3Api::class, ExperimentalLayoutApi::class)
|
||||
@Composable
|
||||
fun ScoreScreen(navController: NavHostController, gameViewModel: GameViewModel) {
|
||||
val context = LocalContext.current
|
||||
val filter = remember { mutableStateOf(Filter()) }
|
||||
var filter by remember { mutableStateOf(Filter()) }
|
||||
val games: List<Game> = gameViewModel.games.observeAsState().value ?: emptyList()
|
||||
|
||||
val nbGames = games.size;
|
||||
val averageSuccessRate = games.sumOf { it.score }.div(max(games.size, 1))
|
||||
|
||||
val filteredGames = filterGames(filter.value, games)
|
||||
val filteredGames by remember(filter, games) { derivedStateOf { filterGames(filter, games) } }
|
||||
val averageSuccessRate by remember(filteredGames) { derivedStateOf { calculateAverageSuccessRate(filteredGames) } }
|
||||
val nbGames by remember(filteredGames) { derivedStateOf { filteredGames.size } }
|
||||
|
||||
Scaffold(
|
||||
topBar = {
|
||||
@@ -89,7 +101,7 @@ fun ScoreScreen(navController: NavHostController, gameViewModel: GameViewModel)
|
||||
Text(text = "Parties jouées", fontSize = 14.sp)
|
||||
}
|
||||
Column(horizontalAlignment = Alignment.CenterHorizontally) {
|
||||
Text(text = "$averageSuccessRate%", fontSize = 24.sp, fontWeight = androidx.compose.ui.text.font.FontWeight.Bold)
|
||||
Text(text = "${String.format(Locale.getDefault(), "%.02f", averageSuccessRate)}%", fontSize = 24.sp, fontWeight = androidx.compose.ui.text.font.FontWeight.Bold)
|
||||
Text(text = "De réussite", fontSize = 14.sp)
|
||||
}
|
||||
}
|
||||
@@ -98,6 +110,89 @@ fun ScoreScreen(navController: NavHostController, gameViewModel: GameViewModel)
|
||||
|
||||
// Todo add filters
|
||||
|
||||
FlowRow(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp),
|
||||
horizontalArrangement = Arrangement.spacedBy(8.dp),
|
||||
) {
|
||||
FilterChip(
|
||||
selected = true,
|
||||
onClick = {
|
||||
// Inverser l'ordre de tri
|
||||
filter = filter.copy(dateOrderIsAscending = !filter.dateOrderIsAscending)
|
||||
},
|
||||
label = { Text("Date") },
|
||||
leadingIcon = {
|
||||
if (filter.dateOrderIsAscending) {
|
||||
Icon(
|
||||
imageVector = Icons.Filled.KeyboardArrowUp,
|
||||
contentDescription = "Tri Ascendant",
|
||||
modifier = Modifier.size(FilterChipDefaults.IconSize)
|
||||
)
|
||||
} else {
|
||||
Icon(
|
||||
imageVector = Icons.Filled.KeyboardArrowDown,
|
||||
contentDescription = "Tri Descendant",
|
||||
modifier = Modifier.size(FilterChipDefaults.IconSize)
|
||||
)
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
val gameModesLabels = context.resources.getStringArray(R.array.game_modes)
|
||||
filter.mode.entries.forEachIndexed { index, entry ->
|
||||
FilterChip(
|
||||
selected = entry.value,
|
||||
onClick = {
|
||||
filter = filter.copy(mode = filter.mode.toMutableMap().apply {
|
||||
this[entry.key] = !entry.value
|
||||
})
|
||||
},
|
||||
|
||||
leadingIcon = {
|
||||
if (entry.value) {
|
||||
Icon(
|
||||
imageVector = Icons.Filled.Check,
|
||||
contentDescription = "Filtre activé",
|
||||
modifier = Modifier.size(FilterChipDefaults.IconSize)
|
||||
)
|
||||
}
|
||||
},
|
||||
|
||||
label = { Text(
|
||||
gameModesLabels[index],
|
||||
maxLines = 1,
|
||||
) },
|
||||
)
|
||||
}
|
||||
|
||||
// Filtrer par nombre de titres
|
||||
FilterChip(
|
||||
selected = true,
|
||||
onClick = {
|
||||
// Inverser l'ordre de tri
|
||||
filter = filter.copy(nbTitleIsAscending = !filter.nbTitleIsAscending)
|
||||
},
|
||||
label = { Text("Nombre de titres") },
|
||||
leadingIcon = {
|
||||
if (filter.nbTitleIsAscending) {
|
||||
Icon(
|
||||
imageVector = Icons.Filled.KeyboardArrowUp,
|
||||
contentDescription = "Tri Ascendant",
|
||||
modifier = Modifier.size(FilterChipDefaults.IconSize)
|
||||
)
|
||||
} else {
|
||||
Icon(
|
||||
imageVector = Icons.Filled.KeyboardArrowDown,
|
||||
contentDescription = "Tri Descendant",
|
||||
modifier = Modifier.size(FilterChipDefaults.IconSize)
|
||||
)
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
if (filteredGames.isEmpty()) {
|
||||
Column (
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
@@ -110,4 +205,18 @@ fun ScoreScreen(navController: NavHostController, gameViewModel: GameViewModel)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun calculateAverageSuccessRate(games: List<Game>): Double {
|
||||
if (games.isEmpty()) return 0.0
|
||||
|
||||
val averageGameScore = games.sumOf {
|
||||
if (it.settings.gameMode == GameMode.ALL) {
|
||||
it.score.toDouble() / (it.settings.numberOfTitles!! * 2)
|
||||
} else {
|
||||
it.score.toDouble() / (it.settings.numberOfTitles ?: 1)
|
||||
}
|
||||
}
|
||||
|
||||
return (averageGameScore / games.size) * 100
|
||||
}
|
||||
@@ -82,6 +82,9 @@ fun SettingsScreen(navController: NavHostController, saveLocation: String = "set
|
||||
settings.value.validate(context)
|
||||
saveSettings(context, settings.value, saveLocation)
|
||||
navController.popBackStack()
|
||||
if (saveLocation != "settings") {
|
||||
navController.navigate("custom_game")
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
Toast.makeText(context, e.message, Toast.LENGTH_LONG).show()
|
||||
}
|
||||
|
||||
@@ -21,6 +21,7 @@ roomPaging = "2.6.1"
|
||||
roomRuntime = "2.6.1"
|
||||
roomRuntimeVersion = "2.5.0"
|
||||
runtimeLivedata = "1.7.6"
|
||||
material = "1.12.0"
|
||||
|
||||
[libraries]
|
||||
androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" }
|
||||
@@ -54,6 +55,7 @@ androidx-room-ktx = { group = "androidx.room", name = "room-ktx", version.ref =
|
||||
room-compiler = { module = "androidx.room:room-compiler" }
|
||||
room-ktx = { module = "androidx.room:room-ktx" }
|
||||
androidx-runtime-livedata = { group = "androidx.compose.runtime", name = "runtime-livedata", version.ref = "runtimeLivedata" }
|
||||
material = { group = "com.google.android.material", name = "material", version.ref = "material" }
|
||||
|
||||
[plugins]
|
||||
android-application = { id = "com.android.application", version.ref = "agp" }
|
||||
|
||||
Reference in New Issue
Block a user