diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 6f7fb286..3afb92a7 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -5,7 +5,7 @@
-
+
= MutableStateFlow(null)
@@ -43,6 +50,43 @@ class AllAlbumsViewModel @Inject constructor(
Pair(SortBy.DURATION, "duration"),
)
+ // Settings
+ init {
+ settingsRepository.albumGridCount.onEach { gridCount ->
+ _allAlbumsUiState.value = _allAlbumsUiState.value.copy(gridCount = gridCount)
+ }.launchIn(viewModelScope)
+
+ combine(
+ settingsRepository.albumSortOrder.distinctUntilChanged(),
+ settingsRepository.albumSortBy.distinctUntilChanged()
+ ) { sortOrder, sortBy ->
+ val sortByPair = sortAlbumsByEntries.find { it.first == sortBy }
+ ?: Pair(SortBy.LAST_PLAYED, "lastplayed")
+
+ Pair(sortOrder, sortByPair)
+ }.onEach { (sortOrder, sortByPair) ->
+ _allAlbumsUiState.value = _allAlbumsUiState.value.copy(
+ sortOrder = sortOrder,
+ sortBy = sortByPair
+ )
+ getPagingAlbums(
+ sortBy = sortByPair.second,
+ sortOrder = sortOrder
+ )
+ }.launchIn(viewModelScope)
+ }
+
+ init {
+ getBaseUrl()
+ getAlbumCount()
+ }
+
+ private fun getBaseUrl() {
+ viewModelScope.launch {
+ _baseUrl.value = authRepository.getBaseUrl()
+ }
+ }
+
private fun getAlbumCount() {
viewModelScope.launch {
artistRepository.getAlbumCount().collectLatest {
@@ -66,64 +110,39 @@ class AllAlbumsViewModel @Inject constructor(
}
}
- init {
- getBaseUrl()
- }
-
-
- private fun getBaseUrl() {
+ private fun updateGridCount(count: Int) {
viewModelScope.launch {
- _baseUrl.value = authRepository.getBaseUrl()
+ settingsRepository.setAlbumGridCount(count)
}
-
}
- init {
- getPagingAlbums(
- sortBy = _allAlbumsUiState.value.sortBy.second,
- sortOrder = _allAlbumsUiState.value.sortOrder
- )
- getAlbumCount()
- }
-
-
fun onAlbumsUiEvent(event: AlbumsUiEvent) {
when (event) {
is AlbumsUiEvent.OnSortBy -> {
- // Retry fetching artist count if the previous sorting resulted to Error
- if (_allAlbumsUiState.value.totalAlbums is Resource.Error) {
- getAlbumCount()
- }
-
- if (event.sortByPair == _allAlbumsUiState.value.sortBy) {
- val newOrder = if (_allAlbumsUiState.value.sortOrder == SortOrder.ASCENDING)
- SortOrder.DESCENDING else SortOrder.ASCENDING
-
- _allAlbumsUiState.value = _allAlbumsUiState.value.copy(sortOrder = newOrder)
- getPagingAlbums(
- sortBy = event.sortByPair.second,
- sortOrder = newOrder
- )
- } else {
- _allAlbumsUiState.value = _allAlbumsUiState.value.copy(
- sortBy = event.sortByPair,
- sortOrder = SortOrder.DESCENDING
- )
- getPagingAlbums(
- sortBy = event.sortByPair.second,
- sortOrder = SortOrder.DESCENDING
- )
+ viewModelScope.launch {
+ // Retry fetching artist count if the previous sorting resulted to Error
+ if (_allAlbumsUiState.value.totalAlbums is Resource.Error) {
+ getAlbumCount()
+ }
+
+ if (event.sortByPair == _allAlbumsUiState.value.sortBy) {
+ val newOrder = if (_allAlbumsUiState.value.sortOrder == SortOrder.ASCENDING)
+ SortOrder.DESCENDING else SortOrder.ASCENDING
+
+ settingsRepository.setAlbumSortOrder(newOrder)
+ } else {
+ settingsRepository.setAlbumSortOrder(SortOrder.DESCENDING)
+ settingsRepository.setAlbumSortBy(event.sortByPair.first)
+ }
}
}
is AlbumsUiEvent.OnClickAlbum -> {
- // TODO: Navigate from the UI (apparently not in the VM)
+ // TODO: Navigate from the UI (handled by UI navigator)
}
is AlbumsUiEvent.OnUpdateGridCount -> {
- _allAlbumsUiState.value = _allAlbumsUiState.value.copy(
- gridCount = event.newCount
- )
+ updateGridCount(event.newCount)
}
is AlbumsUiEvent.OnRetry -> {
diff --git a/feature/artist/build.gradle.kts b/feature/artist/build.gradle.kts
index 718a847a..33d554ec 100644
--- a/feature/artist/build.gradle.kts
+++ b/feature/artist/build.gradle.kts
@@ -48,6 +48,7 @@ dependencies {
implementation(project(":network"))
implementation(project(":uicomponent"))
implementation(project(":feature:player"))
+ implementation(project(":feature:settings"))
// Common Feature
implementation(project(":feature:common"))
diff --git a/feature/artist/src/main/java/com/android/swingmusic/artist/presentation/viewmodel/ArtistsViewModel.kt b/feature/artist/src/main/java/com/android/swingmusic/artist/presentation/viewmodel/ArtistsViewModel.kt
index 59c86cd0..be007d1e 100644
--- a/feature/artist/src/main/java/com/android/swingmusic/artist/presentation/viewmodel/ArtistsViewModel.kt
+++ b/feature/artist/src/main/java/com/android/swingmusic/artist/presentation/viewmodel/ArtistsViewModel.kt
@@ -1,6 +1,7 @@
package com.android.swingmusic.artist.presentation.viewmodel
import androidx.compose.runtime.MutableState
+import androidx.compose.runtime.State
import androidx.compose.runtime.mutableStateOf
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
@@ -12,18 +13,25 @@ import com.android.swingmusic.auth.domain.repository.AuthRepository
import com.android.swingmusic.core.data.util.Resource
import com.android.swingmusic.core.domain.util.SortBy
import com.android.swingmusic.core.domain.util.SortOrder
+import com.android.swingmusic.settings.domain.repository.AppSettingsRepository
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.flow.collectLatest
+import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.flow.launchIn
+import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.launch
import javax.inject.Inject
@HiltViewModel
class ArtistsViewModel @Inject constructor(
private val artistRepository: ArtistRepository,
- private val authRepository: AuthRepository
+ private val authRepository: AuthRepository,
+ private val settingsRepository: AppSettingsRepository
) : ViewModel() {
private var baseUrl: MutableState = mutableStateOf(null)
- val artistsUiState: MutableState = mutableStateOf(ArtistsUiState())
+ private val _artistsUiState: MutableState = mutableStateOf(ArtistsUiState())
+ val artistsUiState: State = _artistsUiState
val sortArtistsByEntries: List> = listOf(
Pair(SortBy.LAST_PLAYED, "lastplayed"),
@@ -36,10 +44,48 @@ class ArtistsViewModel @Inject constructor(
Pair(SortBy.DURATION, "duration"),
)
+ init {
+ settingsRepository.artistGridCount.onEach { gridCount ->
+ _artistsUiState.value = _artistsUiState.value.copy(gridCount = gridCount)
+ }.launchIn(viewModelScope)
+
+ combine(
+ settingsRepository.artistSortOrder.distinctUntilChanged(),
+ settingsRepository.artistSortBy.distinctUntilChanged()
+ ) { sortOrder, sortBy ->
+ val sortByPair = sortArtistsByEntries.find { it.first == sortBy }
+ ?: Pair(SortBy.LAST_PLAYED, "lastplayed")
+
+ Pair(sortOrder, sortByPair)
+ }.onEach { (sortOrder, sortByPair) ->
+ _artistsUiState.value = _artistsUiState.value.copy(
+ sortOrder = sortOrder,
+ sortBy = sortByPair
+ )
+ getPagingArtists(
+ sortBy = _artistsUiState.value.sortBy.second,
+ sortOrder = _artistsUiState.value.sortOrder
+ )
+ }.launchIn(viewModelScope)
+ }
+
+ init {
+ getBaseUrl()
+ getArtistsCount()
+ }
+
+ fun baseUrl() = baseUrl
+
+ private fun getBaseUrl() {
+ viewModelScope.launch {
+ baseUrl.value = authRepository.getBaseUrl()
+ }
+ }
+
private fun getArtistsCount() {
viewModelScope.launch {
artistRepository.getArtistsCount().collectLatest {
- artistsUiState.value = artistsUiState.value.copy(totalArtists = it)
+ _artistsUiState.value = _artistsUiState.value.copy(totalArtists = it)
}
}
}
@@ -50,7 +96,7 @@ class ArtistsViewModel @Inject constructor(
SortOrder.ASCENDING -> 0
}
viewModelScope.launch {
- artistsUiState.value = artistsUiState.value.copy(
+ _artistsUiState.value = _artistsUiState.value.copy(
pagingArtists = artistRepository.getPagingArtists(
sortBy = sortBy,
sortOrder = order
@@ -59,74 +105,50 @@ class ArtistsViewModel @Inject constructor(
}
}
- init {
- getBaseUrl()
- }
-
- fun baseUrl() = baseUrl
-
- private fun getBaseUrl() {
+ private fun updateGridCount(count: Int) {
viewModelScope.launch {
- baseUrl.value = authRepository.getBaseUrl()
+ settingsRepository.setArtistGridCount(count)
}
}
- init {
- getPagingArtists(
- sortBy = artistsUiState.value.sortBy.second,
- sortOrder = artistsUiState.value.sortOrder
- )
- getArtistsCount()
- }
-
fun onArtistUiEvent(event: ArtistUiEvent) {
when (event) {
is ArtistUiEvent.OnSortBy -> {
- // Retry fetching artist count if the previous sorting resulted to Error
- if (artistsUiState.value.totalArtists is Resource.Error) {
- getArtistsCount()
- }
-
- if (event.sortByPair == artistsUiState.value.sortBy) {
- val newOrder = if (artistsUiState.value.sortOrder == SortOrder.ASCENDING)
- SortOrder.DESCENDING else SortOrder.ASCENDING
-
- artistsUiState.value = artistsUiState.value.copy(sortOrder = newOrder)
- getPagingArtists(
- sortBy = event.sortByPair.second,
- sortOrder = newOrder
- )
- } else {
- artistsUiState.value = artistsUiState.value.copy(
- sortBy = event.sortByPair,
- sortOrder = SortOrder.DESCENDING
- )
- getPagingArtists(
- sortBy = event.sortByPair.second,
- sortOrder = SortOrder.DESCENDING
- )
+ viewModelScope.launch {
+ // Retry fetching artist count if the previous sorting resulted to Error
+ if (_artistsUiState.value.totalArtists is Resource.Error) {
+ getArtistsCount()
+ }
+
+ if (event.sortByPair == _artistsUiState.value.sortBy) {
+ val newOrder = if (_artistsUiState.value.sortOrder == SortOrder.ASCENDING)
+ SortOrder.DESCENDING else SortOrder.ASCENDING
+
+ settingsRepository.setArtistSortOrder(newOrder)
+ } else {
+ settingsRepository.setArtistSortOrder(SortOrder.DESCENDING)
+ settingsRepository.setArtistSortBy(event.sortByPair.first)
+ }
}
}
is ArtistUiEvent.OnClickArtist -> {
- // TODO: Navigate from the UI (apparently not in the VM)
+ // TODO: Navigate from the UI (handled by UI navigator)
}
is ArtistUiEvent.OnUpdateGridCount -> {
- artistsUiState.value = artistsUiState.value.copy(
- gridCount = event.newCount
- )
+ updateGridCount(event.newCount)
}
is ArtistUiEvent.OnRetry -> {
- if (artistsUiState.value.totalArtists is Resource.Error) {
+ if (_artistsUiState.value.totalArtists is Resource.Error) {
getArtistsCount()
}
}
is ArtistUiEvent.OnPullToRefresh -> {
- val sortBy = artistsUiState.value.sortBy
- val sortOrder = artistsUiState.value.sortOrder
+ val sortBy = _artistsUiState.value.sortBy
+ val sortOrder = _artistsUiState.value.sortOrder
getPagingArtists(
sortBy = sortBy.second,
diff --git a/feature/settings/.gitignore b/feature/settings/.gitignore
new file mode 100644
index 00000000..42afabfd
--- /dev/null
+++ b/feature/settings/.gitignore
@@ -0,0 +1 @@
+/build
\ No newline at end of file
diff --git a/feature/settings/build.gradle.kts b/feature/settings/build.gradle.kts
new file mode 100644
index 00000000..9d3b3a94
--- /dev/null
+++ b/feature/settings/build.gradle.kts
@@ -0,0 +1,102 @@
+plugins {
+ id("com.android.library")
+ id("org.jetbrains.kotlin.android")
+ id("com.google.dagger.hilt.android")
+ id("com.google.devtools.ksp")
+}
+
+android {
+ namespace = "com.android.swingmusic.settings"
+ compileSdk = 35
+
+ defaultConfig {
+ minSdk = 26
+
+ testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
+ consumerProguardFiles("consumer-rules.pro")
+ }
+
+ buildTypes {
+ release {
+ isMinifyEnabled = true
+ }
+ }
+ compileOptions {
+ sourceCompatibility = JavaVersion.VERSION_17
+ targetCompatibility = JavaVersion.VERSION_17
+ }
+ kotlinOptions {
+ jvmTarget = "17"
+ }
+ buildFeatures {
+ compose = true
+ }
+
+ composeOptions {
+ kotlinCompilerExtensionVersion = "1.5.0"
+ }
+
+ packaging {
+ resources {
+ excludes += "/META-INF/{AL2.0,LGPL2.1}"
+ }
+ }
+}
+
+dependencies {
+ // Core
+ implementation("androidx.core:core-ktx:1.15.0")
+ implementation("androidx.lifecycle:lifecycle-runtime-ktx:2.8.7")
+ // Project Core
+ implementation(project(":core"))
+
+
+ // Compose
+ implementation(platform("androidx.compose:compose-bom:2024.10.01"))
+ implementation("androidx.compose.ui:ui")
+ implementation("androidx.compose.ui:ui-graphics")
+ implementation("androidx.compose.ui:ui-tooling-preview")
+ implementation("androidx.compose.material3:material3")
+ debugImplementation("androidx.compose.ui:ui-tooling")
+ debugImplementation("androidx.compose.ui:ui-test-manifest")
+
+ // Hilt DI
+ implementation("com.google.dagger:hilt-android:2.50")
+ ksp("com.google.dagger:hilt-android-compiler:2.50")
+
+ // Hilt Navigation-Compose
+ implementation("androidx.hilt:hilt-navigation-compose:1.2.0")
+
+
+ //Prefs Datastore
+ implementation("androidx.datastore:datastore-preferences:1.1.1")
+
+ // Timber
+ implementation("com.jakewharton.timber:timber:5.0.1")
+
+ // Navigation
+ implementation("io.github.raamcosta.compose-destinations:core:1.9.63")
+ ksp("io.github.raamcosta.compose-destinations:ksp:1.9.63")
+
+ // Coroutines
+ implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.1")
+ implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:1.8.0")
+ implementation("androidx.lifecycle:lifecycle-runtime-ktx:2.8.7")
+ implementation("androidx.lifecycle:lifecycle-viewmodel-compose:2.8.7")
+}
+
+kotlin {
+ sourceSets {
+ debug {
+ kotlin.srcDir("build/generated/ksp/debug/kotlin")
+ }
+ release {
+ kotlin.srcDir("build/generated/ksp/release/kotlin")
+ }
+ }
+}
+
+ksp {
+ arg("compose-destinations.mode", "destinations")
+ arg("compose-destinations.moduleName", "settings")
+}
diff --git a/feature/settings/consumer-rules.pro b/feature/settings/consumer-rules.pro
new file mode 100644
index 00000000..e69de29b
diff --git a/feature/settings/proguard-rules.pro b/feature/settings/proguard-rules.pro
new file mode 100644
index 00000000..481bb434
--- /dev/null
+++ b/feature/settings/proguard-rules.pro
@@ -0,0 +1,21 @@
+# Add project specific ProGuard rules here.
+# You can control the set of applied configuration files using the
+# proguardFiles setting in build.gradle.
+#
+# For more details, see
+# http://developer.android.com/guide/developing/tools/proguard.html
+
+# If your project uses WebView with JS, uncomment the following
+# and specify the fully qualified class name to the JavaScript interface
+# class:
+#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
+# public *;
+#}
+
+# Uncomment this to preserve the line number information for
+# debugging stack traces.
+#-keepattributes SourceFile,LineNumberTable
+
+# If you keep the line number information, uncomment this to
+# hide the original source file name.
+#-renamesourcefileattribute SourceFile
\ No newline at end of file
diff --git a/feature/settings/src/androidTest/java/com/android/swingmusic/settings/ExampleInstrumentedTest.kt b/feature/settings/src/androidTest/java/com/android/swingmusic/settings/ExampleInstrumentedTest.kt
new file mode 100644
index 00000000..e678b60f
--- /dev/null
+++ b/feature/settings/src/androidTest/java/com/android/swingmusic/settings/ExampleInstrumentedTest.kt
@@ -0,0 +1,24 @@
+package com.android.swingmusic.settings
+
+import androidx.test.platform.app.InstrumentationRegistry
+import androidx.test.ext.junit.runners.AndroidJUnit4
+
+import org.junit.Test
+import org.junit.runner.RunWith
+
+import org.junit.Assert.*
+
+/**
+ * Instrumented test, which will execute on an Android device.
+ *
+ * See [testing documentation](http://d.android.com/tools/testing).
+ */
+@RunWith(AndroidJUnit4::class)
+class ExampleInstrumentedTest {
+ @Test
+ fun useAppContext() {
+ // Context of the app under test.
+ val appContext = InstrumentationRegistry.getInstrumentation().targetContext
+ assertEquals("com.android.swingmusic.settings.test", appContext.packageName)
+ }
+}
\ No newline at end of file
diff --git a/feature/settings/src/main/AndroidManifest.xml b/feature/settings/src/main/AndroidManifest.xml
new file mode 100644
index 00000000..a5918e68
--- /dev/null
+++ b/feature/settings/src/main/AndroidManifest.xml
@@ -0,0 +1,4 @@
+
+
+
+
\ No newline at end of file
diff --git a/feature/settings/src/main/java/com/android/swingmusic/settings/data/datastore/AppSettings.kt b/feature/settings/src/main/java/com/android/swingmusic/settings/data/datastore/AppSettings.kt
new file mode 100644
index 00000000..614e17ea
--- /dev/null
+++ b/feature/settings/src/main/java/com/android/swingmusic/settings/data/datastore/AppSettings.kt
@@ -0,0 +1,75 @@
+package com.android.swingmusic.settings.data.datastore
+
+import android.content.Context
+import androidx.datastore.core.DataStore
+import androidx.datastore.preferences.core.Preferences
+import androidx.datastore.preferences.core.edit
+import androidx.datastore.preferences.core.intPreferencesKey
+import androidx.datastore.preferences.core.stringPreferencesKey
+import androidx.datastore.preferences.preferencesDataStore
+import com.android.swingmusic.core.domain.util.SortBy
+import com.android.swingmusic.core.domain.util.SortOrder
+import dagger.hilt.android.qualifiers.ApplicationContext
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.map
+import javax.inject.Inject
+import javax.inject.Singleton
+
+@Singleton
+class AppSettings @Inject constructor(
+ @ApplicationContext private val context: Context
+) {
+ private companion object {
+ private val Context.dataStore: DataStore by preferencesDataStore(name = "app_settings")
+
+ private val ALBUM_GRID_COUNT = intPreferencesKey("album_grid_count")
+ private val ALBUM_SORT_BY = stringPreferencesKey("album_sort_by")
+ private val ALBUM_SORT_ORDER = stringPreferencesKey("album_sort_order")
+
+ private val ARTIST_GRID_COUNT = intPreferencesKey("artist_grid_count")
+ private val ARTIST_SORT_BY = stringPreferencesKey("artist_sort_by")
+ private val ARTIST_SORT_ORDER = stringPreferencesKey("artist_sort_order")
+ }
+
+ // Album Flows
+ val getAlbumGridCount: Flow = context.dataStore.data.map {
+ it[ALBUM_GRID_COUNT] ?: 2
+ }
+ val getAlbumSortBy: Flow = context.dataStore.data.map {
+ it[ALBUM_SORT_BY] ?: SortBy.LAST_PLAYED.name
+ }
+ val getAlbumSortOrder: Flow = context.dataStore.data.map {
+ it[ALBUM_SORT_ORDER] ?: SortOrder.DESCENDING.name
+ }
+
+ // Artist Flows
+ val getArtistGridCount: Flow = context.dataStore.data.map {
+ it[ARTIST_GRID_COUNT] ?: 2
+ }
+ val getArtistSortBy: Flow = context.dataStore.data.map {
+ it[ARTIST_SORT_BY] ?: SortBy.LAST_PLAYED.name
+ }
+ val getArtistSortOrder: Flow = context.dataStore.data.map {
+ it[ARTIST_SORT_ORDER] ?: SortOrder.DESCENDING.name
+ }
+
+ // Album Update methods
+ suspend fun updateAlbumGridCount(count: Int) =
+ context.dataStore.edit { it[ALBUM_GRID_COUNT] = count }
+
+ suspend fun updateAlbumSortBy(value: String) =
+ context.dataStore.edit { it[ALBUM_SORT_BY] = value }
+
+ suspend fun updateAlbumSortOrder(value: String) =
+ context.dataStore.edit { it[ALBUM_SORT_ORDER] = value }
+
+ // Artist Update Methods
+ suspend fun updateArtistGridCount(count: Int) =
+ context.dataStore.edit { it[ARTIST_GRID_COUNT] = count }
+
+ suspend fun updateArtistSortBy(value: String) =
+ context.dataStore.edit { it[ARTIST_SORT_BY] = value }
+
+ suspend fun updateArtistSortOrder(value: String) =
+ context.dataStore.edit { it[ARTIST_SORT_ORDER] = value }
+}
diff --git a/feature/settings/src/main/java/com/android/swingmusic/settings/data/repository/AppSettingsDataRepository.kt b/feature/settings/src/main/java/com/android/swingmusic/settings/data/repository/AppSettingsDataRepository.kt
new file mode 100644
index 00000000..9294f7b8
--- /dev/null
+++ b/feature/settings/src/main/java/com/android/swingmusic/settings/data/repository/AppSettingsDataRepository.kt
@@ -0,0 +1,62 @@
+package com.android.swingmusic.settings.data.repository
+
+import com.android.swingmusic.core.domain.util.SortBy
+import com.android.swingmusic.core.domain.util.SortOrder
+import com.android.swingmusic.settings.data.datastore.AppSettings
+import com.android.swingmusic.settings.domain.repository.AppSettingsRepository
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.map
+import javax.inject.Inject
+import javax.inject.Singleton
+
+@Singleton
+class AppSettingsDataRepository @Inject constructor(
+ private val appSettings: AppSettings
+) : AppSettingsRepository {
+
+ // --- Album Settings ---
+ override val albumGridCount: Flow = appSettings.getAlbumGridCount
+
+ override val albumSortBy: Flow = appSettings.getAlbumSortBy.map {
+ SortBy.valueOf(it)
+ }
+
+ override val albumSortOrder: Flow = appSettings.getAlbumSortOrder.map {
+ SortOrder.valueOf(it)
+ }
+
+ override suspend fun setAlbumGridCount(count: Int) {
+ appSettings.updateAlbumGridCount(count)
+ }
+
+ override suspend fun setAlbumSortBy(sortBy: SortBy) {
+ appSettings.updateAlbumSortBy(sortBy.name)
+ }
+
+ override suspend fun setAlbumSortOrder(order: SortOrder) {
+ appSettings.updateAlbumSortOrder(order.name)
+ }
+
+ // --- Artist Settings ---
+ override val artistGridCount: Flow = appSettings.getArtistGridCount
+
+ override val artistSortBy: Flow = appSettings.getArtistSortBy.map {
+ SortBy.valueOf(it)
+ }
+
+ override val artistSortOrder: Flow = appSettings.getArtistSortOrder.map {
+ SortOrder.valueOf(it)
+ }
+
+ override suspend fun setArtistGridCount(count: Int) {
+ appSettings.updateArtistGridCount(count)
+ }
+
+ override suspend fun setArtistSortBy(sortBy: SortBy) {
+ appSettings.updateArtistSortBy(sortBy.name)
+ }
+
+ override suspend fun setArtistSortOrder(order: SortOrder) {
+ appSettings.updateArtistSortOrder(order.name)
+ }
+}
diff --git a/feature/settings/src/main/java/com/android/swingmusic/settings/di/AppSettingsModule.kt b/feature/settings/src/main/java/com/android/swingmusic/settings/di/AppSettingsModule.kt
new file mode 100644
index 00000000..279c9ed7
--- /dev/null
+++ b/feature/settings/src/main/java/com/android/swingmusic/settings/di/AppSettingsModule.kt
@@ -0,0 +1,23 @@
+package com.android.swingmusic.settings.di
+
+import android.content.Context
+import com.android.swingmusic.settings.data.datastore.AppSettings
+import dagger.Module
+import dagger.Provides
+import dagger.hilt.InstallIn
+import dagger.hilt.android.qualifiers.ApplicationContext
+import dagger.hilt.components.SingletonComponent
+import javax.inject.Singleton
+
+@Module
+@InstallIn(SingletonComponent::class)
+object AppSettingsModule {
+
+ @Provides
+ @Singleton
+ fun provideAppSettingsDataStore(
+ @ApplicationContext context: Context
+ ): AppSettings {
+ return AppSettings(context)
+ }
+}
diff --git a/feature/settings/src/main/java/com/android/swingmusic/settings/di/RepositoryModule.kt b/feature/settings/src/main/java/com/android/swingmusic/settings/di/RepositoryModule.kt
new file mode 100644
index 00000000..ad8195c9
--- /dev/null
+++ b/feature/settings/src/main/java/com/android/swingmusic/settings/di/RepositoryModule.kt
@@ -0,0 +1,20 @@
+package com.android.swingmusic.settings.di
+
+import com.android.swingmusic.settings.data.repository.AppSettingsDataRepository
+import com.android.swingmusic.settings.domain.repository.AppSettingsRepository
+import dagger.Binds
+import dagger.Module
+import dagger.hilt.InstallIn
+import dagger.hilt.components.SingletonComponent
+import javax.inject.Singleton
+
+@Module
+@InstallIn(SingletonComponent::class)
+abstract class RepositoryModule {
+
+ @Binds
+ @Singleton
+ abstract fun bindAppSettingsRepository(
+ impl: AppSettingsDataRepository
+ ): AppSettingsRepository
+}
diff --git a/feature/settings/src/main/java/com/android/swingmusic/settings/domain/repository/AppSettingsRepository.kt b/feature/settings/src/main/java/com/android/swingmusic/settings/domain/repository/AppSettingsRepository.kt
new file mode 100644
index 00000000..d94d656b
--- /dev/null
+++ b/feature/settings/src/main/java/com/android/swingmusic/settings/domain/repository/AppSettingsRepository.kt
@@ -0,0 +1,23 @@
+package com.android.swingmusic.settings.domain.repository
+
+import com.android.swingmusic.core.domain.util.SortBy
+import com.android.swingmusic.core.domain.util.SortOrder
+import kotlinx.coroutines.flow.Flow
+
+interface AppSettingsRepository {
+ val albumGridCount: Flow
+ val albumSortBy: Flow
+ val albumSortOrder: Flow
+
+ val artistGridCount: Flow
+ val artistSortBy: Flow
+ val artistSortOrder: Flow
+
+ suspend fun setAlbumGridCount(count: Int)
+ suspend fun setAlbumSortBy(sortBy: SortBy)
+ suspend fun setAlbumSortOrder(order: SortOrder)
+
+ suspend fun setArtistGridCount(count: Int)
+ suspend fun setArtistSortBy(sortBy: SortBy)
+ suspend fun setArtistSortOrder(order: SortOrder)
+}
diff --git a/feature/settings/src/test/java/com/android/swingmusic/settings/ExampleUnitTest.kt b/feature/settings/src/test/java/com/android/swingmusic/settings/ExampleUnitTest.kt
new file mode 100644
index 00000000..c16564ec
--- /dev/null
+++ b/feature/settings/src/test/java/com/android/swingmusic/settings/ExampleUnitTest.kt
@@ -0,0 +1,17 @@
+package com.android.swingmusic.settings
+
+import org.junit.Test
+
+import org.junit.Assert.*
+
+/**
+ * Example local unit test, which will execute on the development machine (host).
+ *
+ * See [testing documentation](http://d.android.com/tools/testing).
+ */
+class ExampleUnitTest {
+ @Test
+ fun addition_isCorrect() {
+ assertEquals(4, 2 + 2)
+ }
+}
\ No newline at end of file
diff --git a/settings.gradle.kts b/settings.gradle.kts
index bc94b0b0..99fa134b 100644
--- a/settings.gradle.kts
+++ b/settings.gradle.kts
@@ -36,3 +36,4 @@ include(":feature:home")
include(":feature:album")
include(":feature:common")
include(":feature:search")
+include(":feature:settings")