Cleaned up code
This commit is contained in:
@@ -15,6 +15,7 @@ import android.widget.TextView;
|
|||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
import androidx.activity.result.ActivityResultLauncher;
|
import androidx.activity.result.ActivityResultLauncher;
|
||||||
import androidx.activity.result.contract.ActivityResultContracts;
|
import androidx.activity.result.contract.ActivityResultContracts;
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
import androidx.appcompat.app.AppCompatActivity;
|
import androidx.appcompat.app.AppCompatActivity;
|
||||||
import com.aldo.apps.ochecompanion.database.AppDatabase;
|
import com.aldo.apps.ochecompanion.database.AppDatabase;
|
||||||
import com.aldo.apps.ochecompanion.database.objects.Player;
|
import com.aldo.apps.ochecompanion.database.objects.Player;
|
||||||
@@ -77,6 +78,11 @@ public class AddPlayerActivity extends AppCompatActivity {
|
|||||||
*/
|
*/
|
||||||
private MaterialButton mSaveButton;
|
private MaterialButton mSaveButton;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Button to delete a players profile from the database.
|
||||||
|
*/
|
||||||
|
private ImageView mBtnDelete;
|
||||||
|
|
||||||
// ========== UI - Cropper Views ==========
|
// ========== UI - Cropper Views ==========
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -183,6 +189,7 @@ public class AddPlayerActivity extends AppCompatActivity {
|
|||||||
mUserNameInput = findViewById(R.id.etUsername);
|
mUserNameInput = findViewById(R.id.etUsername);
|
||||||
mTitleView = findViewById(R.id.tvTitle);
|
mTitleView = findViewById(R.id.tvTitle);
|
||||||
mSaveButton = findViewById(R.id.btnSavePlayer);
|
mSaveButton = findViewById(R.id.btnSavePlayer);
|
||||||
|
mBtnDelete = findViewById(R.id.btnDeletePlayer);
|
||||||
|
|
||||||
// Get references to cropper UI elements
|
// Get references to cropper UI elements
|
||||||
mIvCropPreview = findViewById(R.id.ivCropPreview);
|
mIvCropPreview = findViewById(R.id.ivCropPreview);
|
||||||
@@ -191,6 +198,9 @@ public class AddPlayerActivity extends AppCompatActivity {
|
|||||||
// Set up click listeners
|
// Set up click listeners
|
||||||
mProfilePictureView.setOnClickListener(v -> mGetContent.launch("image/*"));
|
mProfilePictureView.setOnClickListener(v -> mGetContent.launch("image/*"));
|
||||||
mSaveButton.setOnClickListener(v -> savePlayer());
|
mSaveButton.setOnClickListener(v -> savePlayer());
|
||||||
|
if (mBtnDelete != null) {
|
||||||
|
mBtnDelete.setOnClickListener(v -> deletePlayer());
|
||||||
|
}
|
||||||
findViewById(R.id.btnConfirmCrop).setOnClickListener(v -> performCrop());
|
findViewById(R.id.btnConfirmCrop).setOnClickListener(v -> performCrop());
|
||||||
findViewById(R.id.btnCancelCrop).setOnClickListener(v -> exitCropMode());
|
findViewById(R.id.btnCancelCrop).setOnClickListener(v -> exitCropMode());
|
||||||
}
|
}
|
||||||
@@ -202,7 +212,7 @@ public class AddPlayerActivity extends AppCompatActivity {
|
|||||||
// Initialize scale detector for pinch-to-zoom functionality
|
// Initialize scale detector for pinch-to-zoom functionality
|
||||||
mScaleDetector = new ScaleGestureDetector(this, new ScaleGestureDetector.SimpleOnScaleGestureListener() {
|
mScaleDetector = new ScaleGestureDetector(this, new ScaleGestureDetector.SimpleOnScaleGestureListener() {
|
||||||
@Override
|
@Override
|
||||||
public boolean onScale(final ScaleGestureDetector detector) {
|
public boolean onScale(@NonNull final ScaleGestureDetector detector) {
|
||||||
// Apply the scale factor from the gesture
|
// Apply the scale factor from the gesture
|
||||||
mScaleFactor *= detector.getScaleFactor();
|
mScaleFactor *= detector.getScaleFactor();
|
||||||
|
|
||||||
@@ -401,6 +411,7 @@ public class AddPlayerActivity extends AppCompatActivity {
|
|||||||
// Update UI labels for "edit mode"
|
// Update UI labels for "edit mode"
|
||||||
mTitleView.setText(R.string.txt_update_profile_header);
|
mTitleView.setText(R.string.txt_update_profile_header);
|
||||||
mSaveButton.setText(R.string.txt_update_profile_username_save);
|
mSaveButton.setText(R.string.txt_update_profile_username_save);
|
||||||
|
if (mBtnDelete != null) mBtnDelete.setVisibility(View.VISIBLE);
|
||||||
|
|
||||||
// Load existing profile picture if available
|
// Load existing profile picture if available
|
||||||
if (mExistingPlayer.profilePictureUri != null) {
|
if (mExistingPlayer.profilePictureUri != null) {
|
||||||
@@ -413,6 +424,20 @@ public class AddPlayerActivity extends AppCompatActivity {
|
|||||||
}).start();
|
}).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.
|
* Validates and persists the player data to the database.
|
||||||
* Inserts new player or updates existing based on mExistingPlayer.
|
* 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
|
// Close activity on main thread after save completes
|
||||||
runOnUiThread(() -> finish());
|
runOnUiThread(this::finish);
|
||||||
}).start();
|
}).start();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -20,9 +20,8 @@ import com.aldo.apps.ochecompanion.utils.DartsConstants;
|
|||||||
import com.aldo.apps.ochecompanion.utils.UIConstants;
|
import com.aldo.apps.ochecompanion.utils.UIConstants;
|
||||||
import com.google.android.material.button.MaterialButton;
|
import com.google.android.material.button.MaterialButton;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.UUID;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Main game activity for playing X01 darts games (501, 301, etc.).
|
* 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";
|
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
|
// Game Logic State
|
||||||
// ========================================================================================
|
// ========================================================================================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The matches UUID from the database.
|
||||||
|
*/
|
||||||
|
private String mMatchUuid;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Index of the current active player (0 to playerCount-1).
|
* Index of the current active player (0 to playerCount-1).
|
||||||
* Cycles through players as turns complete.
|
* Cycles through players as turns complete.
|
||||||
@@ -151,6 +161,7 @@ public class GameActivity extends AppCompatActivity {
|
|||||||
Intent intent = new Intent(context, GameActivity.class);
|
Intent intent = new Intent(context, GameActivity.class);
|
||||||
intent.putParcelableArrayListExtra(EXTRA_PLAYERS, players);
|
intent.putParcelableArrayListExtra(EXTRA_PLAYERS, players);
|
||||||
intent.putExtra(EXTRA_START_SCORE, startScore);
|
intent.putExtra(EXTRA_START_SCORE, startScore);
|
||||||
|
intent.putExtra(EXTRA_MATCH_UUID, UUID.randomUUID().toString());
|
||||||
context.startActivity(intent);
|
context.startActivity(intent);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -166,6 +177,7 @@ public class GameActivity extends AppCompatActivity {
|
|||||||
|
|
||||||
// Extract game parameters from intent
|
// Extract game parameters from intent
|
||||||
mStartingScore = getIntent().getIntExtra(EXTRA_START_SCORE, DartsConstants.DEFAULT_GAME_SCORE);
|
mStartingScore = getIntent().getIntExtra(EXTRA_START_SCORE, DartsConstants.DEFAULT_GAME_SCORE);
|
||||||
|
mMatchUuid = getIntent().getStringExtra(EXTRA_MATCH_UUID);
|
||||||
ArrayList<Player> participants = getIntent().getParcelableArrayListExtra(EXTRA_PLAYERS);
|
ArrayList<Player> participants = getIntent().getParcelableArrayListExtra(EXTRA_PLAYERS);
|
||||||
|
|
||||||
// Initialize activity components in order
|
// Initialize activity components in order
|
||||||
@@ -377,6 +389,15 @@ public class GameActivity extends AppCompatActivity {
|
|||||||
// Update UI for next player
|
// Update UI for next player
|
||||||
updateUI();
|
updateUI();
|
||||||
updateTurnIndicators();
|
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
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ import android.content.Intent;
|
|||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
import android.widget.Toast;
|
|
||||||
|
|
||||||
import androidx.activity.EdgeToEdge;
|
import androidx.activity.EdgeToEdge;
|
||||||
import androidx.appcompat.app.AppCompatActivity;
|
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.AppDatabase;
|
||||||
import com.aldo.apps.ochecompanion.database.objects.Player;
|
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.MatchRecapView;
|
||||||
import com.aldo.apps.ochecompanion.ui.adapter.MainMenuPlayerAdapter;
|
import com.aldo.apps.ochecompanion.ui.adapter.MainMenuPlayerAdapter;
|
||||||
import com.aldo.apps.ochecompanion.utils.DartsConstants;
|
import com.aldo.apps.ochecompanion.utils.DartsConstants;
|
||||||
@@ -156,13 +155,28 @@ public class MainMenuActivity extends AppCompatActivity {
|
|||||||
private void applyTestData(final int counter) {
|
private void applyTestData(final int counter) {
|
||||||
// Create test player objects
|
// Create test player objects
|
||||||
final Player playerOne = new Player(DartsConstants.TEST_PLAYER_1, null);
|
final Player playerOne = new Player(DartsConstants.TEST_PLAYER_1, null);
|
||||||
|
playerOne.id = 1;
|
||||||
final Player playerTwo = new Player(DartsConstants.TEST_PLAYER_2, null);
|
final Player playerTwo = new Player(DartsConstants.TEST_PLAYER_2, null);
|
||||||
|
playerTwo.id = 2;
|
||||||
final Player playerThree = new Player(DartsConstants.TEST_PLAYER_3, null);
|
final Player playerThree = new Player(DartsConstants.TEST_PLAYER_3, null);
|
||||||
|
playerThree.id = 3;
|
||||||
final Player playerFour = new Player(DartsConstants.TEST_PLAYER_4, null);
|
final Player playerFour = new Player(DartsConstants.TEST_PLAYER_4, null);
|
||||||
|
playerFour.id = 4;
|
||||||
|
|
||||||
// Create test match objects with different player configurations
|
// Create score maps for test matches
|
||||||
final Match match1on1 = new Match(playerOne, playerTwo);
|
final java.util.Map<Integer, Integer> scores1v1 = new java.util.HashMap<>();
|
||||||
final Match matchGroup = new Match(playerOne, playerTwo, playerThree, playerFour);
|
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);
|
||||||
|
final Match matchGroup = new Match("501", java.util.Arrays.asList(playerOne, playerTwo, playerThree, playerFour), scoresGroup);
|
||||||
|
|
||||||
// Cycle through different test scenarios based on counter value
|
// Cycle through different test scenarios based on counter value
|
||||||
if (counter % UIConstants.TEST_CYCLE_MODULO == 0) {
|
if (counter % UIConstants.TEST_CYCLE_MODULO == 0) {
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ import com.aldo.apps.ochecompanion.database.objects.Player;
|
|||||||
* @see Player
|
* @see Player
|
||||||
* @see Match
|
* @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 {
|
public abstract class AppDatabase extends RoomDatabase {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package com.aldo.apps.ochecompanion.database.dao;
|
package com.aldo.apps.ochecompanion.database.dao;
|
||||||
|
|
||||||
import androidx.room.Dao;
|
import androidx.room.Dao;
|
||||||
|
import androidx.room.Delete;
|
||||||
import androidx.room.Insert;
|
import androidx.room.Insert;
|
||||||
import androidx.room.Query;
|
import androidx.room.Query;
|
||||||
import androidx.room.Update;
|
import androidx.room.Update;
|
||||||
@@ -34,6 +35,16 @@ public interface PlayerDao {
|
|||||||
@Update
|
@Update
|
||||||
void update(final Player player);
|
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.
|
* Retrieves a player by their unique ID.
|
||||||
* Must be called on a background thread.
|
* Must be called on a background thread.
|
||||||
|
|||||||
@@ -1,14 +1,23 @@
|
|||||||
package com.aldo.apps.ochecompanion.database.objects;
|
package com.aldo.apps.ochecompanion.database.objects;
|
||||||
|
|
||||||
import androidx.room.Entity;
|
import androidx.room.Entity;
|
||||||
|
import androidx.room.Ignore;
|
||||||
import androidx.room.PrimaryKey;
|
import androidx.room.PrimaryKey;
|
||||||
|
import org.json.JSONArray;
|
||||||
|
import org.json.JSONException;
|
||||||
|
import org.json.JSONObject;
|
||||||
import java.io.Serializable;
|
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.
|
* Represents a completed darts match in the Oche Companion application.
|
||||||
* Room entity storing match information including game mode, timestamp, player count,
|
* Room entity storing match information including game mode, timestamp, player count,
|
||||||
* and detailed performance data for all participants. Implements Serializable for
|
* 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.dao.MatchDao
|
||||||
* @see com.aldo.apps.ochecompanion.database.objects.Player
|
* @see com.aldo.apps.ochecompanion.database.objects.Player
|
||||||
@@ -25,7 +34,7 @@ public class Match implements Serializable {
|
|||||||
* @see PrimaryKey
|
* @see PrimaryKey
|
||||||
*/
|
*/
|
||||||
@PrimaryKey(autoGenerate = true)
|
@PrimaryKey(autoGenerate = true)
|
||||||
public int mId;
|
public int id;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Unix epoch timestamp (milliseconds) when match was completed.
|
* Unix epoch timestamp (milliseconds) when match was completed.
|
||||||
@@ -33,20 +42,20 @@ public class Match implements Serializable {
|
|||||||
*
|
*
|
||||||
* @see System#currentTimeMillis()
|
* @see System#currentTimeMillis()
|
||||||
*/
|
*/
|
||||||
public long mTimestamp;
|
public long timestamp;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Identifier for the darts game variant played (e.g., "501", "301", "Cricket").
|
* Identifier for the darts game variant played (e.g., "501", "301", "Cricket").
|
||||||
* Determines scoring rules and UI display for this match.
|
* Determines scoring rules and UI display for this match.
|
||||||
*/
|
*/
|
||||||
public String mGameMode;
|
public String gameMode;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Total number of players who participated in this match.
|
* Total number of players who participated in this match.
|
||||||
* Determines match type (1=solo, 2=1v1, 3+=group) and affects UI display.
|
* Determines match type (1=solo, 2=1v1, 3+=group) and affects UI display.
|
||||||
* Must match the number of entries in participantData JSON.
|
* 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.
|
* 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.JSONArray
|
||||||
* @see org.json.JSONObject
|
* @see org.json.JSONObject
|
||||||
*/
|
*/
|
||||||
public String mParticipantData;
|
public String participantData;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructs a new Match entity ready for database insertion.
|
* 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)
|
* @see com.aldo.apps.ochecompanion.database.dao.MatchDao#insert(Match)
|
||||||
*/
|
*/
|
||||||
public Match(final long timestamp, final String gameMode, final int playerCount, final String participantData) {
|
public Match(final long timestamp, final String gameMode, final int playerCount, final String participantData) {
|
||||||
this.mTimestamp = timestamp;
|
this.timestamp = timestamp;
|
||||||
this.mGameMode = gameMode;
|
this.gameMode = gameMode;
|
||||||
this.mPlayerCount = playerCount;
|
this.playerCount = playerCount;
|
||||||
this.mParticipantData = participantData;
|
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<Player> 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<Player> players, final Map<Integer, Integer> 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<Integer, Integer> getPlayerScores() {
|
||||||
|
final Map<Integer, Integer> 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<Player> getAllPlayers() {
|
||||||
|
final List<Player> 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<Player> players, final Map<Integer, Integer> 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<ParticipantData> getAllParticipants() {
|
||||||
|
final List<ParticipantData> 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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package com.aldo.apps.ochecompanion.database.objects;
|
|||||||
import android.os.Parcel;
|
import android.os.Parcel;
|
||||||
import android.os.Parcelable;
|
import android.os.Parcelable;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
import androidx.room.Entity;
|
import androidx.room.Entity;
|
||||||
import androidx.room.PrimaryKey;
|
import androidx.room.PrimaryKey;
|
||||||
|
|
||||||
@@ -18,32 +19,32 @@ public class Player implements Parcelable {
|
|||||||
* Room auto-populates on insert.
|
* Room auto-populates on insert.
|
||||||
*/
|
*/
|
||||||
@PrimaryKey(autoGenerate = true)
|
@PrimaryKey(autoGenerate = true)
|
||||||
public int mId;
|
public int id;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Player's display name shown throughout the app. Should be non-null and
|
* Player's display name shown throughout the app. Should be non-null and
|
||||||
* ideally 1-30 characters. Supports Unicode.
|
* 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).
|
* File path to player's profile picture. Can be null (shows default avatar).
|
||||||
* Stored as path reference to avoid database bloat.
|
* Stored as path reference to avoid database bloat.
|
||||||
*/
|
*/
|
||||||
public String mProfilePictureUri;
|
public String profilePictureUri;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Player's career three-dart average across all completed matches.
|
* Player's career three-dart average across all completed matches.
|
||||||
* Represents overall skill level. Typical ranges: 0-40 (beginner),
|
* Represents overall skill level. Typical ranges: 0-40 (beginner),
|
||||||
* 40-60 (casual), 60-80 (experienced), 80-100 (advanced), 100+ (pro).
|
* 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
|
* Total number of completed matches for this player. Provides context for
|
||||||
* statistical significance and enables experience-based features.
|
* 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
|
* 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)
|
* @param profilePictureUri Path to profile image (null for default avatar)
|
||||||
*/
|
*/
|
||||||
public Player(final String username, final String profilePictureUri) {
|
public Player(final String username, final String profilePictureUri) {
|
||||||
this.mUsername = username;
|
this.username = username;
|
||||||
this.mProfilePictureUri = profilePictureUri;
|
this.profilePictureUri = profilePictureUri;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -65,17 +66,17 @@ public class Player implements Parcelable {
|
|||||||
* @param in Parcel containing serialized Player data
|
* @param in Parcel containing serialized Player data
|
||||||
*/
|
*/
|
||||||
protected Player(final Parcel in) {
|
protected Player(final Parcel in) {
|
||||||
mId = in.readInt();
|
id = in.readInt();
|
||||||
mUsername = in.readString();
|
username = in.readString();
|
||||||
mProfilePictureUri = in.readString();
|
profilePictureUri = in.readString();
|
||||||
mCareerAverage = in.readDouble();
|
careerAverage = in.readDouble();
|
||||||
mMatchesPlayed = in.readInt();
|
matchesPlayed = in.readInt();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Required Parcelable CREATOR field for Player reconstruction.
|
* Required Parcelable CREATOR field for Player reconstruction.
|
||||||
*/
|
*/
|
||||||
public static final Creator<Player> sCREATOR = new Creator<Player>() {
|
public static final Creator<Player> CREATOR = new Creator<>() {
|
||||||
@Override
|
@Override
|
||||||
public Player createFromParcel(final Parcel in) {
|
public Player createFromParcel(final Parcel in) {
|
||||||
return new Player(in);
|
return new Player(in);
|
||||||
@@ -100,11 +101,11 @@ public class Player implements Parcelable {
|
|||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void writeToParcel(final Parcel dest, final int flags) {
|
public void writeToParcel(final Parcel dest, final int flags) {
|
||||||
dest.writeInt(mId);
|
dest.writeInt(id);
|
||||||
dest.writeString(mUsername);
|
dest.writeString(username);
|
||||||
dest.writeString(mProfilePictureUri);
|
dest.writeString(profilePictureUri);
|
||||||
dest.writeDouble(mCareerAverage);
|
dest.writeDouble(careerAverage);
|
||||||
dest.writeInt(mMatchesPlayed);
|
dest.writeInt(matchesPlayed);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -114,13 +115,14 @@ public class Player implements Parcelable {
|
|||||||
* @return String in format "Player{mId=X, mUsername='...', ...}"
|
* @return String in format "Player{mId=X, mUsername='...', ...}"
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
|
@NonNull
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return "Player{" +
|
return "Player{" +
|
||||||
"mId=" + mId +
|
"mId=" + id +
|
||||||
", mUsername='" + mUsername + '\'' +
|
", mUsername='" + username + '\'' +
|
||||||
", mProfilePictureUri='" + mProfilePictureUri + '\'' +
|
", mProfilePictureUri='" + profilePictureUri + '\'' +
|
||||||
", mCareerAverage=" + mCareerAverage +
|
", mCareerAverage=" + careerAverage +
|
||||||
", mMatchesPlayed=" + mMatchesPlayed +
|
", mMatchesPlayed=" + matchesPlayed +
|
||||||
'}';
|
'}';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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<Player> 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<Player> 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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -8,6 +8,8 @@ import android.graphics.Path;
|
|||||||
import android.graphics.RectF;
|
import android.graphics.RectF;
|
||||||
import android.util.AttributeSet;
|
import android.util.AttributeSet;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import com.aldo.apps.ochecompanion.utils.UIConstants;
|
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. */
|
/** Path with CW outer rect and CCW inner rect creating transparent hole. */
|
||||||
private final Path mPath = new Path();
|
private final Path mPath = new Path();
|
||||||
|
|
||||||
/** Calculated side length of square crop box (80% of width). */
|
|
||||||
private float mBoxSize;
|
|
||||||
|
|
||||||
/** Constructor for programmatic instantiation. */
|
/** Constructor for programmatic instantiation. */
|
||||||
public CropOverlayView(final Context context) {
|
public CropOverlayView(final Context context) {
|
||||||
super(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) {
|
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);
|
super.onLayout(changed, left, top, right, bottom);
|
||||||
|
|
||||||
mBoxSize = getWidth() * UIConstants.CROP_BOX_SIZE_RATIO;
|
/* Calculated side length of square crop box (80% of width). */
|
||||||
final float l = (getWidth() - mBoxSize) / 2;
|
final float boxSize = getWidth() * UIConstants.CROP_BOX_SIZE_RATIO;
|
||||||
final float t = (getHeight() - mBoxSize) / 2;
|
final float l = (getWidth() - boxSize) / 2;
|
||||||
mCropRect.set(l, t, l + mBoxSize, t + mBoxSize);
|
final float t = (getHeight() - boxSize) / 2;
|
||||||
|
mCropRect.set(l, t, l + boxSize, t + boxSize);
|
||||||
|
|
||||||
mPath.reset();
|
mPath.reset();
|
||||||
mPath.addRect(0, 0, getWidth(), getHeight(), Path.Direction.CW);
|
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. */
|
/** Renders the overlay mask with transparent center cutout. */
|
||||||
@Override
|
@Override
|
||||||
protected void onDraw(final Canvas canvas) {
|
protected void onDraw(@NonNull final Canvas canvas) {
|
||||||
super.onDraw(canvas);
|
super.onDraw(canvas);
|
||||||
canvas.drawPath(mPath, mMaskPaint);
|
canvas.drawPath(mPath, mMaskPaint);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ import androidx.annotation.Nullable;
|
|||||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||||
import androidx.recyclerview.widget.RecyclerView;
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
import com.aldo.apps.ochecompanion.R;
|
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;
|
import com.aldo.apps.ochecompanion.ui.adapter.MainMenuGroupMatchAdapter;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -92,10 +92,10 @@ public class MatchRecapView extends FrameLayout {
|
|||||||
updateVisibility(mState1v1);
|
updateVisibility(mState1v1);
|
||||||
|
|
||||||
mTvP1Name.setText(match.getPlayerNameByPosition(0));
|
mTvP1Name.setText(match.getPlayerNameByPosition(0));
|
||||||
mTvP1Score.setText(String.valueOf(match.getPlayerAverageByPosition(0)));
|
mTvP1Score.setText(String.valueOf(match.getPlayerScoreByPosition(0)));
|
||||||
|
|
||||||
mTvP2Name.setText(match.getPlayerNameByPosition(1));
|
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. */
|
/** Configures group state with leaderboard RecyclerView. */
|
||||||
|
|||||||
@@ -11,18 +11,17 @@ import androidx.recyclerview.widget.RecyclerView;
|
|||||||
|
|
||||||
import com.aldo.apps.ochecompanion.R;
|
import com.aldo.apps.ochecompanion.R;
|
||||||
import com.aldo.apps.ochecompanion.database.objects.Player;
|
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.aldo.apps.ochecompanion.ui.PlayerItemView;
|
||||||
import com.bumptech.glide.Glide;
|
import com.bumptech.glide.Glide;
|
||||||
import com.google.android.material.imageview.ShapeableImageView;
|
import com.google.android.material.imageview.ShapeableImageView;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Comparator;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* RecyclerView adapter for displaying group match results in Main Menu.
|
* 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<MainMenuGroupMatchAdapter.GroupMatchHolder> {
|
public class MainMenuGroupMatchAdapter extends RecyclerView.Adapter<MainMenuGroupMatchAdapter.GroupMatchHolder> {
|
||||||
|
|
||||||
@@ -30,7 +29,7 @@ public class MainMenuGroupMatchAdapter extends RecyclerView.Adapter<MainMenuGrou
|
|||||||
private static final String TAG = "MainMenuGroupMatchAdapt";
|
private static final String TAG = "MainMenuGroupMatchAdapt";
|
||||||
|
|
||||||
/** List of players sorted by career average. */
|
/** List of players sorted by career average. */
|
||||||
private final List<Player> mPlayersList = new ArrayList<>();
|
private final List<Match.ParticipantData> mParticipantsList = new ArrayList<>();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new ViewHolder with a PlayerItemView.
|
* Creates a new ViewHolder with a PlayerItemView.
|
||||||
@@ -62,24 +61,24 @@ public class MainMenuGroupMatchAdapter extends RecyclerView.Adapter<MainMenuGrou
|
|||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void onBindViewHolder(@NonNull final GroupMatchHolder holder, final int position) {
|
public void onBindViewHolder(@NonNull final GroupMatchHolder holder, final int position) {
|
||||||
// Retrieve the player at this position and bind it to the holder
|
// Retrieve the participant at this position and bind it to the holder
|
||||||
holder.setPlayer(mPlayersList.get(position));
|
holder.setParticipant(mParticipantsList.get(position));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the number of players.
|
* Returns the number of participants.
|
||||||
*
|
*
|
||||||
* @return Player count.
|
* @return Participant count.
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public int getItemCount() {
|
public int getItemCount() {
|
||||||
return mPlayersList.size();
|
return mParticipantsList.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Updates the adapter with match data, sorting players by career average.
|
* Updates the adapter with match data, sorting participants by match score (descending).
|
||||||
*
|
*
|
||||||
* @param match The match containing players to display.
|
* @param match The match containing participants to display.
|
||||||
*/
|
*/
|
||||||
@SuppressLint("NotifyDataSetChanged")
|
@SuppressLint("NotifyDataSetChanged")
|
||||||
public void updateMatch(final Match match) {
|
public void updateMatch(final Match match) {
|
||||||
@@ -87,23 +86,21 @@ public class MainMenuGroupMatchAdapter extends RecyclerView.Adapter<MainMenuGrou
|
|||||||
Log.d(TAG, "updateMatch: match is null, aborting update.");
|
Log.d(TAG, "updateMatch: match is null, aborting update.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// Clear any existing player data
|
// Clear any existing participant data
|
||||||
mPlayersList.clear();
|
mParticipantsList.clear();
|
||||||
|
|
||||||
if (match.getAllPlayers() == null || match.getAllPlayers().isEmpty()) {
|
final List<Match.ParticipantData> participants = match.getAllParticipants();
|
||||||
Log.d(TAG, "updateMatch: No players found in the match, just clearing.");
|
if (participants == null || participants.isEmpty()) {
|
||||||
|
Log.d(TAG, "updateMatch: No participants found in the match, just clearing.");
|
||||||
notifyDataSetChanged();
|
notifyDataSetChanged();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get all players from the match
|
// Sort participants by match score (highest to lowest)
|
||||||
final List<Player> allPlayers = match.getAllPlayers();
|
participants.sort((p1, p2) -> Integer.compare(p2.score, p1.score));
|
||||||
|
|
||||||
// Sort players by career average (lowest to highest)
|
// Add sorted participants to the display list
|
||||||
allPlayers.sort(new PlayerScoreComparator());
|
mParticipantsList.addAll(participants);
|
||||||
|
|
||||||
// Add sorted players to the display list
|
|
||||||
mPlayersList.addAll(allPlayers);
|
|
||||||
|
|
||||||
// Notify RecyclerView to refresh the display
|
// Notify RecyclerView to refresh the display
|
||||||
notifyDataSetChanged();
|
notifyDataSetChanged();
|
||||||
@@ -143,18 +140,19 @@ public class MainMenuGroupMatchAdapter extends RecyclerView.Adapter<MainMenuGrou
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Binds player data to this ViewHolder.
|
* Binds participant data (player + match score) to this ViewHolder.
|
||||||
*
|
*
|
||||||
* @param player The player to display.
|
* @param participantData The participant data to display.
|
||||||
*/
|
*/
|
||||||
public void setPlayer(final Player player) {
|
public void setParticipant(final Match.ParticipantData participantData) {
|
||||||
|
final Player player = participantData.player;
|
||||||
|
final int score = participantData.score;
|
||||||
|
|
||||||
// Set player name
|
// Set player name
|
||||||
mPlayerNameView.setText(player.username);
|
mPlayerNameView.setText(player.username);
|
||||||
|
|
||||||
// Format and set career average score
|
// Display match score instead of career average
|
||||||
mPlayerScoreView.setText(String.format(
|
mPlayerScoreView.setText(String.valueOf(score));
|
||||||
itemView.getContext().getString(R.string.txt_player_average_base),
|
|
||||||
player.careerAverage));
|
|
||||||
|
|
||||||
// Load profile picture or show default icon
|
// Load profile picture or show default icon
|
||||||
if (player.profilePictureUri != null) {
|
if (player.profilePictureUri != null) {
|
||||||
@@ -168,23 +166,4 @@ public class MainMenuGroupMatchAdapter extends RecyclerView.Adapter<MainMenuGrou
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Comparator for sorting players by career average in ascending order.
|
|
||||||
*/
|
|
||||||
public static class PlayerScoreComparator implements Comparator<Player> {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
9
app/src/main/res/drawable/ic_delete.xml
Normal file
9
app/src/main/res/drawable/ic_delete.xml
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:viewportHeight="24">
|
||||||
|
<path
|
||||||
|
android:fillColor="@color/double_red"
|
||||||
|
android:pathData="M6,19c0,1.1 0.9,2 2,2h8c1.1,0 2,-0.9 2,-2V7H6V19zM19,4h-3.5l-1,-1h-5l-1,1H5v2h14V4z" />
|
||||||
|
</vector>
|
||||||
@@ -5,6 +5,19 @@
|
|||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:background="@color/midnight_black">
|
android:background="@color/midnight_black">
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/btnDeletePlayer"
|
||||||
|
android:layout_width="40dp"
|
||||||
|
android:layout_height="40dp"
|
||||||
|
android:layout_gravity="top|end"
|
||||||
|
android:layout_marginTop="16dp"
|
||||||
|
android:layout_marginEnd="16dp"
|
||||||
|
android:padding="8dp"
|
||||||
|
android:src="@drawable/ic_delete"
|
||||||
|
android:background="?attr/selectableItemBackgroundBorderless"
|
||||||
|
android:visibility="gone"
|
||||||
|
app:tint="@color/double_red" />
|
||||||
|
|
||||||
<!-- FORM VIEW -->
|
<!-- FORM VIEW -->
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:id="@+id/layoutForm"
|
android:id="@+id/layoutForm"
|
||||||
|
|||||||
Reference in New Issue
Block a user