README.md
π± OxygenHub Android
The official native Android client for OxygenHub, designed for modern devices and fully optimized for foldables like the Pixel 10 Pro Fold.
Built with Kotlin, Jetpack Compose, and deep Rust interop (via uniffi-rs) to execute native oxy commands directly on your device.
β What works today
- π Sign in with your browser to oxygenhub.io or any hub (#2). The app
shows a code; you approve it on the hub's
/devicepage, signed in there with your password and second factor, so the app never sees either. The device token is sealed with a key in the Android Keystore (StrongBox where the phone has one), never written in the clear, logged or backed up. Signing out (Account) revokes it on the hub. - π Dashboard: a greeting with your avatar, your newest repositories, the unread count. Your avatar is the picture you uploaded to the hub, or the hub's identicon for you (#13).
- π Repositories: yours or all you can see, filtered as you type; a repository's issues (open, closed, all; pinned first). An issue opens on the hub's web page for now (#4 brings it into the app).
- π Proposals: a repository's proposals beside its issues; a proposal's checks, approvals, changes and what stands in the way, and Land (or land when its checks pass) from the phone (#17).
- π₯ Inbox: notifications, unread first, with a badge on the navigation; opening one marks it read, and the whole inbox can be marked read.
- π± Phone, foldable and tablet: a navigation bar on a phone, a rail when wider; repositories and their issues side by side from a medium width up, so a foldable opened (Pixel 10 Pro Fold) shows both, kept off the hinge.
- π Light and dark, with the wallpaper's colours (dynamic colour), or the hub's own palette.
- Pull to refresh everywhere, with loading, empty and error states.
The screenshots are rendered from Compose previews with made-up data, no
emulator: scripts/screenshots.sh.
π£οΈ Planned
The plan is the repository's issues: browse code, history and drops (#3), issues and proposals (#4), Flow runs and live logs (#5), trust and sealed approvals (#6), releases (#7), and the phone as an OxygenFlow runner (#8β#11).
- 𧬠Commutative History: Native visualization of the change graph.
- π Offline-First Conflicts: Resolve conflicts on the go; they're just data.
- π Job Flow Control: Orchestrate OxygenFlow CI/CD pipelines right from your phone.
π Signing in
- Open the app. The hub is
https://oxygenhub.iounless you change it (any HTTPS address of a hub works; the last one is remembered). - Tap Sign in. The app shows a code such as
BCDF-GHJK. - Tap Open the hub to approve (or open
/deviceon the hub from a computer and type the code), sign in there if you need to, check what the app asks for, and approve it. You may untick scopes. - The app signs in on its own within a few seconds.
The app asks for repo:read and user:read. The token lasts while it is
used and ends after 90 days without use; it is listed on the hub under
Settings, Tokens as "device: OxygenHub for Android on" and the phone's model.
π οΈ Setup
- Clone the repository with
oxy clone, and fetch the pinned Oxygen sources:oxy submodule update --init(rust-core builds overoxygen/). - Install the Android SDK with an NDK (Android Studio's SDK manager), and
point
local.propertiesat it (sdk.dir=β¦) or setANDROID_HOME. - Install the Rust side:
cargo install cargo-ndkandrustup target add aarch64-linux-android x86_64-linux-android. - Build, test and lint; the build compiles rust-core for arm64-v8a and
x86_64 and generates its Kotlin bindings itself:
./gradlew :app:assembleDebug :app:testDebugUnitTest :app:lintDebug
π§ Layout of the code
Under app/src/main/java/io/oxygenhub/android:
api/: the hub's JSON types (by hand, from oxygenhub'shub-api), theHubApiclient over OkHttp, and the device sign-in (DeviceAuth).auth/: the session,KeystoreSessionStore(the sealed token) andSessionManager(sign in, sign out, a refused token).ui/: one package per screen, each a view model and its composables;ui/navholds the routes and the adaptive navigation;ui/commonthe loading, empty and error states.
The unit tests (app/src/test) run the client against MockWebServer with
JSON fixtures written from hub-api's types, and the view models against a
fake hub.
ποΈ CI/CD
We dogfood OxygenFlow for our builds.
The pipeline is defined in .flow/pipeline.kdl.