diff --git a/app/src/main/java/com/aldo/apps/ochecompanion/GameActivity.java b/app/src/main/java/com/aldo/apps/ochecompanion/GameActivity.java
index e0eab33..ee37acb 100644
--- a/app/src/main/java/com/aldo/apps/ochecompanion/GameActivity.java
+++ b/app/src/main/java/com/aldo/apps/ochecompanion/GameActivity.java
@@ -29,6 +29,7 @@ import androidx.preference.PreferenceManager;
import com.aldo.apps.ochecompanion.database.AppDatabase;
import com.aldo.apps.ochecompanion.database.objects.Player;
import com.aldo.apps.ochecompanion.database.objects.Statistics;
+import com.aldo.apps.ochecompanion.ui.PlayerStatsView;
import com.aldo.apps.ochecompanion.utils.CheckoutEngine;
import com.aldo.apps.ochecompanion.utils.DartsConstants;
import com.aldo.apps.ochecompanion.utils.SoundEngine;
@@ -197,6 +198,16 @@ public class GameActivity extends AppCompatActivity {
* Button for selecting triple (3×) multiplier.
*/
private View btnTriple;
+
+ /**
+ * The Button to open the stats view.
+ */
+ private MaterialButton mShowStatsBtn;
+
+ /**
+ * The {@link PlayerStatsView} to display player statistics.
+ */
+ private PlayerStatsView mStatsView;
/**
* Array of three TextViews showing darts thrown in current turn.
@@ -316,6 +327,14 @@ public class GameActivity extends AppCompatActivity {
btnDouble.setOnClickListener(v -> setMultiplier(2));
btnTriple.setOnClickListener(v -> setMultiplier(3));
+ mShowStatsBtn = findViewById(R.id.show_stats_btn);
+ mStatsView = findViewById(R.id.player_stats_view);
+ mShowStatsBtn.setOnClickListener(v -> {
+ mStatsView.setVisibility(View.VISIBLE);
+ mShowStatsBtn.setVisibility(View.GONE);
+ });
+
+
findViewById(R.id.btnSubmitTurn).setOnClickListener(v -> submitTurn());
findViewById(R.id.btnUndoDart).setOnClickListener(v -> undoLastDart());
}
@@ -875,13 +894,28 @@ public class GameActivity extends AppCompatActivity {
if (mIsAudioEnabled) {
mSoundEngine.playWinnerSound();
}
-
+
+ mShowStatsBtn.setVisibility(View.VISIBLE);
+ attachPlayerStats();
// TODO: Consider adding:
- // - Statistics display
// - Save match to database
// - Offer rematch
}
+ private void attachPlayerStats() {
+ new Thread(() -> {
+ try {
+ final Player player = mPlayerStates.get(mActivePlayerIndex).player;
+ final Statistics statistics = AppDatabase.getDatabase(GameActivity.this).statisticsDao().getStatisticsForPlayer(player.id);
+ runOnUiThread(() -> {
+ mStatsView.bind(player, statistics);
+ });
+ } catch (Exception e) {
+ Log.e(TAG, "attachPlayerStats: Failed to increment matches", e);
+ }
+ }).start();
+ }
+
/**
* Plays confetti animation and displays winner's name overlay.
* Shows full-screen dimmer with celebratory confetti effect.
diff --git a/app/src/main/java/com/aldo/apps/ochecompanion/ui/PlayerStatsView.java b/app/src/main/java/com/aldo/apps/ochecompanion/ui/PlayerStatsView.java
new file mode 100644
index 0000000..687692b
--- /dev/null
+++ b/app/src/main/java/com/aldo/apps/ochecompanion/ui/PlayerStatsView.java
@@ -0,0 +1,81 @@
+package com.aldo.apps.ochecompanion.ui;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.view.LayoutInflater;
+import android.widget.ScrollView;
+import android.widget.TextView;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import com.aldo.apps.ochecompanion.R;
+import com.aldo.apps.ochecompanion.database.objects.Player;
+import com.aldo.apps.ochecompanion.database.objects.Statistics;
+import com.bumptech.glide.Glide;
+import com.google.android.material.imageview.ShapeableImageView;
+
+/**
+ * PlayerStatsView: A complete dashboard component that visualizes a player's
+ * career performance, including a heatmap and detailed metrics.
+ */
+public class PlayerStatsView extends ScrollView {
+
+ // UI References
+ private HeatmapView mHeatmap;
+ private ShapeableImageView mIvAvatar;
+ private TextView mTvUsername, mTvCareerAvg, mTvFirst9, mTvCheckoutPct, mTvBestFinish;
+ private TextView mTvCount60, mTvCount100, mTvCount140, mTvCount180;
+
+ public PlayerStatsView(@NonNull final Context context) {
+ this(context, null);
+ }
+
+ public PlayerStatsView(@NonNull final Context context, @Nullable final AttributeSet attrs) {
+ super(context, attrs);
+ LayoutInflater.from(context).inflate(R.layout.player_stats_layout, this, true);
+ initViews();
+ }
+
+ private void initViews() {
+ mHeatmap = findViewById(R.id.statsHeatmap);
+ mIvAvatar = findViewById(R.id.ivPlayerAvatar);
+ mTvUsername = findViewById(R.id.tvUsername);
+ mTvCareerAvg = findViewById(R.id.tvCareerAvgValue);
+ mTvFirst9 = findViewById(R.id.tvFirst9Value);
+ mTvCheckoutPct = findViewById(R.id.tvCheckoutPctValue);
+ mTvBestFinish = findViewById(R.id.tvBestFinishValue);
+
+ // Threshold counters
+ mTvCount60 = findViewById(R.id.tvCount60);
+ mTvCount100 = findViewById(R.id.tvCount100);
+ mTvCount140 = findViewById(R.id.tvCount140);
+ mTvCount180 = findViewById(R.id.tvCount180);
+ }
+
+ /**
+ * Binds both the player identity and their accumulated stats to the UI.
+ */
+ public void bind(@NonNull final Player player, final @NonNull Statistics stats) {
+ // 1. Identity
+ mTvUsername.setText(player.username.toUpperCase());
+ if (player.profilePictureUri != null) {
+ Glide.with(getContext()).load(player.profilePictureUri).into(mIvAvatar);
+ } else {
+ mIvAvatar.setImageResource(R.drawable.ic_users);
+ }
+
+ // 2. High-Level Metrics
+ mTvCareerAvg.setText(String.format("%.1f", stats.getAverage()));
+ mTvFirst9.setText(String.format("%.1f", stats.getFirst9Average()));
+ mTvCheckoutPct.setText(String.format("%.1f%%", stats.getCheckoutPercentage()));
+ mTvBestFinish.setText(String.valueOf(stats.getHighestCheckout()));
+
+ // 3. Threshold Totals
+ mTvCount60.setText(String.valueOf(stats.getCount60Plus()));
+ mTvCount100.setText(String.valueOf(stats.getCount100Plus()));
+ mTvCount140.setText(String.valueOf(stats.getCount140Plus()));
+ mTvCount180.setText(String.valueOf(stats.getCount180()));
+
+ // 4. Heatmap Rendering
+ mHeatmap.setStats(stats);
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/res/layout/activity_game.xml b/app/src/main/res/layout/activity_game.xml
index 0a23158..b7b1dbc 100644
--- a/app/src/main/res/layout/activity_game.xml
+++ b/app/src/main/res/layout/activity_game.xml
@@ -177,10 +177,28 @@
android:visibility="gone"
tools:text="Zander"/>
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/player_stats_layout.xml b/app/src/main/res/layout/player_stats_layout.xml
new file mode 100644
index 0000000..2331e60
--- /dev/null
+++ b/app/src/main/res/layout/player_stats_layout.xml
@@ -0,0 +1,142 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 547cd54..a0a2ef1 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -32,6 +32,7 @@
Triple
Bull
Submit Turn
+ Show Stats
day_night_mode_auto
diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml
index 7275ee8..47add06 100644
--- a/app/src/main/res/values/styles.xml
+++ b/app/src/main/res/values/styles.xml
@@ -87,4 +87,46 @@
- 12dp
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file