KBBI is an unofficial Android dictionary app for Kamus Besar Bahasa Indonesia. The app combines a remote dictionary API with a bundled local word index so users can search words quickly, inspect meanings in detail, and keep bookmarks and recent search history on-device.
This repository contains the Android client for KBBI. The project is organized as a multi-module Clean Architecture codebase:
:appowns application startup, Koin assembly, and the root Navigation3 graph.:core:*contains shared data, domain, logging, presentation, and utility code.:feature:*contains feature-specific presentation, data, and domain modules.- Feature navigation is exposed through each feature's
navigationpackage and wired from:app. - Presentation uses Jetpack Compose, ViewModels, state, and one-shot UI events.
- Data access uses repositories, use cases, Room, Ktor, OkHttp, and kotlinx.serialization.
- Dependency injection uses Koin modules composed at the app boundary.
- Product flavors are available for
developmentandproduction.
- Search Indonesian words from the home screen
- Browse a bundled local word list from
feature/home/data/src/main/assets/entries.json - View detailed word entries and meanings
- Save bookmarked words locally
- Keep recent search history locally
- Animated splash screen and Lottie-based loading/empty states
- Shared design system, UI text handling, error mapping, networking, and logging
- Language: Kotlin
- Build tooling: Gradle, Android Gradle Plugin
9.2.1, Kotlin2.3.21 - Java target: Java 17
- Minimum SDK: 23
- Target/Compile SDK: 37
- UI: Jetpack Compose, Material 3, Lottie Compose
- Navigation: AndroidX Navigation3
- Architecture: Clean Architecture, MVVM-style presentation, Repository pattern, UseCase layer
- Async/data: Coroutines, StateFlow, Channel-based events
- Local storage: Room
- Networking: Ktor Client, kotlinx.serialization, OkHttp engine/logging
- Dependency injection: Koin
4.2.1 - Code quality: Detekt, Ktlint
- Distribution/automation: Fastlane, GitHub Actions
.
├── app/
│ └── src/main/java/com/arrazyfathan/kbbi/
│ ├── BaseApplication.kt # Koin startup
│ ├── MainActivity.kt # Activity entry point
│ ├── di/ # App-level module assembly
│ └── navigation/ # Root app navigation graph
├── core/
│ ├── data/ # Shared Ktor client and safe API call helpers
│ ├── di/ # Shared core Koin modules
│ ├── domain/ # AppResult, DataError, shared domain primitives
│ ├── logging/ # App and network logging helpers
│ ├── presentation/
│ │ ├── designsystem/ # Theme, colors, type, icons, resources, components
│ │ └── ui/ # UiText, DataErrorToText, shared UI helpers
│ └── utils/ # System bar and platform utilities
├── feature/
│ ├── bookmark/
│ │ └── presentation/ # Bookmark screen, ViewModel, route
│ ├── detail/
│ │ └── presentation/ # Detail screen, ViewModel, route
│ ├── home/
│ │ ├── data/ # Room, remote/local data sources, repository impl
│ │ ├── domain/ # Word models, repositories, use cases
│ │ └── presentation/ # Home screen, ViewModel, route
│ ├── splash/
│ │ └── presentation/ # Splash screen and route
│ └── words/
│ └── presentation/ # Word list screen, ViewModel, route
├── fastlane/ # Release/distribution automation
├── gradle/libs.versions.toml # Centralized dependency and plugin versions
└── .github/workflows/ # CI and tagged release pipeline
The dependency direction is kept inward:
app
├── feature:*:presentation
├── core:di
└── core:presentation:designsystem
feature:*:presentation
├── feature:home:domain
├── core:domain
├── core:presentation:ui
└── core:presentation:designsystem
feature:home:data
├── feature:home:domain
├── core:data
├── core:domain
└── core:logging
core modules
└── shared primitives with no feature ownership
HttpClientFactory and SafeApiCall live in :core:data so every feature data module can reuse the same network setup. UiText and DataErrorToText live in :core:presentation:ui so presentation modules can map domain/data errors to localized UI messages consistently.
The app starts at the splash destination, then enters the main flow owned by the root navigation graph in :app.
- Home: Search words, display loading/error states, and store recent searches.
- Words: Filter the bundled local word index, then fetch word details.
- Bookmarks: View and remove locally saved entries.
- Detail: Show meanings for a selected word and toggle bookmark state.
Each feature exposes its route from a navigation package, while AppNavigation in :app composes those destinations into the app graph.
- Base URL:
https://kbbi-api-green.vercel.app - API repository: https://github.com/arrazyfathan/kbbi-api
- Shared network helpers:
core/data/src/main/java/com/arrazyfathan/kbbi/core/data/remote/network
- Room database: stores history and bookmark data in
kbbi_db - Asset file:
feature/home/data/src/main/assets/entries.jsonprovides the local searchable word list
To build the project locally, use:
- Android Studio with current Android SDK tooling
- JDK 17
- Android SDK Platform 37
- An Android device or emulator for runtime testing
git clone https://github.com/arrazyfathan/kbbi.git
cd kbbiYour local.properties should point to a valid Android SDK installation:
sdk.dir=/path/to/Android/sdk./gradlew helpIf Gradle sync succeeds, the project is ready to open in Android Studio.
The app defines one flavor dimension, stage, with two product flavors:
developmentuses application IDcom.arrazyfathan.kbbi.devproductionuses application IDcom.arrazyfathan.kbbi
Examples:
./gradlew assembleDevelopmentDebug
./gradlew assembleProductionDebugRelease APK names are customized automatically:
- Production:
kbbi-v<version>-release.apk - Non-production:
kbbi-<flavor>-v<version>-release.apk
Version values are driven by app/version.properties.
For local development, the normal entry point is the development debug build:
./gradlew installDevelopmentDebugOr from Android Studio, choose the developmentDebug variant and run the app configuration.
Local unit tests are split across the app, core, and feature modules. Instrumented tests currently live under the feature data layer where Android-dependent Room behavior is validated.
Run JVM unit tests:
./gradlew testDevelopmentDebugUnitTest :core:logging:testDebugUnitTest :core:domain:test :feature:home:data:testDebugUnitTest :feature:home:domain:testCompile Android tests:
./gradlew :feature:home:data:compileDebugAndroidTestKotlinRun Android tests on a connected device or emulator:
./gradlew :feature:home:data:connectedDebugAndroidTestThe project uses the JetBrains Kover plugin. For the development debug variant:
./gradlew app:koverLogDevelopmentDebug
./gradlew app:koverHtmlReportDevelopmentDebugThe HTML report is generated at app/build/reports/kover/htmlDevelopmentDebug/index.html.
Useful validation commands:
./gradlew lintDevelopmentDebug
./gradlew detekt
./gradlew ktlintCheck
./gradlew assembleDevelopmentDebugA focused post-refactor validation pass:
./gradlew testDevelopmentDebugUnitTest :core:logging:testDebugUnitTest :core:domain:test :feature:home:data:testDebugUnitTest :feature:home:domain:test :feature:home:data:compileDebugAndroidTestKotlinRelease builds are intentionally blocked unless signing is configured. The Gradle build expects these values through either Gradle properties or environment variables:
ANDROID_KEYSTORE_PATH
ANDROID_KEYSTORE_PASSWORD
ANDROID_KEY_ALIAS
ANDROID_KEY_PASSWORD
Example:
export ANDROID_KEYSTORE_PATH=/absolute/path/to/release.keystore
export ANDROID_KEYSTORE_PASSWORD=your-store-password
export ANDROID_KEY_ALIAS=your-key-alias
export ANDROID_KEY_PASSWORD=your-key-password
./gradlew assembleProductionReleaseWithout those values, any signed release packaging task will fail by design.
The repository includes Fastlane setup.
Install Ruby dependencies:
bundle installAvailable lane:
bundle exec fastlane android testCurrent behavior:
android testruns Gradle tests
GitHub Actions workflow: .github/workflows/android.yml
The validate job runs:
./gradlew testDevelopmentDebugUnitTest lintDevelopmentDebug assembleDevelopmentDebug --stacktraceIf successful, it uploads the development debug APK as a workflow artifact.
When a git tag is pushed, the release job:
- Validates signing secrets
- Builds
assembleProductionRelease - Uploads the signed production APK as an artifact
- Publishes a GitHub release with generated release notes
Required GitHub secrets:
ANDROID_KEYSTORE_BASE64ANDROID_KEYSTORE_PASSWORDANDROID_KEY_ALIASANDROID_KEY_PASSWORD
The current app includes:
- Compose-first screen implementation
- Material 3 components and app theming
- Shared design system module for theme, type, colors, icons, resources, and components
- Shared
UiTextandDataErrorToTextfor localized presentation messages - Custom splash screen animation
- Lottie-based loading and empty states
- AndroidX edge-to-edge system bar setup
- Compose previews for key screens and content components
Latest published APK:
Designed and developed by 2022 arrazyfathan (Ar Razy Fathan Rabbani)
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.




