Fixed the Group Standing view
The group standing view had an issue with not being able to display participant data correctly due to multiple issue and data not being available, not it is available and working properly
This commit is contained in:
@@ -23,13 +23,14 @@ import com.aldo.apps.ochecompanion.ui.MatchRecapView;
|
||||
import com.aldo.apps.ochecompanion.ui.QuickStartButton;
|
||||
import com.aldo.apps.ochecompanion.ui.adapter.MainMenuPlayerAdapter;
|
||||
import com.aldo.apps.ochecompanion.utils.DartsConstants;
|
||||
import com.aldo.apps.ochecompanion.utils.UIConstants;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Main entry point and home screen of the Oche Companion application.
|
||||
* Displays the squad of players, allows adding new players, and shows match recap with test data.
|
||||
* Displays the squad of players, allows adding new players, and shows match
|
||||
* recap.
|
||||
*/
|
||||
public class MainMenuActivity extends BaseActivity {
|
||||
|
||||
@@ -40,34 +41,33 @@ public class MainMenuActivity extends BaseActivity {
|
||||
|
||||
/**
|
||||
* Custom view component that displays a match summary.
|
||||
* Can be clicked to cycle through different test data states.
|
||||
*/
|
||||
private MatchRecapView mMatchRecap;
|
||||
|
||||
/**
|
||||
* Counter for cycling through different test data scenarios.
|
||||
* Increments on each click to cycle through null match, 1v1 match, and group match states.
|
||||
*/
|
||||
private int mTestCounter = 0;
|
||||
|
||||
/**
|
||||
* The {@link SharedPreferences} containing the currently selected settings.
|
||||
*/
|
||||
private SharedPreferences mSettingsPref;
|
||||
|
||||
/**
|
||||
* Centralized database helper that manages all database operations with proper synchronization.
|
||||
* Centralized database helper that manages all database operations with proper
|
||||
* synchronization.
|
||||
*/
|
||||
private DatabaseHelper mDatabaseHelper;
|
||||
|
||||
/** The ongoing match retrieved from the database, if any. Used for quick start functionality. */
|
||||
/**
|
||||
* The ongoing match retrieved from the database, if any. Used for quick start
|
||||
* functionality.
|
||||
*/
|
||||
private Match mOngoingMatch;
|
||||
|
||||
/**
|
||||
* Initializes the activity: enables edge-to-edge display, configures window insets,
|
||||
* Initializes the activity: enables edge-to-edge display, configures window
|
||||
* insets,
|
||||
* and sets up the match recap view with test data click listener.
|
||||
*
|
||||
* @param savedInstanceState Bundle containing saved state, or null if none exists.
|
||||
* @param savedInstanceState Bundle containing saved state, or null if none
|
||||
* exists.
|
||||
*/
|
||||
@Override
|
||||
protected void onCreate(final Bundle savedInstanceState) {
|
||||
@@ -79,28 +79,23 @@ public class MainMenuActivity extends BaseActivity {
|
||||
mSettingsPref = PreferenceManager.getDefaultSharedPreferences(this);
|
||||
mDatabaseHelper = DatabaseHelper.getInstance(this);
|
||||
|
||||
// Configure window insets to properly handle system bars (status bar, navigation bar)
|
||||
// Configure window insets to properly handle system bars (status bar,
|
||||
// navigation bar)
|
||||
ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main), (v, insets) -> {
|
||||
Insets systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars());
|
||||
v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom);
|
||||
return insets;
|
||||
});
|
||||
|
||||
|
||||
findViewById(R.id.btnSettings).setOnClickListener(v -> launchSettings());
|
||||
|
||||
// Set up match recap view with test data functionality
|
||||
// Set up match recap view
|
||||
mMatchRecap = findViewById(R.id.match_recap);
|
||||
mMatchRecap.setOnClickListener(v -> {
|
||||
// Cycle through test data scenarios on each click
|
||||
applyTestData(mTestCounter);
|
||||
mTestCounter++;
|
||||
new Thread(() -> mDatabaseHelper.printAllMatches()).start();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Refreshes the squad view with latest player data from the database when activity resumes.
|
||||
* Refreshes the squad view with latest player data from the database when
|
||||
* activity resumes.
|
||||
*/
|
||||
@Override
|
||||
protected void onResume() {
|
||||
@@ -132,7 +127,8 @@ public class MainMenuActivity extends BaseActivity {
|
||||
startActivity(intent);
|
||||
}
|
||||
|
||||
/**game.
|
||||
/**
|
||||
* game.
|
||||
* Checks for any ongoing matches and resumes them if found,
|
||||
* otherwise starts a new game with the default score.
|
||||
*/
|
||||
@@ -159,7 +155,8 @@ public class MainMenuActivity extends BaseActivity {
|
||||
|
||||
/**
|
||||
* Initializes the squad view: sets up the RecyclerView with adapter,
|
||||
* configures the add player button, and loads all players from the database on a background thread.
|
||||
* configures the add player button, and loads all players from the database on
|
||||
* a background thread.
|
||||
*/
|
||||
private void initSquadView() {
|
||||
// Get references to UI components
|
||||
@@ -181,73 +178,66 @@ public class MainMenuActivity extends BaseActivity {
|
||||
new Thread(() -> {
|
||||
final List<Player> allPlayers = mDatabaseHelper.getAllPlayers();
|
||||
runOnUiThread(() -> adapter.updatePlayers(allPlayers));
|
||||
|
||||
}).start();
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies the last completed match from the database to the match recap view.
|
||||
* Enriches participant data with profile pictures from the database using batch
|
||||
* fetch.
|
||||
*/
|
||||
private void applyLastMatch() {
|
||||
new Thread(() -> {
|
||||
final List<Match.ParticipantData> participants = new ArrayList<>();
|
||||
final Match lastCompleted = mDatabaseHelper.getLastCompletedMatch();
|
||||
if (lastCompleted != null) {
|
||||
Log.d(TAG, "applyLastMatch: Applying last completed match [" + lastCompleted + "]");
|
||||
runOnUiThread(() -> mMatchRecap.setMatch(lastCompleted));
|
||||
|
||||
// Enrich participant data with profile pictures from database (batch fetch)
|
||||
participants.addAll(lastCompleted.getAllParticipants());
|
||||
|
||||
// Collect all player IDs that need enrichment
|
||||
final List<Long> playerIds = new java.util.ArrayList<>();
|
||||
for (final Match.ParticipantData participant : participants) {
|
||||
if (participant.player.id > 0) {
|
||||
playerIds.add(participant.player.id);
|
||||
}
|
||||
}
|
||||
|
||||
// Batch fetch all players in one query
|
||||
if (!playerIds.isEmpty()) {
|
||||
Log.d(TAG, "applyLastMatch: Fetching " + playerIds.size() + " players: " + playerIds);
|
||||
final List<Player> fullPlayers = mDatabaseHelper.getPlayersByIds(playerIds);
|
||||
Log.d(TAG, "applyLastMatch: Retrieved " + fullPlayers.size() + " players from database");
|
||||
|
||||
// Create a map for quick lookup
|
||||
final java.util.Map<Long, Player> playerMap = new java.util.HashMap<>();
|
||||
for (final Player player : fullPlayers) {
|
||||
playerMap.put(player.id, player);
|
||||
Log.d(TAG, "applyLastMatch: Player " + player.id + " has profilePictureUri: "
|
||||
+ player.profilePictureUri);
|
||||
}
|
||||
|
||||
// Enrich each participant with full player data
|
||||
for (final Match.ParticipantData participant : participants) {
|
||||
final Player fullPlayer = playerMap.get(participant.player.id);
|
||||
if (fullPlayer != null) {
|
||||
Log.d(TAG, "applyLastMatch: Enriching participant " + participant.player.username
|
||||
+ " (id=" + participant.player.id + ") with profilePictureUri: "
|
||||
+ fullPlayer.profilePictureUri);
|
||||
participant.player.profilePictureUri = fullPlayer.profilePictureUri;
|
||||
participant.player.careerAverage = fullPlayer.careerAverage;
|
||||
Log.d(TAG, "applyLastMatch: After enrichment, participant.player.profilePictureUri = "
|
||||
+ participant.player.profilePictureUri);
|
||||
} else {
|
||||
// Player was deleted - participant will show with name only (no profile
|
||||
// picture)
|
||||
Log.d(TAG, "applyLastMatch: Player " + participant.player.id + " not found (deleted?)");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
runOnUiThread(() -> mMatchRecap.setMatchWithParticipants(lastCompleted, participants));
|
||||
}).start();
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies test data to the match recap view for development and testing.
|
||||
* Cycles through null match (counter % 3 == 0), 1v1 match (counter % 3 == 1),
|
||||
* and group match (counter % 3 == 2) based on the counter value.
|
||||
*
|
||||
* @param counter Counter value used to determine which test scenario to display.
|
||||
*/
|
||||
private void applyTestData(final int counter) {
|
||||
Log.d(TAG, "applyTestData: Applying Test Data [" + 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 score maps for test matches
|
||||
final java.util.Map<Integer, Integer> 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<Integer, Integer> 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);
|
||||
match1on1.markCompleted(); // Mark as completed for test data
|
||||
|
||||
final Match matchGroup = new Match("501", java.util.Arrays.asList(playerOne, playerTwo, playerThree, playerFour), scoresGroup);
|
||||
matchGroup.markCompleted(); // Mark as completed for test data
|
||||
|
||||
// Cycle through different test scenarios based on counter value
|
||||
if (counter % UIConstants.TEST_CYCLE_MODULO == 0) {
|
||||
Log.d(TAG, "applyTestData: No recent match selected.");
|
||||
// Scenario 1: No match (null state)
|
||||
mMatchRecap.setMatch(null);
|
||||
} else if (counter % UIConstants.TEST_CYCLE_MODULO == 1) {
|
||||
Log.d(TAG, "applyTestData: 1 on 1 Match");
|
||||
// Scenario 2: 1v1 match (two players)
|
||||
mMatchRecap.setMatch(match1on1);
|
||||
} else {
|
||||
Log.d(TAG, "applyTestData: Group Match.");
|
||||
// Scenario 3: Group match (four players)
|
||||
mMatchRecap.setMatch(matchGroup);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -629,6 +629,28 @@ public class DatabaseHelper {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves multiple players by their IDs in a single batch query.
|
||||
* More efficient than calling getPlayerById() multiple times.
|
||||
* Blocks until the operation completes to ensure consistency with any pending
|
||||
* writes.
|
||||
*
|
||||
* @param playerIds List of player IDs to retrieve
|
||||
* @return List of players found (may be fewer than requested if some IDs don't
|
||||
* exist)
|
||||
*/
|
||||
public List<Player> getPlayersByIds(final List<Long> playerIds) {
|
||||
if (playerIds == null || playerIds.isEmpty()) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
try {
|
||||
return mExecutor.submit(() -> mDatabase.playerDao().getPlayersByIds(playerIds)).get();
|
||||
} catch (final Exception e) {
|
||||
Log.e(TAG, "getPlayersByIds: Failed to submit task", e);
|
||||
return new ArrayList<>();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Inserts a PlayerStats entity into the database.
|
||||
*
|
||||
|
||||
@@ -76,6 +76,17 @@ public interface PlayerDao {
|
||||
@Query("SELECT * FROM players WHERE isPrimaryUser = 1 LIMIT 1")
|
||||
Player getPrimaryUser();
|
||||
|
||||
/**
|
||||
* Retrieves multiple players by their IDs in a single query.
|
||||
* Must be called on a background thread.
|
||||
*
|
||||
* @param ids List of player IDs to retrieve
|
||||
* @return List of players found (may be fewer than requested if some IDs don't
|
||||
* exist)
|
||||
*/
|
||||
@Query("SELECT * FROM players WHERE id IN (:ids)")
|
||||
List<Player> getPlayersByIds(final List<Long> ids);
|
||||
|
||||
/**
|
||||
* Clears the primary user flag from all players.
|
||||
* Used before setting a new primary user to ensure only one exists.
|
||||
|
||||
@@ -295,7 +295,8 @@ public class Match implements Serializable {
|
||||
}
|
||||
|
||||
/**
|
||||
* Data class representing a participant in a match with their score.
|
||||
* Data class representing a participant in a match with their score and game
|
||||
* average.
|
||||
*/
|
||||
public static class ParticipantData {
|
||||
/** The Player object */
|
||||
@@ -304,19 +305,48 @@ public class Match implements Serializable {
|
||||
/** The player's score in this match */
|
||||
public final int score;
|
||||
|
||||
public ParticipantData(final Player player, final int score) {
|
||||
/** The player's average for this specific game (points per 3 darts) */
|
||||
public final double gameAverage;
|
||||
|
||||
public ParticipantData(final Player player, final int score, final double gameAverage) {
|
||||
this.player = player;
|
||||
this.score = score;
|
||||
this.gameAverage = gameAverage;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all participants with their match scores.
|
||||
* Returns all participants with their match scores and game averages.
|
||||
* Handles both legacy JSONArray format and MatchProgress JSONObject format.
|
||||
*
|
||||
* @return List of ParticipantData objects containing player info and scores
|
||||
* @return List of ParticipantData objects containing player info, scores, and
|
||||
* game averages
|
||||
*/
|
||||
public List<ParticipantData> getAllParticipants() {
|
||||
final List<ParticipantData> participants = new ArrayList<>();
|
||||
|
||||
// First, try to parse as MatchProgress format (JSONObject with "players" array)
|
||||
final MatchProgress progress = MatchProgressConverter.fromString(participantData);
|
||||
if (progress != null && progress.players != null) {
|
||||
// New format: MatchProgress object
|
||||
for (final MatchProgress.PlayerStateSnapshot playerProgress : progress.players) {
|
||||
final Player player = new Player(playerProgress.name, null);
|
||||
player.id = playerProgress.playerId;
|
||||
|
||||
final int score = playerProgress.remainingScore;
|
||||
|
||||
// Calculate game average: (scored points / darts thrown) * 3
|
||||
final int scoredPoints = progress.startingScore - playerProgress.remainingScore;
|
||||
final double gameAverage = playerProgress.dartsThrown > 0
|
||||
? (double) scoredPoints / playerProgress.dartsThrown * 3
|
||||
: 0.0;
|
||||
|
||||
participants.add(new ParticipantData(player, score, gameAverage));
|
||||
}
|
||||
return participants;
|
||||
}
|
||||
|
||||
// Fall back to legacy JSONArray format
|
||||
try {
|
||||
final JSONArray participantArray = new JSONArray(participantData);
|
||||
for (int i = 0; i < participantArray.length(); i++) {
|
||||
@@ -327,7 +357,8 @@ public class Match implements Serializable {
|
||||
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));
|
||||
// Legacy format doesn't have game average data, use 0.0
|
||||
participants.add(new ParticipantData(player, score, 0.0));
|
||||
}
|
||||
} catch (JSONException e) {
|
||||
// Return empty list if JSON parsing fails
|
||||
|
||||
@@ -16,14 +16,16 @@ import java.util.List;
|
||||
/**
|
||||
* GameManager: Singleton manager for handling all X01 game business logic.
|
||||
* <p>
|
||||
* This class serves as the central data pool and business logic handler for an active darts match.
|
||||
* This class serves as the central data pool and business logic handler for an
|
||||
* active darts match.
|
||||
* It manages:
|
||||
* - Match state (scores, active player, dart throws)
|
||||
* - Database operations (loading/saving match progress)
|
||||
* - Game rules (bust detection, double-out, win conditions)
|
||||
* - Statistics tracking
|
||||
* <p>
|
||||
* The GameManager decouples business logic from UI, making GameActivity a simple view controller
|
||||
* The GameManager decouples business logic from UI, making GameActivity a
|
||||
* simple view controller
|
||||
* that only handles UI updates via the GameStateCallback interface.
|
||||
*/
|
||||
public class GameManager {
|
||||
@@ -135,6 +137,7 @@ public class GameManager {
|
||||
|
||||
/**
|
||||
* Called when the multiplier has changed.
|
||||
*
|
||||
* @param multiplier The new multiplier value (1=Single, 2=Double, 3=Triple)
|
||||
*/
|
||||
void onMultiplierChanged(int multiplier);
|
||||
@@ -146,7 +149,8 @@ public class GameManager {
|
||||
|
||||
/**
|
||||
* Called when a player wins the match.
|
||||
* @param winner The winning player's state
|
||||
*
|
||||
* @param winner The winning player's state
|
||||
* @param checkoutValue The final dart value that won the game
|
||||
*/
|
||||
void onPlayerWin(PlayerState winner, int checkoutValue);
|
||||
@@ -180,7 +184,7 @@ public class GameManager {
|
||||
/**
|
||||
* Constructs a DartHit with the specified base value and multiplier.
|
||||
*
|
||||
* @param baseValue The dartboard number (1-20 or 25 for bull)
|
||||
* @param baseValue The dartboard number (1-20 or 25 for bull)
|
||||
* @param multiplier The multiplier (1=single, 2=double, 3=triple)
|
||||
*/
|
||||
public DartHit(final int baseValue, final int multiplier) {
|
||||
@@ -227,7 +231,7 @@ public class GameManager {
|
||||
/**
|
||||
* Constructs a PlayerState for a player with the specified starting score.
|
||||
*
|
||||
* @param player The Player entity from the database
|
||||
* @param player The Player entity from the database
|
||||
* @param startScore The starting score for this X01 game (e.g., 501)
|
||||
*/
|
||||
public PlayerState(final Player player, final int startScore) {
|
||||
@@ -242,7 +246,8 @@ public class GameManager {
|
||||
* Private constructor to enforce singleton pattern.
|
||||
* Initializes the database helper with application context.
|
||||
*
|
||||
* @param context Application or Activity context (will be converted to application context)
|
||||
* @param context Application or Activity context (will be converted to
|
||||
* application context)
|
||||
*/
|
||||
private GameManager(final Context context) {
|
||||
mDatabaseHelper = DatabaseHelper.getInstance(context);
|
||||
@@ -269,20 +274,24 @@ public class GameManager {
|
||||
*/
|
||||
public void setCallback(final GameStateCallback callback) {
|
||||
mCallback = callback;
|
||||
//Send one initial callback to sync UI with current state
|
||||
// Send one initial callback to sync UI with current state
|
||||
notifyGameStateChanged();
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes a new game or loads an existing match from the database with explicit player list.
|
||||
* This overload allows passing a pre-loaded player list instead of loading from database.
|
||||
* Initializes a new game or loads an existing match from the database with
|
||||
* explicit player list.
|
||||
* This overload allows passing a pre-loaded player list instead of loading from
|
||||
* database.
|
||||
*
|
||||
* @param matchId The match ID to load, or -1 to create a new match
|
||||
* @param matchId The match ID to load, or -1 to create a new match
|
||||
* @param startingScore The starting score (501, 301, etc.)
|
||||
* @param players Pre-loaded list of players to use for this match
|
||||
* @param onComplete Callback executed on the calling thread when initialization is complete
|
||||
* @param players Pre-loaded list of players to use for this match
|
||||
* @param onComplete Callback executed on the calling thread when
|
||||
* initialization is complete
|
||||
*/
|
||||
public void initializeMatch(final int matchId, final int startingScore, final List<Player> players, final Runnable onComplete) {
|
||||
public void initializeMatch(final int matchId, final int startingScore, final List<Player> players,
|
||||
final Runnable onComplete) {
|
||||
mStartingScore = startingScore;
|
||||
mMatchId = matchId;
|
||||
|
||||
@@ -293,13 +302,13 @@ public class GameManager {
|
||||
match = mDatabaseHelper.getMatchById(matchId);
|
||||
Log.d(TAG, "initializeMatch: Loaded match from DB: " + match);
|
||||
|
||||
|
||||
if (match != null && match.participantData != null && !match.participantData.isEmpty()) {
|
||||
// Load match progress from database
|
||||
try {
|
||||
final MatchProgress progress = MatchProgressConverter.fromString(match.participantData);
|
||||
if (progress != null) {
|
||||
Log.d(TAG, "initializeMatch: Found saved progress with " + progress.players.size() + " players");
|
||||
Log.d(TAG, "initializeMatch: Found saved progress with " + progress.players.size()
|
||||
+ " players");
|
||||
// Initialize player states
|
||||
initializePlayerStates(players);
|
||||
loadMatchProgress(progress);
|
||||
@@ -341,14 +350,13 @@ public class GameManager {
|
||||
}).start();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Initializes a new game or loads an existing match from the database.
|
||||
* This should be called when starting/resuming a match.
|
||||
*
|
||||
* @param matchId The match ID to load, or -1 to create a new match
|
||||
* @param matchId The match ID to load, or -1 to create a new match
|
||||
* @param startingScore The starting score (501, 301, etc.)
|
||||
* @param onComplete Callback when initialization is complete
|
||||
* @param onComplete Callback when initialization is complete
|
||||
*/
|
||||
public void initializeMatch(final int matchId, final int startingScore, final Runnable onComplete) {
|
||||
mStartingScore = startingScore;
|
||||
@@ -367,7 +375,8 @@ public class GameManager {
|
||||
* for each player with the current starting score.
|
||||
* If no players are provided, creates a single guest player.
|
||||
*
|
||||
* @param players List of Player entities to initialize, or null/empty for guest player
|
||||
* @param players List of Player entities to initialize, or null/empty for guest
|
||||
* player
|
||||
*/
|
||||
private void initializePlayerStates(final List<Player> players) {
|
||||
mPlayerStates.clear();
|
||||
@@ -390,7 +399,8 @@ public class GameManager {
|
||||
* @param progress The MatchProgress snapshot to restore from
|
||||
*/
|
||||
private void loadMatchProgress(final MatchProgress progress) {
|
||||
if (progress == null || mPlayerStates.isEmpty()) return;
|
||||
if (progress == null || mPlayerStates.isEmpty())
|
||||
return;
|
||||
|
||||
Log.d(TAG, "loadMatchProgress: Loading saved match progress");
|
||||
|
||||
@@ -414,7 +424,8 @@ public class GameManager {
|
||||
* @param baseValue Face value of the number hit (1-20 or 25 for Bull)
|
||||
*/
|
||||
public void onNumberTap(final int baseValue) {
|
||||
if (mCurrentTurnDarts.size() >= 3 || mIsTurnOver) return;
|
||||
if (mCurrentTurnDarts.size() >= 3 || mIsTurnOver)
|
||||
return;
|
||||
|
||||
int points = baseValue * mMultiplier;
|
||||
if (baseValue == DartsConstants.BULL_VALUE && mMultiplier == DartsConstants.MULTIPLIER_TRIPLE) {
|
||||
@@ -423,10 +434,12 @@ public class GameManager {
|
||||
|
||||
PlayerState active = mPlayerStates.get(mActivePlayerIndex);
|
||||
int scoreBeforeDart = active.remainingScore;
|
||||
for (int d : mCurrentTurnDarts) scoreBeforeDart -= d;
|
||||
for (int d : mCurrentTurnDarts)
|
||||
scoreBeforeDart -= d;
|
||||
|
||||
int scoreAfterDart = scoreBeforeDart - points;
|
||||
boolean isDouble = (mMultiplier == DartsConstants.MULTIPLIER_DOUBLE) || (points == DartsConstants.DOUBLE_BULL_VALUE);
|
||||
boolean isDouble = (mMultiplier == DartsConstants.MULTIPLIER_DOUBLE)
|
||||
|| (points == DartsConstants.DOUBLE_BULL_VALUE);
|
||||
|
||||
// --- DOUBLE OUT LOGIC CHECK ---
|
||||
if (scoreAfterDart < 0 || scoreAfterDart == DartsConstants.BUST_SCORE || (scoreAfterDart == 0 && !isDouble)) {
|
||||
@@ -482,9 +495,14 @@ public class GameManager {
|
||||
private void handleWin(final PlayerState winner) {
|
||||
final int dartsThrown = mCurrentTurnDarts.size();
|
||||
int pointsMade = 0;
|
||||
for (int d : mCurrentTurnDarts) pointsMade += d;
|
||||
for (int d : mCurrentTurnDarts)
|
||||
pointsMade += d;
|
||||
final int checkoutValue = mCurrentTurnDarts.get(mCurrentTurnDarts.size() - 1);
|
||||
|
||||
// Update winner's score to 0 and increment darts thrown
|
||||
winner.remainingScore = 0;
|
||||
winner.dartsThrown += dartsThrown;
|
||||
|
||||
// Update statistics
|
||||
updatePlayerStats(winner, dartsThrown, pointsMade, false, checkoutValue);
|
||||
|
||||
@@ -515,11 +533,13 @@ public class GameManager {
|
||||
*/
|
||||
public void submitTurn() {
|
||||
// Don't submit if no darts thrown
|
||||
if (mCurrentTurnDarts.isEmpty()) return;
|
||||
if (mCurrentTurnDarts.isEmpty())
|
||||
return;
|
||||
|
||||
// Calculate turn total
|
||||
int turnTotal = 0;
|
||||
for (int d : mCurrentTurnDarts) turnTotal += d;
|
||||
for (int d : mCurrentTurnDarts)
|
||||
turnTotal += d;
|
||||
|
||||
PlayerState active = mPlayerStates.get(mActivePlayerIndex);
|
||||
|
||||
@@ -608,8 +628,7 @@ public class GameManager {
|
||||
state.playerId,
|
||||
state.name,
|
||||
state.remainingScore,
|
||||
state.dartsThrown
|
||||
));
|
||||
state.dartsThrown));
|
||||
}
|
||||
|
||||
String progressJson = MatchProgressConverter.fromProgress(progress);
|
||||
@@ -641,7 +660,8 @@ public class GameManager {
|
||||
* @param winner The PlayerState of the winning player (used for logging)
|
||||
*/
|
||||
private void saveCompletedMatch(final PlayerState winner) {
|
||||
if (mMatchId <= 0) return;
|
||||
if (mMatchId <= 0)
|
||||
return;
|
||||
|
||||
new Thread(() -> {
|
||||
try {
|
||||
@@ -660,8 +680,7 @@ public class GameManager {
|
||||
state.playerId,
|
||||
state.name,
|
||||
state.remainingScore,
|
||||
state.dartsThrown
|
||||
));
|
||||
state.dartsThrown));
|
||||
}
|
||||
|
||||
match.participantData = MatchProgressConverter.fromProgress(finalProgress);
|
||||
@@ -679,15 +698,16 @@ public class GameManager {
|
||||
|
||||
/**
|
||||
* Updates player statistics in the database after a turn.
|
||||
* Convenience method that delegates to the overloaded version with checkoutValue=0.
|
||||
* Convenience method that delegates to the overloaded version with
|
||||
* checkoutValue=0.
|
||||
*
|
||||
* @param active The PlayerState whose statistics should be updated
|
||||
* @param active The PlayerState whose statistics should be updated
|
||||
* @param dartsThrown Number of darts thrown in this turn
|
||||
* @param pointsMade Total points scored in this turn
|
||||
* @param wasBust Whether this turn resulted in a bust
|
||||
* @param pointsMade Total points scored in this turn
|
||||
* @param wasBust Whether this turn resulted in a bust
|
||||
*/
|
||||
private void updatePlayerStats(final PlayerState active, final int dartsThrown, final int pointsMade,
|
||||
final boolean wasBust) {
|
||||
final boolean wasBust) {
|
||||
updatePlayerStats(active, dartsThrown, pointsMade, wasBust, 0);
|
||||
}
|
||||
|
||||
@@ -696,14 +716,14 @@ public class GameManager {
|
||||
* Tracks darts thrown, points made, bust count, and successful checkouts.
|
||||
* Executes asynchronously on a background thread.
|
||||
*
|
||||
* @param active The PlayerState whose statistics should be updated
|
||||
* @param dartsThrown Number of darts thrown in this turn
|
||||
* @param pointsMade Total points scored in this turn
|
||||
* @param wasBust Whether this turn resulted in a bust
|
||||
* @param active The PlayerState whose statistics should be updated
|
||||
* @param dartsThrown Number of darts thrown in this turn
|
||||
* @param pointsMade Total points scored in this turn
|
||||
* @param wasBust Whether this turn resulted in a bust
|
||||
* @param checkoutValue The checkout score if this was a winning turn (0 if not)
|
||||
*/
|
||||
private void updatePlayerStats(final PlayerState active, final int dartsThrown, final int pointsMade,
|
||||
final boolean wasBust, final int checkoutValue) {
|
||||
final boolean wasBust, final int checkoutValue) {
|
||||
if (active.player != null && active.player.id != 0) {
|
||||
new Thread(() -> mDatabaseHelper.updatePlayerStatistics(
|
||||
active.player.id,
|
||||
@@ -711,8 +731,7 @@ public class GameManager {
|
||||
pointsMade,
|
||||
wasBust,
|
||||
checkoutValue,
|
||||
active.dartsThrown
|
||||
)).start();
|
||||
active.dartsThrown)).start();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -722,7 +741,8 @@ public class GameManager {
|
||||
* they succeeded or missed. Executes asynchronously on a background thread.
|
||||
*
|
||||
* @param playerState The PlayerState of the player who attempted the double
|
||||
* @param isMissed true if the double-out attempt was missed, false if successful
|
||||
* @param isMissed true if the double-out attempt was missed, false if
|
||||
* successful
|
||||
*/
|
||||
private void trackDoubleAttempt(final PlayerState playerState, final boolean isMissed) {
|
||||
new Thread(() -> mDatabaseHelper.trackDoubleAttempt(playerState.playerId, isMissed)).start();
|
||||
@@ -748,10 +768,12 @@ public class GameManager {
|
||||
* Executes asynchronously on a background thread.
|
||||
*
|
||||
* @param playerState The PlayerState whose statistics should be updated
|
||||
* @param dartHits List of DartHit objects representing the darts thrown in this turn
|
||||
* @param dartHits List of DartHit objects representing the darts thrown in
|
||||
* this turn
|
||||
*/
|
||||
private void recordTurnHitsToStatistics(final PlayerState playerState, final List<DartHit> dartHits) {
|
||||
if (dartHits.isEmpty()) return;
|
||||
if (dartHits.isEmpty())
|
||||
return;
|
||||
|
||||
final List<DatabaseHelper.DartHit> dbDartHits = new ArrayList<>();
|
||||
for (DartHit hit : dartHits) {
|
||||
@@ -833,7 +855,8 @@ public class GameManager {
|
||||
* @return The active PlayerState, or null if no players are loaded
|
||||
*/
|
||||
public PlayerState getActivePlayer() {
|
||||
if (mPlayerStates.isEmpty()) return null;
|
||||
if (mPlayerStates.isEmpty())
|
||||
return null;
|
||||
return mPlayerStates.get(mActivePlayerIndex);
|
||||
}
|
||||
|
||||
@@ -885,22 +908,26 @@ public class GameManager {
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates the current target score (remaining score minus current turn darts).
|
||||
* If the turn is busted, returns the remaining score without subtracting bust darts
|
||||
* Calculates the current target score (remaining score minus current turn
|
||||
* darts).
|
||||
* If the turn is busted, returns the remaining score without subtracting bust
|
||||
* darts
|
||||
* since those darts don't count.
|
||||
*
|
||||
* @return The effective remaining score after considering current turn darts
|
||||
*/
|
||||
public int getCurrentTarget() {
|
||||
PlayerState active = getActivePlayer();
|
||||
if (active == null) return 0;
|
||||
if (active == null)
|
||||
return 0;
|
||||
|
||||
if (mIsBustedTurn) {
|
||||
return active.remainingScore;
|
||||
}
|
||||
|
||||
int turnPointsSoFar = 0;
|
||||
for (int d : mCurrentTurnDarts) turnPointsSoFar += d;
|
||||
for (int d : mCurrentTurnDarts)
|
||||
turnPointsSoFar += d;
|
||||
return active.remainingScore - turnPointsSoFar;
|
||||
}
|
||||
|
||||
@@ -930,7 +957,8 @@ public class GameManager {
|
||||
}
|
||||
|
||||
/**
|
||||
* Notifies the UI callback that the turn indicators (dart pills) should be updated.
|
||||
* Notifies the UI callback that the turn indicators (dart pills) should be
|
||||
* updated.
|
||||
* Called whenever darts are thrown or undone.
|
||||
* Null-safe - does nothing if no callback is registered.
|
||||
*/
|
||||
|
||||
@@ -14,6 +14,8 @@ import com.aldo.apps.ochecompanion.R;
|
||||
import com.aldo.apps.ochecompanion.database.objects.Match;
|
||||
import com.aldo.apps.ochecompanion.ui.adapter.MainMenuGroupMatchAdapter;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Displays summary of most recent match. Adapts display based on match type:
|
||||
* empty state (no matches), 1v1 state (2 players), or group state (3+ players).
|
||||
@@ -53,6 +55,9 @@ public class MatchRecapView extends FrameLayout {
|
||||
/** RecyclerView displaying leaderboard for group matches. */
|
||||
private RecyclerView mRvLeaderboard;
|
||||
|
||||
/** Adapter for group match leaderboard, reused across updates. */
|
||||
private MainMenuGroupMatchAdapter mGroupMatchAdapter;
|
||||
|
||||
/** Constructor for programmatic instantiation. */
|
||||
public MatchRecapView(@NonNull final Context context) {
|
||||
this(context, null);
|
||||
@@ -94,6 +99,27 @@ public class MatchRecapView extends FrameLayout {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Binds match with pre-enriched participants (e.g., with profile pictures from
|
||||
* database).
|
||||
* Use this when participants have already been enriched to avoid re-parsing
|
||||
* JSON.
|
||||
*/
|
||||
public void setMatchWithParticipants(@Nullable final Match match,
|
||||
@Nullable final List<Match.ParticipantData> enrichedParticipants) {
|
||||
Log.d(TAG, "setMatchWithParticipants() called with: match = [" + match + "]");
|
||||
if (match == null) {
|
||||
updateVisibility(mStateEmpty);
|
||||
return;
|
||||
}
|
||||
|
||||
if (match.getParticipantCount() > 2) {
|
||||
setupGroupStateWithParticipants(enrichedParticipants);
|
||||
} else {
|
||||
setup1v1State(match);
|
||||
}
|
||||
}
|
||||
|
||||
/** Configures 1v1 state with player names and scores. */
|
||||
private void setup1v1State(final Match match) {
|
||||
updateVisibility(mState1v1);
|
||||
@@ -109,11 +135,30 @@ public class MatchRecapView extends FrameLayout {
|
||||
private void setupGroupState(final Match match) {
|
||||
updateVisibility(mStateGroup);
|
||||
|
||||
mRvLeaderboard.setLayoutManager(new LinearLayoutManager(getContext()));
|
||||
// Initialize adapter and layout manager only once
|
||||
if (mGroupMatchAdapter == null) {
|
||||
mGroupMatchAdapter = new MainMenuGroupMatchAdapter();
|
||||
mRvLeaderboard.setLayoutManager(new LinearLayoutManager(getContext()));
|
||||
mRvLeaderboard.setAdapter(mGroupMatchAdapter);
|
||||
}
|
||||
|
||||
final MainMenuGroupMatchAdapter adapter = new MainMenuGroupMatchAdapter();
|
||||
mRvLeaderboard.setAdapter(adapter);
|
||||
adapter.updateMatch(match);
|
||||
// Update the adapter with new match data
|
||||
mGroupMatchAdapter.updateMatch(match);
|
||||
}
|
||||
|
||||
/** Configures group state with pre-enriched participants. */
|
||||
private void setupGroupStateWithParticipants(final List<Match.ParticipantData> enrichedParticipants) {
|
||||
updateVisibility(mStateGroup);
|
||||
|
||||
// Initialize adapter and layout manager only once
|
||||
if (mGroupMatchAdapter == null) {
|
||||
mGroupMatchAdapter = new MainMenuGroupMatchAdapter();
|
||||
mRvLeaderboard.setLayoutManager(new LinearLayoutManager(getContext()));
|
||||
mRvLeaderboard.setAdapter(mGroupMatchAdapter);
|
||||
}
|
||||
|
||||
// Update the adapter with enriched participant data
|
||||
mGroupMatchAdapter.updateParticipants(enrichedParticipants);
|
||||
}
|
||||
|
||||
/** Shows only the specified state container, hides others. */
|
||||
|
||||
@@ -2,6 +2,7 @@ package com.aldo.apps.ochecompanion.ui;
|
||||
|
||||
import android.content.Context;
|
||||
import android.util.AttributeSet;
|
||||
import android.util.Log;
|
||||
import android.widget.TextView;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
@@ -13,11 +14,18 @@ import com.google.android.material.imageview.ShapeableImageView;
|
||||
import com.aldo.apps.ochecompanion.R;
|
||||
|
||||
/**
|
||||
* Reusable MaterialCardView for displaying player info: profile picture, username,
|
||||
* and career statistics. Uses Glide for image loading with fallback to default icon.
|
||||
* Reusable MaterialCardView for displaying player info: profile picture,
|
||||
* username,
|
||||
* and career statistics. Uses Glide for image loading with fallback to default
|
||||
* icon.
|
||||
*/
|
||||
public class PlayerItemView extends MaterialCardView {
|
||||
|
||||
/**
|
||||
* Tag for debugging purposes.
|
||||
*/
|
||||
private static final String TAG = "PlayerItemView";
|
||||
|
||||
/** Player profile picture (circular, loaded via Glide). */
|
||||
private ShapeableImageView mIvAvatar;
|
||||
|
||||
@@ -52,6 +60,7 @@ public class PlayerItemView extends MaterialCardView {
|
||||
|
||||
/**
|
||||
* Binds player data to view components (username, stats, avatar).
|
||||
*
|
||||
* @param player The Player object containing data to display
|
||||
*/
|
||||
public void bind(@NonNull final Player player) {
|
||||
@@ -62,20 +71,22 @@ public class PlayerItemView extends MaterialCardView {
|
||||
|
||||
if (player.profilePictureUri != null) {
|
||||
Glide.with(getContext())
|
||||
.load(player.profilePictureUri)
|
||||
.into(mIvAvatar);
|
||||
.load(player.profilePictureUri)
|
||||
.into(mIvAvatar);
|
||||
} else {
|
||||
mIvAvatar.setImageResource(R.drawable.ic_users);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Binds player data along with a specific score (e.g., match score) instead of career average.
|
||||
* Binds player data along with a specific score (e.g., match score) instead of
|
||||
* career average.
|
||||
*
|
||||
* @param player The Player object containing data to display
|
||||
* @param score The specific score to display (e.g., current match score)
|
||||
* @param score The specific score to display (e.g., current match score)
|
||||
*/
|
||||
public void bindWithScore(@NonNull final Player player, final int score) {
|
||||
|
||||
Log.d(TAG, "bindWithScore() called with: player = [" + player + "], score = [" + score + "]");
|
||||
mTvUsername.setText(player.username);
|
||||
// Display match score instead of career average
|
||||
mTvStats.setText(String.valueOf(score));
|
||||
@@ -89,4 +100,71 @@ public class PlayerItemView extends MaterialCardView {
|
||||
}
|
||||
}
|
||||
|
||||
public void bindWithGameAverage(@NonNull final Player player, final double gameAverage) {
|
||||
Log.d(TAG, "bindWithGameAverage() called with: player = [" + player
|
||||
+ "], gameAverage = [" + gameAverage + "]");
|
||||
mTvUsername.setText(player.username);
|
||||
// Display game average
|
||||
mTvStats.setText(String.format(
|
||||
getContext().getString(R.string.txt_player_average_base), gameAverage));
|
||||
|
||||
if (player.profilePictureUri != null) {
|
||||
Glide.with(getContext())
|
||||
.load(player.profilePictureUri)
|
||||
.placeholder(R.drawable.ic_users)
|
||||
.into(mIvAvatar);
|
||||
} else {
|
||||
mIvAvatar.setImageResource(R.drawable.ic_users);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Binds player data with game average and position indicator.
|
||||
*
|
||||
* @param player The Player object containing data to display
|
||||
* @param gameAverage The game average to display (points per 3 darts)
|
||||
* @param position The player's position (1 = 1st place, 2 = 2nd place, etc.)
|
||||
* @param isWinner Whether this player won (remaining score = 0)
|
||||
*/
|
||||
public void bindWithGameAverageAndPosition(@NonNull final Player player, final double gameAverage,
|
||||
final int position, final boolean isWinner) {
|
||||
Log.d(TAG, "bindWithGameAverageAndPosition() called with: player = [" + player
|
||||
+ "], gameAverage = [" + gameAverage + "], position = [" + position + "], isWinner = [" + isWinner
|
||||
+ "]");
|
||||
|
||||
// Build username with position indicator
|
||||
final String positionSuffix = getPositionSuffix(position);
|
||||
final String displayName = isWinner
|
||||
? "🏆 " + player.username + " (" + positionSuffix + ")"
|
||||
: player.username + " (" + positionSuffix + ")";
|
||||
|
||||
mTvUsername.setText(displayName);
|
||||
|
||||
// Display game average
|
||||
mTvStats.setText(String.format(
|
||||
getContext().getString(R.string.txt_player_average_base), gameAverage));
|
||||
|
||||
if (player.profilePictureUri != null) {
|
||||
Glide.with(getContext())
|
||||
.load(player.profilePictureUri)
|
||||
.placeholder(R.drawable.ic_users)
|
||||
.into(mIvAvatar);
|
||||
} else {
|
||||
mIvAvatar.setImageResource(R.drawable.ic_users);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the position suffix (1st, 2nd, 3rd, 4th, etc.)
|
||||
*/
|
||||
private String getPositionSuffix(final int position) {
|
||||
if (position == 1)
|
||||
return "1st";
|
||||
if (position == 2)
|
||||
return "2nd";
|
||||
if (position == 3)
|
||||
return "3rd";
|
||||
return position + "th";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -21,7 +21,8 @@ import java.util.List;
|
||||
|
||||
/**
|
||||
* RecyclerView adapter for displaying group match results in Main Menu.
|
||||
* Displays participants sorted by match score (descending) 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<MainMenuGroupMatchAdapter.GroupMatchHolder> {
|
||||
|
||||
@@ -34,7 +35,7 @@ public class MainMenuGroupMatchAdapter extends RecyclerView.Adapter<MainMenuGrou
|
||||
/**
|
||||
* Creates a new ViewHolder with a PlayerItemView.
|
||||
*
|
||||
* @param parent The parent ViewGroup.
|
||||
* @param parent The parent ViewGroup.
|
||||
* @param viewType The view type (unused).
|
||||
* @return A new GroupMatchHolder.
|
||||
*/
|
||||
@@ -42,13 +43,12 @@ public class MainMenuGroupMatchAdapter extends RecyclerView.Adapter<MainMenuGrou
|
||||
@Override
|
||||
public GroupMatchHolder onCreateViewHolder(@NonNull final ViewGroup parent, final int viewType) {
|
||||
// Create a new PlayerItemView for displaying player information
|
||||
final PlayerItemView itemView = new PlayerItemView(parent.getContext());
|
||||
final PlayerItemView itemView = new PlayerItemView(parent.getContext());
|
||||
|
||||
// Configure layout parameters for the item view
|
||||
itemView.setLayoutParams(new RecyclerView.LayoutParams(
|
||||
ViewGroup.LayoutParams.MATCH_PARENT,
|
||||
ViewGroup.LayoutParams.WRAP_CONTENT
|
||||
));
|
||||
ViewGroup.LayoutParams.WRAP_CONTENT));
|
||||
|
||||
return new GroupMatchHolder(itemView);
|
||||
}
|
||||
@@ -56,13 +56,15 @@ public class MainMenuGroupMatchAdapter extends RecyclerView.Adapter<MainMenuGrou
|
||||
/**
|
||||
* Binds player data to the ViewHolder at the specified position.
|
||||
*
|
||||
* @param holder The ViewHolder to update.
|
||||
* @param holder The ViewHolder to update.
|
||||
* @param position The position in the data set.
|
||||
*/
|
||||
@Override
|
||||
public void onBindViewHolder(@NonNull final GroupMatchHolder holder, final int position) {
|
||||
// Retrieve the participant at this position and bind it to the holder
|
||||
holder.setParticipant(mParticipantsList.get(position));
|
||||
// Position is 0-indexed, but we want 1-indexed for display (1st, 2nd, 3rd,
|
||||
// etc.)
|
||||
holder.setParticipant(mParticipantsList.get(position), position + 1);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -76,7 +78,8 @@ public class MainMenuGroupMatchAdapter extends RecyclerView.Adapter<MainMenuGrou
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the adapter with match data, sorting participants by match score (descending).
|
||||
* Updates the adapter with match data, sorting participants by match score
|
||||
* (descending).
|
||||
*
|
||||
* @param match The match containing participants to display.
|
||||
*/
|
||||
@@ -96,8 +99,9 @@ public class MainMenuGroupMatchAdapter extends RecyclerView.Adapter<MainMenuGrou
|
||||
return;
|
||||
}
|
||||
|
||||
// Sort participants by match score (highest to lowest)
|
||||
participants.sort((p1, p2) -> Integer.compare(p2.score, p1.score));
|
||||
// Sort participants by remaining score (lowest to highest = best to worst)
|
||||
// In darts, 0 remaining = winner, so lowest score = best placement
|
||||
participants.sort((p1, p2) -> Integer.compare(p1.score, p2.score));
|
||||
|
||||
// Add sorted participants to the display list
|
||||
mParticipantsList.addAll(participants);
|
||||
@@ -106,6 +110,34 @@ public class MainMenuGroupMatchAdapter extends RecyclerView.Adapter<MainMenuGrou
|
||||
notifyDataSetChanged();
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the adapter with pre-enriched participant data (e.g., with profile
|
||||
* pictures).
|
||||
* Use this when participants have already been enriched with database data.
|
||||
*
|
||||
* @param participants Pre-enriched participant list to display.
|
||||
*/
|
||||
@SuppressLint("NotifyDataSetChanged")
|
||||
public void updateParticipants(final List<Match.ParticipantData> participants) {
|
||||
// Clear any existing participant data
|
||||
mParticipantsList.clear();
|
||||
|
||||
if (participants == null || participants.isEmpty()) {
|
||||
Log.d(TAG, "updateParticipants: No participants provided, just clearing.");
|
||||
notifyDataSetChanged();
|
||||
return;
|
||||
}
|
||||
|
||||
// Sort participants by remaining score (lowest to highest = best to worst)
|
||||
// In darts, 0 remaining = winner, so lowest score = best placement
|
||||
participants.sort((p1, p2) -> Integer.compare(p1.score, p2.score));
|
||||
|
||||
// Add sorted participants to the display list
|
||||
mParticipantsList.addAll(participants);
|
||||
|
||||
// Notify RecyclerView to refresh the display
|
||||
notifyDataSetChanged();
|
||||
}
|
||||
|
||||
/**
|
||||
* ViewHolder for displaying player items in group match view.
|
||||
@@ -132,14 +164,19 @@ public class MainMenuGroupMatchAdapter extends RecyclerView.Adapter<MainMenuGrou
|
||||
}
|
||||
|
||||
/**
|
||||
* Binds participant data (player + match score) to this ViewHolder.
|
||||
* Binds participant data (player + match score + game average + position) to
|
||||
* this
|
||||
* ViewHolder.
|
||||
*
|
||||
* @param participantData The participant data to display.
|
||||
* @param position The player's position (1 = 1st place, 2 = 2nd place,
|
||||
* etc.)
|
||||
*/
|
||||
public void setParticipant(final Match.ParticipantData participantData) {
|
||||
public void setParticipant(final Match.ParticipantData participantData, final int position) {
|
||||
final Player player = participantData.player;
|
||||
final int score = participantData.score;
|
||||
mItemView.bindWithScore(player, score);
|
||||
final double gameAverage = participantData.gameAverage;
|
||||
final boolean isWinner = participantData.score == 0; // Winner has 0 remaining score
|
||||
mItemView.bindWithGameAverageAndPosition(player, gameAverage, position, isWinner);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user