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 a53e5cac1..42a248ca5 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 @@ -73,6 +73,8 @@ abstract class FeedDAO { 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} + OR sst.progress_time >= s.duration * 1000 * 3 / 4) ) AND ( :uploadDateBefore IS NULL 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 9358c1654..d2c361662 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 @@ -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 @@ -100,13 +99,10 @@ class FeedFragment : BaseStateFragment() { private var oldestSubscriptionUpdate: OffsetDateTime? = null 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 - private lateinit var showAllMenuItem: MenuItem - private lateinit var hideWatchedMenuItem: MenuItem - private lateinit var hidePartiallyWatchedMenuItem: MenuItem - private var onSettingsChangeListener: SharedPreferences.OnSharedPreferenceChangeListener? = null private var updateListViewModeOnResume = false private var isRefreshing = false @@ -144,7 +140,8 @@ class FeedFragment : BaseStateFragment() { val factory = FeedViewModel.getFactory(requireContext(), groupId) viewModel = ViewModelProvider(this, factory)[FeedViewModel::class.java] - feedVisibilityStatus = viewModel.getItemsVisibilityFromPreferences() + showPlayedItems = viewModel.getShowPlayedItemsFromPreferences() + showPartiallyPlayedItems = viewModel.getShowPartiallyPlayedItemsFromPreferences() showFutureItems = viewModel.getShowFutureItemsFromPreferences() viewModel.stateLiveData.observe(viewLifecycleOwner) { it?.let(::handleResult) } @@ -220,16 +217,10 @@ class FeedFragment : BaseStateFragment() { activity.supportActionBar?.subtitle = groupName inflater.inflate(R.menu.menu_feed_fragment, menu) - - val itemVisibilityMenu = menu.findItem(R.id.menu_item_feed_toggle_played_items).subMenu - if (itemVisibilityMenu != null) { - 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)) + MenuItemCompat.setTooltipText( + menu.findItem(R.id.menu_item_feed_toggle_played_items), + getString(R.string.feed_show_hide_streams) + ) } override fun onOptionsItemSelected(item: MenuItem): Boolean { @@ -254,27 +245,44 @@ class FeedFragment : BaseStateFragment() { .create() .show() return true - } else if (item.itemId == R.id.menu_item_feed_toggle_show_all_items) { - changeItemsVisibilityStatus(item, StreamVisibilityStatus.DEFAULT) - } 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) + } else if (item.itemId == R.id.menu_item_feed_toggle_played_items) { + showStreamVisibilityDialog() } return super.onOptionsItemSelected(item) } - private fun changeItemsVisibilityStatus(item: MenuItem, streamVisibilityStatus: StreamVisibilityStatus) { - feedVisibilityStatus = streamVisibilityStatus - viewModel.changeVisibilityState(feedVisibilityStatus) - updateItemVisibilityMenu(item) - viewModel.saveStreamVisibilityStateToPreferences(feedVisibilityStatus) + 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.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() { @@ -303,48 +311,6 @@ class FeedFragment : BaseStateFragment() { 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 // ////////////////////////////////////////////////////////////////////////// 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 46c3443a8..2e85a65cb 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 @@ -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 @@ -31,18 +31,24 @@ import java.util.concurrent.TimeUnit class FeedViewModel( private val application: Application, groupId: Long = FeedGroupEntity.GROUP_ALL_ID, - initialStreamVisibility: StreamVisibilityStatus = StreamVisibilityStatus.DEFAULT, + initialShowPlayedItems: Boolean = true, + initialShowPartiallyPlayedItems: Boolean = true, initialShowFutureItems: Boolean = true ) : ViewModel() { private val feedDatabaseManager = FeedDatabaseManager(application) - private val streamVisibilityState = BehaviorProcessor.create() - private val streamVisibilityStateFlowable = streamVisibilityState - .startWithItem(initialStreamVisibility) + private val showPlayedItems = BehaviorProcessor.create() + private val showPlayedItemsFlowable = showPlayedItems + .startWithItem(initialShowPlayedItems) .distinctUntilChanged() - private val toggleShowFutureItems = BehaviorProcessor.create() - private val toggleShowFutureItemsFlowable = toggleShowFutureItems + private val showPartiallyPlayedItems = BehaviorProcessor.create() + private val showPartiallyPlayedItemsFlowable = showPartiallyPlayedItems + .startWithItem(initialShowPartiallyPlayedItems) + .distinctUntilChanged() + + private val showFutureItems = BehaviorProcessor.create() + private val showFutureItemsFlowable = showFutureItems .startWithItem(initialShowFutureItems) .distinctUntilChanged() @@ -52,35 +58,27 @@ class FeedViewModel( private var combineDisposable = Flowable .combineLatest( FeedEventManager.events(), - streamVisibilityStateFlowable, - toggleShowFutureItemsFlowable, + showPlayedItemsFlowable, + showPartiallyPlayedItemsFlowable, + showFutureItemsFlowable, feedDatabaseManager.notLoadedCount(groupId), feedDatabaseManager.oldestSubscriptionUpdate(groupId), - Function5 { t1: FeedEventManager.Event, t2: StreamVisibilityStatus, t3: Boolean, - t4: Long, t5: List -> - return@Function5 CombineResultEventHolder(t1, t2, t3, t4, t5.firstOrNull()) + Function6 { t1: FeedEventManager.Event, t2: Boolean, t3: Boolean, t4: Boolean, + t5: Long, t6: List -> + 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) -> - val streamItems = if (event is SuccessResultEvent || event is IdleEvent) { + .map { (event, showPlayedItems, showPartiallyPlayedItems, showFutureItems, notLoadedCount, oldestUpdate) -> + val streamItems = if (event is SuccessResultEvent || event is IdleEvent) feedDatabaseManager - .getStreams( - groupId, - !( - showPlayedItems == StreamVisibilityStatus.HIDE_WATCHED || - showPlayedItems == StreamVisibilityStatus.HIDE_PARTIALLY_WATCHED - ), - showPlayedItems != StreamVisibilityStatus.HIDE_PARTIALLY_WATCHED, - showFutureItems - ) + .getStreams(groupId, showPlayedItems, showPartiallyPlayedItems, showFutureItems) .blockingGet(arrayListOf()) - } else { + else arrayListOf() - } CombineResultDataHolder(event, streamItems, notLoadedCount, oldestUpdate) } @@ -107,10 +105,11 @@ class FeedViewModel( private data class CombineResultEventHolder( val t1: FeedEventManager.Event, - val t2: StreamVisibilityStatus, + 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( @@ -120,23 +119,32 @@ class FeedViewModel( val t4: OffsetDateTime? ) - fun changeVisibilityState(streamVisibilityStatus: StreamVisibilityStatus) { - streamVisibilityState.onNext(streamVisibilityStatus) + fun setShowPlayedItems(showPlayedItems: Boolean) { + this.showPlayedItems.onNext(showPlayedItems) } - fun saveStreamVisibilityStateToPreferences(streamVisibilityStatus: StreamVisibilityStatus) = + fun saveShowPlayedItemsToPreferences(showPlayedItems: Boolean) = PreferenceManager.getDefaultSharedPreferences(application).edit { - this.putString( - application.getString(R.string.feed_stream_visibility_state_key), - streamVisibilityStatus.toString() - ) + this.putBoolean(application.getString(R.string.feed_show_watched_items_key), showPlayedItems) this.apply() } - fun getItemsVisibilityFromPreferences() = getStreamVisibilityStateFromPreferences(application) + fun getShowPlayedItemsFromPreferences() = getShowPlayedItemsFromPreferences(application) - fun toggleFutureItems(showFutureItems: Boolean) { - toggleShowFutureItems.onNext(showFutureItems) + fun setShowPartiallyPlayedItems(showPartiallyPlayedItems: Boolean) { + 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) = @@ -148,16 +156,13 @@ class FeedViewModel( fun getShowFutureItemsFromPreferences() = getShowFutureItemsFromPreferences(application) 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 { - val s = PreferenceManager.getDefaultSharedPreferences(context) - .getString( - context.getString(R.string.feed_stream_visibility_state_key), - StreamVisibilityStatus.DEFAULT.toString() - ) ?: StreamVisibilityStatus.DEFAULT.toString() - return StreamVisibilityStatus.valueOf(s) - } - + 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) @@ -167,7 +172,8 @@ class FeedViewModel( App.getApp(), groupId, // Read initial value from preferences - getStreamVisibilityStateFromPreferences(context.applicationContext), + getShowPlayedItemsFromPreferences(context.applicationContext), + getShowPartiallyPlayedItemsFromPreferences(context.applicationContext), getShowFutureItemsFromPreferences(context.applicationContext) ) } diff --git a/app/src/main/java/org/schabi/newpipe/local/feed/StreamVisibilityStatus.kt b/app/src/main/java/org/schabi/newpipe/local/feed/StreamVisibilityStatus.kt deleted file mode 100644 index 956594ef3..000000000 --- a/app/src/main/java/org/schabi/newpipe/local/feed/StreamVisibilityStatus.kt +++ /dev/null @@ -1,5 +0,0 @@ -package org.schabi.newpipe.local.feed - -enum class StreamVisibilityStatus { - DEFAULT, HIDE_WATCHED, HIDE_PARTIALLY_WATCHED -} diff --git a/app/src/main/java/org/schabi/newpipe/local/history/HistoryRecordManager.java b/app/src/main/java/org/schabi/newpipe/local/history/HistoryRecordManager.java index be3ab3674..340b22278 100644 --- a/app/src/main/java/org/schabi/newpipe/local/history/HistoryRecordManager.java +++ b/app/src/main/java/org/schabi/newpipe/local/history/HistoryRecordManager.java @@ -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 * 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 * @return a Maybe containing the ID of the item if successful */ diff --git a/app/src/main/res/menu/menu_feed_fragment.xml b/app/src/main/res/menu/menu_feed_fragment.xml index 303d27b6b..2365c30e4 100644 --- a/app/src/main/res/menu/menu_feed_fragment.xml +++ b/app/src/main/res/menu/menu_feed_fragment.xml @@ -4,31 +4,9 @@ - - - - - - - - feed_update_threshold_key 300 - feed_stream_visibility_state + feed_show_watched_items + feed_show_partially_watched_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 854e0db54..e01d3ed72 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -691,7 +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. - Show/hide watched streams + Hide the following streams + Show/Hide streams 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 @@ -759,8 +760,8 @@ Unknown quality Show future items Hide future items - Hide Watched - Hide Fully Watched - Show All + Fully Watched + Partially Watched + Upcoming Sort \ No newline at end of file