Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add option to remove partially watched videos from the 'Whats new' feed #9747

Merged
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions app/src/main/java/org/schabi/newpipe/database/feed/dao/FeedDAO.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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(
"""
Expand Down Expand Up @@ -66,6 +67,15 @@ 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)
OR (sst.progress_time >= s.duration * 1000 - ${StreamStateEntity.PLAYBACK_FINISHED_END_MILLISECONDS}
AND sst.progress_time >= s.duration * 1000 * 3 / 4)
)
AND (
:uploadDateBefore IS NULL
OR s.upload_date IS NULL
Expand All @@ -79,6 +89,7 @@ abstract class FeedDAO {
abstract fun getStreams(
groupId: Long,
includePlayed: Boolean,
includePartiallyPlayed: Boolean,
uploadDateBefore: OffsetDateTime?
): Maybe<List<StreamWithState>>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,11 +43,13 @@ class FeedDatabaseManager(context: Context) {
fun getStreams(
groupId: Long,
includePlayedStreams: Boolean,
includePartiallyPlayedStreams: Boolean,
includeFutureStreams: Boolean
): Maybe<List<StreamWithState>> {
return feedTable.getStreams(
groupId,
includePlayedStreams,
includePartiallyPlayedStreams,
if (includeFutureStreams) null else OffsetDateTime.now()
)
}
Expand Down
83 changes: 37 additions & 46 deletions app/src/main/java/org/schabi/newpipe/local/feed/FeedFragment.kt
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,6 @@ import android.view.View
import android.view.ViewGroup
import android.widget.Button
import androidx.appcompat.app.AlertDialog
import androidx.appcompat.content.res.AppCompatResources
import androidx.core.content.edit
import androidx.core.math.MathUtils
import androidx.core.os.bundleOf
Expand Down Expand Up @@ -101,6 +100,7 @@ class FeedFragment : BaseStateFragment<FeedState>() {

private lateinit var groupAdapter: GroupieAdapter
@State @JvmField var showPlayedItems: Boolean = true
@State @JvmField var showPartiallyPlayedItems: Boolean = true
@State @JvmField var showFutureItems: Boolean = true
Jared234 marked this conversation as resolved.
Show resolved Hide resolved

private var onSettingsChangeListener: SharedPreferences.OnSharedPreferenceChangeListener? = null
Expand Down Expand Up @@ -141,6 +141,7 @@ class FeedFragment : BaseStateFragment<FeedState>() {
val factory = FeedViewModel.getFactory(requireContext(), groupId)
viewModel = ViewModelProvider(this, factory)[FeedViewModel::class.java]
showPlayedItems = viewModel.getShowPlayedItemsFromPreferences()
showPartiallyPlayedItems = viewModel.getShowPartiallyPlayedItemsFromPreferences()
showFutureItems = viewModel.getShowFutureItemsFromPreferences()
viewModel.stateLiveData.observe(viewLifecycleOwner) { it?.let(::handleResult) }

Expand Down Expand Up @@ -216,8 +217,10 @@ class FeedFragment : BaseStateFragment<FeedState>() {
activity.supportActionBar?.subtitle = groupName

inflater.inflate(R.menu.menu_feed_fragment, menu)
updateTogglePlayedItemsButton(menu.findItem(R.id.menu_item_feed_toggle_played_items))
updateToggleFutureItemsButton(menu.findItem(R.id.menu_item_feed_toggle_future_items))
MenuItemCompat.setTooltipText(
menu.findItem(R.id.menu_item_feed_toggle_played_items),
getString(R.string.feed_show_hide_streams)
)
Jared234 marked this conversation as resolved.
Show resolved Hide resolved
}

override fun onOptionsItemSelected(item: MenuItem): Boolean {
Expand All @@ -243,20 +246,42 @@ class FeedFragment : BaseStateFragment<FeedState>() {
.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_future_items) {
showFutureItems = !item.isChecked
updateToggleFutureItemsButton(item)
viewModel.toggleFutureItems(showFutureItems)
viewModel.saveShowFutureItemsToPreferences(showFutureItems)
showStreamVisibilityDialog()
}

return super.onOptionsItemSelected(item)
}

private fun showStreamVisibilityDialog() {
val dialogItems = arrayOf(
getString(R.string.feed_show_watched),
getString(R.string.feed_show_partially_watched),
getString(R.string.feed_show_upcoming)
)

val checkedDialogItems = booleanArrayOf(showPlayedItems, showPartiallyPlayedItems, showFutureItems)

val builder = AlertDialog.Builder(context!!)
builder.setTitle(R.string.feed_hide_streams_title)
builder.setMultiChoiceItems(dialogItems, checkedDialogItems) { _, which, isChecked ->
checkedDialogItems[which] = isChecked
}

builder.setPositiveButton(R.string.ok) { _, _ ->
showPlayedItems = checkedDialogItems[0]
viewModel.setSaveShowPlayedItems(showPlayedItems)

showPartiallyPlayedItems = checkedDialogItems[1]
viewModel.setSaveShowPartiallyPlayedItems(showPartiallyPlayedItems)

showFutureItems = checkedDialogItems[2]
viewModel.setSaveShowFutureItems(showFutureItems)
}
builder.setNegativeButton(R.string.cancel, null)

builder.create().show()
}

override fun onDestroyOptionsMenu() {
super.onDestroyOptionsMenu()
activity?.supportActionBar?.subtitle = null
Expand All @@ -283,40 +308,6 @@ class FeedFragment : BaseStateFragment<FeedState>() {
super.onDestroyView()
}

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
)
)
}

private fun updateToggleFutureItemsButton(menuItem: MenuItem) {
menuItem.isChecked = showFutureItems
menuItem.icon = AppCompatResources.getDrawable(
requireContext(),
if (showFutureItems) R.drawable.ic_history_future else R.drawable.ic_history
)
MenuItemCompat.setTooltipText(
menuItem,
getString(
if (showFutureItems)
R.string.feed_toggle_hide_future_items
else
R.string.feed_toggle_show_future_items
)
)
}

// //////////////////////////////////////////////////////////////////////////
// Handling
// //////////////////////////////////////////////////////////////////////////
Expand Down
71 changes: 45 additions & 26 deletions app/src/main/java/org/schabi/newpipe/local/feed/FeedViewModel.kt
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import androidx.lifecycle.viewmodel.viewModelFactory
import androidx.preference.PreferenceManager
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers
import io.reactivex.rxjava3.core.Flowable
import io.reactivex.rxjava3.functions.Function5
import io.reactivex.rxjava3.functions.Function6
import io.reactivex.rxjava3.processors.BehaviorProcessor
import io.reactivex.rxjava3.schedulers.Schedulers
import org.schabi.newpipe.App
Expand All @@ -31,18 +31,24 @@ import java.util.concurrent.TimeUnit
class FeedViewModel(
private val application: Application,
groupId: Long = FeedGroupEntity.GROUP_ALL_ID,
initialShowPlayedItems: Boolean = true,
initialShowFutureItems: Boolean = true
initialShowPlayedItems: Boolean,
initialShowPartiallyPlayedItems: Boolean,
initialShowFutureItems: Boolean
) : ViewModel() {
private val feedDatabaseManager = FeedDatabaseManager(application)

private val toggleShowPlayedItems = BehaviorProcessor.create<Boolean>()
private val toggleShowPlayedItemsFlowable = toggleShowPlayedItems
private val showPlayedItems = BehaviorProcessor.create<Boolean>()
private val showPlayedItemsFlowable = showPlayedItems
.startWithItem(initialShowPlayedItems)
.distinctUntilChanged()

private val toggleShowFutureItems = BehaviorProcessor.create<Boolean>()
private val toggleShowFutureItemsFlowable = toggleShowFutureItems
private val showPartiallyPlayedItems = BehaviorProcessor.create<Boolean>()
private val showPartiallyPlayedItemsFlowable = showPartiallyPlayedItems
.startWithItem(initialShowPartiallyPlayedItems)
.distinctUntilChanged()

private val showFutureItems = BehaviorProcessor.create<Boolean>()
private val showFutureItemsFlowable = showFutureItems
.startWithItem(initialShowFutureItems)
.distinctUntilChanged()

Expand All @@ -52,23 +58,24 @@ class FeedViewModel(
private var combineDisposable = Flowable
.combineLatest(
FeedEventManager.events(),
toggleShowPlayedItemsFlowable,
toggleShowFutureItemsFlowable,
showPlayedItemsFlowable,
showPartiallyPlayedItemsFlowable,
showFutureItemsFlowable,
feedDatabaseManager.notLoadedCount(groupId),
feedDatabaseManager.oldestSubscriptionUpdate(groupId),

Function5 { t1: FeedEventManager.Event, t2: Boolean, t3: Boolean,
t4: Long, t5: List<OffsetDateTime> ->
return@Function5 CombineResultEventHolder(t1, t2, t3, t4, t5.firstOrNull())
Function6 { t1: FeedEventManager.Event, t2: Boolean, t3: Boolean, t4: Boolean,
t5: Long, t6: List<OffsetDateTime> ->
return@Function6 CombineResultEventHolder(t1, t2, t3, t4, t5, t6.firstOrNull())
}
)
.throttleLatest(DEFAULT_THROTTLE_TIMEOUT, TimeUnit.MILLISECONDS)
.subscribeOn(Schedulers.io())
.observeOn(Schedulers.io())
.map { (event, showPlayedItems, showFutureItems, notLoadedCount, oldestUpdate) ->
.map { (event, showPlayedItems, showPartiallyPlayedItems, showFutureItems, notLoadedCount, oldestUpdate) ->
val streamItems = if (event is SuccessResultEvent || event is IdleEvent)
feedDatabaseManager
.getStreams(groupId, showPlayedItems, showFutureItems)
.getStreams(groupId, showPlayedItems, showPartiallyPlayedItems, showFutureItems)
.blockingGet(arrayListOf())
else
arrayListOf()
Expand Down Expand Up @@ -100,8 +107,9 @@ class FeedViewModel(
val t1: FeedEventManager.Event,
val t2: Boolean,
val t3: Boolean,
val t4: Long,
val t5: OffsetDateTime?
val t4: Boolean,
val t5: Long,
val t6: OffsetDateTime?
)

private data class CombineResultDataHolder(
Expand All @@ -111,34 +119,44 @@ class FeedViewModel(
val t4: OffsetDateTime?
)

fun togglePlayedItems(showPlayedItems: Boolean) {
toggleShowPlayedItems.onNext(showPlayedItems)
}

fun saveShowPlayedItemsToPreferences(showPlayedItems: Boolean) =
fun setSaveShowPlayedItems(showPlayedItems: Boolean) {
this.showPlayedItems.onNext(showPlayedItems)
PreferenceManager.getDefaultSharedPreferences(application).edit {
this.putBoolean(application.getString(R.string.feed_show_played_items_key), showPlayedItems)
this.putBoolean(application.getString(R.string.feed_show_watched_items_key), showPlayedItems)
this.apply()
}
}

fun getShowPlayedItemsFromPreferences() = getShowPlayedItemsFromPreferences(application)

fun toggleFutureItems(showFutureItems: Boolean) {
toggleShowFutureItems.onNext(showFutureItems)
fun setSaveShowPartiallyPlayedItems(showPartiallyPlayedItems: Boolean) {
this.showPartiallyPlayedItems.onNext(showPartiallyPlayedItems)
PreferenceManager.getDefaultSharedPreferences(application).edit {
this.putBoolean(application.getString(R.string.feed_show_partially_watched_items_key), showPartiallyPlayedItems)
this.apply()
}
}

fun saveShowFutureItemsToPreferences(showFutureItems: Boolean) =
fun getShowPartiallyPlayedItemsFromPreferences() = getShowPartiallyPlayedItemsFromPreferences(application)

fun setSaveShowFutureItems(showFutureItems: Boolean) {
this.showFutureItems.onNext(showFutureItems)
PreferenceManager.getDefaultSharedPreferences(application).edit {
this.putBoolean(application.getString(R.string.feed_show_future_items_key), showFutureItems)
this.apply()
}
}

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)
.getBoolean(context.getString(R.string.feed_show_watched_items_key), true)

Stypox marked this conversation as resolved.
Show resolved Hide resolved
private fun getShowPartiallyPlayedItemsFromPreferences(context: Context) =
PreferenceManager.getDefaultSharedPreferences(context)
.getBoolean(context.getString(R.string.feed_show_partially_watched_items_key), true)
private fun getShowFutureItemsFromPreferences(context: Context) =
PreferenceManager.getDefaultSharedPreferences(context)
.getBoolean(context.getString(R.string.feed_show_future_items_key), true)
Stypox marked this conversation as resolved.
Show resolved Hide resolved
Expand All @@ -149,6 +167,7 @@ class FeedViewModel(
groupId,
// Read initial value from preferences
getShowPlayedItemsFromPreferences(context.applicationContext),
getShowPartiallyPlayedItemsFromPreferences(context.applicationContext),
getShowFutureItemsFromPreferences(context.applicationContext)
)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ public HistoryRecordManager(final Context context) {
* Marks a stream item as watched such that it is hidden from the feed if watched videos are
* hidden. Adds a history entry and updates the stream progress to 100%.
*
* @see FeedViewModel#togglePlayedItems
* @see FeedViewModel#setSaveShowPlayedItems
* @param info the item to mark as watched
* @return a Maybe containing the ID of the item if successful
*/
Expand Down
13 changes: 1 addition & 12 deletions app/src/main/res/menu/menu_feed_fragment.xml
Original file line number Diff line number Diff line change
Expand Up @@ -5,19 +5,8 @@
<item
android:id="@+id/menu_item_feed_toggle_played_items"
android:orderInCategory="2"
android:checkable="true"
android:checked="true"
android:icon="@drawable/ic_visibility_on"
android:title="@string/feed_toggle_show_played_items"
app:showAsAction="ifRoom" />

<item
android:id="@+id/menu_item_feed_toggle_future_items"
android:orderInCategory="3"
android:checkable="true"
android:checked="true"
android:icon="@drawable/ic_history_future"
android:title="@string/feed_toggle_show_future_items"
android:title="@string/feed_show_hide_streams"
app:showAsAction="ifRoom" />

<item
Expand Down
3 changes: 2 additions & 1 deletion app/src/main/res/values/settings_keys.xml
Original file line number Diff line number Diff line change
Expand Up @@ -283,7 +283,8 @@

<string name="feed_update_threshold_key">feed_update_threshold_key</string>
<string name="feed_update_threshold_default_value">300</string>
<string name="feed_show_played_items_key">feed_show_played_items</string>
<string name="feed_show_watched_items_key">feed_show_watched_items</string>
Jared234 marked this conversation as resolved.
Show resolved Hide resolved
<string name="feed_show_partially_watched_items_key">feed_show_partially_watched_items</string>
<string name="feed_show_future_items_key">feed_show_future_items</string>

<string name="show_thumbnail_key">show_thumbnail_key</string>
Expand Down
7 changes: 5 additions & 2 deletions app/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -691,8 +691,8 @@
\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.</string>
<string name="feed_toggle_show_played_items">Show watched items</string>
<string name="feed_toggle_hide_played_items">Hide watched items</string>
<string name="feed_hide_streams_title">Show the following streams</string>
<string name="feed_show_hide_streams">Show/Hide streams</string>
<string name="content_not_supported">This content is not yet supported by NewPipe.\n\nIt will hopefully be supported in a future version.</string>
<string name="detail_sub_channel_thumbnail_view_description">Channel\'s avatar thumbnail</string>
<string name="channel_created_by">Created by %s</string>
Expand Down Expand Up @@ -760,5 +760,8 @@
<string name="unknown_quality">Unknown quality</string>
<string name="feed_toggle_show_future_items">Show future items</string>
<string name="feed_toggle_hide_future_items">Hide future items</string>
<string name="feed_show_watched">Fully watched</string>
<string name="feed_show_partially_watched">Partially watched</string>
<string name="feed_show_upcoming">Upcoming</string>
<string name="sort">Sort</string>
</resources>