A simple yet powerful Android application built to demonstrate modern Android development practices. The app allows users to shorten URLs using a remote service, view their history of shortened links, and manage the list.
- Shorten URLs: Enter a long URL and get a shortened version back.
- History List: All shortened URLs are saved locally and displayed in a list for easy access.
- Delete URLs: Remove single entries or clear the entire history.
- Clean & Modern UI: A user-friendly interface built entirely with Jetpack Compose.
- Error Handling: Displays user-friendly messages for network errors or invalid input.
This project follows a modern, scalable, and testable architecture based on Clean Architecture principles, combined with the Model-View-ViewModel (MVVM) pattern for the UI layer.
This separation of concerns makes the codebase:
- Independent of the UI: Business logic can be reused with any UI framework.
- Testable: Each layer can be tested in isolation.
- Maintainable: Changes in one layer have minimal impact on others.
Our architecture is divided into three main layers:
- Pattern: MVVM (Model-View-ViewModel)
- UI: Built with Jetpack Compose 🎨, using a unidirectional data flow (UDF). State flows down from the
ViewModelto theComposables, and events flow up. ViewModel: Responsible for holding and managing UI-related state. It interacts with the Domain layer via Use Cases.- Dependency Injection:
Koinis used to provideViewModelinstances and other dependencies.
- Pure Kotlin Module: This layer is the core of the application. It contains the business logic and is completely independent of the Android framework.
- Use Cases (Interactors): Single-purpose classes that encapsulate a specific piece of business logic (e.g.,
ShortenUrlUseCase,DeleteAllUrlsUseCase). - Models: Defines the core data structures of the application (e.g.,
Url). - Repository Interface: Defines the contract for data operations, which the Data layer will implement. This keeps the Domain layer independent of how data is fetched or stored.
- Repository Implementation: Implements the repository interface defined in the Domain layer. It's the single source of truth for all app data.
- Data Sources: The repository manages multiple data sources:
RemoteDataSource: Responsible for making network calls to the URL shortener API using Ktor.LocalDataSource: Responsible for caching data locally using the Room database.
- Mappers: Utility functions to convert network response models and database entities to the domain models, and vice-versa.
This project leverages a stack of modern, best-practice libraries to build a robust and efficient application.
-
UI & Foundation
- Jetpack Compose 🎨: The modern declarative UI toolkit for building native Android apps.
- Kotlin Coroutines ⏳: For managing background threads and handling asynchronous operations seamlessly.
- Kotlin Flow 🌊: A reactive stream library used for handling streams of data, especially from the database and use cases.
-
Dependency Injection
- Koin 💉: A pragmatic and lightweight dependency injection framework for Kotlin.
-
Networking
- Ktor Client 🌐: A modern, asynchronous networking client built by JetBrains. Used for all API communication.
- Kotlinx.Serialization
↔️ : For parsing JSON responses from the server into Kotlin data classes.
-
Database
- Room 🚪: A robust persistence library that provides an abstraction layer over SQLite.
-
Testing
- JUnit 4 ✅: The standard framework for unit testing.
- MockK 🎭: A powerful mocking library for Kotlin, used to create mock objects in unit tests.
- Jetpack Compose Test 🧪: The official framework for testing Compose UIs, allowing for interaction and verification of
Composables. - Robot Pattern 🤖: Used in our UI tests to create a layer of abstraction, making tests more readable and maintainable.
- Clone the repository:
git clone <repository-url> - Open in Android Studio: Open the project with the latest stable version of Android Studio.
- Sync Gradle: Let Android Studio download all the required dependencies.
- Run the app: Click the 'Run' button to build and install the app on an emulator or a physical device.
Testing is a first-class citizen in this project. We have a suite of tests to ensure the app is correct and robust.
- Unit Tests: Located in
app/src/test. These tests cover the ViewModel, Use Cases, Data Sources and Repository implementations. They run on the local JVM and use MockK to mock dependencies. - UI / Instrumentation Tests: Located in
app/src/androidTest. These tests verify the UI behavior and user flows. We use the Jetpack Compose Test framework along with the Robot Pattern to write clean, readable, and stable UI tests.