Implemented the feature using multiple checkboxes

This commit is contained in:
Jared Fantaye 2023-02-04 18:48:27 +01:00
parent 9c82441c19
commit cd8d57040c
8 changed files with 107 additions and 158 deletions

View file

@ -73,6 +73,8 @@ abstract class FeedDAO {
OR sst.stream_id IS NULL OR sst.stream_id IS NULL
OR (sst.progress_time <= ${StreamStateEntity.PLAYBACK_SAVE_THRESHOLD_START_MILLISECONDS} OR (sst.progress_time <= ${StreamStateEntity.PLAYBACK_SAVE_THRESHOLD_START_MILLISECONDS}
AND sst.progress_time <= s.duration * 1000 / 4) AND sst.progress_time <= s.duration * 1000 / 4)
OR (sst.progress_time >= s.duration * 1000 - ${StreamStateEntity.PLAYBACK_FINISHED_END_MILLISECONDS}
OR sst.progress_time >= s.duration * 1000 * 3 / 4)
) )
AND ( AND (
:uploadDateBefore IS NULL :uploadDateBefore IS NULL

View file

@ -37,7 +37,6 @@ import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import android.widget.Button import android.widget.Button
import androidx.appcompat.app.AlertDialog import androidx.appcompat.app.AlertDialog
import androidx.appcompat.content.res.AppCompatResources
import androidx.core.content.edit import androidx.core.content.edit
import androidx.core.math.MathUtils import androidx.core.math.MathUtils
import androidx.core.os.bundleOf import androidx.core.os.bundleOf
@ -100,13 +99,10 @@ class FeedFragment : BaseStateFragment<FeedState>() {
private var oldestSubscriptionUpdate: OffsetDateTime? = null private var oldestSubscriptionUpdate: OffsetDateTime? = null
private lateinit var groupAdapter: GroupieAdapter private lateinit var groupAdapter: GroupieAdapter
@State @JvmField var feedVisibilityStatus: StreamVisibilityStatus = StreamVisibilityStatus.DEFAULT @State @JvmField var showPlayedItems: Boolean = true
@State @JvmField var showPartiallyPlayedItems: Boolean = true
@State @JvmField var showFutureItems: Boolean = true @State @JvmField var showFutureItems: Boolean = true
private lateinit var showAllMenuItem: MenuItem
private lateinit var hideWatchedMenuItem: MenuItem
private lateinit var hidePartiallyWatchedMenuItem: MenuItem
private var onSettingsChangeListener: SharedPreferences.OnSharedPreferenceChangeListener? = null private var onSettingsChangeListener: SharedPreferences.OnSharedPreferenceChangeListener? = null
private var updateListViewModeOnResume = false private var updateListViewModeOnResume = false
private var isRefreshing = false private var isRefreshing = false
@ -144,7 +140,8 @@ class FeedFragment : BaseStateFragment<FeedState>() {
val factory = FeedViewModel.getFactory(requireContext(), groupId) val factory = FeedViewModel.getFactory(requireContext(), groupId)
viewModel = ViewModelProvider(this, factory)[FeedViewModel::class.java] viewModel = ViewModelProvider(this, factory)[FeedViewModel::class.java]
feedVisibilityStatus = viewModel.getItemsVisibilityFromPreferences() showPlayedItems = viewModel.getShowPlayedItemsFromPreferences()
showPartiallyPlayedItems = viewModel.getShowPartiallyPlayedItemsFromPreferences()
showFutureItems = viewModel.getShowFutureItemsFromPreferences() showFutureItems = viewModel.getShowFutureItemsFromPreferences()
viewModel.stateLiveData.observe(viewLifecycleOwner) { it?.let(::handleResult) } viewModel.stateLiveData.observe(viewLifecycleOwner) { it?.let(::handleResult) }
@ -220,16 +217,10 @@ class FeedFragment : BaseStateFragment<FeedState>() {
activity.supportActionBar?.subtitle = groupName activity.supportActionBar?.subtitle = groupName
inflater.inflate(R.menu.menu_feed_fragment, menu) inflater.inflate(R.menu.menu_feed_fragment, menu)
MenuItemCompat.setTooltipText(
val itemVisibilityMenu = menu.findItem(R.id.menu_item_feed_toggle_played_items).subMenu menu.findItem(R.id.menu_item_feed_toggle_played_items),
if (itemVisibilityMenu != null) { getString(R.string.feed_show_hide_streams)
showAllMenuItem = itemVisibilityMenu.findItem(R.id.menu_item_feed_toggle_show_all_items) )
hideWatchedMenuItem = itemVisibilityMenu.findItem(R.id.menu_item_feed_toggle_show_played_items)
hidePartiallyWatchedMenuItem = itemVisibilityMenu.findItem(R.id.menu_item_feed_toggle_partially_played_items)
}
updateItemVisibilityMenu(menu.findItem(R.id.menu_item_feed_toggle_played_items))
updateToggleFutureItemsButton(menu.findItem(R.id.menu_item_feed_toggle_future_items))
} }
override fun onOptionsItemSelected(item: MenuItem): Boolean { override fun onOptionsItemSelected(item: MenuItem): Boolean {
@ -254,27 +245,44 @@ class FeedFragment : BaseStateFragment<FeedState>() {
.create() .create()
.show() .show()
return true return true
} else if (item.itemId == R.id.menu_item_feed_toggle_show_all_items) { } else if (item.itemId == R.id.menu_item_feed_toggle_played_items) {
changeItemsVisibilityStatus(item, StreamVisibilityStatus.DEFAULT) showStreamVisibilityDialog()
} else if (item.itemId == R.id.menu_item_feed_toggle_show_played_items) {
changeItemsVisibilityStatus(item, StreamVisibilityStatus.HIDE_WATCHED)
} else if (item.itemId == R.id.menu_item_feed_toggle_partially_played_items) {
changeItemsVisibilityStatus(item, StreamVisibilityStatus.HIDE_PARTIALLY_WATCHED)
} else if (item.itemId == R.id.menu_item_feed_toggle_future_items) {
showFutureItems = !item.isChecked
updateToggleFutureItemsButton(item)
viewModel.toggleFutureItems(showFutureItems)
viewModel.saveShowFutureItemsToPreferences(showFutureItems)
} }
return super.onOptionsItemSelected(item) return super.onOptionsItemSelected(item)
} }
private fun changeItemsVisibilityStatus(item: MenuItem, streamVisibilityStatus: StreamVisibilityStatus) { private fun showStreamVisibilityDialog() {
feedVisibilityStatus = streamVisibilityStatus val dialogItems = arrayOf(
viewModel.changeVisibilityState(feedVisibilityStatus) getString(R.string.feed_show_watched),
updateItemVisibilityMenu(item) getString(R.string.feed_show_partially_watched),
viewModel.saveStreamVisibilityStateToPreferences(feedVisibilityStatus) 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.setShowPlayedItems(showPlayedItems)
viewModel.saveShowPlayedItemsToPreferences(showPlayedItems)
showPartiallyPlayedItems = !checkedDialogItems[1]
viewModel.setShowPartiallyPlayedItems(showPartiallyPlayedItems)
viewModel.saveShowPartiallyPlayedItemsToPreferences(showPartiallyPlayedItems)
showFutureItems = !checkedDialogItems[2]
viewModel.setShowFutureItems(showFutureItems)
viewModel.saveShowFutureItemsToPreferences(showFutureItems)
}
builder.setNegativeButton(R.string.cancel, null)
builder.create().show()
} }
override fun onDestroyOptionsMenu() { override fun onDestroyOptionsMenu() {
@ -303,48 +311,6 @@ class FeedFragment : BaseStateFragment<FeedState>() {
super.onDestroyView() super.onDestroyView()
} }
private fun updateItemVisibilityMenu(menuItem: MenuItem) {
when (feedVisibilityStatus) {
StreamVisibilityStatus.DEFAULT -> {
showAllMenuItem.isVisible = false
hideWatchedMenuItem.isVisible = true
hidePartiallyWatchedMenuItem.isVisible = true
}
StreamVisibilityStatus.HIDE_WATCHED -> {
showAllMenuItem.isVisible = true
hideWatchedMenuItem.isVisible = false
hidePartiallyWatchedMenuItem.isVisible = true
}
else -> {
showAllMenuItem.isVisible = true
hideWatchedMenuItem.isVisible = true
hidePartiallyWatchedMenuItem.isVisible = false
}
}
MenuItemCompat.setTooltipText(
menuItem,
getString(R.string.feed_change_stream_visibility_state)
)
}
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 // Handling
// ////////////////////////////////////////////////////////////////////////// // //////////////////////////////////////////////////////////////////////////

View file

@ -11,7 +11,7 @@ import androidx.lifecycle.viewmodel.viewModelFactory
import androidx.preference.PreferenceManager import androidx.preference.PreferenceManager
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers
import io.reactivex.rxjava3.core.Flowable 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.processors.BehaviorProcessor
import io.reactivex.rxjava3.schedulers.Schedulers import io.reactivex.rxjava3.schedulers.Schedulers
import org.schabi.newpipe.App import org.schabi.newpipe.App
@ -31,18 +31,24 @@ import java.util.concurrent.TimeUnit
class FeedViewModel( class FeedViewModel(
private val application: Application, private val application: Application,
groupId: Long = FeedGroupEntity.GROUP_ALL_ID, groupId: Long = FeedGroupEntity.GROUP_ALL_ID,
initialStreamVisibility: StreamVisibilityStatus = StreamVisibilityStatus.DEFAULT, initialShowPlayedItems: Boolean = true,
initialShowPartiallyPlayedItems: Boolean = true,
initialShowFutureItems: Boolean = true initialShowFutureItems: Boolean = true
) : ViewModel() { ) : ViewModel() {
private val feedDatabaseManager = FeedDatabaseManager(application) private val feedDatabaseManager = FeedDatabaseManager(application)
private val streamVisibilityState = BehaviorProcessor.create<StreamVisibilityStatus>() private val showPlayedItems = BehaviorProcessor.create<Boolean>()
private val streamVisibilityStateFlowable = streamVisibilityState private val showPlayedItemsFlowable = showPlayedItems
.startWithItem(initialStreamVisibility) .startWithItem(initialShowPlayedItems)
.distinctUntilChanged() .distinctUntilChanged()
private val toggleShowFutureItems = BehaviorProcessor.create<Boolean>() private val showPartiallyPlayedItems = BehaviorProcessor.create<Boolean>()
private val toggleShowFutureItemsFlowable = toggleShowFutureItems private val showPartiallyPlayedItemsFlowable = showPartiallyPlayedItems
.startWithItem(initialShowPartiallyPlayedItems)
.distinctUntilChanged()
private val showFutureItems = BehaviorProcessor.create<Boolean>()
private val showFutureItemsFlowable = showFutureItems
.startWithItem(initialShowFutureItems) .startWithItem(initialShowFutureItems)
.distinctUntilChanged() .distinctUntilChanged()
@ -52,35 +58,27 @@ class FeedViewModel(
private var combineDisposable = Flowable private var combineDisposable = Flowable
.combineLatest( .combineLatest(
FeedEventManager.events(), FeedEventManager.events(),
streamVisibilityStateFlowable, showPlayedItemsFlowable,
toggleShowFutureItemsFlowable, showPartiallyPlayedItemsFlowable,
showFutureItemsFlowable,
feedDatabaseManager.notLoadedCount(groupId), feedDatabaseManager.notLoadedCount(groupId),
feedDatabaseManager.oldestSubscriptionUpdate(groupId), feedDatabaseManager.oldestSubscriptionUpdate(groupId),
Function5 { t1: FeedEventManager.Event, t2: StreamVisibilityStatus, t3: Boolean, Function6 { t1: FeedEventManager.Event, t2: Boolean, t3: Boolean, t4: Boolean,
t4: Long, t5: List<OffsetDateTime> -> t5: Long, t6: List<OffsetDateTime> ->
return@Function5 CombineResultEventHolder(t1, t2, t3, t4, t5.firstOrNull()) return@Function6 CombineResultEventHolder(t1, t2, t3, t4, t5, t6.firstOrNull())
} }
) )
.throttleLatest(DEFAULT_THROTTLE_TIMEOUT, TimeUnit.MILLISECONDS) .throttleLatest(DEFAULT_THROTTLE_TIMEOUT, TimeUnit.MILLISECONDS)
.subscribeOn(Schedulers.io()) .subscribeOn(Schedulers.io())
.observeOn(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) { val streamItems = if (event is SuccessResultEvent || event is IdleEvent)
feedDatabaseManager feedDatabaseManager
.getStreams( .getStreams(groupId, showPlayedItems, showPartiallyPlayedItems, showFutureItems)
groupId,
!(
showPlayedItems == StreamVisibilityStatus.HIDE_WATCHED ||
showPlayedItems == StreamVisibilityStatus.HIDE_PARTIALLY_WATCHED
),
showPlayedItems != StreamVisibilityStatus.HIDE_PARTIALLY_WATCHED,
showFutureItems
)
.blockingGet(arrayListOf()) .blockingGet(arrayListOf())
} else { else
arrayListOf() arrayListOf()
}
CombineResultDataHolder(event, streamItems, notLoadedCount, oldestUpdate) CombineResultDataHolder(event, streamItems, notLoadedCount, oldestUpdate)
} }
@ -107,10 +105,11 @@ class FeedViewModel(
private data class CombineResultEventHolder( private data class CombineResultEventHolder(
val t1: FeedEventManager.Event, val t1: FeedEventManager.Event,
val t2: StreamVisibilityStatus, val t2: Boolean,
val t3: Boolean, val t3: Boolean,
val t4: Long, val t4: Boolean,
val t5: OffsetDateTime? val t5: Long,
val t6: OffsetDateTime?
) )
private data class CombineResultDataHolder( private data class CombineResultDataHolder(
@ -120,23 +119,32 @@ class FeedViewModel(
val t4: OffsetDateTime? val t4: OffsetDateTime?
) )
fun changeVisibilityState(streamVisibilityStatus: StreamVisibilityStatus) { fun setShowPlayedItems(showPlayedItems: Boolean) {
streamVisibilityState.onNext(streamVisibilityStatus) this.showPlayedItems.onNext(showPlayedItems)
} }
fun saveStreamVisibilityStateToPreferences(streamVisibilityStatus: StreamVisibilityStatus) = fun saveShowPlayedItemsToPreferences(showPlayedItems: Boolean) =
PreferenceManager.getDefaultSharedPreferences(application).edit { PreferenceManager.getDefaultSharedPreferences(application).edit {
this.putString( this.putBoolean(application.getString(R.string.feed_show_watched_items_key), showPlayedItems)
application.getString(R.string.feed_stream_visibility_state_key),
streamVisibilityStatus.toString()
)
this.apply() this.apply()
} }
fun getItemsVisibilityFromPreferences() = getStreamVisibilityStateFromPreferences(application) fun getShowPlayedItemsFromPreferences() = getShowPlayedItemsFromPreferences(application)
fun toggleFutureItems(showFutureItems: Boolean) { fun setShowPartiallyPlayedItems(showPartiallyPlayedItems: Boolean) {
toggleShowFutureItems.onNext(showFutureItems) this.showPartiallyPlayedItems.onNext(showPartiallyPlayedItems)
}
fun saveShowPartiallyPlayedItemsToPreferences(showPartiallyPlayedItems: Boolean) =
PreferenceManager.getDefaultSharedPreferences(application).edit {
this.putBoolean(application.getString(R.string.feed_show_partially_watched_items_key), showPartiallyPlayedItems)
this.apply()
}
fun getShowPartiallyPlayedItemsFromPreferences() = getShowPartiallyPlayedItemsFromPreferences(application)
fun setShowFutureItems(showFutureItems: Boolean) {
this.showFutureItems.onNext(showFutureItems)
} }
fun saveShowFutureItemsToPreferences(showFutureItems: Boolean) = fun saveShowFutureItemsToPreferences(showFutureItems: Boolean) =
@ -148,16 +156,13 @@ class FeedViewModel(
fun getShowFutureItemsFromPreferences() = getShowFutureItemsFromPreferences(application) fun getShowFutureItemsFromPreferences() = getShowFutureItemsFromPreferences(application)
companion object { companion object {
private fun getShowPlayedItemsFromPreferences(context: Context) =
PreferenceManager.getDefaultSharedPreferences(context)
.getBoolean(context.getString(R.string.feed_show_watched_items_key), true)
private fun getStreamVisibilityStateFromPreferences(context: Context): StreamVisibilityStatus { private fun getShowPartiallyPlayedItemsFromPreferences(context: Context) =
val s = PreferenceManager.getDefaultSharedPreferences(context) PreferenceManager.getDefaultSharedPreferences(context)
.getString( .getBoolean(context.getString(R.string.feed_show_partially_watched_items_key), true)
context.getString(R.string.feed_stream_visibility_state_key),
StreamVisibilityStatus.DEFAULT.toString()
) ?: StreamVisibilityStatus.DEFAULT.toString()
return StreamVisibilityStatus.valueOf(s)
}
private fun getShowFutureItemsFromPreferences(context: Context) = private fun getShowFutureItemsFromPreferences(context: Context) =
PreferenceManager.getDefaultSharedPreferences(context) PreferenceManager.getDefaultSharedPreferences(context)
.getBoolean(context.getString(R.string.feed_show_future_items_key), true) .getBoolean(context.getString(R.string.feed_show_future_items_key), true)
@ -167,7 +172,8 @@ class FeedViewModel(
App.getApp(), App.getApp(),
groupId, groupId,
// Read initial value from preferences // Read initial value from preferences
getStreamVisibilityStateFromPreferences(context.applicationContext), getShowPlayedItemsFromPreferences(context.applicationContext),
getShowPartiallyPlayedItemsFromPreferences(context.applicationContext),
getShowFutureItemsFromPreferences(context.applicationContext) getShowFutureItemsFromPreferences(context.applicationContext)
) )
} }

View file

@ -1,5 +0,0 @@
package org.schabi.newpipe.local.feed
enum class StreamVisibilityStatus {
DEFAULT, HIDE_WATCHED, HIDE_PARTIALLY_WATCHED
}

View file

@ -87,7 +87,7 @@ public class HistoryRecordManager {
* Marks a stream item as watched such that it is hidden from the feed if watched videos are * 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%. * hidden. Adds a history entry and updates the stream progress to 100%.
* *
* @see FeedViewModel#changeVisibilityState * @see FeedViewModel#setShowPlayedItems
* @param info the item to mark as watched * @param info the item to mark as watched
* @return a Maybe containing the ID of the item if successful * @return a Maybe containing the ID of the item if successful
*/ */

View file

@ -4,31 +4,9 @@
<item <item
android:id="@+id/menu_item_feed_toggle_played_items" android:id="@+id/menu_item_feed_toggle_played_items"
android:checkable="false" android:orderInCategory="2"
android:checked="false"
android:icon="@drawable/ic_visibility_on" android:icon="@drawable/ic_visibility_on"
android:title="@string/feed_change_stream_visibility_state" android:title="@string/feed_show_hide_streams"
app:showAsAction="ifRoom">
<menu>
<item
android:id="@+id/menu_item_feed_toggle_show_all_items"
android:title="@string/feed_stream_visibility_show_all"/>
<item
android:id="@+id/menu_item_feed_toggle_show_played_items"
android:title="@string/feed_stream_visibility_hide_watched"/>
<item
android:id="@+id/menu_item_feed_toggle_partially_played_items"
android:title="@string/feed_stream_visibility_hide_partially_watched"/>
</menu>
</item>
<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"
app:showAsAction="ifRoom" /> app:showAsAction="ifRoom" />
<item <item

View file

@ -283,7 +283,8 @@
<string name="feed_update_threshold_key">feed_update_threshold_key</string> <string name="feed_update_threshold_key">feed_update_threshold_key</string>
<string name="feed_update_threshold_default_value">300</string> <string name="feed_update_threshold_default_value">300</string>
<string name="feed_stream_visibility_state_key">feed_stream_visibility_state</string> <string name="feed_show_watched_items_key">feed_show_watched_items</string>
<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="feed_show_future_items_key">feed_show_future_items</string>
<string name="show_thumbnail_key">show_thumbnail_key</string> <string name="show_thumbnail_key">show_thumbnail_key</string>

View file

@ -691,7 +691,8 @@
\nYouTube is an example of a service that offers this fast method with its RSS feed. \nYouTube is an example of a service that offers this fast method with its RSS feed.
\n \n
\nSo the choice boils down to what you prefer: speed or precise information.</string> \nSo the choice boils down to what you prefer: speed or precise information.</string>
<string name="feed_change_stream_visibility_state">Show/hide watched streams</string> <string name="feed_hide_streams_title">Hide 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="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="detail_sub_channel_thumbnail_view_description">Channel\'s avatar thumbnail</string>
<string name="channel_created_by">Created by %s</string> <string name="channel_created_by">Created by %s</string>
@ -759,8 +760,8 @@
<string name="unknown_quality">Unknown quality</string> <string name="unknown_quality">Unknown quality</string>
<string name="feed_toggle_show_future_items">Show future items</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_toggle_hide_future_items">Hide future items</string>
<string name="feed_stream_visibility_hide_partially_watched">Hide Watched </string> <string name="feed_show_watched">Fully Watched</string>
<string name="feed_stream_visibility_hide_watched">Hide Fully Watched</string> <string name="feed_show_partially_watched">Partially Watched</string>
<string name="feed_stream_visibility_show_all">Show All</string> <string name="feed_show_upcoming">Upcoming</string>
<string name="sort">Sort</string> <string name="sort">Sort</string>
</resources> </resources>