Complete Android client for encrypted chat platform. 78+ Kotlin files: crypto (X3DH, Double Ratchet, AES-GCM, Ed25519, X25519, RSA-PSS), network (TCP/TLS, 50 endpoints), Hilt DI, Room+SQLCipher DB, Jetpack Compose UI with Catppuccin Mocha theme. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
5.6 KiB
5.6 KiB
Agent D: Auth Screens
Phase: 2 (UI Shells)
Depends on: Agent A (project), Agent B (theme + navigation)
Context
You are building authentication screens for "Kecalek" encrypted chat app. The login uses RSA challenge-response (no password sent to server). Registration requires email verification code. Device pairing allows linking a new device to an existing account.
Task
Create Login, Register, Pairing screens and AuthViewModel skeleton.
Files to Create
1. ui/auth/LoginScreen.kt
Jetpack Compose screen with:
- App title "Kecalek" at the top (headlineLarge)
- Subtitle "Encrypted Messaging" (bodyMedium, Subtext1 color)
- Email/Username text field (OutlinedTextField, Surface1 background)
- Password text field (password visibility toggle icon)
- Login button (filled, Lavender primary, full width)
- "Create Account" text button below (navigates to Register)
- "Link Device" text button below (navigates to Pairing)
- Server config section (expandable/collapsible):
- Host text field (default: "chat.ai-tech.news")
- Port text field (default: "9999")
- TLS toggle switch (default: off)
- Loading state: CircularProgressIndicator replacing login button
- Error state: Red error text below password field
- Biometric login button (fingerprint icon, shown only if biometric available)
Layout: Centered vertically, max width 400dp, padding 24dp.
2. ui/auth/RegisterScreen.kt
Jetpack Compose screen with:
- Back arrow in top bar (navigates back)
- Title "Create Account"
- Username text field
- Email text field
- Password text field (with visibility toggle)
- Confirm Password text field
- Register button (filled, Lavender, full width)
- Confirmation code section (shown after successful registration):
- Info text "Check your email for a verification code"
- 6-digit code input field
- Confirm button
- Loading state + Error state (same pattern as Login)
3. ui/auth/PairingScreen.kt
Jetpack Compose screen with:
- Back arrow in top bar
- Title "Link New Device"
- Info text explaining pairing process
- 8-digit pairing code displayed prominently (headlineLarge, monospace, letter-spacing)
- Countdown timer or progress indicator showing poll status
- "Waiting for authorization..." text with animated dots
- Cancel button (outlined)
- Status messages: "Device authorized", "Pairing failed", etc.
4. ui/auth/AuthViewModel.kt
package com.kecalek.chat.ui.auth
import androidx.lifecycle.ViewModel
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
import javax.inject.Inject
data class AuthUiState(
val isLoading: Boolean = false,
val error: String? = null,
val isLoggedIn: Boolean = false,
val isRegistered: Boolean = false,
val needsConfirmation: Boolean = false,
val pairingCode: String? = null,
val isPairingWaiting: Boolean = false,
val serverHost: String = "chat.ai-tech.news",
val serverPort: Int = 9999,
val useTls: Boolean = false,
val biometricAvailable: Boolean = false,
)
@HiltViewModel
class AuthViewModel @Inject constructor(
// TODO: Inject ChatClient, SessionManager
) : ViewModel() {
private val _uiState = MutableStateFlow(AuthUiState())
val uiState: StateFlow<AuthUiState> = _uiState.asStateFlow()
fun login(emailOrUsername: String, password: String) {
// TODO: Implement RSA challenge-response login
// 1. ChatClient.login(email, password)
// 2. On success: navigate to ConversationList
// 3. On failure: show error
}
fun register(username: String, email: String, password: String) {
// TODO: Implement registration
// 1. ChatClient.register(username, email, password)
// 2. On success: show confirmation code input
// 3. On failure: show error
}
fun confirmRegistration(email: String, code: String) {
// TODO: Confirm with 6-digit code
// 1. ChatClient.confirm_registration(email, code)
// 2. On success: auto-login
}
fun startPairing() {
// TODO: Start device pairing
// 1. ChatClient.pairing_start() -> get 8-digit code
// 2. Show code to user
// 3. Start polling for authorization
}
fun loginWithBiometric() {
// TODO: Biometric authentication
}
fun updateServerConfig(host: String, port: Int, useTls: Boolean) {
_uiState.value = _uiState.value.copy(
serverHost = host,
serverPort = port,
useTls = useTls,
)
}
fun clearError() {
_uiState.value = _uiState.value.copy(error = null)
}
}
Reference: iOS LoginView behavior
- Login/Register are modes of the same view (iOS uses toggle tabs)
- Server configuration is expandable (collapsed by default)
- Biometric login uses Face ID/Touch ID
- Error messages appear below the form
- Loading spinner replaces the action button
Constraints
- Use Material 3 components (OutlinedTextField, FilledTonalButton, etc.)
- Use
CatppuccinMochacolors from theme - Use
hiltViewModel()for ViewModel injection - All screens receive
navController: NavHostControllerparameter - Password fields must have visibility toggle
- Support keyboard "Done" action to submit form
- Use
rememberSaveablefor form state to survive config changes
DO NOT
- Implement actual login/register/pairing logic (just skeleton functions)
- Handle cryptographic operations
- Store credentials or keys
- Modify navigation graph (screen placeholders already exist)