mirror of
https://github.com/XilinJia/Podcini.git
synced 2025-01-31 22:24:51 +01:00
6.6.2 commit
This commit is contained in:
parent
82207979a9
commit
86f0cb9f97
@ -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 = ""
|
||||
|
@ -307,6 +307,7 @@ object UserPreferences {
|
||||
prefQueueKeepSorted,
|
||||
prefQueueKeepSortedOrder,
|
||||
prefDownloadSortedOrder,
|
||||
prefDownloadsFilter,
|
||||
|
||||
// Episodes
|
||||
prefEpisodesSort,
|
||||
|
@ -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
|
||||
|
@ -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.
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
|
@ -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 ")
|
||||
|
@ -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)
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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) }
|
||||
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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 {
|
||||
|
@ -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"
|
||||
|
@ -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>
|
||||
|
@ -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
|
||||
|
8
fastlane/metadata/android/en-US/changelogs/3020247.txt
Normal file
8
fastlane/metadata/android/en-US/changelogs/3020247.txt
Normal 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
|
Loading…
x
Reference in New Issue
Block a user