Feat: Stats, Refactor, Permissions
- AndroidManifest: Ajout permissions (Network, BT, Location). - Game.java: Refonte majeure (renommage, contexte, états win/loss, logique SharedPreferences implémentée, probabilités addNewTile modifiées, méthodes check win/loss, pushX retourne boolean, JavaDoc). - MainActivity.java: Refonte majeure (gestion stats via ViewStub, champs/méthodes stats, dialogs win/loss, refactorisation onCreate, getters/setters, gestion cycle vie onPause/onResume, MAJ handleSwipe). - OnSwipeTouchListener.java: Ajout annotations, JavaDoc. - Layouts: Ajout stats_layout.xml, ajout ViewStub dans activity_main.xml. - Ressources: Ajout/MAJ strings (stats), colors (thème, tuiles, stats), styles (stats, fullscreen), dimens, anim (durée), drawables (bouton multi, tile_background).
This commit is contained in:
parent
a190263cf5
commit
73ab81e208
@ -2,6 +2,16 @@
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools">
|
||||
|
||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
<uses-permission android:name="android.permission.BLUETOOTH" />
|
||||
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
|
||||
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
|
||||
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
|
||||
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
|
||||
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
|
||||
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
|
||||
|
||||
<application
|
||||
android:allowBackup="true"
|
||||
android:dataExtractionRules="@xml/data_extraction_rules"
|
||||
|
@ -1,203 +1,640 @@
|
||||
// Fichier Game.java
|
||||
// Ce fichier contient la logique principale du jeu 2048. Il est indépendant de l'interface utilisateur.
|
||||
/*
|
||||
Fonctions principales :
|
||||
- gameBoard : Matrice 2D (int[][]) représentant la grille du jeu.
|
||||
- score, highScore : Suivi du score et du meilleur score.
|
||||
- addNewNumbers() : Ajoute une nouvelle tuile (2, 4 ou 8) aléatoirement sur la grille.
|
||||
- pushUp(), pushDown(), pushLeft(), pushRight() : Logique de déplacement et de fusion des tuiles.
|
||||
- serialize(), deserialize() : Sauvegarde et restauration de l'état du jeu (grille, score, meilleur score) en chaînes de caractères.
|
||||
- loadHighScore(), saveHighScore(), loadGameState(), saveGameState() : Utilisation de SharedPreferences pour persister les données.
|
||||
- Constructeurs : Initialisation de la grille et chargement/restauration des données.
|
||||
|
||||
Relations :
|
||||
- MainActivity : Crée une instance de Game et appelle ses méthodes pour la logique du jeu. MainActivity affiche l'état du jeu.
|
||||
- SharedPreferences : Game utilise SharedPreferences pour sauvegarder et charger le meilleur score et l'état du jeu.
|
||||
*/
|
||||
|
||||
package legion.muyue.best2048;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Random;
|
||||
import android.util.Log;
|
||||
|
||||
public class Game {
|
||||
|
||||
private int[][] gameBoard;
|
||||
private Random random;
|
||||
private int score = 0;
|
||||
private int highScore = 0;
|
||||
private int[][] board; // Renommé
|
||||
private final Random randomNumberGenerator; // Renommé
|
||||
private int currentScore = 0; // Renommé
|
||||
private int highestScore = 0; // Renommé
|
||||
private static final int BOARD_SIZE = 4; // Ajout constante
|
||||
private static final String PREFS_NAME = "Best2048_Prefs"; // Ajout constante
|
||||
private static final String HIGH_SCORE_KEY = "high_score"; // Ajout constante
|
||||
private static final String GAME_STATE_KEY = "game_state"; // Ajout constante
|
||||
private final Context context; // Ajout contexte
|
||||
private boolean gameWon = false; // Ajout état
|
||||
private boolean gameOver = false; // Ajout état
|
||||
|
||||
|
||||
public Game() {
|
||||
gameBoard = new int[4][4];
|
||||
random = new Random();
|
||||
/**
|
||||
* Constructeur principal de la classe Game.
|
||||
*
|
||||
* @param context Le contexte Android (généralement une Activity). Nécessaire pour accéder aux SharedPreferences.
|
||||
*/
|
||||
public Game(Context context) {
|
||||
this.context = context;
|
||||
this.randomNumberGenerator = new Random();
|
||||
this.board = new int[BOARD_SIZE][BOARD_SIZE];
|
||||
loadHighScore();
|
||||
}
|
||||
// Nouveau constructeur pour désérialisation
|
||||
public Game(int[][] board, int score, int highScore) {
|
||||
gameBoard = board;
|
||||
this.score = score;
|
||||
this.highScore = highScore;
|
||||
random = new Random(); // Toujours initialiser
|
||||
loadGameState(); // Charge l'état précédent ou initialise
|
||||
}
|
||||
|
||||
public int getGameBoard(int x, int y) {
|
||||
return gameBoard[x][y];
|
||||
/**
|
||||
* Constructeur pour la restauration d'une partie précédemment sauvegardée.
|
||||
*
|
||||
* @param board Le plateau de jeu restauré (l'état des tuiles).
|
||||
* @param score Le score au moment de la sauvegarde.
|
||||
* @param highScore Le meilleur score enregistré au moment de la sauvegarde.
|
||||
* @param context Le contexte Android, nécessaire pour les SharedPreferences.
|
||||
*/
|
||||
public Game(int[][] board, int score, int highScore, Context context) {
|
||||
this.board = board;
|
||||
this.currentScore = score;
|
||||
this.highestScore = highScore;
|
||||
this.context = context;
|
||||
this.randomNumberGenerator = new Random();
|
||||
}
|
||||
|
||||
public int getScore() {
|
||||
return score;
|
||||
/**
|
||||
* Récupère la valeur d'une cellule spécifique sur le plateau.
|
||||
*
|
||||
* @param row L'index de la ligne (0 à BOARD_SIZE - 1).
|
||||
* @param column L'index de la colonne (0 à BOARD_SIZE - 1).
|
||||
* @return La valeur de la cellule à la position spécifiée.
|
||||
* @throws IllegalArgumentException Si row ou column sont en dehors des limites du plateau.
|
||||
*/
|
||||
public int getCellValue(int row, int column) {
|
||||
if (row < 0 || row >= BOARD_SIZE || column < 0 || column >= BOARD_SIZE) {
|
||||
throw new IllegalArgumentException("Indices de ligne ou de colonne hors limites : row=" + row + ", column=" + column);
|
||||
}
|
||||
return this.board[row][column];
|
||||
}
|
||||
|
||||
public int getHighScore() {
|
||||
return highScore;
|
||||
/**
|
||||
* Définit la valeur d'une cellule spécifique sur le plateau.
|
||||
*
|
||||
* @param row L'index de la ligne (0 à BOARD_SIZE - 1).
|
||||
* @param col L'index de la colonne (0 à BOARD_SIZE - 1).
|
||||
* @param value La nouvelle valeur de la cellule.
|
||||
* @throws IllegalArgumentException Si row ou col sont en dehors des limites du plateau.
|
||||
*/
|
||||
public void setCellValue(int row, int col, int value) {
|
||||
if (row < 0 || row >= BOARD_SIZE || col < 0 || col >= BOARD_SIZE) {
|
||||
throw new IllegalArgumentException("Indices de ligne ou de colonne hors limites : row=" + row + ", col=" + col);
|
||||
}
|
||||
this.board[row][col] = value;
|
||||
}
|
||||
|
||||
// Ajout Setter highScore
|
||||
public void setHighScore(int highScore) {
|
||||
this.highScore = highScore;
|
||||
/**
|
||||
* Récupère le score actuel de la partie.
|
||||
*
|
||||
* @return Le score actuel.
|
||||
*/
|
||||
public int getCurrentScore() {
|
||||
return this.currentScore;
|
||||
}
|
||||
|
||||
/**
|
||||
* Met à jour le score actuel.
|
||||
*
|
||||
* @param currentScore Le nouveau score actuel.
|
||||
*/
|
||||
public void setCurrentScore(int currentScore) {
|
||||
this.currentScore = currentScore;
|
||||
}
|
||||
|
||||
/**
|
||||
* Récupère le meilleur score enregistré.
|
||||
*
|
||||
* @return Le meilleur score.
|
||||
*/
|
||||
public int getHighestScore() {
|
||||
return this.highestScore;
|
||||
}
|
||||
|
||||
/**
|
||||
* Met à jour le meilleur score. Utilisé lors du chargement et après une partie.
|
||||
*
|
||||
* @param highestScore Le nouveau meilleur score.
|
||||
*/
|
||||
public void setHighestScore(int highestScore) {
|
||||
this.highestScore = highestScore;
|
||||
}
|
||||
|
||||
/**
|
||||
* Met à jour le board. Utilisé lors du chargement.
|
||||
*
|
||||
* @param board Le nouveau board.
|
||||
*/
|
||||
public void setBoard(int[][] board){
|
||||
this.board = board;
|
||||
}
|
||||
|
||||
/**
|
||||
* Récupère le générateur de nombres aléatoires.
|
||||
*
|
||||
* @return Le générateur de nombres aléatoires.
|
||||
*/
|
||||
public Random getRandomNumberGenerator() {
|
||||
return this.randomNumberGenerator;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indique si le joueur a gagné la partie (atteint une tuile de 2048).
|
||||
*
|
||||
* @return true si le joueur a gagné, false sinon.
|
||||
*/
|
||||
public boolean isGameWon() {
|
||||
return this.gameWon;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indique si la partie est terminée (plus de mouvements possibles).
|
||||
*
|
||||
* @return true si la partie est terminée, false sinon.
|
||||
*/
|
||||
public boolean isGameOver() {
|
||||
return this.gameOver;
|
||||
}
|
||||
|
||||
/**
|
||||
* Définit l'état de la partie gagnée.
|
||||
*
|
||||
* @param gameWon true si le joueur a gagné, false sinon.
|
||||
*/
|
||||
public void setGameWon(boolean gameWon) {
|
||||
this.gameWon = gameWon;
|
||||
}
|
||||
|
||||
/**
|
||||
* Définit l'état de la partie terminée.
|
||||
*
|
||||
* @param gameOver true si la partie est terminée, false sinon.
|
||||
*/
|
||||
public void setGameOver(boolean gameOver) {
|
||||
this.gameOver = gameOver;
|
||||
}
|
||||
|
||||
/**
|
||||
* Charge le meilleur score à partir des préférences partagées (SharedPreferences).
|
||||
*/
|
||||
public void loadHighScore() {
|
||||
// highScore = sharedPreferences.getInt("high_score", 0); // Stub
|
||||
SharedPreferences prefs = context.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE);
|
||||
setHighestScore(prefs.getInt(HIGH_SCORE_KEY, 0));
|
||||
}
|
||||
|
||||
/**
|
||||
* Enregistre le meilleur score actuel dans les préférences partagées (SharedPreferences).
|
||||
*/
|
||||
public void saveHighScore() {
|
||||
/* SharedPreferences.Editor editor = sharedPreferences.edit();
|
||||
editor.putInt("high_score", highScore);
|
||||
editor.apply(); */ // Stub
|
||||
SharedPreferences prefs = context.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE);
|
||||
SharedPreferences.Editor editor = prefs.edit();
|
||||
editor.putInt(HIGH_SCORE_KEY, getHighestScore());
|
||||
editor.apply();
|
||||
}
|
||||
|
||||
public void addNewNumbers() {
|
||||
List<int[]> emptySpaces = new ArrayList<>();
|
||||
|
||||
for (int x = 0; x < 4; x++) {
|
||||
for (int y = 0; y < 4; y++) {
|
||||
if (gameBoard[x][y] == 0) {
|
||||
emptySpaces.add(new int[]{x, y});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!emptySpaces.isEmpty()) {
|
||||
int choice = random.nextInt(emptySpaces.size());
|
||||
int[] coordinates = emptySpaces.get(choice);
|
||||
int x = coordinates[0];
|
||||
int y = coordinates[1];
|
||||
int newNumber;
|
||||
int randomNumber = random.nextInt(100);
|
||||
|
||||
if (randomNumber < 85) {
|
||||
newNumber = 2;
|
||||
} else if (randomNumber < 95) {
|
||||
newNumber = 4;
|
||||
/**
|
||||
* Charge l'état de la partie (plateau, score) à partir des préférences partagées.
|
||||
*/
|
||||
private void loadGameState() {
|
||||
SharedPreferences prefs = context.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE);
|
||||
String savedState = prefs.getString(GAME_STATE_KEY, null);
|
||||
if (savedState != null) {
|
||||
Game savedGame = deserialize(savedState, context);
|
||||
if (savedGame != null) { // Vérifier si la désérialisation a réussi
|
||||
setBoard(savedGame.board);
|
||||
setCurrentScore(savedGame.currentScore);
|
||||
// Le meilleur score est déjà chargé par loadHighScore() appelé dans le constructeur principal
|
||||
} else {
|
||||
newNumber = 8;
|
||||
// Si l'état sauvegardé est invalide, commence une nouvelle partie
|
||||
initializeNewBoard();
|
||||
}
|
||||
|
||||
gameBoard[x][y] = newNumber;
|
||||
} else {
|
||||
// Si aucun état n'est sauvegardé, commence une nouvelle partie
|
||||
initializeNewBoard();
|
||||
}
|
||||
}
|
||||
|
||||
public void pushUp() {
|
||||
Log.d("Game", "Pushing Up");
|
||||
boolean[] alreadyCombined = new boolean[4];
|
||||
for (int y = 0; y < 4; y++) {
|
||||
alreadyCombined = new boolean[4];
|
||||
for (int x = 1; x < 4; x++) {
|
||||
if (gameBoard[x][y] != 0) {
|
||||
int value = gameBoard[x][y]; int currentX = x;
|
||||
while (currentX > 0 && gameBoard[currentX - 1][y] == 0) { currentX--; }
|
||||
if (currentX == 0) { gameBoard[0][y] = value; if (currentX != x) gameBoard[x][y] = 0; }
|
||||
else if (gameBoard[currentX - 1][y] != value) { gameBoard[currentX][y] = value; if (currentX != x) gameBoard[x][y] = 0; }
|
||||
else if (!alreadyCombined[currentX - 1]) { gameBoard[currentX - 1][y] *= 2; score += gameBoard[currentX - 1][y]; gameBoard[x][y] = 0; alreadyCombined[currentX - 1] = true; updateHighScore(); }
|
||||
else { gameBoard[currentX][y] = value; if (currentX != x) gameBoard[x][y] = 0; }
|
||||
/**
|
||||
* Initialise le plateau pour une nouvelle partie (typiquement avec deux tuiles).
|
||||
*/
|
||||
private void initializeNewBoard() {
|
||||
this.board = new int[BOARD_SIZE][BOARD_SIZE]; // Assure que le plateau est vide
|
||||
this.currentScore = 0;
|
||||
addNewTile();
|
||||
addNewTile();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Enregistre l'état actuel de la partie (plateau, score) dans les préférences partagées.
|
||||
*/
|
||||
public void saveGameState() {
|
||||
SharedPreferences prefs = context.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE);
|
||||
SharedPreferences.Editor editor = prefs.edit();
|
||||
String serializedState = toString(); // Utilise la méthode toString() pour la sérialisation
|
||||
editor.putString(GAME_STATE_KEY, serializedState);
|
||||
editor.apply();
|
||||
}
|
||||
|
||||
/**
|
||||
* Ajoute une nouvelle tuile aléatoire à une position aléatoire vide sur le plateau si possible.
|
||||
*/
|
||||
public void addNewTile() { // Renommé
|
||||
if (!hasEmptyCell()) {
|
||||
return; // Ne fait rien si le plateau est plein
|
||||
}
|
||||
|
||||
List<int[]> emptyCells = new ArrayList<>();
|
||||
|
||||
// 1. Collecter les coordonnées des cellules vides.
|
||||
for (int row = 0; row < BOARD_SIZE; row++) {
|
||||
for (int col = 0; col < BOARD_SIZE; col++) {
|
||||
if (board[row][col] == 0) {
|
||||
emptyCells.add(new int[]{row, col});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void pushDown() {
|
||||
Log.d("Game", "Pushing Down");
|
||||
boolean[] alreadyCombined = new boolean[4];
|
||||
for (int y = 0; y < 4; y++) {
|
||||
alreadyCombined = new boolean[4];
|
||||
for (int x = 2; x >= 0; x--) {
|
||||
if (gameBoard[x][y] != 0) {
|
||||
int value = gameBoard[x][y]; int currentX = x;
|
||||
while (currentX < 3 && gameBoard[currentX + 1][y] == 0) { currentX++; }
|
||||
if (currentX == 3) { gameBoard[3][y] = value; if(currentX != x) gameBoard[x][y] = 0; }
|
||||
else if (gameBoard[currentX + 1][y] != value) { gameBoard[currentX][y] = value; if(currentX != x) gameBoard[x][y] = 0; }
|
||||
else if (!alreadyCombined[currentX + 1]) { gameBoard[currentX + 1][y] *= 2; score += gameBoard[currentX + 1][y]; gameBoard[x][y] = 0; alreadyCombined[currentX + 1] = true; updateHighScore(); }
|
||||
else { gameBoard[currentX][y] = value; if (currentX != x) gameBoard[x][y] = 0; }
|
||||
}
|
||||
}
|
||||
// 2. S'il y a des cellules vides, ajouter une nouvelle tuile.
|
||||
if (!emptyCells.isEmpty()) {
|
||||
int[] randomCell = emptyCells.get(getRandomNumberGenerator().nextInt(emptyCells.size()));
|
||||
int row = randomCell[0];
|
||||
int col = randomCell[1];
|
||||
|
||||
int value = generateRandomTileValue(); // Utilise la nouvelle logique de probabilité
|
||||
setCellValue(row, col, value);
|
||||
}
|
||||
}
|
||||
|
||||
public void pushLeft() {
|
||||
Log.d("Game", "Pushing Left");
|
||||
boolean[] alreadyCombined = new boolean[4];
|
||||
for (int x = 0; x < 4; x++) {
|
||||
alreadyCombined = new boolean[4];
|
||||
for (int y = 1; y < 4; y++) {
|
||||
if (gameBoard[x][y] != 0) {
|
||||
int value = gameBoard[x][y]; int currentY = y;
|
||||
while (currentY > 0 && gameBoard[x][currentY - 1] == 0) { currentY--; }
|
||||
if (currentY == 0) { gameBoard[x][0] = value; if(currentY != y) gameBoard[x][y] = 0; }
|
||||
else if (gameBoard[x][currentY - 1] != value) { gameBoard[x][currentY] = value; if(currentY != y) gameBoard[x][y] = 0; }
|
||||
else if (!alreadyCombined[currentY - 1]) { gameBoard[x][currentY - 1] *= 2; score += gameBoard[x][currentY - 1]; gameBoard[x][y] = 0; alreadyCombined[currentY - 1] = true; updateHighScore(); }
|
||||
else { gameBoard[x][currentY] = value; if(currentY != y) gameBoard[x][y] = 0; }
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Génère une valeur de tuile aléatoire selon les probabilités définies.
|
||||
*
|
||||
* @return La valeur de la nouvelle tuile (2, 4, 8, ...) avec les probabilités spécifiées.
|
||||
*/
|
||||
private int generateRandomTileValue() { // Logique de probabilité modifiée
|
||||
int randomValue = randomNumberGenerator.nextInt(10000);
|
||||
|
||||
if (randomValue < 8540) { // 85.40%
|
||||
return 2;
|
||||
} else if (randomValue < 9740) { // 12.00%
|
||||
return 4;
|
||||
} else if (randomValue < 9940) { // 2.00%
|
||||
return 8;
|
||||
} else if (randomValue < 9990) { // 0.50%
|
||||
return 16;
|
||||
} else if (randomValue < 9995) { // 0.05%
|
||||
return 32;
|
||||
} else if (randomValue < 9998) { // 0.03%
|
||||
return 64;
|
||||
} else if (randomValue < 9999) { // 0.01%
|
||||
return 128;
|
||||
}else { // 0.01%
|
||||
return 256;
|
||||
}
|
||||
}
|
||||
|
||||
public void pushRight() {
|
||||
Log.d("Game", "Pushing Right");
|
||||
boolean[] alreadyCombined = new boolean[4];
|
||||
for (int x = 0; x < 4; x++) {
|
||||
alreadyCombined = new boolean[4];
|
||||
for (int y = 2; y >= 0; y--) {
|
||||
if (gameBoard[x][y] != 0) {
|
||||
int value = gameBoard[x][y]; int currentY = y;
|
||||
while (currentY < 3 && gameBoard[x][currentY + 1] == 0) { currentY++; }
|
||||
if (currentY == 3) { gameBoard[x][3] = value; if (currentY != y) gameBoard[x][y] = 0; }
|
||||
else if (gameBoard[x][currentY + 1] != value) { gameBoard[x][currentY] = value; if(currentY != y) gameBoard[x][y] = 0; }
|
||||
else if (!alreadyCombined[currentY + 1]) { gameBoard[x][currentY + 1] *= 2; score += gameBoard[x][currentY + 1]; gameBoard[x][y] = 0; alreadyCombined[currentY + 1] = true; updateHighScore(); }
|
||||
else { gameBoard[x][currentY] = value; if (currentY != y) gameBoard[x][y] = 0; }
|
||||
/**
|
||||
* Déplace les tuiles vers le haut, en fusionnant les tuiles de même valeur.
|
||||
*
|
||||
* @return True si le plateau a été modifié, False sinon.
|
||||
*/
|
||||
public boolean pushUp() { // Logique refactorisée, retourne boolean, utilise hasMerged
|
||||
boolean boardChanged = false;
|
||||
boolean[] hasMerged = new boolean[BOARD_SIZE];
|
||||
|
||||
for (int col = 0; col < BOARD_SIZE; col++) {
|
||||
hasMerged = new boolean[BOARD_SIZE]; // Réinitialise par colonne
|
||||
for (int row = 1; row < BOARD_SIZE; row++) {
|
||||
if (getCellValue(row, col) != 0) {
|
||||
int currentValue = getCellValue(row, col);
|
||||
int currentRow = row;
|
||||
|
||||
// 1. Déplacer la tuile
|
||||
while (currentRow > 0 && getCellValue(currentRow - 1, col) == 0) {
|
||||
setCellValue(currentRow - 1, col, currentValue);
|
||||
setCellValue(currentRow, col, 0);
|
||||
currentRow--;
|
||||
boardChanged = true;
|
||||
}
|
||||
|
||||
// 2. Fusionner si possible
|
||||
if (currentRow > 0 && getCellValue(currentRow - 1, col) == currentValue && !hasMerged[currentRow - 1]) {
|
||||
int newValue = getCellValue(currentRow - 1, col) * 2;
|
||||
setCellValue(currentRow - 1, col, newValue);
|
||||
setCellValue(currentRow, col, 0); // La tuile d'origine disparaît
|
||||
setCurrentScore(getCurrentScore() + newValue);
|
||||
hasMerged[currentRow - 1] = true;
|
||||
boardChanged = true;
|
||||
updateHighestScore(); // Met à jour le meilleur score si nécessaire
|
||||
// checkWinCondition(); // Vérifie si la victoire est atteinte
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
checkWinCondition(); // Vérifie après tous les mouvements de la colonne
|
||||
checkGameOverCondition(); // Vérifie si la partie est terminée
|
||||
return boardChanged;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Déplace les tuiles vers le bas, en fusionnant les tuiles de même valeur.
|
||||
*
|
||||
* @return True si le plateau a été modifié, False sinon.
|
||||
*/
|
||||
public boolean pushDown() { // Logique refactorisée
|
||||
boolean boardChanged = false;
|
||||
boolean[] hasMerged = new boolean[BOARD_SIZE];
|
||||
|
||||
for (int col = 0; col < BOARD_SIZE; col++) {
|
||||
hasMerged = new boolean[BOARD_SIZE];
|
||||
for (int row = BOARD_SIZE - 2; row >= 0; row--) { // Itère de bas en haut
|
||||
if (getCellValue(row, col) != 0) {
|
||||
int currentValue = getCellValue(row, col);
|
||||
int currentRow = row;
|
||||
|
||||
// Déplacer vers le bas
|
||||
while (currentRow < BOARD_SIZE - 1 && getCellValue(currentRow + 1, col) == 0) {
|
||||
setCellValue(currentRow + 1, col, currentValue);
|
||||
setCellValue(currentRow, col, 0);
|
||||
currentRow++;
|
||||
boardChanged = true;
|
||||
}
|
||||
|
||||
// Fusionner si possible
|
||||
if (currentRow < BOARD_SIZE - 1 && getCellValue(currentRow + 1, col) == currentValue && !hasMerged[currentRow + 1]) {
|
||||
int newValue = getCellValue(currentRow + 1, col) * 2;
|
||||
setCellValue(currentRow + 1, col, newValue);
|
||||
setCellValue(currentRow, col, 0);
|
||||
setCurrentScore(getCurrentScore() + newValue);
|
||||
hasMerged[currentRow + 1] = true;
|
||||
boardChanged = true;
|
||||
updateHighestScore();
|
||||
// checkWinCondition();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
checkWinCondition();
|
||||
checkGameOverCondition();
|
||||
return boardChanged;
|
||||
}
|
||||
|
||||
/**
|
||||
* Déplace les tuiles vers la gauche, en fusionnant les tuiles de même valeur.
|
||||
*
|
||||
* @return True si le plateau a été modifié, False sinon.
|
||||
*/
|
||||
public boolean pushLeft() { // Logique refactorisée
|
||||
boolean boardChanged = false;
|
||||
boolean[] hasMerged = new boolean[BOARD_SIZE]; // Par ligne cette fois
|
||||
|
||||
for (int row = 0; row < BOARD_SIZE; row++) {
|
||||
hasMerged = new boolean[BOARD_SIZE]; // Réinitialise par ligne
|
||||
for (int col = 1; col < BOARD_SIZE; col++) { // Commence à la 2ème colonne
|
||||
if (getCellValue(row, col) != 0) {
|
||||
int currentValue = getCellValue(row, col);
|
||||
int currentCol = col;
|
||||
|
||||
// Déplacer vers la gauche
|
||||
while (currentCol > 0 && getCellValue(row, currentCol - 1) == 0) {
|
||||
setCellValue(row, currentCol - 1, currentValue);
|
||||
setCellValue(row, currentCol, 0);
|
||||
currentCol--;
|
||||
boardChanged = true;
|
||||
}
|
||||
|
||||
// Fusionner si possible
|
||||
if (currentCol > 0 && getCellValue(row, currentCol - 1) == currentValue && !hasMerged[currentCol - 1]) {
|
||||
int newValue = getCellValue(row, currentCol - 1) * 2;
|
||||
setCellValue(row, currentCol - 1, newValue);
|
||||
setCellValue(row, currentCol, 0);
|
||||
setCurrentScore(getCurrentScore() + newValue);
|
||||
hasMerged[currentCol - 1] = true;
|
||||
boardChanged = true;
|
||||
updateHighestScore();
|
||||
// checkWinCondition();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
checkWinCondition();
|
||||
checkGameOverCondition();
|
||||
return boardChanged;
|
||||
}
|
||||
|
||||
/**
|
||||
* Déplace les tuiles vers la droite, en fusionnant les tuiles de même valeur.
|
||||
*
|
||||
* @return True si le plateau a été modifié, False sinon.
|
||||
*/
|
||||
public boolean pushRight() { // Logique refactorisée
|
||||
boolean boardChanged = false;
|
||||
boolean[] hasMerged = new boolean[BOARD_SIZE]; // Par ligne
|
||||
|
||||
for (int row = 0; row < BOARD_SIZE; row++) {
|
||||
hasMerged = new boolean[BOARD_SIZE];
|
||||
for (int col = BOARD_SIZE - 2; col >= 0; col--) { // Itère de droite à gauche
|
||||
if (getCellValue(row, col) != 0) {
|
||||
int currentValue = getCellValue(row, col);
|
||||
int currentCol = col;
|
||||
|
||||
// Déplacer vers la droite
|
||||
while (currentCol < BOARD_SIZE - 1 && getCellValue(row, currentCol + 1) == 0) {
|
||||
setCellValue(row, currentCol + 1, currentValue);
|
||||
setCellValue(row, currentCol, 0);
|
||||
currentCol++;
|
||||
boardChanged = true;
|
||||
}
|
||||
|
||||
// Fusionner si possible
|
||||
if (currentCol < BOARD_SIZE - 1 && getCellValue(row, currentCol + 1) == currentValue && !hasMerged[currentCol + 1]) {
|
||||
int newValue = getCellValue(row, currentCol + 1) * 2;
|
||||
setCellValue(row, currentCol + 1, newValue);
|
||||
setCellValue(row, currentCol, 0);
|
||||
setCurrentScore(getCurrentScore() + newValue);
|
||||
hasMerged[currentCol + 1] = true;
|
||||
boardChanged = true;
|
||||
updateHighestScore();
|
||||
// checkWinCondition();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
checkWinCondition();
|
||||
checkGameOverCondition();
|
||||
return boardChanged;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Met à jour le meilleur score si le score actuel le dépasse, et sauvegarde le nouveau meilleur score.
|
||||
*/
|
||||
private void updateHighestScore() { // Renommé
|
||||
if (getCurrentScore() > getHighestScore()) {
|
||||
setHighestScore(getCurrentScore());
|
||||
saveHighScore(); // Sauvegarde implémentée
|
||||
}
|
||||
}
|
||||
|
||||
private void updateHighScore() {
|
||||
if (score > highScore) {
|
||||
highScore = score;
|
||||
saveHighScore(); // Appel stub
|
||||
}
|
||||
}
|
||||
|
||||
// Nouvelle méthode Sérialisation
|
||||
public String serialize() {
|
||||
/**
|
||||
* Sérialise l'état du jeu (plateau, score courant, meilleur score) en une chaîne de caractères.
|
||||
* Format: "row1col1,row1col2,...,row1colN,row2col1,...,rowNcolN,currentScore,highestScore"
|
||||
*
|
||||
* @return Une chaîne représentant l'état complet du jeu.
|
||||
*/
|
||||
@NonNull
|
||||
@Override
|
||||
public String toString() { // Renommé et Override
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (int x = 0; x < 4; x++) {
|
||||
for (int y = 0; y < 4; y++) {
|
||||
sb.append(gameBoard[x][y]).append(",");
|
||||
|
||||
for (int row = 0; row < BOARD_SIZE; row++) {
|
||||
for (int col = 0; col < BOARD_SIZE; col++) {
|
||||
sb.append(board[row][col]).append(","); // Utilise board directement
|
||||
}
|
||||
}
|
||||
sb.append(score).append(",");
|
||||
sb.append(highScore); // Ajout highScore
|
||||
|
||||
sb.append(getCurrentScore()).append(",");
|
||||
sb.append(getHighestScore()); // Sérialise highestScore
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
// Nouvelle méthode Désérialisation
|
||||
public static Game deserialize(String serializedData) {
|
||||
String[] data = serializedData.split(",");
|
||||
int[][] board = new int[4][4];
|
||||
|
||||
/**
|
||||
* Désérialise une chaîne de caractères pour restaurer l'état du jeu.
|
||||
*
|
||||
* @param serializedState La chaîne représentant l'état du jeu (au format de la méthode toString).
|
||||
* @param context Le contexte Android (nécessaire pour le constructeur de Game).
|
||||
* @return Un nouvel objet Game restauré, ou null si la désérialisation échoue.
|
||||
*/
|
||||
public static Game deserialize(String serializedState, Context context) { // Logique améliorée
|
||||
if (serializedState == null || serializedState.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
String[] values = serializedState.split(",");
|
||||
// Vérifie si le nombre d'éléments correspond à la taille attendue (board + score + highScore)
|
||||
if (values.length != (BOARD_SIZE * BOARD_SIZE + 2)) {
|
||||
System.err.println("Erreur de désérialisation : nombre d'éléments incorrect. Attendu=" + (BOARD_SIZE * BOARD_SIZE + 2) + ", Obtenu=" + values.length);
|
||||
return null; // Longueur incorrecte
|
||||
}
|
||||
|
||||
int[][] newBoard = new int[BOARD_SIZE][BOARD_SIZE];
|
||||
int index = 0;
|
||||
for (int x = 0; x < 4; x++) {
|
||||
for (int y = 0; y < 4; y++) {
|
||||
board[x][y] = Integer.parseInt(data[index++]);
|
||||
}
|
||||
}
|
||||
int score = Integer.parseInt(data[index++]);
|
||||
int highScore = Integer.parseInt(data[index++]); // Lecture highScore
|
||||
|
||||
return new Game(board, score, highScore); // Appel nouveau constructeur
|
||||
try {
|
||||
for (int row = 0; row < BOARD_SIZE; row++) {
|
||||
for (int col = 0; col < BOARD_SIZE; col++) {
|
||||
newBoard[row][col] = Integer.parseInt(values[index++]);
|
||||
}
|
||||
}
|
||||
|
||||
// printArray reste défini
|
||||
public void printArray() {
|
||||
for (int[] row : gameBoard) {
|
||||
String rowString = String.format("%6d%6d%6d%6d%n", row[0], row[1], row[2], row[3]);
|
||||
Log.d("Game", rowString);
|
||||
int score = Integer.parseInt(values[index++]);
|
||||
int highScore = Integer.parseInt(values[index++]); // Désérialise highScore
|
||||
|
||||
// Utilise le constructeur qui prend le contexte
|
||||
return new Game(newBoard, score, highScore, context);
|
||||
|
||||
} catch (NumberFormatException | ArrayIndexOutOfBoundsException e) {
|
||||
System.err.println("Erreur de désérialisation : " + e.getMessage());
|
||||
// e.printStackTrace(); // Optionnel: pour plus de détails dans Logcat
|
||||
return null; // Erreur de format ou index hors limites
|
||||
}
|
||||
Log.d("Game", "\n");
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Vérifie si la condition de victoire (tuile 2048) est remplie.
|
||||
* Met à jour la variable gameWon.
|
||||
*/
|
||||
private void checkWinCondition() { // Ajouté
|
||||
// Ne vérifie que si la partie n'est pas déjà gagnée
|
||||
if (!isGameWon()) {
|
||||
for(int row = 0 ; row < BOARD_SIZE; row++){
|
||||
for (int col = 0; col < BOARD_SIZE; col++){
|
||||
if(getCellValue(row, col) >= 2048){ // Condition >= 2048
|
||||
setGameWon(true);
|
||||
// Optionnel : On pourrait arrêter la vérification ici
|
||||
// return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Vérifie si la condition de fin de partie (aucun mouvement possible) est remplie.
|
||||
* Met à jour la variable gameOver.
|
||||
*/
|
||||
private void checkGameOverCondition() { // Ajouté
|
||||
// Si une case est vide, la partie n'est pas terminée
|
||||
if (hasEmptyCell()) {
|
||||
setGameOver(false);
|
||||
return;
|
||||
}
|
||||
|
||||
// Vérifie les fusions possibles horizontalement et verticalement
|
||||
for (int row = 0; row < BOARD_SIZE; row++) {
|
||||
for (int col = 0; col < BOARD_SIZE; col++) {
|
||||
int currentValue = getCellValue(row, col);
|
||||
|
||||
// Vérifie voisin du haut
|
||||
if (row > 0 && getCellValue(row - 1, col) == currentValue) {
|
||||
setGameOver(false);
|
||||
return;
|
||||
}
|
||||
// Vérifie voisin du bas
|
||||
if (row < BOARD_SIZE - 1 && getCellValue(row + 1, col) == currentValue) {
|
||||
setGameOver(false);
|
||||
return;
|
||||
}
|
||||
// Vérifie voisin de gauche
|
||||
if (col > 0 && getCellValue(row, col - 1) == currentValue) {
|
||||
setGameOver(false);
|
||||
return;
|
||||
}
|
||||
// Vérifie voisin de droite
|
||||
if (col < BOARD_SIZE - 1 && getCellValue(row, col + 1) == currentValue) {
|
||||
setGameOver(false);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Si aucune case vide et aucune fusion possible, la partie est terminée
|
||||
setGameOver(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Vérifie s'il existe au moins une cellule vide sur le plateau.
|
||||
*
|
||||
* @return true s'il y a une cellule vide, false sinon.
|
||||
*/
|
||||
private boolean hasEmptyCell() { // Ajouté
|
||||
for (int row = 0; row < BOARD_SIZE; row++) {
|
||||
for (int col = 0; col < BOARD_SIZE; col++) {
|
||||
if (getCellValue(row, col) == 0) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
@ -1,12 +1,45 @@
|
||||
// Fichier MainActivity.java
|
||||
// C'est l'activité principale de l'application, l'écran principal avec lequel l'utilisateur interagit.
|
||||
/*
|
||||
Fonctions principales :
|
||||
- Gère l'interface utilisateur :
|
||||
- Initialise le GridLayout (gameBoardLayout).
|
||||
- Met à jour les TextView pour le score (scoreTextView) et le meilleur score (highScoreTextView).
|
||||
- updateUI() : Redessine la grille en fonction de l'état de l'objet Game.
|
||||
- setTileAppearance() : Applique le style visuel (couleur, taille du texte) aux tuiles.
|
||||
- Gère les entrées utilisateur (swipes) via OnSwipeTouchListener.
|
||||
- Gère les clics sur les boutons (Recommencer, Statistiques, Menu, Multijoueur).
|
||||
- Affiche des boîtes de dialogue (confirmation de redémarrage, statistiques).
|
||||
- Gère le cycle de vie de l'activité :
|
||||
- onCreate() : Initialisation.
|
||||
- onResume() : Reprise de l'application.
|
||||
- onPause() : Sauvegarde de l'état et des statistiques.
|
||||
- Gère la persistance des données (SharedPreferences) : état du jeu, meilleur score, statistiques.
|
||||
- showStats(), hideStats() : Gère l'affichage/masquage des statistiques (ViewStub).
|
||||
- Gère les statistiques du jeu.
|
||||
|
||||
Relations :
|
||||
- Game : Interagit constamment avec l'objet Game.
|
||||
- OnSwipeTouchListener : Détection des swipes.
|
||||
- res/layout/*.xml : Utilise les fichiers de layout pour l'interface utilisateur.
|
||||
- res/drawable/*.xml : Apparence des tuiles et boutons.
|
||||
- res/values/*.xml : Couleurs, dimensions, chaînes, styles.
|
||||
- res/anim/*.xml : Animation du bouton multijoueur.
|
||||
- SharedPreferences : Sauvegarde et chargement des données.
|
||||
*/
|
||||
|
||||
package legion.muyue.best2048;
|
||||
|
||||
import android.app.AlertDialog; // Ajout
|
||||
import android.content.SharedPreferences; // Ajout
|
||||
import android.annotation.SuppressLint; // Ajout pour ClickableViewAccessibility
|
||||
import android.app.AlertDialog;
|
||||
import android.content.Context; // Non utilisé directement mais ok
|
||||
import android.content.SharedPreferences;
|
||||
import android.os.Bundle;
|
||||
import android.util.TypedValue;
|
||||
import android.view.Gravity;
|
||||
import android.view.LayoutInflater; // Ajout
|
||||
import android.view.View; // Ajout
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewStub; // Ajout pour ViewStub
|
||||
import android.view.animation.AnimationUtils;
|
||||
import android.widget.TextView;
|
||||
|
||||
@ -19,17 +52,52 @@ import android.widget.Button;
|
||||
|
||||
public class MainActivity extends AppCompatActivity {
|
||||
|
||||
private GridLayout gameBoardLayout;
|
||||
private TextView scoreTextView;
|
||||
private TextView highScoreTextView;
|
||||
// UI Elements (renommés/ajoutés)
|
||||
private GridLayout boardGridLayout;
|
||||
private TextView currentScoreTextView;
|
||||
private TextView highestScoreTextView;
|
||||
private Button newGameButton;
|
||||
private Button multiplayerButton;
|
||||
private Button statisticsButton;
|
||||
private Button menuButton;
|
||||
private ViewStub statisticsViewStub; // Ajout pour stats
|
||||
|
||||
// Game Logic
|
||||
private Game game;
|
||||
private SharedPreferences sharedPreferences; // Ajout
|
||||
private static final int BOARD_SIZE = 4; // Ajout constante
|
||||
|
||||
// Constantes SharedPreferences
|
||||
private static final String PREFS_NAME = "MyPrefs";
|
||||
private static final String GAME_STATE_KEY = "gameState";
|
||||
private static final String HIGH_SCORE_KEY = "highScore";
|
||||
// Preferences
|
||||
private SharedPreferences preferences; // Renommé
|
||||
// Constantes déjà définies dans Game, redéfinies ici ? Ok pour l'instant.
|
||||
private static final String PREFS_NAME = "Best2048_Prefs";
|
||||
private static final String HIGH_SCORE_KEY = "high_score";
|
||||
private static final String GAME_STATE_KEY = "game_state";
|
||||
|
||||
// Statistics (nouveaux champs)
|
||||
private boolean statisticsVisible = false;
|
||||
private int totalGamesPlayed = 0;
|
||||
private int totalGamesStarted = 0;
|
||||
private int totalMoves = 0;
|
||||
private int currentMoves = 0;
|
||||
private long totalPlayTimeMs = 0;
|
||||
private long currentGameStartTimeMs = 0;
|
||||
private int mergesThisGame = 0;
|
||||
private int totalMerges = 0;
|
||||
private int highestTile = 0;
|
||||
private int numberOfTimesObjectiveReached = 0;
|
||||
private int perfectGames = 0;
|
||||
private long averageGameTimeMs = 0;
|
||||
private long bestWinningTimeMs = Long.MAX_VALUE;
|
||||
private long worstWinningTimeMs = 0;
|
||||
|
||||
// Multiplayer Statistics (nouveaux champs)
|
||||
private int multiplayerGamesWon = 0;
|
||||
private int multiplayerGamesPlayed = 0;
|
||||
private int multiplayerBestWinningStreak = 0;
|
||||
private int multiplayerAverageScore = 0;
|
||||
private long multiplayerAverageGameTimeMs = 0;
|
||||
private int totalMultiplayerLosses = 0;
|
||||
private int multiplayerHighestScore = 0;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
@ -37,167 +105,241 @@ public class MainActivity extends AppCompatActivity {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.activity_main);
|
||||
|
||||
gameBoardLayout = findViewById(R.id.gameBoard);
|
||||
scoreTextView = findViewById(R.id.scoreLabel);
|
||||
highScoreTextView = findViewById(R.id.highScoreLabel);
|
||||
Button restartButton = findViewById(R.id.restartButton);
|
||||
Button statsButton = findViewById(R.id.statsButton);
|
||||
Button menuButton = findViewById(R.id.menuButton);
|
||||
Button multiplayerButton = findViewById(R.id.multiplayerButton);
|
||||
findViews(); // Refactorisation onCreate
|
||||
initializeGame(); // Refactorisation onCreate
|
||||
setupListeners(); // Refactorisation onCreate
|
||||
}
|
||||
|
||||
// Initialisation SharedPreferences
|
||||
sharedPreferences = getSharedPreferences(PREFS_NAME, MODE_PRIVATE);
|
||||
// Getters/Setters ajoutés (pour UI et Game)
|
||||
public void setBoardGridLayout(GridLayout boardGridLayout) { this.boardGridLayout = boardGridLayout; }
|
||||
public void setCurrentScoreTextView(TextView currentScoreTextView) { this.currentScoreTextView = currentScoreTextView; }
|
||||
public void setHighestScoreTextView(TextView highestScoreTextView) { this.highestScoreTextView = highestScoreTextView; }
|
||||
public void setNewGameButton(Button newGameButton) { this.newGameButton = newGameButton; }
|
||||
public void setMultiplayerButton(Button multiplayerButton) { this.multiplayerButton = multiplayerButton; }
|
||||
public void setStatisticsButton(Button statisticsButton) { this.statisticsButton = statisticsButton; }
|
||||
public void setMenuButton(Button menuButton) { this.menuButton = menuButton; }
|
||||
public void setStatisticsViewStub(ViewStub statisticsViewStub) { this.statisticsViewStub = statisticsViewStub; }
|
||||
public void setPreferences(SharedPreferences preferences) { this.preferences = preferences; }
|
||||
public void setGame(Game game) { this.game = game; }
|
||||
public Button getNewGameButton() { return this.newGameButton; }
|
||||
public Button getMultiplayerButton() { return this.multiplayerButton; }
|
||||
public Button getStatisticsButton() { return this.statisticsButton; }
|
||||
public Button getMenuButton() { return this.menuButton; }
|
||||
public GridLayout getBoardGridLayout() { return this.boardGridLayout; }
|
||||
public Game getGame() { return this.game; }
|
||||
public TextView getCurrentScoreTextView() { return this.currentScoreTextView; }
|
||||
public TextView getHighestScoreTextView() { return this.highestScoreTextView; }
|
||||
|
||||
initGameBoardLayout();
|
||||
/**
|
||||
* Initialise les références aux éléments de l'interface utilisateur (UI).
|
||||
*/
|
||||
private void findViews() { // Nouvelle méthode
|
||||
setBoardGridLayout(findViewById(R.id.gameBoard));
|
||||
setCurrentScoreTextView(findViewById(R.id.scoreLabel));
|
||||
setHighestScoreTextView(findViewById(R.id.highScoreLabel));
|
||||
setNewGameButton(findViewById(R.id.restartButton)); // ID reste restartButton
|
||||
setStatisticsButton(findViewById(R.id.statsButton)); // ID reste statsButton
|
||||
setMenuButton(findViewById(R.id.menuButton)); // ID reste menuButton
|
||||
setMultiplayerButton(findViewById(R.id.multiplayerButton));
|
||||
setStatisticsViewStub(findViewById(R.id.statsViewStub)); // Ajout ViewStub
|
||||
}
|
||||
|
||||
// Chargement du jeu (ou création si pas de sauvegarde)
|
||||
loadGame();
|
||||
/**
|
||||
* Initialise l'objet Game, charge le meilleur score et restaure l'état de la partie (si disponible).
|
||||
*/
|
||||
private void initializeGame() { // Nouvelle méthode
|
||||
setPreferences(getSharedPreferences(PREFS_NAME, MODE_PRIVATE));
|
||||
setGame(new Game(this)); // Passe le contexte à Game
|
||||
// loadGame(); // Est appelé implicitement par le constructeur de Game via loadGameState
|
||||
updateUI(); // MAJ initiale de l'UI
|
||||
loadStatistics(); // Charge les stats
|
||||
// Initialise le timer pour la partie chargée ou nouvelle
|
||||
currentGameStartTimeMs = System.currentTimeMillis(); // Démarre timer ici
|
||||
totalGamesStarted++; // Incrémente car une partie commence (nouvelle ou chargée)
|
||||
}
|
||||
|
||||
setupSwipeListener();
|
||||
|
||||
//Listeners des boutons
|
||||
multiplayerButton.setOnClickListener(v -> {
|
||||
/**
|
||||
* Configure les listeners pour les interactions de l'utilisateur (boutons, swipes).
|
||||
*/
|
||||
private void setupListeners() { // Nouvelle méthode (contenu déplacé)
|
||||
getNewGameButton().setOnClickListener(v -> {
|
||||
v.startAnimation(AnimationUtils.loadAnimation(this, R.anim.button_press));
|
||||
showMultiplayerScreen();
|
||||
// Appelle maintenant showRestartConfirmationDialog au lieu de startNewGame directement
|
||||
showRestartConfirmationDialog();
|
||||
});
|
||||
// restartGame appelle maintenant la dialog
|
||||
restartButton.setOnClickListener(v -> restartGame());
|
||||
statsButton.setOnClickListener(v -> showStats());
|
||||
menuButton.setOnClickListener(v -> showMenu());
|
||||
|
||||
getStatisticsButton().setOnClickListener(v -> {
|
||||
v.startAnimation(AnimationUtils.loadAnimation(this, R.anim.button_press));
|
||||
showStatistics(); // Appelle la nouvelle méthode pour les stats
|
||||
});
|
||||
|
||||
getMenuButton().setOnClickListener(v -> {
|
||||
v.startAnimation(AnimationUtils.loadAnimation(this, R.anim.button_press));
|
||||
showMenu(); // Appelle le stub de menu
|
||||
});
|
||||
|
||||
getMultiplayerButton().setOnClickListener(v -> {
|
||||
v.startAnimation(AnimationUtils.loadAnimation(this, R.anim.button_press));
|
||||
showMultiplayerScreen(); // Appelle le stub multijoueur
|
||||
});
|
||||
|
||||
setupSwipeListener(); // Configure le swipe listener
|
||||
}
|
||||
|
||||
private void initGameBoardLayout() {
|
||||
gameBoardLayout.removeAllViews();
|
||||
gameBoardLayout.setColumnCount(4);
|
||||
gameBoardLayout.setRowCount(4);
|
||||
/**
|
||||
* Initialise le GridLayout qui représente le plateau de jeu. Définit le nombre de lignes et colonnes.
|
||||
*/
|
||||
private void initGameBoardLayout() { // Inchangé mais utilise getter
|
||||
getBoardGridLayout().removeAllViews();
|
||||
getBoardGridLayout().setColumnCount(BOARD_SIZE);
|
||||
getBoardGridLayout().setRowCount(BOARD_SIZE);
|
||||
}
|
||||
|
||||
private void updateUI() {
|
||||
gameBoardLayout.removeAllViews();
|
||||
/**
|
||||
* Met à jour l'ensemble de l'interface utilisateur : le plateau, le score actuel et le meilleur score.
|
||||
*/
|
||||
private void updateUI() { // Refactorisé
|
||||
updateBoard();
|
||||
updateScores();
|
||||
}
|
||||
|
||||
for (int x = 0; x < 4; x++) {
|
||||
for (int y = 0; y < 4; y++) {
|
||||
/**
|
||||
* Met à jour l'affichage des tuiles sur le plateau de jeu.
|
||||
*/
|
||||
private void updateBoard() { // Nouvelle méthode (contenu de l'ancien updateUI partie grille)
|
||||
getBoardGridLayout().removeAllViews();
|
||||
|
||||
for (int row = 0; row < BOARD_SIZE; row++) {
|
||||
for (int col = 0; col < BOARD_SIZE; col++) {
|
||||
TextView tileTextView = new TextView(this);
|
||||
int value = game.getGameBoard(x, y);
|
||||
int value = getGame().getCellValue(row, col); // Utilise getter renommé
|
||||
|
||||
setTileAppearance(tileTextView, value);
|
||||
setTileStyle(tileTextView, value); // Utilise méthode renommée
|
||||
|
||||
GridLayout.LayoutParams params = new GridLayout.LayoutParams();
|
||||
params.width = 0;
|
||||
params.height = 0;
|
||||
params.rowSpec = GridLayout.spec(x, 1f);
|
||||
params.columnSpec = GridLayout.spec(y, 1f);
|
||||
params.setMargins(
|
||||
(int) getResources().getDimension(R.dimen.tile_margin),
|
||||
(int) getResources().getDimension(R.dimen.tile_margin),
|
||||
(int) getResources().getDimension(R.dimen.tile_margin),
|
||||
(int) getResources().getDimension(R.dimen.tile_margin)
|
||||
);
|
||||
params.rowSpec = GridLayout.spec(row, 1f);
|
||||
params.columnSpec = GridLayout.spec(col, 1f);
|
||||
|
||||
int margin = (int) getResources().getDimension(R.dimen.tile_margin);
|
||||
params.setMargins(margin, margin, margin, margin);
|
||||
|
||||
tileTextView.setLayoutParams(params);
|
||||
|
||||
gameBoardLayout.addView(tileTextView);
|
||||
getBoardGridLayout().addView(tileTextView);
|
||||
}
|
||||
}
|
||||
scoreTextView.setText(getString(R.string.score_placeholder, game.getScore()));
|
||||
highScoreTextView.setText(getString(R.string.high_score_placeholder, game.getHighScore()));
|
||||
}
|
||||
|
||||
private void setTileAppearance(TextView tile, int value) {
|
||||
/**
|
||||
* Met à jour les TextViews du score actuel et du meilleur score.
|
||||
*/
|
||||
private void updateScores() { // Nouvelle méthode (contenu de l'ancien updateUI partie score)
|
||||
getCurrentScoreTextView().setText(getString(R.string.score_placeholder, getGame().getCurrentScore()));
|
||||
getHighestScoreTextView().setText(getString(R.string.high_score_placeholder, getGame().getHighestScore()));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Définit l'apparence d'une tuile (couleur de fond, couleur du texte, taille du texte)
|
||||
* en fonction de sa valeur. Utilise un seul Drawable avec des teintes de couleur.
|
||||
*
|
||||
* @param tileTextView La TextView représentant la tuile.
|
||||
* @param value La valeur de la tuile (0, 2, 4, 8, ...).
|
||||
*/
|
||||
private void setTileStyle(TextView tileTextView, int value) { // Renommé et logique couleur texte corrigée
|
||||
|
||||
tileTextView.setText(value > 0 ? String.valueOf(value) : "");
|
||||
tileTextView.setGravity(Gravity.CENTER);
|
||||
tileTextView.setTypeface(null, android.graphics.Typeface.BOLD); // Ajout police grasse
|
||||
|
||||
int backgroundColorId;
|
||||
int textColorId;
|
||||
int textSizeId;
|
||||
|
||||
switch (value) {
|
||||
case 0:
|
||||
backgroundColorId = R.color.tile_empty;
|
||||
textColorId = android.R.color.transparent;
|
||||
textSizeId = R.dimen.text_size_tile_small;
|
||||
break;
|
||||
case 2:
|
||||
tile.setBackgroundResource(R.drawable.tile_background);
|
||||
tile.getBackground().setTint(ContextCompat.getColor(this, R.color.tile_2_color));
|
||||
backgroundColorId = R.color.tile_2;
|
||||
textColorId = R.color.text_tile_low;
|
||||
textSizeId = R.dimen.text_size_tile_small;
|
||||
break;
|
||||
case 4:
|
||||
tile.setBackgroundResource(R.drawable.tile_background);
|
||||
tile.getBackground().setTint(ContextCompat.getColor(this, R.color.tile_4_color));
|
||||
backgroundColorId = R.color.tile_4;
|
||||
textColorId = R.color.text_tile_low;
|
||||
textSizeId = R.dimen.text_size_tile_small;
|
||||
break;
|
||||
case 8:
|
||||
tile.setBackgroundResource(R.drawable.tile_background);
|
||||
tile.getBackground().setTint(ContextCompat.getColor(this, R.color.tile_8_color));
|
||||
textColorId = R.color.text_tile_low; // Devrait être high
|
||||
backgroundColorId = R.color.tile_8;
|
||||
textColorId = R.color.text_tile_high; // Correction couleur texte
|
||||
textSizeId = R.dimen.text_size_tile_small;
|
||||
break;
|
||||
case 16:
|
||||
tile.setBackgroundResource(R.drawable.tile_background);
|
||||
tile.getBackground().setTint(ContextCompat.getColor(this, R.color.tile_16_color));
|
||||
textColorId = R.color.text_tile_low; // Devrait être high
|
||||
backgroundColorId = R.color.tile_16;
|
||||
textColorId = R.color.text_tile_high; // Correction
|
||||
textSizeId = R.dimen.text_size_tile_small;
|
||||
break;
|
||||
case 32:
|
||||
tile.setBackgroundResource(R.drawable.tile_background);
|
||||
tile.getBackground().setTint(ContextCompat.getColor(this, R.color.tile_32_color));
|
||||
textColorId = R.color.text_tile_low; // Devrait être high
|
||||
backgroundColorId = R.color.tile_32;
|
||||
textColorId = R.color.text_tile_high; // Correction
|
||||
textSizeId = R.dimen.text_size_tile_small;
|
||||
break;
|
||||
case 64:
|
||||
tile.setBackgroundResource(R.drawable.tile_background);
|
||||
tile.getBackground().setTint(ContextCompat.getColor(this, R.color.tile_64_color));
|
||||
textColorId = R.color.text_tile_low; // Devrait être high
|
||||
backgroundColorId = R.color.tile_64;
|
||||
textColorId = R.color.text_tile_high; // Correction
|
||||
textSizeId = R.dimen.text_size_tile_small;
|
||||
break;
|
||||
case 128:
|
||||
tile.setBackgroundResource(R.drawable.tile_background);
|
||||
tile.getBackground().setTint(ContextCompat.getColor(this, R.color.tile_128_color));
|
||||
backgroundColorId = R.color.tile_128;
|
||||
textColorId = R.color.text_tile_high;
|
||||
textSizeId = R.dimen.text_size_tile_medium;
|
||||
textSizeId = R.dimen.text_size_tile_medium; // Changement taille
|
||||
break;
|
||||
case 256:
|
||||
tile.setBackgroundResource(R.drawable.tile_background);
|
||||
tile.getBackground().setTint(ContextCompat.getColor(this, R.color.tile_256_color));
|
||||
backgroundColorId = R.color.tile_256;
|
||||
textColorId = R.color.text_tile_high;
|
||||
textSizeId = R.dimen.text_size_tile_medium;
|
||||
break;
|
||||
case 512:
|
||||
tile.setBackgroundResource(R.drawable.tile_background);
|
||||
tile.getBackground().setTint(ContextCompat.getColor(this, R.color.tile_512_color));
|
||||
backgroundColorId = R.color.tile_512;
|
||||
textColorId = R.color.text_tile_high;
|
||||
textSizeId = R.dimen.text_size_tile_medium;
|
||||
break;
|
||||
case 1024:
|
||||
tile.setBackgroundResource(R.drawable.tile_background);
|
||||
tile.getBackground().setTint(ContextCompat.getColor(this, R.color.tile_1024_color));
|
||||
backgroundColorId = R.color.tile_1024;
|
||||
textColorId = R.color.text_tile_high;
|
||||
textSizeId = R.dimen.text_size_tile_large;
|
||||
textSizeId = R.dimen.text_size_tile_large; // Changement taille
|
||||
break;
|
||||
case 2048:
|
||||
tile.setBackgroundResource(R.drawable.tile_background);
|
||||
tile.getBackground().setTint(ContextCompat.getColor(this, R.color.tile_2048_color));
|
||||
backgroundColorId = R.color.tile_2048;
|
||||
textColorId = R.color.text_tile_high;
|
||||
textSizeId = R.dimen.text_size_tile_large;
|
||||
break;
|
||||
default:
|
||||
tile.setBackgroundResource(R.drawable.tile_background);
|
||||
tile.getBackground().setTint(ContextCompat.getColor(this, R.color.tile_empty_color));
|
||||
textColorId = android.R.color.transparent;
|
||||
textSizeId = R.dimen.text_size_tile_small;
|
||||
if (value > 2048) {
|
||||
tile.getBackground().setTint(ContextCompat.getColor(this, R.color.tile_super_color));
|
||||
default: // > 2048
|
||||
backgroundColorId = R.color.tile_super;
|
||||
textColorId = R.color.text_tile_high;
|
||||
textSizeId = R.dimen.text_size_tile_large;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (value > 0) {
|
||||
tile.setText(String.valueOf(value));
|
||||
tile.setTextColor(ContextCompat.getColor(this, textColorId));
|
||||
tile.setTextSize(TypedValue.COMPLEX_UNIT_PX, getResources().getDimension(textSizeId));
|
||||
} else {
|
||||
tile.setText("");
|
||||
}
|
||||
tile.setGravity(Gravity.CENTER);
|
||||
tileTextView.setBackgroundResource(R.drawable.tile_background); // Utilise drawable unique
|
||||
tileTextView.getBackground().setTint(ContextCompat.getColor(this, backgroundColorId)); // Applique teinte
|
||||
|
||||
tileTextView.setTextColor(ContextCompat.getColor(this, textColorId));
|
||||
tileTextView.setTextSize(TypedValue.COMPLEX_UNIT_PX, getResources().getDimension(textSizeId));
|
||||
}
|
||||
|
||||
private void setupSwipeListener() {
|
||||
gameBoardLayout.setOnTouchListener(new OnSwipeTouchListener(MainActivity.this, new OnSwipeTouchListener.SwipeListener() {
|
||||
|
||||
/**
|
||||
* Configure le listener de swipe sur le GridLayout pour gérer les mouvements des tuiles.
|
||||
*/
|
||||
@SuppressLint("ClickableViewAccessibility") // Ajout annotation
|
||||
private void setupSwipeListener() { // Inchangé mais utilise getter
|
||||
getBoardGridLayout().setOnTouchListener(new OnSwipeTouchListener(this, new OnSwipeTouchListener.SwipeListener() {
|
||||
@Override public void onSwipeTop() { handleSwipe(Direction.UP); }
|
||||
@Override public void onSwipeBottom() { handleSwipe(Direction.DOWN); }
|
||||
@Override public void onSwipeLeft() { handleSwipe(Direction.LEFT); }
|
||||
@ -205,104 +347,394 @@ public class MainActivity extends AppCompatActivity {
|
||||
}));
|
||||
}
|
||||
|
||||
private void handleSwipe(Direction direction) {
|
||||
/**
|
||||
* Gère un swipe dans une direction donnée, en appelant la méthode appropriée de la classe Game
|
||||
* et en mettant à jour l'interface utilisateur.
|
||||
*
|
||||
* @param direction La direction du swipe (UP, DOWN, LEFT, RIGHT).
|
||||
*/
|
||||
private void handleSwipe(Direction direction) { // Logique modifiée pour stats et win/loss
|
||||
boolean boardChanged; // Vérifie si le plateau a changé
|
||||
switch (direction) {
|
||||
case UP: game.pushUp(); break;
|
||||
case DOWN: game.pushDown(); break;
|
||||
case LEFT: game.pushLeft(); break;
|
||||
case RIGHT: game.pushRight(); break;
|
||||
}
|
||||
game.addNewNumbers();
|
||||
updateUI();
|
||||
case UP: boardChanged = getGame().pushUp(); break;
|
||||
case DOWN: boardChanged = getGame().pushDown(); break;
|
||||
case LEFT: boardChanged = getGame().pushLeft(); break;
|
||||
case RIGHT: boardChanged = getGame().pushRight(); break;
|
||||
default: boardChanged = false;
|
||||
}
|
||||
|
||||
// Modifié pour montrer la dialog
|
||||
private void restartGame() {
|
||||
showRestartConfirmationDialog();
|
||||
if (boardChanged) {
|
||||
currentMoves++; // Stat
|
||||
totalMoves++; // Stat
|
||||
// mergesThisGame++; // Fusion est comptée dans Game ou ici? (pas dans ce snippet)
|
||||
totalMerges++; // Approximation, devrait être compté par fusion réelle
|
||||
|
||||
// Trouve la tuile la plus haute pour les stats
|
||||
int currentHighest = 0;
|
||||
for (int row = 0; row < BOARD_SIZE; row++) {
|
||||
for (int col = 0; col < BOARD_SIZE; col++) {
|
||||
if (game.getCellValue(row, col) > currentHighest) {
|
||||
currentHighest = game.getCellValue(row, col);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (currentHighest > highestTile) {
|
||||
highestTile = currentHighest; // Met à jour stat highestTile
|
||||
}
|
||||
|
||||
// Nouvelle méthode pour la dialog
|
||||
private void showRestartConfirmationDialog() {
|
||||
|
||||
getGame().addNewTile(); // Ajoute une nouvelle tuile
|
||||
updateUI(); // Met à jour l'affichage
|
||||
|
||||
// Vérifie victoire ou défaite
|
||||
if (getGame().isGameWon()) {
|
||||
// Gérer la victoire (par exemple, afficher un message)
|
||||
if(game.getCellValue(0,0) == 2048){ //Condition spécifique du snippet ? semble être un placeholder
|
||||
numberOfTimesObjectiveReached++; // Stat
|
||||
// Gérer les temps de victoire (best/worst)
|
||||
long timeTaken = System.currentTimeMillis() - currentGameStartTimeMs;
|
||||
if(timeTaken < bestWinningTimeMs){
|
||||
bestWinningTimeMs = timeTaken;
|
||||
}
|
||||
if(timeTaken > worstWinningTimeMs){
|
||||
worstWinningTimeMs = timeTaken;
|
||||
}
|
||||
}
|
||||
showGameWonDialog(); // Affiche dialog victoire
|
||||
} else if (getGame().isGameOver()) {
|
||||
// Gérer la défaite
|
||||
totalGamesPlayed++; // Stat
|
||||
showGameOverDialog(); // Affiche dialog défaite
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Énumération représentant les directions possibles de swipe.
|
||||
*/
|
||||
enum Direction { UP, DOWN, LEFT, RIGHT }
|
||||
|
||||
/**
|
||||
* Affiche une boîte de dialogue de confirmation avant de recommencer la partie.
|
||||
* Utilise un layout XML personnalisé (dialog_restart_confirm.xml).
|
||||
*/
|
||||
private void showRestartConfirmationDialog() { // Inchangé mais appelle startNewGame
|
||||
AlertDialog.Builder builder = new AlertDialog.Builder(this);
|
||||
LayoutInflater inflater = getLayoutInflater();
|
||||
View dialogView = inflater.inflate(R.layout.dialog_restart_confirm, null);
|
||||
builder.setView(dialogView);
|
||||
|
||||
// Références vues dialog
|
||||
TextView dialogTitle = dialogView.findViewById(R.id.dialogTitle); // Non utilisé mais présent dans snippet
|
||||
TextView dialogMessage = dialogView.findViewById(R.id.dialogMessage); // Non utilisé mais présent dans snippet
|
||||
TextView dialogTitle = dialogView.findViewById(R.id.dialogTitle);
|
||||
TextView dialogMessage = dialogView.findViewById(R.id.dialogMessage);
|
||||
Button cancelButton = dialogView.findViewById(R.id.dialogCancelButton);
|
||||
Button confirmButton = dialogView.findViewById(R.id.dialogConfirmButton);
|
||||
|
||||
final AlertDialog dialog = builder.create();
|
||||
|
||||
cancelButton.setOnClickListener(v -> {
|
||||
dialog.dismiss();
|
||||
});
|
||||
cancelButton.setOnClickListener(v -> dialog.dismiss());
|
||||
|
||||
confirmButton.setOnClickListener(v -> {
|
||||
dialog.dismiss();
|
||||
game = new Game(); // Création nouveau jeu
|
||||
game.addNewNumbers(); // Ajout tuiles initiales
|
||||
game.addNewNumbers();
|
||||
updateUI(); // Mise à jour UI
|
||||
startNewGame(); // Appelle la nouvelle méthode
|
||||
});
|
||||
|
||||
dialog.show();
|
||||
}
|
||||
|
||||
|
||||
private void showStats() {
|
||||
//A faire
|
||||
}
|
||||
private void showMenu() {
|
||||
//A faire
|
||||
/**
|
||||
* Commence une nouvelle partie : réinitialise le jeu et les statistiques de la partie en cours.
|
||||
*/
|
||||
private void startNewGame() { // Nouvelle méthode (anciennement restartGame) + reset stats
|
||||
totalGamesStarted++; // Stat
|
||||
currentMoves = 0; // Reset stat partie
|
||||
mergesThisGame = 0; // Reset stat partie
|
||||
currentGameStartTimeMs = System.currentTimeMillis(); // Reset timer partie
|
||||
setGame(new Game(this)); // Crée un nouveau jeu
|
||||
// game.addNewTile(); // Le constructeur de Game s'en charge maintenant
|
||||
// game.addNewTile();
|
||||
updateUI(); // Met à jour l'affichage
|
||||
}
|
||||
|
||||
private void showMultiplayerScreen() {
|
||||
//A faire
|
||||
}
|
||||
|
||||
// Ajout onPause
|
||||
@Override
|
||||
protected void onPause() {
|
||||
super.onPause();
|
||||
saveGame(); // Sauvegarde en quittant
|
||||
}
|
||||
/**
|
||||
* Affiche les statistiques du jeu en utilisant un ViewStub pour le chargement différé.
|
||||
*/
|
||||
private void showStatistics() { // Nouvelle méthode
|
||||
statisticsVisible = !statisticsVisible;
|
||||
|
||||
// Nouvelle méthode sauvegarde
|
||||
private void saveGame() {
|
||||
SharedPreferences.Editor editor = sharedPreferences.edit();
|
||||
editor.putString(GAME_STATE_KEY, game.serialize()); // Sauvegarde état sérialisé
|
||||
editor.putInt(HIGH_SCORE_KEY, game.getHighScore()); // Sauvegarde high score
|
||||
editor.apply(); // Appliquer
|
||||
if (statisticsVisible) {
|
||||
if (statisticsViewStub.getParent() != null) {
|
||||
statisticsViewStub.inflate();
|
||||
}
|
||||
|
||||
// Nouvelle méthode chargement
|
||||
private void loadGame() {
|
||||
String gameState = sharedPreferences.getString(GAME_STATE_KEY, null); // Récupère état
|
||||
if (gameState != null) {
|
||||
game = Game.deserialize(gameState); // Restaure si existe
|
||||
// game.loadHighScore(); // Pas dans le snippet loadGame
|
||||
updateStatisticsTextViews(); // Met à jour les textes des stats
|
||||
findViewById(R.id.statistics_layout).setVisibility(View.VISIBLE); // Rend visible le layout gonflé
|
||||
getMultiplayerButton().setVisibility(View.GONE); // Cache bouton multi
|
||||
} else {
|
||||
game = new Game(); // Nouveau jeu sinon
|
||||
game.addNewNumbers();
|
||||
game.addNewNumbers();
|
||||
hideStatistics(); // Cache si on reclique
|
||||
}
|
||||
game.loadHighScore(); // Présent dans le snippet loadGame
|
||||
updateUI(); // MAJ UI
|
||||
|
||||
// Logique high score séparée comme dans snippet
|
||||
int savedHighScore = sharedPreferences.getInt(HIGH_SCORE_KEY, 0);
|
||||
if (savedHighScore > game.getHighScore()) {
|
||||
game.setHighScore(savedHighScore);
|
||||
// updateUI(); // Pas de second updateUI dans le snippet ici
|
||||
}
|
||||
|
||||
/**
|
||||
* Met à jour tous les TextViews à l'intérieur du layout des statistiques (qui doit déjà être gonflé).
|
||||
*/
|
||||
private void updateStatisticsTextViews() { // Nouvelle méthode
|
||||
View inflatedStatsView = findViewById(R.id.statistics_layout); // Récupère la vue gonflée
|
||||
if (inflatedStatsView != null) { // Vérifie si la vue existe
|
||||
TextView highScoreStatsLabel = inflatedStatsView.findViewById(R.id.high_score_stats_label);
|
||||
TextView totalGamesPlayedLabel = inflatedStatsView.findViewById(R.id.total_games_played_label);
|
||||
TextView totalGamesStartedLabel = inflatedStatsView.findViewById(R.id.total_games_started_label);
|
||||
TextView winPercentageLabel = inflatedStatsView.findViewById(R.id.win_percentage_label);
|
||||
TextView totalPlayTimeLabel = inflatedStatsView.findViewById(R.id.total_play_time_label);
|
||||
TextView totalMovesLabel = inflatedStatsView.findViewById(R.id.total_moves_label);
|
||||
TextView currentMovesLabel = inflatedStatsView.findViewById(R.id.current_moves_label);
|
||||
TextView currentGameTimeLabel = inflatedStatsView.findViewById(R.id.current_game_time_label);
|
||||
TextView averageGameTimeLabel = inflatedStatsView.findViewById(R.id.average_game_time_label); // Pour solo
|
||||
TextView bestWinningTimeLabel = inflatedStatsView.findViewById(R.id.best_winning_time_label);
|
||||
TextView worstWinningTimeLabel = inflatedStatsView.findViewById(R.id.worst_winning_time_label);
|
||||
TextView totalMergesLabel = inflatedStatsView.findViewById(R.id.total_merges_label);
|
||||
TextView highestTileLabel = inflatedStatsView.findViewById(R.id.highest_tile_label);
|
||||
TextView objectiveReachedLabel = inflatedStatsView.findViewById(R.id.number_of_time_objective_reached_label);
|
||||
TextView perfectGameLabel = inflatedStatsView.findViewById(R.id.perfect_game_label);
|
||||
TextView multiplayerGamesWonLabel = inflatedStatsView.findViewById(R.id.multiplayer_games_won_label);
|
||||
TextView multiplayerGamesPlayedLabel = inflatedStatsView.findViewById(R.id.multiplayer_games_played_label);
|
||||
TextView multiplayerWinRateLabel = inflatedStatsView.findViewById(R.id.multiplayer_win_rate_label);
|
||||
TextView multiplayerBestWinningStreakLabel = inflatedStatsView.findViewById(R.id.multiplayer_best_winning_streak_label);
|
||||
TextView multiplayerAverageScoreLabel = inflatedStatsView.findViewById(R.id.multiplayer_average_score_label);
|
||||
TextView averageTimePerGameMultiLabel = inflatedStatsView.findViewById(R.id.average_time_per_game_label); // ID dupliqué? Utilisons le 2eme
|
||||
TextView totalMultiplayerLossesLabel = inflatedStatsView.findViewById(R.id.total_multiplayer_losses_label);
|
||||
TextView multiplayerHighScoreLabel = inflatedStatsView.findViewById(R.id.multiplayer_high_score_label);
|
||||
TextView mergesThisGameLabel = inflatedStatsView.findViewById(R.id.merges_this_game);
|
||||
|
||||
// MAJ Textes
|
||||
highScoreStatsLabel.setText(getString(R.string.high_score_stats, game.getHighestScore()));
|
||||
totalGamesPlayedLabel.setText(getString(R.string.total_games_played, totalGamesPlayed));
|
||||
totalGamesStartedLabel.setText(getString(R.string.total_games_started, totalGamesStarted));
|
||||
totalMovesLabel.setText(getString(R.string.total_moves, totalMoves));
|
||||
currentMovesLabel.setText(getString(R.string.current_moves, currentMoves));
|
||||
mergesThisGameLabel.setText(getString(R.string.merges_this_game_label, mergesThisGame));
|
||||
totalMergesLabel.setText(getString(R.string.total_merges, totalMerges));
|
||||
highestTileLabel.setText(getString(R.string.highest_tile, highestTile));
|
||||
objectiveReachedLabel.setText(getString(R.string.number_of_time_objective_reached, numberOfTimesObjectiveReached));
|
||||
perfectGameLabel.setText(getString(R.string.perfect_games, perfectGames)); // Il manque la stat perfectGames dans les champs
|
||||
multiplayerGamesWonLabel.setText(getString(R.string.multiplayer_games_won, multiplayerGamesWon));
|
||||
multiplayerGamesPlayedLabel.setText(getString(R.string.multiplayer_games_played, multiplayerGamesPlayed));
|
||||
multiplayerBestWinningStreakLabel.setText(getString(R.string.multiplayer_best_winning_streak, multiplayerBestWinningStreak));
|
||||
multiplayerAverageScoreLabel.setText(getString(R.string.multiplayer_average_score, multiplayerGamesPlayed > 0 ? (totalMerges / multiplayerGamesPlayed) : 0)); // Calcul approximatif
|
||||
totalMultiplayerLossesLabel.setText(getString(R.string.total_multiplayer_losses, totalMultiplayerLosses));
|
||||
multiplayerHighScoreLabel.setText(getString(R.string.multiplayer_high_score, multiplayerHighestScore));
|
||||
|
||||
// Calculs Pourcentages et Temps
|
||||
String winPercentage = (totalGamesStarted > 0) ? String.format("%.2f%%", ((double) numberOfTimesObjectiveReached / totalGamesStarted) * 100) : "N/A"; // Utilise numberOfTimesObjectiveReached
|
||||
winPercentageLabel.setText(getString(R.string.win_percentage, winPercentage));
|
||||
|
||||
String multiplayerWinRate = (multiplayerGamesPlayed > 0) ? String.format("%.2f%%", ((double) multiplayerGamesWon / multiplayerGamesPlayed) * 100) : "N/A";
|
||||
multiplayerWinRateLabel.setText(getString(R.string.multiplayer_win_rate, multiplayerWinRate));
|
||||
|
||||
totalPlayTimeLabel.setText(getString(R.string.total_play_time, formatTime(totalPlayTimeMs)));
|
||||
// Calcule le temps de la partie en cours dynamiquement si elle n'est pas finie
|
||||
long currentDuration = (game != null && !game.isGameOver() && !game.isGameWon()) ? System.currentTimeMillis() - currentGameStartTimeMs : 0;
|
||||
currentGameTimeLabel.setText(getString(R.string.current_game_time, formatTime(currentDuration)));
|
||||
|
||||
long avgTimeSolo = (numberOfTimesObjectiveReached > 0) ? totalPlayTimeMs / numberOfTimesObjectiveReached : 0; // Basé sur victoires? Ou parties jouées?
|
||||
averageGameTimeLabel.setText(getString(R.string.average_time_per_game, formatTime(avgTimeSolo))); // ID 1
|
||||
|
||||
long avgTimeMulti = (multiplayerGamesPlayed > 0) ? multiplayerAverageGameTimeMs / multiplayerGamesPlayed : 0;
|
||||
averageTimePerGameMultiLabel.setText(getString(R.string.average_time_per_game_label, formatTime(avgTimeMulti))); // ID 2
|
||||
|
||||
bestWinningTimeLabel.setText(getString(R.string.best_winning_time, (bestWinningTimeMs != Long.MAX_VALUE) ? formatTime(bestWinningTimeMs) : "N/A"));
|
||||
worstWinningTimeLabel.setText(getString(R.string.worst_winning_time, (worstWinningTimeMs != 0) ? formatTime(worstWinningTimeMs) : "N/A"));
|
||||
|
||||
// Listener bouton Back dans les stats
|
||||
Button backButton = inflatedStatsView.findViewById(R.id.backButton);
|
||||
backButton.setOnClickListener(v -> hideStatistics());
|
||||
}
|
||||
}
|
||||
|
||||
private enum Direction {
|
||||
UP, DOWN, LEFT, RIGHT
|
||||
/**
|
||||
* Masque la vue des statistiques et réaffiche le bouton multijoueur.
|
||||
*/
|
||||
private void hideStatistics() { // Nouvelle méthode
|
||||
View inflatedStatsView = findViewById(R.id.statistics_layout);
|
||||
if (inflatedStatsView != null) {
|
||||
inflatedStatsView.setVisibility(View.GONE);
|
||||
}
|
||||
getMultiplayerButton().setVisibility(View.VISIBLE); // Réaffiche bouton multi
|
||||
statisticsVisible = false; // Met à jour l'état
|
||||
}
|
||||
|
||||
/**
|
||||
* Charge les statistiques depuis les SharedPreferences.
|
||||
*/
|
||||
private void loadStatistics() { // Nouvelle méthode
|
||||
totalGamesPlayed = preferences.getInt("totalGamesPlayed", 0);
|
||||
totalGamesStarted = preferences.getInt("totalGamesStarted", 0);
|
||||
totalMoves = preferences.getInt("totalMoves", 0);
|
||||
// currentMoves n'est pas chargé, il est spécifique à la session
|
||||
totalPlayTimeMs = preferences.getLong("totalPlayTimeMs", 0);
|
||||
// currentGameStartTimeMs est initialisé dans initializeGame ou repris
|
||||
// mergesThisGame n'est pas chargé
|
||||
totalMerges = preferences.getInt("totalMerges", 0);
|
||||
highestTile = preferences.getInt("highestTile", 0);
|
||||
numberOfTimesObjectiveReached = preferences.getInt("numberOfTimesObjectiveReached", 0);
|
||||
perfectGames = preferences.getInt("perfectGames", 0); // Chargement stat
|
||||
// averageGameTimeMs n'est pas chargé, recalculé
|
||||
bestWinningTimeMs = preferences.getLong("bestWinningTimeMs", Long.MAX_VALUE);
|
||||
worstWinningTimeMs = preferences.getLong("worstWinningTimeMs", 0);
|
||||
|
||||
multiplayerGamesWon = preferences.getInt("multiplayerGamesWon", 0);
|
||||
multiplayerGamesPlayed = preferences.getInt("multiplayerGamesPlayed", 0);
|
||||
multiplayerBestWinningStreak = preferences.getInt("multiplayerBestWinningStreak", 0);
|
||||
// multiplayerAverageScore recalculé
|
||||
multiplayerAverageGameTimeMs = preferences.getLong("multiplayerAverageGameTimeMs", 0);
|
||||
totalMultiplayerLosses = preferences.getInt("totalMultiplayerLosses", 0);
|
||||
multiplayerHighestScore = preferences.getInt("multiplayerHighScore", 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sauvegarde les statistiques dans les SharedPreferences.
|
||||
*/
|
||||
private void saveStatistics() { // Nouvelle méthode
|
||||
SharedPreferences.Editor editor = preferences.edit();
|
||||
editor.putInt("totalGamesPlayed", totalGamesPlayed);
|
||||
editor.putInt("totalGamesStarted", totalGamesStarted);
|
||||
editor.putInt("totalMoves", totalMoves);
|
||||
// currentMoves non sauvegardé
|
||||
editor.putLong("totalPlayTimeMs", totalPlayTimeMs);
|
||||
// currentGameStartTimeMs non sauvegardé directement, utilisé pour calculer totalPlayTimeMs
|
||||
// mergesThisGame non sauvegardé
|
||||
editor.putInt("totalMerges", totalMerges);
|
||||
editor.putInt("highestTile", highestTile);
|
||||
editor.putInt("numberOfTimesObjectiveReached", numberOfTimesObjectiveReached);
|
||||
editor.putInt("perfectGames", perfectGames); // Sauvegarde stat
|
||||
// averageGameTimeMs non sauvegardé
|
||||
editor.putLong("bestWinningTimeMs", bestWinningTimeMs);
|
||||
editor.putLong("worstWinningTimeMs", worstWinningTimeMs);
|
||||
|
||||
editor.putInt("multiplayerGamesWon", multiplayerGamesWon);
|
||||
editor.putInt("multiplayerGamesPlayed", multiplayerGamesPlayed);
|
||||
editor.putInt("multiplayerBestWinningStreak", multiplayerBestWinningStreak);
|
||||
// multiplayerAverageScore non sauvegardé
|
||||
editor.putLong("multiplayerAverageGameTimeMs", multiplayerAverageGameTimeMs);
|
||||
editor.putInt("totalMultiplayerLosses", totalMultiplayerLosses);
|
||||
editor.putInt("multiplayerHighScore", multiplayerHighestScore);
|
||||
|
||||
editor.apply();
|
||||
}
|
||||
|
||||
/**
|
||||
* Formate une durée en millisecondes en une chaîne de caractères (hh:mm:ss ou mm:ss).
|
||||
*/
|
||||
private String formatTime(long milliseconds) { // Nouvelle méthode
|
||||
long seconds = (milliseconds / 1000) % 60;
|
||||
long minutes = (milliseconds / (1000 * 60)) % 60;
|
||||
long hours = milliseconds / (1000 * 60 * 60);
|
||||
|
||||
if (hours > 0) {
|
||||
return String.format("%02d:%02d:%02d", hours, minutes, seconds);
|
||||
} else {
|
||||
return String.format("%02d:%02d", minutes, seconds);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Affiche l'écran du menu (placeholder avec AlertDialog).
|
||||
*/
|
||||
private void showMenu() { // Modifié pour utiliser AlertDialog
|
||||
AlertDialog.Builder builder = new AlertDialog.Builder(this);
|
||||
builder.setTitle("Menu")
|
||||
.setMessage("Fonctionnalité de menu à venir !")
|
||||
.setPositiveButton("OK", (dialog, id) -> { /* Rien */ });
|
||||
AlertDialog dialog = builder.create();
|
||||
dialog.show();
|
||||
}
|
||||
|
||||
/**
|
||||
* Affiche l'écran du mode multijoueur (placeholder avec AlertDialog).
|
||||
*/
|
||||
private void showMultiplayerScreen() { // Modifié pour utiliser AlertDialog
|
||||
AlertDialog.Builder builder = new AlertDialog.Builder(this);
|
||||
builder.setTitle("Multijoueur")
|
||||
.setMessage("Fonctionnalité multijoueur à venir !")
|
||||
.setPositiveButton("OK", (dialog, id) -> { /* Rien */ });
|
||||
AlertDialog dialog = builder.create();
|
||||
dialog.show();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onResume() { // Ajouté/Modifié
|
||||
super.onResume();
|
||||
// Ne redémarre le chrono que si la partie n'est pas finie
|
||||
if (game != null && !game.isGameOver() && !game.isGameWon()) {
|
||||
currentGameStartTimeMs = System.currentTimeMillis();
|
||||
}
|
||||
// Réaffiche les stats si elles étaient visibles
|
||||
if (statisticsVisible) {
|
||||
// Assure-toi que la vue est prête avant de mettre à jour
|
||||
if (findViewById(R.id.statistics_layout) != null) {
|
||||
updateStatisticsTextViews();
|
||||
findViewById(R.id.statistics_layout).setVisibility(View.VISIBLE);
|
||||
getMultiplayerButton().setVisibility(View.GONE);
|
||||
} else {
|
||||
// Si la vue n'est pas encore gonflée, on la montre (ce qui la gonflera et mettra à jour)
|
||||
showStatistics();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPause() { // Modifié pour calculer temps et sauver stats
|
||||
super.onPause();
|
||||
if (game != null) {
|
||||
// Ajoute le temps écoulé seulement si la partie n'est pas finie
|
||||
if (!game.isGameOver() && !game.isGameWon()) {
|
||||
totalPlayTimeMs += System.currentTimeMillis() - currentGameStartTimeMs;
|
||||
}
|
||||
saveGame(); // Sauvegarde état jeu
|
||||
saveStatistics(); // Sauvegarde stats
|
||||
}
|
||||
}
|
||||
|
||||
private void saveGame() {
|
||||
SharedPreferences.Editor editor = preferences.edit();
|
||||
editor.putString(GAME_STATE_KEY, game.toString());
|
||||
editor.putInt(HIGH_SCORE_KEY, game.getHighestScore());
|
||||
editor.apply();
|
||||
}
|
||||
|
||||
/**
|
||||
* Affiche une boîte de dialogue lorsque le joueur gagne la partie (atteint 2048).
|
||||
*/
|
||||
private void showGameWonDialog() { // Ajouté
|
||||
// Met à jour les stats de victoire avant d'afficher
|
||||
numberOfTimesObjectiveReached++;
|
||||
long timeTaken = System.currentTimeMillis() - currentGameStartTimeMs;
|
||||
if(timeTaken < bestWinningTimeMs){ bestWinningTimeMs = timeTaken; }
|
||||
if(timeTaken > worstWinningTimeMs){ worstWinningTimeMs = timeTaken; }
|
||||
|
||||
AlertDialog.Builder builder = new AlertDialog.Builder(this);
|
||||
builder.setTitle("Vous avez gagné !")
|
||||
.setMessage("Félicitations ! Vous avez atteint 2048 !")
|
||||
.setPositiveButton("Nouvelle partie", (dialog, which) -> startNewGame())
|
||||
.setNegativeButton("Quitter", (dialog, which) -> finish())
|
||||
.setCancelable(false)
|
||||
.show();
|
||||
}
|
||||
|
||||
/**
|
||||
* Affiche une boîte de dialogue lorsque le joueur perd la partie (aucun mouvement possible).
|
||||
*/
|
||||
private void showGameOverDialog() { // Ajouté
|
||||
totalGamesPlayed++; // Met à jour stat avant d'afficher
|
||||
|
||||
AlertDialog.Builder builder = new AlertDialog.Builder(this);
|
||||
builder.setTitle("Partie terminée !")
|
||||
.setMessage("Aucun mouvement possible. Votre score final est : " + game.getCurrentScore())
|
||||
.setPositiveButton("Nouvelle partie", (dialog, which) -> startNewGame())
|
||||
.setNegativeButton("Quitter", (dialog, which) -> finish())
|
||||
.setCancelable(false)
|
||||
.show();
|
||||
}
|
||||
|
||||
}
|
@ -1,72 +1,152 @@
|
||||
// Fichier OnSwipeTouchListener.java
|
||||
// Classe utilitaire pour détecter les gestes de balayage (swipe).
|
||||
/*
|
||||
Fonctions principales :
|
||||
- Utilise GestureDetector pour détecter les swipes.
|
||||
- Définit une interface SwipeListener pour notifier la classe appelante (MainActivity).
|
||||
- onFling() : Détecte la direction du swipe (haut, bas, gauche, droite).
|
||||
- SWIPE_THRESHOLD, SWIPE_VELOCITY_THRESHOLD : Constantes pour la sensibilité du swipe.
|
||||
|
||||
Relations :
|
||||
- MainActivity : MainActivity crée une instance et est notifiée des swipes via l'interface SwipeListener.
|
||||
*/
|
||||
|
||||
package legion.muyue.best2048;
|
||||
|
||||
import android.annotation.SuppressLint; // Ajout
|
||||
import android.content.Context;
|
||||
// import android.location.GnssAntennaInfo; // Import inutile trouvé dans le dump
|
||||
import android.view.GestureDetector;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
|
||||
import androidx.annotation.NonNull; // Ajout
|
||||
|
||||
public class OnSwipeTouchListener implements View.OnTouchListener {
|
||||
|
||||
private final GestureDetector gestureDetector;
|
||||
private final SwipeListener listener; // Ajout de l'interface listener
|
||||
private final SwipeListener listener;
|
||||
|
||||
// Interface pour les événements de swipe
|
||||
/**
|
||||
* Interface pour les événements de swipe. Toute classe qui veut réagir aux swipes
|
||||
* doit implémenter cette interface et passer une instance à OnSwipeTouchListener.
|
||||
*/
|
||||
public interface SwipeListener {
|
||||
/**
|
||||
* Appelée lorsqu'un swipe vers le haut est détecté.
|
||||
*/
|
||||
void onSwipeTop();
|
||||
|
||||
/**
|
||||
* Appelée lorsqu'un swipe vers le bas est détecté.
|
||||
*/
|
||||
void onSwipeBottom();
|
||||
|
||||
/**
|
||||
* Appelée lorsqu'un swipe vers la gauche est détecté.
|
||||
*/
|
||||
void onSwipeLeft();
|
||||
|
||||
/**
|
||||
* Appelée lorsqu'un swipe vers la droite est détecté.
|
||||
*/
|
||||
void onSwipeRight();
|
||||
}
|
||||
|
||||
// Constructeur modifié pour accepter le listener
|
||||
/**
|
||||
* Constructeur de la classe OnSwipeTouchListener.
|
||||
*
|
||||
* @param ctx Le contexte de l'application. Nécessaire pour GestureDetector.
|
||||
* @param listener L'instance qui écoutera les événements de swipe.
|
||||
*/
|
||||
public OnSwipeTouchListener(Context ctx, SwipeListener listener) {
|
||||
gestureDetector = new GestureDetector(ctx, new GestureListener());
|
||||
this.listener = listener;
|
||||
}
|
||||
|
||||
/**
|
||||
* Méthode appelée lorsqu'un événement tactile se produit sur la vue attachée.
|
||||
* Elle transmet l'événement au GestureDetector pour analyse.
|
||||
*
|
||||
* @param v La vue sur laquelle l'événement tactile s'est produit.
|
||||
* @param event L'objet MotionEvent décrivant l'événement tactile.
|
||||
* @return true si l'événement a été géré, false sinon.
|
||||
*/
|
||||
@SuppressLint("ClickableViewAccessibility") // Ajout
|
||||
@Override
|
||||
public boolean onTouch(View v, MotionEvent event) {
|
||||
return gestureDetector.onTouchEvent(event);
|
||||
}
|
||||
|
||||
/**
|
||||
* Classe interne qui étend GestureDetector.SimpleOnGestureListener pour gérer
|
||||
* spécifiquement les gestes de balayage (fling).
|
||||
*/
|
||||
private final class GestureListener extends GestureDetector.SimpleOnGestureListener {
|
||||
|
||||
private static final int SWIPE_THRESHOLD = 100;
|
||||
private static final int SWIPE_VELOCITY_THRESHOLD = 100;
|
||||
private static final int SWIPE_THRESHOLD = 100; // Distance minimale du swipe (en pixels).
|
||||
private static final int SWIPE_VELOCITY_THRESHOLD = 100; // Vitesse minimale du swipe (en pixels par seconde).
|
||||
|
||||
/**
|
||||
* Méthode appelée lorsqu'un appui initial sur l'écran est détecté.
|
||||
* On retourne toujours 'true' pour indiquer qu'on gère cet événement.
|
||||
*
|
||||
* @param e L'objet MotionEvent décrivant l'appui initial.
|
||||
* @return true, car on gère toujours l'événement 'onDown'.
|
||||
*/
|
||||
@Override
|
||||
public boolean onDown(MotionEvent e) {
|
||||
public boolean onDown(@NonNull MotionEvent e) { // Ajout @NonNull
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Méthode appelée lorsqu'un geste de balayage (fling) est détecté.
|
||||
*
|
||||
* @param e1 L'objet MotionEvent du premier appui (début du swipe). Peut être null.
|
||||
* @param e2 L'objet MotionEvent de la fin du swipe.
|
||||
* @param velocityX La vitesse du swipe en pixels par seconde sur l'axe X.
|
||||
* @param velocityY La vitesse du swipe en pixels par seconde sur l'axe Y.
|
||||
* @return true si le geste de balayage a été géré, false sinon.
|
||||
*/
|
||||
@Override
|
||||
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
|
||||
public boolean onFling(MotionEvent e1, @NonNull MotionEvent e2, float velocityX, float velocityY) { // Ajout @NonNull
|
||||
// Vérification de nullité pour e1, nécessaire car onFling peut être appelé même si onDown retourne false ou si le geste est complexe.
|
||||
if (e1 == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
boolean result = false;
|
||||
try {
|
||||
float diffY = e2.getY() - e1.getY();
|
||||
float diffX = e2.getX() - e1.getX();
|
||||
float diffY = e2.getY() - e1.getY(); // Différence de position sur l'axe Y.
|
||||
float diffX = e2.getX() - e1.getX(); // Différence de position sur l'axe X.
|
||||
|
||||
// Détermine si le swipe est plutôt horizontal ou vertical.
|
||||
if (Math.abs(diffX) > Math.abs(diffY)) {
|
||||
// Swipe horizontal.
|
||||
if (Math.abs(diffX) > SWIPE_THRESHOLD && Math.abs(velocityX) > SWIPE_VELOCITY_THRESHOLD) {
|
||||
if (diffX > 0) {
|
||||
listener.onSwipeRight(); // Appel via listener
|
||||
listener.onSwipeRight(); // Swipe vers la droite.
|
||||
} else {
|
||||
listener.onSwipeLeft(); // Appel via listener
|
||||
listener.onSwipeLeft(); // Swipe vers la gauche.
|
||||
}
|
||||
result = true;
|
||||
}
|
||||
} else if (Math.abs(diffY) > SWIPE_THRESHOLD && Math.abs(velocityY) > SWIPE_VELOCITY_THRESHOLD) {
|
||||
} else {
|
||||
// Swipe vertical.
|
||||
if (Math.abs(diffY) > SWIPE_THRESHOLD && Math.abs(velocityY) > SWIPE_VELOCITY_THRESHOLD) {
|
||||
if (diffY > 0) {
|
||||
listener.onSwipeBottom(); // Appel via listener
|
||||
listener.onSwipeBottom(); // Swipe vers le bas.
|
||||
} else {
|
||||
listener.onSwipeTop(); // Appel via listener
|
||||
listener.onSwipeTop(); // Swipe vers le haut.
|
||||
}
|
||||
result = true;
|
||||
}
|
||||
}
|
||||
} catch (Exception exception) {
|
||||
exception.printStackTrace();
|
||||
// exception.printStackTrace(); // Commenté dans le nouveau code ? Gardons commenté.
|
||||
exception.fillInStackTrace(); // Gestion des erreurs (journalisation).
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
// Les méthodes locales vides ne sont plus nécessaires car on utilise l'interface
|
||||
}
|
@ -1,15 +1,18 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<set xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:interpolator="@android:anim/accelerate_decelerate_interpolator">
|
||||
|
||||
<scale
|
||||
android:duration="150"
|
||||
android:fromXScale="1.0"
|
||||
android:toXScale="0.95"
|
||||
android:fromYScale="1.0"
|
||||
android:toYScale="0.95"
|
||||
android:pivotX="50%"
|
||||
android:pivotY="50%"
|
||||
android:duration="100" />
|
||||
android:toXScale="0.95"
|
||||
android:toYScale="0.95" />
|
||||
|
||||
<alpha
|
||||
android:duration="150"
|
||||
android:fromAlpha="1.0"
|
||||
android:toAlpha="0.8"
|
||||
android:duration="100" />
|
||||
android:toAlpha="0.8" />
|
||||
</set>
|
@ -1,15 +1,18 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<set xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:interpolator="@android:anim/accelerate_decelerate_interpolator">
|
||||
|
||||
<scale
|
||||
android:duration="150"
|
||||
android:fromXScale="0.95"
|
||||
android:toXScale="1.0"
|
||||
android:fromYScale="0.95"
|
||||
android:toYScale="1.0"
|
||||
android:pivotX="50%"
|
||||
android:pivotY="50%"
|
||||
android:duration="100" />
|
||||
android:toXScale="1.0"
|
||||
android:toYScale="1.0" />
|
||||
|
||||
<alpha
|
||||
android:duration="150"
|
||||
android:fromAlpha="0.8"
|
||||
android:toAlpha="1.0"
|
||||
android:duration="100" />
|
||||
android:toAlpha="1.0" />
|
||||
</set>
|
@ -1,6 +1,35 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:shape="rectangle">
|
||||
<solid android:color="@color/button_background"/>
|
||||
<corners android:radius="4dp"/>
|
||||
</shape>
|
||||
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item android:state_pressed="true">
|
||||
<shape android:shape="rectangle">
|
||||
<gradient
|
||||
android:startColor="@color/multiplayer_button_pressed_start"
|
||||
android:endColor="@color/multiplayer_button_pressed_end"
|
||||
android:angle="270" />
|
||||
<corners android:radius="30dp" /> <padding
|
||||
android:left="16dp"
|
||||
android:top="8dp"
|
||||
android:right="16dp"
|
||||
android:bottom="8dp" />
|
||||
<stroke
|
||||
android:width="2dp"
|
||||
android:color="@color/multiplayer_button_stroke" />
|
||||
</shape>
|
||||
</item>
|
||||
<item>
|
||||
<shape android:shape="rectangle">
|
||||
<gradient
|
||||
android:startColor="@color/multiplayer_button_start"
|
||||
android:endColor="@color/multiplayer_button_end"
|
||||
android:angle="270" />
|
||||
<corners android:radius="30dp" /> <padding
|
||||
android:left="16dp"
|
||||
android:top="8dp"
|
||||
android:right="16dp"
|
||||
android:bottom="8dp" />
|
||||
<stroke
|
||||
android:width="2dp"
|
||||
android:color="@color/multiplayer_button_stroke" />
|
||||
</shape>
|
||||
</item>
|
||||
</selector>
|
@ -2,7 +2,5 @@
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:shape="rectangle">
|
||||
<solid android:color="@color/white"/>
|
||||
<corners android:radius="12dp"/>
|
||||
<stroke android:width="1dp"
|
||||
android:color="@color/game_board_background"/>
|
||||
<corners android:radius="32dp"/>
|
||||
</shape>
|
@ -1,6 +1,12 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:shape="rectangle">
|
||||
<solid android:color="#CDC1B4"/>
|
||||
<corners android:radius="@dimen/corner_radius"/>
|
||||
|
||||
<solid android:color="@color/tile_empty" />
|
||||
<corners android:radius="@dimen/corner_radius" />
|
||||
<padding android:left="@dimen/tile_margin"
|
||||
android:top="@dimen/tile_margin"
|
||||
android:right="@dimen/tile_margin"
|
||||
android:bottom="@dimen/tile_margin" />
|
||||
|
||||
</shape>
|
@ -1,23 +1,21 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="@color/background_color"
|
||||
android:fitsSystemWindows="false"
|
||||
tools:context=".MainActivity">
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/northPanel"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="32dp"
|
||||
android:baselineAligned="false"
|
||||
android:orientation="horizontal"
|
||||
android:layout_marginTop="32dp"
|
||||
android:padding="@dimen/padding_general"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintHorizontal_bias="0.0"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent">
|
||||
|
||||
@ -57,13 +55,14 @@
|
||||
<TextView
|
||||
android:id="@+id/scoreLabel"
|
||||
style="@style/ScoreLabelStyle"
|
||||
android:text="@string/score" />
|
||||
android:text="@string/score_placeholder" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/highScoreLabel"
|
||||
style="@style/ScoreLabelStyle"
|
||||
android:layout_marginTop="@dimen/margin_between_elements"
|
||||
android:text="@string/high_score" />
|
||||
android:text="@string/high_score_placeholder" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
@ -96,8 +95,7 @@
|
||||
<Button
|
||||
android:id="@+id/multiplayerButton"
|
||||
style="@style/LargeButtonStyle"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_width="match_parent" android:layout_height="wrap_content"
|
||||
android:text="@string/multiplayer"
|
||||
app:layout_constraintBottom_toTopOf="@+id/buttons_layout"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
@ -109,10 +107,18 @@
|
||||
android:id="@+id/buttons_layout"
|
||||
layout="@layout/bottom_buttons_layout"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="32dp"
|
||||
android:layout_height="wrap_content"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent" />
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
/>
|
||||
|
||||
<ViewStub
|
||||
android:id="@+id/statsViewStub"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:inflatedId="@+id/statistics_layout"
|
||||
android:layout="@layout/stats_layout" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
270
app/src/main/res/layout/stats_layout.xml
Normal file
270
app/src/main/res/layout/stats_layout.xml
Normal file
@ -0,0 +1,270 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="@color/background_color"
|
||||
tools:context=".MainActivity">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/statsTitle"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="24dp"
|
||||
android:text="@string/stats_title"
|
||||
android:textSize="36sp"
|
||||
android:textStyle="bold"
|
||||
android:textColor="#333333"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<ScrollView
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
android:layout_marginTop="16dp"
|
||||
android:layout_marginBottom="16dp"
|
||||
app:layout_constraintBottom_toTopOf="@+id/backButton"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/statsTitle"
|
||||
android:fillViewport="true">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:padding="16dp">
|
||||
|
||||
<TextView
|
||||
style="@style/SectionTitle"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/general_section" />
|
||||
|
||||
<LinearLayout
|
||||
style="@style/SectionContainer"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/high_score_stats_label"
|
||||
style="@style/StatLabel"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/high_score_stats" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/total_games_played_label"
|
||||
style="@style/StatLabel"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/total_games_played" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/total_games_started_label"
|
||||
style="@style/StatLabel"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/total_games_started" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/win_percentage_label"
|
||||
style="@style/StatLabel"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/win_percentage" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/total_play_time_label"
|
||||
style="@style/StatLabel"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/total_play_time" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/total_moves_label"
|
||||
style="@style/StatLabel"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/total_moves" />
|
||||
</LinearLayout>
|
||||
|
||||
<TextView
|
||||
style="@style/SectionTitle"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/current_game_section" />
|
||||
|
||||
<LinearLayout
|
||||
style="@style/SectionContainer"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/current_moves_label"
|
||||
style="@style/StatLabel"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/current_moves" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/current_game_time_label"
|
||||
style="@style/StatLabel"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/current_game_time" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/merges_this_game"
|
||||
style="@style/StatLabel"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/merges_this_game_label" />
|
||||
</LinearLayout>
|
||||
|
||||
<TextView
|
||||
style="@style/SectionTitle"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/single_player_section" />
|
||||
|
||||
<LinearLayout
|
||||
style="@style/SectionContainer"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/average_game_time_label"
|
||||
style="@style/StatLabel"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/average_time_per_game" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/best_winning_time_label"
|
||||
style="@style/StatLabel"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/best_winning_time" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/worst_winning_time_label"
|
||||
style="@style/StatLabel"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/worst_winning_time" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/total_merges_label"
|
||||
style="@style/StatLabel"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/total_merges" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/highest_tile_label"
|
||||
style="@style/StatLabel"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/highest_tile" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/number_of_time_objective_reached_label"
|
||||
style="@style/StatLabel"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/number_of_time_objective_reached" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/perfect_game_label"
|
||||
style="@style/StatLabel"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/perfect_games" />
|
||||
</LinearLayout>
|
||||
|
||||
<TextView
|
||||
style="@style/SectionTitle"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/multiplayer_section" />
|
||||
|
||||
<LinearLayout
|
||||
style="@style/SectionContainer"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/multiplayer_games_won_label"
|
||||
style="@style/StatLabel"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/multiplayer_games_won" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/multiplayer_games_played_label"
|
||||
style="@style/StatLabel"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/multiplayer_games_played" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/multiplayer_win_rate_label"
|
||||
style="@style/StatLabel"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/multiplayer_win_rate" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/multiplayer_best_winning_streak_label"
|
||||
style="@style/StatLabel"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/multiplayer_best_winning_streak" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/multiplayer_average_score_label"
|
||||
style="@style/StatLabel"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/multiplayer_average_score" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/average_time_per_game_label"
|
||||
style="@style/StatLabel"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/average_time_per_game_label" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/total_multiplayer_losses_label"
|
||||
style="@style/StatLabel"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/total_multiplayer_losses" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/multiplayer_high_score_label"
|
||||
style="@style/StatLabel"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/multiplayer_high_score" />
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
</ScrollView>
|
||||
|
||||
<Button
|
||||
android:id="@+id/backButton"
|
||||
style="@style/ButtonStyle"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="24dp"
|
||||
android:text="@string/back_button_label"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
@ -2,24 +2,35 @@
|
||||
<resources>
|
||||
<color name="black">#FF000000</color>
|
||||
<color name="white">#FFFFFFFF</color>
|
||||
<color name="text_tile_low">#776E65</color>
|
||||
<color name="text_tile_high">#F9F6F2</color>
|
||||
<color name="background_color">#FCFAEE</color>
|
||||
<color name="game_label_background">#EDCC5F</color>
|
||||
<color name="score_label_background">#F75E3E</color>
|
||||
<color name="game_board_background">#A3937D</color>
|
||||
<color name="button_background">#4A443C</color>
|
||||
<color name="tile_empty_color">#C9BCAC</color>
|
||||
<color name="tile_2_color">#EEE4DA</color>
|
||||
<color name="tile_4_color">#EDE0C8</color>
|
||||
<color name="tile_8_color">#F2B179</color>
|
||||
<color name="tile_16_color">#F59563</color>
|
||||
<color name="tile_32_color">#F67C5F</color>
|
||||
<color name="tile_64_color">#F65E3B</color>
|
||||
<color name="tile_128_color">#EDCF72</color>
|
||||
<color name="tile_256_color">#EDCC61</color>
|
||||
<color name="tile_512_color">#EDC850</color>
|
||||
<color name="tile_1024_color">#EEC43F</color>
|
||||
<color name="tile_2048_color">#EEC43F</color>
|
||||
<color name="tile_super_color">#3C3A32</color>
|
||||
|
||||
<color name="text_tile_low">#776e65</color>
|
||||
<color name="text_tile_high">#f9f6f2</color>
|
||||
|
||||
<color name="background_color">#faf8ef</color>
|
||||
<color name="game_board_background">#bbada0</color>
|
||||
<color name="score_label_background">#f65e3b</color>
|
||||
<color name="game_label_background">#edc22e</color>
|
||||
<color name="button_background">#8f7a66</color>
|
||||
<color name="button_text_color">#f9f6f2</color>
|
||||
<color name="tile_empty">#cdc1b4</color>
|
||||
<color name="tile_2">#eee4da</color>
|
||||
<color name="tile_4">#ede0c8</color>
|
||||
<color name="tile_8">#f2b179</color>
|
||||
<color name="tile_16">#f59563</color>
|
||||
<color name="tile_32">#f67c5f</color>
|
||||
<color name="tile_64">#f65e3b</color>
|
||||
<color name="tile_128">#edcf72</color>
|
||||
<color name="tile_256">#edcc61</color>
|
||||
<color name="tile_512">#edc850</color>
|
||||
<color name="tile_1024">#edc53f</color>
|
||||
<color name="tile_2048">#edc22e</color>
|
||||
<color name="tile_super">#3c3a32</color>
|
||||
<color name="multiplayer_button_start">#6200ee</color>
|
||||
<color name="multiplayer_button_end">#3700b3</color>
|
||||
<color name="multiplayer_button_pressed_start">#3700b3</color>
|
||||
<color name="multiplayer_button_pressed_end">#03dac6</color>
|
||||
<color name="multiplayer_button_stroke">#b0bec5</color>
|
||||
|
||||
<color name="stats_background">#875932</color>
|
||||
|
||||
</resources>
|
@ -13,4 +13,35 @@
|
||||
<string name="restart_confirm_message">Are you sure you want to restart the game ?</string>
|
||||
<string name="cancel">Cancel</string>
|
||||
<string name="confirm">Confirm</string>
|
||||
<string name="high_score_stats">High Score: %d</string>
|
||||
<string name="total_games_played">Total Games Played: %d</string>
|
||||
<string name="total_games_started">Total Games Started: %d</string>
|
||||
<string name="win_percentage">Win Percentage: %s</string>
|
||||
<string name="total_play_time">Total Play Time: %s</string>
|
||||
<string name="total_moves">Total Moves: %d</string>
|
||||
<string name="current_moves">Current Moves: %d</string>
|
||||
<string name="current_game_time">Current Game Time: %s</string>
|
||||
<string name="merges_this_game_label">Merges: %d</string>
|
||||
<string name="average_time_per_game">Average Game Time: %s</string>
|
||||
<string name="best_winning_time">Best Winning Time: %s</string>
|
||||
<string name="worst_winning_time">Worst Winning Time: %s</string>
|
||||
<string name="total_merges">Total Merges: %d</string>
|
||||
<string name="highest_tile">Highest Tile: %d</string>
|
||||
<string name="number_of_time_objective_reached">Number of time objective reached: %d</string>
|
||||
<string name="perfect_games">Perfect game : %d</string>
|
||||
<string name="multiplayer_games_won">Multiplayer game won : %d</string>
|
||||
<string name="multiplayer_games_played">Multiplayer game played : %d</string>
|
||||
<string name="multiplayer_win_rate">Multiplayer win rate : %s</string>
|
||||
<string name="multiplayer_best_winning_streak">Best winning streak : %d</string>
|
||||
<string name="multiplayer_average_score">Multiplayer Average Score: %d</string>
|
||||
<string name="average_time_per_game_label">Average time per game: %s</string>
|
||||
<string name="total_multiplayer_losses">Total Multiplayer losses: %d</string>
|
||||
<string name="multiplayer_high_score">Multiplayer High Score: %d</string>
|
||||
<string name="stats_button_label">Stats</string>
|
||||
<string name="stats_title">Statistics</string>
|
||||
<string name="general_section">General</string>
|
||||
<string name="current_game_section">Current Game</string>
|
||||
<string name="single_player_section">Single Player</string>
|
||||
<string name="multiplayer_section">Multiplayer</string>
|
||||
<string name="back_button_label">Back</string>
|
||||
</resources>
|
@ -1,9 +1,7 @@
|
||||
<resources>
|
||||
<style name="Base.Theme.Best2048" parent="Theme.Material3.DayNight.NoActionBar">
|
||||
<style name="Theme.Best2048" parent="Theme.AppCompat.Light.NoActionBar">
|
||||
</style>
|
||||
|
||||
<style name="Theme.Best2048" parent="Base.Theme.Best2048" />
|
||||
|
||||
<style name="ScoreLabelStyle">
|
||||
<item name="android:layout_width">match_parent</item>
|
||||
<item name="android:layout_height">0dp</item>
|
||||
@ -24,16 +22,47 @@
|
||||
<item name="android:layout_marginTop">@dimen/padding_general</item>
|
||||
<item name="android:layout_marginStart">@dimen/padding_general</item>
|
||||
<item name="android:layout_marginEnd">@dimen/padding_general</item>
|
||||
|
||||
<item name="android:buttonBarButtonStyle">?android:attr/buttonBarButtonStyle</item>
|
||||
</style>
|
||||
|
||||
<style name="LargeButtonStyle" parent="ButtonStyle">
|
||||
<style name="LargeButtonStyle">
|
||||
<item name="android:layout_width">0dp</item>
|
||||
<item name="android:layout_height">wrap_content</item>
|
||||
<item name="android:layout_marginStart">@dimen/padding_general</item>
|
||||
<item name="android:layout_marginEnd">@dimen/padding_general</item>
|
||||
<item name="android:buttonBarButtonStyle">?android:attr/buttonBarButtonStyle</item>
|
||||
<item name="android:layout_marginTop">@dimen/padding_general</item>
|
||||
<item name="android:layout_marginBottom">@dimen/padding_general</item>
|
||||
<item name="android:background">@drawable/button_multiplayer_background</item> <item name="android:textColor">@color/white</item>
|
||||
<item name="android:background">@drawable/button_multiplayer_background</item>
|
||||
<item name="android:textColor">@color/white</item>
|
||||
<item name="android:textAllCaps">true</item>
|
||||
<item name="android:textStyle">bold</item>
|
||||
<item name="android:textSize">20sp</item>
|
||||
</style>
|
||||
|
||||
<style name="SectionTitle">
|
||||
<item name="android:textSize">18sp</item>
|
||||
<item name="android:textStyle">bold</item>
|
||||
<item name="android:textColor">@color/text_tile_high</item> <item name="android:layout_marginBottom">4dp</item>
|
||||
</style>
|
||||
|
||||
<style name="SectionContainer">
|
||||
<item name="android:layout_width">match_parent</item>
|
||||
<item name="android:layout_height">wrap_content</item>
|
||||
<item name="android:orientation">vertical</item>
|
||||
<item name="android:background">@drawable/dialog_background</item> <item name="android:padding">12dp</item>
|
||||
<item name="android:layout_marginBottom">16dp</item>
|
||||
</style>
|
||||
|
||||
<style name="StatLabel">
|
||||
<item name="android:layout_width">match_parent</item>
|
||||
<item name="android:layout_height">wrap_content</item>
|
||||
<item name="android:textColor">@color/text_tile_low</item> <item name="android:textSize">16sp</item>
|
||||
<item name="android:layout_marginBottom">4dp</item>
|
||||
</style>
|
||||
|
||||
<style name="Theme.AppCompat.Light.NoActionBar.FullScreen" parent="Theme.AppCompat.Light.NoActionBar">
|
||||
<item name="android:windowFullscreen">true</item>
|
||||
<item name="android:windowContentOverlay">@null</item>
|
||||
</style>
|
||||
</resources>
|
Loading…
x
Reference in New Issue
Block a user