mirror of
https://github.com/cmclark00/mintris.git
synced 2025-05-17 23:55:21 +01:00
Fix: Resolve crash on startup due to ProGuard/Gson TypeToken issue
- Added necessary ProGuard rules to keep HighScore class, generic signatures, and annotations. - Fixed bug where music started on title screen instead of game start. - Fixed bug where session stats were blank on game over screen by ensuring stats are displayed correctly after high score entry. - Fixed bug where game music did not restart after playing again.
This commit is contained in:
parent
c1da5ba20f
commit
04b87e8f19
4 changed files with 145 additions and 20 deletions
35
app/proguard-rules.pro
vendored
35
app/proguard-rules.pro
vendored
|
@ -1,10 +1,41 @@
|
|||
# Add project specific ProGuard rules here.
|
||||
# You can control the set of applied configuration files using the
|
||||
# proguardFiles setting in build.gradle.
|
||||
# By default, the flags in this file are appended to flags specified
|
||||
# in /Users/coreyclark/Library/Android/sdk/tools/proguard/proguard-android.txt
|
||||
# You can edit the include path and order by changing the proguardFiles
|
||||
# directive in build.gradle.
|
||||
#
|
||||
# For more details, see
|
||||
# http://developer.android.com/guide/developing/tools/proguard.html
|
||||
|
||||
# Add any project specific keep options here:
|
||||
|
||||
# If your project uses WebView with JS, uncomment the following
|
||||
# and specify the fully qualified class name to the JavaScript interface
|
||||
# class:
|
||||
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
|
||||
# public *;
|
||||
#}
|
||||
|
||||
# Uncomment this to preserve the line number table for debugging exceptions.
|
||||
#-keepattributes SourceFile,LineNumberTable
|
||||
|
||||
# Keep rule for Gson serialization of HighScore data class
|
||||
-keep class com.pixelmintdrop.model.HighScore { *; }
|
||||
-keepclassmembers class com.pixelmintdrop.model.HighScore { *; }
|
||||
|
||||
# Keep generic signatures for Gson TypeToken
|
||||
-keepattributes Signature
|
||||
|
||||
# Keep annotations used by Gson (e.g., @SerializedName, @Expose)
|
||||
-keepattributes *Annotation*
|
||||
|
||||
# Keep the anonymous inner class generated for the TypeToken in HighScoreManager
|
||||
-keep class com.pixelmintdrop.model.HighScoreManager$* { *; }
|
||||
|
||||
# Keep Gson TypeToken related classes
|
||||
-keep class com.google.gson.reflect.TypeToken { *; }
|
||||
-keep class com.google.gson.reflect.** { *; }
|
||||
|
||||
# Keep models intact
|
||||
-keep class com.pixelmintdrop.model.** { *; }
|
||||
|
||||
|
|
|
@ -122,12 +122,27 @@ class MainActivity : AppCompatActivity(),
|
|||
// Add these new properties at the class level
|
||||
private var currentCustomizationMenuSelection = 0
|
||||
|
||||
// CAPTURE session stats needed for display into MainActivity member variables
|
||||
private var lastSessionScore = 0
|
||||
private var lastSessionLines = 0
|
||||
private var lastSessionPieces = 0
|
||||
private var lastSessionTime = 0L
|
||||
private var lastSessionLevel = 0
|
||||
private var lastSessionSingles = 0
|
||||
private var lastSessionDoubles = 0
|
||||
private var lastSessionTriples = 0
|
||||
private var lastSessionQuads = 0
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
// Register activity result launcher for high score entry
|
||||
highScoreEntryLauncher = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
|
||||
// No matter what the result is, we just show the game over container
|
||||
// When returning from HighScoreEntryActivity:
|
||||
// 1. Hide progression screen
|
||||
progressionScreen.visibility = View.GONE
|
||||
binding.gameOverContainer.visibility = View.VISIBLE
|
||||
// 2. Make game over container visible (it's already laid out)
|
||||
binding.gameOverContainer.visibility = View.VISIBLE
|
||||
// 3. *** Call showGameOverScreenDirectly to populate the stats ***
|
||||
showGameOverScreenDirectly(lastSessionScore) // Use the captured score
|
||||
|
||||
// Keep all game UI elements hidden
|
||||
binding.gameControlsContainer.visibility = View.GONE
|
||||
|
@ -406,7 +421,33 @@ class MainActivity : AppCompatActivity(),
|
|||
val newHighScore = highScoreManager.isHighScore(finalScore)
|
||||
statsManager.endSession() // End session after calculations
|
||||
|
||||
Log.d(TAG, "Game Over. Score: $finalScore, Level: ${viewModel.currentLevel.value}, Lines: ${gameBoard.lines}, Start Level: ${viewModel.selectedLevel.value}, New High Score: $newHighScore, XP Gained: $xpGained")
|
||||
// CAPTURE session stats needed for display into MainActivity member variables
|
||||
lastSessionScore = statsManager.getSessionScore()
|
||||
lastSessionLines = statsManager.getSessionLines()
|
||||
lastSessionPieces = statsManager.getSessionPieces()
|
||||
lastSessionTime = statsManager.getSessionTime()
|
||||
lastSessionLevel = statsManager.getSessionLevel()
|
||||
lastSessionSingles = statsManager.getSessionSingles()
|
||||
lastSessionDoubles = statsManager.getSessionDoubles()
|
||||
lastSessionTriples = statsManager.getSessionTriples()
|
||||
lastSessionQuads = statsManager.getSessionQuads()
|
||||
|
||||
// *** Add detailed logging here ***
|
||||
Log.d(TAG, "[GameOverDebug] Captured Stats:")
|
||||
Log.d(TAG, "[GameOverDebug] Score: $lastSessionScore (from manager: ${statsManager.getSessionScore()})")
|
||||
Log.d(TAG, "[GameOverDebug] Lines: $lastSessionLines (from manager: ${statsManager.getSessionLines()})")
|
||||
Log.d(TAG, "[GameOverDebug] Pieces: $lastSessionPieces (from manager: ${statsManager.getSessionPieces()})")
|
||||
Log.d(TAG, "[GameOverDebug] Time: $lastSessionTime (from manager: ${statsManager.getSessionTime()})")
|
||||
Log.d(TAG, "[GameOverDebug] Level: $lastSessionLevel (from manager: ${statsManager.getSessionLevel()})")
|
||||
Log.d(TAG, "[GameOverDebug] Singles: $lastSessionSingles (from manager: ${statsManager.getSessionSingles()})")
|
||||
Log.d(TAG, "[GameOverDebug] Doubles: $lastSessionDoubles (from manager: ${statsManager.getSessionDoubles()})")
|
||||
Log.d(TAG, "[GameOverDebug] Triples: $lastSessionTriples (from manager: ${statsManager.getSessionTriples()})")
|
||||
Log.d(TAG, "[GameOverDebug] Quads: $lastSessionQuads (from manager: ${statsManager.getSessionQuads()})")
|
||||
|
||||
// End the session (updates lifetime stats)
|
||||
statsManager.endSession()
|
||||
|
||||
Log.d(TAG, "Game Over. Captured Score: $lastSessionScore, Level: $lastSessionLevel, Lines: $lastSessionLines, Start Level: ${viewModel.selectedLevel.value}, New High Score: $newHighScore, XP Gained: $xpGained")
|
||||
|
||||
// Show appropriate screen: Progression or Game Over directly
|
||||
if (xpGained > 0 || newHighScore) {
|
||||
|
@ -570,8 +611,8 @@ class MainActivity : AppCompatActivity(),
|
|||
/**
|
||||
* Shows the final game over screen with stats.
|
||||
*/
|
||||
private fun showGameOverScreenDirectly(score: Int) {
|
||||
Log.d(TAG, "Showing final game over screen with score: $score")
|
||||
private fun showGameOverScreenDirectly(score: Int) { // Keep score param for logging if needed elsewhere
|
||||
Log.d(TAG, "Showing final game over screen with score from param: $score, from StatsManager: ${statsManager.getSessionScore()}")
|
||||
// Ensure game UI is hidden
|
||||
binding.gameControlsContainer.visibility = View.GONE
|
||||
binding.holdPieceView.visibility = View.GONE
|
||||
|
@ -584,20 +625,20 @@ class MainActivity : AppCompatActivity(),
|
|||
// Update session stats display in the gameOverContainer
|
||||
val timeFormat = SimpleDateFormat("HH:mm:ss", Locale.getDefault())
|
||||
timeFormat.timeZone = TimeZone.getTimeZone("UTC")
|
||||
val gameTime = System.currentTimeMillis() - gameStartTime // Recalculate or pass from onGameOver?
|
||||
// Let's recalculate for simplicity here.
|
||||
// val gameTime = System.currentTimeMillis() - gameStartTime // No longer needed here
|
||||
|
||||
// Set text directly using interpolation (workaround attempt)
|
||||
binding.sessionScoreText.text = "Score: $score"
|
||||
binding.sessionLinesText.text = "Lines: ${gameBoard.lines}"
|
||||
binding.sessionPiecesText.text = "Pieces: $piecesPlaced"
|
||||
binding.sessionTimeText.text = "Time: ${timeFormat.format(gameTime)}"
|
||||
binding.sessionLevelText.text = "Level: ${viewModel.currentLevel.value ?: 1}"
|
||||
// --- Consistently use StatsManager session getters ---
|
||||
binding.sessionScoreText.text = "Score: ${statsManager.getSessionScore()}"
|
||||
binding.sessionLinesText.text = "Lines: ${statsManager.getSessionLines()}"
|
||||
binding.sessionPiecesText.text = "Pieces: ${statsManager.getSessionPieces()}"
|
||||
binding.sessionTimeText.text = "Time: ${timeFormat.format(statsManager.getSessionTime())}"
|
||||
binding.sessionLevelText.text = "Level: ${statsManager.getSessionLevel()}"
|
||||
|
||||
binding.sessionSinglesText.text = "Singles: ${statsManager.getSessionSingles()}"
|
||||
binding.sessionDoublesText.text = "Doubles: ${statsManager.getSessionDoubles()}"
|
||||
binding.sessionTriplesText.text = "Triples: ${statsManager.getSessionTriples()}"
|
||||
binding.sessionQuadsText.text = "Quads: ${statsManager.getSessionQuads()}"
|
||||
// --- End StatsManager usage ---
|
||||
|
||||
// Make the container visible
|
||||
binding.gameOverContainer.visibility = View.VISIBLE
|
||||
|
@ -900,6 +941,9 @@ class MainActivity : AppCompatActivity(),
|
|||
*/
|
||||
private fun startGame() {
|
||||
Log.d(TAG, "Starting game at level ${viewModel.selectedLevel.value}")
|
||||
// Reset session stats FIRST
|
||||
statsManager.startNewSession()
|
||||
|
||||
// Reset game state
|
||||
viewModel.resetGame() // Resets score and level in ViewModel
|
||||
viewModel.setLevel(viewModel.selectedLevel.value ?: 1) // Set initial level from selection
|
||||
|
@ -973,7 +1017,33 @@ class MainActivity : AppCompatActivity(),
|
|||
val newHighScore = highScoreManager.isHighScore(finalScore)
|
||||
statsManager.endSession() // End session after calculations
|
||||
|
||||
Log.d(TAG, "Game Over. Score: $finalScore, Level: ${viewModel.currentLevel.value}, Lines: ${gameBoard.lines}, Start Level: ${viewModel.selectedLevel.value}, New High Score: $newHighScore, XP Gained: $xpGained")
|
||||
// CAPTURE session stats needed for display into MainActivity member variables
|
||||
lastSessionScore = statsManager.getSessionScore()
|
||||
lastSessionLines = statsManager.getSessionLines()
|
||||
lastSessionPieces = statsManager.getSessionPieces()
|
||||
lastSessionTime = statsManager.getSessionTime()
|
||||
lastSessionLevel = statsManager.getSessionLevel()
|
||||
lastSessionSingles = statsManager.getSessionSingles()
|
||||
lastSessionDoubles = statsManager.getSessionDoubles()
|
||||
lastSessionTriples = statsManager.getSessionTriples()
|
||||
lastSessionQuads = statsManager.getSessionQuads()
|
||||
|
||||
// *** Add detailed logging here ***
|
||||
Log.d(TAG, "[GameOverDebug] Captured Stats:")
|
||||
Log.d(TAG, "[GameOverDebug] Score: $lastSessionScore (from manager: ${statsManager.getSessionScore()})")
|
||||
Log.d(TAG, "[GameOverDebug] Lines: $lastSessionLines (from manager: ${statsManager.getSessionLines()})")
|
||||
Log.d(TAG, "[GameOverDebug] Pieces: $lastSessionPieces (from manager: ${statsManager.getSessionPieces()})")
|
||||
Log.d(TAG, "[GameOverDebug] Time: $lastSessionTime (from manager: ${statsManager.getSessionTime()})")
|
||||
Log.d(TAG, "[GameOverDebug] Level: $lastSessionLevel (from manager: ${statsManager.getSessionLevel()})")
|
||||
Log.d(TAG, "[GameOverDebug] Singles: $lastSessionSingles (from manager: ${statsManager.getSessionSingles()})")
|
||||
Log.d(TAG, "[GameOverDebug] Doubles: $lastSessionDoubles (from manager: ${statsManager.getSessionDoubles()})")
|
||||
Log.d(TAG, "[GameOverDebug] Triples: $lastSessionTriples (from manager: ${statsManager.getSessionTriples()})")
|
||||
Log.d(TAG, "[GameOverDebug] Quads: $lastSessionQuads (from manager: ${statsManager.getSessionQuads()})")
|
||||
|
||||
// End the session (updates lifetime stats)
|
||||
statsManager.endSession()
|
||||
|
||||
Log.d(TAG, "Game Over. Captured Score: $lastSessionScore, Level: $lastSessionLevel, Lines: $lastSessionLines, Start Level: ${viewModel.selectedLevel.value}, New High Score: $newHighScore, XP Gained: $xpGained")
|
||||
|
||||
// Show appropriate screen: Progression or Game Over directly
|
||||
if (xpGained > 0 || newHighScore) {
|
||||
|
@ -1007,6 +1077,7 @@ class MainActivity : AppCompatActivity(),
|
|||
|
||||
// Start background music if enabled
|
||||
if (viewModel.isMusicEnabled.value == true) { // Read from ViewModel
|
||||
gameMusic.prepareMusic() // Ensure player is ready
|
||||
gameMusic.start()
|
||||
}
|
||||
|
||||
|
@ -1014,8 +1085,8 @@ class MainActivity : AppCompatActivity(),
|
|||
gameView.start()
|
||||
// Observer ensures gameMusic is enabled/disabled correctly via gameMusic.setEnabled()
|
||||
|
||||
// Reset session stats
|
||||
statsManager.startNewSession()
|
||||
// Reset session stats - MOVED TO TOP
|
||||
// statsManager.startNewSession()
|
||||
progressionManager.startNewSession()
|
||||
gameBoard.updateLevel(viewModel.selectedLevel.value ?: 1)
|
||||
}
|
||||
|
@ -1222,6 +1293,9 @@ class MainActivity : AppCompatActivity(),
|
|||
} else if (binding.pauseContainer.visibility == View.VISIBLE) {
|
||||
// If pause menu is showing, handle as a resume
|
||||
resumeGame()
|
||||
} else if (binding.customizationContainer.visibility == View.VISIBLE) {
|
||||
// If customization menu is showing, hide it
|
||||
hideCustomizationMenu()
|
||||
} else if (binding.gameOverContainer.visibility == View.VISIBLE) {
|
||||
// If game over is showing, go back to title
|
||||
hideGameOver()
|
||||
|
|
|
@ -141,8 +141,6 @@ class GameMusic(private val context: Context) {
|
|||
|
||||
if (!enabled && mediaPlayer?.isPlaying == true) {
|
||||
pause()
|
||||
} else if (enabled && mediaPlayer != null && isPrepared) {
|
||||
start()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -160,4 +158,26 @@ class GameMusic(private val context: Context) {
|
|||
Log.e("GameMusic", "Error releasing music: ${e.message}")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensures the main media player is prepared for playback.
|
||||
*/
|
||||
fun prepareMusic() {
|
||||
if (mediaPlayer != null && !isPrepared) {
|
||||
try {
|
||||
mediaPlayer?.prepare()
|
||||
isPrepared = true
|
||||
Log.d("GameMusic", "MediaPlayer prepared successfully in prepareMusic()")
|
||||
} catch (e: IllegalStateException) {
|
||||
Log.e("GameMusic", "Error preparing MediaPlayer (already prepared?): ${e.message}")
|
||||
// Assume it's prepared if IllegalStateException is thrown
|
||||
isPrepared = true
|
||||
} catch (e: Exception) {
|
||||
Log.e("GameMusic", "Error preparing MediaPlayer", e)
|
||||
isPrepared = false
|
||||
}
|
||||
} else if (mediaPlayer != null && isPrepared) {
|
||||
Log.d("GameMusic", "MediaPlayer already prepared in prepareMusic()")
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue