README and changelog for the app's foundation (closes #2) 3f3508bd5f16
How to sign in, what works today and what is planned, the layout of the code, and the screenshots rendered from the previews.
- Author
- Andy McCall @andymccall <[email protected]>
- via
- agent
agent:claude@andymccall <[email protected]> · keyless, under your grant for changes,refs,seal · key f4ec0f2e7863
- When
- · 2026-10-24 22:32 UTC
- Signature
- ◈ sealed by agent:claude@andymccall
- Hash
3f3508bd5f16aee7568582819e69ad40a442e03a2fac756de30da440320a69f5- Depends on
- 0a2858c0dad3 1416009b06af 144cbf7bf2b5 206d05a042e7 57c0824300db 5ae4af48ddd2 9481bfa7c5d0 d07edbd2c022
- Depended on by
- d1f635be9442 e2df27a81e5d 0381bd8af55e db00632a61d6
- In bookmarks
- main
- In tags
- v0.1.0 v0.1.1 v0.1.2 v0.1.3 v0.1.4 v0.1.5 v0.1.6 v0.1.7 v0.1.8 v0.1.9
- Edits
- 15 atoms, 8 tree operations, 8 files
Browse the tree at this change
added docs/screenshots/dashboard-phone.png +0 −0
No line changes.
added docs/screenshots/inbox-phone-dark.png +0 −0
No line changes.
added docs/screenshots/repositories-foldable.png +0 −0
No line changes.
added docs/screenshots/repositories-tablet-dark.png +0 −0
No line changes.
added docs/screenshots/sign-in-code.png +0 −0
No line changes.
added docs/screenshots/sign-in.png +0 −0
No line changes.
modified CHANGELOG.md +1 −0
| … | 1 unchanged lines |
||
| 2 | 2 | ||
| 3 | 3 | ## Unreleased |
|
| 4 | 4 | ||
| 5 | + | - **Sign in, the hub's API, navigation and foldable layouts (#2):** sign in with the hub's device flow, as `oh auth login` does (a code to approve on the hub's `/device` page, with the password and second factor there, never in the app), against oxygenhub.io or any hub; the token sealed with a Keystore key, kept out of backups, and revoked on the hub when signing out. A hand-written Kotlin client for the hub's API: the user, repositories, a repository's issues, the inbox and marking it read. A dashboard (greeting, recent repositories, unread count), repositories with their issues side by side from a medium width up (a foldable open, a tablet), and the inbox with a badge; a navigation bar on a phone and a rail when wider; light and dark, in dynamic colour or the hub's own palette; loading, empty and error states with pull to refresh. The placeholder login (username and password) and tabs are gone. Unit tests for the client (against fixtures from hub-api's types) and the view models; README screenshots rendered from Compose previews without an emulator. |
|
| 5 | 6 | - **The project set up properly (#1):** the application id and package are `io.oxygenhub.android` (permanent), minSdk 31 (Android 12), the Gradle and build caches no longer tracked, `rust-core/` part of the repository (it was a nested, empty Git repository, so it had never been committed), and the Rust core built for arm64-v8a and x86_64 and its Kotlin bindings generated by the build itself (`buildRustCore`, `generateRustBindings`) instead of being committed. The pipeline has no jobs until OxygenFlow has an Android image (oxygenflow#19). |
modified README.md +74 −5
| … | 1 unchanged lines |
||
| 2 | 2 | ||
| 3 | 3 | [](.flow/run) |
|
| 4 | 4 | ||
| 5 | - |  |
|
| 5 | + |  |
|
| 6 | 6 |  |
|
| 7 | 7 |  |
|
| 8 | 8 | ||
| … | 3 unchanged lines |
||
| 12 | 12 | ||
| 13 | 13 | --- |
|
| 14 | 14 | ||
| 15 | - | ## ✨ Features |
|
| 15 | + | <p> |
|
| 16 | + | <img src="docs/screenshots/dashboard-phone.png" alt="The dashboard on a phone" width="200"> |
|
| 17 | + | <img src="docs/screenshots/sign-in-code.png" alt="Signing in: the code to approve, dark" width="200"> |
|
| 18 | + | <img src="docs/screenshots/inbox-phone-dark.png" alt="The inbox, dark" width="200"> |
|
| 19 | + | </p> |
|
| 20 | + | <p> |
|
| 21 | + | <img src="docs/screenshots/repositories-foldable.png" alt="Repositories and a repository's issues side by side on an open foldable" width="400"> |
|
| 22 | + | <img src="docs/screenshots/repositories-tablet-dark.png" alt="Repositories on a tablet, dark" width="400"> |
|
| 23 | + | </p> |
|
| 24 | + | ||
| 25 | + | ## ✅ What works today |
|
| 26 | + | ||
| 27 | + | * 🔑 **Sign in with your browser** to oxygenhub.io or any hub (#2). The app |
|
| 28 | + | shows a code; you approve it on the hub's `/device` page, signed in there |
|
| 29 | + | with your password and second factor, so the app never sees either. The |
|
| 30 | + | device token is sealed with a key in the Android Keystore (StrongBox where |
|
| 31 | + | the phone has one), never written in the clear, logged or backed up. |
|
| 32 | + | Signing out (Account) revokes it on the hub. |
|
| 33 | + | * 🏠 **Dashboard:** a greeting, your newest repositories, the unread count. |
|
| 34 | + | * 📚 **Repositories:** yours or all you can see, filtered as you type; a |
|
| 35 | + | repository's **issues** (open, closed, all; pinned first). An issue opens on |
|
| 36 | + | the hub's web page for now (#4 brings it into the app). |
|
| 37 | + | * 📥 **Inbox:** notifications, unread first, with a badge on the navigation; |
|
| 38 | + | opening one marks it read, and the whole inbox can be marked read. |
|
| 39 | + | * 📱 **Phone, foldable and tablet:** a navigation bar on a phone, a rail when |
|
| 40 | + | wider; repositories and their issues side by side from a medium width up, |
|
| 41 | + | so a foldable opened (Pixel 10 Pro Fold) shows both, kept off the hinge. |
|
| 42 | + | * 🌗 **Light and dark**, with the wallpaper's colours (dynamic colour), or the |
|
| 43 | + | hub's own palette. |
|
| 44 | + | * Pull to refresh everywhere, with loading, empty and error states. |
|
| 45 | + | ||
| 46 | + | The screenshots are rendered from Compose previews with made-up data, no |
|
| 47 | + | emulator: `scripts/screenshots.sh`. |
|
| 48 | + | ||
| 49 | + | ## 🛣️ Planned |
|
| 50 | + | ||
| 51 | + | The plan is the repository's issues: browse code, history and drops (#3), |
|
| 52 | + | issues and proposals (#4), Flow runs and live logs (#5), trust and sealed |
|
| 53 | + | approvals (#6), releases (#7), and the phone as an OxygenFlow runner (#8–#11). |
|
| 54 | + | ||
| 16 | 55 | * 🧬 **Commutative History:** Native visualization of the change graph. |
|
| 17 | 56 | * 🔀 **Offline-First Conflicts:** Resolve conflicts on the go; they're just data. |
|
| 18 | 57 | * 🌊 **Job Flow Control:** Orchestrate OxygenFlow CI/CD pipelines right from your phone. |
|
| 58 | + | ||
| 59 | + | ## 🔑 Signing in |
|
| 19 | 60 | ||
| 61 | + | 1. Open the app. The hub is `https://oxygenhub.io` unless you change it (any |
|
| 62 | + | HTTPS address of a hub works; the last one is remembered). |
|
| 63 | + | 2. Tap **Sign in**. The app shows a code such as `BCDF-GHJK`. |
|
| 64 | + | 3. Tap **Open the hub to approve** (or open `/device` on the hub from a |
|
| 65 | + | computer and type the code), sign in there if you need to, check what the |
|
| 66 | + | app asks for, and approve it. You may untick scopes. |
|
| 67 | + | 4. The app signs in on its own within a few seconds. |
|
| 68 | + | ||
| 69 | + | The app asks for `repo:read` and `user:read`. The token lasts while it is |
|
| 70 | + | used and ends after 90 days without use; it is listed on the hub under |
|
| 71 | + | Settings, Tokens as "device: OxygenHub for Android on" and the phone's model. |
|
| 72 | + | ||
| 20 | 73 | ## 🛠️ Setup |
|
| 21 | 74 | ||
| 22 | 75 | 1. Clone the repository with `oxy clone`, and fetch the pinned Oxygen |
|
| … | 2 unchanged lines |
||
| 25 | 78 | point `local.properties` at it (`sdk.dir=…`) or set `ANDROID_HOME`. |
|
| 26 | 79 | 3. Install the Rust side: `cargo install cargo-ndk` and |
|
| 27 | 80 | `rustup target add aarch64-linux-android x86_64-linux-android`. |
|
| 28 | - | 4. Build and test; the build compiles rust-core for arm64-v8a and x86_64 |
|
| 29 | - | and generates its Kotlin bindings itself: |
|
| 81 | + | 4. Build, test and lint; the build compiles rust-core for arm64-v8a and |
|
| 82 | + | x86_64 and generates its Kotlin bindings itself: |
|
| 30 | 83 | ```bash |
|
| 31 | - | ./gradlew :app:assembleDebug :app:testDebugUnitTest |
|
| 84 | + | ./gradlew :app:assembleDebug :app:testDebugUnitTest :app:lintDebug |
|
| 32 | 85 | ``` |
|
| 86 | + | ||
| 87 | + | ## 🧭 Layout of the code |
|
| 88 | + | ||
| 89 | + | Under `app/src/main/java/io/oxygenhub/android`: |
|
| 90 | + | ||
| 91 | + | * `api/`: the hub's JSON types (by hand, from oxygenhub's `hub-api`), the |
|
| 92 | + | `HubApi` client over OkHttp, and the device sign-in (`DeviceAuth`). |
|
| 93 | + | * `auth/`: the session, `KeystoreSessionStore` (the sealed token) and |
|
| 94 | + | `SessionManager` (sign in, sign out, a refused token). |
|
| 95 | + | * `ui/`: one package per screen, each a view model and its composables; |
|
| 96 | + | `ui/nav` holds the routes and the adaptive navigation; `ui/common` the |
|
| 97 | + | loading, empty and error states. |
|
| 98 | + | ||
| 99 | + | The unit tests (`app/src/test`) run the client against MockWebServer with |
|
| 100 | + | JSON fixtures written from hub-api's types, and the view models against a |
|
| 101 | + | fake hub. |
|
| 33 | 102 | ||
| 34 | 103 | ## 🏗️ CI/CD |
|
| 35 | 104 | ||
| … | 2 unchanged lines |