177 lines
8 KiB
Markdown
177 lines
8 KiB
Markdown
# AGENTS.md — matrix-android
|
|
|
|
Progress log and architecture notes for AI agents working on this project.
|
|
|
|
---
|
|
|
|
## Status
|
|
|
|
| Component | Status |
|
|
|---|---|
|
|
| Build infrastructure (Gradle, version catalog) | done |
|
|
| NixOS CLI dev shell / Android SDK toolchain | done |
|
|
| Matrix networking (Retrofit API + Session) | done |
|
|
| SSO / password login flow | done |
|
|
| Room list screen | done |
|
|
| Chat screen (send + receive) | done |
|
|
| E2EE via local Olm wrapper | done |
|
|
| Device verification (SAS) | done |
|
|
| Terminal theme (green on black, monospace) | done |
|
|
| Launcher icon (vector adaptive) | done |
|
|
| README | done |
|
|
|
|
---
|
|
|
|
## Design decisions
|
|
|
|
### No heavy Matrix SDK
|
|
Instead of importing the full `matrix-android-sdk2` (used by Element), we call the Matrix REST API directly with Retrofit. This keeps the APK small and the code easy to follow. The trade-off is no E2E encryption support.
|
|
|
|
### Local Olm build instead of Maven-native `olm-sdk`
|
|
The upstream `org.matrix.android:olm-sdk` Maven artifact shipped a `libolm.so` that was only 4 KB ELF-aligned on arm64, which triggered Android's 16 KB page-size compatibility warning on modern devices. The app now vendors:
|
|
- `app/libs/olm-sdk-3.2.16.jar`
|
|
- `app/src/main/jniLibs/*/libolm.so`
|
|
|
|
Those JNI binaries were rebuilt locally from the Matrix `olm` Android wrapper with linker flags that produce 16 KB-aligned `LOAD` segments on arm64.
|
|
|
|
### Nix-first CLI builds
|
|
The repo includes a `flake.nix` dev shell for NixOS. It provisions:
|
|
- JDK 17
|
|
- Gradle
|
|
- Android SDK platforms 31 and 35
|
|
- Build Tools 30.0.2, 34.0.0, and 35.0.0
|
|
- Android NDK
|
|
|
|
The shell rewrites `local.properties` on entry so CLI builds use the Nix-provided SDK path.
|
|
|
|
### SSO via Custom Tabs
|
|
Login opens the homeserver's `/login/sso/redirect` endpoint in a Chrome Custom Tab. After auth the homeserver redirects to `matrixandroid://sso?loginToken=<token>`. The app intercepts this URI via an `<intent-filter>` in `AndroidManifest.xml` and exchanges the token for an access token.
|
|
|
|
### Single-activity Compose navigation
|
|
`MainActivity` → `AppNavigation` hosts all three screens (Login, Rooms, Chat) inside a single `NavHost`. No fragments.
|
|
|
|
### MatrixSession singleton
|
|
`MatrixSession` is a process-scoped singleton (companion object). It holds the Retrofit client, persists the access token in `SharedPreferences`, and runs the `/sync` long-polling loop in a coroutine scope. All screens observe `syncState: StateFlow<SyncState>`.
|
|
|
|
### Timeline is event-cache driven
|
|
`MatrixSession` now keeps a per-room in-memory `RoomEvent` cache and rebuilds the visible `Message` list from those events. This allows:
|
|
- local filtering/search over loaded events
|
|
- message edits (`m.replace`)
|
|
- reactions (`m.annotation`)
|
|
- incremental history pagination
|
|
- retrying decryption after keys arrive from another device
|
|
|
|
### Sync filter
|
|
Initial and incremental syncs use a compact filter:
|
|
- Timeline limited to 50 events per room
|
|
- State events use lazy member loading
|
|
- Presence and account data stripped
|
|
|
|
This prevents the first sync from being huge on accounts with many rooms.
|
|
|
|
---
|
|
|
|
## Known issues / future work
|
|
|
|
- **Olm is still legacy crypto**: The app uses a locally rebuilt Olm wrapper. Matrix has deprecated libolm in favor of vodozemac. A future migration should replace the current JNI/JAR setup.
|
|
- **SAS verification edge case**: A handshake-ordering fix is currently in progress. The known symptom was that self-verification could fail depending on which device confirmed matching emojis first.
|
|
- **Push notifications**: Not implemented. The app only receives messages while open.
|
|
- **History is partial**: Pagination exists only while the room exposes a `prev_batch` token and only for message history. There is still no durable local database.
|
|
- **Image/file messages**: Silently dropped (only `m.text` is rendered).
|
|
- **Rich message support is partial**: Reactions and edits are handled for loaded events, but there is no reaction picker, no redactions, and no full relation aggregation from the homeserver.
|
|
- **Search is in-memory only**: Chat search only scans events already loaded into memory for the current room.
|
|
- **Error handling**: Basic inline send/load errors are shown, but there is still no global offline state, retry queue, or structured error UX.
|
|
|
|
---
|
|
|
|
## File map
|
|
|
|
```
|
|
matrix-android/
|
|
├── settings.gradle.kts
|
|
├── build.gradle.kts
|
|
├── gradle/
|
|
│ ├── libs.versions.toml ← version catalog
|
|
│ └── wrapper/
|
|
│ └── gradle-wrapper.properties
|
|
├── app/
|
|
│ ├── build.gradle.kts
|
|
│ ├── proguard-rules.pro
|
|
│ └── src/main/
|
|
│ ├── AndroidManifest.xml
|
|
│ ├── java/com/ltadeu6/matrix/
|
|
│ │ ├── MainActivity.kt
|
|
│ │ ├── navigation/AppNavigation.kt
|
|
│ │ ├── auth/LoginScreen.kt
|
|
│ │ ├── auth/LoginViewModel.kt
|
|
│ │ ├── verification/VerificationScreen.kt
|
|
│ │ ├── rooms/RoomListScreen.kt
|
|
│ │ ├── chat/ChatScreen.kt
|
|
│ │ ├── chat/ChatViewModel.kt
|
|
│ │ ├── matrix/MatrixApi.kt
|
|
│ │ ├── matrix/MatrixModels.kt
|
|
│ │ ├── matrix/MatrixSession.kt
|
|
│ │ ├── matrix/CryptoService.kt
|
|
│ │ ├── matrix/VerificationService.kt
|
|
│ │ └── ui/theme/Theme.kt
|
|
│ └── res/
|
|
│ ├── drawable/ic_launcher_foreground.xml
|
|
│ ├── mipmap-anydpi-v26/ic_launcher.xml
|
|
│ ├── mipmap-anydpi-v26/ic_launcher_round.xml
|
|
│ └── values/{strings,colors,themes}.xml
|
|
├── flake.nix
|
|
├── flake.lock
|
|
├── gradlew
|
|
├── gradlew.bat
|
|
├── app/libs/olm-sdk-3.2.16.jar
|
|
├── app/src/main/jniLibs/
|
|
├── README.md
|
|
└── AGENTS.md
|
|
```
|
|
|
|
---
|
|
|
|
## Session 1 — initial build (2026-05-14)
|
|
|
|
- Inspected `matrix-message` terminal app for UI reference:
|
|
- `app.py`: curses chat with `< ` prefix for incoming, `> ` for outgoing
|
|
- `font.py`: big pixel ASCII font for incoming banners
|
|
- Color scheme: `COLOR_GREEN` on black
|
|
- Created full Android project from scratch (no fork)
|
|
- Chose Retrofit + kotlinx.serialization over matrix-android-sdk2 for simplicity
|
|
- SSO flow: Custom Tabs → homeserver → `matrixandroid://sso` deep link → token exchange
|
|
- Compose UI with monospace font, #00FF41 green, #0A0A0A background
|
|
- All screens text-only, no images/icons/avatars
|
|
|
|
## Session 2 — NixOS CLI + 16 KB Android compatibility (2026-05-16)
|
|
|
|
- Added `flake.nix` + `flake.lock` for NixOS CLI builds
|
|
- Generated and committed Gradle wrapper files
|
|
- Discovered Android 16 KB compatibility warning on Pixel 7a
|
|
- Confirmed APK ZIP alignment was already correct; root cause was Maven `libolm.so` being ELF-aligned to 4 KB
|
|
- Cloned upstream Matrix `olm` Android wrapper, patched NDK build flags for 16 KB page size, and rebuilt `libolm.so`
|
|
- Worked around old upstream Android wrapper assumptions:
|
|
- required platform 31 + build-tools 30.0.2
|
|
- required NDK path/layout adjustments
|
|
- required one small C++ compatibility fix in `olm::List` for modern clang/NDK
|
|
- Replaced `implementation(libs.olm.sdk)` with:
|
|
- local `app/libs/olm-sdk-3.2.16.jar`
|
|
- local `app/src/main/jniLibs/*/libolm.so`
|
|
- Validated final APK on-device:
|
|
- `libolm.so` inside the APK has `LOAD Align = 0x4000`
|
|
- APK installed and launched over ADB on the Pixel 7a
|
|
|
|
## Session 3 — verification and chat UX expansion (2026-05-16, in progress)
|
|
|
|
- Created checkpoint commit `0ede945` before feature work
|
|
- Began fixing SAS verification handshake ordering
|
|
- Added event-cache-backed message reconstruction in `MatrixSession`
|
|
- Added local support for:
|
|
- in-room search over loaded messages
|
|
- message edits via `m.replace`
|
|
- reactions via `m.annotation`
|
|
- older-message pagination via `/rooms/{roomId}/messages`
|
|
- room-level encryption/history indicators
|
|
- inline chat error feedback
|
|
- Added first-pass DM name resolution fallback via joined members lookup
|
|
- This session is still dirty in the worktree; verify behavior before treating these changes as final
|