From 69411ede1024b519465de4bdac98fb2ecb5cb552 Mon Sep 17 00:00:00 2001
From: feldenr <135638674+feldenr@users.noreply.github.com>
Date: Sat, 17 Jan 2026 01:35:37 +0100
Subject: [PATCH] Jeu de test, optimisation correction de bug
---
.claude/settings.local.json | 49 +++++-
.idea/deploymentTargetSelector.xml | 8 +
app/build.gradle | 1 +
app/src/main/AndroidManifest.xml | 2 +-
app/src/main/assets/question.json | 162 +++++++++++++++++-
.../BoideloClassicGameActivity.java | 46 ++++-
.../boidelov3/hub/GameSelectionActivity.java | 6 +-
.../example/boidelov3/utils/SecureConfig.java | 22 ++-
app/src/main/res/values/strings.xml | 2 +-
.../games/papelito/PapelitoGameTest.java | 35 +++-
10 files changed, 310 insertions(+), 23 deletions(-)
diff --git a/.claude/settings.local.json b/.claude/settings.local.json
index 2a435d8..7f0c156 100644
--- a/.claude/settings.local.json
+++ b/.claude/settings.local.json
@@ -4,7 +4,54 @@
"Bash(tree:*)",
"Bash(./gradlew:*)",
"Bash(dir:*)",
- "Bash(timeout:*)"
+ "Bash(timeout:*)",
+ "WebSearch",
+ "Bash(git rm:*)",
+ "Bash(python:*)",
+ "Bash(gradlew.bat build:*)",
+ "Bash(gradlew.bat assembleDebug:*)",
+ "Bash(cmd.exe /c \"gradlew.bat assembleDebug 2>&1\")",
+ "Bash(cmd.exe:*)",
+ "Bash(./gradlew.bat assembleDebug:*)",
+ "Bash(git add:*)",
+ "Bash(git commit:*)",
+ "Bash(git push:*)",
+ "mcp__plugin_serena_serena__initial_instructions",
+ "mcp__plugin_serena_serena__list_dir",
+ "mcp__plugin_serena_serena__activate_project",
+ "mcp__plugin_serena_serena__get_symbols_overview",
+ "mcp__plugin_serena_serena__find_symbol",
+ "mcp__plugin_serena_serena__read_file",
+ "mcp__plugin_serena_serena__create_text_file",
+ "mcp__plugin_serena_serena__replace_content",
+ "Bash(adb:*)",
+ "mcp__plugin_serena_serena__search_for_pattern",
+ "Bash(mkdir:*)",
+ "Bash(.\\\\gradlew.bat:*)",
+ "Bash(Select-String -Pattern \"error|Error|BUILD|FAILED|SUCCESS\")",
+ "Bash(Select-Object:*)",
+ "Bash(rm:*)",
+ "Bash(findstr /i \"\\\\.java$\")",
+ "Bash(findstr:*)",
+ "Bash(.\\\\gradlew.bat test:*)",
+ "Bash(./gradlew.bat test)",
+ "Bash(./gradlew.bat test --tests \"*QuestionCategoryTest*\")",
+ "Bash(./gradlew.bat test:*)",
+ "Bash(wc:*)",
+ "Bash(./gradlew.bat:*)",
+ "mcp__plugin_serena_serena__replace_symbol_body",
+ "mcp__plugin_serena_serena__think_about_collected_information",
+ "mcp__plugin_serena_serena__find_file",
+ "Bash(gradlew test:*)",
+ "Bash(cat app/build/reports/tests/testDebugUnitTest/index.html)",
+ "Bash(Select-String:*)",
+ "Bash(gh pr view:*)",
+ "Bash(cmd:*)",
+ "Bash(git restore:*)",
+ "Bash(git config:*)",
+ "mcp__plugin_serena_serena__check_onboarding_performed",
+ "Bash(powershell:*)",
+ "mcp__plugin_serena_serena__execute_shell_command"
]
}
}
diff --git a/.idea/deploymentTargetSelector.xml b/.idea/deploymentTargetSelector.xml
index 849be02..3cc0cd3 100644
--- a/.idea/deploymentTargetSelector.xml
+++ b/.idea/deploymentTargetSelector.xml
@@ -4,6 +4,14 @@
+
+
+
+
+
+
+
+
diff --git a/app/build.gradle b/app/build.gradle
index 0da9676..0f7b3b6 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -52,6 +52,7 @@ dependencies {
implementation 'androidx.constraintlayout:constraintlayout:2.2.0'
testImplementation 'junit:junit:4.13.2'
testImplementation 'org.mockito:mockito-core:5.7.0'
+ testImplementation 'org.robolectric:robolectric:4.11.1'
androidTestImplementation 'androidx.test.ext:junit:1.2.1'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.6.1'
implementation 'com.squareup.okhttp3:okhttp:4.12.0'
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 1196a3a..4c817d4 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -72,7 +72,7 @@
android:screenOrientation="portrait" />
- $1
+
doit parler avec un pendant manches",
+ "gorger": 0,
+ "variante": [
+ "accent allemand",
+ "accent belge",
+ "accent suisse",
+ "accent canadien"
+ ],
+ "arret": "Plus d'accent !"
+ },
+ {
+ "id": 168,
+ "question": "Le/La plus 'marabout' du groupe (celui/celle qui a toujours des remèdes)",
+ "gorger": 2,
+ "distribution": false,
+ "recois": true
+ },
+ {
+ "id": 169,
+ "question": "Ceux qui ont déjà été bloqués sur les réseaux sociaux",
+ "gorger": 3,
+ "distribution": true,
+ "recois": true
+ },
+ {
+ "id": 170,
+ "question": "Ceux qui ont leur permis de conduire",
+ "gorger": 1,
+ "distribution": true,
+ "recois": true
+ },
+ {
+ "id": 171,
+ "question": " doit faire rire en 30 secondes. Si échec : 5 gorgées",
+ "gorger": 0,
+ "recois": true
+ },
+ {
+ "id": 172,
+ "question": "Les fans de K-Pop",
+ "gorger": 3,
+ "distribution": true,
+ "recois": true
+ },
+ {
+ "id": 173,
+ "question": "Ceux qui ont déjà fait du covoiturage avec des inconnus",
+ "gorger": 2,
+ "distribution": true,
+ "recois": true
+ },
+ {
+ "id": 174,
+ "question": " doit mimer pendant que les autres devinent. 30 secondes, 3 gorgées par échec",
+ "gorger": 0,
+ "variante": [
+ "un chat",
+ "un chien",
+ "une voiture",
+ "un téléphone",
+ "un avion"
+ ],
+ "arret": "Fin du mime !"
+ },
+ {
+ "id": 175,
+ "question": "Le/La plus 'intello' du groupe (vote à main levée)",
+ "gorger": 3,
+ "distribution": false,
+ "recois": true
+ },
+ {
+ "id": 176,
+ "question": "Ceux qui ont déjà participé à un marathon ou semi-marathon",
+ "gorger": 2,
+ "distribution": true,
+ "recois": true
+ },
+ {
+ "id": 177,
+ "question": "Jeu du NI : Tous ceux qui ont un sur eux doivent boire 2 gorgées",
+ "gorger": 0,
+ "variante": [
+ "stylo",
+ "post-it",
+ "mèche",
+ "alliance",
+ "bracelet"
+ ],
+ "arret": "Fin du jeu du NI !"
+ },
+ {
+ "id": 178,
+ "question": "Ceux qui ont déjà fait une surprise-party à quelqu'un",
+ "gorger": 2,
+ "distribution": true,
+ "recois": true
+ },
+ {
+ "id": 179,
+ "question": " doit compléter chaque phrase de par 'oui chef' pendant manches",
+ "gorger": 0,
+ "arret": "Plus de 'oui chef' !"
+ },
+ {
+ "id": 180,
+ "question": "Le/La plus 'sportif/sportive' du groupe",
+ "gorger": 2,
+ "distribution": false,
+ "recois": true
+ },
+ {
+ "id": 181,
+ "question": "Ceux qui ont un compte sur onlyfans",
+ "gorger": 4,
+ "distribution": true,
+ "recois": true
+ },
+ {
+ "id": 182,
+ "question": " doit choisir entre . Le perdant boit 3 gorgées",
+ "gorger": 0,
+ "variante": [
+ "Instagram et TikTok",
+ "Netflix et Disney+",
+ "Pizza et Burger",
+ "Chat et Chien",
+ "Montagne et Mer"
+ ]
+ },
+ {
+ "id": 183,
+ "question": "Ceux qui ont déjà fait un regretté post-soirée (message à un ex)",
+ "gorger": 4,
+ "distribution": true,
+ "recois": true
+ },
+ {
+ "id": 184,
+ "question": "Le/La plus 'organisé' du groupe",
+ "gorger": 2,
+ "distribution": false,
+ "recois": true
+ },
+ {
+ "id": 185,
+ "question": "Ceux qui ont déjà été coincés dans un ascenseur",
+ "gorger": 3,
+ "distribution": true,
+ "recois": true
}
]
}
\ No newline at end of file
diff --git a/app/src/main/java/com/example/boidelov3/games/boideloclassic/BoideloClassicGameActivity.java b/app/src/main/java/com/example/boidelov3/games/boideloclassic/BoideloClassicGameActivity.java
index 2c6bd51..fe36c1d 100644
--- a/app/src/main/java/com/example/boidelov3/games/boideloclassic/BoideloClassicGameActivity.java
+++ b/app/src/main/java/com/example/boidelov3/games/boideloclassic/BoideloClassicGameActivity.java
@@ -22,6 +22,7 @@ import com.example.boidelov3.R;
import com.example.boidelov3.data.PlayerStats;
import com.example.boidelov3.data.QuestionCategory;
import com.example.boidelov3.data.QuestionRepository;
+import com.example.boidelov3.game.GameEngine;
import com.example.boidelov3.Question;
import com.example.boidelov3.Questions;
import com.example.boidelov3.utils.ErrorHandler;
@@ -65,8 +66,8 @@ public class BoideloClassicGameActivity extends AppCompatActivity {
private Map playerStatsMap;
// Services
- // private SoundManager soundManager; // TODO: Fix SoundManager constructor
- // private GameEngine gameEngine; // TODO: Fix GameEngine
+ private com.example.boidelov3.utils.SoundManager soundManager;
+ private GameEngine gameEngine;
private QuestionRepository questionRepository;
// Random
@@ -165,8 +166,8 @@ public class BoideloClassicGameActivity extends AppCompatActivity {
* Initialise les services
*/
private void initServices() {
- // soundManager = new SoundManager(this); // TODO: Fix SoundManager
- // gameEngine = new GameEngine(); // TODO: Fix GameEngine
+ soundManager = com.example.boidelov3.utils.SoundManager.getInstance(this);
+ gameEngine = new GameEngine();
questionRepository = new QuestionRepository(this);
}
@@ -179,7 +180,16 @@ public class BoideloClassicGameActivity extends AppCompatActivity {
Questions q = result.getData();
questions = q != null && q.getQuestions() != null ? q.getQuestions() : new ArrayList<>();
} else {
+ android.util.Log.e("BoideloClassicGame", "Erreur lors du chargement des questions: " + result.getError());
questions = new ArrayList<>();
+
+ // Afficher une erreur à l'utilisateur
+ new androidx.appcompat.app.AlertDialog.Builder(this)
+ .setTitle("Erreur de chargement")
+ .setMessage("Impossible de charger les questions. Veuillez réinstaller l'application.")
+ .setPositiveButton("Fermer", (dialog, which) -> finish())
+ .setCancelable(false)
+ .show();
}
questionsAvecManches = new ArrayList<>();
}
@@ -260,6 +270,9 @@ public class BoideloClassicGameActivity extends AppCompatActivity {
*/
private void handleGameEnd() {
isFinishingGame = true;
+
+ // Sauvegarder les stats avant de terminer
+ saveGameStats();
// Prépare les stats pour l'activité de fin
Intent intent = new Intent(this, EndGameActivity.class);
@@ -329,6 +342,8 @@ public class BoideloClassicGameActivity extends AppCompatActivity {
*/
private void displayNewQuestion() {
if (questions == null || questions.isEmpty()) {
+ android.util.Log.e("BoideloClassicGame", "Aucune question disponible - fin du jeu");
+ handleGameEnd();
return;
}
@@ -372,7 +387,15 @@ public class BoideloClassicGameActivity extends AppCompatActivity {
* Affiche la notification de fin de manche
*/
private void showMancheEndNotification() {
- // TODO: Implémenter la notification de fin de manche
+ if (soundManager != null) {
+ soundManager.playManche();
+ }
+
+ // Vibration pour feedback haptique
+ BoideloAnimationUtils.triggerErrorHaptic(this);
+
+ // Afficher un message toast pour informer les joueurs
+ android.widget.Toast.makeText(this, "⏱️ Manche terminée!", android.widget.Toast.LENGTH_SHORT).show();
}
/**
@@ -958,7 +981,18 @@ public class BoideloClassicGameActivity extends AppCompatActivity {
* Sauvegarde les stats du jeu
*/
private void saveGameStats() {
- // TODO: Implémenter la sauvegarde des stats
+ if (questionRepository != null && playerStatsMap != null) {
+ // Calculer le total des questions posées
+ int totalQuestionsPlayed = totalQuestionsAsked;
+ int playersCount = toutlesjoueurs != null ? toutlesjoueurs.size() : 0;
+
+ // Sauvegarder dans SharedPreferences via le repository
+ questionRepository.saveGameStats(totalQuestionsPlayed, playersCount);
+
+ android.util.Log.d("BoideloClassicGame",
+ "Stats sauvegardées: " + totalQuestionsPlayed + " questions, " +
+ playersCount + " joueurs");
+ }
}
/**
diff --git a/app/src/main/java/com/example/boidelov3/hub/GameSelectionActivity.java b/app/src/main/java/com/example/boidelov3/hub/GameSelectionActivity.java
index 6f8bd0a..623b17a 100644
--- a/app/src/main/java/com/example/boidelov3/hub/GameSelectionActivity.java
+++ b/app/src/main/java/com/example/boidelov3/hub/GameSelectionActivity.java
@@ -101,6 +101,8 @@ public class GameSelectionActivity extends AppCompatActivity implements GameAdap
public void onItemClick(GameInfo gameInfo) {
if (!gameInfo.isAvailable()) {
// Afficher un message "Coming soon"
+ android.widget.Toast.makeText(this, gameInfo.getName() + " - Bientôt disponible!",
+ android.widget.Toast.LENGTH_SHORT).show();
return;
}
@@ -115,7 +117,9 @@ public class GameSelectionActivity extends AppCompatActivity implements GameAdap
startActivity(new Intent(this, com.example.boidelov3.games.papelito.PapelitoSetupActivity.class));
break;
case RULES:
- // TODO: Implémenter RulesListActivity
+ // À implémenter: RulesListActivity
+ android.widget.Toast.makeText(this, "Règles de jeux - Bientôt disponible!",
+ android.widget.Toast.LENGTH_SHORT).show();
break;
}
}
diff --git a/app/src/main/java/com/example/boidelov3/utils/SecureConfig.java b/app/src/main/java/com/example/boidelov3/utils/SecureConfig.java
index 50c886d..81f8c2d 100644
--- a/app/src/main/java/com/example/boidelov3/utils/SecureConfig.java
+++ b/app/src/main/java/com/example/boidelov3/utils/SecureConfig.java
@@ -6,7 +6,6 @@ import android.util.Log;
import java.security.MessageDigest;
import java.security.SecureRandom;
-import java.util.Base64;
/**
* Classe utilitaire pour la gestion sécurisée des clés API et configuration sensible.
@@ -188,12 +187,25 @@ public class SecureConfig {
* @return Le hash de la clé
*/
public String hashApiKey(String apiKey) {
+ if (apiKey == null) {
+ Log.w(TAG, "Tentative de hasher une clé API null");
+ return null;
+ }
+
+ if (apiKey.trim().isEmpty()) {
+ Log.w(TAG, "Tentative de hasher une clé API vide");
+ return null;
+ }
+
try {
MessageDigest digest = MessageDigest.getInstance("SHA-256");
byte[] hash = digest.digest(apiKey.getBytes("UTF-8"));
- return Base64.getEncoder().encodeToString(hash);
- } catch (Exception e) {
- Log.e(TAG, "Erreur lors du hash de la clé", e);
+ return android.util.Base64.encodeToString(hash, android.util.Base64.NO_WRAP);
+ } catch (java.security.NoSuchAlgorithmException e) {
+ Log.e(TAG, "SHA-256 non disponible sur cet appareil - hash impossible", e);
+ return null;
+ } catch (java.io.UnsupportedEncodingException e) {
+ Log.e(TAG, "UTF-8 encoding non supporté - impossible", e);
return null;
}
}
@@ -207,7 +219,7 @@ public class SecureConfig {
public String generateSecureToken(int length) {
byte[] token = new byte[length];
secureRandom.nextBytes(token);
- return Base64.getEncoder().encodeToString(token);
+ return android.util.Base64.encodeToString(token, android.util.Base64.NO_WRAP);
}
/**
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 37734e7..9ac5f1f 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -4,7 +4,7 @@
Pseudo
Nom
C\'est qui
- comment il s\'appelle
+ Comment il s\'appelle
who
Joueur
Prénom
diff --git a/app/src/test/java/com/example/boidelov3/games/papelito/PapelitoGameTest.java b/app/src/test/java/com/example/boidelov3/games/papelito/PapelitoGameTest.java
index ac12971..6120c86 100644
--- a/app/src/test/java/com/example/boidelov3/games/papelito/PapelitoGameTest.java
+++ b/app/src/test/java/com/example/boidelov3/games/papelito/PapelitoGameTest.java
@@ -340,21 +340,42 @@ public class PapelitoGameTest {
List playerNames = Arrays.asList("Alice", "Bob", "Charlie", "David", "Eve");
game.setupGame(playerNames, 3);
- // Kill 2 civils using game method
+ // Set game state to voting
game.setGameState(PapelitoGame.GameState.VOTING);
- PapelitoPlayer civil1 = game.getAlivePlayers().get(0);
- PapelitoPlayer civil2 = game.getAlivePlayers().get(1);
- PapelitoPlayer civil3 = game.getAlivePlayers().get(2);
- PapelitoPlayer undercover1 = game.getAlivePlayers().get(3);
- PapelitoPlayer undercover2 = game.getAlivePlayers().get(4);
+ // Find actual civils and undercovers (roles are assigned randomly)
+ PapelitoPlayer civil1 = null;
+ PapelitoPlayer civil2 = null;
+ PapelitoPlayer undercover1 = null;
+ PapelitoPlayer undercover2 = null;
+ PapelitoPlayer undercover3 = null;
+
+ for (PapelitoPlayer player : game.getAlivePlayers()) {
+ if (player.isCivil()) {
+ if (civil1 == null) civil1 = player;
+ else if (civil2 == null) civil2 = player;
+ } else if (player.isUndercover()) {
+ if (undercover1 == null) undercover1 = player;
+ else if (undercover2 == null) undercover2 = player;
+ else if (undercover3 == null) undercover3 = player;
+ }
+ }
+
+ // We should have 2 civils and 3 undercovers
+ assertNotNull("Should have at least 1 civil", civil1);
+ assertNotNull("Should have at least 2 civils", civil2);
+ assertNotNull("Should have at least 1 undercover", undercover1);
+ assertNotNull("Should have at least 2 undercovers", undercover2);
+ assertNotNull("Should have at least 3 undercovers", undercover3);
+
+ // Eliminate both civils
game.vote(undercover1, civil1);
game.eliminateMostVoted();
game.vote(undercover2, civil2);
game.eliminateMostVoted();
- // Act
+ // Act - Now we have 0 civils and 3 undercovers
boolean result = game.checkGameOver();
// Assert