From c4f960f332b6a5f0c38f88aa7d599b2701867fff Mon Sep 17 00:00:00 2001 From: Muyue Date: Fri, 4 Apr 2025 17:02:06 +0200 Subject: [PATCH] =?UTF-8?q?Feat:=20Notifications=20OFF=20par=20d=C3=A9faut?= =?UTF-8?q?=20et=20Impl=C3=A9mentation=20Sonore?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Notifications: - Valeur par défaut pour l'activation des notifications mise à 'false'. - L'utilisateur doit maintenant activer explicitement les notifications via les paramètres. - Son: - Ajout de la structure pour les effets sonores via SoundPool. - Création du dossier 'res/raw' (si nécessaire) pour les fichiers audio (placeholders: move, merge, win, game_over). - Initialisation de SoundPool et chargement des sons dans MainActivity.onCreate. - Ajout méthode playSound() pour jouer les effets (vérifie si chargé et activé). - Déclenchement des sons 'move' et 'merge' dans handleSwipe. - Déclenchement des sons 'win' et 'game_over' à l'affichage des dialogues correspondants. - Activation du switch 'Son' dans les paramètres, sauvegarde/chargement de la préférence 'sound_enabled'. - Libération de SoundPool dans MainActivity.onDestroy. --- .../legion/muyue/best2048/MainActivity.java | 114 +++++++++++++++++- app/src/main/res/layout/dialog_settings.xml | 4 +- app/src/main/res/raw/game_over.mp3 | Bin 0 -> 9195 bytes app/src/main/res/raw/merge.mp3 | Bin 0 -> 707187 bytes app/src/main/res/raw/move.mp3 | Bin 0 -> 25344 bytes app/src/main/res/raw/win.mp3 | Bin 0 -> 332160 bytes app/src/main/res/values/strings.xml | 6 +- 7 files changed, 114 insertions(+), 10 deletions(-) create mode 100644 app/src/main/res/raw/game_over.mp3 create mode 100644 app/src/main/res/raw/merge.mp3 create mode 100644 app/src/main/res/raw/move.mp3 create mode 100644 app/src/main/res/raw/win.mp3 diff --git a/app/src/main/java/legion/muyue/best2048/MainActivity.java b/app/src/main/java/legion/muyue/best2048/MainActivity.java index e73060a..d9781f5 100644 --- a/app/src/main/java/legion/muyue/best2048/MainActivity.java +++ b/app/src/main/java/legion/muyue/best2048/MainActivity.java @@ -45,6 +45,8 @@ import android.animation.AnimatorListenerAdapter; import android.animation.AnimatorSet; import android.animation.ObjectAnimator; import android.view.ViewTreeObserver; +import android.media.AudioAttributes; // Pour SoundPool +import android.media.SoundPool; // Pour SoundPool import java.util.ArrayList; import java.util.List; @@ -68,7 +70,7 @@ public class MainActivity extends AppCompatActivity { private GameStats gameStats; private static final int BOARD_SIZE = 4; private static final String NOTIFICATION_CHANNEL_ID = "BEST_2048_CHANNEL"; - private boolean notificationsEnabled = true; + private boolean notificationsEnabled = false; private static final String LAST_PLAYED_TIME_KEY = "last_played_time"; // --- State Management --- @@ -85,6 +87,15 @@ public class MainActivity extends AppCompatActivity { private static final String HIGH_SCORE_KEY = "high_score"; private static final String GAME_STATE_KEY = "game_state"; + // --- Champs Son --- + private SoundPool soundPool; + private int soundMoveId = -1; // Initialise à -1 pour savoir s'ils sont chargés + private int soundMergeId = -1; + private int soundWinId = -1; + private int soundGameOverId = -1; + private boolean soundPoolLoaded = false; // Flag pour savoir si les sons sont prêts + private boolean soundEnabled = true; // Son activé par défaut + // --- Activity Lifecycle --- private final ActivityResultLauncher requestPermissionLauncher = @@ -115,6 +126,7 @@ public class MainActivity extends AppCompatActivity { NotificationHelper.createNotificationChannel(this); createNotificationChannel(); findViews(); + initializeSoundPool(); initializeGameAndStats(); setupListeners(); if (notificationsEnabled) { @@ -231,6 +243,15 @@ public class MainActivity extends AppCompatActivity { } } + @Override + protected void onDestroy() { // Important de libérer SoundPool + super.onDestroy(); + if (soundPool != null) { + soundPool.release(); + soundPool = null; + } + } + // --- Initialisation --- /** @@ -256,6 +277,7 @@ public class MainActivity extends AppCompatActivity { preferences = getSharedPreferences(PREFS_NAME, MODE_PRIVATE); gameStats = new GameStats(this); loadNotificationPreference(); + loadSoundPreference(); loadGame(); // Charge jeu et met à jour high score updateUI(); if (game == null) { @@ -265,6 +287,73 @@ public class MainActivity extends AppCompatActivity { // L'état (currentGameState) est défini dans loadGame ou startNewGame } + /** Initialise le SoundPool et charge les effets sonores. */ + private void initializeSoundPool() { + // Configuration pour les effets sonores de jeu + AudioAttributes attributes = new AudioAttributes.Builder() + .setUsage(AudioAttributes.USAGE_GAME) + .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION) + .build(); + + // Crée le SoundPool + soundPool = new SoundPool.Builder() + .setMaxStreams(3) // Nombre max de sons joués simultanément + .setAudioAttributes(attributes) + .build(); + + // Listener pour savoir quand les sons sont chargés + soundPool.setOnLoadCompleteListener((soundPool, sampleId, status) -> { + if (status == 0) { + // Vérifie si TOUS les sons sont chargés (ou gère individuellement) + // Ici, on met juste un flag général pour simplifier + soundPoolLoaded = true; + } + }); + + // Charge les sons depuis res/raw + // Le 3ème argument (priority) est 1 (priorité normale) + try { + soundMoveId = soundPool.load(this, R.raw.move, 1); + soundMergeId = soundPool.load(this, R.raw.merge, 1); + soundWinId = soundPool.load(this, R.raw.win, 1); + soundGameOverId = soundPool.load(this, R.raw.game_over, 1); + } catch (Exception e) { + // Gérer l'erreur, peut-être désactiver le son + soundEnabled = false; + } + } + + // --- Préférences Son --- + + /** Sauvegarde la préférence d'activation du son. */ + private void saveSoundPreference(boolean enabled) { + if (preferences != null) { + preferences.edit().putBoolean("sound_enabled", enabled).apply(); + } + } + + /** Charge la préférence d'activation du son. */ + private void loadSoundPreference() { + if (preferences != null) { + soundEnabled = preferences.getBoolean("sound_enabled", true); // Son activé par défaut + } else { + soundEnabled = true; + } + } + + // --- Lecture Son --- + + /** + * Joue un effet sonore si le SoundPool est chargé et si le son est activé. + * @param soundId L'ID du son retourné par soundPool.load(). + */ + private void playSound(int soundId) { + if (soundPoolLoaded && soundEnabled && soundPool != null && soundId > 0) { + // Arguments: soundID, leftVolume, rightVolume, priority, loop, rate + soundPool.play(soundId, 1.0f, 1.0f, 1, 0, 1.0f); + } + } + /** Crée le canal de notification nécessaire pour Android 8.0+. */ private void createNotificationChannel() { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { @@ -419,6 +508,7 @@ public class MainActivity extends AppCompatActivity { } if (boardChanged) { + playSound(soundMoveId); // Capture l'état APRÈS le push mais AVANT l'ajout de la nouvelle tuile int[][] boardAfterPush = game.getBoard(); @@ -426,6 +516,7 @@ public class MainActivity extends AppCompatActivity { gameStats.recordMove(); int scoreAfter = game.getCurrentScore(); if (scoreAfter > scoreBefore) { + playSound(soundMergeId); gameStats.recordMerge(1); // Simplifié if (scoreAfter > game.getHighestScore()) { game.setHighestScore(scoreAfter); @@ -608,6 +699,7 @@ public class MainActivity extends AppCompatActivity { * Propose de continuer à jouer ou de commencer une nouvelle partie. */ private void showGameWonKeepPlayingDialog() { + playSound(soundWinId); AlertDialog.Builder builder = new AlertDialog.Builder(this); LayoutInflater inflater = getLayoutInflater(); View dialogView = inflater.inflate(R.layout.dialog_game_won, null); @@ -634,6 +726,7 @@ public class MainActivity extends AppCompatActivity { * Propose Nouvelle Partie ou Quitter. */ private void showGameOverDialog() { + playSound(soundGameOverId); AlertDialog.Builder builder = new AlertDialog.Builder(this); LayoutInflater inflater = getLayoutInflater(); View dialogView = inflater.inflate(R.layout.dialog_game_over, null); @@ -749,10 +842,16 @@ public class MainActivity extends AppCompatActivity { final AlertDialog dialog = builder.create(); - // Config Son (Placeholder) - switchSound.setEnabled(false); - switchSound.setChecked(false); - switchSound.setOnCheckedChangeListener((v, i) -> Toast.makeText(this, "Gestion du son à venir.", Toast.LENGTH_SHORT).show()); + // Config Son (MAINTENANT ACTIF) + switchSound.setEnabled(true); // Activé + switchSound.setChecked(soundEnabled); // État actuel chargé + switchSound.setOnCheckedChangeListener((buttonView, isChecked) -> { + soundEnabled = isChecked; // Met à jour l'état + saveSoundPreference(isChecked); // Sauvegarde la préférence + Toast.makeText(this, + isChecked ? R.string.sound_enabled : R.string.sound_disabled, + Toast.LENGTH_SHORT).show(); + }); // Config Notifications (Activé + Gestion Permission) switchNotifications.setEnabled(true); // Activé @@ -803,7 +902,10 @@ public class MainActivity extends AppCompatActivity { /** Charge la préférence d'activation des notifications. */ private void loadNotificationPreference() { if (preferences != null) { - notificationsEnabled = preferences.getBoolean("notifications_enabled", true); // Activé par défaut + // Change le défaut de true à false + notificationsEnabled = preferences.getBoolean("notifications_enabled", false); + } else { + notificationsEnabled = false; // Assure une valeur par défaut si prefs est null } } diff --git a/app/src/main/res/layout/dialog_settings.xml b/app/src/main/res/layout/dialog_settings.xml index 5eb4930..e1e7200 100644 --- a/app/src/main/res/layout/dialog_settings.xml +++ b/app/src/main/res/layout/dialog_settings.xml @@ -30,7 +30,7 @@ android:textSize="16sp" android:fontFamily="@font/nunito_family" android:textColor="@color/text_tile_low" - android:enabled="false" /> + android:enabled="true" />