This project now includes:
- Google Sign-In (Firebase Authentication)
- Firestore-backed user profile documents (collection:
users) - Wallet linking to a profile (Solana public key)
- Username editing & duel stats placeholder
- Open Firebase Console > Your Project > Build > Authentication > Sign-in method.
- Enable the Google provider.
- Add your Android app SHA-1 / SHA-256 fingerprints (Project Settings > Your Apps). If you haven't generated them:
keytool -list -v -alias androiddebugkey -keystore "$HOME/.android/debug.keystore" -storepass android -keypass android | grep -E "SHA1|SHA-256"
- After saving, re-download the updated
google-services.json(it will now includeoauth_cliententries). - Replace the existing
app/google-services.jsonwith the new one. - The Google Services Gradle plugin will auto-generate a
default_web_client_idstring resource. If so, you can remove the manual placeholder inres/values/strings.xml. Otherwise, set the value manually to the Web client (auto created) OAuth 2.0 client ID you see in the Google Cloud console (NOT the Android client one).
- Create (or confirm) Firestore in Native Mode.
- Create
userscollection upon first sign-in automatically (code writes the document). - Recommended security rules (basic):
Adjust if you plan public leaderboards (then consider a restricted aggregate or a Cloud Function).
rules_version = '2'; service cloud.firestore { match /databases/{database}/documents { match /users/{userId} { allow read: if request.auth != null && request.auth.uid == userId; // user can read own profile allow create: if request.auth != null && request.auth.uid == userId; allow update, delete: if request.auth != null && request.auth.uid == userId; } } }
UserProfile (Firestore document id = Firebase UID):
data class UserProfile(
val uid: String = "",
val walletPublicKey: String? = null,
val username: String? = null,
val createdAt: Long = System.currentTimeMillis(),
val mathLevel: Int = 1,
val csLevel: Int = 1,
val duelStats: DuelStats = DuelStats()
)DuelStats is embedded, updated via atomic field increments when you implement match outcomes.
- User taps "Sign in with Google" (Profile screen when signed out).
- Google Sign-In returns an ID token; Firebase Auth signs the user in.
ProfileViewModel.refresh()fetches or creates theusers/{uid}document.- If a Solana wallet is already connected and no wallet is linked, it sets
walletPublicKey. - User can edit username (writes to Firestore).
- User can then link a wallet if not yet linked (button shows when both conditions satisfied).
| Concern | File |
|---|---|
| Auth UI state | profile/AuthViewModel.kt |
| Profile data / Firestore access | profile/UserProfileRepository.kt |
| Profile screen (Compose) | profile/ProfileScreen.kt |
| Profile view model | profile/ProfileViewModel.kt |
| DI (Auth/Firestore providers) | di/AppModule.kt |
- Firestore gives you: offline caching, scalable doc/collection model, atomic updates (
FieldValue.increment), security rules tied to Firebase Auth. - You do not need Postgres for early-stage profile + stats. Add a dedicated backend later only if you need complex server logic or multi-collection transactions beyond Firestore's capabilities.
- For deterministic, trustless interactions (on-chain), keep that logic on Solana + signable payloads; store only mirrored/profile metadata in Firestore.
- User profile loads once on entering screen; you can cache it in memory or use a snapshot listener for real-time updates (optional improvement).
- Use batched writes or transactions if you introduce multiple stat increments at once.
- Add snapshot listener to auto-refresh profile when changed from another device.
- Add unique username reservation (Cloud Function or Firestore query + security rules).
- Add avatar upload (Firebase Storage).
- Implement duel result writer:
incrementWin(uid, won)inUserProfileRepositoryalready scaffolded. - Add offline indicator / retry logic.
| Issue | Fix |
|---|---|
default_web_client_id not found |
Re-download google-services.json after enabling Google sign-in. Clean/rebuild. |
| Sign-In loops / returns null user | Ensure SHA-1 added and correct OAuth client selected. |
| Permission denied (Firestore) | Update rules; ensure you are authenticated; check document path. |
| Username not persisting | Check Firestore console for write errors / security rules. |
./gradlew :app:clean :app:assembleDebugIf user signs in first and links wallet later, just connect the wallet; the Profile screen automatically offers a "Link Connected Wallet" button if walletPublicKey is still null.
Feel free to extend this with leaderboard aggregation using a Cloud Function that writes to a leaderboards collection with sanitized public fields.