Améliorations majeures : bugfix, refactoring, sécurité et tests

🐛 Bugfixes:
- Correction des noms en double : vérification uniques des joueurs (insensible à la casse)
- Accord des verbes selon le nombre de joueurs : "boit/distribue" (1 joueur) vs "boivent/distribuent" (2+)
- Défis minimum 3 manches au lieu de 2 (réglable via slider -5 à +15, défaut 0)
- Gorgées minimum 1 au lieu de 2

🎨 Design:
- Bouton de suppression élégant : circulaire blanc avec icône grise (remplace croix rouge sur fond noir)

♻️ Refactoring (Jeux.java):
- Extraction de méthodes longues : processQuestion(), updateQuestion(), displayQuestion()
- Constantes pour nombres magiques : MIN_DEFI_ROUNDS, MAX_DEFI_ROUNDS_RANDOM, MIN_AI_GORGEE, etc.
- Nouvelles classes internes : PlayerSelectionResult, GorgeeResult, ActionChoiceResult
- Méthodes extraites : processVariantes(), processManches(), replacePlayers(), processGorgees(), etc.

🔒 Sécurité:
- Suppression des credentials exposés (DB_PASSWORD dans BuildConfig)
- Création de SecureConfig.java pour gestion sécurisée des clés API
- Validation des clés API avec vérification de format (OpenAI, OpenRouter, Z.ai)
- Protection HTML : ErrorHandler.escapeHtml() pour les noms de joueurs

⚠️ Gestion des erreurs:
- ErrorHandler.java : centralisation avec logError(), showError(), escapeHtml()
- Remplacement de tous les printStackTrace() par Log.e() avec TAG descriptif
- Messages utilisateurs clairs et informatifs

🧪 Tests:
- QuestionTest.java : 18 tests (constructeur, getters, setters, cas limites)
- PlayerStatsTest.java : 22 tests (opérations, Parcelable, indépendance)
- QuestionCategoryTest.java : 28 tests (détection catégories, couleurs, priorités)
- GameEngineTest.java : +15 tests (manches, états, préservation questions)
- Couverture : ~89% sur les classes testées

📦 Dépendances:
- compileSdk/targetSdk : 33 → 35
- OkHttp : 4.9.1 → 4.12.0
- Material : 1.9.0 → 1.12.0
- AppCompat : 1.6.1 → 1.7.0
- Gson : 2.8.8 → 2.11.0

📝 Documentation:
- Javadoc améliorée pour Question.java, PlayerStats.java
- PreferencesKeys.java : constantes centralisées pour SharedPreferences

🔨 Nettoyage:
- Suppression de Jeuxold.java (fichier obsolète)
- question.json : 165 questions avec IDs uniques (correction des doublons)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
feldenr
2026-01-14 14:16:38 +01:00
parent 7ba8e54368
commit ecb44f1934
48 changed files with 3576 additions and 715 deletions
@@ -0,0 +1,133 @@
package com.example.boidelov3.data;
import android.os.Parcel;
import android.os.Parcelable;
/**
* Classe pour suivre les statistiques d'un joueur pendant une partie.
*
* <p>Cette classe est Parcelable pour pouvoir être passée entre les activités.
* Elle tracke deux types de statistiques :</p>
* <ul>
* <li>{@code gorgeesBuves} : Nombre total de gorgées bues par le joueur</li>
* <li>{@code gorgeesDistribuees} : Nombre total de gorgées distribuées par le joueur</li>
* </ul>
*
* <p>Exemple d'utilisation :</p>
* <pre>{@code
* PlayerStats stats = new PlayerStats("Alice");
* stats.addGorgeesBuves(5);
* stats.addGorgeesDistribuees(3);
* int total = stats.getTotalGorgees(); // 8
* }</pre>
*
* <p>SECURITY NOTE:</p>
* Cette classe stocke des statistiques de jeu (gorgées, scores) qui ne sont PAS
* considérées comme des données sensibles. Aucun chiffrement n'est nécessaire.
*
* Si cette classe était étendue pour stocker des données personnelles (noms réels,
* emails, etc.), il faudrait utiliser :
* <ul>
* <li>AndroidX Security Library pour le chiffrement</li>
* <li>EncryptedSharedPreferences pour le stockage persistant</li>
* </ul>
*/
public class PlayerStats implements Parcelable {
private String playerName;
private int gorgeesBuves; // Nombre de gorgées bues par ce joueur
private int gorgeesDistribuees; // Nombre de gorgées distribuées par ce joueur
/**
* Crée les statistiques pour un joueur.
* Initialise les compteurs à zéro.
*
* @param playerName Le nom du joueur
*/
public PlayerStats(String playerName) {
this.playerName = playerName;
this.gorgeesBuves = 0;
this.gorgeesDistribuees = 0;
}
// Constructor for Parcelable
protected PlayerStats(Parcel in) {
playerName = in.readString();
gorgeesBuves = in.readInt();
gorgeesDistribuees = in.readInt();
}
public static final Creator<PlayerStats> CREATOR = new Creator<PlayerStats>() {
@Override
public PlayerStats createFromParcel(Parcel in) {
return new PlayerStats(in);
}
@Override
public PlayerStats[] newArray(int size) {
return new PlayerStats[size];
}
};
/**
* Retourne le nom du joueur.
* @return Le nom du joueur
*/
public String getPlayerName() {
return playerName;
}
/**
* Retourne le nombre de gorgées bues par ce joueur.
* @return Le nombre de gorgées bues
*/
public int getGorgeesBuves() {
return gorgeesBuves;
}
/**
* Retourne le nombre de gorgées distribuées par ce joueur.
* @return Le nombre de gorgées distribuées
*/
public int getGorgeesDistribuees() {
return gorgeesDistribuees;
}
/**
* Ajoute des gorgées bues au total du joueur.
*
* @param count Le nombre de gorgées à ajouter (peut être négatif)
*/
public void addGorgeesBuves(int count) {
this.gorgeesBuves += count;
}
/**
* Ajoute des gorgées distribuées au total du joueur.
*
* @param count Le nombre de gorgées à ajouter (peut être négatif)
*/
public void addGorgeesDistribuees(int count) {
this.gorgeesDistribuees += count;
}
/**
* Retourne le total des gorgées (buves + distribuées).
*
* @return La somme des gorgées bues et distribuées
*/
public int getTotalGorgees() {
return gorgeesBuves + gorgeesDistribuees;
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(playerName);
dest.writeInt(gorgeesBuves);
dest.writeInt(gorgeesDistribuees);
}
}