-
-
Notifications
You must be signed in to change notification settings - Fork 66
Technology Stack
Anjishnu Nandi edited this page Jun 21, 2026
·
5 revisions
This document details the technical architecture and libraries used in Rhythm Music Player.
| Technology | Purpose |
|---|---|
| Jetpack Compose | Modern declarative UI toolkit for Android |
| Material 3 | Material Design components and theming system |
| Material Symbols Variable Font | Custom static font asset replacing deprecated material-icons-extended |
| AndroidX Palette | Dynamic color extraction from images |
| Technology | Purpose |
|---|---|
| Media3 ExoPlayer | Professional-grade media playback engine |
| FFmpeg Decoder | Extended codec support (EAC3-JOC, AC-3, WMA) |
| MediaStore API | Android media content provider |
| AudioFocus | Audio focus management for calls/notifications |
| Technology | Purpose |
|---|---|
| Glance | Modern reactive widgets with Material 3 design |
| RemoteViews | Legacy widget support |
| WorkManager | Background widget updates |
| Technology | Purpose |
|---|---|
| Kotlin | 100% Kotlin codebase |
| Kotlin Coroutines | Asynchronous programming |
| Kotlin Flow | Reactive streams and state management |
MVVM (Model-View-ViewModel) + Clean Architecture
βββββββββββββββββββββββββββββββββββββββββββββββββββ
β UI Layer β
β ββββββββββββββββββββββββββββββββββββββββββββ β
β β Composables (Screens & Components) β β
β ββββββββββββββββββββββββββββββββββββββββββββ β
β β β
β ββββββββββββββββββββββββββββββββββββββββββββ β
β β ViewModels (State) β β
β ββββββββββββββββββββββββββββββββββββββββββββ β
βββββββββββββββββββββββββββββββββββββββββββββββββββ
β
βββββββββββββββββββββββββββββββββββββββββββββββββββ
β Domain Layer β
β ββββββββββββββββββββββββββββββββββββββββββββ β
β β Use Cases (Business Logic) β β
β ββββββββββββββββββββββββββββββββββββββββββββ β
β ββββββββββββββββββββββββββββββββββββββββββββ β
β β Repository Interfaces β β
β ββββββββββββββββββββββββββββββββββββββββββββ β
β ββββββββββββββββββββββββββββββββββββββββββββ β
β β Models (Data Entities) β β
β ββββββββββββββββββββββββββββββββββββββββββββ β
βββββββββββββββββββββββββββββββββββββββββββββββββββ
β
βββββββββββββββββββββββββββββββββββββββββββββββββββ
β Data Layer β
β ββββββββββββββββββββββββββββββββββββββββββββ β
β β Repository Implementations β β
β ββββββββββββββββββββββββββββββββββββββββββββ β
β ββββββββββββββββββββββββββββββββββββββββββββ β
β β Data Sources (Local & Remote) β β
β β β’ MediaStore β β
β β β’ LRCLib API β β
β β β’ Deezer API β β
β β β’ Local Storage β β
β ββββββββββββββββββββββββββββββββββββββββββββ β
βββββββββββββββββββββββββββββββββββββββββββββββββββ
app/src/main/java/chromahub/rhythm/app/
βββ activities/ # Main Android activities (e.g. MainActivity.kt)
βββ core/ # Core Shared Business/Domain Logic
β βββ domain/
β βββ model/ # Core domain entities (Song, Album, Artist, Playlist)
β βββ repository/ # Core repository interfaces
β βββ usecase/ # Core business use cases
βββ features/ # Feature Modules
β βββ local/ # Local media playback feature (Clean Architecture)
β β βββ data/ # Local repositories, room database, MediaStore integration
β β βββ di/ # Local dependency injection configs
β β βββ domain/ # Local use cases and business logic
β β βββ presentation/ # Local screens, viewmodels, themes, and views
β βββ streaming/ # Streaming server client feature (Clean Architecture)
β βββ data/ # Remote repositories and networking clients
β βββ di/ # Streaming dependency injection configs
β βββ domain/ # Streaming use cases
β βββ presentation/ # Streaming screens and viewmodels
βββ infrastructure/ # Base Infrastructure layer
β βββ audio/ # ExoPlayer setup, controller, and FFmpeg configuration
β βββ network/ # Retrofit, OkHttp, and REST API definitions
β βββ service/ # MusicService.kt & background media session handlers
β βββ widget/ # Glance app widget definitions
β βββ worker/ # WorkManager background sync/scan workers
βββ shared/ # Shared Cross-Cutting Presentation/Domain/Data components
β βββ data/
β βββ domain/
β βββ presentation/ # Shared UI elements, color themes, styling, and custom icons
βββ util/ & utils/ # General utility files and extension functions
// Core
androidx.core:core-ktx
androidx.core:core-splashscreen
androidx.lifecycle:lifecycle-runtime-ktx
androidx.lifecycle:lifecycle-viewmodel-compose
androidx.fragment:fragment-ktx
// Compose
androidx.compose.ui:ui
androidx.compose.material3:material3
androidx.compose.ui:ui-tooling
// Navigation
androidx.navigation:navigation-compose
// Media
androidx.media3:media3-exoplayer
androidx.media3:media3-session
androidx.media3:media3-ui
// Widgets
androidx.glance:glance-appwidget
androidx.work:work-runtime-ktx
// Room Database
androidx.room:room-runtime
androidx.room:room-ktx
// Other
androidx.palette:palette-ktx// HTTP Client
com.squareup.retrofit2:retrofit
com.squareup.retrofit2:converter-gson
com.squareup.okhttp3:okhttp
com.squareup.okhttp3:logging-interceptor
// JSON
com.google.code.gson:gson// Coil for Compose
io.coil-kt:coil-compose// Permissions
com.google.accompanist:accompanist-permissions
// Coroutines
org.jetbrains.kotlinx:kotlinx-coroutines-androidRhythm uses Kotlin Flow and Compose state for reactive UI updates:
// ViewModel observes ExoPlayer state via MediaController
class MusicViewModel(application: Application) : AndroidViewModel(application) {
private var mediaController: MediaController? = null
val playbackState: StateFlow<@Player.State Int> =
MutableStateFlow(Player.STATE_IDLE)
fun connect(controller: MediaController) {
mediaController = controller
controller.addListener(object : Player.Listener {
override fun onPlaybackStateChanged(state: Int) {
(playbackState as MutableStateFlow).value = state
}
})
}
}Data access abstracted through repositories:
interface MusicRepository {
fun getSongs(): Flow<List<PlayableItem>>
fun getAlbums(): Flow<List<AlbumItem>>
fun getArtists(): Flow<List<ArtistItem>>
fun getPlaylists(): Flow<List<PlaylistItem>>
suspend fun getSongById(id: String): PlayableItem?
suspend fun getAlbumById(id: String): AlbumItem?
suspend fun searchSongs(query: String): List<PlayableItem>
}βββββββββββββββββββββββββββββββββββββββ
β MusicService β
β (Foreground Service) β
β β
β ββββββββββββββββββββββββββββββββ β
β β ExoPlayer β β
β β β’ Media3 ExoPlayer 1.10.1 β β
β β β’ FFmpeg decoder extension β β
β β β’ Gapless playback β β
β β β’ Audio focus handling β β
β ββββββββββββββββββββββββββββββββ β
β β
β ββββββββββββββββββββββββββββββββ β
β β MediaSession β β
β β β’ Playback state β β
β β β’ Queue management β β
β β β’ Media buttons β β
β ββββββββββββββββββββββββββββββββ β
β β
β ββββββββββββββββββββββββββββββββ β
β β MediaNotification β β
β β β’ Playback controls β β
β β β’ Album art β β
β β β’ Metadata display β β
β ββββββββββββββββββββββββββββββββ β
βββββββββββββββββββββββββββββββββββββββ
β
βββββββββββββββββββββββββββββββββββββββ
β UI (Player Composables) β
β β’ Observe playback state β
β β’ Send playback commands β
β β’ Display metadata β
βββββββββββββββββββββββββββββββββββββββ
class RhythmMusicWidget : GlanceAppWidget() {
override suspend fun provideGlance(context: Context, id: GlanceId) {
provideContent {
RhythmMusicWidgetContent()
}
}
}
@Composable
fun RhythmMusicWidgetContent() {
// Observe playback data via GlanceState
// Material 3 widget UI
MaterialTheme {
// Widget content with play/pause, skip, track info, album art
}
}class RhythmWidgetWorker(
context: Context,
params: WorkerParameters
) : CoroutineWorker(context, params) {
override suspend fun doWork(): Result {
// Update widget data from current playback state
GlanceAppWidgetManager(context)
.getGlanceIds(RhythmMusicWidget::class.java)
.forEach { glanceId ->
RhythmMusicWidget().update(context, glanceId)
}
return Result.success()
}
}// build.gradle.kts
plugins {
alias(libs.plugins.android.application)
alias(libs.plugins.kotlin.compose)
alias(libs.plugins.ksp)
id("kotlin-parcelize")
}
android {
namespace = "chromahub.rhythm.app"
compileSdk = 37
defaultConfig {
applicationId = "chromahub.rhythm.app"
minSdk = 26
targetSdk = 37
versionCode = 514081066
versionName = "5.1.408.1066"
}
buildFeatures {
compose = true
buildConfig = true
}
kotlin {
compilerOptions {
jvmTarget.set(org.jetbrains.kotlin.gradle.dsl.JvmTarget.JVM_17)
freeCompilerArgs.addAll(
"-opt-in=androidx.compose.material3.ExperimentalMaterial3ExpressiveApi",
"-opt-in=androidx.compose.material3.ExperimentalMaterial3Api"
)
}
}
}# gradle/libs.versions.toml
[versions]
agp = "9.2.1"
kotlin = "2.4.0"
ksp = "2.3.6"
composeBom = "2026.06.00"
material3 = "1.5.0-alpha22"
media3 = "1.10.1"
[libraries]
androidx-ui = { group = "androidx.compose.ui", name = "ui" }
androidx-material3-android = { group = "androidx.compose.material3", name = "material3-android", version.ref = "material3" }
androidx-media3-exoplayer = { group = "androidx.media3", name = "media3-exoplayer", version.ref = "media3" }- ViewModel logic testing (JUnit 4)
- Repository testing
- Use case testing
- Compose UI testing (Compose Test)
- Navigation testing
- Integration testing
- Macrobenchmark for baseline profiles
# Unit tests
./gradlew test
# Instrumented tests
./gradlew connectedAndroidTest- No Analytics: Zero tracking code
- Local Storage: All data stored on device
- Minimal Permissions: Only essential permissions
- FOSS Compliance: Fully open source
- Reproducible Builds: Consistent APK generation
- Lazy Loading: Load music library on demand
- Image Caching: Coil caches album art efficiently
- Background Processing: WorkManager for non-urgent tasks
- Compose Optimization: Remember, derivedStateOf, keys
- ExoPlayer Buffering: Optimized buffer sizes
- GitHub Actions for automated builds (android.yml, beta.yml, release.yml)
- Automated testing on push
- Release automation with signing
- Code quality checks and linting
- Reproducible build support
Want to contribute? Check the Contributing Guide! Questions? Ask in Telegram or Discord.