6.6.2 commit

This commit is contained in:
Xilin Jia 2024-09-14 12:33:40 +01:00
parent 82207979a9
commit 86f0cb9f97
21 changed files with 210 additions and 105 deletions

View File

@ -31,8 +31,8 @@ android {
testApplicationId "ac.mdiq.podcini.tests"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
versionCode 3020246
versionName "6.6.1"
versionCode 3020247
versionName "6.6.2"
applicationId "ac.mdiq.podcini.R"
def commit = ""

View File

@ -307,6 +307,7 @@ object UserPreferences {
prefQueueKeepSorted,
prefQueueKeepSortedOrder,
prefDownloadSortedOrder,
prefDownloadsFilter,
// Episodes
prefEpisodesSort,

View File

@ -431,7 +431,7 @@ object Feeds {
feed.fileUrl = File(feedfilePath, getFeedfileName(feed)).toString()
feed.preferences = FeedPreferences(feed.id, false, FeedPreferences.AutoDeleteAction.GLOBAL, VolumeAdaptionSetting.OFF, "", "")
feed.preferences!!.keepUpdated = false
feed.preferences!!.queue = null
feed.preferences!!.queueId = -2L
feed.preferences!!.videoModePolicy = if (video) VideoMode.WINDOW_VIEW else VideoMode.AUDIO_ONLY
upsertBlk(feed) {}
return feed

View File

@ -35,34 +35,28 @@ object Queues {
appPrefs.edit().putBoolean(UserPreferences.Prefs.prefQueueLocked.name, locked).apply()
}
/**
* Returns if the queue is in keep sorted mode.
* Enables/disables the keep sorted mode of the queue.
* @see .queueKeepSortedOrder
*/
var isQueueKeepSorted: Boolean
/**
* Returns if the queue is in keep sorted mode.
* @see .queueKeepSortedOrder
*/
get() = appPrefs.getBoolean(UserPreferences.Prefs.prefQueueKeepSorted.name, false)
/**
* Enables/disables the keep sorted mode of the queue.
* @see .queueKeepSortedOrder
*/
set(keepSorted) {
appPrefs.edit().putBoolean(UserPreferences.Prefs.prefQueueKeepSorted.name, keepSorted).apply()
}
/**
* Returns the sort order for the queue keep sorted mode.
* Note: This value is stored independently from the keep sorted state.
* Sets the sort order for the queue keep sorted mode.
* @see .isQueueKeepSorted
*/
var queueKeepSortedOrder: EpisodeSortOrder?
/**
* Returns the sort order for the queue keep sorted mode.
* Note: This value is stored independently from the keep sorted state.
* @see .isQueueKeepSorted
*/
get() {
val sortOrderStr = appPrefs.getString(UserPreferences.Prefs.prefQueueKeepSortedOrder.name, "use-default")
return EpisodeSortOrder.parseWithDefault(sortOrderStr, EpisodeSortOrder.DATE_NEW_OLD)
}
/**
* Sets the sort order for the queue keep sorted mode.
* @see .setQueueKeepSorted
*/
set(sortOrder) {
if (sortOrder == null) return
appPrefs.edit().putString(UserPreferences.Prefs.prefQueueKeepSortedOrder.name, sortOrder.name).apply()
@ -344,6 +338,14 @@ object Queues {
}
}
fun inAnyQueue(episode: Episode): Boolean {
val queues = realm.query(PlayQueue::class).find()
for (q in queues) {
if (q.contains(episode)) return true
}
return false
}
class EnqueuePositionPolicy(private val enqueueLocation: EnqueueLocation) {
/**
* Determine the position (0-based) that the item(s) should be inserted to the named queue.

View File

@ -1,6 +1,7 @@
package ac.mdiq.podcini.storage.model
import ac.mdiq.podcini.playback.base.InTheatre.curQueue
import ac.mdiq.podcini.storage.database.Queues.inAnyQueue
import java.io.Serializable
class EpisodeFilter(vararg properties: String) : Serializable {
@ -49,8 +50,17 @@ class EpisodeFilter(vararg properties: String) : Serializable {
showNoMedia && item.media != null -> return false
showIsFavorite && !item.isFavorite -> return false
showNotFavorite && item.isFavorite -> return false
showQueued && curQueue.isInQueue(item) -> return false
showNotQueued && !curQueue.isInQueue(item) -> return false
showQueued && !inAnyQueue(item) -> return false
showNotQueued && inAnyQueue(item) -> return false
else -> return true
}
}
// filter on queues does not have a query string so it's not applied on query results, need to filter separately
fun matchesForQueues(item: Episode): Boolean {
when {
showQueued && !inAnyQueue(item) -> return false
showNotQueued && inAnyQueue(item) -> return false
else -> return true
}
}

View File

@ -62,7 +62,7 @@ class FeedFilter(vararg properties: String) : Serializable {
}
when {
showAlwaysAutoDelete -> statements.add(" preferences.autoDelete == ${FeedPreferences.AutoDeleteAction.ALWAYS.code} ")
showNeverAutoDelete -> statements.add(" preferences.playSpeed != ${FeedPreferences.AutoDeleteAction.NEVER.code} ")
showNeverAutoDelete -> statements.add(" preferences.playSpeed == ${FeedPreferences.AutoDeleteAction.NEVER.code} ")
}
when {
showAutoDownload -> statements.add(" preferences.autoDownload == true ")

View File

@ -30,7 +30,7 @@ class PlayQueue : RealmObject {
var idsBinList: RealmList<Long> = realmListOf()
fun isInQueue(episode: Episode): Boolean {
fun contains(episode: Episode): Boolean {
return episodeIds.contains(episode.id)
}

View File

@ -54,7 +54,7 @@ object EpisodeMenuHandler {
val hasMedia = selectedItem.media != null
val isPlaying = hasMedia && InTheatre.isCurMedia(selectedItem.media)
val isInQueue: Boolean = curQueue.isInQueue(selectedItem)
val isInQueue: Boolean = curQueue.contains(selectedItem)
val isLocalFile = hasMedia && selectedItem.feed?.isLocalFeed?:false
val isFavorite: Boolean = selectedItem.isFavorite

View File

@ -148,11 +148,11 @@ class EpisodeMultiSelectHandler(private val activity: MainActivity, private val
val toRemove = mutableSetOf<Long>()
val toRemoveCur = mutableListOf<Episode>()
items.forEach { e ->
if (curQueue.isInQueue(e)) toRemoveCur.add(e)
if (curQueue.contains(e)) toRemoveCur.add(e)
}
items.forEach { e ->
for (q in queues) {
if (q.isInQueue(e)) {
if (q.contains(e)) {
toRemove.add(e.id)
break
}

View File

@ -27,6 +27,7 @@ abstract class EpisodeFilterDialog : BottomSheetDialogFragment() {
var filter: EpisodeFilter? = null
private val buttonMap: MutableMap<String, Button> = mutableMapOf()
val filtersDisabled: MutableSet<FeedItemFilterGroup> = mutableSetOf()
private val newFilterValues: Set<String>
get() {
@ -50,10 +51,9 @@ abstract class EpisodeFilterDialog : BottomSheetDialogFragment() {
//add filter rows
for (item in FeedItemFilterGroup.entries) {
// Logd("EpisodeFilterDialog", "FeedItemFilterGroup: ${item.values[0].filterId} ${item.values[1].filterId}")
if (item in filtersDisabled) continue
val rBinding = FilterDialogRowBinding.inflate(inflater)
// rowBinding.root.addOnButtonCheckedListener { _: MaterialButtonToggleGroup?, _: Int, _: Boolean ->
// onFilterChanged(newFilterValues)
// }
rBinding.filterButton1.setOnClickListener { onFilterChanged(newFilterValues) }
rBinding.filterButton2.setOnClickListener { onFilterChanged(newFilterValues) }

View File

@ -43,6 +43,7 @@ import kotlin.math.min
toolbar.inflateMenu(R.menu.episodes)
toolbar.setTitle(R.string.episodes_label)
updateToolbar()
txtvInformation.visibility = View.VISIBLE
txtvInformation.setOnClickListener {
AllEpisodesFilterDialog.newInstance(getFilter()).show(childFragmentManager, null)
}
@ -66,13 +67,15 @@ import kotlin.math.min
private var loadItemsRunning = false
override fun loadData(): List<Episode> {
val filter = getFilter()
if (!loadItemsRunning) {
loadItemsRunning = true
allEpisodes = getEpisodes(0, Int.MAX_VALUE, getFilter(), allEpisodesSortOrder, false)
allEpisodes = getEpisodes(0, Int.MAX_VALUE, filter, allEpisodesSortOrder, false)
Logd(TAG, "loadData ${allEpisodes.size}")
loadItemsRunning = false
}
if (allEpisodes.isEmpty()) return listOf()
allEpisodes = allEpisodes.filter { filter.matchesForQueues(it) }
return allEpisodes.subList(0, min(allEpisodes.size-1, page * EPISODES_PER_PAGE))
}
@ -144,11 +147,9 @@ import kotlin.math.min
override fun updateToolbar() {
swipeActions.setFilter(getFilter())
if (getFilter().values.isNotEmpty()) {
txtvInformation.visibility = View.VISIBLE
txtvInformation.text = "${adapter.totalNumberOfItems} episodes - filtered"
txtvInformation.text = "${adapter.totalNumberOfItems} episodes - ${getString(R.string.filtered_label)}"
emptyView.setMessage(R.string.no_all_episodes_filtered_label)
} else {
txtvInformation.visibility = View.VISIBLE
txtvInformation.text = "${adapter.totalNumberOfItems} episodes"
emptyView.setMessage(R.string.no_all_episodes_label)
}

View File

@ -22,8 +22,11 @@ import ac.mdiq.podcini.ui.actions.swipeactions.SwipeActions
import ac.mdiq.podcini.ui.activity.MainActivity
import ac.mdiq.podcini.ui.adapter.EpisodesAdapter
import ac.mdiq.podcini.ui.adapter.SelectableAdapter
import ac.mdiq.podcini.ui.dialog.EpisodeFilterDialog
import ac.mdiq.podcini.ui.dialog.EpisodeSortDialog
import ac.mdiq.podcini.ui.dialog.SwitchQueueDialog
import ac.mdiq.podcini.ui.fragment.AllEpisodesFragment.AllEpisodesFilterDialog
import ac.mdiq.podcini.ui.fragment.AllEpisodesFragment.Companion.prefFilterAllEpisodes
import ac.mdiq.podcini.ui.utils.EmptyViewHandler
import ac.mdiq.podcini.ui.utils.LiftOnScrollListener
import ac.mdiq.podcini.ui.view.EpisodeViewHolder
@ -53,6 +56,7 @@ import kotlinx.coroutines.Job
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import org.apache.commons.lang3.StringUtils
import java.io.File
import java.util.*
@ -105,12 +109,8 @@ import java.util.*
lifecycle.addObserver(swipeActions)
swipeActions.setFilter(EpisodeFilter(EpisodeFilter.States.downloaded.name))
refreshSwipeTelltale()
binding.leftActionIcon.setOnClickListener {
swipeActions.showDialog()
}
binding.rightActionIcon.setOnClickListener {
swipeActions.showDialog()
}
binding.leftActionIcon.setOnClickListener { swipeActions.showDialog() }
binding.rightActionIcon.setOnClickListener { swipeActions.showDialog() }
val animator: RecyclerView.ItemAnimator? = recyclerView.itemAnimator
if (animator is SimpleItemAnimator) animator.supportsChangeAnimations = false
@ -141,8 +141,7 @@ import java.util.*
adapter.endSelectMode()
true
}
if (arguments != null && requireArguments().getBoolean(ARG_SHOW_LOGS, false))
DownloadLogFragment().show(childFragmentManager, null)
if (arguments != null && requireArguments().getBoolean(ARG_SHOW_LOGS, false)) DownloadLogFragment().show(childFragmentManager, null)
addEmptyView()
return binding.root
@ -185,7 +184,7 @@ import java.util.*
@UnstableApi override fun onMenuItemClick(item: MenuItem): Boolean {
when (item.itemId) {
// R.id.refresh_item -> FeedUpdateManager.runOnceOrAsk(requireContext())
R.id.filter_items -> DownloadsFilterDialog.newInstance(getFilter()).show(childFragmentManager, null)
R.id.action_download_logs -> DownloadLogFragment().show(childFragmentManager, null)
R.id.action_search -> (activity as MainActivity).loadChildFragment(SearchFragment.newInstance())
R.id.downloads_sort -> DownloadsSortDialog().show(childFragmentManager, "SortDialog")
@ -196,6 +195,10 @@ import java.util.*
return true
}
private fun getFilter(): EpisodeFilter {
return EpisodeFilter(prefFilterDownloads)
}
private val nameEpisodeMap: MutableMap<String, Episode> = mutableMapOf()
private val filesRemoved: MutableList<String> = mutableListOf()
private fun reconsile() {
@ -203,9 +206,7 @@ import java.util.*
val items = realm.query(Episode::class).query("media.episode == nil").find()
Logd(TAG, "number of episode with null backlink: ${items.size}")
for (item in items) {
upsert(item) {
it.media!!.episode = it
}
upsert(item) { it.media!!.episode = it }
}
nameEpisodeMap.clear()
episodes.forEach { e ->
@ -219,9 +220,7 @@ import java.util.*
Logd(TAG, "reconsile: end, episodes missing file: ${nameEpisodeMap.size}")
if (nameEpisodeMap.isNotEmpty()) {
for (e in nameEpisodeMap.values) {
upsertBlk(e) {
it.media?.setfileUrlOrNull(null)
}
upsertBlk(e) { it.media?.setfileUrlOrNull(null) }
}
}
loadItems()
@ -281,6 +280,7 @@ import java.util.*
Logd(TAG, "Received event: ${event.TAG}")
when (event) {
is FlowEvent.EpisodeEvent -> onEpisodeEvent(event)
is FlowEvent.DownloadsFilterEvent -> onFilterChanged(event)
is FlowEvent.EpisodeMediaEvent -> onEpisodeMediaEvent(event)
is FlowEvent.PlayerSettingsEvent -> loadItems()
is FlowEvent.DownloadLogEvent -> loadItems()
@ -302,6 +302,14 @@ import java.util.*
// }
}
private fun onFilterChanged(event: FlowEvent.DownloadsFilterEvent) {
val fSet = event.filterValues?.toMutableSet() ?: mutableSetOf()
fSet.add(EpisodeFilter.States.downloaded.name)
prefFilterDownloads = StringUtils.join(fSet, ",")
Logd(TAG, "onFilterChanged: $prefFilterDownloads")
loadItems()
}
private fun addEmptyView() {
emptyView = EmptyViewHandler(requireContext())
emptyView.setIcon(R.drawable.ic_download)
@ -324,10 +332,7 @@ import java.util.*
}
}
// have to do this as adapter.notifyItemRemoved(pos) when pos == 0 causes crash
if (size > 0) {
// adapter.setDummyViews(0)
adapter.updateItems(episodes)
}
if (size > 0) adapter.updateItems(episodes)
refreshInfoBar()
}
@ -345,10 +350,7 @@ import java.util.*
}
}
// have to do this as adapter.notifyItemRemoved(pos) when pos == 0 causes crash
if (size > 0) {
// adapter.setDummyViews(0)
adapter.updateItems(episodes)
}
if (size > 0) adapter.updateItems(episodes)
refreshInfoBar()
}
@ -366,7 +368,8 @@ import java.util.*
try {
withContext(Dispatchers.IO) {
val sortOrder: EpisodeSortOrder? = downloadsSortedOrder
val downloadedItems = getEpisodes(0, Int.MAX_VALUE, EpisodeFilter(EpisodeFilter.States.downloaded.name), sortOrder)
val filter = getFilter()
val downloadedItems = getEpisodes(0, Int.MAX_VALUE, filter, sortOrder)
if (runningDownloads.isEmpty()) episodes = downloadedItems.toMutableList()
else {
val mediaUrls: MutableList<String> = ArrayList()
@ -378,6 +381,7 @@ import java.util.*
currentDownloads.addAll(downloadedItems)
episodes = currentDownloads
}
episodes = episodes.filter { filter.matchesForQueues(it) }.toMutableList()
}
withContext(Dispatchers.Main) {
// adapter.setDummyViews(0)
@ -389,9 +393,7 @@ import java.util.*
// adapter.setDummyViews(0)
adapter.updateItems(mutableListOf())
Log.e(TAG, Log.getStackTraceString(e))
} finally {
loadItemsRunning = false
}
} finally { loadItemsRunning = false }
}
}
}
@ -416,6 +418,7 @@ import java.util.*
for (item in episodes) sizeMB += item.media?.size ?: 0
info += "" + (sizeMB / 1000000) + " MB"
}
if (getFilter().values.isNotEmpty()) info += " - ${getString(R.string.filtered_label)}"
binding.infoBar.text = info
}
@ -466,6 +469,21 @@ import java.util.*
}
}
class DownloadsFilterDialog : EpisodeFilterDialog() {
override fun onFilterChanged(newFilterValues: Set<String>) {
EventFlow.postEvent(FlowEvent.DownloadsFilterEvent(newFilterValues))
}
companion object {
fun newInstance(filter: EpisodeFilter?): DownloadsFilterDialog {
val dialog = DownloadsFilterDialog()
dialog.filter = filter
dialog.filtersDisabled.add(FeedItemFilterGroup.DOWNLOADED)
dialog.filtersDisabled.add(FeedItemFilterGroup.MEDIA)
return dialog
}
}
}
companion object {
val TAG = DownloadsFragment::class.simpleName ?: "Anonymous"
@ -481,5 +499,11 @@ import java.util.*
set(sortOrder) {
appPrefs.edit().putString(UserPreferences.Prefs.prefDownloadSortedOrder.name, "" + sortOrder!!.code).apply()
}
var prefFilterDownloads: String
get() = appPrefs.getString(UserPreferences.Prefs.prefDownloadsFilter.name, EpisodeFilter.States.downloaded.name) ?: EpisodeFilter.States.downloaded.name
set(filter) {
appPrefs.edit().putString(UserPreferences.Prefs.prefDownloadsFilter.name, filter).apply()
}
}
}

View File

@ -128,12 +128,11 @@ import kotlin.math.min
toolbar.menu.findItem(R.id.clear_history_item).setVisible(episodes.isNotEmpty())
swipeActions.setFilter(getFilter())
txtvInformation.visibility = View.VISIBLE
if (getFilter().values.isNotEmpty()) {
txtvInformation.visibility = View.VISIBLE
txtvInformation.text = "${adapter.totalNumberOfItems} episodes - filtered"
txtvInformation.text = "${adapter.totalNumberOfItems} episodes - ${getString(R.string.filtered_label)}"
emptyView.setMessage(R.string.no_all_episodes_filtered_label)
} else {
txtvInformation.visibility = View.VISIBLE
txtvInformation.text = "${adapter.totalNumberOfItems} episodes"
emptyView.setMessage(R.string.no_all_episodes_label)
}

View File

@ -321,7 +321,7 @@ import kotlin.math.max
}
when (event.action) {
FlowEvent.QueueEvent.Action.ADDED -> {
if (event.episodes.isNotEmpty() && !curQueue.isInQueue(event.episodes[0])) {
if (event.episodes.isNotEmpty() && !curQueue.contains(event.episodes[0])) {
val pos = queueItems.size
queueItems.addAll(event.episodes)
adapter?.notifyItemRangeInserted(pos, queueItems.size)

View File

@ -42,7 +42,6 @@ import android.net.Uri
import android.os.Bundle
import android.util.Log
import android.view.*
import android.view.inputmethod.EditorInfo
import android.widget.*
import androidx.activity.result.ActivityResult
import androidx.activity.result.contract.ActivityResultContracts
@ -108,18 +107,22 @@ class SubscriptionsFragment : Fragment(), Toolbar.OnMenuItemClickListener, Selec
private lateinit var toolbar: MaterialToolbar
private lateinit var speedDialView: SpeedDialView
private lateinit var catAdapter: ArrayAdapter<String>
private val tags: MutableList<String> = mutableListOf()
private val queueIds: MutableList<Long> = mutableListOf()
private lateinit var queuesAdapter: ArrayAdapter<String>
private lateinit var tagsAdapter: ArrayAdapter<String>
private var tagFilterIndex = 1
private var queueFilterIndex = 1
private var infoTextFiltered = ""
private var infoTextUpdate = ""
private var tagFilterIndex = 1
// TODO: currently not used
// TODO: currently not used
private var displayedFolder: String = ""
private var displayUpArrow = false
private var feedList: MutableList<Feed> = mutableListOf()
private var feedListFiltered: List<Feed> = mutableListOf()
private val tags: MutableList<String> = mutableListOf()
private var useGrid: Boolean? = null
private val useGridLayout: Boolean
@ -164,10 +167,26 @@ class SubscriptionsFragment : Fragment(), Toolbar.OnMenuItemClickListener, Selec
resetTags()
catAdapter = ArrayAdapter(requireContext(), android.R.layout.simple_spinner_item, tags)
catAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item)
binding.categorySpinner.setAdapter(catAdapter)
binding.categorySpinner.setSelection(catAdapter.getPosition("All"))
val queues = realm.query(PlayQueue::class).find()
queueIds.addAll(queues.map { it.id })
val spinnerTexts: MutableList<String> = mutableListOf("All", "None")
spinnerTexts.addAll(queues.map { it.name })
queuesAdapter = ArrayAdapter(requireContext(), android.R.layout.simple_spinner_item, spinnerTexts)
queuesAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item)
binding.queuesSpinner.setAdapter(queuesAdapter)
binding.queuesSpinner.setSelection(queuesAdapter.getPosition("All"))
binding.queuesSpinner.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) {
queueFilterIndex = position
loadSubscriptions()
}
override fun onNothingSelected(parent: AdapterView<*>?) {}
}
tagsAdapter = ArrayAdapter(requireContext(), android.R.layout.simple_spinner_item, tags)
tagsAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item)
binding.categorySpinner.setAdapter(tagsAdapter)
binding.categorySpinner.setSelection(tagsAdapter.getPosition("All"))
binding.categorySpinner.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) {
tagFilterIndex = position
@ -177,17 +196,17 @@ class SubscriptionsFragment : Fragment(), Toolbar.OnMenuItemClickListener, Selec
override fun onNothingSelected(parent: AdapterView<*>?) {}
}
val searchBox = binding.searchBox
searchBox.setOnEditorActionListener { _, actionId, _ ->
if (actionId == EditorInfo.IME_ACTION_SEARCH) {
val text = searchBox.text.toString().lowercase(Locale.getDefault())
val resultList = feedListFiltered.filter {
it.title?.lowercase(Locale.getDefault())?.contains(text)?:false || it.author?.lowercase(Locale.getDefault())?.contains(text)?:false
}
adapter.setItems(resultList)
true
} else false
}
// val searchBox = binding.searchBox
// searchBox.setOnEditorActionListener { _, actionId, _ ->
// if (actionId == EditorInfo.IME_ACTION_SEARCH) {
// val text = searchBox.text.toString().lowercase(Locale.getDefault())
// val resultList = feedListFiltered.filter {
// it.title?.lowercase(Locale.getDefault())?.contains(text)?:false || it.author?.lowercase(Locale.getDefault())?.contains(text)?:false
// }
// adapter.setItems(resultList)
// true
// } else false
// }
// binding.progressBar.visibility = View.VISIBLE
binding.progressBar.visibility = View.GONE
@ -291,10 +310,15 @@ class SubscriptionsFragment : Fragment(), Toolbar.OnMenuItemClickListener, Selec
}
}
private fun filterOnTag() {
feedListFiltered = feedList
binding.count.text = feedListFiltered.size.toString() + " / " + feedList.size.toString()
adapter.setItems(feedListFiltered)
private fun queryStringOfQueues() : String {
return when (queueFilterIndex) {
0 -> "" // All feeds
1 -> " preferences.queueId == -2 "
else -> { // feeds associated with the chosen queue
val qid = queueIds[queueFilterIndex-2]
" preferences.queueId == '$qid' "
}
}
}
private fun resetTags() {
@ -383,7 +407,10 @@ class SubscriptionsFragment : Fragment(), Toolbar.OnMenuItemClickListener, Selec
withContext(Dispatchers.Main) {
// We have fewer items. This can result in items being selected that are no longer visible.
if (feedListFiltered.size > feedList.size) adapter.endSelectMode()
filterOnTag()
// filterOnTag()
feedListFiltered = feedList
binding.count.text = feedListFiltered.size.toString() + " / " + feedList.size.toString()
adapter.setItems(feedListFiltered)
// binding.progressBar.visibility = View.GONE
adapter.setItems(feedListFiltered)
binding.count.text = feedListFiltered.size.toString() + " / " + feedList.size.toString()
@ -407,8 +434,11 @@ class SubscriptionsFragment : Fragment(), Toolbar.OnMenuItemClickListener, Selec
}
private fun filterAndSort() {
var fQueryStr = FeedFilter(feedsFilter).queryString()
val tagsQueryStr = queryStringOfTags()
val fQueryStr = if (tagsQueryStr.isEmpty()) FeedFilter(feedsFilter).queryString() else FeedFilter(feedsFilter).queryString() + " AND " + tagsQueryStr
if (tagsQueryStr.isNotEmpty()) fQueryStr += " AND $tagsQueryStr"
val queuesQueryStr = queryStringOfQueues()
if (queuesQueryStr.isNotEmpty()) fQueryStr += " AND $queuesQueryStr"
Logd(TAG, "sortFeeds() called $feedsFilter $fQueryStr")
val feedList_ = getFeedList(fQueryStr).toMutableList()
val feedOrder = feedOrderBy
@ -710,7 +740,7 @@ class SubscriptionsFragment : Fragment(), Toolbar.OnMenuItemClickListener, Selec
@Composable
fun AutoDeleteDialog(showDialog: Boolean, onDismissRequest: () -> Unit) {
if (showDialog) {
val (selectedOption, onOptionSelected) = remember { mutableStateOf("") }
val (selectedOption, _) = remember { mutableStateOf("") }
Dialog(onDismissRequest = { onDismissRequest() }) {
Card(
modifier = Modifier

View File

@ -158,7 +158,7 @@ open class EpisodeViewHolder(private val activity: MainActivity, parent: ViewGro
setPubDate(item)
binding.isFavorite.visibility = if (item.isFavorite) View.VISIBLE else View.GONE
isInQueue.visibility = if (curQueue.isInQueue(item)) View.VISIBLE else View.GONE
isInQueue.visibility = if (curQueue.contains(item)) View.VISIBLE else View.GONE
// container.alpha = if (item.isPlayed()) 0.7f else 1.0f
val newButton = EpisodeActionButton.forItem(item)

View File

@ -160,6 +160,8 @@ sealed class FlowEvent {
data class AllEpisodesSortEvent(val dummy: Unit = Unit) : FlowEvent()
data class DownloadsFilterEvent(val filterValues: Set<String?>?) : FlowEvent()
data class EpisodeEvent(val episodes: List<Episode>) : FlowEvent() {
companion object {
fun updated(vararg episodes: Episode): EpisodeEvent {

View File

@ -82,16 +82,28 @@
android:orientation="horizontal"
android:layout_below="@id/appbar">
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/search_box"
<!-- <com.google.android.material.textfield.TextInputEditText-->
<!-- android:id="@+id/search_box"-->
<!-- android:layout_width="0dp"-->
<!-- android:layout_height="wrap_content"-->
<!-- android:layout_weight="1"-->
<!-- android:inputType="text"-->
<!-- android:hint="@string/feed_search_hint_text"-->
<!-- android:lines="1"-->
<!-- android:imeOptions="actionSearch"-->
<!-- android:background="?attr/background_color"/>-->
<Spinner
android:id="@+id/queues_spinner"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:inputType="text"
android:hint="@string/feed_search_hint_text"
android:lines="1"
android:imeOptions="actionSearch"
android:background="?attr/background_color"/>
android:dropDownWidth="200dp"
android:padding="8dp"
android:paddingBottom="20dp"
android:textColor="?android:attr/textColorPrimary"
android:textSize="@dimen/text_size_micro"
android:spinnerMode="dropdown"/>
<Spinner
android:id="@+id/category_spinner"

View File

@ -16,17 +16,19 @@
android:title="@string/downloads_log_label"
custom:showAsAction="always" />
<item
android:id="@+id/reconsile"
android:title="@string/reconsile_label"
app:showAsAction="never" />
<!-- <item-->
<!-- android:id="@+id/refresh_item"-->
<!-- android:title="@string/refresh_label"-->
<!-- android:menuCategory="container"-->
<!-- app:showAsAction="never" />-->
<item
android:id="@+id/filter_items"
android:icon="@drawable/ic_filter"
android:menuCategory="container"
android:title="@string/filter"
custom:showAsAction="ifRoom"/>
<item
android:id="@+id/downloads_sort"
android:title="@string/sort" />
@ -35,4 +37,9 @@
android:id="@+id/switch_queue"
android:title="@string/switch_queue" />
<item
android:id="@+id/reconsile"
android:title="@string/reconsile_label"
app:showAsAction="never" />
</menu>

View File

@ -1,3 +1,12 @@
# 6.6.2
* fixed issue of filter "never auto delete" in Subscriptions view
* corrected issue of "queued/not queued" filter for episodes
* filter on "queued/not queued" is checked for all queues (not only the active queue)
* added filtering feature to Downloads view
* changed associated queue of Youtube Syndicate to None (rather than active set previously)
* in Subscriptions view, the "search" box is changed to a "Queues" spinner to filter by associated queue
# 6.6.1
* the confirm dialog is more responsive when receiving a youtube media share

View File

@ -0,0 +1,8 @@
Version 6.6.2:
* fixed issue of filter "never auto delete" in Subscriptions view
* corrected issue of "queued/not queued" filter for episodes
* filter on "queued/not queued" is checked for all queues (not only the active queue)
* added filtering feature to Downloads view
* changed associated queue of Youtube Syndicate to None (rather than active set previously)
* in Subscriptions view, the "search" box is changed to a "Queues" spinner to filter by associated queue