Left over files from previous comit :/
This commit is contained in:
@@ -8,7 +8,9 @@ import android.graphics.RectF;
|
|||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
import android.os.Bundle;
|
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.MotionEvent;
|
||||||
import android.view.ScaleGestureDetector;
|
import android.view.ScaleGestureDetector;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
@@ -88,6 +90,11 @@ public class AddPlayerActivity extends BaseActivity {
|
|||||||
*/
|
*/
|
||||||
private MaterialButton mSaveButton;
|
private MaterialButton mSaveButton;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Button to show the player stats.
|
||||||
|
*/
|
||||||
|
private MaterialButton mShowStatsButton;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Button to delete a players profile from the database.
|
* Button to delete a players profile from the database.
|
||||||
*/
|
*/
|
||||||
@@ -105,8 +112,18 @@ public class AddPlayerActivity extends BaseActivity {
|
|||||||
*/
|
*/
|
||||||
private CropOverlayView mCropOverlay;
|
private CropOverlayView mCropOverlay;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* View displaying the player's statistics.
|
||||||
|
*/
|
||||||
|
private PlayerStatsView mPlayerStatsView;
|
||||||
|
|
||||||
// ========== Data State ==========
|
// ========== 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.
|
* 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.
|
* Initializes all UI component references and sets up click listeners.
|
||||||
*/
|
*/
|
||||||
@@ -213,6 +241,8 @@ public class AddPlayerActivity extends BaseActivity {
|
|||||||
mProfilePictureView = findViewById(R.id.ivAddPlayerProfile);
|
mProfilePictureView = findViewById(R.id.ivAddPlayerProfile);
|
||||||
mUserNameInput = findViewById(R.id.etUsername);
|
mUserNameInput = findViewById(R.id.etUsername);
|
||||||
mTitleView = findViewById(R.id.tvTitle);
|
mTitleView = findViewById(R.id.tvTitle);
|
||||||
|
mShowStatsButton = findViewById(R.id.btnShowStats);
|
||||||
|
mPlayerStatsView = findViewById(R.id.player_stats_view);
|
||||||
mSaveButton = findViewById(R.id.btnSavePlayer);
|
mSaveButton = findViewById(R.id.btnSavePlayer);
|
||||||
mBtnDelete = findViewById(R.id.btnDeletePlayer);
|
mBtnDelete = findViewById(R.id.btnDeletePlayer);
|
||||||
|
|
||||||
@@ -452,6 +482,7 @@ public class AddPlayerActivity extends BaseActivity {
|
|||||||
new Thread(() -> {
|
new Thread(() -> {
|
||||||
// Query the database for the player (background thread)
|
// Query the database for the player (background thread)
|
||||||
mExistingPlayer = AppDatabase.getDatabase(this).playerDao().getPlayerById(mExistingPlayerId);
|
mExistingPlayer = AppDatabase.getDatabase(this).playerDao().getPlayerById(mExistingPlayerId);
|
||||||
|
final Statistics statistics = AppDatabase.getDatabase(this).statisticsDao().getStatisticsForPlayer(mExistingPlayerId);
|
||||||
|
|
||||||
// Update UI on the main thread
|
// Update UI on the main thread
|
||||||
runOnUiThread(() -> {
|
runOnUiThread(() -> {
|
||||||
@@ -463,6 +494,14 @@ public class AddPlayerActivity extends BaseActivity {
|
|||||||
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);
|
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
|
// Load existing profile picture if available
|
||||||
if (mExistingPlayer.profilePictureUri != null) {
|
if (mExistingPlayer.profilePictureUri != null) {
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ package com.aldo.apps.ochecompanion;
|
|||||||
import android.content.SharedPreferences;
|
import android.content.SharedPreferences;
|
||||||
import android.content.res.Configuration;
|
import android.content.res.Configuration;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.util.Log;
|
import com.aldo.apps.ochecompanion.utils.Log;
|
||||||
|
|
||||||
import androidx.appcompat.app.AppCompatActivity;
|
import androidx.appcompat.app.AppCompatActivity;
|
||||||
import androidx.appcompat.app.AppCompatDelegate;
|
import androidx.appcompat.app.AppCompatDelegate;
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ package com.aldo.apps.ochecompanion;
|
|||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.content.SharedPreferences;
|
import android.content.SharedPreferences;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.util.Log;
|
import com.aldo.apps.ochecompanion.utils.Log;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
|
|
||||||
@@ -16,6 +16,7 @@ import androidx.recyclerview.widget.LinearLayoutManager;
|
|||||||
import androidx.recyclerview.widget.RecyclerView;
|
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.DatabaseHelper;
|
||||||
import com.aldo.apps.ochecompanion.database.objects.Player;
|
import com.aldo.apps.ochecompanion.database.objects.Player;
|
||||||
import com.aldo.apps.ochecompanion.database.objects.Match;
|
import com.aldo.apps.ochecompanion.database.objects.Match;
|
||||||
import com.aldo.apps.ochecompanion.ui.MatchRecapView;
|
import com.aldo.apps.ochecompanion.ui.MatchRecapView;
|
||||||
@@ -55,6 +56,13 @@ public class MainMenuActivity extends BaseActivity {
|
|||||||
*/
|
*/
|
||||||
private SharedPreferences mSettingsPref;
|
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,
|
* Initializes the activity: enables edge-to-edge display, configures window insets,
|
||||||
* and sets up the match recap view with test data click listener.
|
* and sets up the match recap view with test data click listener.
|
||||||
@@ -69,6 +77,7 @@ public class MainMenuActivity extends BaseActivity {
|
|||||||
EdgeToEdge.enable(this);
|
EdgeToEdge.enable(this);
|
||||||
setContentView(R.layout.activity_main);
|
setContentView(R.layout.activity_main);
|
||||||
mSettingsPref = PreferenceManager.getDefaultSharedPreferences(this);
|
mSettingsPref = PreferenceManager.getDefaultSharedPreferences(this);
|
||||||
|
mDatabaseHelper = DatabaseHelper.getInstance(this);
|
||||||
|
|
||||||
// Configure window insets to properly handle system bars (status bar, navigation bar)
|
// Configure window insets to properly handle system bars (status bar, navigation bar)
|
||||||
ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main), (v, insets) -> {
|
ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main), (v, insets) -> {
|
||||||
@@ -84,12 +93,22 @@ public class MainMenuActivity extends BaseActivity {
|
|||||||
quickStartBtn.setOnClickListener(v -> quickStart());
|
quickStartBtn.setOnClickListener(v -> quickStart());
|
||||||
findViewById(R.id.btnSettings).setOnClickListener(v -> launchSettings());
|
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
|
// Set up match recap view with test data functionality
|
||||||
mMatchRecap = findViewById(R.id.match_recap);
|
mMatchRecap = findViewById(R.id.match_recap);
|
||||||
mMatchRecap.setOnClickListener(v -> {
|
mMatchRecap.setOnClickListener(v -> {
|
||||||
// Cycle through test data scenarios on each click
|
// Cycle through test data scenarios on each click
|
||||||
applyTestData(mTestCounter);
|
applyTestData(mTestCounter);
|
||||||
mTestCounter++;
|
mTestCounter++;
|
||||||
|
new Thread(() -> mDatabaseHelper.printAllMatches()).start();
|
||||||
});
|
});
|
||||||
|
|
||||||
findViewById(R.id.title_view).setOnClickListener(v -> startActivity(new Intent(MainMenuActivity.this, TestActivity.class)));
|
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);
|
startActivity(intent);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**game.
|
||||||
* Initiates a quick-start 501 game with two test players.
|
* Checks for any ongoing matches and resumes them if found,
|
||||||
* Creates test players "Test1" and "Test2" and launches GameActivity.
|
* otherwise starts a new game with the default score.
|
||||||
* Test players are not persisted to the database.
|
|
||||||
*/
|
*/
|
||||||
private void quickStart() {
|
private void quickStart() {
|
||||||
final Player playerOne = new Player(DartsConstants.TEST_PLAYER_1, null);
|
// Check for ongoing matches in background thread
|
||||||
final Player playerTwo = new Player(DartsConstants.TEST_PLAYER_2, null);
|
int startingScore = DartsConstants.DEFAULT_GAME_SCORE;
|
||||||
final ArrayList<Player> players = new ArrayList<>();
|
int matchId = -1;
|
||||||
players.add(playerOne);
|
if (mOngoingMatch != null) {
|
||||||
players.add(playerTwo);
|
try {
|
||||||
|
Log.d(TAG, "quickStart: Parsing [" + mOngoingMatch + "]");
|
||||||
GameActivity.start(MainMenuActivity.this, players, DartsConstants.DEFAULT_GAME_SCORE);
|
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);
|
final Intent intent = new Intent(MainMenuActivity.this, AddPlayerActivity.class);
|
||||||
startActivity(intent);
|
startActivity(intent);
|
||||||
});
|
});
|
||||||
|
|
||||||
// Database operations must be run on a background thread to keep the UI responsive.
|
|
||||||
new Thread(() -> {
|
new Thread(() -> {
|
||||||
// Access the singleton database and query all players
|
final List<Player> allPlayers = (List<Player>) mDatabaseHelper.getAllPlayers();
|
||||||
final List<Player> allPlayers = AppDatabase.getDatabase(getApplicationContext())
|
runOnUiThread(() -> adapter.updatePlayers(allPlayers));
|
||||||
.playerDao()
|
|
||||||
.getAllPlayers();
|
|
||||||
|
|
||||||
// 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();
|
}).start();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -171,14 +188,12 @@ public class MainMenuActivity extends BaseActivity {
|
|||||||
* Applies the last completed match from the database to the match recap view.
|
* Applies the last completed match from the database to the match recap view.
|
||||||
*/
|
*/
|
||||||
private void applyLastMatch() {
|
private void applyLastMatch() {
|
||||||
// Database operations must be run on a background thread to keep the UI responsive.
|
|
||||||
new Thread(() -> {
|
new Thread(() -> {
|
||||||
final Match lastMatch = AppDatabase.getDatabase(getApplicationContext())
|
final Match lastCompleted = mDatabaseHelper.getLastCompletedMatch();
|
||||||
.matchDao()
|
if (lastCompleted != null) {
|
||||||
.getLastCompletedMatch();
|
Log.d(TAG, "applyLastMatch: Applying last completed match [" + lastCompleted + "]");
|
||||||
|
runOnUiThread(() -> mMatchRecap.setMatch(lastCompleted));
|
||||||
// Post-database query UI updates must happen back on the main (UI) thread
|
}
|
||||||
runOnUiThread(() -> mMatchRecap.setMatch(lastMatch));
|
|
||||||
}).start();
|
}).start();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
package com.aldo.apps.ochecompanion;
|
package com.aldo.apps.ochecompanion;
|
||||||
|
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.util.Log;
|
import com.aldo.apps.ochecompanion.utils.Log;
|
||||||
|
|
||||||
import androidx.activity.EdgeToEdge;
|
import androidx.activity.EdgeToEdge;
|
||||||
import androidx.appcompat.app.AppCompatActivity;
|
import androidx.appcompat.app.AppCompatActivity;
|
||||||
|
|||||||
@@ -1,11 +1,13 @@
|
|||||||
package com.aldo.apps.ochecompanion.database;
|
package com.aldo.apps.ochecompanion.database;
|
||||||
|
|
||||||
import android.content.Context;
|
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.Player;
|
||||||
import com.aldo.apps.ochecompanion.database.objects.Statistics;
|
import com.aldo.apps.ochecompanion.database.objects.Statistics;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
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.
|
* Retrieves statistics for a specific player synchronously.
|
||||||
* Blocks until the operation completes to ensure consistency with any pending writes.
|
* Blocks until the operation completes to ensure consistency with any pending writes.
|
||||||
|
|||||||
@@ -20,7 +20,16 @@ public interface MatchDao {
|
|||||||
* @param match The Match entity to persist
|
* @param match The Match entity to persist
|
||||||
*/
|
*/
|
||||||
@Insert
|
@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.
|
* 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")
|
@Query("SELECT * FROM matches ORDER BY timestamp DESC LIMIT 1")
|
||||||
Match getLastMatch();
|
Match getLastMatch();
|
||||||
|
|
||||||
|
@Query("SELECT * FROM matches WHERE id = :matchId")
|
||||||
|
Match getMatchById(final int matchId);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieves the most recently completed match.
|
* Retrieves the most recently completed match.
|
||||||
* Must be called on a background thread.
|
* Must be called on a background thread.
|
||||||
|
|||||||
@@ -1,8 +1,15 @@
|
|||||||
package com.aldo.apps.ochecompanion.database.objects;
|
package com.aldo.apps.ochecompanion.database.objects;
|
||||||
|
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
import androidx.room.Entity;
|
import androidx.room.Entity;
|
||||||
import androidx.room.Ignore;
|
import androidx.room.Ignore;
|
||||||
import androidx.room.PrimaryKey;
|
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.JSONArray;
|
||||||
import org.json.JSONException;
|
import org.json.JSONException;
|
||||||
import org.json.JSONObject;
|
import org.json.JSONObject;
|
||||||
@@ -27,6 +34,8 @@ import java.util.Map;
|
|||||||
@Entity(tableName = "matches")
|
@Entity(tableName = "matches")
|
||||||
public class Match implements Serializable {
|
public class Match implements Serializable {
|
||||||
|
|
||||||
|
private static final String TAG = "Match";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents the current state of a 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
|
* @return The username of the player, or "Unknown" if unavailable
|
||||||
*/
|
*/
|
||||||
public String getPlayerNameByPosition(final int position) {
|
public String getPlayerNameByPosition(final int position) {
|
||||||
try {
|
final MatchProgress progress = MatchProgressConverter.fromString(participantData);
|
||||||
final JSONArray participants = new JSONArray(participantData);
|
if (progress != null && progress.players != null && position < progress.players.size()) {
|
||||||
if (position >= 0 && position < participants.length()) {
|
return progress.players.get(position).name;
|
||||||
final JSONObject participant = participants.getJSONObject(position);
|
|
||||||
return participant.optString("username", "Unknown");
|
|
||||||
}
|
|
||||||
} catch (JSONException e) {
|
|
||||||
// Return default if JSON parsing fails
|
|
||||||
}
|
}
|
||||||
return "Unknown";
|
return "Unknown";
|
||||||
}
|
}
|
||||||
@@ -176,14 +180,14 @@ public class Match implements Serializable {
|
|||||||
* @return The career average, or 0.0 if unavailable
|
* @return The career average, or 0.0 if unavailable
|
||||||
*/
|
*/
|
||||||
public double getPlayerAverageByPosition(final int position) {
|
public double getPlayerAverageByPosition(final int position) {
|
||||||
try {
|
final MatchProgress progress = MatchProgressConverter.fromString(participantData);
|
||||||
final JSONArray participants = new JSONArray(participantData);
|
if (progress != null && progress.players != null && position < progress.players.size()) {
|
||||||
if (position >= 0 && position < participants.length()) {
|
final int dartsThrown = progress.players.get(position).dartsThrown;
|
||||||
final JSONObject participant = participants.getJSONObject(position);
|
final int startingScore = progress.startingScore;
|
||||||
return participant.optDouble("careerAverage", 0.0);
|
final int scoreLeft = progress.players.get(position).remainingScore;
|
||||||
}
|
final int scoredPoints = (scoreLeft - startingScore) * -1;
|
||||||
} catch (JSONException e) {
|
|
||||||
// Return default if JSON parsing fails
|
return (double) scoredPoints / dartsThrown * 3;
|
||||||
}
|
}
|
||||||
return 0.0;
|
return 0.0;
|
||||||
}
|
}
|
||||||
@@ -195,14 +199,9 @@ public class Match implements Serializable {
|
|||||||
* @return The match score, or 0 if unavailable
|
* @return The match score, or 0 if unavailable
|
||||||
*/
|
*/
|
||||||
public int getPlayerScoreByPosition(final int position) {
|
public int getPlayerScoreByPosition(final int position) {
|
||||||
try {
|
final MatchProgress progress = MatchProgressConverter.fromString(participantData);
|
||||||
final JSONArray participants = new JSONArray(participantData);
|
if (progress != null && progress.players != null && position < progress.players.size()) {
|
||||||
if (position >= 0 && position < participants.length()) {
|
return progress.players.get(position).remainingScore;
|
||||||
final JSONObject participant = participants.getJSONObject(position);
|
|
||||||
return participant.optInt("score", 0);
|
|
||||||
}
|
|
||||||
} catch (JSONException e) {
|
|
||||||
// Return default if JSON parsing fails
|
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -360,4 +359,16 @@ public class Match implements Serializable {
|
|||||||
public boolean isCanceled() {
|
public boolean isCanceled() {
|
||||||
return this.state == MatchState.CANCELED;
|
return this.state == MatchState.CANCELED;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "Match{" +
|
||||||
|
"id=" + id +
|
||||||
|
", timestamp=" + timestamp +
|
||||||
|
", gameMode='" + gameMode + '\'' +
|
||||||
|
", playerCount=" + playerCount +
|
||||||
|
", participantData='" + participantData + '\'' +
|
||||||
|
", state=" + state +
|
||||||
|
'}';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
package com.aldo.apps.ochecompanion.database.objects;
|
package com.aldo.apps.ochecompanion.database.objects;
|
||||||
|
|
||||||
import android.util.Log;
|
import com.aldo.apps.ochecompanion.utils.Log;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.room.Entity;
|
import androidx.room.Entity;
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
package com.aldo.apps.ochecompanion.ui;
|
package com.aldo.apps.ochecompanion.ui;
|
||||||
|
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.util.Log;
|
import com.aldo.apps.ochecompanion.utils.Log;
|
||||||
|
|
||||||
import androidx.appcompat.app.AppCompatDelegate;
|
import androidx.appcompat.app.AppCompatDelegate;
|
||||||
import androidx.preference.Preference;
|
import androidx.preference.Preference;
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package com.aldo.apps.ochecompanion.ui;
|
|||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.util.AttributeSet;
|
import android.util.AttributeSet;
|
||||||
|
import com.aldo.apps.ochecompanion.utils.Log;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.widget.FrameLayout;
|
import android.widget.FrameLayout;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
@@ -19,6 +20,11 @@ import com.aldo.apps.ochecompanion.ui.adapter.MainMenuGroupMatchAdapter;
|
|||||||
*/
|
*/
|
||||||
public class MatchRecapView extends FrameLayout {
|
public class MatchRecapView extends FrameLayout {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tag for debugging purposes.
|
||||||
|
*/
|
||||||
|
private static final String TAG = "MatchRecapView";
|
||||||
|
|
||||||
/** View container for empty state (no match history). */
|
/** View container for empty state (no match history). */
|
||||||
private View mStateEmpty;
|
private View mStateEmpty;
|
||||||
|
|
||||||
@@ -75,6 +81,7 @@ public class MatchRecapView extends FrameLayout {
|
|||||||
|
|
||||||
/** Binds match and updates display (empty, 1v1, or group state). */
|
/** Binds match and updates display (empty, 1v1, or group state). */
|
||||||
public void setMatch(@Nullable final Match match) {
|
public void setMatch(@Nullable final Match match) {
|
||||||
|
Log.d(TAG, "setMatch() called with: match = [" + match + "]");
|
||||||
if (match == null) {
|
if (match == null) {
|
||||||
updateVisibility(mStateEmpty);
|
updateVisibility(mStateEmpty);
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
package com.aldo.apps.ochecompanion.ui.adapter;
|
package com.aldo.apps.ochecompanion.ui.adapter;
|
||||||
|
|
||||||
import android.annotation.SuppressLint;
|
import android.annotation.SuppressLint;
|
||||||
import android.util.Log;
|
import com.aldo.apps.ochecompanion.utils.Log;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import static com.aldo.apps.ochecompanion.AddPlayerActivity.EXTRA_PLAYER_ID;
|
|||||||
import android.annotation.SuppressLint;
|
import android.annotation.SuppressLint;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.util.Log;
|
import com.aldo.apps.ochecompanion.utils.Log;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
|
|||||||
@@ -1,4 +1,153 @@
|
|||||||
package com.aldo.apps.ochecompanion.utils;
|
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 {
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,33 @@
|
|||||||
package com.aldo.apps.ochecompanion.utils;
|
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 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -7,7 +7,7 @@ import android.content.Context;
|
|||||||
import android.media.AudioAttributes;
|
import android.media.AudioAttributes;
|
||||||
import android.media.SoundPool;
|
import android.media.SoundPool;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
import android.util.Log;
|
import com.aldo.apps.ochecompanion.utils.Log;
|
||||||
|
|
||||||
import com.aldo.apps.ochecompanion.R;
|
import com.aldo.apps.ochecompanion.R;
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,26 @@
|
|||||||
package com.aldo.apps.ochecompanion.utils.converters;
|
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 {
|
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -61,6 +61,16 @@
|
|||||||
android:textColor="@color/text_primary" />
|
android:textColor="@color/text_primary" />
|
||||||
</com.google.android.material.textfield.TextInputLayout>
|
</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
|
<com.google.android.material.button.MaterialButton
|
||||||
android:id="@+id/btnSavePlayer"
|
android:id="@+id/btnSavePlayer"
|
||||||
style="@style/Widget.Oche_Button_Primary"
|
style="@style/Widget.Oche_Button_Primary"
|
||||||
@@ -120,4 +130,10 @@
|
|||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
</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>
|
</FrameLayout>
|
||||||
@@ -17,6 +17,7 @@
|
|||||||
<string name="txt_create_profile_header">Create Profile</string>
|
<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_hint">Add your username</string>
|
||||||
<string name="txt_create_profile_username_save">Save to Squad</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_header">Update Profile</string>
|
||||||
<string name="txt_update_profile_username_save">Update Squad</string>
|
<string name="txt_update_profile_username_save">Update Squad</string>
|
||||||
<string name="txt_cancel_crop">Cancel</string>
|
<string name="txt_cancel_crop">Cancel</string>
|
||||||
|
|||||||
Reference in New Issue
Block a user