# Agent G: Profile + Group + Verification Screens ## Phase: 2 (UI Shells) ## Depends on: Agent A, Agent B (theme + navigation), Agent C (models) ## Context These screens handle user profiles, group management, and contact verification. Verification uses Signal-style safety numbers, fingerprints, and QR codes. ## Task Create Profile, EditProfile, GroupInfo, SafetyNumber, QRScanner, DeviceList screens and ViewModels. ## Files to Create ### 1. ui/profile/ProfileScreen.kt Jetpack Compose screen showing user profile: - **Top bar**: Back arrow + "Profile" title - **Own profile** (editable mode): - Large circular avatar (80dp) with camera overlay icon - Username (editable text field) - Email (read-only, Subtext1 color) - Phone text field (with visibility toggle switch) - Location text field (with visibility toggle switch) - "Save" button (Lavender, full width) - Divider - "Key Rotation" button (Peach/warning color) - "Authorize Device" button - "Logout" button (Red color) - **Other user profile** (read-only mode): - Large circular avatar (80dp) - Username (headlineMedium) - Email (bodyMedium, Subtext1) - Phone (if visible) - Location (if visible) - Divider - **Security section**: - Verification status badge ("Verified" green or "Not Verified" muted) - "View Safety Number" button (navigates to Verification screen) - Fingerprint display (monospace, small) ### 2. ui/profile/EditProfileScreen.kt (Can be merged into ProfileScreen as a mode, or separate — up to implementation) ### 3. ui/profile/ProfileViewModel.kt ```kotlin package com.kecalek.chat.ui.profile import androidx.lifecycle.SavedStateHandle 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 com.kecalek.chat.data.model.UserProfile import javax.inject.Inject data class ProfileUiState( val profile: UserProfile? = null, val isOwnProfile: Boolean = false, val isEditing: Boolean = false, val isLoading: Boolean = false, val error: String? = null, val verificationStatus: String = "unverified", // "verified", "trusted", "unverified" val fingerprint: String = "", ) @HiltViewModel class ProfileViewModel @Inject constructor( savedStateHandle: SavedStateHandle, // TODO: Inject repositories ) : ViewModel() { val userId: String = savedStateHandle["userId"] ?: "" private val _uiState = MutableStateFlow(ProfileUiState()) val uiState: StateFlow = _uiState.asStateFlow() fun loadProfile() { /* TODO */ } fun updateProfile(phone: String?, location: String?, phoneVisible: Boolean, locationVisible: Boolean) { /* TODO */ } fun updateAvatar(imageBytes: ByteArray) { /* TODO */ } fun rotateKeys() { /* TODO */ } fun logout() { /* TODO */ } } ``` ### 4. ui/groups/GroupInfoScreen.kt Jetpack Compose screen for group management: - **Top bar**: Back arrow + "Group Info" title - **Group avatar** (80dp, circular) with camera overlay (creator only) - **Group name** (editable by creator, with edit icon) - **Member count** ("X members") - Divider - **Members list**: - Each member: avatar (32dp) + username + email - Creator badge (small crown or "Admin" label) - Verified checkmark for verified members (not self) - Creator can tap member → "Remove" option - **"Add Member"** button (creator or all members, depending on server): - Opens dialog with email input - Divider - **"Leave Group"** button (Red, outlined) - **"Delete Group"** button (Red, filled — creator only) - Confirmation dialogs for destructive actions ### 5. ui/groups/CreateGroupSheet.kt Bottom sheet for group creation (reuse from Agent E or create here): - Group name field - Email field + "Add" button - Member chip list - "Create" button ### 6. ui/groups/InvitationBanner.kt Composable for invitation display in conversation list: - Peach/Amber border card - Group name + "invited by Username" - Accept (Green icon button) + Decline (Red icon button) ### 7. ui/verification/SafetyNumberScreen.kt Jetpack Compose screen for contact verification: - **Top bar**: Back arrow + "Verify Contact" title - **Verification status badge**: - "Verified" (Green background) - "Trusted" (Lavender background) - "Not Verified" (Surface1 background) - **Safety number display**: - 60 digits displayed as 12 groups of 5 - 3 lines of 4 groups each - Monospace font, large text - Info text: "Compare this number with your contact's device" - **QR code**: - Generated QR code image (200dp) - "Show my QR code" section - **Fingerprints section**: - "My fingerprint": 30 digits (6 groups of 5, 2 lines) - "Their fingerprint": 30 digits - Monospace font - **Action buttons**: - "Mark as Verified" (Green, filled) — shown when not verified - "Remove Verification" (Red, outlined) — shown when verified - "Scan QR Code" button (opens camera) ### 8. ui/verification/QRScannerScreen.kt Camera-based QR code scanner: - Full-screen camera preview - QR code detection overlay (frame guide) - On successful scan: verify and show result - "Cancel" button overlay - Uses CameraX + ZXing for detection ### 9. ui/verification/VerificationVM.kt ```kotlin package com.kecalek.chat.ui.verification import androidx.lifecycle.SavedStateHandle 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 VerificationUiState( val peerUsername: String = "", val verificationStatus: String = "unverified", val safetyNumber: String = "", // 60 digits formatted val myFingerprint: String = "", // 30 digits formatted val peerFingerprint: String = "", // 30 digits formatted val qrCodeData: ByteArray? = null, // QR payload for generation val isLoading: Boolean = false, val scanResult: String? = null, // Success/failure message ) @HiltViewModel class VerificationVM @Inject constructor( savedStateHandle: SavedStateHandle, // TODO: Inject ChatClient for verification operations ) : ViewModel() { val userId: String = savedStateHandle["userId"] ?: "" private val _uiState = MutableStateFlow(VerificationUiState()) val uiState: StateFlow = _uiState.asStateFlow() fun loadVerificationData() { /* TODO: get safety number, fingerprints, QR data */ } fun markAsVerified() { /* TODO: verify_contact() */ } fun removeVerification() { /* TODO: unverify_contact() */ } fun processQrScanResult(data: String) { /* TODO: verify_qr_code() */ } } ``` ### 10. ui/devices/DeviceListScreen.kt Jetpack Compose screen for device management: - **Top bar**: Back arrow + "My Devices" title - **Device list** (LazyColumn): - Each device: device name/ID + last seen timestamp - Current device highlighted with "(This device)" label - "Remove" button for other devices (Red icon) - **Info text**: "Removing a device will end its session" ### 11. ui/devices/DeviceViewModel.kt ```kotlin package com.kecalek.chat.ui.devices 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 Device( val id: String, val name: String?, val lastSeen: String, val isCurrentDevice: Boolean, ) data class DeviceListState( val devices: List = emptyList(), val isLoading: Boolean = false, val error: String? = null, ) @HiltViewModel class DeviceViewModel @Inject constructor( // TODO: Inject ChatClient ) : ViewModel() { private val _uiState = MutableStateFlow(DeviceListState()) val uiState: StateFlow = _uiState.asStateFlow() fun loadDevices() { /* TODO */ } fun removeDevice(deviceId: String) { /* TODO */ } } ``` ## Safety Number Display Format ``` 12345 67890 12345 67890 12345 67890 12345 67890 12345 67890 12345 67890 ``` - 12 groups of 5 digits - 3 lines of 4 groups - Monospace font - Large text (20sp) ## Fingerprint Display Format ``` 12345 67890 12345 67890 12345 67890 ``` - 6 groups of 5 digits - 2 lines of 3 groups - Monospace font ## Constraints - Use Material 3 components - CameraX for QR scanner (not deprecated Camera1) - QR generation via ZXing BarcodeEncoder - Confirmation dialogs for destructive actions (leave group, delete group, remove device) - Creator-only actions clearly gated in UI ## DO NOT - Implement actual crypto verification logic - Generate real safety numbers or fingerprints - Handle actual server communication - Implement actual QR code encoding/decoding logic