A production-ready Android application demonstrating modern development practices and architectural patterns. This project showcases how to build scalable, maintainable, and testable Android applications using industry-standard tools and libraries.
Built with Clean Architecture principles, this app serves as a comprehensive example of modular design, advanced Gradle configuration, and robust CI/CD practices. Perfect for teams looking to establish solid architectural foundations for large-scale Android projects.
- π Android Showcase 2.0
A music discovery app built with Jetpack Compose that displays album information sourced from the Last.fm API. The application demonstrates real-world scenarios including network requests, local caching, navigation, and state management.
Features:
- Album List - Browse albums with search functionality
- Album Details - View detailed album information and track listings
- Favorites - Save preferred albums (WIP)
- Profile - User preferences and settings (WIP)
Built with modern Android development tools and libraries, prioritizing, project structure stability and production-readiness.
Core Technologies:
- Kotlin 2.2+ - Modern, expressive programming language
- Coroutines - Asynchronous programming
- Flow - Reactive data streams
- KSP - Kotlin Symbol Processing
- Serialization - JSON parsing
Android Jetpack:
- Compose - Declarative UI framework
- Navigation Compose - Type-safe navigation
- ViewModel - UI-related data management
- Room - Local database with SQLite
Networking & Images:
Dependency Injection:
- Koin - Lightweight dependency injection framework
Architecture:
- Clean Architecture - Separation of concerns with defined layers
- Single Activity Architecture - Modern navigation approach
- MVVM + MVI - Reactive presentation layer pattern
- Modular Design - Feature-based modules for scalability
UI & Design:
- Material Design 3 - Latest design system
- Dynamic Theming - Wallpaper-based themes (Android 12+)
- Dark Theme - System-aware dark mode
- Lottie - Vector animations
Testing:
- JUnit 5 - Modern testing framework
- Konsist - Architecture and coding convention tests
- Mockk - Kotlin-first mocking library
- Kluent - Fluent assertion library
- Espresso - UI testing (WIP)
Code Quality:
- Ktlint - Kotlin code formatting
- Detekt - Static analysis and complexity checks
- Android Lint - Android-specific code analysis
- Spotless - Code formatting enforcement
Build & CI:
- Gradle Kotlin DSL - Type-safe build scripts
- Version Catalogs - Centralized dependency management
- Convention Plugins - Shared build logic
- GitHub Actions - Automated CI/CD pipeline
- Renovate - Automated dependency updates
The project implements Clean Architecture with a modular approach, treating each feature as an independent, reusable component similar to a microservice. This design enables maintainability and scalability for large development teams.
Benefits of Modular Architecture:
- Reusability - Shared code across multiple app variants
- Separation of Concerns - Clear module boundaries with explicit dependencies
- Parallel Development - Teams can work on features independently
- Faster Build Times - Incremental compilation and build caching
- Testability - Isolated testing of individual components
Module Types:
app
- Main application module containing navigation setup, DI configuration, and app-level componentsfeature-*
- Feature modules (album, profile, favourite) containing feature-specific business logicfeature-base
- Shared foundation module providing common utilities and base classeslibrary-*
- Utility modules for testing and shared functionality
Clean Architecture
is implemented at the module level - each module contains its own set of Clean Architecture layers:
Notice that the
app
module andlibrary_x
modules structure differs a bit from the feature module structure.
Each feature module contains 3 layers with a distinct set of responsibilities and common module components.
This layer is closest to what the user sees on the screen.
The presentation
layer mixes MVVM
and MVI
patterns:
MVVM
- JetpackViewModel
is used to encapsulate acommon UI state
. It exposes thestate
via observable state holder (Kotlin Flow
)MVI
-action
modifies thecommon UI state
and emits a new state to a view viaKotlin Flow
The
common state
is a single source of truth for each view. This solution derives from Unidirectional Data Flow and Redux principles.
This approach facilitates the creation of consistent states. The state is collected via collectAsUiStateWithLifecycle
method. Flows collection happens in a lifecycle-aware manner, so
no resources are wasted.
Stated is annotated with Immutable annotation that is used by Jetpack compose to enable composition optimizations.
Components:
- Screen (Composable) - observes common view state (through
Kotlin Flow
). Compose transform state (emitted by Kotlin Flow) into application UI Consumes the state and transforms it into application UI (viaJetpack Compose
). Pass user interactions toViewModel
. Views are hard to test, so they should be as simple as possible. - ViewModel - emits (through
Kotlin Flow
) view state changes to the view and deals with user interactions (these view models are not simply POJO classes). - ViewState - common state for a single view
- StateTimeTravelDebugger - logs actions and view state transitions to facilitate debugging.
- NavManager - singleton that facilitates handling all navigation events inside
NavHostActivity
(instead of separately, inside each view)
This is the core layer of the application. Notice that the domain
layer is independent of any other layers. This
allows making domain models and business logic independent from other layers. In other words, changes in other layers
will not affect the domain
layer eg. changing the database (data
layer) or screen UI (presentation
layer) ideally will
not result in any code change within the domain
layer.
Components:
- UseCase - contains business logic
- DomainModel - defines the core structure of the data that will be used within the application. This is the source of truth for application data.
- Repository interface - required to keep the
domain
layer independent from thedata layer
(Dependency inversion).
Encapsulates application data. Provides the data to the domain
layer eg. retrieves data from the internet and cache the
data in disk cache (when the device is offline).
Components:
- Repository is exposing data to the
domain
layer. Depending on the application structure and quality of the external API repository can also merge, filter, and transform the data. These operations intend to create a high-quality data source for thedomain
layer. It is the responsibility of the Repository (one or more) to construct Domain models by reading from theData Source
and accepting Domain models to be written to theData Source
- Mapper - maps
data model
todomain model
(to keepdomain
layer independent from thedata
layer).
This application has two Data Sources
- Retrofit
(used for network access) and Room
(local storage used to access
device persistent memory). These data sources can be treated as an implicit sub-layer. Each data source consists of
multiple classes:
- Retrofit Service - defines a set of API endpoints
- Retrofit Response Model - definition of the network objects for a given endpoint (top-level model for the data
consists of
ApiModels
) - Retrofit Api Data Model - defines the network objects (sub-objects of the
Response Model
) - Room Database - persistence database to store app data
- Room DAO - interact with the stored data
- Room Entity - definition of the stored objects
Both Retrofit API Data Models
and Room Entities
contain annotations, so the given framework understands how to parse the
data into objects.
Each module in the Android project contains several standard items that provide essential functionality and configuration:
Components:
- Gradle Build Script -
build.gradle.kts
defining dependencies, build configurations, and plugins. - Koin DI Module - Dependency injection configuration
- Tests - Unit tests (
test/
) and integration tests (androidTest/
) - Android Resources - resources (
res/
) including strings, drawables, and assets. - Android Manifest - The
AndroidManifest.xml
file declaring module metadata.
The below diagram presents application data flow when a user interacts with the album list screen
:
Gradle versions catalog is used as a centralized dependency management third-party dependency coordinates (group, artifact, version) are shared across all modules (Gradle projects and subprojects).
All of the dependencies are stored in the settings.gradle.kts file (default location). Gradle versions catalog consists of a few major sections:
[versions]
- declare versions that can be referenced by all dependencies[libraries]
- declare the aliases to library coordinates[bundles]
- declare dependency bundles (groups)[plugins]
- declare Gradle plugin dependencies
Each feature module depends on the feature_base
module, so dependencies are shared without the need to add them explicitly in each feature module.
Convention plugins standardize build configuration across modules:
app-convention
- Application module configurationlibrary-convention
- Library module setup with Android configurationkotlin-convention
- Kotlin compilation settings and toolchaintest-convention
- Testing framework setup (JUnit 5, test logging)detekt-convention
- Code analysis rulesspotless-convention
- Code formatting enforcement
All convention plugins are located in buildSrc/src/main/kotlin
.
Enables type-safe project references instead of error-prone string-based module paths:
// Before
implementation(project(":feature:album"))
// After
implementation(projects.feature.album)
Java/JVM versions are centralized in JavaConfig.kt
:
object JavaConfig {
val JAVA_VERSION: JavaVersion = JavaVersion.VERSION_17
val JVM_TARGET: JvmTarget = JvmTarget.JVM_17
const val JVM_TOOLCHAIN_VERSION: Int = 17
}
Quality Checks:
./gradlew konsistTest:test --rerun-tasks # Architecture & convention validation
./gradlew lintDebug # Android lint analysis
./gradlew detektCheck # Code complexity & style analysis
./gradlew spotlessCheck # Code formatting verification
./gradlew testDebugUnitTest # Unit test execution
./gradlew connectedCheck # UI test execution (WIP)
./gradlew :app:bundleDebug # Production build verification
Auto-fix Commands:
./gradlew detektApply # Apply Detekt formatting fixes
./gradlew spotlessApply # Apply code formatting fixes
./gradlew lintDebugBaseline # Update lint baseline
GitHub Actions workflows execute quality checks automatically:
- PR Validation - All checks run in parallel on pull requests
- Main Branch Protection - Post-merge validation ensures code quality
- Automated Dependency Updates - Renovate bot creates PRs for dependency updates
Configuration: .github/workflows
Optional Git hooks can execute quality checks before pushing code, providing fast feedback during development.
Logging System: The app includes comprehensive logging for development debugging:
onCreate
- Activity and ViewModel lifecycle eventsAction
- UI state changes and user interactionsHttp
- Network request/response details (debug builds only)Navigation
- Screen transitions and routing
Filter logs by tag: package:mine tag:Navigation
This showcase prioritizes architecture, tooling, and development practices over complex UI design. The interface uses Material Design 3 components but remains intentionally straightforward to focus on the underlying technical implementation.
Prerequisites:
- Android Studio Giraffe | 2022.3.1+
- JDK 17+
- Android SDK 34+
Setup:
# Clone the repository
git clone https://github.com/igorwojda/android-showcase.git
# Open in Android Studio
# File -> Open -> Select cloned directory
Recommended IDE Plugins:
- Detekt - Configure with detekt.yml
- Kotlin - Usually pre-installed
- Android - Usually pre-installed
Active development continues with focus on modern Android practices. View planned enhancements and contribute ideas.
Development Tools:
- Material Theme Builder - Generate Material 3 dynamic themes
- Compose Material 3 Components - Component reference
- Android Ecosystem Cheat Sheet - 200+ essential Android tools
- Kotlin Coroutines Use Cases - Practical coroutine examples
Recommended Projects:
- Now in Android - Google's official modern Android showcase
- Compose Samples - Official Jetpack Compose examples
- Android Architecture Blueprints - Architecture pattern examples
- Kotlin Android Template - Pre-configured project template
Contributions are welcome! Please check the CONTRIBUTING.md guidelines before submitting PRs.
Areas for Contribution:
- Feature implementations (Profile, Favorites screens)
- UI/UX improvements and animations
- Performance optimizations
- Testing coverage expansion
- Documentation improvements
Igor Wojda - Senior Android Engineer
MIT License
Copyright (c) 2019 Igor Wojda
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
associated documentation files (the "Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial
portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT
LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
WHETHER IN AN ACTION OF TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
Flowing animations are distributed under Creative Commons License 2.0
:
- Error screen by Chetan Potnuru
- Building Screen by Carolina Cajazeira