docs: sync markdown with current code

This commit is contained in:
Hossain Khan
2026-07-09 21:08:03 -04:00
parent 21b76c7297
commit 659cab9284
8 changed files with 23 additions and 19 deletions
+4 -3
View File
@@ -99,6 +99,7 @@ app/src/main/java/ink/trmnl/android/
### Main Features & Screens
- `TrmnlMirrorDisplayScreen` - Main display showing TRMNL image
- `AppSettingsScreen` - Configuration (API token, server URL)
- `AppInfoScreen` - App info and external links
- `DisplayRefreshLogScreen` - Refresh history/logs
- `TrmnlImageRefreshWorker` - Background image refresh job
- `TrmnlWorkScheduler` - Manages WorkManager scheduling
@@ -121,7 +122,7 @@ All workflows are in `.github/workflows/`:
- Triggers: Pushes to main, manual, GitHub releases
- Builds signed release APK and AAB
- Requires: `KEYSTORE_BASE64`, `KEYSTORE_PASSWORD`, `KEY_ALIAS` secrets
- Attaches APK to GitHub releases automatically
- Attaches APK and AAB to GitHub releases automatically
4. **version-management.yml** (Version Bumping)
- Manual workflow for updating app version
@@ -149,7 +150,7 @@ All workflows are in `.github/workflows/`:
- Local release builds will fail without `secret.properties` file
### Issue: WorkManager Minimum 15 Minute Interval
**Solution:** Android OS limitation, not a bug. WorkManager enforces 15-min minimum between periodic jobs for battery optimization.
**Solution:** Android OS limitation, not a bug. WorkManager enforces a 15-min minimum between periodic jobs for battery optimization, and the app adds a 60-second scheduling buffer before enqueueing periodic work.
## Version Management
@@ -186,7 +187,7 @@ See `RELEASE_CHECKLIST.md` for complete release process.
- **Unit Tests:** `app/src/test/` (Robolectric, MockK, Truth assertions)
- **Run Tests:** `./gradlew testDebugUnitTest`
- **Test Configuration:** Tests use JVM args `-XX:+EnableDynamicAgentLoading` (required for MockK)
- **Current Status:** 216 tests, 3 skipped (as of last run)
- **Current Status:** Run `./gradlew testDebugUnitTest` to verify the current test suite state.
## Making Code Changes
+6 -6
View File
@@ -12,7 +12,7 @@ TRMNL Android is a native Android app that displays TRMNL e-ink device content o
- Min SDK: 28 (Android 9.0 Pie), Target SDK: 36 (Android 16.0)
- UI: Jetpack Compose with Circuit UDF architecture (Slack's unidirectional data flow)
- DI: Metro 0.12.1 (dev.zacsweers.metro) with MetroX Android for compile-time DI
- Background Work: WorkManager 2.11.2 (15-minute minimum interval limitation)
- Background Work: WorkManager 2.11.2 (15-minute minimum interval plus a 60-second scheduling buffer)
- Networking: Retrofit 2.11.0 + OkHttp 4.12.0 + Moshi 1.15.2
- Image Loading: Coil 3.4.0 with OkHttp integration
- API Result Modeling: EitherNet 2.0.0 from Slack
@@ -99,7 +99,7 @@ app/src/main/java/ink/trmnl/android/
│ ├── model/ # API response models
│ ├── util/ # Networking utilities
├── ui/ # Jetpack Compose screens
│ ├── aboutapp/AppAboutScreen.kt # About app screen
│ ├── aboutapp/AppInfoScreen.kt # About app screen
│ ├── devicemodel/DeviceModelSelectorScreen.kt # Device model picker
│ ├── display/TrmnlMirrorDisplayScreen.kt # Main display
│ ├── icons/Icons.kt # Custom vector icons
@@ -139,7 +139,7 @@ app/src/main/java/ink/trmnl/android/
- Circuit integration: `arg("circuit.codegen.mode", "metro")`
**Background Work:**
- WorkManager with 15-minute minimum interval (Android OS limitation)
- WorkManager with 15-minute minimum interval plus a 60-second scheduling buffer (Android OS limitation)
- Periodic work: `IMAGE_REFRESH_PERIODIC_WORK_NAME` for scheduled refreshes
- One-time work: `IMAGE_REFRESH_ONETIME_WORK_NAME` for manual refreshes
- `TrmnlImageRefreshWorker` fetches image, logs results, returns URL
@@ -206,7 +206,7 @@ The project uses optimized Gradle settings based on best practices from the [Now
- HttpLoggingInterceptor enabled with BODY level
**Release:**
- Requires production keystore from CI secrets or `secret.properties`
- Requires production keystore file plus `KEYSTORE_PASSWORD` and `KEY_ALIAS` from CI secrets or `secret.properties`
- Keystore env vars: `KEYSTORE_PASSWORD`, `KEY_ALIAS`
- Code shrinking: `isMinifyEnabled = true`, `isShrinkResources = true`
- ProGuard: `proguard-android-optimize.txt` + `proguard-rules.pro`
@@ -233,7 +233,7 @@ The project uses optimized Gradle settings based on best practices from the [Now
- **First build slow**: Expected for clean builds, ~1-2 minutes with optimized Gradle settings
- **"Sharing is only supported for boot loader classes"**: Harmless Robolectric warning, tests still pass
- **"INVISIBLE_REFERENCE" in NetworkingTools.kt:56**: Expected, uses internal Kotlin API (documented warning)
- **WorkManager 15-min minimum**: Android OS limitation, not a bug
- **WorkManager 15-min minimum**: Android OS limitation, plus a 60-second scheduling buffer, not a bug
- **Metro codegen errors about missing modules**: Run `assembleDebug` first to generate code
## Code Style
@@ -281,7 +281,7 @@ The project uses optimized Gradle settings based on best practices from the [Now
From `.github/copilot-instructions.md`:
- Always run format + lint + test before committing
- Use Circuit's `@CircuitInject` for UI components
- WorkManager periodic work has 15-min minimum interval
- WorkManager periodic work has a 15-min minimum interval plus a 60-second scheduling buffer
- Image loading handles HTTP 403 auto-refresh for expired URLs
- Metro uses a Kotlin compiler plugin — no KAPT, no KSP for DI itself
- Trust the documented build commands - they're CI-validated
+3 -3
View File
@@ -16,7 +16,7 @@ The app uses a modern Android architecture with the following components:
- **UI**: Jetpack Compose with Circuit UDF architecture
- **Background Processing**: WorkManager for scheduled image (re)loading
- **Networking**: Retrofit and OkHttp for API communication
- **Networking**: Retrofit, OkHttp, Moshi, and EitherNet for API communication
- **DI**: Metro for dependency injection
- **Data Storage**: DataStore for preferences and token storage
@@ -84,7 +84,7 @@ To build the release APK:
./gradlew assembleRelease
```
This command builds a single, signed APK that works both locally (with debug keystore fallback) and in CI (with production keystore).
This command builds a single, signed APK. Local release builds still require a decoded production keystore file plus `KEYSTORE_PASSWORD` and `KEY_ALIAS`; CI provides those values through secrets.
### Release Process
@@ -92,7 +92,7 @@ For instructions on creating new releases and managing versions across the proje
### Snapshot Builds
Automatic snapshot release builds are available in the [release workflow](https://github.com/usetrmnl/trmnl-android/actions/workflows/android-release.yml) artifacts.
Automatic snapshot release builds are available in the [release workflow](https://github.com/usetrmnl/trmnl-android/actions/workflows/android-release.yml) artifacts as both APK and AAB files.
<img width="823" alt="trmnl-artifacts-light" src="https://github.com/user-attachments/assets/da6a4171-7e6a-41c7-915e-de4cf5c18e65#gh-light-mode-only" />
<img width="819" alt="trmnl-artifacts-dark" src="https://github.com/user-attachments/assets/3740917c-a42f-402e-938a-408c0940391b#gh-dark-mode-only" />
+2 -2
View File
@@ -44,8 +44,8 @@ Download the app from Google Play Store:
### Limitations 🚧
1. Right now, screen lock using Google's [recommended](https://developer.android.com/develop/background-work/background-tasks/awake/screen-on) **`FLAG_KEEP_SCREEN_ON`** is not working on e-Ink tablet due to strict battery optimization. So, if you plan to keep the screen on indefinitely, you should set that in the device settings.
* On normal Android tablet or device, screen wake lock should work. However it's not recommended to use it without device being always plugged-in 🔌.
2. Currently the app uses Android WorkManager to schedule refresh job and it has minimum interval of ⏰ `15 min` between jobs. So, if your TRMNL is setup to refresh every `5 min`, you will not see it refresh until `15 min` is elapsed.
* This can be overcome by using some clever logic or not using `WorkManager`. However, this is a OS optimized and reliable way to refresh image periodically.
2. The app uses Android WorkManager to schedule refresh jobs. Periodic work is rounded up to a minimum of ⏰ `15 min`, and the scheduler adds a `60 s` buffer before enqueuing the job. So if your TRMNL is configured to refresh every `5 min`, the Android app will still wait for the WorkManager minimum.
* This can be overcome by using custom scheduling logic or by not using `WorkManager`. However, this is an OS-optimized and reliable way to refresh images periodically.
* Imagine a user running the app on an Android phone or tablet. When the app is in the background (e.g., the screen is off), it avoids unnecessary image refresh calls, conserving the user's battery. These optimizations are built into `WorkManager`.
3. App has minimum version requirement of **`9.0`**, see [discussion](https://github.com/usetrmnl/trmnl-android/issues/101#issuecomment-4182812396) for more info.
+1 -1
View File
@@ -90,7 +90,7 @@ This will trigger another workflow to automatically build the release APK and up
<details><summary>In case APK is not attached automatically, follow this 👇</summary>
**Download signed release build**: Use the workflow that automatically signs and builds release APK.
**Download signed release build**: Use the workflow that automatically signs and builds the release APK and AAB.
Example PR with instructions:
<img width="1315" height="321" alt="Screenshot 2025-08-15 at 1 29 48PM" src="https://github.com/user-attachments/assets/f600cb63-09f1-4731-bde5-afb54210bb5f" />
+2 -2
View File
@@ -14,7 +14,7 @@ signing the app during development.
## Production Keystore
The production keystore (`trmnl-app-release.keystore`) is used for all release builds and is stored as a base64-encoded secret in GitHub Actions. The keystore is decoded during CI/CD builds. 📚 See [`RELEASE_CHECKLIST.md`](../RELEASE_CHECKLIST.md) for release process.
The production keystore (`trmnl-app-release.keystore`) is used for all release builds and is stored as a base64-encoded secret in GitHub Actions. The keystore is decoded during CI/CD builds, and local release builds must provide the decoded keystore file plus `KEYSTORE_PASSWORD` and `KEY_ALIAS`. 📚 See [`RELEASE_CHECKLIST.md`](../RELEASE_CHECKLIST.md) for release process.
**Note:** This project uses only build types (debug/release), not product flavors.
@@ -41,7 +41,7 @@ The following GitHub Actions secrets are required:
### CI/CD Workflows Using Production Keystore
- **`android-release.yml`**: Builds and signs release APKs using the production keystore in CI, with debug keystore fallback for local builds
- **`android-release.yml`**: Builds and signs release APKs and AABs using the production keystore in CI
The workflow decodes the keystore from the base64 secret and provides the necessary environment variables for signing.
@@ -11,6 +11,8 @@ synchronized with the TRMNL content.
This document outlines the requirements for the TRMNL Android application.
> Note: This is a requirements snapshot, not a full implementation audit. When a requirement conflicts with the current source code, the source code is the source of truth for shipped behavior.
## 2. User Requirements
### Authentication
@@ -263,4 +265,4 @@ This section provides guidance for teams implementing the TRMNL app on other pla
| LIM-11 | Flutter has more limited e-ink display optimization capabilities compared to native implementations. |
| LIM-12 | React Native may have performance limitations when handling large image rendering on low-end devices. |
This PRD provides a comprehensive guide for implementing and maintaining the TRMNL app. Development teams should use this document to ensure the application meets all required functionality while following platform-specific best practices.
This PRD provides a comprehensive guide for implementing and maintaining the TRMNL app. Development teams should use this document to ensure the application meets all required functionality while following platform-specific best practices.
+2 -1
View File
@@ -22,4 +22,5 @@ The TRMNL API uses two authentication methods:
2. **User-level authentication** - Uses `Authorization: Bearer` header with account API key
- Implemented in `TrmnlUserApiService.kt`
- Used for account operations (device management, plugin settings, etc.)
- Kept for backward compatibility and account operations such as device management
- The service is currently deprecated in code because battery reporting now uses the device-level `Percent-Charged` header