From 05b202e1aacd1167cca55e39d249a959fe3b62c0 Mon Sep 17 00:00:00 2001 From: Paul Woitaschek Date: Tue, 26 Aug 2025 15:40:22 +0200 Subject: [PATCH 01/20] Create a :core:ui module and move the datastore classes to the data module. --- app/build.gradle.kts | 1 + .../voice/app/SleepTimerIntegrationTest.kt | 6 ++-- .../voice/app/StartDestinationProvider.kt | 4 +-- .../kotlin/voice/app/features/MainActivity.kt | 2 +- .../features/widget/TriggerWidgetOnChange.kt | 2 +- .../app/features/widget/WidgetUpdater.kt | 2 +- .../main/kotlin/voice/app/injection/App.kt | 4 +-- core/common/build.gradle.kts | 1 - .../voice/core/common/pref/PrefQualifiers.kt | 27 ------------------ core/data/api/build.gradle.kts | 1 + .../voice/core/data/store/StoreQualifiers.kt | 27 ++++++++++++++++++ .../core/data/store}/PrefsDataMigration.kt | 8 +++--- .../voice/core/data/store/StoreModule.kt | 28 ++++++++----------- .../core/data/store}/DataMigrationTests.kt | 7 +++-- .../vocie/core/data/store}/MigrationTests.kt | 24 ++++++++++++---- .../core/data/repo/internals/TestGraph.kt | 2 +- .../core/datastore/VoiceDataStoreFactory.kt | 4 +-- .../voice/core/playback/PlayerController.kt | 2 +- .../voice/core/playback/player/VoicePlayer.kt | 6 ++-- .../session/LibrarySessionCallback.kt | 2 +- .../playback/session/MediaItemProvider.kt | 2 +- .../session/search/BookSearchHandler.kt | 2 +- core/ui/build.gradle.kts | 11 ++++++++ .../voice/core/ui}/DarkThemeSettable.kt | 2 +- .../kotlin/voice/core/ui}/ImmutableFile.kt | 2 +- .../kotlin/voice/core/ui}/PaddingValues.kt | 2 +- .../main/kotlin/voice/core/ui}/PlayButton.kt | 2 +- .../main/kotlin/voice/core/ui}/SharedGraph.kt | 4 +-- .../main/kotlin/voice/core/ui}/ViewModel.kt | 2 +- .../main/kotlin/voice/core/ui}/VoiceTheme.kt | 24 ++-------------- .../main/kotlin/voice/core/ui/isDarkTheme.kt | 20 +++++++++++++ features/bookOverview/build.gradle.kts | 1 + .../overview/BookOverviewItemViewState.kt | 2 +- .../overview/BookOverviewViewModel.kt | 4 +-- .../bookOverview/search/BookSearchScreen.kt | 2 +- .../bookOverview/views/BookOverview.kt | 6 ++-- .../features/bookOverview/views/ListBooks.kt | 2 +- .../views/topbar/BookOverviewTopBar.kt | 2 +- features/bookmark/build.gradle.kts | 2 ++ .../voice/features/bookmark/BookmarkScreen.kt | 2 +- .../features/bookmark/BookmarkViewModel.kt | 2 +- features/cover/build.gradle.kts | 1 + .../features/cover/SelectCoverFromInternet.kt | 4 +-- .../voice/features/cover/api/CoverApiTest.kt | 2 +- features/folderPicker/build.gradle.kts | 1 + .../folderPicker/addcontent/AddContent.kt | 4 +-- .../folderPicker/FolderPickerView.kt | 2 +- .../selectType/FolderModeSelectionCard.kt | 2 +- .../selectType/SelectFolderType.kt | 2 +- features/onboarding/build.gradle.kts | 1 + .../onboarding/OnboardingExplanation.kt | 3 +- .../features/onboarding/OnboardingWelcome.kt | 3 +- .../completion/OnboardingCompletion.kt | 4 +-- .../OnboardingCompletionViewModel.kt | 2 +- features/playbackScreen/build.gradle.kts | 1 + .../playbackScreen/BookPlayController.kt | 2 +- .../playbackScreen/BookPlayViewModel.kt | 6 ++-- .../playbackScreen/BookPlayViewState.kt | 2 +- .../playbackScreen/view/BookPlayView.kt | 2 +- .../features/playbackScreen/view/Cover.kt | 2 +- .../features/playbackScreen/view/CoverRow.kt | 2 +- .../playbackScreen/view/PlaybackRow.kt | 2 +- features/settings/build.gradle.kts | 1 + .../features/settings/SettingsViewModel.kt | 12 ++++---- .../voice/features/settings/views/Settings.kt | 4 +-- .../views/sleeptimer/AutoSleepTimerCard.kt | 2 +- .../views/sleeptimer/AutoSleepTimerRow.kt | 2 +- .../sleepTimer/AutoEnableSleepTimer.kt | 4 +-- .../voice/features/sleepTimer/SleepTimer.kt | 4 +-- gradle/libs.versions.toml | 4 ++- settings.gradle.kts | 2 +- 71 files changed, 189 insertions(+), 154 deletions(-) delete mode 100644 core/common/src/main/kotlin/voice/core/common/pref/PrefQualifiers.kt create mode 100644 core/data/api/src/main/kotlin/voice/core/data/store/StoreQualifiers.kt rename {app/src/main/kotlin/voice/app/injection => core/data/impl/src/main/kotlin/voice/core/data/store}/PrefsDataMigration.kt (88%) rename app/src/main/kotlin/voice/app/injection/PrefsModule.kt => core/data/impl/src/main/kotlin/voice/core/data/store/StoreModule.kt (81%) rename {app/src/test/kotlin/voice/app/injection => core/data/impl/src/test/kotlin/vocie/core/data/store}/DataMigrationTests.kt (88%) rename {app/src/test/kotlin/voice/app/injection => core/data/impl/src/test/kotlin/vocie/core/data/store}/MigrationTests.kt (90%) create mode 100644 core/ui/build.gradle.kts rename core/{common/src/main/kotlin/voice/core/common => ui/src/main/kotlin/voice/core/ui}/DarkThemeSettable.kt (75%) rename core/{common/src/main/kotlin/voice/core/common/compose => ui/src/main/kotlin/voice/core/ui}/ImmutableFile.kt (77%) rename core/{common/src/main/kotlin/voice/core/common/compose => ui/src/main/kotlin/voice/core/ui}/PaddingValues.kt (96%) rename core/{common/src/main/kotlin/voice/core/common/compose => ui/src/main/kotlin/voice/core/ui}/PlayButton.kt (98%) rename core/{common/src/main/kotlin/voice/core/common/compose => ui/src/main/kotlin/voice/core/ui}/SharedGraph.kt (75%) rename core/{common/src/main/kotlin/voice/core/common/compose => ui/src/main/kotlin/voice/core/ui}/ViewModel.kt (92%) rename core/{common/src/main/kotlin/voice/core/common/compose => ui/src/main/kotlin/voice/core/ui}/VoiceTheme.kt (52%) create mode 100644 core/ui/src/main/kotlin/voice/core/ui/isDarkTheme.kt diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 642e41667..6981002f0 100755 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -147,6 +147,7 @@ android { dependencies { implementation(projects.core.strings) + implementation(projects.core.ui) implementation(projects.core.datastore) implementation(projects.core.common) implementation(projects.core.data.api) diff --git a/app/src/androidTest/kotlin/voice/app/SleepTimerIntegrationTest.kt b/app/src/androidTest/kotlin/voice/app/SleepTimerIntegrationTest.kt index 1ffced3e5..d077bc1ec 100644 --- a/app/src/androidTest/kotlin/voice/app/SleepTimerIntegrationTest.kt +++ b/app/src/androidTest/kotlin/voice/app/SleepTimerIntegrationTest.kt @@ -10,9 +10,6 @@ import io.kotest.matchers.longs.shouldBeGreaterThan import kotlinx.coroutines.flow.first import kotlinx.coroutines.test.runTest import org.junit.Test -import voice.core.common.pref.CurrentBookStore -import voice.core.common.pref.FadeOutStore -import voice.core.common.pref.SleepTimerPreferenceStore import voice.core.common.rootGraphAs import voice.core.common.sleepTimer.SleepTimerPreference import voice.core.data.BookContent @@ -21,6 +18,9 @@ import voice.core.data.Chapter import voice.core.data.ChapterId import voice.core.data.repo.BookContentRepo import voice.core.data.repo.ChapterRepo +import voice.core.data.store.CurrentBookStore +import voice.core.data.store.FadeOutStore +import voice.core.data.store.SleepTimerPreferenceStore import voice.core.playback.PlayerController import voice.core.playback.playstate.PlayStateManager import voice.core.playback.session.SleepTimer diff --git a/app/src/main/kotlin/voice/app/StartDestinationProvider.kt b/app/src/main/kotlin/voice/app/StartDestinationProvider.kt index 2f7bd23f3..366809458 100644 --- a/app/src/main/kotlin/voice/app/StartDestinationProvider.kt +++ b/app/src/main/kotlin/voice/app/StartDestinationProvider.kt @@ -6,10 +6,10 @@ import dev.zacsweers.metro.Inject import kotlinx.coroutines.flow.first import kotlinx.coroutines.runBlocking import voice.app.features.MainActivity.Companion.NI_GO_TO_BOOK -import voice.core.common.pref.CurrentBookStore -import voice.core.common.pref.OnboardingCompletedStore import voice.core.data.BookId import voice.core.data.folders.AudiobookFolders +import voice.core.data.store.CurrentBookStore +import voice.core.data.store.OnboardingCompletedStore import voice.core.playback.PlayerController import voice.navigation.Destination diff --git a/app/src/main/kotlin/voice/app/features/MainActivity.kt b/app/src/main/kotlin/voice/app/features/MainActivity.kt index c7fede58d..65e6b55bf 100644 --- a/app/src/main/kotlin/voice/app/features/MainActivity.kt +++ b/app/src/main/kotlin/voice/app/features/MainActivity.kt @@ -19,9 +19,9 @@ import dev.zacsweers.metro.AppScope import dev.zacsweers.metro.ContributesTo import dev.zacsweers.metro.Inject import voice.app.StartDestinationProvider -import voice.core.common.compose.VoiceTheme import voice.core.common.rootGraphAs import voice.core.logging.core.Logger +import voice.core.ui.VoiceTheme import voice.features.review.ReviewFeature import voice.navigation.Destination import voice.navigation.NavigationCommand diff --git a/app/src/main/kotlin/voice/app/features/widget/TriggerWidgetOnChange.kt b/app/src/main/kotlin/voice/app/features/widget/TriggerWidgetOnChange.kt index eff384c07..5931c3d9d 100644 --- a/app/src/main/kotlin/voice/app/features/widget/TriggerWidgetOnChange.kt +++ b/app/src/main/kotlin/voice/app/features/widget/TriggerWidgetOnChange.kt @@ -11,10 +11,10 @@ import kotlinx.coroutines.flow.filterNotNull import kotlinx.coroutines.flow.flatMapLatest import kotlinx.coroutines.flow.merge import kotlinx.coroutines.launch -import voice.core.common.pref.CurrentBookStore import voice.core.data.Book import voice.core.data.BookId import voice.core.data.repo.BookRepository +import voice.core.data.store.CurrentBookStore import voice.core.playback.playstate.PlayStateManager @SingleIn(AppScope::class) diff --git a/app/src/main/kotlin/voice/app/features/widget/WidgetUpdater.kt b/app/src/main/kotlin/voice/app/features/widget/WidgetUpdater.kt index bb88bcb2c..e6ba3e7a1 100644 --- a/app/src/main/kotlin/voice/app/features/widget/WidgetUpdater.kt +++ b/app/src/main/kotlin/voice/app/features/widget/WidgetUpdater.kt @@ -23,10 +23,10 @@ import kotlinx.coroutines.launch import voice.app.R import voice.app.features.MainActivity import voice.core.common.dpToPxRounded -import voice.core.common.pref.CurrentBookStore import voice.core.data.Book import voice.core.data.BookId import voice.core.data.repo.BookRepository +import voice.core.data.store.CurrentBookStore import voice.core.playback.playstate.PlayStateManager import voice.core.playback.receiver.WidgetButtonReceiver import voice.core.common.R as CommonR diff --git a/app/src/main/kotlin/voice/app/injection/App.kt b/app/src/main/kotlin/voice/app/injection/App.kt index d694740d7..21100176d 100644 --- a/app/src/main/kotlin/voice/app/injection/App.kt +++ b/app/src/main/kotlin/voice/app/injection/App.kt @@ -13,10 +13,10 @@ import kotlinx.coroutines.MainScope import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.launch import voice.app.features.widget.TriggerWidgetOnChange -import voice.core.common.DARK_THEME_SETTABLE -import voice.core.common.pref.DarkThemeStore import voice.core.common.rootGraph +import voice.core.data.store.DarkThemeStore import voice.core.scanner.MediaScanTrigger +import voice.core.ui.DARK_THEME_SETTABLE import voice.features.sleepTimer.AutoEnableSleepTimer open class App : Application() { diff --git a/core/common/build.gradle.kts b/core/common/build.gradle.kts index f5769fa5d..0a7877eba 100644 --- a/core/common/build.gradle.kts +++ b/core/common/build.gradle.kts @@ -19,7 +19,6 @@ dependencies { api(libs.datastore) implementation(libs.androidxCore) api(libs.navigation3.runtime) - implementation(libs.lifecycle.viewmodel.compose) implementation(libs.serialization.json) testImplementation(kotlin("reflect")) diff --git a/core/common/src/main/kotlin/voice/core/common/pref/PrefQualifiers.kt b/core/common/src/main/kotlin/voice/core/common/pref/PrefQualifiers.kt deleted file mode 100644 index b388aafcb..000000000 --- a/core/common/src/main/kotlin/voice/core/common/pref/PrefQualifiers.kt +++ /dev/null @@ -1,27 +0,0 @@ -package voice.core.common.pref - -import dev.zacsweers.metro.Qualifier - -@Qualifier -annotation class OnboardingCompletedStore - -@Qualifier -annotation class CurrentBookStore - -@Qualifier -annotation class AutoRewindAmountStore - -@Qualifier -annotation class SeekTimeStore - -@Qualifier -annotation class SleepTimerPreferenceStore - -@Qualifier -annotation class GridModeStore - -@Qualifier -annotation class DarkThemeStore - -@Qualifier -annotation class FadeOutStore diff --git a/core/data/api/build.gradle.kts b/core/data/api/build.gradle.kts index 2f01503c7..749e11e00 100644 --- a/core/data/api/build.gradle.kts +++ b/core/data/api/build.gradle.kts @@ -11,6 +11,7 @@ kotlin { dependencies { api(projects.core.common) api(projects.core.documentfile) + implementation(libs.metro.runtime) implementation(libs.appCompat) implementation(libs.androidxCore) implementation(libs.serialization.json) diff --git a/core/data/api/src/main/kotlin/voice/core/data/store/StoreQualifiers.kt b/core/data/api/src/main/kotlin/voice/core/data/store/StoreQualifiers.kt new file mode 100644 index 000000000..7aecff496 --- /dev/null +++ b/core/data/api/src/main/kotlin/voice/core/data/store/StoreQualifiers.kt @@ -0,0 +1,27 @@ +package voice.core.data.store + +import dev.zacsweers.metro.Qualifier + +@Qualifier +public annotation class OnboardingCompletedStore + +@Qualifier +public annotation class CurrentBookStore + +@Qualifier +public annotation class AutoRewindAmountStore + +@Qualifier +public annotation class SeekTimeStore + +@Qualifier +public annotation class SleepTimerPreferenceStore + +@Qualifier +public annotation class GridModeStore + +@Qualifier +public annotation class DarkThemeStore + +@Qualifier +public annotation class FadeOutStore diff --git a/app/src/main/kotlin/voice/app/injection/PrefsDataMigration.kt b/core/data/impl/src/main/kotlin/voice/core/data/store/PrefsDataMigration.kt similarity index 88% rename from app/src/main/kotlin/voice/app/injection/PrefsDataMigration.kt rename to core/data/impl/src/main/kotlin/voice/core/data/store/PrefsDataMigration.kt index 65db89174..458815459 100644 --- a/app/src/main/kotlin/voice/app/injection/PrefsDataMigration.kt +++ b/core/data/impl/src/main/kotlin/voice/core/data/store/PrefsDataMigration.kt @@ -1,10 +1,10 @@ -package voice.app.injection +package voice.core.data.store import android.content.SharedPreferences import androidx.core.content.edit import androidx.datastore.core.DataMigration -class PrefsDataMigration( +internal class PrefsDataMigration( private val sharedPreferences: SharedPreferences, private val key: String, private val getFromSharedPreferences: () -> T, @@ -24,7 +24,7 @@ class PrefsDataMigration( } } -fun booleanPrefsDataMigration( +internal fun booleanPrefsDataMigration( sharedPreferences: SharedPreferences, key: String, ): DataMigration { @@ -37,7 +37,7 @@ fun booleanPrefsDataMigration( ) } -fun intPrefsDataMigration( +internal fun intPrefsDataMigration( sharedPreferences: SharedPreferences, key: String, ): DataMigration { diff --git a/app/src/main/kotlin/voice/app/injection/PrefsModule.kt b/core/data/impl/src/main/kotlin/voice/core/data/store/StoreModule.kt similarity index 81% rename from app/src/main/kotlin/voice/app/injection/PrefsModule.kt rename to core/data/impl/src/main/kotlin/voice/core/data/store/StoreModule.kt index be1acc59e..f4552dddb 100644 --- a/app/src/main/kotlin/voice/app/injection/PrefsModule.kt +++ b/core/data/impl/src/main/kotlin/voice/core/data/store/StoreModule.kt @@ -1,5 +1,6 @@ -package voice.app.injection +package voice.core.data.store +import android.app.Application import android.content.Context import android.content.SharedPreferences import androidx.datastore.core.DataStore @@ -10,16 +11,8 @@ import dev.zacsweers.metro.Provides import dev.zacsweers.metro.SingleIn import kotlinx.serialization.builtins.nullable import kotlinx.serialization.builtins.serializer -import voice.app.BuildConfig +import voice.core.common.AppInfoProvider import voice.core.common.grid.GridMode -import voice.core.common.pref.AutoRewindAmountStore -import voice.core.common.pref.CurrentBookStore -import voice.core.common.pref.DarkThemeStore -import voice.core.common.pref.FadeOutStore -import voice.core.common.pref.GridModeStore -import voice.core.common.pref.OnboardingCompletedStore -import voice.core.common.pref.SeekTimeStore -import voice.core.common.pref.SleepTimerPreferenceStore import voice.core.common.sleepTimer.SleepTimerPreference import voice.core.data.BookId import voice.core.datastore.VoiceDataStoreFactory @@ -28,12 +21,15 @@ import kotlin.time.Duration.Companion.seconds @BindingContainer @ContributesTo(AppScope::class) -object PrefsModule { +internal object StoreModule { @Provides @SingleIn(AppScope::class) - fun sharedPreferences(context: Context): SharedPreferences { - return context.getSharedPreferences("${BuildConfig.APPLICATION_ID}_preferences", Context.MODE_PRIVATE) + fun sharedPreferences( + context: Application, + appInfoProvider: AppInfoProvider, + ): SharedPreferences { + return context.getSharedPreferences("${appInfoProvider.applicationID}_preferences", Context.MODE_PRIVATE) } @Provides @@ -73,7 +69,7 @@ object PrefsModule { return factory.create( fileName = "fadeOut", defaultValue = 10.seconds, - serializer = Duration.serializer(), + serializer = Duration.Companion.serializer(), ) } @@ -96,9 +92,9 @@ object PrefsModule { @SleepTimerPreferenceStore fun sleepTimerPreference(factory: VoiceDataStoreFactory): DataStore { return factory.create( - serializer = SleepTimerPreference.serializer(), + serializer = SleepTimerPreference.Companion.serializer(), fileName = "sleepTime3", - defaultValue = SleepTimerPreference.Default, + defaultValue = SleepTimerPreference.Companion.Default, ) } diff --git a/app/src/test/kotlin/voice/app/injection/DataMigrationTests.kt b/core/data/impl/src/test/kotlin/vocie/core/data/store/DataMigrationTests.kt similarity index 88% rename from app/src/test/kotlin/voice/app/injection/DataMigrationTests.kt rename to core/data/impl/src/test/kotlin/vocie/core/data/store/DataMigrationTests.kt index be28299a4..940aad76a 100644 --- a/app/src/test/kotlin/voice/app/injection/DataMigrationTests.kt +++ b/core/data/impl/src/test/kotlin/vocie/core/data/store/DataMigrationTests.kt @@ -1,5 +1,6 @@ -package voice.app.injection +package vocie.core.data.store +import android.app.Application import android.content.Context import android.content.SharedPreferences import androidx.core.content.edit @@ -12,6 +13,8 @@ import kotlinx.serialization.json.Json import org.junit.Before import org.junit.Test import org.junit.runner.RunWith +import voice.core.data.store.booleanPrefsDataMigration +import voice.core.data.store.intPrefsDataMigration import voice.core.datastore.VoiceDataStoreFactory @RunWith(AndroidJUnit4::class) @@ -22,7 +25,7 @@ class DataMigrationTests { @Before fun setup() { - val context = ApplicationProvider.getApplicationContext() + val context = ApplicationProvider.getApplicationContext() sharedPreferences = context.getSharedPreferences("de.ph1b.audiobook_preferences", Context.MODE_PRIVATE) factory = VoiceDataStoreFactory(Json { ignoreUnknownKeys = true }, context) } diff --git a/app/src/test/kotlin/voice/app/injection/MigrationTests.kt b/core/data/impl/src/test/kotlin/vocie/core/data/store/MigrationTests.kt similarity index 90% rename from app/src/test/kotlin/voice/app/injection/MigrationTests.kt rename to core/data/impl/src/test/kotlin/vocie/core/data/store/MigrationTests.kt index ff8510ee9..a4fbbd12a 100644 --- a/app/src/test/kotlin/voice/app/injection/MigrationTests.kt +++ b/core/data/impl/src/test/kotlin/vocie/core/data/store/MigrationTests.kt @@ -1,4 +1,4 @@ -package voice.app.injection +package vocie.core.data.store import android.app.Application import android.content.SharedPreferences @@ -18,11 +18,13 @@ import kotlinx.coroutines.test.runTest import kotlinx.serialization.json.Json import org.junit.Test import org.junit.runner.RunWith +import voice.core.common.AppInfoProvider import voice.core.common.grid.GridMode -import voice.core.common.pref.AutoRewindAmountStore -import voice.core.common.pref.DarkThemeStore -import voice.core.common.pref.GridModeStore -import voice.core.common.pref.SeekTimeStore +import voice.core.data.store.AutoRewindAmountStore +import voice.core.data.store.DarkThemeStore +import voice.core.data.store.GridModeStore +import voice.core.data.store.SeekTimeStore +import voice.core.data.store.intPrefsDataMigration import voice.core.datastore.VoiceDataStoreFactory @SingleIn(AppScope::class) @@ -46,6 +48,18 @@ interface MigrationTestGraph { @get:Provides val application: Application get() = ApplicationProvider.getApplicationContext() + @get:Provides + val json: Json get() = Json.Default + + @get:Provides + val appInfoProvider: AppInfoProvider + get() = object : AppInfoProvider { + override val applicationID: String + get() = "de.ph1b.audiobook" + override val versionName: String + get() = "1.2.3" + } + val sharedPreferences: SharedPreferences } diff --git a/core/data/impl/src/test/kotlin/voice/core/data/repo/internals/TestGraph.kt b/core/data/impl/src/test/kotlin/voice/core/data/repo/internals/TestGraph.kt index ba06b93a0..3b14ec947 100644 --- a/core/data/impl/src/test/kotlin/voice/core/data/repo/internals/TestGraph.kt +++ b/core/data/impl/src/test/kotlin/voice/core/data/repo/internals/TestGraph.kt @@ -9,7 +9,7 @@ import dev.zacsweers.metro.DependencyGraph import dev.zacsweers.metro.Provides import dev.zacsweers.metro.SingleIn import dev.zacsweers.metro.createGraphFactory -import voice.core.common.pref.DarkThemeStore +import voice.core.data.store.DarkThemeStore @SingleIn(AppScope::class) @DependencyGraph( diff --git a/core/datastore/src/main/kotlin/voice/core/datastore/VoiceDataStoreFactory.kt b/core/datastore/src/main/kotlin/voice/core/datastore/VoiceDataStoreFactory.kt index d98d384a3..2eb5c7d3d 100644 --- a/core/datastore/src/main/kotlin/voice/core/datastore/VoiceDataStoreFactory.kt +++ b/core/datastore/src/main/kotlin/voice/core/datastore/VoiceDataStoreFactory.kt @@ -1,6 +1,6 @@ package voice.core.datastore -import android.content.Context +import android.app.Application import androidx.datastore.core.DataMigration import androidx.datastore.core.DataStore import androidx.datastore.core.DataStoreFactory @@ -13,7 +13,7 @@ import kotlinx.serialization.json.Json @Inject class VoiceDataStoreFactory( private val json: Json, - private val context: Context, + private val context: Application, ) { fun create( diff --git a/core/playback/src/main/kotlin/voice/core/playback/PlayerController.kt b/core/playback/src/main/kotlin/voice/core/playback/PlayerController.kt index 9794396d9..cd98f7ff4 100644 --- a/core/playback/src/main/kotlin/voice/core/playback/PlayerController.kt +++ b/core/playback/src/main/kotlin/voice/core/playback/PlayerController.kt @@ -15,10 +15,10 @@ import kotlinx.coroutines.ensureActive import kotlinx.coroutines.flow.first import kotlinx.coroutines.guava.asDeferred import kotlinx.coroutines.launch -import voice.core.common.pref.CurrentBookStore import voice.core.data.BookId import voice.core.data.ChapterId import voice.core.data.repo.BookRepository +import voice.core.data.store.CurrentBookStore import voice.core.logging.core.Logger import voice.core.playback.misc.Decibel import voice.core.playback.session.CustomCommand diff --git a/core/playback/src/main/kotlin/voice/core/playback/player/VoicePlayer.kt b/core/playback/src/main/kotlin/voice/core/playback/player/VoicePlayer.kt index ef0d40201..bd7f6b77b 100644 --- a/core/playback/src/main/kotlin/voice/core/playback/player/VoicePlayer.kt +++ b/core/playback/src/main/kotlin/voice/core/playback/player/VoicePlayer.kt @@ -11,14 +11,14 @@ import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.first import kotlinx.coroutines.launch import kotlinx.coroutines.runBlocking -import voice.core.common.pref.AutoRewindAmountStore -import voice.core.common.pref.CurrentBookStore -import voice.core.common.pref.SeekTimeStore import voice.core.data.BookContent import voice.core.data.BookId import voice.core.data.Chapter import voice.core.data.repo.BookRepository import voice.core.data.repo.ChapterRepo +import voice.core.data.store.AutoRewindAmountStore +import voice.core.data.store.CurrentBookStore +import voice.core.data.store.SeekTimeStore import voice.core.logging.core.Logger import voice.core.playback.misc.Decibel import voice.core.playback.misc.VolumeGain diff --git a/core/playback/src/main/kotlin/voice/core/playback/session/LibrarySessionCallback.kt b/core/playback/src/main/kotlin/voice/core/playback/session/LibrarySessionCallback.kt index bbea2cc4e..78bedd6e8 100644 --- a/core/playback/src/main/kotlin/voice/core/playback/session/LibrarySessionCallback.kt +++ b/core/playback/src/main/kotlin/voice/core/playback/session/LibrarySessionCallback.kt @@ -24,10 +24,10 @@ import kotlinx.coroutines.flow.first import kotlinx.coroutines.guava.await import kotlinx.coroutines.guava.future import kotlinx.coroutines.launch -import voice.core.common.pref.CurrentBookStore import voice.core.data.Book import voice.core.data.BookId import voice.core.data.repo.BookRepository +import voice.core.data.store.CurrentBookStore import voice.core.logging.core.Logger import voice.core.playback.player.VoicePlayer import voice.core.playback.session.search.BookSearchHandler diff --git a/core/playback/src/main/kotlin/voice/core/playback/session/MediaItemProvider.kt b/core/playback/src/main/kotlin/voice/core/playback/session/MediaItemProvider.kt index 99a6e5605..e0b334420 100644 --- a/core/playback/src/main/kotlin/voice/core/playback/session/MediaItemProvider.kt +++ b/core/playback/src/main/kotlin/voice/core/playback/session/MediaItemProvider.kt @@ -9,7 +9,6 @@ import androidx.media3.session.MediaSession.MediaItemsWithStartPosition import dev.zacsweers.metro.Inject import kotlinx.coroutines.flow.first import kotlinx.coroutines.runBlocking -import voice.core.common.pref.CurrentBookStore import voice.core.data.Book import voice.core.data.BookComparator import voice.core.data.BookContent @@ -18,6 +17,7 @@ import voice.core.data.Chapter import voice.core.data.repo.BookContentRepo import voice.core.data.repo.BookRepository import voice.core.data.repo.ChapterRepo +import voice.core.data.store.CurrentBookStore import voice.core.data.toUri import java.io.File import voice.core.strings.R as StringsR diff --git a/core/playback/src/main/kotlin/voice/core/playback/session/search/BookSearchHandler.kt b/core/playback/src/main/kotlin/voice/core/playback/session/search/BookSearchHandler.kt index e4a9e1213..fe29a438f 100644 --- a/core/playback/src/main/kotlin/voice/core/playback/session/search/BookSearchHandler.kt +++ b/core/playback/src/main/kotlin/voice/core/playback/session/search/BookSearchHandler.kt @@ -4,10 +4,10 @@ import android.provider.MediaStore import androidx.datastore.core.DataStore import dev.zacsweers.metro.Inject import kotlinx.coroutines.flow.first -import voice.core.common.pref.CurrentBookStore import voice.core.data.Book import voice.core.data.BookId import voice.core.data.repo.BookRepository +import voice.core.data.store.CurrentBookStore import voice.core.logging.core.Logger @Inject diff --git a/core/ui/build.gradle.kts b/core/ui/build.gradle.kts new file mode 100644 index 000000000..bfafed43e --- /dev/null +++ b/core/ui/build.gradle.kts @@ -0,0 +1,11 @@ +plugins { + id("voice.library") + id("voice.compose") + alias(libs.plugins.metro) +} + +dependencies { + implementation(projects.core.data.api) + implementation(projects.core.strings) + implementation(libs.lifecycle.viewmodel.compose) +} diff --git a/core/common/src/main/kotlin/voice/core/common/DarkThemeSettable.kt b/core/ui/src/main/kotlin/voice/core/ui/DarkThemeSettable.kt similarity index 75% rename from core/common/src/main/kotlin/voice/core/common/DarkThemeSettable.kt rename to core/ui/src/main/kotlin/voice/core/ui/DarkThemeSettable.kt index 43c243ce9..fe80f0e27 100644 --- a/core/common/src/main/kotlin/voice/core/common/DarkThemeSettable.kt +++ b/core/ui/src/main/kotlin/voice/core/ui/DarkThemeSettable.kt @@ -1,4 +1,4 @@ -package voice.core.common +package voice.core.ui import android.os.Build diff --git a/core/common/src/main/kotlin/voice/core/common/compose/ImmutableFile.kt b/core/ui/src/main/kotlin/voice/core/ui/ImmutableFile.kt similarity index 77% rename from core/common/src/main/kotlin/voice/core/common/compose/ImmutableFile.kt rename to core/ui/src/main/kotlin/voice/core/ui/ImmutableFile.kt index 210dafa46..ef5ef1fac 100644 --- a/core/common/src/main/kotlin/voice/core/common/compose/ImmutableFile.kt +++ b/core/ui/src/main/kotlin/voice/core/ui/ImmutableFile.kt @@ -1,4 +1,4 @@ -package voice.core.common.compose +package voice.core.ui import androidx.compose.runtime.Immutable import java.io.File diff --git a/core/common/src/main/kotlin/voice/core/common/compose/PaddingValues.kt b/core/ui/src/main/kotlin/voice/core/ui/PaddingValues.kt similarity index 96% rename from core/common/src/main/kotlin/voice/core/common/compose/PaddingValues.kt rename to core/ui/src/main/kotlin/voice/core/ui/PaddingValues.kt index ef2d0c48b..7c2cd91cf 100644 --- a/core/common/src/main/kotlin/voice/core/common/compose/PaddingValues.kt +++ b/core/ui/src/main/kotlin/voice/core/ui/PaddingValues.kt @@ -1,6 +1,6 @@ @file:Suppress("ktlint:standard:filename") -package voice.core.common.compose +package voice.core.ui import androidx.compose.foundation.layout.PaddingValues import androidx.compose.ui.unit.Dp diff --git a/core/common/src/main/kotlin/voice/core/common/compose/PlayButton.kt b/core/ui/src/main/kotlin/voice/core/ui/PlayButton.kt similarity index 98% rename from core/common/src/main/kotlin/voice/core/common/compose/PlayButton.kt rename to core/ui/src/main/kotlin/voice/core/ui/PlayButton.kt index 302a3cdc8..ee1ced320 100644 --- a/core/common/src/main/kotlin/voice/core/common/compose/PlayButton.kt +++ b/core/ui/src/main/kotlin/voice/core/ui/PlayButton.kt @@ -1,4 +1,4 @@ -package voice.core.common.compose +package voice.core.ui import androidx.compose.animation.core.animateDpAsState import androidx.compose.animation.graphics.res.animatedVectorResource diff --git a/core/common/src/main/kotlin/voice/core/common/compose/SharedGraph.kt b/core/ui/src/main/kotlin/voice/core/ui/SharedGraph.kt similarity index 75% rename from core/common/src/main/kotlin/voice/core/common/compose/SharedGraph.kt rename to core/ui/src/main/kotlin/voice/core/ui/SharedGraph.kt index 20864405a..f6e850e16 100644 --- a/core/common/src/main/kotlin/voice/core/common/compose/SharedGraph.kt +++ b/core/ui/src/main/kotlin/voice/core/ui/SharedGraph.kt @@ -1,9 +1,9 @@ -package voice.core.common.compose +package voice.core.ui import androidx.datastore.core.DataStore import dev.zacsweers.metro.AppScope import dev.zacsweers.metro.ContributesTo -import voice.core.common.pref.DarkThemeStore +import voice.core.data.store.DarkThemeStore @ContributesTo(AppScope::class) interface SharedGraph { diff --git a/core/common/src/main/kotlin/voice/core/common/compose/ViewModel.kt b/core/ui/src/main/kotlin/voice/core/ui/ViewModel.kt similarity index 92% rename from core/common/src/main/kotlin/voice/core/common/compose/ViewModel.kt rename to core/ui/src/main/kotlin/voice/core/ui/ViewModel.kt index 51c6b628a..e50f474f9 100644 --- a/core/common/src/main/kotlin/voice/core/common/compose/ViewModel.kt +++ b/core/ui/src/main/kotlin/voice/core/ui/ViewModel.kt @@ -1,4 +1,4 @@ -package voice.core.common.compose +package voice.core.ui import androidx.compose.runtime.Composable import androidx.lifecycle.ViewModel diff --git a/core/common/src/main/kotlin/voice/core/common/compose/VoiceTheme.kt b/core/ui/src/main/kotlin/voice/core/ui/VoiceTheme.kt similarity index 52% rename from core/common/src/main/kotlin/voice/core/common/compose/VoiceTheme.kt rename to core/ui/src/main/kotlin/voice/core/ui/VoiceTheme.kt index c92402f9f..2ffe38d53 100644 --- a/core/common/src/main/kotlin/voice/core/common/compose/VoiceTheme.kt +++ b/core/ui/src/main/kotlin/voice/core/ui/VoiceTheme.kt @@ -1,23 +1,17 @@ -package voice.core.common.compose +package voice.core.ui import android.os.Build -import androidx.compose.foundation.isSystemInDarkTheme +import androidx.compose.material3.MaterialTheme import androidx.compose.material3.darkColorScheme import androidx.compose.material3.dynamicDarkColorScheme import androidx.compose.material3.dynamicLightColorScheme import androidx.compose.material3.lightColorScheme import androidx.compose.runtime.Composable -import androidx.compose.runtime.collectAsState -import androidx.compose.runtime.remember import androidx.compose.ui.platform.LocalContext -import kotlinx.coroutines.Dispatchers -import voice.core.common.DARK_THEME_SETTABLE -import voice.core.common.rootGraphAs -import androidx.compose.material3.MaterialTheme as Material3Theme @Composable fun VoiceTheme(content: @Composable () -> Unit) { - Material3Theme( + MaterialTheme( colorScheme = if (isDarkTheme()) { if (Build.VERSION.SDK_INT >= 31) { dynamicDarkColorScheme(LocalContext.current) @@ -35,15 +29,3 @@ fun VoiceTheme(content: @Composable () -> Unit) { content() } } - -@Composable -fun isDarkTheme(): Boolean { - return if (DARK_THEME_SETTABLE) { - val darkThemeFlow = remember { - rootGraphAs().useDarkThemeStore.data - } - darkThemeFlow.collectAsState(initial = false, context = Dispatchers.Unconfined).value - } else { - isSystemInDarkTheme() - } -} diff --git a/core/ui/src/main/kotlin/voice/core/ui/isDarkTheme.kt b/core/ui/src/main/kotlin/voice/core/ui/isDarkTheme.kt new file mode 100644 index 000000000..7ea4ae220 --- /dev/null +++ b/core/ui/src/main/kotlin/voice/core/ui/isDarkTheme.kt @@ -0,0 +1,20 @@ +package voice.core.ui + +import androidx.compose.foundation.isSystemInDarkTheme +import androidx.compose.runtime.Composable +import androidx.compose.runtime.collectAsState +import androidx.compose.runtime.remember +import kotlinx.coroutines.Dispatchers +import voice.core.common.rootGraphAs + +@Composable +fun isDarkTheme(): Boolean { + return if (DARK_THEME_SETTABLE) { + val darkThemeFlow = remember { + rootGraphAs().useDarkThemeStore.data + } + darkThemeFlow.collectAsState(initial = false, context = Dispatchers.Unconfined).value + } else { + isSystemInDarkTheme() + } +} diff --git a/features/bookOverview/build.gradle.kts b/features/bookOverview/build.gradle.kts index e03af4a09..3a6dae951 100644 --- a/features/bookOverview/build.gradle.kts +++ b/features/bookOverview/build.gradle.kts @@ -8,6 +8,7 @@ dependencies { implementation(projects.navigation) implementation(projects.core.common) implementation(projects.core.search) + implementation(projects.core.ui) implementation(projects.core.strings) implementation(projects.core.playback) implementation(projects.core.data.api) diff --git a/features/bookOverview/src/main/kotlin/voice/features/bookOverview/overview/BookOverviewItemViewState.kt b/features/bookOverview/src/main/kotlin/voice/features/bookOverview/overview/BookOverviewItemViewState.kt index d2face69f..c97f26a15 100644 --- a/features/bookOverview/src/main/kotlin/voice/features/bookOverview/overview/BookOverviewItemViewState.kt +++ b/features/bookOverview/src/main/kotlin/voice/features/bookOverview/overview/BookOverviewItemViewState.kt @@ -2,10 +2,10 @@ package voice.features.bookOverview.overview import android.text.format.DateUtils import androidx.compose.runtime.Immutable -import voice.core.common.compose.ImmutableFile import voice.core.data.Book import voice.core.data.BookId import voice.core.logging.core.Logger +import voice.core.ui.ImmutableFile @Immutable data class BookOverviewItemViewState( diff --git a/features/bookOverview/src/main/kotlin/voice/features/bookOverview/overview/BookOverviewViewModel.kt b/features/bookOverview/src/main/kotlin/voice/features/bookOverview/overview/BookOverviewViewModel.kt index 58a5e7387..3838a43cc 100644 --- a/features/bookOverview/src/main/kotlin/voice/features/bookOverview/overview/BookOverviewViewModel.kt +++ b/features/bookOverview/src/main/kotlin/voice/features/bookOverview/overview/BookOverviewViewModel.kt @@ -20,12 +20,12 @@ import kotlinx.coroutines.launch import voice.core.common.comparator.sortedNaturally import voice.core.common.grid.GridCount import voice.core.common.grid.GridMode -import voice.core.common.pref.CurrentBookStore -import voice.core.common.pref.GridModeStore import voice.core.data.BookId import voice.core.data.repo.BookContentRepo import voice.core.data.repo.BookRepository import voice.core.data.repo.internals.dao.RecentBookSearchDao +import voice.core.data.store.CurrentBookStore +import voice.core.data.store.GridModeStore import voice.core.playback.PlayerController import voice.core.playback.playstate.PlayStateManager import voice.core.scanner.DeviceHasStoragePermissionBug diff --git a/features/bookOverview/src/main/kotlin/voice/features/bookOverview/search/BookSearchScreen.kt b/features/bookOverview/src/main/kotlin/voice/features/bookOverview/search/BookSearchScreen.kt index 923458cff..065f4fecc 100644 --- a/features/bookOverview/src/main/kotlin/voice/features/bookOverview/search/BookSearchScreen.kt +++ b/features/bookOverview/src/main/kotlin/voice/features/bookOverview/search/BookSearchScreen.kt @@ -23,8 +23,8 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp -import voice.core.common.compose.plus import voice.core.data.BookId +import voice.core.ui.plus import voice.features.bookOverview.overview.BookOverviewLayoutMode import voice.features.bookOverview.views.GridBook import voice.features.bookOverview.views.ListBookRow diff --git a/features/bookOverview/src/main/kotlin/voice/features/bookOverview/views/BookOverview.kt b/features/bookOverview/src/main/kotlin/voice/features/bookOverview/views/BookOverview.kt index f21f5e60a..b4d4ae423 100644 --- a/features/bookOverview/src/main/kotlin/voice/features/bookOverview/views/BookOverview.kt +++ b/features/bookOverview/src/main/kotlin/voice/features/bookOverview/views/BookOverview.kt @@ -34,11 +34,11 @@ import dev.zacsweers.metro.IntoSet import dev.zacsweers.metro.Provides import kotlinx.collections.immutable.persistentMapOf import kotlinx.coroutines.launch -import voice.core.common.compose.PlayButton -import voice.core.common.compose.VoiceTheme -import voice.core.common.compose.rememberScoped import voice.core.common.rootGraphAs import voice.core.data.BookId +import voice.core.ui.PlayButton +import voice.core.ui.VoiceTheme +import voice.core.ui.rememberScoped import voice.features.bookOverview.bottomSheet.BottomSheetContent import voice.features.bookOverview.bottomSheet.BottomSheetItem import voice.features.bookOverview.deleteBook.DeleteBookDialog diff --git a/features/bookOverview/src/main/kotlin/voice/features/bookOverview/views/ListBooks.kt b/features/bookOverview/src/main/kotlin/voice/features/bookOverview/views/ListBooks.kt index ec9ea20f6..188e9da57 100644 --- a/features/bookOverview/src/main/kotlin/voice/features/bookOverview/views/ListBooks.kt +++ b/features/bookOverview/src/main/kotlin/voice/features/bookOverview/views/ListBooks.kt @@ -30,8 +30,8 @@ import androidx.compose.ui.text.toUpperCase import androidx.compose.ui.unit.dp import coil.compose.AsyncImage import kotlinx.collections.immutable.ImmutableMap -import voice.core.common.compose.ImmutableFile import voice.core.data.BookId +import voice.core.ui.ImmutableFile import voice.features.bookOverview.overview.BookOverviewCategory import voice.features.bookOverview.overview.BookOverviewItemViewState import voice.core.common.R as CommonR diff --git a/features/bookOverview/src/main/kotlin/voice/features/bookOverview/views/topbar/BookOverviewTopBar.kt b/features/bookOverview/src/main/kotlin/voice/features/bookOverview/views/topbar/BookOverviewTopBar.kt index 2f54016c8..609ffbe19 100644 --- a/features/bookOverview/src/main/kotlin/voice/features/bookOverview/views/topbar/BookOverviewTopBar.kt +++ b/features/bookOverview/src/main/kotlin/voice/features/bookOverview/views/topbar/BookOverviewTopBar.kt @@ -16,8 +16,8 @@ import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import kotlinx.collections.immutable.persistentMapOf import kotlinx.coroutines.delay -import voice.core.common.compose.VoiceTheme import voice.core.data.BookId +import voice.core.ui.VoiceTheme import voice.features.bookOverview.overview.BookOverviewLayoutMode import voice.features.bookOverview.overview.BookOverviewViewState import voice.features.bookOverview.search.BookSearchViewState diff --git a/features/bookmark/build.gradle.kts b/features/bookmark/build.gradle.kts index 5df419c4d..7bda0ab8e 100644 --- a/features/bookmark/build.gradle.kts +++ b/features/bookmark/build.gradle.kts @@ -7,9 +7,11 @@ plugins { dependencies { implementation(projects.core.common) implementation(projects.core.strings) + implementation(projects.core.ui) implementation(projects.core.playback) implementation(projects.navigation) implementation(projects.core.data.api) + implementation(libs.materialDialog.core) implementation(libs.materialDialog.input) } diff --git a/features/bookmark/src/main/kotlin/voice/features/bookmark/BookmarkScreen.kt b/features/bookmark/src/main/kotlin/voice/features/bookmark/BookmarkScreen.kt index 6db213faf..2e6a1461f 100644 --- a/features/bookmark/src/main/kotlin/voice/features/bookmark/BookmarkScreen.kt +++ b/features/bookmark/src/main/kotlin/voice/features/bookmark/BookmarkScreen.kt @@ -48,10 +48,10 @@ import dev.zacsweers.metro.AppScope import dev.zacsweers.metro.ContributesTo import dev.zacsweers.metro.IntoSet import dev.zacsweers.metro.Provides -import voice.core.common.compose.rememberScoped import voice.core.common.rootGraphAs import voice.core.data.BookId import voice.core.data.Bookmark +import voice.core.ui.rememberScoped import voice.features.bookmark.dialogs.AddBookmarkDialog import voice.features.bookmark.dialogs.EditBookmarkDialog import voice.navigation.Destination diff --git a/features/bookmark/src/main/kotlin/voice/features/bookmark/BookmarkViewModel.kt b/features/bookmark/src/main/kotlin/voice/features/bookmark/BookmarkViewModel.kt index 1bce2157f..bb4082fb7 100644 --- a/features/bookmark/src/main/kotlin/voice/features/bookmark/BookmarkViewModel.kt +++ b/features/bookmark/src/main/kotlin/voice/features/bookmark/BookmarkViewModel.kt @@ -14,13 +14,13 @@ import dev.zacsweers.metro.Inject import kotlinx.coroutines.MainScope import kotlinx.coroutines.launch import voice.core.common.formatTime -import voice.core.common.pref.CurrentBookStore import voice.core.data.BookId import voice.core.data.Bookmark import voice.core.data.Chapter import voice.core.data.markForPosition import voice.core.data.repo.BookRepository import voice.core.data.repo.BookmarkRepo +import voice.core.data.store.CurrentBookStore import voice.core.playback.PlayerController import voice.core.playback.playstate.PlayStateManager import voice.core.strings.R diff --git a/features/cover/build.gradle.kts b/features/cover/build.gradle.kts index 068bcc3d0..b82bd411c 100644 --- a/features/cover/build.gradle.kts +++ b/features/cover/build.gradle.kts @@ -9,6 +9,7 @@ dependencies { implementation(projects.core.data.api) implementation(projects.navigation) implementation(projects.core.strings) + implementation(projects.core.ui) implementation(projects.core.remoteconfig.core) implementation(libs.bundles.retrofit) diff --git a/features/cover/src/main/kotlin/voice/features/cover/SelectCoverFromInternet.kt b/features/cover/src/main/kotlin/voice/features/cover/SelectCoverFromInternet.kt index 46d31f2f2..38d425569 100644 --- a/features/cover/src/main/kotlin/voice/features/cover/SelectCoverFromInternet.kt +++ b/features/cover/src/main/kotlin/voice/features/cover/SelectCoverFromInternet.kt @@ -13,10 +13,10 @@ import dev.zacsweers.metro.IntoSet import dev.zacsweers.metro.Provides import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.MutableStateFlow -import voice.core.common.compose.VoiceTheme -import voice.core.common.compose.rememberScoped import voice.core.common.rootGraphAs import voice.core.data.BookId +import voice.core.ui.VoiceTheme +import voice.core.ui.rememberScoped import voice.features.cover.api.SearchResponse import voice.navigation.Destination import voice.navigation.NavEntryProvider diff --git a/features/cover/src/test/kotlin/voice/features/cover/api/CoverApiTest.kt b/features/cover/src/test/kotlin/voice/features/cover/api/CoverApiTest.kt index f289c6848..62253cd03 100644 --- a/features/cover/src/test/kotlin/voice/features/cover/api/CoverApiTest.kt +++ b/features/cover/src/test/kotlin/voice/features/cover/api/CoverApiTest.kt @@ -11,7 +11,7 @@ import io.kotest.matchers.collections.shouldNotBeEmpty import io.kotest.matchers.string.shouldNotBeEmpty import kotlinx.coroutines.test.runTest import org.junit.Test -import voice.core.common.compose.SharedGraph +import voice.core.ui.SharedGraph @SingleIn(AppScope::class) @DependencyGraph( diff --git a/features/folderPicker/build.gradle.kts b/features/folderPicker/build.gradle.kts index 52a1633b5..2f5e2d529 100644 --- a/features/folderPicker/build.gradle.kts +++ b/features/folderPicker/build.gradle.kts @@ -11,6 +11,7 @@ android { } dependencies { + implementation(projects.core.ui) implementation(projects.core.common) implementation(projects.core.strings) implementation(projects.core.playback) diff --git a/features/folderPicker/src/main/kotlin/voice/features/folderPicker/addcontent/AddContent.kt b/features/folderPicker/src/main/kotlin/voice/features/folderPicker/addcontent/AddContent.kt index 7ba98de0e..415e4e885 100644 --- a/features/folderPicker/src/main/kotlin/voice/features/folderPicker/addcontent/AddContent.kt +++ b/features/folderPicker/src/main/kotlin/voice/features/folderPicker/addcontent/AddContent.kt @@ -7,9 +7,9 @@ import dev.zacsweers.metro.AppScope import dev.zacsweers.metro.ContributesTo import dev.zacsweers.metro.IntoSet import dev.zacsweers.metro.Provides -import voice.core.common.compose.VoiceTheme -import voice.core.common.compose.rememberScoped import voice.core.common.rootGraphAs +import voice.core.ui.VoiceTheme +import voice.core.ui.rememberScoped import voice.navigation.Destination import voice.navigation.NavEntryProvider diff --git a/features/folderPicker/src/main/kotlin/voice/features/folderPicker/folderPicker/FolderPickerView.kt b/features/folderPicker/src/main/kotlin/voice/features/folderPicker/folderPicker/FolderPickerView.kt index 4981f1af2..e39b00140 100644 --- a/features/folderPicker/src/main/kotlin/voice/features/folderPicker/folderPicker/FolderPickerView.kt +++ b/features/folderPicker/src/main/kotlin/voice/features/folderPicker/folderPicker/FolderPickerView.kt @@ -28,9 +28,9 @@ import dev.zacsweers.metro.AppScope import dev.zacsweers.metro.ContributesTo import dev.zacsweers.metro.IntoSet import dev.zacsweers.metro.Provides -import voice.core.common.compose.rememberScoped import voice.core.common.rootGraphAs import voice.core.data.folders.FolderType +import voice.core.ui.rememberScoped import voice.features.folderPicker.FolderTypeIcon import voice.navigation.Destination import voice.navigation.NavEntryProvider diff --git a/features/folderPicker/src/main/kotlin/voice/features/folderPicker/selectType/FolderModeSelectionCard.kt b/features/folderPicker/src/main/kotlin/voice/features/folderPicker/selectType/FolderModeSelectionCard.kt index 473310ce4..14a618241 100644 --- a/features/folderPicker/src/main/kotlin/voice/features/folderPicker/selectType/FolderModeSelectionCard.kt +++ b/features/folderPicker/src/main/kotlin/voice/features/folderPicker/selectType/FolderModeSelectionCard.kt @@ -18,8 +18,8 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp -import voice.core.common.compose.VoiceTheme import voice.core.data.folders.FolderType +import voice.core.ui.VoiceTheme import voice.features.folderPicker.FolderTypeIcon import voice.core.strings.R as StringsR diff --git a/features/folderPicker/src/main/kotlin/voice/features/folderPicker/selectType/SelectFolderType.kt b/features/folderPicker/src/main/kotlin/voice/features/folderPicker/selectType/SelectFolderType.kt index 460c1d7e2..1585b3bff 100644 --- a/features/folderPicker/src/main/kotlin/voice/features/folderPicker/selectType/SelectFolderType.kt +++ b/features/folderPicker/src/main/kotlin/voice/features/folderPicker/selectType/SelectFolderType.kt @@ -37,8 +37,8 @@ import dev.zacsweers.metro.AppScope import dev.zacsweers.metro.ContributesTo import dev.zacsweers.metro.IntoSet import dev.zacsweers.metro.Provides -import voice.core.common.compose.rememberScoped import voice.core.common.rootGraphAs +import voice.core.ui.rememberScoped import voice.navigation.Destination import voice.navigation.NavEntryProvider import voice.core.strings.R as StringsR diff --git a/features/onboarding/build.gradle.kts b/features/onboarding/build.gradle.kts index ce67122e3..d06ef6444 100644 --- a/features/onboarding/build.gradle.kts +++ b/features/onboarding/build.gradle.kts @@ -14,6 +14,7 @@ dependencies { implementation(projects.navigation) implementation(projects.core.common) implementation(projects.core.strings) + implementation(projects.core.ui) implementation(libs.datastore) implementation(libs.coil) diff --git a/features/onboarding/src/main/kotlin/voice/features/onboarding/OnboardingExplanation.kt b/features/onboarding/src/main/kotlin/voice/features/onboarding/OnboardingExplanation.kt index b5b28ea16..44ed0d776 100644 --- a/features/onboarding/src/main/kotlin/voice/features/onboarding/OnboardingExplanation.kt +++ b/features/onboarding/src/main/kotlin/voice/features/onboarding/OnboardingExplanation.kt @@ -29,8 +29,7 @@ import dev.zacsweers.metro.AppScope import dev.zacsweers.metro.ContributesTo import dev.zacsweers.metro.IntoSet import dev.zacsweers.metro.Provides -import voice.core.common.compose.VoiceTheme -import voice.features.onboarding.R +import voice.core.ui.VoiceTheme import voice.navigation.Destination import voice.navigation.NavEntryProvider import voice.core.strings.R as StringsR diff --git a/features/onboarding/src/main/kotlin/voice/features/onboarding/OnboardingWelcome.kt b/features/onboarding/src/main/kotlin/voice/features/onboarding/OnboardingWelcome.kt index d7b9a2bbe..faf77e11e 100644 --- a/features/onboarding/src/main/kotlin/voice/features/onboarding/OnboardingWelcome.kt +++ b/features/onboarding/src/main/kotlin/voice/features/onboarding/OnboardingWelcome.kt @@ -29,8 +29,7 @@ import dev.zacsweers.metro.AppScope import dev.zacsweers.metro.ContributesTo import dev.zacsweers.metro.IntoSet import dev.zacsweers.metro.Provides -import voice.core.common.compose.VoiceTheme -import voice.features.onboarding.R +import voice.core.ui.VoiceTheme import voice.navigation.Destination import voice.navigation.NavEntryProvider import voice.core.strings.R as StringsR diff --git a/features/onboarding/src/main/kotlin/voice/features/onboarding/completion/OnboardingCompletion.kt b/features/onboarding/src/main/kotlin/voice/features/onboarding/completion/OnboardingCompletion.kt index 1bad9455f..bc573a39a 100644 --- a/features/onboarding/src/main/kotlin/voice/features/onboarding/completion/OnboardingCompletion.kt +++ b/features/onboarding/src/main/kotlin/voice/features/onboarding/completion/OnboardingCompletion.kt @@ -29,9 +29,9 @@ import dev.zacsweers.metro.AppScope import dev.zacsweers.metro.ContributesTo import dev.zacsweers.metro.IntoSet import dev.zacsweers.metro.Provides -import voice.core.common.compose.VoiceTheme -import voice.core.common.compose.rememberScoped import voice.core.common.rootGraphAs +import voice.core.ui.VoiceTheme +import voice.core.ui.rememberScoped import voice.features.onboarding.R import voice.navigation.Destination import voice.navigation.NavEntryProvider diff --git a/features/onboarding/src/main/kotlin/voice/features/onboarding/completion/OnboardingCompletionViewModel.kt b/features/onboarding/src/main/kotlin/voice/features/onboarding/completion/OnboardingCompletionViewModel.kt index c198fa62f..b57c3619f 100644 --- a/features/onboarding/src/main/kotlin/voice/features/onboarding/completion/OnboardingCompletionViewModel.kt +++ b/features/onboarding/src/main/kotlin/voice/features/onboarding/completion/OnboardingCompletionViewModel.kt @@ -4,7 +4,7 @@ import androidx.datastore.core.DataStore import dev.zacsweers.metro.Inject import kotlinx.coroutines.MainScope import kotlinx.coroutines.launch -import voice.core.common.pref.OnboardingCompletedStore +import voice.core.data.store.OnboardingCompletedStore import voice.navigation.Destination import voice.navigation.Navigator diff --git a/features/playbackScreen/build.gradle.kts b/features/playbackScreen/build.gradle.kts index 6cee13003..f6bb6dad2 100644 --- a/features/playbackScreen/build.gradle.kts +++ b/features/playbackScreen/build.gradle.kts @@ -10,6 +10,7 @@ dependencies { implementation(projects.core.strings) implementation(projects.core.playback) implementation(projects.core.data.api) + implementation(projects.core.ui) implementation(projects.core.datastore) implementation(projects.features.sleepTimer) diff --git a/features/playbackScreen/src/main/kotlin/voice/features/playbackScreen/BookPlayController.kt b/features/playbackScreen/src/main/kotlin/voice/features/playbackScreen/BookPlayController.kt index cf98a4824..b11aadf5e 100644 --- a/features/playbackScreen/src/main/kotlin/voice/features/playbackScreen/BookPlayController.kt +++ b/features/playbackScreen/src/main/kotlin/voice/features/playbackScreen/BookPlayController.kt @@ -14,9 +14,9 @@ import dev.zacsweers.metro.AppScope import dev.zacsweers.metro.ContributesTo import dev.zacsweers.metro.IntoSet import dev.zacsweers.metro.Provides -import voice.core.common.compose.rememberScoped import voice.core.common.rootGraphAs import voice.core.data.BookId +import voice.core.ui.rememberScoped import voice.features.playbackScreen.view.BookPlayView import voice.features.sleepTimer.SleepTimerDialog import voice.navigation.Destination diff --git a/features/playbackScreen/src/main/kotlin/voice/features/playbackScreen/BookPlayViewModel.kt b/features/playbackScreen/src/main/kotlin/voice/features/playbackScreen/BookPlayViewModel.kt index 6be6a8191..f90f0e545 100644 --- a/features/playbackScreen/src/main/kotlin/voice/features/playbackScreen/BookPlayViewModel.kt +++ b/features/playbackScreen/src/main/kotlin/voice/features/playbackScreen/BookPlayViewModel.kt @@ -18,20 +18,20 @@ import kotlinx.coroutines.flow.first import kotlinx.coroutines.launch import voice.core.common.DispatcherProvider import voice.core.common.MainScope -import voice.core.common.compose.ImmutableFile -import voice.core.common.pref.CurrentBookStore -import voice.core.common.pref.SleepTimerPreferenceStore import voice.core.common.sleepTimer.SleepTimerPreference import voice.core.data.BookId import voice.core.data.durationMs import voice.core.data.markForPosition import voice.core.data.repo.BookRepository import voice.core.data.repo.BookmarkRepo +import voice.core.data.store.CurrentBookStore +import voice.core.data.store.SleepTimerPreferenceStore import voice.core.logging.core.Logger import voice.core.playback.PlayerController import voice.core.playback.misc.Decibel import voice.core.playback.misc.VolumeGain import voice.core.playback.playstate.PlayStateManager +import voice.core.ui.ImmutableFile import voice.features.playbackScreen.batteryOptimization.BatteryOptimization import voice.features.sleepTimer.SleepTimer import voice.features.sleepTimer.SleepTimerViewState diff --git a/features/playbackScreen/src/main/kotlin/voice/features/playbackScreen/BookPlayViewState.kt b/features/playbackScreen/src/main/kotlin/voice/features/playbackScreen/BookPlayViewState.kt index 0deabc862..8b1eac89c 100644 --- a/features/playbackScreen/src/main/kotlin/voice/features/playbackScreen/BookPlayViewState.kt +++ b/features/playbackScreen/src/main/kotlin/voice/features/playbackScreen/BookPlayViewState.kt @@ -1,8 +1,8 @@ package voice.features.playbackScreen import androidx.compose.runtime.Immutable -import voice.core.common.compose.ImmutableFile import voice.core.playback.misc.Decibel +import voice.core.ui.ImmutableFile import voice.features.sleepTimer.SleepTimerViewState import kotlin.time.Duration diff --git a/features/playbackScreen/src/main/kotlin/voice/features/playbackScreen/view/BookPlayView.kt b/features/playbackScreen/src/main/kotlin/voice/features/playbackScreen/view/BookPlayView.kt index 802f68bba..2f9c68a46 100644 --- a/features/playbackScreen/src/main/kotlin/voice/features/playbackScreen/view/BookPlayView.kt +++ b/features/playbackScreen/src/main/kotlin/voice/features/playbackScreen/view/BookPlayView.kt @@ -8,7 +8,7 @@ import androidx.compose.runtime.remember import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.tooling.preview.PreviewParameterProvider -import voice.core.common.compose.VoiceTheme +import voice.core.ui.VoiceTheme import voice.features.playbackScreen.BookPlayViewState import kotlin.time.Duration import kotlin.time.Duration.Companion.minutes diff --git a/features/playbackScreen/src/main/kotlin/voice/features/playbackScreen/view/Cover.kt b/features/playbackScreen/src/main/kotlin/voice/features/playbackScreen/view/Cover.kt index f91413abd..f6f36ab63 100644 --- a/features/playbackScreen/src/main/kotlin/voice/features/playbackScreen/view/Cover.kt +++ b/features/playbackScreen/src/main/kotlin/voice/features/playbackScreen/view/Cover.kt @@ -13,7 +13,7 @@ import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp import coil.compose.AsyncImage import voice.core.common.R -import voice.core.common.compose.ImmutableFile +import voice.core.ui.ImmutableFile import voice.core.strings.R as StringsR @Composable diff --git a/features/playbackScreen/src/main/kotlin/voice/features/playbackScreen/view/CoverRow.kt b/features/playbackScreen/src/main/kotlin/voice/features/playbackScreen/view/CoverRow.kt index b25eed044..ea2579f83 100644 --- a/features/playbackScreen/src/main/kotlin/voice/features/playbackScreen/view/CoverRow.kt +++ b/features/playbackScreen/src/main/kotlin/voice/features/playbackScreen/view/CoverRow.kt @@ -10,8 +10,8 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.unit.dp -import voice.core.common.compose.ImmutableFile import voice.core.common.formatTime +import voice.core.ui.ImmutableFile import kotlin.time.Duration @Composable diff --git a/features/playbackScreen/src/main/kotlin/voice/features/playbackScreen/view/PlaybackRow.kt b/features/playbackScreen/src/main/kotlin/voice/features/playbackScreen/view/PlaybackRow.kt index 239ac2d29..ea6ecf66c 100644 --- a/features/playbackScreen/src/main/kotlin/voice/features/playbackScreen/view/PlaybackRow.kt +++ b/features/playbackScreen/src/main/kotlin/voice/features/playbackScreen/view/PlaybackRow.kt @@ -9,7 +9,7 @@ import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.unit.dp -import voice.core.common.compose.PlayButton +import voice.core.ui.PlayButton @Composable internal fun PlaybackRow( diff --git a/features/settings/build.gradle.kts b/features/settings/build.gradle.kts index 84d1aa5be..ddba62635 100644 --- a/features/settings/build.gradle.kts +++ b/features/settings/build.gradle.kts @@ -9,6 +9,7 @@ dependencies { implementation(projects.navigation) implementation(projects.core.strings) implementation(projects.core.playback) + implementation(projects.core.ui) implementation(libs.androidxCore) implementation(libs.materialDialog.core) diff --git a/features/settings/src/main/kotlin/voice/features/settings/SettingsViewModel.kt b/features/settings/src/main/kotlin/voice/features/settings/SettingsViewModel.kt index 9f3271dae..9558350d3 100644 --- a/features/settings/src/main/kotlin/voice/features/settings/SettingsViewModel.kt +++ b/features/settings/src/main/kotlin/voice/features/settings/SettingsViewModel.kt @@ -11,17 +11,17 @@ import androidx.datastore.core.DataStore import dev.zacsweers.metro.Inject import kotlinx.coroutines.launch import voice.core.common.AppInfoProvider -import voice.core.common.DARK_THEME_SETTABLE import voice.core.common.DispatcherProvider import voice.core.common.MainScope import voice.core.common.grid.GridCount import voice.core.common.grid.GridMode -import voice.core.common.pref.AutoRewindAmountStore -import voice.core.common.pref.DarkThemeStore -import voice.core.common.pref.GridModeStore -import voice.core.common.pref.SeekTimeStore -import voice.core.common.pref.SleepTimerPreferenceStore import voice.core.common.sleepTimer.SleepTimerPreference +import voice.core.data.store.AutoRewindAmountStore +import voice.core.data.store.DarkThemeStore +import voice.core.data.store.GridModeStore +import voice.core.data.store.SeekTimeStore +import voice.core.data.store.SleepTimerPreferenceStore +import voice.core.ui.DARK_THEME_SETTABLE import voice.navigation.Destination import voice.navigation.Navigator import java.time.LocalTime diff --git a/features/settings/src/main/kotlin/voice/features/settings/views/Settings.kt b/features/settings/src/main/kotlin/voice/features/settings/views/Settings.kt index 3bd2c7112..356327780 100644 --- a/features/settings/src/main/kotlin/voice/features/settings/views/Settings.kt +++ b/features/settings/src/main/kotlin/voice/features/settings/views/Settings.kt @@ -28,9 +28,9 @@ import dev.zacsweers.metro.AppScope import dev.zacsweers.metro.ContributesTo import dev.zacsweers.metro.IntoSet import dev.zacsweers.metro.Provides -import voice.core.common.compose.VoiceTheme -import voice.core.common.compose.rememberScoped import voice.core.common.rootGraphAs +import voice.core.ui.VoiceTheme +import voice.core.ui.rememberScoped import voice.features.settings.SettingsListener import voice.features.settings.SettingsViewModel import voice.features.settings.SettingsViewState diff --git a/features/settings/src/main/kotlin/voice/features/settings/views/sleeptimer/AutoSleepTimerCard.kt b/features/settings/src/main/kotlin/voice/features/settings/views/sleeptimer/AutoSleepTimerCard.kt index f71b9c20a..75ef27e2f 100644 --- a/features/settings/src/main/kotlin/voice/features/settings/views/sleeptimer/AutoSleepTimerCard.kt +++ b/features/settings/src/main/kotlin/voice/features/settings/views/sleeptimer/AutoSleepTimerCard.kt @@ -9,7 +9,7 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp -import voice.core.common.compose.VoiceTheme +import voice.core.ui.VoiceTheme import voice.features.settings.SettingsListener import voice.features.settings.SettingsViewState import voice.core.strings.R as StringsR diff --git a/features/settings/src/main/kotlin/voice/features/settings/views/sleeptimer/AutoSleepTimerRow.kt b/features/settings/src/main/kotlin/voice/features/settings/views/sleeptimer/AutoSleepTimerRow.kt index 15adc3e87..75f02f268 100644 --- a/features/settings/src/main/kotlin/voice/features/settings/views/sleeptimer/AutoSleepTimerRow.kt +++ b/features/settings/src/main/kotlin/voice/features/settings/views/sleeptimer/AutoSleepTimerRow.kt @@ -14,7 +14,7 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp -import voice.core.common.compose.VoiceTheme +import voice.core.ui.VoiceTheme import java.time.LocalTime import voice.core.strings.R as StringsR diff --git a/features/sleepTimer/src/main/kotlin/voice/features/sleepTimer/AutoEnableSleepTimer.kt b/features/sleepTimer/src/main/kotlin/voice/features/sleepTimer/AutoEnableSleepTimer.kt index e23fb7ec9..c1655aa04 100644 --- a/features/sleepTimer/src/main/kotlin/voice/features/sleepTimer/AutoEnableSleepTimer.kt +++ b/features/sleepTimer/src/main/kotlin/voice/features/sleepTimer/AutoEnableSleepTimer.kt @@ -7,12 +7,12 @@ import kotlinx.coroutines.flow.first import kotlinx.coroutines.launch import voice.core.common.DispatcherProvider import voice.core.common.MainScope -import voice.core.common.pref.CurrentBookStore -import voice.core.common.pref.SleepTimerPreferenceStore import voice.core.common.sleepTimer.SleepTimerPreference import voice.core.data.BookId import voice.core.data.repo.BookRepository import voice.core.data.repo.BookmarkRepo +import voice.core.data.store.CurrentBookStore +import voice.core.data.store.SleepTimerPreferenceStore import voice.core.playback.playstate.PlayStateManager import voice.core.playback.playstate.PlayStateManager.PlayState.Playing import voice.core.playback.session.SleepTimer diff --git a/features/sleepTimer/src/main/kotlin/voice/features/sleepTimer/SleepTimer.kt b/features/sleepTimer/src/main/kotlin/voice/features/sleepTimer/SleepTimer.kt index dfc35b564..bbd196910 100644 --- a/features/sleepTimer/src/main/kotlin/voice/features/sleepTimer/SleepTimer.kt +++ b/features/sleepTimer/src/main/kotlin/voice/features/sleepTimer/SleepTimer.kt @@ -14,9 +14,9 @@ import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.first import kotlinx.coroutines.launch import kotlinx.coroutines.withTimeout -import voice.core.common.pref.FadeOutStore -import voice.core.common.pref.SleepTimerPreferenceStore import voice.core.common.sleepTimer.SleepTimerPreference +import voice.core.data.store.FadeOutStore +import voice.core.data.store.SleepTimerPreferenceStore import voice.core.logging.core.Logger import voice.core.playback.PlayerController import voice.core.playback.playstate.PlayStateManager diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index bf1ae903c..23d3a419a 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -17,6 +17,7 @@ agp = "8.12.1" ktlint-core = "1.7.1" ktlint-gradlePlugin = "0.11.0" retrofit = "3.0.0" +metro = "0.6.3" [libraries] androidPluginForGradle = { module = "com.android.tools.build:gradle", version.ref = "agp" } @@ -104,6 +105,7 @@ compose-activity = "androidx.activity:activity-compose:1.10.1" navigation3-ui = "androidx.navigation3:navigation3-ui:1.0.0-alpha07" navigation3-runtime = "androidx.navigation3:navigation3-runtime:1.0.0-alpha07" +metro-runtime = { module = "dev.zacsweers.metro:runtime", version.ref = "metro" } ktlint-core = { module = "com.pinterest.ktlint:ktlint-cli", version.ref = "ktlint-core" } ktlint-gradlePlugin = { module = "io.github.usefulness:ktlint-gradle-plugin", version.ref = "ktlint-gradlePlugin" } @@ -154,4 +156,4 @@ kotlin-serialization = { id = "org.jetbrains.kotlin.plugin.serialization", versi ksp = { id = "com.google.devtools.ksp", version = "2.2.10-2.0.2" } ktlint = { id = "io.github.usefulness.ktlint-gradle-plugin", version.ref = "ktlint-gradlePlugin" } playPublish = { id = "com.github.triplet.play", version = "3.12.1" } -metro = { id = "dev.zacsweers.metro", version = "0.6.3" } +metro = { id = "dev.zacsweers.metro", version.ref = "metro" } diff --git a/settings.gradle.kts b/settings.gradle.kts index c421429b4..af34db82b 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -77,4 +77,4 @@ include(":core:remoteconfig:noop") include(":core:scanner") include(":core:search") include(":core:strings") - +include(":core:ui") From 590c269b36f31b541fda732c228df541dc6cfd9d Mon Sep 17 00:00:00 2001 From: Paul Woitaschek Date: Tue, 26 Aug 2025 15:57:21 +0200 Subject: [PATCH 02/20] Rename the app packages --- app/src/androidTest/kotlin/voice/app/TestApp.kt | 4 ++-- app/src/androidTest/kotlin/voice/app/TestGraph.kt | 2 +- app/src/main/AndroidManifest.xml | 15 +++++++++++++-- .../voice/app/{features => }/MainActivity.kt | 5 +++-- .../voice/app/{injection => di}/AndroidModule.kt | 2 +- .../kotlin/voice/app/{injection => di}/App.kt | 2 +- .../voice/app/{injection => di}/AppGraph.kt | 3 +-- .../app/{injection => di}/ProductionAppGraph.kt | 2 +- .../{imagepicker => editCover}/CropOverlay.kt | 11 ++++++----- .../editCover}/CropTransformation.kt | 3 +-- .../EditCoverDialog.kt | 4 +--- .../app/features/widget/BaseWidgetProvider.kt | 2 +- .../voice/app/features/widget/WidgetUpdater.kt | 2 +- .../app/misc/MainActivityIntentProviderImpl.kt | 2 +- .../{features => navigation}/NavEntryResolver.kt | 2 +- .../{ => navigation}/StartDestinationProvider.kt | 6 +++--- app/src/main/res/xml/shortcuts.xml | 2 +- .../voice/app/features/NavEntryResolverTest.kt | 1 + 18 files changed, 40 insertions(+), 30 deletions(-) rename app/src/main/kotlin/voice/app/{features => }/MainActivity.kt (97%) rename app/src/main/kotlin/voice/app/{injection => di}/AndroidModule.kt (98%) rename app/src/main/kotlin/voice/app/{injection => di}/App.kt (98%) rename app/src/main/kotlin/voice/app/{injection => di}/AppGraph.kt (68%) rename app/src/main/kotlin/voice/app/{injection => di}/ProductionAppGraph.kt (93%) rename app/src/main/kotlin/voice/app/features/{imagepicker => editCover}/CropOverlay.kt (97%) rename app/src/main/kotlin/voice/app/{uitools => features/editCover}/CropTransformation.kt (92%) rename app/src/main/kotlin/voice/app/features/{bookOverview => editCover}/EditCoverDialog.kt (97%) rename app/src/main/kotlin/voice/app/{features => navigation}/NavEntryResolver.kt (96%) rename app/src/main/kotlin/voice/app/{ => navigation}/StartDestinationProvider.kt (91%) diff --git a/app/src/androidTest/kotlin/voice/app/TestApp.kt b/app/src/androidTest/kotlin/voice/app/TestApp.kt index 9bff05435..db8e72b19 100644 --- a/app/src/androidTest/kotlin/voice/app/TestApp.kt +++ b/app/src/androidTest/kotlin/voice/app/TestApp.kt @@ -1,8 +1,8 @@ package voice.app import dev.zacsweers.metro.createGraphFactory -import voice.app.injection.App -import voice.app.injection.AppGraph +import voice.app.di.App +import voice.app.di.AppGraph class TestApp : App() { diff --git a/app/src/androidTest/kotlin/voice/app/TestGraph.kt b/app/src/androidTest/kotlin/voice/app/TestGraph.kt index 26b01e83a..5aadb9417 100644 --- a/app/src/androidTest/kotlin/voice/app/TestGraph.kt +++ b/app/src/androidTest/kotlin/voice/app/TestGraph.kt @@ -5,7 +5,7 @@ import dev.zacsweers.metro.AppScope import dev.zacsweers.metro.DependencyGraph import dev.zacsweers.metro.Provides import dev.zacsweers.metro.SingleIn -import voice.app.injection.AppGraph +import voice.app.di.AppGraph @SingleIn(AppScope::class) @DependencyGraph( diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 8d49bf2aa..df30a6c19 100755 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -14,7 +14,7 @@ - + + + + + + + + diff --git a/app/src/main/kotlin/voice/app/features/MainActivity.kt b/app/src/main/kotlin/voice/app/MainActivity.kt similarity index 97% rename from app/src/main/kotlin/voice/app/features/MainActivity.kt rename to app/src/main/kotlin/voice/app/MainActivity.kt index 65e6b55bf..532d2de94 100644 --- a/app/src/main/kotlin/voice/app/features/MainActivity.kt +++ b/app/src/main/kotlin/voice/app/MainActivity.kt @@ -1,4 +1,4 @@ -package voice.app.features +package voice.app import android.content.ActivityNotFoundException import android.content.Context @@ -18,7 +18,8 @@ import androidx.navigation3.ui.NavDisplay import dev.zacsweers.metro.AppScope import dev.zacsweers.metro.ContributesTo import dev.zacsweers.metro.Inject -import voice.app.StartDestinationProvider +import voice.app.navigation.NavEntryResolver +import voice.app.navigation.StartDestinationProvider import voice.core.common.rootGraphAs import voice.core.logging.core.Logger import voice.core.ui.VoiceTheme diff --git a/app/src/main/kotlin/voice/app/injection/AndroidModule.kt b/app/src/main/kotlin/voice/app/di/AndroidModule.kt similarity index 98% rename from app/src/main/kotlin/voice/app/injection/AndroidModule.kt rename to app/src/main/kotlin/voice/app/di/AndroidModule.kt index 5ea9d577a..b61c39227 100644 --- a/app/src/main/kotlin/voice/app/injection/AndroidModule.kt +++ b/app/src/main/kotlin/voice/app/di/AndroidModule.kt @@ -1,4 +1,4 @@ -package voice.app.injection +package voice.app.di import android.app.Application import android.content.Context diff --git a/app/src/main/kotlin/voice/app/injection/App.kt b/app/src/main/kotlin/voice/app/di/App.kt similarity index 98% rename from app/src/main/kotlin/voice/app/injection/App.kt rename to app/src/main/kotlin/voice/app/di/App.kt index 21100176d..9fc6e7335 100644 --- a/app/src/main/kotlin/voice/app/injection/App.kt +++ b/app/src/main/kotlin/voice/app/di/App.kt @@ -1,4 +1,4 @@ -package voice.app.injection +package voice.app.di import android.app.Application import androidx.annotation.VisibleForTesting diff --git a/app/src/main/kotlin/voice/app/injection/AppGraph.kt b/app/src/main/kotlin/voice/app/di/AppGraph.kt similarity index 68% rename from app/src/main/kotlin/voice/app/injection/AppGraph.kt rename to app/src/main/kotlin/voice/app/di/AppGraph.kt index fc2f97352..2c351c1c6 100644 --- a/app/src/main/kotlin/voice/app/injection/AppGraph.kt +++ b/app/src/main/kotlin/voice/app/di/AppGraph.kt @@ -1,6 +1,5 @@ -package voice.app.injection +package voice.app.di -import voice.app.features.MainActivity import voice.app.features.widget.BaseWidgetProvider interface AppGraph { diff --git a/app/src/main/kotlin/voice/app/injection/ProductionAppGraph.kt b/app/src/main/kotlin/voice/app/di/ProductionAppGraph.kt similarity index 93% rename from app/src/main/kotlin/voice/app/injection/ProductionAppGraph.kt rename to app/src/main/kotlin/voice/app/di/ProductionAppGraph.kt index 5b5aa29b7..3c91df0fb 100644 --- a/app/src/main/kotlin/voice/app/injection/ProductionAppGraph.kt +++ b/app/src/main/kotlin/voice/app/di/ProductionAppGraph.kt @@ -1,4 +1,4 @@ -package voice.app.injection +package voice.app.di import android.app.Application import dev.zacsweers.metro.AppScope diff --git a/app/src/main/kotlin/voice/app/features/imagepicker/CropOverlay.kt b/app/src/main/kotlin/voice/app/features/editCover/CropOverlay.kt similarity index 97% rename from app/src/main/kotlin/voice/app/features/imagepicker/CropOverlay.kt rename to app/src/main/kotlin/voice/app/features/editCover/CropOverlay.kt index db4e8d807..68a566c89 100644 --- a/app/src/main/kotlin/voice/app/features/imagepicker/CropOverlay.kt +++ b/app/src/main/kotlin/voice/app/features/editCover/CropOverlay.kt @@ -1,4 +1,4 @@ -package voice.app.features.imagepicker +package voice.app.features.editCover import android.annotation.SuppressLint import android.content.Context @@ -8,7 +8,7 @@ import android.graphics.PointF import android.graphics.Rect import android.graphics.RectF import android.util.AttributeSet -import android.view.LayoutInflater.from +import android.view.LayoutInflater import android.view.MotionEvent import android.view.ScaleGestureDetector import android.view.View @@ -89,9 +89,10 @@ class CropOverlay @JvmOverloads constructor( private var resizeType: Resize? = null private val touchOffset = context.dpToPxRounded(16F) - private fun newCircle() = from(context).inflate(R.layout.circle, this@CropOverlay, false).apply { - isVisible = false - } + private fun newCircle() = LayoutInflater.from(context) + .inflate(R.layout.circle, this@CropOverlay, false).apply { + isVisible = false + } private fun minRectSize() = min(bounds.width(), bounds.height()) / 3f diff --git a/app/src/main/kotlin/voice/app/uitools/CropTransformation.kt b/app/src/main/kotlin/voice/app/features/editCover/CropTransformation.kt similarity index 92% rename from app/src/main/kotlin/voice/app/uitools/CropTransformation.kt rename to app/src/main/kotlin/voice/app/features/editCover/CropTransformation.kt index 73927a7bf..86c6a3e01 100644 --- a/app/src/main/kotlin/voice/app/uitools/CropTransformation.kt +++ b/app/src/main/kotlin/voice/app/features/editCover/CropTransformation.kt @@ -1,10 +1,9 @@ -package voice.app.uitools +package voice.app.features.editCover import android.graphics.Bitmap import android.graphics.Rect import coil.size.Size import coil.transform.Transformation -import voice.app.features.imagepicker.CropOverlay class CropTransformation( cropOverlay: CropOverlay, diff --git a/app/src/main/kotlin/voice/app/features/bookOverview/EditCoverDialog.kt b/app/src/main/kotlin/voice/app/features/editCover/EditCoverDialog.kt similarity index 97% rename from app/src/main/kotlin/voice/app/features/bookOverview/EditCoverDialog.kt rename to app/src/main/kotlin/voice/app/features/editCover/EditCoverDialog.kt index 45fb42cc0..c4aeeaab2 100644 --- a/app/src/main/kotlin/voice/app/features/bookOverview/EditCoverDialog.kt +++ b/app/src/main/kotlin/voice/app/features/editCover/EditCoverDialog.kt @@ -1,4 +1,4 @@ -package voice.app.features.bookOverview +package voice.app.features.editCover import android.net.Uri import androidx.compose.foundation.layout.Box @@ -31,8 +31,6 @@ import dev.zacsweers.metro.ContributesTo import dev.zacsweers.metro.IntoSet import dev.zacsweers.metro.Provides import kotlinx.coroutines.launch -import voice.app.features.imagepicker.CropOverlay -import voice.app.uitools.CropTransformation import voice.core.common.rootGraphAs import voice.core.data.BookId import voice.core.scanner.CoverSaver diff --git a/app/src/main/kotlin/voice/app/features/widget/BaseWidgetProvider.kt b/app/src/main/kotlin/voice/app/features/widget/BaseWidgetProvider.kt index afe255b3e..8b5c94032 100644 --- a/app/src/main/kotlin/voice/app/features/widget/BaseWidgetProvider.kt +++ b/app/src/main/kotlin/voice/app/features/widget/BaseWidgetProvider.kt @@ -6,7 +6,7 @@ import android.content.Context import android.content.Intent import android.os.Bundle import dev.zacsweers.metro.Inject -import voice.app.injection.appGraph +import voice.app.di.appGraph class BaseWidgetProvider : AppWidgetProvider() { diff --git a/app/src/main/kotlin/voice/app/features/widget/WidgetUpdater.kt b/app/src/main/kotlin/voice/app/features/widget/WidgetUpdater.kt index e6ba3e7a1..d2a6d849c 100644 --- a/app/src/main/kotlin/voice/app/features/widget/WidgetUpdater.kt +++ b/app/src/main/kotlin/voice/app/features/widget/WidgetUpdater.kt @@ -20,8 +20,8 @@ import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.first import kotlinx.coroutines.launch +import voice.app.MainActivity import voice.app.R -import voice.app.features.MainActivity import voice.core.common.dpToPxRounded import voice.core.data.Book import voice.core.data.BookId diff --git a/app/src/main/kotlin/voice/app/misc/MainActivityIntentProviderImpl.kt b/app/src/main/kotlin/voice/app/misc/MainActivityIntentProviderImpl.kt index bd7e9192d..010c280ed 100644 --- a/app/src/main/kotlin/voice/app/misc/MainActivityIntentProviderImpl.kt +++ b/app/src/main/kotlin/voice/app/misc/MainActivityIntentProviderImpl.kt @@ -3,7 +3,7 @@ package voice.app.misc import android.app.PendingIntent import android.content.Context import dev.zacsweers.metro.Inject -import voice.app.features.MainActivity +import voice.app.MainActivity import voice.core.playback.notification.MainActivityIntentProvider @Inject diff --git a/app/src/main/kotlin/voice/app/features/NavEntryResolver.kt b/app/src/main/kotlin/voice/app/navigation/NavEntryResolver.kt similarity index 96% rename from app/src/main/kotlin/voice/app/features/NavEntryResolver.kt rename to app/src/main/kotlin/voice/app/navigation/NavEntryResolver.kt index 05d89a68f..328e6a957 100644 --- a/app/src/main/kotlin/voice/app/features/NavEntryResolver.kt +++ b/app/src/main/kotlin/voice/app/navigation/NavEntryResolver.kt @@ -1,4 +1,4 @@ -package voice.app.features +package voice.app.navigation import androidx.navigation3.runtime.NavBackStack import androidx.navigation3.runtime.NavEntry diff --git a/app/src/main/kotlin/voice/app/StartDestinationProvider.kt b/app/src/main/kotlin/voice/app/navigation/StartDestinationProvider.kt similarity index 91% rename from app/src/main/kotlin/voice/app/StartDestinationProvider.kt rename to app/src/main/kotlin/voice/app/navigation/StartDestinationProvider.kt index 366809458..77744a6ec 100644 --- a/app/src/main/kotlin/voice/app/StartDestinationProvider.kt +++ b/app/src/main/kotlin/voice/app/navigation/StartDestinationProvider.kt @@ -1,11 +1,11 @@ -package voice.app +package voice.app.navigation import android.content.Intent import androidx.datastore.core.DataStore import dev.zacsweers.metro.Inject import kotlinx.coroutines.flow.first import kotlinx.coroutines.runBlocking -import voice.app.features.MainActivity.Companion.NI_GO_TO_BOOK +import voice.app.MainActivity import voice.core.data.BookId import voice.core.data.folders.AudiobookFolders import voice.core.data.store.CurrentBookStore @@ -29,7 +29,7 @@ class StartDestinationProvider( return listOf(Destination.OnboardingWelcome) } - val goToBook = intent.getBooleanExtra(NI_GO_TO_BOOK, false) + val goToBook = intent.getBooleanExtra(MainActivity.Companion.NI_GO_TO_BOOK, false) if (goToBook) { val bookId = runBlocking { currentBookStore.data.first() } if (bookId != null) { diff --git a/app/src/main/res/xml/shortcuts.xml b/app/src/main/res/xml/shortcuts.xml index b3ae72e69..fbaaa3fdf 100644 --- a/app/src/main/res/xml/shortcuts.xml +++ b/app/src/main/res/xml/shortcuts.xml @@ -5,7 +5,7 @@ android:shortcutShortLabel="@string/play_current"> diff --git a/app/src/test/kotlin/voice/app/features/NavEntryResolverTest.kt b/app/src/test/kotlin/voice/app/features/NavEntryResolverTest.kt index 85d356e83..cfd815050 100644 --- a/app/src/test/kotlin/voice/app/features/NavEntryResolverTest.kt +++ b/app/src/test/kotlin/voice/app/features/NavEntryResolverTest.kt @@ -11,6 +11,7 @@ import io.kotest.matchers.collections.shouldContainAll import io.kotest.matchers.collections.shouldContainExactly import io.kotest.matchers.collections.shouldContainExactlyInAnyOrder import org.junit.Test +import voice.app.navigation.NavEntryResolver import voice.navigation.Destination import kotlin.reflect.KClass From 59d9722bc78b9372862c46eff6e328ed3afd0aac Mon Sep 17 00:00:00 2001 From: Paul Woitaschek Date: Tue, 26 Aug 2025 16:14:55 +0200 Subject: [PATCH 03/20] Inline the datastore module --- AGENTS.md | 39 +++++++++++++++---- app/build.gradle.kts | 1 - .../voice/core/data/store/StoreQualifiers.kt | 6 +++ core/data/impl/build.gradle.kts | 1 - .../data/folders/AudiobookFoldersGraph.kt | 2 +- .../data/store}/KotlinxDataStoreSerializer.kt | 4 +- .../voice/core/data/store/StoreModule.kt | 20 +++++++++- .../core/data/store}/VoiceDataStoreFactory.kt | 4 +- .../core/data/store/DataMigrationTests.kt | 2 +- .../vocie/core/data/store/MigrationTests.kt | 2 +- core/datastore/build.gradle.kts | 9 ----- features/playbackScreen/build.gradle.kts | 1 - .../BatteryOptimization.kt | 2 + .../BatteryOptimizationModule.kt | 20 ---------- features/review/play/build.gradle.kts | 1 - .../voice/features/review/ReviewModule.kt | 15 ------- .../features/review/ShouldShowReviewDialog.kt | 3 +- settings.gradle.kts | 1 - 18 files changed, 66 insertions(+), 67 deletions(-) rename core/{datastore/src/main/kotlin/voice/core/datastore => data/impl/src/main/kotlin/voice/core/data/store}/KotlinxDataStoreSerializer.kt (91%) rename core/{datastore/src/main/kotlin/voice/core/datastore => data/impl/src/main/kotlin/voice/core/data/store}/VoiceDataStoreFactory.kt (95%) delete mode 100644 core/datastore/build.gradle.kts delete mode 100644 features/playbackScreen/src/main/kotlin/voice/features/playbackScreen/batteryOptimization/BatteryOptimizationModule.kt diff --git a/AGENTS.md b/AGENTS.md index d5a2efcc6..5b9d637da 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -21,14 +21,37 @@ Voice is a minimal, user‑focused audiobook player for Android, built for relia Each module contains its own `build.gradle.kts`, `src/main/kotlin`, and `src/test/kotlin`: -* **app**: Main application -* **common**: Shared utilities -* **data**: Repositories and data layer -* **playback**: Audio playback logic -* **scanner**: File scanning and metadata extraction -* **cover**: Cover art handling -* **settings**: Configuration UI -* …additional feature modules +**Infrastructure**: + +* `:app` - Main application entry point and DI setup +* `:navigation` - Navigation framework +* `:plugins` - Gradle build plugins +* `:scripts` - Build and utility scripts + +**Core Modules** (shared domain logic): + +* `:core:common` - Legacy, to be removed +* `:core:ui` - UI components and theming +* `:core:data:api` & `:core:data:impl` - Data layer interfaces and implementations +* `:core:playback` - Audio playback logic +* `:core:scanner` - File scanning and metadata extraction +* `:core:strings` - Localized strings +* `:core:search` - Search functionality +* `:core:documentfile` - File system abstractions +* `:core:logging` - Logging implementations (crashlytics, debug, core) +* `:core:remoteconfig` - Remote configuration (firebase, noop, core) + +**Feature Modules** (UI screens and features): + +* `:features:playbackScreen` - Book playing interface +* `:features:bookOverview` - Library/book list +* `:features:sleepTimer` - Sleep timer functionality +* `:features:settings` - App settings +* `:features:folderPicker` - Folder selection +* `:features:cover` - Cover art management +* `:features:onboarding` - First-time user flow +* `:features:bookmark` - Bookmark management +* `:features:review` - App review prompts (play/noop variants) ## Build & Run diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 6981002f0..6c7dc3cd6 100755 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -148,7 +148,6 @@ android { dependencies { implementation(projects.core.strings) implementation(projects.core.ui) - implementation(projects.core.datastore) implementation(projects.core.common) implementation(projects.core.data.api) implementation(projects.core.data.impl) diff --git a/core/data/api/src/main/kotlin/voice/core/data/store/StoreQualifiers.kt b/core/data/api/src/main/kotlin/voice/core/data/store/StoreQualifiers.kt index 7aecff496..0509d7e69 100644 --- a/core/data/api/src/main/kotlin/voice/core/data/store/StoreQualifiers.kt +++ b/core/data/api/src/main/kotlin/voice/core/data/store/StoreQualifiers.kt @@ -25,3 +25,9 @@ public annotation class DarkThemeStore @Qualifier public annotation class FadeOutStore + +@Qualifier +public annotation class AmountOfBatteryOptimizationRequestedStore + +@Qualifier +public annotation class ReviewDialogShownStore diff --git a/core/data/impl/build.gradle.kts b/core/data/impl/build.gradle.kts index 6eb741cf4..10f518e62 100644 --- a/core/data/impl/build.gradle.kts +++ b/core/data/impl/build.gradle.kts @@ -32,7 +32,6 @@ dependencies { api(projects.core.data.api) api(projects.core.common) api(projects.core.documentfile) - implementation(projects.core.datastore) implementation(libs.androidxCore) implementation(libs.serialization.json) diff --git a/core/data/impl/src/main/kotlin/voice/core/data/folders/AudiobookFoldersGraph.kt b/core/data/impl/src/main/kotlin/voice/core/data/folders/AudiobookFoldersGraph.kt index 0581ce973..4ccd7d97c 100644 --- a/core/data/impl/src/main/kotlin/voice/core/data/folders/AudiobookFoldersGraph.kt +++ b/core/data/impl/src/main/kotlin/voice/core/data/folders/AudiobookFoldersGraph.kt @@ -9,7 +9,7 @@ import dev.zacsweers.metro.Provides import dev.zacsweers.metro.SingleIn import kotlinx.serialization.builtins.SetSerializer import voice.core.common.serialization.UriSerializer -import voice.core.datastore.VoiceDataStoreFactory +import voice.core.data.store.VoiceDataStoreFactory @BindingContainer @ContributesTo(AppScope::class) diff --git a/core/datastore/src/main/kotlin/voice/core/datastore/KotlinxDataStoreSerializer.kt b/core/data/impl/src/main/kotlin/voice/core/data/store/KotlinxDataStoreSerializer.kt similarity index 91% rename from core/datastore/src/main/kotlin/voice/core/datastore/KotlinxDataStoreSerializer.kt rename to core/data/impl/src/main/kotlin/voice/core/data/store/KotlinxDataStoreSerializer.kt index 5a0bbb14c..aa1ebc473 100644 --- a/core/datastore/src/main/kotlin/voice/core/datastore/KotlinxDataStoreSerializer.kt +++ b/core/data/impl/src/main/kotlin/voice/core/data/store/KotlinxDataStoreSerializer.kt @@ -1,4 +1,4 @@ -package voice.core.datastore +package voice.core.data.store import androidx.datastore.core.Serializer import kotlinx.serialization.ExperimentalSerializationApi @@ -9,7 +9,7 @@ import kotlinx.serialization.json.encodeToStream import java.io.InputStream import java.io.OutputStream -class KotlinxDataStoreSerializer( +internal class KotlinxDataStoreSerializer( override val defaultValue: T, private val json: Json, private val serializer: KSerializer, diff --git a/core/data/impl/src/main/kotlin/voice/core/data/store/StoreModule.kt b/core/data/impl/src/main/kotlin/voice/core/data/store/StoreModule.kt index f4552dddb..9ead6a936 100644 --- a/core/data/impl/src/main/kotlin/voice/core/data/store/StoreModule.kt +++ b/core/data/impl/src/main/kotlin/voice/core/data/store/StoreModule.kt @@ -15,7 +15,6 @@ import voice.core.common.AppInfoProvider import voice.core.common.grid.GridMode import voice.core.common.sleepTimer.SleepTimerPreference import voice.core.data.BookId -import voice.core.datastore.VoiceDataStoreFactory import kotlin.time.Duration import kotlin.time.Duration.Companion.seconds @@ -29,7 +28,10 @@ internal object StoreModule { context: Application, appInfoProvider: AppInfoProvider, ): SharedPreferences { - return context.getSharedPreferences("${appInfoProvider.applicationID}_preferences", Context.MODE_PRIVATE) + return context.getSharedPreferences( + "${appInfoProvider.applicationID}_preferences", + Context.MODE_PRIVATE, + ) } @Provides @@ -142,4 +144,18 @@ internal object StoreModule { defaultValue = null, ) } + + @Provides + @SingleIn(AppScope::class) + @AmountOfBatteryOptimizationRequestedStore + fun amountOfBatteryOptimizationsRequestedStore(factory: VoiceDataStoreFactory): DataStore { + return factory.int("amountOfBatteryOptimizationsRequestedStore", 0) + } + + @Provides + @SingleIn(AppScope::class) + @ReviewDialogShownStore + fun reviewDialogShown(factory: VoiceDataStoreFactory): DataStore { + return factory.create(Boolean.serializer(), false, "reviewDialogShown") + } } diff --git a/core/datastore/src/main/kotlin/voice/core/datastore/VoiceDataStoreFactory.kt b/core/data/impl/src/main/kotlin/voice/core/data/store/VoiceDataStoreFactory.kt similarity index 95% rename from core/datastore/src/main/kotlin/voice/core/datastore/VoiceDataStoreFactory.kt rename to core/data/impl/src/main/kotlin/voice/core/data/store/VoiceDataStoreFactory.kt index 2eb5c7d3d..d68d991c0 100644 --- a/core/datastore/src/main/kotlin/voice/core/datastore/VoiceDataStoreFactory.kt +++ b/core/data/impl/src/main/kotlin/voice/core/data/store/VoiceDataStoreFactory.kt @@ -1,4 +1,4 @@ -package voice.core.datastore +package voice.core.data.store import android.app.Application import androidx.datastore.core.DataMigration @@ -11,7 +11,7 @@ import kotlinx.serialization.builtins.serializer import kotlinx.serialization.json.Json @Inject -class VoiceDataStoreFactory( +internal class VoiceDataStoreFactory( private val json: Json, private val context: Application, ) { diff --git a/core/data/impl/src/test/kotlin/vocie/core/data/store/DataMigrationTests.kt b/core/data/impl/src/test/kotlin/vocie/core/data/store/DataMigrationTests.kt index 940aad76a..8bf6d1797 100644 --- a/core/data/impl/src/test/kotlin/vocie/core/data/store/DataMigrationTests.kt +++ b/core/data/impl/src/test/kotlin/vocie/core/data/store/DataMigrationTests.kt @@ -13,9 +13,9 @@ import kotlinx.serialization.json.Json import org.junit.Before import org.junit.Test import org.junit.runner.RunWith +import voice.core.data.store.VoiceDataStoreFactory import voice.core.data.store.booleanPrefsDataMigration import voice.core.data.store.intPrefsDataMigration -import voice.core.datastore.VoiceDataStoreFactory @RunWith(AndroidJUnit4::class) class DataMigrationTests { diff --git a/core/data/impl/src/test/kotlin/vocie/core/data/store/MigrationTests.kt b/core/data/impl/src/test/kotlin/vocie/core/data/store/MigrationTests.kt index a4fbbd12a..7db779a19 100644 --- a/core/data/impl/src/test/kotlin/vocie/core/data/store/MigrationTests.kt +++ b/core/data/impl/src/test/kotlin/vocie/core/data/store/MigrationTests.kt @@ -24,8 +24,8 @@ import voice.core.data.store.AutoRewindAmountStore import voice.core.data.store.DarkThemeStore import voice.core.data.store.GridModeStore import voice.core.data.store.SeekTimeStore +import voice.core.data.store.VoiceDataStoreFactory import voice.core.data.store.intPrefsDataMigration -import voice.core.datastore.VoiceDataStoreFactory @SingleIn(AppScope::class) @DependencyGraph( diff --git a/core/datastore/build.gradle.kts b/core/datastore/build.gradle.kts deleted file mode 100644 index 052dc68c8..000000000 --- a/core/datastore/build.gradle.kts +++ /dev/null @@ -1,9 +0,0 @@ -plugins { - id("voice.library") - alias(libs.plugins.metro) -} - -dependencies { - api(libs.serialization.json) - api(libs.datastore) -} diff --git a/features/playbackScreen/build.gradle.kts b/features/playbackScreen/build.gradle.kts index f6bb6dad2..957715acb 100644 --- a/features/playbackScreen/build.gradle.kts +++ b/features/playbackScreen/build.gradle.kts @@ -11,7 +11,6 @@ dependencies { implementation(projects.core.playback) implementation(projects.core.data.api) implementation(projects.core.ui) - implementation(projects.core.datastore) implementation(projects.features.sleepTimer) implementation(libs.datastore) diff --git a/features/playbackScreen/src/main/kotlin/voice/features/playbackScreen/batteryOptimization/BatteryOptimization.kt b/features/playbackScreen/src/main/kotlin/voice/features/playbackScreen/batteryOptimization/BatteryOptimization.kt index 0f0fd84f0..3dd8eac4e 100644 --- a/features/playbackScreen/src/main/kotlin/voice/features/playbackScreen/batteryOptimization/BatteryOptimization.kt +++ b/features/playbackScreen/src/main/kotlin/voice/features/playbackScreen/batteryOptimization/BatteryOptimization.kt @@ -4,10 +4,12 @@ import android.os.Build import androidx.datastore.core.DataStore import dev.zacsweers.metro.Inject import kotlinx.coroutines.flow.first +import voice.core.data.store.AmountOfBatteryOptimizationRequestedStore @Inject class BatteryOptimization( private val isIgnoringBatteryOptimizations: IsIgnoringBatteryOptimizations, + @AmountOfBatteryOptimizationRequestedStore private val amountOfBatteryOptimizationsRequested: DataStore, ) { diff --git a/features/playbackScreen/src/main/kotlin/voice/features/playbackScreen/batteryOptimization/BatteryOptimizationModule.kt b/features/playbackScreen/src/main/kotlin/voice/features/playbackScreen/batteryOptimization/BatteryOptimizationModule.kt deleted file mode 100644 index 8783531f7..000000000 --- a/features/playbackScreen/src/main/kotlin/voice/features/playbackScreen/batteryOptimization/BatteryOptimizationModule.kt +++ /dev/null @@ -1,20 +0,0 @@ -package voice.features.playbackScreen.batteryOptimization - -import androidx.datastore.core.DataStore -import dev.zacsweers.metro.AppScope -import dev.zacsweers.metro.BindingContainer -import dev.zacsweers.metro.ContributesTo -import dev.zacsweers.metro.Provides -import dev.zacsweers.metro.SingleIn -import voice.core.datastore.VoiceDataStoreFactory - -@BindingContainer -@ContributesTo(AppScope::class) -object BatteryOptimizationModule { - - @Provides - @SingleIn(AppScope::class) - fun amountOfBatteryOptimizationsRequestedStore(factory: VoiceDataStoreFactory): DataStore { - return factory.int("amountOfBatteryOptimizationsRequestedStore", 0) - } -} diff --git a/features/review/play/build.gradle.kts b/features/review/play/build.gradle.kts index 2e3651302..051f30d9d 100644 --- a/features/review/play/build.gradle.kts +++ b/features/review/play/build.gradle.kts @@ -16,7 +16,6 @@ dependencies { implementation(libs.lottie) implementation(projects.core.common) implementation(projects.core.data.api) - implementation(projects.core.datastore) implementation(projects.core.playback) implementation(projects.core.remoteconfig.core) implementation(projects.core.strings) diff --git a/features/review/play/src/main/kotlin/voice/features/review/ReviewModule.kt b/features/review/play/src/main/kotlin/voice/features/review/ReviewModule.kt index b01cf6a79..2082a484b 100644 --- a/features/review/play/src/main/kotlin/voice/features/review/ReviewModule.kt +++ b/features/review/play/src/main/kotlin/voice/features/review/ReviewModule.kt @@ -1,34 +1,19 @@ package voice.features.review import android.content.Context -import androidx.datastore.core.DataStore import com.google.android.play.core.review.ReviewManager import com.google.android.play.core.review.ReviewManagerFactory import dev.zacsweers.metro.AppScope import dev.zacsweers.metro.BindingContainer import dev.zacsweers.metro.ContributesTo import dev.zacsweers.metro.Provides -import dev.zacsweers.metro.Qualifier -import dev.zacsweers.metro.SingleIn -import kotlinx.serialization.builtins.serializer -import voice.core.datastore.VoiceDataStoreFactory @ContributesTo(AppScope::class) @BindingContainer object ReviewModule { - @Provides - @SingleIn(AppScope::class) - @ReviewDialogShown - fun reviewDialogShown(factory: VoiceDataStoreFactory): DataStore { - return factory.create(Boolean.serializer(), false, "reviewDialogShown") - } - @Provides fun reviewManager(context: Context): ReviewManager { return ReviewManagerFactory.create(context) } } - -@Qualifier -annotation class ReviewDialogShown diff --git a/features/review/play/src/main/kotlin/voice/features/review/ShouldShowReviewDialog.kt b/features/review/play/src/main/kotlin/voice/features/review/ShouldShowReviewDialog.kt index e9f237c1d..9af725d7a 100644 --- a/features/review/play/src/main/kotlin/voice/features/review/ShouldShowReviewDialog.kt +++ b/features/review/play/src/main/kotlin/voice/features/review/ShouldShowReviewDialog.kt @@ -4,6 +4,7 @@ import androidx.datastore.core.DataStore import dev.zacsweers.metro.Inject import kotlinx.coroutines.flow.first import voice.core.data.repo.BookRepository +import voice.core.data.store.ReviewDialogShownStore import voice.core.playback.playstate.PlayStateManager import voice.core.remoteconfig.core.RemoteConfig import java.time.Clock @@ -17,7 +18,7 @@ import kotlin.time.Duration.Companion.minutes class ShouldShowReviewDialog( private val installationTimeProvider: InstallationTimeProvider, private val clock: Clock, - @ReviewDialogShown + @ReviewDialogShownStore private val reviewDialogShown: DataStore, private val bookRepository: BookRepository, private val playStateManager: PlayStateManager, diff --git a/settings.gradle.kts b/settings.gradle.kts index af34db82b..1954dc187 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -65,7 +65,6 @@ include(":features:bookmark") include(":core:common") include(":core:data:api") include(":core:data:impl") -include(":core:datastore") include(":core:documentfile") include(":core:logging:core") include(":core:logging:crashlytics") From 5b00250a2f4e9fd2b7ca5f93daf51a37c989e96e Mon Sep 17 00:00:00 2001 From: Paul Woitaschek Date: Tue, 26 Aug 2025 16:20:39 +0200 Subject: [PATCH 04/20] Move the cover cropping to the cover module --- features/cover/build.gradle.kts | 6 ++++++ .../main/kotlin/voice/features/cover/crop}/CropOverlay.kt | 4 ++-- .../kotlin/voice/features/cover/crop}/CropTransformation.kt | 2 +- .../kotlin/voice/features/cover/crop}/EditCoverDialog.kt | 4 ++-- {app => features/cover}/src/main/res/layout/circle.xml | 0 5 files changed, 11 insertions(+), 5 deletions(-) rename {app/src/main/kotlin/voice/app/features/editCover => features/cover/src/main/kotlin/voice/features/cover/crop}/CropOverlay.kt (99%) rename {app/src/main/kotlin/voice/app/features/editCover => features/cover/src/main/kotlin/voice/features/cover/crop}/CropTransformation.kt (96%) rename {app/src/main/kotlin/voice/app/features/editCover => features/cover/src/main/kotlin/voice/features/cover/crop}/EditCoverDialog.kt (97%) rename {app => features/cover}/src/main/res/layout/circle.xml (100%) diff --git a/features/cover/build.gradle.kts b/features/cover/build.gradle.kts index b82bd411c..c3269e334 100644 --- a/features/cover/build.gradle.kts +++ b/features/cover/build.gradle.kts @@ -5,9 +5,14 @@ plugins { alias(libs.plugins.metro) } +android { + androidResources.enable = true +} + dependencies { implementation(projects.core.data.api) implementation(projects.navigation) + implementation(projects.core.scanner) implementation(projects.core.strings) implementation(projects.core.ui) implementation(projects.core.remoteconfig.core) @@ -16,6 +21,7 @@ dependencies { implementation(libs.okhttp) implementation(libs.paging.compose) implementation(libs.paging.runtime) + implementation(libs.navigation3.ui) implementation(libs.serialization.json) testImplementation(projects.core.data.impl) diff --git a/app/src/main/kotlin/voice/app/features/editCover/CropOverlay.kt b/features/cover/src/main/kotlin/voice/features/cover/crop/CropOverlay.kt similarity index 99% rename from app/src/main/kotlin/voice/app/features/editCover/CropOverlay.kt rename to features/cover/src/main/kotlin/voice/features/cover/crop/CropOverlay.kt index 68a566c89..3dfe74766 100644 --- a/app/src/main/kotlin/voice/app/features/editCover/CropOverlay.kt +++ b/features/cover/src/main/kotlin/voice/features/cover/crop/CropOverlay.kt @@ -1,4 +1,4 @@ -package voice.app.features.editCover +package voice.features.cover.crop import android.annotation.SuppressLint import android.content.Context @@ -14,8 +14,8 @@ import android.view.ScaleGestureDetector import android.view.View import android.widget.FrameLayout import androidx.core.view.isVisible -import voice.app.R import voice.core.common.dpToPxRounded +import voice.features.cover.R import kotlin.math.max import kotlin.math.min import kotlin.math.roundToInt diff --git a/app/src/main/kotlin/voice/app/features/editCover/CropTransformation.kt b/features/cover/src/main/kotlin/voice/features/cover/crop/CropTransformation.kt similarity index 96% rename from app/src/main/kotlin/voice/app/features/editCover/CropTransformation.kt rename to features/cover/src/main/kotlin/voice/features/cover/crop/CropTransformation.kt index 86c6a3e01..a7f8a82ff 100644 --- a/app/src/main/kotlin/voice/app/features/editCover/CropTransformation.kt +++ b/features/cover/src/main/kotlin/voice/features/cover/crop/CropTransformation.kt @@ -1,4 +1,4 @@ -package voice.app.features.editCover +package voice.features.cover.crop import android.graphics.Bitmap import android.graphics.Rect diff --git a/app/src/main/kotlin/voice/app/features/editCover/EditCoverDialog.kt b/features/cover/src/main/kotlin/voice/features/cover/crop/EditCoverDialog.kt similarity index 97% rename from app/src/main/kotlin/voice/app/features/editCover/EditCoverDialog.kt rename to features/cover/src/main/kotlin/voice/features/cover/crop/EditCoverDialog.kt index c4aeeaab2..41c6063f3 100644 --- a/app/src/main/kotlin/voice/app/features/editCover/EditCoverDialog.kt +++ b/features/cover/src/main/kotlin/voice/features/cover/crop/EditCoverDialog.kt @@ -1,4 +1,4 @@ -package voice.app.features.editCover +package voice.features.cover.crop import android.net.Uri import androidx.compose.foundation.layout.Box @@ -138,7 +138,7 @@ fun EditCoverDialog( }, dismissButton = { TextButton(onClick = { onDismiss() }) { - Text(text = context.getString(android.R.string.cancel)) + Text(text = context.getString(StringsR.string.dialog_cancel)) } }, ) diff --git a/app/src/main/res/layout/circle.xml b/features/cover/src/main/res/layout/circle.xml similarity index 100% rename from app/src/main/res/layout/circle.xml rename to features/cover/src/main/res/layout/circle.xml From f6765ceef8d2de2829a2afd9d70aa0e275011369 Mon Sep 17 00:00:00 2001 From: Paul Woitaschek Date: Tue, 26 Aug 2025 19:56:32 +0200 Subject: [PATCH 05/20] Move the widgets to a separate module. --- app/build.gradle.kts | 1 + app/src/main/kotlin/voice/app/di/App.kt | 2 +- app/src/main/kotlin/voice/app/di/AppGraph.kt | 5 ++-- app/src/main/res/values/colors.xml | 7 ----- .../cover}/src/main/res/drawable/circle.xml | 0 features/widget/build.gradle.kts | 28 +++++++++++++++++++ .../app/features/widget/BaseWidgetProvider.kt | 6 ++-- .../features/widget/TriggerWidgetOnChange.kt | 2 +- .../voice/features/widget/WidgetGraph.kt | 7 +++++ .../voice}/features/widget/WidgetUpdater.kt | 27 ++++-------------- .../copy_abc_primary_text_material_dark.xml | 0 .../copy_abc_secondary_text_material_dark.xml | 0 .../res/drawable/notification_divider.xml | 0 .../main/res/drawable/widget_background.xml | 0 .../widget}/src/main/res/layout/widget.xml | 2 +- .../widget/src/main/res/values/colors.xml | 9 ++++++ .../widget/src/main/res/values/dimens.xml | 2 +- settings.gradle.kts | 23 +++++++-------- 18 files changed, 74 insertions(+), 47 deletions(-) rename {app => features/cover}/src/main/res/drawable/circle.xml (100%) create mode 100644 features/widget/build.gradle.kts rename {app => features/widget}/src/main/kotlin/voice/app/features/widget/BaseWidgetProvider.kt (83%) rename {app/src/main/kotlin/voice/app => features/widget/src/main/kotlin/voice}/features/widget/TriggerWidgetOnChange.kt (98%) create mode 100644 features/widget/src/main/kotlin/voice/features/widget/WidgetGraph.kt rename {app/src/main/kotlin/voice/app => features/widget/src/main/kotlin/voice}/features/widget/WidgetUpdater.kt (90%) rename {app => features/widget}/src/main/res/color/copy_abc_primary_text_material_dark.xml (100%) rename {app => features/widget}/src/main/res/color/copy_abc_secondary_text_material_dark.xml (100%) rename {app => features/widget}/src/main/res/drawable/notification_divider.xml (100%) rename {app => features/widget}/src/main/res/drawable/widget_background.xml (100%) rename {app => features/widget}/src/main/res/layout/widget.xml (99%) create mode 100644 features/widget/src/main/res/values/colors.xml rename app/src/main/res/values/dimen.xml => features/widget/src/main/res/values/dimens.xml (93%) diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 6c7dc3cd6..a6977f5ec 100755 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -164,6 +164,7 @@ dependencies { implementation(projects.core.documentfile) implementation(projects.features.onboarding) implementation(projects.features.bookmark) + implementation(projects.features.widget) implementation(libs.appCompat) implementation(libs.material) diff --git a/app/src/main/kotlin/voice/app/di/App.kt b/app/src/main/kotlin/voice/app/di/App.kt index 9fc6e7335..530b221ad 100644 --- a/app/src/main/kotlin/voice/app/di/App.kt +++ b/app/src/main/kotlin/voice/app/di/App.kt @@ -12,12 +12,12 @@ import dev.zacsweers.metro.createGraphFactory import kotlinx.coroutines.MainScope import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.launch -import voice.app.features.widget.TriggerWidgetOnChange import voice.core.common.rootGraph import voice.core.data.store.DarkThemeStore import voice.core.scanner.MediaScanTrigger import voice.core.ui.DARK_THEME_SETTABLE import voice.features.sleepTimer.AutoEnableSleepTimer +import voice.features.widget.TriggerWidgetOnChange open class App : Application() { diff --git a/app/src/main/kotlin/voice/app/di/AppGraph.kt b/app/src/main/kotlin/voice/app/di/AppGraph.kt index 2c351c1c6..74316469f 100644 --- a/app/src/main/kotlin/voice/app/di/AppGraph.kt +++ b/app/src/main/kotlin/voice/app/di/AppGraph.kt @@ -1,9 +1,10 @@ package voice.app.di import voice.app.features.widget.BaseWidgetProvider +import voice.features.widget.WidgetGraph -interface AppGraph { +interface AppGraph : WidgetGraph { fun inject(target: App) - fun inject(target: BaseWidgetProvider) + override fun inject(target: BaseWidgetProvider) } diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml index 0df07f8b7..45b867797 100755 --- a/app/src/main/res/values/colors.xml +++ b/app/src/main/res/values/colors.xml @@ -1,11 +1,4 @@ - - #4Dffffff - #ffffffff - @color/material_grey_850 - #36ffffff - #b3ffffff - #FFFFFF diff --git a/app/src/main/res/drawable/circle.xml b/features/cover/src/main/res/drawable/circle.xml similarity index 100% rename from app/src/main/res/drawable/circle.xml rename to features/cover/src/main/res/drawable/circle.xml diff --git a/features/widget/build.gradle.kts b/features/widget/build.gradle.kts new file mode 100644 index 000000000..b85dcd15a --- /dev/null +++ b/features/widget/build.gradle.kts @@ -0,0 +1,28 @@ +plugins { + id("voice.library") + alias(libs.plugins.metro) +} + +android { + androidResources { + enable = true + } +} + +dependencies { + implementation(projects.core.strings) + implementation(projects.core.common) + implementation(projects.core.data.api) + implementation(projects.core.playback) + + implementation(libs.appCompat) + implementation(libs.datastore) + implementation(libs.coil) + implementation(libs.androidxCore) + + testImplementation(libs.junit) + testImplementation(libs.androidX.test.core) + testImplementation(libs.androidX.test.junit) + testImplementation(libs.androidX.test.runner) + testImplementation(libs.robolectric) +} diff --git a/app/src/main/kotlin/voice/app/features/widget/BaseWidgetProvider.kt b/features/widget/src/main/kotlin/voice/app/features/widget/BaseWidgetProvider.kt similarity index 83% rename from app/src/main/kotlin/voice/app/features/widget/BaseWidgetProvider.kt rename to features/widget/src/main/kotlin/voice/app/features/widget/BaseWidgetProvider.kt index 8b5c94032..9e1a20a53 100644 --- a/app/src/main/kotlin/voice/app/features/widget/BaseWidgetProvider.kt +++ b/features/widget/src/main/kotlin/voice/app/features/widget/BaseWidgetProvider.kt @@ -6,7 +6,9 @@ import android.content.Context import android.content.Intent import android.os.Bundle import dev.zacsweers.metro.Inject -import voice.app.di.appGraph +import voice.core.common.rootGraph +import voice.features.widget.WidgetGraph +import voice.features.widget.WidgetUpdater class BaseWidgetProvider : AppWidgetProvider() { @@ -17,7 +19,7 @@ class BaseWidgetProvider : AppWidgetProvider() { context: Context, intent: Intent?, ) { - appGraph.inject(this) + (rootGraph as WidgetGraph).inject(this) super.onReceive(context, intent) } diff --git a/app/src/main/kotlin/voice/app/features/widget/TriggerWidgetOnChange.kt b/features/widget/src/main/kotlin/voice/features/widget/TriggerWidgetOnChange.kt similarity index 98% rename from app/src/main/kotlin/voice/app/features/widget/TriggerWidgetOnChange.kt rename to features/widget/src/main/kotlin/voice/features/widget/TriggerWidgetOnChange.kt index 5931c3d9d..4b07e4ce5 100644 --- a/app/src/main/kotlin/voice/app/features/widget/TriggerWidgetOnChange.kt +++ b/features/widget/src/main/kotlin/voice/features/widget/TriggerWidgetOnChange.kt @@ -1,4 +1,4 @@ -package voice.app.features.widget +package voice.features.widget import androidx.datastore.core.DataStore import dev.zacsweers.metro.AppScope diff --git a/features/widget/src/main/kotlin/voice/features/widget/WidgetGraph.kt b/features/widget/src/main/kotlin/voice/features/widget/WidgetGraph.kt new file mode 100644 index 000000000..c11ee0309 --- /dev/null +++ b/features/widget/src/main/kotlin/voice/features/widget/WidgetGraph.kt @@ -0,0 +1,7 @@ +package voice.features.widget + +import voice.app.features.widget.BaseWidgetProvider + +interface WidgetGraph { + fun inject(target: BaseWidgetProvider) +} diff --git a/app/src/main/kotlin/voice/app/features/widget/WidgetUpdater.kt b/features/widget/src/main/kotlin/voice/features/widget/WidgetUpdater.kt similarity index 90% rename from app/src/main/kotlin/voice/app/features/widget/WidgetUpdater.kt rename to features/widget/src/main/kotlin/voice/features/widget/WidgetUpdater.kt index d2a6d849c..59b5e5884 100644 --- a/app/src/main/kotlin/voice/app/features/widget/WidgetUpdater.kt +++ b/features/widget/src/main/kotlin/voice/features/widget/WidgetUpdater.kt @@ -1,10 +1,8 @@ -package voice.app.features.widget +package voice.features.widget -import android.app.PendingIntent import android.appwidget.AppWidgetManager import android.content.ComponentName import android.content.Context -import android.content.Intent import android.content.res.Configuration import android.os.Bundle import android.view.View @@ -20,13 +18,13 @@ import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.first import kotlinx.coroutines.launch -import voice.app.MainActivity -import voice.app.R +import voice.app.features.widget.BaseWidgetProvider import voice.core.common.dpToPxRounded import voice.core.data.Book import voice.core.data.BookId import voice.core.data.repo.BookRepository import voice.core.data.store.CurrentBookStore +import voice.core.playback.notification.MainActivityIntentProvider import voice.core.playback.playstate.PlayStateManager import voice.core.playback.receiver.WidgetButtonReceiver import voice.core.common.R as CommonR @@ -39,6 +37,7 @@ class WidgetUpdater( @CurrentBookStore private val currentBookStore: DataStore, private val playStateManager: PlayStateManager, + private val mainActivityIntentProvider: MainActivityIntentProvider, ) { private val appWidgetManager = AppWidgetManager.getInstance(context) @@ -109,15 +108,7 @@ class WidgetUpdater( private fun initWidgetForAbsentBook(widgetId: Int) { val remoteViews = RemoteViews(context.packageName, R.layout.widget) - // directly going back to bookChoose - val wholeWidgetClickI = Intent(context, MainActivity::class.java) - wholeWidgetClickI.flags = Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_NEW_TASK - val wholeWidgetClickPI = PendingIntent.getActivity( - context, - System.currentTimeMillis().toInt(), - wholeWidgetClickI, - PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE, - ) + val wholeWidgetClickPI = mainActivityIntentProvider.toCurrentBook() remoteViews.setImageViewResource(R.id.imageView, CommonR.drawable.album_art) remoteViews.setOnClickPendingIntent(R.id.wholeWidget, wholeWidgetClickPI) appWidgetManager.updateAppWidget(widgetId, remoteViews) @@ -157,13 +148,7 @@ class WidgetUpdater( remoteViews.setTextViewText(R.id.summary, name) - val wholeWidgetClickI = MainActivity.goToBookIntent(context) - val wholeWidgetClickPI = PendingIntent.getActivity( - context, - System.currentTimeMillis().toInt(), - wholeWidgetClickI, - PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE, - ) + val wholeWidgetClickPI = mainActivityIntentProvider.toCurrentBook() val coverFile = book.content.cover if (coverFile != null && coverSize > 0) { diff --git a/app/src/main/res/color/copy_abc_primary_text_material_dark.xml b/features/widget/src/main/res/color/copy_abc_primary_text_material_dark.xml similarity index 100% rename from app/src/main/res/color/copy_abc_primary_text_material_dark.xml rename to features/widget/src/main/res/color/copy_abc_primary_text_material_dark.xml diff --git a/app/src/main/res/color/copy_abc_secondary_text_material_dark.xml b/features/widget/src/main/res/color/copy_abc_secondary_text_material_dark.xml similarity index 100% rename from app/src/main/res/color/copy_abc_secondary_text_material_dark.xml rename to features/widget/src/main/res/color/copy_abc_secondary_text_material_dark.xml diff --git a/app/src/main/res/drawable/notification_divider.xml b/features/widget/src/main/res/drawable/notification_divider.xml similarity index 100% rename from app/src/main/res/drawable/notification_divider.xml rename to features/widget/src/main/res/drawable/notification_divider.xml diff --git a/app/src/main/res/drawable/widget_background.xml b/features/widget/src/main/res/drawable/widget_background.xml similarity index 100% rename from app/src/main/res/drawable/widget_background.xml rename to features/widget/src/main/res/drawable/widget_background.xml diff --git a/app/src/main/res/layout/widget.xml b/features/widget/src/main/res/layout/widget.xml similarity index 99% rename from app/src/main/res/layout/widget.xml rename to features/widget/src/main/res/layout/widget.xml index b645565b3..7fcbc5da0 100644 --- a/app/src/main/res/layout/widget.xml +++ b/features/widget/src/main/res/layout/widget.xml @@ -90,4 +90,4 @@ - + \ No newline at end of file diff --git a/features/widget/src/main/res/values/colors.xml b/features/widget/src/main/res/values/colors.xml new file mode 100644 index 000000000..535d8aff3 --- /dev/null +++ b/features/widget/src/main/res/values/colors.xml @@ -0,0 +1,9 @@ + + + + #4Dffffff + #ffffffff + #ff424242 + #36ffffff + #b3ffffff + \ No newline at end of file diff --git a/app/src/main/res/values/dimen.xml b/features/widget/src/main/res/values/dimens.xml similarity index 93% rename from app/src/main/res/values/dimen.xml rename to features/widget/src/main/res/values/dimens.xml index f2cba6f1a..5d839b087 100644 --- a/app/src/main/res/values/dimen.xml +++ b/features/widget/src/main/res/values/dimens.xml @@ -3,4 +3,4 @@ 16sp 14sp - + \ No newline at end of file diff --git a/settings.gradle.kts b/settings.gradle.kts index 1954dc187..9b48ba39b 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -51,17 +51,6 @@ include(":app") include(":navigation") include(":scripts") -include(":features:bookOverview") -include(":features:cover") -include(":features:folderPicker") -include(":features:onboarding") -include(":features:playbackScreen") -include(":features:review:noop") -include(":features:review:play") -include(":features:settings") -include(":features:sleepTimer") -include(":features:bookmark") - include(":core:common") include(":core:data:api") include(":core:data:impl") @@ -77,3 +66,15 @@ include(":core:scanner") include(":core:search") include(":core:strings") include(":core:ui") + +include(":features:bookOverview") +include(":features:bookmark") +include(":features:cover") +include(":features:folderPicker") +include(":features:onboarding") +include(":features:playbackScreen") +include(":features:review:noop") +include(":features:review:play") +include(":features:settings") +include(":features:sleepTimer") +include(":features:widget") From cf6cbb0ef716f14fd6f9371217e6d0aa6a76a569 Mon Sep 17 00:00:00 2001 From: Paul Woitaschek Date: Tue, 26 Aug 2025 20:00:36 +0200 Subject: [PATCH 06/20] Remove the ApplicationId from the AppInfoProvider. --- app/src/main/kotlin/voice/app/misc/AppInfoProviderImpl.kt | 3 --- .../src/main/kotlin/voice/core/common/AppInfoProvider.kt | 1 - .../src/main/kotlin/voice/core/data/store/StoreModule.kt | 8 ++------ .../test/kotlin/vocie/core/data/store/MigrationTests.kt | 2 -- 4 files changed, 2 insertions(+), 12 deletions(-) diff --git a/app/src/main/kotlin/voice/app/misc/AppInfoProviderImpl.kt b/app/src/main/kotlin/voice/app/misc/AppInfoProviderImpl.kt index f08ee2530..49bc54619 100644 --- a/app/src/main/kotlin/voice/app/misc/AppInfoProviderImpl.kt +++ b/app/src/main/kotlin/voice/app/misc/AppInfoProviderImpl.kt @@ -6,8 +6,5 @@ import voice.core.common.AppInfoProvider @Inject class AppInfoProviderImpl : AppInfoProvider { - - override val applicationID = BuildConfig.APPLICATION_ID - override val versionName: String = BuildConfig.VERSION_NAME } diff --git a/core/common/src/main/kotlin/voice/core/common/AppInfoProvider.kt b/core/common/src/main/kotlin/voice/core/common/AppInfoProvider.kt index 1c77f443a..24f5b86d4 100644 --- a/core/common/src/main/kotlin/voice/core/common/AppInfoProvider.kt +++ b/core/common/src/main/kotlin/voice/core/common/AppInfoProvider.kt @@ -1,6 +1,5 @@ package voice.core.common interface AppInfoProvider { - val applicationID: String val versionName: String } diff --git a/core/data/impl/src/main/kotlin/voice/core/data/store/StoreModule.kt b/core/data/impl/src/main/kotlin/voice/core/data/store/StoreModule.kt index 9ead6a936..6ecbf4442 100644 --- a/core/data/impl/src/main/kotlin/voice/core/data/store/StoreModule.kt +++ b/core/data/impl/src/main/kotlin/voice/core/data/store/StoreModule.kt @@ -11,7 +11,6 @@ import dev.zacsweers.metro.Provides import dev.zacsweers.metro.SingleIn import kotlinx.serialization.builtins.nullable import kotlinx.serialization.builtins.serializer -import voice.core.common.AppInfoProvider import voice.core.common.grid.GridMode import voice.core.common.sleepTimer.SleepTimerPreference import voice.core.data.BookId @@ -24,12 +23,9 @@ internal object StoreModule { @Provides @SingleIn(AppScope::class) - fun sharedPreferences( - context: Application, - appInfoProvider: AppInfoProvider, - ): SharedPreferences { + fun sharedPreferences(context: Application): SharedPreferences { return context.getSharedPreferences( - "${appInfoProvider.applicationID}_preferences", + "${context.packageName}_preferences", Context.MODE_PRIVATE, ) } diff --git a/core/data/impl/src/test/kotlin/vocie/core/data/store/MigrationTests.kt b/core/data/impl/src/test/kotlin/vocie/core/data/store/MigrationTests.kt index 7db779a19..34c7657c5 100644 --- a/core/data/impl/src/test/kotlin/vocie/core/data/store/MigrationTests.kt +++ b/core/data/impl/src/test/kotlin/vocie/core/data/store/MigrationTests.kt @@ -54,8 +54,6 @@ interface MigrationTestGraph { @get:Provides val appInfoProvider: AppInfoProvider get() = object : AppInfoProvider { - override val applicationID: String - get() = "de.ph1b.audiobook" override val versionName: String get() = "1.2.3" } From 87b16c3c27a315a08d9e4a6290248aa3e774af9f Mon Sep 17 00:00:00 2001 From: Paul Woitaschek Date: Tue, 26 Aug 2025 20:57:28 +0200 Subject: [PATCH 07/20] Create an api/impl module for the sleeptimer and move it out of common --- app/build.gradle.kts | 2 ++ app/src/main/kotlin/voice/app/di/App.kt | 2 +- .../data/sleeptimer}/SleepTimerPreference.kt | 8 +++--- .../voice/core/data/store/StoreModule.kt | 2 +- core/playback/build.gradle.kts | 1 + core/sleeptimer/api/build.gradle.kts | 11 ++++++++ .../voice/core/sleeptimer}/SleepTimer.kt | 5 ++-- core/sleeptimer/impl/build.gradle.kts | 26 +++++++++++++++++++ .../core/sleeptimer}/AutoEnableSleepTimer.kt | 5 ++-- .../voice/core/sleeptimer}/IsTimeInRange.kt | 2 +- .../voice/core/sleeptimer}/ShakeDetector.kt | 2 +- .../voice/core/sleeptimer/SleepTimerImpl.kt | 12 ++++----- .../sleeptimer}/AutoEnableSleepTimerTest.kt | 15 +++++------ .../core/sleeptimer}/IsTimeInRangeTest.kt | 2 +- .../voice/core/sleeptimer}/ManualClock.kt | 2 +- features/playbackScreen/build.gradle.kts | 1 + .../playbackScreen/BookPlayViewModel.kt | 6 ++--- .../playbackScreen/BookPlayViewModelTest.kt | 4 +-- features/settings/build.gradle.kts | 1 + .../features/settings/SettingsViewModel.kt | 2 +- features/sleepTimer/build.gradle.kts | 12 ++++----- settings.gradle.kts | 2 ++ 22 files changed, 84 insertions(+), 41 deletions(-) rename core/{common/src/main/kotlin/voice/core/common/sleepTimer => data/api/src/main/kotlin/voice/core/data/sleeptimer}/SleepTimerPreference.kt (82%) create mode 100644 core/sleeptimer/api/build.gradle.kts rename core/{playback/src/main/kotlin/voice/core/playback/session => sleeptimer/api/src/main/kotlin/voice/core/sleeptimer}/SleepTimer.kt (64%) create mode 100644 core/sleeptimer/impl/build.gradle.kts rename {features/sleepTimer/src/main/kotlin/voice/features/sleepTimer => core/sleeptimer/impl/src/main/kotlin/voice/core/sleeptimer}/AutoEnableSleepTimer.kt (94%) rename {features/sleepTimer/src/main/kotlin/voice/features/sleepTimer => core/sleeptimer/impl/src/main/kotlin/voice/core/sleeptimer}/IsTimeInRange.kt (92%) rename {features/sleepTimer/src/main/kotlin/voice/features/sleepTimer => core/sleeptimer/impl/src/main/kotlin/voice/core/sleeptimer}/ShakeDetector.kt (96%) rename features/sleepTimer/src/main/kotlin/voice/features/sleepTimer/SleepTimer.kt => core/sleeptimer/impl/src/main/kotlin/voice/core/sleeptimer/SleepTimerImpl.kt (94%) rename {features/sleepTimer/src/test/kotlin/voice/features/sleepTimer => core/sleeptimer/impl/src/test/kotlin/voice/core/sleeptimer}/AutoEnableSleepTimerTest.kt (96%) rename {features/sleepTimer/src/test/kotlin/voice/features/sleepTimer => core/sleeptimer/impl/src/test/kotlin/voice/core/sleeptimer}/IsTimeInRangeTest.kt (97%) rename {features/sleepTimer/src/test/kotlin/voice/features/sleepTimer => core/sleeptimer/impl/src/test/kotlin/voice/core/sleeptimer}/ManualClock.kt (91%) diff --git a/app/build.gradle.kts b/app/build.gradle.kts index a6977f5ec..ec2053136 100755 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -155,6 +155,8 @@ dependencies { implementation(projects.core.scanner) implementation(projects.features.playbackScreen) implementation(projects.navigation) + implementation(projects.core.sleeptimer.api) + implementation(projects.core.sleeptimer.impl) implementation(projects.features.sleepTimer) implementation(projects.features.settings) implementation(projects.features.folderPicker) diff --git a/app/src/main/kotlin/voice/app/di/App.kt b/app/src/main/kotlin/voice/app/di/App.kt index 530b221ad..4697071c1 100644 --- a/app/src/main/kotlin/voice/app/di/App.kt +++ b/app/src/main/kotlin/voice/app/di/App.kt @@ -15,8 +15,8 @@ import kotlinx.coroutines.launch import voice.core.common.rootGraph import voice.core.data.store.DarkThemeStore import voice.core.scanner.MediaScanTrigger +import voice.core.sleeptimer.AutoEnableSleepTimer import voice.core.ui.DARK_THEME_SETTABLE -import voice.features.sleepTimer.AutoEnableSleepTimer import voice.features.widget.TriggerWidgetOnChange open class App : Application() { diff --git a/core/common/src/main/kotlin/voice/core/common/sleepTimer/SleepTimerPreference.kt b/core/data/api/src/main/kotlin/voice/core/data/sleeptimer/SleepTimerPreference.kt similarity index 82% rename from core/common/src/main/kotlin/voice/core/common/sleepTimer/SleepTimerPreference.kt rename to core/data/api/src/main/kotlin/voice/core/data/sleeptimer/SleepTimerPreference.kt index 22e4b009e..7d26e9704 100644 --- a/core/common/src/main/kotlin/voice/core/common/sleepTimer/SleepTimerPreference.kt +++ b/core/data/api/src/main/kotlin/voice/core/data/sleeptimer/SleepTimerPreference.kt @@ -1,4 +1,4 @@ -package voice.core.common.sleepTimer +package voice.core.data.sleeptimer import kotlinx.serialization.Serializable import voice.core.common.serialization.LocalTimeSerializer @@ -7,7 +7,7 @@ import kotlin.time.Duration import kotlin.time.Duration.Companion.minutes @Serializable -data class SleepTimerPreference( +public data class SleepTimerPreference( /** * The custom sleep time duration */ @@ -22,8 +22,8 @@ data class SleepTimerPreference( val autoSleepEndTime: LocalTime, ) { - companion object { - val Default = SleepTimerPreference( + public companion object { + public val Default: SleepTimerPreference = SleepTimerPreference( autoSleepTimerEnabled = false, autoSleepStartTime = LocalTime.of(22, 0), autoSleepEndTime = LocalTime.of(6, 0), diff --git a/core/data/impl/src/main/kotlin/voice/core/data/store/StoreModule.kt b/core/data/impl/src/main/kotlin/voice/core/data/store/StoreModule.kt index 6ecbf4442..81b982680 100644 --- a/core/data/impl/src/main/kotlin/voice/core/data/store/StoreModule.kt +++ b/core/data/impl/src/main/kotlin/voice/core/data/store/StoreModule.kt @@ -12,8 +12,8 @@ import dev.zacsweers.metro.SingleIn import kotlinx.serialization.builtins.nullable import kotlinx.serialization.builtins.serializer import voice.core.common.grid.GridMode -import voice.core.common.sleepTimer.SleepTimerPreference import voice.core.data.BookId +import voice.core.data.sleeptimer.SleepTimerPreference import kotlin.time.Duration import kotlin.time.Duration.Companion.seconds diff --git a/core/playback/build.gradle.kts b/core/playback/build.gradle.kts index 24c07ab41..4b285ac68 100644 --- a/core/playback/build.gradle.kts +++ b/core/playback/build.gradle.kts @@ -14,6 +14,7 @@ dependencies { implementation(projects.core.common) implementation(projects.core.strings) implementation(projects.core.data.api) + implementation(projects.core.sleeptimer.api) implementation(libs.androidxCore) implementation(libs.datastore) diff --git a/core/sleeptimer/api/build.gradle.kts b/core/sleeptimer/api/build.gradle.kts new file mode 100644 index 000000000..2fe25c62c --- /dev/null +++ b/core/sleeptimer/api/build.gradle.kts @@ -0,0 +1,11 @@ +plugins { + id("voice.library") +} + +dependencies { + testImplementation(libs.junit) + testImplementation(libs.androidX.test.core) + testImplementation(libs.androidX.test.junit) + testImplementation(libs.androidX.test.runner) + testImplementation(libs.robolectric) +} diff --git a/core/playback/src/main/kotlin/voice/core/playback/session/SleepTimer.kt b/core/sleeptimer/api/src/main/kotlin/voice/core/sleeptimer/SleepTimer.kt similarity index 64% rename from core/playback/src/main/kotlin/voice/core/playback/session/SleepTimer.kt rename to core/sleeptimer/api/src/main/kotlin/voice/core/sleeptimer/SleepTimer.kt index 4c27845cd..2941f766e 100644 --- a/core/playback/src/main/kotlin/voice/core/playback/session/SleepTimer.kt +++ b/core/sleeptimer/api/src/main/kotlin/voice/core/sleeptimer/SleepTimer.kt @@ -1,10 +1,11 @@ -package voice.core.playback.session +package voice.core.sleeptimer import kotlinx.coroutines.flow.Flow import kotlin.time.Duration -interface SleepTimer { +public interface SleepTimer { val leftSleepTimeFlow: Flow fun sleepTimerActive(): Boolean fun setActive(enable: Boolean) + fun setActive(sleepTime: Duration) } diff --git a/core/sleeptimer/impl/build.gradle.kts b/core/sleeptimer/impl/build.gradle.kts new file mode 100644 index 000000000..2aa1a903f --- /dev/null +++ b/core/sleeptimer/impl/build.gradle.kts @@ -0,0 +1,26 @@ +plugins { + id("voice.library") + alias(libs.plugins.metro) +} + +dependencies { + implementation(projects.core.sleeptimer.api) + implementation(projects.core.data.api) + implementation(projects.core.common) + implementation(projects.core.playback) + implementation(projects.core.logging.core) + + implementation(libs.datastore) + implementation(libs.androidxCore) + implementation(libs.seismic) + implementation(libs.appCompat) + + testImplementation(libs.junit) + testImplementation(libs.mockk) + testImplementation(libs.koTest.assert) + testImplementation(libs.coroutines.test) + testImplementation(libs.androidX.test.core) + testImplementation(libs.androidX.test.junit) + testImplementation(libs.androidX.test.runner) + testImplementation(libs.robolectric) +} diff --git a/features/sleepTimer/src/main/kotlin/voice/features/sleepTimer/AutoEnableSleepTimer.kt b/core/sleeptimer/impl/src/main/kotlin/voice/core/sleeptimer/AutoEnableSleepTimer.kt similarity index 94% rename from features/sleepTimer/src/main/kotlin/voice/features/sleepTimer/AutoEnableSleepTimer.kt rename to core/sleeptimer/impl/src/main/kotlin/voice/core/sleeptimer/AutoEnableSleepTimer.kt index c1655aa04..b553c17dd 100644 --- a/features/sleepTimer/src/main/kotlin/voice/features/sleepTimer/AutoEnableSleepTimer.kt +++ b/core/sleeptimer/impl/src/main/kotlin/voice/core/sleeptimer/AutoEnableSleepTimer.kt @@ -1,4 +1,4 @@ -package voice.features.sleepTimer +package voice.core.sleeptimer import androidx.datastore.core.DataStore import dev.zacsweers.metro.Inject @@ -7,15 +7,14 @@ import kotlinx.coroutines.flow.first import kotlinx.coroutines.launch import voice.core.common.DispatcherProvider import voice.core.common.MainScope -import voice.core.common.sleepTimer.SleepTimerPreference import voice.core.data.BookId import voice.core.data.repo.BookRepository import voice.core.data.repo.BookmarkRepo +import voice.core.data.sleeptimer.SleepTimerPreference import voice.core.data.store.CurrentBookStore import voice.core.data.store.SleepTimerPreferenceStore import voice.core.playback.playstate.PlayStateManager import voice.core.playback.playstate.PlayStateManager.PlayState.Playing -import voice.core.playback.session.SleepTimer import java.time.Clock @Inject diff --git a/features/sleepTimer/src/main/kotlin/voice/features/sleepTimer/IsTimeInRange.kt b/core/sleeptimer/impl/src/main/kotlin/voice/core/sleeptimer/IsTimeInRange.kt similarity index 92% rename from features/sleepTimer/src/main/kotlin/voice/features/sleepTimer/IsTimeInRange.kt rename to core/sleeptimer/impl/src/main/kotlin/voice/core/sleeptimer/IsTimeInRange.kt index b5c182fed..767ef2d4f 100644 --- a/features/sleepTimer/src/main/kotlin/voice/features/sleepTimer/IsTimeInRange.kt +++ b/core/sleeptimer/impl/src/main/kotlin/voice/core/sleeptimer/IsTimeInRange.kt @@ -1,4 +1,4 @@ -package voice.features.sleepTimer +package voice.core.sleeptimer import java.time.LocalTime diff --git a/features/sleepTimer/src/main/kotlin/voice/features/sleepTimer/ShakeDetector.kt b/core/sleeptimer/impl/src/main/kotlin/voice/core/sleeptimer/ShakeDetector.kt similarity index 96% rename from features/sleepTimer/src/main/kotlin/voice/features/sleepTimer/ShakeDetector.kt rename to core/sleeptimer/impl/src/main/kotlin/voice/core/sleeptimer/ShakeDetector.kt index 370362a7d..075f20330 100644 --- a/features/sleepTimer/src/main/kotlin/voice/features/sleepTimer/ShakeDetector.kt +++ b/core/sleeptimer/impl/src/main/kotlin/voice/core/sleeptimer/ShakeDetector.kt @@ -1,4 +1,4 @@ -package voice.features.sleepTimer +package voice.core.sleeptimer import android.content.Context import android.hardware.SensorManager diff --git a/features/sleepTimer/src/main/kotlin/voice/features/sleepTimer/SleepTimer.kt b/core/sleeptimer/impl/src/main/kotlin/voice/core/sleeptimer/SleepTimerImpl.kt similarity index 94% rename from features/sleepTimer/src/main/kotlin/voice/features/sleepTimer/SleepTimer.kt rename to core/sleeptimer/impl/src/main/kotlin/voice/core/sleeptimer/SleepTimerImpl.kt index bbd196910..c0b0ed87f 100644 --- a/features/sleepTimer/src/main/kotlin/voice/features/sleepTimer/SleepTimer.kt +++ b/core/sleeptimer/impl/src/main/kotlin/voice/core/sleeptimer/SleepTimerImpl.kt @@ -1,4 +1,4 @@ -package voice.features.sleepTimer +package voice.core.sleeptimer import androidx.datastore.core.DataStore import androidx.interpolator.view.animation.FastOutSlowInInterpolator @@ -14,7 +14,7 @@ import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.first import kotlinx.coroutines.launch import kotlinx.coroutines.withTimeout -import voice.core.common.sleepTimer.SleepTimerPreference +import voice.core.data.sleeptimer.SleepTimerPreference import voice.core.data.store.FadeOutStore import voice.core.data.store.SleepTimerPreferenceStore import voice.core.logging.core.Logger @@ -24,12 +24,12 @@ import voice.core.playback.playstate.PlayStateManager.PlayState.Playing import kotlin.time.Duration import kotlin.time.Duration.Companion.milliseconds import kotlin.time.Duration.Companion.seconds -import voice.core.playback.session.SleepTimer as PlaybackSleepTimer +import voice.core.sleeptimer.SleepTimer as SleepTimerApi @SingleIn(AppScope::class) @ContributesBinding(AppScope::class) @Inject -class SleepTimer( +class SleepTimerImpl( private val playStateManager: PlayStateManager, private val shakeDetector: ShakeDetector, @SleepTimerPreferenceStore @@ -37,7 +37,7 @@ class SleepTimer( private val playerController: PlayerController, @FadeOutStore private val fadeOutStore: DataStore, -) : PlaybackSleepTimer { +) : SleepTimerApi { private val scope = MainScope() @@ -68,7 +68,7 @@ class SleepTimer( } } - fun setActive(sleepTime: Duration) { + override fun setActive(sleepTime: Duration) { Logger.i("Starting sleepTimer. Pause in $sleepTime.") leftSleepTime = sleepTime playerController.setVolume(1F) diff --git a/features/sleepTimer/src/test/kotlin/voice/features/sleepTimer/AutoEnableSleepTimerTest.kt b/core/sleeptimer/impl/src/test/kotlin/voice/core/sleeptimer/AutoEnableSleepTimerTest.kt similarity index 96% rename from features/sleepTimer/src/test/kotlin/voice/features/sleepTimer/AutoEnableSleepTimerTest.kt rename to core/sleeptimer/impl/src/test/kotlin/voice/core/sleeptimer/AutoEnableSleepTimerTest.kt index 6cc7a7509..6c5cb17b5 100644 --- a/features/sleepTimer/src/test/kotlin/voice/features/sleepTimer/AutoEnableSleepTimerTest.kt +++ b/core/sleeptimer/impl/src/test/kotlin/voice/core/sleeptimer/AutoEnableSleepTimerTest.kt @@ -1,4 +1,4 @@ -package voice.features.sleepTimer +package voice.core.sleeptimer import androidx.datastore.core.DataStore import io.mockk.coEvery @@ -20,13 +20,12 @@ import org.junit.Assert.assertTrue import org.junit.Before import org.junit.Test import voice.core.common.DispatcherProvider -import voice.core.common.sleepTimer.SleepTimerPreference import voice.core.data.Book import voice.core.data.BookId import voice.core.data.repo.BookRepository import voice.core.data.repo.BookmarkRepo +import voice.core.data.sleeptimer.SleepTimerPreference import voice.core.playback.playstate.PlayStateManager -import voice.core.playback.session.SleepTimer import java.time.Instant import java.time.LocalDateTime import java.time.LocalTime @@ -122,7 +121,7 @@ class AutoEnableSleepTimerTest { playStateFlow.value = PlayStateManager.PlayState.Playing advanceUntilIdle() - coVerify(exactly = 0) { sleepTimer.setActive(any()) } + coVerify(exactly = 0) { sleepTimer.setActive(any()) } coVerify(exactly = 0) { bookmarkRepo.addBookmarkAtBookPosition(any(), any(), any()) } } @@ -138,7 +137,7 @@ class AutoEnableSleepTimerTest { playStateFlow.value = PlayStateManager.PlayState.Playing advanceUntilIdle() - coVerify(exactly = 0) { sleepTimer.setActive(any()) } + coVerify(exactly = 0) { sleepTimer.setActive(any()) } coVerify(exactly = 0) { bookmarkRepo.addBookmarkAtBookPosition(any(), any(), any()) } } @@ -156,7 +155,7 @@ class AutoEnableSleepTimerTest { playStateFlow.value = PlayStateManager.PlayState.Playing advanceUntilIdle() - coVerify(exactly = 0) { sleepTimer.setActive(any()) } + coVerify(exactly = 0) { sleepTimer.setActive(any()) } coVerify(exactly = 0) { bookmarkRepo.addBookmarkAtBookPosition(any(), any(), any()) } } @@ -173,7 +172,7 @@ class AutoEnableSleepTimerTest { advanceUntilIdle() coVerify(exactly = 0) { sleepTimerPreferenceStore.data } - coVerify(exactly = 0) { sleepTimer.setActive(any()) } + coVerify(exactly = 0) { sleepTimer.setActive(any()) } coVerify(exactly = 0) { bookmarkRepo.addBookmarkAtBookPosition(any(), any(), any()) } } @@ -233,7 +232,7 @@ class AutoEnableSleepTimerTest { } advanceUntilIdle() - coVerify(exactly = 0) { sleepTimer.setActive(any()) } + coVerify(exactly = 0) { sleepTimer.setActive(any()) } coVerify(exactly = 0) { bookmarkRepo.addBookmarkAtBookPosition(any(), any(), any()) } } diff --git a/features/sleepTimer/src/test/kotlin/voice/features/sleepTimer/IsTimeInRangeTest.kt b/core/sleeptimer/impl/src/test/kotlin/voice/core/sleeptimer/IsTimeInRangeTest.kt similarity index 97% rename from features/sleepTimer/src/test/kotlin/voice/features/sleepTimer/IsTimeInRangeTest.kt rename to core/sleeptimer/impl/src/test/kotlin/voice/core/sleeptimer/IsTimeInRangeTest.kt index de210eaf9..dd540e2ae 100644 --- a/features/sleepTimer/src/test/kotlin/voice/features/sleepTimer/IsTimeInRangeTest.kt +++ b/core/sleeptimer/impl/src/test/kotlin/voice/core/sleeptimer/IsTimeInRangeTest.kt @@ -1,4 +1,4 @@ -package voice.features.sleepTimer +package voice.core.sleeptimer import io.kotest.matchers.shouldBe import org.junit.Test diff --git a/features/sleepTimer/src/test/kotlin/voice/features/sleepTimer/ManualClock.kt b/core/sleeptimer/impl/src/test/kotlin/voice/core/sleeptimer/ManualClock.kt similarity index 91% rename from features/sleepTimer/src/test/kotlin/voice/features/sleepTimer/ManualClock.kt rename to core/sleeptimer/impl/src/test/kotlin/voice/core/sleeptimer/ManualClock.kt index 5b2c812be..6ef3c65fb 100644 --- a/features/sleepTimer/src/test/kotlin/voice/features/sleepTimer/ManualClock.kt +++ b/core/sleeptimer/impl/src/test/kotlin/voice/core/sleeptimer/ManualClock.kt @@ -1,4 +1,4 @@ -package voice.features.sleepTimer +package voice.core.sleeptimer import java.time.Clock import java.time.Instant diff --git a/features/playbackScreen/build.gradle.kts b/features/playbackScreen/build.gradle.kts index 957715acb..8e347856d 100644 --- a/features/playbackScreen/build.gradle.kts +++ b/features/playbackScreen/build.gradle.kts @@ -11,6 +11,7 @@ dependencies { implementation(projects.core.playback) implementation(projects.core.data.api) implementation(projects.core.ui) + implementation(projects.core.sleeptimer.api) implementation(projects.features.sleepTimer) implementation(libs.datastore) diff --git a/features/playbackScreen/src/main/kotlin/voice/features/playbackScreen/BookPlayViewModel.kt b/features/playbackScreen/src/main/kotlin/voice/features/playbackScreen/BookPlayViewModel.kt index f90f0e545..c9ce6d3c4 100644 --- a/features/playbackScreen/src/main/kotlin/voice/features/playbackScreen/BookPlayViewModel.kt +++ b/features/playbackScreen/src/main/kotlin/voice/features/playbackScreen/BookPlayViewModel.kt @@ -18,12 +18,12 @@ import kotlinx.coroutines.flow.first import kotlinx.coroutines.launch import voice.core.common.DispatcherProvider import voice.core.common.MainScope -import voice.core.common.sleepTimer.SleepTimerPreference import voice.core.data.BookId import voice.core.data.durationMs import voice.core.data.markForPosition import voice.core.data.repo.BookRepository import voice.core.data.repo.BookmarkRepo +import voice.core.data.sleeptimer.SleepTimerPreference import voice.core.data.store.CurrentBookStore import voice.core.data.store.SleepTimerPreferenceStore import voice.core.logging.core.Logger @@ -31,9 +31,9 @@ import voice.core.playback.PlayerController import voice.core.playback.misc.Decibel import voice.core.playback.misc.VolumeGain import voice.core.playback.playstate.PlayStateManager +import voice.core.sleeptimer.SleepTimer import voice.core.ui.ImmutableFile import voice.features.playbackScreen.batteryOptimization.BatteryOptimization -import voice.features.sleepTimer.SleepTimer import voice.features.sleepTimer.SleepTimerViewState import voice.navigation.Destination import voice.navigation.Navigator @@ -82,7 +82,7 @@ class BookPlayViewModel( playStateManager.flow }.collectAsState() - val sleepTime by remember { sleepTimer.leftSleepTimeFlow }.collectAsState() + val sleepTime by remember { sleepTimer.leftSleepTimeFlow }.collectAsState(Duration.ZERO) val currentMark = book.currentChapter.markForPosition(book.content.positionInChapter) val hasMoreThanOneChapter = book.chapters.sumOf { it.chapterMarks.count() } > 1 diff --git a/features/playbackScreen/src/test/kotlin/voice/features/playbackScreen/BookPlayViewModelTest.kt b/features/playbackScreen/src/test/kotlin/voice/features/playbackScreen/BookPlayViewModelTest.kt index 47f49d5d0..b3f555ca0 100644 --- a/features/playbackScreen/src/test/kotlin/voice/features/playbackScreen/BookPlayViewModelTest.kt +++ b/features/playbackScreen/src/test/kotlin/voice/features/playbackScreen/BookPlayViewModelTest.kt @@ -16,7 +16,6 @@ import kotlinx.coroutines.test.runTest import kotlinx.coroutines.yield import org.junit.Test import voice.core.common.DispatcherProvider -import voice.core.common.sleepTimer.SleepTimerPreference import voice.core.data.Book import voice.core.data.BookContent import voice.core.data.BookId @@ -24,8 +23,9 @@ import voice.core.data.Bookmark import voice.core.data.Chapter import voice.core.data.ChapterId import voice.core.data.MarkData +import voice.core.data.sleeptimer.SleepTimerPreference import voice.core.playback.PlayerController -import voice.features.sleepTimer.SleepTimer +import voice.core.sleeptimer.SleepTimer import voice.features.sleepTimer.SleepTimerViewState import java.time.Instant import java.util.UUID diff --git a/features/settings/build.gradle.kts b/features/settings/build.gradle.kts index ddba62635..537856143 100644 --- a/features/settings/build.gradle.kts +++ b/features/settings/build.gradle.kts @@ -10,6 +10,7 @@ dependencies { implementation(projects.core.strings) implementation(projects.core.playback) implementation(projects.core.ui) + implementation(projects.core.data.api) implementation(libs.androidxCore) implementation(libs.materialDialog.core) diff --git a/features/settings/src/main/kotlin/voice/features/settings/SettingsViewModel.kt b/features/settings/src/main/kotlin/voice/features/settings/SettingsViewModel.kt index 9558350d3..7bedb7aae 100644 --- a/features/settings/src/main/kotlin/voice/features/settings/SettingsViewModel.kt +++ b/features/settings/src/main/kotlin/voice/features/settings/SettingsViewModel.kt @@ -15,7 +15,7 @@ import voice.core.common.DispatcherProvider import voice.core.common.MainScope import voice.core.common.grid.GridCount import voice.core.common.grid.GridMode -import voice.core.common.sleepTimer.SleepTimerPreference +import voice.core.data.sleeptimer.SleepTimerPreference import voice.core.data.store.AutoRewindAmountStore import voice.core.data.store.DarkThemeStore import voice.core.data.store.GridModeStore diff --git a/features/sleepTimer/build.gradle.kts b/features/sleepTimer/build.gradle.kts index bf64c7224..953dac6eb 100644 --- a/features/sleepTimer/build.gradle.kts +++ b/features/sleepTimer/build.gradle.kts @@ -1,18 +1,18 @@ plugins { id("voice.library") id("voice.compose") - alias(libs.plugins.metro) } dependencies { - implementation(projects.core.common) implementation(projects.core.strings) - implementation(projects.core.playback) implementation(projects.core.data.api) - implementation(libs.androidxCore) implementation(libs.materialDialog.core) - implementation(libs.androidxCore) implementation(libs.material) - implementation(libs.seismic) + + testImplementation(libs.junit) + testImplementation(libs.androidX.test.core) + testImplementation(libs.androidX.test.junit) + testImplementation(libs.androidX.test.runner) + testImplementation(libs.robolectric) } diff --git a/settings.gradle.kts b/settings.gradle.kts index 9b48ba39b..88680fe96 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -78,3 +78,5 @@ include(":features:review:play") include(":features:settings") include(":features:sleepTimer") include(":features:widget") +include(":core:sleeptimer:api") +include(":core:sleeptimer:impl") \ No newline at end of file From c814171b865c53f5f3ea39cb12900bbb9ebf7f49 Mon Sep 17 00:00:00 2001 From: Paul Woitaschek Date: Tue, 26 Aug 2025 21:00:31 +0200 Subject: [PATCH 08/20] Move the GridCount out of common --- .../api/src/main/kotlin/voice/core/data}/GridMode.kt | 4 ++-- .../impl/src/main/kotlin/voice/core/data/store/StoreModule.kt | 2 +- .../src/test/kotlin/vocie/core/data/store/MigrationTests.kt | 2 +- .../api/src/main/kotlin/voice/core/sleeptimer/SleepTimer.kt | 2 +- .../grid => ui/src/main/kotlin/voice/core/ui}/GridCount.kt | 2 +- .../features/bookOverview/overview/BookOverviewViewModel.kt | 4 ++-- .../main/kotlin/voice/features/settings/SettingsViewModel.kt | 4 ++-- 7 files changed, 10 insertions(+), 10 deletions(-) rename core/{common/src/main/kotlin/voice/core/common/grid => data/api/src/main/kotlin/voice/core/data}/GridMode.kt (63%) rename core/{common/src/main/kotlin/voice/core/common/grid => ui/src/main/kotlin/voice/core/ui}/GridCount.kt (92%) diff --git a/core/common/src/main/kotlin/voice/core/common/grid/GridMode.kt b/core/data/api/src/main/kotlin/voice/core/data/GridMode.kt similarity index 63% rename from core/common/src/main/kotlin/voice/core/common/grid/GridMode.kt rename to core/data/api/src/main/kotlin/voice/core/data/GridMode.kt index e42383cdd..9ebb2994c 100644 --- a/core/common/src/main/kotlin/voice/core/common/grid/GridMode.kt +++ b/core/data/api/src/main/kotlin/voice/core/data/GridMode.kt @@ -1,9 +1,9 @@ -package voice.core.common.grid +package voice.core.data import kotlinx.serialization.Serializable @Serializable -enum class GridMode { +public enum class GridMode { LIST, GRID, FOLLOW_DEVICE, diff --git a/core/data/impl/src/main/kotlin/voice/core/data/store/StoreModule.kt b/core/data/impl/src/main/kotlin/voice/core/data/store/StoreModule.kt index 81b982680..463247335 100644 --- a/core/data/impl/src/main/kotlin/voice/core/data/store/StoreModule.kt +++ b/core/data/impl/src/main/kotlin/voice/core/data/store/StoreModule.kt @@ -11,8 +11,8 @@ import dev.zacsweers.metro.Provides import dev.zacsweers.metro.SingleIn import kotlinx.serialization.builtins.nullable import kotlinx.serialization.builtins.serializer -import voice.core.common.grid.GridMode import voice.core.data.BookId +import voice.core.data.GridMode import voice.core.data.sleeptimer.SleepTimerPreference import kotlin.time.Duration import kotlin.time.Duration.Companion.seconds diff --git a/core/data/impl/src/test/kotlin/vocie/core/data/store/MigrationTests.kt b/core/data/impl/src/test/kotlin/vocie/core/data/store/MigrationTests.kt index 34c7657c5..1e4b2f142 100644 --- a/core/data/impl/src/test/kotlin/vocie/core/data/store/MigrationTests.kt +++ b/core/data/impl/src/test/kotlin/vocie/core/data/store/MigrationTests.kt @@ -19,7 +19,7 @@ import kotlinx.serialization.json.Json import org.junit.Test import org.junit.runner.RunWith import voice.core.common.AppInfoProvider -import voice.core.common.grid.GridMode +import voice.core.data.GridMode import voice.core.data.store.AutoRewindAmountStore import voice.core.data.store.DarkThemeStore import voice.core.data.store.GridModeStore diff --git a/core/sleeptimer/api/src/main/kotlin/voice/core/sleeptimer/SleepTimer.kt b/core/sleeptimer/api/src/main/kotlin/voice/core/sleeptimer/SleepTimer.kt index 2941f766e..25cd088ac 100644 --- a/core/sleeptimer/api/src/main/kotlin/voice/core/sleeptimer/SleepTimer.kt +++ b/core/sleeptimer/api/src/main/kotlin/voice/core/sleeptimer/SleepTimer.kt @@ -3,7 +3,7 @@ package voice.core.sleeptimer import kotlinx.coroutines.flow.Flow import kotlin.time.Duration -public interface SleepTimer { +interface SleepTimer { val leftSleepTimeFlow: Flow fun sleepTimerActive(): Boolean fun setActive(enable: Boolean) diff --git a/core/common/src/main/kotlin/voice/core/common/grid/GridCount.kt b/core/ui/src/main/kotlin/voice/core/ui/GridCount.kt similarity index 92% rename from core/common/src/main/kotlin/voice/core/common/grid/GridCount.kt rename to core/ui/src/main/kotlin/voice/core/ui/GridCount.kt index 29d6c53a5..9038bda0d 100644 --- a/core/common/src/main/kotlin/voice/core/common/grid/GridCount.kt +++ b/core/ui/src/main/kotlin/voice/core/ui/GridCount.kt @@ -1,4 +1,4 @@ -package voice.core.common.grid +package voice.core.ui import android.content.Context import dev.zacsweers.metro.Inject diff --git a/features/bookOverview/src/main/kotlin/voice/features/bookOverview/overview/BookOverviewViewModel.kt b/features/bookOverview/src/main/kotlin/voice/features/bookOverview/overview/BookOverviewViewModel.kt index 3838a43cc..e55a01293 100644 --- a/features/bookOverview/src/main/kotlin/voice/features/bookOverview/overview/BookOverviewViewModel.kt +++ b/features/bookOverview/src/main/kotlin/voice/features/bookOverview/overview/BookOverviewViewModel.kt @@ -18,9 +18,8 @@ import kotlinx.collections.immutable.toImmutableMap import kotlinx.coroutines.MainScope import kotlinx.coroutines.launch import voice.core.common.comparator.sortedNaturally -import voice.core.common.grid.GridCount -import voice.core.common.grid.GridMode import voice.core.data.BookId +import voice.core.data.GridMode import voice.core.data.repo.BookContentRepo import voice.core.data.repo.BookRepository import voice.core.data.repo.internals.dao.RecentBookSearchDao @@ -31,6 +30,7 @@ import voice.core.playback.playstate.PlayStateManager import voice.core.scanner.DeviceHasStoragePermissionBug import voice.core.scanner.MediaScanTrigger import voice.core.search.BookSearch +import voice.core.ui.GridCount import voice.features.bookOverview.di.BookOverviewScope import voice.features.bookOverview.search.BookSearchViewState import voice.navigation.Destination diff --git a/features/settings/src/main/kotlin/voice/features/settings/SettingsViewModel.kt b/features/settings/src/main/kotlin/voice/features/settings/SettingsViewModel.kt index 7bedb7aae..72e81a02d 100644 --- a/features/settings/src/main/kotlin/voice/features/settings/SettingsViewModel.kt +++ b/features/settings/src/main/kotlin/voice/features/settings/SettingsViewModel.kt @@ -13,8 +13,7 @@ import kotlinx.coroutines.launch import voice.core.common.AppInfoProvider import voice.core.common.DispatcherProvider import voice.core.common.MainScope -import voice.core.common.grid.GridCount -import voice.core.common.grid.GridMode +import voice.core.data.GridMode import voice.core.data.sleeptimer.SleepTimerPreference import voice.core.data.store.AutoRewindAmountStore import voice.core.data.store.DarkThemeStore @@ -22,6 +21,7 @@ import voice.core.data.store.GridModeStore import voice.core.data.store.SeekTimeStore import voice.core.data.store.SleepTimerPreferenceStore import voice.core.ui.DARK_THEME_SETTABLE +import voice.core.ui.GridCount import voice.navigation.Destination import voice.navigation.Navigator import java.time.LocalTime From fc66b443c1cbd4bc87634d33b117a84a6eb1f8bb Mon Sep 17 00:00:00 2001 From: Paul Woitaschek Date: Tue, 26 Aug 2025 21:02:11 +0200 Subject: [PATCH 09/20] Move the UI extensions out of common --- .../src/main/kotlin/voice/core/ui/Dp.kt} | 2 +- .../src/main/kotlin/voice/features/cover/crop/CropOverlay.kt | 2 +- features/widget/build.gradle.kts | 1 + .../src/main/kotlin/voice/features/widget/WidgetUpdater.kt | 2 +- 4 files changed, 4 insertions(+), 3 deletions(-) rename core/{common/src/main/kotlin/voice/core/common/AndroidExtensions.kt => ui/src/main/kotlin/voice/core/ui/Dp.kt} (91%) diff --git a/core/common/src/main/kotlin/voice/core/common/AndroidExtensions.kt b/core/ui/src/main/kotlin/voice/core/ui/Dp.kt similarity index 91% rename from core/common/src/main/kotlin/voice/core/common/AndroidExtensions.kt rename to core/ui/src/main/kotlin/voice/core/ui/Dp.kt index dbc53fbf2..f787dd46f 100644 --- a/core/common/src/main/kotlin/voice/core/common/AndroidExtensions.kt +++ b/core/ui/src/main/kotlin/voice/core/ui/Dp.kt @@ -1,4 +1,4 @@ -package voice.core.common +package voice.core.ui import android.content.Context import android.util.TypedValue diff --git a/features/cover/src/main/kotlin/voice/features/cover/crop/CropOverlay.kt b/features/cover/src/main/kotlin/voice/features/cover/crop/CropOverlay.kt index 3dfe74766..cb5480710 100644 --- a/features/cover/src/main/kotlin/voice/features/cover/crop/CropOverlay.kt +++ b/features/cover/src/main/kotlin/voice/features/cover/crop/CropOverlay.kt @@ -14,7 +14,7 @@ import android.view.ScaleGestureDetector import android.view.View import android.widget.FrameLayout import androidx.core.view.isVisible -import voice.core.common.dpToPxRounded +import voice.core.ui.dpToPxRounded import voice.features.cover.R import kotlin.math.max import kotlin.math.min diff --git a/features/widget/build.gradle.kts b/features/widget/build.gradle.kts index b85dcd15a..760fcfaba 100644 --- a/features/widget/build.gradle.kts +++ b/features/widget/build.gradle.kts @@ -12,6 +12,7 @@ android { dependencies { implementation(projects.core.strings) implementation(projects.core.common) + implementation(projects.core.ui) implementation(projects.core.data.api) implementation(projects.core.playback) diff --git a/features/widget/src/main/kotlin/voice/features/widget/WidgetUpdater.kt b/features/widget/src/main/kotlin/voice/features/widget/WidgetUpdater.kt index 59b5e5884..3dc8fa7ca 100644 --- a/features/widget/src/main/kotlin/voice/features/widget/WidgetUpdater.kt +++ b/features/widget/src/main/kotlin/voice/features/widget/WidgetUpdater.kt @@ -19,7 +19,6 @@ import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.first import kotlinx.coroutines.launch import voice.app.features.widget.BaseWidgetProvider -import voice.core.common.dpToPxRounded import voice.core.data.Book import voice.core.data.BookId import voice.core.data.repo.BookRepository @@ -27,6 +26,7 @@ import voice.core.data.store.CurrentBookStore import voice.core.playback.notification.MainActivityIntentProvider import voice.core.playback.playstate.PlayStateManager import voice.core.playback.receiver.WidgetButtonReceiver +import voice.core.ui.dpToPxRounded import voice.core.common.R as CommonR @SingleIn(AppScope::class) From 753868fbc54c3257e358ef34511bc7e01d069109 Mon Sep 17 00:00:00 2001 From: Paul Woitaschek Date: Tue, 26 Aug 2025 21:04:43 +0200 Subject: [PATCH 10/20] Move the time formatting out of the common module --- app/src/test/kotlin/voice/app/misc/FormatTimeKtTest.kt | 2 +- .../common => ui/src/main/kotlin/voice/core/ui}/FormatTime.kt | 2 +- .../main/kotlin/voice/features/bookmark/BookmarkViewModel.kt | 2 +- .../main/kotlin/voice/features/playbackScreen/view/CoverRow.kt | 2 +- .../main/kotlin/voice/features/playbackScreen/view/SliderRow.kt | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) rename core/{common/src/main/kotlin/voice/core/common => ui/src/main/kotlin/voice/core/ui}/FormatTime.kt (97%) diff --git a/app/src/test/kotlin/voice/app/misc/FormatTimeKtTest.kt b/app/src/test/kotlin/voice/app/misc/FormatTimeKtTest.kt index 36c15ad26..a480ef9c6 100644 --- a/app/src/test/kotlin/voice/app/misc/FormatTimeKtTest.kt +++ b/app/src/test/kotlin/voice/app/misc/FormatTimeKtTest.kt @@ -2,7 +2,7 @@ package voice.app.misc import io.kotest.matchers.shouldBe import org.junit.Test -import voice.core.common.formatTime +import voice.core.ui.formatTime import java.util.concurrent.TimeUnit class FormatTimeKtTest { diff --git a/core/common/src/main/kotlin/voice/core/common/FormatTime.kt b/core/ui/src/main/kotlin/voice/core/ui/FormatTime.kt similarity index 97% rename from core/common/src/main/kotlin/voice/core/common/FormatTime.kt rename to core/ui/src/main/kotlin/voice/core/ui/FormatTime.kt index b34263cf6..0ae254ed6 100644 --- a/core/common/src/main/kotlin/voice/core/common/FormatTime.kt +++ b/core/ui/src/main/kotlin/voice/core/ui/FormatTime.kt @@ -1,4 +1,4 @@ -package voice.core.common +package voice.core.ui import java.util.concurrent.TimeUnit diff --git a/features/bookmark/src/main/kotlin/voice/features/bookmark/BookmarkViewModel.kt b/features/bookmark/src/main/kotlin/voice/features/bookmark/BookmarkViewModel.kt index bb4082fb7..ed4f2eb85 100644 --- a/features/bookmark/src/main/kotlin/voice/features/bookmark/BookmarkViewModel.kt +++ b/features/bookmark/src/main/kotlin/voice/features/bookmark/BookmarkViewModel.kt @@ -13,7 +13,6 @@ import dev.zacsweers.metro.AssistedFactory import dev.zacsweers.metro.Inject import kotlinx.coroutines.MainScope import kotlinx.coroutines.launch -import voice.core.common.formatTime import voice.core.data.BookId import voice.core.data.Bookmark import voice.core.data.Chapter @@ -24,6 +23,7 @@ import voice.core.data.store.CurrentBookStore import voice.core.playback.PlayerController import voice.core.playback.playstate.PlayStateManager import voice.core.strings.R +import voice.core.ui.formatTime import voice.navigation.Navigator import java.time.Instant import java.time.temporal.ChronoUnit diff --git a/features/playbackScreen/src/main/kotlin/voice/features/playbackScreen/view/CoverRow.kt b/features/playbackScreen/src/main/kotlin/voice/features/playbackScreen/view/CoverRow.kt index ea2579f83..9a92820af 100644 --- a/features/playbackScreen/src/main/kotlin/voice/features/playbackScreen/view/CoverRow.kt +++ b/features/playbackScreen/src/main/kotlin/voice/features/playbackScreen/view/CoverRow.kt @@ -10,8 +10,8 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.unit.dp -import voice.core.common.formatTime import voice.core.ui.ImmutableFile +import voice.core.ui.formatTime import kotlin.time.Duration @Composable diff --git a/features/playbackScreen/src/main/kotlin/voice/features/playbackScreen/view/SliderRow.kt b/features/playbackScreen/src/main/kotlin/voice/features/playbackScreen/view/SliderRow.kt index ad58d0834..235c72027 100644 --- a/features/playbackScreen/src/main/kotlin/voice/features/playbackScreen/view/SliderRow.kt +++ b/features/playbackScreen/src/main/kotlin/voice/features/playbackScreen/view/SliderRow.kt @@ -15,7 +15,7 @@ import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.unit.dp -import voice.core.common.formatTime +import voice.core.ui.formatTime import kotlin.time.Duration @Composable From 7030c49791b6a5b924def3ed1cf0434270ead8f5 Mon Sep 17 00:00:00 2001 From: Paul Woitaschek Date: Tue, 26 Aug 2025 21:07:59 +0200 Subject: [PATCH 11/20] For the main dispatcher, switch to Main.immediate --- app/src/main/kotlin/voice/app/di/AndroidModule.kt | 6 +----- .../main/kotlin/voice/core/common/DispatcherProvider.kt | 8 +++++--- .../voice/core/sleeptimer/AutoEnableSleepTimerTest.kt | 2 +- .../selectType/SelectFolderTypeViewModelTest.kt | 2 +- .../features/playbackScreen/BookPlayViewModelTest.kt | 2 +- 5 files changed, 9 insertions(+), 11 deletions(-) diff --git a/app/src/main/kotlin/voice/app/di/AndroidModule.kt b/app/src/main/kotlin/voice/app/di/AndroidModule.kt index b61c39227..219039f9c 100644 --- a/app/src/main/kotlin/voice/app/di/AndroidModule.kt +++ b/app/src/main/kotlin/voice/app/di/AndroidModule.kt @@ -8,7 +8,6 @@ import dev.zacsweers.metro.BindingContainer import dev.zacsweers.metro.ContributesTo import dev.zacsweers.metro.Provides import dev.zacsweers.metro.SingleIn -import kotlinx.coroutines.Dispatchers import kotlinx.serialization.json.Json import voice.app.misc.AppInfoProviderImpl import voice.app.misc.MainActivityIntentProviderImpl @@ -48,10 +47,7 @@ object AndroidModule { @Provides @SingleIn(AppScope::class) fun dispatcherProvider(): DispatcherProvider { - return DispatcherProvider( - main = Dispatchers.Main, - io = Dispatchers.IO, - ) + return DispatcherProvider() } @Provides diff --git a/core/common/src/main/kotlin/voice/core/common/DispatcherProvider.kt b/core/common/src/main/kotlin/voice/core/common/DispatcherProvider.kt index 700160d46..7b602d704 100644 --- a/core/common/src/main/kotlin/voice/core/common/DispatcherProvider.kt +++ b/core/common/src/main/kotlin/voice/core/common/DispatcherProvider.kt @@ -1,15 +1,17 @@ package voice.core.common import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.SupervisorJob import kotlin.coroutines.CoroutineContext data class DispatcherProvider( - val main: CoroutineContext, - val io: CoroutineContext, + val main: CoroutineContext = Dispatchers.Main, + val io: CoroutineContext = Dispatchers.IO, + val mainImmediate: CoroutineContext = Dispatchers.Main.immediate, ) @Suppress("FunctionName") fun MainScope(dispatcherProvider: DispatcherProvider): CoroutineScope { - return CoroutineScope(SupervisorJob() + dispatcherProvider.main) + return CoroutineScope(SupervisorJob() + dispatcherProvider.mainImmediate) } diff --git a/core/sleeptimer/impl/src/test/kotlin/voice/core/sleeptimer/AutoEnableSleepTimerTest.kt b/core/sleeptimer/impl/src/test/kotlin/voice/core/sleeptimer/AutoEnableSleepTimerTest.kt index 6c5cb17b5..3324eeef2 100644 --- a/core/sleeptimer/impl/src/test/kotlin/voice/core/sleeptimer/AutoEnableSleepTimerTest.kt +++ b/core/sleeptimer/impl/src/test/kotlin/voice/core/sleeptimer/AutoEnableSleepTimerTest.kt @@ -62,7 +62,7 @@ class AutoEnableSleepTimerTest { bookRepository = mockk(relaxUnitFun = true) currentBookStore = mockk>(relaxUnitFun = true) - val dispatcherProvider = DispatcherProvider(main = testDispatcher, testDispatcher) + val dispatcherProvider = DispatcherProvider(testDispatcher, testDispatcher, testDispatcher) autoEnableSleepTimer = AutoEnableSleepTimer( sleepTimerPreferenceStore = sleepTimerPreferenceStore, dispatcherProvider = dispatcherProvider, diff --git a/features/folderPicker/src/test/kotlin/voice/features/folderPicker/selectType/SelectFolderTypeViewModelTest.kt b/features/folderPicker/src/test/kotlin/voice/features/folderPicker/selectType/SelectFolderTypeViewModelTest.kt index 8a0a54ca1..01ef9109c 100644 --- a/features/folderPicker/src/test/kotlin/voice/features/folderPicker/selectType/SelectFolderTypeViewModelTest.kt +++ b/features/folderPicker/src/test/kotlin/voice/features/folderPicker/selectType/SelectFolderTypeViewModelTest.kt @@ -34,7 +34,7 @@ class SelectFolderTypeViewModelTest { newFile("audiobooks/SecondBook/2.mp3") } val viewModel = SelectFolderTypeViewModel( - dispatcherProvider = DispatcherProvider(coroutineContext, coroutineContext), + dispatcherProvider = DispatcherProvider(coroutineContext, coroutineContext, coroutineContext), audiobookFolders = mockk(), navigator = mockk(), documentFileFactory = FileBasedDocumentFactory, diff --git a/features/playbackScreen/src/test/kotlin/voice/features/playbackScreen/BookPlayViewModelTest.kt b/features/playbackScreen/src/test/kotlin/voice/features/playbackScreen/BookPlayViewModelTest.kt index b3f555ca0..5ebfd5595 100644 --- a/features/playbackScreen/src/test/kotlin/voice/features/playbackScreen/BookPlayViewModelTest.kt +++ b/features/playbackScreen/src/test/kotlin/voice/features/playbackScreen/BookPlayViewModelTest.kt @@ -72,7 +72,7 @@ class BookPlayViewModelTest { batteryOptimization = mockk(), sleepTimerPreferenceStore = sleepTimerDataStore, bookId = book.id, - dispatcherProvider = DispatcherProvider(scope.coroutineContext, scope.coroutineContext), + dispatcherProvider = DispatcherProvider(scope.coroutineContext, scope.coroutineContext, scope.coroutineContext), ) @Test From 3c765d533496cde3ae8b69f83d3ca1825595251c Mon Sep 17 00:00:00 2001 From: Paul Woitaschek Date: Tue, 26 Aug 2025 21:08:37 +0200 Subject: [PATCH 12/20] Fix the integration test --- .../androidTest/kotlin/voice/app/SleepTimerIntegrationTest.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/src/androidTest/kotlin/voice/app/SleepTimerIntegrationTest.kt b/app/src/androidTest/kotlin/voice/app/SleepTimerIntegrationTest.kt index d077bc1ec..6c40fa8a5 100644 --- a/app/src/androidTest/kotlin/voice/app/SleepTimerIntegrationTest.kt +++ b/app/src/androidTest/kotlin/voice/app/SleepTimerIntegrationTest.kt @@ -11,19 +11,19 @@ import kotlinx.coroutines.flow.first import kotlinx.coroutines.test.runTest import org.junit.Test import voice.core.common.rootGraphAs -import voice.core.common.sleepTimer.SleepTimerPreference import voice.core.data.BookContent import voice.core.data.BookId import voice.core.data.Chapter import voice.core.data.ChapterId import voice.core.data.repo.BookContentRepo import voice.core.data.repo.ChapterRepo +import voice.core.data.sleeptimer.SleepTimerPreference import voice.core.data.store.CurrentBookStore import voice.core.data.store.FadeOutStore import voice.core.data.store.SleepTimerPreferenceStore import voice.core.playback.PlayerController import voice.core.playback.playstate.PlayStateManager -import voice.core.playback.session.SleepTimer +import voice.core.sleeptimer.SleepTimer import java.io.File import java.time.Instant import java.util.UUID From 4b98b59db7a1591e0aa689f5cf65ed61d97b7f3a Mon Sep 17 00:00:00 2001 From: Paul Woitaschek Date: Tue, 26 Aug 2025 21:19:18 +0200 Subject: [PATCH 13/20] Move resources to core:ui --- core/common/build.gradle.kts | 2 -- core/playback/build.gradle.kts | 1 + core/ui/build.gradle.kts | 4 ++++ .../ui/src/main/kotlin/voice/core/ui/PlayButton.kt | 3 ++- .../main/res/color/gradient_launcher_finish.xml | 0 .../main/res/color/gradient_launcher_shadow.xml | 0 .../drawable-hdpi/ic_fast_forward_white_36dp.webp | Bin .../main/res/drawable-hdpi/ic_notification.webp | Bin .../res/drawable-hdpi/ic_pause_white_36dp.webp | Bin .../main/res/drawable-hdpi/ic_play_white_36dp.webp | Bin .../res/drawable-hdpi/ic_rewind_white_36dp.webp | Bin .../drawable-mdpi/ic_fast_forward_white_36dp.webp | Bin .../main/res/drawable-mdpi/ic_notification.webp | Bin .../res/drawable-mdpi/ic_pause_white_36dp.webp | Bin .../main/res/drawable-mdpi/ic_play_white_36dp.webp | Bin .../res/drawable-mdpi/ic_rewind_white_36dp.webp | Bin .../drawable-xhdpi/ic_fast_forward_white_36dp.webp | Bin .../main/res/drawable-xhdpi/ic_notification.webp | Bin .../res/drawable-xhdpi/ic_pause_white_36dp.webp | Bin .../res/drawable-xhdpi/ic_play_white_36dp.webp | Bin .../res/drawable-xhdpi/ic_rewind_white_36dp.webp | Bin .../ic_fast_forward_white_36dp.webp | Bin .../main/res/drawable-xxhdpi/ic_notification.webp | Bin .../res/drawable-xxhdpi/ic_pause_white_36dp.webp | Bin .../res/drawable-xxhdpi/ic_play_white_36dp.webp | Bin .../res/drawable-xxhdpi/ic_rewind_white_36dp.webp | Bin .../ic_fast_forward_white_36dp.webp | Bin .../main/res/drawable-xxxhdpi/ic_notification.webp | Bin .../res/drawable-xxxhdpi/ic_pause_white_36dp.webp | Bin .../res/drawable-xxxhdpi/ic_play_white_36dp.webp | Bin .../res/drawable-xxxhdpi/ic_rewind_white_36dp.webp | Bin .../src/main/res/drawable/album_art.xml | 0 .../src/main/res/drawable/avd_pause_to_play.xml | 0 .../src/main/res/values-night-v31/colors.xml | 0 .../src/main/res/values-night/colors.xml | 0 .../src/main/res/values-v31/colors.xml | 0 core/{common => ui}/src/main/res/values/colors.xml | 0 .../src/main/res/values/donottranslate.xml | 0 .../voice/features/bookOverview/views/GridBooks.kt | 5 +++-- .../voice/features/bookOverview/views/ListBooks.kt | 5 +++-- .../folderPicker/selectType/FolderModeBook.kt | 3 ++- .../voice/features/playbackScreen/view/Cover.kt | 5 +++-- .../kotlin/voice/features/widget/WidgetUpdater.kt | 13 +++++++------ 43 files changed, 25 insertions(+), 16 deletions(-) rename core/{common => ui}/src/main/res/color/gradient_launcher_finish.xml (100%) rename core/{common => ui}/src/main/res/color/gradient_launcher_shadow.xml (100%) rename core/{common => ui}/src/main/res/drawable-hdpi/ic_fast_forward_white_36dp.webp (100%) rename core/{common => ui}/src/main/res/drawable-hdpi/ic_notification.webp (100%) rename core/{common => ui}/src/main/res/drawable-hdpi/ic_pause_white_36dp.webp (100%) rename core/{common => ui}/src/main/res/drawable-hdpi/ic_play_white_36dp.webp (100%) rename core/{common => ui}/src/main/res/drawable-hdpi/ic_rewind_white_36dp.webp (100%) rename core/{common => ui}/src/main/res/drawable-mdpi/ic_fast_forward_white_36dp.webp (100%) rename core/{common => ui}/src/main/res/drawable-mdpi/ic_notification.webp (100%) rename core/{common => ui}/src/main/res/drawable-mdpi/ic_pause_white_36dp.webp (100%) rename core/{common => ui}/src/main/res/drawable-mdpi/ic_play_white_36dp.webp (100%) rename core/{common => ui}/src/main/res/drawable-mdpi/ic_rewind_white_36dp.webp (100%) rename core/{common => ui}/src/main/res/drawable-xhdpi/ic_fast_forward_white_36dp.webp (100%) rename core/{common => ui}/src/main/res/drawable-xhdpi/ic_notification.webp (100%) rename core/{common => ui}/src/main/res/drawable-xhdpi/ic_pause_white_36dp.webp (100%) rename core/{common => ui}/src/main/res/drawable-xhdpi/ic_play_white_36dp.webp (100%) rename core/{common => ui}/src/main/res/drawable-xhdpi/ic_rewind_white_36dp.webp (100%) rename core/{common => ui}/src/main/res/drawable-xxhdpi/ic_fast_forward_white_36dp.webp (100%) rename core/{common => ui}/src/main/res/drawable-xxhdpi/ic_notification.webp (100%) rename core/{common => ui}/src/main/res/drawable-xxhdpi/ic_pause_white_36dp.webp (100%) rename core/{common => ui}/src/main/res/drawable-xxhdpi/ic_play_white_36dp.webp (100%) rename core/{common => ui}/src/main/res/drawable-xxhdpi/ic_rewind_white_36dp.webp (100%) rename core/{common => ui}/src/main/res/drawable-xxxhdpi/ic_fast_forward_white_36dp.webp (100%) rename core/{common => ui}/src/main/res/drawable-xxxhdpi/ic_notification.webp (100%) rename core/{common => ui}/src/main/res/drawable-xxxhdpi/ic_pause_white_36dp.webp (100%) rename core/{common => ui}/src/main/res/drawable-xxxhdpi/ic_play_white_36dp.webp (100%) rename core/{common => ui}/src/main/res/drawable-xxxhdpi/ic_rewind_white_36dp.webp (100%) rename core/{common => ui}/src/main/res/drawable/album_art.xml (100%) rename core/{common => ui}/src/main/res/drawable/avd_pause_to_play.xml (100%) rename core/{common => ui}/src/main/res/values-night-v31/colors.xml (100%) rename core/{common => ui}/src/main/res/values-night/colors.xml (100%) rename core/{common => ui}/src/main/res/values-v31/colors.xml (100%) rename core/{common => ui}/src/main/res/values/colors.xml (100%) rename core/{common => ui}/src/main/res/values/donottranslate.xml (100%) diff --git a/core/common/build.gradle.kts b/core/common/build.gradle.kts index 0a7877eba..8a5c1a51a 100644 --- a/core/common/build.gradle.kts +++ b/core/common/build.gradle.kts @@ -21,10 +21,8 @@ dependencies { api(libs.navigation3.runtime) implementation(libs.serialization.json) - testImplementation(kotlin("reflect")) testImplementation(libs.junit) testImplementation(libs.androidX.test.core) testImplementation(libs.androidX.test.junit) testImplementation(libs.androidX.test.runner) - testImplementation(libs.robolectric) } diff --git a/core/playback/build.gradle.kts b/core/playback/build.gradle.kts index 4b285ac68..eed9a257b 100644 --- a/core/playback/build.gradle.kts +++ b/core/playback/build.gradle.kts @@ -12,6 +12,7 @@ android { dependencies { implementation(projects.core.common) + implementation(projects.core.ui) implementation(projects.core.strings) implementation(projects.core.data.api) implementation(projects.core.sleeptimer.api) diff --git a/core/ui/build.gradle.kts b/core/ui/build.gradle.kts index bfafed43e..406989d31 100644 --- a/core/ui/build.gradle.kts +++ b/core/ui/build.gradle.kts @@ -4,6 +4,10 @@ plugins { alias(libs.plugins.metro) } +android { + androidResources.enable = true +} + dependencies { implementation(projects.core.data.api) implementation(projects.core.strings) diff --git a/core/ui/src/main/kotlin/voice/core/ui/PlayButton.kt b/core/ui/src/main/kotlin/voice/core/ui/PlayButton.kt index ee1ced320..b1a8c04d9 100644 --- a/core/ui/src/main/kotlin/voice/core/ui/PlayButton.kt +++ b/core/ui/src/main/kotlin/voice/core/ui/PlayButton.kt @@ -15,6 +15,7 @@ import androidx.compose.ui.graphics.painter.Painter import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp +import voice.core.ui.R import voice.core.common.R as CommonR import voice.core.strings.R as StringsR @@ -53,7 +54,7 @@ fun PlayButton( private fun rememberPlayIconPainter(playing: Boolean): Painter { return rememberAnimatedVectorPainter( animatedImageVector = AnimatedImageVector.animatedVectorResource( - id = CommonR.drawable.avd_pause_to_play, + id = R.drawable.avd_pause_to_play, ), atEnd = !playing, ) diff --git a/core/common/src/main/res/color/gradient_launcher_finish.xml b/core/ui/src/main/res/color/gradient_launcher_finish.xml similarity index 100% rename from core/common/src/main/res/color/gradient_launcher_finish.xml rename to core/ui/src/main/res/color/gradient_launcher_finish.xml diff --git a/core/common/src/main/res/color/gradient_launcher_shadow.xml b/core/ui/src/main/res/color/gradient_launcher_shadow.xml similarity index 100% rename from core/common/src/main/res/color/gradient_launcher_shadow.xml rename to core/ui/src/main/res/color/gradient_launcher_shadow.xml diff --git a/core/common/src/main/res/drawable-hdpi/ic_fast_forward_white_36dp.webp b/core/ui/src/main/res/drawable-hdpi/ic_fast_forward_white_36dp.webp similarity index 100% rename from core/common/src/main/res/drawable-hdpi/ic_fast_forward_white_36dp.webp rename to core/ui/src/main/res/drawable-hdpi/ic_fast_forward_white_36dp.webp diff --git a/core/common/src/main/res/drawable-hdpi/ic_notification.webp b/core/ui/src/main/res/drawable-hdpi/ic_notification.webp similarity index 100% rename from core/common/src/main/res/drawable-hdpi/ic_notification.webp rename to core/ui/src/main/res/drawable-hdpi/ic_notification.webp diff --git a/core/common/src/main/res/drawable-hdpi/ic_pause_white_36dp.webp b/core/ui/src/main/res/drawable-hdpi/ic_pause_white_36dp.webp similarity index 100% rename from core/common/src/main/res/drawable-hdpi/ic_pause_white_36dp.webp rename to core/ui/src/main/res/drawable-hdpi/ic_pause_white_36dp.webp diff --git a/core/common/src/main/res/drawable-hdpi/ic_play_white_36dp.webp b/core/ui/src/main/res/drawable-hdpi/ic_play_white_36dp.webp similarity index 100% rename from core/common/src/main/res/drawable-hdpi/ic_play_white_36dp.webp rename to core/ui/src/main/res/drawable-hdpi/ic_play_white_36dp.webp diff --git a/core/common/src/main/res/drawable-hdpi/ic_rewind_white_36dp.webp b/core/ui/src/main/res/drawable-hdpi/ic_rewind_white_36dp.webp similarity index 100% rename from core/common/src/main/res/drawable-hdpi/ic_rewind_white_36dp.webp rename to core/ui/src/main/res/drawable-hdpi/ic_rewind_white_36dp.webp diff --git a/core/common/src/main/res/drawable-mdpi/ic_fast_forward_white_36dp.webp b/core/ui/src/main/res/drawable-mdpi/ic_fast_forward_white_36dp.webp similarity index 100% rename from core/common/src/main/res/drawable-mdpi/ic_fast_forward_white_36dp.webp rename to core/ui/src/main/res/drawable-mdpi/ic_fast_forward_white_36dp.webp diff --git a/core/common/src/main/res/drawable-mdpi/ic_notification.webp b/core/ui/src/main/res/drawable-mdpi/ic_notification.webp similarity index 100% rename from core/common/src/main/res/drawable-mdpi/ic_notification.webp rename to core/ui/src/main/res/drawable-mdpi/ic_notification.webp diff --git a/core/common/src/main/res/drawable-mdpi/ic_pause_white_36dp.webp b/core/ui/src/main/res/drawable-mdpi/ic_pause_white_36dp.webp similarity index 100% rename from core/common/src/main/res/drawable-mdpi/ic_pause_white_36dp.webp rename to core/ui/src/main/res/drawable-mdpi/ic_pause_white_36dp.webp diff --git a/core/common/src/main/res/drawable-mdpi/ic_play_white_36dp.webp b/core/ui/src/main/res/drawable-mdpi/ic_play_white_36dp.webp similarity index 100% rename from core/common/src/main/res/drawable-mdpi/ic_play_white_36dp.webp rename to core/ui/src/main/res/drawable-mdpi/ic_play_white_36dp.webp diff --git a/core/common/src/main/res/drawable-mdpi/ic_rewind_white_36dp.webp b/core/ui/src/main/res/drawable-mdpi/ic_rewind_white_36dp.webp similarity index 100% rename from core/common/src/main/res/drawable-mdpi/ic_rewind_white_36dp.webp rename to core/ui/src/main/res/drawable-mdpi/ic_rewind_white_36dp.webp diff --git a/core/common/src/main/res/drawable-xhdpi/ic_fast_forward_white_36dp.webp b/core/ui/src/main/res/drawable-xhdpi/ic_fast_forward_white_36dp.webp similarity index 100% rename from core/common/src/main/res/drawable-xhdpi/ic_fast_forward_white_36dp.webp rename to core/ui/src/main/res/drawable-xhdpi/ic_fast_forward_white_36dp.webp diff --git a/core/common/src/main/res/drawable-xhdpi/ic_notification.webp b/core/ui/src/main/res/drawable-xhdpi/ic_notification.webp similarity index 100% rename from core/common/src/main/res/drawable-xhdpi/ic_notification.webp rename to core/ui/src/main/res/drawable-xhdpi/ic_notification.webp diff --git a/core/common/src/main/res/drawable-xhdpi/ic_pause_white_36dp.webp b/core/ui/src/main/res/drawable-xhdpi/ic_pause_white_36dp.webp similarity index 100% rename from core/common/src/main/res/drawable-xhdpi/ic_pause_white_36dp.webp rename to core/ui/src/main/res/drawable-xhdpi/ic_pause_white_36dp.webp diff --git a/core/common/src/main/res/drawable-xhdpi/ic_play_white_36dp.webp b/core/ui/src/main/res/drawable-xhdpi/ic_play_white_36dp.webp similarity index 100% rename from core/common/src/main/res/drawable-xhdpi/ic_play_white_36dp.webp rename to core/ui/src/main/res/drawable-xhdpi/ic_play_white_36dp.webp diff --git a/core/common/src/main/res/drawable-xhdpi/ic_rewind_white_36dp.webp b/core/ui/src/main/res/drawable-xhdpi/ic_rewind_white_36dp.webp similarity index 100% rename from core/common/src/main/res/drawable-xhdpi/ic_rewind_white_36dp.webp rename to core/ui/src/main/res/drawable-xhdpi/ic_rewind_white_36dp.webp diff --git a/core/common/src/main/res/drawable-xxhdpi/ic_fast_forward_white_36dp.webp b/core/ui/src/main/res/drawable-xxhdpi/ic_fast_forward_white_36dp.webp similarity index 100% rename from core/common/src/main/res/drawable-xxhdpi/ic_fast_forward_white_36dp.webp rename to core/ui/src/main/res/drawable-xxhdpi/ic_fast_forward_white_36dp.webp diff --git a/core/common/src/main/res/drawable-xxhdpi/ic_notification.webp b/core/ui/src/main/res/drawable-xxhdpi/ic_notification.webp similarity index 100% rename from core/common/src/main/res/drawable-xxhdpi/ic_notification.webp rename to core/ui/src/main/res/drawable-xxhdpi/ic_notification.webp diff --git a/core/common/src/main/res/drawable-xxhdpi/ic_pause_white_36dp.webp b/core/ui/src/main/res/drawable-xxhdpi/ic_pause_white_36dp.webp similarity index 100% rename from core/common/src/main/res/drawable-xxhdpi/ic_pause_white_36dp.webp rename to core/ui/src/main/res/drawable-xxhdpi/ic_pause_white_36dp.webp diff --git a/core/common/src/main/res/drawable-xxhdpi/ic_play_white_36dp.webp b/core/ui/src/main/res/drawable-xxhdpi/ic_play_white_36dp.webp similarity index 100% rename from core/common/src/main/res/drawable-xxhdpi/ic_play_white_36dp.webp rename to core/ui/src/main/res/drawable-xxhdpi/ic_play_white_36dp.webp diff --git a/core/common/src/main/res/drawable-xxhdpi/ic_rewind_white_36dp.webp b/core/ui/src/main/res/drawable-xxhdpi/ic_rewind_white_36dp.webp similarity index 100% rename from core/common/src/main/res/drawable-xxhdpi/ic_rewind_white_36dp.webp rename to core/ui/src/main/res/drawable-xxhdpi/ic_rewind_white_36dp.webp diff --git a/core/common/src/main/res/drawable-xxxhdpi/ic_fast_forward_white_36dp.webp b/core/ui/src/main/res/drawable-xxxhdpi/ic_fast_forward_white_36dp.webp similarity index 100% rename from core/common/src/main/res/drawable-xxxhdpi/ic_fast_forward_white_36dp.webp rename to core/ui/src/main/res/drawable-xxxhdpi/ic_fast_forward_white_36dp.webp diff --git a/core/common/src/main/res/drawable-xxxhdpi/ic_notification.webp b/core/ui/src/main/res/drawable-xxxhdpi/ic_notification.webp similarity index 100% rename from core/common/src/main/res/drawable-xxxhdpi/ic_notification.webp rename to core/ui/src/main/res/drawable-xxxhdpi/ic_notification.webp diff --git a/core/common/src/main/res/drawable-xxxhdpi/ic_pause_white_36dp.webp b/core/ui/src/main/res/drawable-xxxhdpi/ic_pause_white_36dp.webp similarity index 100% rename from core/common/src/main/res/drawable-xxxhdpi/ic_pause_white_36dp.webp rename to core/ui/src/main/res/drawable-xxxhdpi/ic_pause_white_36dp.webp diff --git a/core/common/src/main/res/drawable-xxxhdpi/ic_play_white_36dp.webp b/core/ui/src/main/res/drawable-xxxhdpi/ic_play_white_36dp.webp similarity index 100% rename from core/common/src/main/res/drawable-xxxhdpi/ic_play_white_36dp.webp rename to core/ui/src/main/res/drawable-xxxhdpi/ic_play_white_36dp.webp diff --git a/core/common/src/main/res/drawable-xxxhdpi/ic_rewind_white_36dp.webp b/core/ui/src/main/res/drawable-xxxhdpi/ic_rewind_white_36dp.webp similarity index 100% rename from core/common/src/main/res/drawable-xxxhdpi/ic_rewind_white_36dp.webp rename to core/ui/src/main/res/drawable-xxxhdpi/ic_rewind_white_36dp.webp diff --git a/core/common/src/main/res/drawable/album_art.xml b/core/ui/src/main/res/drawable/album_art.xml similarity index 100% rename from core/common/src/main/res/drawable/album_art.xml rename to core/ui/src/main/res/drawable/album_art.xml diff --git a/core/common/src/main/res/drawable/avd_pause_to_play.xml b/core/ui/src/main/res/drawable/avd_pause_to_play.xml similarity index 100% rename from core/common/src/main/res/drawable/avd_pause_to_play.xml rename to core/ui/src/main/res/drawable/avd_pause_to_play.xml diff --git a/core/common/src/main/res/values-night-v31/colors.xml b/core/ui/src/main/res/values-night-v31/colors.xml similarity index 100% rename from core/common/src/main/res/values-night-v31/colors.xml rename to core/ui/src/main/res/values-night-v31/colors.xml diff --git a/core/common/src/main/res/values-night/colors.xml b/core/ui/src/main/res/values-night/colors.xml similarity index 100% rename from core/common/src/main/res/values-night/colors.xml rename to core/ui/src/main/res/values-night/colors.xml diff --git a/core/common/src/main/res/values-v31/colors.xml b/core/ui/src/main/res/values-v31/colors.xml similarity index 100% rename from core/common/src/main/res/values-v31/colors.xml rename to core/ui/src/main/res/values-v31/colors.xml diff --git a/core/common/src/main/res/values/colors.xml b/core/ui/src/main/res/values/colors.xml similarity index 100% rename from core/common/src/main/res/values/colors.xml rename to core/ui/src/main/res/values/colors.xml diff --git a/core/common/src/main/res/values/donottranslate.xml b/core/ui/src/main/res/values/donottranslate.xml similarity index 100% rename from core/common/src/main/res/values/donottranslate.xml rename to core/ui/src/main/res/values/donottranslate.xml diff --git a/features/bookOverview/src/main/kotlin/voice/features/bookOverview/views/GridBooks.kt b/features/bookOverview/src/main/kotlin/voice/features/bookOverview/views/GridBooks.kt index 516a6b515..6b49a4a2b 100644 --- a/features/bookOverview/src/main/kotlin/voice/features/bookOverview/views/GridBooks.kt +++ b/features/bookOverview/src/main/kotlin/voice/features/bookOverview/views/GridBooks.kt @@ -35,6 +35,7 @@ import voice.features.bookOverview.overview.BookOverviewCategory import voice.features.bookOverview.overview.BookOverviewItemViewState import kotlin.math.roundToInt import voice.core.common.R as CommonR +import voice.core.ui.R as UiR @Composable internal fun GridBooks( @@ -116,8 +117,8 @@ internal fun GridBook( .clip(RoundedCornerShape(8.dp)), contentScale = ContentScale.Crop, model = book.cover?.file, - placeholder = painterResource(id = CommonR.drawable.album_art), - error = painterResource(id = CommonR.drawable.album_art), + placeholder = painterResource(id = UiR.drawable.album_art), + error = painterResource(id = UiR.drawable.album_art), contentDescription = null, ) Text( diff --git a/features/bookOverview/src/main/kotlin/voice/features/bookOverview/views/ListBooks.kt b/features/bookOverview/src/main/kotlin/voice/features/bookOverview/views/ListBooks.kt index 188e9da57..88af08eac 100644 --- a/features/bookOverview/src/main/kotlin/voice/features/bookOverview/views/ListBooks.kt +++ b/features/bookOverview/src/main/kotlin/voice/features/bookOverview/views/ListBooks.kt @@ -35,6 +35,7 @@ import voice.core.ui.ImmutableFile import voice.features.bookOverview.overview.BookOverviewCategory import voice.features.bookOverview.overview.BookOverviewItemViewState import voice.core.common.R as CommonR +import voice.core.ui.R as UiR @Composable internal fun ListBooks( @@ -149,8 +150,8 @@ private fun CoverImage(cover: ImmutableFile?) { .size(76.dp) .clip(RoundedCornerShape(8.dp)), model = cover?.file, - placeholder = painterResource(id = CommonR.drawable.album_art), - error = painterResource(id = CommonR.drawable.album_art), + placeholder = painterResource(id = UiR.drawable.album_art), + error = painterResource(id = UiR.drawable.album_art), contentDescription = null, ) } diff --git a/features/folderPicker/src/main/kotlin/voice/features/folderPicker/selectType/FolderModeBook.kt b/features/folderPicker/src/main/kotlin/voice/features/folderPicker/selectType/FolderModeBook.kt index 819848c1b..061d451af 100644 --- a/features/folderPicker/src/main/kotlin/voice/features/folderPicker/selectType/FolderModeBook.kt +++ b/features/folderPicker/src/main/kotlin/voice/features/folderPicker/selectType/FolderModeBook.kt @@ -17,6 +17,7 @@ import androidx.compose.ui.res.pluralStringResource import androidx.compose.ui.unit.dp import voice.core.common.R as CommonR import voice.core.strings.R as StringsR +import voice.core.ui.R as UiR @Composable internal fun FolderModeBook( @@ -31,7 +32,7 @@ internal fun FolderModeBook( .padding(start = 8.dp, end = 8.dp, top = 8.dp) .clip(RoundedCornerShape(8.dp)), contentScale = ContentScale.Crop, - painter = painterResource(id = CommonR.drawable.album_art), + painter = painterResource(id = UiR.drawable.album_art), contentDescription = null, ) Text( diff --git a/features/playbackScreen/src/main/kotlin/voice/features/playbackScreen/view/Cover.kt b/features/playbackScreen/src/main/kotlin/voice/features/playbackScreen/view/Cover.kt index f6f36ab63..9bcd79114 100644 --- a/features/playbackScreen/src/main/kotlin/voice/features/playbackScreen/view/Cover.kt +++ b/features/playbackScreen/src/main/kotlin/voice/features/playbackScreen/view/Cover.kt @@ -15,6 +15,7 @@ import coil.compose.AsyncImage import voice.core.common.R import voice.core.ui.ImmutableFile import voice.core.strings.R as StringsR +import voice.core.ui.R as UiR @Composable internal fun Cover( @@ -34,8 +35,8 @@ internal fun Cover( .clip(RoundedCornerShape(20.dp)), contentScale = ContentScale.Crop, model = cover?.file, - placeholder = painterResource(id = R.drawable.album_art), - error = painterResource(id = R.drawable.album_art), + placeholder = painterResource(id = UiR.drawable.album_art), + error = painterResource(id = UiR.drawable.album_art), contentDescription = stringResource(id = StringsR.string.cover), ) } diff --git a/features/widget/src/main/kotlin/voice/features/widget/WidgetUpdater.kt b/features/widget/src/main/kotlin/voice/features/widget/WidgetUpdater.kt index 3dc8fa7ca..3855b2e09 100644 --- a/features/widget/src/main/kotlin/voice/features/widget/WidgetUpdater.kt +++ b/features/widget/src/main/kotlin/voice/features/widget/WidgetUpdater.kt @@ -28,6 +28,7 @@ import voice.core.playback.playstate.PlayStateManager import voice.core.playback.receiver.WidgetButtonReceiver import voice.core.ui.dpToPxRounded import voice.core.common.R as CommonR +import voice.core.ui.R as UiR @SingleIn(AppScope::class) @Inject @@ -109,7 +110,7 @@ class WidgetUpdater( private fun initWidgetForAbsentBook(widgetId: Int) { val remoteViews = RemoteViews(context.packageName, R.layout.widget) val wholeWidgetClickPI = mainActivityIntentProvider.toCurrentBook() - remoteViews.setImageViewResource(R.id.imageView, CommonR.drawable.album_art) + remoteViews.setImageViewResource(R.id.imageView, UiR.drawable.album_art) remoteViews.setOnClickPendingIntent(R.id.wholeWidget, wholeWidgetClickPI) appWidgetManager.updateAppWidget(widgetId, remoteViews) } @@ -135,9 +136,9 @@ class WidgetUpdater( remoteViews.setOnClickPendingIntent(R.id.rewind, rewindPI) val playIcon = if (playStateManager.playState == PlayStateManager.PlayState.Playing) { - CommonR.drawable.ic_pause_white_36dp + UiR.drawable.ic_pause_white_36dp } else { - CommonR.drawable.ic_play_white_36dp + UiR.drawable.ic_play_white_36dp } remoteViews.setImageViewResource(R.id.playPause, playIcon) @@ -157,15 +158,15 @@ class WidgetUpdater( ImageRequest.Builder(context) .data(coverFile) .size(coverSize, coverSize) - .fallback(CommonR.drawable.album_art) - .error(CommonR.drawable.album_art) + .fallback(UiR.drawable.album_art) + .error(UiR.drawable.album_art) .allowHardware(false) .build(), ) .drawable!!.toBitmap() remoteViews.setImageViewBitmap(R.id.imageView, bitmap) } else { - remoteViews.setImageViewResource(R.id.imageView, CommonR.drawable.album_art) + remoteViews.setImageViewResource(R.id.imageView, UiR.drawable.album_art) } remoteViews.setOnClickPendingIntent(R.id.wholeWidget, wholeWidgetClickPI) From 5951d3cd593b1ed07cdcd4d0224a46df3c0289ea Mon Sep 17 00:00:00 2001 From: Paul Woitaschek Date: Tue, 26 Aug 2025 21:29:01 +0200 Subject: [PATCH 14/20] Reduce dependencies that common declares --- core/common/build.gradle.kts | 22 ++----------------- core/data/impl/build.gradle.kts | 1 + core/ui/build.gradle.kts | 1 + .../main/kotlin/voice/core/ui/PlayButton.kt | 2 -- features/bookOverview/build.gradle.kts | 1 + .../features/bookOverview/views/GridBooks.kt | 1 - .../features/bookOverview/views/ListBooks.kt | 1 - .../folderPicker/selectType/FolderModeBook.kt | 1 - .../features/playbackScreen/view/Cover.kt | 1 - features/review/play/build.gradle.kts | 2 ++ .../voice/features/widget/WidgetUpdater.kt | 1 - 11 files changed, 7 insertions(+), 27 deletions(-) diff --git a/core/common/build.gradle.kts b/core/common/build.gradle.kts index 8a5c1a51a..1509452e8 100644 --- a/core/common/build.gradle.kts +++ b/core/common/build.gradle.kts @@ -1,28 +1,10 @@ plugins { id("voice.library") - id("voice.compose") - alias(libs.plugins.metro) - alias(libs.plugins.kotlin.serialization) -} - -android { - androidResources { - enable = true - } } dependencies { - implementation(projects.core.strings) - implementation(libs.appCompat) - implementation(libs.material) - api(libs.immutable) - api(libs.datastore) - implementation(libs.androidxCore) - api(libs.navigation3.runtime) implementation(libs.serialization.json) + implementation(libs.androidxCore) - testImplementation(libs.junit) - testImplementation(libs.androidX.test.core) - testImplementation(libs.androidX.test.junit) - testImplementation(libs.androidX.test.runner) + testImplementation(libs.bundles.testing.jvm) } diff --git a/core/data/impl/build.gradle.kts b/core/data/impl/build.gradle.kts index 10f518e62..252bab7fe 100644 --- a/core/data/impl/build.gradle.kts +++ b/core/data/impl/build.gradle.kts @@ -33,6 +33,7 @@ dependencies { api(projects.core.common) api(projects.core.documentfile) + implementation(libs.datastore) implementation(libs.androidxCore) implementation(libs.serialization.json) implementation(libs.coroutines.core) diff --git a/core/ui/build.gradle.kts b/core/ui/build.gradle.kts index 406989d31..8af33403b 100644 --- a/core/ui/build.gradle.kts +++ b/core/ui/build.gradle.kts @@ -9,6 +9,7 @@ android { } dependencies { + api(libs.datastore) implementation(projects.core.data.api) implementation(projects.core.strings) implementation(libs.lifecycle.viewmodel.compose) diff --git a/core/ui/src/main/kotlin/voice/core/ui/PlayButton.kt b/core/ui/src/main/kotlin/voice/core/ui/PlayButton.kt index b1a8c04d9..fab2d4dfd 100644 --- a/core/ui/src/main/kotlin/voice/core/ui/PlayButton.kt +++ b/core/ui/src/main/kotlin/voice/core/ui/PlayButton.kt @@ -15,8 +15,6 @@ import androidx.compose.ui.graphics.painter.Painter import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp -import voice.core.ui.R -import voice.core.common.R as CommonR import voice.core.strings.R as StringsR @Composable diff --git a/features/bookOverview/build.gradle.kts b/features/bookOverview/build.gradle.kts index 3a6dae951..033905f17 100644 --- a/features/bookOverview/build.gradle.kts +++ b/features/bookOverview/build.gradle.kts @@ -15,6 +15,7 @@ dependencies { implementation(projects.core.scanner) implementation(libs.lifecycle) + api(libs.immutable) implementation(libs.documentFile) implementation(libs.datastore) diff --git a/features/bookOverview/src/main/kotlin/voice/features/bookOverview/views/GridBooks.kt b/features/bookOverview/src/main/kotlin/voice/features/bookOverview/views/GridBooks.kt index 6b49a4a2b..9806012f2 100644 --- a/features/bookOverview/src/main/kotlin/voice/features/bookOverview/views/GridBooks.kt +++ b/features/bookOverview/src/main/kotlin/voice/features/bookOverview/views/GridBooks.kt @@ -34,7 +34,6 @@ import voice.core.data.BookId import voice.features.bookOverview.overview.BookOverviewCategory import voice.features.bookOverview.overview.BookOverviewItemViewState import kotlin.math.roundToInt -import voice.core.common.R as CommonR import voice.core.ui.R as UiR @Composable diff --git a/features/bookOverview/src/main/kotlin/voice/features/bookOverview/views/ListBooks.kt b/features/bookOverview/src/main/kotlin/voice/features/bookOverview/views/ListBooks.kt index 88af08eac..1e70c21e8 100644 --- a/features/bookOverview/src/main/kotlin/voice/features/bookOverview/views/ListBooks.kt +++ b/features/bookOverview/src/main/kotlin/voice/features/bookOverview/views/ListBooks.kt @@ -34,7 +34,6 @@ import voice.core.data.BookId import voice.core.ui.ImmutableFile import voice.features.bookOverview.overview.BookOverviewCategory import voice.features.bookOverview.overview.BookOverviewItemViewState -import voice.core.common.R as CommonR import voice.core.ui.R as UiR @Composable diff --git a/features/folderPicker/src/main/kotlin/voice/features/folderPicker/selectType/FolderModeBook.kt b/features/folderPicker/src/main/kotlin/voice/features/folderPicker/selectType/FolderModeBook.kt index 061d451af..62456f1c6 100644 --- a/features/folderPicker/src/main/kotlin/voice/features/folderPicker/selectType/FolderModeBook.kt +++ b/features/folderPicker/src/main/kotlin/voice/features/folderPicker/selectType/FolderModeBook.kt @@ -15,7 +15,6 @@ import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.pluralStringResource import androidx.compose.ui.unit.dp -import voice.core.common.R as CommonR import voice.core.strings.R as StringsR import voice.core.ui.R as UiR diff --git a/features/playbackScreen/src/main/kotlin/voice/features/playbackScreen/view/Cover.kt b/features/playbackScreen/src/main/kotlin/voice/features/playbackScreen/view/Cover.kt index 9bcd79114..9fff85e41 100644 --- a/features/playbackScreen/src/main/kotlin/voice/features/playbackScreen/view/Cover.kt +++ b/features/playbackScreen/src/main/kotlin/voice/features/playbackScreen/view/Cover.kt @@ -12,7 +12,6 @@ import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp import coil.compose.AsyncImage -import voice.core.common.R import voice.core.ui.ImmutableFile import voice.core.strings.R as StringsR import voice.core.ui.R as UiR diff --git a/features/review/play/build.gradle.kts b/features/review/play/build.gradle.kts index 051f30d9d..3e6b45af9 100644 --- a/features/review/play/build.gradle.kts +++ b/features/review/play/build.gradle.kts @@ -14,6 +14,8 @@ dependencies { api(libs.review) implementation(libs.lottie) + implementation(libs.datastore) + implementation(projects.core.common) implementation(projects.core.data.api) implementation(projects.core.playback) diff --git a/features/widget/src/main/kotlin/voice/features/widget/WidgetUpdater.kt b/features/widget/src/main/kotlin/voice/features/widget/WidgetUpdater.kt index 3855b2e09..def8ee6b3 100644 --- a/features/widget/src/main/kotlin/voice/features/widget/WidgetUpdater.kt +++ b/features/widget/src/main/kotlin/voice/features/widget/WidgetUpdater.kt @@ -27,7 +27,6 @@ import voice.core.playback.notification.MainActivityIntentProvider import voice.core.playback.playstate.PlayStateManager import voice.core.playback.receiver.WidgetButtonReceiver import voice.core.ui.dpToPxRounded -import voice.core.common.R as CommonR import voice.core.ui.R as UiR @SingleIn(AppScope::class) From f38478cc9abd1a829419608a8df42fcc9aac07c0 Mon Sep 17 00:00:00 2001 From: Paul Woitaschek Date: Tue, 26 Aug 2025 21:34:24 +0200 Subject: [PATCH 15/20] Declare the dependency on material for the resources --- core/ui/build.gradle.kts | 1 + 1 file changed, 1 insertion(+) diff --git a/core/ui/build.gradle.kts b/core/ui/build.gradle.kts index 8af33403b..6d83de24d 100644 --- a/core/ui/build.gradle.kts +++ b/core/ui/build.gradle.kts @@ -10,6 +10,7 @@ android { dependencies { api(libs.datastore) + implementation(libs.material) implementation(projects.core.data.api) implementation(projects.core.strings) implementation(libs.lifecycle.viewmodel.compose) From 6366378208ff83541c895b26b66c8522a4baa224 Mon Sep 17 00:00:00 2001 From: Paul Woitaschek Date: Tue, 26 Aug 2025 21:42:57 +0200 Subject: [PATCH 16/20] More dependency reductions --- core/documentfile/build.gradle.kts | 1 - core/playback/build.gradle.kts | 2 -- .../src/main/res/drawable-hdpi/ic_notification.webp | Bin .../src/main/res/drawable-mdpi/ic_notification.webp | Bin .../main/res/drawable-xhdpi/ic_notification.webp | Bin .../main/res/drawable-xxhdpi/ic_notification.webp | Bin .../main/res/drawable-xxxhdpi/ic_notification.webp | Bin core/remoteconfig/firebase/build.gradle.kts | 1 - core/remoteconfig/noop/build.gradle.kts | 1 - core/scanner/build.gradle.kts | 1 - features/sleepTimer/build.gradle.kts | 1 - 11 files changed, 7 deletions(-) rename core/{ui => playback}/src/main/res/drawable-hdpi/ic_notification.webp (100%) rename core/{ui => playback}/src/main/res/drawable-mdpi/ic_notification.webp (100%) rename core/{ui => playback}/src/main/res/drawable-xhdpi/ic_notification.webp (100%) rename core/{ui => playback}/src/main/res/drawable-xxhdpi/ic_notification.webp (100%) rename core/{ui => playback}/src/main/res/drawable-xxxhdpi/ic_notification.webp (100%) diff --git a/core/documentfile/build.gradle.kts b/core/documentfile/build.gradle.kts index bdf69fd71..27f22b63f 100644 --- a/core/documentfile/build.gradle.kts +++ b/core/documentfile/build.gradle.kts @@ -4,6 +4,5 @@ plugins { } dependencies { - implementation(projects.core.common) implementation(libs.androidxCore) } diff --git a/core/playback/build.gradle.kts b/core/playback/build.gradle.kts index eed9a257b..24c07ab41 100644 --- a/core/playback/build.gradle.kts +++ b/core/playback/build.gradle.kts @@ -12,10 +12,8 @@ android { dependencies { implementation(projects.core.common) - implementation(projects.core.ui) implementation(projects.core.strings) implementation(projects.core.data.api) - implementation(projects.core.sleeptimer.api) implementation(libs.androidxCore) implementation(libs.datastore) diff --git a/core/ui/src/main/res/drawable-hdpi/ic_notification.webp b/core/playback/src/main/res/drawable-hdpi/ic_notification.webp similarity index 100% rename from core/ui/src/main/res/drawable-hdpi/ic_notification.webp rename to core/playback/src/main/res/drawable-hdpi/ic_notification.webp diff --git a/core/ui/src/main/res/drawable-mdpi/ic_notification.webp b/core/playback/src/main/res/drawable-mdpi/ic_notification.webp similarity index 100% rename from core/ui/src/main/res/drawable-mdpi/ic_notification.webp rename to core/playback/src/main/res/drawable-mdpi/ic_notification.webp diff --git a/core/ui/src/main/res/drawable-xhdpi/ic_notification.webp b/core/playback/src/main/res/drawable-xhdpi/ic_notification.webp similarity index 100% rename from core/ui/src/main/res/drawable-xhdpi/ic_notification.webp rename to core/playback/src/main/res/drawable-xhdpi/ic_notification.webp diff --git a/core/ui/src/main/res/drawable-xxhdpi/ic_notification.webp b/core/playback/src/main/res/drawable-xxhdpi/ic_notification.webp similarity index 100% rename from core/ui/src/main/res/drawable-xxhdpi/ic_notification.webp rename to core/playback/src/main/res/drawable-xxhdpi/ic_notification.webp diff --git a/core/ui/src/main/res/drawable-xxxhdpi/ic_notification.webp b/core/playback/src/main/res/drawable-xxxhdpi/ic_notification.webp similarity index 100% rename from core/ui/src/main/res/drawable-xxxhdpi/ic_notification.webp rename to core/playback/src/main/res/drawable-xxxhdpi/ic_notification.webp diff --git a/core/remoteconfig/firebase/build.gradle.kts b/core/remoteconfig/firebase/build.gradle.kts index 1c3d3c51b..4b7c2ecd0 100644 --- a/core/remoteconfig/firebase/build.gradle.kts +++ b/core/remoteconfig/firebase/build.gradle.kts @@ -5,6 +5,5 @@ plugins { dependencies { implementation(projects.core.remoteconfig.core) - implementation(projects.core.common) implementation(libs.firebase.remoteconfig) } diff --git a/core/remoteconfig/noop/build.gradle.kts b/core/remoteconfig/noop/build.gradle.kts index 268a6cc3f..146240e6d 100644 --- a/core/remoteconfig/noop/build.gradle.kts +++ b/core/remoteconfig/noop/build.gradle.kts @@ -5,5 +5,4 @@ plugins { dependencies { implementation(projects.core.remoteconfig.core) - implementation(projects.core.common) } diff --git a/core/scanner/build.gradle.kts b/core/scanner/build.gradle.kts index dbdbabf2e..3758a09ed 100644 --- a/core/scanner/build.gradle.kts +++ b/core/scanner/build.gradle.kts @@ -9,7 +9,6 @@ kotlin { dependencies { implementation(projects.core.data.api) - implementation(projects.core.common) implementation(libs.slf4j.noop) implementation(libs.jebml) diff --git a/features/sleepTimer/build.gradle.kts b/features/sleepTimer/build.gradle.kts index 953dac6eb..9a8f00996 100644 --- a/features/sleepTimer/build.gradle.kts +++ b/features/sleepTimer/build.gradle.kts @@ -5,7 +5,6 @@ plugins { dependencies { implementation(projects.core.strings) - implementation(projects.core.data.api) implementation(libs.materialDialog.core) implementation(libs.material) From f5898c02a1da2058eb41549bec15349cb783f04f Mon Sep 17 00:00:00 2001 From: Paul Woitaschek Date: Tue, 26 Aug 2025 21:44:30 +0200 Subject: [PATCH 17/20] Remove material dialog --- app/build.gradle.kts | 2 -- features/bookmark/build.gradle.kts | 3 --- features/folderPicker/build.gradle.kts | 1 - features/playbackScreen/build.gradle.kts | 1 - features/settings/build.gradle.kts | 1 - features/sleepTimer/build.gradle.kts | 1 - gradle/libs.versions.toml | 4 ---- 7 files changed, 13 deletions(-) diff --git a/app/build.gradle.kts b/app/build.gradle.kts index ec2053136..9363877d4 100755 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -177,8 +177,6 @@ dependencies { implementation(libs.serialization.json) - implementation(libs.materialDialog.core) - implementation(libs.materialDialog.input) implementation(libs.coil) if (includeProprietaryLibraries()) { diff --git a/features/bookmark/build.gradle.kts b/features/bookmark/build.gradle.kts index 7bda0ab8e..81d96ddcb 100644 --- a/features/bookmark/build.gradle.kts +++ b/features/bookmark/build.gradle.kts @@ -11,7 +11,4 @@ dependencies { implementation(projects.core.playback) implementation(projects.navigation) implementation(projects.core.data.api) - - implementation(libs.materialDialog.core) - implementation(libs.materialDialog.input) } diff --git a/features/folderPicker/build.gradle.kts b/features/folderPicker/build.gradle.kts index 2f5e2d529..dee711741 100644 --- a/features/folderPicker/build.gradle.kts +++ b/features/folderPicker/build.gradle.kts @@ -21,7 +21,6 @@ dependencies { implementation(libs.datastore) implementation(libs.coil) - implementation(libs.materialDialog.core) implementation(libs.androidxCore) implementation(libs.documentFile) diff --git a/features/playbackScreen/build.gradle.kts b/features/playbackScreen/build.gradle.kts index 8e347856d..22326c7ca 100644 --- a/features/playbackScreen/build.gradle.kts +++ b/features/playbackScreen/build.gradle.kts @@ -17,7 +17,6 @@ dependencies { implementation(libs.datastore) implementation(libs.coil) implementation(libs.androidxCore) - implementation(libs.materialDialog.core) implementation(libs.androidxCore) implementation(libs.material) diff --git a/features/settings/build.gradle.kts b/features/settings/build.gradle.kts index 537856143..b2446f8ad 100644 --- a/features/settings/build.gradle.kts +++ b/features/settings/build.gradle.kts @@ -13,7 +13,6 @@ dependencies { implementation(projects.core.data.api) implementation(libs.androidxCore) - implementation(libs.materialDialog.core) implementation(libs.androidxCore) implementation(libs.material) } diff --git a/features/sleepTimer/build.gradle.kts b/features/sleepTimer/build.gradle.kts index 9a8f00996..7afff1b4e 100644 --- a/features/sleepTimer/build.gradle.kts +++ b/features/sleepTimer/build.gradle.kts @@ -6,7 +6,6 @@ plugins { dependencies { implementation(projects.core.strings) - implementation(libs.materialDialog.core) implementation(libs.material) testImplementation(libs.junit) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 23d3a419a..6eb372c01 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -10,7 +10,6 @@ sdk-robolectric = "35" coroutines = "1.10.2" room = "2.7.2" -materialDialog = "3.3.0" kotlin = "2.2.10" media3 = "1.8.0" agp = "8.12.1" @@ -76,9 +75,6 @@ androidX-test-rules = "androidx.test:rules:1.7.0" androidX-test-services = "androidx.test.services:test-services:1.6.0" androidX-test-orchestrator = "androidx.test:orchestrator:1.6.1" -materialDialog-core = { module = "com.afollestad.material-dialogs:core", version.ref = "materialDialog" } -materialDialog-input = { module = "com.afollestad.material-dialogs:input", version.ref = "materialDialog" } - media3-exoplayer = { module = "androidx.media3:media3-exoplayer", version.ref = "media3" } media3-session = { module = "androidx.media3:media3-session", version.ref = "media3" } media3-testUtils-core = { module = "androidx.media3:media3-test-utils", version.ref = "media3" } From 901cf3535ed9fb0bd3c50a1e0ad23097df1468e5 Mon Sep 17 00:00:00 2001 From: Paul Woitaschek Date: Tue, 26 Aug 2025 22:03:47 +0200 Subject: [PATCH 18/20] Add an architecture page. --- AGENTS.md | 8 ++-- docs/architecture.md | 95 ++++++++++++++++++++++++++++++++++++++++++++ mkdocs.yml | 1 + 3 files changed, 101 insertions(+), 3 deletions(-) create mode 100644 docs/architecture.md diff --git a/AGENTS.md b/AGENTS.md index 5b9d637da..0f26ae272 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -38,8 +38,9 @@ Each module contains its own `build.gradle.kts`, `src/main/kotlin`, and `src/tes * `:core:strings` - Localized strings * `:core:search` - Search functionality * `:core:documentfile` - File system abstractions -* `:core:logging` - Logging implementations (crashlytics, debug, core) -* `:core:remoteconfig` - Remote configuration (firebase, noop, core) +* `:core:logging:core`, `:core:logging:crashlytics`, `:core:logging:debug` - Logging implementations +* `:core:remoteconfig:core`, `:core:remoteconfig:firebase`, `:core:remoteconfig:noop` - Remote configuration +* `:core:sleeptimer:api` & `:core:sleeptimer:impl` - Sleep timer core logic **Feature Modules** (UI screens and features): @@ -51,7 +52,8 @@ Each module contains its own `build.gradle.kts`, `src/main/kotlin`, and `src/tes * `:features:cover` - Cover art management * `:features:onboarding` - First-time user flow * `:features:bookmark` - Bookmark management -* `:features:review` - App review prompts (play/noop variants) +* `:features:widget` - Home screen widget functionality +* `:features:review:play` & `:features:review:noop` - App review prompts ## Build & Run diff --git a/docs/architecture.md b/docs/architecture.md new file mode 100644 index 000000000..8496e716b --- /dev/null +++ b/docs/architecture.md @@ -0,0 +1,95 @@ +# Architecture + +## Overview + +Voice is built on a **modular, layered architecture**. The design separates infrastructure, shared domain logic, and user-facing features. +This modularization improves build times, testability, and maintainability, while keeping feature ownership clear. + +At a high level: + +* **Infrastructure modules** provide the app entry point, build tooling, and navigation. +* **Core modules** encapsulate reusable domain and service logic (playback, scanning, data, logging, etc.). +* **Feature modules** implement user-facing screens, composed from core logic and UI components. + +## Layers + +### Infrastructure + +* `:app` – Main application entry point, dependency injection setup +* `:navigation` – Navigation framework abstractions and route definitions +* `:plugins` – Gradle build plugins for consistency and convention +* `:scripts` – Build and utility scripts + +### Core (Shared Logic) + +Core modules provide the underlying services and abstractions: + +* **UI & Theming** + + * `:core:ui` – Shared UI components, typography, colors, Material 3 theming + +* **Data & Storage** + + * `:core:data:api` – Interfaces for repositories and data sources + * `:core:data:impl` – Implementations for Room, network, or file access + * `:core:documentfile` – File system abstractions + +* **Playback & Media** + + * `:core:playback` – Audio playback logic using ExoPlayer + * `:core:sleeptimer:api` & `:core:sleeptimer:impl` – Sleep timer contracts and implementation + +* **Utility Modules** + + * `:core:scanner` – File scanning and metadata extraction + * `:core:search` – Search logic + * `:core:strings` – Localized string resources + +* **Cross-Cutting Concerns** + + * Logging: `:core:logging:core`, `:core:logging:crashlytics`, `:core:logging:debug` + * Remote Config: `:core:remoteconfig:core`, `:core:remoteconfig:firebase`, `:core:remoteconfig:noop` + +### Features + +Feature modules are screen- or flow-based. Each module owns its UI (Compose) and presentation logic, while delegating to `:core` modules for +data and services: + +* `:features:playbackScreen` – Main playback interface +* `:features:bookOverview` – Library / book list +* `:features:sleepTimer` – Sleep timer control UI +* `:features:settings` – App settings +* `:features:folderPicker` – Folder selection flow +* `:features:cover` – Cover art management +* `:features:onboarding` – First-time user flow +* `:features:bookmark` – Bookmark management +* `:features:widget` – Homescreen widget support +* `:features:review:play` & `:features:review:noop` – App review prompts + +## Dependency Flow + +* Features depend **only on `:core` and infrastructure abstractions**. +* `:core` modules depend on each other as needed, but never on features. +* Infrastructure modules (`:app`, `:navigation`) wire everything together at runtime. + +This ensures **unidirectional dependency flow**: + +``` +Infrastructure → Core → Features +``` + +## Tech Decisions + +* **Compose (UI)** – Declarative UI with Material 3 for consistency and accessibility +* **Metro (DI)** – Lightweight dependency injection across modules +* **Navigation3** – Type-safe, modular navigation +* **ExoPlayer (Media3)** – Robust audio playback engine +* **Room** – Persistent storage +* **Kotlin Serialization** – JSON parsing and object serialization +* **Coil** – Efficient image loading + +## Module Lifecycle + +1. **Add new functionality as a feature module.** +2. **Extract reusable logic into `:core` modules** once multiple features need it. +3. **Keep infrastructure minimal** — mainly for wiring and build configuration. diff --git a/mkdocs.yml b/mkdocs.yml index 2f3f72269..bd56c4dac 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -14,6 +14,7 @@ nav: - Contributing: contributing.md - About: about.md - Development: development.md + - Architecture: architecture.md - FAQ: faq.md theme: From df429111835bc2f76fa4a3fc9b25991ed67fd68e Mon Sep 17 00:00:00 2001 From: Paul Woitaschek Date: Tue, 26 Aug 2025 22:13:14 +0200 Subject: [PATCH 19/20] Add a chart --- docs/architecture.md | 24 ++++++++++++++++++++++++ mkdocs.yml | 6 +++++- 2 files changed, 29 insertions(+), 1 deletion(-) diff --git a/docs/architecture.md b/docs/architecture.md index 8496e716b..82fe7ce5f 100644 --- a/docs/architecture.md +++ b/docs/architecture.md @@ -78,6 +78,29 @@ This ensures **unidirectional dependency flow**: Infrastructure → Core → Features ``` +## Diagram + +````mermaid +flowchart LR + subgraph Infrastructure + app(":app") + navigation(":navigation") + end + + subgraph Core + core(":core") + end + + subgraph Features + features(":features") + end + + app --> navigation + app --> features + features --> core + features --> navigation +```` + ## Tech Decisions * **Compose (UI)** – Declarative UI with Material 3 for consistency and accessibility @@ -93,3 +116,4 @@ Infrastructure → Core → Features 1. **Add new functionality as a feature module.** 2. **Extract reusable logic into `:core` modules** once multiple features need it. 3. **Keep infrastructure minimal** — mainly for wiring and build configuration. + diff --git a/mkdocs.yml b/mkdocs.yml index bd56c4dac..07ab41baa 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -32,7 +32,11 @@ markdown_extensions: - md_in_html - admonition - pymdownx.details - - pymdownx.superfences + - pymdownx.superfences: + custom_fences: + - name: mermaid + class: mermaid + format: !!python/name:pymdownx.superfences.fence_code_format - pymdownx.highlight: anchor_linenums: true line_spans: __span From 2aebe9ec175d72ac246f6e31e65ae1ffab7b190d Mon Sep 17 00:00:00 2001 From: Paul Woitaschek Date: Tue, 26 Aug 2025 22:15:48 +0200 Subject: [PATCH 20/20] Remove unused resources --- app/src/main/res/drawable/close.xml | 9 --------- app/src/main/res/drawable/ic_more_vert.xml | 9 --------- app/src/main/res/drawable/ic_sleep.xml | 9 --------- app/src/main/res/drawable/plus.xml | 9 --------- app/src/main/res/layout/dialog_cover_edit.xml | 18 ------------------ app/src/main/res/values/attrs.xml | 4 ---- 6 files changed, 58 deletions(-) delete mode 100644 app/src/main/res/drawable/close.xml delete mode 100644 app/src/main/res/drawable/ic_more_vert.xml delete mode 100644 app/src/main/res/drawable/ic_sleep.xml delete mode 100644 app/src/main/res/drawable/plus.xml delete mode 100644 app/src/main/res/layout/dialog_cover_edit.xml delete mode 100644 app/src/main/res/values/attrs.xml diff --git a/app/src/main/res/drawable/close.xml b/app/src/main/res/drawable/close.xml deleted file mode 100644 index 0a9daa11c..000000000 --- a/app/src/main/res/drawable/close.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - diff --git a/app/src/main/res/drawable/ic_more_vert.xml b/app/src/main/res/drawable/ic_more_vert.xml deleted file mode 100644 index dda82e8d9..000000000 --- a/app/src/main/res/drawable/ic_more_vert.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - diff --git a/app/src/main/res/drawable/ic_sleep.xml b/app/src/main/res/drawable/ic_sleep.xml deleted file mode 100644 index 0227d2338..000000000 --- a/app/src/main/res/drawable/ic_sleep.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - diff --git a/app/src/main/res/drawable/plus.xml b/app/src/main/res/drawable/plus.xml deleted file mode 100644 index 24a4589dd..000000000 --- a/app/src/main/res/drawable/plus.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - diff --git a/app/src/main/res/layout/dialog_cover_edit.xml b/app/src/main/res/layout/dialog_cover_edit.xml deleted file mode 100644 index bf55a4cef..000000000 --- a/app/src/main/res/layout/dialog_cover_edit.xml +++ /dev/null @@ -1,18 +0,0 @@ - - - - - - - - diff --git a/app/src/main/res/values/attrs.xml b/app/src/main/res/values/attrs.xml deleted file mode 100644 index eca0ba866..000000000 --- a/app/src/main/res/values/attrs.xml +++ /dev/null @@ -1,4 +0,0 @@ - - - -