# 36. Android App — Authentication, Onboarding, and Account Setup meta: id: shieldai-unified-restructure-36 feature: shieldai-unified-restructure priority: P1 depends_on: [shieldai-unified-restructure-34, shieldai-unified-restructure-35] tags: [android, jetpack-compose, auth, onboarding, mobile] objective: - Build the authentication and onboarding flow for the Android app: login, signup, password reset, and a multi-step onboarding experience. Use native Android authentication where possible (Google Sign-In, Credential Manager) and maintain visual consistency with the web app. deliverables: - `android/app/src/main/java/com/shieldai/android/ui/screens/auth/` — Auth screens: - `AuthScreen.kt` — Auth container with login/signup toggle - `LoginScreen.kt` — Email + password login form - `SignupScreen.kt` — Account creation form - `ForgotPasswordScreen.kt` — Password reset request - `ResetPasswordScreen.kt` — Password reset confirmation - `android/app/src/main/java/com/shieldai/android/ui/screens/onboarding/` — Onboarding: - `OnboardingScreen.kt` — Horizontal pager with 4 steps - `PlanSelectionStep.kt` — Tier cards (Basic, Plus, Premium) - `WatchlistSetupStep.kt` — Add first email/phone - `FamilyInviteStep.kt` — Invite family members - `CompleteStep.kt` — Success animation and "Get Started" - `android/app/src/main/java/com/shieldai/android/ui/screens/auth/BiometricAuthScreen.kt` — Biometric prompt: - Uses `BiometricPrompt` from `androidx.biometric:biometric` - Face/fingerprint authentication - Fallback to device credential - `android/app/src/main/java/com/shieldai/android/viewmodel/AuthViewModel.kt` — Auth logic: - `login(email, password)` → calls API (task 37) - `signup(name, email, password)` → calls API - `resetPassword(email)` → calls API - `signInWithGoogle()` → Google Sign-In - `enableBiometricAuth()` → stores credential in EncryptedSharedPreferences - `logout()` → clears tokens and state - `android/app/src/main/java/com/shieldai/android/data/repository/AuthRepository.kt` — Data layer: - Token storage in `EncryptedSharedPreferences` - API calls via repository pattern steps: 1. Create auth and onboarding screen directories. 2. **AuthScreen**: - `Column` with ShieldAI logo and tagline - `TabRow` or custom toggle for Login/Signup - `ShieldCard` containing the form 3. **LoginScreen**: - `ShieldTextField` for email and password - "Remember me" `Switch` - "Forgot password?" `TextButton` - "Sign In" `ShieldButton` - Google Sign-In button (Material `Button` with Google icon) - On success: store JWT in `EncryptedSharedPreferences`, navigate to main app 4. **SignupScreen**: - Additional fields: name, confirm password - Password strength indicator using `ShieldProgressBar` - Terms `Checkbox` (required) - "Create Account" `ShieldButton` 5. **ForgotPasswordScreen**: - Email `ShieldTextField` + submit - Success state with instructions 6. **OnboardingScreen**: - `HorizontalPager` from `androidx.compose.foundation:pager` - 4 pages with `PageIndicator` dots at bottom - Step 1: Plan cards with feature comparison - Step 2: Input for email/phone with "Add" button - Step 3: Email inputs for family invites with "Skip" button - Step 4: Success animation using `Lottie` or Compose animation - "Get Started" button navigates to Dashboard 7. **BiometricAuthScreen**: - `BiometricPrompt` setup with `BiometricManager` - `setDeviceCredentialAllowed(true)` for fallback - On success: store "useBiometric" flag and credential in `EncryptedSharedPreferences` - Show prompt on app launch if enabled 8. **AuthViewModel**: - `ViewModel` with `StateFlow` for UI state (`Loading`, `Success`, `Error`) - `login()`, `signup()`, etc. methods - `signInWithGoogle()` using `com.google.android.gms:play-services-auth` - Token management: save to `EncryptedSharedPreferences`, include in API calls 9. **AuthRepository**: - Interface + implementation - `login(email, password)` returns `Result` - `saveToken(token)`, `getToken()`, `clearToken()` 10. Wire auth state to navigation: - `MainActivity` observes auth state - Unauthenticated → show LoginScreen - Authenticated but new → show OnboardingScreen - Authenticated → show Dashboard steps: - Unit: AuthViewModel emits correct states for login/signup - Unit: Password strength calculator returns correct level - Unit: Onboarding pager advances and collects data - Integration: Google Sign-In button triggers auth flow - E2E: Complete login → onboarding → main app flow acceptance_criteria: - [ ] Login screen accepts email/password and authenticates via API - [ ] Signup screen validates inputs and creates account via API - [ ] Password reset flow sends request to API and shows success state - [ ] Onboarding has 4 steps with pager indicator and swipe navigation - [ ] Google Sign-In button works and authenticates user - [ ] Biometric auth (face/fingerprint) can be enabled after first login - [ ] Tokens are stored securely in EncryptedSharedPreferences - [ ] Logout clears all auth state and returns to login screen - [ ] Auth flow matches web app visual style validation: - Run app on emulator, complete login with test credentials - Verify JWT stored in EncryptedSharedPreferences (use Android Studio Database Inspector or log) - Test Google Sign-In flow (emulator supports mock Google account) - Complete onboarding and verify data sent to API - Test biometric prompt on device (emulator can simulate with Extended Controls) - Run `./gradlew test` for unit tests notes: - Credential Manager (`androidx.credentials:credentials`) is the modern replacement for Google Sign-In. Use it if targeting API 34+; otherwise, use `play-services-auth`. - `EncryptedSharedPreferences` from `androidx.security:security-crypto` is the standard for secure token storage. - The onboarding data should be collected in a ViewModel state and submitted to the API at the final step. - For the success animation in onboarding, consider using Lottie (`com.airbnb.android:lottie-compose`) with a checkmark animation. - The `BiometricPrompt` should be shown as a system dialog, not a custom screen. The `BiometricAuthScreen.kt` is just a wrapper/helper. - Handle configuration changes (rotation) correctly: ViewModel survives, but UI state should be restored.