mirror of
https://github.com/cmclark00/mintris.git
synced 2025-05-17 20:25:20 +01:00
Compare commits
3 commits
21b2513ad4
...
14205b2b0a
Author | SHA1 | Date | |
---|---|---|---|
|
14205b2b0a | ||
|
808dc79396 | ||
|
220caa39f7 |
11 changed files with 441 additions and 15 deletions
|
@ -11,6 +11,7 @@ A modern falling block puzzle game for Android, featuring smooth animations, res
|
|||
- Hard drop and soft drop controls
|
||||
- Advanced move detection (e.g., T-Spins) and scoring
|
||||
- Persistent high score system (Top 5)
|
||||
- Global leaderboard via Google Play Games Services
|
||||
|
||||
### Modern Android Features
|
||||
- Optimized for Android 11+ (API 30+)
|
||||
|
@ -20,6 +21,7 @@ A modern falling block puzzle game for Android, featuring smooth animations, res
|
|||
- Automatic Dark theme support
|
||||
- Intuitive and responsive touch controls
|
||||
- Full edge-to-edge display utilization
|
||||
- Google Play Games Services integration for leaderboards
|
||||
|
||||
### Scoring System
|
||||
|
||||
|
@ -74,6 +76,12 @@ Pixelmint Drop features a comprehensive scoring system designed to reward skillf
|
|||
- Multiple theme options with ability to change manually or enable Random Mode (unlocked when 2+ themes are available).
|
||||
- In Random Mode, themes change automatically every 10 line clears (1 level).
|
||||
|
||||
### Online Features
|
||||
- Global leaderboard support through Google Play Games Services
|
||||
- Sign in with your Google account to compete with players worldwide
|
||||
- Your highest scores are automatically submitted to the leaderboard
|
||||
- View the global leaderboard directly from the game
|
||||
|
||||
## Technical Details
|
||||
|
||||
### Requirements
|
||||
|
|
|
@ -11,8 +11,8 @@ android {
|
|||
applicationId "com.pixelmintdrop"
|
||||
minSdk 30
|
||||
targetSdk 35
|
||||
versionCode 2
|
||||
versionName "0.1"
|
||||
versionCode 5
|
||||
versionName "0.4"
|
||||
|
||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||
}
|
||||
|
@ -63,6 +63,8 @@ dependencies {
|
|||
implementation 'androidx.window:window:1.2.0' // For better display support
|
||||
implementation 'androidx.window:window-java:1.2.0'
|
||||
implementation 'com.google.code.gson:gson:2.10.1'
|
||||
implementation 'com.google.android.gms:play-services-games-v2:19.0.0'
|
||||
implementation 'com.google.android.gms:play-services-auth:20.7.0'
|
||||
testImplementation 'junit:junit:4.13.2'
|
||||
androidTestImplementation 'androidx.test.ext:junit:1.1.5'
|
||||
androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1'
|
||||
|
|
|
@ -2,6 +2,8 @@
|
|||
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<uses-permission android:name="android.permission.VIBRATE" />
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
||||
<!-- Add permission to handle system gestures if needed on some devices -->
|
||||
|
||||
<application
|
||||
|
@ -11,6 +13,12 @@
|
|||
android:roundIcon="@mipmap/ic_launcher_round"
|
||||
android:supportsRtl="true"
|
||||
android:theme="@style/Theme.pixelmintdrop">
|
||||
<meta-data
|
||||
android:name="com.google.android.gms.games.APP_ID"
|
||||
android:value="@string/app_id" />
|
||||
<meta-data
|
||||
android:name="com.google.android.gms.version"
|
||||
android:value="@integer/google_play_services_version" />
|
||||
<activity
|
||||
android:name=".MainActivity"
|
||||
android:exported="true"
|
||||
|
|
|
@ -7,15 +7,18 @@ import com.pixelmintdrop.databinding.HighScoresBinding
|
|||
import com.pixelmintdrop.model.HighScoreAdapter
|
||||
import com.pixelmintdrop.model.HighScoreManager
|
||||
import com.pixelmintdrop.model.PlayerProgressionManager
|
||||
import com.pixelmintdrop.model.GooglePlayGamesManager
|
||||
import android.graphics.Color
|
||||
import android.util.Log
|
||||
import android.view.KeyEvent
|
||||
import android.view.InputDevice
|
||||
import android.graphics.Rect
|
||||
import android.view.View
|
||||
import android.widget.Toast
|
||||
import androidx.core.view.ViewCompat
|
||||
import androidx.core.view.WindowInsetsCompat
|
||||
import androidx.core.view.updatePadding
|
||||
import android.content.Intent
|
||||
|
||||
class HighScoresActivity : AppCompatActivity() {
|
||||
private lateinit var binding: HighScoresBinding
|
||||
|
@ -23,6 +26,10 @@ class HighScoresActivity : AppCompatActivity() {
|
|||
private lateinit var highScoreAdapter: HighScoreAdapter
|
||||
private lateinit var progressionManager: PlayerProgressionManager
|
||||
private var currentTheme = PlayerProgressionManager.THEME_CLASSIC
|
||||
|
||||
companion object {
|
||||
private const val RC_LEADERBOARD_UI_HS = 9005 // Unique request code for HighScoresActivity
|
||||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
@ -68,6 +75,9 @@ class HighScoresActivity : AppCompatActivity() {
|
|||
binding.backButton.setOnClickListener {
|
||||
finish()
|
||||
}
|
||||
|
||||
// Hide the leaderboard button
|
||||
binding.leaderboardButton?.visibility = View.GONE
|
||||
} catch (e: Exception) {
|
||||
Log.e("HighScoresActivity", "Error in onCreate", e)
|
||||
// Show an error message if necessary, or finish gracefully
|
||||
|
@ -104,7 +114,7 @@ class HighScoresActivity : AppCompatActivity() {
|
|||
else -> Color.WHITE
|
||||
}
|
||||
|
||||
// Apply theme to back button
|
||||
// Apply theme to buttons
|
||||
binding.backButton.setTextColor(textColor)
|
||||
|
||||
// Update adapter theme
|
||||
|
@ -122,6 +132,35 @@ class HighScoresActivity : AppCompatActivity() {
|
|||
Log.e("HighScoresActivity", "Error updating high scores", e)
|
||||
}
|
||||
}
|
||||
|
||||
private fun showGlobalLeaderboard() {
|
||||
try {
|
||||
if (!highScoreManager.isGooglePlaySignedIn()) {
|
||||
Toast.makeText(this, "Signing in to Google Play Games...", Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
|
||||
// Call the updated showLeaderboard with a listener
|
||||
highScoreManager.getGooglePlayGamesManager().showLeaderboard(this,
|
||||
object : GooglePlayGamesManager.LeaderboardIntentListener {
|
||||
override fun onLeaderboardIntentSuccess(intent: Intent) {
|
||||
try {
|
||||
startActivityForResult(intent, RC_LEADERBOARD_UI_HS)
|
||||
} catch (e: Exception) {
|
||||
Log.e("HighScoresActivity", "Failed to start leaderboard activity for result", e)
|
||||
Toast.makeText(this@HighScoresActivity, "Could not display leaderboard.", Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onLeaderboardIntentFailure(exception: Exception) {
|
||||
Log.e("HighScoresActivity", "Failed to get leaderboard intent.", exception)
|
||||
Toast.makeText(this@HighScoresActivity, "Could not display leaderboard.", Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
})
|
||||
} catch (e: Exception) {
|
||||
Log.e("HighScoresActivity", "Error showing leaderboard", e)
|
||||
Toast.makeText(this, "Could not open leaderboard", Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
|
@ -143,6 +182,11 @@ class HighScoresActivity : AppCompatActivity() {
|
|||
finish()
|
||||
return true
|
||||
}
|
||||
KeyEvent.KEYCODE_BUTTON_Y -> {
|
||||
// Y button shows global leaderboard
|
||||
showGlobalLeaderboard()
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -55,6 +55,14 @@ import androidx.core.view.updatePadding
|
|||
import kotlin.math.max
|
||||
import kotlin.math.min
|
||||
import kotlin.random.Random
|
||||
// Google Play Games Services imports
|
||||
import com.google.android.gms.games.PlayGames
|
||||
import com.google.android.gms.games.PlayGamesSdk
|
||||
import com.google.android.gms.games.GamesSignInClient
|
||||
import com.google.android.gms.auth.api.signin.GoogleSignIn // Added for silent sign-in
|
||||
import com.google.android.gms.auth.api.signin.GoogleSignInClient // Added for silent sign-in
|
||||
import com.google.android.gms.auth.api.signin.GoogleSignInOptions // Added for silent sign-in
|
||||
import com.pixelmintdrop.model.GooglePlayGamesManager // Added import for listener
|
||||
|
||||
class MainActivity : AppCompatActivity(),
|
||||
GamepadController.GamepadConnectionListener,
|
||||
|
@ -63,11 +71,16 @@ class MainActivity : AppCompatActivity(),
|
|||
|
||||
companion object {
|
||||
private const val TAG = "MainActivity"
|
||||
private const val RC_LEADERBOARD_UI = 9004 // Request code for leaderboard UI
|
||||
}
|
||||
|
||||
// ViewModel
|
||||
private val viewModel: MainActivityViewModel by viewModels() // Added ViewModel
|
||||
|
||||
// Google Play Games Services
|
||||
private lateinit var gamesSignInClient: GamesSignInClient
|
||||
private lateinit var googleSignInClient: GoogleSignInClient // Added for silent sign-in
|
||||
|
||||
// UI components
|
||||
private lateinit var binding: ActivityMainBinding
|
||||
private lateinit var gameView: GameView
|
||||
|
@ -157,6 +170,16 @@ class MainActivity : AppCompatActivity(),
|
|||
binding = ActivityMainBinding.inflate(layoutInflater)
|
||||
setContentView(binding.root)
|
||||
|
||||
// Initialize Google Play Games Services
|
||||
PlayGamesSdk.initialize(this)
|
||||
gamesSignInClient = PlayGames.getGamesSignInClient(this)
|
||||
// Initialize GoogleSignInClient for silent sign-in
|
||||
val signInOptions = GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_GAMES_SIGN_IN).build()
|
||||
googleSignInClient = GoogleSignIn.getClient(this, signInOptions)
|
||||
|
||||
// Request sign-in silently
|
||||
signInToPlayGames()
|
||||
|
||||
// Store initial padding values before applying insets
|
||||
val initialPausePadding = Rect(binding.pauseContainer.paddingLeft, binding.pauseContainer.paddingTop,
|
||||
binding.pauseContainer.paddingRight, binding.pauseContainer.paddingBottom)
|
||||
|
@ -450,6 +473,10 @@ class MainActivity : AppCompatActivity(),
|
|||
Log.d(TAG, "[GameOverDebug] Triples: $lastSessionTriples (from manager: ${statsManager.getSessionTriples()})")
|
||||
Log.d(TAG, "[GameOverDebug] Quads: $lastSessionQuads (from manager: ${statsManager.getSessionQuads()})")
|
||||
|
||||
// *** Submit score to Google Play Games ***
|
||||
Log.d(TAG, "Attempting to submit end-game score to Play Games: $lastSessionScore")
|
||||
highScoreManager.getGooglePlayGamesManager().submitScore(lastSessionScore.toLong(), this@MainActivity)
|
||||
|
||||
// End the session (updates lifetime stats)
|
||||
statsManager.endSession()
|
||||
|
||||
|
@ -529,9 +556,16 @@ class MainActivity : AppCompatActivity(),
|
|||
startGame()
|
||||
}
|
||||
|
||||
// High Scores button
|
||||
binding.highScoresButton.setOnClickListener {
|
||||
gameHaptics.performHapticFeedback(it, HapticFeedbackConstants.VIRTUAL_KEY)
|
||||
showHighScores()
|
||||
binding.pauseContainer.visibility = View.GONE
|
||||
val intent = Intent(this, HighScoresActivity::class.java)
|
||||
startActivity(intent)
|
||||
}
|
||||
|
||||
// Leaderboard button
|
||||
binding.leaderboardButton?.setOnClickListener {
|
||||
showLeaderboard()
|
||||
}
|
||||
|
||||
binding.pauseLevelUpButton.setOnClickListener {
|
||||
|
@ -815,6 +849,7 @@ class MainActivity : AppCompatActivity(),
|
|||
binding.pauseRestartButton.setTextColor(themeColor)
|
||||
binding.resumeButton.setTextColor(themeColor)
|
||||
binding.highScoresButton.setTextColor(themeColor)
|
||||
binding.leaderboardButton?.setTextColor(themeColor)
|
||||
binding.statsButton.setTextColor(themeColor)
|
||||
binding.pauseLevelUpButton?.setTextColor(themeColor) // Safe call
|
||||
binding.pauseLevelDownButton?.setTextColor(themeColor) // Safe call
|
||||
|
@ -1046,6 +1081,10 @@ class MainActivity : AppCompatActivity(),
|
|||
Log.d(TAG, "[GameOverDebug] Triples: $lastSessionTriples (from manager: ${statsManager.getSessionTriples()})")
|
||||
Log.d(TAG, "[GameOverDebug] Quads: $lastSessionQuads (from manager: ${statsManager.getSessionQuads()})")
|
||||
|
||||
// *** Submit score to Google Play Games ***
|
||||
Log.d(TAG, "Attempting to submit end-game score to Play Games: $lastSessionScore")
|
||||
highScoreManager.getGooglePlayGamesManager().submitScore(lastSessionScore.toLong(), this@MainActivity)
|
||||
|
||||
// End the session (updates lifetime stats)
|
||||
statsManager.endSession()
|
||||
|
||||
|
@ -1640,6 +1679,7 @@ class MainActivity : AppCompatActivity(),
|
|||
|
||||
// Group 2: Stats and Scoring
|
||||
orderedViews.add(binding.highScoresButton)
|
||||
orderedViews.add(binding.leaderboardButton)
|
||||
orderedViews.add(binding.statsButton)
|
||||
|
||||
// Group 3: Level selection (use safe calls)
|
||||
|
@ -2206,4 +2246,82 @@ class MainActivity : AppCompatActivity(),
|
|||
Log.d("RandomMode", "Cannot apply random theme/skin - no unlocked options available")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles sign-in to Google Play Games Services
|
||||
*/
|
||||
private fun signInToPlayGames() {
|
||||
Log.d(TAG, "Attempting to sign into Google Play Games")
|
||||
gamesSignInClient.isAuthenticated.addOnCompleteListener { task ->
|
||||
val isAuthenticated = task.isSuccessful && task.result.isAuthenticated
|
||||
|
||||
if (!isAuthenticated) {
|
||||
// Silent sign-in failed, but don't prompt right away
|
||||
Log.d(TAG, "Not authenticated with Play Games, will prompt for sign-in when needed")
|
||||
|
||||
// Optional: Add a sign-in button to your menu or UI for manual sign-in
|
||||
// We'll attempt sign-in when accessing leaderboards or submitting scores
|
||||
} else {
|
||||
Log.d(TAG, "Already authenticated with Play Games")
|
||||
|
||||
// If we were already authenticated, make sure scores are synced
|
||||
val lastHighScore = highScoreManager.getHighScores().maxByOrNull { it.score }
|
||||
lastHighScore?.let {
|
||||
// Submit the high score if we're already authenticated
|
||||
Log.d(TAG, "User is signed in, submitting last high score: ${it.score}")
|
||||
highScoreManager.getGooglePlayGamesManager().submitScore(it.score.toLong(), this)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Method to show Google Play Games leaderboard
|
||||
private fun showLeaderboard() {
|
||||
// New logic: Try silent sign-in first, then interactive if needed.
|
||||
Log.d(TAG, "Attempting silent sign-in before showing leaderboard...")
|
||||
// Use googleSignInClient for silentSignIn
|
||||
googleSignInClient.silentSignIn().addOnCompleteListener { silentSignInTask ->
|
||||
if (silentSignInTask.isSuccessful) {
|
||||
// Silent sign-in successful OR user already signed in.
|
||||
Log.d(TAG, "Silent sign-in successful/already signed in. Getting leaderboard intent.")
|
||||
getAndShowLeaderboardIntent()
|
||||
} else {
|
||||
// Silent sign-in failed. Need explicit sign-in.
|
||||
Log.w(TAG, "Silent sign-in failed. Attempting interactive sign-in.", silentSignInTask.exception)
|
||||
// Use gamesSignInClient for interactive signIn
|
||||
gamesSignInClient.signIn().addOnCompleteListener { interactiveSignInTask ->
|
||||
if (interactiveSignInTask.isSuccessful) {
|
||||
// Interactive sign-in successful
|
||||
Log.d(TAG, "Interactive sign-in successful. Getting leaderboard intent.")
|
||||
getAndShowLeaderboardIntent()
|
||||
} else {
|
||||
// Interactive sign-in failed
|
||||
Log.e(TAG, "Interactive sign-in failed.", interactiveSignInTask.exception)
|
||||
Toast.makeText(this, "Sign-in failed. Please try again.", Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Helper function to get the intent from GooglePlayGamesManager and show it
|
||||
private fun getAndShowLeaderboardIntent() {
|
||||
highScoreManager.getGooglePlayGamesManager().showLeaderboard(this,
|
||||
object : GooglePlayGamesManager.LeaderboardIntentListener {
|
||||
override fun onLeaderboardIntentSuccess(intent: Intent) {
|
||||
Log.d(TAG, "Received leaderboard intent, starting activity for result.")
|
||||
try {
|
||||
startActivityForResult(intent, RC_LEADERBOARD_UI)
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "Failed to start leaderboard activity for result", e)
|
||||
Toast.makeText(this@MainActivity, "Could not display leaderboard.", Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onLeaderboardIntentFailure(exception: Exception) {
|
||||
Log.e(TAG, "Failed to get leaderboard intent.", exception)
|
||||
Toast.makeText(this@MainActivity, "Could not display leaderboard.", Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
|
@ -0,0 +1,169 @@
|
|||
package com.pixelmintdrop.model
|
||||
|
||||
import android.app.Activity
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.util.Log
|
||||
import androidx.activity.result.ActivityResultLauncher
|
||||
import com.google.android.gms.auth.api.signin.GoogleSignIn
|
||||
import com.google.android.gms.auth.api.signin.GoogleSignInAccount
|
||||
import com.google.android.gms.auth.api.signin.GoogleSignInClient
|
||||
import com.google.android.gms.auth.api.signin.GoogleSignInOptions
|
||||
import com.google.android.gms.games.PlayGames
|
||||
import com.google.android.gms.games.PlayGamesSdk
|
||||
import com.google.android.gms.tasks.Task
|
||||
|
||||
class GooglePlayGamesManager(private val context: Context) {
|
||||
private val TAG = "GooglePlayGamesManager"
|
||||
|
||||
// Define a listener interface for the leaderboard intent
|
||||
interface LeaderboardIntentListener {
|
||||
fun onLeaderboardIntentSuccess(intent: Intent)
|
||||
fun onLeaderboardIntentFailure(exception: Exception)
|
||||
}
|
||||
|
||||
// Leaderboard ID
|
||||
companion object {
|
||||
const val LEADERBOARD_ID = "CgkImJW2mKsSEAIQAQ"
|
||||
}
|
||||
|
||||
// Initialize the Play Games SDK
|
||||
init {
|
||||
try {
|
||||
PlayGamesSdk.initialize(context)
|
||||
Log.d(TAG, "PlayGamesSdk initialized")
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "Error initializing PlayGamesSdk", e)
|
||||
}
|
||||
}
|
||||
|
||||
// Check if user is already signed in
|
||||
fun isSignedIn(): Boolean {
|
||||
val account = GoogleSignIn.getLastSignedInAccount(context)
|
||||
return account != null && !account.isExpired
|
||||
}
|
||||
|
||||
// Get the sign-in client
|
||||
fun getSignInClient(): GoogleSignInClient {
|
||||
val signInOptions = GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_GAMES_SIGN_IN).build()
|
||||
return GoogleSignIn.getClient(context, signInOptions)
|
||||
}
|
||||
|
||||
// Handle sign-in result
|
||||
fun handleSignInResult(task: Task<GoogleSignInAccount>): Boolean {
|
||||
return if (task.isSuccessful) {
|
||||
Log.d(TAG, "Sign-in successful")
|
||||
true
|
||||
} else {
|
||||
Log.e(TAG, "Sign-in failed", task.exception)
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
// Submit score to leaderboard (requires an activity for potential sign-in)
|
||||
fun submitScore(score: Long, activity: Activity? = null) {
|
||||
if (activity == null) {
|
||||
Log.w(TAG, "Activity context is required for score submission.")
|
||||
return
|
||||
}
|
||||
|
||||
Log.d(TAG, "Attempting to submit score: $score")
|
||||
|
||||
// Always force a sign-in to ensure we have a valid account
|
||||
val signInClient = PlayGames.getGamesSignInClient(activity)
|
||||
signInClient.signIn().addOnCompleteListener { signInTask ->
|
||||
if (signInTask.isSuccessful) {
|
||||
Log.d(TAG, "Sign-in successful, retrieving account for score submission")
|
||||
|
||||
// After sign-in, get the account and submit score
|
||||
try {
|
||||
// Use PlayGames API directly to get client and submit score
|
||||
val leaderboardsClient = PlayGames.getLeaderboardsClient(activity)
|
||||
leaderboardsClient.submitScore(LEADERBOARD_ID, score)
|
||||
Log.d(TAG, "Successfully submitted score $score to leaderboard $LEADERBOARD_ID")
|
||||
|
||||
// Show confirmation to user
|
||||
android.widget.Toast.makeText(
|
||||
activity,
|
||||
"Score submitted to leaderboard",
|
||||
android.widget.Toast.LENGTH_SHORT
|
||||
).show()
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "Error submitting score after sign-in", e)
|
||||
android.widget.Toast.makeText(
|
||||
activity,
|
||||
"Failed to submit score to leaderboard",
|
||||
android.widget.Toast.LENGTH_SHORT
|
||||
).show()
|
||||
}
|
||||
} else {
|
||||
Log.e(TAG, "Sign-in failed, unable to submit score", signInTask.exception)
|
||||
android.widget.Toast.makeText(
|
||||
activity,
|
||||
"Sign-in required to submit score to leaderboard",
|
||||
android.widget.Toast.LENGTH_SHORT
|
||||
).show()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Show the leaderboard - Now uses a listener to return the Intent
|
||||
fun showLeaderboard(activity: Activity, listener: LeaderboardIntentListener) {
|
||||
Log.d(TAG, "showLeaderboard called in GooglePlayGamesManager")
|
||||
|
||||
// First check if already authenticated
|
||||
PlayGames.getGamesSignInClient(activity).isAuthenticated.addOnCompleteListener { authTask ->
|
||||
val isAuthenticated = authTask.isSuccessful && authTask.result.isAuthenticated
|
||||
|
||||
if (isAuthenticated) {
|
||||
// Already authenticated, get leaderboard
|
||||
getLeaderboardIntent(activity, listener)
|
||||
} else {
|
||||
// Need to authenticate first
|
||||
Log.d(TAG, "User not authenticated for leaderboard, attempting sign-in")
|
||||
PlayGames.getGamesSignInClient(activity).signIn().addOnCompleteListener { signInTask ->
|
||||
if (signInTask.isSuccessful) {
|
||||
// Successfully signed in, now get leaderboard
|
||||
Log.d(TAG, "Sign-in successful, getting leaderboard")
|
||||
getLeaderboardIntent(activity, listener)
|
||||
} else {
|
||||
// Failed to sign in
|
||||
Log.e(TAG, "Failed to sign in for leaderboard access", signInTask.exception)
|
||||
listener.onLeaderboardIntentFailure(
|
||||
Exception("Sign-in required to view leaderboard", signInTask.exception)
|
||||
)
|
||||
|
||||
// Inform user
|
||||
android.widget.Toast.makeText(
|
||||
activity,
|
||||
"Sign-in required to view leaderboard",
|
||||
android.widget.Toast.LENGTH_SHORT
|
||||
).show()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Private helper to get leaderboard intent
|
||||
private fun getLeaderboardIntent(activity: Activity, listener: LeaderboardIntentListener) {
|
||||
Log.d(TAG, "Attempting to get LeaderboardsClient")
|
||||
try {
|
||||
val leaderboardsClient = PlayGames.getLeaderboardsClient(activity)
|
||||
Log.d(TAG, "LeaderboardsClient obtained, attempting to get leaderboard intent for ID: $LEADERBOARD_ID")
|
||||
leaderboardsClient
|
||||
.getLeaderboardIntent(LEADERBOARD_ID)
|
||||
.addOnSuccessListener { intent ->
|
||||
Log.d(TAG, "Successfully obtained leaderboard intent. Calling listener.")
|
||||
listener.onLeaderboardIntentSuccess(intent)
|
||||
}
|
||||
.addOnFailureListener { e ->
|
||||
Log.e(TAG, "Error getting leaderboard intent", e)
|
||||
listener.onLeaderboardIntentFailure(e)
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "Error obtaining LeaderboardsClient or getting leaderboard intent", e)
|
||||
listener.onLeaderboardIntentFailure(e)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -10,6 +10,7 @@ class HighScoreManager(private val context: Context) {
|
|||
private val prefs: SharedPreferences = context.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE)
|
||||
private val gson = Gson()
|
||||
private val type: Type = object : TypeToken<List<HighScore>>() {}.type
|
||||
private val googlePlayGamesManager = GooglePlayGamesManager(context)
|
||||
|
||||
companion object {
|
||||
private const val PREFS_NAME = "pixelmintdrop_highscores"
|
||||
|
@ -27,6 +28,7 @@ class HighScoreManager(private val context: Context) {
|
|||
}
|
||||
|
||||
fun addHighScore(highScore: HighScore) {
|
||||
// Save to local high scores
|
||||
val currentScores = getHighScores().toMutableList()
|
||||
currentScores.add(highScore)
|
||||
|
||||
|
@ -37,6 +39,9 @@ class HighScoreManager(private val context: Context) {
|
|||
// Save to SharedPreferences
|
||||
val json = gson.toJson(topScores)
|
||||
prefs.edit().putString(KEY_HIGHSCORES, json).apply()
|
||||
|
||||
// Submit to Google Play Games leaderboard if signed in
|
||||
submitScoreToGooglePlay(highScore.score)
|
||||
}
|
||||
|
||||
fun isHighScore(score: Int): Boolean {
|
||||
|
@ -44,4 +49,26 @@ class HighScoreManager(private val context: Context) {
|
|||
return currentScores.size < MAX_HIGHSCORES ||
|
||||
score > (currentScores.lastOrNull()?.score ?: 0)
|
||||
}
|
||||
|
||||
// Submit score to Google Play Games leaderboard
|
||||
private fun submitScoreToGooglePlay(score: Int) {
|
||||
// We don't have an activity here, so we can't submit the score directly
|
||||
// The score will be submitted later when an activity is available
|
||||
try {
|
||||
googlePlayGamesManager.submitScore(score.toLong(), null)
|
||||
} catch (e: Exception) {
|
||||
// Log error but don't crash
|
||||
android.util.Log.e("HighScoreManager", "Error submitting score to leaderboard", e)
|
||||
}
|
||||
}
|
||||
|
||||
// Method to check if user is signed in to Google Play Games
|
||||
fun isGooglePlaySignedIn(): Boolean {
|
||||
return googlePlayGamesManager.isSignedIn()
|
||||
}
|
||||
|
||||
// Get Google Play Games manager
|
||||
fun getGooglePlayGamesManager(): GooglePlayGamesManager {
|
||||
return googlePlayGamesManager
|
||||
}
|
||||
}
|
|
@ -438,6 +438,20 @@
|
|||
android:singleLine="true"
|
||||
android:focusable="true"
|
||||
android:focusableInTouchMode="true" />
|
||||
|
||||
<!-- Leaderboard Button -->
|
||||
<Button
|
||||
android:id="@+id/leaderboardButton"
|
||||
android:layout_width="200dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="8dp"
|
||||
android:background="@color/transparent"
|
||||
android:text="Global Leaderboard"
|
||||
android:textColor="@color/white"
|
||||
android:textSize="20sp"
|
||||
android:textStyle="bold"
|
||||
android:fontFamily="sans-serif"
|
||||
android:textAllCaps="false" />
|
||||
</LinearLayout>
|
||||
</ScrollView>
|
||||
</LinearLayout>
|
||||
|
|
|
@ -406,7 +406,20 @@
|
|||
android:textStyle="bold"
|
||||
android:fontFamily="sans-serif"
|
||||
android:textAllCaps="false" />
|
||||
|
||||
|
||||
<Button
|
||||
android:id="@+id/leaderboardButton"
|
||||
android:layout_width="200dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="16dp"
|
||||
android:background="@color/transparent"
|
||||
android:text="Global Leaderboard"
|
||||
android:textColor="@color/white"
|
||||
android:textSize="24sp"
|
||||
android:textStyle="bold"
|
||||
android:fontFamily="sans-serif"
|
||||
android:textAllCaps="false" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/statsButton"
|
||||
android:layout_width="200dp"
|
||||
|
|
|
@ -25,15 +25,37 @@
|
|||
android:layout_weight="1"
|
||||
android:layout_marginBottom="16dp"/>
|
||||
|
||||
<Button
|
||||
android:id="@+id/backButton"
|
||||
android:layout_width="200dp"
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:background="@color/transparent"
|
||||
android:text="@string/back"
|
||||
android:textColor="@color/white"
|
||||
android:textSize="18sp"
|
||||
android:fontFamily="monospace"/>
|
||||
android:orientation="horizontal"
|
||||
android:gravity="center">
|
||||
|
||||
<Button
|
||||
android:id="@+id/backButton"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:layout_gravity="center"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:background="@color/transparent"
|
||||
android:text="@string/back"
|
||||
android:textColor="@color/white"
|
||||
android:textSize="18sp"
|
||||
android:fontFamily="monospace"/>
|
||||
|
||||
<Button
|
||||
android:id="@+id/leaderboardButton"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:layout_gravity="center"
|
||||
android:layout_marginStart="8dp"
|
||||
android:background="@color/transparent"
|
||||
android:text="Global Leaderboard"
|
||||
android:textColor="@color/white"
|
||||
android:textSize="18sp"
|
||||
android:fontFamily="monospace"/>
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
|
@ -1,6 +1,7 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="app_name">Pixel Mint Drop</string>
|
||||
<string name="app_id">630069234328</string>
|
||||
<string name="game_over">game over</string>
|
||||
<string name="score">score</string>
|
||||
<string name="level">level</string>
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue