Skip to content

Commit 58cabd2

Browse files
authored
Merge pull request #1 from vandamd/camera2
Migration from CameraX to Camera2
2 parents 3be02f1 + 41b463d commit 58cabd2

16 files changed

Lines changed: 3390 additions & 1655 deletions

.editorconfig

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
[*.{kt,kts}]
2+
ktlint_function_naming_ignore_when_annotated_with = Composable
3+
ktlint_standard_no-wildcard-imports = disabled

AGENTS.md

Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
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+
```

README.md

Lines changed: 27 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,17 +6,39 @@
66
![GitHub Release](https://img.shields.io/github/v/release/vandamd/zero)
77

88
## Installation
9-
The lastest .apk file is available in [releases](https://github.com/vandamd/zero/releases/latest).
9+
10+
The latest .apk file is available in [releases](https://github.com/vandamd/zero/releases/latest).
1011

1112
I recommend using [Obtainium](https://github.com/ImranR98/Obtainium) and adding the repository's URL to receive updates.
1213

1314
## Features
15+
1416
- Uses the Light Phone III's camera button
15-
- Capture images without any processing
16-
- RAW or JPGs
17-
- Manual and Auto mode
17+
- Minimal processing
18+
- Noise reduction: OFF
19+
- Edge enhancement: OFF
20+
- Hot pixel correction: OFF
21+
- Lens shading correction: OFF
22+
- Distortion correction: OFF
23+
- Tonemap: linear identity curve
24+
- Color correction: FAST (AWB on, aberration OFF)
25+
- Low latency (~160ms from shutter press to exposure) for JPG and RAW
26+
- Zero shutter lag with Hyperfocal mode
27+
- Manual and Auto exposure
28+
- RAW and JPG output
29+
- Composition Grid
30+
- Preview images after capture (long press grid icon to toggle)
31+
- High-res viewfinder (long press flash icon to toggle)
32+
- Red text mode for night photography (long press mode button to toggle). Requires granting permission, outlined in [Greyscale Toggle](#greyscale-toggle).
33+
34+
## Capture Modes
35+
36+
- **JPG** - Standard capture with autofocus and flash
37+
- **HF** - Hyperfocal mode for street photography. Instant shutter with focus locked at 2.2m (sharp from ~1.1m to infinity). No flash.
38+
- **RAW** - Unprocessed DNG files
1839

1940
## Key Mapper
41+
2042
If you use Key Mapper, I recommend setting a trigger to open Zero when the camera button is pressed. Don't forget to add a constraint so it only works when Zero is not in the foreground!
2143

2244
## Greyscale Toggle
@@ -30,6 +52,7 @@ adb shell pm grant com.vandam.zero android.permission.WRITE_SECURE_SETTINGS
3052
```
3153

3254
## Support
55+
3356
Zero is developed and maintained in my free time.
3457

3558
If you find it useful, please [consider sponsoring](https://github.com/sponsors/vandamd)! :)

app/build.gradle.kts

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,14 @@ plugins {
22
id("com.android.application")
33
id("org.jetbrains.kotlin.android")
44
id("org.jetbrains.kotlin.plugin.compose")
5+
id("org.jlleitschuh.gradle.ktlint")
6+
}
7+
8+
ktlint {
9+
version.set("1.8.0")
10+
android.set(true)
11+
verbose.set(true)
12+
outputColorName.set("RED")
513
}
614

715
android {
@@ -12,8 +20,8 @@ android {
1220
applicationId = "com.vandam.zero"
1321
minSdk = 29
1422
targetSdk = 35
15-
versionCode = 2
16-
versionName = "1.1"
23+
versionCode = 3
24+
versionName = "1.2"
1725

1826
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
1927
vectorDrawables {
@@ -60,11 +68,6 @@ android {
6068
}
6169

6270
dependencies {
63-
implementation("androidx.camera:camera-core:1.5.1")
64-
implementation("androidx.camera:camera-camera2:1.5.1")
65-
implementation("androidx.camera:camera-lifecycle:1.5.1")
66-
implementation("androidx.camera:camera-view:1.5.1")
67-
6871
implementation(platform("androidx.compose:compose-bom:2024.09.00"))
6972
implementation("androidx.compose.ui:ui")
7073
implementation("androidx.compose.material3:material3")

app/proguard-rules.pro

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,12 @@
77
-dontwarn org.bouncycastle.**
88
-dontwarn org.conscrypt.**
99
-dontwarn org.openjsse.**
10+
11+
# Remove all Log calls in release builds
12+
-assumenosideeffects class android.util.Log {
13+
public static int v(...);
14+
public static int d(...);
15+
public static int i(...);
16+
public static int w(...);
17+
public static int e(...);
18+
}

0 commit comments

Comments
 (0)