diff --git a/app/src/main/java/org/schabi/newpipe/database/feed/dao/FeedDAO.kt b/app/src/main/java/org/schabi/newpipe/database/feed/dao/FeedDAO.kt index 968d0c88f..4faafb2ea 100644 --- a/app/src/main/java/org/schabi/newpipe/database/feed/dao/FeedDAO.kt +++ b/app/src/main/java/org/schabi/newpipe/database/feed/dao/FeedDAO.kt @@ -32,6 +32,7 @@ abstract class FeedDAO { * @return the feed streams filtered according to the conditions provided in the parameters * @see StreamStateEntity.isFinished() * @see StreamStateEntity.PLAYBACK_FINISHED_END_MILLISECONDS + * @see StreamStateEntity.PLAYBACK_SAVE_THRESHOLD_START_MILLISECONDS */ @Query( """ @@ -66,6 +67,13 @@ abstract class FeedDAO { OR s.stream_type = 'LIVE_STREAM' OR s.stream_type = 'AUDIO_LIVE_STREAM' ) + AND ( + :includePartiallyPlayed + OR sh.stream_id IS NULL + OR sst.stream_id IS NULL + OR (sst.progress_time < ${StreamStateEntity.PLAYBACK_SAVE_THRESHOLD_START_MILLISECONDS} + AND sst.progress_time < s.duration * 1000 / 4) + ) AND ( :uploadDateBefore IS NULL OR s.upload_date IS NULL @@ -79,6 +87,7 @@ abstract class FeedDAO { abstract fun getStreams( groupId: Long, includePlayed: Boolean, + includePartiallyPlayed: Boolean, uploadDateBefore: OffsetDateTime? ): Maybe> diff --git a/app/src/main/java/org/schabi/newpipe/database/stream/model/StreamStateEntity.java b/app/src/main/java/org/schabi/newpipe/database/stream/model/StreamStateEntity.java index 75766850f..627acea45 100644 --- a/app/src/main/java/org/schabi/newpipe/database/stream/model/StreamStateEntity.java +++ b/app/src/main/java/org/schabi/newpipe/database/stream/model/StreamStateEntity.java @@ -30,7 +30,7 @@ public class StreamStateEntity { /** * Playback state will not be saved, if playback time is less than this threshold (5000ms = 5s). */ - private static final long PLAYBACK_SAVE_THRESHOLD_START_MILLISECONDS = 5000; + public static final long PLAYBACK_SAVE_THRESHOLD_START_MILLISECONDS = 5000; /** * Stream will be considered finished if the playback time left exceeds this threshold diff --git a/app/src/main/java/org/schabi/newpipe/local/feed/FeedDatabaseManager.kt b/app/src/main/java/org/schabi/newpipe/local/feed/FeedDatabaseManager.kt index 07edb0499..ed65d4048 100644 --- a/app/src/main/java/org/schabi/newpipe/local/feed/FeedDatabaseManager.kt +++ b/app/src/main/java/org/schabi/newpipe/local/feed/FeedDatabaseManager.kt @@ -43,11 +43,13 @@ class FeedDatabaseManager(context: Context) { fun getStreams( groupId: Long, includePlayedStreams: Boolean, + includePartiallyPlayedStreams: Boolean, includeFutureStreams: Boolean ): Maybe> { return feedTable.getStreams( groupId, includePlayedStreams, + includePartiallyPlayedStreams, if (includeFutureStreams) null else OffsetDateTime.now() ) } diff --git a/app/src/main/java/org/schabi/newpipe/local/feed/FeedFragment.kt b/app/src/main/java/org/schabi/newpipe/local/feed/FeedFragment.kt index 2bb2f9986..07421a8ea 100644 --- a/app/src/main/java/org/schabi/newpipe/local/feed/FeedFragment.kt +++ b/app/src/main/java/org/schabi/newpipe/local/feed/FeedFragment.kt @@ -100,7 +100,7 @@ class FeedFragment : BaseStateFragment() { private var oldestSubscriptionUpdate: OffsetDateTime? = null private lateinit var groupAdapter: GroupieAdapter - @State @JvmField var showPlayedItems: Boolean = true + @State @JvmField var showPlayedItems: ShowItems = ShowItems.DEFAULT @State @JvmField var showFutureItems: Boolean = true private var onSettingsChangeListener: SharedPreferences.OnSharedPreferenceChangeListener? = null @@ -140,7 +140,7 @@ class FeedFragment : BaseStateFragment() { val factory = FeedViewModel.getFactory(requireContext(), groupId) viewModel = ViewModelProvider(this, factory)[FeedViewModel::class.java] - showPlayedItems = viewModel.getShowPlayedItemsFromPreferences() + showPlayedItems = viewModel.getItemsVisibilityFromPreferences() showFutureItems = viewModel.getShowFutureItemsFromPreferences() viewModel.stateLiveData.observe(viewLifecycleOwner) { it?.let(::handleResult) } @@ -242,11 +242,12 @@ class FeedFragment : BaseStateFragment() { .create() .show() return true - } else if (item.itemId == R.id.menu_item_feed_toggle_played_items) { - showPlayedItems = !item.isChecked - updateTogglePlayedItemsButton(item) - viewModel.togglePlayedItems(showPlayedItems) - viewModel.saveShowPlayedItemsToPreferences(showPlayedItems) + } else if (item.itemId == R.id.menu_item_feed_toggle_show_all_items) { + setShowPlayedItemsMethod(item, ShowItems.DEFAULT) + } else if (item.itemId == R.id.menu_item_feed_toggle_show_played_items) { + setShowPlayedItemsMethod(item, ShowItems.WATCHED) + } else if (item.itemId == R.id.menu_item_feed_toggle_partially_played_items) { + setShowPlayedItemsMethod(item, ShowItems.PARTIALLY_WATCHED) } else if (item.itemId == R.id.menu_item_feed_toggle_future_items) { showFutureItems = !item.isChecked updateToggleFutureItemsButton(item) @@ -257,6 +258,13 @@ class FeedFragment : BaseStateFragment() { return super.onOptionsItemSelected(item) } + private fun setShowPlayedItemsMethod(item: MenuItem, showItems: ShowItems) { + showPlayedItems = showItems + viewModel.togglePlayedItems(showPlayedItems) + updateTogglePlayedItemsButton(item) + viewModel.saveShowPlayedItemsToPreferences(showPlayedItems) + } + override fun onDestroyOptionsMenu() { super.onDestroyOptionsMenu() activity?.supportActionBar?.subtitle = null @@ -284,19 +292,9 @@ class FeedFragment : BaseStateFragment() { } private fun updateTogglePlayedItemsButton(menuItem: MenuItem) { - menuItem.isChecked = showPlayedItems - menuItem.icon = AppCompatResources.getDrawable( - requireContext(), - if (showPlayedItems) R.drawable.ic_visibility_on else R.drawable.ic_visibility_off - ) MenuItemCompat.setTooltipText( menuItem, - getString( - if (showPlayedItems) - R.string.feed_toggle_hide_played_items - else - R.string.feed_toggle_show_played_items - ) + getString(R.string.feed_toggle_show_hide_played_items) ) } diff --git a/app/src/main/java/org/schabi/newpipe/local/feed/FeedViewModel.kt b/app/src/main/java/org/schabi/newpipe/local/feed/FeedViewModel.kt index 76d5e9d63..5785c8e3f 100644 --- a/app/src/main/java/org/schabi/newpipe/local/feed/FeedViewModel.kt +++ b/app/src/main/java/org/schabi/newpipe/local/feed/FeedViewModel.kt @@ -28,15 +28,18 @@ import org.schabi.newpipe.util.DEFAULT_THROTTLE_TIMEOUT import java.time.OffsetDateTime import java.util.concurrent.TimeUnit +enum class ShowItems { + WATCHED, PARTIALLY_WATCHED, DEFAULT +} class FeedViewModel( private val application: Application, groupId: Long = FeedGroupEntity.GROUP_ALL_ID, - initialShowPlayedItems: Boolean = true, + initialShowPlayedItems: ShowItems = ShowItems.DEFAULT, initialShowFutureItems: Boolean = true ) : ViewModel() { private val feedDatabaseManager = FeedDatabaseManager(application) - private val toggleShowPlayedItems = BehaviorProcessor.create() + private val toggleShowPlayedItems = BehaviorProcessor.create() private val toggleShowPlayedItemsFlowable = toggleShowPlayedItems .startWithItem(initialShowPlayedItems) .distinctUntilChanged() @@ -57,7 +60,7 @@ class FeedViewModel( feedDatabaseManager.notLoadedCount(groupId), feedDatabaseManager.oldestSubscriptionUpdate(groupId), - Function5 { t1: FeedEventManager.Event, t2: Boolean, t3: Boolean, + Function5 { t1: FeedEventManager.Event, t2: ShowItems, t3: Boolean, t4: Long, t5: List -> return@Function5 CombineResultEventHolder(t1, t2, t3, t4, t5.firstOrNull()) } @@ -66,12 +69,21 @@ class FeedViewModel( .subscribeOn(Schedulers.io()) .observeOn(Schedulers.io()) .map { (event, showPlayedItems, showFutureItems, notLoadedCount, oldestUpdate) -> - val streamItems = if (event is SuccessResultEvent || event is IdleEvent) + val streamItems = if (event is SuccessResultEvent || event is IdleEvent) { feedDatabaseManager - .getStreams(groupId, showPlayedItems, showFutureItems) + .getStreams( + groupId, + !( + showPlayedItems == ShowItems.WATCHED || + showPlayedItems == ShowItems.PARTIALLY_WATCHED + ), + showPlayedItems != ShowItems.PARTIALLY_WATCHED, + showFutureItems + ) .blockingGet(arrayListOf()) - else + } else { arrayListOf() + } CombineResultDataHolder(event, streamItems, notLoadedCount, oldestUpdate) } @@ -98,7 +110,7 @@ class FeedViewModel( private data class CombineResultEventHolder( val t1: FeedEventManager.Event, - val t2: Boolean, + val t2: ShowItems, val t3: Boolean, val t4: Long, val t5: OffsetDateTime? @@ -111,17 +123,20 @@ class FeedViewModel( val t4: OffsetDateTime? ) - fun togglePlayedItems(showPlayedItems: Boolean) { - toggleShowPlayedItems.onNext(showPlayedItems) + fun togglePlayedItems(showItems: ShowItems) { + toggleShowPlayedItems.onNext(showItems) } - fun saveShowPlayedItemsToPreferences(showPlayedItems: Boolean) = + fun saveShowPlayedItemsToPreferences(showItems: ShowItems) = PreferenceManager.getDefaultSharedPreferences(application).edit { - this.putBoolean(application.getString(R.string.feed_show_played_items_key), showPlayedItems) + this.putString( + application.getString(R.string.feed_show_played_items_key), + showItems.toString() + ) this.apply() } - fun getShowPlayedItemsFromPreferences() = getShowPlayedItemsFromPreferences(application) + fun getItemsVisibilityFromPreferences() = getItemsVisibilityFromPreferences(application) fun toggleFutureItems(showFutureItems: Boolean) { toggleShowFutureItems.onNext(showFutureItems) @@ -136,9 +151,16 @@ class FeedViewModel( fun getShowFutureItemsFromPreferences() = getShowFutureItemsFromPreferences(application) companion object { - private fun getShowPlayedItemsFromPreferences(context: Context) = - PreferenceManager.getDefaultSharedPreferences(context) - .getBoolean(context.getString(R.string.feed_show_played_items_key), true) + + private fun getItemsVisibilityFromPreferences(context: Context): ShowItems { + val s = PreferenceManager.getDefaultSharedPreferences(context) + .getString( + context.getString(R.string.feed_show_played_items_key), + ShowItems.DEFAULT.toString() + ) ?: ShowItems.DEFAULT.toString() + return ShowItems.valueOf(s) + } + private fun getShowFutureItemsFromPreferences(context: Context) = PreferenceManager.getDefaultSharedPreferences(context) .getBoolean(context.getString(R.string.feed_show_future_items_key), true) @@ -148,7 +170,7 @@ class FeedViewModel( App.getApp(), groupId, // Read initial value from preferences - getShowPlayedItemsFromPreferences(context.applicationContext), + getItemsVisibilityFromPreferences(context.applicationContext), getShowFutureItemsFromPreferences(context.applicationContext) ) } diff --git a/app/src/main/res/menu/menu_feed_fragment.xml b/app/src/main/res/menu/menu_feed_fragment.xml index 9e5cc862e..fc371b2fe 100644 --- a/app/src/main/res/menu/menu_feed_fragment.xml +++ b/app/src/main/res/menu/menu_feed_fragment.xml @@ -4,12 +4,23 @@ + android:title="@string/feed_toggle_show_hide_played_items" + app:showAsAction="ifRoom"> + + + + + + feed_update_threshold_key 300 - feed_show_played_items + feed_show_items feed_show_future_items show_thumbnail_key diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 5a4ce92f2..46a3cad74 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -691,8 +691,7 @@ \nYouTube is an example of a service that offers this fast method with its RSS feed. \n \nSo the choice boils down to what you prefer: speed or precise information. - Show watched items - Hide watched items + Show/hide watched items This content is not yet supported by NewPipe.\n\nIt will hopefully be supported in a future version. Channel\'s avatar thumbnail Created by %s @@ -760,5 +759,8 @@ Unknown quality Show future items Hide future items + Hide Watched and Partially Watched + Hide Watched + Show All Sort \ No newline at end of file diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 5116c5b18..ae04661ee 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists distributionUrl=https\://services.gradle.org/distributions/gradle-7.5.1-bin.zip -distributionSha256Sum=f6b8596b10cce501591e92f229816aa4046424f3b24d771751b06779d58c8ec4 zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists