Skip to content

huuphuoc1396/android-github-users

Repository files navigation

GitHub Users

An Android application for browsing GitHub users and their details, with the ability to save favorites for quick access.

Features

User List

  • Browse all GitHub users with infinite scroll pagination (20 items per page).
  • Offline support — previously loaded users are cached locally and accessible without a network connection.
  • Mark and unmark any user as a favorite directly from the list.
Screenshot_1775559168

 

  • If loading the next page fails, an inline retry option is shown.
Screenshot_1775559177

User Details

  • View profile information: avatar, location, follower and following counts, and GitHub profile link.
  • Add or remove the user from favorites with a single tap.
Screenshot_1775559294

Favorites

  • View all saved favorite users in a dedicated screen.
  • Remove favorites with a confirmation dialog to prevent accidental deletion.
Screenshot_1775559098

Theme

 

Adaptive Layouts

  • Supports adaptive layouts for both User List and User Details on large screens and foldables.

Deep Links

  • User List: https://github-users.example.com/users

    adb shell am start -a android.intent.action.VIEW -d "https://github-users.example.com/users"
  • User Details: https://github-users.example.com/users/{username}

    adb shell am start -a android.intent.action.VIEW -d "https://github-users.example.com/users/huuphuoc1396"

Note: https://github-users.example.com is a demo domain that has not been verified. You must enable it manually in the app's supported links settings.


Architecture

The project follows Clean Architecture in a multi-module setup. Each feature is split into an api module (public contract) and an impl module (private implementation), with shared infrastructure extracted into core modules.

Module Map

:app                          ← wires everything together, owns AppNavigatorImpl and DI bindings
core/
  common/                     ← shared Kotlin contracts (models, dispatchers, Result helpers) — no Android, no Hilt
  ui/                         ← AppTheme, shared Compose components (AppButton, LoadingScreen, ErrorScreen, etc.)
  navigation/                 ← AppNavigator interface, NavigationIntent, Route, NavigationEffects
  network/                    ← Retrofit, OkHttpClient, safeApiCall, ApiException — owns NetworkModule
  database/                   ← AppDatabase, all DAOs, Room migrations, SQLCipher setup — owns DatabaseModule
  security/                   ← CMake/C++ key provider, SSL certificate handling
  config/                     ← build-flavor-aware configuration (base URLs, feature flags)
feature/
  users/
    api/                      ← UserDetailsDestination (nav contract); UserModel exposed via core:common
    impl/                     ← UserRepository, UserApi, UserRemoteMediator, UserListViewModel, UserDetailsViewModel
  favorites/
    api/                      ← FavoriteRepository interface, AddFavoriteUseCase, RemoveFavoriteUseCase,
                                 IsFavoriteUseCase, GetFavoritesUseCase
    impl/                     ← FavoriteRepositoryImpl, FavoritesViewModel

Dependency Graph

graph TD
    app(":app")

    subgraph feature["Feature Modules"]
        fu_api(":feature:users:api")
        fu_impl(":feature:users:impl")
        ff_api(":feature:favorites:api")
        ff_impl(":feature:favorites:impl")
    end

    subgraph core["Core Modules"]
        core_common(":core:common")
        core_ui(":core:ui")
        core_nav(":core:navigation")
        core_net(":core:network")
        core_sec(":core:security")
        core_cfg(":core:config")
        core_db(":core:database")
    end

    %% app dependencies
    app --> fu_api
    app --> fu_impl
    app --> ff_api
    app --> ff_impl
    app --> core_ui
    app --> core_nav
    app --> core_cfg
    app --> core_common

    %% feature:users:impl dependencies
    fu_impl --> fu_api
    fu_impl --> ff_api
    fu_impl --> core_ui
    fu_impl --> core_nav
    fu_impl --> core_net
    fu_impl --> core_db
    fu_impl --> core_common

    %% feature:favorites:api dependencies
    ff_api --> core_common

    %% feature:favorites:impl dependencies
    ff_impl --> ff_api
    ff_impl --> fu_api
    ff_impl --> core_ui
    ff_impl --> core_nav
    ff_impl --> core_net
    ff_impl --> core_db
    ff_impl --> core_common

    %% core:ui dependencies
    core_ui --> core_common

    %% core:network dependencies
    core_net --> core_common

    %% core:database dependencies
    core_db --> core_sec

    %% core:config dependencies
    core_cfg --> core_net
    core_cfg --> core_sec
Loading

Dependency Direction

:app → feature:x:impl → feature:x:api → core:*
:app → feature:x:api
feature:x:impl → feature:y:api        ← allowed (public contract only)
feature:x:impl → feature:y:impl       ← forbidden
core:* → feature:*                    ← forbidden

Layer Responsibilities

Layer Location Responsibility
Presentation feature:x:impl/presentation/ ViewModel, UiState, Screen composables
Domain feature:x:api/domain/ or feature:x:impl/domain/ Use cases, repository interfaces, domain models
Data feature:x:impl/data/ Repository impls, Retrofit API, Room DAOs, mappers
Infrastructure core:network, core:database, core:security Network stack, database, encryption

Error Handling

  • Write operations: Repositories return Result<Unit>. Errors are caught at the repository boundary with runCatching {} and surfaced to the ViewModel via .onFailure {}.
  • Reactive flows: Repositories return Flow<T>. ViewModels apply .catch {} before .stateIn() to prevent silent Flow termination on errors.

Tech Stack

UI

Navigation

Dependency Injection

Network

  • Retrofit + OkHttp + Gson
  • Custom safeApiCall wrapper maps HTTP errors to typed ApiException
  • Features contribute interceptors via @IntoSetNetworkModule is never modified per feature

Database & Persistence

  • Room + Paging 3 for paginated user list with offline cache
  • SQLCipher for encrypted local storage (disabled in dev, enabled in stag/prod)
  • DataStore for key-value persistence

Async

Security

  • SSL Pinning — certificate pinned in core:security via OkHttp CertificatePinner
  • Database encryption — SQLCipher, key managed by core:security
  • Native key storage — sensitive keys stored in C++ via CMake, harder to decompile than JVM bytecode
  • R8 — minification and obfuscation enabled in release builds

Debugging & Quality

  • LeakCanary — memory leak detection in debug builds
  • Timber — structured logging

Testing

Tests use JUnit 4, MockK, Turbine, Kotest, and Robolectric.

Coverage is measured with Kover across all modules. Generate an HTML report:

./gradlew koverHtmlReportDevDebug
android-github-users_app_build_reports_kover_htmlDevDebug

Test coverage targets:

Area What is tested
ViewModels All user interactions, UiState transitions, error paths
Use cases Delegation to repository, Result propagation
Repositories DAO/API calls, runCatching failure paths
Mappers Domain ↔ UI model conversions

Product Flavors

Flavor App ID suffix DB encryption Notes
dev .dev disabled For local development and inspection
stag .stag enabled Mirrors production security settings
prod (none) enabled Production release

Get Started

Prerequisites

  • Android Studio Ladybug or newer
  • JDK 11+
  • Android Gradle Plugin 8.6.1+
  • Kotlin 2.0.20+
  • NDK and CMake (required for core:security)

SDK

Property Value
minSdk 28
targetSdk 36
compileSdk 36

Installation

  1. Clone the repository:
    git clone https://github.com/huuphuoc1396/android-github-users.git
  2. Open the android-github-users/ directory in Android Studio.
  3. Copy credentials.cpp into core/security/cpp/.
  4. Select a Build Variant (e.g. devDebug).
  5. Run the :app configuration.

License

This project is licensed under the MIT License — see the LICENSE file for details.

Contact

For any inquiries, reach out to Phuoc Bui.

About

This app allows an administrator to browse all users of the GitHub site and view more detailed information about them.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Languages