|
| 1 | +# Zero Camera |
| 2 | + |
| 3 | +Zero is a minimal camera app for the Light Phone III. It aims to take photos with as little processing as possible. |
| 4 | + |
| 5 | +**Tech Stack:** Kotlin 2.1.0, Jetpack Compose/Material 3, Camera2 API, Gradle 9.2.1 (Kotlin DSL), Min SDK 29, Target SDK 35 |
| 6 | + |
| 7 | +## Build Commands |
| 8 | + |
| 9 | +```bash |
| 10 | +./gradlew assembleDebug # Build debug APK |
| 11 | +./gradlew assembleRelease # Build release APK (minified) |
| 12 | +./gradlew clean assembleDebug # Clean build |
| 13 | +./gradlew build # Full build with tests |
| 14 | +``` |
| 15 | + |
| 16 | +## Lint & Code Style |
| 17 | + |
| 18 | +This project uses **ktlint 1.8.0** for code formatting. Always run ktlint before committing. |
| 19 | + |
| 20 | +```bash |
| 21 | +./gradlew ktlintCheck # Check for code style violations |
| 22 | +./gradlew ktlintFormat # Auto-fix formatting issues |
| 23 | +./gradlew lint # Run Android lint |
| 24 | +./gradlew lintDebug |
| 25 | +``` |
| 26 | + |
| 27 | +**ktlint Configuration** (see `.editorconfig`): |
| 28 | +- Composable functions MAY use PascalCase (standard Compose convention) |
| 29 | +- Wildcard imports are allowed for Android/Compose packages |
| 30 | + |
| 31 | +## Project Structure |
| 32 | + |
| 33 | +``` |
| 34 | +app/src/main/java/com/vandam/zero/ |
| 35 | +├── MainActivity.kt # Entry point, key event handling |
| 36 | +├── CameraViewModel.kt # UI state management, settings persistence |
| 37 | +├── camera/ |
| 38 | +│ ├── CameraController.kt # Camera2 API implementation |
| 39 | +│ └── GrayscaleConverter.kt # BT.709 grayscale conversion |
| 40 | +└── ui/ |
| 41 | + ├── CameraScreen.kt # Main screen composable |
| 42 | + ├── components/ |
| 43 | + │ ├── CameraSliders.kt # Exposure/ISO/Shutter sliders |
| 44 | + │ ├── Crosshair.kt # Focus indicator |
| 45 | + │ ├── GridOverlay.kt # Rule of thirds grid |
| 46 | + │ └── ModifierExtensions.kt |
| 47 | + └── theme/ |
| 48 | + └── CameraTheme.kt # Typography, colors, dimensions |
| 49 | +``` |
| 50 | + |
| 51 | +## Code Style Guidelines |
| 52 | + |
| 53 | +### Naming Conventions |
| 54 | + |
| 55 | +| Type | Convention | Example | |
| 56 | +|------|------------|---------| |
| 57 | +| Composable functions | PascalCase | `CameraScreen()`, `ExposureSlider()` | |
| 58 | +| Regular functions | camelCase | `takePhoto()`, `formatShutterSpeed()` | |
| 59 | +| Classes/Objects | PascalCase | `CameraController`, `CameraColors` | |
| 60 | +| Constants | SCREAMING_SNAKE_CASE | `OUTPUT_FORMAT_JPEG`, `R_WEIGHT` | |
| 61 | +| StateFlow properties | camelCase with underscore prefix for private | `_isCapturing` / `isCapturing` | |
| 62 | + |
| 63 | +### Compose Best Practices |
| 64 | + |
| 65 | +1. **Composable naming**: Use PascalCase for `@Composable` functions that emit UI |
| 66 | +2. **State hoisting**: Keep state in ViewModel, pass values and callbacks to composables |
| 67 | +3. **Modifier parameter**: Always accept `modifier: Modifier = Modifier` as a parameter |
| 68 | +4. **Parameter order**: Required params first, then `modifier`, then optional params, then trailing lambda |
| 69 | +5. **Single responsibility**: Each composable should emit 0 or 1 layout node |
| 70 | + |
| 71 | +```kotlin |
| 72 | +@Composable |
| 73 | +fun MyButton( |
| 74 | + text: String, // Required |
| 75 | + onClick: () -> Unit, // Required callback |
| 76 | + modifier: Modifier = Modifier, // Optional, starts with modifier |
| 77 | + enabled: Boolean = true, // Other optional params |
| 78 | +) { ... } |
| 79 | +``` |
| 80 | + |
| 81 | +### State Management |
| 82 | + |
| 83 | +- Use `StateFlow` for observable state in ViewModel |
| 84 | +- Collect state with `collectAsState()` in composables |
| 85 | +- Group related state into data classes when practical |
| 86 | +- Private mutable state with public read-only exposure: |
| 87 | + |
| 88 | +```kotlin |
| 89 | +private val _isCapturing = MutableStateFlow(false) |
| 90 | +val isCapturing: StateFlow<Boolean> = _isCapturing |
| 91 | +``` |
| 92 | + |
| 93 | +### Import Organisation |
| 94 | + |
| 95 | +Imports are ordered lexicographically by ktlint. Wildcard imports are allowed for Android/Compose packages. |
| 96 | + |
| 97 | +### Error Handling |
| 98 | + |
| 99 | +- Use nullable types with safe calls (`?.`) for optional operations |
| 100 | +- Log errors with Android Log class: `Log.e("Tag", "message")` |
| 101 | +- Camera operations should handle `CameraAccessException` |
| 102 | +- Never crash on permission denial - show appropriate UI |
| 103 | + |
| 104 | +### Documentation |
| 105 | + |
| 106 | +- Use KDoc (`/** */`) for public APIs and complex functions |
| 107 | +- Document non-obvious parameters and return values |
| 108 | + |
| 109 | +## Theme Constants |
| 110 | + |
| 111 | +Use centralized constants from `ui/theme/CameraTheme.kt`: |
| 112 | + |
| 113 | +```kotlin |
| 114 | +CameraColors.background // Color.Black |
| 115 | +CameraColors.onSurface // Color.White |
| 116 | +CameraColors.onSurfaceVariant // Color.White.copy(alpha = 0.7f) |
| 117 | +CameraDimens.toolbarWidth // 60.dp |
| 118 | +CameraDimens.crosshairSize // 80.dp |
| 119 | +CameraTypography.toolbarText // 32.sp, ExtraBold |
| 120 | +CameraTiming.TOAST_DISPLAY_DURATION_MS // 2000L |
| 121 | +``` |
0 commit comments