Feat: Notifications OFF par défaut et Implémentation Sonore

- 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.
This commit is contained in:
Augustin ROUX 2025-04-04 17:02:06 +02:00
parent 470649ad36
commit c4f960f332
7 changed files with 114 additions and 10 deletions

View File

@ -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<String> 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
}
}

View File

@ -30,7 +30,7 @@
android:textSize="16sp"
android:fontFamily="@font/nunito_family"
android:textColor="@color/text_tile_low"
android:enabled="false" /> <com.google.android.material.switchmaterial.SwitchMaterial
android:enabled="true" /> <com.google.android.material.switchmaterial.SwitchMaterial
android:id="@+id/switchNotifications"
android:layout_width="match_parent"
android:layout_height="wrap_content"
@ -39,7 +39,7 @@
android:textSize="16sp"
android:fontFamily="@font/nunito_family"
android:textColor="@color/text_tile_low"
android:enabled="false" /> </LinearLayout>
android:enabled="true" /> </LinearLayout>
<Button
android:id="@+id/buttonManagePermissions"

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -63,8 +63,8 @@
<string name="about_website_text">Website : legion-muyue.fr</string> <string name="about_website_url">https://legion-muyue.fr</string>
<string name="ok">OK</string>
<string name="settings_title">Settings</string>
<string name="settings_sound_label">Sound (coming soon)</string>
<string name="settings_notifications_label">Notifications (coming soon)</string>
<string name="settings_sound_label">Sound</string>
<string name="settings_notifications_label">Notifications</string>
<string name="settings_permissions_button">Manage Permissions</string>
<string name="settings_share_stats_button">Share my Statistics</string>
<string name="settings_reset_stats_button">Reset Statistics</string>
@ -91,4 +91,6 @@
<string name="notifications_disabled">Notifications disabled.</string>
<string name="settings_test_notif_highscore">Test High Score Notif</string>
<string name="settings_test_notif_inactivity">Test Inactivity Notif</string>
<string name="sound_enabled">Sound effects enabled.</string>
<string name="sound_disabled">Sound effects disabled.</string>
</resources>