6.12.4 commit
This commit is contained in:
parent
7b6d976c9a
commit
15bc1efdca
|
@ -31,8 +31,8 @@ android {
|
||||||
testApplicationId "ac.mdiq.podcini.tests"
|
testApplicationId "ac.mdiq.podcini.tests"
|
||||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||||
|
|
||||||
versionCode 3020281
|
versionCode 3020282
|
||||||
versionName "6.12.3"
|
versionName "6.12.4"
|
||||||
|
|
||||||
applicationId "ac.mdiq.podcini.R"
|
applicationId "ac.mdiq.podcini.R"
|
||||||
def commit = ""
|
def commit = ""
|
||||||
|
|
|
@ -89,6 +89,7 @@ class Episode : RealmObject {
|
||||||
var isFavorite: Boolean = (rating == Rating.FAVORITE.code)
|
var isFavorite: Boolean = (rating == Rating.FAVORITE.code)
|
||||||
private set
|
private set
|
||||||
|
|
||||||
|
@FullText
|
||||||
var comment: String = ""
|
var comment: String = ""
|
||||||
|
|
||||||
@Ignore
|
@Ignore
|
||||||
|
|
|
@ -2,7 +2,6 @@ package ac.mdiq.podcini.storage.model
|
||||||
|
|
||||||
import ac.mdiq.podcini.R
|
import ac.mdiq.podcini.R
|
||||||
import ac.mdiq.podcini.storage.database.Queues.inAnyQueue
|
import ac.mdiq.podcini.storage.database.Queues.inAnyQueue
|
||||||
import ac.mdiq.podcini.storage.model.MediaType.Companion.AUDIO_APPLICATION_MIME_STRINGS
|
|
||||||
import java.io.Serializable
|
import java.io.Serializable
|
||||||
|
|
||||||
class EpisodeFilter(vararg properties_: String) : Serializable {
|
class EpisodeFilter(vararg properties_: String) : Serializable {
|
||||||
|
@ -20,29 +19,6 @@ class EpisodeFilter(vararg properties_: String) : Serializable {
|
||||||
|
|
||||||
constructor(properties: String) : this(*(properties.split(",").toTypedArray()))
|
constructor(properties: String) : this(*(properties.split(",").toTypedArray()))
|
||||||
|
|
||||||
// fun matches(item: Episode): Boolean {
|
|
||||||
// when {
|
|
||||||
// showNew && !item.isNew -> return false
|
|
||||||
// showPlayed && item.playState < PlayState.PLAYED.code -> return false
|
|
||||||
// showUnplayed && item.playState >= PlayState.PLAYED.code -> return false
|
|
||||||
// properties.contains(States.paused.name) && !item.isInProgress -> return false
|
|
||||||
// properties.contains(States.not_paused.name) && item.isInProgress -> return false
|
|
||||||
// showDownloaded && !item.isDownloaded -> return false
|
|
||||||
// showNotDownloaded && item.isDownloaded -> return false
|
|
||||||
// properties.contains(States.auto_downloadable.name) && !item.isAutoDownloadEnabled -> return false
|
|
||||||
// properties.contains(States.not_auto_downloadable.name) && item.isAutoDownloadEnabled -> return false
|
|
||||||
// properties.contains(States.has_media.name) && item.media == null -> return false
|
|
||||||
// properties.contains(States.no_media.name) && item.media != null -> return false
|
|
||||||
// properties.contains(States.has_comments.name) && item.comment.isEmpty() -> return false
|
|
||||||
// properties.contains(States.no_comments.name) && item.comment.isNotEmpty() -> return false
|
|
||||||
// showIsFavorite && !item.isFavorite -> return false
|
|
||||||
// showNotFavorite && item.isFavorite -> 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
|
// 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 {
|
fun matchesForQueues(item: Episode): Boolean {
|
||||||
return when {
|
return when {
|
||||||
|
@ -64,10 +40,10 @@ class EpisodeFilter(vararg properties_: String) : Serializable {
|
||||||
if (properties.contains(States.unknown.name)) mediaTypeQuerys.add(" media == nil OR media.mimeType == nil OR media.mimeType == '' ")
|
if (properties.contains(States.unknown.name)) mediaTypeQuerys.add(" media == nil OR media.mimeType == nil OR media.mimeType == '' ")
|
||||||
if (properties.contains(States.audio.name)) mediaTypeQuerys.add(" media.mimeType BEGINSWITH 'audio' ")
|
if (properties.contains(States.audio.name)) mediaTypeQuerys.add(" media.mimeType BEGINSWITH 'audio' ")
|
||||||
if (properties.contains(States.video.name)) mediaTypeQuerys.add(" media.mimeType BEGINSWITH 'video' ")
|
if (properties.contains(States.video.name)) mediaTypeQuerys.add(" media.mimeType BEGINSWITH 'video' ")
|
||||||
if (properties.contains(States.audio_app.name)) mediaTypeQuerys.add(" media.mimeType IN ${AUDIO_APPLICATION_MIME_STRINGS.toList()} ")
|
if (properties.contains(States.audio_app.name)) mediaTypeQuerys.add(" media.mimeType IN {\"application/ogg\", \"application/opus\", \"application/x-flac\"} ")
|
||||||
if (mediaTypeQuerys.isNotEmpty()) {
|
if (mediaTypeQuerys.isNotEmpty()) {
|
||||||
val query = StringBuilder(" (" + mediaTypeQuerys[0])
|
val query = StringBuilder(" (" + mediaTypeQuerys[0])
|
||||||
if (mediaTypeQuerys.size > 1) for (r in statements.subList(1, mediaTypeQuerys.size)) {
|
if (mediaTypeQuerys.size > 1) for (r in mediaTypeQuerys.subList(1, mediaTypeQuerys.size)) {
|
||||||
query.append(" OR ")
|
query.append(" OR ")
|
||||||
query.append(r)
|
query.append(r)
|
||||||
}
|
}
|
||||||
|
@ -84,7 +60,7 @@ class EpisodeFilter(vararg properties_: String) : Serializable {
|
||||||
if (properties.contains(States.favorite.name)) ratingQuerys.add(" rating == ${Rating.FAVORITE.code} ")
|
if (properties.contains(States.favorite.name)) ratingQuerys.add(" rating == ${Rating.FAVORITE.code} ")
|
||||||
if (ratingQuerys.isNotEmpty()) {
|
if (ratingQuerys.isNotEmpty()) {
|
||||||
val query = StringBuilder(" (" + ratingQuerys[0])
|
val query = StringBuilder(" (" + ratingQuerys[0])
|
||||||
if (ratingQuerys.size > 1) for (r in statements.subList(1, ratingQuerys.size)) {
|
if (ratingQuerys.size > 1) for (r in ratingQuerys.subList(1, ratingQuerys.size)) {
|
||||||
query.append(" OR ")
|
query.append(" OR ")
|
||||||
query.append(r)
|
query.append(r)
|
||||||
}
|
}
|
||||||
|
@ -106,7 +82,7 @@ class EpisodeFilter(vararg properties_: String) : Serializable {
|
||||||
if (properties.contains(States.ignored.name)) stateQuerys.add(" playState == ${PlayState.IGNORED.code} ")
|
if (properties.contains(States.ignored.name)) stateQuerys.add(" playState == ${PlayState.IGNORED.code} ")
|
||||||
if (stateQuerys.isNotEmpty()) {
|
if (stateQuerys.isNotEmpty()) {
|
||||||
val query = StringBuilder(" (" + stateQuerys[0])
|
val query = StringBuilder(" (" + stateQuerys[0])
|
||||||
if (stateQuerys.size > 1) for (r in statements.subList(1, stateQuerys.size)) {
|
if (stateQuerys.size > 1) for (r in stateQuerys.subList(1, stateQuerys.size)) {
|
||||||
query.append(" OR ")
|
query.append(" OR ")
|
||||||
query.append(r)
|
query.append(r)
|
||||||
}
|
}
|
||||||
|
@ -198,11 +174,10 @@ class EpisodeFilter(vararg properties_: String) : Serializable {
|
||||||
favorite,
|
favorite,
|
||||||
}
|
}
|
||||||
|
|
||||||
enum class EpisodesFilterGroup(val nameRes: Int, vararg values: ItemProperties) {
|
enum class EpisodesFilterGroup(val nameRes: Int, vararg values_: ItemProperties) {
|
||||||
// PLAYED(ItemProperties(R.string.hide_played_episodes_label, States.played.name), ItemProperties(R.string.not_played, States.unplayed.name)),
|
// PLAYED(ItemProperties(R.string.hide_played_episodes_label, States.played.name), ItemProperties(R.string.not_played, States.unplayed.name)),
|
||||||
// PAUSED(ItemProperties(R.string.hide_paused_episodes_label, States.paused.name), ItemProperties(R.string.not_paused, States.not_paused.name)),
|
// PAUSED(ItemProperties(R.string.hide_paused_episodes_label, States.paused.name), ItemProperties(R.string.not_paused, States.not_paused.name)),
|
||||||
// FAVORITE(ItemProperties(R.string.hide_is_favorite_label, States.is_favorite.name), ItemProperties(R.string.not_favorite, States.not_favorite.name)),
|
// FAVORITE(ItemProperties(R.string.hide_is_favorite_label, States.is_favorite.name), ItemProperties(R.string.not_favorite, States.not_favorite.name)),
|
||||||
MEDIA(R.string.has_media, ItemProperties(R.string.yes, States.has_media.name), ItemProperties(R.string.no, States.no_media.name)),
|
|
||||||
RATING(R.string.rating_label, ItemProperties(R.string.unrated, States.unrated.name),
|
RATING(R.string.rating_label, ItemProperties(R.string.unrated, States.unrated.name),
|
||||||
ItemProperties(R.string.trash, States.trash.name),
|
ItemProperties(R.string.trash, States.trash.name),
|
||||||
ItemProperties(R.string.bad, States.bad.name),
|
ItemProperties(R.string.bad, States.bad.name),
|
||||||
|
@ -224,6 +199,7 @@ class EpisodeFilter(vararg properties_: String) : Serializable {
|
||||||
),
|
),
|
||||||
OPINION(R.string.has_comments, ItemProperties(R.string.yes, States.has_comments.name), ItemProperties(R.string.no, States.no_comments.name)),
|
OPINION(R.string.has_comments, ItemProperties(R.string.yes, States.has_comments.name), ItemProperties(R.string.no, States.no_comments.name)),
|
||||||
// QUEUED(ItemProperties(R.string.queued_label, States.queued.name), ItemProperties(R.string.not_queued_label, States.not_queued.name)),
|
// QUEUED(ItemProperties(R.string.queued_label, States.queued.name), ItemProperties(R.string.not_queued_label, States.not_queued.name)),
|
||||||
|
MEDIA(R.string.has_media, ItemProperties(R.string.yes, States.has_media.name), ItemProperties(R.string.no, States.no_media.name)),
|
||||||
DOWNLOADED(R.string.downloaded_label, ItemProperties(R.string.yes, States.downloaded.name), ItemProperties(R.string.no, States.not_downloaded.name)),
|
DOWNLOADED(R.string.downloaded_label, ItemProperties(R.string.yes, States.downloaded.name), ItemProperties(R.string.no, States.not_downloaded.name)),
|
||||||
CHAPTERS(R.string.has_chapters, ItemProperties(R.string.yes, States.has_chapters.name), ItemProperties(R.string.no, States.no_chapters.name)),
|
CHAPTERS(R.string.has_chapters, ItemProperties(R.string.yes, States.has_chapters.name), ItemProperties(R.string.no, States.no_chapters.name)),
|
||||||
MEDIA_TYPE(R.string.media_type, ItemProperties(R.string.unknown, States.unknown.name),
|
MEDIA_TYPE(R.string.media_type, ItemProperties(R.string.unknown, States.unknown.name),
|
||||||
|
@ -233,10 +209,9 @@ class EpisodeFilter(vararg properties_: String) : Serializable {
|
||||||
),
|
),
|
||||||
AUTO_DOWNLOADABLE(R.string.auto_downloadable_label, ItemProperties(R.string.yes, States.auto_downloadable.name), ItemProperties(R.string.no, States.not_auto_downloadable.name));
|
AUTO_DOWNLOADABLE(R.string.auto_downloadable_label, ItemProperties(R.string.yes, States.auto_downloadable.name), ItemProperties(R.string.no, States.not_auto_downloadable.name));
|
||||||
|
|
||||||
@JvmField
|
val values: Array<ItemProperties> = arrayOf(*values_)
|
||||||
val values: Array<ItemProperties> = arrayOf(*values)
|
|
||||||
|
|
||||||
class ItemProperties(@JvmField val displayName: Int, @JvmField val filterId: String)
|
class ItemProperties(val displayName: Int, val filterId: String)
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
|
|
@ -49,6 +49,7 @@ class Feed : RealmObject {
|
||||||
|
|
||||||
@FullText
|
@FullText
|
||||||
var author: String? = null
|
var author: String? = null
|
||||||
|
|
||||||
var imageUrl: String? = null
|
var imageUrl: String? = null
|
||||||
|
|
||||||
var episodes: RealmList<Episode> = realmListOf()
|
var episodes: RealmList<Episode> = realmListOf()
|
||||||
|
@ -98,6 +99,7 @@ class Feed : RealmObject {
|
||||||
|
|
||||||
var rating: Int = Rating.NEUTRAL.code
|
var rating: Int = Rating.NEUTRAL.code
|
||||||
|
|
||||||
|
@FullText
|
||||||
var comment: String = ""
|
var comment: String = ""
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -11,37 +11,17 @@ class FeedFilter(vararg properties_: String) : Serializable {
|
||||||
|
|
||||||
constructor(properties: String) : this(*(properties.split(",").toTypedArray()))
|
constructor(properties: String) : this(*(properties.split(",").toTypedArray()))
|
||||||
|
|
||||||
// fun matches(feed: Feed): Boolean {
|
|
||||||
// when {
|
|
||||||
// properties.contains(States.keepUpdated.name) && feed.preferences?.keepUpdated != true -> return false
|
|
||||||
// properties.contains(States.not_keepUpdated.name) && feed.preferences?.keepUpdated != false -> return false
|
|
||||||
// properties.contains(States.global_playSpeed.name) && feed.preferences?.playSpeed != SPEED_USE_GLOBAL -> return false
|
|
||||||
// properties.contains(States.custom_playSpeed.name) && feed.preferences?.playSpeed == SPEED_USE_GLOBAL -> return false
|
|
||||||
// properties.contains(States.has_comments.name) && feed.comment.isEmpty() -> return false
|
|
||||||
// properties.contains(States.no_comments.name) && feed.comment.isNotEmpty() -> return false
|
|
||||||
// properties.contains(States.has_skips.name) && feed.preferences?.introSkip == 0 && feed.preferences?.endingSkip == 0 -> return false
|
|
||||||
// properties.contains(States.no_skips.name) && (feed.preferences?.introSkip != 0 || feed.preferences?.endingSkip != 0) -> return false
|
|
||||||
// properties.contains(States.global_auto_delete.name) && feed.preferences?.autoDeleteAction != FeedPreferences.AutoDeleteAction.GLOBAL -> return false
|
|
||||||
// properties.contains(States.always_auto_delete.name) && feed.preferences?.autoDeleteAction != FeedPreferences.AutoDeleteAction.ALWAYS -> return false
|
|
||||||
// properties.contains(States.never_auto_delete.name) && feed.preferences?.autoDeleteAction != FeedPreferences.AutoDeleteAction.NEVER -> return false
|
|
||||||
// properties.contains(States.autoDownload.name) && feed.preferences?.autoDownload != true -> return false
|
|
||||||
// properties.contains(States.not_autoDownload.name) && feed.preferences?.autoDownload != false -> return false
|
|
||||||
// properties.contains(States.unrated.name) && feed.rating != Rating.UNRATED.code -> return false
|
|
||||||
// properties.contains(States.trash.name) && feed.rating != Rating.TRASH.code -> return false
|
|
||||||
// properties.contains(States.bad.name) && feed.rating != Rating.BAD.code -> return false
|
|
||||||
// properties.contains(States.neutral.name) && feed.rating != Rating.NEUTRAL.code -> return false
|
|
||||||
// properties.contains(States.good.name) && feed.rating != Rating.GOOD.code -> return false
|
|
||||||
// properties.contains(States.favorite.name) && feed.rating != Rating.FAVORITE.code -> return false
|
|
||||||
// else -> return true
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
fun queryString(): String {
|
fun queryString(): String {
|
||||||
val statements: MutableList<String> = mutableListOf()
|
val statements: MutableList<String> = mutableListOf()
|
||||||
when {
|
when {
|
||||||
properties.contains(States.keepUpdated.name) -> statements.add("preferences.keepUpdated == true ")
|
properties.contains(States.keepUpdated.name) -> statements.add("preferences.keepUpdated == true ")
|
||||||
properties.contains(States.not_keepUpdated.name) -> statements.add(" preferences.keepUpdated == false ")
|
properties.contains(States.not_keepUpdated.name) -> statements.add(" preferences.keepUpdated == false ")
|
||||||
}
|
}
|
||||||
|
when {
|
||||||
|
properties.contains(States.pref_streaming.name) -> statements.add("preferences.prefStreamOverDownload == true ")
|
||||||
|
properties.contains(States.not_pref_streaming.name) -> statements.add(" preferences.prefStreamOverDownload == false ")
|
||||||
|
}
|
||||||
|
|
||||||
when {
|
when {
|
||||||
properties.contains(States.global_playSpeed.name) -> statements.add(" preferences.playSpeed == $SPEED_USE_GLOBAL ")
|
properties.contains(States.global_playSpeed.name) -> statements.add(" preferences.playSpeed == $SPEED_USE_GLOBAL ")
|
||||||
properties.contains(States.custom_playSpeed.name) -> statements.add(" preferences.playSpeed != $SPEED_USE_GLOBAL ")
|
properties.contains(States.custom_playSpeed.name) -> statements.add(" preferences.playSpeed != $SPEED_USE_GLOBAL ")
|
||||||
|
@ -64,7 +44,7 @@ class FeedFilter(vararg properties_: String) : Serializable {
|
||||||
}
|
}
|
||||||
when {
|
when {
|
||||||
properties.contains(States.youtube.name) -> statements.add(" downloadUrl CONTAINS[c] 'youtube' OR link CONTAINS[c] 'youtube' OR downloadUrl CONTAINS[c] 'youtu.be' OR link CONTAINS[c] 'youtu.be' ")
|
properties.contains(States.youtube.name) -> statements.add(" downloadUrl CONTAINS[c] 'youtube' OR link CONTAINS[c] 'youtube' OR downloadUrl CONTAINS[c] 'youtu.be' OR link CONTAINS[c] 'youtu.be' ")
|
||||||
properties.contains(States.rss.name) -> statements.add(" downloadUrl NOT CONTAINS[c] 'youtube' AND link NOT CONTAINS[c] 'youtube' AND downloadUrl NOT CONTAINS[c] 'youtu.be' AND link NOT CONTAINS[c] 'youtu.be' ")
|
properties.contains(States.rss.name) -> statements.add(" !(downloadUrl CONTAINS[c] 'youtube' OR link CONTAINS[c] 'youtube' OR downloadUrl CONTAINS[c] 'youtu.be' OR link CONTAINS[c] 'youtu.be') ")
|
||||||
}
|
}
|
||||||
|
|
||||||
val ratingQuerys = mutableListOf<String>()
|
val ratingQuerys = mutableListOf<String>()
|
||||||
|
@ -76,7 +56,7 @@ class FeedFilter(vararg properties_: String) : Serializable {
|
||||||
if (properties.contains(States.favorite.name)) ratingQuerys.add(" rating == ${Rating.FAVORITE.code} ")
|
if (properties.contains(States.favorite.name)) ratingQuerys.add(" rating == ${Rating.FAVORITE.code} ")
|
||||||
if (ratingQuerys.isNotEmpty()) {
|
if (ratingQuerys.isNotEmpty()) {
|
||||||
val query = StringBuilder(" (" + ratingQuerys[0])
|
val query = StringBuilder(" (" + ratingQuerys[0])
|
||||||
if (ratingQuerys.size > 1) for (r in statements.subList(1, ratingQuerys.size)) {
|
if (ratingQuerys.size > 1) for (r in ratingQuerys.subList(1, ratingQuerys.size)) {
|
||||||
query.append(" OR ")
|
query.append(" OR ")
|
||||||
query.append(r)
|
query.append(r)
|
||||||
}
|
}
|
||||||
|
@ -90,7 +70,7 @@ class FeedFilter(vararg properties_: String) : Serializable {
|
||||||
if (properties.contains(States.never_auto_delete.name)) audoDeleteQuerys.add(" preferences.playSpeed == ${FeedPreferences.AutoDeleteAction.NEVER.code} ")
|
if (properties.contains(States.never_auto_delete.name)) audoDeleteQuerys.add(" preferences.playSpeed == ${FeedPreferences.AutoDeleteAction.NEVER.code} ")
|
||||||
if (audoDeleteQuerys.isNotEmpty()) {
|
if (audoDeleteQuerys.isNotEmpty()) {
|
||||||
val query = StringBuilder(" (" + audoDeleteQuerys[0])
|
val query = StringBuilder(" (" + audoDeleteQuerys[0])
|
||||||
if (audoDeleteQuerys.size > 1) for (r in statements.subList(1, audoDeleteQuerys.size)) {
|
if (audoDeleteQuerys.size > 1) for (r in audoDeleteQuerys.subList(1, audoDeleteQuerys.size)) {
|
||||||
query.append(" OR ")
|
query.append(" OR ")
|
||||||
query.append(r)
|
query.append(r)
|
||||||
}
|
}
|
||||||
|
@ -110,6 +90,7 @@ class FeedFilter(vararg properties_: String) : Serializable {
|
||||||
query.append(r)
|
query.append(r)
|
||||||
}
|
}
|
||||||
query.append(") ")
|
query.append(") ")
|
||||||
|
Logd("queryString", "${query}")
|
||||||
return query.toString()
|
return query.toString()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -117,6 +98,8 @@ class FeedFilter(vararg properties_: String) : Serializable {
|
||||||
enum class States {
|
enum class States {
|
||||||
keepUpdated,
|
keepUpdated,
|
||||||
not_keepUpdated,
|
not_keepUpdated,
|
||||||
|
pref_streaming,
|
||||||
|
not_pref_streaming,
|
||||||
global_playSpeed,
|
global_playSpeed,
|
||||||
custom_playSpeed,
|
custom_playSpeed,
|
||||||
has_skips,
|
has_skips,
|
||||||
|
@ -142,14 +125,9 @@ class FeedFilter(vararg properties_: String) : Serializable {
|
||||||
favorite,
|
favorite,
|
||||||
}
|
}
|
||||||
|
|
||||||
enum class FeedFilterGroup(val nameRes: Int, vararg values: ItemProperties) {
|
enum class FeedFilterGroup(val nameRes: Int, vararg values_: ItemProperties) {
|
||||||
KEEP_UPDATED(R.string.keep_updated, ItemProperties(R.string.yes, States.keepUpdated.name), ItemProperties(R.string.no, States.not_keepUpdated.name)),
|
KEEP_UPDATED(R.string.keep_updated, ItemProperties(R.string.yes, States.keepUpdated.name), ItemProperties(R.string.no, States.not_keepUpdated.name)),
|
||||||
PLAY_SPEED(R.string.play_speed, ItemProperties(R.string.global_speed, States.global_playSpeed.name), ItemProperties(R.string.custom_speed, States.custom_playSpeed.name)),
|
|
||||||
OPINION(R.string.commented, ItemProperties(R.string.yes, States.has_comments.name), ItemProperties(R.string.no, States.no_comments.name)),
|
OPINION(R.string.commented, ItemProperties(R.string.yes, States.has_comments.name), ItemProperties(R.string.no, States.no_comments.name)),
|
||||||
HAS_VIDEO(R.string.has_video, ItemProperties(R.string.yes, States.has_video.name), ItemProperties(R.string.no, States.no_video.name)),
|
|
||||||
ORIGIN(R.string.feed_origin, ItemProperties(R.string.youtube, States.youtube.name), ItemProperties(R.string.rss, States.rss.name)),
|
|
||||||
TYPE(R.string.feed_type, ItemProperties(R.string.synthetic, States.synthetic.name), ItemProperties(R.string.normal, States.normal.name)),
|
|
||||||
SKIPS(R.string.has_skips, ItemProperties(R.string.yes, States.has_skips.name), ItemProperties(R.string.no, States.no_skips.name)),
|
|
||||||
RATING(R.string.rating_label, ItemProperties(R.string.unrated, States.unrated.name),
|
RATING(R.string.rating_label, ItemProperties(R.string.unrated, States.unrated.name),
|
||||||
ItemProperties(R.string.trash, States.trash.name),
|
ItemProperties(R.string.trash, States.trash.name),
|
||||||
ItemProperties(R.string.bad, States.bad.name),
|
ItemProperties(R.string.bad, States.bad.name),
|
||||||
|
@ -157,15 +135,20 @@ class FeedFilter(vararg properties_: String) : Serializable {
|
||||||
ItemProperties(R.string.good, States.good.name),
|
ItemProperties(R.string.good, States.good.name),
|
||||||
ItemProperties(R.string.favorite, States.favorite.name),
|
ItemProperties(R.string.favorite, States.favorite.name),
|
||||||
),
|
),
|
||||||
|
HAS_VIDEO(R.string.has_video, ItemProperties(R.string.yes, States.has_video.name), ItemProperties(R.string.no, States.no_video.name)),
|
||||||
|
PLAY_SPEED(R.string.play_speed, ItemProperties(R.string.global_speed, States.global_playSpeed.name), ItemProperties(R.string.custom_speed, States.custom_playSpeed.name)),
|
||||||
|
ORIGIN(R.string.feed_origin, ItemProperties(R.string.youtube, States.youtube.name), ItemProperties(R.string.rss, States.rss.name)),
|
||||||
|
TYPE(R.string.feed_type, ItemProperties(R.string.synthetic, States.synthetic.name), ItemProperties(R.string.normal, States.normal.name)),
|
||||||
|
SKIPS(R.string.has_skips, ItemProperties(R.string.yes, States.has_skips.name), ItemProperties(R.string.no, States.no_skips.name)),
|
||||||
AUTO_DELETE(R.string.auto_delete, ItemProperties(R.string.always, States.always_auto_delete.name),
|
AUTO_DELETE(R.string.auto_delete, ItemProperties(R.string.always, States.always_auto_delete.name),
|
||||||
ItemProperties(R.string.never, States.never_auto_delete.name),
|
ItemProperties(R.string.never, States.never_auto_delete.name),
|
||||||
ItemProperties(R.string.global, States.global_auto_delete.name), ),
|
ItemProperties(R.string.global, States.global_auto_delete.name), ),
|
||||||
|
PREF_STREAMING(R.string.pref_stream_over_download_title, ItemProperties(R.string.yes, States.pref_streaming.name), ItemProperties(R.string.no, States.not_pref_streaming.name)),
|
||||||
AUTO_DOWNLOAD(R.string.auto_download, ItemProperties(R.string.yes, States.autoDownload.name), ItemProperties(R.string.no, States.not_autoDownload.name));
|
AUTO_DOWNLOAD(R.string.auto_download, ItemProperties(R.string.yes, States.autoDownload.name), ItemProperties(R.string.no, States.not_autoDownload.name));
|
||||||
|
|
||||||
@JvmField
|
val values: Array<ItemProperties> = arrayOf(*values_)
|
||||||
val values: Array<ItemProperties> = arrayOf(*values)
|
|
||||||
|
|
||||||
class ItemProperties(@JvmField val displayName: Int, @JvmField val filterId: String)
|
class ItemProperties(val displayName: Int, val filterId: String)
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
|
|
@ -4,12 +4,6 @@ enum class MediaType {
|
||||||
AUDIO, VIDEO, UNKNOWN;
|
AUDIO, VIDEO, UNKNOWN;
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
// private val AUDIO_APPLICATION_MIME_STRINGS: Set<String> = HashSet(mutableListOf(
|
|
||||||
// "application/ogg",
|
|
||||||
// "application/opus",
|
|
||||||
// "application/x-flac"
|
|
||||||
// ))
|
|
||||||
|
|
||||||
val AUDIO_APPLICATION_MIME_STRINGS: HashSet<String> = hashSetOf(
|
val AUDIO_APPLICATION_MIME_STRINGS: HashSet<String> = hashSetOf(
|
||||||
"application/ogg",
|
"application/ogg",
|
||||||
"application/opus",
|
"application/opus",
|
||||||
|
|
|
@ -40,9 +40,6 @@ import org.apache.commons.io.input.BOMInputStream
|
||||||
import java.io.InputStreamReader
|
import java.io.InputStreamReader
|
||||||
import java.io.Reader
|
import java.io.Reader
|
||||||
|
|
||||||
/**
|
|
||||||
* Activity for Opml Import.
|
|
||||||
*/
|
|
||||||
class OpmlImportActivity : AppCompatActivity() {
|
class OpmlImportActivity : AppCompatActivity() {
|
||||||
private var uri: Uri? = null
|
private var uri: Uri? = null
|
||||||
private var _binding: OpmlSelectionBinding? = null
|
private var _binding: OpmlSelectionBinding? = null
|
||||||
|
@ -82,6 +79,7 @@ class OpmlImportActivity : AppCompatActivity() {
|
||||||
supportActionBar?.setDisplayHomeAsUpEnabled(true)
|
supportActionBar?.setDisplayHomeAsUpEnabled(true)
|
||||||
_binding = OpmlSelectionBinding.inflate(layoutInflater)
|
_binding = OpmlSelectionBinding.inflate(layoutInflater)
|
||||||
setContentView(binding.root)
|
setContentView(binding.root)
|
||||||
|
Logd(TAG, "onCreate")
|
||||||
|
|
||||||
binding.feedlist.choiceMode = ListView.CHOICE_MODE_MULTIPLE
|
binding.feedlist.choiceMode = ListView.CHOICE_MODE_MULTIPLE
|
||||||
binding.feedlist.onItemClickListener = OnItemClickListener { _: AdapterView<*>?, _: View?, _: Int, _: Long ->
|
binding.feedlist.onItemClickListener = OnItemClickListener { _: AdapterView<*>?, _: View?, _: Int, _: Long ->
|
||||||
|
@ -106,7 +104,6 @@ class OpmlImportActivity : AppCompatActivity() {
|
||||||
}
|
}
|
||||||
binding.butConfirm.setOnClickListener {
|
binding.butConfirm.setOnClickListener {
|
||||||
binding.progressBar.visibility = View.VISIBLE
|
binding.progressBar.visibility = View.VISIBLE
|
||||||
|
|
||||||
lifecycleScope.launch {
|
lifecycleScope.launch {
|
||||||
try {
|
try {
|
||||||
withContext(Dispatchers.IO) {
|
withContext(Dispatchers.IO) {
|
||||||
|
|
|
@ -943,7 +943,7 @@ fun EpisodesFilterDialog(filter: EpisodeFilter? = null, filtersDisabled: Mutable
|
||||||
window.setGravity(Gravity.BOTTOM)
|
window.setGravity(Gravity.BOTTOM)
|
||||||
window.setDimAmount(0f)
|
window.setDimAmount(0f)
|
||||||
}
|
}
|
||||||
Surface(modifier = Modifier.fillMaxWidth().height(500.dp), shape = RoundedCornerShape(16.dp)) {
|
Surface(modifier = Modifier.fillMaxWidth().height(350.dp), color = MaterialTheme.colorScheme.surface.copy(alpha = 0.8f), shape = RoundedCornerShape(16.dp)) {
|
||||||
val textColor = MaterialTheme.colorScheme.onSurface
|
val textColor = MaterialTheme.colorScheme.onSurface
|
||||||
val scrollState = rememberScrollState()
|
val scrollState = rememberScrollState()
|
||||||
Column(Modifier.fillMaxSize().verticalScroll(scrollState)) {
|
Column(Modifier.fillMaxSize().verticalScroll(scrollState)) {
|
||||||
|
|
|
@ -1,147 +0,0 @@
|
||||||
package ac.mdiq.podcini.ui.dialog
|
|
||||||
|
|
||||||
import ac.mdiq.podcini.R
|
|
||||||
import ac.mdiq.podcini.storage.model.EpisodeFilter
|
|
||||||
import ac.mdiq.podcini.ui.compose.CustomTheme
|
|
||||||
import ac.mdiq.podcini.ui.compose.NonlazyGrid
|
|
||||||
import android.os.Bundle
|
|
||||||
import android.view.LayoutInflater
|
|
||||||
import android.view.View
|
|
||||||
import android.view.ViewGroup
|
|
||||||
import androidx.compose.foundation.BorderStroke
|
|
||||||
import androidx.compose.foundation.layout.*
|
|
||||||
import androidx.compose.foundation.rememberScrollState
|
|
||||||
import androidx.compose.foundation.verticalScroll
|
|
||||||
import androidx.compose.material3.Button
|
|
||||||
import androidx.compose.material3.MaterialTheme
|
|
||||||
import androidx.compose.material3.OutlinedButton
|
|
||||||
import androidx.compose.material3.Text
|
|
||||||
import androidx.compose.runtime.*
|
|
||||||
import androidx.compose.ui.Alignment
|
|
||||||
import androidx.compose.ui.Modifier
|
|
||||||
import androidx.compose.ui.graphics.Color
|
|
||||||
import androidx.compose.ui.platform.ComposeView
|
|
||||||
import androidx.compose.ui.res.stringResource
|
|
||||||
import androidx.compose.ui.text.font.FontWeight
|
|
||||||
import androidx.compose.ui.unit.dp
|
|
||||||
import com.google.android.material.bottomsheet.BottomSheetDialogFragment
|
|
||||||
|
|
||||||
// TODO: to be removed
|
|
||||||
abstract class EpisodeFilterDialog : BottomSheetDialogFragment() {
|
|
||||||
|
|
||||||
var filter: EpisodeFilter? = null
|
|
||||||
val filtersDisabled: MutableSet<EpisodeFilter.EpisodesFilterGroup> = mutableSetOf()
|
|
||||||
private val filterValues: MutableSet<String> = mutableSetOf()
|
|
||||||
|
|
||||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
|
|
||||||
val composeView = ComposeView(requireContext()).apply {
|
|
||||||
setContent {
|
|
||||||
CustomTheme(requireContext()) {
|
|
||||||
MainView()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return composeView
|
|
||||||
}
|
|
||||||
|
|
||||||
@Composable
|
|
||||||
fun MainView() {
|
|
||||||
val textColor = MaterialTheme.colorScheme.onSurface
|
|
||||||
val scrollState = rememberScrollState()
|
|
||||||
Column(Modifier.fillMaxSize().verticalScroll(scrollState)) {
|
|
||||||
var selectNone by remember { mutableStateOf(false) }
|
|
||||||
for (item in EpisodeFilter.EpisodesFilterGroup.entries) {
|
|
||||||
if (item in filtersDisabled) continue
|
|
||||||
if (item.values.size == 2) {
|
|
||||||
Row(modifier = Modifier.padding(2.dp).fillMaxWidth(), horizontalArrangement = Arrangement.Center, verticalAlignment = Alignment.CenterVertically) {
|
|
||||||
var selectedIndex by remember { mutableStateOf(-1) }
|
|
||||||
if (selectNone) selectedIndex = -1
|
|
||||||
LaunchedEffect(Unit) {
|
|
||||||
if (filter != null) {
|
|
||||||
if (item.values[0].filterId in filter!!.properties) selectedIndex = 0
|
|
||||||
else if (item.values[1].filterId in filter!!.properties) selectedIndex = 1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Text(stringResource(item.nameRes) + " :", fontWeight = FontWeight.Bold, style = MaterialTheme.typography.headlineSmall, color = textColor, modifier = Modifier.padding(end = 10.dp))
|
|
||||||
Spacer(Modifier.weight(0.3f))
|
|
||||||
OutlinedButton(
|
|
||||||
modifier = Modifier.padding(2.dp), border = BorderStroke(2.dp, if (selectedIndex != 0) textColor else Color.Green),
|
|
||||||
onClick = {
|
|
||||||
if (selectedIndex != 0) {
|
|
||||||
selectNone = false
|
|
||||||
selectedIndex = 0
|
|
||||||
filterValues.add(item.values[0].filterId)
|
|
||||||
filterValues.remove(item.values[1].filterId)
|
|
||||||
} else {
|
|
||||||
selectedIndex = -1
|
|
||||||
filterValues.remove(item.values[0].filterId)
|
|
||||||
}
|
|
||||||
onFilterChanged(filterValues)
|
|
||||||
},
|
|
||||||
) {
|
|
||||||
Text(text = stringResource(item.values[0].displayName), color = textColor)
|
|
||||||
}
|
|
||||||
Spacer(Modifier.weight(0.1f))
|
|
||||||
OutlinedButton(
|
|
||||||
modifier = Modifier.padding(2.dp), border = BorderStroke(2.dp, if (selectedIndex != 1) textColor else Color.Green),
|
|
||||||
onClick = {
|
|
||||||
if (selectedIndex != 1) {
|
|
||||||
selectNone = false
|
|
||||||
selectedIndex = 1
|
|
||||||
filterValues.add(item.values[1].filterId)
|
|
||||||
filterValues.remove(item.values[0].filterId)
|
|
||||||
} else {
|
|
||||||
selectedIndex = -1
|
|
||||||
filterValues.remove(item.values[1].filterId)
|
|
||||||
}
|
|
||||||
onFilterChanged(filterValues)
|
|
||||||
},
|
|
||||||
) {
|
|
||||||
Text(text = stringResource(item.values[1].displayName), color = textColor)
|
|
||||||
}
|
|
||||||
Spacer(Modifier.weight(0.5f))
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Column(modifier = Modifier.padding(start = 5.dp, bottom = 2.dp).fillMaxWidth()) {
|
|
||||||
Text(stringResource(item.nameRes) + " :", fontWeight = FontWeight.Bold, style = MaterialTheme.typography.headlineSmall, color = textColor)
|
|
||||||
NonlazyGrid(columns = 3, itemCount = item.values.size) { index ->
|
|
||||||
var selected by remember { mutableStateOf(false) }
|
|
||||||
if (selectNone) selected = false
|
|
||||||
OutlinedButton(modifier = Modifier.padding(0.dp).heightIn(min = 20.dp).widthIn(min = 20.dp).wrapContentWidth(),
|
|
||||||
border = BorderStroke(2.dp, if (selected) Color.Green else textColor),
|
|
||||||
onClick = {
|
|
||||||
selectNone = false
|
|
||||||
selected = !selected
|
|
||||||
if (selected) filterValues.add(item.values[index].filterId)
|
|
||||||
else filterValues.remove(item.values[index].filterId)
|
|
||||||
onFilterChanged(filterValues)
|
|
||||||
},
|
|
||||||
) {
|
|
||||||
Text(text = stringResource(item.values[index].displayName), maxLines = 1, color = textColor)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Row {
|
|
||||||
Spacer(Modifier.weight(0.3f))
|
|
||||||
Button(onClick = {
|
|
||||||
selectNone = true
|
|
||||||
onFilterChanged(setOf(""))
|
|
||||||
}) {
|
|
||||||
Text(stringResource(R.string.reset))
|
|
||||||
}
|
|
||||||
Spacer(Modifier.weight(0.4f))
|
|
||||||
Button(onClick = {
|
|
||||||
dismiss()
|
|
||||||
}) {
|
|
||||||
Text(stringResource(R.string.close_label))
|
|
||||||
}
|
|
||||||
Spacer(Modifier.weight(0.3f))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
abstract fun onFilterChanged(newFilterValues: Set<String>)
|
|
||||||
|
|
||||||
}
|
|
|
@ -93,12 +93,12 @@ class AllEpisodesFragment : BaseEpisodesFragment() {
|
||||||
showFilterDialog = true
|
showFilterDialog = true
|
||||||
// AllEpisodesFilterDialog.newInstance(getFilter()).show(childFragmentManager, null)
|
// AllEpisodesFilterDialog.newInstance(getFilter()).show(childFragmentManager, null)
|
||||||
}
|
}
|
||||||
R.id.action_favorites -> {
|
// R.id.action_favorites -> {
|
||||||
val filter = getFilter().properties.toMutableSet()
|
// val filter = getFilter().properties.toMutableSet()
|
||||||
if (filter.contains(EpisodeFilter.States.is_favorite.name)) filter.remove(EpisodeFilter.States.is_favorite.name)
|
// if (filter.contains(EpisodeFilter.States.is_favorite.name)) filter.remove(EpisodeFilter.States.is_favorite.name)
|
||||||
else filter.add(EpisodeFilter.States.is_favorite.name)
|
// else filter.add(EpisodeFilter.States.is_favorite.name)
|
||||||
onFilterChanged(FlowEvent.AllEpisodesFilterEvent(HashSet(filter)))
|
// onFilterChanged(FlowEvent.AllEpisodesFilterEvent(HashSet(filter)))
|
||||||
}
|
// }
|
||||||
R.id.episodes_sort -> AllEpisodesSortDialog().show(childFragmentManager.beginTransaction(), "SortDialog")
|
R.id.episodes_sort -> AllEpisodesSortDialog().show(childFragmentManager.beginTransaction(), "SortDialog")
|
||||||
R.id.switch_queue -> SwitchQueueDialog(activity as MainActivity).show()
|
R.id.switch_queue -> SwitchQueueDialog(activity as MainActivity).show()
|
||||||
else -> return false
|
else -> return false
|
||||||
|
@ -117,7 +117,7 @@ class AllEpisodesFragment : BaseEpisodesFragment() {
|
||||||
EventFlow.events.collectLatest { event ->
|
EventFlow.events.collectLatest { event ->
|
||||||
Logd(TAG, "Received event: ${event.TAG}")
|
Logd(TAG, "Received event: ${event.TAG}")
|
||||||
when (event) {
|
when (event) {
|
||||||
is FlowEvent.AllEpisodesFilterEvent -> onFilterChanged(event)
|
// is FlowEvent.AllEpisodesFilterEvent -> onFilterChanged(event)
|
||||||
is FlowEvent.AllEpisodesSortEvent -> {
|
is FlowEvent.AllEpisodesSortEvent -> {
|
||||||
page = 1
|
page = 1
|
||||||
loadItems()
|
loadItems()
|
||||||
|
@ -128,11 +128,11 @@ class AllEpisodesFragment : BaseEpisodesFragment() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun onFilterChanged(event: FlowEvent.AllEpisodesFilterEvent) {
|
// private fun onFilterChanged(event: FlowEvent.AllEpisodesFilterEvent) {
|
||||||
prefFilterAllEpisodes = StringUtils.join(event.filterValues, ",")
|
// prefFilterAllEpisodes = StringUtils.join(event.filterValues, ",")
|
||||||
page = 1
|
// page = 1
|
||||||
loadItems()
|
// loadItems()
|
||||||
}
|
// }
|
||||||
|
|
||||||
override fun updateToolbar() {
|
override fun updateToolbar() {
|
||||||
swipeActions.setFilter(getFilter())
|
swipeActions.setFilter(getFilter())
|
||||||
|
@ -142,11 +142,14 @@ class AllEpisodesFragment : BaseEpisodesFragment() {
|
||||||
emptyView.setMessage(R.string.no_all_episodes_filtered_label)
|
emptyView.setMessage(R.string.no_all_episodes_filtered_label)
|
||||||
} else emptyView.setMessage(R.string.no_all_episodes_label)
|
} else emptyView.setMessage(R.string.no_all_episodes_label)
|
||||||
infoBarText.value = info
|
infoBarText.value = info
|
||||||
toolbar.menu?.findItem(R.id.action_favorites)?.setIcon(if (getFilter().showIsFavorite) R.drawable.ic_star else R.drawable.ic_star_border)
|
// toolbar.menu?.findItem(R.id.action_favorites)?.setIcon(if (getFilter().showIsFavorite) R.drawable.ic_star else R.drawable.ic_star_border)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onFilterChanged(filterValues: Set<String>) {
|
override fun onFilterChanged(filterValues: Set<String>) {
|
||||||
EventFlow.postEvent(FlowEvent.AllEpisodesFilterEvent(filterValues))
|
// EventFlow.postEvent(FlowEvent.AllEpisodesFilterEvent(filterValues))
|
||||||
|
prefFilterAllEpisodes = StringUtils.join(filterValues, ",")
|
||||||
|
page = 1
|
||||||
|
loadItems()
|
||||||
}
|
}
|
||||||
|
|
||||||
class AllEpisodesSortDialog : EpisodeSortDialog() {
|
class AllEpisodesSortDialog : EpisodeSortDialog() {
|
||||||
|
|
|
@ -100,7 +100,12 @@ import java.util.*
|
||||||
if (showFilterDialog) EpisodesFilterDialog(filter = getFilter(),
|
if (showFilterDialog) EpisodesFilterDialog(filter = getFilter(),
|
||||||
filtersDisabled = mutableSetOf(EpisodeFilter.EpisodesFilterGroup.DOWNLOADED, EpisodeFilter.EpisodesFilterGroup.MEDIA),
|
filtersDisabled = mutableSetOf(EpisodeFilter.EpisodesFilterGroup.DOWNLOADED, EpisodeFilter.EpisodesFilterGroup.MEDIA),
|
||||||
onDismissRequest = { showFilterDialog = false } ) {
|
onDismissRequest = { showFilterDialog = false } ) {
|
||||||
EventFlow.postEvent(FlowEvent.DownloadsFilterEvent(it))
|
// EventFlow.postEvent(FlowEvent.DownloadsFilterEvent(it))
|
||||||
|
val fSet = it.toMutableSet()
|
||||||
|
fSet.add(EpisodeFilter.States.downloaded.name)
|
||||||
|
prefFilterDownloads = StringUtils.join(fSet, ",")
|
||||||
|
Logd(TAG, "onFilterChanged: $prefFilterDownloads")
|
||||||
|
loadItems()
|
||||||
}
|
}
|
||||||
Column {
|
Column {
|
||||||
InforBar(infoBarText, leftAction = leftActionState, rightAction = rightActionState, actionConfig = {swipeActions.showDialog()})
|
InforBar(infoBarText, leftAction = leftActionState, rightAction = rightActionState, actionConfig = {swipeActions.showDialog()})
|
||||||
|
@ -261,7 +266,7 @@ import java.util.*
|
||||||
Logd(TAG, "Received event: ${event.TAG}")
|
Logd(TAG, "Received event: ${event.TAG}")
|
||||||
when (event) {
|
when (event) {
|
||||||
is FlowEvent.EpisodeEvent -> onEpisodeEvent(event)
|
is FlowEvent.EpisodeEvent -> onEpisodeEvent(event)
|
||||||
is FlowEvent.DownloadsFilterEvent -> onFilterChanged(event)
|
// is FlowEvent.DownloadsFilterEvent -> onFilterChanged(event)
|
||||||
is FlowEvent.EpisodeMediaEvent -> onEpisodeMediaEvent(event)
|
is FlowEvent.EpisodeMediaEvent -> onEpisodeMediaEvent(event)
|
||||||
is FlowEvent.PlayerSettingsEvent -> loadItems()
|
is FlowEvent.PlayerSettingsEvent -> loadItems()
|
||||||
is FlowEvent.DownloadLogEvent -> loadItems()
|
is FlowEvent.DownloadLogEvent -> loadItems()
|
||||||
|
@ -283,13 +288,13 @@ import java.util.*
|
||||||
// }
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun onFilterChanged(event: FlowEvent.DownloadsFilterEvent) {
|
// private fun onFilterChanged(event: FlowEvent.DownloadsFilterEvent) {
|
||||||
val fSet = event.filterValues?.toMutableSet() ?: mutableSetOf()
|
// val fSet = event.filterValues?.toMutableSet() ?: mutableSetOf()
|
||||||
fSet.add(EpisodeFilter.States.downloaded.name)
|
// fSet.add(EpisodeFilter.States.downloaded.name)
|
||||||
prefFilterDownloads = StringUtils.join(fSet, ",")
|
// prefFilterDownloads = StringUtils.join(fSet, ",")
|
||||||
Logd(TAG, "onFilterChanged: $prefFilterDownloads")
|
// Logd(TAG, "onFilterChanged: $prefFilterDownloads")
|
||||||
loadItems()
|
// loadItems()
|
||||||
}
|
// }
|
||||||
|
|
||||||
private fun addEmptyView() {
|
private fun addEmptyView() {
|
||||||
emptyView = EmptyViewHandler(requireContext())
|
emptyView = EmptyViewHandler(requireContext())
|
||||||
|
@ -434,21 +439,6 @@ 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(EpisodeFilter.EpisodesFilterGroup.DOWNLOADED)
|
|
||||||
// dialog.filtersDisabled.add(EpisodeFilter.EpisodesFilterGroup.MEDIA)
|
|
||||||
// return dialog
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
val TAG = DownloadsFragment::class.simpleName ?: "Anonymous"
|
val TAG = DownloadsFragment::class.simpleName ?: "Anonymous"
|
||||||
|
|
||||||
|
|
|
@ -42,7 +42,6 @@ import kotlin.math.min
|
||||||
|
|
||||||
@UnstableApi override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
|
@UnstableApi override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
|
||||||
val root = super.onCreateView(inflater, container, savedInstanceState)
|
val root = super.onCreateView(inflater, container, savedInstanceState)
|
||||||
|
|
||||||
Logd(TAG, "fragment onCreateView")
|
Logd(TAG, "fragment onCreateView")
|
||||||
toolbar.inflateMenu(R.menu.playback_history)
|
toolbar.inflateMenu(R.menu.playback_history)
|
||||||
toolbar.setTitle(R.string.playback_history_label)
|
toolbar.setTitle(R.string.playback_history_label)
|
||||||
|
@ -50,7 +49,6 @@ import kotlin.math.min
|
||||||
emptyView.setIcon(R.drawable.ic_history)
|
emptyView.setIcon(R.drawable.ic_history)
|
||||||
emptyView.setTitle(R.string.no_history_head_label)
|
emptyView.setTitle(R.string.no_history_head_label)
|
||||||
emptyView.setMessage(R.string.no_history_label)
|
emptyView.setMessage(R.string.no_history_label)
|
||||||
|
|
||||||
return root
|
return root
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -130,7 +128,7 @@ import kotlin.math.min
|
||||||
emptyView.setMessage(R.string.no_all_episodes_filtered_label)
|
emptyView.setMessage(R.string.no_all_episodes_filtered_label)
|
||||||
} else emptyView.setMessage(R.string.no_all_episodes_label)
|
} else emptyView.setMessage(R.string.no_all_episodes_label)
|
||||||
infoBarText.value = info
|
infoBarText.value = info
|
||||||
toolbar.menu?.findItem(R.id.action_favorites)?.setIcon(if (getFilter().showIsFavorite) R.drawable.ic_star else R.drawable.ic_star_border)
|
// toolbar.menu?.findItem(R.id.action_favorites)?.setIcon(if (getFilter().showIsFavorite) R.drawable.ic_star else R.drawable.ic_star_border)
|
||||||
}
|
}
|
||||||
|
|
||||||
private var eventSink: Job? = null
|
private var eventSink: Job? = null
|
||||||
|
|
|
@ -720,14 +720,14 @@ class OnlineFeedFragment : Fragment() {
|
||||||
updateToolbar()
|
updateToolbar()
|
||||||
return root
|
return root
|
||||||
}
|
}
|
||||||
override fun onStart() {
|
// override fun onStart() {
|
||||||
super.onStart()
|
// super.onStart()
|
||||||
procFlowEvents()
|
//// procFlowEvents()
|
||||||
}
|
// }
|
||||||
override fun onStop() {
|
// override fun onStop() {
|
||||||
super.onStop()
|
// super.onStop()
|
||||||
cancelFlowEvents()
|
//// cancelFlowEvents()
|
||||||
}
|
// }
|
||||||
override fun onDestroyView() {
|
override fun onDestroyView() {
|
||||||
episodeList.clear()
|
episodeList.clear()
|
||||||
super.onDestroyView()
|
super.onDestroyView()
|
||||||
|
@ -750,7 +750,7 @@ class OnlineFeedFragment : Fragment() {
|
||||||
binding.toolbar.menu.findItem(R.id.episodes_sort).setVisible(false)
|
binding.toolbar.menu.findItem(R.id.episodes_sort).setVisible(false)
|
||||||
// binding.toolbar.menu.findItem(R.id.refresh_item).setVisible(false)
|
// binding.toolbar.menu.findItem(R.id.refresh_item).setVisible(false)
|
||||||
binding.toolbar.menu.findItem(R.id.action_search).setVisible(false)
|
binding.toolbar.menu.findItem(R.id.action_search).setVisible(false)
|
||||||
binding.toolbar.menu.findItem(R.id.action_favorites).setVisible(false)
|
// binding.toolbar.menu.findItem(R.id.action_favorites).setVisible(false)
|
||||||
binding.toolbar.menu.findItem(R.id.filter_items).setVisible(false)
|
binding.toolbar.menu.findItem(R.id.filter_items).setVisible(false)
|
||||||
infoBarText.value = "${episodes.size} episodes"
|
infoBarText.value = "${episodes.size} episodes"
|
||||||
}
|
}
|
||||||
|
@ -760,23 +760,23 @@ class OnlineFeedFragment : Fragment() {
|
||||||
else -> return false
|
else -> return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
private var eventSink: Job? = null
|
// private var eventSink: Job? = null
|
||||||
private fun cancelFlowEvents() {
|
// private fun cancelFlowEvents() {
|
||||||
eventSink?.cancel()
|
// eventSink?.cancel()
|
||||||
eventSink = null
|
// eventSink = null
|
||||||
}
|
// }
|
||||||
private fun procFlowEvents() {
|
// private fun procFlowEvents() {
|
||||||
if (eventSink != null) return
|
// if (eventSink != null) return
|
||||||
eventSink = lifecycleScope.launch {
|
// eventSink = lifecycleScope.launch {
|
||||||
EventFlow.events.collectLatest { event ->
|
// EventFlow.events.collectLatest { event ->
|
||||||
Logd(TAG, "Received event: ${event.TAG}")
|
// Logd(TAG, "Received event: ${event.TAG}")
|
||||||
when (event) {
|
// when (event) {
|
||||||
is FlowEvent.AllEpisodesFilterEvent -> page = 1
|
// is FlowEvent.AllEpisodesFilterEvent -> page = 1
|
||||||
else -> {}
|
// else -> {}
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
const val PREF_NAME: String = "EpisodesListFragment"
|
const val PREF_NAME: String = "EpisodesListFragment"
|
||||||
|
|
|
@ -2,49 +2,60 @@ package ac.mdiq.podcini.ui.fragment
|
||||||
|
|
||||||
|
|
||||||
import ac.mdiq.podcini.R
|
import ac.mdiq.podcini.R
|
||||||
import ac.mdiq.podcini.databinding.HorizontalFeedItemBinding
|
|
||||||
import ac.mdiq.podcini.databinding.SearchFragmentBinding
|
import ac.mdiq.podcini.databinding.SearchFragmentBinding
|
||||||
import ac.mdiq.podcini.net.download.DownloadStatus
|
import ac.mdiq.podcini.net.download.DownloadStatus
|
||||||
import ac.mdiq.podcini.net.feed.discovery.CombinedSearcher
|
import ac.mdiq.podcini.net.feed.discovery.CombinedSearcher
|
||||||
import ac.mdiq.podcini.storage.database.RealmDB.realm
|
import ac.mdiq.podcini.storage.database.RealmDB.realm
|
||||||
import ac.mdiq.podcini.storage.model.Episode
|
import ac.mdiq.podcini.storage.model.Episode
|
||||||
import ac.mdiq.podcini.storage.model.Feed
|
import ac.mdiq.podcini.storage.model.Feed
|
||||||
|
import ac.mdiq.podcini.storage.model.Rating
|
||||||
import ac.mdiq.podcini.storage.utils.EpisodeUtil
|
import ac.mdiq.podcini.storage.utils.EpisodeUtil
|
||||||
import ac.mdiq.podcini.ui.actions.MenuItemUtils
|
|
||||||
import ac.mdiq.podcini.ui.activity.MainActivity
|
import ac.mdiq.podcini.ui.activity.MainActivity
|
||||||
import ac.mdiq.podcini.ui.compose.CustomTheme
|
import ac.mdiq.podcini.ui.compose.CustomTheme
|
||||||
import ac.mdiq.podcini.ui.compose.EpisodeLazyColumn
|
import ac.mdiq.podcini.ui.compose.EpisodeLazyColumn
|
||||||
import ac.mdiq.podcini.ui.compose.EpisodeVM
|
import ac.mdiq.podcini.ui.compose.EpisodeVM
|
||||||
import ac.mdiq.podcini.ui.dialog.CustomFeedNameDialog
|
import ac.mdiq.podcini.ui.compose.NonlazyGrid
|
||||||
import ac.mdiq.podcini.ui.dialog.RemoveFeedDialog
|
|
||||||
import ac.mdiq.podcini.ui.dialog.TagSettingsDialog
|
|
||||||
import ac.mdiq.podcini.ui.utils.EmptyViewHandler
|
|
||||||
import ac.mdiq.podcini.ui.view.SquareImageView
|
|
||||||
import ac.mdiq.podcini.util.EventFlow
|
import ac.mdiq.podcini.util.EventFlow
|
||||||
import ac.mdiq.podcini.util.FlowEvent
|
import ac.mdiq.podcini.util.FlowEvent
|
||||||
import ac.mdiq.podcini.util.Logd
|
import ac.mdiq.podcini.util.Logd
|
||||||
import android.annotation.SuppressLint
|
import android.annotation.SuppressLint
|
||||||
import android.app.Activity
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.os.Handler
|
import android.os.Handler
|
||||||
import android.os.Looper
|
import android.os.Looper
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import android.util.Pair
|
import android.util.Pair
|
||||||
import android.view.*
|
import android.view.LayoutInflater
|
||||||
|
import android.view.MenuItem
|
||||||
|
import android.view.View
|
||||||
|
import android.view.ViewGroup
|
||||||
import android.view.inputmethod.InputMethodManager
|
import android.view.inputmethod.InputMethodManager
|
||||||
import android.widget.Button
|
|
||||||
import androidx.annotation.StringRes
|
|
||||||
import androidx.appcompat.widget.SearchView
|
import androidx.appcompat.widget.SearchView
|
||||||
import androidx.cardview.widget.CardView
|
import androidx.compose.foundation.ExperimentalFoundationApi
|
||||||
import androidx.compose.runtime.mutableStateListOf
|
import androidx.compose.foundation.background
|
||||||
|
import androidx.compose.foundation.combinedClickable
|
||||||
|
import androidx.compose.foundation.layout.*
|
||||||
|
import androidx.compose.foundation.lazy.LazyRow
|
||||||
|
import androidx.compose.foundation.lazy.rememberLazyListState
|
||||||
|
import androidx.compose.material3.Checkbox
|
||||||
|
import androidx.compose.material3.Icon
|
||||||
|
import androidx.compose.material3.MaterialTheme
|
||||||
|
import androidx.compose.material3.Text
|
||||||
|
import androidx.compose.runtime.*
|
||||||
|
import androidx.compose.ui.Alignment
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.graphics.Color
|
||||||
|
import androidx.compose.ui.graphics.vector.ImageVector
|
||||||
|
import androidx.compose.ui.platform.LocalContext
|
||||||
|
import androidx.compose.ui.res.stringResource
|
||||||
|
import androidx.compose.ui.res.vectorResource
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import androidx.constraintlayout.compose.ConstraintLayout
|
||||||
import androidx.fragment.app.Fragment
|
import androidx.fragment.app.Fragment
|
||||||
import androidx.lifecycle.lifecycleScope
|
import androidx.lifecycle.lifecycleScope
|
||||||
import androidx.media3.common.util.UnstableApi
|
import androidx.media3.common.util.UnstableApi
|
||||||
import androidx.recyclerview.widget.LinearLayoutManager
|
import coil.compose.AsyncImage
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import coil.request.CachePolicy
|
||||||
import coil.ImageLoader
|
|
||||||
import coil.load
|
|
||||||
import coil.request.ImageRequest
|
import coil.request.ImageRequest
|
||||||
import com.google.android.material.appbar.MaterialToolbar
|
import com.google.android.material.appbar.MaterialToolbar
|
||||||
import com.google.android.material.chip.Chip
|
import com.google.android.material.chip.Chip
|
||||||
|
@ -53,7 +64,7 @@ import kotlinx.coroutines.Job
|
||||||
import kotlinx.coroutines.flow.collectLatest
|
import kotlinx.coroutines.flow.collectLatest
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
import java.lang.ref.WeakReference
|
import java.text.NumberFormat
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Performs a search operation on all feeds or one specific feed and displays the search result.
|
* Performs a search operation on all feeds or one specific feed and displays the search result.
|
||||||
|
@ -63,12 +74,11 @@ class SearchFragment : Fragment() {
|
||||||
private var _binding: SearchFragmentBinding? = null
|
private var _binding: SearchFragmentBinding? = null
|
||||||
private val binding get() = _binding!!
|
private val binding get() = _binding!!
|
||||||
|
|
||||||
private lateinit var adapterFeeds: HorizontalFeedListAdapter
|
|
||||||
private lateinit var emptyViewHandler: EmptyViewHandler
|
|
||||||
private lateinit var searchView: SearchView
|
private lateinit var searchView: SearchView
|
||||||
private lateinit var chip: Chip
|
private lateinit var chip: Chip
|
||||||
private lateinit var automaticSearchDebouncer: Handler
|
private lateinit var automaticSearchDebouncer: Handler
|
||||||
|
|
||||||
|
private val resultFeeds = mutableStateListOf<Feed>()
|
||||||
private val results = mutableListOf<Episode>()
|
private val results = mutableListOf<Episode>()
|
||||||
private val vms = mutableStateListOf<EpisodeVM>()
|
private val vms = mutableStateListOf<EpisodeVM>()
|
||||||
|
|
||||||
|
@ -86,34 +96,20 @@ class SearchFragment : Fragment() {
|
||||||
Logd(TAG, "fragment onCreateView")
|
Logd(TAG, "fragment onCreateView")
|
||||||
setupToolbar(binding.toolbar)
|
setupToolbar(binding.toolbar)
|
||||||
|
|
||||||
binding.lazyColumn.setContent {
|
binding.resultsListView.setContent {
|
||||||
CustomTheme(requireContext()) {
|
CustomTheme(requireContext()) {
|
||||||
EpisodeLazyColumn(activity as MainActivity, vms = vms)
|
Column {
|
||||||
|
CriteriaList()
|
||||||
|
FeedsRow()
|
||||||
|
EpisodeLazyColumn(activity as MainActivity, vms = vms)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val recyclerViewFeeds = binding.recyclerViewFeeds
|
|
||||||
val layoutManagerFeeds = LinearLayoutManager(activity)
|
|
||||||
layoutManagerFeeds.orientation = RecyclerView.HORIZONTAL
|
|
||||||
recyclerViewFeeds.layoutManager = layoutManagerFeeds
|
|
||||||
adapterFeeds = object : HorizontalFeedListAdapter(activity as MainActivity) {
|
|
||||||
override fun onCreateContextMenu(contextMenu: ContextMenu, view: View, contextMenuInfo: ContextMenu.ContextMenuInfo?) {
|
|
||||||
super.onCreateContextMenu(contextMenu, view, contextMenuInfo)
|
|
||||||
MenuItemUtils.setOnClickListeners(contextMenu) { item: MenuItem -> this@SearchFragment.onContextItemSelected(item) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
recyclerViewFeeds.adapter = adapterFeeds
|
|
||||||
|
|
||||||
emptyViewHandler = EmptyViewHandler(requireContext())
|
|
||||||
// emptyViewHandler.attachToRecyclerView(recyclerView)
|
|
||||||
emptyViewHandler.setIcon(R.drawable.ic_search)
|
|
||||||
emptyViewHandler.setTitle(R.string.search_status_no_results)
|
|
||||||
emptyViewHandler.setMessage(R.string.type_to_search)
|
|
||||||
|
|
||||||
chip = binding.feedTitleChip
|
chip = binding.feedTitleChip
|
||||||
chip.setOnCloseIconClickListener {
|
chip.setOnCloseIconClickListener {
|
||||||
requireArguments().putLong(ARG_FEED, 0)
|
requireArguments().putLong(ARG_FEED, 0)
|
||||||
searchWithProgressBar()
|
search()
|
||||||
}
|
}
|
||||||
chip.visibility = if (requireArguments().getLong(ARG_FEED, 0) == 0L) View.GONE else View.VISIBLE
|
chip.visibility = if (requireArguments().getLong(ARG_FEED, 0) == 0L) View.GONE else View.VISIBLE
|
||||||
chip.text = requireArguments().getString(ARG_FEED_NAME, "")
|
chip.text = requireArguments().getString(ARG_FEED_NAME, "")
|
||||||
|
@ -139,6 +135,7 @@ class SearchFragment : Fragment() {
|
||||||
Logd(TAG, "onDestroyView")
|
Logd(TAG, "onDestroyView")
|
||||||
_binding = null
|
_binding = null
|
||||||
results.clear()
|
results.clear()
|
||||||
|
resultFeeds.clear()
|
||||||
vms.clear()
|
vms.clear()
|
||||||
super.onDestroyView()
|
super.onDestroyView()
|
||||||
}
|
}
|
||||||
|
@ -157,7 +154,7 @@ class SearchFragment : Fragment() {
|
||||||
searchView.setOnQueryTextListener(object : SearchView.OnQueryTextListener {
|
searchView.setOnQueryTextListener(object : SearchView.OnQueryTextListener {
|
||||||
@UnstableApi override fun onQueryTextSubmit(s: String): Boolean {
|
@UnstableApi override fun onQueryTextSubmit(s: String): Boolean {
|
||||||
searchView.clearFocus()
|
searchView.clearFocus()
|
||||||
searchWithProgressBar()
|
search()
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
@UnstableApi override fun onQueryTextChange(s: String): Boolean {
|
@UnstableApi override fun onQueryTextChange(s: String): Boolean {
|
||||||
|
@ -181,24 +178,24 @@ class SearchFragment : Fragment() {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onContextItemSelected(item: MenuItem): Boolean {
|
// override fun onContextItemSelected(item: MenuItem): Boolean {
|
||||||
val selectedFeedItem: Feed? = adapterFeeds.longPressedItem
|
//// val selectedFeedItem: Feed? = adapterFeeds.longPressedItem
|
||||||
if (selectedFeedItem != null && onMenuItemClicked(this, item.itemId, selectedFeedItem) {}) return true
|
//// if (selectedFeedItem != null && onMenuItemClicked(this, item.itemId, selectedFeedItem) {}) return true
|
||||||
return super.onContextItemSelected(item)
|
// return super.onContextItemSelected(item)
|
||||||
}
|
// }
|
||||||
|
|
||||||
private fun onMenuItemClicked(fragment: Fragment, menuItemId: Int, selectedFeed: Feed, callback: Runnable): Boolean {
|
// private fun onMenuItemClicked(fragment: Fragment, menuItemId: Int, selectedFeed: Feed, callback: Runnable): Boolean {
|
||||||
val context = fragment.requireContext()
|
// val context = fragment.requireContext()
|
||||||
when (menuItemId) {
|
// when (menuItemId) {
|
||||||
// R.id.rename_folder_item -> CustomFeedNameDialog(fragment.activity as Activity, selectedFeed).show()
|
//// R.id.rename_folder_item -> CustomFeedNameDialog(fragment.activity as Activity, selectedFeed).show()
|
||||||
R.id.edit_tags -> if (selectedFeed.preferences != null) TagSettingsDialog.newInstance(listOf(selectedFeed))
|
// R.id.edit_tags -> if (selectedFeed.preferences != null) TagSettingsDialog.newInstance(listOf(selectedFeed))
|
||||||
.show(fragment.childFragmentManager, TagSettingsDialog.TAG)
|
// .show(fragment.childFragmentManager, TagSettingsDialog.TAG)
|
||||||
R.id.rename_item -> CustomFeedNameDialog(fragment.activity as Activity, selectedFeed).show()
|
// R.id.rename_item -> CustomFeedNameDialog(fragment.activity as Activity, selectedFeed).show()
|
||||||
R.id.remove_feed -> RemoveFeedDialog.show(context, selectedFeed, null)
|
// R.id.remove_feed -> RemoveFeedDialog.show(context, selectedFeed, null)
|
||||||
else -> return false
|
// else -> return false
|
||||||
}
|
// }
|
||||||
return true
|
// return true
|
||||||
}
|
// }
|
||||||
|
|
||||||
private var eventSink: Job? = null
|
private var eventSink: Job? = null
|
||||||
private var eventStickySink: Job? = null
|
private var eventStickySink: Job? = null
|
||||||
|
@ -240,14 +237,9 @@ class SearchFragment : Fragment() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@UnstableApi private fun searchWithProgressBar() {
|
|
||||||
emptyViewHandler.hide()
|
|
||||||
search()
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressLint("StringFormatMatches")
|
@SuppressLint("StringFormatMatches")
|
||||||
@UnstableApi private fun search() {
|
@UnstableApi private fun search() {
|
||||||
adapterFeeds.setEndButton(R.string.search_online) { this.searchOnline() }
|
// adapterFeeds.setEndButton(R.string.search_online) { this.searchOnline() }
|
||||||
chip.visibility = if ((requireArguments().getLong(ARG_FEED, 0) == 0L)) View.GONE else View.VISIBLE
|
chip.visibility = if ((requireArguments().getLong(ARG_FEED, 0) == 0L)) View.GONE else View.VISIBLE
|
||||||
|
|
||||||
lifecycleScope.launch {
|
lifecycleScope.launch {
|
||||||
|
@ -262,15 +254,43 @@ class SearchFragment : Fragment() {
|
||||||
for (e in first_) { vms.add(EpisodeVM(e)) }
|
for (e in first_) { vms.add(EpisodeVM(e)) }
|
||||||
}
|
}
|
||||||
if (requireArguments().getLong(ARG_FEED, 0) == 0L) {
|
if (requireArguments().getLong(ARG_FEED, 0) == 0L) {
|
||||||
if (results_.second != null) adapterFeeds.updateData(results_.second!!)
|
if (results_.second != null) {
|
||||||
} else adapterFeeds.updateData(emptyList())
|
resultFeeds.clear()
|
||||||
if (searchView.query.toString().isEmpty()) emptyViewHandler.setMessage(R.string.type_to_search)
|
resultFeeds.addAll(results_.second!!)
|
||||||
else emptyViewHandler.setMessage(getString(R.string.no_results_for_query, searchView.query))
|
}
|
||||||
|
} else resultFeeds.clear()
|
||||||
}
|
}
|
||||||
} catch (e: Throwable) { Log.e(TAG, Log.getStackTraceString(e)) }
|
} catch (e: Throwable) { Log.e(TAG, Log.getStackTraceString(e)) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum class SearchBy(val nameRes: Int, var selected: Boolean = true) {
|
||||||
|
TITLE(R.string.title),
|
||||||
|
AUTHOR(R.string.author),
|
||||||
|
DESCRIPTION(R.string.description_label),
|
||||||
|
COMMENT(R.string.my_opinion_label),
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun CriteriaList() {
|
||||||
|
val textColor = MaterialTheme.colorScheme.onSurface
|
||||||
|
NonlazyGrid(columns = 2, itemCount = SearchBy.entries.size) { index ->
|
||||||
|
val c = SearchBy.entries[index]
|
||||||
|
Row(verticalAlignment = Alignment.CenterVertically, modifier = Modifier.padding(start = 10.dp, end = 10.dp)) {
|
||||||
|
var isChecked by remember { mutableStateOf(true) }
|
||||||
|
Checkbox(
|
||||||
|
checked = isChecked,
|
||||||
|
onCheckedChange = { newValue ->
|
||||||
|
c.selected = newValue
|
||||||
|
isChecked = newValue
|
||||||
|
}
|
||||||
|
)
|
||||||
|
Spacer(modifier = Modifier.width(2.dp))
|
||||||
|
Text(stringResource(c.nameRes), color = textColor)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@UnstableApi private fun performSearch(): Pair<List<Episode>, List<Feed>> {
|
@UnstableApi private fun performSearch(): Pair<List<Episode>, List<Feed>> {
|
||||||
val query = searchView.query.toString()
|
val query = searchView.query.toString()
|
||||||
if (query.isEmpty()) return Pair<List<Episode>, List<Feed>>(emptyList(), emptyList())
|
if (query.isEmpty()) return Pair<List<Episode>, List<Feed>>(emptyList(), emptyList())
|
||||||
|
@ -282,45 +302,41 @@ class SearchFragment : Fragment() {
|
||||||
return Pair<List<Episode>, List<Feed>>(items, feeds)
|
return Pair<List<Episode>, List<Feed>>(items, feeds)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun prepareFeedQueryString(query: String): String {
|
private fun searchFeeds(query: String): List<Feed> {
|
||||||
|
Logd(TAG, "searchFeeds called ${SearchBy.AUTHOR.selected}")
|
||||||
val queryWords = query.split("\\s+".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()
|
val queryWords = query.split("\\s+".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()
|
||||||
val sb = StringBuilder()
|
val sb = StringBuilder()
|
||||||
for (i in queryWords.indices) {
|
for (i in queryWords.indices) {
|
||||||
sb.append("(")
|
sb.append("(")
|
||||||
.append("eigenTitle TEXT '${queryWords[i]}'")
|
var isStart = true
|
||||||
.append(" OR ")
|
if (SearchBy.TITLE.selected) {
|
||||||
.append("customTitle TEXT '${queryWords[i]}'")
|
sb.append("eigenTitle TEXT '${queryWords[i]}'")
|
||||||
.append(" OR ")
|
sb.append(" OR ")
|
||||||
.append("author TEXT '${queryWords[i]}'")
|
sb.append("customTitle TEXT '${queryWords[i]}'")
|
||||||
.append(" OR ")
|
isStart = false
|
||||||
.append("description TEXT '${queryWords[i]}'")
|
}
|
||||||
.append(") ")
|
if (SearchBy.AUTHOR.selected) {
|
||||||
|
if (!isStart) sb.append(" OR ")
|
||||||
|
sb.append("author TEXT '${queryWords[i]}'")
|
||||||
|
isStart = false
|
||||||
|
}
|
||||||
|
if (SearchBy.DESCRIPTION.selected) {
|
||||||
|
if (!isStart) sb.append(" OR ")
|
||||||
|
sb.append("description TEXT '${queryWords[i]}'")
|
||||||
|
isStart = false
|
||||||
|
}
|
||||||
|
if (SearchBy.COMMENT.selected) {
|
||||||
|
if (!isStart) sb.append(" OR ")
|
||||||
|
sb.append("comment TEXT '${queryWords[i]}'")
|
||||||
|
}
|
||||||
|
sb.append(") ")
|
||||||
if (i != queryWords.size - 1) sb.append("AND ")
|
if (i != queryWords.size - 1) sb.append("AND ")
|
||||||
}
|
}
|
||||||
return sb.toString()
|
val queryString = sb.toString()
|
||||||
}
|
|
||||||
|
|
||||||
private fun searchFeeds(query: String): List<Feed> {
|
|
||||||
Logd(TAG, "searchFeeds called")
|
|
||||||
val queryString = prepareFeedQueryString(query)
|
|
||||||
Logd(TAG, "searchFeeds queryString: $queryString")
|
Logd(TAG, "searchFeeds queryString: $queryString")
|
||||||
return realm.query(Feed::class).query(queryString).find()
|
return realm.query(Feed::class).query(queryString).find()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun prepareEpisodeQueryString(query: String): String {
|
|
||||||
val queryWords = query.split("\\s+".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()
|
|
||||||
val sb = StringBuilder()
|
|
||||||
for (i in queryWords.indices) {
|
|
||||||
sb.append("(")
|
|
||||||
.append("description TEXT '${queryWords[i]}'")
|
|
||||||
.append(" OR ")
|
|
||||||
.append("title TEXT '${queryWords[i]}'" )
|
|
||||||
.append(") ")
|
|
||||||
if (i != queryWords.size - 1) sb.append("AND ")
|
|
||||||
}
|
|
||||||
return sb.toString()
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Searches the FeedItems of a specific Feed for a given string.
|
* Searches the FeedItems of a specific Feed for a given string.
|
||||||
* @param feedID The id of the feed whose episodes should be searched.
|
* @param feedID The id of the feed whose episodes should be searched.
|
||||||
|
@ -330,7 +346,30 @@ class SearchFragment : Fragment() {
|
||||||
*/
|
*/
|
||||||
private fun searchEpisodes(feedID: Long, query: String): List<Episode> {
|
private fun searchEpisodes(feedID: Long, query: String): List<Episode> {
|
||||||
Logd(TAG, "searchEpisodes called")
|
Logd(TAG, "searchEpisodes called")
|
||||||
var queryString = prepareEpisodeQueryString(query)
|
val queryWords = query.split("\\s+".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()
|
||||||
|
val sb = StringBuilder()
|
||||||
|
for (i in queryWords.indices) {
|
||||||
|
sb.append("(")
|
||||||
|
var isStart = true
|
||||||
|
if (SearchBy.TITLE.selected) {
|
||||||
|
sb.append("title TEXT '${queryWords[i]}'" )
|
||||||
|
isStart = false
|
||||||
|
}
|
||||||
|
if (SearchBy.DESCRIPTION.selected) {
|
||||||
|
if (!isStart) sb.append(" OR ")
|
||||||
|
sb.append("description TEXT '${queryWords[i]}'")
|
||||||
|
sb.append(" OR ")
|
||||||
|
sb.append("transcript TEXT '${queryWords[i]}'")
|
||||||
|
isStart = false
|
||||||
|
}
|
||||||
|
if (SearchBy.COMMENT.selected) {
|
||||||
|
if (!isStart) sb.append(" OR ")
|
||||||
|
sb.append("comment TEXT '${queryWords[i]}'")
|
||||||
|
}
|
||||||
|
sb.append(") ")
|
||||||
|
if (i != queryWords.size - 1) sb.append("AND ")
|
||||||
|
}
|
||||||
|
var queryString = sb.toString()
|
||||||
if (feedID != 0L) queryString = "(feedId == $feedID) AND $queryString"
|
if (feedID != 0L) queryString = "(feedId == $feedID) AND $queryString"
|
||||||
Logd(TAG, "searchEpisodes queryString: $queryString")
|
Logd(TAG, "searchEpisodes queryString: $queryString")
|
||||||
return realm.query(Episode::class).query(queryString).find()
|
return realm.query(Episode::class).query(queryString).find()
|
||||||
|
@ -354,89 +393,49 @@ class SearchFragment : Fragment() {
|
||||||
(activity as MainActivity).loadChildFragment(SearchResultsFragment.newInstance(CombinedSearcher::class.java, query))
|
(activity as MainActivity).loadChildFragment(SearchResultsFragment.newInstance(CombinedSearcher::class.java, query))
|
||||||
}
|
}
|
||||||
|
|
||||||
open class HorizontalFeedListAdapter(mainActivity: MainActivity)
|
@OptIn(ExperimentalFoundationApi::class)
|
||||||
: RecyclerView.Adapter<HorizontalFeedListAdapter.Holder>(), View.OnCreateContextMenuListener {
|
@Composable
|
||||||
|
fun FeedsRow() {
|
||||||
private val mainActivityRef: WeakReference<MainActivity> = WeakReference<MainActivity>(mainActivity)
|
val context = LocalContext.current
|
||||||
private val data: MutableList<Feed> = ArrayList()
|
val lazyGridState = rememberLazyListState()
|
||||||
private var dummyViews = 0
|
LazyRow (state = lazyGridState, horizontalArrangement = Arrangement.spacedBy(16.dp),
|
||||||
var longPressedItem: Feed? = null
|
contentPadding = PaddingValues(start = 12.dp, top = 16.dp, end = 12.dp, bottom = 16.dp)
|
||||||
@StringRes
|
) {
|
||||||
private var endButtonText = 0
|
items(resultFeeds.size, key = {index -> resultFeeds[index].id}) { index ->
|
||||||
private var endButtonAction: Runnable? = null
|
val feed by remember { mutableStateOf(resultFeeds[index]) }
|
||||||
|
ConstraintLayout {
|
||||||
fun updateData(newData: List<Feed>?) {
|
val (coverImage, episodeCount, rating, error) = createRefs()
|
||||||
data.clear()
|
val imgLoc = remember(feed) { feed.imageUrl }
|
||||||
data.addAll(newData!!)
|
AsyncImage(model = ImageRequest.Builder(context).data(imgLoc)
|
||||||
notifyDataSetChanged()
|
.memoryCachePolicy(CachePolicy.ENABLED).placeholder(R.mipmap.ic_launcher).error(R.mipmap.ic_launcher).build(),
|
||||||
}
|
contentDescription = "coverImage",
|
||||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): Holder {
|
modifier = Modifier.height(100.dp).aspectRatio(1f)
|
||||||
val convertView = View.inflate(mainActivityRef.get(), R.layout.horizontal_feed_item, null)
|
.constrainAs(coverImage) {
|
||||||
return Holder(convertView)
|
top.linkTo(parent.top)
|
||||||
}
|
bottom.linkTo(parent.bottom)
|
||||||
@UnstableApi override fun onBindViewHolder(holder: Holder, position: Int) {
|
start.linkTo(parent.start)
|
||||||
if (position == itemCount - 1 && endButtonAction != null) {
|
}.combinedClickable(onClick = {
|
||||||
holder.cardView.visibility = View.GONE
|
Logd(SubscriptionsFragment.TAG, "clicked: ${feed.title}")
|
||||||
holder.actionButton.visibility = View.VISIBLE
|
(activity as MainActivity).loadChildFragment(FeedEpisodesFragment.newInstance(feed.id))
|
||||||
holder.actionButton.setText(endButtonText)
|
}, onLongClick = {
|
||||||
holder.actionButton.setOnClickListener { endButtonAction!!.run() }
|
Logd(SubscriptionsFragment.TAG, "long clicked: ${feed.title}")
|
||||||
return
|
// val inflater: MenuInflater = (activity as MainActivity).menuInflater
|
||||||
}
|
// inflater.inflate(R.menu.feed_context, contextMenu)
|
||||||
holder.cardView.visibility = View.VISIBLE
|
// contextMenu.setHeaderTitle(feed.title)
|
||||||
holder.actionButton.visibility = View.GONE
|
})
|
||||||
if (position >= data.size) {
|
)
|
||||||
holder.itemView.alpha = 0.1f
|
Text(NumberFormat.getInstance().format(feed.episodes.size.toLong()), color = Color.Green,
|
||||||
// Glide.with(mainActivityRef.get()!!).clear(holder.imageView)
|
modifier = Modifier.background(Color.Gray).constrainAs(episodeCount) {
|
||||||
val imageLoader = ImageLoader.Builder(mainActivityRef.get()!!).build()
|
end.linkTo(parent.end)
|
||||||
imageLoader.enqueue(ImageRequest.Builder(mainActivityRef.get()!!).data(null).target(holder.imageView).build())
|
top.linkTo(coverImage.top)
|
||||||
holder.imageView.setImageResource(R.color.medium_gray)
|
})
|
||||||
return
|
if (feed.rating != Rating.UNRATED.code)
|
||||||
}
|
Icon(imageVector = ImageVector.vectorResource(Rating.fromCode(feed.rating).res), tint = MaterialTheme.colorScheme.tertiary, contentDescription = "rating",
|
||||||
holder.itemView.alpha = 1.0f
|
modifier = Modifier.background(MaterialTheme.colorScheme.tertiaryContainer).constrainAs(rating) {
|
||||||
val podcast: Feed = data[position]
|
start.linkTo(parent.start)
|
||||||
holder.imageView.setContentDescription(podcast.title)
|
centerVerticallyTo(coverImage)
|
||||||
holder.imageView.setOnClickListener {
|
})
|
||||||
mainActivityRef.get()?.loadChildFragment(FeedEpisodesFragment.newInstance(podcast.id))
|
}
|
||||||
}
|
|
||||||
holder.imageView.setOnCreateContextMenuListener(this)
|
|
||||||
holder.imageView.setOnLongClickListener {
|
|
||||||
val currentItemPosition = holder.bindingAdapterPosition
|
|
||||||
longPressedItem = data[currentItemPosition]
|
|
||||||
false
|
|
||||||
}
|
|
||||||
holder.imageView.load(podcast.imageUrl) {
|
|
||||||
placeholder(R.color.light_gray)
|
|
||||||
error(R.mipmap.ic_launcher)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
override fun getItemId(position: Int): Long {
|
|
||||||
if (position >= data.size) return RecyclerView.NO_ID // Dummy views
|
|
||||||
return data[position].id
|
|
||||||
}
|
|
||||||
override fun getItemCount(): Int {
|
|
||||||
return dummyViews + data.size + (if ((endButtonAction == null)) 0 else 1)
|
|
||||||
}
|
|
||||||
override fun onCreateContextMenu(contextMenu: ContextMenu, view: View, contextMenuInfo: ContextMenu.ContextMenuInfo?) {
|
|
||||||
val inflater: MenuInflater = mainActivityRef.get()!!.menuInflater
|
|
||||||
if (longPressedItem == null) return
|
|
||||||
inflater.inflate(R.menu.feed_context, contextMenu)
|
|
||||||
contextMenu.setHeaderTitle(longPressedItem!!.title)
|
|
||||||
}
|
|
||||||
fun setEndButton(@StringRes text: Int, action: Runnable?) {
|
|
||||||
endButtonAction = action
|
|
||||||
endButtonText = text
|
|
||||||
notifyDataSetChanged()
|
|
||||||
}
|
|
||||||
class Holder(itemView: View) : RecyclerView.ViewHolder(itemView) {
|
|
||||||
val binding = HorizontalFeedItemBinding.bind(itemView)
|
|
||||||
var imageView: SquareImageView = binding.discoveryCover
|
|
||||||
var cardView: CardView
|
|
||||||
var actionButton: Button
|
|
||||||
|
|
||||||
init {
|
|
||||||
imageView.setDirection(SquareImageView.DIRECTION_HEIGHT)
|
|
||||||
actionButton = binding.actionButton
|
|
||||||
cardView = binding.cardView
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -285,7 +285,7 @@ class SubscriptionsFragment : Fragment(), Toolbar.OnMenuItemClickListener {
|
||||||
Logd(TAG, "Received event: ${event.TAG}")
|
Logd(TAG, "Received event: ${event.TAG}")
|
||||||
when (event) {
|
when (event) {
|
||||||
is FlowEvent.FeedListEvent, is FlowEvent.FeedsSortedEvent -> loadSubscriptions()
|
is FlowEvent.FeedListEvent, is FlowEvent.FeedsSortedEvent -> loadSubscriptions()
|
||||||
is FlowEvent.FeedsFilterEvent -> loadSubscriptions()
|
// is FlowEvent.FeedsFilterEvent -> loadSubscriptions()
|
||||||
is FlowEvent.EpisodePlayedEvent -> loadSubscriptions()
|
is FlowEvent.EpisodePlayedEvent -> loadSubscriptions()
|
||||||
is FlowEvent.FeedTagsChangedEvent -> loadSubscriptions()
|
is FlowEvent.FeedTagsChangedEvent -> loadSubscriptions()
|
||||||
is FlowEvent.FeedPrefsChangeEvent -> loadSubscriptions()
|
is FlowEvent.FeedPrefsChangeEvent -> loadSubscriptions()
|
||||||
|
@ -1089,7 +1089,8 @@ class SubscriptionsFragment : Fragment(), Toolbar.OnMenuItemClickListener {
|
||||||
fun onFilterChanged(newFilterValues: Set<String>) {
|
fun onFilterChanged(newFilterValues: Set<String>) {
|
||||||
feedsFilter = StringUtils.join(newFilterValues, ",")
|
feedsFilter = StringUtils.join(newFilterValues, ",")
|
||||||
Logd(TAG, "onFilterChanged: $feedsFilter")
|
Logd(TAG, "onFilterChanged: $feedsFilter")
|
||||||
EventFlow.postEvent(FlowEvent.FeedsFilterEvent(newFilterValues))
|
loadSubscriptions()
|
||||||
|
// EventFlow.postEvent(FlowEvent.FeedsFilterEvent(newFilterValues))
|
||||||
}
|
}
|
||||||
Dialog(properties = DialogProperties(usePlatformDefaultWidth = false), onDismissRequest = { onDismissRequest() }) {
|
Dialog(properties = DialogProperties(usePlatformDefaultWidth = false), onDismissRequest = { onDismissRequest() }) {
|
||||||
val dialogWindowProvider = LocalView.current.parent as? DialogWindowProvider
|
val dialogWindowProvider = LocalView.current.parent as? DialogWindowProvider
|
||||||
|
@ -1097,7 +1098,7 @@ class SubscriptionsFragment : Fragment(), Toolbar.OnMenuItemClickListener {
|
||||||
window.setGravity(Gravity.BOTTOM)
|
window.setGravity(Gravity.BOTTOM)
|
||||||
window.setDimAmount(0f)
|
window.setDimAmount(0f)
|
||||||
}
|
}
|
||||||
Surface(modifier = Modifier.fillMaxWidth().height(500.dp), shape = RoundedCornerShape(16.dp)) {
|
Surface(modifier = Modifier.fillMaxWidth().height(350.dp), color = MaterialTheme.colorScheme.surface.copy(alpha = 0.8f), shape = RoundedCornerShape(16.dp)) {
|
||||||
val textColor = MaterialTheme.colorScheme.onSurface
|
val textColor = MaterialTheme.colorScheme.onSurface
|
||||||
val scrollState = rememberScrollState()
|
val scrollState = rememberScrollState()
|
||||||
Column(Modifier.fillMaxSize().verticalScroll(scrollState)) {
|
Column(Modifier.fillMaxSize().verticalScroll(scrollState)) {
|
||||||
|
@ -1109,8 +1110,8 @@ class SubscriptionsFragment : Fragment(), Toolbar.OnMenuItemClickListener {
|
||||||
if (selectNone) selectedIndex = -1
|
if (selectNone) selectedIndex = -1
|
||||||
LaunchedEffect(Unit) {
|
LaunchedEffect(Unit) {
|
||||||
if (filter != null) {
|
if (filter != null) {
|
||||||
if (item.values[0].filterId in filter!!.properties) selectedIndex = 0
|
if (item.values[0].filterId in filter.properties) selectedIndex = 0
|
||||||
else if (item.values[1].filterId in filter!!.properties) selectedIndex = 1
|
else if (item.values[1].filterId in filter.properties) selectedIndex = 1
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Text(stringResource(item.nameRes) + " :", fontWeight = FontWeight.Bold, style = MaterialTheme.typography.headlineSmall, color = textColor, modifier = Modifier.padding(end = 10.dp))
|
Text(stringResource(item.nameRes) + " :", fontWeight = FontWeight.Bold, style = MaterialTheme.typography.headlineSmall, color = textColor, modifier = Modifier.padding(end = 10.dp))
|
||||||
|
@ -1231,137 +1232,6 @@ class SubscriptionsFragment : Fragment(), Toolbar.OnMenuItemClickListener {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// class FeedFilterDialog : BottomSheetDialogFragment() {
|
|
||||||
// var filter: FeedFilter? = null
|
|
||||||
// private val filterValues: MutableSet<String> = mutableSetOf()
|
|
||||||
//
|
|
||||||
// override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
|
|
||||||
// val composeView = ComposeView(requireContext()).apply {
|
|
||||||
// setContent {
|
|
||||||
// CustomTheme(requireContext()) {
|
|
||||||
// MainView()
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// return composeView
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// @Composable
|
|
||||||
// fun MainView() {
|
|
||||||
// val textColor = MaterialTheme.colorScheme.onSurface
|
|
||||||
// val scrollState = rememberScrollState()
|
|
||||||
// Column(Modifier.fillMaxSize().verticalScroll(scrollState)) {
|
|
||||||
// var selectNone by remember { mutableStateOf(false) }
|
|
||||||
// for (item in FeedFilter.FeedFilterGroup.entries) {
|
|
||||||
// if (item.values.size == 2) {
|
|
||||||
// Row(modifier = Modifier.padding(start = 5.dp).fillMaxWidth(), horizontalArrangement = Arrangement.Absolute.Left, verticalAlignment = Alignment.CenterVertically) {
|
|
||||||
// var selectedIndex by remember { mutableStateOf(-1) }
|
|
||||||
// if (selectNone) selectedIndex = -1
|
|
||||||
// LaunchedEffect(Unit) {
|
|
||||||
// if (filter != null) {
|
|
||||||
// if (item.values[0].filterId in filter!!.properties) selectedIndex = 0
|
|
||||||
// else if (item.values[1].filterId in filter!!.properties) selectedIndex = 1
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// Text(stringResource(item.nameRes) + " :", fontWeight = FontWeight.Bold, style = MaterialTheme.typography.headlineSmall, color = textColor, modifier = Modifier.padding(end = 10.dp))
|
|
||||||
// Spacer(Modifier.weight(0.3f))
|
|
||||||
// OutlinedButton(
|
|
||||||
// modifier = Modifier.padding(2.dp).heightIn(min = 20.dp).widthIn(min = 20.dp), border = BorderStroke(2.dp, if (selectedIndex != 0) textColor else Color.Green),
|
|
||||||
// onClick = {
|
|
||||||
// if (selectedIndex != 0) {
|
|
||||||
// selectNone = false
|
|
||||||
// selectedIndex = 0
|
|
||||||
// filterValues.add(item.values[0].filterId)
|
|
||||||
// filterValues.remove(item.values[1].filterId)
|
|
||||||
// } else {
|
|
||||||
// selectedIndex = -1
|
|
||||||
// filterValues.remove(item.values[0].filterId)
|
|
||||||
// }
|
|
||||||
// onFilterChanged(filterValues)
|
|
||||||
// },
|
|
||||||
// ) {
|
|
||||||
// Text(text = stringResource(item.values[0].displayName), color = textColor)
|
|
||||||
// }
|
|
||||||
// Spacer(Modifier.weight(0.1f))
|
|
||||||
// OutlinedButton(
|
|
||||||
// modifier = Modifier.padding(2.dp).heightIn(min = 20.dp).widthIn(min = 20.dp), border = BorderStroke(2.dp, if (selectedIndex != 1) textColor else Color.Green),
|
|
||||||
// onClick = {
|
|
||||||
// if (selectedIndex != 1) {
|
|
||||||
// selectNone = false
|
|
||||||
// selectedIndex = 1
|
|
||||||
// filterValues.add(item.values[1].filterId)
|
|
||||||
// filterValues.remove(item.values[0].filterId)
|
|
||||||
// } else {
|
|
||||||
// selectedIndex = -1
|
|
||||||
// filterValues.remove(item.values[1].filterId)
|
|
||||||
// }
|
|
||||||
// onFilterChanged(filterValues)
|
|
||||||
// },
|
|
||||||
// ) {
|
|
||||||
// Text(text = stringResource(item.values[1].displayName), color = textColor)
|
|
||||||
// }
|
|
||||||
// Spacer(Modifier.weight(0.5f))
|
|
||||||
// }
|
|
||||||
// } else {
|
|
||||||
// Column(modifier = Modifier.padding(start = 5.dp, bottom = 2.dp).fillMaxWidth()) {
|
|
||||||
// Text(stringResource(item.nameRes) + " :", fontWeight = FontWeight.Bold, style = MaterialTheme.typography.headlineSmall, color = textColor)
|
|
||||||
// val lazyGridState = rememberLazyGridState()
|
|
||||||
// LazyVerticalGrid(state = lazyGridState, columns = GridCells.Adaptive(100.dp),
|
|
||||||
// verticalArrangement = Arrangement.spacedBy(2.dp), horizontalArrangement = Arrangement.spacedBy(2.dp)) {
|
|
||||||
// items(item.values.size) { index ->
|
|
||||||
// var selected by remember { mutableStateOf(false) }
|
|
||||||
// if (selectNone) selected = false
|
|
||||||
// OutlinedButton(
|
|
||||||
// modifier = Modifier.padding(2.dp).heightIn(min = 20.dp).widthIn(min = 20.dp).wrapContentWidth(),
|
|
||||||
// border = BorderStroke(2.dp, if (selected) Color.Green else textColor),
|
|
||||||
// onClick = {
|
|
||||||
// selectNone = false
|
|
||||||
// selected = !selected
|
|
||||||
// if (selected) filterValues.add(item.values[index].filterId)
|
|
||||||
// else filterValues.remove(item.values[index].filterId)
|
|
||||||
// onFilterChanged(filterValues)
|
|
||||||
// },
|
|
||||||
// ) {
|
|
||||||
// Text(text = stringResource(item.values[index].displayName), maxLines = 1, color = textColor)
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// Row {
|
|
||||||
// Spacer(Modifier.weight(0.3f))
|
|
||||||
// Button(onClick = {
|
|
||||||
// selectNone = true
|
|
||||||
// onFilterChanged(setOf(""))
|
|
||||||
// }) {
|
|
||||||
// Text(stringResource(R.string.reset))
|
|
||||||
// }
|
|
||||||
// Spacer(Modifier.weight(0.4f))
|
|
||||||
// Button(onClick = {
|
|
||||||
// dismiss()
|
|
||||||
// }) {
|
|
||||||
// Text(stringResource(R.string.close_label))
|
|
||||||
// }
|
|
||||||
// Spacer(Modifier.weight(0.3f))
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// private fun onFilterChanged(newFilterValues: Set<String>) {
|
|
||||||
// feedsFilter = StringUtils.join(newFilterValues, ",")
|
|
||||||
// Logd(TAG, "onFilterChanged: $feedsFilter")
|
|
||||||
// EventFlow.postEvent(FlowEvent.FeedsFilterEvent(newFilterValues))
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// companion object {
|
|
||||||
// fun newInstance(filter: FeedFilter?): FeedFilterDialog {
|
|
||||||
// val dialog = FeedFilterDialog()
|
|
||||||
// dialog.filter = filter
|
|
||||||
// return dialog
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
val TAG = SubscriptionsFragment::class.simpleName ?: "Anonymous"
|
val TAG = SubscriptionsFragment::class.simpleName ?: "Anonymous"
|
||||||
|
|
||||||
|
|
|
@ -141,7 +141,7 @@ sealed class FlowEvent {
|
||||||
|
|
||||||
data class FeedsSortedEvent(val dummy: Unit = Unit) : FlowEvent()
|
data class FeedsSortedEvent(val dummy: Unit = Unit) : FlowEvent()
|
||||||
|
|
||||||
data class FeedsFilterEvent(val filterValues: Set<String?>?) : FlowEvent()
|
// data class FeedsFilterEvent(val filterValues: Set<String?>?) : FlowEvent()
|
||||||
|
|
||||||
// data class SkipIntroEndingChangedEvent(val skipIntro: Int, val skipEnding: Int, val feedId: Long) : FlowEvent()
|
// data class SkipIntroEndingChangedEvent(val skipIntro: Int, val skipEnding: Int, val feedId: Long) : FlowEvent()
|
||||||
|
|
||||||
|
@ -167,11 +167,11 @@ sealed class FlowEvent {
|
||||||
|
|
||||||
data class RatingEvent(val episode: Episode, val rating: Int = Rating.FAVORITE.code) : FlowEvent()
|
data class RatingEvent(val episode: Episode, val rating: Int = Rating.FAVORITE.code) : FlowEvent()
|
||||||
|
|
||||||
data class AllEpisodesFilterEvent(val filterValues: Set<String?>?) : FlowEvent()
|
// data class AllEpisodesFilterEvent(val filterValues: Set<String?>?) : FlowEvent()
|
||||||
|
|
||||||
data class AllEpisodesSortEvent(val dummy: Unit = Unit) : FlowEvent()
|
data class AllEpisodesSortEvent(val dummy: Unit = Unit) : FlowEvent()
|
||||||
|
|
||||||
data class DownloadsFilterEvent(val filterValues: Set<String?>?) : FlowEvent()
|
// data class DownloadsFilterEvent(val filterValues: Set<String?>?) : FlowEvent()
|
||||||
|
|
||||||
data class EpisodeEvent(val episodes: List<Episode>) : FlowEvent() {
|
data class EpisodeEvent(val episodes: List<Episode>) : FlowEvent() {
|
||||||
companion object {
|
companion object {
|
||||||
|
|
|
@ -1,43 +0,0 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<LinearLayout
|
|
||||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
xmlns:squareImageView="http://schemas.android.com/apk/ac.mdiq.podcini"
|
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="96dp"
|
|
||||||
android:orientation="vertical"
|
|
||||||
android:id="@+id/horizontal_feed_item"
|
|
||||||
android:padding="4dp"
|
|
||||||
android:clipToPadding="false"
|
|
||||||
android:clipToOutline="false"
|
|
||||||
android:clipChildren="false">
|
|
||||||
|
|
||||||
<androidx.cardview.widget.CardView
|
|
||||||
android:id="@+id/cardView"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
app:cardBackgroundColor="@color/non_square_icon_background"
|
|
||||||
app:cardCornerRadius="16dp"
|
|
||||||
app:cardPreventCornerOverlap="false"
|
|
||||||
app:cardElevation="2dp">
|
|
||||||
|
|
||||||
<ac.mdiq.podcini.ui.view.SquareImageView
|
|
||||||
android:id="@+id/discovery_cover"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="96dp"
|
|
||||||
android:elevation="4dp"
|
|
||||||
android:outlineProvider="bounds"
|
|
||||||
android:foreground="?android:attr/selectableItemBackground"
|
|
||||||
android:background="?android:attr/colorBackground"
|
|
||||||
squareImageView:direction="height" />
|
|
||||||
|
|
||||||
</androidx.cardview.widget.CardView>
|
|
||||||
|
|
||||||
<Button
|
|
||||||
android:id="@+id/actionButton"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:visibility="gone"
|
|
||||||
style="@style/Widget.Material3.Button.OutlinedButton" />
|
|
||||||
|
|
||||||
</LinearLayout>
|
|
|
@ -27,16 +27,8 @@
|
||||||
android:visibility="gone"
|
android:visibility="gone"
|
||||||
app:closeIconVisible="true" />
|
app:closeIconVisible="true" />
|
||||||
|
|
||||||
<androidx.recyclerview.widget.RecyclerView
|
|
||||||
android:id="@+id/recyclerViewFeeds"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:paddingLeft="12dp"
|
|
||||||
android:paddingRight="12dp"
|
|
||||||
android:clipToPadding="false" />
|
|
||||||
|
|
||||||
<androidx.compose.ui.platform.ComposeView
|
<androidx.compose.ui.platform.ComposeView
|
||||||
android:id="@+id/lazyColumn"
|
android:id="@+id/resultsListView"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"/>
|
android:layout_height="wrap_content"/>
|
||||||
|
|
||||||
|
|
|
@ -9,12 +9,12 @@
|
||||||
custom:showAsAction="always"
|
custom:showAsAction="always"
|
||||||
android:title="@string/search_label"/>
|
android:title="@string/search_label"/>
|
||||||
|
|
||||||
<item
|
<!-- <item-->
|
||||||
android:id="@+id/action_favorites"
|
<!-- android:id="@+id/action_favorites"-->
|
||||||
android:icon="@drawable/ic_star_border"
|
<!-- android:icon="@drawable/ic_star_border"-->
|
||||||
android:menuCategory="container"
|
<!-- android:menuCategory="container"-->
|
||||||
android:title="@string/favorite_episodes_label"
|
<!-- android:title="@string/favorite_episodes_label"-->
|
||||||
custom:showAsAction="always"/>
|
<!-- custom:showAsAction="always"/>-->
|
||||||
|
|
||||||
<item
|
<item
|
||||||
android:id="@+id/filter_items"
|
android:id="@+id/filter_items"
|
||||||
|
|
|
@ -419,6 +419,8 @@
|
||||||
<string name="duration">Duration</string>
|
<string name="duration">Duration</string>
|
||||||
<string name="episode_title">Episode title</string>
|
<string name="episode_title">Episode title</string>
|
||||||
<string name="feed_title">Podcast title</string>
|
<string name="feed_title">Podcast title</string>
|
||||||
|
<string name="title">Title</string>
|
||||||
|
<string name="author">Author</string>
|
||||||
<string name="random">Random</string>
|
<string name="random">Random</string>
|
||||||
<string name="smart_shuffle">Smart shuffle</string>
|
<string name="smart_shuffle">Smart shuffle</string>
|
||||||
<string name="size">Size</string>
|
<string name="size">Size</string>
|
||||||
|
|
|
@ -1,3 +1,9 @@
|
||||||
|
# 6.12.4
|
||||||
|
|
||||||
|
* bug fixes and enhancements in filters routines
|
||||||
|
* in SearchFragment, added search criteria options: title, author(feed only), description(including transcript in episodes), and comment (My opinion)
|
||||||
|
* feed list in SearchFragment is in Compose
|
||||||
|
|
||||||
# 6.12.3
|
# 6.12.3
|
||||||
|
|
||||||
* reworked and expanded the filters routines for episodes and feeds
|
* reworked and expanded the filters routines for episodes and feeds
|
||||||
|
|
|
@ -0,0 +1,5 @@
|
||||||
|
Version 6.12.4
|
||||||
|
|
||||||
|
* bug fixes and enhancements in filters routines
|
||||||
|
* in SearchFragment, added search criteria options: title, author(feed only), description(including transcript in episodes), and comment (My opinion)
|
||||||
|
* feed list in SearchFragment is in Compose
|
Loading…
Reference in New Issue