feat: Ajout du jeu Papelito, améliorations UX et corrections de bugs

Nouveau jeu:
- Ajout du jeu Papelito (Undercover) avec flow complet
- Configuration des joueurs, temps de discussion, votes
- Système d'élimination et gestion des égalités
- Interface Material Design avec cartes et dialogues

Corrections de bugs critiques:
- Fix crash Papelito au lancement (MaterialSwitch vs Switch)
- Fix crash lors des votes nuls (égalité entre joueurs)
- Fix crash fin de partie lors du retour (navigation vers hub)
- Fix visibilité texte questions (couleur dynamique)
- Fix compteur tours défis invisible (blanc sur blanc)
- Fix icone question manquante pendant défis

Améliorations UX Boidelo Classic:
- Harmonisation des couleurs dynamiques (toolbar, bouton)
- Bouton de réglages maintenant visible (MaterialButton)
- Conteneur IA se rétracte quand désactivé
- Meilleure gestion des couleurs selon catégorie
- Fix délai entre manches pour affichage message fin

Améliorations techniques:
- Mise à jour CLAUDE.md avec architecture Papelito
- Amélioration tests unitaires (GameEngine, PlayerStats, QuestionCategory)
- Standardisation des clés Intent entre activités
- Nettoyage code mort (méthodes non utilisées)

Tests:
- 302 tests unitaires passants
- Couverture GameEngine, PlayerStats, QuestionCategory
- Tests Papelito (game logic, player management)
- Tests Game89 (challenges, players)

🤖 Generated with Claude Code

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
feldenr
2026-01-16 12:05:29 +01:00
parent 1b3d67526d
commit c803469643
42 changed files with 6551 additions and 203 deletions
@@ -84,6 +84,9 @@ public class SecureConfig {
* @return La clé API ou null si non trouvée
*/
public String getApiKey(String provider) {
if (provider == null || provider.trim().isEmpty()) {
return null;
}
String key = getPrefKeyForProvider(provider);
return sharedPreferences.getString(key, null);
}
@@ -95,6 +98,11 @@ public class SecureConfig {
* @return true si supprimée avec succès
*/
public boolean removeApiKey(String provider) {
if (provider == null || provider.trim().isEmpty()) {
Log.w(TAG, "Provider null ou vide pour removeApiKey");
return false;
}
SharedPreferences.Editor editor = sharedPreferences.edit();
String key = getPrefKeyForProvider(provider);
editor.remove(key);
@@ -127,6 +135,9 @@ public class SecureConfig {
* @return true si une clé existe
*/
public boolean hasApiKey(String provider) {
if (provider == null || provider.trim().isEmpty()) {
return false;
}
String key = getPrefKeyForProvider(provider);
return sharedPreferences.contains(key) && sharedPreferences.getString(key, null) != null;
}
@@ -143,9 +154,14 @@ public class SecureConfig {
return false;
}
if (provider == null || provider.trim().isEmpty()) {
Log.w(TAG, "Provider null ou vide");
return false;
}
String trimmedKey = apiKey.trim();
switch (provider.toLowerCase()) {
switch (provider.toLowerCase().trim()) {
case "openai":
// Les clés OpenAI commencent par "sk-"
return trimmedKey.startsWith("sk-") && trimmedKey.length() >= 20;
@@ -198,7 +214,10 @@ public class SecureConfig {
* Retourne la clé SharedPreferences appropriée selon le provider
*/
private String getPrefKeyForProvider(String provider) {
switch (provider.toLowerCase()) {
if (provider == null) {
return KEY_API_KEY; // Default to OpenAI key
}
switch (provider.toLowerCase().trim()) {
case "openrouter":
return KEY_API_KEY_OPENROUTER;
case "zai":