Left over files from previous comit :/

This commit is contained in:
Alexander Doerflinger
2026-02-03 15:38:58 +01:00
parent f64a9291bb
commit 039350e988
18 changed files with 467 additions and 66 deletions

View File

@@ -8,7 +8,9 @@ import android.graphics.RectF;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.util.Log;
import com.aldo.apps.ochecompanion.ui.PlayerStatsView;
import com.aldo.apps.ochecompanion.utils.Log;
import android.view.MotionEvent;
import android.view.ScaleGestureDetector;
import android.view.View;
@@ -88,6 +90,11 @@ public class AddPlayerActivity extends BaseActivity {
*/
private MaterialButton mSaveButton;
/**
* Button to show the player stats.
*/
private MaterialButton mShowStatsButton;
/**
* Button to delete a players profile from the database.
*/
@@ -105,7 +112,17 @@ public class AddPlayerActivity extends BaseActivity {
*/
private CropOverlayView mCropOverlay;
/**
* View displaying the player's statistics.
*/
private PlayerStatsView mPlayerStatsView;
// ========== Data State ==========
/**
* Boolean flag indicating whether the stats view is shown.
*/
private boolean mIsStatsViewShown = false;
/**
* Absolute file path to the saved profile picture in internal storage.
@@ -201,6 +218,17 @@ public class AddPlayerActivity extends BaseActivity {
}
}
@Override
public void onBackPressed() {
Log.d(TAG, "onBackPressed() called with StatsView shown = [" + mIsStatsViewShown + "]");
if (mIsStatsViewShown) {
mPlayerStatsView.setVisibility(View.GONE);
mIsStatsViewShown = false;
return;
}
super.onBackPressed();
}
/**
* Initializes all UI component references and sets up click listeners.
*/
@@ -213,6 +241,8 @@ public class AddPlayerActivity extends BaseActivity {
mProfilePictureView = findViewById(R.id.ivAddPlayerProfile);
mUserNameInput = findViewById(R.id.etUsername);
mTitleView = findViewById(R.id.tvTitle);
mShowStatsButton = findViewById(R.id.btnShowStats);
mPlayerStatsView = findViewById(R.id.player_stats_view);
mSaveButton = findViewById(R.id.btnSavePlayer);
mBtnDelete = findViewById(R.id.btnDeletePlayer);
@@ -452,6 +482,7 @@ public class AddPlayerActivity extends BaseActivity {
new Thread(() -> {
// Query the database for the player (background thread)
mExistingPlayer = AppDatabase.getDatabase(this).playerDao().getPlayerById(mExistingPlayerId);
final Statistics statistics = AppDatabase.getDatabase(this).statisticsDao().getStatisticsForPlayer(mExistingPlayerId);
// Update UI on the main thread
runOnUiThread(() -> {
@@ -463,6 +494,14 @@ public class AddPlayerActivity extends BaseActivity {
mTitleView.setText(R.string.txt_update_profile_header);
mSaveButton.setText(R.string.txt_update_profile_username_save);
if (mBtnDelete != null) mBtnDelete.setVisibility(View.VISIBLE);
if (mShowStatsButton != null) mShowStatsButton.setVisibility(View.VISIBLE);
mShowStatsButton.setOnClickListener(v -> {
mPlayerStatsView.setVisibility(View.VISIBLE);
mIsStatsViewShown = true;
});
if (statistics != null && mPlayerStatsView != null) {
mPlayerStatsView.bind(mExistingPlayer, statistics);
}
// Load existing profile picture if available
if (mExistingPlayer.profilePictureUri != null) {

View File

@@ -3,7 +3,7 @@ package com.aldo.apps.ochecompanion;
import android.content.SharedPreferences;
import android.content.res.Configuration;
import android.os.Bundle;
import android.util.Log;
import com.aldo.apps.ochecompanion.utils.Log;
import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.app.AppCompatDelegate;

View File

@@ -3,7 +3,7 @@ package com.aldo.apps.ochecompanion;
import android.content.Intent;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.util.Log;
import com.aldo.apps.ochecompanion.utils.Log;
import android.view.View;
import android.widget.TextView;
@@ -16,6 +16,7 @@ import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import com.aldo.apps.ochecompanion.database.AppDatabase;
import com.aldo.apps.ochecompanion.database.DatabaseHelper;
import com.aldo.apps.ochecompanion.database.objects.Player;
import com.aldo.apps.ochecompanion.database.objects.Match;
import com.aldo.apps.ochecompanion.ui.MatchRecapView;
@@ -55,6 +56,13 @@ public class MainMenuActivity extends BaseActivity {
*/
private SharedPreferences mSettingsPref;
/**
* Centralized database helper that manages all database operations with proper synchronization.
*/
private DatabaseHelper mDatabaseHelper;
private Match mOngoingMatch;
/**
* Initializes the activity: enables edge-to-edge display, configures window insets,
* and sets up the match recap view with test data click listener.
@@ -69,6 +77,7 @@ public class MainMenuActivity extends BaseActivity {
EdgeToEdge.enable(this);
setContentView(R.layout.activity_main);
mSettingsPref = PreferenceManager.getDefaultSharedPreferences(this);
mDatabaseHelper = DatabaseHelper.getInstance(this);
// Configure window insets to properly handle system bars (status bar, navigation bar)
ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main), (v, insets) -> {
@@ -84,12 +93,22 @@ public class MainMenuActivity extends BaseActivity {
quickStartBtn.setOnClickListener(v -> quickStart());
findViewById(R.id.btnSettings).setOnClickListener(v -> launchSettings());
final List<Match> ongoingMatches = (List<Match>) mDatabaseHelper.getOngoingMatches();
if (ongoingMatches != null && !ongoingMatches.isEmpty()) {
mOngoingMatch = ongoingMatches.get(0);
}
if (mOngoingMatch != null) {
Log.d(TAG, "onCreate: Found ongoing match [" + mOngoingMatch + "]");
quickStartBtn.setSubText("Continue match with " + mOngoingMatch.gameMode + " score");
}
// Set up match recap view with test data functionality
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();
});
findViewById(R.id.title_view).setOnClickListener(v -> startActivity(new Intent(MainMenuActivity.this, TestActivity.class)));
@@ -115,19 +134,26 @@ public class MainMenuActivity extends BaseActivity {
startActivity(intent);
}
/**
* Initiates a quick-start 501 game with two test players.
* Creates test players "Test1" and "Test2" and launches GameActivity.
* Test players are not persisted to the database.
/**game.
* Checks for any ongoing matches and resumes them if found,
* otherwise starts a new game with the default score.
*/
private void quickStart() {
final Player playerOne = new Player(DartsConstants.TEST_PLAYER_1, null);
final Player playerTwo = new Player(DartsConstants.TEST_PLAYER_2, null);
final ArrayList<Player> players = new ArrayList<>();
players.add(playerOne);
players.add(playerTwo);
GameActivity.start(MainMenuActivity.this, players, DartsConstants.DEFAULT_GAME_SCORE);
// Check for ongoing matches in background thread
int startingScore = DartsConstants.DEFAULT_GAME_SCORE;
int matchId = -1;
if (mOngoingMatch != null) {
try {
Log.d(TAG, "quickStart: Parsing [" + mOngoingMatch + "]");
startingScore = Integer.parseInt(mOngoingMatch.gameMode);
matchId = mOngoingMatch.id;
} catch (final NumberFormatException exception) {
Log.e(TAG, "quickStart: Could not extract score from [" + mOngoingMatch + "], use default");
}
}
Log.d(TAG, "quickStart: Starting match with StartingScore = [" + startingScore
+ "] and matchId = [" + matchId + "]");
GameActivity.start(MainMenuActivity.this, startingScore, matchId);
}
/**
@@ -151,19 +177,10 @@ public class MainMenuActivity extends BaseActivity {
final Intent intent = new Intent(MainMenuActivity.this, AddPlayerActivity.class);
startActivity(intent);
});
// Database operations must be run on a background thread to keep the UI responsive.
new Thread(() -> {
// Access the singleton database and query all players
final List<Player> allPlayers = AppDatabase.getDatabase(getApplicationContext())
.playerDao()
.getAllPlayers();
final List<Player> allPlayers = (List<Player>) mDatabaseHelper.getAllPlayers();
runOnUiThread(() -> adapter.updatePlayers(allPlayers));
// Post-database query UI updates must happen back on the main (UI) thread
runOnUiThread(() -> {
// Update the adapter with the retrieved player data
adapter.updatePlayers(allPlayers);
});
}).start();
}
@@ -171,14 +188,12 @@ public class MainMenuActivity extends BaseActivity {
* Applies the last completed match from the database to the match recap view.
*/
private void applyLastMatch() {
// Database operations must be run on a background thread to keep the UI responsive.
new Thread(() -> {
final Match lastMatch = AppDatabase.getDatabase(getApplicationContext())
.matchDao()
.getLastCompletedMatch();
// Post-database query UI updates must happen back on the main (UI) thread
runOnUiThread(() -> mMatchRecap.setMatch(lastMatch));
final Match lastCompleted = mDatabaseHelper.getLastCompletedMatch();
if (lastCompleted != null) {
Log.d(TAG, "applyLastMatch: Applying last completed match [" + lastCompleted + "]");
runOnUiThread(() -> mMatchRecap.setMatch(lastCompleted));
}
}).start();
}

View File

@@ -1,7 +1,7 @@
package com.aldo.apps.ochecompanion;
import android.os.Bundle;
import android.util.Log;
import com.aldo.apps.ochecompanion.utils.Log;
import androidx.activity.EdgeToEdge;
import androidx.appcompat.app.AppCompatActivity;

View File

@@ -1,11 +1,13 @@
package com.aldo.apps.ochecompanion.database;
import android.content.Context;
import android.util.Log;
import com.aldo.apps.ochecompanion.utils.Log;
import com.aldo.apps.ochecompanion.database.objects.Match;
import com.aldo.apps.ochecompanion.database.objects.Player;
import com.aldo.apps.ochecompanion.database.objects.Statistics;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@@ -306,6 +308,104 @@ public class DatabaseHelper {
}
}
public long createNewMatch(final String gameMode, final List<Player> players) {
final Match match = new Match(System.currentTimeMillis(), gameMode, players.size(), null, Match.MatchState.ONGOING);
try {
return mExecutor.submit(() -> {
try {
return mDatabase.matchDao().insert(match);
} catch (Exception e) {
Log.e(TAG, "createNewMatch: Failed to insert match", e);
return -1L;
}
}).get();
} catch (Exception e) {
Log.e(TAG, "createNewMatch: Failed to submit task", e);
return -1;
}
}
/**
* Updates an existing match in the database.
*
* @param match The Match entity to update
*/
public void updateMatch(final Match match) {
mExecutor.execute(() -> {
try {
mDatabase.matchDao().update(match);
} catch (Exception e) {
Log.e(TAG, "updateMatch: Failed to update match", e);
}
});
}
public List<?> getOngoingMatches() {
try {
return mExecutor.submit(() -> {
try {
return mDatabase.matchDao().getOngoingMatches();
} catch (Exception e) {
Log.d(TAG, "getOngoingMatch() failed");
return new ArrayList<>();
}
}).get();
} catch (Exception e) {
Log.e(TAG, "getOngoingMatch: Failed fetching ongoing matches");
return new ArrayList<>();
}
}
public void printAllMatches() {
Log.d(TAG, "printAllMatches() called");
try {
mExecutor.submit(() -> {
final List<Match> allMatches = mDatabase.matchDao().getAllMatches();
if (allMatches != null && !allMatches.isEmpty()) {
for (final Match match : allMatches) {
Log.d(TAG, "printAllMatches: Match = [" + match + "]");
}
} else {
Log.d(TAG, "printAllMatches: allMatches = [" + allMatches + "]");
}
});
} catch (Exception e) {
Log.e(TAG, "printAllMatches: Failed to submit task", e);
}
}
public Match getLastCompletedMatch() {
try {
return mExecutor.submit(() -> {
try {
return mDatabase.matchDao().getLastCompletedMatch();
} catch (Exception e) {
Log.e(TAG, "getLastCompletedMatch: Failed to retrieve last completed match", e);
return null;
}
}).get();
} catch (Exception e) {
Log.e(TAG, "getLastCompletedMatch: Failed to submit task", e);
return null;
}
}
public Match getMatchById(final int matchId) {
try {
return mExecutor.submit(() -> {
try {
return mDatabase.matchDao().getMatchById(matchId);
} catch (Exception e) {
Log.e(TAG, "getMatchById: Failed to retrieve match", e);
return null;
}
}).get();
} catch (Exception e) {
Log.e(TAG, "getMatchById: Failed to submit task", e);
return null;
}
}
/**
* Retrieves statistics for a specific player synchronously.
* Blocks until the operation completes to ensure consistency with any pending writes.

View File

@@ -20,7 +20,16 @@ public interface MatchDao {
* @param match The Match entity to persist
*/
@Insert
void insert(final Match match);
long insert(final Match match);
/**
* Updates an existing match record in the database.
* Must be called on a background thread.
*
* @param match The Match entity to update
*/
@androidx.room.Update
void update(final Match match);
/**
* Retrieves all match records ordered by most recent first.
@@ -40,6 +49,9 @@ public interface MatchDao {
@Query("SELECT * FROM matches ORDER BY timestamp DESC LIMIT 1")
Match getLastMatch();
@Query("SELECT * FROM matches WHERE id = :matchId")
Match getMatchById(final int matchId);
/**
* Retrieves the most recently completed match.
* Must be called on a background thread.

View File

@@ -1,8 +1,15 @@
package com.aldo.apps.ochecompanion.database.objects;
import android.util.Log;
import androidx.room.Entity;
import androidx.room.Ignore;
import androidx.room.PrimaryKey;
import com.aldo.apps.ochecompanion.utils.DartsConstants;
import com.aldo.apps.ochecompanion.utils.MatchProgress;
import com.aldo.apps.ochecompanion.utils.converters.MatchProgressConverter;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
@@ -27,6 +34,8 @@ import java.util.Map;
@Entity(tableName = "matches")
public class Match implements Serializable {
private static final String TAG = "Match";
/**
* Represents the current state of a match.
*/
@@ -157,14 +166,9 @@ public class Match implements Serializable {
* @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
final MatchProgress progress = MatchProgressConverter.fromString(participantData);
if (progress != null && progress.players != null && position < progress.players.size()) {
return progress.players.get(position).name;
}
return "Unknown";
}
@@ -176,14 +180,14 @@ public class Match implements Serializable {
* @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
final MatchProgress progress = MatchProgressConverter.fromString(participantData);
if (progress != null && progress.players != null && position < progress.players.size()) {
final int dartsThrown = progress.players.get(position).dartsThrown;
final int startingScore = progress.startingScore;
final int scoreLeft = progress.players.get(position).remainingScore;
final int scoredPoints = (scoreLeft - startingScore) * -1;
return (double) scoredPoints / dartsThrown * 3;
}
return 0.0;
}
@@ -195,14 +199,9 @@ public class Match implements Serializable {
* @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
final MatchProgress progress = MatchProgressConverter.fromString(participantData);
if (progress != null && progress.players != null && position < progress.players.size()) {
return progress.players.get(position).remainingScore;
}
return 0;
}
@@ -360,4 +359,16 @@ public class Match implements Serializable {
public boolean isCanceled() {
return this.state == MatchState.CANCELED;
}
@Override
public String toString() {
return "Match{" +
"id=" + id +
", timestamp=" + timestamp +
", gameMode='" + gameMode + '\'' +
", playerCount=" + playerCount +
", participantData='" + participantData + '\'' +
", state=" + state +
'}';
}
}

View File

@@ -1,6 +1,6 @@
package com.aldo.apps.ochecompanion.database.objects;
import android.util.Log;
import com.aldo.apps.ochecompanion.utils.Log;
import androidx.annotation.NonNull;
import androidx.room.Entity;

View File

@@ -1,7 +1,7 @@
package com.aldo.apps.ochecompanion.ui;
import android.os.Bundle;
import android.util.Log;
import com.aldo.apps.ochecompanion.utils.Log;
import androidx.appcompat.app.AppCompatDelegate;
import androidx.preference.Preference;

View File

@@ -2,6 +2,7 @@ package com.aldo.apps.ochecompanion.ui;
import android.content.Context;
import android.util.AttributeSet;
import com.aldo.apps.ochecompanion.utils.Log;
import android.view.View;
import android.widget.FrameLayout;
import android.widget.TextView;
@@ -19,6 +20,11 @@ import com.aldo.apps.ochecompanion.ui.adapter.MainMenuGroupMatchAdapter;
*/
public class MatchRecapView extends FrameLayout {
/**
* Tag for debugging purposes.
*/
private static final String TAG = "MatchRecapView";
/** View container for empty state (no match history). */
private View mStateEmpty;
@@ -75,6 +81,7 @@ public class MatchRecapView extends FrameLayout {
/** Binds match and updates display (empty, 1v1, or group state). */
public void setMatch(@Nullable final Match match) {
Log.d(TAG, "setMatch() called with: match = [" + match + "]");
if (match == null) {
updateVisibility(mStateEmpty);
return;

View File

@@ -1,7 +1,7 @@
package com.aldo.apps.ochecompanion.ui.adapter;
import android.annotation.SuppressLint;
import android.util.Log;
import com.aldo.apps.ochecompanion.utils.Log;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;

View File

@@ -5,7 +5,7 @@ import static com.aldo.apps.ochecompanion.AddPlayerActivity.EXTRA_PLAYER_ID;
import android.annotation.SuppressLint;
import android.content.Context;
import android.content.Intent;
import android.util.Log;
import com.aldo.apps.ochecompanion.utils.Log;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;

View File

@@ -1,4 +1,153 @@
package com.aldo.apps.ochecompanion.utils;
/**
* Class {@link Log} is a wrapper class around android.util.Log.
*
* The sole purpose of this class is to have a single TAG by which all log output from the
* CoreSyncService can later on be found in the log. The classes using this logging class may
* still define their custom tag. This will ease identifying OcheCompanion logs.
*/
public class Log {
/**
* The TAG that identifies OcheCompanion messages in the log.
*/
private static final String TAG = "OcheCompanion_Log";
/**
* Send a debug message.
*
* @param tag string used to identify the source of a log message
* @param msg the message to be logged
*/
public static void d(final String tag, final String msg) {
android.util.Log.d(TAG, String.format("(%s): %s", tag, msg));
}
/**
* Send a debug message and print the exception.
*
* @param tag string used to identify the source of a log message
* @param msg the message to be logged
* @param throwable the exception to be logged
*/
public static void d(final String tag, final String msg, final Throwable throwable) {
android.util.Log.d(TAG, String.format("(%s): %s", tag, msg), throwable);
}
/**
* Send a info message.
*
* @param tag string used to identify the source of a log message
* @param msg the message to be logged
*/
public static void i(final String tag, final String msg) {
android.util.Log.i(TAG, String.format("(%s): %s", tag, msg));
}
/**
* Send a info message and print the exception.
*
* @param tag string used to identify the source of a log message
* @param msg the message to be logged
* @param throwable the exception to be logged
*/
public static void i(final String tag, final String msg, final Throwable throwable) {
android.util.Log.i(TAG, String.format("(%s): %s", tag, msg), throwable);
}
/**
* Send a error message.
*
* @param tag string used to identify the source of a log message
* @param msg the message to be logged
*/
public static void e(final String tag, final String msg) {
android.util.Log.e(TAG, String.format("(%s): %s", tag, msg));
}
/**
* Send a error message and print the exception.
*
* @param tag string used to identify the source of a log message
* @param msg the message to be logged
* @param throwable the exception to be logged
*/
public static void e(final String tag, final String msg, final Throwable throwable) {
android.util.Log.e(TAG, String.format("(%s): %s", tag, msg), throwable);
}
/**
* Send a verbose message.
*
* @param tag string used to identify the source of a log message
* @param msg the message to be logged
*/
public static void v(final String tag, final String msg) {
android.util.Log.v(TAG, String.format("(%s): %s", tag, msg));
}
/**
* Send a verbose message and print the exception.
*
* @param tag string used to identify the source of a log message
* @param msg the message to be logged
* @param throwable the exception to be logged
*/
public static void v(final String tag, final String msg, final Throwable throwable) {
android.util.Log.v(TAG, String.format("(%s): %s", tag, msg), throwable);
}
/**
* Send a warning message.
*
* @param tag string used to identify the source of a log message
* @param msg the message to be logged
*/
public static void w(final String tag, final String msg) {
android.util.Log.w(TAG, String.format("(%s): %s", tag, msg));
}
/**
* Send a warning message and print the exception.
*
* @param tag string used to identify the source of a log message
* @param msg the message to be logged
* @param throwable the exception to be logged
*/
public static void w(final String tag, final String msg, final Throwable throwable) {
android.util.Log.w(TAG, String.format("(%s): %s", tag, msg), throwable);
}
/**
* Send a "what a terrible failure" message and print the exception.
*
* @param tag string used to identify the source of a log message
* @param msg the message to be logged
* @param throwable the exception to be logged
*/
public static void wtf(final String tag, final String msg, final Throwable throwable) {
android.util.Log.wtf(TAG, String.format("(%s): %s", tag, msg), throwable);
}
/**
* Send a "what a terrible failure" message.
*
* @param tag string used to identify the source of a log message
* @param msg the message to be logged
*/
public static void wtf(final String tag, final String msg) {
android.util.Log.wtf(TAG, String.format("(%s): %s", tag, msg));
}
/**
* Send a "what a terrible failure" tag and print the exception.
*
* @param tag string used to identify the source of a log message
* @param throwable the exception to be logged
*/
public static void wtf(final String tag, final Throwable throwable) {
android.util.Log.wtf(TAG, String.format("(%s):", tag), throwable);
}
}

View File

@@ -1,4 +1,33 @@
package com.aldo.apps.ochecompanion.utils;
import java.util.List;
/**
* MatchProgress: A serializable snapshot of an ongoing X01 session.
* Stores the current scores, darts thrown, and active player index
* to allow resuming matches from the database.
*/
public class MatchProgress {
}
public int activePlayerIndex;
public int startingScore;
public List<PlayerStateSnapshot> players;
/**
* Represents the state of an individual player at a point in time.
*/
public static class PlayerStateSnapshot {
public long playerId; // 0 for guests
public String name;
public int remainingScore;
public int dartsThrown;
public PlayerStateSnapshot() {}
public PlayerStateSnapshot(final long playerId, final String name, final int remainingScore, final int dartsThrown) {
this.playerId = playerId;
this.name = name;
this.remainingScore = remainingScore;
this.dartsThrown = dartsThrown;
}
}
}

View File

@@ -7,7 +7,7 @@ import android.content.Context;
import android.media.AudioAttributes;
import android.media.SoundPool;
import android.os.Build;
import android.util.Log;
import com.aldo.apps.ochecompanion.utils.Log;
import com.aldo.apps.ochecompanion.R;

View File

@@ -1,4 +1,26 @@
package com.aldo.apps.ochecompanion.utils.converters;
import androidx.room.TypeConverter;
import com.aldo.apps.ochecompanion.utils.MatchProgress;
import com.google.gson.Gson;
/**
* MatchProgressConverter: Handles serialization of complex match states
* into JSON strings for storage in the Room 'matches' table.
*/
public class MatchProgressConverter {
}
private static final Gson gson = new Gson();
@TypeConverter
public static MatchProgress fromString(String value) {
if (value == null || value.isEmpty()) return null;
return gson.fromJson(value, MatchProgress.class);
}
@TypeConverter
public static String fromProgress(MatchProgress progress) {
if (progress == null) return null;
return gson.toJson(progress);
}
}

View File

@@ -61,6 +61,16 @@
android:textColor="@color/text_primary" />
</com.google.android.material.textfield.TextInputLayout>
<com.google.android.material.button.MaterialButton
android:id="@+id/btnShowStats"
style="@style/Widget.Oche_Button_Primary"
android:layout_width="match_parent"
android:layout_height="64dp"
android:layout_marginTop="32dp"
android:text="@string/txt_create_profile_show_stats"
android:visibility="gone"
/>
<com.google.android.material.button.MaterialButton
android:id="@+id/btnSavePlayer"
style="@style/Widget.Oche_Button_Primary"
@@ -120,4 +130,10 @@
</LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
<com.aldo.apps.ochecompanion.ui.PlayerStatsView
android:id="@+id/player_stats_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:visibility="gone"/>
</FrameLayout>

View File

@@ -17,6 +17,7 @@
<string name="txt_create_profile_header">Create Profile</string>
<string name="txt_create_profile_username_hint">Add your username</string>
<string name="txt_create_profile_username_save">Save to Squad</string>
<string name="txt_create_profile_show_stats">Show Stats</string>
<string name="txt_update_profile_header">Update Profile</string>
<string name="txt_update_profile_username_save">Update Squad</string>
<string name="txt_cancel_crop">Cancel</string>