Skip to content

Commit c8c29ac

Browse files
committed
fix: make feed notifications robust against subscription changes
1 parent 54cf717 commit c8c29ac

6 files changed

Lines changed: 38 additions & 36 deletions

File tree

app/src/main/java/com/github/libretube/constants/PreferenceKeys.kt

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -140,11 +140,13 @@ object PreferenceKeys {
140140

141141
// Internally saved data / not a preference
142142
const val ERROR_LOG = "error_log"
143-
const val LAST_STREAM_VIDEO_ID = "last_stream_video_id"
144-
const val LAST_WATCHED_FEED_TIME = "last_watched_feed_time"
145143
const val AUTH_PREF_FILE = "auth"
146144
const val IMAGE_PROXY_URL = "image_proxy_url"
147145
const val SELECTED_CHANNEL_GROUP = "selected_channel_group"
148146
const val SELECTED_DOWNLOAD_SORT_TYPE = "selected_download_sort_type"
149147
const val LAST_SHOWN_INFO_MESSAGE_VERSION_CODE = "last_shown_info_message_version"
148+
149+
// use the helper methods at PreferenceHelper to access these
150+
const val LAST_USER_SEEN_FEED_TIME = "last_watched_feed_time"
151+
const val LAST_REFRESHED_FEED_TIME = "last_refreshed_feed_time"
150152
}

app/src/main/java/com/github/libretube/helpers/PreferenceHelper.kt

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -96,24 +96,24 @@ object PreferenceHelper {
9696
authSettings.edit { putString(PreferenceKeys.USERNAME, newValue) }
9797
}
9898

99-
fun setLastSeenVideoId(videoId: String) {
100-
putString(PreferenceKeys.LAST_STREAM_VIDEO_ID, videoId)
101-
}
102-
103-
fun getLastSeenVideoId(): String {
104-
return getString(PreferenceKeys.LAST_STREAM_VIDEO_ID, "")
105-
}
106-
107-
fun updateLastFeedWatchedTime(time: Long) {
99+
fun updateLastFeedWatchedTime(time: Long, seenByUser: Boolean) {
108100
// only update the time if the time is newer
109101
// this avoids cases, where the user last saw an older video, which had already been seen,
110102
// causing all following video to be incorrectly marked as unseen again
111-
if (getLastCheckedFeedTime() < time)
112-
putLong(PreferenceKeys.LAST_WATCHED_FEED_TIME, time)
103+
if (getLastCheckedFeedTime(false) < time)
104+
putLong(PreferenceKeys.LAST_REFRESHED_FEED_TIME, time)
105+
106+
// this value holds the last time the user opened the subscriptions feed
107+
// whereas [LAST_REFRESHED_FEED_TIME] considers the last time the feed was loaded,
108+
// which could also be possible in the background (e.g. via notifications)
109+
if (seenByUser && getLastCheckedFeedTime(true) < time)
110+
putLong(PreferenceKeys.LAST_USER_SEEN_FEED_TIME, time)
113111
}
114112

115-
fun getLastCheckedFeedTime(): Long {
116-
return getLong(PreferenceKeys.LAST_WATCHED_FEED_TIME, 0)
113+
fun getLastCheckedFeedTime(seenByUser: Boolean): Long {
114+
val key =
115+
if (seenByUser) PreferenceKeys.LAST_USER_SEEN_FEED_TIME else PreferenceKeys.LAST_REFRESHED_FEED_TIME
116+
return getLong(key, 0)
117117
}
118118

119119
fun saveErrorLog(log: String) {

app/src/main/java/com/github/libretube/ui/activities/MainActivity.kt

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,6 @@ import com.github.libretube.constants.IntentData
3737
import com.github.libretube.constants.PreferenceKeys
3838
import com.github.libretube.databinding.ActivityMainBinding
3939
import com.github.libretube.enums.ImportFormat
40-
import com.github.libretube.extensions.toID
4140
import com.github.libretube.helpers.ImportHelper
4241
import com.github.libretube.helpers.IntentHelper
4342
import com.github.libretube.helpers.NavBarHelper
@@ -240,9 +239,12 @@ class MainActivity : BaseActivity() {
240239
subscriptionsViewModel.fetchSubscriptions(this)
241240

242241
subscriptionsViewModel.videoFeed.observe(this) { feed ->
242+
val lastCheckedFeedTime = PreferenceHelper.getLastCheckedFeedTime(seenByUser = true)
243243
val lastSeenVideoIndex = feed.orEmpty()
244-
.indexOfFirst { PreferenceHelper.getLastSeenVideoId() == it.url?.toID() }
244+
.filter { !it.isUpcoming }
245+
.indexOfFirst { it.uploaded <= lastCheckedFeedTime }
245246
if (lastSeenVideoIndex < 1) return@observe
247+
246248
binding.bottomNav.getOrCreateBadge(R.id.subscriptionsFragment).apply {
247249
number = lastSeenVideoIndex
248250
backgroundColor = ThemeHelper.getThemeColor(

app/src/main/java/com/github/libretube/ui/fragments/SubscriptionsFragment.kt

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -140,13 +140,17 @@ class SubscriptionsFragment : DynamicLayoutManagerFragment(R.layout.fragment_sub
140140
// any other feed updates are caused by manual refreshing and thus should reset the scroll
141141
// position to zero
142142
var alreadyShowedFeedOnce = false
143-
viewModel.videoFeed.observe(viewLifecycleOwner) {
144-
if (!viewModel.isCurrentTabSubChannels && it != null) {
143+
viewModel.videoFeed.observe(viewLifecycleOwner) { feed ->
144+
if (!viewModel.isCurrentTabSubChannels && feed != null) {
145145
lifecycleScope.launch {
146146
showFeed(!alreadyShowedFeedOnce)
147147
}
148148
alreadyShowedFeedOnce = true
149149
}
150+
151+
feed?.firstOrNull { !it.isUpcoming }?.uploaded?.let {
152+
PreferenceHelper.updateLastFeedWatchedTime(it, true)
153+
}
150154
}
151155

152156
// restore the scroll position, same conditions as above
@@ -384,7 +388,7 @@ class SubscriptionsFragment : DynamicLayoutManagerFragment(R.layout.fragment_sub
384388

385389
// add an "all caught up item"
386390
if (selectedSortOrder == 0) {
387-
val lastCheckedFeedTime = PreferenceHelper.getLastCheckedFeedTime()
391+
val lastCheckedFeedTime = PreferenceHelper.getLastCheckedFeedTime(seenByUser = true)
388392
val caughtUpIndex =
389393
feed.indexOfFirst { it.uploaded <= lastCheckedFeedTime && !it.isUpcoming }
390394
if (caughtUpIndex > 0 && !feed[caughtUpIndex - 1].isUpcoming) {
@@ -404,10 +408,6 @@ class SubscriptionsFragment : DynamicLayoutManagerFragment(R.layout.fragment_sub
404408

405409
binding.toggleSubs.text = getString(R.string.subscriptions)
406410

407-
feed.firstOrNull { !it.isUpcoming }?.uploaded?.let {
408-
PreferenceHelper.updateLastFeedWatchedTime(it)
409-
}
410-
411411
binding.subRefresh.isRefreshing = false
412412

413413
feedAdapter.submitList(sortedFeed) {

app/src/main/java/com/github/libretube/ui/models/SubscriptionsViewModel.kt

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ import com.github.libretube.api.SubscriptionHelper
1111
import com.github.libretube.api.obj.StreamItem
1212
import com.github.libretube.api.obj.Subscription
1313
import com.github.libretube.extensions.TAG
14-
import com.github.libretube.extensions.toID
1514
import com.github.libretube.extensions.toastFromMainDispatcher
1615
import com.github.libretube.helpers.PreferenceHelper
1716
import com.github.libretube.repo.FeedProgress
@@ -41,9 +40,8 @@ class SubscriptionsViewModel : ViewModel() {
4140
return@launch
4241
}
4342
this@SubscriptionsViewModel.videoFeed.postValue(videoFeed)
44-
if (videoFeed.isNotEmpty()) {
45-
// save the last recent video to the prefs for the notification worker
46-
PreferenceHelper.setLastSeenVideoId(videoFeed[0].url!!.toID())
43+
videoFeed.firstOrNull { !it.isUpcoming }?.uploaded?.let {
44+
PreferenceHelper.updateLastFeedWatchedTime(it, false)
4745
}
4846
}
4947
}

app/src/main/java/com/github/libretube/workers/NotificationWorker.kt

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -83,33 +83,33 @@ class NotificationWorker(appContext: Context, parameters: WorkerParameters) :
8383
val videoFeed = try {
8484
withContext(Dispatchers.IO) {
8585
SubscriptionHelper.getFeed(forceRefresh = true)
86-
}
86+
}.filter { !it.isUpcoming }
8787
} catch (e: Exception) {
8888
return false
8989
}
9090

91-
val lastUserSeenVideoId = PreferenceHelper.getLastSeenVideoId()
92-
val mostRecentStreamId = videoFeed.firstOrNull()?.url?.toID() ?: return true
93-
// save the latest streams that got notified about
94-
PreferenceHelper.setLastSeenVideoId(mostRecentStreamId)
91+
val lastFeedCheckMillis = PreferenceHelper.getLastCheckedFeedTime(seenByUser = false)
9592

9693
// first time notifications are enabled or no new video available
97-
if (lastUserSeenVideoId.isEmpty() || lastUserSeenVideoId == mostRecentStreamId) return true
94+
if (lastFeedCheckMillis == 0L || videoFeed.none { it.uploaded > lastFeedCheckMillis }) return true
9895

9996
val channelsToIgnore = PreferenceHelper.getIgnorableNotificationChannels()
10097
val enableShortsNotification =
10198
PreferenceHelper.getBoolean(PreferenceKeys.SHORTS_NOTIFICATIONS, false)
10299

103100
val channelGroups = videoFeed.asSequence()
104101
// filter the new videos until the last seen video in the feed
105-
.takeWhile { it.url!!.toID() != lastUserSeenVideoId }
102+
.filter { it.uploaded > lastFeedCheckMillis }
106103
// don't show notifications for shorts videos if not enabled
107104
.filter { enableShortsNotification || !it.isShort }
108105
// hide for notifications unsubscribed channels
109106
.filter { it.uploaderUrl!!.toID() !in channelsToIgnore }
110107
// group the new streams by the uploader
111108
.groupBy { it.uploaderUrl!!.toID() }
112109

110+
// update the last feed check time in order to not show the same notification again
111+
PreferenceHelper.updateLastFeedWatchedTime(videoFeed.first().uploaded, seenByUser = false)
112+
113113
// return if the previous video didn't get found or all the channels have notifications disabled
114114
if (channelGroups.isEmpty()) return true
115115

@@ -226,6 +226,6 @@ class NotificationWorker(appContext: Context, parameters: WorkerParameters) :
226226

227227
companion object {
228228
private const val INTENT_FLAGS = Intent.FLAG_ACTIVITY_CLEAR_TOP or
229-
Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
229+
Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
230230
}
231231
}

0 commit comments

Comments
 (0)