diff --git a/app/src/main/java/au/com/shiftyjelly/pocketcasts/ui/MainActivity.kt b/app/src/main/java/au/com/shiftyjelly/pocketcasts/ui/MainActivity.kt
index 6bcdd76cb80..682cf761536 100644
--- a/app/src/main/java/au/com/shiftyjelly/pocketcasts/ui/MainActivity.kt
+++ b/app/src/main/java/au/com/shiftyjelly/pocketcasts/ui/MainActivity.kt
@@ -996,6 +996,10 @@ class MainActivity :
return binding.snackbarFragment
}
+ override fun setFullScreenDarkOverlayViewVisibility(visible: Boolean) {
+ binding.fullScreenDarkOverlayView.isVisible = visible
+ }
+
override fun onMiniPlayerHidden() {
updateSnackbarPosition(miniPlayerOpen = false)
settings.updateBottomInset(0)
diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml
index de02a102eca..8fdc761e374 100644
--- a/app/src/main/res/layout/activity_main.xml
+++ b/app/src/main/res/layout/activity_main.xml
@@ -66,6 +66,13 @@
android:layout_width="match_parent"
android:layout_height="match_parent" />
+
+
{
adapter?.setBookmarks(
@@ -1003,6 +1091,7 @@ class PodcastFragment : BaseFragment(), Toolbar.OnMenuItemClickListener {
binding = null
currentSnackBar?.dismiss()
currentSnackBar = null
+ (activity as? FragmentHostListener)?.setFullScreenDarkOverlayViewVisibility(false)
}
private fun archiveAllPlayed() {
diff --git a/modules/features/podcasts/src/main/java/au/com/shiftyjelly/pocketcasts/podcasts/view/podcast/PodcastTooltip.kt b/modules/features/podcasts/src/main/java/au/com/shiftyjelly/pocketcasts/podcasts/view/podcast/PodcastTooltip.kt
new file mode 100644
index 00000000000..f8cd5bac2f0
--- /dev/null
+++ b/modules/features/podcasts/src/main/java/au/com/shiftyjelly/pocketcasts/podcasts/view/podcast/PodcastTooltip.kt
@@ -0,0 +1,136 @@
+package au.com.shiftyjelly.pocketcasts.podcasts.view.podcast
+
+import androidx.compose.foundation.Canvas
+import androidx.compose.foundation.background
+import androidx.compose.foundation.clickable
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.BoxScope
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.width
+import androidx.compose.foundation.layout.widthIn
+import androidx.compose.foundation.shape.RoundedCornerShape
+import androidx.compose.material.Card
+import androidx.compose.material.Icon
+import androidx.compose.material.MaterialTheme
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.filled.Close
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.graphics.Path
+import androidx.compose.ui.res.stringResource
+import androidx.compose.ui.unit.IntOffset
+import androidx.compose.ui.unit.dp
+import androidx.compose.ui.unit.sp
+import androidx.compose.ui.window.Popup
+import au.com.shiftyjelly.pocketcasts.compose.CallOnce
+import au.com.shiftyjelly.pocketcasts.compose.components.TextH30
+import au.com.shiftyjelly.pocketcasts.compose.components.TextP40
+import au.com.shiftyjelly.pocketcasts.compose.theme
+import au.com.shiftyjelly.pocketcasts.localization.R as LR
+
+@Composable
+fun PodcastTooltip(
+ title: String,
+ subtitle: String,
+ offset: IntOffset,
+ onTooltipShown: () -> Unit,
+ onDismissRequest: () -> Unit,
+ onCloseButtonClick: () -> Unit,
+ modifier: Modifier = Modifier,
+) {
+ val tooltipColor = MaterialTheme.theme.colors.primaryUi01
+
+ CallOnce {
+ onTooltipShown.invoke()
+ }
+
+ Popup(
+ alignment = Alignment.TopStart,
+ offset = offset,
+ onDismissRequest = onDismissRequest,
+ ) {
+ Box(
+ modifier = modifier.background(Color.Transparent).padding(16.dp).widthIn(max = 400.dp),
+ ) {
+ TooltipContent(tooltipColor, title, subtitle, onCloseButtonClick)
+
+ TooltipArrow(tooltipColor)
+ }
+ }
+}
+
+@Composable
+private fun TooltipContent(
+ tooltipColor: Color,
+ title: String,
+ subtitle: String,
+ onCloseButtonClick: () -> Unit,
+ modifier: Modifier = Modifier,
+) {
+ Card(
+ backgroundColor = tooltipColor,
+ shape = RoundedCornerShape(8.dp),
+ elevation = 0.dp,
+ modifier = modifier,
+ ) {
+ Row(
+ modifier = Modifier.background(tooltipColor).padding(16.dp),
+ verticalAlignment = Alignment.Top,
+ ) {
+ Column(
+ modifier = Modifier.weight(1f),
+ ) {
+ TextH30(
+ text = title,
+ modifier = Modifier.padding(bottom = 4.dp),
+ )
+
+ TextP40(
+ text = subtitle,
+ fontSize = 12.sp,
+ color = MaterialTheme.theme.colors.primaryText02,
+ )
+ }
+
+ Spacer(modifier = Modifier.width(12.dp))
+
+ Icon(
+ imageVector = Icons.Default.Close,
+ contentDescription = stringResource(LR.string.close),
+ tint = MaterialTheme.theme.colors.primaryIcon02,
+ modifier = Modifier
+ .align(Alignment.Top)
+ .width(24.dp)
+ .clickable {
+ onCloseButtonClick.invoke()
+ },
+ )
+ }
+ }
+}
+
+@Composable
+private fun BoxScope.TooltipArrow(tooltipColor: Color) {
+ Canvas(
+ modifier = Modifier
+ .align(Alignment.BottomEnd)
+ .padding(end = 28.dp),
+ ) {
+ val triangleSize = 12.dp.toPx()
+
+ drawPath(
+ path = Path().apply {
+ moveTo(0f, -2f)
+ lineTo(triangleSize, 0f)
+ lineTo(triangleSize / 2f, triangleSize)
+ close()
+ },
+ color = tooltipColor,
+ )
+ }
+}
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 8313a0deaf9..1fd7605aa11 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
@@ -60,6 +60,7 @@ import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.MutableSharedFlow
+import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asSharedFlow
import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.launch
@@ -102,6 +103,8 @@ class PodcastViewModel
private val _refreshState = MutableSharedFlow()
val refreshState = _refreshState.asSharedFlow()
+ val shouldShowPodcastTooltip = MutableStateFlow(settings.showPodcastRefreshTooltip.value)
+
val groupedEpisodes: MutableLiveData>> = MutableLiveData()
val signInState = userManager.getSignInState().toLiveData()
@@ -592,6 +595,11 @@ class PodcastViewModel
}
}
+ fun hidePodcastRefreshTooltip() {
+ settings.showPodcastRefreshTooltip.set(false, updateModifiedAt = false)
+ shouldShowPodcastTooltip.value = false
+ }
+
private fun trackEpisodeBulkEvent(event: AnalyticsEvent, count: Int) {
episodeAnalytics.trackBulkEvent(
event,
diff --git a/modules/features/podcasts/src/main/res/layout/fragment_podcast.xml b/modules/features/podcasts/src/main/res/layout/fragment_podcast.xml
index dd1dcaa0c74..22121afd578 100644
--- a/modules/features/podcasts/src/main/res/layout/fragment_podcast.xml
+++ b/modules/features/podcasts/src/main/res/layout/fragment_podcast.xml
@@ -105,6 +105,12 @@
android:scaleType="centerCrop"
tools:src="@tools:sample/avatars" />
+
+
diff --git a/modules/services/analytics/src/main/java/au/com/shiftyjelly/pocketcasts/analytics/AnalyticsEvent.kt b/modules/services/analytics/src/main/java/au/com/shiftyjelly/pocketcasts/analytics/AnalyticsEvent.kt
index c5ddf9ff1c5..919a3e587bd 100644
--- a/modules/services/analytics/src/main/java/au/com/shiftyjelly/pocketcasts/analytics/AnalyticsEvent.kt
+++ b/modules/services/analytics/src/main/java/au/com/shiftyjelly/pocketcasts/analytics/AnalyticsEvent.kt
@@ -232,6 +232,8 @@ enum class AnalyticsEvent(val key: String) {
PODCAST_SCREEN_REFRESH_EPISODE_LIST("podcast_screen_refresh_episode_list"),
PODCAST_SCREEN_REFRESH_NEW_EPISODE_FOUND("podcast_screen_refresh_new_episode_found"),
PODCAST_SCREEN_REFRESH_NO_EPISODES_FOUND("podcast_screen_refresh_no_episodes_found"),
+ PODCAST_REFRESH_EPISODE_TOOLTIP_SHOWN("podcast_refresh_episode_tooltip_shown"),
+ PODCAST_REFRESH_EPISODE_TOOLTIP_DISMISSED("podcast_refresh_episode_tooltip_dismissed"),
/* Podcast Settings */
PODCAST_SETTINGS_FEED_ERROR_TAPPED("podcast_settings_feed_error_tapped"),
diff --git a/modules/services/localization/src/main/res/values/strings.xml b/modules/services/localization/src/main/res/values/strings.xml
index 113b0ba9148..33f54503aac 100644
--- a/modules/services/localization/src/main/res/values/strings.xml
+++ b/modules/services/localization/src/main/res/values/strings.xml
@@ -566,6 +566,8 @@
@string/unplayed
Hide archived
There was an error loading the podcast.
+ Fresh episodes, coming right up!
+ Pull down or use this menu to see if there\'s something new.
Loading your podcasts
Next episode any day now
Next episode today
diff --git a/modules/services/preferences/src/main/java/au/com/shiftyjelly/pocketcasts/preferences/Settings.kt b/modules/services/preferences/src/main/java/au/com/shiftyjelly/pocketcasts/preferences/Settings.kt
index 300c2bbc38e..10bde7057ca 100644
--- a/modules/services/preferences/src/main/java/au/com/shiftyjelly/pocketcasts/preferences/Settings.kt
+++ b/modules/services/preferences/src/main/java/au/com/shiftyjelly/pocketcasts/preferences/Settings.kt
@@ -119,6 +119,8 @@ interface Settings {
const val AUTOMOTIVE_CONNECTED_TO_MEDIA_SESSION = "automotive_connected_to_media_session"
const val SHOW_REFERRALS_TOOLTIP = "show_referrals_tooltip"
+
+ const val SHOW_PODCAST_REFRESH_TOOLTIP = "show_podcast_refresh_tooltip"
}
enum class NotificationChannel(val id: String) {
@@ -556,6 +558,8 @@ interface Settings {
val showReferralsTooltip: UserSetting
+ val showPodcastRefreshTooltip: UserSetting
+
val playerOrUpNextBottomSheetState: Flow
fun updatePlayerOrUpNextBottomSheetState(state: Int)
diff --git a/modules/services/preferences/src/main/java/au/com/shiftyjelly/pocketcasts/preferences/SettingsImpl.kt b/modules/services/preferences/src/main/java/au/com/shiftyjelly/pocketcasts/preferences/SettingsImpl.kt
index 8c2af4d86d4..c8e44eecacc 100644
--- a/modules/services/preferences/src/main/java/au/com/shiftyjelly/pocketcasts/preferences/SettingsImpl.kt
+++ b/modules/services/preferences/src/main/java/au/com/shiftyjelly/pocketcasts/preferences/SettingsImpl.kt
@@ -1536,6 +1536,12 @@ class SettingsImpl @Inject constructor(
sharedPrefs = sharedPreferences,
)
+ override val showPodcastRefreshTooltip: UserSetting = UserSetting.BoolPref(
+ sharedPrefKey = Settings.SHOW_PODCAST_REFRESH_TOOLTIP,
+ defaultValue = true,
+ sharedPrefs = sharedPreferences,
+ )
+
override val referralClaimCode = UserSetting.StringPref(
sharedPrefKey = "referralCode",
defaultValue = "",
diff --git a/modules/services/ui/src/main/java/au/com/shiftyjelly/pocketcasts/ui/helper/FragmentHostListener.kt b/modules/services/ui/src/main/java/au/com/shiftyjelly/pocketcasts/ui/helper/FragmentHostListener.kt
index 7664cb2da73..b203a3729e3 100644
--- a/modules/services/ui/src/main/java/au/com/shiftyjelly/pocketcasts/ui/helper/FragmentHostListener.kt
+++ b/modules/services/ui/src/main/java/au/com/shiftyjelly/pocketcasts/ui/helper/FragmentHostListener.kt
@@ -21,6 +21,7 @@ interface FragmentHostListener {
fun openPodcastPage(uuid: String, sourceView: String? = null)
fun openCloudFiles()
fun snackBarView(): View
+ fun setFullScreenDarkOverlayViewVisibility(visible: Boolean)
fun showAccountUpgradeNow(autoSelectPlus: Boolean)
fun updateStatusBar()
fun getPlayerBottomSheetState(): Int