Thank you for contributing to this project.
Tabby is an Android GUI application for Mihomo
(formerly Clash Meta), a rule-based proxy kernel.
The app is written in Kotlin with Jetpack Compose for the UI, and embeds a compiled Go binary
(libclash.so) built from the Mihomo submodule.
- Application ID:
io.github.goooler.tabby - Min SDK: 28 | Compile SDK: 37 | Target SDK: 35
- Default branch:
trunk
Tabby/
├── app/ # Application shell: MainActivity, MainApplication, AppModule (Koin), BroadcastReceivers, TileService
├── core/ # Mihomo bridge: Go/JNI bindings, data models, C++ CMake layer
│ # └── src/foss/golang/clash/ (git submodule → MetaCubeX/mihomo)
├── service/ # Background VPN service, Room database, IPC via kaidl, OkHttp profile fetching
├── common/ # Shared constants, store providers, and utility extensions; includes Android-specific helpers
├── glue/ # Dependency-injection wiring via Koin; exposes api() of core, service, common
└── ui/ # Shared UI components, theme, icons (also a library module)
├── crash/ # Crash reporting screen
├── home/ # Dashboard / tunnel toggle
├── log/ # Real-time logcat viewer
├── proxy/ # Proxy group selector
├── profile/ # Profile management
└── settings/ # App settings
app → glue → core → common
└── service → core
└── common
app → ui/*
| Layer | Technology |
|---|---|
| Language | Kotlin 2.x, Go 1.26+ |
| UI | Jetpack Compose + Material 3, Navigation3 |
| DI | Koin 4 |
| IPC | kaidl (custom AIDL generator) |
| DB | Room |
| Network | OkHttp 5 |
| Core proxy | Mihomo (Go submodule via golang-gradle-plugin) |
| Code style | ktfmt (Google style) via Spotless |
| Build | Gradle 9+ with Kotlin DSL, Version Catalog (gradle/libs.versions.toml) |
- Search existing issues and pull requests before opening a new one.
- For bug reports, include steps to reproduce, expected behavior, and logs/screenshots when possible.
- For feature requests, explain the use case and scope clearly.
- Fork this repository and create a branch from
trunk. - Clone with submodules (or initialize them after clone):
git clone --recurse-submodules https://github.com/Goooler/Tabby # or after a plain clone: git submodule update --init --recursive - Install required tools:
- JDK 21 or above
- Android SDK (set
sdk.dirinlocal.properties) - NDK
29.0.14206865(installed automatically by AGP) - CMake 4.x
- Go 1.26+
- Create
local.propertiesin the project root:sdk.dir=/path/to/android-sdk # Optional: skip downloading geo data files if they already exist # skip.downloadGeoFiles=true
# Check and fix code style (required before every commit)
./gradlew spotlessApply
# Build a debug APK (required before every commit)
./gradlew androidApp:assembleDebug
# Build a release APK (don't have to run this in general developments)
./gradlew androidApp:assembleRelease
# Run all checks
./gradlew checkNote: The first build downloads three geo database files from
MetaCubeX/meta-rules-datintoapp/src/main/assets/. Setskip.downloadGeoFiles=trueinlocal.propertiesto skip this when the files already exist.
- Formatter: ktfmt (Google style). Run
./gradlew spotlessApplyto fix. All Kotlin files insrc/**/*.ktand*.gradle.ktsare covered. - Warnings as errors:
allWarningsAsErrors = truefor all Kotlin compilations. Fix every warning before merging. - JVM target: Java 21.
- Opt-ins applied project-wide:
kotlin.uuid.ExperimentalUuidApiandroidx.compose.foundation.ExperimentalFoundationApiandroidx.compose.material3.ExperimentalMaterial3Api
- Compiler flags:
-Xcontext-sensitive-resolution,-Xexplicit-backing-fields. - Imports: No wildcard imports (star import threshold is
Int.MAX_VALUE).ktfmtmanages ordering automatically. - Trailing commas: allowed in both declarations and call sites.
- Indentation: 2 spaces for Kotlin/Gradle, 4 spaces for C/CMake.
Every Android module's namespace follows the pattern:
com.github.kr328.clash.<module-name>
For example:
:app→com.github.kr328.clash.app:glue→com.github.kr328.clash.glue:ui:home→com.github.kr328.clash.home
This is enforced automatically in the root build.gradle.kts via
namespace = "com.github.kr328.clash.${project.name}".
- The current Koin module definition lives in
app/AppModule.kt. MainApplicationstarts Koin withappModule, so add or update DI bindings there unless the application startup is changed.- UI modules should consume injected dependencies via
koinInject()/getKoin()in Compose; avoid constructor injection inActivity.
- The
servicemodule defines IPC interfaces usingkaidlannotations. - Generated code is produced by
ksp(libs.kaidl.compiler)at build time. - Do not hand-edit generated files.
- The repository ships
app/release.keystorewith its credentials already configured inapp/build.gradle.kts. - Both debug and release builds are signed automatically with this keystore; no extra files or manual configuration are required.
- Do not replace or remove
app/release.keystore, and do not commit any personal keystores or override signing credentials.
When adding or modifying string/text resources, please ensure that:
- Alphabetical Sorting: All resource entries must be sorted alphabetically by their
namekeys. - Single-Line Format: Each resource entry must be written entirely on a single line (no manual line wraps or breaks), and do not modify any other unrelated lines.
- Complete Translations: For any translatable content, make sure to completely supplement the internationalized translations for all supported languages in the project.
GitHub Actions runs on every push to trunk and on every pull request:
| Job | Command | Description |
|---|---|---|
check-style |
./gradlew spotlessCheck |
Fails if formatting issues exist |
lint |
./gradlew lintDebug |
Runs Android lint checks for the debug variant |
build |
./gradlew androidApp:assembleRelease |
Full release build including Go cross-compilation |
final-status |
— | Required branch-protection status combining all CI checks |
A nightly pre-release is published automatically on pushes to trunk.
- Branch from
trunk. - Keep changes focused and small.
- Include a clear description of what changed and why.
- Run
./gradlew spotlessApplyand./gradlew androidApp:assembleDebuglocally before opening a PR. - Link related issues when applicable.
- Update docs when behavior or developer workflow changes.