From 2953a1bf67493f6c64bb83acb5d71ad6df2e848b Mon Sep 17 00:00:00 2001 From: Alexander Doerflinger Date: Wed, 28 Jan 2026 15:28:39 +0100 Subject: [PATCH] Cleaned up code --- .../apps/ochecompanion/AddPlayerActivity.java | 29 ++- .../aldo/apps/ochecompanion/GameActivity.java | 27 +- .../apps/ochecompanion/MainMenuActivity.java | 24 +- .../ochecompanion/database/AppDatabase.java | 2 +- .../ochecompanion/database/dao/PlayerDao.java | 11 + .../ochecompanion/database/objects/Match.java | 239 +++++++++++++++++- .../database/objects/Player.java | 48 ++-- .../aldo/apps/ochecompanion/models/Match.java | 131 ---------- .../ochecompanion/ui/CropOverlayView.java | 16 +- .../apps/ochecompanion/ui/MatchRecapView.java | 6 +- .../ui/adapter/MainMenuGroupMatchAdapter.java | 75 ++---- app/src/main/res/drawable/ic_delete.xml | 9 + .../main/res/layout/activity_add_player.xml | 13 + 13 files changed, 396 insertions(+), 234 deletions(-) delete mode 100644 app/src/main/java/com/aldo/apps/ochecompanion/models/Match.java create mode 100644 app/src/main/res/drawable/ic_delete.xml diff --git a/app/src/main/java/com/aldo/apps/ochecompanion/AddPlayerActivity.java b/app/src/main/java/com/aldo/apps/ochecompanion/AddPlayerActivity.java index 688a497..1b4cd3f 100644 --- a/app/src/main/java/com/aldo/apps/ochecompanion/AddPlayerActivity.java +++ b/app/src/main/java/com/aldo/apps/ochecompanion/AddPlayerActivity.java @@ -15,6 +15,7 @@ import android.widget.TextView; import android.widget.Toast; import androidx.activity.result.ActivityResultLauncher; import androidx.activity.result.contract.ActivityResultContracts; +import androidx.annotation.NonNull; import androidx.appcompat.app.AppCompatActivity; import com.aldo.apps.ochecompanion.database.AppDatabase; import com.aldo.apps.ochecompanion.database.objects.Player; @@ -77,6 +78,11 @@ public class AddPlayerActivity extends AppCompatActivity { */ private MaterialButton mSaveButton; + /** + * Button to delete a players profile from the database. + */ + private ImageView mBtnDelete; + // ========== UI - Cropper Views ========== /** @@ -183,6 +189,7 @@ public class AddPlayerActivity extends AppCompatActivity { mUserNameInput = findViewById(R.id.etUsername); mTitleView = findViewById(R.id.tvTitle); mSaveButton = findViewById(R.id.btnSavePlayer); + mBtnDelete = findViewById(R.id.btnDeletePlayer); // Get references to cropper UI elements mIvCropPreview = findViewById(R.id.ivCropPreview); @@ -191,6 +198,9 @@ public class AddPlayerActivity extends AppCompatActivity { // Set up click listeners mProfilePictureView.setOnClickListener(v -> mGetContent.launch("image/*")); mSaveButton.setOnClickListener(v -> savePlayer()); + if (mBtnDelete != null) { + mBtnDelete.setOnClickListener(v -> deletePlayer()); + } findViewById(R.id.btnConfirmCrop).setOnClickListener(v -> performCrop()); findViewById(R.id.btnCancelCrop).setOnClickListener(v -> exitCropMode()); } @@ -202,7 +212,7 @@ public class AddPlayerActivity extends AppCompatActivity { // Initialize scale detector for pinch-to-zoom functionality mScaleDetector = new ScaleGestureDetector(this, new ScaleGestureDetector.SimpleOnScaleGestureListener() { @Override - public boolean onScale(final ScaleGestureDetector detector) { + public boolean onScale(@NonNull final ScaleGestureDetector detector) { // Apply the scale factor from the gesture mScaleFactor *= detector.getScaleFactor(); @@ -401,6 +411,7 @@ public class AddPlayerActivity extends AppCompatActivity { // Update UI labels for "edit mode" mTitleView.setText(R.string.txt_update_profile_header); mSaveButton.setText(R.string.txt_update_profile_username_save); + if (mBtnDelete != null) mBtnDelete.setVisibility(View.VISIBLE); // Load existing profile picture if available if (mExistingPlayer.profilePictureUri != null) { @@ -413,6 +424,20 @@ public class AddPlayerActivity extends AppCompatActivity { }).start(); } + /** + * Deletes the selected player from the database. + */ + private void deletePlayer() { + if (mExistingPlayer == null) return; + new Thread(() -> { + AppDatabase.getDatabase(this).playerDao().delete(mExistingPlayer); + runOnUiThread(() -> { + Toast.makeText(this, "Player removed from squad", Toast.LENGTH_SHORT).show(); + finish(); + }); + }).start(); + } + /** * Validates and persists the player data to the database. * Inserts new player or updates existing based on mExistingPlayer. @@ -439,7 +464,7 @@ public class AddPlayerActivity extends AppCompatActivity { } // Close activity on main thread after save completes - runOnUiThread(() -> finish()); + runOnUiThread(this::finish); }).start(); } } \ No newline at end of file diff --git a/app/src/main/java/com/aldo/apps/ochecompanion/GameActivity.java b/app/src/main/java/com/aldo/apps/ochecompanion/GameActivity.java index e34ca1a..3edef79 100644 --- a/app/src/main/java/com/aldo/apps/ochecompanion/GameActivity.java +++ b/app/src/main/java/com/aldo/apps/ochecompanion/GameActivity.java @@ -20,9 +20,8 @@ import com.aldo.apps.ochecompanion.utils.DartsConstants; import com.aldo.apps.ochecompanion.utils.UIConstants; import com.google.android.material.button.MaterialButton; import java.util.ArrayList; -import java.util.HashMap; import java.util.List; -import java.util.Map; +import java.util.UUID; /** * Main game activity for playing X01 darts games (501, 301, etc.). @@ -41,10 +40,21 @@ public class GameActivity extends AppCompatActivity { */ private static final String EXTRA_START_SCORE = "extra_start_score"; + /** + * Intent extra for a match ID. Making it possible to load a match from the database. + */ + private static final String EXTRA_MATCH_UUID = "extra_match_uuid"; + + // ======================================================================================== // Game Logic State // ======================================================================================== - + + /** + * The matches UUID from the database. + */ + private String mMatchUuid; + /** * Index of the current active player (0 to playerCount-1). * Cycles through players as turns complete. @@ -151,6 +161,7 @@ public class GameActivity extends AppCompatActivity { Intent intent = new Intent(context, GameActivity.class); intent.putParcelableArrayListExtra(EXTRA_PLAYERS, players); intent.putExtra(EXTRA_START_SCORE, startScore); + intent.putExtra(EXTRA_MATCH_UUID, UUID.randomUUID().toString()); context.startActivity(intent); } @@ -166,6 +177,7 @@ public class GameActivity extends AppCompatActivity { // Extract game parameters from intent mStartingScore = getIntent().getIntExtra(EXTRA_START_SCORE, DartsConstants.DEFAULT_GAME_SCORE); + mMatchUuid = getIntent().getStringExtra(EXTRA_MATCH_UUID); ArrayList participants = getIntent().getParcelableArrayListExtra(EXTRA_PLAYERS); // Initialize activity components in order @@ -377,6 +389,15 @@ public class GameActivity extends AppCompatActivity { // Update UI for next player updateUI(); updateTurnIndicators(); + saveMatchProgress(); + } + + /** + * Saves the current game state to the database. + */ + private void saveMatchProgress() { + // TODO: Persist current state to Room using mMatchUuid + // This allows the "Continue Game" feature on the Main Menu } /** diff --git a/app/src/main/java/com/aldo/apps/ochecompanion/MainMenuActivity.java b/app/src/main/java/com/aldo/apps/ochecompanion/MainMenuActivity.java index e9e8dcb..c13a14e 100644 --- a/app/src/main/java/com/aldo/apps/ochecompanion/MainMenuActivity.java +++ b/app/src/main/java/com/aldo/apps/ochecompanion/MainMenuActivity.java @@ -4,7 +4,6 @@ import android.content.Intent; import android.os.Bundle; import android.view.View; import android.widget.TextView; -import android.widget.Toast; import androidx.activity.EdgeToEdge; import androidx.appcompat.app.AppCompatActivity; @@ -16,7 +15,7 @@ import androidx.recyclerview.widget.RecyclerView; import com.aldo.apps.ochecompanion.database.AppDatabase; import com.aldo.apps.ochecompanion.database.objects.Player; -import com.aldo.apps.ochecompanion.models.Match; +import com.aldo.apps.ochecompanion.database.objects.Match; import com.aldo.apps.ochecompanion.ui.MatchRecapView; import com.aldo.apps.ochecompanion.ui.adapter.MainMenuPlayerAdapter; import com.aldo.apps.ochecompanion.utils.DartsConstants; @@ -156,13 +155,28 @@ public class MainMenuActivity extends AppCompatActivity { private void applyTestData(final int counter) { // Create test player objects final Player playerOne = new Player(DartsConstants.TEST_PLAYER_1, null); + playerOne.id = 1; final Player playerTwo = new Player(DartsConstants.TEST_PLAYER_2, null); + playerTwo.id = 2; final Player playerThree = new Player(DartsConstants.TEST_PLAYER_3, null); + playerThree.id = 3; final Player playerFour = new Player(DartsConstants.TEST_PLAYER_4, null); + playerFour.id = 4; - // Create test match objects with different player configurations - final Match match1on1 = new Match(playerOne, playerTwo); - final Match matchGroup = new Match(playerOne, playerTwo, playerThree, playerFour); + // Create score maps for test matches + final java.util.Map scores1v1 = new java.util.HashMap<>(); + scores1v1.put(1, 0); // Player 1 won (reached 0 from 501) + scores1v1.put(2, 157); // Player 2 remaining score + + final java.util.Map scoresGroup = new java.util.HashMap<>(); + scoresGroup.put(1, 0); // Player 1 won (reached 0 from 501) + scoresGroup.put(2, 89); // Player 2 remaining score + scoresGroup.put(3, 234); // Player 3 remaining score + scoresGroup.put(4, 312); // Player 4 remaining score + + // Create test match objects with different player configurations and scores + final Match match1on1 = new Match("501", java.util.Arrays.asList(playerOne, playerTwo), scores1v1); + final Match matchGroup = new Match("501", java.util.Arrays.asList(playerOne, playerTwo, playerThree, playerFour), scoresGroup); // Cycle through different test scenarios based on counter value if (counter % UIConstants.TEST_CYCLE_MODULO == 0) { diff --git a/app/src/main/java/com/aldo/apps/ochecompanion/database/AppDatabase.java b/app/src/main/java/com/aldo/apps/ochecompanion/database/AppDatabase.java index ed0d386..748c968 100644 --- a/app/src/main/java/com/aldo/apps/ochecompanion/database/AppDatabase.java +++ b/app/src/main/java/com/aldo/apps/ochecompanion/database/AppDatabase.java @@ -21,7 +21,7 @@ import com.aldo.apps.ochecompanion.database.objects.Player; * @see Player * @see Match */ -@Database(entities = {Player.class, Match.class}, version = 2, exportSchema = false) +@Database(entities = {Player.class, Match.class}, version = 3, exportSchema = false) public abstract class AppDatabase extends RoomDatabase { /** diff --git a/app/src/main/java/com/aldo/apps/ochecompanion/database/dao/PlayerDao.java b/app/src/main/java/com/aldo/apps/ochecompanion/database/dao/PlayerDao.java index 758b26c..1e89e29 100644 --- a/app/src/main/java/com/aldo/apps/ochecompanion/database/dao/PlayerDao.java +++ b/app/src/main/java/com/aldo/apps/ochecompanion/database/dao/PlayerDao.java @@ -1,6 +1,7 @@ package com.aldo.apps.ochecompanion.database.dao; import androidx.room.Dao; +import androidx.room.Delete; import androidx.room.Insert; import androidx.room.Query; import androidx.room.Update; @@ -34,6 +35,16 @@ public interface PlayerDao { @Update void update(final Player player); + /** + * Deletes an existing player from the database. + * Player is identified by its primary key ID. + * Must be called on a background thread. + * + * @param player The player to be deleted. + */ + @Delete + void delete(final Player player); + /** * Retrieves a player by their unique ID. * Must be called on a background thread. diff --git a/app/src/main/java/com/aldo/apps/ochecompanion/database/objects/Match.java b/app/src/main/java/com/aldo/apps/ochecompanion/database/objects/Match.java index 8095226..5030ea4 100644 --- a/app/src/main/java/com/aldo/apps/ochecompanion/database/objects/Match.java +++ b/app/src/main/java/com/aldo/apps/ochecompanion/database/objects/Match.java @@ -1,14 +1,23 @@ package com.aldo.apps.ochecompanion.database.objects; import androidx.room.Entity; +import androidx.room.Ignore; import androidx.room.PrimaryKey; +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; import java.io.Serializable; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; /** * Represents a completed darts match in the Oche Companion application. * Room entity storing match information including game mode, timestamp, player count, * and detailed performance data for all participants. Implements Serializable for - * passing between Android components. + * passing between Android components. Provides helper methods to parse participant + * JSON data and reconstruct Player objects. * * @see com.aldo.apps.ochecompanion.database.dao.MatchDao * @see com.aldo.apps.ochecompanion.database.objects.Player @@ -25,7 +34,7 @@ public class Match implements Serializable { * @see PrimaryKey */ @PrimaryKey(autoGenerate = true) - public int mId; + public int id; /** * Unix epoch timestamp (milliseconds) when match was completed. @@ -33,20 +42,20 @@ public class Match implements Serializable { * * @see System#currentTimeMillis() */ - public long mTimestamp; + public long timestamp; /** * Identifier for the darts game variant played (e.g., "501", "301", "Cricket"). * Determines scoring rules and UI display for this match. */ - public String mGameMode; + public String gameMode; /** * Total number of players who participated in this match. * Determines match type (1=solo, 2=1v1, 3+=group) and affects UI display. * Must match the number of entries in participantData JSON. */ - public int mPlayerCount; + public int playerCount; /** * JSON string containing detailed performance data for all match participants. @@ -56,7 +65,7 @@ public class Match implements Serializable { * @see org.json.JSONArray * @see org.json.JSONObject */ - public String mParticipantData; + public String participantData; /** * Constructs a new Match entity ready for database insertion. @@ -69,9 +78,219 @@ public class Match implements Serializable { * @see com.aldo.apps.ochecompanion.database.dao.MatchDao#insert(Match) */ public Match(final long timestamp, final String gameMode, final int playerCount, final String participantData) { - this.mTimestamp = timestamp; - this.mGameMode = gameMode; - this.mPlayerCount = playerCount; - this.mParticipantData = participantData; + this.timestamp = timestamp; + this.gameMode = gameMode; + this.playerCount = playerCount; + this.participantData = participantData; + } + + /** + * Convenience constructor for creating a Match from a list of Player objects. + * Automatically generates JSON participant data and sets timestamp to current time. + * All players will have a score of 0. + * + * @param gameMode Identifier for the darts game variant (e.g., "501", "Cricket") + * @param players List of Player objects to include in this match + */ + @Ignore + public Match(final String gameMode, final List players) { + this.timestamp = System.currentTimeMillis(); + this.gameMode = gameMode; + this.playerCount = players.size(); + this.participantData = generateParticipantJson(players, null); + } + + /** + * Convenience constructor for creating a Match from players with their scores. + * Automatically generates JSON participant data and sets timestamp to current time. + * + * @param gameMode Identifier for the darts game variant (e.g., "501", "Cricket") + * @param players List of Player objects to include in this match + * @param scores Map of player IDs to their final scores + */ + @Ignore + public Match(final String gameMode, final List players, final Map scores) { + this.timestamp = System.currentTimeMillis(); + this.gameMode = gameMode; + this.playerCount = players.size(); + this.participantData = generateParticipantJson(players, scores); + } + + /** + * Returns the number of players participating in this match. + * + * @return The player count + */ + public int getParticipantCount() { + return playerCount; + } + + /** + * Retrieves the username of the player at the specified position. + * + * @param position The zero-based index of the player in the match + * @return The username of the player, or "Unknown" if unavailable + */ + public String getPlayerNameByPosition(final int position) { + try { + final JSONArray participants = new JSONArray(participantData); + if (position >= 0 && position < participants.length()) { + final JSONObject participant = participants.getJSONObject(position); + return participant.optString("username", "Unknown"); + } + } catch (JSONException e) { + // Return default if JSON parsing fails + } + return "Unknown"; + } + + /** + * Retrieves the career average of the player at the specified position. + * + * @param position The zero-based index of the player in the match + * @return The career average, or 0.0 if unavailable + */ + public double getPlayerAverageByPosition(final int position) { + try { + final JSONArray participants = new JSONArray(participantData); + if (position >= 0 && position < participants.length()) { + final JSONObject participant = participants.getJSONObject(position); + return participant.optDouble("careerAverage", 0.0); + } + } catch (JSONException e) { + // Return default if JSON parsing fails + } + return 0.0; + } + + /** + * Retrieves the match score of the player at the specified position. + * + * @param position The zero-based index of the player in the match + * @return The match score, or 0 if unavailable + */ + public int getPlayerScoreByPosition(final int position) { + try { + final JSONArray participants = new JSONArray(participantData); + if (position >= 0 && position < participants.length()) { + final JSONObject participant = participants.getJSONObject(position); + return participant.optInt("score", 0); + } + } catch (JSONException e) { + // Return default if JSON parsing fails + } + return 0; + } + + /** + * Returns a map of player IDs to their match scores. + * + * @return Map of player IDs to scores, or empty map if parsing fails + */ + public Map getPlayerScores() { + final Map scores = new HashMap<>(); + try { + final JSONArray participants = new JSONArray(participantData); + for (int i = 0; i < participants.length(); i++) { + final JSONObject participant = participants.getJSONObject(i); + final int playerId = participant.optInt("id", 0); + final int score = participant.optInt("score", 0); + scores.put(playerId, score); + } + } catch (JSONException e) { + // Return empty map if JSON parsing fails + } + return scores; + } + + /** + * Returns a list of all Player objects reconstructed from participant data. + * + * @return List of Player objects, or empty list if parsing fails + */ + public List getAllPlayers() { + final List players = new ArrayList<>(); + try { + final JSONArray participants = new JSONArray(participantData); + for (int i = 0; i < participants.length(); i++) { + final JSONObject participant = participants.getJSONObject(i); + final String username = participant.optString("username", "Unknown"); + final String photoUri = participant.optString("photoUri", null); + final Player player = new Player(username, photoUri); + player.id = participant.optInt("id", 0); + player.careerAverage = participant.optDouble("careerAverage", 0.0); + players.add(player); + } + } catch (JSONException e) { + // Return empty list if JSON parsing fails + } + return players; + } + + /** + * Generates JSON string from a list of Player objects with optional scores. + * + * @param players List of Player objects to convert + * @param scores Map of player IDs to their match scores (null for all zeros) + * @return JSON string representation of player data + */ + private String generateParticipantJson(final List players, final Map scores) { + final JSONArray participants = new JSONArray(); + try { + for (final Player player : players) { + final JSONObject participant = new JSONObject(); + participant.put("id", player.id); + participant.put("username", player.username); + participant.put("photoUri", player.profilePictureUri); + participant.put("careerAverage", player.careerAverage); + final int score = (scores != null && scores.containsKey(player.id)) ? scores.get(player.id) : 0; + participant.put("score", score); + participants.put(participant); + } + } catch (JSONException e) { + // Return empty array if JSON generation fails + } + return participants.toString(); + } + + /** + * Data class representing a participant in a match with their score. + */ + public static class ParticipantData { + /** The Player object */ + public final Player player; + + /** The player's score in this match */ + public final int score; + + public ParticipantData(final Player player, final int score) { + this.player = player; + this.score = score; + } + } + + /** + * Returns all participants with their match scores. + * + * @return List of ParticipantData objects containing player info and scores + */ + public List getAllParticipants() { + final List participants = new ArrayList<>(); + try { + final JSONArray participantArray = new JSONArray(participantData); + for (int i = 0; i < participantArray.length(); i++) { + final JSONObject participant = participantArray.getJSONObject(i); + final String username = participant.optString("username", "Unknown"); + final String photoUri = participant.optString("photoUri", null); + final Player player = new Player(username, photoUri); + player.id = participant.optInt("id", 0); + player.careerAverage = participant.optDouble("careerAverage", 0.0); + final int score = participant.optInt("score", 0); + participants.add(new ParticipantData(player, score)); + } + } catch (JSONException e) { + // Return empty list if JSON parsing fails + } + return participants; } } diff --git a/app/src/main/java/com/aldo/apps/ochecompanion/database/objects/Player.java b/app/src/main/java/com/aldo/apps/ochecompanion/database/objects/Player.java index d83046f..7fbcbdd 100644 --- a/app/src/main/java/com/aldo/apps/ochecompanion/database/objects/Player.java +++ b/app/src/main/java/com/aldo/apps/ochecompanion/database/objects/Player.java @@ -3,6 +3,7 @@ package com.aldo.apps.ochecompanion.database.objects; import android.os.Parcel; import android.os.Parcelable; +import androidx.annotation.NonNull; import androidx.room.Entity; import androidx.room.PrimaryKey; @@ -18,32 +19,32 @@ public class Player implements Parcelable { * Room auto-populates on insert. */ @PrimaryKey(autoGenerate = true) - public int mId; + public int id; /** * Player's display name shown throughout the app. Should be non-null and * ideally 1-30 characters. Supports Unicode. */ - public String mUsername; + public String username; /** * File path to player's profile picture. Can be null (shows default avatar). * Stored as path reference to avoid database bloat. */ - public String mProfilePictureUri; + public String profilePictureUri; /** * Player's career three-dart average across all completed matches. * Represents overall skill level. Typical ranges: 0-40 (beginner), * 40-60 (casual), 60-80 (experienced), 80-100 (advanced), 100+ (pro). */ - public double mCareerAverage = 0.0; + public double careerAverage = 0.0; /** * Total number of completed matches for this player. Provides context for * statistical significance and enables experience-based features. */ - public int mMatchesPlayed = 0; + public int matchesPlayed = 0; /** * Constructs a new Player ready for database insertion. ID is auto-generated @@ -53,8 +54,8 @@ public class Player implements Parcelable { * @param profilePictureUri Path to profile image (null for default avatar) */ public Player(final String username, final String profilePictureUri) { - this.mUsername = username; - this.mProfilePictureUri = profilePictureUri; + this.username = username; + this.profilePictureUri = profilePictureUri; } @@ -65,17 +66,17 @@ public class Player implements Parcelable { * @param in Parcel containing serialized Player data */ protected Player(final Parcel in) { - mId = in.readInt(); - mUsername = in.readString(); - mProfilePictureUri = in.readString(); - mCareerAverage = in.readDouble(); - mMatchesPlayed = in.readInt(); + id = in.readInt(); + username = in.readString(); + profilePictureUri = in.readString(); + careerAverage = in.readDouble(); + matchesPlayed = in.readInt(); } /** * Required Parcelable CREATOR field for Player reconstruction. */ - public static final Creator sCREATOR = new Creator() { + public static final Creator CREATOR = new Creator<>() { @Override public Player createFromParcel(final Parcel in) { return new Player(in); @@ -100,11 +101,11 @@ public class Player implements Parcelable { */ @Override public void writeToParcel(final Parcel dest, final int flags) { - dest.writeInt(mId); - dest.writeString(mUsername); - dest.writeString(mProfilePictureUri); - dest.writeDouble(mCareerAverage); - dest.writeInt(mMatchesPlayed); + dest.writeInt(id); + dest.writeString(username); + dest.writeString(profilePictureUri); + dest.writeDouble(careerAverage); + dest.writeInt(matchesPlayed); } /** @@ -114,13 +115,14 @@ public class Player implements Parcelable { * @return String in format "Player{mId=X, mUsername='...', ...}" */ @Override + @NonNull public String toString() { return "Player{" + - "mId=" + mId + - ", mUsername='" + mUsername + '\'' + - ", mProfilePictureUri='" + mProfilePictureUri + '\'' + - ", mCareerAverage=" + mCareerAverage + - ", mMatchesPlayed=" + mMatchesPlayed + + "mId=" + id + + ", mUsername='" + username + '\'' + + ", mProfilePictureUri='" + profilePictureUri + '\'' + + ", mCareerAverage=" + careerAverage + + ", mMatchesPlayed=" + matchesPlayed + '}'; } } diff --git a/app/src/main/java/com/aldo/apps/ochecompanion/models/Match.java b/app/src/main/java/com/aldo/apps/ochecompanion/models/Match.java deleted file mode 100644 index 288a0ca..0000000 --- a/app/src/main/java/com/aldo/apps/ochecompanion/models/Match.java +++ /dev/null @@ -1,131 +0,0 @@ -package com.aldo.apps.ochecompanion.models; - -import android.util.Log; - -import androidx.annotation.NonNull; - -import java.util.ArrayList; -import java.util.List; -import com.aldo.apps.ochecompanion.database.objects.Player; - -/** - * Model class representing a darts match with multiple participants. - */ -public class Match { - - /** - * Tag for logging. - */ - private static final String TAG = "Match"; - - /** - * List of players participating in this match. - */ - private final List mPlayers; - - /** - * Constructs an empty Match with no participants. - */ - public Match() { - // Initialize empty player list - mPlayers = new ArrayList<>(); - // Log creation for debugging purposes - Log.d(TAG, "Match: Creating new empty match."); - } - - /** - * Constructs a Match with the specified players. - * - * @param players Variable number of Player objects to participate in the match. - */ - public Match(final Player... players) { - // Initialize empty player list - mPlayers = new ArrayList<>(); - - // Add each player to the match in order - for (final Player player : players) { - // Log the addition for debugging - Log.d(TAG, "Match: Adding [" + player + "]"); - // Add player to the internal list - mPlayers.add(player); - } - } - - /** - * Returns the number of players participating in this match. - * - * @return The number of players in this match. - */ - public int getParticipantCount() { - return mPlayers.size(); - } - - /** - * Retrieves the username of the player at the specified position. - * - * @param position The zero-based index of the player in the match. - * @return The username of the player at the specified position, or "INVALID" if the position is out of bounds. - */ - public String getPlayerNameByPosition(final int position) { - // Validate position is within bounds - // Note: Consider changing <= to < to prevent IndexOutOfBoundsException - if (position >= 0 && position <= mPlayers.size()) { - // Return the username of the player at this position - return mPlayers.get(position).username; - } - // Return sentinel value for invalid position - return "INVALID"; - } - - /** - * Retrieves the career average of the player at the specified position. - * - * @param position The zero-based index of the player in the match. - * @return The career average of the player at the specified position, or -1 if the position is out of bounds. - */ - public double getPlayerAverageByPosition(final int position) { - // Validate position is within bounds - // Note: Consider changing <= to < to prevent IndexOutOfBoundsException - if (position >= 0 && position <= mPlayers.size()) { - // Return the career average of the player at this position - return mPlayers.get(position).careerAverage; - } - // Return sentinel value for invalid position - return -1; - } - - /** - * Returns the list of all players in this match. - * - * @return The list of all Player objects in this match. - */ - public List getAllPlayers() { - return mPlayers; - } - - /** - * Returns a string representation of this Match. - * - * @return A string representation of this match including all participating players. - */ - @NonNull - @Override - public String toString() { - // Use StringBuilder for efficient concatenation - final StringBuilder sb = new StringBuilder(); - - // Start the match representation - sb.append("Match {"); - - // Append each player's string representation - for (final Player player : mPlayers) { - sb.append("[").append(player).append("]"); - } - - // Close the match representation - // Note: This adds "]]" instead of "}". Consider fixing to sb.append("}"); - sb.append("]"); - - return sb.toString(); - } -} diff --git a/app/src/main/java/com/aldo/apps/ochecompanion/ui/CropOverlayView.java b/app/src/main/java/com/aldo/apps/ochecompanion/ui/CropOverlayView.java index 74177da..18fbbfc 100644 --- a/app/src/main/java/com/aldo/apps/ochecompanion/ui/CropOverlayView.java +++ b/app/src/main/java/com/aldo/apps/ochecompanion/ui/CropOverlayView.java @@ -8,6 +8,8 @@ import android.graphics.Path; import android.graphics.RectF; import android.util.AttributeSet; import android.view.View; + +import androidx.annotation.NonNull; import androidx.annotation.Nullable; import com.aldo.apps.ochecompanion.utils.UIConstants; @@ -26,9 +28,6 @@ public class CropOverlayView extends View { /** Path with CW outer rect and CCW inner rect creating transparent hole. */ private final Path mPath = new Path(); - /** Calculated side length of square crop box (80% of width). */ - private float mBoxSize; - /** Constructor for programmatic instantiation. */ public CropOverlayView(final Context context) { super(context); @@ -58,10 +57,11 @@ public class CropOverlayView extends View { protected void onLayout(final boolean changed, final int left, final int top, final int right, final int bottom) { super.onLayout(changed, left, top, right, bottom); - mBoxSize = getWidth() * UIConstants.CROP_BOX_SIZE_RATIO; - final float l = (getWidth() - mBoxSize) / 2; - final float t = (getHeight() - mBoxSize) / 2; - mCropRect.set(l, t, l + mBoxSize, t + mBoxSize); + /* Calculated side length of square crop box (80% of width). */ + final float boxSize = getWidth() * UIConstants.CROP_BOX_SIZE_RATIO; + final float l = (getWidth() - boxSize) / 2; + final float t = (getHeight() - boxSize) / 2; + mCropRect.set(l, t, l + boxSize, t + boxSize); mPath.reset(); mPath.addRect(0, 0, getWidth(), getHeight(), Path.Direction.CW); @@ -70,7 +70,7 @@ public class CropOverlayView extends View { /** Renders the overlay mask with transparent center cutout. */ @Override - protected void onDraw(final Canvas canvas) { + protected void onDraw(@NonNull final Canvas canvas) { super.onDraw(canvas); canvas.drawPath(mPath, mMaskPaint); } diff --git a/app/src/main/java/com/aldo/apps/ochecompanion/ui/MatchRecapView.java b/app/src/main/java/com/aldo/apps/ochecompanion/ui/MatchRecapView.java index 975e191..8a9bc2c 100644 --- a/app/src/main/java/com/aldo/apps/ochecompanion/ui/MatchRecapView.java +++ b/app/src/main/java/com/aldo/apps/ochecompanion/ui/MatchRecapView.java @@ -10,7 +10,7 @@ import androidx.annotation.Nullable; import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.RecyclerView; import com.aldo.apps.ochecompanion.R; -import com.aldo.apps.ochecompanion.models.Match; +import com.aldo.apps.ochecompanion.database.objects.Match; import com.aldo.apps.ochecompanion.ui.adapter.MainMenuGroupMatchAdapter; /** @@ -92,10 +92,10 @@ public class MatchRecapView extends FrameLayout { updateVisibility(mState1v1); mTvP1Name.setText(match.getPlayerNameByPosition(0)); - mTvP1Score.setText(String.valueOf(match.getPlayerAverageByPosition(0))); + mTvP1Score.setText(String.valueOf(match.getPlayerScoreByPosition(0))); mTvP2Name.setText(match.getPlayerNameByPosition(1)); - mTvP2Score.setText(String.valueOf(match.getPlayerAverageByPosition(1))); + mTvP2Score.setText(String.valueOf(match.getPlayerScoreByPosition(1))); } /** Configures group state with leaderboard RecyclerView. */ diff --git a/app/src/main/java/com/aldo/apps/ochecompanion/ui/adapter/MainMenuGroupMatchAdapter.java b/app/src/main/java/com/aldo/apps/ochecompanion/ui/adapter/MainMenuGroupMatchAdapter.java index b54bd34..52438e6 100644 --- a/app/src/main/java/com/aldo/apps/ochecompanion/ui/adapter/MainMenuGroupMatchAdapter.java +++ b/app/src/main/java/com/aldo/apps/ochecompanion/ui/adapter/MainMenuGroupMatchAdapter.java @@ -11,18 +11,17 @@ import androidx.recyclerview.widget.RecyclerView; import com.aldo.apps.ochecompanion.R; import com.aldo.apps.ochecompanion.database.objects.Player; -import com.aldo.apps.ochecompanion.models.Match; +import com.aldo.apps.ochecompanion.database.objects.Match; import com.aldo.apps.ochecompanion.ui.PlayerItemView; import com.bumptech.glide.Glide; import com.google.android.material.imageview.ShapeableImageView; import java.util.ArrayList; -import java.util.Comparator; import java.util.List; /** * RecyclerView adapter for displaying group match results in Main Menu. - * Displays players sorted by career average with their names, scores, and profile pictures. + * Displays participants sorted by match score (descending) with their names, scores, and profile pictures. */ public class MainMenuGroupMatchAdapter extends RecyclerView.Adapter { @@ -30,7 +29,7 @@ public class MainMenuGroupMatchAdapter extends RecyclerView.Adapter mPlayersList = new ArrayList<>(); + private final List mParticipantsList = new ArrayList<>(); /** * Creates a new ViewHolder with a PlayerItemView. @@ -62,24 +61,24 @@ public class MainMenuGroupMatchAdapter extends RecyclerView.Adapter participants = match.getAllParticipants(); + if (participants == null || participants.isEmpty()) { + Log.d(TAG, "updateMatch: No participants found in the match, just clearing."); notifyDataSetChanged(); return; } - // Get all players from the match - final List allPlayers = match.getAllPlayers(); + // Sort participants by match score (highest to lowest) + participants.sort((p1, p2) -> Integer.compare(p2.score, p1.score)); - // Sort players by career average (lowest to highest) - allPlayers.sort(new PlayerScoreComparator()); - - // Add sorted players to the display list - mPlayersList.addAll(allPlayers); + // Add sorted participants to the display list + mParticipantsList.addAll(participants); // Notify RecyclerView to refresh the display notifyDataSetChanged(); @@ -143,18 +140,19 @@ public class MainMenuGroupMatchAdapter extends RecyclerView.Adapter { - - /** - * Compares two players by career average. - * - * @param p1 First player. - * @param p2 Second player. - * @return Comparison result. - */ - @Override - public int compare(final Player p1, final Player p2) { - // Compare career averages in ascending order - return Double.compare(p1.careerAverage, p2.careerAverage); - } - } } diff --git a/app/src/main/res/drawable/ic_delete.xml b/app/src/main/res/drawable/ic_delete.xml new file mode 100644 index 0000000..7f584fc --- /dev/null +++ b/app/src/main/res/drawable/ic_delete.xml @@ -0,0 +1,9 @@ + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_add_player.xml b/app/src/main/res/layout/activity_add_player.xml index 452c0ee..645c2ed 100644 --- a/app/src/main/res/layout/activity_add_player.xml +++ b/app/src/main/res/layout/activity_add_player.xml @@ -5,6 +5,19 @@ android:layout_height="match_parent" android:background="@color/midnight_black"> + +