From 689bf2808a130a6101a09229b8b3401d4b2f1942 Mon Sep 17 00:00:00 2001 From: Alexander Doerflinger Date: Mon, 2 Feb 2026 14:54:06 +0100 Subject: [PATCH] Added a Player stats view to the GameResult Currently only showing the stats of the winning player and also only the overall stats, has to be improved to show stats of all players and also only the stats of the match, not the overall one. --- .../aldo/apps/ochecompanion/GameActivity.java | 38 ++++- .../ochecompanion/ui/PlayerStatsView.java | 81 ++++++++++ app/src/main/res/layout/activity_game.xml | 18 +++ .../main/res/layout/player_stats_layout.xml | 142 ++++++++++++++++++ app/src/main/res/values/strings.xml | 1 + app/src/main/res/values/styles.xml | 42 ++++++ 6 files changed, 320 insertions(+), 2 deletions(-) create mode 100644 app/src/main/java/com/aldo/apps/ochecompanion/ui/PlayerStatsView.java create mode 100644 app/src/main/res/layout/player_stats_layout.xml 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 + + + + + + + + + + + +