From f8d68d513a783d805c14671fe600794c286d28ea Mon Sep 17 00:00:00 2001 From: Eduarda Barbosa Date: Fri, 27 Dec 2024 10:37:50 -0300 Subject: [PATCH 1/8] detect when feed is private and persist it into database --- .../pocketcasts/models/db/AppDatabaseTest.kt | 1 + .../105.json | 12 +++++++++--- .../shiftyjelly/pocketcasts/models/db/AppDatabase.kt | 5 +++++ .../shiftyjelly/pocketcasts/models/entity/Podcast.kt | 1 + .../pocketcasts/servers/podcast/PodcastResponse.kt | 2 ++ 5 files changed, 18 insertions(+), 3 deletions(-) diff --git a/app/src/androidTest/java/au/com/shiftyjelly/pocketcasts/models/db/AppDatabaseTest.kt b/app/src/androidTest/java/au/com/shiftyjelly/pocketcasts/models/db/AppDatabaseTest.kt index 39b302644e6..70483ecdc44 100644 --- a/app/src/androidTest/java/au/com/shiftyjelly/pocketcasts/models/db/AppDatabaseTest.kt +++ b/app/src/androidTest/java/au/com/shiftyjelly/pocketcasts/models/db/AppDatabaseTest.kt @@ -174,6 +174,7 @@ class AppDatabaseTest { // 102 to 103 added via auto migration AppDatabase.MIGRATION_103_104, AppDatabase.MIGRATION_104_105, + AppDatabase.MIGRATION_105_106, ) .build() // close the database and release any stream resources when the test finishes diff --git a/modules/services/model/schemas/au.com.shiftyjelly.pocketcasts.models.db.AppDatabase/105.json b/modules/services/model/schemas/au.com.shiftyjelly.pocketcasts.models.db.AppDatabase/105.json index ee7089154e2..462aa7875a9 100644 --- a/modules/services/model/schemas/au.com.shiftyjelly.pocketcasts.models.db.AppDatabase/105.json +++ b/modules/services/model/schemas/au.com.shiftyjelly.pocketcasts.models.db.AppDatabase/105.json @@ -2,7 +2,7 @@ "formatVersion": 1, "database": { "version": 105, - "identityHash": "5081aaece3489e97782187c7cb1ab7e4", + "identityHash": "10131517fd9b7d158e054ece972adb8f", "entities": [ { "tableName": "bump_stats", @@ -696,7 +696,7 @@ }, { "tableName": "podcasts", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`uuid` TEXT NOT NULL, `added_date` INTEGER, `thumbnail_url` TEXT, `title` TEXT NOT NULL, `podcast_url` TEXT, `podcast_description` TEXT NOT NULL, `podcast_html_description` TEXT NOT NULL, `podcast_category` TEXT NOT NULL, `podcast_language` TEXT NOT NULL, `media_type` TEXT, `latest_episode_uuid` TEXT, `author` TEXT NOT NULL, `sort_order` INTEGER NOT NULL, `episodes_sort_order` INTEGER NOT NULL, `episodes_sort_order_modified` INTEGER, `latest_episode_date` INTEGER, `episodes_to_keep` INTEGER NOT NULL, `override_global_settings` INTEGER NOT NULL, `override_global_effects` INTEGER NOT NULL, `override_global_effects_modified` INTEGER, `start_from` INTEGER NOT NULL, `start_from_modified` INTEGER, `playback_speed` REAL NOT NULL, `playback_speed_modified` INTEGER, `volume_boosted` INTEGER NOT NULL, `volume_boosted_modified` INTEGER, `is_folder` INTEGER NOT NULL, `subscribed` INTEGER NOT NULL, `show_notifications` INTEGER NOT NULL, `show_notifications_modified` INTEGER, `auto_download_status` INTEGER NOT NULL, `auto_add_to_up_next` INTEGER NOT NULL, `auto_add_to_up_next_modified` INTEGER, `most_popular_color` INTEGER NOT NULL, `primary_color` INTEGER NOT NULL, `secondary_color` INTEGER NOT NULL, `light_overlay_color` INTEGER NOT NULL, `fab_for_light_bg` INTEGER NOT NULL, `link_for_dark_bg` INTEGER NOT NULL, `link_for_light_bg` INTEGER NOT NULL, `color_version` INTEGER NOT NULL, `color_last_downloaded` INTEGER NOT NULL, `sync_status` INTEGER NOT NULL, `exclude_from_auto_archive` INTEGER NOT NULL, `override_global_archive` INTEGER NOT NULL, `override_global_archive_modified` INTEGER, `auto_archive_played_after` INTEGER NOT NULL, `auto_archive_played_after_modified` INTEGER, `auto_archive_inactive_after` INTEGER NOT NULL, `auto_archive_inactive_after_modified` INTEGER, `auto_archive_episode_limit` INTEGER NOT NULL, `auto_archive_episode_limit_modified` INTEGER, `estimated_next_episode` INTEGER, `episode_frequency` TEXT, `grouping` INTEGER NOT NULL, `grouping_modified` INTEGER, `skip_last` INTEGER NOT NULL, `skip_last_modified` INTEGER, `show_archived` INTEGER NOT NULL, `show_archived_modified` INTEGER, `trim_silence_level` INTEGER NOT NULL, `trim_silence_level_modified` INTEGER, `refresh_available` INTEGER NOT NULL, `folder_uuid` TEXT, `licensing` INTEGER NOT NULL, `isPaid` INTEGER NOT NULL, `bundleuuid` TEXT, `bundlebundleUrl` TEXT, `bundlepaymentUrl` TEXT, `bundledescription` TEXT, `bundlepodcastUuid` TEXT, `bundlepaidType` TEXT, PRIMARY KEY(`uuid`))", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`uuid` TEXT NOT NULL, `added_date` INTEGER, `thumbnail_url` TEXT, `title` TEXT NOT NULL, `podcast_url` TEXT, `podcast_description` TEXT NOT NULL, `podcast_html_description` TEXT NOT NULL, `podcast_category` TEXT NOT NULL, `podcast_language` TEXT NOT NULL, `media_type` TEXT, `latest_episode_uuid` TEXT, `author` TEXT NOT NULL, `sort_order` INTEGER NOT NULL, `episodes_sort_order` INTEGER NOT NULL, `episodes_sort_order_modified` INTEGER, `latest_episode_date` INTEGER, `episodes_to_keep` INTEGER NOT NULL, `override_global_settings` INTEGER NOT NULL, `override_global_effects` INTEGER NOT NULL, `override_global_effects_modified` INTEGER, `start_from` INTEGER NOT NULL, `start_from_modified` INTEGER, `playback_speed` REAL NOT NULL, `playback_speed_modified` INTEGER, `volume_boosted` INTEGER NOT NULL, `volume_boosted_modified` INTEGER, `is_folder` INTEGER NOT NULL, `subscribed` INTEGER NOT NULL, `show_notifications` INTEGER NOT NULL, `show_notifications_modified` INTEGER, `auto_download_status` INTEGER NOT NULL, `auto_add_to_up_next` INTEGER NOT NULL, `auto_add_to_up_next_modified` INTEGER, `most_popular_color` INTEGER NOT NULL, `primary_color` INTEGER NOT NULL, `secondary_color` INTEGER NOT NULL, `light_overlay_color` INTEGER NOT NULL, `fab_for_light_bg` INTEGER NOT NULL, `link_for_dark_bg` INTEGER NOT NULL, `link_for_light_bg` INTEGER NOT NULL, `color_version` INTEGER NOT NULL, `color_last_downloaded` INTEGER NOT NULL, `sync_status` INTEGER NOT NULL, `exclude_from_auto_archive` INTEGER NOT NULL, `override_global_archive` INTEGER NOT NULL, `override_global_archive_modified` INTEGER, `auto_archive_played_after` INTEGER NOT NULL, `auto_archive_played_after_modified` INTEGER, `auto_archive_inactive_after` INTEGER NOT NULL, `auto_archive_inactive_after_modified` INTEGER, `auto_archive_episode_limit` INTEGER NOT NULL, `auto_archive_episode_limit_modified` INTEGER, `estimated_next_episode` INTEGER, `episode_frequency` TEXT, `grouping` INTEGER NOT NULL, `grouping_modified` INTEGER, `skip_last` INTEGER NOT NULL, `skip_last_modified` INTEGER, `show_archived` INTEGER NOT NULL, `show_archived_modified` INTEGER, `trim_silence_level` INTEGER NOT NULL, `trim_silence_level_modified` INTEGER, `refresh_available` INTEGER NOT NULL, `folder_uuid` TEXT, `licensing` INTEGER NOT NULL, `isPaid` INTEGER NOT NULL, `is_private` INTEGER NOT NULL, `bundleuuid` TEXT, `bundlebundleUrl` TEXT, `bundlepaymentUrl` TEXT, `bundledescription` TEXT, `bundlepodcastUuid` TEXT, `bundlepaidType` TEXT, PRIMARY KEY(`uuid`))", "fields": [ { "fieldPath": "uuid", @@ -1094,6 +1094,12 @@ "affinity": "INTEGER", "notNull": true }, + { + "fieldPath": "isPrivate", + "columnName": "is_private", + "affinity": "INTEGER", + "notNull": true + }, { "fieldPath": "singleBundle.uuid", "columnName": "bundleuuid", @@ -1859,7 +1865,7 @@ "views": [], "setupQueries": [ "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", - "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '5081aaece3489e97782187c7cb1ab7e4')" + "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '10131517fd9b7d158e054ece972adb8f')" ] } } \ No newline at end of file diff --git a/modules/services/model/src/main/java/au/com/shiftyjelly/pocketcasts/models/db/AppDatabase.kt b/modules/services/model/src/main/java/au/com/shiftyjelly/pocketcasts/models/db/AppDatabase.kt index 63e800c116c..094119ef12e 100644 --- a/modules/services/model/src/main/java/au/com/shiftyjelly/pocketcasts/models/db/AppDatabase.kt +++ b/modules/services/model/src/main/java/au/com/shiftyjelly/pocketcasts/models/db/AppDatabase.kt @@ -876,6 +876,10 @@ abstract class AppDatabase : RoomDatabase() { database.execSQL("ALTER TABLE podcasts ADD COLUMN podcast_html_description TEXT NOT NULL DEFAULT ''") } + val MIGRATION_105_106 = addMigration(105, 106) { database -> + database.execSQL("ALTER TABLE podcasts ADD COLUMN is_private INTEGER NOT NULL DEFAULT 0") + } + fun addMigrations(databaseBuilder: Builder, context: Context) { databaseBuilder.addMigrations( addMigration(1, 2) { }, @@ -1271,6 +1275,7 @@ abstract class AppDatabase : RoomDatabase() { // 102 to 103 added via auto migration MIGRATION_103_104, MIGRATION_104_105, + MIGRATION_105_106, ) } diff --git a/modules/services/model/src/main/java/au/com/shiftyjelly/pocketcasts/models/entity/Podcast.kt b/modules/services/model/src/main/java/au/com/shiftyjelly/pocketcasts/models/entity/Podcast.kt index 5e22669a46d..0d3f2d060e0 100644 --- a/modules/services/model/src/main/java/au/com/shiftyjelly/pocketcasts/models/entity/Podcast.kt +++ b/modules/services/model/src/main/java/au/com/shiftyjelly/pocketcasts/models/entity/Podcast.kt @@ -115,6 +115,7 @@ data class Podcast( @ColumnInfo(name = "folder_uuid") internal var rawFolderUuid: String? = null, @ColumnInfo(name = "licensing") var licensing: Licensing = Licensing.KEEP_EPISODES, @ColumnInfo(name = "isPaid") var isPaid: Boolean = false, + @ColumnInfo(name = "is_private") var isPrivate: Boolean = false, @Embedded(prefix = "bundle") var singleBundle: Bundle? = null, @Ignore val episodes: MutableList = mutableListOf(), ) : Serializable { diff --git a/modules/services/servers/src/main/java/au/com/shiftyjelly/pocketcasts/servers/podcast/PodcastResponse.kt b/modules/services/servers/src/main/java/au/com/shiftyjelly/pocketcasts/servers/podcast/PodcastResponse.kt index 573a01dda69..fa61044906e 100644 --- a/modules/services/servers/src/main/java/au/com/shiftyjelly/pocketcasts/servers/podcast/PodcastResponse.kt +++ b/modules/services/servers/src/main/java/au/com/shiftyjelly/pocketcasts/servers/podcast/PodcastResponse.kt @@ -46,6 +46,7 @@ data class PodcastInfo( @field:Json(name = "category") val category: String?, @field:Json(name = "audio") val audio: Boolean?, @field:Json(name = "episodes") val episodes: List?, + @field:Json(name = "is_private") val isPrivate: Boolean?, ) { fun toPodcast(): Podcast { @@ -63,6 +64,7 @@ data class PodcastInfo( episodes?.mapNotNull { it.toEpisode(uuid) }?.let { episodes -> podcast.episodes.addAll(episodes) } + podcast.isPrivate = isPrivate ?: false return podcast } } From e430e33cd9c0ad71c542de87a58ee3c09639c27e Mon Sep 17 00:00:00 2001 From: Eduarda Barbosa Date: Fri, 27 Dec 2024 10:56:14 -0300 Subject: [PATCH 2/8] do not share private feed when tapping on sharing from podcast view --- .../pocketcasts/podcasts/view/podcast/PodcastFragment.kt | 4 ++++ .../pocketcasts/podcasts/viewmodel/PodcastViewModel.kt | 2 ++ .../shiftyjelly/pocketcasts/utils/featureflag/Feature.kt | 8 ++++++++ 3 files changed, 14 insertions(+) diff --git a/modules/features/podcasts/src/main/java/au/com/shiftyjelly/pocketcasts/podcasts/view/podcast/PodcastFragment.kt b/modules/features/podcasts/src/main/java/au/com/shiftyjelly/pocketcasts/podcasts/view/podcast/PodcastFragment.kt index 40a64731b54..d2dd6db8f9f 100644 --- a/modules/features/podcasts/src/main/java/au/com/shiftyjelly/pocketcasts/podcasts/view/podcast/PodcastFragment.kt +++ b/modules/features/podcasts/src/main/java/au/com/shiftyjelly/pocketcasts/podcasts/view/podcast/PodcastFragment.kt @@ -976,6 +976,10 @@ class PodcastFragment : BaseFragment(), Toolbar.OnMenuItemClickListener { } private fun share() { + if (!viewModel.canSharePodcast() && FeatureFlag.isEnabled(Feature.SHARE_PODCAST_PRIVATE_NOT_AVAILABLE)) { + return + } + val podcast = viewModel.podcast.value ?: return analyticsTracker.track(AnalyticsEvent.PODCAST_SCREEN_SHARE_TAPPED) if (FeatureFlag.isEnabled(Feature.REIMAGINE_SHARING)) { diff --git a/modules/features/podcasts/src/main/java/au/com/shiftyjelly/pocketcasts/podcasts/viewmodel/PodcastViewModel.kt b/modules/features/podcasts/src/main/java/au/com/shiftyjelly/pocketcasts/podcasts/viewmodel/PodcastViewModel.kt index 69217b2a733..8839388778d 100644 --- a/modules/features/podcasts/src/main/java/au/com/shiftyjelly/pocketcasts/podcasts/viewmodel/PodcastViewModel.kt +++ b/modules/features/podcasts/src/main/java/au/com/shiftyjelly/pocketcasts/podcasts/viewmodel/PodcastViewModel.kt @@ -348,6 +348,8 @@ class PodcastViewModel return episodes.find { !it.isArchived && it.isFinished } != null } + fun canSharePodcast(): Boolean = podcast.value?.isPrivate == false + fun archivePlayed() { val podcast = this.podcast.value ?: return launch { diff --git a/modules/services/utils/src/main/java/au/com/shiftyjelly/pocketcasts/utils/featureflag/Feature.kt b/modules/services/utils/src/main/java/au/com/shiftyjelly/pocketcasts/utils/featureflag/Feature.kt index 91730edfb33..24d9943288a 100644 --- a/modules/services/utils/src/main/java/au/com/shiftyjelly/pocketcasts/utils/featureflag/Feature.kt +++ b/modules/services/utils/src/main/java/au/com/shiftyjelly/pocketcasts/utils/featureflag/Feature.kt @@ -187,6 +187,14 @@ enum class Feature( hasFirebaseRemoteFlag = true, hasDevToggle = true, ), + SHARE_PODCAST_PRIVATE_NOT_AVAILABLE( + key = "share_podcast_private_not_available", + title = "Sharing is not available for private podcasts", + defaultValue = BuildConfig.DEBUG, + tier = FeatureTier.Free, + hasFirebaseRemoteFlag = false, + hasDevToggle = true, + ), ; companion object { From b94e6d57da1928233bcbe02ad55c81b187f56a81 Mon Sep 17 00:00:00 2001 From: Eduarda Barbosa Date: Fri, 27 Dec 2024 11:09:11 -0300 Subject: [PATCH 3/8] show snackbar when trying to share private feed --- .../pocketcasts/podcasts/view/podcast/PodcastFragment.kt | 7 ++++++- .../services/localization/src/main/res/values/strings.xml | 1 + 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/modules/features/podcasts/src/main/java/au/com/shiftyjelly/pocketcasts/podcasts/view/podcast/PodcastFragment.kt b/modules/features/podcasts/src/main/java/au/com/shiftyjelly/pocketcasts/podcasts/view/podcast/PodcastFragment.kt index d2dd6db8f9f..d85da19ca2c 100644 --- a/modules/features/podcasts/src/main/java/au/com/shiftyjelly/pocketcasts/podcasts/view/podcast/PodcastFragment.kt +++ b/modules/features/podcasts/src/main/java/au/com/shiftyjelly/pocketcasts/podcasts/view/podcast/PodcastFragment.kt @@ -84,6 +84,7 @@ import au.com.shiftyjelly.pocketcasts.views.helper.UiUtil import au.com.shiftyjelly.pocketcasts.views.multiselect.MultiSelectBookmarksHelper.NavigationState import au.com.shiftyjelly.pocketcasts.views.multiselect.MultiSelectHelper import au.com.shiftyjelly.pocketcasts.views.multiselect.MultiSelectToolbar +import com.google.android.material.snackbar.Snackbar import dagger.hilt.android.AndroidEntryPoint import javax.inject.Inject import kotlin.time.Duration.Companion.seconds @@ -976,12 +977,16 @@ class PodcastFragment : BaseFragment(), Toolbar.OnMenuItemClickListener { } private fun share() { + analyticsTracker.track(AnalyticsEvent.PODCAST_SCREEN_SHARE_TAPPED) + if (!viewModel.canSharePodcast() && FeatureFlag.isEnabled(Feature.SHARE_PODCAST_PRIVATE_NOT_AVAILABLE)) { + (activity as? FragmentHostListener)?.snackBarView()?.let { snackBarView -> + Snackbar.make(snackBarView, getString(LR.string.sharing_is_not_available_for_private_podcasts), Snackbar.LENGTH_LONG).show() + } return } val podcast = viewModel.podcast.value ?: return - analyticsTracker.track(AnalyticsEvent.PODCAST_SCREEN_SHARE_TAPPED) if (FeatureFlag.isEnabled(Feature.REIMAGINE_SHARING)) { SharePodcastFragment .newInstance(podcast, SourceView.PODCAST_SCREEN) diff --git a/modules/services/localization/src/main/res/values/strings.xml b/modules/services/localization/src/main/res/values/strings.xml index 9910f9bb72b..95b3f0ba8b8 100644 --- a/modules/services/localization/src/main/res/values/strings.xml +++ b/modules/services/localization/src/main/res/values/strings.xml @@ -130,6 +130,7 @@ Error Something went wrong. Please try again later. Are you sure? + Sharing is not available for private podcasts Back Retry Queue for later From 6bcf48579771a65794009b103ff8ac90ba79b42f Mon Sep 17 00:00:00 2001 From: Eduarda Barbosa Date: Fri, 27 Dec 2024 11:50:35 -0300 Subject: [PATCH 4/8] check if can share podcast from now playing view --- .../pocketcasts/player/view/PlayerHeaderFragment.kt | 1 + .../pocketcasts/player/view/shelf/PlayerShelf.kt | 9 ++++++++- .../player/view/shelf/ShelfBottomSheetPage.kt | 9 ++++++++- .../pocketcasts/player/viewmodel/ShelfSharedViewModel.kt | 8 ++++++++ 4 files changed, 25 insertions(+), 2 deletions(-) diff --git a/modules/features/player/src/main/java/au/com/shiftyjelly/pocketcasts/player/view/PlayerHeaderFragment.kt b/modules/features/player/src/main/java/au/com/shiftyjelly/pocketcasts/player/view/PlayerHeaderFragment.kt index fe9622d7205..48c15d3b599 100644 --- a/modules/features/player/src/main/java/au/com/shiftyjelly/pocketcasts/player/view/PlayerHeaderFragment.kt +++ b/modules/features/player/src/main/java/au/com/shiftyjelly/pocketcasts/player/view/PlayerHeaderFragment.kt @@ -274,6 +274,7 @@ class PlayerHeaderFragment : BaseFragment(), PlayerClickListener { SnackbarMessage.EpisodeDownloadStarted -> LR.string.episode_queued_for_download SnackbarMessage.EpisodeRemoved -> LR.string.episode_was_removed SnackbarMessage.TranscriptNotAvailable -> LR.string.transcript_error_not_available + SnackbarMessage.ShareNotAvailable -> LR.string.sharing_is_not_available_for_private_podcasts } showSnackBar(text = getString(text)) } diff --git a/modules/features/player/src/main/java/au/com/shiftyjelly/pocketcasts/player/view/shelf/PlayerShelf.kt b/modules/features/player/src/main/java/au/com/shiftyjelly/pocketcasts/player/view/shelf/PlayerShelf.kt index 88d0c6ae896..b89e5379a20 100644 --- a/modules/features/player/src/main/java/au/com/shiftyjelly/pocketcasts/player/view/shelf/PlayerShelf.kt +++ b/modules/features/player/src/main/java/au/com/shiftyjelly/pocketcasts/player/view/shelf/PlayerShelf.kt @@ -43,6 +43,8 @@ import au.com.shiftyjelly.pocketcasts.settings.onboarding.OnboardingUpgradeSourc import au.com.shiftyjelly.pocketcasts.ui.helper.ColorUtils import au.com.shiftyjelly.pocketcasts.ui.theme.Theme import au.com.shiftyjelly.pocketcasts.ui.theme.ThemeColor +import au.com.shiftyjelly.pocketcasts.utils.featureflag.Feature +import au.com.shiftyjelly.pocketcasts.utils.featureflag.FeatureFlag import au.com.shiftyjelly.pocketcasts.views.extensions.updateColor import com.airbnb.lottie.LottieProperty import com.airbnb.lottie.SimpleColorFilter @@ -112,7 +114,12 @@ fun PlayerShelf( onShareClick = { val podcast = playerViewModel.podcast ?: return@PlayerShelfContent val episode = playerViewModel.episode as? PodcastEpisode ?: return@PlayerShelfContent - shelfSharedViewModel.onShareClick(podcast, episode, ShelfItemSource.Shelf) + + if (podcast.isPrivate && FeatureFlag.isEnabled(Feature.SHARE_PODCAST_PRIVATE_NOT_AVAILABLE)) { + shelfSharedViewModel.onShareNotAvailable(ShelfItemSource.Shelf) + } else { + shelfSharedViewModel.onShareClick(podcast, episode, ShelfItemSource.Shelf) + } }, onShowPodcast = { shelfSharedViewModel.onShowPodcastOrCloudFiles(playerViewModel.podcast, ShelfItemSource.Shelf) diff --git a/modules/features/player/src/main/java/au/com/shiftyjelly/pocketcasts/player/view/shelf/ShelfBottomSheetPage.kt b/modules/features/player/src/main/java/au/com/shiftyjelly/pocketcasts/player/view/shelf/ShelfBottomSheetPage.kt index fc40a015ef3..bcb97fb8ae3 100644 --- a/modules/features/player/src/main/java/au/com/shiftyjelly/pocketcasts/player/view/shelf/ShelfBottomSheetPage.kt +++ b/modules/features/player/src/main/java/au/com/shiftyjelly/pocketcasts/player/view/shelf/ShelfBottomSheetPage.kt @@ -40,6 +40,8 @@ import au.com.shiftyjelly.pocketcasts.player.viewmodel.ShelfViewModel import au.com.shiftyjelly.pocketcasts.preferences.model.ShelfItem import au.com.shiftyjelly.pocketcasts.settings.onboarding.OnboardingUpgradeSource import au.com.shiftyjelly.pocketcasts.ui.theme.Theme +import au.com.shiftyjelly.pocketcasts.utils.featureflag.Feature +import au.com.shiftyjelly.pocketcasts.utils.featureflag.FeatureFlag import com.google.android.gms.cast.framework.CastButtonFactory import kotlinx.coroutines.delay import kotlinx.coroutines.flow.MutableSharedFlow @@ -80,7 +82,12 @@ fun ShelfBottomSheetPage( ShelfItem.Share -> { val podcast = playerViewModel.podcast ?: return@MenuShelfItems val episode = playerViewModel.episode as? PodcastEpisode ?: return@MenuShelfItems - shelfSharedViewModel.onShareClick(podcast, episode, ShelfItemSource.OverflowMenu) + + if (podcast.isPrivate && FeatureFlag.isEnabled(Feature.SHARE_PODCAST_PRIVATE_NOT_AVAILABLE)) { + shelfSharedViewModel.onShareNotAvailable(ShelfItemSource.OverflowMenu) + } else { + shelfSharedViewModel.onShareClick(podcast, episode, ShelfItemSource.OverflowMenu) + } } ShelfItem.Podcast -> shelfSharedViewModel.onShowPodcastOrCloudFiles(playerViewModel.podcast, ShelfItemSource.OverflowMenu) diff --git a/modules/features/player/src/main/java/au/com/shiftyjelly/pocketcasts/player/viewmodel/ShelfSharedViewModel.kt b/modules/features/player/src/main/java/au/com/shiftyjelly/pocketcasts/player/viewmodel/ShelfSharedViewModel.kt index 2fa1f08cbc5..ac053d4fccd 100644 --- a/modules/features/player/src/main/java/au/com/shiftyjelly/pocketcasts/player/viewmodel/ShelfSharedViewModel.kt +++ b/modules/features/player/src/main/java/au/com/shiftyjelly/pocketcasts/player/viewmodel/ShelfSharedViewModel.kt @@ -172,6 +172,13 @@ class ShelfSharedViewModel @Inject constructor( } } + fun onShareNotAvailable(source: ShelfItemSource) { + trackShelfAction(ShelfItem.Share, source) + viewModelScope.launch { + _snackbarMessages.emit(SnackbarMessage.ShareNotAvailable) + } + } + fun onEpisodeRemoveClick(source: ShelfItemSource) { trackShelfAction(ShelfItem.Download, source) viewModelScope.launch { @@ -365,6 +372,7 @@ class ShelfSharedViewModel @Inject constructor( data object EpisodeDownloadStarted : SnackbarMessage data object EpisodeRemoved : SnackbarMessage data object TranscriptNotAvailable : SnackbarMessage + data object ShareNotAvailable : SnackbarMessage } sealed class TransitionState { From 8a5a206e8f262a6a38c7517a1902fadc8ace67b1 Mon Sep 17 00:00:00 2001 From: Eduarda Barbosa Date: Fri, 27 Dec 2024 12:56:26 -0300 Subject: [PATCH 5/8] refactor share eligibility check --- .../pocketcasts/player/view/shelf/PlayerShelf.kt | 8 +++----- .../pocketcasts/player/view/shelf/ShelfBottomSheetPage.kt | 8 +++----- .../pocketcasts/podcasts/view/podcast/PodcastFragment.kt | 2 +- .../pocketcasts/podcasts/viewmodel/PodcastViewModel.kt | 2 +- .../com/shiftyjelly/pocketcasts/models/entity/Podcast.kt | 5 +++++ 5 files changed, 13 insertions(+), 12 deletions(-) diff --git a/modules/features/player/src/main/java/au/com/shiftyjelly/pocketcasts/player/view/shelf/PlayerShelf.kt b/modules/features/player/src/main/java/au/com/shiftyjelly/pocketcasts/player/view/shelf/PlayerShelf.kt index b89e5379a20..b8c0b6d326a 100644 --- a/modules/features/player/src/main/java/au/com/shiftyjelly/pocketcasts/player/view/shelf/PlayerShelf.kt +++ b/modules/features/player/src/main/java/au/com/shiftyjelly/pocketcasts/player/view/shelf/PlayerShelf.kt @@ -43,8 +43,6 @@ import au.com.shiftyjelly.pocketcasts.settings.onboarding.OnboardingUpgradeSourc import au.com.shiftyjelly.pocketcasts.ui.helper.ColorUtils import au.com.shiftyjelly.pocketcasts.ui.theme.Theme import au.com.shiftyjelly.pocketcasts.ui.theme.ThemeColor -import au.com.shiftyjelly.pocketcasts.utils.featureflag.Feature -import au.com.shiftyjelly.pocketcasts.utils.featureflag.FeatureFlag import au.com.shiftyjelly.pocketcasts.views.extensions.updateColor import com.airbnb.lottie.LottieProperty import com.airbnb.lottie.SimpleColorFilter @@ -115,10 +113,10 @@ fun PlayerShelf( val podcast = playerViewModel.podcast ?: return@PlayerShelfContent val episode = playerViewModel.episode as? PodcastEpisode ?: return@PlayerShelfContent - if (podcast.isPrivate && FeatureFlag.isEnabled(Feature.SHARE_PODCAST_PRIVATE_NOT_AVAILABLE)) { - shelfSharedViewModel.onShareNotAvailable(ShelfItemSource.Shelf) - } else { + if (podcast.canShare) { shelfSharedViewModel.onShareClick(podcast, episode, ShelfItemSource.Shelf) + } else { + shelfSharedViewModel.onShareNotAvailable(ShelfItemSource.Shelf) } }, onShowPodcast = { diff --git a/modules/features/player/src/main/java/au/com/shiftyjelly/pocketcasts/player/view/shelf/ShelfBottomSheetPage.kt b/modules/features/player/src/main/java/au/com/shiftyjelly/pocketcasts/player/view/shelf/ShelfBottomSheetPage.kt index bcb97fb8ae3..4f9f061b9df 100644 --- a/modules/features/player/src/main/java/au/com/shiftyjelly/pocketcasts/player/view/shelf/ShelfBottomSheetPage.kt +++ b/modules/features/player/src/main/java/au/com/shiftyjelly/pocketcasts/player/view/shelf/ShelfBottomSheetPage.kt @@ -40,8 +40,6 @@ import au.com.shiftyjelly.pocketcasts.player.viewmodel.ShelfViewModel import au.com.shiftyjelly.pocketcasts.preferences.model.ShelfItem import au.com.shiftyjelly.pocketcasts.settings.onboarding.OnboardingUpgradeSource import au.com.shiftyjelly.pocketcasts.ui.theme.Theme -import au.com.shiftyjelly.pocketcasts.utils.featureflag.Feature -import au.com.shiftyjelly.pocketcasts.utils.featureflag.FeatureFlag import com.google.android.gms.cast.framework.CastButtonFactory import kotlinx.coroutines.delay import kotlinx.coroutines.flow.MutableSharedFlow @@ -83,10 +81,10 @@ fun ShelfBottomSheetPage( val podcast = playerViewModel.podcast ?: return@MenuShelfItems val episode = playerViewModel.episode as? PodcastEpisode ?: return@MenuShelfItems - if (podcast.isPrivate && FeatureFlag.isEnabled(Feature.SHARE_PODCAST_PRIVATE_NOT_AVAILABLE)) { - shelfSharedViewModel.onShareNotAvailable(ShelfItemSource.OverflowMenu) - } else { + if (podcast.canShare) { shelfSharedViewModel.onShareClick(podcast, episode, ShelfItemSource.OverflowMenu) + } else { + shelfSharedViewModel.onShareNotAvailable(ShelfItemSource.OverflowMenu) } } diff --git a/modules/features/podcasts/src/main/java/au/com/shiftyjelly/pocketcasts/podcasts/view/podcast/PodcastFragment.kt b/modules/features/podcasts/src/main/java/au/com/shiftyjelly/pocketcasts/podcasts/view/podcast/PodcastFragment.kt index d85da19ca2c..94e4ee2eb2e 100644 --- a/modules/features/podcasts/src/main/java/au/com/shiftyjelly/pocketcasts/podcasts/view/podcast/PodcastFragment.kt +++ b/modules/features/podcasts/src/main/java/au/com/shiftyjelly/pocketcasts/podcasts/view/podcast/PodcastFragment.kt @@ -979,7 +979,7 @@ class PodcastFragment : BaseFragment(), Toolbar.OnMenuItemClickListener { private fun share() { analyticsTracker.track(AnalyticsEvent.PODCAST_SCREEN_SHARE_TAPPED) - if (!viewModel.canSharePodcast() && FeatureFlag.isEnabled(Feature.SHARE_PODCAST_PRIVATE_NOT_AVAILABLE)) { + if (!viewModel.canSharePodcast()) { (activity as? FragmentHostListener)?.snackBarView()?.let { snackBarView -> Snackbar.make(snackBarView, getString(LR.string.sharing_is_not_available_for_private_podcasts), Snackbar.LENGTH_LONG).show() } diff --git a/modules/features/podcasts/src/main/java/au/com/shiftyjelly/pocketcasts/podcasts/viewmodel/PodcastViewModel.kt b/modules/features/podcasts/src/main/java/au/com/shiftyjelly/pocketcasts/podcasts/viewmodel/PodcastViewModel.kt index 8839388778d..6896d72f464 100644 --- a/modules/features/podcasts/src/main/java/au/com/shiftyjelly/pocketcasts/podcasts/viewmodel/PodcastViewModel.kt +++ b/modules/features/podcasts/src/main/java/au/com/shiftyjelly/pocketcasts/podcasts/viewmodel/PodcastViewModel.kt @@ -348,7 +348,7 @@ class PodcastViewModel return episodes.find { !it.isArchived && it.isFinished } != null } - fun canSharePodcast(): Boolean = podcast.value?.isPrivate == false + fun canSharePodcast(): Boolean = podcast.value?.canShare == true fun archivePlayed() { val podcast = this.podcast.value ?: return diff --git a/modules/services/model/src/main/java/au/com/shiftyjelly/pocketcasts/models/entity/Podcast.kt b/modules/services/model/src/main/java/au/com/shiftyjelly/pocketcasts/models/entity/Podcast.kt index 0d3f2d060e0..3518c33eaa1 100644 --- a/modules/services/model/src/main/java/au/com/shiftyjelly/pocketcasts/models/entity/Podcast.kt +++ b/modules/services/model/src/main/java/au/com/shiftyjelly/pocketcasts/models/entity/Podcast.kt @@ -19,6 +19,8 @@ import au.com.shiftyjelly.pocketcasts.models.to.PlaybackEffects import au.com.shiftyjelly.pocketcasts.models.to.PodcastGrouping import au.com.shiftyjelly.pocketcasts.models.type.EpisodesSortType import au.com.shiftyjelly.pocketcasts.models.type.TrimMode +import au.com.shiftyjelly.pocketcasts.utils.featureflag.Feature +import au.com.shiftyjelly.pocketcasts.utils.featureflag.FeatureFlag import java.io.Serializable import java.net.MalformedURLException import java.net.URL @@ -172,6 +174,9 @@ data class Podcast( val isSilenceRemoved: Boolean get() = trimMode != TrimMode.OFF + val canShare: Boolean + get() = !FeatureFlag.isEnabled(Feature.SHARE_PODCAST_PRIVATE_NOT_AVAILABLE) || (!isPrivate) + val isUsingEffects: Boolean get() = overrideGlobalEffects && (isSilenceRemoved || isVolumeBoosted || playbackSpeed != 1.0) From 7991797b5ef3d802802ff751a15c1400d220de4e Mon Sep 17 00:00:00 2001 From: Eduarda Barbosa Date: Fri, 27 Dec 2024 13:11:56 -0300 Subject: [PATCH 6/8] remove private podcasts from podcast sharing --- .../pocketcasts/podcasts/view/share/ShareListCreateViewModel.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/features/podcasts/src/main/java/au/com/shiftyjelly/pocketcasts/podcasts/view/share/ShareListCreateViewModel.kt b/modules/features/podcasts/src/main/java/au/com/shiftyjelly/pocketcasts/podcasts/view/share/ShareListCreateViewModel.kt index 9d0a899be3f..c6e74e0ac85 100644 --- a/modules/features/podcasts/src/main/java/au/com/shiftyjelly/pocketcasts/podcasts/view/share/ShareListCreateViewModel.kt +++ b/modules/features/podcasts/src/main/java/au/com/shiftyjelly/pocketcasts/podcasts/view/share/ShareListCreateViewModel.kt @@ -39,7 +39,7 @@ class ShareListCreateViewModel @Inject constructor( init { viewModelScope.launch { val podcasts = podcastManager.findPodcastsOrderByTitle() - mutableState.value = mutableState.value.copy(podcasts = podcasts) + mutableState.value = mutableState.value.copy(podcasts = podcasts.filter { it.canShare }) } } From 3b621fa0525ae50eae258be98264cfa0d80cc652 Mon Sep 17 00:00:00 2001 From: Eduarda Barbosa Date: Fri, 27 Dec 2024 13:36:05 -0300 Subject: [PATCH 7/8] simplify code --- .../pocketcasts/podcasts/view/podcast/PodcastFragment.kt | 5 +++-- .../pocketcasts/podcasts/viewmodel/PodcastViewModel.kt | 2 -- .../au/com/shiftyjelly/pocketcasts/models/entity/Podcast.kt | 2 +- 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/modules/features/podcasts/src/main/java/au/com/shiftyjelly/pocketcasts/podcasts/view/podcast/PodcastFragment.kt b/modules/features/podcasts/src/main/java/au/com/shiftyjelly/pocketcasts/podcasts/view/podcast/PodcastFragment.kt index 94e4ee2eb2e..63351bd9985 100644 --- a/modules/features/podcasts/src/main/java/au/com/shiftyjelly/pocketcasts/podcasts/view/podcast/PodcastFragment.kt +++ b/modules/features/podcasts/src/main/java/au/com/shiftyjelly/pocketcasts/podcasts/view/podcast/PodcastFragment.kt @@ -977,16 +977,17 @@ class PodcastFragment : BaseFragment(), Toolbar.OnMenuItemClickListener { } private fun share() { + val podcast = viewModel.podcast.value ?: return + analyticsTracker.track(AnalyticsEvent.PODCAST_SCREEN_SHARE_TAPPED) - if (!viewModel.canSharePodcast()) { + if (!podcast.canShare) { (activity as? FragmentHostListener)?.snackBarView()?.let { snackBarView -> Snackbar.make(snackBarView, getString(LR.string.sharing_is_not_available_for_private_podcasts), Snackbar.LENGTH_LONG).show() } return } - val podcast = viewModel.podcast.value ?: return if (FeatureFlag.isEnabled(Feature.REIMAGINE_SHARING)) { SharePodcastFragment .newInstance(podcast, SourceView.PODCAST_SCREEN) diff --git a/modules/features/podcasts/src/main/java/au/com/shiftyjelly/pocketcasts/podcasts/viewmodel/PodcastViewModel.kt b/modules/features/podcasts/src/main/java/au/com/shiftyjelly/pocketcasts/podcasts/viewmodel/PodcastViewModel.kt index 6896d72f464..69217b2a733 100644 --- a/modules/features/podcasts/src/main/java/au/com/shiftyjelly/pocketcasts/podcasts/viewmodel/PodcastViewModel.kt +++ b/modules/features/podcasts/src/main/java/au/com/shiftyjelly/pocketcasts/podcasts/viewmodel/PodcastViewModel.kt @@ -348,8 +348,6 @@ class PodcastViewModel return episodes.find { !it.isArchived && it.isFinished } != null } - fun canSharePodcast(): Boolean = podcast.value?.canShare == true - fun archivePlayed() { val podcast = this.podcast.value ?: return launch { diff --git a/modules/services/model/src/main/java/au/com/shiftyjelly/pocketcasts/models/entity/Podcast.kt b/modules/services/model/src/main/java/au/com/shiftyjelly/pocketcasts/models/entity/Podcast.kt index 3518c33eaa1..cffa7dbcccc 100644 --- a/modules/services/model/src/main/java/au/com/shiftyjelly/pocketcasts/models/entity/Podcast.kt +++ b/modules/services/model/src/main/java/au/com/shiftyjelly/pocketcasts/models/entity/Podcast.kt @@ -175,7 +175,7 @@ data class Podcast( get() = trimMode != TrimMode.OFF val canShare: Boolean - get() = !FeatureFlag.isEnabled(Feature.SHARE_PODCAST_PRIVATE_NOT_AVAILABLE) || (!isPrivate) + get() = !FeatureFlag.isEnabled(Feature.SHARE_PODCAST_PRIVATE_NOT_AVAILABLE) || !isPrivate val isUsingEffects: Boolean get() = overrideGlobalEffects && (isSilenceRemoved || isVolumeBoosted || playbackSpeed != 1.0) From 28fe968d9fd6f3af30903a2f000d7c00cb1aa783 Mon Sep 17 00:00:00 2001 From: Eduarda Barbosa Date: Fri, 27 Dec 2024 14:25:13 -0300 Subject: [PATCH 8/8] update CHANGELOG.md --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index abfdc7bd058..e53d4848283 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,8 @@ * Updates * Add the ability to dismiss the low storage banner in download screen ([#3385](https://github.com/Automattic/pocket-casts-android/pull/3385)) + * Disable Private Feed Sharing + ([#3395](https://github.com/Automattic/pocket-casts-android/pull/3395)) 7.79 -----