Compare commits

...

3 commits

Author SHA1 Message Date
cmclark00
14205b2b0a Enhance: Update Google Play Games integration and versioning
- Incremented version code to 5 and version name to 0.4 in build.gradle.
- Removed Google Play sign-in buttons from both portrait and landscape layouts.
- Updated HighScoresActivity to hide the leaderboard button.
- Improved score submission process in GooglePlayGamesManager with forced sign-in and user feedback.
- Enhanced MainActivity to submit scores to Google Play Games upon game over.
2025-04-02 19:52:02 -04:00
cmclark00
808dc79396 Enhance: Update Google Play Games integration and versioning
- Incremented version code to 3 and version name to 0.2 in build.gradle.
- Added INTERNET and ACCESS_NETWORK_STATE permissions in AndroidManifest.xml.
- Refactored leaderboard display logic in HighScoresActivity and MainActivity to utilize a listener for intent success and failure.
- Introduced silent sign-in functionality for Google Play Games in MainActivity.
- Updated GooglePlayGamesManager to support the new listener interface for leaderboard intent handling.
- Added Google Play sign-in buttons in both portrait and landscape layouts for improved user interaction.
2025-04-02 18:04:24 -04:00
cmclark00
220caa39f7 Enhance: Integrate Google Play Games Services for global leaderboard
- Added global leaderboard functionality via Google Play Games Services.
- Implemented sign-in process for Google Play Games and score submission.
- Updated UI to include leaderboard buttons in both MainActivity and HighScoresActivity.
- Enhanced README to document new online features and leaderboard integration.
- Added necessary dependencies in build.gradle and updated AndroidManifest.xml for Google Play Games configuration.
2025-04-02 14:32:09 -04:00
11 changed files with 441 additions and 15 deletions

View file

@ -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

View file

@ -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'

View file

@ -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"

View file

@ -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
}
}
}

View file

@ -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()
}
})
}
}

View file

@ -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)
}
}
}

View file

@ -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
}
}

View file

@ -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>

View file

@ -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"

View file

@ -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>

View file

@ -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>