# 35. Android App — Design System Components Matching Web Theme meta: id: shieldai-unified-restructure-35 feature: shieldai-unified-restructure priority: P1 depends_on: [shieldai-unified-restructure-34] tags: [android, jetpack-compose, design-system, components, mobile] objective: - Build a comprehensive set of reusable Jetpack Compose components that mirror the web app's UI primitives (Button, Card, Input, Badge, Modal, Toast) using the Android theme system from task 34. These should feel native to Android while maintaining visual consistency. deliverables: - `android/app/src/main/java/com/shieldai/android/ui/components/` — Component library: - `ShieldButton.kt` — Button component: - Variants: primary (filled), secondary (outlined), ghost (text), danger - Sizes: small, medium, large - States: enabled, disabled, loading (shows `CircularProgressIndicator`) - Icon support (leading/trailing) - `ShieldCard.kt` — Card container: - Gradient background matching web `.gradient-card` - Border with corner radius - Optional header and footer content slots - Click handling - `ShieldTextField.kt` — Text input: - Label, placeholder, error state, helper text - Password visibility toggle - Focus state styling - Validation support - `ShieldBadge.kt` — Status badge: - Variants: default, success, warning, error, info - Small rounded pill shape - Icon + text or text only - `ShieldModal.kt` — Modal bottom sheet / dialog: - `ModalBottomSheet` for mobile-appropriate presentation - Title, content, and action buttons - Swipe-to-dismiss - `ShieldToast.kt` — Toast / Snackbar: - Slide-up from bottom - Auto-dismiss after 3-4 seconds - Variants: success, error, warning, info - Action button support (e.g., "Undo") - `ShieldAvatar.kt` — User avatar: - Async image loading with Coil - Initials fallback - Sizes: small, medium, large - Online status indicator - `ShieldProgressBar.kt` — Progress indicator: - Linear progress bar with percentage - Color variants - `ShieldEmptyState.kt` — Empty state: - Icon, title, description, action button - `ShieldSkeleton.kt` — Skeleton loading: - Shimmer animation using `Brush.linearGradient` with infinite animation - Text line and rectangle shapes steps: 1. Create `android/app/src/main/java/com/shieldai/android/ui/components/` directory. 2. **ShieldButton**: - `@Composable fun ShieldButton(...)` - Primary: `Button` with `containerColor = BrandPrimary` - Secondary: `OutlinedButton` with `borderColor = BrandPrimary` - Ghost: `TextButton` with `contentColor = BrandPrimary` - Danger: `Button` with red gradient - Loading: wrap content in `Box` with `CircularProgressIndicator` overlay - Full-width: `modifier = Modifier.fillMaxWidth()` 3. **ShieldCard**: - `Card` with custom `colors` using gradient brush background - `border = BorderStroke(1.dp, BorderColor)` - `shape = MaterialTheme.shapes.large` - Header/footer as optional `@Composable` slots 4. **ShieldTextField**: - `OutlinedTextField` or `TextField` with custom colors - `VisualTransformation` for password toggle - Error state: red border and supporting text - `KeyboardOptions` for appropriate keyboard type per input 5. **ShieldBadge**: - `Surface` with `shape = RoundedCornerShape(50%)` - Small padding, small text - Background color from semantic tokens 6. **ShieldModal**: - `ModalBottomSheet` from Material3 - Or `AlertDialog` for confirmation dialogs - `SheetState` management 7. **ShieldToast**: - `SnackbarHost` with custom `Snackbar` composable - `Scaffold` provides `SnackbarHostState` - Show via `scope.launch { snackbarHostState.showSnackbar(...) }` - Custom colors per variant 8. **ShieldAvatar**: - `AsyncImage` from Coil library - Fallback: `Box` with initials text on brand color circle - Status dot: small `Canvas` circle overlay 9. **ShieldProgressBar**: - `LinearProgressIndicator` with custom `trackColor` and `progressColor` - Percentage text overlay 10. **ShieldEmptyState**: - `Column` with icon (`Image` or `Icon`), title, description, optional `ShieldButton` - Centered alignment 11. **ShieldSkeleton**: - `Brush.linearGradient` with animated offset using `rememberInfiniteTransition` - Apply as background to `Box` shapes - Reference: https://developer.android.com/jetpack/compose/animation/shimmer 12. Create a preview composable showing all components in light and dark mode. steps: - Unit: Each component renders correctly in Compose previews - Unit: ShieldButton click handler fires correctly - Unit: ShieldTextField validation shows error state - Unit: ShieldToast auto-dismisses after specified duration - Visual: All components match web app appearance in both color schemes - Visual: Components adapt to different font sizes (accessibility) acceptance_criteria: - [ ] All 10 component types exist in `ui/components/` - [ ] Each component supports light and dark modes - [ ] Button supports all 4 styles, 3 sizes, and loading state - [ ] TextField supports validation, secure entry, and focus styling - [ ] Toast/Snackbar system can queue and auto-dismiss multiple messages - [ ] Card uses gradient background matching web theme - [ ] Skeleton has smooth shimmer animation - [ ] All components use theme tokens (no hardcoded colors) - [ ] Components adapt to accessibility font sizes validation: - Open Compose preview for each component and verify appearance - Run app on emulator and navigate to component test screen - Toggle dark mode and verify all components shift colors - Enable large text in Accessibility settings and verify layouts don't break - Run `./gradlew test` for unit tests notes: - Compose previews are essential for component development. Use `@Preview` with both light and dark themes. - Use `CompositionLocalProvider` for theme overrides if needed (e.g., a section that needs inverted colors). - The shimmer animation in Compose uses `rememberInfiniteTransition` and `Brush.linearGradient`. This is the standard Android approach. - For image loading, Coil (`io.coil-kt:coil-compose`) is the standard. Add it to dependencies. - Keep components stateless where possible. State should be hoisted to parent composables or ViewModels. - Material3 `ModalBottomSheet` requires `androidx.compose.material3:material3` version 1.2.0+. Ensure BOM includes it.