No description
Find a file
ltadeu6 3747172488 Fix E2EE encryption surviving app restarts
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>
2026-05-17 15:21:09 -03:00
app Fix E2EE encryption surviving app restarts 2026-05-17 15:21:09 -03:00
gradle Add Nix shell and local 16KB-aligned Olm build 2026-05-16 15:59:41 -03:00
.gitignore Add Nix shell and local 16KB-aligned Olm build 2026-05-16 15:59:41 -03:00
AGENTS.md Add chat UX improvements and fix DM naming sync 2026-05-16 16:24:32 -03:00
build.gradle.kts Initial commit 2026-05-14 23:22:06 -03:00
CLAUDE.md Initial commit 2026-05-14 23:22:06 -03:00
flake.lock Add Nix shell and local 16KB-aligned Olm build 2026-05-16 15:59:41 -03:00
flake.nix Fix E2EE encryption surviving app restarts 2026-05-17 15:21:09 -03:00
gradle.properties Initial commit 2026-05-14 23:22:06 -03:00
gradlew Add Nix shell and local 16KB-aligned Olm build 2026-05-16 15:59:41 -03:00
gradlew.bat Add Nix shell and local 16KB-aligned Olm build 2026-05-16 15:59:41 -03:00
README.md Add Nix shell and local 16KB-aligned Olm build 2026-05-16 15:59:41 -03:00
settings.gradle.kts Initial commit 2026-05-14 23:22:06 -03:00
settings.json Initial commit 2026-05-14 23:22:06 -03:00
state.md Fix E2EE encryption surviving app restarts 2026-05-17 15:21:09 -03:00

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 /sync long-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

  1. Clone the repo
  2. Open the matrix-android/ folder in Android Studio
  3. Let Gradle sync (it downloads all dependencies automatically)
  4. 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

  1. Enter your homeserver (default: matrix.org)
  2. Tap LOGIN WITH BROWSER (SSO)
  3. A browser tab opens to the homeserver's login/SSO page
  4. 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 /sync returns on first sync)