Six interconnected fixes to make Olm/Megolm state persist correctly across restarts: - Fix OlmAccount/OlmSession unpickle argument order (data, key) — was reversed, causing INVALID_BASE64 on every restore - Fix pickle byte encoding: pickle() returns ByteArray, not StringBuffer; encode/decode with ISO_8859_1 - Track publishedOtkCount in SharedPreferences to avoid evicting existing OTKs on restore (only generate MAX - published new keys) - Delay startSync() until after uploadKeys() on fresh login to avoid concurrent OlmAccount access (race condition causing BAD_MESSAGE_KEY_ID) - Persist encryptedRooms to SharedPreferences so rooms stay encrypted across restarts - Check m.room.encryption in both state and timeline events (server returns it in timeline for rooms where encryption was enabled early) Also adds SAS device verification UI, DM naming improvements, and updates state.md with full architecture and debugging notes. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> |
||
|---|---|---|
| app | ||
| gradle | ||
| .gitignore | ||
| AGENTS.md | ||
| build.gradle.kts | ||
| CLAUDE.md | ||
| flake.lock | ||
| flake.nix | ||
| gradle.properties | ||
| gradlew | ||
| gradlew.bat | ||
| README.md | ||
| settings.gradle.kts | ||
| settings.json | ||
| state.md | ||
matrix-android
A minimal Android Matrix chat client with a terminal aesthetic — dark background, green monospace text, < / > message prefixes — inspired by the matrix-message living-room terminal app.
Features
- Browser-based SSO login (Custom Tabs — no password stored by the app)
- Password login fallback
- Room list after login
- Text-only chat (no images, no avatars)
- Real-time sync via Matrix
/synclong-polling - Persistent session (survives app restart)
Screenshots / UI description
> MATRIX LOGIN (login screen)
────────────────────────
homeserver: matrix.org
[ LOGIN WITH BROWSER (SSO) ]
──── or password login ────
username: @user:matrix.org
password: ••••••••
[ LOGIN ]
> ROOMS (room list)
────────────────────────────────────────
> My Room
!abc123:matrix.org
> Family chat
!xyz456:example.com
[logout]
< back My Room (chat screen)
──────────────────────────────────────
< alice
hello!
> hey there >
< alice
how are you?
──────────────────────────────────────
> message...
Requirements
- Android Studio Ladybug (2024.2) or later
- Android SDK 35
- minSdk 26 (Android 8.0)
Build
- Clone the repo
- Open the
matrix-android/folder in Android Studio - Let Gradle sync (it downloads all dependencies automatically)
- Run on a device or emulator
If you prefer the command line, first generate the Gradle wrapper:
cd matrix-android
gradle wrapper --gradle-version 8.9
./gradlew assembleDebug
The APK is at app/build/outputs/apk/debug/app-debug.apk.
NixOS / CLI build
This repo includes a flake.nix dev shell with:
- JDK 17
- Gradle
- Android SDK 35 + Build Tools 35.0.0
Enter the shell and build:
cd matrix-android
nix develop
gradle wrapper --gradle-version 8.9
./gradlew assembleDebug
The shell exports ANDROID_HOME / ANDROID_SDK_ROOT and rewrites local.properties to the Nix SDK path so Gradle uses the packaged Android SDK instead of a user-specific installation.
Login
SSO (recommended)
- Enter your homeserver (default:
matrix.org) - Tap LOGIN WITH BROWSER (SSO)
- A browser tab opens to the homeserver's login/SSO page
- After authenticating, you are redirected back to the app automatically
The app registers the matrixandroid://sso URI scheme for this redirect.
Password
Enter your full Matrix username (@user:homeserver.tld) and password directly.
Architecture
app/src/main/java/com/ltadeu6/matrix/
├── MainActivity.kt — single Activity; handles SSO deep link
├── navigation/
│ └── AppNavigation.kt — Compose NavHost (Login → Rooms → Chat)
├── auth/
│ ├── LoginScreen.kt — login UI
│ └── LoginViewModel.kt — SSO URL builder, login calls
├── rooms/
│ └── RoomListScreen.kt — joined rooms list
├── chat/
│ ├── ChatScreen.kt — message list + input bar
│ └── ChatViewModel.kt — sends messages, maps sync state
├── matrix/
│ ├── MatrixApi.kt — Retrofit interface (Matrix REST v3)
│ ├── MatrixModels.kt — serialization data classes + local models
│ └── MatrixSession.kt — singleton: auth, sync loop, state flows
└── ui/theme/
└── Theme.kt — terminal color scheme (green on black)
State flow: MatrixSession owns a syncState: StateFlow<SyncState> that all screens observe. The background sync loop continuously updates it via /sync long-polling.
Dependencies
| Library | Purpose |
|---|---|
| Jetpack Compose + Material3 | UI |
| Navigation Compose | Screen routing |
| Retrofit 2 + OkHttp | Matrix HTTP API |
| kotlinx.serialization | JSON parsing |
| retrofit2-kotlinx-serialization-converter | Retrofit ↔ kotlinx bridge |
| kotlinx.coroutines | Async / sync loop |
| androidx.browser (Custom Tabs) | SSO browser login |
Known limitations
- No E2E encryption (plain-text transport only; suitable for unencrypted rooms)
- No push notifications
- No media/file messages (text only)
- Room list is not paginated (loads whatever
/syncreturns on first sync)