QuranApp Repository Structure: MVVM Architecture and Android Implementation Guide
The alfaazplus/quranapp repository is structured around a clean MVVM architecture that separates Jetpack Compose UI components from business logic through ViewModel state management, Domain layer use-cases, and Data layer persistence using DataStore and SQLite.
The alfaazplus/quranapp repository delivers an ad-free, privacy-focused Qur'an reader for Android built entirely in Kotlin. Understanding the repository structure requires mapping how the Presentation, ViewModel, Domain, and Data layers interact to handle everything from UI rendering to offline tafsir downloads. This guide walks through each architectural component with direct source links to the master branch.
Architecture Breakdown
The codebase organizes functionality into five distinct layers, each with specific responsibilities and entry points:
Presentation Layer
- Responsibility: Rendering UI through Jetpack Compose and custom Android Views
- Key Files:
- [
PeaceRadioGroup.kt](https://github.com/alfaazplus/quranapp/blob/master/app/src/main/java/com/quranapp/android/widgets/PeaceRadioGroup.kt) – Custom widget for grouped selection controls - [
FragMain.kt](https://github.com/alfaazplus/quranapp/blob/master/app/src/main/java/com/quranapp/android/frags/FragMain.kt) – Primary navigation fragment hosting the main drawer
- [
ViewModel Layer
- Responsibility: Holding UI state, processing user events, and mediating between UI and data operations
- Key Files:
- [
TafsirViewModel.kt](https://github.com/alfaazplus/quranapp/blob/master/app/src/main/java/com/quranapp/android/viewmodels/TafsirViewModel.kt) – Manages tafsir selection, download states, and initialization flows
- [
Domain Layer
- Responsibility: Encapsulating business logic for resource fetching and download orchestration
- Key Files:
- [
TafsirManager.kt](https://github.com/alfaazplus/quranapp/blob/master/app/src/main/java/com/quranapp/android/utils/reader/tafsir/TafsirManager.kt) – Parses and caches tafsir JSON data - [
TafsirDownloadManager.kt](https://github.com/alfaazplus/quranapp/blob/master/app/src/main/java/com/quranapp/android/utils/managers/TafsirDownloadManager.kt) – Orchestrates background downloads via WorkManager
- [
Data Layer
- Responsibility: Local persistence and preference storage
- Key Files:
- [
QuranTafsirDBHelper.kt](https://github.com/alfaazplus/quranapp/blob/master/app/src/main/java/com/quranapp/android/utils/reader/tafsir/QuranTafsirDBHelper.kt) – SQLite helper for offline tafsir storage - [
DataStoreManager.kt](https://github.com/alfaazplus/quranapp/blob/master/app/src/main/java/com/quranapp/android/utils/prefs/DataStoreManager.kt) – Wrapper around Jetpack DataStore for type-safe preferences
- [
Network Layer
- Responsibility: Remote API communication using Retrofit and Kotlinx Serialization
- Key Files:
- [
AlfaazPlusApi.kt](https://github.com/alfaazplus/quranapp/blob/master/app/src/main/java/com/quranapp/android/api/AlfaazPlusApi.kt) – Central Retrofit interface defining API endpoints app/build.gradle.kts– Declaresretrofitandkotlinx-serializationdependencies
- [
Build Configuration
- Responsibility: Module configuration and dependency versioning
- Key Files:
settings.gradle.kts– Root project settings- [
gradle/libs.versions.toml](https://github.com/alfaazplus/quranapp/blob/master/gradle/libs.versions.toml) – Centralized version catalog for all third-party libraries
Component Interaction Flow
The architecture enforces unidirectional data flow from UI to Data and back. Here is how a typical tafsir download operation traverses the layers:
-
Initialization:
TafsirViewModelinitializes in [TafsirViewModel.kt](https://github.com/alfaazplus/quranapp/blob/master/app/src/main/java/com/quranapp/android/viewmodels/TafsirViewModel.kt) by reading the saved tafsir key fromDataStoreManager, callingTafsirDownloadManager.initialize(context), and loading cached keys fromQuranTafsirDBHelper. -
State Observation: The UI layer collects
StateFlow<TafsirUiState>from the ViewModel. WhenobserveDownloadStates()detects progress updates from the download manager, it maps them toTafsirDownloadStateobjects and emits new states, triggering automatic UI recomposition. -
User Action: When a user triggers a download, the UI calls
viewModel.onEvent(TafsirEvent.DownloadTafsir(key)), which delegates toTafsirDownloadManager.startDownload(). -
Background Processing: The download manager verifies network connectivity via
NetworkStateReceiver, then launches a WorkManager task. Progress updates flow back to the ViewModel through callback observers. -
Persistence: Upon completion,
QuranTafsirDBHelperwrites the tafsir data to SQLite, and the ViewModel updatesdownloadedTafsirKeysin the UI state, immediately reflecting the new available resource in the interface.
Code Examples
Observing ViewModel State in Compose
The UI layer consumes state through Kotlin Coroutines collectAsState(), ensuring the interface reacts to data changes without manual callback wiring:
@Composable
fun TafsirScreen(viewModel: TafsirViewModel = viewModel()) {
val uiState by viewModel.uiState.collectAsState()
when {
uiState.isLoading -> CircularProgressIndicator()
uiState.error != null -> Text(uiState.error?.message ?: "Error")
else -> TafsirList(
state = uiState,
onEvent = viewModel::onEvent
)
}
}
Initiating a Download from UI
User interactions are encapsulated as sealed class events, maintaining type-safe communication between UI and ViewModel:
Button(
onClick = {
viewModel.onEvent(TafsirEvent.DownloadTafsir(tafsirKey))
}
) {
Text("Download Tafsir")
}
Reading Saved Preferences via DataStore
The repository uses Jetpack DataStore instead of SharedPreferences for asynchronous, type-safe storage operations:
object SPReader {
private val Context.dataStore by preferencesDataStore(name = "quranapp_prefs")
private val SAVED_TAFSIR_KEY = stringPreferencesKey("saved_tafsir_key")
suspend fun getSavedTafsirKey(context: Context): String? =
context.dataStore.data
.map { it[SAVED_TAFSIR_KEY] }
.firstOrNull()
}
Key Files Reference
Navigate the repository efficiently using these direct links to critical implementation files:
- [
README.md](https://github.com/alfaazplus/quranapp/blob/master/README.md) – Project overview and contribution guidelines LICENSE– GPL-v3 license termsapp/build.gradle.kts– Module-level build configuration enabling Compose and ViewBinding- [
TafsirViewModel.kt](https://github.com/alfaazplus/quranapp/blob/master/app/src/main/java/com/quranapp/android/viewmodels/TafsirViewModel.kt) – Primary ViewModel implementation for tafsir management - [
AlfaazPlusApi.kt](https://github.com/alfaazplus/quranapp/blob/master/app/src/main/java/com/quranapp/android/api/AlfaazPlusApi.kt) – REST API interface definitions - [
QuranTafsirDBHelper.kt](https://github.com/alfaazplus/quranapp/blob/master/app/src/main/java/com/quranapp/android/utils/reader/tafsir/QuranTafsirDBHelper.kt) – SQLite database helper for offline storage - [
DataStoreManager.kt](https://github.com/alfaazplus/quranapp/blob/master/app/src/main/java/com/quranapp/android/utils/prefs/DataStoreManager.kt) – DataStore preferences wrapper - [
PeaceRadioGroup.kt](https://github.com/alfaazplus/quranapp/blob/master/app/src/main/java/com/quranapp/android/widgets/PeaceRadioGroup.kt) – Example custom UI component - [
FragMain.kt](https://github.com/alfaazplus/quranapp/blob/master/app/src/main/java/com/quranapp/android/frags/FragMain.kt) – Main activity fragment container
Summary
- MVVM Architecture: The repository strictly separates Presentation, ViewModel, Domain, and Data layers to ensure testability and maintainability.
- State Management: UI states flow through
StateFlowobjects in ViewModels, consumed by Jetpack Compose screens for reactive updates. - Offline-First Design: Tafsir content persists in SQLite via
QuranTafsirDBHelper, with DataStore managing user preferences asynchronously. - Modular UI: Custom widgets like
PeaceRadioGroupreside in thewidgetspackage, while navigation logic lives in thefragspackage. - Dependency Versioning: The
gradle/libs.versions.tomlfile centralizes library versions across the multi-module project including thepeacedesignUI library.
Frequently Asked Questions
What architecture pattern does QuranApp use?
The repository implements the Model-View-ViewModel (MVVM) pattern with unidirectional data flow. Activities and Fragments host Jetpack Compose UI elements that observe immutable state objects from ViewModels, while business logic remains isolated in domain managers and data repositories.
How are UI components organized in the repository?
UI components follow a hierarchical structure: custom Android Views and Compose wrappers reside in app/src/main/java/com/quranapp/android/widgets/, while screen-level containers and navigation logic are located in the frags directory. Shared theming components live in the separate peacedesign module.
Where is dependency injection handled?
The project does not use a DI framework like Hilt or Dagger. Instead, it employs manual singleton initialization through static initialize(context) methods, as seen in [TafsirViewModel.kt](https://github.com/alfaazplus/quranapp/blob/master/app/src/main/java/com/quranapp/android/viewmodels/TafsirViewModel.kt) where TafsirDownloadManager is initialized with an application context.
How does the app manage offline tafsir data?
Offline storage combines SQLite for content persistence and DataStore for metadata. When a user downloads a tafsir, TafsirDownloadManager streams the data to QuranTafsirDBHelper, which writes to a local SQLite database. The ViewModel then queries this database via loadDownloadedTafsirKeys() to display available offline content without network calls.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →