diff --git a/README.md b/README.md index 2a67a0f..15b03d7 100644 --- a/README.md +++ b/README.md @@ -102,119 +102,6 @@ The game features a comprehensive scoring system: - Follows Material Design guidelines - Implements high score persistence using SharedPreferences -## Project Improvements and Best Practices - -### Performance Optimizations - -The codebase includes several performance optimizations: - -1. **Release Build Configuration** - - Minification enabled to reduce APK size - - Resource shrinking to remove unused resources - - ProGuard rules to optimize while preserving critical classes - -2. **Memory Management** - - Proper lifecycle handling to prevent memory leaks - - Resource cleanup through `releaseResources()` methods - - Efficient bitmap handling with reuse when possible - -3. **Rendering Efficiency** - - Custom view invalidation limited to areas that need updating - - Hardware acceleration for canvas operations - - Bitmap caching for frequently used graphics - -### Code Organization - -The codebase follows good architecture practices: - -1. **Package Structure** - - `model`: Data classes and game logic - - `game`: Core gameplay implementation - - `ui`: User interface components - - `audio`: Sound and music management - - `accessibility`: Accessibility helpers - -2. **Responsibility Separation** - - `GameLifecycleManager`: Handles lifecycle events - - `GameUIManager`: Manages UI state and updates - - `GameAccessibilityHelper`: Improves accessibility features - - `GamepadController`: Manages gamepad input - -### Google Play Compliance - -The app meets Google Play standards: - -1. **Manifest Configuration** - - Proper permissions declaration - - Screen orientation handling - - Full backup rules for user data - -2. **Accessibility Support** - - Content descriptions for UI elements - - Color contrast considerations - - Screen reader compatibility - -3. **Shortcuts and Deep Links** - - App shortcuts for quick actions - - Proper intent handling - -### Usage Examples - -#### Lifecycle Management - -```kotlin -// In your activity -private lateinit var lifecycleManager: GameLifecycleManager - -override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - lifecycleManager = GameLifecycleManager(this) -} - -override fun onPause() { - super.onPause() - lifecycleManager.onPause(gameView, gameMusic, statsManager, highScoreManager) -} - -override fun onResume() { - super.onResume() - lifecycleManager.onResume(gameView, gameMusic, isMusicEnabled) -} - -override fun onDestroy() { - lifecycleManager.onDestroy(gameView, gameMusic, statsManager, highScoreManager) - super.onDestroy() -} -``` - -#### Accessibility Implementation - -```kotlin -// In your activity -private lateinit var accessibilityHelper: GameAccessibilityHelper - -override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - accessibilityHelper = GameAccessibilityHelper(this) - - // Setup accessibility descriptions for controls - accessibilityHelper.setupAccessibilityDescriptions( - leftButton, rightButton, rotateButton, dropButton, - holdButton, pauseButton, gameView, holdPieceView, nextPieceView - ) -} - -// When level changes -private fun onLevelUp(newLevel: Int) { - accessibilityHelper.announceLevelUp(gameView, newLevel) -} - -// When game ends -private fun onGameOver(score: Long) { - accessibilityHelper.announceGameOver(gameView, score) -} -``` - ## Building from Source 1. Clone the repository: diff --git a/app/build.gradle b/app/build.gradle index 21d6285..da55975 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -19,8 +19,7 @@ android { buildTypes { release { - minifyEnabled true - shrinkResources true + minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' } } @@ -39,15 +38,6 @@ android { } } -// Enable strict mode for debug builds -android.applicationVariants.all { variant -> - if (variant.buildType.name == "debug") { - variant.mergedFlavor.manifestPlaceholders = [enableStrictMode: "true"] - } else { - variant.mergedFlavor.manifestPlaceholders = [enableStrictMode: "false"] - } -} - dependencies { implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version" implementation 'androidx.core:core-ktx:1.12.0' diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro index 403e50d..e64cabd 100644 --- a/app/proguard-rules.pro +++ b/app/proguard-rules.pro @@ -8,39 +8,6 @@ # Keep models intact -keep class com.mintris.model.** { *; } -# Keep game classes intact to prevent issues --keep class com.mintris.game.** { *; } - -# Preserve critical classes that might be used through reflection --keep class com.mintris.audio.GameMusic { *; } --keep class com.mintris.ui.** { *; } - -# Keep all public methods in the MainActivity --keepclassmembers class com.mintris.MainActivity { - public *; -} - -# Keep serializable and parcelable classes for proper game state saving --keepnames class * implements java.io.Serializable --keepclassmembers class * implements java.io.Serializable { - static final long serialVersionUID; - private static final java.io.ObjectStreamField[] serialPersistentFields; - !static !transient ; - private void writeObject(java.io.ObjectOutputStream); - private void readObject(java.io.ObjectInputStream); - java.lang.Object writeReplace(); - java.lang.Object readResolve(); -} - -# Preserve line number information for debugging stack traces --keepattributes SourceFile,LineNumberTable - -# Keep Gson usage intact --keep class com.google.gson.** { *; } --keep class * implements com.google.gson.TypeAdapterFactory --keep class * implements com.google.gson.JsonSerializer --keep class * implements com.google.gson.JsonDeserializer - # Uncomment this to preserve the line number information for # debugging stack traces. #-keepattributes SourceFile,LineNumberTable diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 3322422..8815afb 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -7,7 +7,6 @@ + android:excludeFromRecents="false"> - + android:exported="false" /> \ No newline at end of file diff --git a/app/src/main/java/com/mintris/MainActivity.kt b/app/src/main/java/com/mintris/MainActivity.kt index 22f2ea8..ff56ed5 100644 --- a/app/src/main/java/com/mintris/MainActivity.kt +++ b/app/src/main/java/com/mintris/MainActivity.kt @@ -267,9 +267,6 @@ class MainActivity : AppCompatActivity(), gameBoard.onPieceMove = { binding.holdPieceView.invalidate() } - gameBoard.onPiecePlaced = { - piecesPlaced++ - } // Set up music toggle binding.musicToggle.setOnClickListener { diff --git a/app/src/main/java/com/mintris/accessibility/GameAccessibilityHelper.kt b/app/src/main/java/com/mintris/accessibility/GameAccessibilityHelper.kt deleted file mode 100644 index 450e47f..0000000 --- a/app/src/main/java/com/mintris/accessibility/GameAccessibilityHelper.kt +++ /dev/null @@ -1,137 +0,0 @@ -package com.mintris.accessibility - -import android.content.Context -import android.os.Build -import android.view.View -import android.view.accessibility.AccessibilityEvent -import android.view.accessibility.AccessibilityManager -import android.widget.ImageButton -import android.widget.TextView -import androidx.core.view.ViewCompat -import com.mintris.R -import com.mintris.game.GameView -import com.mintris.model.TetrominoType - -/** - * Helper class to improve the game's accessibility for users with visual impairments - * or other accessibility needs. - */ -class GameAccessibilityHelper(private val context: Context) { - - private val accessibilityManager = context.getSystemService(Context.ACCESSIBILITY_SERVICE) as AccessibilityManager - - /** - * Sets up accessibility content descriptions for game controls - */ - fun setupAccessibilityDescriptions( - leftButton: ImageButton?, - rightButton: ImageButton?, - rotateButton: ImageButton?, - dropButton: ImageButton?, - holdButton: ImageButton?, - pauseButton: ImageButton?, - gameView: GameView?, - holdPieceView: View?, - nextPieceView: View? - ) { - // Set content descriptions for all control buttons - leftButton?.contentDescription = context.getString(R.string.accessibility_move_left) - rightButton?.contentDescription = context.getString(R.string.accessibility_move_right) - rotateButton?.contentDescription = context.getString(R.string.accessibility_rotate_piece) - dropButton?.contentDescription = context.getString(R.string.accessibility_drop_piece) - holdButton?.contentDescription = context.getString(R.string.accessibility_hold_piece) - pauseButton?.contentDescription = context.getString(R.string.accessibility_pause_game) - - // Set content descriptions for game views - gameView?.contentDescription = context.getString(R.string.accessibility_game_board) - holdPieceView?.contentDescription = context.getString(R.string.accessibility_held_piece) - nextPieceView?.contentDescription = context.getString(R.string.accessibility_next_piece) - - // Add accessibility delegate to the game view for custom events - gameView?.let { - ViewCompat.setAccessibilityDelegate(it, object : androidx.core.view.AccessibilityDelegateCompat() { - override fun onPopulateAccessibilityEvent(host: View, event: AccessibilityEvent) { - super.onPopulateAccessibilityEvent(host, event) - - // Add custom content to accessibility events if needed - if (event.eventType == AccessibilityEvent.TYPE_VIEW_SELECTED) { - // Example: announce the current piece type - event.text.add("Current piece: ${getCurrentPieceDescription(it)}") - } - } - }) - } - } - - /** - * Announces important game events via accessibility services - */ - fun announceGameEvent(view: View, message: String) { - if (accessibilityManager.isEnabled) { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { - view.announceForAccessibility(message) - } else { - // For older Android versions - val event = AccessibilityEvent.obtain(AccessibilityEvent.TYPE_ANNOUNCEMENT) - event.text.add(message) - view.sendAccessibilityEvent(AccessibilityEvent.TYPE_ANNOUNCEMENT) - } - } - } - - /** - * Updates score and level information to be more accessible - */ - fun updateGameStatusAccessibility( - scoreTextView: TextView?, - levelTextView: TextView?, - linesTextView: TextView?, - score: Long, - level: Int, - lines: Int - ) { - // Make sure score is accessible to screen readers with proper formatting - scoreTextView?.let { - it.contentDescription = "Score: $score points" - } - - levelTextView?.let { - it.contentDescription = "Level: $level" - } - - linesTextView?.let { - it.contentDescription = "Lines cleared: $lines" - } - } - - /** - * Get a description of the current Tetromino piece for accessibility announcements - */ - private fun getCurrentPieceDescription(gameView: GameView): String { - val pieceType = gameView.getCurrentPieceType() ?: return "No piece" - - return when (pieceType) { - TetrominoType.I -> "I piece, long bar" - TetrominoType.J -> "J piece, hook shape pointing left" - TetrominoType.L -> "L piece, hook shape pointing right" - TetrominoType.O -> "O piece, square shape" - TetrominoType.S -> "S piece, zigzag shape" - TetrominoType.T -> "T piece, T shape" - TetrominoType.Z -> "Z piece, reverse zigzag shape" - } - } - - /** - * Announce the game over event with final score - */ - fun announceGameOver(view: View, score: Long) { - announceGameEvent(view, "Game over. Final score: $score points") - } - - /** - * Announce level up - */ - fun announceLevelUp(view: View, newLevel: Int) { - announceGameEvent(view, "Level up! Now at level $newLevel") - } -} \ No newline at end of file diff --git a/app/src/main/java/com/mintris/audio/GameMusic.kt b/app/src/main/java/com/mintris/audio/GameMusic.kt index cca3244..32c2f6e 100644 --- a/app/src/main/java/com/mintris/audio/GameMusic.kt +++ b/app/src/main/java/com/mintris/audio/GameMusic.kt @@ -148,25 +148,16 @@ class GameMusic(private val context: Context) { fun isEnabled(): Boolean = isEnabled - /** - * Releases all media player resources to prevent memory leaks - */ - fun releaseResources() { + fun release() { try { - Log.d("GameMusic", "Releasing MediaPlayer resources") + Log.d("GameMusic", "Releasing MediaPlayer") mediaPlayer?.release() gameOverPlayer?.release() mediaPlayer = null gameOverPlayer = null isPrepared = false } catch (e: Exception) { - Log.e("GameMusic", "Error releasing music resources: ${e.message}") + Log.e("GameMusic", "Error releasing music: ${e.message}") } } - - // Keeping old method for backward compatibility - @Deprecated("Use releaseResources() instead", ReplaceWith("releaseResources()")) - fun release() { - releaseResources() - } } \ No newline at end of file diff --git a/app/src/main/java/com/mintris/game/GameLifecycleManager.kt b/app/src/main/java/com/mintris/game/GameLifecycleManager.kt deleted file mode 100644 index 76bc96b..0000000 --- a/app/src/main/java/com/mintris/game/GameLifecycleManager.kt +++ /dev/null @@ -1,154 +0,0 @@ -package com.mintris.game - -import android.content.Context -import android.content.SharedPreferences -import android.os.Bundle -import com.google.gson.Gson -import com.mintris.audio.GameMusic -import com.mintris.model.GameBoard -import com.mintris.model.HighScoreManager -import com.mintris.model.StatsManager - -/** - * Handles the game's lifecycle events to ensure proper resource management - * and state saving/restoration. - */ -class GameLifecycleManager(private val context: Context) { - private val sharedPreferences: SharedPreferences = - context.getSharedPreferences("com.mintris.game_state", Context.MODE_PRIVATE) - private val gson = Gson() - - /** - * Save the current game state when the app is paused - */ - fun saveGameState( - gameBoard: GameBoard, - currentScore: Long, - currentLevel: Int, - piecesPlaced: Int, - gameStartTime: Long - ) { - val editor = sharedPreferences.edit() - - // Save primitive data - editor.putLong("current_score", currentScore) - editor.putInt("current_level", currentLevel) - editor.putInt("pieces_placed", piecesPlaced) - editor.putLong("game_start_time", gameStartTime) - - // Save complex GameBoard state - we don't serialize the GameBoard directly - // Instead we save key properties that can be used to recreate the board state - try { - editor.putInt("board_level", gameBoard.level) - editor.putInt("board_lines", gameBoard.lines) - editor.putInt("board_score", gameBoard.score) - } catch (e: Exception) { - // If serialization fails, just clear the saved state - editor.remove("board_level") - editor.remove("board_lines") - editor.remove("board_score") - } - - editor.apply() - } - - /** - * Restore the saved game state when the app is resumed - * Returns a bundle with the restored state or null if no state exists - */ - fun restoreGameState(): Bundle? { - // Check if we have a saved state - if (!sharedPreferences.contains("current_score")) { - return null - } - - val bundle = Bundle() - - // Restore primitive data - bundle.putLong("current_score", sharedPreferences.getLong("current_score", 0)) - bundle.putInt("current_level", sharedPreferences.getInt("current_level", 1)) - bundle.putInt("pieces_placed", sharedPreferences.getInt("pieces_placed", 0)) - bundle.putLong("game_start_time", sharedPreferences.getLong("game_start_time", 0)) - - // We don't try to deserialize the GameBoard, as it's too complex - // Instead, we'll create a new game board and apply saved properties later - bundle.putInt("board_level", sharedPreferences.getInt("board_level", 1)) - bundle.putInt("board_lines", sharedPreferences.getInt("board_lines", 0)) - bundle.putInt("board_score", sharedPreferences.getInt("board_score", 0)) - - return bundle - } - - /** - * Clears the saved game state - */ - fun clearGameState() { - sharedPreferences.edit().clear().apply() - } - - /** - * Handle activity pause - */ - fun onPause( - gameView: GameView?, - gameMusic: GameMusic?, - statsManager: StatsManager?, - highScoreManager: HighScoreManager? - ) { - // Pause the game view - gameView?.let { - if (!it.isPaused && !it.isGameOver()) { - it.pause() - } - } - - // Pause music - gameMusic?.pause() - - // Save any pending stats and scores - these methods must be made public in their respective classes - // or this functionality should be removed if those methods can't be accessed - try { - statsManager?.let { /* Call public save method if available */ } - highScoreManager?.let { /* Call public save method if available */ } - } catch (e: Exception) { - // Log error but continue - } - } - - /** - * Handle activity resume - */ - fun onResume( - gameView: GameView?, - gameMusic: GameMusic?, - isMusicEnabled: Boolean - ) { - // Resume music if enabled - if (isMusicEnabled) { - gameMusic?.resume() - } - } - - /** - * Handle activity destroy - */ - fun onDestroy( - gameView: GameView?, - gameMusic: GameMusic?, - statsManager: StatsManager?, - highScoreManager: HighScoreManager? - ) { - // Release resources - gameView?.releaseResources() - gameMusic?.releaseResources() - - // Save any pending data - these methods must be made public in their respective classes - // or this functionality should be removed if those methods can't be accessed - try { - statsManager?.let { /* Call public save method if available */ } - highScoreManager?.let { /* Call public save method if available */ } - } catch (e: Exception) { - // Log error but continue - } - } -} \ No newline at end of file diff --git a/app/src/main/java/com/mintris/game/GameView.kt b/app/src/main/java/com/mintris/game/GameView.kt index e85f665..a89ad77 100644 --- a/app/src/main/java/com/mintris/game/GameView.kt +++ b/app/src/main/java/com/mintris/game/GameView.kt @@ -1191,46 +1191,12 @@ class GameView @JvmOverloads constructor( */ fun getGameBoard(): GameBoard = gameBoard - /** - * Get the current piece type (for accessibility) - */ - fun getCurrentPieceType(): TetrominoType? { - return gameBoard.getCurrentPiece()?.type - } - /** * Clean up resources when view is detached */ override fun onDetachedFromWindow() { super.onDetachedFromWindow() handler.removeCallbacks(gameLoopRunnable) - releaseResources() - } - - /** - * Release resources to prevent memory leaks - */ - fun releaseResources() { - // Stop all animations - pulseAnimator?.cancel() - pulseAnimator?.removeAllUpdateListeners() - pulseAnimator = null - - // Stop game over animation - gameOverAnimator?.cancel() - gameOverAnimator?.removeAllUpdateListeners() - gameOverAnimator = null - - // Remove callbacks - handler.removeCallbacksAndMessages(null) - - // Clean up references - gameHaptics = null - onGameOver = null - onGameStateChanged = null - onPieceMove = null - onPieceLock = null - onLineClear = null } /** diff --git a/app/src/main/java/com/mintris/model/GameBoard.kt b/app/src/main/java/com/mintris/model/GameBoard.kt index 61bcf11..9dafdea 100644 --- a/app/src/main/java/com/mintris/model/GameBoard.kt +++ b/app/src/main/java/com/mintris/model/GameBoard.kt @@ -60,7 +60,6 @@ class GameBoard( var onPieceLock: (() -> Unit)? = null var onNextPieceChanged: (() -> Unit)? = null var onLineClear: ((Int, List) -> Unit)? = null - var onPiecePlaced: (() -> Unit)? = null // New callback for when a piece is placed // Store the last cleared lines private val lastClearedLines = mutableListOf() @@ -415,9 +414,6 @@ class GameBoard( // Trigger the piece lock vibration onPieceLock?.invoke() - // Notify that a piece was placed - onPiecePlaced?.invoke() - // Find and clear lines immediately findAndClearLines() diff --git a/app/src/main/java/com/mintris/model/PlayerProgressionManager.kt b/app/src/main/java/com/mintris/model/PlayerProgressionManager.kt index a395bb7..0ddc2a7 100644 --- a/app/src/main/java/com/mintris/model/PlayerProgressionManager.kt +++ b/app/src/main/java/com/mintris/model/PlayerProgressionManager.kt @@ -52,11 +52,10 @@ class PlayerProgressionManager(context: Context) { unlockedThemes.add(THEME_CLASSIC) } - // Always ensure default block skin is present (Level 1) - unlockedBlocks.add("block_skin_1") - - // Explicitly check and save all unlocks for the current level on load - checkAllUnlocksForCurrentLevel() + // Add default block skin if nothing is unlocked + if (unlockedBlocks.isEmpty()) { + unlockedBlocks.add("block_skin_1") + } } /** @@ -183,27 +182,11 @@ class PlayerProgressionManager(context: Context) { } } - // Check for block skin unlocks (start from skin 2 at level 7) - when (level) { - 7 -> { - if (unlockedBlocks.add("block_skin_2")) { - newRewards.add("Unlocked Neon Block Skin!") - } - } - 14 -> { - if (unlockedBlocks.add("block_skin_3")) { - newRewards.add("Unlocked Retro Block Skin!") - } - } - 21 -> { - if (unlockedBlocks.add("block_skin_4")) { - newRewards.add("Unlocked Minimalist Block Skin!") - } - } - 28 -> { - if (unlockedBlocks.add("block_skin_5")) { - newRewards.add("Unlocked Galaxy Block Skin!") - } + // Check for block skin unlocks + if (level % 7 == 0 && level <= 35) { + val blockSkin = "block_skin_${level / 7}" + if (unlockedBlocks.add(blockSkin)) { + newRewards.add("Unlocked New Block Skin!") } } @@ -222,12 +205,13 @@ class PlayerProgressionManager(context: Context) { if (playerLevel >= 20) unlockedThemes.add(THEME_MINIMALIST) if (playerLevel >= 25) unlockedThemes.add(THEME_GALAXY) - // Check block skin unlocks (start from skin 2 at level 7) - // Skin 1 is default (added in loadProgress) - if (playerLevel >= 7) unlockedBlocks.add("block_skin_2") - if (playerLevel >= 14) unlockedBlocks.add("block_skin_3") - if (playerLevel >= 21) unlockedBlocks.add("block_skin_4") - if (playerLevel >= 28) unlockedBlocks.add("block_skin_5") + // Check block skin unlocks + for (i in 1..5) { + val requiredLevel = i * 7 + if (playerLevel >= requiredLevel) { + unlockedBlocks.add("block_skin_$i") + } + } // Save any newly unlocked items saveProgress() @@ -285,7 +269,7 @@ class PlayerProgressionManager(context: Context) { // Add default theme unlockedThemes.add(THEME_CLASSIC) - // Add default block skin (Level 1) + // Add default block skin unlockedBlocks.add("block_skin_1") saveProgress() diff --git a/app/src/main/java/com/mintris/ui/BlockSkinSelector.kt b/app/src/main/java/com/mintris/ui/BlockSkinSelector.kt index d2c7d35..cf3bb9e 100644 --- a/app/src/main/java/com/mintris/ui/BlockSkinSelector.kt +++ b/app/src/main/java/com/mintris/ui/BlockSkinSelector.kt @@ -311,7 +311,7 @@ class BlockSkinSelector @JvmOverloads constructor( * Triggers the onBlockSkinSelected callback. */ fun confirmSelection() { - if (focusedSkinId == null || focusedSkinId == selectedSkin) { + if (!hasComponentFocus || focusedSkinId == null || focusedSkinId == selectedSkin) { return // No change needed } diff --git a/app/src/main/java/com/mintris/ui/GameUIManager.kt b/app/src/main/java/com/mintris/ui/GameUIManager.kt deleted file mode 100644 index 586a13e..0000000 --- a/app/src/main/java/com/mintris/ui/GameUIManager.kt +++ /dev/null @@ -1,191 +0,0 @@ -package com.mintris.ui - -import android.content.Context -import android.graphics.Color -import android.view.View -import android.widget.ImageButton -import android.widget.TextView -import com.mintris.R -import com.mintris.databinding.ActivityMainBinding -import com.mintris.model.PlayerProgressionManager -import kotlin.math.roundToInt - -/** - * Handles UI updates and state management for the game interface - * to reduce the responsibility of the MainActivity. - */ -class GameUIManager( - private val context: Context, - private val binding: ActivityMainBinding -) { - // UI state - private var currentScore = 0L - private var currentLevel = 1 - private var currentLines = 0 - - // Theme management - private var currentTheme = PlayerProgressionManager.THEME_CLASSIC - - /** - * Update the game state UI elements - */ - fun updateGameStateUI(score: Long, level: Int, lines: Int) { - // Update cached values - currentScore = score - currentLevel = level - currentLines = lines - - // Update UI - binding.scoreText.text = score.toString() - binding.currentLevelText.text = level.toString() - binding.linesText.text = lines.toString() - } - - /** - * Show the game over UI - */ - fun showGameOver(finalScore: Long) { - // Hide game UI elements - binding.gameControlsContainer.visibility = View.GONE - binding.holdPieceView.visibility = View.GONE - binding.nextPieceView.visibility = View.GONE - binding.pauseButton.visibility = View.GONE - binding.leftControlsPanel?.visibility = View.GONE - binding.rightControlsPanel?.visibility = View.GONE - - // Show game over container - binding.gameOverContainer.visibility = View.VISIBLE - binding.sessionScoreText.text = "Score: $finalScore" - } - - /** - * Hide the game over UI - */ - fun hideGameOver() { - binding.gameOverContainer.visibility = View.GONE - } - - /** - * Show the pause menu - */ - fun showPauseMenu() { - binding.pauseContainer.visibility = View.VISIBLE - } - - /** - * Hide the pause menu - */ - fun hidePauseMenu() { - binding.pauseContainer.visibility = View.GONE - } - - /** - * Update the music toggle UI based on current state - */ - fun updateMusicToggleUI(isMusicEnabled: Boolean) { - // This assumes there's a musicToggle view in the layout - // Modify as needed based on the actual UI - } - - /** - * Update the level selector display - */ - fun updateLevelSelector(selectedLevel: Int) { - // Assuming pauseLevelText is part of the LevelBadge component - // This may need to be adapted based on how the level badge works - } - - /** - * Show the game elements (views, controls) - */ - fun showGameElements() { - binding.gameView.visibility = View.VISIBLE - binding.gameControlsContainer.visibility = View.VISIBLE - binding.holdPieceView.visibility = View.VISIBLE - binding.nextPieceView.visibility = View.VISIBLE - binding.pauseButton.visibility = View.VISIBLE - - // These might not exist in the actual layout - // binding.leftControlsPanel?.visibility = View.VISIBLE - // binding.rightControlsPanel?.visibility = View.VISIBLE - } - - /** - * Hide the game elements - */ - fun hideGameElements() { - binding.gameControlsContainer.visibility = View.GONE - binding.holdPieceView.visibility = View.GONE - binding.nextPieceView.visibility = View.GONE - binding.pauseButton.visibility = View.GONE - - // These might not exist in the actual layout - // binding.leftControlsPanel?.visibility = View.GONE - // binding.rightControlsPanel?.visibility = View.GONE - } - - /** - * Apply a theme to the game UI - */ - fun applyTheme(themeId: String) { - currentTheme = themeId - - // Set background color based on theme - val backgroundColor = getThemeBackgroundColor(themeId) - binding.root.setBackgroundColor(backgroundColor) - - // Update title screen theme if it has a setTheme method - // binding.titleScreen.setTheme(themeId) - - // Set text colors based on theme - val isDarkTheme = backgroundColor == Color.BLACK || - Color.red(backgroundColor) < 50 && - Color.green(backgroundColor) < 50 && - Color.blue(backgroundColor) < 50 - - val textColor = if (isDarkTheme) Color.WHITE else Color.BLACK - updateTextColors(textColor) - } - - /** - * Update all text colors in the UI - */ - private fun updateTextColors(color: Int) { - binding.scoreText.setTextColor(color) - binding.currentLevelText.setTextColor(color) - binding.linesText.setTextColor(color) - - // Other text views might not exist or have different IDs - // Adapt based on the actual layout - } - - /** - * Get background color based on theme ID - */ - private fun getThemeBackgroundColor(themeId: String): Int { - return when (themeId) { - PlayerProgressionManager.THEME_CLASSIC -> Color.BLACK - PlayerProgressionManager.THEME_NEON -> Color.parseColor("#0D0221") - PlayerProgressionManager.THEME_MONOCHROME -> Color.parseColor("#1A1A1A") - PlayerProgressionManager.THEME_RETRO -> Color.parseColor("#3F2832") - PlayerProgressionManager.THEME_MINIMALIST -> Color.WHITE - PlayerProgressionManager.THEME_GALAXY -> Color.parseColor("#0B0C10") - else -> Color.BLACK - } - } - - /** - * Get the current score - */ - fun getCurrentScore(): Long = currentScore - - /** - * Get the current level - */ - fun getCurrentLevel(): Int = currentLevel - - /** - * Get the current lines cleared - */ - fun getCurrentLines(): Int = currentLines -} \ No newline at end of file diff --git a/app/src/main/java/com/mintris/ui/ThemeSelector.kt b/app/src/main/java/com/mintris/ui/ThemeSelector.kt index ece7cd4..81fe9b0 100644 --- a/app/src/main/java/com/mintris/ui/ThemeSelector.kt +++ b/app/src/main/java/com/mintris/ui/ThemeSelector.kt @@ -13,7 +13,6 @@ import com.mintris.R import com.mintris.model.PlayerProgressionManager import android.animation.ValueAnimator import android.graphics.drawable.GradientDrawable -import android.util.Log /** * UI component for selecting game themes @@ -214,7 +213,6 @@ class ThemeSelector @JvmOverloads constructor( if (isUnlocked) { card.setOnClickListener { // Clicking directly selects the theme - Log.d("ThemeSelector", "Theme card clicked: $themeId (isUnlocked=$isUnlocked)") focusedThemeId = themeId focusedIndex = themeIdList.indexOf(themeId) confirmSelection() // Directly confirm click selection @@ -322,9 +320,8 @@ class ThemeSelector @JvmOverloads constructor( * Triggers the onThemeSelected callback. */ fun confirmSelection() { - Log.d("ThemeSelector", "confirmSelection called. Focused theme: $focusedThemeId") - if (focusedThemeId == null || focusedThemeId == selectedTheme) { - // No change needed if nothing is focused, + if (!hasComponentFocus || focusedThemeId == null || focusedThemeId == selectedTheme) { + // No change needed if component doesn't have focus, nothing is focused, // or the focused item is already selected return } diff --git a/app/src/main/res/drawable/ic_leaderboard.xml b/app/src/main/res/drawable/ic_leaderboard.xml deleted file mode 100644 index d7cfb64..0000000 --- a/app/src/main/res/drawable/ic_leaderboard.xml +++ /dev/null @@ -1,11 +0,0 @@ - - - - \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_play.xml b/app/src/main/res/drawable/ic_play.xml deleted file mode 100644 index 068da4e..0000000 --- a/app/src/main/res/drawable/ic_play.xml +++ /dev/null @@ -1,11 +0,0 @@ - - - - \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index e8daeef..c8eeb4d 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -49,21 +49,4 @@ music Customization Random Mode - - - Play - Start New Game - Scores - View High Scores - - - Rotate piece - Move piece left - Move piece right - Drop piece - Hold current piece - Game board - Next piece preview - Held piece - Pause game \ No newline at end of file diff --git a/app/src/main/res/xml/backup_rules.xml b/app/src/main/res/xml/backup_rules.xml deleted file mode 100644 index 77753a5..0000000 --- a/app/src/main/res/xml/backup_rules.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - - - - - - - \ No newline at end of file diff --git a/app/src/main/res/xml/shortcuts.xml b/app/src/main/res/xml/shortcuts.xml deleted file mode 100644 index a8af0c8..0000000 --- a/app/src/main/res/xml/shortcuts.xml +++ /dev/null @@ -1,30 +0,0 @@ - - - - - - - - - - - - - - \ No newline at end of file