Created a Player Selection
When a new Game is started now a Player Selection Screen pops up, to select the players to be performing in the next match.
This commit is contained in:
@@ -70,6 +70,11 @@ public class GameActivity extends BaseActivity implements GameManager.GameStateC
|
||||
*/
|
||||
private static final String EXTRA_MATCH_ID = "extra_match_uuid";
|
||||
|
||||
/**
|
||||
* Intent extra for a player list. Making it possible to start a match with pre-defined players.
|
||||
*/
|
||||
private static final String EXTRA_PLAYERS = "extra_players";
|
||||
|
||||
|
||||
// ========================================================================================
|
||||
// Game Manager (Singleton Business Logic Handler)
|
||||
@@ -196,9 +201,18 @@ public class GameActivity extends BaseActivity implements GameManager.GameStateC
|
||||
* @param matchId The ID of the match to be started/loaded.
|
||||
*/
|
||||
public static void start(final Context context, final int startScore, final int matchId) {
|
||||
final GameManager gameManager = GameManager.getInstance(context);
|
||||
gameManager.initializeMatch(matchId, startScore,null);
|
||||
|
||||
Intent intent = new Intent(context, GameActivity.class);
|
||||
context.startActivity(intent);
|
||||
}
|
||||
|
||||
public static void start(final Context context, final List<Player> players, final int startScore) {
|
||||
final GameManager gameManager = GameManager.getInstance(context);
|
||||
gameManager.initializeMatch(-1, startScore, players,null);
|
||||
|
||||
Intent intent = new Intent(context, GameActivity.class);
|
||||
intent.putExtra(EXTRA_START_SCORE, startScore);
|
||||
intent.putExtra(EXTRA_MATCH_ID, matchId);
|
||||
context.startActivity(intent);
|
||||
}
|
||||
|
||||
@@ -223,16 +237,12 @@ public class GameActivity extends BaseActivity implements GameManager.GameStateC
|
||||
|
||||
// Initialize GameManager singleton
|
||||
mGameManager = GameManager.getInstance(this);
|
||||
mGameManager.setCallback(this);
|
||||
|
||||
// Extract game parameters from intent
|
||||
final int startingScore = getIntent().getIntExtra(EXTRA_START_SCORE, DartsConstants.DEFAULT_GAME_SCORE);
|
||||
final int matchId = getIntent().getIntExtra(EXTRA_MATCH_ID, -1);
|
||||
|
||||
// Initialize activity components in order
|
||||
initViews();
|
||||
setupKeyboard();
|
||||
|
||||
mGameManager.setCallback(this);
|
||||
getOnBackPressedDispatcher().addCallback(this, new OnBackPressedCallback(true) {
|
||||
|
||||
@Override
|
||||
@@ -240,13 +250,9 @@ public class GameActivity extends BaseActivity implements GameManager.GameStateC
|
||||
handleBackPressed();
|
||||
}
|
||||
});
|
||||
|
||||
// Initialize match through GameManager (handles loading existing or creating new)
|
||||
mGameManager.initializeMatch(matchId, startingScore, () -> runOnUiThread(() -> {
|
||||
updateUI();
|
||||
updateTurnIndicators();
|
||||
setMultiplier(DartsConstants.MULTIPLIER_SINGLE);
|
||||
}));
|
||||
updateUI();
|
||||
updateTurnIndicators();
|
||||
setMultiplier(DartsConstants.MULTIPLIER_SINGLE);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -3,6 +3,8 @@ package com.aldo.apps.ochecompanion;
|
||||
import android.content.Intent;
|
||||
import android.content.SharedPreferences;
|
||||
import android.os.Bundle;
|
||||
|
||||
import com.aldo.apps.ochecompanion.ui.PlayerSelectionDialogFragment;
|
||||
import com.aldo.apps.ochecompanion.utils.Log;
|
||||
import android.widget.TextView;
|
||||
|
||||
@@ -145,10 +147,13 @@ public class MainMenuActivity extends BaseActivity {
|
||||
} 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);
|
||||
} else {
|
||||
Log.d(TAG, "quickStart: No ongoing matches found");
|
||||
new PlayerSelectionDialogFragment().show(getSupportFragmentManager(), "squad_selector");
|
||||
}
|
||||
Log.d(TAG, "quickStart: Starting match with StartingScore = [" + startingScore
|
||||
+ "] and matchId = [" + matchId + "]");
|
||||
GameActivity.start(MainMenuActivity.this, startingScore, matchId);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -88,4 +88,12 @@ public interface MatchDao {
|
||||
*/
|
||||
@Query("SELECT * FROM matches WHERE state = :state ORDER BY timestamp DESC")
|
||||
List<Match> getMatchesByState(final String state);
|
||||
|
||||
/**
|
||||
* Retrieves ONLY the participant JSON string from the most recent match.
|
||||
* Used for the "Auto-select last players" feature in the Squad Selector.
|
||||
*/
|
||||
@Query("SELECT participantData FROM matches ORDER BY timestamp DESC LIMIT 1")
|
||||
String getLastMatchParticipantData();
|
||||
|
||||
}
|
||||
|
||||
@@ -149,7 +149,69 @@ public class GameManager {
|
||||
*/
|
||||
public void setCallback(final GameStateCallback callback) {
|
||||
mCallback = callback;
|
||||
//Send one initial callback
|
||||
notifyGameStateChanged();
|
||||
}
|
||||
|
||||
public void initializeMatch(final int matchId,final int startingScore, final List<Player> players, final Runnable onComplete) {
|
||||
mStartingScore = startingScore;
|
||||
mMatchId = matchId;
|
||||
|
||||
new Thread(() -> {
|
||||
Match match = null;
|
||||
if (matchId > 0) {
|
||||
// Try to load existing match
|
||||
match = mDatabaseHelper.getMatchById(matchId);
|
||||
Log.d(TAG, "initializeMatch: Loaded match from DB: " + match);
|
||||
|
||||
|
||||
if (match != null && match.participantData != null && !match.participantData.isEmpty()) {
|
||||
// Load match progress from database
|
||||
try {
|
||||
final MatchProgress progress = MatchProgressConverter.fromString(match.participantData);
|
||||
if (progress != null) {
|
||||
Log.d(TAG, "initializeMatch: Found saved progress with " + progress.players.size() + " players");
|
||||
// Initialize player states
|
||||
initializePlayerStates(players);
|
||||
loadMatchProgress(progress);
|
||||
|
||||
if (onComplete != null) {
|
||||
onComplete.run();
|
||||
}
|
||||
notifyGameStateChanged();
|
||||
return;
|
||||
} else {
|
||||
Log.w(TAG, "initializeMatch: Progress is null, treating as new match");
|
||||
match = null;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "initializeMatch: Failed to load match progress", e);
|
||||
match = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Create new match if not found or invalid
|
||||
if (match == null) {
|
||||
final long newMatchId = mDatabaseHelper.createNewMatch(String.valueOf(startingScore), players);
|
||||
if (newMatchId > 0) {
|
||||
mMatchId = (int) newMatchId;
|
||||
Log.d(TAG, "initializeMatch: Created new match with ID: " + mMatchId);
|
||||
} else {
|
||||
Log.e(TAG, "initializeMatch: Failed to create new match");
|
||||
}
|
||||
|
||||
// Setup new game
|
||||
initializePlayerStates(players);
|
||||
}
|
||||
|
||||
if (onComplete != null) {
|
||||
onComplete.run();
|
||||
}
|
||||
notifyGameStateChanged();
|
||||
}).start();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Initializes a new game or loads an existing match from the database.
|
||||
@@ -166,58 +228,7 @@ public class GameManager {
|
||||
new Thread(() -> {
|
||||
final List<Player> allPlayers = mDatabaseHelper.getAllPlayers();
|
||||
Log.d(TAG, "initializeMatch: Loading players, count = " + (allPlayers != null ? allPlayers.size() : 0));
|
||||
|
||||
Match match = null;
|
||||
if (matchId > 0) {
|
||||
// Try to load existing match
|
||||
match = mDatabaseHelper.getMatchById(matchId);
|
||||
Log.d(TAG, "initializeMatch: Loaded match from DB: " + match);
|
||||
|
||||
|
||||
if (match != null && match.participantData != null && !match.participantData.isEmpty()) {
|
||||
// Load match progress from database
|
||||
try {
|
||||
final MatchProgress progress = MatchProgressConverter.fromString(match.participantData);
|
||||
if (progress != null) {
|
||||
Log.d(TAG, "initializeMatch: Found saved progress with " + progress.players.size() + " players");
|
||||
// Initialize player states
|
||||
initializePlayerStates(allPlayers);
|
||||
loadMatchProgress(progress);
|
||||
|
||||
if (onComplete != null) {
|
||||
onComplete.run();
|
||||
}
|
||||
notifyGameStateChanged();
|
||||
return;
|
||||
} else {
|
||||
Log.w(TAG, "initializeMatch: Progress is null, treating as new match");
|
||||
match = null;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "initializeMatch: Failed to load match progress", e);
|
||||
match = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Create new match if not found or invalid
|
||||
if (match == null) {
|
||||
final long newMatchId = mDatabaseHelper.createNewMatch(String.valueOf(startingScore), allPlayers);
|
||||
if (newMatchId > 0) {
|
||||
mMatchId = (int) newMatchId;
|
||||
Log.d(TAG, "initializeMatch: Created new match with ID: " + mMatchId);
|
||||
} else {
|
||||
Log.e(TAG, "initializeMatch: Failed to create new match");
|
||||
}
|
||||
|
||||
// Setup new game
|
||||
initializePlayerStates(allPlayers);
|
||||
}
|
||||
|
||||
if (onComplete != null) {
|
||||
onComplete.run();
|
||||
}
|
||||
notifyGameStateChanged();
|
||||
initializeMatch(matchId, startingScore, allPlayers, onComplete);
|
||||
}).start();
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,134 @@
|
||||
package com.aldo.apps.ochecompanion.ui;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import com.aldo.apps.ochecompanion.GameActivity;
|
||||
import com.aldo.apps.ochecompanion.R;
|
||||
import com.aldo.apps.ochecompanion.database.AppDatabase;
|
||||
import com.aldo.apps.ochecompanion.database.objects.Player;
|
||||
import com.aldo.apps.ochecompanion.ui.adapter.PlayerSelectionAdapter;
|
||||
import com.aldo.apps.ochecompanion.utils.DartsConstants;
|
||||
import com.aldo.apps.ochecompanion.utils.MatchProgress;
|
||||
import com.aldo.apps.ochecompanion.utils.converters.MatchProgressConverter;
|
||||
import com.google.android.material.bottomsheet.BottomSheetDialogFragment;
|
||||
import com.google.android.material.button.MaterialButton;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* PlayerSelectionDialogFragment: A modern bottom-sheet selector for match participants.
|
||||
* Automatically pre-selects players from the most recent match for speed.
|
||||
*/
|
||||
public class PlayerSelectionDialogFragment extends BottomSheetDialogFragment {
|
||||
|
||||
/**
|
||||
* Tag for debugging and logging purposes.
|
||||
*/
|
||||
private static final String TAG = "PlayerSelectionDialogFragment";
|
||||
|
||||
/**
|
||||
* The {@link List} of the selected {@link Player} for the match.
|
||||
*/
|
||||
private final List<Player> mAllPlayers = new ArrayList<>();
|
||||
private final Set<Integer> mSelectedIds = new HashSet<>();
|
||||
|
||||
private RecyclerView mRvSquad;
|
||||
private MaterialButton mBtnStart;
|
||||
private PlayerSelectionAdapter mAdapter;
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
|
||||
return inflater.inflate(R.layout.layout_player_selection_sheet, container, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
|
||||
super.onViewCreated(view, savedInstanceState);
|
||||
|
||||
mRvSquad = view.findViewById(R.id.rvSquadSelection);
|
||||
mBtnStart = view.findViewById(R.id.btnConfirmSelection);
|
||||
|
||||
mRvSquad.setLayoutManager(new LinearLayoutManager(getContext()));
|
||||
mAdapter = new PlayerSelectionAdapter(mAllPlayers, mSelectedIds, this::onSelectionChanged);
|
||||
mRvSquad.setAdapter(mAdapter);
|
||||
|
||||
mBtnStart.setOnClickListener(v -> initiateMatch());
|
||||
|
||||
loadData();
|
||||
}
|
||||
|
||||
private void loadData() {
|
||||
new Thread(() -> {
|
||||
AppDatabase db = AppDatabase.getDatabase(requireContext());
|
||||
|
||||
// 1. Get All Players
|
||||
List<Player> players = db.playerDao().getAllPlayers();
|
||||
|
||||
// 2. Get Last Participants for Pre-selection
|
||||
String lastJson = db.matchDao().getLastMatchParticipantData();
|
||||
MatchProgress lastProgress = MatchProgressConverter.fromString(lastJson);
|
||||
|
||||
final Set<Integer> lastPlayerIds = new HashSet<>();
|
||||
if (lastProgress != null && lastProgress.players != null) {
|
||||
for (MatchProgress.PlayerStateSnapshot p : lastProgress.players) {
|
||||
lastPlayerIds.add((int) p.playerId);
|
||||
}
|
||||
}
|
||||
|
||||
if (getActivity() != null) {
|
||||
getActivity().runOnUiThread(() -> {
|
||||
mAllPlayers.clear();
|
||||
mAllPlayers.addAll(players);
|
||||
|
||||
// Auto-select players from the previous session
|
||||
mSelectedIds.clear();
|
||||
for (Player p : mAllPlayers) {
|
||||
if (lastPlayerIds.contains((int) p.id)) {
|
||||
mSelectedIds.add((int) p.id);
|
||||
}
|
||||
}
|
||||
|
||||
mAdapter.notifyDataSetChanged();
|
||||
updateButtonState();
|
||||
});
|
||||
}
|
||||
}).start();
|
||||
}
|
||||
|
||||
private void onSelectionChanged() {
|
||||
updateButtonState();
|
||||
}
|
||||
|
||||
private void updateButtonState() {
|
||||
int count = mSelectedIds.size();
|
||||
mBtnStart.setEnabled(count > 0);
|
||||
mBtnStart.setText(count > 0 ? "START MATCH (" + count + ")" : "SELECT PLAYERS");
|
||||
}
|
||||
|
||||
private void initiateMatch() {
|
||||
ArrayList<Player> selectedList = new ArrayList<>();
|
||||
for (Player p : mAllPlayers) {
|
||||
if (mSelectedIds.contains((int) p.id)) {
|
||||
selectedList.add(p);
|
||||
}
|
||||
}
|
||||
|
||||
if (selectedList.isEmpty()) return;
|
||||
|
||||
// Start the game!
|
||||
GameActivity.start(requireContext(), selectedList, DartsConstants.DEFAULT_GAME_SCORE);
|
||||
dismiss();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,105 @@
|
||||
package com.aldo.apps.ochecompanion.ui.adapter;
|
||||
|
||||
import android.content.Context;
|
||||
import android.os.Vibrator;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.TextView;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.core.content.ContextCompat;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
import com.aldo.apps.ochecompanion.R;
|
||||
import com.aldo.apps.ochecompanion.database.objects.Player;
|
||||
import com.bumptech.glide.Glide;
|
||||
import com.google.android.material.card.MaterialCardView;
|
||||
import com.google.android.material.imageview.ShapeableImageView;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* PlayerSelectionAdapter: Optimized for the "Squad Selector" Bottom Sheet.
|
||||
* Features specialized selection visual states and haptic feedback.
|
||||
*/
|
||||
public class PlayerSelectionAdapter extends RecyclerView.Adapter<PlayerSelectionAdapter.SelectionHolder> {
|
||||
|
||||
private final List<Player> mPlayers;
|
||||
private final Set<Integer> mSelectedIds;
|
||||
private final Runnable mOnChanged;
|
||||
|
||||
public PlayerSelectionAdapter(List<Player> players, Set<Integer> selected, Runnable onChanged) {
|
||||
this.mPlayers = players;
|
||||
this.mSelectedIds = selected;
|
||||
this.mOnChanged = onChanged;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public SelectionHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
|
||||
View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_player_selection, parent, false);
|
||||
return new SelectionHolder(v);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(@NonNull SelectionHolder holder, int position) {
|
||||
Player p = mPlayers.get(position);
|
||||
holder.bind(p, mSelectedIds.contains((int) p.id));
|
||||
|
||||
holder.itemView.setOnClickListener(v -> {
|
||||
// Haptic Feedback
|
||||
Vibrator vib = (Vibrator) v.getContext().getSystemService(Context.VIBRATOR_SERVICE);
|
||||
if (vib != null) vib.vibrate(20);
|
||||
|
||||
if (mSelectedIds.contains((int) p.id)) {
|
||||
mSelectedIds.remove((int) p.id);
|
||||
} else {
|
||||
if (mSelectedIds.size() < 8) { // PDC/Standard limit
|
||||
mSelectedIds.add((int) p.id);
|
||||
}
|
||||
}
|
||||
notifyItemChanged(position);
|
||||
mOnChanged.run();
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemCount() {
|
||||
return mPlayers.size();
|
||||
}
|
||||
|
||||
static class SelectionHolder extends RecyclerView.ViewHolder {
|
||||
private final ShapeableImageView ivAvatar;
|
||||
private final TextView tvName;
|
||||
private final MaterialCardView card;
|
||||
private final View selectionIndicator;
|
||||
|
||||
public SelectionHolder(@NonNull View itemView) {
|
||||
super(itemView);
|
||||
ivAvatar = itemView.findViewById(R.id.ivPlayerProfile);
|
||||
tvName = itemView.findViewById(R.id.tvPlayerName);
|
||||
card = (MaterialCardView) itemView;
|
||||
selectionIndicator = itemView.findViewById(R.id.selectionOverlay);
|
||||
}
|
||||
|
||||
public void bind(Player p, boolean isSelected) {
|
||||
tvName.setText(p.username);
|
||||
|
||||
if (p.profilePictureUri != null) {
|
||||
Glide.with(itemView.getContext()).load(p.profilePictureUri).into(ivAvatar);
|
||||
} else {
|
||||
ivAvatar.setImageResource(R.drawable.ic_users);
|
||||
}
|
||||
|
||||
// Visual toggle
|
||||
if (isSelected) {
|
||||
card.setStrokeColor(ContextCompat.getColor(itemView.getContext(), R.color.volt_green));
|
||||
card.setStrokeWidth(4);
|
||||
selectionIndicator.setVisibility(View.VISIBLE);
|
||||
} else {
|
||||
card.setStrokeColor(ContextCompat.getColor(itemView.getContext(), R.color.border_subtle));
|
||||
card.setStrokeWidth(1);
|
||||
selectionIndicator.setVisibility(View.GONE);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,21 +1,31 @@
|
||||
package com.aldo.apps.ochecompanion.utils.converters;
|
||||
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.room.TypeConverter;
|
||||
|
||||
import com.aldo.apps.ochecompanion.utils.MatchProgress;
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.JsonSyntaxException;
|
||||
|
||||
/**
|
||||
* MatchProgressConverter: Handles serialization of complex match states
|
||||
* into JSON strings for storage in the Room 'matches' table.
|
||||
*/
|
||||
public class MatchProgressConverter {
|
||||
|
||||
private static final String TAG = "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);
|
||||
try {
|
||||
return gson.fromJson(value, MatchProgress.class);
|
||||
} catch (final JsonSyntaxException ex) {
|
||||
Log.e(TAG, "fromString: Failed to parse json, return null", ex);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@TypeConverter
|
||||
|
||||
71
app/src/main/res/layout/item_player_selection.xml
Normal file
71
app/src/main/res/layout/item_player_selection.xml
Normal file
@@ -0,0 +1,71 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<com.google.android.material.card.MaterialCardView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginVertical="6dp"
|
||||
app:cardBackgroundColor="@color/surface_secondary"
|
||||
app:cardCornerRadius="12dp"
|
||||
app:cardElevation="0dp"
|
||||
app:strokeColor="@color/border_subtle"
|
||||
app:strokeWidth="1dp">
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:padding="12dp">
|
||||
|
||||
<com.google.android.material.imageview.ShapeableImageView
|
||||
android:id="@+id/ivPlayerProfile"
|
||||
android:layout_width="44dp"
|
||||
android:layout_height="44dp"
|
||||
android:scaleType="centerCrop"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:shapeAppearanceOverlay="@style/ShapeAppearance.MaterialComponents.SmallComponent"
|
||||
tools:src="@drawable/ic_users" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tvPlayerName"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="16dp"
|
||||
android:fontFamily="sans-serif-black"
|
||||
android:textColor="@color/text_primary"
|
||||
android:textSize="15sp"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toStartOf="@+id/selectionOverlay"
|
||||
app:layout_constraintStart_toEndOf="@id/ivPlayerProfile"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
tools:text="Snakebite" />
|
||||
|
||||
<!-- Checkmark/Selection Indicator -->
|
||||
<FrameLayout
|
||||
android:id="@+id/selectionOverlay"
|
||||
android:layout_width="24dp"
|
||||
android:layout_height="24dp"
|
||||
android:visibility="gone"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
tools:visibility="visible">
|
||||
|
||||
<View
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="@drawable/shape_circle_overlay"
|
||||
android:backgroundTint="@color/volt_green" />
|
||||
|
||||
<ImageView
|
||||
android:layout_width="16dp"
|
||||
android:layout_height="16dp"
|
||||
android:layout_gravity="center"
|
||||
android:src="@drawable/ic_chevron_right"
|
||||
app:tint="@color/midnight_black" />
|
||||
</FrameLayout>
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
</com.google.android.material.card.MaterialCardView>
|
||||
62
app/src/main/res/layout/layout_player_selection_sheet.xml
Normal file
62
app/src/main/res/layout/layout_player_selection_sheet.xml
Normal file
@@ -0,0 +1,62 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@color/surface_primary"
|
||||
android:orientation="vertical"
|
||||
android:padding="24dp">
|
||||
|
||||
<!-- Bottom Sheet Drag Handle -->
|
||||
<View
|
||||
android:layout_width="40dp"
|
||||
android:layout_height="4dp"
|
||||
android:layout_gravity="center"
|
||||
android:layout_marginBottom="24dp"
|
||||
android:background="@color/text_dim"
|
||||
android:alpha="0.5" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="WHO'S ON THE OCHE?"
|
||||
android:fontFamily="sans-serif-black"
|
||||
android:textColor="@color/text_primary"
|
||||
android:textSize="20sp"
|
||||
android:letterSpacing="0.05" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="4dp"
|
||||
android:layout_marginBottom="20dp"
|
||||
android:text="Select up to 8 players for this match"
|
||||
android:textColor="@color/text_secondary"
|
||||
android:textSize="12sp"
|
||||
android:textAllCaps="true"
|
||||
android:letterSpacing="0.1" />
|
||||
|
||||
<!-- Squad List -->
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/rvSquadSelection"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="320dp"
|
||||
android:clipToPadding="false"
|
||||
android:paddingBottom="80dp"
|
||||
tools:listitem="@layout/item_player_selection" />
|
||||
|
||||
<!-- Action Button (Positioned at bottom of sheet) -->
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/btnConfirmSelection"
|
||||
style="@style/Widget.Oche_Button_Primary"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="64dp"
|
||||
android:layout_marginTop="-70dp"
|
||||
android:text="START MATCH"
|
||||
app:icon="@drawable/ic_play"
|
||||
app:iconGravity="textEnd"
|
||||
app:iconSize="20dp"
|
||||
app:cornerRadius="16dp" />
|
||||
|
||||
</LinearLayout>
|
||||
Reference in New Issue
Block a user