Replacesd magic numbers with constants

This commit is contained in:
Alexander Doerflinger
2026-01-28 14:14:29 +01:00
parent 52a18b40d0
commit dde11329bf
10 changed files with 789 additions and 69 deletions

View File

@@ -19,6 +19,7 @@ import androidx.appcompat.app.AppCompatActivity;
import com.aldo.apps.ochecompanion.database.AppDatabase;
import com.aldo.apps.ochecompanion.database.objects.Player;
import com.aldo.apps.ochecompanion.ui.CropOverlayView;
import com.aldo.apps.ochecompanion.utils.UIConstants;
import com.google.android.material.button.MaterialButton;
import com.google.android.material.imageview.ShapeableImageView;
@@ -130,7 +131,7 @@ public class AddPlayerActivity extends AppCompatActivity {
/**
* Current scale factor applied to the crop preview image (1.0 default, clamped 0.1 to 10.0).
*/
private float mScaleFactor = 1.0f;
private float mScaleFactor = UIConstants.SCALE_NORMAL;
/**
* ActivityResultLauncher for selecting images from the device gallery.
@@ -207,7 +208,7 @@ public class AddPlayerActivity extends AppCompatActivity {
// Prevent the image from becoming too small or impossibly large
// Clamp between 0.1× (10% size) and 10.0× (1000% size)
mScaleFactor = Math.max(0.1f, Math.min(mScaleFactor, 10.0f));
mScaleFactor = Math.max(UIConstants.SCALE_MIN_ZOOM, Math.min(mScaleFactor, UIConstants.SCALE_MAX_ZOOM));
// Apply the scale to both X and Y axes for uniform scaling
mIvCropPreview.setScaleX(mScaleFactor);
@@ -259,9 +260,9 @@ public class AddPlayerActivity extends AppCompatActivity {
mLayoutCropper.setVisibility(View.VISIBLE);
// Reset transformation state for a fresh start
mScaleFactor = 1.0f; // Reset zoom to 100%
mIvCropPreview.setScaleX(1.0f);
mIvCropPreview.setScaleY(1.0f);
mScaleFactor = UIConstants.SCALE_NORMAL; // Reset zoom to 100%
mIvCropPreview.setScaleX(UIConstants.SCALE_NORMAL);
mIvCropPreview.setScaleY(UIConstants.SCALE_NORMAL);
mIvCropPreview.setTranslationX(0); // Reset horizontal position
mIvCropPreview.setTranslationY(0); // Reset vertical position
@@ -371,7 +372,7 @@ public class AddPlayerActivity extends AppCompatActivity {
// Write bitmap to file as JPEG with 90% quality (good balance of quality/size)
try (FileOutputStream fos = new FileOutputStream(file)) {
bmp.compress(Bitmap.CompressFormat.JPEG, 90, fos);
bmp.compress(Bitmap.CompressFormat.JPEG, UIConstants.JPEG_QUALITY, fos);
}
// Return the absolute path for database storage

View File

@@ -15,6 +15,9 @@ import android.widget.Toast;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.content.ContextCompat;
import com.aldo.apps.ochecompanion.database.objects.Player;
import com.aldo.apps.ochecompanion.utils.CheckoutConstants;
import com.aldo.apps.ochecompanion.utils.DartsConstants;
import com.aldo.apps.ochecompanion.utils.UIConstants;
import com.google.android.material.button.MaterialButton;
import java.util.ArrayList;
import java.util.HashMap;
@@ -57,7 +60,7 @@ public class GameActivity extends AppCompatActivity {
/**
* Starting score for this X01 game (typically 501, 301, or 701).
*/
private int mStartingScore = 501;
private int mStartingScore = DartsConstants.DEFAULT_GAME_SCORE;
/**
* List of player game states, one per participant.
@@ -162,7 +165,7 @@ public class GameActivity extends AppCompatActivity {
setContentView(R.layout.activity_game);
// Extract game parameters from intent
mStartingScore = getIntent().getIntExtra(EXTRA_START_SCORE, 501);
mStartingScore = getIntent().getIntExtra(EXTRA_START_SCORE, DartsConstants.DEFAULT_GAME_SCORE);
ArrayList<Player> participants = getIntent().getParcelableArrayListExtra(EXTRA_PLAYERS);
// Initialize activity components in order
@@ -237,7 +240,7 @@ public class GameActivity extends AppCompatActivity {
mPlayerStates.add(new X01State("GUEST 1", mStartingScore));
}
updateUI();
setMultiplier(1);
setMultiplier(DartsConstants.MULTIPLIER_SINGLE);
}
/**
@@ -250,17 +253,19 @@ public class GameActivity extends AppCompatActivity {
if (mCurrentTurnDarts.size() >= 3 || mIsTurnOver) return;
int points = baseValue * mMultiplier;
if (baseValue == 25 && mMultiplier == 3) points = 50; // Triple Bull is Double Bull
if (baseValue == DartsConstants.BULL_VALUE && mMultiplier == DartsConstants.MULTIPLIER_TRIPLE) {
points = DartsConstants.DOUBLE_BULL_VALUE; // Triple Bull is Double Bull
}
X01State active = mPlayerStates.get(mActivePlayerIndex);
int scoreBeforeDart = active.remainingScore;
for (int d : mCurrentTurnDarts) scoreBeforeDart -= d;
int scoreAfterDart = scoreBeforeDart - points;
boolean isDouble = (mMultiplier == 2) || (points == 50);
boolean isDouble = (mMultiplier == DartsConstants.MULTIPLIER_DOUBLE) || (points == DartsConstants.DOUBLE_BULL_VALUE);
// --- DOUBLE OUT LOGIC CHECK ---
if (scoreAfterDart < 0 || scoreAfterDart == 1 || (scoreAfterDart == 0 && !isDouble)) {
if (scoreAfterDart < 0 || scoreAfterDart == DartsConstants.BUST_SCORE || (scoreAfterDart == 0 && !isDouble)) {
// BUST CONDITION: Score < 0, Score == 1, or Score == 0 on a non-double
mCurrentTurnDarts.add(points);
updateTurnIndicators();
@@ -279,12 +284,12 @@ public class GameActivity extends AppCompatActivity {
updateTurnIndicators();
updateUI();
if (mCurrentTurnDarts.size() == 3) {
if (mCurrentTurnDarts.size() == DartsConstants.MAX_DARTS_PER_TURN) {
mIsTurnOver = true;
}
}
setMultiplier(1);
setMultiplier(DartsConstants.MULTIPLIER_SINGLE);
}
/**
@@ -293,7 +298,7 @@ public class GameActivity extends AppCompatActivity {
* @param v The clicked View (Bull button)
*/
public void onBullTap(final View v) {
onNumberTap(25);
onNumberTap(DartsConstants.BULL_VALUE);
}
/**
@@ -303,21 +308,21 @@ public class GameActivity extends AppCompatActivity {
*/
private void setMultiplier(final int m) {
mMultiplier = m;
btnSingle.setAlpha(m == 1 ? 1.0f : 0.4f);
btnDouble.setAlpha(m == 2 ? 1.0f : 0.4f);
btnTriple.setAlpha(m == 3 ? 1.0f : 0.4f);
btnSingle.setAlpha(m == DartsConstants.MULTIPLIER_SINGLE ? UIConstants.ALPHA_FULL : UIConstants.ALPHA_INACTIVE);
btnDouble.setAlpha(m == DartsConstants.MULTIPLIER_DOUBLE ? UIConstants.ALPHA_FULL : UIConstants.ALPHA_INACTIVE);
btnTriple.setAlpha(m == DartsConstants.MULTIPLIER_TRIPLE ? UIConstants.ALPHA_FULL : UIConstants.ALPHA_INACTIVE);
btnSingle.setBackgroundResource(m == 1 ? R.drawable.shape_multiplier_active : 0);
btnDouble.setBackgroundResource(m == 2 ? R.drawable.shape_multiplier_red : 0);
btnTriple.setBackgroundResource(m == 3 ? R.drawable.shape_multiplier_blue : 0);
btnSingle.setBackgroundResource(m == DartsConstants.MULTIPLIER_SINGLE ? R.drawable.shape_multiplier_active : 0);
btnDouble.setBackgroundResource(m == DartsConstants.MULTIPLIER_DOUBLE ? R.drawable.shape_multiplier_red : 0);
btnTriple.setBackgroundResource(m == DartsConstants.MULTIPLIER_TRIPLE ? R.drawable.shape_multiplier_blue : 0);
int bgColor, textColor, strokeColor;
if (m == 3) {
bgColor = Color.parseColor("#1A007AFF");
if (m == DartsConstants.MULTIPLIER_TRIPLE) {
bgColor = Color.parseColor(UIConstants.COLOR_BG_VALID);
textColor = ContextCompat.getColor(this, R.color.triple_blue);
strokeColor = textColor;
} else if (m == 2) {
bgColor = Color.parseColor("#1AFF3B30");
} else if (m == DartsConstants.MULTIPLIER_DOUBLE) {
bgColor = Color.parseColor(UIConstants.COLOR_BG_BUST);
textColor = ContextCompat.getColor(this, R.color.double_red);
strokeColor = textColor;
} else {
@@ -441,15 +446,15 @@ public class GameActivity extends AppCompatActivity {
* @param dartsLeft Number of darts remaining (0-3)
*/
private void updateCheckoutSuggestion(final int score, final int dartsLeft) {
if (score <= 170 && score > 1 && dartsLeft > 0) {
if (score <= DartsConstants.MAX_CHECKOUT_SCORE && score > DartsConstants.BUST_SCORE && dartsLeft > 0) {
String route = CheckoutEngine.getRoute(score, dartsLeft);
if (route != null) {
layoutCheckoutSuggestion.setVisibility(View.VISIBLE);
tvCheckout.setText(route);
Animation pulse = new AlphaAnimation(0.5f, 1.0f);
pulse.setDuration(1000);
Animation pulse = new AlphaAnimation(UIConstants.ALPHA_PULSE_MIN, UIConstants.ALPHA_PULSE_MAX);
pulse.setDuration(UIConstants.ANIMATION_PULSE_DURATION);
pulse.setRepeatMode(Animation.REVERSE);
pulse.setRepeatCount(Animation.INFINITE);
layoutCheckoutSuggestion.startAnimation(pulse);
@@ -486,8 +491,8 @@ public class GameActivity extends AppCompatActivity {
* @return Display label for UI
*/
private String getDartLabel(final int score) {
if (score == 50) return "DB"; // Double Bull / Bullseye
if (score == 25) return "B"; // Single Bull
if (score == DartsConstants.DOUBLE_BULL_VALUE) return DartsConstants.LABEL_DOUBLE_BULL; // Double Bull / Bullseye
if (score == DartsConstants.BULL_VALUE) return DartsConstants.LABEL_BULL; // Single Bull
// Return numeric value for all other scores
return String.valueOf(score);
}
@@ -585,21 +590,6 @@ public class GameActivity extends AppCompatActivity {
* @see #getRoute(int, int)
*/
private static class CheckoutEngine {
/**
* Pre-calculated checkout routes for classic finishes.
*/
private static final Map<Integer, String[]> checkoutMap = new HashMap<>();
// Initialize pre-calculated checkout routes
static {
checkoutMap.put(170, new String[]{"T20", "T20", "BULL"}); // Max 3-dart checkout
checkoutMap.put(141, new String[]{"T20", "T19", "D12"}); // Common high finish
// ... add more fixed routes as needed
// Examples to add:
// checkoutMap.put(167, new String[]{"T20", "T19", "BULL"});
// checkoutMap.put(164, new String[]{"T20", "T18", "BULL"});
// checkoutMap.put(160, new String[]{"T20", "T20", "D20"});
}
/**
* Returns optimal checkout route for given score and darts remaining.
@@ -611,8 +601,10 @@ public class GameActivity extends AppCompatActivity {
*/
public static String getRoute(final int score, final int dartsLeft) {
// 1. Direct Out check (highest priority)
if (score <= 40 && score % 2 == 0) return "D" + (score / 2);
if (score == 50) return "BULL";
if (score <= DartsConstants.MAX_DIRECT_DOUBLE && score % 2 == 0) {
return DartsConstants.PREFIX_DOUBLE + (score / 2);
}
if (score == DartsConstants.DOUBLE_BULL_VALUE) return DartsConstants.LABEL_BULLSEYE;
// 2. Logic for Setup Darts (preventing score of 1)
if (dartsLeft >= 2) {
@@ -620,24 +612,30 @@ public class GameActivity extends AppCompatActivity {
// Suggesting 1 leaves 6 (D3). Correct.
if (score <= 41 && score % 2 != 0) {
// Try to leave a common double (32, 40, 16)
if (score - 32 > 0 && score - 32 <= 20) return (score - 32) + " • D16";
if (score - 40 > 0 && score - 40 <= 20) return (score - 40) + " • D20";
return "1 • D" + ((score - 1) / 2); // Default setup
if (score - DartsConstants.SETUP_DOUBLE_32 > 0 && score - DartsConstants.SETUP_DOUBLE_32 <= DartsConstants.MAX_DARTBOARD_NUMBER) {
return (score - DartsConstants.SETUP_DOUBLE_32) + DartsConstants.CHECKOUT_SEPARATOR + DartsConstants.PREFIX_DOUBLE + "16";
}
if (score - DartsConstants.SETUP_DOUBLE_40 > 0 && score - DartsConstants.SETUP_DOUBLE_40 <= DartsConstants.MAX_DARTBOARD_NUMBER) {
return (score - DartsConstants.SETUP_DOUBLE_40) + DartsConstants.CHECKOUT_SEPARATOR + DartsConstants.PREFIX_DOUBLE + "20";
}
return "1" + DartsConstants.CHECKOUT_SEPARATOR + DartsConstants.PREFIX_DOUBLE + ((score - 1) / 2); // Default setup
}
}
// 3. Fallback to Map or High Scoring Route
if (checkoutMap.containsKey(score) && checkoutMap.get(score).length <= dartsLeft) {
String[] parts = checkoutMap.get(score);
StringBuilder sb = new StringBuilder();
for (int i = 0; i < parts.length; i++) {
sb.append(parts[i]);
if (i < parts.length - 1) sb.append("");
if (CheckoutConstants.hasCheckoutRoute(score)) {
String[] parts = CheckoutConstants.getCheckoutRoute(score);
if (parts != null && parts.length <= dartsLeft) {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < parts.length; i++) {
sb.append(parts[i]);
if (i < parts.length - 1) sb.append(DartsConstants.CHECKOUT_SEPARATOR);
}
return sb.toString();
}
return sb.toString();
}
if (score > 60) return "T20 Route";
if (score > DartsConstants.HIGH_SCORE_THRESHOLD) return DartsConstants.LABEL_T20_ROUTE;
return null;
}
}

View File

@@ -19,6 +19,8 @@ import com.aldo.apps.ochecompanion.database.objects.Player;
import com.aldo.apps.ochecompanion.models.Match;
import com.aldo.apps.ochecompanion.ui.MatchRecapView;
import com.aldo.apps.ochecompanion.ui.adapter.MainMenuPlayerAdapter;
import com.aldo.apps.ochecompanion.utils.DartsConstants;
import com.aldo.apps.ochecompanion.utils.UIConstants;
import java.util.ArrayList;
import java.util.List;
@@ -97,13 +99,13 @@ public class MainMenuActivity extends AppCompatActivity {
* Test players are not persisted to the database.
*/
private void quickStart() {
final Player playerOne = new Player("Test1", null);
final Player playerTwo = new Player("Test2", null);
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, 501);
GameActivity.start(MainMenuActivity.this, players, DartsConstants.DEFAULT_GAME_SCORE);
}
/**
@@ -153,20 +155,20 @@ public class MainMenuActivity extends AppCompatActivity {
*/
private void applyTestData(final int counter) {
// Create test player objects
final Player playerOne = new Player("Test1", null);
final Player playerTwo = new Player("Test2", null);
final Player playerThree = new Player("Test3", null);
final Player playerFour = new Player("Test4", null);
final Player playerOne = new Player(DartsConstants.TEST_PLAYER_1, null);
final Player playerTwo = new Player(DartsConstants.TEST_PLAYER_2, null);
final Player playerThree = new Player(DartsConstants.TEST_PLAYER_3, null);
final Player playerFour = new Player(DartsConstants.TEST_PLAYER_4, null);
// Create test match objects with different player configurations
final Match match1on1 = new Match(playerOne, playerTwo);
final Match matchGroup = new Match(playerOne, playerTwo, playerThree, playerFour);
// Cycle through different test scenarios based on counter value
if (counter % 3 == 0) {
if (counter % UIConstants.TEST_CYCLE_MODULO == 0) {
// Scenario 1: No match (null state)
mMatchRecap.setMatch(null);
} else if (counter % 3 == 1) {
} else if (counter % UIConstants.TEST_CYCLE_MODULO == 1) {
// Scenario 2: 1v1 match (two players)
mMatchRecap.setMatch(match1on1);
} else {

View File

@@ -9,6 +9,7 @@ import android.graphics.RectF;
import android.util.AttributeSet;
import android.view.View;
import androidx.annotation.Nullable;
import com.aldo.apps.ochecompanion.utils.UIConstants;
/**
* Visual cropping guide overlay with semi-transparent mask and center crop window.
@@ -48,7 +49,7 @@ public class CropOverlayView extends View {
/** Initializes paint with Midnight Black at 85% opacity. */
private void init() {
mMaskPaint.setColor(Color.parseColor("#D90A0A0A"));
mMaskPaint.setColor(Color.parseColor(UIConstants.COLOR_MASK_OVERLAY));
mMaskPaint.setStyle(Paint.Style.FILL);
}
@@ -57,7 +58,7 @@ public class CropOverlayView extends View {
protected void onLayout(final boolean changed, final int left, final int top, final int right, final int bottom) {
super.onLayout(changed, left, top, right, bottom);
mBoxSize = getWidth() * 0.8f;
mBoxSize = getWidth() * UIConstants.CROP_BOX_SIZE_RATIO;
final float l = (getWidth() - mBoxSize) / 2;
final float t = (getHeight() - mBoxSize) / 2;
mCropRect.set(l, t, l + mBoxSize, t + mBoxSize);

View File

@@ -0,0 +1,98 @@
package com.aldo.apps.ochecompanion.utils;
import java.util.HashMap;
import java.util.Map;
/**
* Pre-calculated checkout routes for classic darts finishes.
* Contains standard 3-dart checkout combinations for common scores.
*/
public final class CheckoutConstants {
// Prevent instantiation
private CheckoutConstants() {
throw new UnsupportedOperationException("Constants class cannot be instantiated");
}
/**
* Map of pre-calculated checkout routes.
* Key: Target score, Value: Array of dart notations to achieve the checkout
*/
private static final Map<Integer, String[]> CHECKOUT_MAP = new HashMap<>();
static {
// Maximum 3-dart checkouts
CHECKOUT_MAP.put(170, new String[]{"T20", "T20", "BULL"});
CHECKOUT_MAP.put(167, new String[]{"T20", "T19", "BULL"});
CHECKOUT_MAP.put(164, new String[]{"T20", "T18", "BULL"});
CHECKOUT_MAP.put(161, new String[]{"T20", "T17", "BULL"});
CHECKOUT_MAP.put(160, new String[]{"T20", "T20", "D20"});
// Common high finishes
CHECKOUT_MAP.put(141, new String[]{"T20", "T19", "D12"});
CHECKOUT_MAP.put(140, new String[]{"T20", "T20", "D10"});
CHECKOUT_MAP.put(139, new String[]{"T20", "T19", "D11"});
CHECKOUT_MAP.put(138, new String[]{"T20", "T18", "D12"});
CHECKOUT_MAP.put(137, new String[]{"T20", "T19", "D10"});
CHECKOUT_MAP.put(136, new String[]{"T20", "T20", "D8"});
CHECKOUT_MAP.put(135, new String[]{"T20", "T17", "D12"});
CHECKOUT_MAP.put(134, new String[]{"T20", "T14", "D16"});
CHECKOUT_MAP.put(133, new String[]{"T20", "T19", "D8"});
CHECKOUT_MAP.put(132, new String[]{"T20", "T16", "D12"});
CHECKOUT_MAP.put(131, new String[]{"T20", "T13", "D16"});
CHECKOUT_MAP.put(130, new String[]{"T20", "T20", "D5"});
// Mid-range finishes
CHECKOUT_MAP.put(121, new String[]{"T17", "T18", "D10"});
CHECKOUT_MAP.put(120, new String[]{"T20", "20", "D20"});
CHECKOUT_MAP.put(119, new String[]{"T19", "T12", "D13"});
CHECKOUT_MAP.put(118, new String[]{"T18", "T14", "D14"});
CHECKOUT_MAP.put(117, new String[]{"T20", "17", "D20"});
CHECKOUT_MAP.put(116, new String[]{"T19", "19", "D20"});
CHECKOUT_MAP.put(115, new String[]{"T19", "18", "D20"});
CHECKOUT_MAP.put(114, new String[]{"T18", "18", "D20"});
CHECKOUT_MAP.put(113, new String[]{"T19", "16", "D20"});
CHECKOUT_MAP.put(112, new String[]{"T20", "12", "D20"});
CHECKOUT_MAP.put(111, new String[]{"T20", "11", "D20"});
CHECKOUT_MAP.put(110, new String[]{"T20", "10", "D20"});
// Lower finishes
CHECKOUT_MAP.put(107, new String[]{"T19", "10", "D20"});
CHECKOUT_MAP.put(106, new String[]{"T20", "10", "D18"});
CHECKOUT_MAP.put(105, new String[]{"T20", "13", "D16"});
CHECKOUT_MAP.put(104, new String[]{"T18", "18", "D16"});
CHECKOUT_MAP.put(103, new String[]{"T17", "12", "D20"});
CHECKOUT_MAP.put(102, new String[]{"T20", "10", "D16"});
CHECKOUT_MAP.put(101, new String[]{"T17", "10", "D20"});
CHECKOUT_MAP.put(100, new String[]{"T20", "D20"});
}
/**
* Gets the pre-calculated checkout route for a given score.
*
* @param score The target score to checkout
* @return Array of dart notations, or null if no route exists
*/
public static String[] getCheckoutRoute(final int score) {
return CHECKOUT_MAP.get(score);
}
/**
* Checks if a pre-calculated checkout route exists for the given score.
*
* @param score The target score
* @return true if a route exists, false otherwise
*/
public static boolean hasCheckoutRoute(final int score) {
return CHECKOUT_MAP.containsKey(score);
}
/**
* Gets all available pre-calculated checkout scores.
*
* @return Set of scores with pre-calculated routes
*/
public static java.util.Set<Integer> getAvailableCheckouts() {
return CHECKOUT_MAP.keySet();
}
}

View File

@@ -0,0 +1,131 @@
package com.aldo.apps.ochecompanion.utils;
/**
* Central constants for darts game logic and scoring.
* Contains standard X01 game values, dart scores, and checkout limits.
*/
public final class DartsConstants {
// Prevent instantiation
private DartsConstants() {
throw new UnsupportedOperationException("Constants class cannot be instantiated");
}
// ========================================================================================
// X01 Game Scores
// ========================================================================================
/** Standard 501 game starting score */
public static final int GAME_SCORE_501 = 501;
/** Standard 301 game starting score */
public static final int GAME_SCORE_301 = 301;
/** Standard 701 game starting score */
public static final int GAME_SCORE_701 = 701;
/** Default game score when not specified */
public static final int DEFAULT_GAME_SCORE = GAME_SCORE_501;
// ========================================================================================
// Dart Values
// ========================================================================================
/** Bull/Single bull value */
public static final int BULL_VALUE = 25;
/** Double bull/Bullseye value */
public static final int DOUBLE_BULL_VALUE = 50;
/** Maximum number on dartboard */
public static final int MAX_DARTBOARD_NUMBER = 20;
/** Minimum dartboard number */
public static final int MIN_DARTBOARD_NUMBER = 1;
// ========================================================================================
// Multipliers
// ========================================================================================
/** Single multiplier */
public static final int MULTIPLIER_SINGLE = 1;
/** Double multiplier */
public static final int MULTIPLIER_DOUBLE = 2;
/** Triple multiplier */
public static final int MULTIPLIER_TRIPLE = 3;
// ========================================================================================
// Checkout Constants
// ========================================================================================
/** Maximum possible checkout score in darts */
public static final int MAX_CHECKOUT_SCORE = 170;
/** Minimum valid checkout score */
public static final int MIN_CHECKOUT_SCORE = 2;
/** Score that results in bust (impossible to finish) */
public static final int BUST_SCORE = 1;
// ========================================================================================
// Game Limits
// ========================================================================================
/** Maximum darts per turn */
public static final int MAX_DARTS_PER_TURN = 3;
/** Maximum common double value (D20 = 40) */
public static final int MAX_DIRECT_DOUBLE = 40;
/** Common setup double target (D16 = 32) */
public static final int SETUP_DOUBLE_32 = 32;
/** Common setup double target (D20 = 40) */
public static final int SETUP_DOUBLE_40 = 40;
/** High score threshold for "T20 Route" suggestion */
public static final int HIGH_SCORE_THRESHOLD = 60;
// ========================================================================================
// Display Labels
// ========================================================================================
/** Label for double bull/bullseye */
public static final String LABEL_DOUBLE_BULL = "DB";
/** Label for single bull */
public static final String LABEL_BULL = "B";
/** Label for bullseye finish */
public static final String LABEL_BULLSEYE = "BULL";
/** Separator for checkout route steps */
public static final String CHECKOUT_SEPARATOR = "";
/** Generic high score route label */
public static final String LABEL_T20_ROUTE = "T20 Route";
/** Prefix for double notation */
public static final String PREFIX_DOUBLE = "D";
/** Prefix for triple notation */
public static final String PREFIX_TRIPLE = "T";
// ========================================================================================
// Test/Debug Values
// ========================================================================================
/** Test player 1 name */
public static final String TEST_PLAYER_1 = "Test1";
/** Test player 2 name */
public static final String TEST_PLAYER_2 = "Test2";
/** Test player 3 name */
public static final String TEST_PLAYER_3 = "Test3";
/** Test player 4 name */
public static final String TEST_PLAYER_4 = "Test4";
}

View File

@@ -0,0 +1,80 @@
package com.aldo.apps.ochecompanion.utils;
import android.content.Context;
import android.graphics.drawable.Drawable;
import androidx.annotation.ColorInt;
import androidx.annotation.ColorRes;
import androidx.annotation.DrawableRes;
import androidx.annotation.StringRes;
import androidx.core.content.ContextCompat;
/**
* Helper class for accessing Android resources (colors, drawables, strings).
* Provides convenient methods to extract resources with proper context handling.
*/
public final class ResourceHelper {
// Prevent instantiation
private ResourceHelper() {
throw new UnsupportedOperationException("Utility class cannot be instantiated");
}
// ========================================================================================
// Color Resources
// ========================================================================================
/**
* Gets a color value from resources.
*
* @param context Application or activity context
* @param colorResId Color resource ID (e.g., R.color.volt_green)
* @return Resolved color integer value
*/
@ColorInt
public static int getColor(final Context context, @ColorRes final int colorResId) {
return ContextCompat.getColor(context, colorResId);
}
// ========================================================================================
// Drawable Resources
// ========================================================================================
/**
* Gets a drawable from resources.
*
* @param context Application or activity context
* @param drawableResId Drawable resource ID (e.g., R.drawable.ic_users)
* @return Drawable object or null if not found
*/
public static Drawable getDrawable(final Context context, @DrawableRes final int drawableResId) {
return ContextCompat.getDrawable(context, drawableResId);
}
// ========================================================================================
// String Resources
// ========================================================================================
/**
* Gets a string from resources.
*
* @param context Application or activity context
* @param stringResId String resource ID (e.g., R.string.app_name)
* @return String value
*/
public static String getString(final Context context, @StringRes final int stringResId) {
return context.getString(stringResId);
}
/**
* Gets a formatted string from resources with arguments.
*
* @param context Application or activity context
* @param stringResId String resource ID with format specifiers
* @param formatArgs Arguments to format into the string
* @return Formatted string value
*/
public static String getString(final Context context, @StringRes final int stringResId,
final Object... formatArgs) {
return context.getString(stringResId, formatArgs);
}
}

View File

@@ -0,0 +1,78 @@
package com.aldo.apps.ochecompanion.utils;
/**
* UI-related constants for animations, opacity, scaling, and durations.
*/
public final class UIConstants {
// Prevent instantiation
private UIConstants() {
throw new UnsupportedOperationException("Constants class cannot be instantiated");
}
// ========================================================================================
// Alpha/Opacity Values
// ========================================================================================
/** Full opacity */
public static final float ALPHA_FULL = 1.0f;
/** Inactive/dimmed opacity */
public static final float ALPHA_INACTIVE = 0.4f;
/** Pulse animation minimum alpha */
public static final float ALPHA_PULSE_MIN = 0.5f;
/** Pulse animation maximum alpha */
public static final float ALPHA_PULSE_MAX = 1.0f;
// ========================================================================================
// Scale/Zoom Values
// ========================================================================================
/** Normal scale (100%) */
public static final float SCALE_NORMAL = 1.0f;
/** Minimum zoom scale for image cropping (10%) */
public static final float SCALE_MIN_ZOOM = 0.1f;
/** Maximum zoom scale for image cropping (1000%) */
public static final float SCALE_MAX_ZOOM = 10.0f;
/** Crop box size as percentage of view width */
public static final float CROP_BOX_SIZE_RATIO = 0.8f;
// ========================================================================================
// Animation Durations
// ========================================================================================
/** Pulse animation duration in milliseconds */
public static final int ANIMATION_PULSE_DURATION = 1000;
// ========================================================================================
// Modulo Values for Cycling
// ========================================================================================
/** Modulo for test data cycling (3 states: null, 1v1, group) */
public static final int TEST_CYCLE_MODULO = 3;
// ========================================================================================
// Color Values (Hex Codes)
// ========================================================================================
/** Semi-transparent Midnight Black (85% opacity) for crop overlay mask */
public static final String COLOR_MASK_OVERLAY = "#D90A0A0A";
/** Light Blue background tint (10% opacity) for valid/success state */
public static final String COLOR_BG_VALID = "#1A007AFF";
/** Light Red background tint (10% opacity) for bust/error state */
public static final String COLOR_BG_BUST = "#1AFF3B30";
// ========================================================================================
// Quality/Compression Values
// ========================================================================================
/** JPEG compression quality (0-100) for profile images */
public static final int JPEG_QUALITY = 90;
}