From d0581e26ad36b34ee4a7a16667dcb60598460b1e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Mlynari=C4=8D?= Date: Fri, 16 May 2025 09:22:59 +0200 Subject: [PATCH 1/2] Fix spotless for Jetcaster --- Jetcaster/.editorconfig | 26 ++++++++++++++++++++++++++ Jetcaster/build.gradle.kts | 37 +++++-------------------------------- 2 files changed, 31 insertions(+), 32 deletions(-) create mode 100644 Jetcaster/.editorconfig diff --git a/Jetcaster/.editorconfig b/Jetcaster/.editorconfig new file mode 100644 index 000000000..e699cf2e2 --- /dev/null +++ b/Jetcaster/.editorconfig @@ -0,0 +1,26 @@ +# When authoring changes in .editorconfig, run ./gradlew spotlessApply --no-daemon +# Reference: https://github.com/diffplug/spotless/issues/1924 +[*.{kt,kts}] +ktlint_code_style = android_studio +ij_kotlin_allow_trailing_comma = true +ij_kotlin_allow_trailing_comma_on_call_site = true +max_line_length = 140 # ktlint official +ktlint_function_naming_ignore_when_annotated_with = Composable, Test +ktlint_standard_filename = disabled +ktlint_standard_package-name = disabled +ktlint_standard_property-naming = disabled +ktlint_standard_backing-property-naming = disabled +ktlint_standard_argument-list-wrapping = disabled +ktlint_standard_parameter-list-wrapping = disabled +ktlint_standard_double-colon-spacing = disabled +ktlint_standard_enum-entry-name-case = disabled +ktlint_standard_multiline-if-else = disabled +ktlint_standard_no-empty-first-line-in-method-block = disabled +ktlint_standard_package-name = disabled +ktlint_standard_trailing-comma = disabled +ktlint_standard_spacing-around-angle-brackets = disabled +ktlint_standard_spacing-between-declarations-with-annotations = disabled +ktlint_standard_spacing-between-declarations-with-comments = disabled +ktlint_standard_unary-op-spacing = disabled +ktlint_standard_function-expression-body = disabled +ktlint_standard_value-parameter-comment = disabled diff --git a/Jetcaster/build.gradle.kts b/Jetcaster/build.gradle.kts index bcd5617e2..39178a700 100644 --- a/Jetcaster/build.gradle.kts +++ b/Jetcaster/build.gradle.kts @@ -32,45 +32,18 @@ apply("${project.rootDir}/buildscripts/toml-updater-config.gradle") subprojects { apply(plugin = "com.diffplug.spotless") configure { - ratchetFrom = "origin/main" kotlin { target("**/*.kt") - targetExclude("**/build/**/*.kt") - ktlint().editorConfigOverride( - mapOf( - "ktlint_code_style" to "android_studio", - "ij_kotlin_allow_trailing_comma" to true, - "ktlint_function_naming_ignore_when_annotated_with" to "Composable", - // These rules were introduced in ktlint 0.46.0 and should not be - // enabled without further discussion. They are disabled for now. - // See: https://github.com/pinterest/ktlint/releases/tag/0.46.0 - "disabled_rules" to - "filename," + - "annotation,annotation-spacing," + - "argument-list-wrapping," + - "double-colon-spacing," + - "enum-entry-name-case," + - "multiline-if-else," + - "no-empty-first-line-in-method-block," + - "package-name," + - "trailing-comma," + - "spacing-around-angle-brackets," + - "spacing-between-declarations-with-annotations," + - "spacing-between-declarations-with-comments," + - "unary-op-spacing" - ) - ) + targetExclude("${layout.buildDirectory}/**/*.kt") + ktlint() licenseHeaderFile(rootProject.file("spotless/copyright.kt")) } - format("kts") { - target("**/*.kts") - targetExclude("**/build/**/*.kts") - // Look for the first line that doesn't have a block comment (assumed to be the license) - licenseHeaderFile(rootProject.file("spotless/copyright.kt"), "(^(?![\\/ ]\\*).*$)") - } kotlinGradle { target("*.gradle.kts") + targetExclude("${layout.buildDirectory}/**/*.kt") ktlint() + // Look for the first line that doesn't have a block comment (assumed to be the license) + licenseHeaderFile(rootProject.file("spotless/copyright.kt"), "(^(?![\\/ ]\\*).*$)") } } } From bb567f5f72871652b6f542c5ff4c5aeb853412c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Mlynari=C4=8D?= Date: Fri, 16 May 2025 12:37:23 +0200 Subject: [PATCH 2/2] Apply spotless for Jetcaster --- Jetcaster/core/data-testing/build.gradle.kts | 29 ++- .../testing/repository/TestCategoryStore.kt | 18 +- .../testing/repository/TestEpisodeStore.kt | 72 ++++---- .../testing/repository/TestPodcastStore.kt | 93 +++++----- Jetcaster/core/data/build.gradle.kts | 27 ++- .../core/data/database/JetcasterDatabase.kt | 4 +- .../core/data/database/dao/CategoriesDao.kt | 6 +- .../core/data/database/dao/EpisodesDao.kt | 25 +-- .../core/data/database/dao/PodcastsDao.kt | 31 +--- .../core/data/database/model/Category.kt | 6 +- .../core/data/database/model/Episode.kt | 11 +- .../core/data/database/model/Podcast.kt | 6 +- .../database/model/PodcastCategoryEntry.kt | 12 +- .../database/model/PodcastFollowedEntry.kt | 10 +- .../jetcaster/core/data/di/DataDiModule.kt | 42 ++--- .../jetcaster/core/data/network/Feeds.kt | 2 +- .../core/data/network/OkHttpExtensions.kt | 2 +- .../core/data/network/PodcastFetcher.kt | 23 +-- .../core/data/repository/CategoryStore.kt | 31 +--- .../core/data/repository/EpisodeStore.kt | 30 +-- .../core/data/repository/PodcastStore.kt | 35 +--- .../data/repository/PodcastsRepository.kt | 4 +- .../com/example/jetcaster/core/util/Flows.kt | 92 +++++----- Jetcaster/core/designsystem/build.gradle.kts | 97 ++++++---- .../component/HtmlTextContainer.kt | 5 +- .../designsystem/component/ImageBackground.kt | 26 +-- .../designsystem/component/PodcastImage.kt | 13 +- .../component/thumbnailPlaceholder.kt | 8 +- .../jetcaster/designsystem/theme/Shape.kt | 2 +- .../jetcaster/designsystem/theme/Type.kt | 28 +-- .../designsystem/theme/Typography.kt | 5 +- .../core/domain-testing/build.gradle.kts | 17 ++ .../core/domain/testing/PreviewData.kt | 20 +- Jetcaster/core/domain/build.gradle.kts | 17 ++ .../jetcaster/core/di/DomainDiModule.kt | 5 +- .../domain/FilterableCategoriesUseCase.kt | 21 +-- .../GetLatestFollowedEpisodesUseCase.kt | 15 +- .../domain/PodcastCategoryFilterUseCase.kt | 10 +- .../jetcaster/core/model/CategoryInfo.kt | 14 +- .../jetcaster/core/model/EpisodeInfo.kt | 46 +++-- .../core/model/FilterableCategoriesModel.kt | 5 +- .../jetcaster/core/model/LibraryInfo.kt | 4 +- .../core/model/PodcastCategoryFilterResult.kt | 2 +- .../jetcaster/core/model/PodcastInfo.kt | 24 ++- .../core/model/PodcastToEpisodeInfo.kt | 14 +- .../jetcaster/core/player/EpisodePlayer.kt | 4 +- .../core/player/MockEpisodePlayer.kt | 17 +- .../core/player/model/PlayerEpisode.kt | 29 ++- .../domain/FilterableCategoriesUseCaseTest.kt | 6 +- .../GetLatestFollowedEpisodesUseCaseTest.kt | 13 +- .../PodcastCategoryFilterUseCaseTest.kt | 30 +-- .../domain/player/MockEpisodePlayerTest.kt | 24 +-- Jetcaster/glancewidget/build.gradle.kts | 17 ++ .../glancewidget/JetcasterAppWidget.kt | 55 ++---- .../glancewidget/JetcasterAppWidgetPreview.kt | 10 +- Jetcaster/mobile/build.gradle.kts | 22 ++- .../example/jetcaster/JetcasterApplication.kt | 4 +- .../com/example/jetcaster/ui/JetcasterApp.kt | 19 +- .../example/jetcaster/ui/JetcasterAppState.kt | 18 +- .../com/example/jetcaster/ui/MainActivity.kt | 2 +- .../com/example/jetcaster/ui/home/Home.kt | 130 ++++++------- .../jetcaster/ui/home/HomeViewModel.kt | 36 ++-- .../ui/home/category/PodcastCategory.kt | 21 +-- .../jetcaster/ui/home/discover/Discover.kt | 15 +- .../jetcaster/ui/home/library/Library.kt | 2 +- .../jetcaster/ui/player/PlayerScreen.kt | 173 ++++++++---------- .../jetcaster/ui/player/PlayerViewModel.kt | 6 +- .../ui/podcast/PodcastDetailsScreen.kt | 94 ++++------ .../ui/podcast/PodcastDetailsViewModel.kt | 9 +- .../jetcaster/ui/shared/EpisodeListItem.kt | 54 +++--- .../example/jetcaster/ui/shared/Loading.kt | 4 +- .../com/example/jetcaster/ui/theme/Theme.kt | 13 +- .../com/example/jetcaster/util/Buttons.kt | 19 +- .../example/jetcaster/util/GradientScrim.kt | 14 +- .../jetcaster/util/LazyVerticalGrid.kt | 8 +- .../com/example/jetcaster/util/ViewModel.kt | 4 +- Jetcaster/tv/build.gradle.kts | 3 +- .../com/example/jetcaster/tv/MainActivity.kt | 2 +- .../jetcaster/tv/model/CategorySelection.kt | 4 +- .../example/jetcaster/tv/ui/JetcasterApp.kt | 32 ++-- .../jetcaster/tv/ui/JetcasterAppState.kt | 13 +- .../jetcaster/tv/ui/component/Background.kt | 27 ++- .../jetcaster/tv/ui/component/Button.kt | 63 ++----- .../jetcaster/tv/ui/component/Catalog.kt | 14 +- .../jetcaster/tv/ui/component/EpisodeCard.kt | 15 +- .../tv/ui/component/EpisodeDateAndDuration.kt | 4 +- .../tv/ui/component/EpisodeDetails.kt | 12 +- .../jetcaster/tv/ui/component/EpisodeRow.kt | 4 +- .../jetcaster/tv/ui/component/ErrorState.kt | 10 +- .../jetcaster/tv/ui/component/Loading.kt | 52 ++---- .../tv/ui/component/NotAvailableFeature.kt | 2 +- .../jetcaster/tv/ui/component/PodcastCard.kt | 10 +- .../jetcaster/tv/ui/component/Seekbar.kt | 4 +- .../jetcaster/tv/ui/component/Thumbnail.kt | 59 +++--- .../jetcaster/tv/ui/component/TwoColumn.kt | 4 +- .../tv/ui/discover/DiscoverScreen.kt | 11 +- .../tv/ui/discover/DiscoverScreenViewModel.kt | 8 +- .../jetcaster/tv/ui/episode/EpisodeScreen.kt | 29 +-- .../tv/ui/episode/EpisodeScreenViewModel.kt | 4 +- .../jetcaster/tv/ui/library/LibraryScreen.kt | 13 +- .../tv/ui/library/LibraryScreenViewModel.kt | 7 +- .../jetcaster/tv/ui/player/PlayerScreen.kt | 108 +++++------ .../tv/ui/player/PlayerScreenViewModel.kt | 10 +- .../tv/ui/podcast/PodcastDetailsScreen.kt | 36 ++-- .../podcast/PodcastDetailsScreenViewModel.kt | 10 +- .../jetcaster/tv/ui/search/SearchScreen.kt | 40 ++-- .../tv/ui/search/SearchScreenViewModel.kt | 26 +-- .../tv/ui/settings/SettingsScreen.kt | 4 +- .../example/jetcaster/tv/ui/theme/Space.kt | 21 +-- .../example/jetcaster/tv/ui/theme/Theme.kt | 7 +- .../com/example/jetcaster/tv/ui/theme/Type.kt | 2 +- .../jetcaster/JetcasterWearApplication.kt | 7 +- .../java/com/example/jetcaster/WearApp.kt | 24 +-- .../java/com/example/jetcaster/theme/Color.kt | 2 +- .../java/com/example/jetcaster/theme/Type.kt | 4 +- .../example/jetcaster/theme/WearAppTheme.kt | 6 +- .../jetcaster/ui/components/MediaContent.kt | 6 +- .../ui/components/SettingsButtons.kt | 8 +- .../jetcaster/ui/episode/EpisodeScreen.kt | 32 ++-- .../jetcaster/ui/episode/EpisodeViewModel.kt | 6 +- .../latest_episodes/LatestEpisodeViewModel.kt | 6 +- .../latest_episodes/LatestEpisodesScreen.kt | 48 +++-- .../jetcaster/ui/library/LibraryScreen.kt | 30 ++- .../jetcaster/ui/library/LibraryViewModel.kt | 12 +- .../jetcaster/ui/player/PlayerScreen.kt | 14 +- .../jetcaster/ui/player/PlayerViewModel.kt | 41 ++--- .../ui/podcast/PodcastDetailsScreen.kt | 47 ++--- .../ui/podcast/PodcastDetailsViewModel.kt | 6 +- .../jetcaster/ui/podcasts/PodcastsScreen.kt | 56 ++---- .../ui/podcasts/PodcastsViewModel.kt | 13 +- .../example/jetcaster/ui/queue/QueueScreen.kt | 59 +++--- .../jetcaster/ui/queue/QueueViewModel.kt | 9 +- 132 files changed, 1336 insertions(+), 1651 deletions(-) diff --git a/Jetcaster/core/data-testing/build.gradle.kts b/Jetcaster/core/data-testing/build.gradle.kts index a8644e1c1..40cc5d1cf 100644 --- a/Jetcaster/core/data-testing/build.gradle.kts +++ b/Jetcaster/core/data-testing/build.gradle.kts @@ -1,3 +1,20 @@ +/* + * Copyright 2025 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + plugins { alias(libs.plugins.android.library) alias(libs.plugins.kotlin.android) @@ -5,10 +22,16 @@ plugins { android { namespace = "com.example.jetcaster.core.data.testing" - compileSdk = libs.versions.compileSdk.get().toInt() + compileSdk = + libs.versions.compileSdk + .get() + .toInt() defaultConfig { - minSdk = libs.versions.minSdk.get().toInt() + minSdk = + libs.versions.minSdk + .get() + .toInt() testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" consumerProguardFiles("consumer-rules.pro") @@ -19,7 +42,7 @@ android { isMinifyEnabled = false proguardFiles( getDefaultProguardFile("proguard-android-optimize.txt"), - "proguard-rules.pro" + "proguard-rules.pro", ) } } diff --git a/Jetcaster/core/data-testing/src/main/java/com/example/jetcaster/core/data/testing/repository/TestCategoryStore.kt b/Jetcaster/core/data-testing/src/main/java/com/example/jetcaster/core/data/testing/repository/TestCategoryStore.kt index 60de97944..8f8ac64db 100644 --- a/Jetcaster/core/data-testing/src/main/java/com/example/jetcaster/core/data/testing/repository/TestCategoryStore.kt +++ b/Jetcaster/core/data-testing/src/main/java/com/example/jetcaster/core/data/testing/repository/TestCategoryStore.kt @@ -39,20 +39,14 @@ class TestCategoryStore : CategoryStore { private val episodesFromPodcasts = MutableStateFlow>>(emptyMap()) - override fun categoriesSortedByPodcastCount(limit: Int): Flow> = - categoryFlow + override fun categoriesSortedByPodcastCount(limit: Int): Flow> = categoryFlow - override fun podcastsInCategorySortedByPodcastCount( - categoryId: Long, - limit: Int - ): Flow> = podcastsInCategoryFlow.map { - it[categoryId]?.take(limit) ?: emptyList() - } + override fun podcastsInCategorySortedByPodcastCount(categoryId: Long, limit: Int): Flow> = + podcastsInCategoryFlow.map { + it[categoryId]?.take(limit) ?: emptyList() + } - override fun episodesFromPodcastsInCategory( - categoryId: Long, - limit: Int - ): Flow> = episodesFromPodcasts.map { + override fun episodesFromPodcastsInCategory(categoryId: Long, limit: Int): Flow> = episodesFromPodcasts.map { it[categoryId]?.take(limit) ?: emptyList() } diff --git a/Jetcaster/core/data-testing/src/main/java/com/example/jetcaster/core/data/testing/repository/TestEpisodeStore.kt b/Jetcaster/core/data-testing/src/main/java/com/example/jetcaster/core/data/testing/repository/TestEpisodeStore.kt index ba23cd237..2afb76aa0 100644 --- a/Jetcaster/core/data-testing/src/main/java/com/example/jetcaster/core/data/testing/repository/TestEpisodeStore.kt +++ b/Jetcaster/core/data-testing/src/main/java/com/example/jetcaster/core/data/testing/repository/TestEpisodeStore.kt @@ -29,57 +29,47 @@ import kotlinx.coroutines.flow.update class TestEpisodeStore : EpisodeStore { private val episodesFlow = MutableStateFlow>(listOf()) - override fun episodeWithUri(episodeUri: String): Flow = - episodesFlow.map { episodes -> - episodes.first { it.uri == episodeUri } + override fun episodeWithUri(episodeUri: String): Flow = episodesFlow.map { episodes -> + episodes.first { it.uri == episodeUri } + } + + override fun episodeAndPodcastWithUri(episodeUri: String): Flow = episodesFlow.map { episodes -> + val e = episodes.first { + it.uri == episodeUri + } + EpisodeToPodcast().apply { + episode = e + _podcasts = emptyList() } + } - override fun episodeAndPodcastWithUri(episodeUri: String): Flow = - episodesFlow.map { episodes -> - val e = episodes.first { - it.uri == episodeUri - } + override fun episodesInPodcast(podcastUri: String, limit: Int): Flow> = episodesFlow.map { episodes -> + episodes.filter { + it.podcastUri == podcastUri + }.map { e -> EpisodeToPodcast().apply { episode = e - _podcasts = emptyList() } } + } - override fun episodesInPodcast(podcastUri: String, limit: Int): Flow> = - episodesFlow.map { episodes -> - episodes.filter { - it.podcastUri == podcastUri - }.map { e -> - EpisodeToPodcast().apply { - episode = e - } - } - } - - override fun episodesInPodcasts( - podcastUris: List, - limit: Int - ): Flow> = - episodesFlow.map { episodes -> - episodes.filter { - podcastUris.contains(it.podcastUri) - }.map { ep -> - EpisodeToPodcast().apply { - episode = ep - } + override fun episodesInPodcasts(podcastUris: List, limit: Int): Flow> = episodesFlow.map { episodes -> + episodes.filter { + podcastUris.contains(it.podcastUri) + }.map { ep -> + EpisodeToPodcast().apply { + episode = ep } } + } - override suspend fun addEpisodes(episodes: Collection) = - episodesFlow.update { - it + episodes - } + override suspend fun addEpisodes(episodes: Collection) = episodesFlow.update { + it + episodes + } - override suspend fun deleteEpisode(episode: Episode) = - episodesFlow.update { - it - episode - } + override suspend fun deleteEpisode(episode: Episode) = episodesFlow.update { + it - episode + } - override suspend fun isEmpty(): Boolean = - episodesFlow.first().isEmpty() + override suspend fun isEmpty(): Boolean = episodesFlow.first().isEmpty() } diff --git a/Jetcaster/core/data-testing/src/main/java/com/example/jetcaster/core/data/testing/repository/TestPodcastStore.kt b/Jetcaster/core/data-testing/src/main/java/com/example/jetcaster/core/data/testing/repository/TestPodcastStore.kt index 8a4808ecf..24e691632 100644 --- a/Jetcaster/core/data-testing/src/main/java/com/example/jetcaster/core/data/testing/repository/TestPodcastStore.kt +++ b/Jetcaster/core/data-testing/src/main/java/com/example/jetcaster/core/data/testing/repository/TestPodcastStore.kt @@ -31,71 +31,62 @@ class TestPodcastStore : PodcastStore { private val podcastFlow = MutableStateFlow>(listOf()) private val followedPodcasts = mutableSetOf() - override fun podcastWithUri(uri: String): Flow = - podcastFlow.map { podcasts -> - podcasts.first { it.uri == uri } - } + override fun podcastWithUri(uri: String): Flow = podcastFlow.map { podcasts -> + podcasts.first { it.uri == uri } + } - override fun podcastWithExtraInfo(podcastUri: String): Flow = - podcastFlow.map { podcasts -> - val podcast = podcasts.first { it.uri == podcastUri } - PodcastWithExtraInfo().apply { - this.podcast = podcast - } + override fun podcastWithExtraInfo(podcastUri: String): Flow = podcastFlow.map { podcasts -> + val podcast = podcasts.first { it.uri == podcastUri } + PodcastWithExtraInfo().apply { + this.podcast = podcast } + } - override fun podcastsSortedByLastEpisode(limit: Int): Flow> = - podcastFlow.map { podcasts -> - podcasts.map { p -> - PodcastWithExtraInfo().apply { - podcast = p - isFollowed = followedPodcasts.contains(p.uri) - } + override fun podcastsSortedByLastEpisode(limit: Int): Flow> = podcastFlow.map { podcasts -> + podcasts.map { p -> + PodcastWithExtraInfo().apply { + podcast = p + isFollowed = followedPodcasts.contains(p.uri) } } + } - override fun followedPodcastsSortedByLastEpisode(limit: Int): Flow> = - podcastFlow.map { podcasts -> - podcasts.filter { - followedPodcasts.contains(it.uri) - }.map { p -> - PodcastWithExtraInfo().apply { - podcast = p - isFollowed = true - } + override fun followedPodcastsSortedByLastEpisode(limit: Int): Flow> = podcastFlow.map { podcasts -> + podcasts.filter { + followedPodcasts.contains(it.uri) + }.map { p -> + PodcastWithExtraInfo().apply { + podcast = p + isFollowed = true } } + } - override fun searchPodcastByTitle( - keyword: String, - limit: Int - ): Flow> = - podcastFlow.map { podcastList -> - podcastList.filter { - it.title.contains(keyword) - }.map { p -> - PodcastWithExtraInfo().apply { - podcast = p - isFollowed = true - } + override fun searchPodcastByTitle(keyword: String, limit: Int): Flow> = podcastFlow.map { podcastList -> + podcastList.filter { + it.title.contains(keyword) + }.map { p -> + PodcastWithExtraInfo().apply { + podcast = p + isFollowed = true } } + } override fun searchPodcastByTitleAndCategories( keyword: String, categories: List, - limit: Int - ): Flow> = - podcastFlow.map { podcastList -> - podcastList.filter { - it.title.contains(keyword) - }.map { p -> - PodcastWithExtraInfo().apply { - podcast = p - isFollowed = true - } + limit: Int, + ): Flow> = podcastFlow.map { podcastList -> + podcastList.filter { + it.title.contains(keyword) + }.map { p -> + PodcastWithExtraInfo().apply { + podcast = p + isFollowed = true } } + } override suspend fun togglePodcastFollowed(podcastUri: String) { if (podcastUri in followedPodcasts) { @@ -113,9 +104,7 @@ class TestPodcastStore : PodcastStore { followedPodcasts.remove(podcastUri) } - override suspend fun addPodcast(podcast: Podcast) = - podcastFlow.update { it + podcast } + override suspend fun addPodcast(podcast: Podcast) = podcastFlow.update { it + podcast } - override suspend fun isEmpty(): Boolean = - podcastFlow.first().isEmpty() + override suspend fun isEmpty(): Boolean = podcastFlow.first().isEmpty() } diff --git a/Jetcaster/core/data/build.gradle.kts b/Jetcaster/core/data/build.gradle.kts index bd81f036e..ab7e5cacb 100644 --- a/Jetcaster/core/data/build.gradle.kts +++ b/Jetcaster/core/data/build.gradle.kts @@ -1,3 +1,20 @@ +/* + * Copyright 2025 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + plugins { alias(libs.plugins.android.library) alias(libs.plugins.kotlin.android) @@ -7,10 +24,16 @@ plugins { android { namespace = "com.example.jetcaster.core.data" - compileSdk = libs.versions.compileSdk.get().toInt() + compileSdk = + libs.versions.compileSdk + .get() + .toInt() defaultConfig { - minSdk = libs.versions.minSdk.get().toInt() + minSdk = + libs.versions.minSdk + .get() + .toInt() testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" consumerProguardFiles("consumer-rules.pro") diff --git a/Jetcaster/core/data/src/main/java/com/example/jetcaster/core/data/database/JetcasterDatabase.kt b/Jetcaster/core/data/src/main/java/com/example/jetcaster/core/data/database/JetcasterDatabase.kt index ced5d408b..b1804d527 100644 --- a/Jetcaster/core/data/src/main/java/com/example/jetcaster/core/data/database/JetcasterDatabase.kt +++ b/Jetcaster/core/data/src/main/java/com/example/jetcaster/core/data/database/JetcasterDatabase.kt @@ -40,10 +40,10 @@ import com.example.jetcaster.core.data.database.model.PodcastFollowedEntry Episode::class, PodcastCategoryEntry::class, Category::class, - PodcastFollowedEntry::class + PodcastFollowedEntry::class, ], version = 1, - exportSchema = false + exportSchema = false, ) @TypeConverters(DateTimeTypeConverters::class) abstract class JetcasterDatabase : RoomDatabase() { diff --git a/Jetcaster/core/data/src/main/java/com/example/jetcaster/core/data/database/dao/CategoriesDao.kt b/Jetcaster/core/data/src/main/java/com/example/jetcaster/core/data/database/dao/CategoriesDao.kt index baf958f13..a3b0f7eb3 100644 --- a/Jetcaster/core/data/src/main/java/com/example/jetcaster/core/data/database/dao/CategoriesDao.kt +++ b/Jetcaster/core/data/src/main/java/com/example/jetcaster/core/data/database/dao/CategoriesDao.kt @@ -35,11 +35,9 @@ abstract class CategoriesDao : BaseDao { ) ON category_id = categories.id ORDER BY podcast_count DESC LIMIT :limit - """ + """, ) - abstract fun categoriesSortedByPodcastCount( - limit: Int - ): Flow> + abstract fun categoriesSortedByPodcastCount(limit: Int): Flow> @Query("SELECT * FROM categories WHERE name = :name") abstract suspend fun getCategoryWithName(name: String): Category? diff --git a/Jetcaster/core/data/src/main/java/com/example/jetcaster/core/data/database/dao/EpisodesDao.kt b/Jetcaster/core/data/src/main/java/com/example/jetcaster/core/data/database/dao/EpisodesDao.kt index e1d60d5f0..c7373840c 100644 --- a/Jetcaster/core/data/src/main/java/com/example/jetcaster/core/data/database/dao/EpisodesDao.kt +++ b/Jetcaster/core/data/src/main/java/com/example/jetcaster/core/data/database/dao/EpisodesDao.kt @@ -32,7 +32,7 @@ abstract class EpisodesDao : BaseDao { @Query( """ SELECT * FROM episodes WHERE uri = :uri - """ + """, ) abstract fun episode(uri: String): Flow @@ -42,7 +42,7 @@ abstract class EpisodesDao : BaseDao { SELECT episodes.* FROM episodes INNER JOIN podcasts ON episodes.podcast_uri = podcasts.uri WHERE episodes.uri = :episodeUri - """ + """, ) abstract fun episodeAndPodcast(episodeUri: String): Flow @@ -52,12 +52,9 @@ abstract class EpisodesDao : BaseDao { SELECT * FROM episodes WHERE podcast_uri = :podcastUri ORDER BY datetime(published) DESC LIMIT :limit - """ + """, ) - abstract fun episodesForPodcastUri( - podcastUri: String, - limit: Int - ): Flow> + abstract fun episodesForPodcastUri(podcastUri: String, limit: Int): Flow> @Transaction @Query( @@ -67,12 +64,9 @@ abstract class EpisodesDao : BaseDao { WHERE category_id = :categoryId ORDER BY datetime(published) DESC LIMIT :limit - """ + """, ) - abstract fun episodesFromPodcastsInCategory( - categoryId: Long, - limit: Int - ): Flow> + abstract fun episodesFromPodcastsInCategory(categoryId: Long, limit: Int): Flow> @Query("SELECT COUNT(*) FROM episodes") abstract suspend fun count(): Int @@ -83,10 +77,7 @@ abstract class EpisodesDao : BaseDao { SELECT * FROM episodes WHERE podcast_uri IN (:podcastUris) ORDER BY datetime(published) DESC LIMIT :limit - """ + """, ) - abstract fun episodesForPodcasts( - podcastUris: List, - limit: Int - ): Flow> + abstract fun episodesForPodcasts(podcastUris: List, limit: Int): Flow> } diff --git a/Jetcaster/core/data/src/main/java/com/example/jetcaster/core/data/database/dao/PodcastsDao.kt b/Jetcaster/core/data/src/main/java/com/example/jetcaster/core/data/database/dao/PodcastsDao.kt index 4d5ce7175..b010938e0 100644 --- a/Jetcaster/core/data/src/main/java/com/example/jetcaster/core/data/database/dao/PodcastsDao.kt +++ b/Jetcaster/core/data/src/main/java/com/example/jetcaster/core/data/database/dao/PodcastsDao.kt @@ -44,7 +44,7 @@ abstract class PodcastsDao : BaseDao { LEFT JOIN podcast_followed_entries AS followed_entries ON followed_entries.podcast_uri = podcasts.uri WHERE podcasts.uri = :podcastUri ORDER BY datetime(last_episode_date) DESC - """ + """, ) abstract fun podcastWithExtraInfo(podcastUri: String): Flow @@ -61,11 +61,9 @@ abstract class PodcastsDao : BaseDao { LEFT JOIN podcast_followed_entries AS followed_entries ON followed_entries.podcast_uri = episodes.podcast_uri ORDER BY datetime(last_episode_date) DESC LIMIT :limit - """ + """, ) - abstract fun podcastsSortedByLastEpisode( - limit: Int - ): Flow> + abstract fun podcastsSortedByLastEpisode(limit: Int): Flow> @Transaction @Query( @@ -82,12 +80,9 @@ abstract class PodcastsDao : BaseDao { LEFT JOIN podcast_followed_entries AS followed_entries ON followed_entries.podcast_uri = inner_query.podcast_uri ORDER BY datetime(last_episode_date) DESC LIMIT :limit - """ + """, ) - abstract fun podcastsInCategorySortedByLastEpisode( - categoryId: Long, - limit: Int - ): Flow> + abstract fun podcastsInCategorySortedByLastEpisode(categoryId: Long, limit: Int): Flow> @Transaction @Query( @@ -100,11 +95,9 @@ abstract class PodcastsDao : BaseDao { INNER JOIN podcast_followed_entries AS followed_entries ON followed_entries.podcast_uri = episodes.podcast_uri ORDER BY datetime(last_episode_date) DESC LIMIT :limit - """ + """, ) - abstract fun followedPodcastsSortedByLastEpisode( - limit: Int - ): Flow> + abstract fun followedPodcastsSortedByLastEpisode(limit: Int): Flow> @Transaction @Query( @@ -118,7 +111,7 @@ abstract class PodcastsDao : BaseDao { WHERE podcasts.title LIKE '%' || :keyword || '%' ORDER BY datetime(last_episode_date) DESC LIMIT :limit - """ + """, ) abstract fun searchPodcastByTitle(keyword: String, limit: Int): Flow> @@ -138,13 +131,9 @@ abstract class PodcastsDao : BaseDao { WHERE podcasts.title LIKE '%' || :keyword || '%' ORDER BY datetime(last_episode_date) DESC LIMIT :limit - """ + """, ) - abstract fun searchPodcastByTitleAndCategory( - keyword: String, - categoryIdList: List, - limit: Int - ): Flow> + abstract fun searchPodcastByTitleAndCategory(keyword: String, categoryIdList: List, limit: Int): Flow> @Query("SELECT COUNT(*) FROM podcasts") abstract suspend fun count(): Int diff --git a/Jetcaster/core/data/src/main/java/com/example/jetcaster/core/data/database/model/Category.kt b/Jetcaster/core/data/src/main/java/com/example/jetcaster/core/data/database/model/Category.kt index 4dff2871e..9391e859f 100644 --- a/Jetcaster/core/data/src/main/java/com/example/jetcaster/core/data/database/model/Category.kt +++ b/Jetcaster/core/data/src/main/java/com/example/jetcaster/core/data/database/model/Category.kt @@ -25,11 +25,11 @@ import androidx.room.PrimaryKey @Entity( tableName = "categories", indices = [ - Index("name", unique = true) - ] + Index("name", unique = true), + ], ) @Immutable data class Category( @PrimaryKey(autoGenerate = true) @ColumnInfo(name = "id") val id: Long = 0, - @ColumnInfo(name = "name") val name: String + @ColumnInfo(name = "name") val name: String, ) diff --git a/Jetcaster/core/data/src/main/java/com/example/jetcaster/core/data/database/model/Episode.kt b/Jetcaster/core/data/src/main/java/com/example/jetcaster/core/data/database/model/Episode.kt index c6dd7ad86..e159e5bd2 100644 --- a/Jetcaster/core/data/src/main/java/com/example/jetcaster/core/data/database/model/Episode.kt +++ b/Jetcaster/core/data/src/main/java/com/example/jetcaster/core/data/database/model/Episode.kt @@ -16,7 +16,6 @@ package com.example.jetcaster.core.data.database.model -import androidx.compose.runtime.Immutable import androidx.room.ColumnInfo import androidx.room.Entity import androidx.room.ForeignKey @@ -31,7 +30,7 @@ import java.time.OffsetDateTime tableName = "episodes", indices = [ Index("uri", unique = true), - Index("podcast_uri") + Index("podcast_uri"), ], foreignKeys = [ ForeignKey( @@ -39,9 +38,9 @@ import java.time.OffsetDateTime parentColumns = ["uri"], childColumns = ["podcast_uri"], onUpdate = ForeignKey.CASCADE, - onDelete = ForeignKey.CASCADE - ) - ] + onDelete = ForeignKey.CASCADE, + ), + ], ) @TypeConverters(ListOfStringConverter::class) data class Episode( @@ -53,7 +52,7 @@ data class Episode( @ColumnInfo(name = "author") val author: String? = null, @ColumnInfo(name = "published") val published: OffsetDateTime, @ColumnInfo(name = "duration") val duration: Duration? = null, - @ColumnInfo(name = "media_urls") val mediaUrls: List + @ColumnInfo(name = "media_urls") val mediaUrls: List, ) class ListOfStringConverter { diff --git a/Jetcaster/core/data/src/main/java/com/example/jetcaster/core/data/database/model/Podcast.kt b/Jetcaster/core/data/src/main/java/com/example/jetcaster/core/data/database/model/Podcast.kt index 1d86f31f9..8e8d28f1b 100644 --- a/Jetcaster/core/data/src/main/java/com/example/jetcaster/core/data/database/model/Podcast.kt +++ b/Jetcaster/core/data/src/main/java/com/example/jetcaster/core/data/database/model/Podcast.kt @@ -25,8 +25,8 @@ import androidx.room.PrimaryKey @Entity( tableName = "podcasts", indices = [ - Index("uri", unique = true) - ] + Index("uri", unique = true), + ], ) @Immutable data class Podcast( @@ -35,5 +35,5 @@ data class Podcast( @ColumnInfo(name = "description") val description: String? = null, @ColumnInfo(name = "author") val author: String? = null, @ColumnInfo(name = "image_url") val imageUrl: String? = null, - @ColumnInfo(name = "copyright") val copyright: String? = null + @ColumnInfo(name = "copyright") val copyright: String? = null, ) diff --git a/Jetcaster/core/data/src/main/java/com/example/jetcaster/core/data/database/model/PodcastCategoryEntry.kt b/Jetcaster/core/data/src/main/java/com/example/jetcaster/core/data/database/model/PodcastCategoryEntry.kt index 3c2c67878..1a99493f0 100644 --- a/Jetcaster/core/data/src/main/java/com/example/jetcaster/core/data/database/model/PodcastCategoryEntry.kt +++ b/Jetcaster/core/data/src/main/java/com/example/jetcaster/core/data/database/model/PodcastCategoryEntry.kt @@ -31,25 +31,25 @@ import androidx.room.PrimaryKey parentColumns = ["id"], childColumns = ["category_id"], onUpdate = ForeignKey.CASCADE, - onDelete = ForeignKey.CASCADE + onDelete = ForeignKey.CASCADE, ), ForeignKey( entity = Podcast::class, parentColumns = ["uri"], childColumns = ["podcast_uri"], onUpdate = ForeignKey.CASCADE, - onDelete = ForeignKey.CASCADE - ) + onDelete = ForeignKey.CASCADE, + ), ], indices = [ Index("podcast_uri", "category_id", unique = true), Index("category_id"), - Index("podcast_uri") - ] + Index("podcast_uri"), + ], ) @Immutable data class PodcastCategoryEntry( @PrimaryKey(autoGenerate = true) @ColumnInfo(name = "id") val id: Long = 0, @ColumnInfo(name = "podcast_uri") val podcastUri: String, - @ColumnInfo(name = "category_id") val categoryId: Long + @ColumnInfo(name = "category_id") val categoryId: Long, ) diff --git a/Jetcaster/core/data/src/main/java/com/example/jetcaster/core/data/database/model/PodcastFollowedEntry.kt b/Jetcaster/core/data/src/main/java/com/example/jetcaster/core/data/database/model/PodcastFollowedEntry.kt index 420e68f38..7452f09a6 100644 --- a/Jetcaster/core/data/src/main/java/com/example/jetcaster/core/data/database/model/PodcastFollowedEntry.kt +++ b/Jetcaster/core/data/src/main/java/com/example/jetcaster/core/data/database/model/PodcastFollowedEntry.kt @@ -31,15 +31,15 @@ import androidx.room.PrimaryKey parentColumns = ["uri"], childColumns = ["podcast_uri"], onUpdate = ForeignKey.CASCADE, - onDelete = ForeignKey.CASCADE - ) + onDelete = ForeignKey.CASCADE, + ), ], indices = [ - Index("podcast_uri", unique = true) - ] + Index("podcast_uri", unique = true), + ], ) @Immutable data class PodcastFollowedEntry( @PrimaryKey(autoGenerate = true) @ColumnInfo(name = "id") val id: Long = 0, - @ColumnInfo(name = "podcast_uri") val podcastUri: String + @ColumnInfo(name = "podcast_uri") val podcastUri: String, ) diff --git a/Jetcaster/core/data/src/main/java/com/example/jetcaster/core/data/di/DataDiModule.kt b/Jetcaster/core/data/src/main/java/com/example/jetcaster/core/data/di/DataDiModule.kt index 68d4b1920..2cf54ca72 100644 --- a/Jetcaster/core/data/src/main/java/com/example/jetcaster/core/data/di/DataDiModule.kt +++ b/Jetcaster/core/data/src/main/java/com/example/jetcaster/core/data/di/DataDiModule.kt @@ -55,9 +55,7 @@ object DataDiModule { @Provides @Singleton - fun provideOkHttpClient( - @ApplicationContext context: Context - ): OkHttpClient = OkHttpClient.Builder() + fun provideOkHttpClient(@ApplicationContext context: Context): OkHttpClient = OkHttpClient.Builder() .cache(Cache(File(context.cacheDir, "http_cache"), (20 * 1024 * 1024).toLong())) .apply { if (BuildConfig.DEBUG) eventListenerFactory(LoggingEventListener.Factory()) @@ -66,9 +64,7 @@ object DataDiModule { @Provides @Singleton - fun provideDatabase( - @ApplicationContext context: Context - ): JetcasterDatabase = + fun provideDatabase(@ApplicationContext context: Context): JetcasterDatabase = Room.databaseBuilder(context, JetcasterDatabase::class.java, "data.db") // This is not recommended for normal apps, but the goal of this sample isn't to // showcase all of Room. @@ -77,48 +73,34 @@ object DataDiModule { @Provides @Singleton - fun provideImageLoader( - @ApplicationContext context: Context - ): ImageLoader = ImageLoader.Builder(context) + fun provideImageLoader(@ApplicationContext context: Context): ImageLoader = ImageLoader.Builder(context) // Disable `Cache-Control` header support as some podcast images disable disk caching. .respectCacheHeaders(false) .build() @Provides @Singleton - fun provideCategoriesDao( - database: JetcasterDatabase - ): CategoriesDao = database.categoriesDao() + fun provideCategoriesDao(database: JetcasterDatabase): CategoriesDao = database.categoriesDao() @Provides @Singleton - fun providePodcastCategoryEntryDao( - database: JetcasterDatabase - ): PodcastCategoryEntryDao = database.podcastCategoryEntryDao() + fun providePodcastCategoryEntryDao(database: JetcasterDatabase): PodcastCategoryEntryDao = database.podcastCategoryEntryDao() @Provides @Singleton - fun providePodcastsDao( - database: JetcasterDatabase - ): PodcastsDao = database.podcastsDao() + fun providePodcastsDao(database: JetcasterDatabase): PodcastsDao = database.podcastsDao() @Provides @Singleton - fun provideEpisodesDao( - database: JetcasterDatabase - ): EpisodesDao = database.episodesDao() + fun provideEpisodesDao(database: JetcasterDatabase): EpisodesDao = database.episodesDao() @Provides @Singleton - fun providePodcastFollowedEntryDao( - database: JetcasterDatabase - ): PodcastFollowedEntryDao = database.podcastFollowedEntryDao() + fun providePodcastFollowedEntryDao(database: JetcasterDatabase): PodcastFollowedEntryDao = database.podcastFollowedEntryDao() @Provides @Singleton - fun provideTransactionRunner( - database: JetcasterDatabase - ): TransactionRunner = database.transactionRunnerDao() + fun provideTransactionRunner(database: JetcasterDatabase): TransactionRunner = database.transactionRunnerDao() @Provides @Singleton @@ -136,9 +118,7 @@ object DataDiModule { @Provides @Singleton - fun provideEpisodeStore( - episodeDao: EpisodesDao - ): EpisodeStore = LocalEpisodeStore(episodeDao) + fun provideEpisodeStore(episodeDao: EpisodesDao): EpisodeStore = LocalEpisodeStore(episodeDao) @Provides @Singleton @@ -149,7 +129,7 @@ object DataDiModule { ): PodcastStore = LocalPodcastStore( podcastDao = podcastDao, podcastFollowedEntryDao = podcastFollowedEntryDao, - transactionRunner = transactionRunner + transactionRunner = transactionRunner, ) @Provides diff --git a/Jetcaster/core/data/src/main/java/com/example/jetcaster/core/data/network/Feeds.kt b/Jetcaster/core/data/src/main/java/com/example/jetcaster/core/data/network/Feeds.kt index 216cce6b9..ff1ef0dc0 100644 --- a/Jetcaster/core/data/src/main/java/com/example/jetcaster/core/data/network/Feeds.kt +++ b/Jetcaster/core/data/src/main/java/com/example/jetcaster/core/data/network/Feeds.kt @@ -43,5 +43,5 @@ val SampleFeeds = listOf( "https://audioboom.com/channels/5025217.rss", "https://feeds.simplecast.com/7PvD7RPL", "https://feeds.buzzsprout.com/1006078.rss", - "https://feeds.megaphone.fm/HSW9992617712" + "https://feeds.megaphone.fm/HSW9992617712", ) diff --git a/Jetcaster/core/data/src/main/java/com/example/jetcaster/core/data/network/OkHttpExtensions.kt b/Jetcaster/core/data/src/main/java/com/example/jetcaster/core/data/network/OkHttpExtensions.kt index e572f23ab..b0b75933c 100644 --- a/Jetcaster/core/data/src/main/java/com/example/jetcaster/core/data/network/OkHttpExtensions.kt +++ b/Jetcaster/core/data/src/main/java/com/example/jetcaster/core/data/network/OkHttpExtensions.kt @@ -45,7 +45,7 @@ suspend fun Call.await(): Response = suspendCancellableCoroutine { continuation override fun onFailure(call: Call, e: IOException) { continuation.resumeWithException(e) } - } + }, ) continuation.invokeOnCancellation { diff --git a/Jetcaster/core/data/src/main/java/com/example/jetcaster/core/data/network/PodcastFetcher.kt b/Jetcaster/core/data/src/main/java/com/example/jetcaster/core/data/network/PodcastFetcher.kt index 459394743..ea74d9980 100644 --- a/Jetcaster/core/data/src/main/java/com/example/jetcaster/core/data/network/PodcastFetcher.kt +++ b/Jetcaster/core/data/src/main/java/com/example/jetcaster/core/data/network/PodcastFetcher.kt @@ -54,7 +54,7 @@ import okhttp3.Request class PodcastsFetcher @Inject constructor( private val okHttpClient: OkHttpClient, private val syndFeedInput: SyndFeedInput, - @Dispatcher(JetcasterDispatchers.IO) private val ioDispatcher: CoroutineDispatcher + @Dispatcher(JetcasterDispatchers.IO) private val ioDispatcher: CoroutineDispatcher, ) { /** @@ -109,15 +109,9 @@ class PodcastsFetcher @Inject constructor( } sealed class PodcastRssResponse { - data class Error( - val throwable: Throwable?, - ) : PodcastRssResponse() - - data class Success( - val podcast: Podcast, - val episodes: List, - val categories: Set - ) : PodcastRssResponse() + data class Error(val throwable: Throwable?) : PodcastRssResponse() + + data class Success(val podcast: Podcast, val episodes: List, val categories: Set) : PodcastRssResponse() } /** @@ -134,7 +128,7 @@ private fun SyndFeed.toPodcastResponse(feedUrl: String): PodcastRssResponse { description = feedInfo?.summary ?: description, author = author, copyright = copyright, - imageUrl = feedInfo?.imageUri?.toString() + imageUrl = feedInfo?.imageUri?.toString(), ) val categories = feedInfo?.categories @@ -147,10 +141,7 @@ private fun SyndFeed.toPodcastResponse(feedUrl: String): PodcastRssResponse { /** * Map a Rome [SyndEntry] instance to our own [Episode] data class. */ -private fun SyndEntry.toEpisode( - podcastUri: String, - enclosures: List -): Episode { +private fun SyndEntry.toEpisode(podcastUri: String, enclosures: List): Episode { val entryInformation = getModule(PodcastModuleDtd) as? EntryInformation return Episode( uri = uri, @@ -161,7 +152,7 @@ private fun SyndEntry.toEpisode( subtitle = entryInformation?.subtitle, published = Instant.ofEpochMilli(publishedDate.time).atOffset(ZoneOffset.UTC), duration = entryInformation?.duration?.milliseconds?.let { Duration.ofMillis(it) }, - mediaUrls = enclosures.map { it.url } + mediaUrls = enclosures.map { it.url }, ) } diff --git a/Jetcaster/core/data/src/main/java/com/example/jetcaster/core/data/repository/CategoryStore.kt b/Jetcaster/core/data/src/main/java/com/example/jetcaster/core/data/repository/CategoryStore.kt index 0c2918805..820f61af7 100644 --- a/Jetcaster/core/data/src/main/java/com/example/jetcaster/core/data/repository/CategoryStore.kt +++ b/Jetcaster/core/data/src/main/java/com/example/jetcaster/core/data/repository/CategoryStore.kt @@ -30,27 +30,19 @@ interface CategoryStore { * Returns a flow containing a list of categories which is sorted by the number * of podcasts in each category. */ - fun categoriesSortedByPodcastCount( - limit: Int = Integer.MAX_VALUE - ): Flow> + fun categoriesSortedByPodcastCount(limit: Int = Integer.MAX_VALUE): Flow> /** * Returns a flow containing a list of podcasts in the category with the given [categoryId], * sorted by the their last episode date. */ - fun podcastsInCategorySortedByPodcastCount( - categoryId: Long, - limit: Int = Int.MAX_VALUE - ): Flow> + fun podcastsInCategorySortedByPodcastCount(categoryId: Long, limit: Int = Int.MAX_VALUE): Flow> /** * Returns a flow containing a list of episodes from podcasts in the category with the * given [categoryId], sorted by the their last episode date. */ - fun episodesFromPodcastsInCategory( - categoryId: Long, - limit: Int = Integer.MAX_VALUE - ): Flow> + fun episodesFromPodcastsInCategory(categoryId: Long, limit: Int = Integer.MAX_VALUE): Flow> /** * Adds the category to the database if it doesn't already exist. @@ -74,7 +66,7 @@ class LocalCategoryStore constructor( private val categoriesDao: CategoriesDao, private val categoryEntryDao: PodcastCategoryEntryDao, private val episodesDao: EpisodesDao, - private val podcastsDao: PodcastsDao + private val podcastsDao: PodcastsDao, ) : CategoryStore { /** * Returns a flow containing a list of categories which is sorted by the number @@ -88,10 +80,7 @@ class LocalCategoryStore constructor( * Returns a flow containing a list of podcasts in the category with the given [categoryId], * sorted by the their last episode date. */ - override fun podcastsInCategorySortedByPodcastCount( - categoryId: Long, - limit: Int - ): Flow> { + override fun podcastsInCategorySortedByPodcastCount(categoryId: Long, limit: Int): Flow> { return podcastsDao.podcastsInCategorySortedByLastEpisode(categoryId, limit) } @@ -99,10 +88,7 @@ class LocalCategoryStore constructor( * Returns a flow containing a list of episodes from podcasts in the category with the * given [categoryId], sorted by the their last episode date. */ - override fun episodesFromPodcastsInCategory( - categoryId: Long, - limit: Int - ): Flow> { + override fun episodesFromPodcastsInCategory(categoryId: Long, limit: Int): Flow> { return episodesDao.episodesFromPodcastsInCategory(categoryId, limit) } @@ -120,10 +106,9 @@ class LocalCategoryStore constructor( override suspend fun addPodcastToCategory(podcastUri: String, categoryId: Long) { categoryEntryDao.insert( - PodcastCategoryEntry(podcastUri = podcastUri, categoryId = categoryId) + PodcastCategoryEntry(podcastUri = podcastUri, categoryId = categoryId), ) } - override fun getCategory(name: String): Flow = - categoriesDao.observeCategory(name) + override fun getCategory(name: String): Flow = categoriesDao.observeCategory(name) } diff --git a/Jetcaster/core/data/src/main/java/com/example/jetcaster/core/data/repository/EpisodeStore.kt b/Jetcaster/core/data/src/main/java/com/example/jetcaster/core/data/repository/EpisodeStore.kt index e854e158a..126c6a5bf 100644 --- a/Jetcaster/core/data/src/main/java/com/example/jetcaster/core/data/repository/EpisodeStore.kt +++ b/Jetcaster/core/data/src/main/java/com/example/jetcaster/core/data/repository/EpisodeStore.kt @@ -36,19 +36,13 @@ interface EpisodeStore { * Returns a flow containing the list of episodes associated with the podcast with the * given [podcastUri]. */ - fun episodesInPodcast( - podcastUri: String, - limit: Int = Integer.MAX_VALUE - ): Flow> + fun episodesInPodcast(podcastUri: String, limit: Int = Integer.MAX_VALUE): Flow> /** * Returns a list of episodes for the given podcast URIs ordering by most recently published * to least recently published. */ - fun episodesInPodcasts( - podcastUris: List, - limit: Int = Integer.MAX_VALUE - ): Flow> + fun episodesInPodcasts(podcastUris: List, limit: Int = Integer.MAX_VALUE): Flow> /** * Add a new [Episode] to this store. @@ -68,9 +62,7 @@ interface EpisodeStore { /** * A data repository for [Episode] instances. */ -class LocalEpisodeStore( - private val episodesDao: EpisodesDao -) : EpisodeStore { +class LocalEpisodeStore(private val episodesDao: EpisodesDao) : EpisodeStore { /** * Returns a flow containing the episode given [episodeUri]. */ @@ -78,17 +70,13 @@ class LocalEpisodeStore( return episodesDao.episode(episodeUri) } - override fun episodeAndPodcastWithUri(episodeUri: String): Flow = - episodesDao.episodeAndPodcast(episodeUri) + override fun episodeAndPodcastWithUri(episodeUri: String): Flow = episodesDao.episodeAndPodcast(episodeUri) /** * Returns a flow containing the list of episodes associated with the podcast with the * given [podcastUri]. */ - override fun episodesInPodcast( - podcastUri: String, - limit: Int - ): Flow> { + override fun episodesInPodcast(podcastUri: String, limit: Int): Flow> { return episodesDao.episodesForPodcastUri(podcastUri, limit) } @@ -96,10 +84,7 @@ class LocalEpisodeStore( * Returns a list of episodes for the given podcast URIs ordering by most recently published * to least recently published. */ - override fun episodesInPodcasts( - podcastUris: List, - limit: Int - ): Flow> = + override fun episodesInPodcasts(podcastUris: List, limit: Int): Flow> = episodesDao.episodesForPodcasts(podcastUris, limit) /** @@ -107,8 +92,7 @@ class LocalEpisodeStore( * * This automatically switches to the main thread to maintain thread consistency. */ - override suspend fun addEpisodes(episodes: Collection) = - episodesDao.insertAll(episodes) + override suspend fun addEpisodes(episodes: Collection) = episodesDao.insertAll(episodes) /** * Deletes an [Episode] from this store. diff --git a/Jetcaster/core/data/src/main/java/com/example/jetcaster/core/data/repository/PodcastStore.kt b/Jetcaster/core/data/src/main/java/com/example/jetcaster/core/data/repository/PodcastStore.kt index ee809c9e3..57e4328c6 100644 --- a/Jetcaster/core/data/src/main/java/com/example/jetcaster/core/data/repository/PodcastStore.kt +++ b/Jetcaster/core/data/src/main/java/com/example/jetcaster/core/data/repository/PodcastStore.kt @@ -40,26 +40,19 @@ interface PodcastStore { * Returns a flow containing the entire collection of podcasts, sorted by the last episode * publish date for each podcast. */ - fun podcastsSortedByLastEpisode( - limit: Int = Int.MAX_VALUE - ): Flow> + fun podcastsSortedByLastEpisode(limit: Int = Int.MAX_VALUE): Flow> /** * Returns a flow containing a list of all followed podcasts, sorted by the their last * episode date. */ - fun followedPodcastsSortedByLastEpisode( - limit: Int = Int.MAX_VALUE - ): Flow> + fun followedPodcastsSortedByLastEpisode(limit: Int = Int.MAX_VALUE): Flow> /** * Returns a flow containing a list of podcasts such that its name partially matches * with the specified keyword */ - fun searchPodcastByTitle( - keyword: String, - limit: Int = Int.MAX_VALUE - ): Flow> + fun searchPodcastByTitle(keyword: String, limit: Int = Int.MAX_VALUE): Flow> /** * Return a flow containing a list of podcast such that it belongs to the any of categories @@ -69,7 +62,7 @@ interface PodcastStore { fun searchPodcastByTitleAndCategories( keyword: String, categories: List, - limit: Int = Int.MAX_VALUE + limit: Int = Int.MAX_VALUE, ): Flow> suspend fun togglePodcastFollowed(podcastUri: String) @@ -94,7 +87,7 @@ interface PodcastStore { class LocalPodcastStore constructor( private val podcastDao: PodcastsDao, private val podcastFollowedEntryDao: PodcastFollowedEntryDao, - private val transactionRunner: TransactionRunner + private val transactionRunner: TransactionRunner, ) : PodcastStore { /** * Return a flow containing the [Podcast] with the given [uri]. @@ -106,16 +99,13 @@ class LocalPodcastStore constructor( /** * Return a flow containing the [PodcastWithExtraInfo] with the given [podcastUri]. */ - override fun podcastWithExtraInfo(podcastUri: String): Flow = - podcastDao.podcastWithExtraInfo(podcastUri) + override fun podcastWithExtraInfo(podcastUri: String): Flow = podcastDao.podcastWithExtraInfo(podcastUri) /** * Returns a flow containing the entire collection of podcasts, sorted by the last episode * publish date for each podcast. */ - override fun podcastsSortedByLastEpisode( - limit: Int - ): Flow> { + override fun podcastsSortedByLastEpisode(limit: Int): Flow> { return podcastDao.podcastsSortedByLastEpisode(limit) } @@ -123,23 +113,18 @@ class LocalPodcastStore constructor( * Returns a flow containing a list of all followed podcasts, sorted by the their last * episode date. */ - override fun followedPodcastsSortedByLastEpisode( - limit: Int - ): Flow> { + override fun followedPodcastsSortedByLastEpisode(limit: Int): Flow> { return podcastDao.followedPodcastsSortedByLastEpisode(limit) } - override fun searchPodcastByTitle( - keyword: String, - limit: Int - ): Flow> { + override fun searchPodcastByTitle(keyword: String, limit: Int): Flow> { return podcastDao.searchPodcastByTitle(keyword, limit) } override fun searchPodcastByTitleAndCategories( keyword: String, categories: List, - limit: Int + limit: Int, ): Flow> { val categoryIdList = categories.map { it.id } return podcastDao.searchPodcastByTitleAndCategory(keyword, categoryIdList, limit) diff --git a/Jetcaster/core/data/src/main/java/com/example/jetcaster/core/data/repository/PodcastsRepository.kt b/Jetcaster/core/data/src/main/java/com/example/jetcaster/core/data/repository/PodcastsRepository.kt index a102caf96..60dd6c4b0 100644 --- a/Jetcaster/core/data/src/main/java/com/example/jetcaster/core/data/repository/PodcastsRepository.kt +++ b/Jetcaster/core/data/src/main/java/com/example/jetcaster/core/data/repository/PodcastsRepository.kt @@ -39,7 +39,7 @@ class PodcastsRepository @Inject constructor( private val episodeStore: EpisodeStore, private val categoryStore: CategoryStore, private val transactionRunner: TransactionRunner, - @Dispatcher(JetcasterDispatchers.Main) mainDispatcher: CoroutineDispatcher + @Dispatcher(JetcasterDispatchers.Main) mainDispatcher: CoroutineDispatcher, ) { private var refreshingJob: Job? = null @@ -65,7 +65,7 @@ class PodcastsRepository @Inject constructor( // Now we can add the podcast to the category categoryStore.addPodcastToCategory( podcastUri = podcast.uri, - categoryId = categoryId + categoryId = categoryId, ) } } diff --git a/Jetcaster/core/data/src/main/java/com/example/jetcaster/core/util/Flows.kt b/Jetcaster/core/data/src/main/java/com/example/jetcaster/core/util/Flows.kt index a9940f315..f03fc493f 100644 --- a/Jetcaster/core/data/src/main/java/com/example/jetcaster/core/util/Flows.kt +++ b/Jetcaster/core/data/src/main/java/com/example/jetcaster/core/util/Flows.kt @@ -32,23 +32,17 @@ fun combine( flow3: Flow, flow4: Flow, flow5: Flow, - transform: suspend (T1, T2, T3, T4, T5) -> R -): Flow = - kotlinx.coroutines.flow.combine(flow, flow2, flow3, flow4, flow5) { args: Array<*> -> - transform( - args[0] as T1, - args[1] as T2, - args[2] as T3, - args[3] as T4, - args[4] as T5, - ) - } -fun combine( - flow: Flow, - flow2: Flow, - - transform: suspend (T1, T2) -> R -): Flow = + transform: suspend (T1, T2, T3, T4, T5) -> R, +): Flow = kotlinx.coroutines.flow.combine(flow, flow2, flow3, flow4, flow5) { args: Array<*> -> + transform( + args[0] as T1, + args[1] as T2, + args[2] as T3, + args[3] as T4, + args[4] as T5, + ) +} +fun combine(flow: Flow, flow2: Flow, transform: suspend (T1, T2) -> R): Flow = kotlinx.coroutines.flow.combine(flow, flow2) { args: Array<*> -> transform( args[0] as T1, @@ -75,18 +69,17 @@ fun combine( flow4: Flow, flow5: Flow, flow6: Flow, - transform: suspend (T1, T2, T3, T4, T5, T6) -> R -): Flow = - kotlinx.coroutines.flow.combine(flow, flow2, flow3, flow4, flow5, flow6) { args: Array<*> -> - transform( - args[0] as T1, - args[1] as T2, - args[2] as T3, - args[3] as T4, - args[4] as T5, - args[5] as T6, - ) - } + transform: suspend (T1, T2, T3, T4, T5, T6) -> R, +): Flow = kotlinx.coroutines.flow.combine(flow, flow2, flow3, flow4, flow5, flow6) { args: Array<*> -> + transform( + args[0] as T1, + args[1] as T2, + args[2] as T3, + args[3] as T4, + args[4] as T5, + args[5] as T6, + ) +} /** * Combines seven flows into a single flow by combining their latest values using the provided transform function. @@ -109,24 +102,23 @@ fun combine( flow5: Flow, flow6: Flow, flow7: Flow, - transform: suspend (T1, T2, T3, T4, T5, T6, T7) -> R -): Flow = - kotlinx.coroutines.flow.combine( - flow, - flow2, - flow3, - flow4, - flow5, - flow6, - flow7 - ) { args: Array<*> -> - transform( - args[0] as T1, - args[1] as T2, - args[2] as T3, - args[3] as T4, - args[4] as T5, - args[5] as T6, - args[6] as T7, - ) - } + transform: suspend (T1, T2, T3, T4, T5, T6, T7) -> R, +): Flow = kotlinx.coroutines.flow.combine( + flow, + flow2, + flow3, + flow4, + flow5, + flow6, + flow7, +) { args: Array<*> -> + transform( + args[0] as T1, + args[1] as T2, + args[2] as T3, + args[3] as T4, + args[4] as T5, + args[5] as T6, + args[6] as T7, + ) +} diff --git a/Jetcaster/core/designsystem/build.gradle.kts b/Jetcaster/core/designsystem/build.gradle.kts index 3e4ba24cb..8d3650b7e 100644 --- a/Jetcaster/core/designsystem/build.gradle.kts +++ b/Jetcaster/core/designsystem/build.gradle.kts @@ -1,51 +1,74 @@ +/* + * Copyright 2025 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + plugins { - alias(libs.plugins.android.library) - alias(libs.plugins.kotlin.android) - alias(libs.plugins.compose) + alias(libs.plugins.android.library) + alias(libs.plugins.kotlin.android) + alias(libs.plugins.compose) } // TODO(chris): Set up convention plugin android { - namespace = "com.example.jetcaster.core.designsystem" - compileSdk = libs.versions.compileSdk.get().toInt() - - defaultConfig { - minSdk = libs.versions.minSdk.get().toInt() - vectorDrawables.useSupportLibrary = true - testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" - consumerProguardFiles("consumer-rules.pro") - } - - buildTypes { - release { - isMinifyEnabled = false - proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro") + namespace = "com.example.jetcaster.core.designsystem" + compileSdk = + libs.versions.compileSdk + .get() + .toInt() + + defaultConfig { + minSdk = + libs.versions.minSdk + .get() + .toInt() + vectorDrawables.useSupportLibrary = true + testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" + consumerProguardFiles("consumer-rules.pro") + } + + buildTypes { + release { + isMinifyEnabled = false + proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro") + } } - } - buildFeatures { - compose = true - buildConfig = true - } + buildFeatures { + compose = true + buildConfig = true + } - compileOptions { - sourceCompatibility = JavaVersion.VERSION_17 - targetCompatibility = JavaVersion.VERSION_17 - } + compileOptions { + sourceCompatibility = JavaVersion.VERSION_17 + targetCompatibility = JavaVersion.VERSION_17 + } } kotlin { - jvmToolchain(17) + jvmToolchain(17) } dependencies { - val composeBom = platform(libs.androidx.compose.bom) - implementation(composeBom) - implementation(libs.androidx.compose.foundation) - implementation(libs.androidx.compose.material3) - implementation(libs.androidx.compose.ui.graphics) - implementation(libs.androidx.compose.ui.text) - implementation(libs.coil.kt.compose) - - implementation(libs.androidx.core.ktx) - implementation(libs.androidx.appcompat) + val composeBom = platform(libs.androidx.compose.bom) + implementation(composeBom) + implementation(libs.androidx.compose.foundation) + implementation(libs.androidx.compose.material3) + implementation(libs.androidx.compose.ui.graphics) + implementation(libs.androidx.compose.ui.text) + implementation(libs.coil.kt.compose) + + implementation(libs.androidx.core.ktx) + implementation(libs.androidx.appcompat) } diff --git a/Jetcaster/core/designsystem/src/main/java/com/example/jetcaster/designsystem/component/HtmlTextContainer.kt b/Jetcaster/core/designsystem/src/main/java/com/example/jetcaster/designsystem/component/HtmlTextContainer.kt index e0e91040f..b9e8bd06b 100644 --- a/Jetcaster/core/designsystem/src/main/java/com/example/jetcaster/designsystem/component/HtmlTextContainer.kt +++ b/Jetcaster/core/designsystem/src/main/java/com/example/jetcaster/designsystem/component/HtmlTextContainer.kt @@ -27,10 +27,7 @@ import androidx.compose.ui.text.fromHtml * annotated string from [text], and enable text selection if [text] has any selectable element. */ @Composable -fun HtmlTextContainer( - text: String, - content: @Composable (AnnotatedString) -> Unit -) { +fun HtmlTextContainer(text: String, content: @Composable (AnnotatedString) -> Unit) { val annotatedString = remember(key1 = text) { AnnotatedString.fromHtml(htmlString = text) } diff --git a/Jetcaster/core/designsystem/src/main/java/com/example/jetcaster/designsystem/component/ImageBackground.kt b/Jetcaster/core/designsystem/src/main/java/com/example/jetcaster/designsystem/component/ImageBackground.kt index 4cb124dc6..28bd129d7 100644 --- a/Jetcaster/core/designsystem/src/main/java/com/example/jetcaster/designsystem/component/ImageBackground.kt +++ b/Jetcaster/core/designsystem/src/main/java/com/example/jetcaster/designsystem/component/ImageBackground.kt @@ -29,26 +29,18 @@ import androidx.compose.ui.layout.ContentScale import coil.compose.AsyncImage @Composable -fun ImageBackgroundColorScrim( - url: String?, - color: Color, - modifier: Modifier = Modifier, -) { +fun ImageBackgroundColorScrim(url: String?, color: Color, modifier: Modifier = Modifier) { ImageBackground( url = url, modifier = modifier, overlay = { drawRect(color) - } + }, ) } @Composable -fun ImageBackgroundRadialGradientScrim( - url: String?, - colors: List, - modifier: Modifier = Modifier, -) { +fun ImageBackgroundRadialGradientScrim(url: String?, colors: List, modifier: Modifier = Modifier) { ImageBackground( url = url, modifier = modifier, @@ -56,10 +48,10 @@ fun ImageBackgroundRadialGradientScrim( val brush = Brush.radialGradient( colors = colors, center = Offset(0f, size.height), - radius = size.width * 1.5f + radius = size.width * 1.5f, ) drawRect(brush, blendMode = BlendMode.Multiply) - } + }, ) } @@ -67,11 +59,7 @@ fun ImageBackgroundRadialGradientScrim( * Displays an image scaled 150% overlaid by [overlay] */ @Composable -fun ImageBackground( - url: String?, - overlay: DrawScope.() -> Unit, - modifier: Modifier = Modifier, -) { +fun ImageBackground(url: String?, overlay: DrawScope.() -> Unit, modifier: Modifier = Modifier) { AsyncImage( model = url, contentDescription = null, @@ -83,6 +71,6 @@ fun ImageBackground( drawContent() overlay() } - } + }, ) } diff --git a/Jetcaster/core/designsystem/src/main/java/com/example/jetcaster/designsystem/component/PodcastImage.kt b/Jetcaster/core/designsystem/src/main/java/com/example/jetcaster/designsystem/component/PodcastImage.kt index 7432207af..a17d35fa8 100644 --- a/Jetcaster/core/designsystem/src/main/java/com/example/jetcaster/designsystem/component/PodcastImage.kt +++ b/Jetcaster/core/designsystem/src/main/java/com/example/jetcaster/designsystem/component/PodcastImage.kt @@ -63,28 +63,29 @@ fun PodcastImage( .crossfade(true) .build(), contentScale = contentScale, - onState = { state -> imagePainterState = state } + onState = { state -> imagePainterState = state }, ) Box( modifier = modifier, - contentAlignment = Alignment.Center + contentAlignment = Alignment.Center, ) { when (imagePainterState) { is AsyncImagePainter.State.Loading, - is AsyncImagePainter.State.Error -> { + is AsyncImagePainter.State.Error, + -> { Image( painter = painterResource(id = R.drawable.img_empty), contentDescription = null, modifier = Modifier - .fillMaxSize() + .fillMaxSize(), ) } else -> { Box( modifier = modifier .background(placeholderBrush) - .fillMaxSize() + .fillMaxSize(), ) } @@ -94,7 +95,7 @@ fun PodcastImage( painter = imageLoader, contentDescription = contentDescription, contentScale = contentScale, - modifier = modifier.then(imageModifier) + modifier = modifier.then(imageModifier), ) } } diff --git a/Jetcaster/core/designsystem/src/main/java/com/example/jetcaster/designsystem/component/thumbnailPlaceholder.kt b/Jetcaster/core/designsystem/src/main/java/com/example/jetcaster/designsystem/component/thumbnailPlaceholder.kt index 865dac313..80f1f21bc 100644 --- a/Jetcaster/core/designsystem/src/main/java/com/example/jetcaster/designsystem/component/thumbnailPlaceholder.kt +++ b/Jetcaster/core/designsystem/src/main/java/com/example/jetcaster/designsystem/component/thumbnailPlaceholder.kt @@ -25,16 +25,12 @@ import com.example.jetcaster.designsystem.theme.surfaceVariantDark import com.example.jetcaster.designsystem.theme.surfaceVariantLight @Composable -internal fun thumbnailPlaceholderDefaultBrush( - color: Color = thumbnailPlaceHolderDefaultColor() -): Brush { +internal fun thumbnailPlaceholderDefaultBrush(color: Color = thumbnailPlaceHolderDefaultColor()): Brush { return SolidColor(color) } @Composable -private fun thumbnailPlaceHolderDefaultColor( - isInDarkMode: Boolean = isSystemInDarkTheme() -): Color { +private fun thumbnailPlaceHolderDefaultColor(isInDarkMode: Boolean = isSystemInDarkTheme()): Color { return if (isInDarkMode) { surfaceVariantDark } else { diff --git a/Jetcaster/core/designsystem/src/main/java/com/example/jetcaster/designsystem/theme/Shape.kt b/Jetcaster/core/designsystem/src/main/java/com/example/jetcaster/designsystem/theme/Shape.kt index 41bbfefbb..a5f74d71d 100644 --- a/Jetcaster/core/designsystem/src/main/java/com/example/jetcaster/designsystem/theme/Shape.kt +++ b/Jetcaster/core/designsystem/src/main/java/com/example/jetcaster/designsystem/theme/Shape.kt @@ -23,5 +23,5 @@ import androidx.compose.ui.unit.dp val JetcasterShapes = Shapes( small = RoundedCornerShape(percent = 50), medium = RoundedCornerShape(size = 8.dp), - large = RoundedCornerShape(size = 16.dp) + large = RoundedCornerShape(size = 16.dp), ) diff --git a/Jetcaster/core/designsystem/src/main/java/com/example/jetcaster/designsystem/theme/Type.kt b/Jetcaster/core/designsystem/src/main/java/com/example/jetcaster/designsystem/theme/Type.kt index 00df46b46..be704c3eb 100644 --- a/Jetcaster/core/designsystem/src/main/java/com/example/jetcaster/designsystem/theme/Type.kt +++ b/Jetcaster/core/designsystem/src/main/java/com/example/jetcaster/designsystem/theme/Type.kt @@ -33,92 +33,92 @@ val JetcasterTypography = androidx.compose.material3.Typography( fontFamily = RobotoFlex, fontSize = 45.sp, fontWeight = FontWeight.W400, - lineHeight = 52.sp + lineHeight = 52.sp, ), displaySmall = TextStyle( fontFamily = Montserrat, fontSize = 36.sp, fontWeight = FontWeight.W400, - lineHeight = 44.sp + lineHeight = 44.sp, ), headlineLarge = TextStyle( fontFamily = Montserrat, fontSize = 32.sp, fontWeight = FontWeight.W500, - lineHeight = 40.sp + lineHeight = 40.sp, ), headlineMedium = TextStyle( fontFamily = Montserrat, fontSize = 28.sp, fontWeight = FontWeight.W500, - lineHeight = 36.sp + lineHeight = 36.sp, ), headlineSmall = TextStyle( fontFamily = Montserrat, fontSize = 24.sp, fontWeight = FontWeight.W500, - lineHeight = 32.sp + lineHeight = 32.sp, ), titleLarge = TextStyle( fontFamily = Montserrat, fontSize = 22.sp, fontWeight = FontWeight.W400, - lineHeight = 28.sp + lineHeight = 28.sp, ), titleMedium = TextStyle( fontFamily = Montserrat, fontSize = 16.sp, fontWeight = FontWeight.W500, lineHeight = 24.sp, - letterSpacing = 0.15.sp + letterSpacing = 0.15.sp, ), titleSmall = TextStyle( fontFamily = Montserrat, fontSize = 14.sp, fontWeight = FontWeight.W500, lineHeight = 20.sp, - letterSpacing = 0.1.sp + letterSpacing = 0.1.sp, ), labelLarge = TextStyle( fontFamily = Montserrat, fontSize = 14.sp, fontWeight = FontWeight.W500, lineHeight = 20.sp, - letterSpacing = 0.1.sp + letterSpacing = 0.1.sp, ), labelMedium = TextStyle( fontFamily = Montserrat, fontSize = 12.sp, fontWeight = FontWeight.W500, lineHeight = 16.sp, - letterSpacing = 0.5.sp + letterSpacing = 0.5.sp, ), labelSmall = TextStyle( fontFamily = Montserrat, fontSize = 11.sp, fontWeight = FontWeight.W500, lineHeight = 16.sp, - letterSpacing = 0.5.sp + letterSpacing = 0.5.sp, ), bodyLarge = TextStyle( fontFamily = Montserrat, fontSize = 16.sp, fontWeight = FontWeight.W500, lineHeight = 24.sp, - letterSpacing = 0.5.sp + letterSpacing = 0.5.sp, ), bodyMedium = TextStyle( fontFamily = Montserrat, fontSize = 14.sp, fontWeight = FontWeight.W500, lineHeight = 20.sp, - letterSpacing = 0.25.sp + letterSpacing = 0.25.sp, ), bodySmall = TextStyle( fontFamily = Montserrat, fontSize = 12.sp, fontWeight = FontWeight.W500, lineHeight = 16.sp, - letterSpacing = 0.4.sp + letterSpacing = 0.4.sp, ), ) diff --git a/Jetcaster/core/designsystem/src/main/java/com/example/jetcaster/designsystem/theme/Typography.kt b/Jetcaster/core/designsystem/src/main/java/com/example/jetcaster/designsystem/theme/Typography.kt index df7b2e645..4f494ccb2 100644 --- a/Jetcaster/core/designsystem/src/main/java/com/example/jetcaster/designsystem/theme/Typography.kt +++ b/Jetcaster/core/designsystem/src/main/java/com/example/jetcaster/designsystem/theme/Typography.kt @@ -25,10 +25,9 @@ val Montserrat = FontFamily( Font(R.font.montserrat_light, FontWeight.Light), Font(R.font.montserrat_regular, FontWeight.Normal), Font(R.font.montserrat_medium, FontWeight.Medium), - Font(R.font.montserrat_semibold, FontWeight.SemiBold) + Font(R.font.montserrat_semibold, FontWeight.SemiBold), ) val RobotoFlex = FontFamily( - Font(R.font.roboto_flex) + Font(R.font.roboto_flex), ) - diff --git a/Jetcaster/core/domain-testing/build.gradle.kts b/Jetcaster/core/domain-testing/build.gradle.kts index e80955a71..56cdb8f86 100644 --- a/Jetcaster/core/domain-testing/build.gradle.kts +++ b/Jetcaster/core/domain-testing/build.gradle.kts @@ -1,3 +1,20 @@ +/* + * Copyright 2025 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + plugins { alias(libs.plugins.android.library) alias(libs.plugins.kotlin.android) diff --git a/Jetcaster/core/domain-testing/src/main/java/com/example/jetcaster/core/domain/testing/PreviewData.kt b/Jetcaster/core/domain-testing/src/main/java/com/example/jetcaster/core/domain/testing/PreviewData.kt index de1dfde9a..c53c71713 100644 --- a/Jetcaster/core/domain-testing/src/main/java/com/example/jetcaster/core/domain/testing/PreviewData.kt +++ b/Jetcaster/core/domain-testing/src/main/java/com/example/jetcaster/core/domain/testing/PreviewData.kt @@ -27,7 +27,7 @@ import java.time.ZoneOffset val PreviewCategories = listOf( CategoryInfo(id = 1, name = "Crime"), CategoryInfo(id = 2, name = "News"), - CategoryInfo(id = 3, name = "Comedy") + CategoryInfo(id = 3, name = "Comedy"), ) val PreviewPodcasts = listOf( @@ -36,14 +36,14 @@ val PreviewPodcasts = listOf( title = "Android Developers Backstage", author = "Android Developers", isSubscribed = true, - lastEpisodeDate = OffsetDateTime.now() + lastEpisodeDate = OffsetDateTime.now(), ), PodcastInfo( uri = "fakeUri://podcast/2", title = "Google Developers podcast", author = "Google Developers", - lastEpisodeDate = OffsetDateTime.now() - ) + lastEpisodeDate = OffsetDateTime.now(), + ), ) val PreviewEpisodes = listOf( @@ -54,21 +54,21 @@ val PreviewEpisodes = listOf( "Tsurkan from the System UI team about... Bubbles!", published = OffsetDateTime.of( 2020, 6, 2, 9, - 27, 0, 0, ZoneOffset.of("-0800") - ) - ) + 27, 0, 0, ZoneOffset.of("-0800"), + ), + ), ) val PreviewPlayerEpisodes = listOf( PlayerEpisode( PreviewPodcasts[0], - PreviewEpisodes[0] - ) + PreviewEpisodes[0], + ), ) val PreviewPodcastEpisodes = listOf( PodcastToEpisodeInfo( podcast = PreviewPodcasts[0], episode = PreviewEpisodes[0], - ) + ), ) diff --git a/Jetcaster/core/domain/build.gradle.kts b/Jetcaster/core/domain/build.gradle.kts index d654aee28..4c28b5335 100644 --- a/Jetcaster/core/domain/build.gradle.kts +++ b/Jetcaster/core/domain/build.gradle.kts @@ -1,3 +1,20 @@ +/* + * Copyright 2025 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + plugins { alias(libs.plugins.android.library) alias(libs.plugins.kotlin.android) diff --git a/Jetcaster/core/domain/src/main/java/com/example/jetcaster/core/di/DomainDiModule.kt b/Jetcaster/core/domain/src/main/java/com/example/jetcaster/core/di/DomainDiModule.kt index 13aa949a5..7cd2ce606 100644 --- a/Jetcaster/core/domain/src/main/java/com/example/jetcaster/core/di/DomainDiModule.kt +++ b/Jetcaster/core/domain/src/main/java/com/example/jetcaster/core/di/DomainDiModule.kt @@ -32,7 +32,6 @@ import kotlinx.coroutines.CoroutineDispatcher object DomainDiModule { @Provides @Singleton - fun provideEpisodePlayer( - @Dispatcher(JetcasterDispatchers.Main) mainDispatcher: CoroutineDispatcher - ): EpisodePlayer = MockEpisodePlayer(mainDispatcher) + fun provideEpisodePlayer(@Dispatcher(JetcasterDispatchers.Main) mainDispatcher: CoroutineDispatcher): EpisodePlayer = + MockEpisodePlayer(mainDispatcher) } diff --git a/Jetcaster/core/domain/src/main/java/com/example/jetcaster/core/domain/FilterableCategoriesUseCase.kt b/Jetcaster/core/domain/src/main/java/com/example/jetcaster/core/domain/FilterableCategoriesUseCase.kt index 8b5f1a9b1..255f3458e 100644 --- a/Jetcaster/core/domain/src/main/java/com/example/jetcaster/core/domain/FilterableCategoriesUseCase.kt +++ b/Jetcaster/core/domain/src/main/java/com/example/jetcaster/core/domain/FilterableCategoriesUseCase.kt @@ -27,22 +27,19 @@ import kotlinx.coroutines.flow.map /** * Use case for categories that can be used to filter podcasts. */ -class FilterableCategoriesUseCase @Inject constructor( - private val categoryStore: CategoryStore -) { +class FilterableCategoriesUseCase @Inject constructor(private val categoryStore: CategoryStore) { /** * Created a [FilterableCategoriesModel] from the list of categories in [categoryStore]. * @param selectedCategory the currently selected category. If null, the first category * returned by the backing category list will be selected in the returned * FilterableCategoriesModel */ - operator fun invoke(selectedCategory: CategoryInfo?): Flow = - categoryStore.categoriesSortedByPodcastCount() - .map { categories -> - FilterableCategoriesModel( - categories = categories.map { it.asExternalModel() }, - selectedCategory = selectedCategory - ?: categories.firstOrNull()?.asExternalModel() - ) - } + operator fun invoke(selectedCategory: CategoryInfo?): Flow = categoryStore.categoriesSortedByPodcastCount() + .map { categories -> + FilterableCategoriesModel( + categories = categories.map { it.asExternalModel() }, + selectedCategory = selectedCategory + ?: categories.firstOrNull()?.asExternalModel(), + ) + } } diff --git a/Jetcaster/core/domain/src/main/java/com/example/jetcaster/core/domain/GetLatestFollowedEpisodesUseCase.kt b/Jetcaster/core/domain/src/main/java/com/example/jetcaster/core/domain/GetLatestFollowedEpisodesUseCase.kt index 7e7254525..344c65b94 100644 --- a/Jetcaster/core/domain/src/main/java/com/example/jetcaster/core/domain/GetLatestFollowedEpisodesUseCase.kt +++ b/Jetcaster/core/domain/src/main/java/com/example/jetcaster/core/domain/GetLatestFollowedEpisodesUseCase.kt @@ -32,12 +32,11 @@ class GetLatestFollowedEpisodesUseCase @Inject constructor( private val podcastStore: PodcastStore, ) { @OptIn(ExperimentalCoroutinesApi::class) - operator fun invoke(): Flow> = - podcastStore.followedPodcastsSortedByLastEpisode() - .flatMapLatest { followedPodcasts -> - episodeStore.episodesInPodcasts( - followedPodcasts.map { it.podcast.uri }, - followedPodcasts.size * 5 - ) - } + operator fun invoke(): Flow> = podcastStore.followedPodcastsSortedByLastEpisode() + .flatMapLatest { followedPodcasts -> + episodeStore.episodesInPodcasts( + followedPodcasts.map { it.podcast.uri }, + followedPodcasts.size * 5, + ) + } } diff --git a/Jetcaster/core/domain/src/main/java/com/example/jetcaster/core/domain/PodcastCategoryFilterUseCase.kt b/Jetcaster/core/domain/src/main/java/com/example/jetcaster/core/domain/PodcastCategoryFilterUseCase.kt index 97620a63c..dbfbba91b 100644 --- a/Jetcaster/core/domain/src/main/java/com/example/jetcaster/core/domain/PodcastCategoryFilterUseCase.kt +++ b/Jetcaster/core/domain/src/main/java/com/example/jetcaster/core/domain/PodcastCategoryFilterUseCase.kt @@ -30,9 +30,7 @@ import kotlinx.coroutines.flow.flowOf /** * A use case which returns top podcasts and matching episodes in a given [Category]. */ -class PodcastCategoryFilterUseCase @Inject constructor( - private val categoryStore: CategoryStore -) { +class PodcastCategoryFilterUseCase @Inject constructor(private val categoryStore: CategoryStore) { operator fun invoke(category: CategoryInfo?): Flow { if (category == null) { return flowOf(PodcastCategoryFilterResult()) @@ -40,19 +38,19 @@ class PodcastCategoryFilterUseCase @Inject constructor( val recentPodcastsFlow = categoryStore.podcastsInCategorySortedByPodcastCount( category.id, - limit = 10 + limit = 10, ) val episodesFlow = categoryStore.episodesFromPodcastsInCategory( category.id, - limit = 20 + limit = 20, ) // Combine our flows and collect them into the view state StateFlow return combine(recentPodcastsFlow, episodesFlow) { topPodcasts, episodes -> PodcastCategoryFilterResult( topPodcasts = topPodcasts.map { it.asExternalModel() }, - episodes = episodes.map { it.asPodcastToEpisodeInfo() } + episodes = episodes.map { it.asPodcastToEpisodeInfo() }, ) } } diff --git a/Jetcaster/core/domain/src/main/java/com/example/jetcaster/core/model/CategoryInfo.kt b/Jetcaster/core/domain/src/main/java/com/example/jetcaster/core/model/CategoryInfo.kt index 833ada892..f462e4cb9 100644 --- a/Jetcaster/core/domain/src/main/java/com/example/jetcaster/core/model/CategoryInfo.kt +++ b/Jetcaster/core/domain/src/main/java/com/example/jetcaster/core/model/CategoryInfo.kt @@ -18,15 +18,11 @@ package com.example.jetcaster.core.model import com.example.jetcaster.core.data.database.model.Category -data class CategoryInfo( - val id: Long, - val name: String -) +data class CategoryInfo(val id: Long, val name: String) const val CategoryTechnology = "Technology" -fun Category.asExternalModel() = - CategoryInfo( - id = id, - name = name - ) +fun Category.asExternalModel() = CategoryInfo( + id = id, + name = name, +) diff --git a/Jetcaster/core/domain/src/main/java/com/example/jetcaster/core/model/EpisodeInfo.kt b/Jetcaster/core/domain/src/main/java/com/example/jetcaster/core/model/EpisodeInfo.kt index 68e7c0379..8bf70ed11 100644 --- a/Jetcaster/core/domain/src/main/java/com/example/jetcaster/core/model/EpisodeInfo.kt +++ b/Jetcaster/core/domain/src/main/java/com/example/jetcaster/core/model/EpisodeInfo.kt @@ -35,28 +35,26 @@ data class EpisodeInfo( val mediaUrls: List = emptyList(), ) -fun Episode.asExternalModel(): EpisodeInfo = - EpisodeInfo( - uri = uri, - podcastUri = podcastUri, - title = title, - subTitle = subtitle ?: "", - summary = summary ?: "", - author = author ?: "", - published = published, - duration = duration, - mediaUrls = mediaUrls - ) +fun Episode.asExternalModel(): EpisodeInfo = EpisodeInfo( + uri = uri, + podcastUri = podcastUri, + title = title, + subTitle = subtitle ?: "", + summary = summary ?: "", + author = author ?: "", + published = published, + duration = duration, + mediaUrls = mediaUrls, +) -fun EpisodeInfo.asDaoModel(): Episode = - Episode( - uri = uri, - title = title, - subtitle = subTitle, - summary = summary, - author = author, - published = published, - duration = duration, - podcastUri = podcastUri, - mediaUrls = mediaUrls - ) +fun EpisodeInfo.asDaoModel(): Episode = Episode( + uri = uri, + title = title, + subtitle = subTitle, + summary = summary, + author = author, + published = published, + duration = duration, + podcastUri = podcastUri, + mediaUrls = mediaUrls, +) diff --git a/Jetcaster/core/domain/src/main/java/com/example/jetcaster/core/model/FilterableCategoriesModel.kt b/Jetcaster/core/domain/src/main/java/com/example/jetcaster/core/model/FilterableCategoriesModel.kt index 4cca64694..a35943792 100644 --- a/Jetcaster/core/domain/src/main/java/com/example/jetcaster/core/model/FilterableCategoriesModel.kt +++ b/Jetcaster/core/domain/src/main/java/com/example/jetcaster/core/model/FilterableCategoriesModel.kt @@ -19,9 +19,6 @@ package com.example.jetcaster.core.model /** * Model holding a list of categories and a selected category in the collection */ -data class FilterableCategoriesModel( - val categories: List = emptyList(), - val selectedCategory: CategoryInfo? = null -) { +data class FilterableCategoriesModel(val categories: List = emptyList(), val selectedCategory: CategoryInfo? = null) { val isEmpty = categories.isEmpty() || selectedCategory == null } diff --git a/Jetcaster/core/domain/src/main/java/com/example/jetcaster/core/model/LibraryInfo.kt b/Jetcaster/core/domain/src/main/java/com/example/jetcaster/core/model/LibraryInfo.kt index 5731b07f8..d268ea97c 100644 --- a/Jetcaster/core/domain/src/main/java/com/example/jetcaster/core/model/LibraryInfo.kt +++ b/Jetcaster/core/domain/src/main/java/com/example/jetcaster/core/model/LibraryInfo.kt @@ -16,6 +16,4 @@ package com.example.jetcaster.core.model -data class LibraryInfo( - val episodes: List = emptyList() -) : List by episodes +data class LibraryInfo(val episodes: List = emptyList()) : List by episodes diff --git a/Jetcaster/core/domain/src/main/java/com/example/jetcaster/core/model/PodcastCategoryFilterResult.kt b/Jetcaster/core/domain/src/main/java/com/example/jetcaster/core/model/PodcastCategoryFilterResult.kt index c0e6761ed..871780034 100644 --- a/Jetcaster/core/domain/src/main/java/com/example/jetcaster/core/model/PodcastCategoryFilterResult.kt +++ b/Jetcaster/core/domain/src/main/java/com/example/jetcaster/core/model/PodcastCategoryFilterResult.kt @@ -21,5 +21,5 @@ package com.example.jetcaster.core.model */ data class PodcastCategoryFilterResult( val topPodcasts: List = emptyList(), - val episodes: List = emptyList() + val episodes: List = emptyList(), ) diff --git a/Jetcaster/core/domain/src/main/java/com/example/jetcaster/core/model/PodcastInfo.kt b/Jetcaster/core/domain/src/main/java/com/example/jetcaster/core/model/PodcastInfo.kt index 6f03fe56a..2150c06ec 100644 --- a/Jetcaster/core/domain/src/main/java/com/example/jetcaster/core/model/PodcastInfo.kt +++ b/Jetcaster/core/domain/src/main/java/com/example/jetcaster/core/model/PodcastInfo.kt @@ -33,17 +33,15 @@ data class PodcastInfo( val lastEpisodeDate: OffsetDateTime? = null, ) -fun Podcast.asExternalModel(): PodcastInfo = - PodcastInfo( - uri = this.uri, - title = this.title, - author = this.author ?: "", - imageUrl = this.imageUrl ?: "", - description = this.description ?: "", - ) +fun Podcast.asExternalModel(): PodcastInfo = PodcastInfo( + uri = this.uri, + title = this.title, + author = this.author ?: "", + imageUrl = this.imageUrl ?: "", + description = this.description ?: "", +) -fun PodcastWithExtraInfo.asExternalModel(): PodcastInfo = - this.podcast.asExternalModel().copy( - isSubscribed = isFollowed, - lastEpisodeDate = lastEpisodeDate, - ) +fun PodcastWithExtraInfo.asExternalModel(): PodcastInfo = this.podcast.asExternalModel().copy( + isSubscribed = isFollowed, + lastEpisodeDate = lastEpisodeDate, +) diff --git a/Jetcaster/core/domain/src/main/java/com/example/jetcaster/core/model/PodcastToEpisodeInfo.kt b/Jetcaster/core/domain/src/main/java/com/example/jetcaster/core/model/PodcastToEpisodeInfo.kt index a7e458cad..2b69d1fe4 100644 --- a/Jetcaster/core/domain/src/main/java/com/example/jetcaster/core/model/PodcastToEpisodeInfo.kt +++ b/Jetcaster/core/domain/src/main/java/com/example/jetcaster/core/model/PodcastToEpisodeInfo.kt @@ -18,13 +18,9 @@ package com.example.jetcaster.core.model import com.example.jetcaster.core.data.database.model.EpisodeToPodcast -data class PodcastToEpisodeInfo( - val episode: EpisodeInfo, - val podcast: PodcastInfo, -) +data class PodcastToEpisodeInfo(val episode: EpisodeInfo, val podcast: PodcastInfo) -fun EpisodeToPodcast.asPodcastToEpisodeInfo(): PodcastToEpisodeInfo = - PodcastToEpisodeInfo( - episode = episode.asExternalModel(), - podcast = podcast.asExternalModel(), - ) +fun EpisodeToPodcast.asPodcastToEpisodeInfo(): PodcastToEpisodeInfo = PodcastToEpisodeInfo( + episode = episode.asExternalModel(), + podcast = podcast.asExternalModel(), +) diff --git a/Jetcaster/core/domain/src/main/java/com/example/jetcaster/core/player/EpisodePlayer.kt b/Jetcaster/core/domain/src/main/java/com/example/jetcaster/core/player/EpisodePlayer.kt index eb88fdef5..c01d64c61 100644 --- a/Jetcaster/core/domain/src/main/java/com/example/jetcaster/core/player/EpisodePlayer.kt +++ b/Jetcaster/core/domain/src/main/java/com/example/jetcaster/core/player/EpisodePlayer.kt @@ -53,8 +53,8 @@ interface EpisodePlayer { fun addToQueue(episode: PlayerEpisode) /* - * Flushes the queue - */ + * Flushes the queue + */ fun removeAllFromQueue() /** diff --git a/Jetcaster/core/domain/src/main/java/com/example/jetcaster/core/player/MockEpisodePlayer.kt b/Jetcaster/core/domain/src/main/java/com/example/jetcaster/core/player/MockEpisodePlayer.kt index f94a552b5..ab7c17c9f 100644 --- a/Jetcaster/core/domain/src/main/java/com/example/jetcaster/core/player/MockEpisodePlayer.kt +++ b/Jetcaster/core/domain/src/main/java/com/example/jetcaster/core/player/MockEpisodePlayer.kt @@ -32,9 +32,7 @@ import kotlinx.coroutines.flow.update import kotlinx.coroutines.isActive import kotlinx.coroutines.launch -class MockEpisodePlayer( - private val mainDispatcher: CoroutineDispatcher -) : EpisodePlayer { +class MockEpisodePlayer(private val mainDispatcher: CoroutineDispatcher) : EpisodePlayer { private val _playerState = MutableStateFlow(EpisodePlayerState()) private val _currentEpisode = MutableStateFlow(null) @@ -54,14 +52,14 @@ class MockEpisodePlayer( queue, isPlaying, timeElapsed, - _playerSpeed + _playerSpeed, ) { currentEpisode, queue, isPlaying, timeElapsed, playerSpeed -> EpisodePlayerState( currentEpisode = currentEpisode, queue = queue, isPlaying = isPlaying, timeElapsed = timeElapsed, - playbackSpeed = playerSpeed + playbackSpeed = playerSpeed, ) }.catch { // TODO handle error state @@ -218,13 +216,8 @@ class MockEpisodePlayer( } // Used to enable property delegation -private operator fun MutableStateFlow.setValue( - thisObj: Any?, - property: KProperty<*>, - value: T -) { +private operator fun MutableStateFlow.setValue(thisObj: Any?, property: KProperty<*>, value: T) { this.value = value } -private operator fun MutableStateFlow.getValue(thisObj: Any?, property: KProperty<*>): T = - this.value +private operator fun MutableStateFlow.getValue(thisObj: Any?, property: KProperty<*>): T = this.value diff --git a/Jetcaster/core/domain/src/main/java/com/example/jetcaster/core/player/model/PlayerEpisode.kt b/Jetcaster/core/domain/src/main/java/com/example/jetcaster/core/player/model/PlayerEpisode.kt index 361849299..adc1a21c0 100644 --- a/Jetcaster/core/domain/src/main/java/com/example/jetcaster/core/player/model/PlayerEpisode.kt +++ b/Jetcaster/core/domain/src/main/java/com/example/jetcaster/core/player/model/PlayerEpisode.kt @@ -35,7 +35,7 @@ data class PlayerEpisode( val author: String = "", val summary: String = "", val podcastImageUrl: String = "", - val mediaUrls:List = emptyList() + val mediaUrls: List = emptyList(), ) { constructor(podcastInfo: PodcastInfo, episodeInfo: EpisodeInfo) : this( title = episodeInfo.title, @@ -47,20 +47,19 @@ data class PlayerEpisode( summary = episodeInfo.summary, podcastImageUrl = podcastInfo.imageUrl, uri = episodeInfo.uri, - mediaUrls = episodeInfo.mediaUrls + mediaUrls = episodeInfo.mediaUrls, ) } -fun EpisodeToPodcast.toPlayerEpisode(): PlayerEpisode = - PlayerEpisode( - uri = episode.uri, - title = episode.title, - subTitle = episode.subtitle ?: "", - published = episode.published, - duration = episode.duration, - podcastName = podcast.title, - author = episode.author ?: podcast.author ?: "", - summary = episode.summary ?: "", - podcastImageUrl = podcast.imageUrl ?: "", - mediaUrls = episode.mediaUrls - ) +fun EpisodeToPodcast.toPlayerEpisode(): PlayerEpisode = PlayerEpisode( + uri = episode.uri, + title = episode.title, + subTitle = episode.subtitle ?: "", + published = episode.published, + duration = episode.duration, + podcastName = podcast.title, + author = episode.author ?: podcast.author ?: "", + summary = episode.summary ?: "", + podcastImageUrl = podcast.imageUrl ?: "", + mediaUrls = episode.mediaUrls, +) diff --git a/Jetcaster/core/domain/src/test/kotlin/com/example/jetcaster/core/domain/FilterableCategoriesUseCaseTest.kt b/Jetcaster/core/domain/src/test/kotlin/com/example/jetcaster/core/domain/FilterableCategoriesUseCaseTest.kt index 4431dc29f..71ef508c2 100644 --- a/Jetcaster/core/domain/src/test/kotlin/com/example/jetcaster/core/domain/FilterableCategoriesUseCaseTest.kt +++ b/Jetcaster/core/domain/src/test/kotlin/com/example/jetcaster/core/domain/FilterableCategoriesUseCaseTest.kt @@ -36,7 +36,7 @@ class FilterableCategoriesUseCaseTest { ) val useCase = FilterableCategoriesUseCase( - categoryStore = categoriesStore + categoryStore = categoriesStore, ) @Before @@ -49,7 +49,7 @@ class FilterableCategoriesUseCaseTest { val filterableCategories = useCase(null).first() assertEquals( filterableCategories.categories[0], - filterableCategories.selectedCategory + filterableCategories.selectedCategory, ) } @@ -59,7 +59,7 @@ class FilterableCategoriesUseCaseTest { val filterableCategories = useCase(selectedCategory.asExternalModel()).first() assertEquals( selectedCategory.asExternalModel(), - filterableCategories.selectedCategory + filterableCategories.selectedCategory, ) } } diff --git a/Jetcaster/core/domain/src/test/kotlin/com/example/jetcaster/core/domain/GetLatestFollowedEpisodesUseCaseTest.kt b/Jetcaster/core/domain/src/test/kotlin/com/example/jetcaster/core/domain/GetLatestFollowedEpisodesUseCaseTest.kt index c2a3133ed..3781fe620 100644 --- a/Jetcaster/core/domain/src/test/kotlin/com/example/jetcaster/core/domain/GetLatestFollowedEpisodesUseCaseTest.kt +++ b/Jetcaster/core/domain/src/test/kotlin/com/example/jetcaster/core/domain/GetLatestFollowedEpisodesUseCaseTest.kt @@ -32,7 +32,7 @@ class GetLatestFollowedEpisodesUseCaseTest { val useCase = GetLatestFollowedEpisodesUseCase( episodeStore = episodeStore, - podcastStore = podcastStore + podcastStore = podcastStore, ) val testEpisodes = listOf( @@ -40,20 +40,20 @@ class GetLatestFollowedEpisodesUseCaseTest { uri = "", podcastUri = testPodcasts[0].podcast.uri, title = "title1", - published = OffsetDateTime.MIN + published = OffsetDateTime.MIN, ), Episode( uri = "", podcastUri = testPodcasts[0].podcast.uri, title = "title2", - published = OffsetDateTime.now() + published = OffsetDateTime.now(), ), Episode( uri = "", podcastUri = testPodcasts[1].podcast.uri, title = "title3", - published = OffsetDateTime.MAX - ) + published = OffsetDateTime.MAX, + ), ) @Test @@ -91,8 +91,7 @@ class GetLatestFollowedEpisodesUseCaseTest { } podcastStore.togglePodcastFollowed(testPodcasts[0].podcast.uri) - result.first().zipWithNext { - ep1, ep2 -> + result.first().zipWithNext { ep1, ep2 -> ep1.episode.published > ep2.episode.published }.all { it } } diff --git a/Jetcaster/core/domain/src/test/kotlin/com/example/jetcaster/core/domain/PodcastCategoryFilterUseCaseTest.kt b/Jetcaster/core/domain/src/test/kotlin/com/example/jetcaster/core/domain/PodcastCategoryFilterUseCaseTest.kt index 7568d175b..2e59383a8 100644 --- a/Jetcaster/core/domain/src/test/kotlin/com/example/jetcaster/core/domain/PodcastCategoryFilterUseCaseTest.kt +++ b/Jetcaster/core/domain/src/test/kotlin/com/example/jetcaster/core/domain/PodcastCategoryFilterUseCaseTest.kt @@ -40,13 +40,13 @@ class PodcastCategoryFilterUseCaseTest { "", "", "Episode 1", - published = OffsetDateTime.now() + published = OffsetDateTime.now(), ) _podcasts = listOf( Podcast( uri = "", - title = "Podcast 1" - ) + title = "Podcast 1", + ), ) }, EpisodeToPodcast().apply { @@ -54,13 +54,13 @@ class PodcastCategoryFilterUseCaseTest { "", "", "Episode 2", - published = OffsetDateTime.now() + published = OffsetDateTime.now(), ) _podcasts = listOf( Podcast( uri = "", - title = "Podcast 2" - ) + title = "Podcast 2", + ), ) }, EpisodeToPodcast().apply { @@ -68,20 +68,20 @@ class PodcastCategoryFilterUseCaseTest { "", "", "Episode 3", - published = OffsetDateTime.now() + published = OffsetDateTime.now(), ) _podcasts = listOf( Podcast( uri = "", - title = "Podcast 3" - ) + title = "Podcast 3", + ), ) - } + }, ) private val testCategory = Category(1, "Technology") val useCase = PodcastCategoryFilterUseCase( - categoryStore = categoriesStore + categoryStore = categoriesStore, ) @Test @@ -106,11 +106,11 @@ class PodcastCategoryFilterUseCaseTest { val result = resultFlow.first() assertEquals( testPodcasts.map { it.asExternalModel() }, - result.topPodcasts + result.topPodcasts, ) assertEquals( testEpisodeToPodcast.map { it.asPodcastToEpisodeInfo() }, - result.episodes + result.episodes, ) } @@ -120,11 +120,11 @@ class PodcastCategoryFilterUseCaseTest { categoriesStore.setEpisodesFromPodcast( testCategory.id, - List(8) { testEpisodeToPodcast }.flatten() + List(8) { testEpisodeToPodcast }.flatten(), ) categoriesStore.setPodcastsInCategory( testCategory.id, - List(4) { testPodcasts }.flatten() + List(4) { testPodcasts }.flatten(), ) val result = resultFlow.first() diff --git a/Jetcaster/core/domain/src/test/kotlin/com/example/jetcaster/core/domain/player/MockEpisodePlayerTest.kt b/Jetcaster/core/domain/src/test/kotlin/com/example/jetcaster/core/domain/player/MockEpisodePlayerTest.kt index 96c91a66c..e43e47c97 100644 --- a/Jetcaster/core/domain/src/test/kotlin/com/example/jetcaster/core/domain/player/MockEpisodePlayerTest.kt +++ b/Jetcaster/core/domain/src/test/kotlin/com/example/jetcaster/core/domain/player/MockEpisodePlayerTest.kt @@ -36,15 +36,15 @@ class MockEpisodePlayerTest { private val testEpisodes = listOf( PlayerEpisode( uri = "uri1", - duration = Duration.ofSeconds(60) + duration = Duration.ofSeconds(60), ), PlayerEpisode( uri = "uri2", - duration = Duration.ofSeconds(60) + duration = Duration.ofSeconds(60), ), PlayerEpisode( uri = "uri3", - duration = Duration.ofSeconds(60) + duration = Duration.ofSeconds(60), ), ) @@ -53,7 +53,7 @@ class MockEpisodePlayerTest { val playSpeed = Duration.ofSeconds(2) val currEpisode = PlayerEpisode( uri = "currentEpisode", - duration = Duration.ofSeconds(60) + duration = Duration.ofSeconds(60), ) mockEpisodePlayer.currentEpisode = currEpisode mockEpisodePlayer.playerSpeed = playSpeed @@ -69,7 +69,7 @@ class MockEpisodePlayerTest { val duration = Duration.ofSeconds(60) val currEpisode = PlayerEpisode( uri = "currentEpisode", - duration = duration + duration = duration, ) mockEpisodePlayer.currentEpisode = currEpisode testEpisodes.forEach { mockEpisodePlayer.addToQueue(it) } @@ -85,7 +85,7 @@ class MockEpisodePlayerTest { val duration = Duration.ofSeconds(60) val currEpisode = PlayerEpisode( uri = "currentEpisode", - duration = duration + duration = duration, ) mockEpisodePlayer.currentEpisode = currEpisode @@ -101,18 +101,18 @@ class MockEpisodePlayerTest { val duration = Duration.ofSeconds(60) val currEpisode = PlayerEpisode( uri = "currentEpisode", - duration = duration + duration = duration, ) val firstEpisodeFromList = PlayerEpisode( uri = "firstEpisodeFromList", - duration = duration + duration = duration, ) val secondEpisodeFromList = PlayerEpisode( uri = "secondEpisodeFromList", - duration = duration + duration = duration, ) val episodeListToBeAddedToTheQueue: List = listOf( - firstEpisodeFromList, secondEpisodeFromList + firstEpisodeFromList, secondEpisodeFromList, ) mockEpisodePlayer.currentEpisode = currEpisode @@ -154,7 +154,7 @@ class MockEpisodePlayerTest { fun whenNext_queueIsNotEmpty_removeFromQueue() = runTest(testDispatcher) { mockEpisodePlayer.currentEpisode = PlayerEpisode( uri = "currentEpisode", - duration = Duration.ofSeconds(60) + duration = Duration.ofSeconds(60), ) testEpisodes.forEach { mockEpisodePlayer.addToQueue(it) } @@ -174,7 +174,7 @@ class MockEpisodePlayerTest { fun whenNext_queueIsNotEmpty_notRemovedFromQueue() = runTest(testDispatcher) { mockEpisodePlayer.currentEpisode = PlayerEpisode( uri = "currentEpisode", - duration = Duration.ofSeconds(60) + duration = Duration.ofSeconds(60), ) testEpisodes.forEach { mockEpisodePlayer.addToQueue(it) } diff --git a/Jetcaster/glancewidget/build.gradle.kts b/Jetcaster/glancewidget/build.gradle.kts index 55e23f8e1..bebf465b9 100644 --- a/Jetcaster/glancewidget/build.gradle.kts +++ b/Jetcaster/glancewidget/build.gradle.kts @@ -1,3 +1,20 @@ +/* + * Copyright 2025 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + plugins { alias(libs.plugins.android.library) alias(libs.plugins.kotlin.android) diff --git a/Jetcaster/glancewidget/src/main/java/com/example/jetcaster/glancewidget/JetcasterAppWidget.kt b/Jetcaster/glancewidget/src/main/java/com/example/jetcaster/glancewidget/JetcasterAppWidget.kt index ca090bc47..3840e7c00 100644 --- a/Jetcaster/glancewidget/src/main/java/com/example/jetcaster/glancewidget/JetcasterAppWidget.kt +++ b/Jetcaster/glancewidget/src/main/java/com/example/jetcaster/glancewidget/JetcasterAppWidget.kt @@ -78,12 +78,7 @@ class JetcasterAppWidgetReceiver : GlanceAppWidgetReceiver() { get() = JetcasterAppWidget() } -data class JetcasterAppWidgetViewState( - val episodeTitle: String, - val podcastTitle: String, - val isPlaying: Boolean, - val albumArtUri: String, -) +data class JetcasterAppWidgetViewState(val episodeTitle: String, val podcastTitle: String, val isPlaying: Boolean, val albumArtUri: String) private object Sizes { val short = 72.dp @@ -123,7 +118,7 @@ class JetcasterAppWidget : GlanceAppWidget() { podcastTitle = "Now in Android", isPlaying = false, albumArtUri = "https://static.libsyn.com/p/assets/9/f/f/3/" + - "9ff3cb5dc6cfb3e2e5bbc093207a2619/NIA000_PodcastThumbnail.png" + "9ff3cb5dc6cfb3e2e5bbc093207a2619/NIA000_PodcastThumbnail.png", ) provideContent { @@ -137,7 +132,7 @@ class JetcasterAppWidget : GlanceAppWidget() { SizeBucket.Narrow -> Widget( iconSize = Sizes.medium, imageUri = artUri, - playPauseIcon = playPauseIcon + playPauseIcon = playPauseIcon, ) SizeBucket.Normal -> WidgetUiNormal( @@ -145,13 +140,13 @@ class JetcasterAppWidget : GlanceAppWidget() { title = testState.episodeTitle, subtitle = testState.podcastTitle, imageUri = artUri, - playPauseIcon = playPauseIcon + playPauseIcon = playPauseIcon, ) SizeBucket.NarrowShort -> Widget( iconSize = Sizes.condensed, imageUri = artUri, - playPauseIcon = playPauseIcon + playPauseIcon = playPauseIcon, ) SizeBucket.NormalShort -> WidgetUiNormal( @@ -159,7 +154,7 @@ class JetcasterAppWidget : GlanceAppWidget() { title = testState.episodeTitle, subtitle = testState.podcastTitle, imageUri = artUri, - playPauseIcon = playPauseIcon + playPauseIcon = playPauseIcon, ) } } @@ -168,17 +163,11 @@ class JetcasterAppWidget : GlanceAppWidget() { } @Composable -private fun WidgetUiNormal( - title: String, - subtitle: String, - imageUri: Uri, - playPauseIcon: PlayPauseIcon, - iconSize: Dp, -) { +private fun WidgetUiNormal(title: String, subtitle: String, imageUri: Uri, playPauseIcon: PlayPauseIcon, iconSize: Dp) { Scaffold { Row( GlanceModifier.fillMaxSize(), - verticalAlignment = Alignment.Vertical.CenterVertically + verticalAlignment = Alignment.Vertical.CenterVertically, ) { AlbumArt(imageUri, GlanceModifier.size(iconSize)) PodcastText(title, subtitle, modifier = GlanceModifier.padding(16.dp).defaultWeight()) @@ -193,7 +182,7 @@ private fun Widget(iconSize: Dp, imageUri: Uri, playPauseIcon: PlayPauseIcon) { Scaffold(titleBar = {}) { Row( modifier = GlanceModifier.fillMaxSize(), - verticalAlignment = Alignment.Vertical.CenterVertically + verticalAlignment = Alignment.Vertical.CenterVertically, ) { AlbumArt(imageUri, GlanceModifier.size(iconSize)) Spacer(GlanceModifier.defaultWeight()) @@ -225,14 +214,14 @@ fun PodcastText(title: String, subtitle: String, modifier: GlanceModifier = Glan style = TextStyle( fontSize = 16.sp, fontWeight = FontWeight.Medium, - color = fgColor + color = fgColor, ), - maxLines = 2 + maxLines = 2, ) Text( text = subtitle, style = TextStyle(fontSize = 14.sp, color = fgColor), - maxLines = 2 + maxLines = 2, ) } else -> Column(modifier) { @@ -241,20 +230,16 @@ fun PodcastText(title: String, subtitle: String, modifier: GlanceModifier = Glan style = TextStyle( fontSize = 12.sp, fontWeight = FontWeight.Medium, - color = fgColor + color = fgColor, ), - maxLines = 1 + maxLines = 1, ) } } } @Composable -private fun PlayPauseButton( - modifier: GlanceModifier = GlanceModifier.size(Sizes.normal), - state: PlayPauseIcon, - onClick: () -> Unit, -) { +private fun PlayPauseButton(modifier: GlanceModifier = GlanceModifier.size(Sizes.normal), state: PlayPauseIcon, onClick: () -> Unit) { val (iconRes: Int, description: Int) = when (state) { PlayPauseIcon.Play -> R.drawable.outline_play_arrow_24 to R.string.content_description_play PlayPauseIcon.Pause -> R.drawable.outline_pause_24 to R.string.content_description_pause @@ -267,7 +252,7 @@ private fun PlayPauseButton( modifier = modifier, imageProvider = provider, contentDescription = contentDescription, - onClick = onClick + onClick = onClick, ) } @@ -277,11 +262,7 @@ enum class PlayPauseIcon { Play, Pause } * Uses Coil to load images. */ @Composable -private fun WidgetAsyncImage( - uri: Uri, - contentDescription: String?, - modifier: GlanceModifier = GlanceModifier, -) { +private fun WidgetAsyncImage(uri: Uri, contentDescription: String?, modifier: GlanceModifier = GlanceModifier) { var bitmap by remember { mutableStateOf(null) } val context = LocalContext.current val scope = rememberCoroutineScope() @@ -309,7 +290,7 @@ private fun WidgetAsyncImage( provider = ImageProvider(bitmap), contentDescription = contentDescription, contentScale = ContentScale.FillBounds, - modifier = modifier.cornerRadius(12.dp) // TODO: confirm radius with design + modifier = modifier.cornerRadius(12.dp), // TODO: confirm radius with design ) } } diff --git a/Jetcaster/glancewidget/src/main/java/com/example/jetcaster/glancewidget/JetcasterAppWidgetPreview.kt b/Jetcaster/glancewidget/src/main/java/com/example/jetcaster/glancewidget/JetcasterAppWidgetPreview.kt index 59524ae96..186e4495b 100644 --- a/Jetcaster/glancewidget/src/main/java/com/example/jetcaster/glancewidget/JetcasterAppWidgetPreview.kt +++ b/Jetcaster/glancewidget/src/main/java/com/example/jetcaster/glancewidget/JetcasterAppWidgetPreview.kt @@ -66,8 +66,8 @@ fun updateWidgetPreview(context: Context) { AppWidgetProviderInfo.WIDGET_CATEGORY_HOME_SCREEN, JetcasterAppWidgetPreview().compose( context, - size = DpSize(160.dp, 64.dp) - ) + size = DpSize(160.dp, 64.dp), + ), ) } catch (e: Exception) { Log.e(TAG, e.message, e) @@ -94,19 +94,19 @@ private fun Widget() { Scaffold { Row( modifier = GlanceModifier.fillMaxSize(), - verticalAlignment = Alignment.Vertical.CenterVertically + verticalAlignment = Alignment.Vertical.CenterVertically, ) { Image( modifier = GlanceModifier.wrapContentSize().size(SizesPreview.medium), provider = ImageProvider(R.drawable.widget_preview_thumbnail), - contentDescription = "" + contentDescription = "", ) Spacer(GlanceModifier.defaultWeight()) SquareIconButton( modifier = GlanceModifier.size(SizesPreview.medium), imageProvider = ImageProvider(R.drawable.outline_play_arrow_24), contentDescription = "", - onClick = { } + onClick = { }, ) } } diff --git a/Jetcaster/mobile/build.gradle.kts b/Jetcaster/mobile/build.gradle.kts index 044fc2442..8121ed923 100644 --- a/Jetcaster/mobile/build.gradle.kts +++ b/Jetcaster/mobile/build.gradle.kts @@ -5,7 +5,7 @@ * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * - * https://www.apache.org/licenses/LICENSE-2.0 + * https://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, @@ -14,6 +14,7 @@ * limitations under the License. */ + plugins { alias(libs.plugins.android.application) alias(libs.plugins.kotlin.android) @@ -22,15 +23,23 @@ plugins { alias(libs.plugins.compose) } - android { - compileSdk = libs.versions.compileSdk.get().toInt() + compileSdk = + libs.versions.compileSdk + .get() + .toInt() namespace = "com.example.jetcaster" defaultConfig { applicationId = "com.example.jetcaster" - minSdk = libs.versions.minSdk.get().toInt() - targetSdk = libs.versions.targetSdk.get().toInt() + minSdk = + libs.versions.minSdk + .get() + .toInt() + targetSdk = + libs.versions.targetSdk + .get() + .toInt() versionCode = 1 versionName = "1.0" testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" @@ -52,7 +61,6 @@ android { buildTypes { getByName("debug") { - } getByName("release") { @@ -60,7 +68,7 @@ android { signingConfig = signingConfigs.getByName("release") proguardFiles( getDefaultProguardFile("proguard-android-optimize.txt"), - "proguard-rules.pro" + "proguard-rules.pro", ) } } diff --git a/Jetcaster/mobile/src/main/java/com/example/jetcaster/JetcasterApplication.kt b/Jetcaster/mobile/src/main/java/com/example/jetcaster/JetcasterApplication.kt index 120187d2d..af3748020 100644 --- a/Jetcaster/mobile/src/main/java/com/example/jetcaster/JetcasterApplication.kt +++ b/Jetcaster/mobile/src/main/java/com/example/jetcaster/JetcasterApplication.kt @@ -26,7 +26,9 @@ import javax.inject.Inject * Application which sets up our dependency [Graph] with a context. */ @HiltAndroidApp -class JetcasterApplication : Application(), ImageLoaderFactory { +class JetcasterApplication : + Application(), + ImageLoaderFactory { @Inject lateinit var imageLoader: ImageLoader diff --git a/Jetcaster/mobile/src/main/java/com/example/jetcaster/ui/JetcasterApp.kt b/Jetcaster/mobile/src/main/java/com/example/jetcaster/ui/JetcasterApp.kt index 9e613b28f..f67a0295f 100644 --- a/Jetcaster/mobile/src/main/java/com/example/jetcaster/ui/JetcasterApp.kt +++ b/Jetcaster/mobile/src/main/java/com/example/jetcaster/ui/JetcasterApp.kt @@ -41,42 +41,39 @@ import com.example.jetcaster.ui.player.PlayerScreen @Composable @OptIn(ExperimentalSharedTransitionApi::class) -fun JetcasterApp( - displayFeatures: List, - appState: JetcasterAppState = rememberJetcasterAppState(), -) { +fun JetcasterApp(displayFeatures: List, appState: JetcasterAppState = rememberJetcasterAppState()) { val adaptiveInfo = currentWindowAdaptiveInfo() if (appState.isOnline) { SharedTransitionLayout { CompositionLocalProvider( - LocalSharedTransitionScope provides this + LocalSharedTransitionScope provides this, ) { NavHost( navController = appState.navController, startDestination = Screen.Home.route, popExitTransition = { scaleOut(targetScale = 0.9f) }, - popEnterTransition = { EnterTransition.None } + popEnterTransition = { EnterTransition.None }, ) { composable(Screen.Home.route) { backStackEntry -> CompositionLocalProvider( - LocalAnimatedVisibilityScope provides this + LocalAnimatedVisibilityScope provides this, ) { MainScreen( windowSizeClass = adaptiveInfo.windowSizeClass, navigateToPlayer = { episode -> appState.navigateToPlayer(episode.uri, backStackEntry) - } + }, ) } } composable(Screen.Player.route) { CompositionLocalProvider( - LocalAnimatedVisibilityScope provides this + LocalAnimatedVisibilityScope provides this, ) { PlayerScreen( windowSizeClass = adaptiveInfo.windowSizeClass, displayFeatures = displayFeatures, - onBackPress = appState::navigateBack + onBackPress = appState::navigateBack, ) } } @@ -98,7 +95,7 @@ fun OfflineDialog(onRetry: () -> Unit) { TextButton(onClick = onRetry) { Text(stringResource(R.string.retry_label)) } - } + }, ) } diff --git a/Jetcaster/mobile/src/main/java/com/example/jetcaster/ui/JetcasterAppState.kt b/Jetcaster/mobile/src/main/java/com/example/jetcaster/ui/JetcasterAppState.kt index ee938066a..a3f5645ad 100644 --- a/Jetcaster/mobile/src/main/java/com/example/jetcaster/ui/JetcasterAppState.kt +++ b/Jetcaster/mobile/src/main/java/com/example/jetcaster/ui/JetcasterAppState.kt @@ -55,17 +55,12 @@ sealed class Screen(val route: String) { } @Composable -fun rememberJetcasterAppState( - navController: NavHostController = rememberNavController(), - context: Context = LocalContext.current -) = remember(navController, context) { - JetcasterAppState(navController, context) -} +fun rememberJetcasterAppState(navController: NavHostController = rememberNavController(), context: Context = LocalContext.current) = + remember(navController, context) { + JetcasterAppState(navController, context) + } -class JetcasterAppState( - val navController: NavHostController, - private val context: Context -) { +class JetcasterAppState(val navController: NavHostController, private val context: Context) { var isOnline by mutableStateOf(checkIfOnline()) private set @@ -111,5 +106,4 @@ class JetcasterAppState( * * This is used to de-duplicate navigation events. */ -private fun NavBackStackEntry.lifecycleIsResumed() = - this.lifecycle.currentState == Lifecycle.State.RESUMED +private fun NavBackStackEntry.lifecycleIsResumed() = this.lifecycle.currentState == Lifecycle.State.RESUMED diff --git a/Jetcaster/mobile/src/main/java/com/example/jetcaster/ui/MainActivity.kt b/Jetcaster/mobile/src/main/java/com/example/jetcaster/ui/MainActivity.kt index 9349b5fe5..d7cfca1a7 100644 --- a/Jetcaster/mobile/src/main/java/com/example/jetcaster/ui/MainActivity.kt +++ b/Jetcaster/mobile/src/main/java/com/example/jetcaster/ui/MainActivity.kt @@ -37,7 +37,7 @@ class MainActivity : ComponentActivity() { JetcasterTheme { JetcasterApp( - displayFeatures + displayFeatures, ) } } diff --git a/Jetcaster/mobile/src/main/java/com/example/jetcaster/ui/home/Home.kt b/Jetcaster/mobile/src/main/java/com/example/jetcaster/ui/home/Home.kt index bbba2aa9b..79e6f9095 100644 --- a/Jetcaster/mobile/src/main/java/com/example/jetcaster/ui/home/Home.kt +++ b/Jetcaster/mobile/src/main/java/com/example/jetcaster/ui/home/Home.kt @@ -181,27 +181,22 @@ fun calculateScaffoldDirective( maxVerticalPartitions, horizontalSpacerSize, defaultPanePreferredWidth, - getExcludedVerticalBounds(windowAdaptiveInfo.windowPosture, verticalHingePolicy) + getExcludedVerticalBounds(windowAdaptiveInfo.windowPosture, verticalHingePolicy), ) } /** * Copied from `getExcludedVerticalBounds()` in [PaneScaffoldDirective] since it is private. */ -private fun getExcludedVerticalBounds(posture: Posture, hingePolicy: HingePolicy): List = - when (hingePolicy) { - HingePolicy.AvoidSeparating -> posture.separatingVerticalHingeBounds - HingePolicy.AvoidOccluding -> posture.occludingVerticalHingeBounds - HingePolicy.AlwaysAvoid -> posture.allVerticalHingeBounds - else -> emptyList() - } +private fun getExcludedVerticalBounds(posture: Posture, hingePolicy: HingePolicy): List = when (hingePolicy) { + HingePolicy.AvoidSeparating -> posture.separatingVerticalHingeBounds + HingePolicy.AvoidOccluding -> posture.occludingVerticalHingeBounds + HingePolicy.AlwaysAvoid -> posture.allVerticalHingeBounds + else -> emptyList() +} @Composable -fun MainScreen( - windowSizeClass: WindowSizeClass, - navigateToPlayer: (EpisodeInfo) -> Unit, - viewModel: HomeViewModel = hiltViewModel(), -) { +fun MainScreen(windowSizeClass: WindowSizeClass, navigateToPlayer: (EpisodeInfo) -> Unit, viewModel: HomeViewModel = hiltViewModel()) { val homeScreenUiState by viewModel.state.collectAsStateWithLifecycle() val uiState = homeScreenUiState Box { @@ -209,7 +204,7 @@ fun MainScreen( uiState = uiState, windowSizeClass = windowSizeClass, navigateToPlayer = navigateToPlayer, - viewModel = viewModel + viewModel = viewModel, ) if (uiState.errorMessage != null) { @@ -224,11 +219,11 @@ private fun HomeScreenError(onRetry: () -> Unit, modifier: Modifier = Modifier) Column( verticalArrangement = Arrangement.Center, horizontalAlignment = Alignment.CenterHorizontally, - modifier = Modifier.fillMaxSize() + modifier = Modifier.fillMaxSize(), ) { Text( text = stringResource(id = R.string.an_error_has_occurred), - modifier = Modifier.padding(16.dp) + modifier = Modifier.padding(16.dp), ) Button(onClick = onRetry) { Text(text = stringResource(id = R.string.retry_label)) @@ -254,7 +249,7 @@ private fun HomeScreenReady( viewModel: HomeViewModel = hiltViewModel(), ) { val navigator = rememberSupportingPaneScaffoldNavigator( - scaffoldDirective = calculateScaffoldDirective(currentWindowAdaptiveInfo()) + scaffoldDirective = calculateScaffoldDirective(currentWindowAdaptiveInfo()), ) val scope = rememberCoroutineScope() @@ -285,7 +280,7 @@ private fun HomeScreenReady( } }, navigateToPlayer = navigateToPlayer, - modifier = Modifier.fillMaxSize() + modifier = Modifier.fillMaxSize(), ) }, supportingPane = { @@ -293,7 +288,7 @@ private fun HomeScreenReady( if (!podcastUri.isNullOrEmpty()) { val podcastDetailsViewModel = hiltViewModel( - key = podcastUri + key = podcastUri, ) { it.create(podcastUri) } @@ -307,11 +302,11 @@ private fun HomeScreenReady( } } }, - showBackButton = navigator.isMainPaneHidden() + showBackButton = navigator.isMainPaneHidden(), ) } }, - modifier = Modifier.fillMaxSize() + modifier = Modifier.fillMaxSize(), ) } } @@ -327,7 +322,7 @@ private fun HomeAppBar(isExpanded: Boolean, modifier: Modifier = Modifier) { modifier = modifier .fillMaxWidth() .background(Color.Transparent) - .padding(horizontal = 16.dp, vertical = 8.dp) + .padding(horizontal = 16.dp, vertical = 8.dp), ) { SearchBar( inputField = { @@ -344,38 +339,35 @@ private fun HomeAppBar(isExpanded: Boolean, modifier: Modifier = Modifier) { leadingIcon = { Icon( imageVector = Icons.Default.Search, - contentDescription = null + contentDescription = null, ) }, trailingIcon = { Icon( imageVector = Icons.Default.AccountCircle, - contentDescription = stringResource(R.string.cd_account) + contentDescription = stringResource(R.string.cd_account), ) }, interactionSource = null, - modifier = if (isExpanded) Modifier.fillMaxWidth() else Modifier + modifier = if (isExpanded) Modifier.fillMaxWidth() else Modifier, ) }, expanded = false, - onExpandedChange = {} + onExpandedChange = {}, ) {} } } @Composable -private fun HomeScreenBackground( - modifier: Modifier = Modifier, - content: @Composable BoxScope.() -> Unit, -) { +private fun HomeScreenBackground(modifier: Modifier = Modifier, content: @Composable BoxScope.() -> Unit) { Box( modifier = modifier - .background(MaterialTheme.colorScheme.background) + .background(MaterialTheme.colorScheme.background), ) { Box( modifier = Modifier .fillMaxSize() - .radialGradientScrim(MaterialTheme.colorScheme.primary.copy(alpha = 0.15f)) + .radialGradientScrim(MaterialTheme.colorScheme.primary.copy(alpha = 0.15f)), ) content() } @@ -406,20 +398,20 @@ private fun HomeScreen( val coroutineScope = rememberCoroutineScope() val snackbarHostState = remember { SnackbarHostState() } HomeScreenBackground( - modifier = modifier.windowInsetsPadding(WindowInsets.navigationBars) + modifier = modifier.windowInsetsPadding(WindowInsets.navigationBars), ) { Scaffold( topBar = { Column { HomeAppBar( isExpanded = windowSizeClass.isCompact, - modifier = Modifier.fillMaxWidth() + modifier = Modifier.fillMaxWidth(), ) if (isLoading) { LinearProgressIndicator( Modifier .fillMaxWidth() - .padding(horizontal = 16.dp) + .padding(horizontal = 16.dp), ) } } @@ -427,7 +419,7 @@ private fun HomeScreen( snackbarHost = { SnackbarHost(hostState = snackbarHostState) }, - containerColor = Color.Transparent + containerColor = Color.Transparent, ) { contentPadding -> // Main Content val snackBarText = stringResource(id = R.string.episode_added_to_your_queue) @@ -448,14 +440,14 @@ private fun HomeScreen( onHomeAction(action) }, navigateToPodcastDetails = navigateToPodcastDetails, - navigateToPlayer = navigateToPlayer + navigateToPlayer = navigateToPlayer, ) if (showHomeCategoryTabs) { PillToolbar( selectedHomeCategory, onHomeAction, - Modifier.align(Alignment.BottomCenter) + Modifier.align(Alignment.BottomCenter), ) } } @@ -464,18 +456,14 @@ private fun HomeScreen( @OptIn(ExperimentalMaterial3ExpressiveApi::class) @Composable -fun PillToolbar( - selectedHomeCategory: HomeCategory, - onHomeAction: (HomeAction) -> Unit, - modifier: Modifier = Modifier, -) { +fun PillToolbar(selectedHomeCategory: HomeCategory, onHomeAction: (HomeAction) -> Unit, modifier: Modifier = Modifier) { HorizontalFloatingToolbar( modifier = modifier, colors = FloatingToolbarColors( toolbarContainerColor = MaterialTheme.colorScheme.surfaceContainerHighest, toolbarContentColor = MaterialTheme.colorScheme.onSurfaceVariant, fabContainerColor = MaterialTheme.colorScheme.tertiary, - fabContentColor = MaterialTheme.colorScheme.onTertiary + fabContentColor = MaterialTheme.colorScheme.onTertiary, ), expanded = true, content = { @@ -499,16 +487,16 @@ fun PillToolbar( containerColor = libraryContainerColor, contentColor = libraryContentColor, disabledContainerColor = MaterialTheme.colorScheme.surfaceContainerHighest, - disabledContentColor = MaterialTheme.colorScheme.onSurfaceVariant - ) + disabledContentColor = MaterialTheme.colorScheme.onSurfaceVariant, + ), ) { Row(Modifier) { Icon( Icons.Filled.LibraryMusic, modifier = Modifier.padding(end = 8.dp), contentDescription = stringResource( - R.string.library_toolbar_content_description - ) + R.string.library_toolbar_content_description, + ), ) Text(stringResource(R.string.library_toolbar)) } @@ -534,21 +522,21 @@ fun PillToolbar( containerColor = discoverContainerColor, contentColor = discoverContentColor, disabledContainerColor = MaterialTheme.colorScheme.secondary, - disabledContentColor = MaterialTheme.colorScheme.surfaceContainerHighest - ) + disabledContentColor = MaterialTheme.colorScheme.surfaceContainerHighest, + ), ) { Row { Icon( painterResource(R.drawable.genres), modifier = Modifier.padding(end = 8.dp), contentDescription = stringResource( - R.string.discover_toolbar_content_description - ) + R.string.discover_toolbar_content_description, + ), ) Text(stringResource(R.string.discover_toolbar)) } } - } + }, ) } @@ -582,7 +570,7 @@ private fun HomeContent( modifier = modifier, onHomeAction = onHomeAction, navigateToPodcastDetails = navigateToPodcastDetails, - navigateToPlayer = navigateToPlayer + navigateToPlayer = navigateToPlayer, ) } @@ -600,7 +588,7 @@ private fun HomeContentGrid( ) { LazyVerticalGrid( columns = GridCells.Adaptive(362.dp), - modifier = modifier.fillMaxSize() + modifier = modifier.fillMaxSize(), ) { when (selectedHomeCategory) { HomeCategory.Library -> { @@ -613,7 +601,7 @@ private fun HomeContentGrid( }, navigateToPodcastDetails = navigateToPodcastDetails, modifier = Modifier - .fillMaxWidth() + .fillMaxWidth(), ) } } @@ -622,7 +610,7 @@ private fun HomeContentGrid( library = library, navigateToPlayer = navigateToPlayer, onQueueEpisode = { onHomeAction(HomeAction.QueueEpisode(it)) }, - removeFromQueue = { onHomeAction(HomeAction.RemoveEpisode(it)) } + removeFromQueue = { onHomeAction(HomeAction.RemoveEpisode(it)) }, ) } @@ -637,7 +625,7 @@ private fun HomeContentGrid( onHomeAction(HomeAction.TogglePodcastFollowed(it)) }, onQueueEpisode = { onHomeAction(HomeAction.QueueEpisode(it)) }, - removeFromQueue = { onHomeAction(HomeAction.RemoveEpisode(it)) } + removeFromQueue = { onHomeAction(HomeAction.RemoveEpisode(it)) }, ) } } @@ -658,7 +646,7 @@ private fun FollowedPodcastItem( items = items, onPodcastUnfollowed = onPodcastUnfollowed, navigateToPodcastDetails = navigateToPodcastDetails, - modifier = Modifier.fillMaxWidth() + modifier = Modifier.fillMaxWidth(), ) Spacer(Modifier.height(16.dp)) @@ -679,14 +667,14 @@ private fun FollowedPodcasts( // which solves this problem and avoids this calculation altogether. Once 1.7.0 is // stable, this implementation can be updated. BoxWithConstraints( - modifier = modifier.background(Color.Transparent) + modifier = modifier.background(Color.Transparent), ) { val horizontalPadding = this.maxWidth HorizontalMultiBrowseCarousel( state = rememberCarouselState { items.count() }, preferredItemWidth = 205.dp, itemSpacing = 12.dp, - contentPadding = PaddingValues(8.dp) + contentPadding = PaddingValues(8.dp), ) { page -> val podcast = items[page] FollowedPodcastCarouselItem( @@ -699,7 +687,7 @@ private fun FollowedPodcasts( .maskClip(MaterialTheme.shapes.large) .clickable { navigateToPodcastDetails(podcast) - } + }, ) } } @@ -717,19 +705,19 @@ private fun FollowedPodcastCarouselItem( Box( modifier - .height(230.dp) + .height(230.dp), ) { PodcastImage( podcastImageUrl = podcastImageUrl, contentDescription = podcastTitle, modifier = Modifier .fillMaxSize() - .clip(MaterialTheme.shapes.medium) + .clip(MaterialTheme.shapes.medium), ) ToggleFollowPodcastIconButton( onClick = onUnfollowedClick, isFollowed = true, /* All podcasts are followed in this feed */ - modifier = Modifier.align(Alignment.TopStart) + modifier = Modifier.align(Alignment.TopStart), ) Box(modifier = Modifier.matchParentSize().background(gradient)) if (lastEpisodeDateText != null) { @@ -741,7 +729,7 @@ private fun FollowedPodcastCarouselItem( overflow = TextOverflow.Ellipsis, modifier = Modifier .padding(12.dp) - .align(Alignment.BottomStart) + .align(Alignment.BottomStart), ) } } @@ -769,7 +757,7 @@ private fun lastUpdated(updated: OffsetDateTime): String { private fun HomeAppBarPreview() { JetcasterTheme { HomeAppBar( - isExpanded = false + isExpanded = false, ) } } @@ -788,16 +776,16 @@ private fun PreviewHome() { selectedHomeCategory = HomeCategory.Discover, filterableCategoriesModel = FilterableCategoriesModel( categories = PreviewCategories, - selectedCategory = PreviewCategories.firstOrNull() + selectedCategory = PreviewCategories.firstOrNull(), ), podcastCategoryFilterResult = PodcastCategoryFilterResult( topPodcasts = PreviewPodcasts, - episodes = PreviewPodcastEpisodes + episodes = PreviewPodcastEpisodes, ), library = LibraryInfo(), onHomeAction = {}, navigateToPodcastDetails = {}, - navigateToPlayer = {} + navigateToPlayer = {}, ) } } @@ -810,7 +798,7 @@ private fun PreviewPodcastCard() { modifier = Modifier.size(128.dp), podcastTitle = "", podcastImageUrl = "", - onUnfollowedClick = {} + onUnfollowedClick = {}, ) } } diff --git a/Jetcaster/mobile/src/main/java/com/example/jetcaster/ui/home/HomeViewModel.kt b/Jetcaster/mobile/src/main/java/com/example/jetcaster/ui/home/HomeViewModel.kt index 117f0ffb5..b3106ee1e 100644 --- a/Jetcaster/mobile/src/main/java/com/example/jetcaster/ui/home/HomeViewModel.kt +++ b/Jetcaster/mobile/src/main/java/com/example/jetcaster/ui/home/HomeViewModel.kt @@ -102,16 +102,18 @@ class HomeViewModel @Inject constructor( subscribedPodcasts.flatMapLatest { podcasts -> episodeStore.episodesInPodcasts( podcastUris = podcasts.map { it.podcast.uri }, - limit = 20 + limit = 20, ) - } - ) { homeCategories, - homeCategory, - podcasts, - refreshing, - filterableCategories, - podcastCategoryFilterResult, - libraryEpisodes -> + }, + ) { + homeCategories, + homeCategory, + podcasts, + refreshing, + filterableCategories, + podcastCategoryFilterResult, + libraryEpisodes, + -> _selectedCategory.value = filterableCategories.selectedCategory @@ -127,14 +129,14 @@ class HomeViewModel @Inject constructor( featuredPodcasts = podcasts.map { it.asExternalModel() }.toPersistentList(), filterableCategoriesModel = filterableCategories, podcastCategoryFilterResult = podcastCategoryFilterResult, - library = libraryEpisodes.asLibrary() + library = libraryEpisodes.asLibrary(), ) }.catch { throwable -> emit( HomeScreenUiState( isLoading = false, - errorMessage = throwable.message - ) + errorMessage = throwable.message, + ), ) }.collect { _state.value = it @@ -203,13 +205,13 @@ class HomeViewModel @Inject constructor( } } -private fun List.asLibrary(): LibraryInfo = - LibraryInfo( - episodes = this.map { it.asPodcastToEpisodeInfo() } - ) +private fun List.asLibrary(): LibraryInfo = LibraryInfo( + episodes = this.map { it.asPodcastToEpisodeInfo() }, +) enum class HomeCategory { - Library, Discover + Library, + Discover, } @Immutable diff --git a/Jetcaster/mobile/src/main/java/com/example/jetcaster/ui/home/category/PodcastCategory.kt b/Jetcaster/mobile/src/main/java/com/example/jetcaster/ui/home/category/PodcastCategory.kt index bcc24e20b..fa84db701 100644 --- a/Jetcaster/mobile/src/main/java/com/example/jetcaster/ui/home/category/PodcastCategory.kt +++ b/Jetcaster/mobile/src/main/java/com/example/jetcaster/ui/home/category/PodcastCategory.kt @@ -64,14 +64,13 @@ fun LazyGridScope.podcastCategory( navigateToPlayer: (EpisodeInfo) -> Unit, onQueueEpisode: (PlayerEpisode) -> Unit, removeFromQueue: (EpisodeInfo) -> Unit, - onTogglePodcastFollowed: (PodcastInfo) -> Unit, ) { fullWidthItem { CategoryPodcasts( topPodcasts = podcastCategoryFilterResult.topPodcasts, navigateToPodcastDetails = navigateToPodcastDetails, - onTogglePodcastFollowed = onTogglePodcastFollowed + onTogglePodcastFollowed = onTogglePodcastFollowed, ) } @@ -92,10 +91,10 @@ fun LazyGridScope.podcastCategory( .animateItem(), imageModifier = Modifier.sharedElement( sharedContentState = rememberSharedContentState( - key = item.episode.title + key = item.episode.title, ), animatedVisibilityScope = animatedVisibilityScope, - clipInOverlayDuringTransition = OverlayClip(MaterialTheme.shapes.medium) + clipInOverlayDuringTransition = OverlayClip(MaterialTheme.shapes.medium), ), removeFromQueue = removeFromQueue, ) @@ -113,7 +112,7 @@ private fun CategoryPodcasts( podcasts = topPodcasts, onTogglePodcastFollowed = onTogglePodcastFollowed, navigateToPodcastDetails = navigateToPodcastDetails, - modifier = Modifier.fillMaxWidth() + modifier = Modifier.fillMaxWidth(), ) } @@ -129,7 +128,7 @@ private fun CategoryPodcastRow( state = rememberCarouselState { podcasts.count() }, modifier = modifier.padding(start = 8.dp), itemWidth = 128.dp, - itemSpacing = 4.dp + itemSpacing = 4.dp, ) { i -> val podcast = podcasts[i] TopPodcastRowItem( @@ -142,7 +141,7 @@ private fun CategoryPodcastRow( .clickable { navigateToPodcastDetails(podcast) } - .maskClip(MaterialTheme.shapes.large) + .maskClip(MaterialTheme.shapes.large), ) } } @@ -162,7 +161,7 @@ private fun TopPodcastRowItem( .fillMaxWidth() .height(128.dp) .aspectRatio(1f) - .clip(MaterialTheme.shapes.large) + .clip(MaterialTheme.shapes.large), ) { PodcastImage( modifier = Modifier @@ -175,7 +174,7 @@ private fun TopPodcastRowItem( ToggleFollowPodcastIconButton( onClick = onToggleFollowClicked, isFollowed = isFollowed, - modifier = Modifier.align(Alignment.TopStart) + modifier = Modifier.align(Alignment.TopStart), ) Box(modifier = Modifier.matchParentSize().background(gradient)) @@ -188,7 +187,7 @@ private fun TopPodcastRowItem( overflow = TextOverflow.Ellipsis, modifier = Modifier .align(Alignment.BottomStart) - .padding(start = 16.dp, bottom = 16.dp) + .padding(start = 16.dp, bottom = 16.dp), ) } } @@ -202,7 +201,7 @@ fun PreviewEpisodeListItem() { podcast = PreviewPodcasts[0], onClick = { }, onQueueEpisode = { }, - modifier = Modifier.fillMaxWidth() + modifier = Modifier.fillMaxWidth(), ) } } diff --git a/Jetcaster/mobile/src/main/java/com/example/jetcaster/ui/home/discover/Discover.kt b/Jetcaster/mobile/src/main/java/com/example/jetcaster/ui/home/discover/Discover.kt index 2126da404..45011d4fe 100644 --- a/Jetcaster/mobile/src/main/java/com/example/jetcaster/ui/home/discover/Discover.kt +++ b/Jetcaster/mobile/src/main/java/com/example/jetcaster/ui/home/discover/Discover.kt @@ -69,7 +69,7 @@ fun LazyGridScope.discoverItems( PodcastCategoryTabs( filterableCategoriesModel = filterableCategoriesModel, onCategorySelected = onCategorySelected, - modifier = Modifier.fillMaxWidth() + modifier = Modifier.fillMaxWidth(), ) Spacer(Modifier.height(8.dp)) @@ -92,7 +92,7 @@ private fun PodcastCategoryTabs( modifier: Modifier = Modifier, ) { val selectedIndex = filterableCategoriesModel.categories.indexOf( - filterableCategoriesModel.selectedCategory + filterableCategoriesModel.selectedCategory, ) LazyRow( modifier = modifier, @@ -101,7 +101,7 @@ private fun PodcastCategoryTabs( ) { itemsIndexed( items = filterableCategoriesModel.categories, - key = { i, category -> category.id } + key = { i, category -> category.id }, ) { index, category -> ChoiceChipContent( text = category.name, @@ -115,12 +115,7 @@ private fun PodcastCategoryTabs( @OptIn(ExperimentalMaterial3Api::class) @Composable -private fun ChoiceChipContent( - text: String, - selected: Boolean, - onClick: () -> Unit, - modifier: Modifier = Modifier, -) { +private fun ChoiceChipContent(text: String, selected: Boolean, onClick: () -> Unit, modifier: Modifier = Modifier) { FilterChip( selected = selected, onClick = onClick, @@ -129,7 +124,7 @@ private fun ChoiceChipContent( Icon( imageVector = Icons.Default.Check, contentDescription = stringResource(id = R.string.cd_selected_category), - modifier = Modifier.height(18.dp) + modifier = Modifier.height(18.dp), ) } }, diff --git a/Jetcaster/mobile/src/main/java/com/example/jetcaster/ui/home/library/Library.kt b/Jetcaster/mobile/src/main/java/com/example/jetcaster/ui/home/library/Library.kt index a6533d497..95c810944 100644 --- a/Jetcaster/mobile/src/main/java/com/example/jetcaster/ui/home/library/Library.kt +++ b/Jetcaster/mobile/src/main/java/com/example/jetcaster/ui/home/library/Library.kt @@ -52,7 +52,7 @@ fun LazyGridScope.libraryItems( items( library, - key = { it.episode.uri } + key = { it.episode.uri }, ) { item -> EpisodeListItem( episode = item.episode, diff --git a/Jetcaster/mobile/src/main/java/com/example/jetcaster/ui/player/PlayerScreen.kt b/Jetcaster/mobile/src/main/java/com/example/jetcaster/ui/player/PlayerScreen.kt index 678cf9f7a..9c7a4498b 100644 --- a/Jetcaster/mobile/src/main/java/com/example/jetcaster/ui/player/PlayerScreen.kt +++ b/Jetcaster/mobile/src/main/java/com/example/jetcaster/ui/player/PlayerScreen.kt @@ -60,7 +60,6 @@ import androidx.compose.material.icons.filled.SkipPrevious import androidx.compose.material.icons.outlined.Pause import androidx.compose.material.icons.outlined.PlayArrow import androidx.compose.material3.ButtonGroup -import androidx.compose.material3.ButtonShapes import androidx.compose.material3.CircularProgressIndicator import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi import androidx.compose.material3.Icon @@ -75,6 +74,7 @@ import androidx.compose.material3.SnackbarHostState import androidx.compose.material3.Text import androidx.compose.material3.ToggleButton import androidx.compose.material3.ToggleButtonColors +import androidx.compose.material3.ToggleButtonShapes import androidx.compose.runtime.Composable import androidx.compose.runtime.DisposableEffect import androidx.compose.runtime.getValue @@ -113,8 +113,8 @@ import com.example.jetcaster.util.verticalGradientScrim import com.google.accompanist.adaptive.HorizontalTwoPaneStrategy import com.google.accompanist.adaptive.TwoPane import com.google.accompanist.adaptive.VerticalTwoPaneStrategy -import kotlinx.coroutines.launch import java.time.Duration +import kotlinx.coroutines.launch /** * Stateful version of the Podcast player @@ -174,7 +174,7 @@ private fun PlayerScreen( snackbarHost = { SnackbarHost(hostState = snackbarHostState) }, - modifier = modifier + modifier = modifier, ) { contentPadding -> if (uiState.episodePlayerState.currentEpisode != null) { PlayerContentWithBackground( @@ -198,10 +198,7 @@ private fun PlayerScreen( } @Composable -private fun PlayerBackground( - episode: PlayerEpisode?, - modifier: Modifier, -) { +private fun PlayerBackground(episode: PlayerEpisode?, modifier: Modifier) { ImageBackgroundColorScrim( url = episode?.podcastImageUrl, color = MaterialTheme.colorScheme.surface.copy(alpha = 0.9f), @@ -225,7 +222,7 @@ fun PlayerContentWithBackground( episode = uiState.episodePlayerState.currentEpisode, modifier = Modifier .fillMaxSize() - .padding(contentPadding) + .padding(contentPadding), ) PlayerContent( uiState = uiState, @@ -279,7 +276,8 @@ fun PlayerContent( isTableTopPosture(foldingFeature) || ( isSeparatingPosture(foldingFeature) && - foldingFeature.orientation == FoldingFeature.Orientation.HORIZONTAL + foldingFeature.orientation == + FoldingFeature.Orientation.HORIZONTAL ) if (usingVerticalStrategy) { @@ -308,10 +306,10 @@ fun PlayerContent( .verticalGradientScrim( color = MaterialTheme.colorScheme.primary.copy(alpha = 0.50f), startYPercentage = 1f, - endYPercentage = 0f + endYPercentage = 0f, ) .systemBarsPadding() - .padding(horizontal = 8.dp) + .padding(horizontal = 8.dp), ) { TopAppBar( onBackPress = onBackPress, @@ -328,7 +326,7 @@ fun PlayerContent( ) }, strategy = HorizontalTwoPaneStrategy(splitFraction = 0.5f), - displayFeatures = displayFeatures + displayFeatures = displayFeatures, ) } } @@ -368,10 +366,10 @@ private fun PlayerContentRegular( .verticalGradientScrim( color = MaterialTheme.colorScheme.primary.copy(alpha = 0.50f), startYPercentage = 1f, - endYPercentage = 0f + endYPercentage = 0f, ) .systemBarsPadding() - .padding(horizontal = 8.dp) + .padding(horizontal = 8.dp), ) { TopAppBar( onBackPress = onBackPress, @@ -379,7 +377,7 @@ private fun PlayerContentRegular( ) Column( horizontalAlignment = Alignment.CenterHorizontally, - modifier = Modifier.padding(horizontal = 8.dp) + modifier = Modifier.padding(horizontal = 8.dp), ) { Spacer(modifier = Modifier.weight(1f)) with(sharedTransitionScope) { @@ -390,14 +388,15 @@ private fun PlayerContentRegular( .weight(10f) .animateEnterExit( enter = fadeIn(spring(stiffness = Spring.StiffnessLow)), - exit = fadeOut() + exit = fadeOut(), ), imageModifier = Modifier.sharedElement( sharedContentState = rememberSharedContentState( - key = currentEpisode.title + key = currentEpisode.title, ), animatedVisibilityScope = animatedVisibilityScope, - clipInOverlayDuringTransition = OverlayClip(MaterialTheme.shapes.medium) + clipInOverlayDuringTransition = + OverlayClip(MaterialTheme.shapes.medium), ), ) } @@ -406,13 +405,13 @@ private fun PlayerContentRegular( Spacer(modifier = Modifier.height(32.dp)) Column( horizontalAlignment = Alignment.CenterHorizontally, - modifier = Modifier.weight(10f) + modifier = Modifier.weight(10f), ) { PlayerSlider( timeElapsed = playerEpisode.timeElapsed, episodeDuration = currentEpisode.duration, onSeekingStarted = playerControlActions.onSeekingStarted, - onSeekingFinished = playerControlActions.onSeekingFinished + onSeekingFinished = playerControlActions.onSeekingFinished, ) PlayerButtons( hasNext = playerEpisode.queue.isNotEmpty(), @@ -423,7 +422,7 @@ private fun PlayerContentRegular( onRewindBy = playerControlActions.onRewindBy, onNext = playerControlActions.onNext, onPrevious = playerControlActions.onPrevious, - Modifier.padding(vertical = 8.dp) + Modifier.padding(vertical = 8.dp), ) } Spacer(modifier = Modifier.weight(1f)) @@ -436,10 +435,7 @@ private fun PlayerContentRegular( * The UI for the top pane of a tabletop layout. */ @Composable -private fun PlayerContentTableTopTop( - uiState: PlayerUiState, - modifier: Modifier = Modifier, -) { +private fun PlayerContentTableTopTop(uiState: PlayerUiState, modifier: Modifier = Modifier) { // Content for the top part of the screen val episode = uiState.episodePlayerState.currentEpisode ?: return Column( @@ -448,15 +444,15 @@ private fun PlayerContentTableTopTop( .verticalGradientScrim( color = MaterialTheme.colorScheme.primary.copy(alpha = 0.50f), startYPercentage = 1f, - endYPercentage = 0f + endYPercentage = 0f, ) .windowInsetsPadding( WindowInsets.systemBars.only( - WindowInsetsSides.Horizontal + WindowInsetsSides.Top - ) + WindowInsetsSides.Horizontal + WindowInsetsSides.Top, + ), ) .padding(32.dp), - horizontalAlignment = Alignment.CenterHorizontally + horizontalAlignment = Alignment.CenterHorizontally, ) { PlayerImage(episode.podcastImageUrl) } @@ -480,11 +476,11 @@ private fun PlayerContentTableTopBottom( modifier = modifier .windowInsetsPadding( WindowInsets.systemBars.only( - WindowInsetsSides.Horizontal + WindowInsetsSides.Bottom - ) + WindowInsetsSides.Horizontal + WindowInsetsSides.Bottom, + ), ) .padding(horizontal = 32.dp, vertical = 8.dp), - horizontalAlignment = Alignment.CenterHorizontally + horizontalAlignment = Alignment.CenterHorizontally, ) { TopAppBar( onBackPress = onBackPress, @@ -497,7 +493,7 @@ private fun PlayerContentTableTopBottom( Spacer(modifier = Modifier.weight(0.5f)) Column( horizontalAlignment = Alignment.CenterHorizontally, - modifier = Modifier.weight(10f) + modifier = Modifier.weight(10f), ) { PlayerButtons( hasNext = episodePlayerState.queue.isNotEmpty(), @@ -508,13 +504,13 @@ private fun PlayerContentTableTopBottom( onRewindBy = playerControlActions.onRewindBy, onNext = playerControlActions.onNext, onPrevious = playerControlActions.onPrevious, - modifier = Modifier.padding(top = 8.dp) + modifier = Modifier.padding(top = 8.dp), ) PlayerSlider( timeElapsed = episodePlayerState.timeElapsed, episodeDuration = episode.duration, onSeekingStarted = playerControlActions.onSeekingStarted, - onSeekingFinished = playerControlActions.onSeekingFinished + onSeekingFinished = playerControlActions.onSeekingFinished, ) } } @@ -524,10 +520,7 @@ private fun PlayerContentTableTopBottom( * The UI for the start pane of a book layout. */ @Composable -private fun PlayerContentBookStart( - uiState: PlayerUiState, - modifier: Modifier = Modifier, -) { +private fun PlayerContentBookStart(uiState: PlayerUiState, modifier: Modifier = Modifier) { val episode = uiState.episodePlayerState.currentEpisode ?: return Column( modifier = modifier @@ -535,7 +528,7 @@ private fun PlayerContentBookStart( .verticalScroll(rememberScrollState()) .padding( vertical = 40.dp, - horizontal = 16.dp + horizontal = 16.dp, ), horizontalAlignment = Alignment.CenterHorizontally, ) { @@ -551,11 +544,7 @@ private fun PlayerContentBookStart( * The UI for the end pane of a book layout. */ @Composable -private fun PlayerContentBookEnd( - uiState: PlayerUiState, - playerControlActions: PlayerControlActions, - modifier: Modifier = Modifier, -) { +private fun PlayerContentBookEnd(uiState: PlayerUiState, playerControlActions: PlayerControlActions, modifier: Modifier = Modifier) { val episodePlayerState = uiState.episodePlayerState val episode = episodePlayerState.currentEpisode ?: return Column( @@ -569,7 +558,7 @@ private fun PlayerContentBookEnd( podcastImageUrl = episode.podcastImageUrl, modifier = Modifier .padding(vertical = 16.dp) - .weight(1f) + .weight(1f), ) PlayerSlider( timeElapsed = episodePlayerState.timeElapsed, @@ -586,45 +575,38 @@ private fun PlayerContentBookEnd( onRewindBy = playerControlActions.onRewindBy, onNext = playerControlActions.onNext, onPrevious = playerControlActions.onPrevious, - Modifier.padding(vertical = 8.dp) + Modifier.padding(vertical = 8.dp), ) } } @Composable -private fun TopAppBar( - onBackPress: () -> Unit, - onAddToQueue: () -> Unit, -) { +private fun TopAppBar(onBackPress: () -> Unit, onAddToQueue: () -> Unit) { Row(Modifier.fillMaxWidth()) { IconButton(onClick = onBackPress) { Icon( imageVector = Icons.AutoMirrored.Filled.ArrowBack, - contentDescription = stringResource(R.string.cd_back) + contentDescription = stringResource(R.string.cd_back), ) } Spacer(Modifier.weight(1f)) IconButton(onClick = onAddToQueue) { Icon( imageVector = Icons.AutoMirrored.Filled.PlaylistAdd, - contentDescription = stringResource(R.string.cd_add) + contentDescription = stringResource(R.string.cd_add), ) } IconButton(onClick = { /* TODO */ }) { Icon( imageVector = Icons.Default.MoreVert, - contentDescription = stringResource(R.string.cd_more) + contentDescription = stringResource(R.string.cd_more), ) } } } @Composable -private fun PlayerImage( - podcastImageUrl: String, - modifier: Modifier = Modifier, - imageModifier: Modifier = Modifier, -) { +private fun PlayerImage(podcastImageUrl: String, modifier: Modifier = Modifier, imageModifier: Modifier = Modifier) { PodcastImage( podcastImageUrl = podcastImageUrl, contentDescription = null, @@ -633,27 +615,24 @@ private fun PlayerImage( .sizeIn(maxWidth = 500.dp, maxHeight = 500.dp) .aspectRatio(1f) .clip(MaterialTheme.shapes.medium), - imageModifier = imageModifier + imageModifier = imageModifier, ) } @Composable -private fun PodcastDescription( - title: String, - podcastName: String -) { +private fun PodcastDescription(title: String, podcastName: String) { Text( text = title, style = MaterialTheme.typography.displayLarge, maxLines = 2, color = MaterialTheme.colorScheme.onSurface, - modifier = Modifier.basicMarquee() + modifier = Modifier.basicMarquee(), ) Text( text = podcastName, style = MaterialTheme.typography.bodyLarge, color = MaterialTheme.colorScheme.onSurface, - maxLines = 1 + maxLines = 1, ) } @@ -675,19 +654,19 @@ private fun PodcastInformation( text = name, style = nameTextStyle, maxLines = 1, - overflow = TextOverflow.Ellipsis + overflow = TextOverflow.Ellipsis, ) Text( text = title, style = titleTextStyle, maxLines = 1, - overflow = TextOverflow.Ellipsis + overflow = TextOverflow.Ellipsis, ) HtmlTextContainer(text = summary) { Text( text = it, style = MaterialTheme.typography.bodyMedium, - color = LocalContentColor.current + color = LocalContentColor.current, ) } } @@ -709,7 +688,7 @@ private fun PlayerSlider( Column( Modifier .fillMaxWidth() - .padding(horizontal = 16.dp) + .padding(horizontal = 16.dp), ) { var sliderValue by remember(timeElapsed) { mutableStateOf(timeElapsed) } val maxRange = (episodeDuration?.toSeconds() ?: 0).toFloat() @@ -718,7 +697,7 @@ private fun PlayerSlider( Text( text = "${sliderValue.formatString()} • ${episodeDuration?.formatString()}", style = MaterialTheme.typography.bodyMedium, - color = MaterialTheme.colorScheme.onSurfaceVariant + color = MaterialTheme.colorScheme.onSurfaceVariant, ) } @@ -729,7 +708,7 @@ private fun PlayerSlider( onSeekingStarted() sliderValue = Duration.ofSeconds(it.toLong()) }, - onValueChangeFinished = { onSeekingFinished(sliderValue) } + onValueChangeFinished = { onSeekingFinished(sliderValue) }, ) } } @@ -750,7 +729,13 @@ private fun PlayerButtons( Column(modifier = modifier, horizontalAlignment = Alignment.CenterHorizontally) { ToggleButton( checked = isPlaying, - onCheckedChange = { if(isPlaying) { onPausePress() } else { onPlayPress() } }, + onCheckedChange = { + if (isPlaying) { + onPausePress() + } else { + onPlayPress() + } + }, colors = ToggleButtonColors( containerColor = MaterialTheme.colorScheme.primary, contentColor = MaterialTheme.colorScheme.onPrimary, @@ -759,22 +744,24 @@ private fun PlayerButtons( checkedContainerColor = MaterialTheme.colorScheme.primary, checkedContentColor = MaterialTheme.colorScheme.onPrimary, ), - shapes = ButtonShapes( - shape = RoundedCornerShape(60.dp), - pressedShape = if(isPlaying) RoundedCornerShape(60.dp) else RoundedCornerShape(30.dp), - checkedShape = RoundedCornerShape(30.dp) + shapes = ToggleButtonShapes( + shape = RoundedCornerShape(60.dp), + pressedShape = RoundedCornerShape(if (isPlaying) 60.dp else 30.dp), + checkedShape = RoundedCornerShape(30.dp), ), - modifier = Modifier.width(186.dp).height(136.dp) + modifier = Modifier + .width(186.dp) + .height(136.dp), ) { Icon( - imageVector = if(isPlaying) Icons.Outlined.Pause else Icons.Outlined.PlayArrow, + imageVector = if (isPlaying) Icons.Outlined.Pause else Icons.Outlined.PlayArrow, modifier = Modifier.fillMaxSize(), - contentDescription = null + contentDescription = null, ) } ButtonGroup( modifier = Modifier.padding(vertical = 16.dp, horizontal = 8.dp), - horizontalArrangement = Arrangement.spacedBy(8.dp) + horizontalArrangement = Arrangement.spacedBy(8.dp), ) { val skipButtonsModifier = Modifier .width(56.dp) @@ -791,31 +778,31 @@ private fun PlayerButtons( containerColor = MaterialTheme.colorScheme.surfaceContainerHighest, contentColor = MaterialTheme.colorScheme.onSurface, disabledContainerColor = MaterialTheme.colorScheme.surfaceContainerHighest, - disabledContentColor = MaterialTheme.colorScheme.onPrimary + disabledContentColor = MaterialTheme.colorScheme.onPrimary, ), enabled = isPlaying, ) { Icon( imageVector = Icons.Filled.SkipPrevious, - contentDescription = null + contentDescription = null, ) } IconButton( - onClick = { onRewindBy(Duration.ofSeconds(10)) } , + onClick = { onRewindBy(Duration.ofSeconds(10)) }, modifier = rewindFastForwardButtonsModifier, shape = RoundedCornerShape(15.dp), colors = IconButtonColors( containerColor = MaterialTheme.colorScheme.secondary, contentColor = MaterialTheme.colorScheme.onSecondary, disabledContainerColor = MaterialTheme.colorScheme.secondary, - disabledContentColor = MaterialTheme.colorScheme.onSecondary + disabledContentColor = MaterialTheme.colorScheme.onSecondary, ), enabled = isPlaying, ) { Icon( imageVector = Icons.Filled.Replay10, - contentDescription = null + contentDescription = null, ) } @@ -827,13 +814,13 @@ private fun PlayerButtons( containerColor = MaterialTheme.colorScheme.secondary, contentColor = MaterialTheme.colorScheme.onSecondary, disabledContainerColor = MaterialTheme.colorScheme.secondary, - disabledContentColor = MaterialTheme.colorScheme.onSecondary + disabledContentColor = MaterialTheme.colorScheme.onSecondary, ), enabled = isPlaying, ) { Icon( imageVector = Icons.Filled.Forward10, - contentDescription = null + contentDescription = null, ) } @@ -845,13 +832,13 @@ private fun PlayerButtons( containerColor = MaterialTheme.colorScheme.surfaceContainerHighest, contentColor = MaterialTheme.colorScheme.onSurface, disabledContainerColor = MaterialTheme.colorScheme.surfaceContainerHighest, - disabledContentColor = MaterialTheme.colorScheme.onSurface + disabledContentColor = MaterialTheme.colorScheme.onSurface, ), enabled = hasNext, ) { Icon( imageVector = Icons.Filled.SkipNext, - contentDescription = null + contentDescription = null, ) } } @@ -866,7 +853,7 @@ private fun FullScreenLoading(modifier: Modifier = Modifier) { Box( modifier = modifier .fillMaxSize() - .wrapContentSize(Alignment.Center) + .wrapContentSize(Alignment.Center), ) { CircularProgressIndicator() } @@ -918,7 +905,7 @@ fun PlayerScreenPreview() { PlayerEpisode(), PlayerEpisode(), PlayerEpisode(), - ) + ), ), ), displayFeatures = emptyList(), @@ -935,7 +922,7 @@ fun PlayerScreenPreview() { onSeekingFinished = {}, onNext = {}, onPrevious = {}, - ) + ), ) } } diff --git a/Jetcaster/mobile/src/main/java/com/example/jetcaster/ui/player/PlayerViewModel.kt b/Jetcaster/mobile/src/main/java/com/example/jetcaster/ui/player/PlayerViewModel.kt index a264db77c..a12300790 100644 --- a/Jetcaster/mobile/src/main/java/com/example/jetcaster/ui/player/PlayerViewModel.kt +++ b/Jetcaster/mobile/src/main/java/com/example/jetcaster/ui/player/PlayerViewModel.kt @@ -36,9 +36,7 @@ import kotlinx.coroutines.flow.flatMapConcat import kotlinx.coroutines.flow.map import kotlinx.coroutines.launch -data class PlayerUiState( - val episodePlayerState: EpisodePlayerState = EpisodePlayerState() -) +data class PlayerUiState(val episodePlayerState: EpisodePlayerState = EpisodePlayerState()) /** * ViewModel that handles the business logic and screen state of the Player screen @@ -48,7 +46,7 @@ data class PlayerUiState( class PlayerViewModel @Inject constructor( episodeStore: EpisodeStore, private val episodePlayer: EpisodePlayer, - savedStateHandle: SavedStateHandle + savedStateHandle: SavedStateHandle, ) : ViewModel() { // episodeUri should always be present in the PlayerViewModel. diff --git a/Jetcaster/mobile/src/main/java/com/example/jetcaster/ui/podcast/PodcastDetailsScreen.kt b/Jetcaster/mobile/src/main/java/com/example/jetcaster/ui/podcast/PodcastDetailsScreen.kt index cc65a3f2f..a08752784 100644 --- a/Jetcaster/mobile/src/main/java/com/example/jetcaster/ui/podcast/PodcastDetailsScreen.kt +++ b/Jetcaster/mobile/src/main/java/com/example/jetcaster/ui/podcast/PodcastDetailsScreen.kt @@ -40,7 +40,6 @@ import androidx.compose.material.icons.filled.Check import androidx.compose.material.icons.filled.NotificationsActive import androidx.compose.material.icons.filled.NotificationsNone import androidx.compose.material3.ButtonGroup -import androidx.compose.material3.ButtonShapes import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi import androidx.compose.material3.Icon @@ -52,6 +51,7 @@ import androidx.compose.material3.SnackbarHostState import androidx.compose.material3.Text import androidx.compose.material3.ToggleButton import androidx.compose.material3.ToggleButtonColors +import androidx.compose.material3.ToggleButtonShapes import androidx.compose.material3.TopAppBar import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue @@ -90,13 +90,13 @@ fun PodcastDetailsScreen( navigateToPlayer: (EpisodeInfo) -> Unit, navigateBack: () -> Unit, showBackButton: Boolean, - modifier: Modifier = Modifier + modifier: Modifier = Modifier, ) { val state by viewModel.state.collectAsStateWithLifecycle() when (val s = state) { is PodcastUiState.Loading -> { PodcastDetailsLoadingScreen( - modifier = Modifier.fillMaxSize() + modifier = Modifier.fillMaxSize(), ) } @@ -117,9 +117,7 @@ fun PodcastDetailsScreen( } @Composable -private fun PodcastDetailsLoadingScreen( - modifier: Modifier = Modifier -) { +private fun PodcastDetailsLoadingScreen(modifier: Modifier = Modifier) { Loading(modifier = modifier) } @@ -144,13 +142,13 @@ fun PodcastDetailsScreen( if (showBackButton) { PodcastDetailsTopAppBar( navigateBack = navigateBack, - modifier = Modifier.fillMaxWidth() + modifier = Modifier.fillMaxWidth(), ) } }, snackbarHost = { SnackbarHost(hostState = snackbarHostState) - } + }, ) { contentPadding -> PodcastDetailsContent( podcast = podcast, @@ -164,7 +162,7 @@ fun PodcastDetailsScreen( onQueueEpisode(it) }, navigateToPlayer = navigateToPlayer, - modifier = Modifier.padding(contentPadding) + modifier = Modifier.padding(contentPadding), ) } } @@ -177,11 +175,11 @@ fun PodcastDetailsContent( toggleSubscribe: (PodcastInfo) -> Unit, onQueueEpisode: (PlayerEpisode) -> Unit, navigateToPlayer: (EpisodeInfo) -> Unit, - modifier: Modifier = Modifier + modifier: Modifier = Modifier, ) { LazyVerticalGrid( columns = GridCells.Adaptive(362.dp), - modifier.fillMaxSize() + modifier.fillMaxSize(), ) { fullWidthItem { PodcastDetailsHeaderItem( @@ -201,20 +199,16 @@ fun PodcastDetailsContent( .fillMaxWidth() .animateItem(), showPodcastImage = false, - showSummary = true + showSummary = true, ) } } } @Composable -fun PodcastDetailsHeaderItem( - podcast: PodcastInfo, - toggleSubscribe: (PodcastInfo) -> Unit, - modifier: Modifier = Modifier -) { +fun PodcastDetailsHeaderItem(podcast: PodcastInfo, toggleSubscribe: (PodcastInfo) -> Unit, modifier: Modifier = Modifier) { Box( - modifier = modifier.padding(Keyline1) + modifier = modifier.padding(Keyline1), ) { Column { PodcastImage( @@ -223,7 +217,7 @@ fun PodcastDetailsHeaderItem( .clip(MaterialTheme.shapes.large) .align(Alignment.CenterHorizontally), podcastImageUrl = podcast.imageUrl, - contentDescription = podcast.title + contentDescription = podcast.title, ) Text( text = podcast.title, @@ -231,34 +225,31 @@ fun PodcastDetailsHeaderItem( overflow = TextOverflow.Ellipsis, color = MaterialTheme.colorScheme.onSurface, style = MaterialTheme.typography.displayMedium, - modifier = Modifier.padding(top = 16.dp) + modifier = Modifier.padding(top = 16.dp), ) PodcastDetailsDescription( podcast = podcast, modifier = Modifier .fillMaxWidth() - .padding(vertical = 16.dp) + .padding(vertical = 16.dp), ) PodcastDetailsHeaderItemButtons( isSubscribed = podcast.isSubscribed ?: false, onClick = { toggleSubscribe(podcast) }, - modifier = Modifier.fillMaxWidth() + modifier = Modifier.fillMaxWidth(), ) } } } @Composable -fun PodcastDetailsDescription( - podcast: PodcastInfo, - modifier: Modifier -) { +fun PodcastDetailsDescription(podcast: PodcastInfo, modifier: Modifier) { var isExpanded by remember { mutableStateOf(false) } var showSeeMore by remember { mutableStateOf(false) } Box( - modifier = modifier.clickable { isExpanded = !isExpanded } + modifier = modifier.clickable { isExpanded = !isExpanded }, ) { Text( text = podcast.description, @@ -271,23 +262,23 @@ fun PodcastDetailsDescription( modifier = Modifier.animateContentSize( animationSpec = tween( durationMillis = 200, - easing = EaseOutExpo - ) - ) + easing = EaseOutExpo, + ), + ), ) if (showSeeMore) { Box( modifier = Modifier .align(Alignment.BottomEnd) - .background(MaterialTheme.colorScheme.surface) + .background(MaterialTheme.colorScheme.surface), ) { Text( text = stringResource(id = R.string.see_more), style = MaterialTheme.typography.bodyMedium.copy( textDecoration = TextDecoration.Underline, - fontWeight = FontWeight.Bold + fontWeight = FontWeight.Bold, ), - modifier = Modifier.padding(start = 16.dp) + modifier = Modifier.padding(start = 16.dp), ) } } @@ -296,11 +287,7 @@ fun PodcastDetailsDescription( @OptIn(ExperimentalMaterial3ExpressiveApi::class) @Composable -fun PodcastDetailsHeaderItemButtons( - isSubscribed: Boolean, - onClick: () -> Unit, - modifier: Modifier = Modifier -) { +fun PodcastDetailsHeaderItemButtons(isSubscribed: Boolean, onClick: () -> Unit, modifier: Modifier = Modifier) { var isNotificationOn by remember { mutableStateOf(false) } ButtonGroup(modifier = modifier) { ToggleButton( @@ -314,17 +301,15 @@ fun PodcastDetailsHeaderItemButtons( checkedContainerColor = MaterialTheme.colorScheme.surfaceContainer, checkedContentColor = MaterialTheme.colorScheme.secondary, ), - shapes = ButtonShapes( + shapes = ToggleButtonShapes( shape = RoundedCornerShape(15.dp), - pressedShape = if (isSubscribed) RoundedCornerShape(15.dp) else RoundedCornerShape( - 60.dp - ), - checkedShape = RoundedCornerShape(60.dp) + pressedShape = RoundedCornerShape(if (isSubscribed) 15.dp else 60.dp), + checkedShape = RoundedCornerShape(60.dp), ), modifier = Modifier .width(76.dp) .height(56.dp) - .semantics(mergeDescendants = true) { } + .semantics(mergeDescendants = true) { }, ) { Icon( imageVector = if (isSubscribed) @@ -345,15 +330,19 @@ fun PodcastDetailsHeaderItemButtons( checkedContainerColor = MaterialTheme.colorScheme.surfaceContainer, checkedContentColor = MaterialTheme.colorScheme.secondary, ), - shapes = ButtonShapes( + shapes = ToggleButtonShapes( shape = RoundedCornerShape(100.dp), - pressedShape = if (isNotificationOn) RoundedCornerShape(100.dp) else RoundedCornerShape(20.dp), - checkedShape = RoundedCornerShape(20.dp) + pressedShape = RoundedCornerShape(if (isNotificationOn) 100.dp else 20.dp), + checkedShape = RoundedCornerShape(20.dp), ), modifier = Modifier.size(56.dp), ) { Icon( - imageVector = if (isNotificationOn) Icons.Default.NotificationsActive else Icons.Default.NotificationsNone, + imageVector = if (isNotificationOn) { + Icons.Default.NotificationsActive + } else { + Icons.Default.NotificationsNone + }, contentDescription = stringResource(R.string.cd_more), ) } @@ -362,21 +351,18 @@ fun PodcastDetailsHeaderItemButtons( @OptIn(ExperimentalMaterial3Api::class) @Composable -fun PodcastDetailsTopAppBar( - navigateBack: () -> Unit, - modifier: Modifier = Modifier -) { +fun PodcastDetailsTopAppBar(navigateBack: () -> Unit, modifier: Modifier = Modifier) { TopAppBar( title = { }, navigationIcon = { IconButton(onClick = navigateBack) { Icon( imageVector = Icons.AutoMirrored.Filled.ArrowBack, - contentDescription = stringResource(id = R.string.cd_back) + contentDescription = stringResource(id = R.string.cd_back), ) } }, - modifier = modifier + modifier = modifier, ) } diff --git a/Jetcaster/mobile/src/main/java/com/example/jetcaster/ui/podcast/PodcastDetailsViewModel.kt b/Jetcaster/mobile/src/main/java/com/example/jetcaster/ui/podcast/PodcastDetailsViewModel.kt index 4f4d94474..9d4332743 100644 --- a/Jetcaster/mobile/src/main/java/com/example/jetcaster/ui/podcast/PodcastDetailsViewModel.kt +++ b/Jetcaster/mobile/src/main/java/com/example/jetcaster/ui/podcast/PodcastDetailsViewModel.kt @@ -39,10 +39,7 @@ import kotlinx.coroutines.launch sealed interface PodcastUiState { data object Loading : PodcastUiState - data class Ready( - val podcast: PodcastInfo, - val episodes: List, - ) : PodcastUiState + data class Ready(val podcast: PodcastInfo, val episodes: List) : PodcastUiState } /** @@ -61,7 +58,7 @@ class PodcastDetailsViewModel @AssistedInject constructor( val state: StateFlow = combine( podcastStore.podcastWithExtraInfo(decodedPodcastUri), - episodeStore.episodesInPodcast(decodedPodcastUri) + episodeStore.episodesInPodcast(decodedPodcastUri), ) { podcast, episodeToPodcasts -> val episodes = episodeToPodcasts.map { it.episode.asExternalModel() } PodcastUiState.Ready( @@ -71,7 +68,7 @@ class PodcastDetailsViewModel @AssistedInject constructor( }.stateIn( scope = viewModelScope, started = SharingStarted.WhileSubscribed(5_000), - initialValue = PodcastUiState.Loading + initialValue = PodcastUiState.Loading, ) fun toggleSusbcribe(podcast: PodcastInfo) { diff --git a/Jetcaster/mobile/src/main/java/com/example/jetcaster/ui/shared/EpisodeListItem.kt b/Jetcaster/mobile/src/main/java/com/example/jetcaster/ui/shared/EpisodeListItem.kt index 301124f41..acb293336 100644 --- a/Jetcaster/mobile/src/main/java/com/example/jetcaster/ui/shared/EpisodeListItem.kt +++ b/Jetcaster/mobile/src/main/java/com/example/jetcaster/ui/shared/EpisodeListItem.kt @@ -88,28 +88,28 @@ fun EpisodeListItem( Box( modifier = Modifier .fillMaxSize() - .padding(end = 40.dp) + .padding(end = 40.dp), ) { Icon( imageVector = Icons.Default.Delete, contentDescription = null, - modifier = Modifier.align(Alignment.CenterEnd) + modifier = Modifier.align(Alignment.CenterEnd), ) } - } + }, ) { Box( modifier = Modifier .fillMaxWidth() - .padding(vertical = 8.dp, horizontal = 16.dp) + .padding(vertical = 8.dp, horizontal = 16.dp), ) { Surface( shape = MaterialTheme.shapes.large, color = MaterialTheme.colorScheme.surfaceContainer, - onClick = { onClick(episode) } + onClick = { onClick(episode) }, ) { Column( - modifier = Modifier.padding(horizontal = 16.dp, vertical = 8.dp) + modifier = Modifier.padding(horizontal = 16.dp, vertical = 8.dp), ) { // Top Part EpisodeListItemHeader( @@ -118,7 +118,7 @@ fun EpisodeListItem( showPodcastImage = showPodcastImage, showSummary = showSummary, modifier = Modifier.padding(bottom = 8.dp), - imageModifier = imageModifier + imageModifier = imageModifier, ) // Bottom Part @@ -149,11 +149,11 @@ private fun EpisodeListItemFooter( episode: EpisodeInfo, podcast: PodcastInfo, onQueueEpisode: (PlayerEpisode) -> Unit, - modifier: Modifier = Modifier + modifier: Modifier = Modifier, ) { Row( verticalAlignment = Alignment.CenterVertically, - modifier = modifier + modifier = modifier, ) { Image( imageVector = Icons.Rounded.PlayCircleFilled, @@ -163,11 +163,11 @@ private fun EpisodeListItemFooter( modifier = Modifier .clickable( interactionSource = remember { MutableInteractionSource() }, - indication = ripple(bounded = false, radius = 24.dp) + indication = ripple(bounded = false, radius = 24.dp), ) { /* TODO */ } .size(48.dp) .padding(6.dp) - .semantics { role = Role.Button } + .semantics { role = Role.Button }, ) val duration = episode.duration @@ -179,7 +179,7 @@ private fun EpisodeListItemFooter( stringResource( R.string.episode_date_duration, MediumDateFormatter.format(episode.published), - duration.toMinutes().toInt() + duration.toMinutes().toInt(), ) } // Otherwise we just use the date @@ -190,7 +190,7 @@ private fun EpisodeListItemFooter( style = MaterialTheme.typography.bodySmall, modifier = Modifier .padding(horizontal = 8.dp) - .weight(1f) + .weight(1f), ) IconButton( @@ -198,15 +198,15 @@ private fun EpisodeListItemFooter( onQueueEpisode( PlayerEpisode( podcastInfo = podcast, - episodeInfo = episode - ) + episodeInfo = episode, + ), ) }, ) { Icon( imageVector = Icons.AutoMirrored.Filled.PlaylistAdd, contentDescription = stringResource(R.string.cd_add), - tint = MaterialTheme.colorScheme.onSurfaceVariant + tint = MaterialTheme.colorScheme.onSurfaceVariant, ) } @@ -216,7 +216,7 @@ private fun EpisodeListItemFooter( Icon( imageVector = Icons.Default.MoreVert, contentDescription = stringResource(R.string.cd_more), - tint = MaterialTheme.colorScheme.onSurfaceVariant + tint = MaterialTheme.colorScheme.onSurfaceVariant, ) } } @@ -236,7 +236,7 @@ private fun EpisodeListItemHeader( modifier = Modifier .weight(1f) - .padding(end = 16.dp) + .padding(end = 16.dp), ) { Text( text = episode.title, @@ -244,7 +244,7 @@ private fun EpisodeListItemHeader( minLines = 1, overflow = TextOverflow.Ellipsis, style = MaterialTheme.typography.titleMedium, - modifier = Modifier.padding(vertical = 2.dp) + modifier = Modifier.padding(vertical = 2.dp), ) if (showSummary) { @@ -273,35 +273,31 @@ private fun EpisodeListItemHeader( modifier = Modifier .size(56.dp) .clip(MaterialTheme.shapes.medium), - imageModifier = imageModifier + imageModifier = imageModifier, ) } } } @Composable -private fun EpisodeListItemImage( - podcast: PodcastInfo, - modifier: Modifier = Modifier, - imageModifier: Modifier = Modifier -) { +private fun EpisodeListItemImage(podcast: PodcastInfo, modifier: Modifier = Modifier, imageModifier: Modifier = Modifier) { PodcastImage( podcastImageUrl = podcast.imageUrl, contentDescription = null, modifier = modifier, - imageModifier = imageModifier + imageModifier = imageModifier, ) } @Preview( name = "Light Mode", showBackground = true, - uiMode = Configuration.UI_MODE_NIGHT_NO + uiMode = Configuration.UI_MODE_NIGHT_NO, ) @Preview( name = "Dark Mode", showBackground = true, - uiMode = Configuration.UI_MODE_NIGHT_YES + uiMode = Configuration.UI_MODE_NIGHT_YES, ) @Composable private fun EpisodeListItemPreview() { @@ -311,7 +307,7 @@ private fun EpisodeListItemPreview() { podcast = PreviewPodcasts[0], onClick = {}, onQueueEpisode = {}, - showSummary = true + showSummary = true, ) } } diff --git a/Jetcaster/mobile/src/main/java/com/example/jetcaster/ui/shared/Loading.kt b/Jetcaster/mobile/src/main/java/com/example/jetcaster/ui/shared/Loading.kt index 4b96dc6e8..4b81f235d 100644 --- a/Jetcaster/mobile/src/main/java/com/example/jetcaster/ui/shared/Loading.kt +++ b/Jetcaster/mobile/src/main/java/com/example/jetcaster/ui/shared/Loading.kt @@ -28,10 +28,10 @@ import androidx.compose.ui.Modifier fun Loading(modifier: Modifier = Modifier) { Surface(modifier = modifier) { Box( - modifier = Modifier.fillMaxSize() + modifier = Modifier.fillMaxSize(), ) { CircularProgressIndicator( - Modifier.align(Alignment.Center) + Modifier.align(Alignment.Center), ) } } diff --git a/Jetcaster/mobile/src/main/java/com/example/jetcaster/ui/theme/Theme.kt b/Jetcaster/mobile/src/main/java/com/example/jetcaster/ui/theme/Theme.kt index 538dc5d18..38e15668c 100644 --- a/Jetcaster/mobile/src/main/java/com/example/jetcaster/ui/theme/Theme.kt +++ b/Jetcaster/mobile/src/main/java/com/example/jetcaster/ui/theme/Theme.kt @@ -471,15 +471,10 @@ private val highContrastDarkColorScheme = darkColorScheme( ) @Immutable -data class ColorFamily( - val color: Color, - val onColor: Color, - val colorContainer: Color, - val onColorContainer: Color -) +data class ColorFamily(val color: Color, val onColor: Color, val colorContainer: Color, val onColorContainer: Color) val unspecified_scheme = ColorFamily( - Color.Unspecified, Color.Unspecified, Color.Unspecified, Color.Unspecified + Color.Unspecified, Color.Unspecified, Color.Unspecified, Color.Unspecified, ) @OptIn(ExperimentalMaterial3ExpressiveApi::class) @@ -488,7 +483,7 @@ fun JetcasterTheme( darkTheme: Boolean = isSystemInDarkTheme(), // Dynamic color is available on Android 12+ dynamicColor: Boolean = false, - content: @Composable () -> Unit + content: @Composable () -> Unit, ) { val colorScheme = when { dynamicColor && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> { @@ -505,6 +500,6 @@ fun JetcasterTheme( motionScheme = MotionScheme.expressive(), shapes = JetcasterShapes, typography = JetcasterTypography, - content = content + content = content, ) } diff --git a/Jetcaster/mobile/src/main/java/com/example/jetcaster/util/Buttons.kt b/Jetcaster/mobile/src/main/java/com/example/jetcaster/util/Buttons.kt index 5cd4a2430..a3e135375 100644 --- a/Jetcaster/mobile/src/main/java/com/example/jetcaster/util/Buttons.kt +++ b/Jetcaster/mobile/src/main/java/com/example/jetcaster/util/Buttons.kt @@ -23,9 +23,9 @@ import androidx.compose.material.icons.filled.Add import androidx.compose.material.icons.filled.Check import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi import androidx.compose.material3.Icon -import androidx.compose.material3.IconButtonShapes import androidx.compose.material3.IconToggleButton import androidx.compose.material3.IconToggleButtonColors +import androidx.compose.material3.IconToggleButtonShapes import androidx.compose.material3.MaterialTheme import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier @@ -33,14 +33,9 @@ import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp import com.example.jetcaster.R - @OptIn(ExperimentalMaterial3ExpressiveApi::class) @Composable -fun ToggleFollowPodcastIconButton( - isFollowed: Boolean, - onClick: () -> Unit, - modifier: Modifier = Modifier -) { +fun ToggleFollowPodcastIconButton(isFollowed: Boolean, onClick: () -> Unit, modifier: Modifier = Modifier) { IconToggleButton( checked = isFollowed, onCheckedChange = { onClick() }, @@ -51,13 +46,13 @@ fun ToggleFollowPodcastIconButton( disabledContainerColor = MaterialTheme.colorScheme.secondary, disabledContentColor = MaterialTheme.colorScheme.onSecondary, checkedContainerColor = MaterialTheme.colorScheme.surfaceContainer, - checkedContentColor = MaterialTheme.colorScheme.secondary + checkedContentColor = MaterialTheme.colorScheme.secondary, ), - shapes = IconButtonShapes( + shapes = IconToggleButtonShapes( shape = RoundedCornerShape(10.dp), - pressedShape = if(isFollowed) RoundedCornerShape(10.dp) else CircleShape, - checkedShape = CircleShape - ) + pressedShape = if (isFollowed) RoundedCornerShape(10.dp) else CircleShape, + checkedShape = CircleShape, + ), ) { Icon( // TODO: think about animating these icons diff --git a/Jetcaster/mobile/src/main/java/com/example/jetcaster/util/GradientScrim.kt b/Jetcaster/mobile/src/main/java/com/example/jetcaster/util/GradientScrim.kt index 671373472..796447d1b 100644 --- a/Jetcaster/mobile/src/main/java/com/example/jetcaster/util/GradientScrim.kt +++ b/Jetcaster/mobile/src/main/java/com/example/jetcaster/util/GradientScrim.kt @@ -49,7 +49,7 @@ fun Modifier.radialGradientScrim(color: Color): Modifier { center = size.center.copy(y = size.height / 4), colors = listOf(color, Color.Transparent), radius = largerDimension / 2, - colorStops = listOf(0f, 0.9f) + colorStops = listOf(0f, 0.9f), ) } } @@ -74,7 +74,7 @@ fun Modifier.verticalGradientScrim( @FloatRange(from = 0.0, to = 1.0) startYPercentage: Float = 0f, @FloatRange(from = 0.0, to = 1.0) endYPercentage: Float = 1f, decay: Float = 1.0f, - numStops: Int = 16 + numStops: Int = 16, ) = this then VerticalGradientElement(color, startYPercentage, endYPercentage, decay, numStops) private data class VerticalGradientElement( @@ -82,7 +82,7 @@ private data class VerticalGradientElement( var startYPercentage: Float = 0f, var endYPercentage: Float = 1f, var decay: Float = 1.0f, - var numStops: Int = 16 + var numStops: Int = 16, ) : ModifierNodeElement() { fun createOnDraw(): DrawScope.() -> Unit { val colors = if (decay != 1f) { @@ -113,7 +113,7 @@ private data class VerticalGradientElement( drawRect( topLeft = topLeft, size = Rect(topLeft, bottomRight).size, - brush = brush + brush = brush, ) } } @@ -137,9 +137,9 @@ private data class VerticalGradientElement( } } -private class VerticalGradientModifier( - var onDraw: DrawScope.() -> Unit -) : Modifier.Node(), DrawModifierNode { +private class VerticalGradientModifier(var onDraw: DrawScope.() -> Unit) : + Modifier.Node(), + DrawModifierNode { override fun ContentDrawScope.draw() { onDraw() diff --git a/Jetcaster/mobile/src/main/java/com/example/jetcaster/util/LazyVerticalGrid.kt b/Jetcaster/mobile/src/main/java/com/example/jetcaster/util/LazyVerticalGrid.kt index 6233653f6..bf4e35786 100644 --- a/Jetcaster/mobile/src/main/java/com/example/jetcaster/util/LazyVerticalGrid.kt +++ b/Jetcaster/mobile/src/main/java/com/example/jetcaster/util/LazyVerticalGrid.kt @@ -24,13 +24,9 @@ import androidx.compose.runtime.Composable /** * An item that occupies the entire width. */ -fun LazyGridScope.fullWidthItem( - key: Any? = null, - contentType: Any? = null, - content: @Composable LazyGridItemScope.() -> Unit -) = item( +fun LazyGridScope.fullWidthItem(key: Any? = null, contentType: Any? = null, content: @Composable LazyGridItemScope.() -> Unit) = item( span = { GridItemSpan(this.maxLineSpan) }, key = key, contentType = contentType, - content = content + content = content, ) diff --git a/Jetcaster/mobile/src/main/java/com/example/jetcaster/util/ViewModel.kt b/Jetcaster/mobile/src/main/java/com/example/jetcaster/util/ViewModel.kt index ff2794665..1d2e5d168 100644 --- a/Jetcaster/mobile/src/main/java/com/example/jetcaster/util/ViewModel.kt +++ b/Jetcaster/mobile/src/main/java/com/example/jetcaster/util/ViewModel.kt @@ -28,9 +28,7 @@ import androidx.lifecycle.viewmodel.viewModelFactory * If the created [ViewModel] does not match the requested class, an [IllegalArgumentException] * exception is thrown. */ -inline fun viewModelProviderFactoryOf( - crossinline create: () -> VM -): ViewModelProvider.Factory = viewModelFactory { +inline fun viewModelProviderFactoryOf(crossinline create: () -> VM): ViewModelProvider.Factory = viewModelFactory { initializer { create() } diff --git a/Jetcaster/tv/build.gradle.kts b/Jetcaster/tv/build.gradle.kts index 3179e8017..d8befdfe1 100644 --- a/Jetcaster/tv/build.gradle.kts +++ b/Jetcaster/tv/build.gradle.kts @@ -5,7 +5,7 @@ * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * https://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, @@ -14,6 +14,7 @@ * limitations under the License. */ + plugins { alias(libs.plugins.android.application) alias(libs.plugins.kotlin.android) diff --git a/Jetcaster/tv/src/main/java/com/example/jetcaster/tv/MainActivity.kt b/Jetcaster/tv/src/main/java/com/example/jetcaster/tv/MainActivity.kt index 1c978f952..9edf820c0 100644 --- a/Jetcaster/tv/src/main/java/com/example/jetcaster/tv/MainActivity.kt +++ b/Jetcaster/tv/src/main/java/com/example/jetcaster/tv/MainActivity.kt @@ -36,7 +36,7 @@ class MainActivity : ComponentActivity() { JetcasterTheme(isInDarkTheme = true) { Surface( modifier = Modifier.fillMaxSize(), - shape = RectangleShape + shape = RectangleShape, ) { JetcasterApp() } diff --git a/Jetcaster/tv/src/main/java/com/example/jetcaster/tv/model/CategorySelection.kt b/Jetcaster/tv/src/main/java/com/example/jetcaster/tv/model/CategorySelection.kt index c5943815b..a0a4df910 100644 --- a/Jetcaster/tv/src/main/java/com/example/jetcaster/tv/model/CategorySelection.kt +++ b/Jetcaster/tv/src/main/java/com/example/jetcaster/tv/model/CategorySelection.kt @@ -22,6 +22,4 @@ import com.example.jetcaster.core.model.CategoryInfo data class CategorySelection(val categoryInfo: CategoryInfo, val isSelected: Boolean = false) @Immutable -data class CategorySelectionList( - val member: List -) : List by member +data class CategorySelectionList(val member: List) : List by member diff --git a/Jetcaster/tv/src/main/java/com/example/jetcaster/tv/ui/JetcasterApp.kt b/Jetcaster/tv/src/main/java/com/example/jetcaster/tv/ui/JetcasterApp.kt index f9f07236a..56405f9ac 100644 --- a/Jetcaster/tv/src/main/java/com/example/jetcaster/tv/ui/JetcasterApp.kt +++ b/Jetcaster/tv/src/main/java/com/example/jetcaster/tv/ui/JetcasterApp.kt @@ -64,7 +64,7 @@ fun JetcasterApp(jetcasterAppState: JetcasterAppState = rememberJetcasterAppStat private fun GlobalNavigationContainer( jetcasterAppState: JetcasterAppState, modifier: Modifier = Modifier, - content: @Composable () -> Unit + content: @Composable () -> Unit, ) { val (discover, library) = remember { FocusRequester.createRefs() } val currentRoute @@ -85,7 +85,7 @@ private fun GlobalNavigationContainer( } } } - .focusGroup() + .focusGroup(), ) { NavigationDrawerItem( selected = isClosed && currentRoute == Screen.Profile.route, @@ -96,7 +96,7 @@ private fun GlobalNavigationContainer( Text(text = "Name") Text( text = "Switch Account", - style = MaterialTheme.typography.labelSmall + style = MaterialTheme.typography.labelSmall, ) } } @@ -107,9 +107,9 @@ private fun GlobalNavigationContainer( leadingContent = { Icon( Icons.Default.Search, - contentDescription = null + contentDescription = null, ) - } + }, ) { Text(text = "Search") } @@ -119,10 +119,10 @@ private fun GlobalNavigationContainer( leadingContent = { Icon( Icons.Default.Home, - contentDescription = null + contentDescription = null, ) }, - modifier = Modifier.focusRequester(discover) + modifier = Modifier.focusRequester(discover), ) { Text(text = "Discover") } @@ -132,10 +132,10 @@ private fun GlobalNavigationContainer( leadingContent = { Icon( Icons.Default.VideoLibrary, - contentDescription = null + contentDescription = null, ) }, - modifier = Modifier.focusRequester(library) + modifier = Modifier.focusRequester(library), ) { Text(text = "Library") } @@ -143,14 +143,14 @@ private fun GlobalNavigationContainer( NavigationDrawerItem( selected = isClosed && currentRoute == Screen.Settings.route, onClick = jetcasterAppState::navigateToSettings, - leadingContent = { Icon(Icons.Default.Settings, contentDescription = null) } + leadingContent = { Icon(Icons.Default.Settings, contentDescription = null) }, ) { Text(text = "Settings") } } }, content = content, - modifier = modifier + modifier = modifier, ) } @@ -166,7 +166,7 @@ private fun Route(jetcasterAppState: JetcasterAppState) { playEpisode = { jetcasterAppState.playEpisode() }, - modifier = Modifier.fillMaxSize() + modifier = Modifier.fillMaxSize(), ) } } @@ -181,7 +181,7 @@ private fun Route(jetcasterAppState: JetcasterAppState) { playEpisode = { jetcasterAppState.playEpisode() }, - modifier = Modifier.fillMaxSize() + modifier = Modifier.fillMaxSize(), ) } } @@ -193,7 +193,7 @@ private fun Route(jetcasterAppState: JetcasterAppState) { }, modifier = Modifier .padding(JetcasterAppDefaults.overScanMargin.default.intoPaddingValues()) - .fillMaxSize() + .fillMaxSize(), ) } @@ -231,7 +231,7 @@ private fun Route(jetcasterAppState: JetcasterAppState) { ProfileScreen( modifier = Modifier .fillMaxSize() - .padding(JetcasterAppDefaults.overScanMargin.default.intoPaddingValues()) + .padding(JetcasterAppDefaults.overScanMargin.default.intoPaddingValues()), ) } @@ -239,7 +239,7 @@ private fun Route(jetcasterAppState: JetcasterAppState) { SettingsScreen( modifier = Modifier .fillMaxSize() - .padding(JetcasterAppDefaults.overScanMargin.default.intoPaddingValues()) + .padding(JetcasterAppDefaults.overScanMargin.default.intoPaddingValues()), ) } } diff --git a/Jetcaster/tv/src/main/java/com/example/jetcaster/tv/ui/JetcasterAppState.kt b/Jetcaster/tv/src/main/java/com/example/jetcaster/tv/ui/JetcasterAppState.kt index bc714c99a..05f600171 100644 --- a/Jetcaster/tv/src/main/java/com/example/jetcaster/tv/ui/JetcasterAppState.kt +++ b/Jetcaster/tv/src/main/java/com/example/jetcaster/tv/ui/JetcasterAppState.kt @@ -24,9 +24,7 @@ import androidx.navigation.compose.rememberNavController import com.example.jetcaster.core.player.model.PlayerEpisode import kotlinx.coroutines.flow.map -class JetcasterAppState( - val navHostController: NavHostController -) { +class JetcasterAppState(val navHostController: NavHostController) { val currentRouteFlow = navHostController.currentBackStackEntryFlow.map { it.destination.route @@ -83,12 +81,9 @@ class JetcasterAppState( } @Composable -fun rememberJetcasterAppState( - navHostController: NavHostController = rememberNavController() -) = - remember(navHostController) { - JetcasterAppState(navHostController) - } +fun rememberJetcasterAppState(navHostController: NavHostController = rememberNavController()) = remember(navHostController) { + JetcasterAppState(navHostController) +} sealed interface Screen { val route: String diff --git a/Jetcaster/tv/src/main/java/com/example/jetcaster/tv/ui/component/Background.kt b/Jetcaster/tv/src/main/java/com/example/jetcaster/tv/ui/component/Background.kt index 4cdd5ccb5..9a9cb17ea 100644 --- a/Jetcaster/tv/src/main/java/com/example/jetcaster/tv/ui/component/Background.kt +++ b/Jetcaster/tv/src/main/java/com/example/jetcaster/tv/ui/component/Background.kt @@ -32,30 +32,28 @@ internal fun BackgroundContainer( playerEpisode: PlayerEpisode, modifier: Modifier = Modifier, contentAlignment: Alignment = Alignment.Center, - content: @Composable BoxScope.() -> Unit -) = - BackgroundContainer( - imageUrl = playerEpisode.podcastImageUrl, - modifier, - contentAlignment, - content - ) + content: @Composable BoxScope.() -> Unit, +) = BackgroundContainer( + imageUrl = playerEpisode.podcastImageUrl, + modifier, + contentAlignment, + content, +) @Composable internal fun BackgroundContainer( podcastInfo: PodcastInfo, modifier: Modifier = Modifier, contentAlignment: Alignment = Alignment.Center, - content: @Composable BoxScope.() -> Unit -) = - BackgroundContainer(imageUrl = podcastInfo.imageUrl, modifier, contentAlignment, content) + content: @Composable BoxScope.() -> Unit, +) = BackgroundContainer(imageUrl = podcastInfo.imageUrl, modifier, contentAlignment, content) @Composable internal fun BackgroundContainer( imageUrl: String, modifier: Modifier = Modifier, contentAlignment: Alignment = Alignment.Center, - content: @Composable BoxScope.() -> Unit + content: @Composable BoxScope.() -> Unit, ) { Box(modifier = modifier, contentAlignment = contentAlignment) { Background(imageUrl = imageUrl, modifier = Modifier.fillMaxSize()) @@ -64,10 +62,7 @@ internal fun BackgroundContainer( } @Composable -private fun Background( - imageUrl: String, - modifier: Modifier = Modifier, -) { +private fun Background(imageUrl: String, modifier: Modifier = Modifier) { ImageBackgroundRadialGradientScrim( url = imageUrl, colors = listOf(Color.Black, Color.Transparent), diff --git a/Jetcaster/tv/src/main/java/com/example/jetcaster/tv/ui/component/Button.kt b/Jetcaster/tv/src/main/java/com/example/jetcaster/tv/ui/component/Button.kt index aeeaee413..79f2f8016 100644 --- a/Jetcaster/tv/src/main/java/com/example/jetcaster/tv/ui/component/Button.kt +++ b/Jetcaster/tv/src/main/java/com/example/jetcaster/tv/ui/component/Button.kt @@ -38,24 +38,16 @@ import androidx.tv.material3.IconButton import com.example.jetcaster.tv.R @Composable -internal fun PlayButton( - onClick: () -> Unit, - modifier: Modifier = Modifier, - scale: ButtonScale = ButtonDefaults.scale(), -) = - ButtonWithIcon( - icon = Icons.Outlined.PlayArrow, - label = stringResource(R.string.label_play), - onClick = onClick, - modifier = modifier, - scale = scale - ) +internal fun PlayButton(onClick: () -> Unit, modifier: Modifier = Modifier, scale: ButtonScale = ButtonDefaults.scale()) = ButtonWithIcon( + icon = Icons.Outlined.PlayArrow, + label = stringResource(R.string.label_play), + onClick = onClick, + modifier = modifier, + scale = scale, +) @Composable -internal fun EnqueueButton( - onClick: () -> Unit, - modifier: Modifier = Modifier -) { +internal fun EnqueueButton(onClick: () -> Unit, modifier: Modifier = Modifier) { IconButton(onClick = onClick, modifier = modifier) { Icon( Icons.AutoMirrored.Filled.PlaylistAdd, @@ -65,10 +57,7 @@ internal fun EnqueueButton( } @Composable -internal fun InfoButton( - onClick: () -> Unit, - modifier: Modifier = Modifier -) { +internal fun InfoButton(onClick: () -> Unit, modifier: Modifier = Modifier) { IconButton(onClick = onClick, modifier = modifier) { Icon( Icons.Outlined.Info, @@ -78,37 +67,27 @@ internal fun InfoButton( } @Composable -internal fun PreviousButton( - onClick: () -> Unit, - modifier: Modifier = Modifier -) { +internal fun PreviousButton(onClick: () -> Unit, modifier: Modifier = Modifier) { IconButton(onClick = onClick, modifier = modifier) { Icon( Icons.Default.SkipPrevious, - contentDescription = stringResource(R.string.label_previous_episode) + contentDescription = stringResource(R.string.label_previous_episode), ) } } @Composable -internal fun NextButton( - onClick: () -> Unit, - modifier: Modifier = Modifier -) { +internal fun NextButton(onClick: () -> Unit, modifier: Modifier = Modifier) { IconButton(onClick = onClick, modifier = modifier) { Icon( Icons.Default.SkipNext, - contentDescription = stringResource(R.string.label_next_episode) + contentDescription = stringResource(R.string.label_next_episode), ) } } @Composable -internal fun PlayPauseButton( - isPlaying: Boolean, - onClick: () -> Unit, - modifier: Modifier = Modifier -) { +internal fun PlayPauseButton(isPlaying: Boolean, onClick: () -> Unit, modifier: Modifier = Modifier) { val (icon, description) = if (isPlaying) { Icons.Default.Pause to stringResource(R.string.label_pause) } else { @@ -120,27 +99,21 @@ internal fun PlayPauseButton( } @Composable -internal fun RewindButton( - onClick: () -> Unit, - modifier: Modifier = Modifier -) { +internal fun RewindButton(onClick: () -> Unit, modifier: Modifier = Modifier) { IconButton(onClick = onClick, modifier = modifier) { Icon( Icons.Default.Replay10, - contentDescription = stringResource(R.string.label_rewind) + contentDescription = stringResource(R.string.label_rewind), ) } } @Composable -internal fun SkipButton( - onClick: () -> Unit, - modifier: Modifier = Modifier -) { +internal fun SkipButton(onClick: () -> Unit, modifier: Modifier = Modifier) { IconButton(onClick = onClick, modifier = modifier) { Icon( Icons.Default.Forward10, - contentDescription = stringResource(R.string.label_skip) + contentDescription = stringResource(R.string.label_skip), ) } } diff --git a/Jetcaster/tv/src/main/java/com/example/jetcaster/tv/ui/component/Catalog.kt b/Jetcaster/tv/src/main/java/com/example/jetcaster/tv/ui/component/Catalog.kt index 649fbd5c5..eef6f9e92 100644 --- a/Jetcaster/tv/src/main/java/com/example/jetcaster/tv/ui/component/Catalog.kt +++ b/Jetcaster/tv/src/main/java/com/example/jetcaster/tv/ui/component/Catalog.kt @@ -68,14 +68,14 @@ internal fun Catalog( PodcastSection( podcastList = podcastList, onPodcastSelected = onPodcastSelected, - title = stringResource(R.string.label_podcast) + title = stringResource(R.string.label_podcast), ) } item { LatestEpisodeSection( episodeList = latestEpisodeList, onEpisodeSelected = onEpisodeSelected, - title = stringResource(R.string.label_latest_episode) + title = stringResource(R.string.label_latest_episode), ) } } @@ -90,7 +90,7 @@ private fun PodcastSection( ) { Section( title = title, - modifier = modifier + modifier = modifier, ) { PodcastRow( podcastList = podcastList, @@ -104,11 +104,11 @@ private fun LatestEpisodeSection( episodeList: EpisodeList, onEpisodeSelected: (PlayerEpisode) -> Unit, modifier: Modifier = Modifier, - title: String? = null + title: String? = null, ) { Section( modifier = modifier, - title = title + title = title, ) { EpisodeRow( playerEpisodeList = episodeList, @@ -129,7 +129,7 @@ private fun Section( Text( text = title, style = style, - modifier = Modifier.padding(JetcasterAppDefaults.padding.sectionTitle) + modifier = Modifier.padding(JetcasterAppDefaults.padding.sectionTitle), ) } content() @@ -176,7 +176,7 @@ private fun PodcastRow( PodcastCard( podcastInfo = podcastInfo, onClick = { onPodcastSelected(podcastInfo) }, - modifier = cardModifier.width(JetcasterAppDefaults.cardWidth.medium) + modifier = cardModifier.width(JetcasterAppDefaults.cardWidth.medium), ) } } diff --git a/Jetcaster/tv/src/main/java/com/example/jetcaster/tv/ui/component/EpisodeCard.kt b/Jetcaster/tv/src/main/java/com/example/jetcaster/tv/ui/component/EpisodeCard.kt index ddde4bcb5..2193379a0 100644 --- a/Jetcaster/tv/src/main/java/com/example/jetcaster/tv/ui/component/EpisodeCard.kt +++ b/Jetcaster/tv/src/main/java/com/example/jetcaster/tv/ui/component/EpisodeCard.kt @@ -55,10 +55,10 @@ internal fun EpisodeCard( playerEpisode = playerEpisode, modifier = Modifier .padding(horizontal = 16.dp, vertical = 12.dp) - .width(JetcasterAppDefaults.cardWidth.small * 2) + .width(JetcasterAppDefaults.cardWidth.small * 2), ) }, - modifier = modifier + modifier = modifier, ) } @@ -67,7 +67,7 @@ private fun EpisodeThumbnail( playerEpisode: PlayerEpisode, onClick: () -> Unit, modifier: Modifier = Modifier, - interactionSource: MutableInteractionSource = remember { MutableInteractionSource() } + interactionSource: MutableInteractionSource = remember { MutableInteractionSource() }, ) { Card( onClick = onClick, @@ -81,22 +81,19 @@ private fun EpisodeThumbnail( } @Composable -private fun EpisodeMetaData( - playerEpisode: PlayerEpisode, - modifier: Modifier = Modifier -) { +private fun EpisodeMetaData(playerEpisode: PlayerEpisode, modifier: Modifier = Modifier) { val duration = playerEpisode.duration Column(modifier = modifier) { Text( text = playerEpisode.title, style = MaterialTheme.typography.bodyLarge, maxLines = 1, - overflow = TextOverflow.Ellipsis + overflow = TextOverflow.Ellipsis, ) Text(text = playerEpisode.podcastName, style = MaterialTheme.typography.bodySmall) if (duration != null) { Spacer( - modifier = Modifier.height(JetcasterAppDefaults.gap.podcastRow * 0.8f) + modifier = Modifier.height(JetcasterAppDefaults.gap.podcastRow * 0.8f), ) EpisodeDataAndDuration(offsetDateTime = playerEpisode.published, duration = duration) } diff --git a/Jetcaster/tv/src/main/java/com/example/jetcaster/tv/ui/component/EpisodeDateAndDuration.kt b/Jetcaster/tv/src/main/java/com/example/jetcaster/tv/ui/component/EpisodeDateAndDuration.kt index 0ce6dbeaf..97684ada0 100644 --- a/Jetcaster/tv/src/main/java/com/example/jetcaster/tv/ui/component/EpisodeDateAndDuration.kt +++ b/Jetcaster/tv/src/main/java/com/example/jetcaster/tv/ui/component/EpisodeDateAndDuration.kt @@ -43,9 +43,9 @@ internal fun EpisodeDataAndDuration( text = stringResource( R.string.episode_date_duration, MediumDateFormatter.format(offsetDateTime), - duration.toMinutes().toInt() + duration.toMinutes().toInt(), ), style = style, - modifier = modifier + modifier = modifier, ) } diff --git a/Jetcaster/tv/src/main/java/com/example/jetcaster/tv/ui/component/EpisodeDetails.kt b/Jetcaster/tv/src/main/java/com/example/jetcaster/tv/ui/component/EpisodeDetails.kt index 01664adb9..46e108a6f 100644 --- a/Jetcaster/tv/src/main/java/com/example/jetcaster/tv/ui/component/EpisodeDetails.kt +++ b/Jetcaster/tv/src/main/java/com/example/jetcaster/tv/ui/component/EpisodeDetails.kt @@ -33,20 +33,20 @@ internal fun EpisodeDetails( modifier: Modifier = Modifier, controls: (@Composable () -> Unit)? = null, verticalArrangement: Arrangement.Vertical = Arrangement.spacedBy(JetcasterAppDefaults.gap.item), - content: @Composable ColumnScope.() -> Unit + content: @Composable ColumnScope.() -> Unit, ) { TwoColumn( modifier = modifier, first = { Thumbnail( playerEpisode, - size = JetcasterAppDefaults.thumbnailSize.episodeDetails + size = JetcasterAppDefaults.thumbnailSize.episodeDetails, ) }, second = { Column( modifier = modifier, - verticalArrangement = verticalArrangement + verticalArrangement = verticalArrangement, ) { EpisodeAuthor(playerEpisode = playerEpisode) EpisodeTitle(playerEpisode = playerEpisode) @@ -55,7 +55,7 @@ internal fun EpisodeDetails( controls() } } - } + }, ) } @@ -63,7 +63,7 @@ internal fun EpisodeDetails( internal fun EpisodeAuthor( playerEpisode: PlayerEpisode, modifier: Modifier = Modifier, - style: TextStyle = MaterialTheme.typography.bodySmall + style: TextStyle = MaterialTheme.typography.bodySmall, ) { Text(text = playerEpisode.author, modifier = modifier, style = style) } @@ -72,7 +72,7 @@ internal fun EpisodeAuthor( internal fun EpisodeTitle( playerEpisode: PlayerEpisode, modifier: Modifier = Modifier, - style: TextStyle = MaterialTheme.typography.headlineLarge + style: TextStyle = MaterialTheme.typography.headlineLarge, ) { Text(text = playerEpisode.title, modifier = modifier, style = style) } diff --git a/Jetcaster/tv/src/main/java/com/example/jetcaster/tv/ui/component/EpisodeRow.kt b/Jetcaster/tv/src/main/java/com/example/jetcaster/tv/ui/component/EpisodeRow.kt index 3861482cb..70def2b17 100644 --- a/Jetcaster/tv/src/main/java/com/example/jetcaster/tv/ui/component/EpisodeRow.kt +++ b/Jetcaster/tv/src/main/java/com/example/jetcaster/tv/ui/component/EpisodeRow.kt @@ -45,7 +45,7 @@ internal fun EpisodeRow( Arrangement.spacedBy(JetcasterAppDefaults.gap.item), contentPadding: PaddingValues = JetcasterAppDefaults.padding.episodeRowContentPadding, focusRequester: FocusRequester = remember { FocusRequester() }, - lazyListState: LazyListState = remember(playerEpisodeList) { LazyListState() } + lazyListState: LazyListState = remember(playerEpisodeList) { LazyListState() }, ) { val firstItem = remember { FocusRequester() } var previousEpisodeListHash by remember { mutableIntStateOf(playerEpisodeList.hashCode()) } @@ -82,7 +82,7 @@ internal fun EpisodeRow( EpisodeCard( playerEpisode = item, onClick = { onSelected(item) }, - modifier = cardModifier + modifier = cardModifier, ) } } diff --git a/Jetcaster/tv/src/main/java/com/example/jetcaster/tv/ui/component/ErrorState.kt b/Jetcaster/tv/src/main/java/com/example/jetcaster/tv/ui/component/ErrorState.kt index be7f99cab..961f0099c 100644 --- a/Jetcaster/tv/src/main/java/com/example/jetcaster/tv/ui/component/ErrorState.kt +++ b/Jetcaster/tv/src/main/java/com/example/jetcaster/tv/ui/component/ErrorState.kt @@ -34,11 +34,7 @@ import com.example.jetcaster.tv.R import com.example.jetcaster.tv.ui.theme.JetcasterAppDefaults @Composable -fun ErrorState( - backToHome: () -> Unit, - modifier: Modifier = Modifier, - focusRequester: FocusRequester = remember { FocusRequester() } -) { +fun ErrorState(backToHome: () -> Unit, modifier: Modifier = Modifier, focusRequester: FocusRequester = remember { FocusRequester() }) { LaunchedEffect(Unit) { focusRequester.requestFocus() } @@ -46,13 +42,13 @@ fun ErrorState( Column { Text( text = stringResource(R.string.display_error_state), - style = MaterialTheme.typography.displayMedium + style = MaterialTheme.typography.displayMedium, ) Button( onClick = backToHome, modifier .padding(top = JetcasterAppDefaults.gap.podcastRow) - .focusRequester(focusRequester) + .focusRequester(focusRequester), ) { Text(text = stringResource(R.string.label_back_to_home)) } diff --git a/Jetcaster/tv/src/main/java/com/example/jetcaster/tv/ui/component/Loading.kt b/Jetcaster/tv/src/main/java/com/example/jetcaster/tv/ui/component/Loading.kt index 460349750..ff77ba006 100644 --- a/Jetcaster/tv/src/main/java/com/example/jetcaster/tv/ui/component/Loading.kt +++ b/Jetcaster/tv/src/main/java/com/example/jetcaster/tv/ui/component/Loading.kt @@ -62,11 +62,11 @@ fun Loading( ) { Box( modifier = modifier, - contentAlignment = contentAlignment + contentAlignment = contentAlignment, ) { Column( horizontalAlignment = Alignment.CenterHorizontally, - verticalArrangement = Arrangement.spacedBy(JetcasterAppDefaults.gap.default) + verticalArrangement = Arrangement.spacedBy(JetcasterAppDefaults.gap.default), ) { CircularProgressIndicator() Text(text = message, style = style) @@ -95,10 +95,10 @@ fun CircularProgressIndicator( infiniteRepeatable( animation = tween( durationMillis = RotationDuration * RotationsPerCycle, - easing = LinearEasing - ) + easing = LinearEasing, + ), ), - "loading_current_rotation" + "loading_current_rotation", ) // How far forward (degrees) the base point should be from the start point val baseRotation = transition.animateFloat( @@ -107,10 +107,10 @@ fun CircularProgressIndicator( infiniteRepeatable( animation = tween( durationMillis = RotationDuration, - easing = LinearEasing - ) + easing = LinearEasing, + ), ), - "loading_base_rotation_angle" + "loading_base_rotation_angle", ) // How far forward (degrees) both the head and tail should be from the base point val endAngle = transition.animateFloat( @@ -121,9 +121,9 @@ fun CircularProgressIndicator( durationMillis = HeadAndTailAnimationDuration + HeadAndTailDelayDuration 0f at 0 using CircularEasing JumpRotationAngle at HeadAndTailAnimationDuration - } + }, ), - "loading_end_rotation_angle" + "loading_end_rotation_angle", ) val startAngle = transition.animateFloat( 0f, @@ -133,15 +133,15 @@ fun CircularProgressIndicator( durationMillis = HeadAndTailAnimationDuration + HeadAndTailDelayDuration 0f at HeadAndTailDelayDuration using CircularEasing JumpRotationAngle at durationMillis - } + }, ), - "loading_start_angle" + "loading_start_angle", ) Canvas( modifier .progressSemantics() - .size(CircularIndicatorDiameter) + .size(CircularIndicatorDiameter), ) { drawCircularIndicatorTrack(trackColor, stroke) @@ -157,17 +157,12 @@ fun CircularProgressIndicator( strokeWidth, sweep, color, - stroke + stroke, ) } } -private fun DrawScope.drawCircularIndicator( - startAngle: Float, - sweep: Float, - color: Color, - stroke: Stroke -) { +private fun DrawScope.drawCircularIndicator(startAngle: Float, sweep: Float, color: Color, stroke: Stroke) { // To draw this circle we need a rect with edges that line up with the midpoint of the stroke. // To do this we need to remove half the stroke width from the total diameter for both sides. val diameterOffset = stroke.width / 2 @@ -179,22 +174,13 @@ private fun DrawScope.drawCircularIndicator( useCenter = false, topLeft = Offset(diameterOffset, diameterOffset), size = Size(arcDimen, arcDimen), - style = stroke + style = stroke, ) } -private fun DrawScope.drawCircularIndicatorTrack( - color: Color, - stroke: Stroke -) = drawCircularIndicator(0f, 360f, color, stroke) - -private fun DrawScope.drawIndeterminateCircularIndicator( - startAngle: Float, - strokeWidth: Dp, - sweep: Float, - color: Color, - stroke: Stroke -) { +private fun DrawScope.drawCircularIndicatorTrack(color: Color, stroke: Stroke) = drawCircularIndicator(0f, 360f, color, stroke) + +private fun DrawScope.drawIndeterminateCircularIndicator(startAngle: Float, strokeWidth: Dp, sweep: Float, color: Color, stroke: Stroke) { val strokeCapOffset = if (stroke.cap == StrokeCap.Butt) { 0f } else { diff --git a/Jetcaster/tv/src/main/java/com/example/jetcaster/tv/ui/component/NotAvailableFeature.kt b/Jetcaster/tv/src/main/java/com/example/jetcaster/tv/ui/component/NotAvailableFeature.kt index 21575e4c2..11b5681f3 100644 --- a/Jetcaster/tv/src/main/java/com/example/jetcaster/tv/ui/component/NotAvailableFeature.kt +++ b/Jetcaster/tv/src/main/java/com/example/jetcaster/tv/ui/component/NotAvailableFeature.kt @@ -25,7 +25,7 @@ import com.example.jetcaster.tv.R @Composable internal fun NotAvailableFeature( modifier: Modifier = Modifier, - message: String = stringResource(id = R.string.message_not_available_feature) + message: String = stringResource(id = R.string.message_not_available_feature), ) { Text(message, modifier = modifier) } diff --git a/Jetcaster/tv/src/main/java/com/example/jetcaster/tv/ui/component/PodcastCard.kt b/Jetcaster/tv/src/main/java/com/example/jetcaster/tv/ui/component/PodcastCard.kt index 3524cae81..a1a683f34 100644 --- a/Jetcaster/tv/src/main/java/com/example/jetcaster/tv/ui/component/PodcastCard.kt +++ b/Jetcaster/tv/src/main/java/com/example/jetcaster/tv/ui/component/PodcastCard.kt @@ -30,22 +30,18 @@ import com.example.jetcaster.core.model.PodcastInfo import com.example.jetcaster.tv.ui.theme.JetcasterAppDefaults @Composable -internal fun PodcastCard( - podcastInfo: PodcastInfo, - onClick: () -> Unit, - modifier: Modifier = Modifier, -) { +internal fun PodcastCard(podcastInfo: PodcastInfo, onClick: () -> Unit, modifier: Modifier = Modifier) { StandardCardContainer( imageCard = { Card( onClick = onClick, interactionSource = it, scale = CardScale.None, - shape = CardDefaults.shape(RoundedCornerShape(12.dp)) + shape = CardDefaults.shape(RoundedCornerShape(12.dp)), ) { Thumbnail( podcastInfo = podcastInfo, - size = JetcasterAppDefaults.thumbnailSize.podcast + size = JetcasterAppDefaults.thumbnailSize.podcast, ) } }, diff --git a/Jetcaster/tv/src/main/java/com/example/jetcaster/tv/ui/component/Seekbar.kt b/Jetcaster/tv/src/main/java/com/example/jetcaster/tv/ui/component/Seekbar.kt index c36c3c7fc..4ffa5f50f 100644 --- a/Jetcaster/tv/src/main/java/com/example/jetcaster/tv/ui/component/Seekbar.kt +++ b/Jetcaster/tv/src/main/java/com/example/jetcaster/tv/ui/component/Seekbar.kt @@ -63,7 +63,7 @@ internal fun Seekbar( val end = start.copy(x = size.width) val knobCenter = start.copy( - x = timeElapsed.seconds.toFloat() / length.seconds.toFloat() * size.width + x = timeElapsed.seconds.toFloat() / length.seconds.toFloat() * size.width, ) drawLine( brush, start, end, @@ -91,6 +91,6 @@ internal fun Seekbar( else -> false } - } + }, ) } diff --git a/Jetcaster/tv/src/main/java/com/example/jetcaster/tv/ui/component/Thumbnail.kt b/Jetcaster/tv/src/main/java/com/example/jetcaster/tv/ui/component/Thumbnail.kt index ba3046716..fa5f30104 100644 --- a/Jetcaster/tv/src/main/java/com/example/jetcaster/tv/ui/component/Thumbnail.kt +++ b/Jetcaster/tv/src/main/java/com/example/jetcaster/tv/ui/component/Thumbnail.kt @@ -36,17 +36,16 @@ fun Thumbnail( shape: RoundedCornerShape = RoundedCornerShape(12.dp), size: DpSize = DpSize( JetcasterAppDefaults.cardWidth.medium, - JetcasterAppDefaults.cardWidth.medium + JetcasterAppDefaults.cardWidth.medium, ), - contentScale: ContentScale = ContentScale.Crop -) = - Thumbnail( - podcastInfo.imageUrl, - modifier, - shape, - size, - contentScale - ) + contentScale: ContentScale = ContentScale.Crop, +) = Thumbnail( + podcastInfo.imageUrl, + modifier, + shape, + size, + contentScale, +) @Composable fun Thumbnail( @@ -55,17 +54,16 @@ fun Thumbnail( shape: RoundedCornerShape = RoundedCornerShape(12.dp), size: DpSize = DpSize( JetcasterAppDefaults.cardWidth.medium, - JetcasterAppDefaults.cardWidth.medium + JetcasterAppDefaults.cardWidth.medium, ), - contentScale: ContentScale = ContentScale.Crop -) = - Thumbnail( - episode.podcastImageUrl, - modifier, - shape, - size, - contentScale - ) + contentScale: ContentScale = ContentScale.Crop, +) = Thumbnail( + episode.podcastImageUrl, + modifier, + shape, + size, + contentScale, +) @Composable fun Thumbnail( @@ -74,15 +72,14 @@ fun Thumbnail( shape: RoundedCornerShape = RoundedCornerShape(12.dp), size: DpSize = DpSize( JetcasterAppDefaults.cardWidth.medium, - JetcasterAppDefaults.cardWidth.medium + JetcasterAppDefaults.cardWidth.medium, ), - contentScale: ContentScale = ContentScale.Crop -) = - PodcastImage( - podcastImageUrl = url, - contentDescription = null, - contentScale = contentScale, - modifier = modifier - .clip(shape) - .size(size), - ) + contentScale: ContentScale = ContentScale.Crop, +) = PodcastImage( + podcastImageUrl = url, + contentDescription = null, + contentScale = contentScale, + modifier = modifier + .clip(shape) + .size(size), +) diff --git a/Jetcaster/tv/src/main/java/com/example/jetcaster/tv/ui/component/TwoColumn.kt b/Jetcaster/tv/src/main/java/com/example/jetcaster/tv/ui/component/TwoColumn.kt index 94658ad17..7f5e4b919 100644 --- a/Jetcaster/tv/src/main/java/com/example/jetcaster/tv/ui/component/TwoColumn.kt +++ b/Jetcaster/tv/src/main/java/com/example/jetcaster/tv/ui/component/TwoColumn.kt @@ -29,11 +29,11 @@ internal fun TwoColumn( second: (@Composable RowScope.() -> Unit), modifier: Modifier = Modifier, horizontalArrangement: Arrangement.Horizontal = - Arrangement.spacedBy(JetcasterAppDefaults.gap.twoColumn) + Arrangement.spacedBy(JetcasterAppDefaults.gap.twoColumn), ) { Row( horizontalArrangement = horizontalArrangement, - modifier = modifier + modifier = modifier, ) { first() second() diff --git a/Jetcaster/tv/src/main/java/com/example/jetcaster/tv/ui/discover/DiscoverScreen.kt b/Jetcaster/tv/src/main/java/com/example/jetcaster/tv/ui/discover/DiscoverScreen.kt index a0727cd55..15cbfc786 100644 --- a/Jetcaster/tv/src/main/java/com/example/jetcaster/tv/ui/discover/DiscoverScreen.kt +++ b/Jetcaster/tv/src/main/java/com/example/jetcaster/tv/ui/discover/DiscoverScreen.kt @@ -49,7 +49,7 @@ fun DiscoverScreen( showPodcastDetails: (PodcastInfo) -> Unit, playEpisode: (PlayerEpisode) -> Unit, modifier: Modifier = Modifier, - discoverScreenViewModel: DiscoverScreenViewModel = hiltViewModel() + discoverScreenViewModel: DiscoverScreenViewModel = hiltViewModel(), ) { val uiState by discoverScreenViewModel.uiState.collectAsState() @@ -58,7 +58,7 @@ fun DiscoverScreen( Loading( modifier = Modifier .fillMaxSize() - .then(modifier) + .then(modifier), ) } @@ -76,7 +76,7 @@ fun DiscoverScreen( }, modifier = Modifier .fillMaxSize() - .then(modifier) + .then(modifier), ) } } @@ -87,7 +87,6 @@ fun DiscoverScreen( private fun CatalogWithCategorySelection( categoryInfoList: CategoryInfoList, podcastList: PodcastList, - selectedCategory: CategoryInfo, latestEpisodeList: EpisodeList, onPodcastSelected: (PodcastInfo) -> Unit, @@ -124,7 +123,7 @@ private fun CatalogWithCategorySelection( enter = { selectedTab } - } + }, ) { categoryInfoList.forEachIndexed { index, category -> val tabModifier = if (selectedTabIndex == index) { @@ -142,7 +141,7 @@ private fun CatalogWithCategorySelection( ) { Text( text = category.name, - modifier = Modifier.padding(JetcasterAppDefaults.padding.tab) + modifier = Modifier.padding(JetcasterAppDefaults.padding.tab), ) } } diff --git a/Jetcaster/tv/src/main/java/com/example/jetcaster/tv/ui/discover/DiscoverScreenViewModel.kt b/Jetcaster/tv/src/main/java/com/example/jetcaster/tv/ui/discover/DiscoverScreenViewModel.kt index 925fd321b..46b318856 100644 --- a/Jetcaster/tv/src/main/java/com/example/jetcaster/tv/ui/discover/DiscoverScreenViewModel.kt +++ b/Jetcaster/tv/src/main/java/com/example/jetcaster/tv/ui/discover/DiscoverScreenViewModel.kt @@ -55,14 +55,14 @@ class DiscoverScreenViewModel @Inject constructor( categoryList.map { category -> CategoryInfo( id = category.id, - name = category.name.filter { !it.isWhitespace() } + name = category.name.filter { !it.isWhitespace() }, ) } } private val selectedCategoryFlow = combine( categoryListFlow, - _selectedCategory + _selectedCategory, ) { categoryList, category -> category ?: categoryList.firstOrNull() } @@ -100,7 +100,7 @@ class DiscoverScreenViewModel @Inject constructor( CategoryInfoList(categoryList), category, podcastList, - latestEpisodes + latestEpisodes, ) } else { DiscoverScreenUiState.Loading @@ -108,7 +108,7 @@ class DiscoverScreenViewModel @Inject constructor( }.stateIn( viewModelScope, SharingStarted.WhileSubscribed(5_000), - DiscoverScreenUiState.Loading + DiscoverScreenUiState.Loading, ) init { diff --git a/Jetcaster/tv/src/main/java/com/example/jetcaster/tv/ui/episode/EpisodeScreen.kt b/Jetcaster/tv/src/main/java/com/example/jetcaster/tv/ui/episode/EpisodeScreen.kt index b5b02abfb..3042cd460 100644 --- a/Jetcaster/tv/src/main/java/com/example/jetcaster/tv/ui/episode/EpisodeScreen.kt +++ b/Jetcaster/tv/src/main/java/com/example/jetcaster/tv/ui/episode/EpisodeScreen.kt @@ -48,7 +48,7 @@ fun EpisodeScreen( playEpisode: () -> Unit, backToHome: () -> Unit, modifier: Modifier = Modifier, - episodeScreenViewModel: EpisodeScreenViewModel = hiltViewModel() + episodeScreenViewModel: EpisodeScreenViewModel = hiltViewModel(), ) { val uiState by episodeScreenViewModel.uiStateFlow.collectAsState() @@ -64,7 +64,7 @@ fun EpisodeScreen( playEpisode() }, addPlayList = episodeScreenViewModel::addPlayList, - modifier = screenModifier + modifier = screenModifier, ) } } @@ -79,14 +79,14 @@ private fun EpisodeDetailsWithBackground( BackgroundContainer( playerEpisode = playerEpisode, contentAlignment = Alignment.Center, - modifier = modifier + modifier = modifier, ) { EpisodeDetails( playerEpisode = playerEpisode, playEpisode = playEpisode, addPlayList = addPlayList, modifier = Modifier - .padding(JetcasterAppDefaults.overScanMargin.episode.intoPaddingValues()) + .padding(JetcasterAppDefaults.overScanMargin.episode.intoPaddingValues()), ) } } @@ -102,7 +102,7 @@ private fun EpisodeDetails( first = { Thumbnail( episode = playerEpisode, - size = JetcasterAppDefaults.thumbnailSize.episodeDetails + size = JetcasterAppDefaults.thumbnailSize.episodeDetails, ) }, second = { @@ -110,7 +110,7 @@ private fun EpisodeDetails( playerEpisode = playerEpisode, playEpisode = { playEpisode(playerEpisode) }, addPlayList = { addPlayList(playerEpisode) }, - modifier = Modifier.weight(1f) + modifier = Modifier.weight(1f), ) }, modifier = modifier, @@ -118,12 +118,7 @@ private fun EpisodeDetails( } @Composable -private fun EpisodeInfo( - playerEpisode: PlayerEpisode, - playEpisode: () -> Unit, - addPlayList: () -> Unit, - modifier: Modifier = Modifier -) { +private fun EpisodeInfo(playerEpisode: PlayerEpisode, playEpisode: () -> Unit, addPlayList: () -> Unit, modifier: Modifier = Modifier) { val duration = playerEpisode.duration Column(modifier) { @@ -137,7 +132,7 @@ private fun EpisodeInfo( text = playerEpisode.summary, softWrap = true, maxLines = 5, - overflow = TextOverflow.Ellipsis + overflow = TextOverflow.Ellipsis, ) Spacer(modifier = Modifier.height(JetcasterAppDefaults.gap.paragraph)) Controls(playEpisode = playEpisode, addPlayList = addPlayList) @@ -145,15 +140,11 @@ private fun EpisodeInfo( } @Composable -private fun Controls( - playEpisode: () -> Unit, - addPlayList: () -> Unit, - modifier: Modifier = Modifier -) { +private fun Controls(playEpisode: () -> Unit, addPlayList: () -> Unit, modifier: Modifier = Modifier) { Row( horizontalArrangement = Arrangement.spacedBy(JetcasterAppDefaults.gap.item), verticalAlignment = Alignment.CenterVertically, - modifier = modifier + modifier = modifier, ) { PlayButton(onClick = playEpisode) EnqueueButton(onClick = addPlayList) diff --git a/Jetcaster/tv/src/main/java/com/example/jetcaster/tv/ui/episode/EpisodeScreenViewModel.kt b/Jetcaster/tv/src/main/java/com/example/jetcaster/tv/ui/episode/EpisodeScreenViewModel.kt index 2a5bec06f..dd773b50d 100644 --- a/Jetcaster/tv/src/main/java/com/example/jetcaster/tv/ui/episode/EpisodeScreenViewModel.kt +++ b/Jetcaster/tv/src/main/java/com/example/jetcaster/tv/ui/episode/EpisodeScreenViewModel.kt @@ -55,7 +55,7 @@ class EpisodeScreenViewModel @Inject constructor( }.stateIn( viewModelScope, SharingStarted.WhileSubscribed(5_000), - null + null, ) val uiStateFlow = episodeToPodcastFlow.map { @@ -67,7 +67,7 @@ class EpisodeScreenViewModel @Inject constructor( }.stateIn( viewModelScope, SharingStarted.WhileSubscribed(5_000), - EpisodeScreenUiState.Loading + EpisodeScreenUiState.Loading, ) fun addPlayList(episode: PlayerEpisode) { diff --git a/Jetcaster/tv/src/main/java/com/example/jetcaster/tv/ui/library/LibraryScreen.kt b/Jetcaster/tv/src/main/java/com/example/jetcaster/tv/ui/library/LibraryScreen.kt index ed73883b0..2a823d8e6 100644 --- a/Jetcaster/tv/src/main/java/com/example/jetcaster/tv/ui/library/LibraryScreen.kt +++ b/Jetcaster/tv/src/main/java/com/example/jetcaster/tv/ui/library/LibraryScreen.kt @@ -50,7 +50,7 @@ fun LibraryScreen( navigateToDiscover: () -> Unit, showPodcastDetails: (PodcastInfo) -> Unit, playEpisode: (PlayerEpisode) -> Unit, - libraryScreenViewModel: LibraryScreenViewModel = hiltViewModel() + libraryScreenViewModel: LibraryScreenViewModel = hiltViewModel(), ) { val uiState by libraryScreenViewModel.uiState.collectAsState() when (val s = uiState) { @@ -93,15 +93,12 @@ private fun Library( onEpisodeSelected = onEpisodeSelected, modifier = modifier .focusRequester(focusRequester) - .focusRestorer() + .focusRestorer(), ) } @Composable -private fun NavigateToDiscover( - onNavigationRequested: () -> Unit, - modifier: Modifier = Modifier, -) { +private fun NavigateToDiscover(onNavigationRequested: () -> Unit, modifier: Modifier = Modifier) { val focusRequester = remember { FocusRequester() } LaunchedEffect(Unit) { focusRequester.requestFocus() @@ -110,14 +107,14 @@ private fun NavigateToDiscover( Column { Text( text = stringResource(id = R.string.display_no_subscribed_podcast), - style = MaterialTheme.typography.displayMedium + style = MaterialTheme.typography.displayMedium, ) Text(text = stringResource(id = R.string.message_no_subscribed_podcast)) Button( onClick = onNavigationRequested, modifier = Modifier .padding(top = JetcasterAppDefaults.gap.podcastRow) - .focusRequester(focusRequester) + .focusRequester(focusRequester), ) { Text(text = stringResource(id = R.string.label_navigate_to_discover)) } diff --git a/Jetcaster/tv/src/main/java/com/example/jetcaster/tv/ui/library/LibraryScreenViewModel.kt b/Jetcaster/tv/src/main/java/com/example/jetcaster/tv/ui/library/LibraryScreenViewModel.kt index 3797b98e8..03418dc48 100644 --- a/Jetcaster/tv/src/main/java/com/example/jetcaster/tv/ui/library/LibraryScreenViewModel.kt +++ b/Jetcaster/tv/src/main/java/com/example/jetcaster/tv/ui/library/LibraryScreenViewModel.kt @@ -78,7 +78,7 @@ class LibraryScreenViewModel @Inject constructor( }.stateIn( viewModelScope, SharingStarted.WhileSubscribed(5_000), - LibraryScreenUiState.Loading + LibraryScreenUiState.Loading, ) init { @@ -95,8 +95,5 @@ class LibraryScreenViewModel @Inject constructor( sealed interface LibraryScreenUiState { data object Loading : LibraryScreenUiState data object NoSubscribedPodcast : LibraryScreenUiState - data class Ready( - val subscribedPodcastList: PodcastList, - val latestEpisodeList: EpisodeList, - ) : LibraryScreenUiState + data class Ready(val subscribedPodcastList: PodcastList, val latestEpisodeList: EpisodeList) : LibraryScreenUiState } diff --git a/Jetcaster/tv/src/main/java/com/example/jetcaster/tv/ui/player/PlayerScreen.kt b/Jetcaster/tv/src/main/java/com/example/jetcaster/tv/ui/player/PlayerScreen.kt index 9c11c295e..4f7af6f23 100644 --- a/Jetcaster/tv/src/main/java/com/example/jetcaster/tv/ui/player/PlayerScreen.kt +++ b/Jetcaster/tv/src/main/java/com/example/jetcaster/tv/ui/player/PlayerScreen.kt @@ -100,7 +100,7 @@ fun PlayerScreen( backToHome: () -> Unit, showDetails: (PlayerEpisode) -> Unit, modifier: Modifier = Modifier, - playScreenViewModel: PlayerScreenViewModel = hiltViewModel() + playScreenViewModel: PlayerScreenViewModel = hiltViewModel(), ) { val uiState by playScreenViewModel.uiStateFlow.collectAsStateWithLifecycle() @@ -140,7 +140,7 @@ private fun Player( showDetails: (PlayerEpisode) -> Unit, playEpisode: (PlayerEpisode) -> Unit, modifier: Modifier = Modifier, - autoStart: Boolean = true + autoStart: Boolean = true, ) { LaunchedEffect(key1 = autoStart) { if (autoStart && !episodePlayerState.isPlaying) { @@ -165,39 +165,47 @@ private fun Player( exoPlayer.release() } } - //Adding PlayerSurface at the bottom of the stack + // Adding PlayerSurface at the bottom of the stack // as it is just the audio player Box { PlayerSurface( player = exoPlayer, modifier = Modifier.resizeWithContentScale( contentScale = ContentScale.Fit, - sourceSizeDp = null - ) + sourceSizeDp = null, + ), ) EpisodePlayerWithBackground( playerEpisode = currentEpisode, queue = EpisodeList(episodePlayerState.queue), isPlaying = episodePlayerState.isPlaying, timeElapsed = episodePlayerState.timeElapsed, - play = ({ - play() - exoPlayer.play() - }), - pause = ({ - pause() - exoPlayer.pause() - }), + play = ( + { + play() + exoPlayer.play() + } + ), + pause = ( + { + pause() + exoPlayer.pause() + } + ), previous = previous, next = next, - skip = ({ - skip() - exoPlayer.seekForward() - }), - rewind = ({ - rewind() - exoPlayer.seekBack() - }), + skip = ( + { + skip() + exoPlayer.seekForward() + } + ), + rewind = ( + { + rewind() + exoPlayer.seekBack() + } + ), enqueue = enqueue, showDetails = showDetails, playEpisode = playEpisode, @@ -223,7 +231,7 @@ private fun EpisodePlayerWithBackground( enqueue: (PlayerEpisode) -> Unit, showDetails: (PlayerEpisode) -> Unit, playEpisode: (PlayerEpisode) -> Unit, - modifier: Modifier = Modifier + modifier: Modifier = Modifier, ) { val episodePlayer = remember { FocusRequester() } @@ -234,7 +242,7 @@ private fun EpisodePlayerWithBackground( BackgroundContainer( playerEpisode = playerEpisode, modifier = modifier, - contentAlignment = Alignment.Center + contentAlignment = Alignment.Center, ) { EpisodePlayer( @@ -251,7 +259,7 @@ private fun EpisodePlayerWithBackground( showDetails = showDetails, focusRequester = episodePlayer, modifier = Modifier - .padding(JetcasterAppDefaults.overScanMargin.player.intoPaddingValues()) + .padding(JetcasterAppDefaults.overScanMargin.player.intoPaddingValues()), ) PlayerQueueOverlay( @@ -282,7 +290,7 @@ private fun EpisodePlayer( modifier: Modifier = Modifier, bringIntoViewRequester: BringIntoViewRequester = remember { BringIntoViewRequester() }, coroutineScope: CoroutineScope = rememberCoroutineScope(), - focusRequester: FocusRequester = remember { FocusRequester() } + focusRequester: FocusRequester = remember { FocusRequester() }, ) { Column( verticalArrangement = Arrangement.spacedBy(JetcasterAppDefaults.gap.section), @@ -295,7 +303,7 @@ private fun EpisodePlayer( } } } - .then(modifier) + .then(modifier), ) { EpisodeDetails( playerEpisode = playerEpisode, @@ -303,7 +311,7 @@ private fun EpisodePlayer( controls = { EpisodeControl( showDetails = { showDetails(playerEpisode) }, - enqueue = { enqueue(playerEpisode) } + enqueue = { enqueue(playerEpisode) }, ) }, ) @@ -317,28 +325,24 @@ private fun EpisodePlayer( next = next, skip = skip, rewind = rewind, - focusRequester = focusRequester + focusRequester = focusRequester, ) } } @Composable -private fun EpisodeControl( - showDetails: () -> Unit, - enqueue: () -> Unit, - modifier: Modifier = Modifier, -) { +private fun EpisodeControl(showDetails: () -> Unit, enqueue: () -> Unit, modifier: Modifier = Modifier) { Row( modifier = modifier, - horizontalArrangement = Arrangement.spacedBy(JetcasterAppDefaults.gap.item) + horizontalArrangement = Arrangement.spacedBy(JetcasterAppDefaults.gap.item), ) { EnqueueButton( onClick = enqueue, - modifier = Modifier.size(JetcasterAppDefaults.iconButtonSize.default.intoDpSize()) + modifier = Modifier.size(JetcasterAppDefaults.iconButtonSize.default.intoDpSize()), ) InfoButton( onClick = showDetails, - modifier = Modifier.size(JetcasterAppDefaults.iconButtonSize.default.intoDpSize()) + modifier = Modifier.size(JetcasterAppDefaults.iconButtonSize.default.intoDpSize()), ) } } @@ -355,7 +359,7 @@ private fun PlayerControl( skip: () -> Unit, rewind: () -> Unit, modifier: Modifier = Modifier, - focusRequester: FocusRequester = remember { FocusRequester() } + focusRequester: FocusRequester = remember { FocusRequester() }, ) { val playPauseButton = remember { FocusRequester() } @@ -366,7 +370,7 @@ private fun PlayerControl( Row( horizontalArrangement = Arrangement.spacedBy( JetcasterAppDefaults.gap.default, - Alignment.CenterHorizontally + Alignment.CenterHorizontally, ), verticalAlignment = Alignment.CenterVertically, modifier = Modifier @@ -381,11 +385,11 @@ private fun PlayerControl( ) { PreviousButton( onClick = previous, - modifier = Modifier.size(JetcasterAppDefaults.iconButtonSize.medium.intoDpSize()) + modifier = Modifier.size(JetcasterAppDefaults.iconButtonSize.medium.intoDpSize()), ) RewindButton( onClick = rewind, - modifier = Modifier.size(JetcasterAppDefaults.iconButtonSize.medium.intoDpSize()) + modifier = Modifier.size(JetcasterAppDefaults.iconButtonSize.medium.intoDpSize()), ) PlayPauseButton( isPlaying = isPlaying, @@ -398,15 +402,15 @@ private fun PlayerControl( }, modifier = Modifier .size(JetcasterAppDefaults.iconButtonSize.large.intoDpSize()) - .focusRequester(playPauseButton) + .focusRequester(playPauseButton), ) SkipButton( onClick = skip, - modifier = Modifier.size(JetcasterAppDefaults.iconButtonSize.medium.intoDpSize()) + modifier = Modifier.size(JetcasterAppDefaults.iconButtonSize.medium.intoDpSize()), ) NextButton( onClick = next, - modifier = Modifier.size(JetcasterAppDefaults.iconButtonSize.medium.intoDpSize()) + modifier = Modifier.size(JetcasterAppDefaults.iconButtonSize.medium.intoDpSize()), ) } if (length != null) { @@ -422,11 +426,11 @@ private fun ElapsedTimeIndicator( skip: () -> Unit, rewind: () -> Unit, modifier: Modifier = Modifier, - knobSize: Dp = 8.dp + knobSize: Dp = 8.dp, ) { Column( modifier = modifier, - verticalArrangement = Arrangement.spacedBy(JetcasterAppDefaults.gap.tiny) + verticalArrangement = Arrangement.spacedBy(JetcasterAppDefaults.gap.tiny), ) { ElapsedTime(timeElapsed = timeElapsed, length = length) Seekbar( @@ -435,7 +439,7 @@ private fun ElapsedTimeIndicator( knobSize = knobSize, onMoveLeft = rewind, onMoveRight = skip, - modifier = Modifier.fillMaxWidth() + modifier = Modifier.fillMaxWidth(), ) } } @@ -445,20 +449,20 @@ private fun ElapsedTime( timeElapsed: Duration, length: Duration, modifier: Modifier = Modifier, - style: TextStyle = MaterialTheme.typography.bodySmall + style: TextStyle = MaterialTheme.typography.bodySmall, ) { val elapsed = stringResource( R.string.minutes_seconds, timeElapsed.toMinutes(), - timeElapsed.toSeconds() % 60 + timeElapsed.toSeconds() % 60, ) val l = stringResource(R.string.minutes_seconds, length.toMinutes(), length.toSeconds() % 60) Text( text = stringResource(R.string.elapsed_time, elapsed, l), style = style, - modifier = modifier + modifier = modifier, ) } @@ -466,7 +470,7 @@ private fun ElapsedTime( private fun NoEpisodeInQueue( backToHome: () -> Unit, modifier: Modifier = Modifier, - focusRequester: FocusRequester = remember { FocusRequester() } + focusRequester: FocusRequester = remember { FocusRequester() }, ) { LaunchedEffect(Unit) { focusRequester.requestFocus() @@ -475,7 +479,7 @@ private fun NoEpisodeInQueue( Column { Text( text = stringResource(R.string.display_nothing_in_queue), - style = MaterialTheme.typography.displayMedium + style = MaterialTheme.typography.displayMedium, ) Spacer(modifier = Modifier.height(JetcasterAppDefaults.gap.paragraph)) Text(text = stringResource(R.string.message_nothing_in_queue)) @@ -526,7 +530,7 @@ private fun PlayerQueueOverlay( contentPadding = contentPadding, modifier = Modifier .offset(actualOffset.x, actualOffset.y) - .onFocusChanged { hasFocus = it.hasFocus } + .onFocusChanged { hasFocus = it.hasFocus }, ) } } diff --git a/Jetcaster/tv/src/main/java/com/example/jetcaster/tv/ui/player/PlayerScreenViewModel.kt b/Jetcaster/tv/src/main/java/com/example/jetcaster/tv/ui/player/PlayerScreenViewModel.kt index 9b66a9359..a03381604 100644 --- a/Jetcaster/tv/src/main/java/com/example/jetcaster/tv/ui/player/PlayerScreenViewModel.kt +++ b/Jetcaster/tv/src/main/java/com/example/jetcaster/tv/ui/player/PlayerScreenViewModel.kt @@ -29,9 +29,7 @@ import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.stateIn @HiltViewModel -class PlayerScreenViewModel @Inject constructor( - private val episodePlayer: EpisodePlayer, -) : ViewModel() { +class PlayerScreenViewModel @Inject constructor(private val episodePlayer: EpisodePlayer) : ViewModel() { val uiStateFlow = episodePlayer.playerState.map { if (it.currentEpisode == null && it.queue.isEmpty()) { @@ -42,7 +40,7 @@ class PlayerScreenViewModel @Inject constructor( }.stateIn( viewModelScope, SharingStarted.WhileSubscribed(5_000), - PlayerScreenUiState.Loading + PlayerScreenUiState.Loading, ) private val skipAmount = Duration.ofSeconds(10L) @@ -75,9 +73,7 @@ class PlayerScreenViewModel @Inject constructor( sealed interface PlayerScreenUiState { data object Loading : PlayerScreenUiState - data class Ready( - val playerState: EpisodePlayerState - ) : PlayerScreenUiState + data class Ready(val playerState: EpisodePlayerState) : PlayerScreenUiState data object NoEpisodeInQueue : PlayerScreenUiState } diff --git a/Jetcaster/tv/src/main/java/com/example/jetcaster/tv/ui/podcast/PodcastDetailsScreen.kt b/Jetcaster/tv/src/main/java/com/example/jetcaster/tv/ui/podcast/PodcastDetailsScreen.kt index 26e84b7dc..75b4b743f 100644 --- a/Jetcaster/tv/src/main/java/com/example/jetcaster/tv/ui/podcast/PodcastDetailsScreen.kt +++ b/Jetcaster/tv/src/main/java/com/example/jetcaster/tv/ui/podcast/PodcastDetailsScreen.kt @@ -112,7 +112,7 @@ private fun PodcastDetailsWithBackground( showEpisodeDetails: (PlayerEpisode) -> Unit, enqueue: (PlayerEpisode) -> Unit, modifier: Modifier = Modifier, - focusRequester: FocusRequester = remember { FocusRequester() } + focusRequester: FocusRequester = remember { FocusRequester() }, ) { BackgroundContainer(podcastInfo = podcastInfo, modifier = modifier) { @@ -127,7 +127,7 @@ private fun PodcastDetailsWithBackground( showEpisodeDetails = showEpisodeDetails, enqueue = enqueue, modifier = Modifier - .fillMaxSize() + .fillMaxSize(), ) } } @@ -144,7 +144,7 @@ private fun PodcastDetails( showEpisodeDetails: (PlayerEpisode) -> Unit, enqueue: (PlayerEpisode) -> Unit, modifier: Modifier = Modifier, - focusRequester: FocusRequester = remember { FocusRequester() } + focusRequester: FocusRequester = remember { FocusRequester() }, ) { TwoColumn( modifier = modifier, @@ -160,7 +160,7 @@ private fun PodcastDetails( .weight(0.3f) .padding( JetcasterAppDefaults.overScanMargin.podcast.copy(end = 0.dp) - .intoPaddingValues() + .intoPaddingValues(), ), ) }, @@ -173,9 +173,9 @@ private fun PodcastDetails( modifier = Modifier .focusRequester(focusRequester) .focusRestorer() - .weight(0.7f) + .weight(0.7f), ) - } + }, ) LaunchedEffect(Unit) { @@ -197,7 +197,7 @@ private fun PodcastInfo( Text( text = podcastInfo.author, - style = MaterialTheme.typography.bodySmall + style = MaterialTheme.typography.bodySmall, ) Text( text = podcastInfo.title, @@ -207,7 +207,7 @@ private fun PodcastInfo( text = podcastInfo.description, maxLines = 2, overflow = TextOverflow.Ellipsis, - style = MaterialTheme.typography.bodyMedium + style = MaterialTheme.typography.bodyMedium, ) ToggleSubscriptionButton( podcastInfo, @@ -215,7 +215,7 @@ private fun PodcastInfo( subscribe, unsubscribe, modifier = Modifier - .padding(top = JetcasterAppDefaults.gap.podcastRow) + .padding(top = JetcasterAppDefaults.gap.podcastRow), ) } } @@ -226,7 +226,7 @@ private fun ToggleSubscriptionButton( isSubscribed: Boolean, subscribe: (PodcastInfo, Boolean) -> Unit, unsubscribe: (PodcastInfo, Boolean) -> Unit, - modifier: Modifier = Modifier + modifier: Modifier = Modifier, ) { val icon = if (isSubscribed) { Icons.Default.Remove @@ -248,7 +248,7 @@ private fun ToggleSubscriptionButton( icon = icon, onClick = { action(podcastInfo, isSubscribed) }, scale = ButtonDefaults.scale(scale = 1f), - modifier = modifier + modifier = modifier, ) } @@ -258,12 +258,12 @@ private fun PodcastEpisodeList( playEpisode: (PlayerEpisode) -> Unit, showDetails: (PlayerEpisode) -> Unit, enqueue: (PlayerEpisode) -> Unit, - modifier: Modifier = Modifier + modifier: Modifier = Modifier, ) { LazyColumn( verticalArrangement = Arrangement.spacedBy(JetcasterAppDefaults.gap.podcastRow), modifier = modifier, - contentPadding = JetcasterAppDefaults.overScanMargin.podcast.intoPaddingValues() + contentPadding = JetcasterAppDefaults.overScanMargin.podcast.intoPaddingValues(), ) { items(episodeList) { EpisodeListItem( @@ -321,7 +321,7 @@ private fun EpisodeListItem( .border(borderWidth, borderColor, shape) .background(backgroundColor) .shadow(elevation, shape) - .padding(start = 12.dp, top = 12.dp, bottom = 12.dp, end = 16.dp) + .padding(start = 12.dp, top = 12.dp, bottom = 12.dp, end = 16.dp), ) } @@ -337,7 +337,7 @@ private fun EpisodeListItemContentLayer( val playButton = remember { FocusRequester() } Box( contentAlignment = Alignment.CenterStart, - modifier = modifier + modifier = modifier, ) { Column( @@ -348,11 +348,11 @@ private fun EpisodeListItemContentLayer( horizontalArrangement = Arrangement.spacedBy(JetcasterAppDefaults.gap.default), verticalAlignment = Alignment.CenterVertically, modifier = Modifier - .padding(top = JetcasterAppDefaults.gap.paragraph) + .padding(top = JetcasterAppDefaults.gap.paragraph), ) { PlayButton( onClick = onEpisodeSelected, - modifier = Modifier.focusRequester(playButton) + modifier = Modifier.focusRequester(playButton), ) if (duration != null) { EpisodeDataAndDuration(playerEpisode.published, duration) @@ -370,6 +370,6 @@ private fun EpisodeTitle(playerEpisode: PlayerEpisode, modifier: Modifier = Modi Text( text = playerEpisode.title, style = MaterialTheme.typography.titleLarge, - modifier = modifier + modifier = modifier, ) } diff --git a/Jetcaster/tv/src/main/java/com/example/jetcaster/tv/ui/podcast/PodcastDetailsScreenViewModel.kt b/Jetcaster/tv/src/main/java/com/example/jetcaster/tv/ui/podcast/PodcastDetailsScreenViewModel.kt index c68033c65..09731eca0 100644 --- a/Jetcaster/tv/src/main/java/com/example/jetcaster/tv/ui/podcast/PodcastDetailsScreenViewModel.kt +++ b/Jetcaster/tv/src/main/java/com/example/jetcaster/tv/ui/podcast/PodcastDetailsScreenViewModel.kt @@ -76,7 +76,7 @@ class PodcastDetailsScreenViewModel @Inject constructor( val uiStateFlow = combine( podcastFlow, episodeListFlow, - subscribedPodcastListFlow + subscribedPodcastListFlow, ) { podcast, episodeList, subscribedPodcastList -> if (podcast != null) { val isSubscribed = subscribedPodcastList.any { it.podcast.uri == podcastUri } @@ -87,7 +87,7 @@ class PodcastDetailsScreenViewModel @Inject constructor( }.stateIn( viewModelScope, SharingStarted.WhileSubscribed(5_000), - PodcastScreenUiState.Loading + PodcastScreenUiState.Loading, ) fun subscribe(podcastInfo: PodcastInfo, isSubscribed: Boolean) { @@ -118,9 +118,5 @@ class PodcastDetailsScreenViewModel @Inject constructor( sealed interface PodcastScreenUiState { data object Loading : PodcastScreenUiState data object Error : PodcastScreenUiState - data class Ready( - val podcastInfo: PodcastInfo, - val episodeList: EpisodeList, - val isSubscribed: Boolean - ) : PodcastScreenUiState + data class Ready(val podcastInfo: PodcastInfo, val episodeList: EpisodeList, val isSubscribed: Boolean) : PodcastScreenUiState } diff --git a/Jetcaster/tv/src/main/java/com/example/jetcaster/tv/ui/search/SearchScreen.kt b/Jetcaster/tv/src/main/java/com/example/jetcaster/tv/ui/search/SearchScreen.kt index f4b7cd100..0fd3bb0ee 100644 --- a/Jetcaster/tv/src/main/java/com/example/jetcaster/tv/ui/search/SearchScreen.kt +++ b/Jetcaster/tv/src/main/java/com/example/jetcaster/tv/ui/search/SearchScreen.kt @@ -68,7 +68,7 @@ import com.example.jetcaster.tv.ui.theme.JetcasterAppDefaults fun SearchScreen( onPodcastSelected: (PodcastInfo) -> Unit, modifier: Modifier = Modifier, - searchScreenViewModel: SearchScreenViewModel = hiltViewModel() + searchScreenViewModel: SearchScreenViewModel = hiltViewModel(), ) { val uiState by searchScreenViewModel.uiStateFlow.collectAsState() @@ -80,7 +80,7 @@ fun SearchScreen( onKeywordInput = searchScreenViewModel::setKeyword, onCategorySelected = searchScreenViewModel::addCategoryToSelectedCategoryList, onCategoryUnselected = searchScreenViewModel::removeCategoryFromSelectedCategoryList, - modifier = modifier + modifier = modifier, ) is SearchScreenUiState.HasResult -> HasResult( @@ -103,7 +103,7 @@ private fun Ready( onKeywordInput: (String) -> Unit, onCategorySelected: (CategoryInfo) -> Unit, onCategoryUnselected: (CategoryInfo) -> Unit, - modifier: Modifier = Modifier + modifier: Modifier = Modifier, ) { Controls( keyword = keyword, @@ -112,7 +112,7 @@ private fun Ready( onCategorySelected = onCategorySelected, onCategoryUnselected = onCategoryUnselected, modifier = modifier, - toRequestFocus = true + toRequestFocus = true, ) } @@ -125,7 +125,7 @@ private fun HasResult( onCategorySelected: (CategoryInfo) -> Unit, onCategoryUnselected: (CategoryInfo) -> Unit, onPodcastSelected: (PodcastInfo) -> Unit, - modifier: Modifier = Modifier + modifier: Modifier = Modifier, ) { SearchResult( podcastList = podcastList, @@ -139,7 +139,7 @@ private fun HasResult( onCategoryUnselected = onCategoryUnselected, ) }, - modifier = modifier + modifier = modifier, ) } @@ -153,7 +153,7 @@ private fun Controls( onCategoryUnselected: (CategoryInfo) -> Unit, modifier: Modifier = Modifier, focusRequester: FocusRequester = remember { FocusRequester() }, - toRequestFocus: Boolean = false + toRequestFocus: Boolean = false, ) { LaunchedEffect(toRequestFocus) { if (toRequestFocus) { @@ -163,7 +163,7 @@ private fun Controls( Column( verticalArrangement = Arrangement.spacedBy(JetcasterAppDefaults.gap.item), - modifier = modifier + modifier = modifier, ) { KeywordInput( keyword = keyword, @@ -175,19 +175,15 @@ private fun Controls( onCategoryUnselected = onCategoryUnselected, modifier = Modifier .focusRestorer() - .focusRequester(focusRequester) + .focusRequester(focusRequester), ) } } @Composable -private fun KeywordInput( - keyword: String, - onKeywordInput: (String) -> Unit, - modifier: Modifier = Modifier, -) { +private fun KeywordInput(keyword: String, onKeywordInput: (String) -> Unit, modifier: Modifier = Modifier) { val textStyle = MaterialTheme.typography.bodyMedium.copy( - color = MaterialTheme.colorScheme.onSurfaceVariant + color = MaterialTheme.colorScheme.onSurfaceVariant, ) val cursorBrush = SolidColor(MaterialTheme.colorScheme.onSurfaceVariant) BasicTextField( @@ -203,22 +199,22 @@ private fun KeywordInput( .fillMaxWidth() .background( MaterialTheme.colorScheme.surfaceVariant, - RoundedCornerShape(percent = 50) - ) + RoundedCornerShape(percent = 50), + ), ) { Row( modifier = Modifier.padding(horizontal = 16.dp, vertical = 12.dp), - verticalAlignment = Alignment.CenterVertically + verticalAlignment = Alignment.CenterVertically, ) { Icon( Icons.Default.Search, contentDescription = stringResource(R.string.label_search), - modifier = Modifier.padding(end = 12.dp) + modifier = Modifier.padding(end = 12.dp), ) innerTextField() } } - } + }, ) } @@ -228,7 +224,7 @@ private fun CategorySelection( categorySelectionList: CategorySelectionList, onCategorySelected: (CategoryInfo) -> Unit, onCategoryUnselected: (CategoryInfo) -> Unit, - modifier: Modifier = Modifier + modifier: Modifier = Modifier, ) { FlowRow( modifier = modifier, @@ -244,7 +240,7 @@ private fun CategorySelection( } else { onCategorySelected(it.categoryInfo) } - } + }, ) { Text(text = it.categoryInfo.name) } diff --git a/Jetcaster/tv/src/main/java/com/example/jetcaster/tv/ui/search/SearchScreenViewModel.kt b/Jetcaster/tv/src/main/java/com/example/jetcaster/tv/ui/search/SearchScreenViewModel.kt index d16d143b7..e6df19845 100644 --- a/Jetcaster/tv/src/main/java/com/example/jetcaster/tv/ui/search/SearchScreenViewModel.kt +++ b/Jetcaster/tv/src/main/java/com/example/jetcaster/tv/ui/search/SearchScreenViewModel.kt @@ -55,7 +55,7 @@ class SearchScreenViewModel @Inject constructor( combine( keywordFlow, selectedCategoryListFlow, - categoryInfoListFlow + categoryInfoListFlow, ) { keyword, selectedCategories, categories -> val selected = selectedCategories.ifEmpty { categories @@ -67,18 +67,18 @@ class SearchScreenViewModel @Inject constructor( private val searchResultFlow = searchConditionFlow.flatMapLatest { podcastStore.searchPodcastByTitleAndCategories( it.keyword, - it.selectedCategories.intoCategoryList() + it.selectedCategories.intoCategoryList(), ) }.stateIn( viewModelScope, SharingStarted.WhileSubscribed(5_000), - emptyList() + emptyList(), ) private val categorySelectionFlow = combine( categoryInfoListFlow, - selectedCategoryListFlow + selectedCategoryListFlow, ) { categoryList, selectedCategories -> val list = categoryList.map { CategorySelection(it, selectedCategories.contains(it)) @@ -90,7 +90,7 @@ class SearchScreenViewModel @Inject constructor( combine( keywordFlow, categorySelectionFlow, - searchResultFlow + searchResultFlow, ) { keyword, categorySelection, result -> val podcastList = result.map { it.asExternalModel() } when { @@ -133,20 +133,14 @@ class SearchScreenViewModel @Inject constructor( private data class SearchCondition(val keyword: String, val selectedCategories: CategoryInfoList) { constructor(keyword: String, categoryInfoList: List) : this( keyword, - CategoryInfoList(categoryInfoList) + CategoryInfoList(categoryInfoList), ) } sealed interface SearchScreenUiState { data object Loading : SearchScreenUiState - data class Ready( - val keyword: String, - val categorySelectionList: CategorySelectionList - ) : SearchScreenUiState - - data class HasResult( - val keyword: String, - val categorySelectionList: CategorySelectionList, - val result: PodcastList - ) : SearchScreenUiState + data class Ready(val keyword: String, val categorySelectionList: CategorySelectionList) : SearchScreenUiState + + data class HasResult(val keyword: String, val categorySelectionList: CategorySelectionList, val result: PodcastList) : + SearchScreenUiState } diff --git a/Jetcaster/tv/src/main/java/com/example/jetcaster/tv/ui/settings/SettingsScreen.kt b/Jetcaster/tv/src/main/java/com/example/jetcaster/tv/ui/settings/SettingsScreen.kt index 53bf32f50..c7a54f16d 100644 --- a/Jetcaster/tv/src/main/java/com/example/jetcaster/tv/ui/settings/SettingsScreen.kt +++ b/Jetcaster/tv/src/main/java/com/example/jetcaster/tv/ui/settings/SettingsScreen.kt @@ -21,8 +21,6 @@ import androidx.compose.ui.Modifier import com.example.jetcaster.tv.ui.component.NotAvailableFeature @Composable -fun SettingsScreen( - modifier: Modifier = Modifier -) { +fun SettingsScreen(modifier: Modifier = Modifier) { NotAvailableFeature(modifier = modifier) } diff --git a/Jetcaster/tv/src/main/java/com/example/jetcaster/tv/ui/theme/Space.kt b/Jetcaster/tv/src/main/java/com/example/jetcaster/tv/ui/theme/Space.kt index def4a3786..ad8cdb0a1 100644 --- a/Jetcaster/tv/src/main/java/com/example/jetcaster/tv/ui/theme/Space.kt +++ b/Jetcaster/tv/src/main/java/com/example/jetcaster/tv/ui/theme/Space.kt @@ -39,37 +39,28 @@ internal data class OverScanMarginSettings( top = 40.dp, bottom = 40.dp, start = 80.dp, - end = 80.dp + end = 80.dp, ), val player: OverScanMargin = OverScanMargin( top = 40.dp, bottom = 40.dp, start = 80.dp, - end = 80.dp + end = 80.dp, ), ) -internal data class OverScanMargin( - val top: Dp = 24.dp, - val bottom: Dp = 24.dp, - val start: Dp = 48.dp, - val end: Dp = 48.dp, -) { +internal data class OverScanMargin(val top: Dp = 24.dp, val bottom: Dp = 24.dp, val start: Dp = 48.dp, val end: Dp = 48.dp) { fun intoPaddingValues(): PaddingValues { return PaddingValues(start, top, end, bottom) } } -internal data class CardWidth( - val large: Dp = 268.dp, - val medium: Dp = 196.dp, - val small: Dp = 124.dp -) +internal data class CardWidth(val large: Dp = 268.dp, val medium: Dp = 196.dp, val small: Dp = 124.dp) internal data class ThumbnailSize( val episodeDetails: DpSize = DpSize(266.dp, 266.dp), val podcast: DpSize = DpSize(196.dp, 196.dp), - val episode: DpSize = DpSize(124.dp, 124.dp) + val episode: DpSize = DpSize(124.dp, 124.dp), ) internal data class PaddingSettings( @@ -98,7 +89,7 @@ internal data class GapSettings( internal data class IconButtonSize( val default: Radius = Radius(14.dp), val medium: Radius = Radius(20.dp), - val large: Radius = Radius(28.dp) + val large: Radius = Radius(28.dp), ) internal data class Radius(private val value: Dp) { diff --git a/Jetcaster/tv/src/main/java/com/example/jetcaster/tv/ui/theme/Theme.kt b/Jetcaster/tv/src/main/java/com/example/jetcaster/tv/ui/theme/Theme.kt index f895300f7..c490ce6cd 100644 --- a/Jetcaster/tv/src/main/java/com/example/jetcaster/tv/ui/theme/Theme.kt +++ b/Jetcaster/tv/src/main/java/com/example/jetcaster/tv/ui/theme/Theme.kt @@ -21,10 +21,7 @@ import androidx.compose.runtime.Composable import androidx.tv.material3.MaterialTheme @Composable -fun JetcasterTheme( - isInDarkTheme: Boolean = isSystemInDarkTheme(), - content: @Composable () -> Unit, -) { +fun JetcasterTheme(isInDarkTheme: Boolean = isSystemInDarkTheme(), content: @Composable () -> Unit) { val colorScheme = if (isInDarkTheme) { colorSchemeForDarkMode } else { @@ -33,6 +30,6 @@ fun JetcasterTheme( MaterialTheme( colorScheme = colorScheme, typography = Typography, - content = content + content = content, ) } diff --git a/Jetcaster/tv/src/main/java/com/example/jetcaster/tv/ui/theme/Type.kt b/Jetcaster/tv/src/main/java/com/example/jetcaster/tv/ui/theme/Type.kt index 1be9cc97c..8075fe231 100644 --- a/Jetcaster/tv/src/main/java/com/example/jetcaster/tv/ui/theme/Type.kt +++ b/Jetcaster/tv/src/main/java/com/example/jetcaster/tv/ui/theme/Type.kt @@ -113,5 +113,5 @@ val Typography = Typography( fontWeight = FontWeight.Normal, fontSize = 12.sp, lineHeight = 16.sp, - ) + ), ) diff --git a/Jetcaster/wear/src/main/java/com/example/jetcaster/JetcasterWearApplication.kt b/Jetcaster/wear/src/main/java/com/example/jetcaster/JetcasterWearApplication.kt index a633b967a..a5d212593 100644 --- a/Jetcaster/wear/src/main/java/com/example/jetcaster/JetcasterWearApplication.kt +++ b/Jetcaster/wear/src/main/java/com/example/jetcaster/JetcasterWearApplication.kt @@ -24,7 +24,9 @@ import dagger.hilt.android.HiltAndroidApp import javax.inject.Inject @HiltAndroidApp -class JetcasterWearApplication : Application(), ImageLoaderFactory { +class JetcasterWearApplication : + Application(), + ImageLoaderFactory { @Inject lateinit var imageLoader: ImageLoader @@ -44,6 +46,5 @@ class JetcasterWearApplication : Application(), ImageLoaderFactory { ) } - override fun newImageLoader(): ImageLoader = - imageLoader + override fun newImageLoader(): ImageLoader = imageLoader } diff --git a/Jetcaster/wear/src/main/java/com/example/jetcaster/WearApp.kt b/Jetcaster/wear/src/main/java/com/example/jetcaster/WearApp.kt index f48d6d50c..67014e02b 100644 --- a/Jetcaster/wear/src/main/java/com/example/jetcaster/WearApp.kt +++ b/Jetcaster/wear/src/main/java/com/example/jetcaster/WearApp.kt @@ -67,12 +67,12 @@ fun WearApp(navController: NavHostController) { startDestination = NavigationScreens.Player.playerDestination(), navController = navController, modifier = Modifier.background(Color.Transparent), - state = navHostState + state = navHostState, ) { composable( route = NavigationScreens.Player.navRoute, arguments = NavigationScreens.Player.arguments, - deepLinks = NavigationScreens.Player.deepLinks("") + deepLinks = NavigationScreens.Player.deepLinks(""), ) { val volumeState by volumeViewModel.volumeUiState.collectAsStateWithLifecycle() val pagerState = rememberPagerState(initialPage = 0, pageCount = { 2 }) @@ -87,24 +87,24 @@ fun WearApp(navController: NavHostController) { volumeViewModel = volumeViewModel, onVolumeClick = { navController.navigateToVolume() - } + }, ) }, libraryScreen = { LibraryScreen( onLatestEpisodeClick = { navController.navigateToLatestEpisode() }, onYourPodcastClick = { navController.navigateToYourPodcast() }, - onUpNextClick = { navController.navigateToUpNext() } + onUpNextClick = { navController.navigateToUpNext() }, ) }, - backStack = it + backStack = it, ) } composable( route = NavigationScreens.Volume.navRoute, arguments = NavigationScreens.Volume.arguments, - deepLinks = NavigationScreens.Volume.deepLinks("") + deepLinks = NavigationScreens.Volume.deepLinks(""), ) { ScreenScaffold(timeText = {}) { VolumeScreen(volumeViewModel = volumeViewModel) @@ -112,19 +112,19 @@ fun WearApp(navController: NavHostController) { } composable( - route = LatestEpisodes.navRoute + route = LatestEpisodes.navRoute, ) { LatestEpisodesScreen( onPlayButtonClick = { navController.navigateToPlayer() }, - onDismiss = { navController.popBackStack() } + onDismiss = { navController.popBackStack() }, ) } composable(route = YourPodcasts.navRoute) { PodcastsScreen( onPodcastsItemClick = { navController.navigateToPodcastDetails(it.uri) }, - onDismiss = { navController.popBackStack() } + onDismiss = { navController.popBackStack() }, ) } composable(route = PodcastDetails.navRoute) { @@ -133,7 +133,7 @@ fun WearApp(navController: NavHostController) { navController.navigateToPlayer() }, onEpisodeItemClick = { navController.navigateToEpisode(it.uri) }, - onDismiss = { navController.popBackStack() } + onDismiss = { navController.popBackStack() }, ) } composable(route = UpNext.navRoute) { @@ -145,7 +145,7 @@ fun WearApp(navController: NavHostController) { onDismiss = { navController.popBackStack() navController.navigateToYourPodcast() - } + }, ) } composable(route = Episode.navRoute) { @@ -156,7 +156,7 @@ fun WearApp(navController: NavHostController) { onDismiss = { navController.popBackStack() navController.navigateToYourPodcast() - } + }, ) } } diff --git a/Jetcaster/wear/src/main/java/com/example/jetcaster/theme/Color.kt b/Jetcaster/wear/src/main/java/com/example/jetcaster/theme/Color.kt index 2bb7c18ff..cbec73b34 100644 --- a/Jetcaster/wear/src/main/java/com/example/jetcaster/theme/Color.kt +++ b/Jetcaster/wear/src/main/java/com/example/jetcaster/theme/Color.kt @@ -34,5 +34,5 @@ internal val wearColorPalette: Colors = Colors( error = errorDark, onPrimary = onPrimaryDark, onSecondary = onSecondaryDark, - onError = onErrorDark + onError = onErrorDark, ) diff --git a/Jetcaster/wear/src/main/java/com/example/jetcaster/theme/Type.kt b/Jetcaster/wear/src/main/java/com/example/jetcaster/theme/Type.kt index 2ce188903..39eecfb36 100644 --- a/Jetcaster/wear/src/main/java/com/example/jetcaster/theme/Type.kt +++ b/Jetcaster/wear/src/main/java/com/example/jetcaster/theme/Type.kt @@ -27,8 +27,8 @@ val Typography = Typography( body1 = TextStyle( fontFamily = Montserrat, fontWeight = FontWeight.Normal, - fontSize = 16.sp - ) + fontSize = 16.sp, + ), /* Other default text styles to override button = TextStyle( fontFamily = FontFamily.Default, diff --git a/Jetcaster/wear/src/main/java/com/example/jetcaster/theme/WearAppTheme.kt b/Jetcaster/wear/src/main/java/com/example/jetcaster/theme/WearAppTheme.kt index c797a8c04..b8411cdd2 100644 --- a/Jetcaster/wear/src/main/java/com/example/jetcaster/theme/WearAppTheme.kt +++ b/Jetcaster/wear/src/main/java/com/example/jetcaster/theme/WearAppTheme.kt @@ -20,14 +20,12 @@ import androidx.compose.runtime.Composable import androidx.wear.compose.material.MaterialTheme @Composable -fun WearAppTheme( - content: @Composable () -> Unit -) { +fun WearAppTheme(content: @Composable () -> Unit) { MaterialTheme( colors = wearColorPalette, typography = Typography, // For shapes, we generally recommend using the default Material Wear shapes which are // optimized for round and non-round devices. - content = content + content = content, ) } diff --git a/Jetcaster/wear/src/main/java/com/example/jetcaster/ui/components/MediaContent.kt b/Jetcaster/wear/src/main/java/com/example/jetcaster/ui/components/MediaContent.kt index fe8b33c1d..cf5e6aa4a 100644 --- a/Jetcaster/wear/src/main/java/com/example/jetcaster/ui/components/MediaContent.kt +++ b/Jetcaster/wear/src/main/java/com/example/jetcaster/ui/components/MediaContent.kt @@ -33,7 +33,7 @@ fun MediaContent( episode: PlayerEpisode, episodeArtworkPlaceholder: Painter?, onItemClick: (PlayerEpisode) -> Unit, - modifier: Modifier = Modifier + modifier: Modifier = Modifier, ) { val mediaTitle = episode.title val duration = episode.duration @@ -45,7 +45,7 @@ fun MediaContent( stringResource( R.string.episode_date_duration, MediumDateFormatter.format(episode.published), - duration.toMinutes().toInt() + duration.toMinutes().toInt(), ) } // Otherwise we just use the date @@ -59,7 +59,7 @@ fun MediaContent( icon = CoilPaintable(episode.podcastImageUrl, episodeArtworkPlaceholder), largeIcon = true, colors = ChipDefaults.secondaryChipColors(), - modifier = modifier + modifier = modifier, ) } diff --git a/Jetcaster/wear/src/main/java/com/example/jetcaster/ui/components/SettingsButtons.kt b/Jetcaster/wear/src/main/java/com/example/jetcaster/ui/components/SettingsButtons.kt index 91f32debc..86684701c 100644 --- a/Jetcaster/wear/src/main/java/com/example/jetcaster/ui/components/SettingsButtons.kt +++ b/Jetcaster/wear/src/main/java/com/example/jetcaster/ui/components/SettingsButtons.kt @@ -55,7 +55,7 @@ fun SettingsButtons( currentPlayerSpeed = playerUiState.episodePlayerState .playbackSpeed.toMillis().toFloat() / 1000, onPlaybackSpeedChange = onPlaybackSpeedChange, - enabled = enabled + enabled = enabled, ) SettingsButtonsDefaults.BrandIcon( @@ -66,7 +66,7 @@ fun SettingsButtons( SetVolumeButton( onVolumeClick = onVolumeClick, volumeUiState = volumeUiState, - enabled = enabled + enabled = enabled, ) } } @@ -86,7 +86,9 @@ fun PlaybackSpeedButton( when (currentPlayerSpeed) { 1f -> ImageVector.vectorResource(R.drawable.speed_1x) 1.5f -> ImageVector.vectorResource(R.drawable.speed_15x) - else -> { ImageVector.vectorResource(R.drawable.speed_2x) } + else -> { + ImageVector.vectorResource(R.drawable.speed_2x) + } }, iconRtlMode = IconRtlMode.Mirrored, contentDescription = stringResource(R.string.change_playback_speed_content_description), diff --git a/Jetcaster/wear/src/main/java/com/example/jetcaster/ui/episode/EpisodeScreen.kt b/Jetcaster/wear/src/main/java/com/example/jetcaster/ui/episode/EpisodeScreen.kt index d03b8fb2d..02b68ff9d 100644 --- a/Jetcaster/wear/src/main/java/com/example/jetcaster/ui/episode/EpisodeScreen.kt +++ b/Jetcaster/wear/src/main/java/com/example/jetcaster/ui/episode/EpisodeScreen.kt @@ -63,7 +63,7 @@ fun EpisodeScreen( onPlayButtonClick: () -> Unit, onDismiss: () -> Unit, modifier: Modifier = Modifier, - episodeViewModel: EpisodeViewModel = hiltViewModel() + episodeViewModel: EpisodeViewModel = hiltViewModel(), ) { val uiState by episodeViewModel.uiState.collectAsStateWithLifecycle() @@ -89,12 +89,12 @@ fun EpisodeScreen( val columnState = rememberResponsiveColumnState( contentPadding = padding( first = ScalingLazyColumnDefaults.ItemType.Text, - last = ScalingLazyColumnDefaults.ItemType.Chip - ) + last = ScalingLazyColumnDefaults.ItemType.Chip, + ), ) ScreenScaffold( scrollState = columnState, - modifier = modifier + modifier = modifier, ) { when (uiState) { is EpisodeScreenState.Loaded -> { @@ -103,12 +103,12 @@ fun EpisodeScreen( EntityScreen( headerContent = { ResponsiveListHeader( - contentPadding = ListHeaderDefaults.firstItemPadding() + contentPadding = ListHeaderDefaults.firstItemPadding(), ) { Text( text = title, maxLines = 1, - overflow = TextOverflow.Ellipsis + overflow = TextOverflow.Ellipsis, ) } }, @@ -117,12 +117,12 @@ fun EpisodeScreen( episode = uiState.episode, onPlayButtonClick = onPlayButtonClick, onPlayEpisode = onPlayEpisode, - onAddToQueue = onAddToQueue + onAddToQueue = onAddToQueue, ) }, content = { episodeInfoContent(episode = uiState.episode) - } + }, ) } @@ -131,7 +131,7 @@ fun EpisodeScreen( AlertDialog( showDialog = true, onDismiss = { onDismiss }, - message = stringResource(R.string.episode_info_not_available) + message = stringResource(R.string.episode_info_not_available), ) } EpisodeScreenState.Loading -> { @@ -148,7 +148,7 @@ fun LoadedButtonsContent( onPlayButtonClick: () -> Unit, onPlayEpisode: (PlayerEpisode) -> Unit, onAddToQueue: (PlayerEpisode) -> Unit, - enabled: Boolean = true + enabled: Boolean = true, ) { Row( @@ -187,7 +187,7 @@ fun LoadingScreen() { EntityScreen( headerContent = { ResponsiveListHeader( - contentPadding = ListHeaderDefaults.firstItemPadding() + contentPadding = ListHeaderDefaults.firstItemPadding(), ) { Text(text = stringResource(R.string.loading)) } @@ -199,7 +199,7 @@ fun LoadingScreen() { items(count = 2) { PlaceholderChip(colors = ChipDefaults.secondaryChipColors()) } - } + }, ) } @@ -246,7 +246,7 @@ private fun ScalingLazyListScope.episodeInfoContent(episode: EpisodeToPodcast) { text = author, maxLines = 1, overflow = TextOverflow.Ellipsis, - style = MaterialTheme.typography.body2 + style = MaterialTheme.typography.body2, ) } } @@ -260,7 +260,7 @@ private fun ScalingLazyListScope.episodeInfoContent(episode: EpisodeToPodcast) { stringResource( R.string.episode_date_duration, MediumDateFormatter.format(published), - duration.toMinutes().toInt() + duration.toMinutes().toInt(), ) } // Otherwise we just use the date @@ -270,7 +270,7 @@ private fun ScalingLazyListScope.episodeInfoContent(episode: EpisodeToPodcast) { overflow = TextOverflow.Ellipsis, style = MaterialTheme.typography.body2, modifier = Modifier - .padding(horizontal = 8.dp) + .padding(horizontal = 8.dp), ) } if (summary != null) { @@ -281,7 +281,7 @@ private fun ScalingLazyListScope.episodeInfoContent(episode: EpisodeToPodcast) { text = it, style = MaterialTheme.typography.body2, color = LocalContentColor.current, - modifier = Modifier.listTextPadding() + modifier = Modifier.listTextPadding(), ) } } diff --git a/Jetcaster/wear/src/main/java/com/example/jetcaster/ui/episode/EpisodeViewModel.kt b/Jetcaster/wear/src/main/java/com/example/jetcaster/ui/episode/EpisodeViewModel.kt index 138146291..7a15b622b 100644 --- a/Jetcaster/wear/src/main/java/com/example/jetcaster/ui/episode/EpisodeViewModel.kt +++ b/Jetcaster/wear/src/main/java/com/example/jetcaster/ui/episode/EpisodeViewModel.kt @@ -72,7 +72,7 @@ class EpisodeViewModel @Inject constructor( }.stateIn( viewModelScope, SharingStarted.WhileSubscribed(5_000), - null + null, ) val uiState: StateFlow = @@ -102,9 +102,7 @@ sealed interface EpisodeScreenState { data object Loading : EpisodeScreenState - data class Loaded( - val episode: EpisodeToPodcast - ) : EpisodeScreenState + data class Loaded(val episode: EpisodeToPodcast) : EpisodeScreenState data object Empty : EpisodeScreenState } diff --git a/Jetcaster/wear/src/main/java/com/example/jetcaster/ui/latest_episodes/LatestEpisodeViewModel.kt b/Jetcaster/wear/src/main/java/com/example/jetcaster/ui/latest_episodes/LatestEpisodeViewModel.kt index cc7889170..c0324b281 100644 --- a/Jetcaster/wear/src/main/java/com/example/jetcaster/ui/latest_episodes/LatestEpisodeViewModel.kt +++ b/Jetcaster/wear/src/main/java/com/example/jetcaster/ui/latest_episodes/LatestEpisodeViewModel.kt @@ -41,7 +41,7 @@ class LatestEpisodeViewModel @Inject constructor( LatestEpisodeScreenState.Loaded( episodeToPodcastList.map { it.toPlayerEpisode() - } + }, ) } else { LatestEpisodeScreenState.Empty @@ -67,9 +67,7 @@ sealed interface LatestEpisodeScreenState { data object Loading : LatestEpisodeScreenState - data class Loaded( - val episodeList: List - ) : LatestEpisodeScreenState + data class Loaded(val episodeList: List) : LatestEpisodeScreenState data object Empty : LatestEpisodeScreenState } diff --git a/Jetcaster/wear/src/main/java/com/example/jetcaster/ui/latest_episodes/LatestEpisodesScreen.kt b/Jetcaster/wear/src/main/java/com/example/jetcaster/ui/latest_episodes/LatestEpisodesScreen.kt index 732e0f68c..4cd295250 100644 --- a/Jetcaster/wear/src/main/java/com/example/jetcaster/ui/latest_episodes/LatestEpisodesScreen.kt +++ b/Jetcaster/wear/src/main/java/com/example/jetcaster/ui/latest_episodes/LatestEpisodesScreen.kt @@ -56,7 +56,7 @@ import com.google.android.horologist.media.ui.screens.entity.EntityScreen onPlayButtonClick: () -> Unit, onDismiss: () -> Unit, modifier: Modifier = Modifier, - latestEpisodeViewModel: LatestEpisodeViewModel = hiltViewModel() + latestEpisodeViewModel: LatestEpisodeViewModel = hiltViewModel(), ) { val uiState by latestEpisodeViewModel.uiState.collectAsStateWithLifecycle() LatestEpisodeScreen( @@ -65,7 +65,7 @@ import com.google.android.horologist.media.ui.screens.entity.EntityScreen onPlayButtonClick = onPlayButtonClick, onDismiss = onDismiss, onPlayEpisodes = latestEpisodeViewModel::onPlayEpisodes, - onPlayEpisode = latestEpisodeViewModel::onPlayEpisode + onPlayEpisode = latestEpisodeViewModel::onPlayEpisode, ) } @@ -81,12 +81,12 @@ fun LatestEpisodeScreen( val columnState = rememberResponsiveColumnState( contentPadding = padding( first = ScalingLazyColumnDefaults.ItemType.Text, - last = ScalingLazyColumnDefaults.ItemType.Chip - ) + last = ScalingLazyColumnDefaults.ItemType.Chip, + ), ) ScreenScaffold( scrollState = columnState, - modifier = modifier + modifier = modifier, ) { when (uiState) { is LatestEpisodeScreenState.Loaded -> { @@ -95,7 +95,7 @@ fun LatestEpisodeScreen( onPlayButtonClick = onPlayButtonClick, onPlayEpisode = onPlayEpisode, onPlayEpisodes = onPlayEpisodes, - modifier = modifier + modifier = modifier, ) } @@ -103,13 +103,13 @@ fun LatestEpisodeScreen( AlertDialog( showDialog = true, onDismiss = onDismiss, - message = stringResource(R.string.podcasts_no_episode_podcasts) + message = stringResource(R.string.podcasts_no_episode_podcasts), ) } is LatestEpisodeScreenState.Loading -> { LatestEpisodesScreenLoading( - modifier = modifier + modifier = modifier, ) } } @@ -122,7 +122,7 @@ fun ButtonsContent( episodes: List, onPlayButtonClick: () -> Unit, onPlayEpisodes: (List) -> Unit, - modifier: Modifier = Modifier + modifier: Modifier = Modifier, ) { Chip( label = stringResource(id = R.string.button_play_content_description), @@ -141,15 +141,15 @@ fun LatestEpisodesScreen( onPlayButtonClick: () -> Unit, onPlayEpisode: (PlayerEpisode) -> Unit, onPlayEpisodes: (List) -> Unit, - modifier: Modifier = Modifier + modifier: Modifier = Modifier, ) { EntityScreen( modifier = modifier, headerContent = { ResponsiveListHeader( - contentPadding = ListHeaderDefaults.firstItemPadding() + contentPadding = ListHeaderDefaults.firstItemPadding(), ) { - Text(text = stringResource(id = R.string.latest_episodes),) + Text(text = stringResource(id = R.string.latest_episodes)) } }, content = { @@ -163,7 +163,7 @@ fun LatestEpisodesScreen( onItemClick = { onPlayButtonClick() onPlayEpisode(episodeList[index]) - } + }, ) } }, @@ -171,7 +171,7 @@ fun LatestEpisodesScreen( ButtonsContent( episodes = episodeList, onPlayButtonClick = onPlayButtonClick, - onPlayEpisodes = onPlayEpisodes + onPlayEpisodes = onPlayEpisodes, ) }, ) @@ -179,16 +179,14 @@ fun LatestEpisodesScreen( @OptIn(ExperimentalWearMaterialApi::class) @Composable -fun LatestEpisodesScreenLoading( - modifier: Modifier = Modifier -) { +fun LatestEpisodesScreenLoading(modifier: Modifier = Modifier) { EntityScreen( modifier = modifier, headerContent = { ResponsiveListHeader( - contentPadding = ListHeaderDefaults.firstItemPadding() + contentPadding = ListHeaderDefaults.firstItemPadding(), ) { - Text(text = stringResource(id = R.string.latest_episodes),) + Text(text = stringResource(id = R.string.latest_episodes)) } }, content = { @@ -211,19 +209,19 @@ fun LatestEpisodesScreenLoading( @Composable fun LatestEpisodeScreenLoadedPreview( @PreviewParameter(WearPreviewEpisodes::class) - episode: PlayerEpisode + episode: PlayerEpisode, ) { val columnState = rememberResponsiveColumnState( contentPadding = padding( first = ScalingLazyColumnDefaults.ItemType.Text, - last = ScalingLazyColumnDefaults.ItemType.Chip - ) + last = ScalingLazyColumnDefaults.ItemType.Chip, + ), ) LatestEpisodesScreen( episodeList = listOf(episode), onPlayButtonClick = { }, onPlayEpisode = { }, - onPlayEpisodes = { } + onPlayEpisodes = { }, ) } @@ -234,8 +232,8 @@ fun LatestEpisodeScreenLoadingPreview() { val columnState = rememberResponsiveColumnState( contentPadding = padding( first = ScalingLazyColumnDefaults.ItemType.Text, - last = ScalingLazyColumnDefaults.ItemType.Chip - ) + last = ScalingLazyColumnDefaults.ItemType.Chip, + ), ) LatestEpisodesScreenLoading() } diff --git a/Jetcaster/wear/src/main/java/com/example/jetcaster/ui/library/LibraryScreen.kt b/Jetcaster/wear/src/main/java/com/example/jetcaster/ui/library/LibraryScreen.kt index 3c8092832..8b722a149 100644 --- a/Jetcaster/wear/src/main/java/com/example/jetcaster/ui/library/LibraryScreen.kt +++ b/Jetcaster/wear/src/main/java/com/example/jetcaster/ui/library/LibraryScreen.kt @@ -58,7 +58,7 @@ fun LibraryScreen( onYourPodcastClick: () -> Unit, onUpNextClick: () -> Unit, modifier: Modifier = Modifier, - libraryScreenViewModel: LibraryViewModel = hiltViewModel() + libraryScreenViewModel: LibraryViewModel = hiltViewModel(), ) { val uiState by libraryScreenViewModel.uiState.collectAsState() @@ -72,14 +72,14 @@ fun LibraryScreen( when (val s = uiState) { is LibraryScreenUiState.Loading -> LoadingScreen( - modifier = modifier + modifier = modifier, ) is LibraryScreenUiState.NoSubscribedPodcast -> NoSubscribedPodcastScreen( columnState = columnState, modifier = modifier, topPodcasts = s.topPodcasts, - onTogglePodcastFollowed = libraryScreenViewModel::onTogglePodcastFollowed + onTogglePodcastFollowed = libraryScreenViewModel::onTogglePodcastFollowed, ) is LibraryScreenUiState.Ready -> @@ -89,20 +89,18 @@ fun LibraryScreen( onLatestEpisodeClick = onLatestEpisodeClick, onYourPodcastClick = onYourPodcastClick, onUpNextClick = onUpNextClick, - queue = s.queue + queue = s.queue, ) } } @OptIn(ExperimentalWearMaterialApi::class) @Composable -fun LoadingScreen( - modifier: Modifier, -) { +fun LoadingScreen(modifier: Modifier) { EntityScreen( headerContent = { ResponsiveListHeader( - contentPadding = ListHeaderDefaults.firstItemPadding() + contentPadding = ListHeaderDefaults.firstItemPadding(), ) { Text(text = stringResource(R.string.loading)) } @@ -112,7 +110,7 @@ fun LoadingScreen( items(count = 2) { PlaceholderChip(colors = ChipDefaults.secondaryChipColors()) } - } + }, ) } @@ -122,14 +120,14 @@ fun NoSubscribedPodcastScreen( columnState: ScalingLazyColumnState, modifier: Modifier, topPodcasts: List, - onTogglePodcastFollowed: (uri: String) -> Unit + onTogglePodcastFollowed: (uri: String) -> Unit, ) { ScreenScaffold(scrollState = columnState, modifier = modifier) { ScalingLazyColumn(columnState = columnState) { item { ResponsiveListHeader( modifier = modifier.listTextPadding(), - contentColor = MaterialTheme.colors.onSurface + contentColor = MaterialTheme.colors.onSurface, ) { Text(stringResource(R.string.entity_no_featured_podcasts)) } @@ -151,7 +149,7 @@ fun NoSubscribedPodcastScreen( item { PlaceholderChip( contentDescription = "", - colors = ChipDefaults.secondaryChipColors() + colors = ChipDefaults.secondaryChipColors(), ) } } @@ -185,7 +183,7 @@ fun LibraryScreen( onLatestEpisodeClick: () -> Unit, onYourPodcastClick: () -> Unit, onUpNextClick: () -> Unit, - queue: List + queue: List, ) { ScreenScaffold(scrollState = columnState, modifier = modifier) { ScalingLazyColumn(columnState = columnState) { @@ -199,7 +197,7 @@ fun LibraryScreen( label = stringResource(R.string.latest_episodes), onClick = onLatestEpisodeClick, icon = DrawableResPaintable(R.drawable.new_releases), - colors = ChipDefaults.secondaryChipColors() + colors = ChipDefaults.secondaryChipColors(), ) } item { @@ -207,7 +205,7 @@ fun LibraryScreen( label = stringResource(R.string.podcasts), onClick = onYourPodcastClick, icon = DrawableResPaintable(R.drawable.podcast), - colors = ChipDefaults.secondaryChipColors() + colors = ChipDefaults.secondaryChipColors(), ) } item { @@ -223,7 +221,7 @@ fun LibraryScreen( label = stringResource(R.string.up_next), onClick = onUpNextClick, icon = DrawableResPaintable(R.drawable.up_next), - colors = ChipDefaults.secondaryChipColors() + colors = ChipDefaults.secondaryChipColors(), ) } } diff --git a/Jetcaster/wear/src/main/java/com/example/jetcaster/ui/library/LibraryViewModel.kt b/Jetcaster/wear/src/main/java/com/example/jetcaster/ui/library/LibraryViewModel.kt index 27ae2e85e..ff30dfcac 100644 --- a/Jetcaster/wear/src/main/java/com/example/jetcaster/ui/library/LibraryViewModel.kt +++ b/Jetcaster/wear/src/main/java/com/example/jetcaster/ui/library/LibraryViewModel.kt @@ -64,7 +64,7 @@ class LibraryViewModel @Inject constructor( private val podcastStore: PodcastStore, private val episodePlayer: EpisodePlayer, private val categoryStore: CategoryStore, - private val podcastCategoryFilterUseCase: PodcastCategoryFilterUseCase + private val podcastCategoryFilterUseCase: PodcastCategoryFilterUseCase, ) : ViewModel() { private val defaultCategory = categoryStore.getCategory(CategoryTechnology) @@ -100,7 +100,7 @@ class LibraryViewModel @Inject constructor( topPodcastsFlow, followingPodcastListFlow, latestEpisodeListFlow, - queue + queue, ) { topPodcasts, podcastList, episodeList, queue -> if (podcastList.isEmpty()) { LibraryScreenUiState.NoSubscribedPodcast(topPodcasts.topPodcasts) @@ -110,7 +110,7 @@ class LibraryViewModel @Inject constructor( }.stateIn( viewModelScope, SharingStarted.WhileSubscribed(5_000), - LibraryScreenUiState.Loading + LibraryScreenUiState.Loading, ) init { @@ -132,12 +132,10 @@ class LibraryViewModel @Inject constructor( sealed interface LibraryScreenUiState { data object Loading : LibraryScreenUiState - data class NoSubscribedPodcast( - val topPodcasts: List - ) : LibraryScreenUiState + data class NoSubscribedPodcast(val topPodcasts: List) : LibraryScreenUiState data class Ready( val subscribedPodcastList: List, val latestEpisodeList: List, - val queue: List + val queue: List, ) : LibraryScreenUiState } diff --git a/Jetcaster/wear/src/main/java/com/example/jetcaster/ui/player/PlayerScreen.kt b/Jetcaster/wear/src/main/java/com/example/jetcaster/ui/player/PlayerScreen.kt index 7e6f957db..b24b8645b 100644 --- a/Jetcaster/wear/src/main/java/com/example/jetcaster/ui/player/PlayerScreen.kt +++ b/Jetcaster/wear/src/main/java/com/example/jetcaster/ui/player/PlayerScreen.kt @@ -71,7 +71,7 @@ fun PlayerScreen( volumeUiState = volumeUiState, onVolumeClick = onVolumeClick, onUpdateVolume = { newVolume -> volumeViewModel.setVolume(newVolume) }, - modifier = modifier + modifier = modifier, ) } @@ -93,7 +93,7 @@ private fun PlayerScreen( mediaDisplay = { TextMediaDisplay( title = stringResource(R.string.nothing_playing), - subtitle = "" + subtitle = "", ) }, controlButtons = { @@ -105,7 +105,7 @@ private fun PlayerScreen( onSeekBackButtonClick = playerScreenViewModel::onRewindBy, seekBackButtonEnabled = false, onSeekForwardButtonClick = playerScreenViewModel::onAdvanceBy, - seekForwardButtonEnabled = false + seekForwardButtonEnabled = false, ) }, buttons = { @@ -130,12 +130,12 @@ private fun PlayerScreen( if (episode != null && episode.title.isNotEmpty()) { TextMediaDisplay( title = episode.podcastName, - subtitle = episode.title + subtitle = episode.title, ) } else { TextMediaDisplay( title = stringResource(R.string.nothing_playing), - subtitle = "" + subtitle = "", ) } }, @@ -152,7 +152,7 @@ private fun PlayerScreen( seekForwardButtonEnabled = true, seekBackButtonIncrement = SeekButtonIncrement.Ten, seekForwardButtonIncrement = SeekButtonIncrement.Ten, - trackPositionUiModel = state.playerState.trackPositionUiModel + trackPositionUiModel = state.playerState.trackPositionUiModel, ) }, buttons = { @@ -178,7 +178,7 @@ private fun PlayerScreen( defaultColor = MaterialTheme.colors.primary, modifier = Modifier.fillMaxSize(), ) - } + }, ) } } diff --git a/Jetcaster/wear/src/main/java/com/example/jetcaster/ui/player/PlayerViewModel.kt b/Jetcaster/wear/src/main/java/com/example/jetcaster/ui/player/PlayerViewModel.kt index c640b9202..79f93d8d4 100644 --- a/Jetcaster/wear/src/main/java/com/example/jetcaster/ui/player/PlayerViewModel.kt +++ b/Jetcaster/wear/src/main/java/com/example/jetcaster/ui/player/PlayerViewModel.kt @@ -33,7 +33,7 @@ import kotlinx.coroutines.flow.stateIn @OptIn(ExperimentalHorologistApi::class) data class PlayerUiState( val episodePlayerState: EpisodePlayerState = EpisodePlayerState(), - var trackPositionUiModel: TrackPositionUiModel = TrackPositionUiModel.Actual.ZERO + var trackPositionUiModel: TrackPositionUiModel = TrackPositionUiModel.Actual.ZERO, ) /** @@ -41,9 +41,7 @@ data class PlayerUiState( */ @HiltViewModel @OptIn(ExperimentalHorologistApi::class) -class PlayerViewModel @Inject constructor( - private val episodePlayer: EpisodePlayer, -) : ViewModel() { +class PlayerViewModel @Inject constructor(private val episodePlayer: EpisodePlayer) : ViewModel() { val uiState = episodePlayer.playerState.map { if (it.currentEpisode == null && it.queue.isEmpty()) { @@ -54,24 +52,23 @@ class PlayerViewModel @Inject constructor( }.stateIn( viewModelScope, SharingStarted.WhileSubscribed(5_000), - PlayerScreenUiState.Loading + PlayerScreenUiState.Loading, ) - private fun buildPositionModel(it: EpisodePlayerState) = - if (it.currentEpisode != null) { - TrackPositionUiModel.Actual( - percent = it.timeElapsed.toMillis().toFloat() / - ( - it.currentEpisode?.duration?.toMillis() - ?.toFloat() ?: 0f - ), - duration = it.currentEpisode?.duration?.toKotlinDuration() - ?: Duration.ZERO.toKotlinDuration(), - position = it.timeElapsed.toKotlinDuration() - ) - } else { - TrackPositionUiModel.Actual.ZERO - } + private fun buildPositionModel(it: EpisodePlayerState) = if (it.currentEpisode != null) { + TrackPositionUiModel.Actual( + percent = it.timeElapsed.toMillis().toFloat() / + ( + it.currentEpisode?.duration?.toMillis() + ?.toFloat() ?: 0f + ), + duration = it.currentEpisode?.duration?.toKotlinDuration() + ?: Duration.ZERO.toKotlinDuration(), + position = it.timeElapsed.toKotlinDuration(), + ) + } else { + TrackPositionUiModel.Actual.ZERO + } fun onPlay() { episodePlayer.play() @@ -100,9 +97,7 @@ class PlayerViewModel @Inject constructor( sealed class PlayerScreenUiState { data object Loading : PlayerScreenUiState() - data class Ready( - val playerState: PlayerUiState - ) : PlayerScreenUiState() + data class Ready(val playerState: PlayerUiState) : PlayerScreenUiState() data object Empty : PlayerScreenUiState() } diff --git a/Jetcaster/wear/src/main/java/com/example/jetcaster/ui/podcast/PodcastDetailsScreen.kt b/Jetcaster/wear/src/main/java/com/example/jetcaster/ui/podcast/PodcastDetailsScreen.kt index cd0644d5e..e1b2e03f6 100644 --- a/Jetcaster/wear/src/main/java/com/example/jetcaster/ui/podcast/PodcastDetailsScreen.kt +++ b/Jetcaster/wear/src/main/java/com/example/jetcaster/ui/podcast/PodcastDetailsScreen.kt @@ -60,7 +60,7 @@ import com.google.android.horologist.media.ui.screens.entity.EntityScreen onEpisodeItemClick: (PlayerEpisode) -> Unit, onDismiss: () -> Unit, modifier: Modifier = Modifier, - podcastDetailsViewModel: PodcastDetailsViewModel = hiltViewModel() + podcastDetailsViewModel: PodcastDetailsViewModel = hiltViewModel(), ) { val uiState by podcastDetailsViewModel.uiState.collectAsStateWithLifecycle() @@ -82,24 +82,24 @@ fun PodcastDetailsScreen( modifier: Modifier = Modifier, onEpisodeItemClick: (PlayerEpisode) -> Unit, onPlayEpisode: (List) -> Unit, - onDismiss: () -> Unit + onDismiss: () -> Unit, ) { val columnState = rememberResponsiveColumnState( contentPadding = padding( first = ScalingLazyColumnDefaults.ItemType.Text, - last = ScalingLazyColumnDefaults.ItemType.Chip - ) + last = ScalingLazyColumnDefaults.ItemType.Chip, + ), ) ScreenScaffold( scrollState = columnState, - modifier = modifier + modifier = modifier, ) { when (uiState) { is PodcastDetailsScreenState.Loaded -> { EntityScreen( headerContent = { ResponsiveListHeader( - contentPadding = ListHeaderDefaults.firstItemPadding() + contentPadding = ListHeaderDefaults.firstItemPadding(), ) { Text(text = uiState.podcast.title) } @@ -108,7 +108,7 @@ fun PodcastDetailsScreen( ButtonsContent( episodes = uiState.episodeList, onPlayButtonClick = onPlayButtonClick, - onPlayEpisode = onPlayEpisode + onPlayEpisode = onPlayEpisode, ) }, content = { @@ -119,10 +119,10 @@ fun PodcastDetailsScreen( image = Icons.Default.MusicNote, tintColor = Color.Blue, ), - onEpisodeItemClick + onEpisodeItemClick, ) } - } + }, ) } @@ -130,14 +130,14 @@ fun PodcastDetailsScreen( AlertDialog( showDialog = true, onDismiss = { onDismiss }, - message = stringResource(R.string.podcasts_no_episode_podcasts) + message = stringResource(R.string.podcasts_no_episode_podcasts), ) } PodcastDetailsScreenState.Loading -> { EntityScreen( headerContent = { ResponsiveListHeader( - contentPadding = ListHeaderDefaults.firstItemPadding() + contentPadding = ListHeaderDefaults.firstItemPadding(), ) { Text(text = stringResource(id = R.string.loading)) } @@ -146,14 +146,14 @@ fun PodcastDetailsScreen( ButtonsContent( episodes = emptyList(), onPlayButtonClick = { }, - onPlayEpisode = { } + onPlayEpisode = { }, ) }, content = { items(count = 2) { PlaceholderChip(colors = ChipDefaults.secondaryChipColors()) } - } + }, ) } } @@ -162,11 +162,7 @@ fun PodcastDetailsScreen( @OptIn(ExperimentalHorologistApi::class) @Composable -fun ButtonsContent( - episodes: List, - onPlayButtonClick: () -> Unit, - onPlayEpisode: (List) -> Unit, -) { +fun ButtonsContent(episodes: List, onPlayButtonClick: () -> Unit, onPlayEpisode: (List) -> Unit) { Chip( label = stringResource(id = R.string.button_play_content_description), @@ -184,10 +180,7 @@ sealed class PodcastDetailsScreenState { data object Loading : PodcastDetailsScreenState() - data class Loaded( - val episodeList: List, - val podcast: PodcastInfo, - ) : PodcastDetailsScreenState() + data class Loaded(val episodeList: List, val podcast: PodcastInfo) : PodcastDetailsScreenState() data object Empty : PodcastDetailsScreenState() } @@ -197,17 +190,17 @@ sealed class PodcastDetailsScreenState { @Composable fun PodcastDetailsScreenLoadedPreview( @PreviewParameter(WearPreviewEpisodes::class) - episode: PlayerEpisode + episode: PlayerEpisode, ) { PodcastDetailsScreen( uiState = PodcastDetailsScreenState.Loaded( episodeList = listOf(episode), - podcast = PreviewPodcastEpisodes.first().podcast + podcast = PreviewPodcastEpisodes.first().podcast, ), onPlayButtonClick = { }, onEpisodeItemClick = {}, onPlayEpisode = {}, - onDismiss = {} + onDismiss = {}, ) } @@ -216,13 +209,13 @@ fun PodcastDetailsScreenLoadedPreview( @Composable fun PodcastDetailsScreenLoadingPreview( @PreviewParameter(WearPreviewEpisodes::class) - episode: PlayerEpisode + episode: PlayerEpisode, ) { PodcastDetailsScreen( uiState = PodcastDetailsScreenState.Loading, onPlayButtonClick = { }, onEpisodeItemClick = {}, onPlayEpisode = {}, - onDismiss = {} + onDismiss = {}, ) } diff --git a/Jetcaster/wear/src/main/java/com/example/jetcaster/ui/podcast/PodcastDetailsViewModel.kt b/Jetcaster/wear/src/main/java/com/example/jetcaster/ui/podcast/PodcastDetailsViewModel.kt index b8c778044..8ef4f321d 100644 --- a/Jetcaster/wear/src/main/java/com/example/jetcaster/ui/podcast/PodcastDetailsViewModel.kt +++ b/Jetcaster/wear/src/main/java/com/example/jetcaster/ui/podcast/PodcastDetailsViewModel.kt @@ -61,7 +61,7 @@ class PodcastDetailsViewModel @Inject constructor( savedStateHandle: SavedStateHandle, episodeStore: EpisodeStore, private val episodePlayer: EpisodePlayer, - podcastStore: PodcastStore + podcastStore: PodcastStore, ) : ViewModel() { private val podcastUri: String = @@ -76,7 +76,7 @@ class PodcastDetailsViewModel @Inject constructor( }.stateIn( viewModelScope, SharingStarted.WhileSubscribed(5_000), - null + null, ) private val episodeListFlow = podcastFlow.flatMapLatest { @@ -92,7 +92,7 @@ class PodcastDetailsViewModel @Inject constructor( val uiState: StateFlow = combine( podcastFlow, - episodeListFlow + episodeListFlow, ) { podcast, episodes -> if (podcast != null) { PodcastDetailsScreenState.Loaded( diff --git a/Jetcaster/wear/src/main/java/com/example/jetcaster/ui/podcasts/PodcastsScreen.kt b/Jetcaster/wear/src/main/java/com/example/jetcaster/ui/podcasts/PodcastsScreen.kt index 55d941d6a..2c4a79dab 100644 --- a/Jetcaster/wear/src/main/java/com/example/jetcaster/ui/podcasts/PodcastsScreen.kt +++ b/Jetcaster/wear/src/main/java/com/example/jetcaster/ui/podcasts/PodcastsScreen.kt @@ -75,7 +75,7 @@ fun PodcastsScreen( PodcastsScreen( podcastsScreenState = modifiedState, onPodcastsItemClick = onPodcastsItemClick, - onDismiss = onDismiss + onDismiss = onDismiss, ) } @@ -91,12 +91,12 @@ fun PodcastsScreen( val columnState = rememberResponsiveColumnState() ScreenScaffold( scrollState = columnState, - modifier = modifier + modifier = modifier, ) { when (podcastsScreenState) { is PodcastsScreenState.Loaded -> PodcastScreenLoaded( podcastList = podcastsScreenState.podcastList, - onPodcastsItemClick = onPodcastsItemClick + onPodcastsItemClick = onPodcastsItemClick, ) PodcastsScreenState.Empty -> PodcastScreenEmpty(onDismiss) @@ -107,85 +107,73 @@ fun PodcastsScreen( } @Composable -fun PodcastScreenLoaded( - podcastList: List, - onPodcastsItemClick: (PodcastInfo) -> Unit, - modifier: Modifier = Modifier -) { +fun PodcastScreenLoaded(podcastList: List, onPodcastsItemClick: (PodcastInfo) -> Unit, modifier: Modifier = Modifier) { EntityScreen( modifier = modifier, headerContent = { ResponsiveListHeader( - contentPadding = ListHeaderDefaults.firstItemPadding() + contentPadding = ListHeaderDefaults.firstItemPadding(), ) { Text(text = stringResource(id = R.string.podcasts)) } }, content = { - items(count = podcastList.size) { - index -> + items(count = podcastList.size) { index -> MediaContent( podcast = podcastList[index], downloadItemArtworkPlaceholder = rememberVectorPainter( image = Icons.Default.MusicNote, tintColor = Color.Blue, ), - onPodcastsItemClick = onPodcastsItemClick + onPodcastsItemClick = onPodcastsItemClick, ) } - } + }, ) } @Composable -fun PodcastScreenEmpty( - onDismiss: () -> Unit, - modifier: Modifier = Modifier -) { +fun PodcastScreenEmpty(onDismiss: () -> Unit, modifier: Modifier = Modifier) { AlertDialog( showDialog = true, message = stringResource(R.string.podcasts_no_podcasts), onDismiss = onDismiss, - modifier = modifier + modifier = modifier, ) } @OptIn(ExperimentalWearMaterialApi::class) @Composable -fun PodcastScreenLoading( - modifier: Modifier = Modifier -) { +fun PodcastScreenLoading(modifier: Modifier = Modifier) { EntityScreen( modifier = modifier, headerContent = { DefaultEntityScreenHeader( - title = stringResource(R.string.podcasts) + title = stringResource(R.string.podcasts), ) }, content = { items(count = 2) { PlaceholderChip(colors = ChipDefaults.secondaryChipColors()) } - } + }, ) } @WearPreviewDevices @WearPreviewFontScales @Composable -fun PodcastScreenLoadedPreview( - @PreviewParameter(WearPreviewPodcasts::class) podcasts: PodcastInfo -) { +fun PodcastScreenLoadedPreview(@PreviewParameter(WearPreviewPodcasts::class) podcasts: PodcastInfo) { val columnState = rememberResponsiveColumnState( contentPadding = ScalingLazyColumnDefaults.padding( first = ScalingLazyColumnDefaults.ItemType.Text, - last = ScalingLazyColumnDefaults.ItemType.Chip - ) + last = ScalingLazyColumnDefaults.ItemType.Chip, + ), ) PodcastScreenLoaded( podcastList = listOf(podcasts), - onPodcastsItemClick = {} + onPodcastsItemClick = {}, ) } @@ -196,8 +184,8 @@ fun PodcastScreenLoadingPreview() { val columnState = rememberResponsiveColumnState( contentPadding = ScalingLazyColumnDefaults.padding( first = ScalingLazyColumnDefaults.ItemType.Text, - last = ScalingLazyColumnDefaults.ItemType.Chip - ) + last = ScalingLazyColumnDefaults.ItemType.Chip, + ), ) PodcastScreenLoading() } @@ -210,11 +198,7 @@ fun PodcastScreenEmptyPreview() { } @Composable -fun MediaContent( - podcast: PodcastInfo, - downloadItemArtworkPlaceholder: Painter?, - onPodcastsItemClick: (PodcastInfo) -> Unit -) { +fun MediaContent(podcast: PodcastInfo, downloadItemArtworkPlaceholder: Painter?, onPodcastsItemClick: (PodcastInfo) -> Unit) { val mediaTitle = podcast.title val secondaryLabel = podcast.author diff --git a/Jetcaster/wear/src/main/java/com/example/jetcaster/ui/podcasts/PodcastsViewModel.kt b/Jetcaster/wear/src/main/java/com/example/jetcaster/ui/podcasts/PodcastsViewModel.kt index 65d7f0666..667eccf7a 100644 --- a/Jetcaster/wear/src/main/java/com/example/jetcaster/ui/podcasts/PodcastsViewModel.kt +++ b/Jetcaster/wear/src/main/java/com/example/jetcaster/ui/podcasts/PodcastsViewModel.kt @@ -32,9 +32,7 @@ import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.stateIn @HiltViewModel -class PodcastsViewModel @Inject constructor( - podcastStore: PodcastStore, -) : ViewModel() { +class PodcastsViewModel @Inject constructor(podcastStore: PodcastStore) : ViewModel() { val uiState: StateFlow = podcastStore.followedPodcastsSortedByLastEpisode(limit = 10).map { @@ -57,10 +55,7 @@ object PodcastMapper { /** * Maps from [Podcast]. */ - fun map( - podcastWithExtraInfo: PodcastWithExtraInfo, - ): PodcastInfo = - podcastWithExtraInfo.asExternalModel() + fun map(podcastWithExtraInfo: PodcastWithExtraInfo): PodcastInfo = podcastWithExtraInfo.asExternalModel() } @ExperimentalHorologistApi @@ -68,9 +63,7 @@ sealed interface PodcastsScreenState { data object Loading : PodcastsScreenState - data class Loaded( - val podcastList: List, - ) : PodcastsScreenState + data class Loaded(val podcastList: List) : PodcastsScreenState data object Empty : PodcastsScreenState } diff --git a/Jetcaster/wear/src/main/java/com/example/jetcaster/ui/queue/QueueScreen.kt b/Jetcaster/wear/src/main/java/com/example/jetcaster/ui/queue/QueueScreen.kt index f06d09c88..2a69c4be7 100644 --- a/Jetcaster/wear/src/main/java/com/example/jetcaster/ui/queue/QueueScreen.kt +++ b/Jetcaster/wear/src/main/java/com/example/jetcaster/ui/queue/QueueScreen.kt @@ -63,7 +63,7 @@ import com.google.android.horologist.media.ui.screens.entity.EntityScreen onEpisodeItemClick: (PlayerEpisode) -> Unit, onDismiss: () -> Unit, modifier: Modifier = Modifier, - queueViewModel: QueueViewModel = hiltViewModel() + queueViewModel: QueueViewModel = hiltViewModel(), ) { val uiState by queueViewModel.uiState.collectAsStateWithLifecycle() @@ -74,7 +74,7 @@ import com.google.android.horologist.media.ui.screens.entity.EntityScreen modifier = modifier, onEpisodeItemClick = onEpisodeItemClick, onDeleteQueueEpisodes = queueViewModel::onDeleteQueueEpisodes, - onDismiss = onDismiss + onDismiss = onDismiss, ) } @@ -86,17 +86,17 @@ fun QueueScreen( modifier: Modifier = Modifier, onEpisodeItemClick: (PlayerEpisode) -> Unit, onDeleteQueueEpisodes: () -> Unit, - onDismiss: () -> Unit + onDismiss: () -> Unit, ) { val columnState = rememberResponsiveColumnState( contentPadding = padding( first = ScalingLazyColumnDefaults.ItemType.Text, - last = ScalingLazyColumnDefaults.ItemType.Chip - ) + last = ScalingLazyColumnDefaults.ItemType.Chip, + ), ) ScreenScaffold( scrollState = columnState, - modifier = modifier + modifier = modifier, ) { when (uiState) { is QueueScreenState.Loaded -> QueueScreenLoaded( @@ -104,7 +104,7 @@ fun QueueScreen( onPlayButtonClick = onPlayButtonClick, onPlayEpisodes = onPlayEpisodes, onDeleteQueueEpisodes = onDeleteQueueEpisodes, - onEpisodeItemClick = onEpisodeItemClick + onEpisodeItemClick = onEpisodeItemClick, ) QueueScreenState.Loading -> QueueScreenLoading() QueueScreenState.Empty -> QueueScreenEmpty(onDismiss) @@ -119,13 +119,13 @@ fun QueueScreenLoaded( onPlayEpisodes: (List) -> Unit, onDeleteQueueEpisodes: () -> Unit, onEpisodeItemClick: (PlayerEpisode) -> Unit, - modifier: Modifier = Modifier + modifier: Modifier = Modifier, ) { EntityScreen( modifier = modifier, headerContent = { ResponsiveListHeader( - contentPadding = ListHeaderDefaults.firstItemPadding() + contentPadding = ListHeaderDefaults.firstItemPadding(), ) { Text(text = stringResource(R.string.queue)) } @@ -135,7 +135,7 @@ fun QueueScreenLoaded( episodes = episodeList, onPlayButtonClick = onPlayButtonClick, onPlayEpisodes = onPlayEpisodes, - onDeleteQueueEpisodes = onDeleteQueueEpisodes + onDeleteQueueEpisodes = onDeleteQueueEpisodes, ) }, content = { @@ -146,23 +146,21 @@ fun QueueScreenLoaded( image = Icons.Default.MusicNote, tintColor = Color.Blue, ), - onItemClick = onEpisodeItemClick + onItemClick = onEpisodeItemClick, ) } - } + }, ) } @OptIn(ExperimentalWearMaterialApi::class) @Composable -fun QueueScreenLoading( - modifier: Modifier = Modifier -) { +fun QueueScreenLoading(modifier: Modifier = Modifier) { EntityScreen( modifier = modifier, headerContent = { DefaultEntityScreenHeader( - title = stringResource(R.string.queue) + title = stringResource(R.string.queue), ) }, buttonsContent = { @@ -171,28 +169,25 @@ fun QueueScreenLoading( onPlayButtonClick = {}, onPlayEpisodes = {}, onDeleteQueueEpisodes = { }, - enabled = false + enabled = false, ) }, content = { items(count = 2) { PlaceholderChip(colors = ChipDefaults.secondaryChipColors()) } - } + }, ) } @Composable -fun QueueScreenEmpty( - onDismiss: () -> Unit, - modifier: Modifier = Modifier -) { +fun QueueScreenEmpty(onDismiss: () -> Unit, modifier: Modifier = Modifier) { AlertDialog( showDialog = true, onDismiss = onDismiss, title = stringResource(R.string.display_nothing_in_queue), message = stringResource(R.string.no_episodes_from_queue), - modifier = modifier + modifier = modifier, ) } @@ -204,7 +199,7 @@ fun ButtonsContent( onPlayEpisodes: (List) -> Unit, onDeleteQueueEpisodes: () -> Unit, enabled: Boolean = true, - modifier: Modifier = Modifier + modifier: Modifier = Modifier, ) { Row( @@ -223,7 +218,7 @@ fun ButtonsContent( }, modifier = Modifier .weight(weight = 0.3F, fill = false), - enabled = enabled + enabled = enabled, ) Button( imageVector = Icons.Outlined.Delete, @@ -232,7 +227,7 @@ fun ButtonsContent( onClick = onDeleteQueueEpisodes, modifier = Modifier .weight(weight = 0.3F, fill = false), - enabled = enabled + enabled = enabled, ) } } @@ -242,20 +237,20 @@ fun ButtonsContent( @Composable fun QueueScreenLoadedPreview( @PreviewParameter(WearPreviewEpisodes::class) - episode: PlayerEpisode + episode: PlayerEpisode, ) { val columnState = rememberResponsiveColumnState( contentPadding = padding( first = ScalingLazyColumnDefaults.ItemType.Text, - last = ScalingLazyColumnDefaults.ItemType.Chip - ) + last = ScalingLazyColumnDefaults.ItemType.Chip, + ), ) QueueScreenLoaded( episodeList = listOf(episode), onPlayButtonClick = { }, onPlayEpisodes = { }, onDeleteQueueEpisodes = { }, - onEpisodeItemClick = { } + onEpisodeItemClick = { }, ) } @@ -266,8 +261,8 @@ fun QueueScreenLoadingPreview() { val columnState = rememberResponsiveColumnState( contentPadding = padding( first = ScalingLazyColumnDefaults.ItemType.Text, - last = ScalingLazyColumnDefaults.ItemType.Chip - ) + last = ScalingLazyColumnDefaults.ItemType.Chip, + ), ) QueueScreenLoading() } diff --git a/Jetcaster/wear/src/main/java/com/example/jetcaster/ui/queue/QueueViewModel.kt b/Jetcaster/wear/src/main/java/com/example/jetcaster/ui/queue/QueueViewModel.kt index bdd38f694..3f99ca3f4 100644 --- a/Jetcaster/wear/src/main/java/com/example/jetcaster/ui/queue/QueueViewModel.kt +++ b/Jetcaster/wear/src/main/java/com/example/jetcaster/ui/queue/QueueViewModel.kt @@ -32,10 +32,7 @@ import kotlinx.coroutines.flow.stateIn * ViewModel that handles the business logic and screen state of the Queue screen. */ @HiltViewModel -class QueueViewModel @Inject constructor( - private val episodePlayer: EpisodePlayer, - -) : ViewModel() { +class QueueViewModel @Inject constructor(private val episodePlayer: EpisodePlayer) : ViewModel() { val uiState: StateFlow = episodePlayer.playerState.map { if (it.queue.isNotEmpty()) { @@ -69,9 +66,7 @@ sealed interface QueueScreenState { data object Loading : QueueScreenState - data class Loaded( - val episodeList: List - ) : QueueScreenState + data class Loaded(val episodeList: List) : QueueScreenState data object Empty : QueueScreenState }