From 884abb5eec741303d89201c240551402747f2a95 Mon Sep 17 00:00:00 2001
From: Xilin Jia <6257601+XilinJia@users.noreply.github.com>
Date: Fri, 29 Nov 2024 13:28:02 +0100
Subject: [PATCH] 6.15.0 commit

---
 app/build.gradle                              |   4 +-
 app/src/main/AndroidManifest.xml              |  27 +-
 .../service/DownloadServiceInterfaceImpl.kt   |  24 +-
 .../podcini/net/sync/wifi/WifiSyncService.kt  |   2 +-
 .../podcini/preferences/OpmlTransporter.kt    |  79 +--
 .../podcini/preferences/UserPreferences.kt    |   6 +-
 .../ac/mdiq/podcini/storage/database/Feeds.kt |  31 +-
 .../mdiq/podcini/storage/database/Queues.kt   |   6 +-
 .../mdiq/podcini/storage/database/RealmDB.kt  |   2 +-
 .../ac/mdiq/podcini/storage/model/Episode.kt  |   5 +
 .../podcini/storage/model/EpisodeFilter.kt    |  18 +-
 .../podcini/storage/model/EpisodeSortOrder.kt |  19 +-
 .../mdiq/podcini/ui/actions/SwipeActions.kt   | 139 ++---
 .../mdiq/podcini/ui/activity/MainActivity.kt  |  14 +-
 .../podcini/ui/activity/OpmlImportActivity.kt | 248 --------
 .../podcini/ui/activity/PreferenceActivity.kt | 530 ++++++++----------
 .../ui/activity/ShareReceiverActivity.kt      |   4 +-
 .../ac/mdiq/podcini/ui/compose/Composables.kt |  52 ++
 .../ac/mdiq/podcini/ui/compose/EpisodesVM.kt  |  65 ++-
 .../ac/mdiq/podcini/ui/compose/Feeds.kt       |  65 ++-
 .../ui/fragment/AllEpisodesFragment.kt        | 112 ----
 .../ui/fragment/BaseEpisodesFragment.kt       |  69 ++-
 .../podcini/ui/fragment/DownloadsFragment.kt  | 426 --------------
 .../podcini/ui/fragment/EpisodesFragment.kt   | 354 ++++++++++++
 .../ui/fragment/FeedEpisodesFragment.kt       |   6 +-
 .../podcini/ui/fragment/HistoryFragment.kt    | 199 -------
 .../mdiq/podcini/ui/fragment/LogsFragment.kt  |   4 +-
 .../podcini/ui/fragment/NavDrawerFragment.kt  |  21 +-
 .../podcini/ui/fragment/OnlineFeedFragment.kt |  23 +-
 .../ui/fragment/OnlineSearchFragment.kt       |  87 ++-
 .../podcini/ui/fragment/QueuesFragment.kt     |  12 +-
 .../podcini/ui/fragment/SearchFragment.kt     |   5 +-
 .../ui/fragment/SubscriptionsFragment.kt      |  50 +-
 .../res/layout/dialog_switch_preference.xml   |  16 -
 app/src/main/res/layout/opml_selection.xml    |  44 --
 app/src/main/res/menu/episodes.xml            |  34 +-
 app/src/main/res/values/strings.xml           |   4 +
 changelog.md                                  |  16 +
 .../android/en-US/changelogs/3020308.txt      |  15 +
 gradle/libs.versions.toml                     |   8 +-
 40 files changed, 1082 insertions(+), 1763 deletions(-)
 delete mode 100644 app/src/main/kotlin/ac/mdiq/podcini/ui/activity/OpmlImportActivity.kt
 delete mode 100644 app/src/main/kotlin/ac/mdiq/podcini/ui/fragment/AllEpisodesFragment.kt
 delete mode 100644 app/src/main/kotlin/ac/mdiq/podcini/ui/fragment/DownloadsFragment.kt
 create mode 100644 app/src/main/kotlin/ac/mdiq/podcini/ui/fragment/EpisodesFragment.kt
 delete mode 100644 app/src/main/kotlin/ac/mdiq/podcini/ui/fragment/HistoryFragment.kt
 delete mode 100644 app/src/main/res/layout/dialog_switch_preference.xml
 delete mode 100644 app/src/main/res/layout/opml_selection.xml
 create mode 100644 fastlane/metadata/android/en-US/changelogs/3020308.txt

diff --git a/app/build.gradle b/app/build.gradle
index 03dfff21..810e4156 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -26,8 +26,8 @@ android {
         vectorDrawables.useSupportLibrary false
         vectorDrawables.generatedDensities = []
 
-        versionCode 3020307
-        versionName "6.14.8"
+        versionCode 3020308
+        versionName "6.15.0"
 
         applicationId "ac.mdiq.podcini.R"
         def commit = ""
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 51a74434..b7d00313 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -190,32 +190,6 @@
                     android:resource="@xml/player_widget_info"/>
         </receiver>
 
-        <activity
-            android:name=".ui.activity.OpmlImportActivity"
-            android:configChanges="keyboardHidden|orientation|screenSize"
-            android:label="@string/opml_import_label"
-            android:exported="true">
-            <intent-filter>
-                <action android:name="android.intent.action.VIEW"/>
-                <action android:name="android.intent.action.SEND"/>
-
-                <category android:name="android.intent.category.DEFAULT"/>
-                <category android:name="android.intent.category.BROWSABLE"/>
-
-                <data android:mimeType="text/xml"/>
-                <data android:mimeType="text/x-opml"/>
-                <data android:mimeType="application/xml"/>
-
-                <data android:scheme="file"/>
-                <data android:scheme="content"/>
-                <data android:scheme="http"/>
-                <data android:scheme="https"/>
-
-<!--                <data android:host="*"/>-->
-                <data android:pathPattern="/.*\\.xml" />
-                <data android:pathPattern="/.*\\.opml" />
-            </intent-filter>
-        </activity>
         <activity
                 android:name=".ui.activity.BugReportActivity"
                 android:label="@string/bug_report_title">
@@ -257,6 +231,7 @@
                 <category android:name="android.intent.category.BROWSABLE"/>
 
                 <data android:scheme="http"/>
+                <data android:host="*"/>
                 <data android:scheme="https"/>
                 <data android:mimeType="text/xml"/>
                 <data android:mimeType="application/rss+xml"/>
diff --git a/app/src/main/kotlin/ac/mdiq/podcini/net/download/service/DownloadServiceInterfaceImpl.kt b/app/src/main/kotlin/ac/mdiq/podcini/net/download/service/DownloadServiceInterfaceImpl.kt
index 5354be0c..855342e3 100644
--- a/app/src/main/kotlin/ac/mdiq/podcini/net/download/service/DownloadServiceInterfaceImpl.kt
+++ b/app/src/main/kotlin/ac/mdiq/podcini/net/download/service/DownloadServiceInterfaceImpl.kt
@@ -233,16 +233,16 @@ class DownloadServiceInterfaceImpl : DownloadServiceInterface() {
                 { ctx: Context -> MainActivityStarter(ctx).withDownloadLogsOpen().start() },
                 applicationContext.getString(R.string.download_error_details)))
         }
-        private fun getDownloadLogsIntent(context: Context): PendingIntent {
-            val intent = MainActivityStarter(context).withDownloadLogsOpen().getIntent()
-            return PendingIntent.getActivity(context, R.id.pending_intent_download_service_report, intent,
-                PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE)
-        }
-        private fun getDownloadsIntent(context: Context): PendingIntent {
-            val intent = MainActivityStarter(context).withFragmentLoaded("DownloadsFragment").getIntent()
-            return PendingIntent.getActivity(context, R.id.pending_intent_download_service_notification, intent,
-                PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE)
-        }
+//        private fun getDownloadLogsIntent(context: Context): PendingIntent {
+//            val intent = MainActivityStarter(context).withDownloadLogsOpen().getIntent()
+//            return PendingIntent.getActivity(context, R.id.pending_intent_download_service_report, intent,
+//                PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE)
+//        }
+//        private fun getDownloadsIntent(context: Context): PendingIntent {
+//            val intent = MainActivityStarter(context).withFragmentLoaded("DownloadsFragment").getIntent()
+//            return PendingIntent.getActivity(context, R.id.pending_intent_download_service_notification, intent,
+//                PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE)
+//        }
         private fun sendErrorNotification(title: String) {
 //        TODO: need to get number of subscribers in SharedFlow
 //        if (EventBus.getDefault().hasSubscriberForEvent(FlowEvent.MessageEvent::class.java)) {
@@ -254,7 +254,7 @@ class DownloadServiceInterfaceImpl : DownloadServiceInterface() {
                 .setContentTitle(applicationContext.getString(R.string.download_report_title))
                 .setContentText(applicationContext.getString(R.string.download_error_tap_for_details))
                 .setSmallIcon(R.drawable.ic_notification_sync_error)
-                .setContentIntent(getDownloadLogsIntent(applicationContext))
+//                .setContentIntent(getDownloadLogsIntent(applicationContext))
                 .setAutoCancel(true)
             builder.setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
             val nm = applicationContext.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
@@ -277,7 +277,7 @@ class DownloadServiceInterfaceImpl : DownloadServiceInterface() {
                 .setContentTitle(applicationContext.getString(R.string.download_notification_title_episodes))
                 .setContentText(contentText)
                 .setStyle(NotificationCompat.BigTextStyle().bigText(bigText))
-                .setContentIntent(getDownloadsIntent(applicationContext))
+//                .setContentIntent(getDownloadsIntent(applicationContext))
                 .setAutoCancel(false)
                 .setOngoing(true)
                 .setWhen(0)
diff --git a/app/src/main/kotlin/ac/mdiq/podcini/net/sync/wifi/WifiSyncService.kt b/app/src/main/kotlin/ac/mdiq/podcini/net/sync/wifi/WifiSyncService.kt
index ad178d86..9ece23a0 100644
--- a/app/src/main/kotlin/ac/mdiq/podcini/net/sync/wifi/WifiSyncService.kt
+++ b/app/src/main/kotlin/ac/mdiq/podcini/net/sync/wifi/WifiSyncService.kt
@@ -245,7 +245,7 @@ class WifiSyncService(val context: Context, params: WorkerParameters)  : SyncSer
 //            only push downloaded items
             val pausedItems = getEpisodes(0, Int.MAX_VALUE, EpisodeFilter(EpisodeFilter.States.paused.name), EpisodeSortOrder.DATE_NEW_OLD)
             val readItems = getEpisodes(0, Int.MAX_VALUE, EpisodeFilter(EpisodeFilter.States.played.name), EpisodeSortOrder.DATE_NEW_OLD)
-            val favoriteItems = getEpisodes(0, Int.MAX_VALUE, EpisodeFilter(EpisodeFilter.States.favorite.name), EpisodeSortOrder.DATE_NEW_OLD)
+            val favoriteItems = getEpisodes(0, Int.MAX_VALUE, EpisodeFilter(EpisodeFilter.States.superb.name), EpisodeSortOrder.DATE_NEW_OLD)
             val comItems = mutableSetOf<Episode>()
             comItems.addAll(pausedItems)
             comItems.addAll(readItems)
diff --git a/app/src/main/kotlin/ac/mdiq/podcini/preferences/OpmlTransporter.kt b/app/src/main/kotlin/ac/mdiq/podcini/preferences/OpmlTransporter.kt
index 14eca8be..34b16aa2 100644
--- a/app/src/main/kotlin/ac/mdiq/podcini/preferences/OpmlTransporter.kt
+++ b/app/src/main/kotlin/ac/mdiq/podcini/preferences/OpmlTransporter.kt
@@ -16,6 +16,10 @@ import android.util.Log
 import android.util.Xml
 import androidx.core.app.ActivityCompat
 import com.google.android.material.dialog.MaterialAlertDialogBuilder
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.withContext
 import org.apache.commons.io.input.BOMInputStream
 import org.xmlpull.v1.XmlPullParser
 import org.xmlpull.v1.XmlPullParserException
@@ -164,54 +168,51 @@ class OpmlTransporter {
     }
 
     companion object {
-        fun startImport(context: Context, uri: Uri) {
+        fun startImport(context: Context, uri: Uri, CB: (List<OpmlElement>)->Unit) {
             val TAG = "OpmlTransporter"
-//            CoroutineScope(Dispatchers.IO).launch {
-            try {
-                val opmlFileStream = context.contentResolver.openInputStream(uri)
-                val bomInputStream = BOMInputStream(opmlFileStream)
-                val bom = bomInputStream.bom
-                val charsetName = if (bom == null) "UTF-8" else bom.charsetName
-                val reader: Reader = InputStreamReader(bomInputStream, charsetName)
-                val opmlReader = OpmlReader()
-                val result = opmlReader.readDocument(reader)
-                reader.close()
-//                    withContext(Dispatchers.Main) {
-//                        binding.progressBar.visibility = View.GONE
-                Logd(TAG, "Parsing was successful")
-//                        readElements = result
-//                    }
-            } catch (e: Throwable) {
-//                    withContext(Dispatchers.Main) {
-                Logd(TAG, Log.getStackTraceString(e))
-                val message = if (e.message == null) "" else e.message!!
-                if (message.lowercase().contains("permission")) {
-                    val permission = ActivityCompat.checkSelfPermission(context, Manifest.permission.READ_EXTERNAL_STORAGE)
-                    if (permission != PackageManager.PERMISSION_GRANTED) {
+            CoroutineScope(Dispatchers.IO).launch {
+                try {
+                    val opmlFileStream = context.contentResolver.openInputStream(uri)
+                    val bomInputStream = BOMInputStream(opmlFileStream)
+                    val bom = bomInputStream.bom
+                    val charsetName = if (bom == null) "UTF-8" else bom.charsetName
+                    val reader: Reader = InputStreamReader(bomInputStream, charsetName)
+                    val opmlReader = OpmlReader()
+                    val result = opmlReader.readDocument(reader)
+                    reader.close()
+                    withContext(Dispatchers.Main) { CB(result) }
+                } catch (e: Throwable) {
+                    withContext(Dispatchers.Main) {
+                        Logd(TAG, Log.getStackTraceString(e))
+                        val message = if (e.message == null) "" else e.message!!
+                        if (message.lowercase().contains("permission")) {
+                            val permission = ActivityCompat.checkSelfPermission(context, Manifest.permission.READ_EXTERNAL_STORAGE)
+                            if (permission != PackageManager.PERMISSION_GRANTED) {
 //                                requestPermission()
-                        return
-                    }
-                }
-//                        binding.progressBar.visibility = View.GONE
-                val alert = MaterialAlertDialogBuilder(context)
-                alert.setTitle(R.string.error_label)
-                val userReadable = context.getString(R.string.opml_reader_error)
-                val details = e.message
-                val total = """
+                                CB(listOf())
+                                return@withContext
+                            }
+                        }
+                        val alert = MaterialAlertDialogBuilder(context)
+                        alert.setTitle(R.string.error_label)
+                        val userReadable = context.getString(R.string.opml_reader_error)
+                        val details = e.message
+                        val total = """
                     $userReadable
                     
                     $details
                     """.trimIndent()
-                val errorMessage = SpannableString(total)
-                errorMessage.setSpan(ForegroundColorSpan(-0x77777778), userReadable.length, total.length, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
-                alert.setMessage(errorMessage)
-                alert.setPositiveButton(android.R.string.ok) { _: DialogInterface?, _: Int ->
+                        val errorMessage = SpannableString(total)
+                        errorMessage.setSpan(ForegroundColorSpan(-0x77777778), userReadable.length, total.length, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
+                        alert.setMessage(errorMessage)
+                        alert.setPositiveButton(android.R.string.ok) { _: DialogInterface?, _: Int ->
 //                            finish()
+                        }
+                        alert.show()
+                        CB(listOf())
+                    }
                 }
-                alert.show()
-//                    }
             }
-//            }
         }
     }
 }
\ No newline at end of file
diff --git a/app/src/main/kotlin/ac/mdiq/podcini/preferences/UserPreferences.kt b/app/src/main/kotlin/ac/mdiq/podcini/preferences/UserPreferences.kt
index 42ea8f9b..7c0ca087 100644
--- a/app/src/main/kotlin/ac/mdiq/podcini/preferences/UserPreferences.kt
+++ b/app/src/main/kotlin/ac/mdiq/podcini/preferences/UserPreferences.kt
@@ -50,10 +50,12 @@ object UserPreferences {
     val isThemeColorTinted: Boolean
         get() = Build.VERSION.SDK_INT >= 31 && appPrefs.getBoolean(Prefs.prefTintedColors.name, false)
 
+    // not using this
     var hiddenDrawerItems: List<String>
         get() {
-            val hiddenItems = appPrefs.getString(Prefs.prefHiddenDrawerItems.name, "")
-            return hiddenItems?.split(",") ?: listOf()
+            return listOf()
+//            val hiddenItems = appPrefs.getString(Prefs.prefHiddenDrawerItems.name, "")
+//            return hiddenItems?.split(",") ?: listOf()
         }
         set(items) {
             val str = items.joinToString()
diff --git a/app/src/main/kotlin/ac/mdiq/podcini/storage/database/Feeds.kt b/app/src/main/kotlin/ac/mdiq/podcini/storage/database/Feeds.kt
index 0e8c5e3b..f6268887 100644
--- a/app/src/main/kotlin/ac/mdiq/podcini/storage/database/Feeds.kt
+++ b/app/src/main/kotlin/ac/mdiq/podcini/storage/database/Feeds.kt
@@ -22,6 +22,7 @@ import ac.mdiq.podcini.storage.database.RealmDB.upsert
 import ac.mdiq.podcini.storage.database.RealmDB.upsertBlk
 import ac.mdiq.podcini.storage.model.*
 import ac.mdiq.podcini.storage.model.Feed.Companion.MAX_NATURAL_SYNTHETIC_ID
+import ac.mdiq.podcini.storage.model.FeedPreferences.AudioType
 import ac.mdiq.podcini.storage.model.FeedPreferences.AutoDeleteAction
 import ac.mdiq.podcini.storage.model.FeedPreferences.Companion.TAG_ROOT
 import ac.mdiq.podcini.storage.utils.FilesUtils.feedfilePath
@@ -481,6 +482,13 @@ object Feeds {
         return !feed.isLocalFeed || isAutoDeleteLocal
     }
 
+    fun createYTSyndicates() {
+        getYoutubeSyndicate(true, false)
+        getYoutubeSyndicate(false, false)
+        getYoutubeSyndicate(true, true)
+        getYoutubeSyndicate(false, true)
+    }
+
     private fun getYoutubeSyndicate(video: Boolean, music: Boolean): Feed {
         var feedId: Long = if (video) 1 else 2
         if (music) feedId += 2  // music feed takes ids 3 and 4
@@ -492,6 +500,7 @@ object Feeds {
         feed = createSynthetic(feedId, name)
         feed.type = Feed.FeedType.YOUTUBE.name
         feed.hasVideoMedia = video
+        feed.preferences!!.audioTypeSetting = if (music) AudioType.MOVIE else AudioType.SPEECH
         feed.preferences!!.videoModePolicy = if (video) VideoMode.WINDOW_VIEW else VideoMode.AUDIO_ONLY
         upsertBlk(feed) {}
         EventFlow.postEvent(FlowEvent.FeedListEvent(FlowEvent.FeedListEvent.Action.ADDED))
@@ -509,8 +518,26 @@ object Feeds {
         episode.feedId = feed.id
         episode.media?.id = episode.id
         upsertBlk(episode) {}
-        feed.episodes.add(episode)
-        upsertBlk(feed) {}
+        upsertBlk(feed) {
+            it.episodes.add(episode)
+        }
+        EventFlow.postStickyEvent(FlowEvent.FeedUpdatingEvent(false))
+        return 1
+    }
+
+    fun addToSyndicate(episode: Episode, feed: Feed) : Int {
+        Logd(TAG, "addToYoutubeSyndicate: feed: ${feed.title}")
+        if (searchEpisodeByIdentifyingValue(feed.episodes, episode) != null) return 2
+
+        Logd(TAG, "addToSyndicate adding new episode: ${episode.title}")
+        episode.feed = feed
+        episode.id = Feed.newId()
+        episode.feedId = feed.id
+        episode.media?.id = episode.id
+        upsertBlk(episode) {}
+        upsertBlk(feed) {
+            it.episodes.add(episode)
+        }
         EventFlow.postStickyEvent(FlowEvent.FeedUpdatingEvent(false))
         return 1
     }
diff --git a/app/src/main/kotlin/ac/mdiq/podcini/storage/database/Queues.kt b/app/src/main/kotlin/ac/mdiq/podcini/storage/database/Queues.kt
index eba8b805..0ea8f095 100644
--- a/app/src/main/kotlin/ac/mdiq/podcini/storage/database/Queues.kt
+++ b/app/src/main/kotlin/ac/mdiq/podcini/storage/database/Queues.kt
@@ -51,7 +51,7 @@ object Queues {
      */
     var queueKeepSortedOrder: EpisodeSortOrder?
         get() {
-            val sortOrderStr = appPrefs.getString(UserPreferences.Prefs.prefQueueKeepSortedOrder.name, "use-default")
+            val sortOrderStr = appPrefs.getString(UserPreferences.Prefs.prefQueueKeepSortedOrder.name, "use-default")!!
             return EpisodeSortOrder.parseWithDefault(sortOrderStr, EpisodeSortOrder.DATE_NEW_OLD)
         }
         set(sortOrder) {
@@ -61,8 +61,8 @@ object Queues {
 
     var enqueueLocation: EnqueueLocation
         get() {
-            val valStr = appPrefs.getString(UserPreferences.Prefs.prefEnqueueLocation.name, EnqueueLocation.BACK.name)
-            try { return EnqueueLocation.valueOf(valStr!!)
+            val valStr = appPrefs.getString(UserPreferences.Prefs.prefEnqueueLocation.name, EnqueueLocation.BACK.name)!!
+            try { return EnqueueLocation.valueOf(valStr)
             } catch (t: Throwable) {
                 // should never happen but just in case
                 Log.e(TAG, "getEnqueueLocation: invalid value '$valStr' Use default.", t)
diff --git a/app/src/main/kotlin/ac/mdiq/podcini/storage/database/RealmDB.kt b/app/src/main/kotlin/ac/mdiq/podcini/storage/database/RealmDB.kt
index 590bbd92..23c35965 100644
--- a/app/src/main/kotlin/ac/mdiq/podcini/storage/database/RealmDB.kt
+++ b/app/src/main/kotlin/ac/mdiq/podcini/storage/database/RealmDB.kt
@@ -40,7 +40,7 @@ object RealmDB {
                 SubscriptionLog::class,
                 Chapter::class))
             .name("Podcini.realm")
-            .schemaVersion(35)
+            .schemaVersion(36)
             .migration({ mContext ->
                 val oldRealm = mContext.oldRealm // old realm using the previous schema
                 val newRealm = mContext.newRealm // new realm using the new schema
diff --git a/app/src/main/kotlin/ac/mdiq/podcini/storage/model/Episode.kt b/app/src/main/kotlin/ac/mdiq/podcini/storage/model/Episode.kt
index b5c94e3b..1292ac22 100644
--- a/app/src/main/kotlin/ac/mdiq/podcini/storage/model/Episode.kt
+++ b/app/src/main/kotlin/ac/mdiq/podcini/storage/model/Episode.kt
@@ -56,6 +56,11 @@ class Episode : RealmObject {
 
     var feedId: Long? = null
 
+    // parent in these refers to the original parent of the content (shared)
+    var parentTitle: String? = null
+
+    var parentURL: String? = null
+
     var podcastIndexChapterUrl: String? = null
 
     var playState: Int
diff --git a/app/src/main/kotlin/ac/mdiq/podcini/storage/model/EpisodeFilter.kt b/app/src/main/kotlin/ac/mdiq/podcini/storage/model/EpisodeFilter.kt
index a5348ddd..ca8708ff 100644
--- a/app/src/main/kotlin/ac/mdiq/podcini/storage/model/EpisodeFilter.kt
+++ b/app/src/main/kotlin/ac/mdiq/podcini/storage/model/EpisodeFilter.kt
@@ -7,13 +7,17 @@ import java.io.Serializable
 class EpisodeFilter(vararg properties_: String) : Serializable {
     val properties: HashSet<String> = setOf(*properties_).filter { it.isNotEmpty() }.map {it.trim()}.toHashSet()
 
-    val showQueued: Boolean = properties.contains(States.queued.name)
-    val showNotQueued: Boolean = properties.contains(States.not_queued.name)
+//    val showQueued: Boolean = properties.contains(States.queued.name)
+//    val showNotQueued: Boolean = properties.contains(States.not_queued.name)
     val showDownloaded: Boolean = properties.contains(States.downloaded.name)
     val showNotDownloaded: Boolean = properties.contains(States.not_downloaded.name)
 
     constructor(properties: String) : this(*(properties.split(",").toTypedArray()))
 
+    fun add(vararg properties_: String) {
+        properties.addAll(setOf(*properties_).filter { it.isNotEmpty() }.map {it.trim()})
+    }
+
     fun queryString(): String {
         val statements: MutableList<String> = mutableListOf()
         val mediaTypeQuerys = mutableListOf<String>()
@@ -37,7 +41,7 @@ class EpisodeFilter(vararg properties_: String) : Serializable {
         if (properties.contains(States.bad.name)) ratingQuerys.add(" rating == ${Rating.BAD.code} ")
         if (properties.contains(States.neutral.name)) ratingQuerys.add(" rating == ${Rating.OK.code} ")
         if (properties.contains(States.good.name)) ratingQuerys.add(" rating == ${Rating.GOOD.code} ")
-        if (properties.contains(States.favorite.name)) ratingQuerys.add(" rating == ${Rating.SUPER.code} ")
+        if (properties.contains(States.superb.name)) ratingQuerys.add(" rating == ${Rating.SUPER.code} ")
         if (ratingQuerys.isNotEmpty()) {
             val query = StringBuilder(" (" + ratingQuerys[0])
             if (ratingQuerys.size > 1) for (r in ratingQuerys.subList(1, ratingQuerys.size)) {
@@ -135,8 +139,8 @@ class EpisodeFilter(vararg properties_: String) : Serializable {
         no_media,
         has_comments,
         no_comments,
-        queued,
-        not_queued,
+//        queued,
+//        not_queued,
         downloaded,
         not_downloaded,
         auto_downloadable,
@@ -146,7 +150,7 @@ class EpisodeFilter(vararg properties_: String) : Serializable {
         bad,
         neutral,
         good,
-        favorite,
+        superb,
     }
 
     enum class EpisodesFilterGroup(val nameRes: Int, vararg values_: ItemProperties) {
@@ -155,7 +159,7 @@ class EpisodeFilter(vararg properties_: String) : Serializable {
             ItemProperties(R.string.bad, States.bad.name),
             ItemProperties(R.string.OK, States.neutral.name),
             ItemProperties(R.string.good, States.good.name),
-            ItemProperties(R.string.Super, States.favorite.name),
+            ItemProperties(R.string.Super, States.superb.name),
         ),
         PLAY_STATE(R.string.playstate, ItemProperties(R.string.unspecified, States.unspecified.name),
             ItemProperties(R.string.building, States.building.name),
diff --git a/app/src/main/kotlin/ac/mdiq/podcini/storage/model/EpisodeSortOrder.kt b/app/src/main/kotlin/ac/mdiq/podcini/storage/model/EpisodeSortOrder.kt
index e98f8d9b..2780266c 100644
--- a/app/src/main/kotlin/ac/mdiq/podcini/storage/model/EpisodeSortOrder.kt
+++ b/app/src/main/kotlin/ac/mdiq/podcini/storage/model/EpisodeSortOrder.kt
@@ -38,25 +38,26 @@ enum class EpisodeSortOrder(val code: Int, val res: Int) {
          * Converts the string representation to its enum value. If the string value is unknown,
          * the given default value is returned.
          */
-        fun parseWithDefault(value: String?, defaultValue: EpisodeSortOrder): EpisodeSortOrder {
-            return try { valueOf(value!!) } catch (e: IllegalArgumentException) { defaultValue }
+        fun parseWithDefault(value: String, defaultValue: EpisodeSortOrder): EpisodeSortOrder {
+            return try { valueOf(value) } catch (e: IllegalArgumentException) { defaultValue }
         }
 
-        fun fromCodeString(codeStr: String?): EpisodeSortOrder? {
-            if (codeStr.isNullOrEmpty()) return null
+        fun fromCodeString(codeStr: String?): EpisodeSortOrder {
+            if (codeStr.isNullOrEmpty()) return EPISODE_TITLE_A_Z
             val code = codeStr.toInt()
             for (sortOrder in entries) {
                 if (sortOrder.code == code) return sortOrder
             }
-            throw IllegalArgumentException("Unsupported code: $code")
+            return EPISODE_TITLE_A_Z
+//            throw IllegalArgumentException("Unsupported code: $code")
         }
 
-        fun fromCode(code: Int): EpisodeSortOrder? {
-            return enumValues<EpisodeSortOrder>().firstOrNull { it.code == code }
+        fun fromCode(code: Int): EpisodeSortOrder {
+            return enumValues<EpisodeSortOrder>().firstOrNull { it.code == code } ?: EPISODE_TITLE_A_Z
         }
 
-        fun toCodeString(sortOrder: EpisodeSortOrder?): String? {
-            return sortOrder?.code?.toString()
+        fun toCodeString(sortOrder: EpisodeSortOrder): String? {
+            return sortOrder.code.toString()
         }
 
         fun valuesOf(stringValues: Array<String?>): Array<EpisodeSortOrder?> {
diff --git a/app/src/main/kotlin/ac/mdiq/podcini/ui/actions/SwipeActions.kt b/app/src/main/kotlin/ac/mdiq/podcini/ui/actions/SwipeActions.kt
index bfe0012c..58f802a9 100644
--- a/app/src/main/kotlin/ac/mdiq/podcini/ui/actions/SwipeActions.kt
+++ b/app/src/main/kotlin/ac/mdiq/podcini/ui/actions/SwipeActions.kt
@@ -3,7 +3,7 @@ package ac.mdiq.podcini.ui.actions
 import ac.mdiq.podcini.R
 import ac.mdiq.podcini.playback.base.InTheatre.curQueue
 import ac.mdiq.podcini.storage.database.Episodes.deleteEpisodeMedia
-import ac.mdiq.podcini.storage.database.Episodes.setPlayState
+import ac.mdiq.podcini.storage.database.Episodes.hasAlmostEnded
 import ac.mdiq.podcini.storage.database.Episodes.setPlayStateSync
 import ac.mdiq.podcini.storage.database.Queues.addToQueue
 import ac.mdiq.podcini.storage.database.Queues.removeFromQueueSync
@@ -14,7 +14,6 @@ import ac.mdiq.podcini.storage.database.RealmDB.upsertBlk
 import ac.mdiq.podcini.storage.model.Episode
 import ac.mdiq.podcini.storage.model.EpisodeFilter
 import ac.mdiq.podcini.storage.model.PlayState
-import ac.mdiq.podcini.storage.database.Episodes.hasAlmostEnded
 import ac.mdiq.podcini.ui.activity.MainActivity
 import ac.mdiq.podcini.ui.compose.*
 import ac.mdiq.podcini.ui.fragment.*
@@ -22,7 +21,6 @@ import ac.mdiq.podcini.util.EventFlow
 import ac.mdiq.podcini.util.FlowEvent
 import ac.mdiq.podcini.util.Logd
 import ac.mdiq.podcini.util.MiscFormatter.fullDateTimeString
-import ac.mdiq.podcini.util.MiscFormatter.localDateTimeString
 import android.content.Context
 import android.content.DialogInterface
 import android.content.SharedPreferences
@@ -55,7 +53,6 @@ import androidx.lifecycle.DefaultLifecycleObserver
 import androidx.lifecycle.LifecycleOwner
 import com.google.android.material.dialog.MaterialAlertDialogBuilder
 import com.google.android.material.snackbar.Snackbar
-import kotlinx.coroutines.Job
 import kotlinx.coroutines.runBlocking
 import java.util.*
 
@@ -70,30 +67,14 @@ interface SwipeAction {
     @DrawableRes
     fun getActionColor(): Int
 
-    fun performAction(item: Episode, fragment: Fragment, filter: EpisodeFilter)
-
-    fun willRemove(filter: EpisodeFilter, item: Episode): Boolean {
-        return false
-    }
+    fun performAction(item: Episode, fragment: Fragment)
 }
 
 class SwipeActions(private val fragment: Fragment, private val tag: String) : DefaultLifecycleObserver {
-
-    @set:JvmName("setFilterProperty")
-    var filter: EpisodeFilter? = null
-    var actions: Actions
-
-    init {
-        actions = getPrefs(tag)
-    }
+    var actions: Actions = getPrefs(tag, "")
 
     override fun onStart(owner: LifecycleOwner) {
-        actions = getPrefs(tag)
-    }
-
-    @JvmName("setFilterFunction")
-    fun setFilter(filter: EpisodeFilter?) {
-        this.filter = filter
+        actions = getPrefs(tag, "")
     }
 
     fun showDialog() {
@@ -106,7 +87,7 @@ class SwipeActions(private val fragment: Fragment, private val tag: String) : De
                         showDialog.value = false
                         (fragment.view as? ViewGroup)?.removeView(this@apply)
                     }) {
-                        actions = getPrefs(this@SwipeActions.tag)
+                        actions = getPrefs(this@SwipeActions.tag, "")
                         // TODO: remove the need of event
                         EventFlow.postEvent(FlowEvent.SwipeActionsChangedEvent())
                     }
@@ -162,7 +143,7 @@ class SwipeActions(private val fragment: Fragment, private val tag: String) : De
         override fun getTitle(context: Context): String {
             return context.getString(R.string.add_to_queue_label)
         }
-        override fun performAction(item: Episode, fragment: Fragment, filter: EpisodeFilter) {
+        override fun performAction(item: Episode, fragment: Fragment) {
             addToQueue(item)
         }
     }
@@ -180,7 +161,7 @@ class SwipeActions(private val fragment: Fragment, private val tag: String) : De
         override fun getTitle(context: Context): String {
             return context.getString(R.string.combo_action)
         }
-        override fun performAction(item: Episode, fragment: Fragment, filter: EpisodeFilter) {
+        override fun performAction(item: Episode, fragment: Fragment) {
             val composeView = ComposeView(fragment.requireContext()).apply {
                 setContent {
                     var showDialog by remember { mutableStateOf(true) }
@@ -195,7 +176,7 @@ class SwipeActions(private val fragment: Fragment, private val tag: String) : De
                                     for (action in swipeActions) {
                                         if (action.getId() == ActionTypes.NO_ACTION.name || action.getId() == ActionTypes.COMBO.name) continue
                                         Row(verticalAlignment = Alignment.CenterVertically, modifier = Modifier.padding(4.dp).clickable {
-                                            action.performAction(item, fragment, filter)
+                                            action.performAction(item, fragment)
                                             showDialog = false
                                             (fragment.view as? ViewGroup)?.removeView(this@apply)
                                         }) {
@@ -231,20 +212,18 @@ class SwipeActions(private val fragment: Fragment, private val tag: String) : De
         override fun getTitle(context: Context): String {
             return context.getString(R.string.delete_episode_label)
         }
-        override fun performAction(item_: Episode, fragment: Fragment, filter: EpisodeFilter) {
+        override fun performAction(item_: Episode, fragment: Fragment) {
             var item = item_
             if (!item.isDownloaded && item.feed?.isLocalFeed != true) return
             val media = item.media
             if (media != null) {
                 val almostEnded = hasAlmostEnded(media)
-                if (almostEnded && item.playState < PlayState.PLAYED.code) item = runBlocking { setPlayStateSync(PlayState.PLAYED.code, item, resetMediaPosition = true, removeFromQueue = false) }
+                if (almostEnded && item.playState < PlayState.PLAYED.code)
+                    item = runBlocking { setPlayStateSync(PlayState.PLAYED.code, item, resetMediaPosition = true, removeFromQueue = false) }
                 if (almostEnded) item = upsertBlk(item) { it.media?.playbackCompletionDate = Date() }
             }
             deleteEpisodesWarnLocal(fragment.requireContext(), listOf(item))
         }
-        override fun willRemove(filter: EpisodeFilter, item: Episode): Boolean {
-            return filter.showDownloaded && (item.isDownloaded || item.feed?.isLocalFeed == true)
-        }
     }
 
     class SetRatingSwipeAction : SwipeAction {
@@ -260,7 +239,7 @@ class SwipeActions(private val fragment: Fragment, private val tag: String) : De
         override fun getTitle(context: Context): String {
             return context.getString(R.string.set_rating_label)
         }
-        override fun performAction(item: Episode, fragment: Fragment, filter: EpisodeFilter) {
+        override fun performAction(item: Episode, fragment: Fragment) {
             var showChooseRatingDialog by mutableStateOf(true)
             val composeView = ComposeView(fragment.requireContext()).apply {
                 setContent {
@@ -289,7 +268,7 @@ class SwipeActions(private val fragment: Fragment, private val tag: String) : De
         override fun getTitle(context: Context): String {
             return context.getString(R.string.add_opinion_label)
         }
-        override fun performAction(item: Episode, fragment: Fragment, filter: EpisodeFilter) {
+        override fun performAction(item: Episode, fragment: Fragment) {
             var showEditComment by mutableStateOf(true)
             val composeView = ComposeView(fragment.requireContext()).apply {
                 setContent {
@@ -328,7 +307,7 @@ class SwipeActions(private val fragment: Fragment, private val tag: String) : De
         override fun getTitle(context: Context): String {
             return context.getString(R.string.no_action_label)
         }
-        override fun performAction(item: Episode, fragment: Fragment, filter: EpisodeFilter) {}
+        override fun performAction(item: Episode, fragment: Fragment) {}
     }
 
     class RemoveFromHistorySwipeAction : SwipeAction {
@@ -346,7 +325,7 @@ class SwipeActions(private val fragment: Fragment, private val tag: String) : De
         override fun getTitle(context: Context): String {
             return context.getString(R.string.remove_history_label)
         }
-        override fun performAction(item: Episode, fragment: Fragment, filter: EpisodeFilter) {
+        override fun performAction(item: Episode, fragment: Fragment) {
             val playbackCompletionDate: Date? = item.media?.playbackCompletionDate
             val lastPlayedDate = item.media?.lastPlayedTime
             setHistoryDates(item)
@@ -356,9 +335,6 @@ class SwipeActions(private val fragment: Fragment, private val tag: String) : De
                 .setAction(fragment.getString(R.string.undo)) {
                     if (playbackCompletionDate != null) setHistoryDates(item, lastPlayedDate?:0, playbackCompletionDate) }
         }
-        override fun willRemove(filter: EpisodeFilter, item: Episode): Boolean {
-            return true
-        }
         private fun setHistoryDates(episode: Episode, lastPlayed: Long = 0, completed: Date = Date(0)) {
             runOnIOScope {
                 val episode_ = realm.query(Episode::class).query("id == $0", episode.id).first().find()
@@ -386,8 +362,8 @@ class SwipeActions(private val fragment: Fragment, private val tag: String) : De
         override fun getTitle(context: Context): String {
             return context.getString(R.string.remove_from_queue_label)
         }
-        override fun performAction(item_: Episode, fragment: Fragment, filter: EpisodeFilter) {
-            val position: Int = curQueue.episodes.indexOf(item_)
+        override fun performAction(item_: Episode, fragment: Fragment) {
+//            val position: Int = curQueue.episodes.indexOf(item_)
             var item = item_
             val media = item.media
             if (media != null) {
@@ -398,36 +374,6 @@ class SwipeActions(private val fragment: Fragment, private val tag: String) : De
             if (item.playState < PlayState.SKIPPED.code) item = runBlocking { setPlayStateSync(PlayState.SKIPPED.code, item, resetMediaPosition = false, removeFromQueue = false) }
 //            removeFromQueue(item)
             runOnIOScope { removeFromQueueSync(curQueue, item) }
-            if (willRemove(filter, item)) {
-                (fragment.requireActivity() as MainActivity).showSnackbarAbovePlayer(fragment.resources.getQuantityString(R.plurals.removed_from_queue_batch_label, 1, 1), Snackbar.LENGTH_LONG)
-                    .setAction(fragment.getString(R.string.undo)) {
-                        addToQueueAt(item, position)
-                    }
-            }
-        }
-        override fun willRemove(filter: EpisodeFilter, item: Episode): Boolean {
-            return filter.showQueued || filter.showNotQueued
-        }
-        /**
-         * Inserts a Episode in the queue at the specified index. The 'read'-attribute of the Episode will be set to
-         * true. If the Episode is already in the queue, the queue will not be modified.
-         * @param episode                the Episode that should be added to the queue.
-         * @param index               Destination index. Must be in range 0..queue.size()
-         * @throws IndexOutOfBoundsException if index < 0 || index >= queue.size()
-         */
-
-        private fun addToQueueAt(episode: Episode, index: Int) : Job {
-            return runOnIOScope {
-                if (curQueue.episodeIds.contains(episode.id)) return@runOnIOScope
-                if (episode.isNew) setPlayState(PlayState.UNPLAYED.code, false, episode)
-                curQueue = upsert(curQueue) {
-                    it.episodeIds.add(index, episode.id)
-                    it.update()
-                }
-//            curQueue.episodes.add(index, episode)
-                EventFlow.postEvent(FlowEvent.QueueEvent.added(episode, index))
-//            if (performAutoDownload) autodownloadEpisodeMedia(context)
-            }
         }
     }
 
@@ -444,7 +390,7 @@ class SwipeActions(private val fragment: Fragment, private val tag: String) : De
         override fun getTitle(context: Context): String {
             return context.getString(R.string.put_in_queue_label)
         }
-        override fun performAction(item: Episode, fragment: Fragment, filter: EpisodeFilter) {
+        override fun performAction(item: Episode, fragment: Fragment) {
             var showPutToQueueDialog by mutableStateOf(true)
             val composeView = ComposeView(fragment.requireContext()).apply {
                 setContent {
@@ -473,7 +419,7 @@ class SwipeActions(private val fragment: Fragment, private val tag: String) : De
         override fun getTitle(context: Context): String {
             return context.getString(R.string.download_label)
         }
-        override fun performAction(item: Episode, fragment: Fragment, filter: EpisodeFilter) {
+        override fun performAction(item: Episode, fragment: Fragment) {
             if (!item.isDownloaded && item.feed != null && !item.feed!!.isLocalFeed) {
                 DownloadActionButton(item).onClick(fragment.requireContext())
             }
@@ -493,7 +439,7 @@ class SwipeActions(private val fragment: Fragment, private val tag: String) : De
         override fun getTitle(context: Context): String {
             return context.getString(R.string.set_play_state_label)
         }
-        override fun performAction(item: Episode, fragment: Fragment, filter: EpisodeFilter) {
+        override fun performAction(item: Episode, fragment: Fragment) {
             var showPlayStateDialog by mutableStateOf(true)
             val composeView = ComposeView(fragment.requireContext()).apply {
                 setContent {
@@ -531,7 +477,7 @@ class SwipeActions(private val fragment: Fragment, private val tag: String) : De
         override fun getTitle(context: Context): String {
             return context.getString(R.string.shelve_label)
         }
-        override fun performAction(item: Episode, fragment: Fragment, filter: EpisodeFilter) {
+        override fun performAction(item: Episode, fragment: Fragment) {
             var showShelveDialog by mutableStateOf(true)
             val composeView = ComposeView(fragment.requireContext()).apply {
                 setContent {
@@ -560,7 +506,7 @@ class SwipeActions(private val fragment: Fragment, private val tag: String) : De
         override fun getTitle(context: Context): String {
             return context.getString(R.string.erase_episodes_label)
         }
-        override fun performAction(item: Episode, fragment: Fragment, filter: EpisodeFilter) {
+        override fun performAction(item: Episode, fragment: Fragment) {
             var showEraseDialog by mutableStateOf(true)
             val composeView = ComposeView(fragment.requireContext()).apply {
                 setContent {
@@ -600,16 +546,6 @@ class SwipeActions(private val fragment: Fragment, private val tag: String) : De
             return Actions(prefsString)
         }
 
-        fun getPrefs(tag: String): Actions {
-            return getPrefs(tag, "")
-        }
-
-         @JvmStatic
-        fun getPrefsWithDefaults(tag: String): Actions {
-             val defaultActions = "${ActionTypes.NO_ACTION.name},${ActionTypes.NO_ACTION.name}"
-            return getPrefs(tag, defaultActions)
-        }
-
 //        fun isSwipeActionEnabled(tag: String): Boolean {
 //            return prefs!!.getBoolean(KEY_PREFIX_NO_ACTION + tag, true)
 //        }
@@ -638,7 +574,7 @@ class SwipeActions(private val fragment: Fragment, private val tag: String) : De
             val context = LocalContext.current
             val textColor = MaterialTheme.colorScheme.onSurface
 
-            val actions = getPrefsWithDefaults(tag)
+            val actions = getPrefs(tag, "${ActionTypes.NO_ACTION.name},${ActionTypes.NO_ACTION.name}")
             val leftAction = remember { mutableStateOf(actions.left) }
             val rightAction = remember { mutableStateOf(actions.right) }
             var keys = swipeActions
@@ -679,15 +615,18 @@ class SwipeActions(private val fragment: Fragment, private val tag: String) : De
             Dialog(onDismissRequest = { onDismissRequest() }) {
                 var forFragment = ""
                 when (tag) {
-                    AllEpisodesFragment.TAG -> {
+//                    AllEpisodesFragment.TAG -> {
+//                        forFragment = stringResource(R.string.episodes_label)
+//                        keys = keys.filter { a: SwipeAction -> !a.getId().equals(ActionTypes.REMOVE_FROM_HISTORY.name) }
+//                    }
+                    EpisodesFragment.TAG -> {
                         forFragment = stringResource(R.string.episodes_label)
-                        keys = keys.filter { a: SwipeAction -> !a.getId().equals(ActionTypes.REMOVE_FROM_HISTORY.name) }
-                    }
-                    DownloadsFragment.TAG -> {
-                        forFragment = stringResource(R.string.downloads_label)
-                        keys = keys.filter { a: SwipeAction ->
-                            (!a.getId().equals(ActionTypes.REMOVE_FROM_HISTORY.name) && !a.getId().equals(ActionTypes.START_DOWNLOAD.name)) }
                     }
+//                    DownloadsFragment.TAG -> {
+//                        forFragment = stringResource(R.string.downloads_label)
+//                        keys = keys.filter { a: SwipeAction ->
+//                            (!a.getId().equals(ActionTypes.REMOVE_FROM_HISTORY.name) && !a.getId().equals(ActionTypes.START_DOWNLOAD.name)) }
+//                    }
                     FeedEpisodesFragment.TAG -> {
                         forFragment = stringResource(R.string.subscription)
                         keys = keys.filter { a: SwipeAction -> !a.getId().equals(ActionTypes.REMOVE_FROM_HISTORY.name) }
@@ -698,10 +637,10 @@ class SwipeActions(private val fragment: Fragment, private val tag: String) : De
                             (!a.getId().equals(ActionTypes.ADD_TO_QUEUE.name) && !a.getId().equals(ActionTypes.REMOVE_FROM_HISTORY.name)) }.toList()
 //                        keys = keys.filter { a: SwipeAction -> (!a.getId().equals(ActionTypes.REMOVE_FROM_HISTORY.name)) }
                     }
-                    HistoryFragment.TAG -> {
-                        forFragment = stringResource(R.string.playback_history_label)
-                        keys = keys.toList()
-                    }
+//                    HistoryFragment.TAG -> {
+//                        forFragment = stringResource(R.string.playback_history_label)
+//                        keys = keys.toList()
+//                    }
                     else -> {}
                 }
                 if (tag != QueuesFragment.TAG) keys = keys.filter { a: SwipeAction -> !a.getId().equals(ActionTypes.REMOVE_FROM_QUEUE.name) }
@@ -740,9 +679,7 @@ class SwipeActions(private val fragment: Fragment, private val tag: String) : De
                             EventFlow.postEvent(FlowEvent.SwipeActionsChangedEvent())
                             callback()
                             onDismissRequest()
-                        }) {
-                            Text("Confirm")
-                        }
+                        }) { Text("Confirm") }
                     }
                 }
             }
diff --git a/app/src/main/kotlin/ac/mdiq/podcini/ui/activity/MainActivity.kt b/app/src/main/kotlin/ac/mdiq/podcini/ui/activity/MainActivity.kt
index a636e7a8..b3840d54 100644
--- a/app/src/main/kotlin/ac/mdiq/podcini/ui/activity/MainActivity.kt
+++ b/app/src/main/kotlin/ac/mdiq/podcini/ui/activity/MainActivity.kt
@@ -456,11 +456,11 @@ class MainActivity : CastEnabledActivity() {
         val fragment: Fragment
         when (tag) {
             QueuesFragment.TAG -> fragment = QueuesFragment()
-            AllEpisodesFragment.TAG -> fragment = AllEpisodesFragment()
-            DownloadsFragment.TAG -> fragment = DownloadsFragment()
+            EpisodesFragment.TAG -> fragment = EpisodesFragment()
+//            AllEpisodesFragment.TAG -> fragment = AllEpisodesFragment()
+//            DownloadsFragment.TAG -> fragment = DownloadsFragment()
             LogsFragment.TAG -> fragment = LogsFragment()
-//            SubscriptionLogFragment.TAG -> fragment = SubscriptionLogFragment()
-            HistoryFragment.TAG -> fragment = HistoryFragment()
+//            HistoryFragment.TAG -> fragment = HistoryFragment()
             OnlineSearchFragment.TAG -> fragment = OnlineSearchFragment()
             SubscriptionsFragment.TAG -> fragment = SubscriptionsFragment()
             StatisticsFragment.TAG -> fragment = StatisticsFragment()
@@ -734,9 +734,9 @@ class MainActivity : CastEnabledActivity() {
             "/deeplink/main" -> {
                 val feature = uri.getQueryParameter("page") ?: return
                 when (feature) {
-                    "DOWNLOADS" -> loadFragment(DownloadsFragment.TAG, null)
-                    "HISTORY" -> loadFragment(HistoryFragment.TAG, null)
-                    "EPISODES" -> loadFragment(AllEpisodesFragment.TAG, null)
+//                    "DOWNLOADS" -> loadFragment(DownloadsFragment.TAG, null)
+//                    "HISTORY" -> loadFragment(HistoryFragment.TAG, null)
+                    "EPISODES" -> loadFragment(EpisodesFragment.TAG, null)
                     "QUEUE" -> loadFragment(QueuesFragment.TAG, null)
                     "SUBSCRIPTIONS" -> loadFragment(SubscriptionsFragment.TAG, null)
                     "STATISTCS" -> loadFragment(StatisticsFragment.TAG, null)
diff --git a/app/src/main/kotlin/ac/mdiq/podcini/ui/activity/OpmlImportActivity.kt b/app/src/main/kotlin/ac/mdiq/podcini/ui/activity/OpmlImportActivity.kt
deleted file mode 100644
index 2a0662b2..00000000
--- a/app/src/main/kotlin/ac/mdiq/podcini/ui/activity/OpmlImportActivity.kt
+++ /dev/null
@@ -1,248 +0,0 @@
-package ac.mdiq.podcini.ui.activity
-
-import ac.mdiq.podcini.R
-import ac.mdiq.podcini.databinding.OpmlSelectionBinding
-import ac.mdiq.podcini.net.feed.FeedUpdateManager.runOnce
-import ac.mdiq.podcini.preferences.ThemeSwitcher.getTheme
-import ac.mdiq.podcini.storage.database.Feeds.updateFeed
-import ac.mdiq.podcini.preferences.OpmlTransporter.OpmlElement
-import ac.mdiq.podcini.preferences.OpmlTransporter.OpmlReader
-import ac.mdiq.podcini.storage.model.Feed
-import ac.mdiq.podcini.util.Logd
-import android.Manifest
-import android.content.DialogInterface
-import android.content.Intent
-import android.content.pm.PackageManager
-import android.net.Uri
-import android.os.Bundle
-import android.text.Spannable
-import android.text.SpannableString
-import android.text.style.ForegroundColorSpan
-import android.util.Log
-import android.view.Menu
-import android.view.MenuItem
-import android.view.View
-import android.widget.AdapterView
-import android.widget.AdapterView.OnItemClickListener
-import android.widget.ArrayAdapter
-import android.widget.ListView
-import android.widget.Toast
-import androidx.activity.result.contract.ActivityResultContracts
-import androidx.appcompat.app.AppCompatActivity
-import androidx.core.app.ActivityCompat
-import androidx.lifecycle.lifecycleScope
-
-import com.google.android.material.dialog.MaterialAlertDialogBuilder
-import kotlinx.coroutines.Dispatchers
-import kotlinx.coroutines.launch
-import kotlinx.coroutines.withContext
-import org.apache.commons.io.input.BOMInputStream
-import java.io.InputStreamReader
-import java.io.Reader
-
-class OpmlImportActivity : AppCompatActivity() {
-    private var uri: Uri? = null
-    private var _binding: OpmlSelectionBinding? = null
-    private val binding get() = _binding!!
-
-    private lateinit var selectAll: MenuItem
-    private lateinit var deselectAll: MenuItem
-
-    private var listAdapter: ArrayAdapter<String>? = null
-    private var readElements: ArrayList<OpmlElement>? = null
-
-    private val titleList: List<String>
-        get() {
-            val result: MutableList<String> = ArrayList()
-            if (!readElements.isNullOrEmpty()) for (element in readElements!!) if (element.text != null) result.add(element.text!!)
-            return result
-        }
-
-    private val requestPermissionLauncher = registerForActivityResult(ActivityResultContracts.RequestPermission()) { isGranted: Boolean ->
-        if (isGranted) startImport()
-        else {
-            MaterialAlertDialogBuilder(this)
-                .setMessage(R.string.opml_import_ask_read_permission)
-                .setPositiveButton(android.R.string.ok) { _: DialogInterface?, _: Int -> requestPermission() }
-                .setNegativeButton(R.string.cancel_label) { _: DialogInterface?, _: Int -> finish() }
-                .show()
-        }
-    }
-
-     override fun onCreate(savedInstanceState: Bundle?) {
-        setTheme(getTheme(this))
-        super.onCreate(savedInstanceState)
-        supportActionBar?.setDisplayHomeAsUpEnabled(true)
-        _binding = OpmlSelectionBinding.inflate(layoutInflater)
-        setContentView(binding.root)
-        Logd(TAG, "onCreate")
-
-        binding.feedlist.choiceMode = ListView.CHOICE_MODE_MULTIPLE
-        binding.feedlist.onItemClickListener = OnItemClickListener { _: AdapterView<*>?, _: View?, _: Int, _: Long ->
-            val checked = binding.feedlist.checkedItemPositions
-            var checkedCount = 0
-            for (i in 0 until checked.size()) if (checked.valueAt(i)) checkedCount++
-            if (listAdapter != null) {
-                if (checkedCount == listAdapter!!.count) {
-                    selectAll.isVisible = false
-                    deselectAll.isVisible = true
-                } else {
-                    deselectAll.isVisible = false
-                    selectAll.isVisible = true
-                }
-            }
-        }
-        binding.butCancel.setOnClickListener {
-            setResult(RESULT_CANCELED)
-            finish()
-        }
-        binding.butConfirm.setOnClickListener {
-            binding.progressBar.visibility = View.VISIBLE
-            val checked = binding.feedlist.checkedItemPositions
-            lifecycleScope.launch {
-                try {
-                    withContext(Dispatchers.IO) {
-                        for (i in 0 until checked.size()) {
-                            if (!checked.valueAt(i)) continue
-
-                            if (!readElements.isNullOrEmpty()) {
-                                val element = readElements!![checked.keyAt(i)]
-                                val feed = Feed(element.xmlUrl, null, if (element.text != null) element.text else "Unknown podcast")
-                                feed.episodes.clear()
-                                updateFeed(this@OpmlImportActivity, feed, false)
-                            }
-                        }
-                        runOnce(this@OpmlImportActivity)
-                    }
-                    binding.progressBar.visibility = View.GONE
-                    val intent = Intent(this@OpmlImportActivity, MainActivity::class.java)
-                    intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_NEW_TASK)
-                    startActivity(intent)
-                    finish()
-                } catch (e: Throwable) {
-                    e.printStackTrace()
-                    binding.progressBar.visibility = View.GONE
-                    Toast.makeText(this@OpmlImportActivity, (e.message ?: "Import error"), Toast.LENGTH_LONG).show()
-                }
-            }
-        }
-
-        var uri = intent.data
-        if (uri != null && uri.toString().startsWith("/")) uri = Uri.parse("file://$uri")
-        else {
-            val extraText = intent.getStringExtra(Intent.EXTRA_TEXT)
-            if (extraText != null) uri = Uri.parse(extraText)
-        }
-        importUri(uri)
-    }
-
-    private fun importUri(uri: Uri?) {
-        if (uri == null) {
-            MaterialAlertDialogBuilder(this).setMessage(R.string.opml_import_error_no_file).setPositiveButton(android.R.string.ok, null).show()
-            return
-        }
-        this.uri = uri
-        startImport()
-    }
-
-    override fun onCreateOptionsMenu(menu: Menu): Boolean {
-        super.onCreateOptionsMenu(menu)
-        val inflater = menuInflater
-        inflater.inflate(R.menu.opml_selection_options, menu)
-        selectAll = menu.findItem(R.id.select_all_item)
-        deselectAll = menu.findItem(R.id.deselect_all_item)
-        deselectAll.isVisible = false
-        return true
-    }
-
-    override fun onOptionsItemSelected(item: MenuItem): Boolean {
-        val itemId = item.itemId
-        when (itemId) {
-            R.id.select_all_item -> {
-                selectAll.isVisible = false
-                selectAllItems(true)
-                deselectAll.isVisible = true
-                return true
-            }
-            R.id.deselect_all_item -> {
-                deselectAll.isVisible = false
-                selectAllItems(false)
-                selectAll.isVisible = true
-                return true
-            }
-            android.R.id.home -> finish()
-        }
-        return false
-    }
-
-    private fun selectAllItems(b: Boolean) {
-        for (i in 0 until binding.feedlist.count) {
-            binding.feedlist.setItemChecked(i, b)
-        }
-    }
-
-    private fun requestPermission() {
-        requestPermissionLauncher.launch(Manifest.permission.READ_EXTERNAL_STORAGE)
-    }
-
-    /** Starts the import process.  */
-    private fun startImport() {
-        binding.progressBar.visibility = View.VISIBLE
-
-        lifecycleScope.launch(Dispatchers.IO) {
-            try {
-                val opmlFileStream = contentResolver.openInputStream(uri!!)
-                val bomInputStream = BOMInputStream(opmlFileStream)
-                val bom = bomInputStream.bom
-                val charsetName = if (bom == null) "UTF-8" else bom.charsetName
-                val reader: Reader = InputStreamReader(bomInputStream, charsetName)
-                val opmlReader = OpmlReader()
-                val result = opmlReader.readDocument(reader)
-                reader.close()
-                withContext(Dispatchers.Main) {
-                    binding.progressBar.visibility = View.GONE
-                    Logd(TAG, "Parsing was successful")
-                    readElements = result
-                    listAdapter = ArrayAdapter(this@OpmlImportActivity, android.R.layout.simple_list_item_multiple_choice, titleList)
-                    binding.feedlist.adapter = listAdapter
-                }
-            } catch (e: Throwable) {
-                withContext(Dispatchers.Main) {
-                    Logd(TAG, Log.getStackTraceString(e))
-                    val message = if (e.message == null) "" else e.message!!
-                    if (message.lowercase().contains("permission")) {
-                        val permission = ActivityCompat.checkSelfPermission(this@OpmlImportActivity, Manifest.permission.READ_EXTERNAL_STORAGE)
-                        if (permission != PackageManager.PERMISSION_GRANTED) {
-                            requestPermission()
-                            return@withContext
-                        }
-                    }
-                    binding.progressBar.visibility = View.GONE
-                    val alert = MaterialAlertDialogBuilder(this@OpmlImportActivity)
-                    alert.setTitle(R.string.error_label)
-                    val userReadable = getString(R.string.opml_reader_error)
-                    val details = e.message
-                    val total = """
-                    $userReadable
-                    
-                    $details
-                    """.trimIndent()
-                    val errorMessage = SpannableString(total)
-                    errorMessage.setSpan(ForegroundColorSpan(-0x77777778), userReadable.length, total.length, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
-                    alert.setMessage(errorMessage)
-                    alert.setPositiveButton(android.R.string.ok) { _: DialogInterface?, _: Int -> finish() }
-                    alert.show()
-                }
-            }
-        }
-    }
-
-    override fun onDestroy() {
-        _binding = null
-        super.onDestroy()
-    }
-
-    companion object {
-        private val TAG: String = OpmlImportActivity::class.simpleName ?: "Anonymous"
-    }
-}
diff --git a/app/src/main/kotlin/ac/mdiq/podcini/ui/activity/PreferenceActivity.kt b/app/src/main/kotlin/ac/mdiq/podcini/ui/activity/PreferenceActivity.kt
index d4e6fd13..3d664af1 100644
--- a/app/src/main/kotlin/ac/mdiq/podcini/ui/activity/PreferenceActivity.kt
+++ b/app/src/main/kotlin/ac/mdiq/podcini/ui/activity/PreferenceActivity.kt
@@ -9,6 +9,7 @@ import ac.mdiq.podcini.net.download.service.PodciniHttpClient.getHttpClient
 import ac.mdiq.podcini.net.download.service.PodciniHttpClient.newBuilder
 import ac.mdiq.podcini.net.download.service.PodciniHttpClient.reinit
 import ac.mdiq.podcini.net.feed.FeedUpdateManager.restartUpdateAlarm
+import ac.mdiq.podcini.net.feed.FeedUpdateManager.runOnce
 import ac.mdiq.podcini.net.sync.SyncService
 import ac.mdiq.podcini.net.sync.SyncService.Companion.isValidGuid
 import ac.mdiq.podcini.net.sync.SynchronizationCredentials
@@ -28,14 +29,13 @@ import ac.mdiq.podcini.net.sync.wifi.WifiSyncService.Companion.hostPort
 import ac.mdiq.podcini.net.sync.wifi.WifiSyncService.Companion.startInstantSync
 import ac.mdiq.podcini.playback.base.MediaPlayerBase.Companion.prefPlaybackSpeed
 import ac.mdiq.podcini.preferences.*
+import ac.mdiq.podcini.preferences.OpmlTransporter.OpmlElement
 import ac.mdiq.podcini.preferences.OpmlTransporter.OpmlWriter
 import ac.mdiq.podcini.preferences.ThemeSwitcher.getTheme
 import ac.mdiq.podcini.preferences.UserPreferences.appPrefs
-import ac.mdiq.podcini.preferences.UserPreferences.defaultPage
 import ac.mdiq.podcini.preferences.UserPreferences.fallbackSpeed
 import ac.mdiq.podcini.preferences.UserPreferences.fastForwardSecs
 import ac.mdiq.podcini.preferences.UserPreferences.fullNotificationButtons
-import ac.mdiq.podcini.preferences.UserPreferences.hiddenDrawerItems
 import ac.mdiq.podcini.preferences.UserPreferences.proxyConfig
 import ac.mdiq.podcini.preferences.UserPreferences.rewindSecs
 import ac.mdiq.podcini.preferences.UserPreferences.setVideoMode
@@ -45,6 +45,7 @@ import ac.mdiq.podcini.storage.database.Episodes.getEpisodeByGuidOrUrl
 import ac.mdiq.podcini.storage.database.Episodes.getEpisodes
 import ac.mdiq.podcini.storage.database.Episodes.hasAlmostEnded
 import ac.mdiq.podcini.storage.database.Feeds.getFeedList
+import ac.mdiq.podcini.storage.database.Feeds.updateFeed
 import ac.mdiq.podcini.storage.database.Queues.EnqueueLocation
 import ac.mdiq.podcini.storage.database.RealmDB.realm
 import ac.mdiq.podcini.storage.database.RealmDB.upsertBlk
@@ -52,9 +53,9 @@ import ac.mdiq.podcini.storage.model.*
 import ac.mdiq.podcini.storage.utils.FileNameGenerator.generateFileName
 import ac.mdiq.podcini.ui.actions.SwipeActions.Companion.SwipeActionsDialog
 import ac.mdiq.podcini.ui.compose.CustomTheme
+import ac.mdiq.podcini.ui.compose.OpmlImportSelectionDialog
 import ac.mdiq.podcini.ui.compose.PlaybackSpeedDialog
 import ac.mdiq.podcini.ui.fragment.*
-import ac.mdiq.podcini.ui.fragment.NavDrawerFragment.Companion.navMap
 import ac.mdiq.podcini.ui.utils.ThemeUtils.getColorFromAttr
 import ac.mdiq.podcini.util.EventFlow
 import ac.mdiq.podcini.util.FlowEvent
@@ -77,6 +78,7 @@ import android.text.method.HideReturnsTransformationMethod
 import android.text.method.PasswordTransformationMethod
 import android.util.Log
 import android.util.Patterns
+import android.util.SparseBooleanArray
 import android.view.*
 import android.view.inputmethod.EditorInfo
 import android.view.inputmethod.InputMethodManager
@@ -120,8 +122,6 @@ import androidx.fragment.app.Fragment
 import androidx.lifecycle.lifecycleScope
 import androidx.preference.Preference
 import androidx.preference.PreferenceFragmentCompat
-import com.bytehamster.lib.preferencesearch.SearchPreferenceResult
-import com.bytehamster.lib.preferencesearch.SearchPreferenceResultListener
 import com.google.android.material.dialog.MaterialAlertDialogBuilder
 import com.google.android.material.snackbar.Snackbar
 import kotlinx.coroutines.*
@@ -146,7 +146,6 @@ import java.util.*
 import java.util.concurrent.TimeUnit
 import java.util.regex.Pattern
 import javax.xml.parsers.DocumentBuilderFactory
-import kotlin.Throws
 import kotlin.math.round
 
 /**
@@ -594,12 +593,12 @@ class PreferenceActivity : AppCompatActivity() {
                                     ActivityCompat.recreate(requireActivity())
                                 })
                             }
-                            Column(modifier = Modifier.fillMaxWidth().padding(start = 16.dp, top = 10.dp).clickable(onClick = {
-                                drawerPreferencesDialog(requireContext(), null)
-                            })) {
-                                Text(stringResource(R.string.pref_nav_drawer_items_title), color = textColor, style = MaterialTheme.typography.titleLarge, fontWeight = FontWeight.Bold)
-                                Text(stringResource(R.string.pref_nav_drawer_items_sum), color = textColor)
-                            }
+//                            Column(modifier = Modifier.fillMaxWidth().padding(start = 16.dp, top = 10.dp).clickable(onClick = {
+//                                drawerPreferencesDialog(requireContext(), null)
+//                            })) {
+//                                Text(stringResource(R.string.pref_nav_drawer_items_title), color = textColor, style = MaterialTheme.typography.titleLarge, fontWeight = FontWeight.Bold)
+//                                Text(stringResource(R.string.pref_nav_drawer_items_sum), color = textColor)
+//                            }
                             Row(verticalAlignment = Alignment.CenterVertically, modifier = Modifier.fillMaxWidth().padding(start = 16.dp, top = 10.dp)) {
                                 Column(modifier = Modifier.weight(1f)) {
                                     Text(stringResource(R.string.pref_episode_cover_title), color = textColor, style = MaterialTheme.typography.titleLarge, fontWeight = FontWeight.Bold)
@@ -731,44 +730,43 @@ class PreferenceActivity : AppCompatActivity() {
         enum class DefaultPages(val res: Int) {
             SubscriptionsFragment(R.string.subscriptions_label),
             QueuesFragment(R.string.queue_label),
-            AllEpisodesFragment(R.string.episodes_label),
-            DownloadsFragment(R.string.downloads_label),
+            EpisodesFragment(R.string.episodes_label),
+//            DownloadsFragment(R.string.downloads_label),
             PlaybackHistoryFragment(R.string.playback_history_label),
             AddFeedFragment(R.string.add_feed_label),
             StatisticsFragment(R.string.statistics_label),
             remember(R.string.remember_last_page);
         }
 
-        fun drawerPreferencesDialog(context: Context, callback: Runnable?) {
-            val hiddenItems = hiddenDrawerItems.map { it.trim() }.toMutableSet()
-//        val navTitles = context.resources.getStringArray(R.array.nav_drawer_titles)
-            val navTitles = navMap.values.map { context.resources.getString(it.nameRes).trim() }.toTypedArray()
-            val checked = BooleanArray(navMap.size)
-            for (i in navMap.keys.indices) {
-                val tag = navMap.keys.toList()[i]
-                if (!hiddenItems.contains(tag)) checked[i] = true
-            }
-            val builder = MaterialAlertDialogBuilder(context)
-            builder.setTitle(R.string.drawer_preferences)
-            builder.setMultiChoiceItems(navTitles, checked) { _: DialogInterface?, which: Int, isChecked: Boolean ->
-                if (isChecked) hiddenItems.remove(navMap.keys.toList()[which])
-                else hiddenItems.add((navMap.keys.toList()[which]).trim())
-            }
-            builder.setPositiveButton(R.string.confirm_label) { _: DialogInterface?, _: Int ->
-                hiddenDrawerItems = hiddenItems.toList()
-                if (hiddenItems.contains(defaultPage)) {
-                    for (tag in navMap.keys) {
-                        if (!hiddenItems.contains(tag)) {
-                            defaultPage = tag
-                            break
-                        }
-                    }
-                }
-                callback?.run()
-            }
-            builder.setNegativeButton(R.string.cancel_label, null)
-            builder.create().show()
-        }
+//        fun drawerPreferencesDialog(context: Context, callback: Runnable?) {
+//            val hiddenItems = hiddenDrawerItems.map { it.trim() }.toMutableSet()
+//            val navTitles = navMap.values.map { context.resources.getString(it.nameRes).trim() }.toTypedArray()
+//            val checked = BooleanArray(navMap.size)
+//            for (i in navMap.keys.indices) {
+//                val tag = navMap.keys.toList()[i]
+//                if (!hiddenItems.contains(tag)) checked[i] = true
+//            }
+//            val builder = MaterialAlertDialogBuilder(context)
+//            builder.setTitle(R.string.drawer_preferences)
+//            builder.setMultiChoiceItems(navTitles, checked) { _: DialogInterface?, which: Int, isChecked: Boolean ->
+//                if (isChecked) hiddenItems.remove(navMap.keys.toList()[which])
+//                else hiddenItems.add((navMap.keys.toList()[which]).trim())
+//            }
+//            builder.setPositiveButton(R.string.confirm_label) { _: DialogInterface?, _: Int ->
+//                hiddenDrawerItems = hiddenItems.toList()
+//                if (hiddenItems.contains(defaultPage)) {
+//                    for (tag in navMap.keys) {
+//                        if (!hiddenItems.contains(tag)) {
+//                            defaultPage = tag
+//                            break
+//                        }
+//                    }
+//                }
+//                callback?.run()
+//            }
+//            builder.setNegativeButton(R.string.cancel_label, null)
+//            builder.create().show()
+//        }
 
         private fun showFullNotificationButtonsDialog() {
             val context: Context? = activity
@@ -855,10 +853,10 @@ class PreferenceActivity : AppCompatActivity() {
         @Suppress("EnumEntryName")
         private enum class Prefs(val res: Int, val tag: String) {
             prefSwipeQueue(R.string.queue_label, QueuesFragment.TAG),
-            prefSwipeEpisodes(R.string.episodes_label, AllEpisodesFragment.TAG),
-            prefSwipeDownloads(R.string.downloads_label, DownloadsFragment.TAG),
+            prefSwipeEpisodes(R.string.episodes_label, EpisodesFragment.TAG),
+//            prefSwipeDownloads(R.string.downloads_label, DownloadsFragment.TAG),
             prefSwipeFeed(R.string.individual_subscription, FeedEpisodesFragment.TAG),
-            prefSwipeHistory(R.string.playback_history_label, HistoryFragment.TAG)
+//            prefSwipeHistory(R.string.playback_history_label, HistoryFragment.TAG)
         }
     }
 
@@ -1209,32 +1207,145 @@ class PreferenceActivity : AppCompatActivity() {
     }
 
     class ImportExportPreferencesFragment : PreferenceFragmentCompat() {
+        private val chooseOpmlExportPathLauncher = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result: ActivityResult ->
+            if (result.resultCode != RESULT_OK || result.data == null) return@registerForActivityResult
+            val uri = result.data!!.data!!
+//        val takeFlags = result.data?.flags?.and(Intent.FLAG_GRANT_READ_URI_PERMISSION or Intent.FLAG_GRANT_WRITE_URI_PERMISSION) ?: 0
+//        requireContext().contentResolver.takePersistableUriPermission(uri, takeFlags)
+            exportWithWriter(OpmlWriter(), uri, ExportTypes.OPML)
+        }
 
-        private val chooseOpmlExportPathLauncher = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {
-                result: ActivityResult -> this.chooseOpmlExportPathResult(result) }
+        private val chooseHtmlExportPathLauncher = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result: ActivityResult ->
+            if (result.resultCode != RESULT_OK || result.data == null) return@registerForActivityResult
+            val uri = result.data!!.data!!
+//        val takeFlags = result.data?.flags?.and(Intent.FLAG_GRANT_READ_URI_PERMISSION or Intent.FLAG_GRANT_WRITE_URI_PERMISSION) ?: 0
+//        requireContext().contentResolver.takePersistableUriPermission(uri, takeFlags)
+            exportWithWriter(HtmlWriter(), uri, ExportTypes.HTML)
+        }
 
-        private val chooseHtmlExportPathLauncher = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {
-                result: ActivityResult -> this.chooseHtmlExportPathResult(result) }
+        private val chooseFavoritesExportPathLauncher = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result: ActivityResult ->
+            if (result.resultCode != RESULT_OK || result.data == null) return@registerForActivityResult
+            val uri = result.data!!.data!!
+//        val takeFlags = result.data?.flags?.and(Intent.FLAG_GRANT_READ_URI_PERMISSION or Intent.FLAG_GRANT_WRITE_URI_PERMISSION) ?: 0
+//        requireContext().contentResolver.takePersistableUriPermission(uri, takeFlags)
+            exportWithWriter(FavoritesWriter(), uri, ExportTypes.FAVORITES)
+        }
 
-        private val chooseFavoritesExportPathLauncher = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {
-                result: ActivityResult -> this.chooseFavoritesExportPathResult(result) }
+        private val chooseProgressExportPathLauncher = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result: ActivityResult ->
+            if (result.resultCode != RESULT_OK || result.data == null) return@registerForActivityResult
+            val uri = result.data!!.data!!
+//        val takeFlags = result.data?.flags?.and(Intent.FLAG_GRANT_READ_URI_PERMISSION or Intent.FLAG_GRANT_WRITE_URI_PERMISSION) ?: 0
+//        requireContext().contentResolver.takePersistableUriPermission(uri, takeFlags)
+            exportWithWriter(EpisodesProgressWriter(), uri, ExportTypes.PROGRESS)
+        }
 
-        private val chooseProgressExportPathLauncher = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {
-                result: ActivityResult -> this.chooseProgressExportPathResult(result) }
+        private val restoreProgressLauncher = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result: ActivityResult ->
+            if (result.resultCode != RESULT_OK || result.data?.data == null) return@registerForActivityResult
+            val uri = result.data!!.data
+            uri?.let {
+//            val takeFlags = result.data?.flags?.and(Intent.FLAG_GRANT_READ_URI_PERMISSION) ?: 0
+//            requireContext().contentResolver.takePersistableUriPermission(uri, takeFlags)
+                if (isJsonFile(uri)) {
+                    showProgress = true
+                    lifecycleScope.launch {
+                        try {
+                            withContext(Dispatchers.IO) {
+                                val inputStream: InputStream? = requireContext().contentResolver.openInputStream(uri)
+                                val reader = BufferedReader(InputStreamReader(inputStream))
+                                EpisodeProgressReader.readDocument(reader)
+                                reader.close()
+                            }
+                            withContext(Dispatchers.Main) {
+                                showImportSuccessDialog()
+                                showProgress = false
+                            }
+                        } catch (e: Throwable) { showTransportErrorDialog(e) }
+                    }
+                } else {
+                    val context = requireContext()
+                    val message = context.getString(R.string.import_file_type_toast) + ".json"
+                    showTransportErrorDialog(Throwable(message))
+                }
+            }
+        }
 
-        private val restoreProgressLauncher = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {
-                result: ActivityResult -> this.restoreProgressResult(result) }
+        private val restoreDatabaseLauncher = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result: ActivityResult ->
+            if (result.resultCode != RESULT_OK || result.data == null) return@registerForActivityResult
+            val uri = result.data!!.data
+            uri?.let {
+//            val takeFlags = result.data?.flags?.and(Intent.FLAG_GRANT_READ_URI_PERMISSION) ?: 0
+//            requireContext().contentResolver.takePersistableUriPermission(uri, takeFlags)
+                if (isRealmFile(uri)) {
+                    showProgress = true
+                    lifecycleScope.launch {
+                        try {
+                            withContext(Dispatchers.IO) {
+                                DatabaseTransporter.importBackup(uri, requireContext())
+                            }
+                            withContext(Dispatchers.Main) {
+                                showImportSuccessDialog()
+                                showProgress = false
+                            }
+                        } catch (e: Throwable) { showTransportErrorDialog(e) }
+                    }
+                } else {
+                    val context = requireContext()
+                    val message = context.getString(R.string.import_file_type_toast) + ".realm"
+                    showTransportErrorDialog(Throwable(message))
+                }
+            }
+        }
 
-        private val restoreDatabaseLauncher = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {
-                result: ActivityResult -> this.restoreDatabaseResult(result) }
+        private val backupDatabaseLauncher = registerForActivityResult<String, Uri>(BackupDatabase()) { uri: Uri? ->
+            if (uri == null) return@registerForActivityResult
+            showProgress = true
+            lifecycleScope.launch {
+                try {
+                    withContext(Dispatchers.IO) { DatabaseTransporter.exportToDocument(uri, requireContext()) }
+                    withContext(Dispatchers.Main) {
+                        showExportSuccessSnackbar(uri, "application/x-sqlite3")
+                        showProgress = false
+                    }
+                } catch (e: Throwable) { showTransportErrorDialog(e) }
+            }
+        }
 
-        private val backupDatabaseLauncher = registerForActivityResult<String, Uri>(BackupDatabase()) { uri: Uri? -> this.backupDatabaseResult(uri) }
+        private var showOpmlImportSelectionDialog by mutableStateOf(false)
+        private val readElements = mutableStateListOf<OpmlElement>()
 
-        private val chooseOpmlImportPathLauncher = registerForActivityResult<String, Uri>(ActivityResultContracts.GetContent()) {
-                uri: Uri? -> this.chooseOpmlImportPathResult(uri) }
+        private val chooseOpmlImportPathLauncher = registerForActivityResult<String, Uri>(ActivityResultContracts.GetContent()) { uri: Uri? ->
+            if (uri == null) return@registerForActivityResult
+            Logd(TAG, "chooseOpmlImportPathResult: uri: $uri")
+            OpmlTransporter.startImport(requireContext(), uri) {
+                readElements.addAll(it)
+                Logd(TAG, "readElements: ${readElements.size}")
+            }
+//        showImportSuccessDialog()
+            showOpmlImportSelectionDialog = true
+        }
 
-        private val restorePreferencesLauncher = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {
-                result: ActivityResult -> this.restorePreferencesResult(result) }
+        private val restorePreferencesLauncher = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result: ActivityResult ->
+            if (result.resultCode != RESULT_OK || result.data?.data == null) return@registerForActivityResult
+            val uri = result.data!!.data!!
+//        val takeFlags = result.data?.flags?.and(Intent.FLAG_GRANT_READ_URI_PERMISSION) ?: 0
+//        requireContext().contentResolver.takePersistableUriPermission(uri, takeFlags)
+            if (isPrefDir(uri)) {
+                showProgress = true
+                lifecycleScope.launch {
+                    try {
+                        withContext(Dispatchers.IO) { PreferencesTransporter.importBackup(uri, requireContext()) }
+                        withContext(Dispatchers.Main) {
+                            showImportSuccessDialog()
+                            showProgress = false
+                        }
+                    } catch (e: Throwable) { showTransportErrorDialog(e) }
+                }
+            } else {
+                val context = requireContext()
+                val message = context.getString(R.string.import_directory_toast) + "Podcini-Prefs"
+                showTransportErrorDialog(Throwable(message))
+            }
+        }
 
         private val backupPreferencesLauncher = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {
             if (it.resultCode == RESULT_OK) {
@@ -1243,11 +1354,45 @@ class PreferenceActivity : AppCompatActivity() {
             }
         }
 
-        private val restoreMediaFilesLauncher = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {
-                result: ActivityResult -> this.restoreMediaFilesResult(result) }
+        private val restoreMediaFilesLauncher = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result: ActivityResult ->
+            if (result.resultCode != RESULT_OK || result.data?.data == null) return@registerForActivityResult
+            val uri = result.data!!.data!!
+//        val takeFlags = result.data?.flags?.and(Intent.FLAG_GRANT_READ_URI_PERMISSION) ?: 0
+//        requireContext().contentResolver.takePersistableUriPermission(uri, takeFlags)
+            if (isMediaFilesDir(uri)) {
+                showProgress = true
+                lifecycleScope.launch {
+                    try {
+                        withContext(Dispatchers.IO) { MediaFilesTransporter.importBackup(uri, requireContext()) }
+                        withContext(Dispatchers.Main) {
+                            showImportSuccessDialog()
+                            showProgress = false
+                        }
+                    } catch (e: Throwable) { showTransportErrorDialog(e) }
+                }
+            } else {
+                val context = requireContext()
+                val message = context.getString(R.string.import_directory_toast) + "Podcini-MediaFiles"
+                showTransportErrorDialog(Throwable(message))
+            }
+        }
 
-        private val backupMediaFilesLauncher = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {
-                result: ActivityResult -> this.exportMediaFilesResult(result) }
+        private val backupMediaFilesLauncher = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result: ActivityResult ->
+            if (result.resultCode != RESULT_OK || result.data?.data == null) return@registerForActivityResult
+            val uri = result.data!!.data!!
+//        val takeFlags = result.data?.flags?.and(Intent.FLAG_GRANT_READ_URI_PERMISSION or Intent.FLAG_GRANT_WRITE_URI_PERMISSION) ?: 0
+//        requireContext().contentResolver.takePersistableUriPermission(uri, takeFlags)
+            showProgress = true
+            lifecycleScope.launch {
+                try {
+                    withContext(Dispatchers.IO) { MediaFilesTransporter.exportToDocument(uri, requireContext()) }
+                    withContext(Dispatchers.Main) {
+                        showExportSuccessSnackbar(uri, null)
+                        showProgress = false
+                    }
+                } catch (e: Throwable) { showTransportErrorDialog(e) }
+            }
+        }
 
         private var showProgress by mutableStateOf(false)
 
@@ -1273,7 +1418,7 @@ class PreferenceActivity : AppCompatActivity() {
                         Column(modifier = Modifier.fillMaxWidth().padding(start = 16.dp, end = 16.dp).verticalScroll(scrollState)) {
                             Text(stringResource(R.string.database), color = textColor, style = MaterialTheme.typography.headlineMedium, fontWeight = FontWeight.Bold)
                             Column(modifier = Modifier.fillMaxWidth().padding(start = 16.dp, top = 10.dp).clickable(onClick = {
-                                exportDatabase()
+                                backupDatabaseLauncher.launch(dateStampFilename("PodciniBackup-%s.realm"))
                             })) {
                                 Text(stringResource(R.string.database_export_label), color = textColor, style = MaterialTheme.typography.titleLarge, fontWeight = FontWeight.Bold)
                                 Text(stringResource(R.string.database_export_summary), color = textColor)
@@ -1285,7 +1430,6 @@ class PreferenceActivity : AppCompatActivity() {
                                 Text(stringResource(R.string.database_import_summary), color = textColor)
                             }
                             HorizontalDivider(modifier = Modifier.fillMaxWidth().height(1.dp))
-
                             Text(stringResource(R.string.media_files), color = textColor, style = MaterialTheme.typography.headlineMedium, fontWeight = FontWeight.Bold, modifier = Modifier.padding(top = 10.dp))
                             Column(modifier = Modifier.fillMaxWidth().padding(start = 16.dp, top = 10.dp).clickable(onClick = {
                                 exportMediaFiles()
@@ -1300,7 +1444,6 @@ class PreferenceActivity : AppCompatActivity() {
                                 Text(stringResource(R.string.media_files_import_summary), color = textColor)
                             }
                             HorizontalDivider(modifier = Modifier.fillMaxWidth().height(1.dp))
-
                             Text(stringResource(R.string.preferences), color = textColor, style = MaterialTheme.typography.headlineMedium, fontWeight = FontWeight.Bold, modifier = Modifier.padding(top = 10.dp))
                             Column(modifier = Modifier.fillMaxWidth().padding(start = 16.dp, top = 10.dp).clickable(onClick = {
                                 exportPreferences()
@@ -1315,7 +1458,6 @@ class PreferenceActivity : AppCompatActivity() {
                                 Text(stringResource(R.string.preferences_import_summary), color = textColor)
                             }
                             HorizontalDivider(modifier = Modifier.fillMaxWidth().height(1.dp))
-
                             Text(stringResource(R.string.opml), color = textColor, style = MaterialTheme.typography.headlineMedium, fontWeight = FontWeight.Bold, modifier = Modifier.padding(top = 10.dp))
                             Column(modifier = Modifier.fillMaxWidth().padding(start = 16.dp, top = 10.dp).clickable(onClick = {
                                 openExportPathPicker(ExportTypes.OPML, chooseOpmlExportPathLauncher, OpmlWriter())
@@ -1323,18 +1465,14 @@ class PreferenceActivity : AppCompatActivity() {
                                 Text(stringResource(R.string.opml_export_label), color = textColor, style = MaterialTheme.typography.titleLarge, fontWeight = FontWeight.Bold)
                                 Text(stringResource(R.string.opml_export_summary), color = textColor)
                             }
+                            if (showOpmlImportSelectionDialog) OpmlImportSelectionDialog(readElements) { showOpmlImportSelectionDialog = false }
                             Column(modifier = Modifier.fillMaxWidth().padding(start = 16.dp, top = 10.dp).clickable(onClick = {
-                                try {
-                                    chooseOpmlImportPathLauncher.launch("*/*")
-                                } catch (e: ActivityNotFoundException) {
-                                    Log.e(TAG, "No activity found. Should never happen...")
-                                }
+                                try { chooseOpmlImportPathLauncher.launch("*/*") } catch (e: ActivityNotFoundException) { Log.e(TAG, "No activity found. Should never happen...") }
                             })) {
                                 Text(stringResource(R.string.opml_import_label), color = textColor, style = MaterialTheme.typography.titleLarge, fontWeight = FontWeight.Bold)
                                 Text(stringResource(R.string.opml_import_summary), color = textColor)
                             }
                             HorizontalDivider(modifier = Modifier.fillMaxWidth().height(1.dp))
-
                             Text(stringResource(R.string.progress), color = textColor, style = MaterialTheme.typography.headlineMedium, fontWeight = FontWeight.Bold, modifier = Modifier.padding(top = 10.dp))
                             Column(modifier = Modifier.fillMaxWidth().padding(start = 16.dp, top = 10.dp).clickable(onClick = {
                                 openExportPathPicker(ExportTypes.PROGRESS, chooseProgressExportPathLauncher, EpisodesProgressWriter())
@@ -1349,7 +1487,6 @@ class PreferenceActivity : AppCompatActivity() {
                                 Text(stringResource(R.string.progress_import_summary), color = textColor)
                             }
                             HorizontalDivider(modifier = Modifier.fillMaxWidth().height(1.dp))
-
                             Text(stringResource(R.string.html), color = textColor, style = MaterialTheme.typography.headlineMedium, fontWeight = FontWeight.Bold, modifier = Modifier.padding(top = 10.dp))
                             Column(modifier = Modifier.fillMaxWidth().padding(start = 16.dp, top = 10.dp).clickable(onClick = {
                                 openExportPathPicker(ExportTypes.HTML, chooseHtmlExportPathLauncher, HtmlWriter())
@@ -1392,9 +1529,7 @@ class PreferenceActivity : AppCompatActivity() {
                     val worker = DocumentFileExportWorker(exportWriter, context!!, uri)
                     try {
                         val output = worker.exportFile()
-                        withContext(Dispatchers.Main) {
-                            showExportSuccessSnackbar(output.uri, exportType.contentType)
-                        }
+                        withContext(Dispatchers.Main) { showExportSuccessSnackbar(output.uri, exportType.contentType) }
                     } catch (e: Exception) { showTransportErrorDialog(e)
                     } finally { showProgress = false }
                 }
@@ -1451,10 +1586,6 @@ class PreferenceActivity : AppCompatActivity() {
             builder.show()
         }
 
-        private fun exportDatabase() {
-            backupDatabaseLauncher.launch(dateStampFilename("PodciniBackup-%s.realm"))
-        }
-
         private fun importDatabase() {
             // setup the alert builder
             val builder = MaterialAlertDialogBuilder(requireActivity())
@@ -1519,100 +1650,11 @@ class PreferenceActivity : AppCompatActivity() {
             builder.show()
         }
 
-        private fun chooseProgressExportPathResult(result: ActivityResult) {
-            if (result.resultCode != RESULT_OK || result.data == null) return
-            val uri = result.data!!.data!!
-//        val takeFlags = result.data?.flags?.and(Intent.FLAG_GRANT_READ_URI_PERMISSION or Intent.FLAG_GRANT_WRITE_URI_PERMISSION) ?: 0
-//        requireContext().contentResolver.takePersistableUriPermission(uri, takeFlags)
-            exportWithWriter(EpisodesProgressWriter(), uri, ExportTypes.PROGRESS)
-        }
-
-        private fun chooseOpmlExportPathResult(result: ActivityResult) {
-            if (result.resultCode != RESULT_OK || result.data == null) return
-            val uri = result.data!!.data!!
-//        val takeFlags = result.data?.flags?.and(Intent.FLAG_GRANT_READ_URI_PERMISSION or Intent.FLAG_GRANT_WRITE_URI_PERMISSION) ?: 0
-//        requireContext().contentResolver.takePersistableUriPermission(uri, takeFlags)
-            exportWithWriter(OpmlWriter(), uri, ExportTypes.OPML)
-        }
-
-        private fun chooseHtmlExportPathResult(result: ActivityResult) {
-            if (result.resultCode != RESULT_OK || result.data == null) return
-            val uri = result.data!!.data!!
-//        val takeFlags = result.data?.flags?.and(Intent.FLAG_GRANT_READ_URI_PERMISSION or Intent.FLAG_GRANT_WRITE_URI_PERMISSION) ?: 0
-//        requireContext().contentResolver.takePersistableUriPermission(uri, takeFlags)
-            exportWithWriter(HtmlWriter(), uri, ExportTypes.HTML)
-        }
-
-        private fun chooseFavoritesExportPathResult(result: ActivityResult) {
-            if (result.resultCode != RESULT_OK || result.data == null) return
-            val uri = result.data!!.data!!
-//        val takeFlags = result.data?.flags?.and(Intent.FLAG_GRANT_READ_URI_PERMISSION or Intent.FLAG_GRANT_WRITE_URI_PERMISSION) ?: 0
-//        requireContext().contentResolver.takePersistableUriPermission(uri, takeFlags)
-            exportWithWriter(FavoritesWriter(), uri, ExportTypes.FAVORITES)
-        }
-
-        private fun restoreProgressResult(result: ActivityResult) {
-            if (result.resultCode != RESULT_OK || result.data?.data == null) return
-            val uri = result.data!!.data
-            uri?.let {
-//            val takeFlags = result.data?.flags?.and(Intent.FLAG_GRANT_READ_URI_PERMISSION) ?: 0
-//            requireContext().contentResolver.takePersistableUriPermission(uri, takeFlags)
-                if (isJsonFile(uri)) {
-                    showProgress = true
-                    lifecycleScope.launch {
-                        try {
-                            withContext(Dispatchers.IO) {
-                                val inputStream: InputStream? = requireContext().contentResolver.openInputStream(uri)
-                                val reader = BufferedReader(InputStreamReader(inputStream))
-                                EpisodeProgressReader.readDocument(reader)
-                                reader.close()
-                            }
-                            withContext(Dispatchers.Main) {
-                                showImportSuccessDialog()
-                                showProgress = false
-                            }
-                        } catch (e: Throwable) { showTransportErrorDialog(e) }
-                    }
-                } else {
-                    val context = requireContext()
-                    val message = context.getString(R.string.import_file_type_toast) + ".json"
-                    showTransportErrorDialog(Throwable(message))
-                }
-            }
-        }
-
         private fun isJsonFile(uri: Uri): Boolean {
             val fileName = uri.lastPathSegment ?: return false
             return fileName.endsWith(".json", ignoreCase = true)
         }
 
-        private fun restoreDatabaseResult(result: ActivityResult) {
-            if (result.resultCode != RESULT_OK || result.data == null) return
-            val uri = result.data!!.data
-            uri?.let {
-//            val takeFlags = result.data?.flags?.and(Intent.FLAG_GRANT_READ_URI_PERMISSION) ?: 0
-//            requireContext().contentResolver.takePersistableUriPermission(uri, takeFlags)
-                if (isRealmFile(uri)) {
-                    showProgress = true
-                    lifecycleScope.launch {
-                        try {
-                            withContext(Dispatchers.IO) {
-                                DatabaseTransporter.importBackup(uri, requireContext())
-                            }
-                            withContext(Dispatchers.Main) {
-                                showImportSuccessDialog()
-                                showProgress = false
-                            }
-                        } catch (e: Throwable) { showTransportErrorDialog(e) }
-                    }
-                } else {
-                    val context = requireContext()
-                    val message = context.getString(R.string.import_file_type_toast) + ".realm"
-                    showTransportErrorDialog(Throwable(message))
-                }
-            }
-        }
-
         private fun isRealmFile(uri: Uri): Boolean {
             val fileName = uri.lastPathSegment ?: return false
             return fileName.endsWith(".realm", ignoreCase = true)
@@ -1628,101 +1670,6 @@ class PreferenceActivity : AppCompatActivity() {
             return fileName.contains("Podcini-MediaFiles", ignoreCase = true)
         }
 
-        private fun restorePreferencesResult(result: ActivityResult) {
-            if (result.resultCode != RESULT_OK || result.data?.data == null) return
-            val uri = result.data!!.data!!
-//        val takeFlags = result.data?.flags?.and(Intent.FLAG_GRANT_READ_URI_PERMISSION) ?: 0
-//        requireContext().contentResolver.takePersistableUriPermission(uri, takeFlags)
-            if (isPrefDir(uri)) {
-                showProgress = true
-                lifecycleScope.launch {
-                    try {
-                        withContext(Dispatchers.IO) {
-                            PreferencesTransporter.importBackup(uri, requireContext())
-                        }
-                        withContext(Dispatchers.Main) {
-                            showImportSuccessDialog()
-                            showProgress = false
-                        }
-                    } catch (e: Throwable) { showTransportErrorDialog(e) }
-                }
-            } else {
-                val context = requireContext()
-                val message = context.getString(R.string.import_directory_toast) + "Podcini-Prefs"
-                showTransportErrorDialog(Throwable(message))
-            }
-        }
-
-        private fun restoreMediaFilesResult(result: ActivityResult) {
-            if (result.resultCode != RESULT_OK || result.data?.data == null) return
-            val uri = result.data!!.data!!
-//        val takeFlags = result.data?.flags?.and(Intent.FLAG_GRANT_READ_URI_PERMISSION) ?: 0
-//        requireContext().contentResolver.takePersistableUriPermission(uri, takeFlags)
-            if (isMediaFilesDir(uri)) {
-                showProgress = true
-                lifecycleScope.launch {
-                    try {
-                        withContext(Dispatchers.IO) {
-                            MediaFilesTransporter.importBackup(uri, requireContext())
-                        }
-                        withContext(Dispatchers.Main) {
-                            showImportSuccessDialog()
-                            showProgress = false
-                        }
-                    } catch (e: Throwable) { showTransportErrorDialog(e) }
-                }
-            } else {
-                val context = requireContext()
-                val message = context.getString(R.string.import_directory_toast) + "Podcini-MediaFiles"
-                showTransportErrorDialog(Throwable(message))
-            }
-        }
-
-        private fun exportMediaFilesResult(result: ActivityResult) {
-            if (result.resultCode != RESULT_OK || result.data?.data == null) return
-            val uri = result.data!!.data!!
-//        val takeFlags = result.data?.flags?.and(Intent.FLAG_GRANT_READ_URI_PERMISSION or Intent.FLAG_GRANT_WRITE_URI_PERMISSION) ?: 0
-//        requireContext().contentResolver.takePersistableUriPermission(uri, takeFlags)
-            showProgress = true
-            lifecycleScope.launch {
-                try {
-                    withContext(Dispatchers.IO) {
-                        MediaFilesTransporter.exportToDocument(uri, requireContext())
-                    }
-                    withContext(Dispatchers.Main) {
-                        showExportSuccessSnackbar(uri, null)
-                        showProgress = false
-                    }
-                } catch (e: Throwable) { showTransportErrorDialog(e) }
-            }
-        }
-
-        private fun backupDatabaseResult(uri: Uri?) {
-            if (uri == null) return
-            showProgress = true
-            lifecycleScope.launch {
-                try {
-                    withContext(Dispatchers.IO) {
-                        DatabaseTransporter.exportToDocument(uri, requireContext())
-                    }
-                    withContext(Dispatchers.Main) {
-                        showExportSuccessSnackbar(uri, "application/x-sqlite3")
-                        showProgress = false
-                    }
-                } catch (e: Throwable) { showTransportErrorDialog(e) }
-            }
-        }
-
-        private fun chooseOpmlImportPathResult(uri: Uri?) {
-            if (uri == null) return
-            Logd(TAG, "chooseOpmlImportPathResult: uri: $uri")
-//        OpmlTransporter.startImport(requireContext(), uri)
-//        showImportSuccessDialog()
-            val intent = Intent(context, OpmlImportActivity::class.java)
-            intent.setData(uri)
-            startActivity(intent)
-        }
-
         private fun openExportPathPicker(exportType: ExportTypes, result: ActivityResultLauncher<Intent>, writer: ExportWriter) {
             val title = dateStampFilename(exportType.outputNameTemplate)
 
@@ -1745,9 +1692,7 @@ class PreferenceActivity : AppCompatActivity() {
 
         private class BackupDatabase : CreateDocument() {
             override fun createIntent(context: Context, input: String): Intent {
-                return super.createIntent(context, input)
-                    .addCategory(Intent.CATEGORY_OPENABLE)
-                    .setType("application/x-sqlite3")
+                return super.createIntent(context, input).addCategory(Intent.CATEGORY_OPENABLE).setType("application/x-sqlite3")
             }
         }
 
@@ -1837,9 +1782,7 @@ class PreferenceActivity : AppCompatActivity() {
                                 }
                             }
                             when {
-//                  for debug version importing release version
                                 BuildConfig.DEBUG && !destName.contains(".debug") -> destName = destName.replace("podcini.R", "podcini.R.debug")
-//                  for release version importing debug version
                                 !BuildConfig.DEBUG && destName.contains(".debug") -> destName = destName.replace(".debug", "")
                             }
                             val destFile = File(sharedPreferencesDir, destName)
@@ -1865,9 +1808,7 @@ class PreferenceActivity : AppCompatActivity() {
                     val mediaDir = context.getExternalFilesDir("media") ?: return
                     val chosenDir = DocumentFile.fromTreeUri(context, uri) ?: throw IOException("Destination directory is not valid")
                     val exportSubDir = chosenDir.createDirectory("Podcini-MediaFiles") ?: throw IOException("Error creating subdirectory Podcini-Prefs")
-                    mediaDir.listFiles()?.forEach { file ->
-                        copyRecursive(context, file, mediaDir, exportSubDir)
-                    }
+                    mediaDir.listFiles()?.forEach { file -> copyRecursive(context, file, mediaDir, exportSubDir) }
                 } catch (e: IOException) {
                     Log.e(TAG, Log.getStackTraceString(e))
                     throw e
@@ -1879,9 +1820,7 @@ class PreferenceActivity : AppCompatActivity() {
                     val dirFiles = srcFile.listFiles()
                     if (!dirFiles.isNullOrEmpty()) {
                         val destDir = destRootDir.findFile(relativePath) ?: destRootDir.createDirectory(relativePath) ?: return
-                        dirFiles.forEach { file ->
-                            copyRecursive(context, file, srcFile, destDir)
-                        }
+                        dirFiles.forEach { file -> copyRecursive(context, file, srcFile, destDir) }
                     }
                 } else {
                     val destFile = destRootDir.createFile("application/octet-stream", relativePath) ?: return
@@ -1907,14 +1846,10 @@ class PreferenceActivity : AppCompatActivity() {
                     feed = nameFeedMap[relativePath] ?: return
                     Logd(TAG, "copyRecursive found feed: ${feed?.title}")
                     nameEpisodeMap.clear()
-                    feed!!.episodes.forEach { e ->
-                        if (!e.title.isNullOrEmpty()) nameEpisodeMap[generateFileName(e.title!!)] = e
-                    }
+                    feed!!.episodes.forEach { e -> if (!e.title.isNullOrEmpty()) nameEpisodeMap[generateFileName(e.title!!)] = e }
                     val destFile = File(destRootDir, relativePath)
                     if (!destFile.exists()) destFile.mkdirs()
-                    srcFile.listFiles().forEach { file ->
-                        copyRecursive(context, file, srcFile, destFile)
-                    }
+                    srcFile.listFiles().forEach { file -> copyRecursive(context, file, srcFile, destFile) }
                 } else {
                     val nameParts = relativePath.split(".")
                     if (nameParts.size < 3) return
@@ -1950,9 +1885,7 @@ class PreferenceActivity : AppCompatActivity() {
             private fun copyStream(inputStream: InputStream, outputStream: OutputStream) {
                 val buffer = ByteArray(1024)
                 var bytesRead: Int
-                while (inputStream.read(buffer).also { bytesRead = it } != -1) {
-                    outputStream.write(buffer, 0, bytesRead)
-                }
+                while (inputStream.read(buffer).also { bytesRead = it } != -1) outputStream.write(buffer, 0, bytesRead)
             }
             @Throws(IOException::class)
             fun importBackup(uri: Uri, context: Context) {
@@ -1963,12 +1896,8 @@ class PreferenceActivity : AppCompatActivity() {
                     val fileList = exportedDir.listFiles()
                     if (fileList.isNotEmpty()) {
                         val feeds = getFeedList()
-                        feeds.forEach { f ->
-                            if (!f.title.isNullOrEmpty()) nameFeedMap[generateFileName(f.title!!)] = f
-                        }
-                        fileList.forEach { file ->
-                            copyRecursive(context, file, exportedDir, mediaDir)
-                        }
+                        feeds.forEach { f -> if (!f.title.isNullOrEmpty()) nameFeedMap[generateFileName(f.title!!)] = f }
+                        fileList.forEach { file -> copyRecursive(context, file, exportedDir, mediaDir) }
                     }
                 } catch (e: IOException) {
                     Log.e(TAG, Log.getStackTraceString(e))
@@ -2094,7 +2023,7 @@ class PreferenceActivity : AppCompatActivity() {
                 val queuedEpisodeActions: MutableList<EpisodeAction> = mutableListOf()
                 val pausedItems = getEpisodes(0, Int.MAX_VALUE, EpisodeFilter(EpisodeFilter.States.paused.name), EpisodeSortOrder.DATE_NEW_OLD)
                 val readItems = getEpisodes(0, Int.MAX_VALUE, EpisodeFilter(EpisodeFilter.States.played.name), EpisodeSortOrder.DATE_NEW_OLD)
-                val favoriteItems = getEpisodes(0, Int.MAX_VALUE, EpisodeFilter(EpisodeFilter.States.favorite.name), EpisodeSortOrder.DATE_NEW_OLD)
+                val favoriteItems = getEpisodes(0, Int.MAX_VALUE, EpisodeFilter(EpisodeFilter.States.superb.name), EpisodeSortOrder.DATE_NEW_OLD)
                 val comItems = mutableSetOf<Episode>()
                 comItems.addAll(pausedItems)
                 comItems.addAll(readItems)
@@ -2153,7 +2082,7 @@ class PreferenceActivity : AppCompatActivity() {
                 val favTemplate = IOUtils.toString(favTemplateStream, UTF_8)
                 val feedTemplateStream = context.assets.open(FEED_TEMPLATE)
                 val feedTemplate = IOUtils.toString(feedTemplateStream, UTF_8)
-                val allFavorites = getEpisodes(0, Int.MAX_VALUE, EpisodeFilter(EpisodeFilter.States.favorite.name), EpisodeSortOrder.DATE_NEW_OLD)
+                val allFavorites = getEpisodes(0, Int.MAX_VALUE, EpisodeFilter(EpisodeFilter.States.superb.name), EpisodeSortOrder.DATE_NEW_OLD)
                 val favoritesByFeed = buildFeedMap(allFavorites)
                 writer!!.append(templateParts[0])
                 for (feedId in favoritesByFeed.keys) {
@@ -2724,7 +2653,6 @@ class PreferenceActivity : AppCompatActivity() {
     }
 
     class SynchronizationPreferencesFragment : PreferenceFragmentCompat() {
-
         var selectedProvider by mutableStateOf(SynchronizationProviderViewData.fromIdentifier(selectedSyncProviderKey))
         var loggedIn by mutableStateOf(isProviderConnected)
 
@@ -3153,7 +3081,7 @@ class PreferenceActivity : AppCompatActivity() {
                     login.isEnabled = false
                     progressBar.visibility = View.VISIBLE
                     txtvError.visibility = View.GONE
-                    val inputManager = requireContext().getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
+                    val inputManager = requireContext().getSystemService(INPUT_METHOD_SERVICE) as InputMethodManager
                     inputManager.hideSoftInputFromWindow(login.windowToken, InputMethodManager.HIDE_NOT_ALWAYS)
 
                     lifecycleScope.launch {
@@ -3442,7 +3370,7 @@ class PreferenceActivity : AppCompatActivity() {
                                     appPrefs.edit().putBoolean(UserPreferences.Prefs.prefShowDownloadReport.name, it).apply()
                                 })
                             }
-                            if (SynchronizationSettings.isProviderConnected) {
+                            if (isProviderConnected) {
                                 Row(verticalAlignment = Alignment.CenterVertically, modifier = Modifier.fillMaxWidth().padding(start = 16.dp, top = 10.dp)) {
                                     Column(modifier = Modifier.weight(1f)) {
                                         Text(stringResource(R.string.notification_channel_sync_error), color = textColor, style = MaterialTheme.typography.titleLarge, fontWeight = FontWeight.Bold)
diff --git a/app/src/main/kotlin/ac/mdiq/podcini/ui/activity/ShareReceiverActivity.kt b/app/src/main/kotlin/ac/mdiq/podcini/ui/activity/ShareReceiverActivity.kt
index d93037cb..12583bda 100644
--- a/app/src/main/kotlin/ac/mdiq/podcini/ui/activity/ShareReceiverActivity.kt
+++ b/app/src/main/kotlin/ac/mdiq/podcini/ui/activity/ShareReceiverActivity.kt
@@ -4,7 +4,7 @@ import ac.mdiq.podcini.R
 import ac.mdiq.podcini.storage.database.RealmDB.realm
 import ac.mdiq.podcini.storage.database.RealmDB.upsertBlk
 import ac.mdiq.podcini.storage.model.ShareLog
-import ac.mdiq.podcini.ui.compose.ConfirmAddYoutubeEpisode
+import ac.mdiq.podcini.ui.compose.ConfirmAddYoutubeEpisode1
 import ac.mdiq.podcini.ui.compose.CustomTheme
 import ac.mdiq.podcini.util.Logd
 import ac.mdiq.vista.extractor.services.youtube.YoutubeParsingHelper.isYoutubeServiceURL
@@ -52,7 +52,7 @@ class ShareReceiverActivity : AppCompatActivity() {
             setContent {
                 val showDialog = remember { mutableStateOf(true) }
                 CustomTheme(this) {
-                    ConfirmAddYoutubeEpisode(listOf(sharedUrl!!), showDialog.value, onDismissRequest = {
+                    ConfirmAddYoutubeEpisode1(listOf(sharedUrl!!), showDialog.value, onDismissRequest = {
                         showDialog.value = false
                         finish()
                     })
diff --git a/app/src/main/kotlin/ac/mdiq/podcini/ui/compose/Composables.kt b/app/src/main/kotlin/ac/mdiq/podcini/ui/compose/Composables.kt
index c909307e..c59d50ba 100644
--- a/app/src/main/kotlin/ac/mdiq/podcini/ui/compose/Composables.kt
+++ b/app/src/main/kotlin/ac/mdiq/podcini/ui/compose/Composables.kt
@@ -1,16 +1,22 @@
 package ac.mdiq.podcini.ui.compose
 
+import ac.mdiq.podcini.R
+import ac.mdiq.podcini.preferences.UserPreferences
+import ac.mdiq.podcini.preferences.UserPreferences.appPrefs
 import androidx.compose.foundation.BorderStroke
 import androidx.compose.foundation.background
 import androidx.compose.foundation.border
+import androidx.compose.foundation.clickable
 import androidx.compose.foundation.layout.*
 import androidx.compose.foundation.shape.RoundedCornerShape
 import androidx.compose.foundation.text.BasicTextField
 import androidx.compose.material3.*
 import androidx.compose.runtime.*
+import androidx.compose.runtime.setValue
 import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.res.stringResource
 import androidx.compose.ui.text.TextStyle
 import androidx.compose.ui.text.font.FontWeight
 import androidx.compose.ui.text.input.TextFieldValue
@@ -165,3 +171,49 @@ fun NonlazyGrid(columns: Int, itemCount: Int, modifier: Modifier = Modifier, con
         }
     }
 }
+
+@Composable
+fun SimpleSwitchDialog(title: String, text: String, onDismissRequest: ()->Unit, callback: (Boolean)-> Unit) {
+    val textColor = MaterialTheme.colorScheme.onSurface
+    var isChecked by remember { mutableStateOf(false) }
+    AlertDialog(onDismissRequest = { onDismissRequest() },
+        title = { Text(title, style = MaterialTheme.typography.titleLarge) },
+        text = {
+            Row(verticalAlignment = Alignment.CenterVertically, modifier = Modifier.fillMaxWidth().padding(start = 16.dp, top = 10.dp)) {
+                Text(text, color = textColor, style = MaterialTheme.typography.titleLarge, fontWeight = FontWeight.Bold, modifier = Modifier.weight(1f))
+                Switch(checked = isChecked, onCheckedChange = { isChecked = it })
+            }
+        },
+        confirmButton = {
+            TextButton(onClick = {
+                callback(isChecked)
+                onDismissRequest()
+            }) { Text(text = "OK") }
+        },
+        dismissButton = { TextButton(onClick = { onDismissRequest() }) { Text(text = "Cancel") } }
+    )
+}
+
+@Composable
+fun TitleSummaryColumn(titleRes: Int, summaryRes: Int, callback: ()-> Unit) {
+    val textColor = MaterialTheme.colorScheme.onSurface
+    Column(modifier = Modifier.fillMaxWidth().padding(start = 16.dp, top = 10.dp).clickable(onClick = { callback() })) {
+        Text(stringResource(titleRes), color = textColor, style = MaterialTheme.typography.titleLarge, fontWeight = FontWeight.Bold)
+        Text(stringResource(summaryRes), color = textColor)
+    }
+}
+
+@Composable
+fun TitleSummarySwitchRow(titleRes: Int, summaryRes: Int, prefName: String) {
+    val textColor = MaterialTheme.colorScheme.onSurface
+    Row(verticalAlignment = Alignment.CenterVertically, modifier = Modifier.fillMaxWidth().padding(start = 16.dp, top = 10.dp)) {
+        Column(modifier = Modifier.weight(1f)) {
+            Text(stringResource(titleRes), color = textColor, style = MaterialTheme.typography.titleLarge, fontWeight = FontWeight.Bold)
+            Text(stringResource(summaryRes), color = textColor)
+        }
+        var isChecked by remember { mutableStateOf(appPrefs.getBoolean(prefName, false)) }
+        Switch(checked = isChecked, onCheckedChange = {
+            isChecked = it
+            appPrefs.edit().putBoolean(prefName, it).apply() })
+    }
+}
\ No newline at end of file
diff --git a/app/src/main/kotlin/ac/mdiq/podcini/ui/compose/EpisodesVM.kt b/app/src/main/kotlin/ac/mdiq/podcini/ui/compose/EpisodesVM.kt
index bd3e90d4..c4054f81 100644
--- a/app/src/main/kotlin/ac/mdiq/podcini/ui/compose/EpisodesVM.kt
+++ b/app/src/main/kotlin/ac/mdiq/podcini/ui/compose/EpisodesVM.kt
@@ -35,6 +35,8 @@ import ac.mdiq.podcini.storage.model.Feed.Companion.MAX_SYNTHETIC_ID
 import ac.mdiq.podcini.storage.model.Feed.Companion.newId
 import ac.mdiq.podcini.storage.utils.DurationConverter.getDurationStringLong
 import ac.mdiq.podcini.storage.database.Episodes.hasAlmostEnded
+import ac.mdiq.podcini.storage.database.Feeds.addToSyndicate
+import ac.mdiq.podcini.storage.database.Feeds.createYTSyndicates
 import ac.mdiq.podcini.storage.utils.ImageResourceUtils
 import ac.mdiq.podcini.ui.actions.EpisodeActionButton
 import ac.mdiq.podcini.ui.actions.NullActionButton
@@ -71,6 +73,7 @@ import androidx.compose.material.icons.Icons
 import androidx.compose.material.icons.filled.AddCircle
 import androidx.compose.material.icons.filled.Edit
 import androidx.compose.material3.*
+import androidx.compose.material3.Text
 import androidx.compose.material3.pulltorefresh.PullToRefreshBox
 import androidx.compose.runtime.*
 import androidx.compose.ui.Alignment
@@ -98,6 +101,7 @@ import androidx.documentfile.provider.DocumentFile
 import coil.compose.AsyncImage
 import coil.request.CachePolicy
 import coil.request.ImageRequest
+import com.skydoves.balloon.textForm
 import io.realm.kotlin.notifications.SingleQueryChange
 import io.realm.kotlin.notifications.UpdatedObject
 import kotlinx.coroutines.*
@@ -341,9 +345,7 @@ fun ShelveDialog(selected: List<Episode>, onDismissRequest: () -> Unit) {
     Dialog(onDismissRequest = onDismissRequest) {
         Surface(shape = RoundedCornerShape(16.dp), border = BorderStroke(1.dp, MaterialTheme.colorScheme.tertiary)) {
             val scrollState = rememberScrollState()
-            Column(modifier = Modifier
-                .verticalScroll(scrollState)
-                .padding(16.dp), verticalArrangement = Arrangement.spacedBy(1.dp)) {
+            Column(modifier = Modifier.verticalScroll(scrollState).padding(16.dp), verticalArrangement = Arrangement.spacedBy(1.dp)) {
                 var removeChecked by remember { mutableStateOf(false) }
                 var toFeed by remember { mutableStateOf<Feed?>(null) }
                 if (synthetics.isNotEmpty()) {
@@ -455,7 +457,7 @@ fun EpisodeLazyColumn(activity: MainActivity, vms: MutableList<EpisodeVM>, feed:
 
     val showConfirmYoutubeDialog = remember { mutableStateOf(false) }
     val youtubeUrls = remember { mutableListOf<String>() }
-    ConfirmAddYoutubeEpisode(youtubeUrls, showConfirmYoutubeDialog.value, onDismissRequest = { showConfirmYoutubeDialog.value = false })
+    ConfirmAddYoutubeEpisode1(youtubeUrls, showConfirmYoutubeDialog.value, onDismissRequest = { showConfirmYoutubeDialog.value = false })
 
     var showChooseRatingDialog by remember { mutableStateOf(false) }
     if (showChooseRatingDialog) ChooseRatingDialog(selected) { showChooseRatingDialog = false }
@@ -581,14 +583,12 @@ fun EpisodeLazyColumn(activity: MainActivity, vms: MutableList<EpisodeVM>, feed:
                         for (e in selected) {
                             Logd(TAG, "downloadUrl: ${e.media?.downloadUrl}")
                             val url = URL(e.media?.downloadUrl ?: "")
-                            if ((isYoutubeURL(url) && url.path.startsWith("/watch")) || isYoutubeServiceURL(url)) {
+                            if ((isYoutubeURL(url) && url.path.startsWith("/watch")) || isYoutubeServiceURL(url))
                                 youtubeUrls.add(e.media!!.downloadUrl!!)
-                            } else addToMiscSyndicate(e)
+                            else addToMiscSyndicate(e)
                         }
                         Logd(TAG, "youtubeUrls: ${youtubeUrls.size}")
-                        withContext(Dispatchers.Main) {
-                            showConfirmYoutubeDialog.value = youtubeUrls.isNotEmpty()
-                        }
+                        withContext(Dispatchers.Main) { showConfirmYoutubeDialog.value = youtubeUrls.isNotEmpty() }
                     }
                 }, verticalAlignment = Alignment.CenterVertically) {
                     Icon(Icons.Filled.AddCircle, "Reserve episodes")
@@ -899,32 +899,56 @@ fun EpisodeLazyColumn(activity: MainActivity, vms: MutableList<EpisodeVM>, feed:
 }
 
 @Composable
-fun ConfirmAddYoutubeEpisode(sharedUrls: List<String>, showDialog: Boolean, onDismissRequest: () -> Unit) {
+fun ConfirmAddYoutubeEpisode1(sharedUrls: List<String>, showDialog: Boolean, onDismissRequest: () -> Unit) {
     val TAG = "confirmAddEpisode"
     var showToast by remember { mutableStateOf(false) }
     var toastMassege by remember { mutableStateOf("")}
     if (showToast) CustomToast(message = toastMassege, onDismiss = { showToast = false })
 
     if (showDialog) {
+        val YTSyndMap = remember { mutableStateMapOf<Int, Boolean>() }
+        val synthetics = remember { realm.query(Feed::class).query("id >= 1 && id <= 1000").find().toMutableStateList() }
         Dialog(onDismissRequest = { onDismissRequest() }) {
-            Card(modifier = Modifier.wrapContentSize(align = Alignment.Center).padding(16.dp), shape = RoundedCornerShape(16.dp), border = BorderStroke(1.dp, MaterialTheme.colorScheme.tertiary)) {
-                Column(modifier = Modifier.padding(16.dp), verticalArrangement = Arrangement.Center) {
-                    var audioOnly by remember { mutableStateOf(false) }
-                    Row(Modifier.fillMaxWidth()) {
-                        Checkbox(checked = audioOnly, onCheckedChange = { audioOnly = it })
-                        Text(text = stringResource(R.string.pref_video_mode_audio_only), style = MaterialTheme.typography.bodyLarge.merge())
+            val textColor = MaterialTheme.colorScheme.onSurface
+            Card(modifier = Modifier.height(350.dp).padding(16.dp), shape = RoundedCornerShape(16.dp), border = BorderStroke(1.dp, MaterialTheme.colorScheme.tertiary)) {
+                var toFeed by remember { mutableStateOf<Feed?>(null) }
+                var showComfirmButton by remember { mutableStateOf(toFeed != null) }
+                Column(modifier = Modifier.fillMaxWidth()) {
+                    Text(stringResource(R.string.add_to_feed), color = textColor, style = MaterialTheme.typography.headlineSmall, fontWeight = FontWeight.Bold)
+                    if (YTSyndMap.size < 4) {
+                        Button(onClick = {
+                            createYTSyndicates()
+                            synthetics.clear()
+                            synthetics.addAll(realm.query(Feed::class).query("id >= 1 && id <= 1000").find())
+                        }) { Text(stringResource(R.string.create_YT_syndicates)) }
                     }
-                    var showComfirmButton by remember { mutableStateOf(true) }
+                    if (synthetics.isNotEmpty()) {
+                        LazyColumn(modifier = Modifier.weight(1f).padding(start = 10.dp, end = 10.dp), verticalArrangement = Arrangement.Center) {
+                            items(synthetics.size) { index ->
+                                val f = synthetics[index]
+                                if (f.id <= 4) YTSyndMap[f.id.toInt()] = true
+                                Row(verticalAlignment = Alignment.CenterVertically) {
+                                    RadioButton(selected = toFeed == f, onClick = {
+                                        toFeed = f
+                                        showComfirmButton = true
+                                    })
+                                    Text(f.title ?: "No title", color = textColor)
+                                }
+                            }
+                        }
+                    } else Text(text = stringResource(R.string.create_synthetic_first_note), color = textColor)
+                    var showProgress by remember { mutableStateOf(false) }
                     if (showComfirmButton) {
                         Button(onClick = {
                             showComfirmButton = false
+                            showProgress = true
                             CoroutineScope(Dispatchers.IO).launch {
                                 for (url in sharedUrls) {
                                     val log = realm.query(ShareLog::class).query("url == $0", url).first().find()
                                     try {
                                         val info = StreamInfo.getInfo(Vista.getService(0), url)
                                         val episode = episodeFromStreamInfo(info)
-                                        val status = addToYoutubeSyndicate(episode, !audioOnly)
+                                        val status = addToSyndicate(episode, toFeed!!)
                                         if (log != null) upsert(log) {
                                             it.title = episode.title
                                             it.status = status
@@ -932,14 +956,15 @@ fun ConfirmAddYoutubeEpisode(sharedUrls: List<String>, showDialog: Boolean, onDi
                                     } catch (e: Throwable) {
                                         toastMassege = "Receive share error: ${e.message}"
                                         Log.e(TAG, toastMassege)
-                                        if (log != null) upsert(log) { it.details = e.message?: "error" }
+                                        if (log != null) upsert(log) { it.details = e.message ?: "error" }
                                         withContext(Dispatchers.Main) { showToast = true }
                                     }
                                 }
                                 withContext(Dispatchers.Main) { onDismissRequest() }
                             }
                         }) { Text("Confirm") }
-                    } else CircularProgressIndicator(progress = { 0.6f }, strokeWidth = 4.dp, modifier = Modifier.padding(start = 20.dp, end = 20.dp).width(30.dp).height(30.dp))
+                    }
+                    if (showProgress) CircularProgressIndicator(progress = { 0.6f }, strokeWidth = 4.dp, modifier = Modifier.padding(start = 40.dp, end = 40.dp).width(30.dp).height(30.dp))
                 }
             }
         }
diff --git a/app/src/main/kotlin/ac/mdiq/podcini/ui/compose/Feeds.kt b/app/src/main/kotlin/ac/mdiq/podcini/ui/compose/Feeds.kt
index 4a5ad136..0e244f84 100644
--- a/app/src/main/kotlin/ac/mdiq/podcini/ui/compose/Feeds.kt
+++ b/app/src/main/kotlin/ac/mdiq/podcini/ui/compose/Feeds.kt
@@ -2,6 +2,7 @@ package ac.mdiq.podcini.ui.compose
 
 import ac.mdiq.podcini.R
 import ac.mdiq.podcini.net.feed.FeedBuilder
+import ac.mdiq.podcini.net.feed.FeedUpdateManager.runOnce
 import ac.mdiq.podcini.net.feed.searcher.PodcastSearchResult
 import ac.mdiq.podcini.playback.base.InTheatre.curEpisode
 import ac.mdiq.podcini.playback.base.InTheatre.curMedia
@@ -10,19 +11,19 @@ import ac.mdiq.podcini.playback.base.MediaPlayerBase.Companion.prefPlaybackSpeed
 import ac.mdiq.podcini.playback.base.VideoMode
 import ac.mdiq.podcini.playback.service.PlaybackService.Companion.curSpeedFB
 import ac.mdiq.podcini.playback.service.PlaybackService.Companion.playbackService
+import ac.mdiq.podcini.preferences.OpmlTransporter
 import ac.mdiq.podcini.preferences.UserPreferences
 import ac.mdiq.podcini.preferences.UserPreferences.appPrefs
 import ac.mdiq.podcini.preferences.UserPreferences.isSkipSilence
 import ac.mdiq.podcini.storage.database.Feeds.buildTags
 import ac.mdiq.podcini.storage.database.Feeds.createSynthetic
 import ac.mdiq.podcini.storage.database.Feeds.deleteFeedSync
-import ac.mdiq.podcini.storage.database.Feeds.getFeedList
 import ac.mdiq.podcini.storage.database.Feeds.getTags
+import ac.mdiq.podcini.storage.database.Feeds.updateFeed
 import ac.mdiq.podcini.storage.database.RealmDB.realm
 import ac.mdiq.podcini.storage.database.RealmDB.upsert
 import ac.mdiq.podcini.storage.database.RealmDB.upsertBlk
 import ac.mdiq.podcini.storage.model.*
-import ac.mdiq.podcini.storage.model.Feed.Companion.MAX_SYNTHETIC_ID
 import ac.mdiq.podcini.ui.activity.MainActivity
 import ac.mdiq.podcini.ui.fragment.FeedEpisodesFragment
 import ac.mdiq.podcini.ui.fragment.OnlineFeedFragment
@@ -33,8 +34,11 @@ import ac.mdiq.podcini.util.MiscFormatter
 import ac.mdiq.podcini.util.MiscFormatter.localDateTimeString
 import android.util.Log
 import android.view.Gravity
+import android.widget.Toast
 import androidx.compose.foundation.*
 import androidx.compose.foundation.layout.*
+import androidx.compose.foundation.lazy.LazyColumn
+import androidx.compose.foundation.lazy.itemsIndexed
 import androidx.compose.foundation.shape.RoundedCornerShape
 import androidx.compose.foundation.text.BasicTextField
 import androidx.compose.foundation.text.KeyboardActions
@@ -44,6 +48,7 @@ import androidx.compose.material.icons.filled.Add
 import androidx.compose.material.icons.filled.Close
 import androidx.compose.material3.*
 import androidx.compose.runtime.*
+import androidx.compose.runtime.snapshots.SnapshotStateList
 import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.draw.alpha
@@ -69,6 +74,7 @@ import coil.request.ImageRequest
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.Dispatchers
 import kotlinx.coroutines.launch
+import kotlinx.coroutines.withContext
 import org.json.JSONArray
 import org.json.JSONException
 import java.text.DecimalFormat
@@ -589,3 +595,58 @@ fun PlaybackSpeedFullDialog(settingCode: BooleanArray, indexDefault: Int, maxSpe
         }
     }
 }
+
+@Composable
+fun OpmlImportSelectionDialog(readElements: SnapshotStateList<OpmlTransporter.OpmlElement>, onDismissRequest: () -> Unit) {
+    val context = LocalContext.current
+    val selectedItems = remember {  mutableStateMapOf<Int, Boolean>() }
+    AlertDialog(onDismissRequest = { onDismissRequest() },
+        title = { Text("Import OPML file") },
+        text = {
+            var isSelectAllChecked by remember { mutableStateOf(false) }
+            Column(modifier = Modifier.fillMaxSize()) {
+                Row(modifier = Modifier.fillMaxWidth().padding(8.dp), verticalAlignment = Alignment.CenterVertically) {
+                    Text(text = "Select/Deselect All", modifier = Modifier.weight(1f))
+                    Checkbox(checked = isSelectAllChecked, onCheckedChange = { isChecked ->
+                        isSelectAllChecked = isChecked
+                        readElements.forEachIndexed { index, _ -> selectedItems.put(index, isChecked) }
+                    })
+                }
+                LazyColumn(modifier = Modifier.fillMaxSize()) {
+                    itemsIndexed(readElements) { index, item ->
+                        Row(modifier = Modifier.fillMaxWidth().padding(start = 8.dp, end = 8.dp), verticalAlignment = Alignment.CenterVertically) {
+                            Text(text = item.text?:"", modifier = Modifier.weight(1f))
+                            Checkbox(checked = selectedItems[index]?: false, onCheckedChange = { checked -> selectedItems.put(index, checked) })
+                        }
+                    }
+                }
+            }
+        },
+        confirmButton = {
+            Button(onClick = {
+                Logd("OpmlImportSelectionDialog", "checked: $selectedItems")
+                CoroutineScope(Dispatchers.IO).launch {
+                    try {
+                        withContext(Dispatchers.IO) {
+                            if (readElements.isNotEmpty()) {
+                                for (i in selectedItems.keys) {
+                                    if (selectedItems[i] != true) continue
+                                    val element = readElements[i]
+                                    val feed = Feed(element.xmlUrl, null, if (element.text != null) element.text else "Unknown podcast")
+                                    feed.episodes.clear()
+                                    updateFeed(context, feed, false)
+                                }
+                                runOnce(context)
+                            }
+                        }
+                    } catch (e: Throwable) {
+                        e.printStackTrace()
+                        Toast.makeText(context, (e.message ?: "Import error"), Toast.LENGTH_LONG).show()
+                    }
+                }
+                onDismissRequest()
+            }) { Text("Confirm") }
+        },
+        dismissButton = { Button(onClick = { onDismissRequest() }) { Text("Dismiss") } }
+    )
+}
\ No newline at end of file
diff --git a/app/src/main/kotlin/ac/mdiq/podcini/ui/fragment/AllEpisodesFragment.kt b/app/src/main/kotlin/ac/mdiq/podcini/ui/fragment/AllEpisodesFragment.kt
deleted file mode 100644
index 95b6ec83..00000000
--- a/app/src/main/kotlin/ac/mdiq/podcini/ui/fragment/AllEpisodesFragment.kt
+++ /dev/null
@@ -1,112 +0,0 @@
-package ac.mdiq.podcini.ui.fragment
-
-import ac.mdiq.podcini.R
-import ac.mdiq.podcini.preferences.UserPreferences
-import ac.mdiq.podcini.preferences.UserPreferences.appPrefs
-import ac.mdiq.podcini.storage.database.Episodes.getEpisodes
-import ac.mdiq.podcini.storage.database.Episodes.getEpisodesCount
-import ac.mdiq.podcini.storage.model.Episode
-import ac.mdiq.podcini.storage.model.EpisodeFilter
-import ac.mdiq.podcini.storage.model.EpisodeSortOrder
-import ac.mdiq.podcini.util.Logd
-import android.os.Bundle
-import android.view.LayoutInflater
-import android.view.MenuItem
-import android.view.View
-import android.view.ViewGroup
-import org.apache.commons.lang3.StringUtils
-
-
-class AllEpisodesFragment : BaseEpisodesFragment() {
-    private var allEpisodes: List<Episode> = listOf()
-
-    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
-        val root = super.onCreateView(inflater, container, savedInstanceState)
-        Logd(TAG, "fragment onCreateView")
-
-        toolbar.inflateMenu(R.menu.episodes)
-        toolbar.setTitle(R.string.episodes_label)
-        sortOrder = allEpisodesSortOrder ?: EpisodeSortOrder.DATE_NEW_OLD
-        updateToolbar()
-//        txtvInformation.setOnClickListener {
-//            AllEpisodesFilterDialog.newInstance(getFilter()).show(childFragmentManager, null)
-//        }
-        return root
-    }
-
-    override fun onDestroyView() {
-        allEpisodes = listOf()
-        super.onDestroyView()
-    }
-
-    private var loadItemsRunning = false
-    override fun loadData(): List<Episode> {
-        val filter = getFilter()
-        if (!loadItemsRunning) {
-            loadItemsRunning = true
-            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
-    }
-
-    override fun loadTotalItemCount(): Int {
-        return getEpisodesCount(getFilter())
-    }
-
-    override fun getFilter(): EpisodeFilter {
-        return EpisodeFilter(prefFilterAllEpisodes)
-    }
-
-    override fun getPrefName(): String {
-        return PREF_NAME
-    }
-
-     override fun onMenuItemClick(item: MenuItem): Boolean {
-        if (super.onOptionsItemSelected(item)) return true
-
-        when (item.itemId) {
-            R.id.filter_items -> showFilterDialog = true
-            R.id.episodes_sort -> showSortDialog = true
-            else -> return false
-        }
-        return true
-    }
-
-    override fun updateToolbar() {
-        swipeActions.setFilter(getFilter())
-        var info = "${episodes.size} episodes"
-        if (getFilter().properties.isNotEmpty()) info += " - ${getString(R.string.filtered_label)}"
-        infoBarText.value = info
-    }
-
-    override fun onFilterChanged(filterValues: Set<String>) {
-        prefFilterAllEpisodes = StringUtils.join(filterValues, ",")
-        page = 1
-        loadItems()
-    }
-
-    override fun onSort(order: EpisodeSortOrder) {
-        allEpisodesSortOrder = order
-        page = 1
-        loadItems()
-    }
-
-    companion object {
-        val TAG = AllEpisodesFragment::class.simpleName ?: "Anonymous"
-        const val PREF_NAME: String = "PrefAllEpisodesFragment"
-        var allEpisodesSortOrder: EpisodeSortOrder?
-            get() = EpisodeSortOrder.fromCodeString(appPrefs.getString(UserPreferences.Prefs.prefEpisodesSort.name, "" + EpisodeSortOrder.DATE_NEW_OLD.code))
-            set(s) {
-                appPrefs.edit().putString(UserPreferences.Prefs.prefEpisodesSort.name, "" + s!!.code).apply()
-            }
-        var prefFilterAllEpisodes: String
-            get() = appPrefs.getString(UserPreferences.Prefs.prefEpisodesFilter.name, "")?:""
-            set(filter) {
-                appPrefs.edit().putString(UserPreferences.Prefs.prefEpisodesFilter.name, filter).apply()
-            }
-    }
-}
diff --git a/app/src/main/kotlin/ac/mdiq/podcini/ui/fragment/BaseEpisodesFragment.kt b/app/src/main/kotlin/ac/mdiq/podcini/ui/fragment/BaseEpisodesFragment.kt
index f3713e31..e6354d85 100644
--- a/app/src/main/kotlin/ac/mdiq/podcini/ui/fragment/BaseEpisodesFragment.kt
+++ b/app/src/main/kotlin/ac/mdiq/podcini/ui/fragment/BaseEpisodesFragment.kt
@@ -7,6 +7,7 @@ import ac.mdiq.podcini.storage.database.Episodes
 import ac.mdiq.podcini.storage.model.Episode
 import ac.mdiq.podcini.storage.model.EpisodeFilter
 import ac.mdiq.podcini.storage.model.EpisodeSortOrder
+import ac.mdiq.podcini.ui.actions.EpisodeActionButton
 import ac.mdiq.podcini.ui.actions.SwipeAction
 import ac.mdiq.podcini.ui.actions.SwipeActions
 import ac.mdiq.podcini.ui.actions.SwipeActions.NoActionSwipeAction
@@ -24,7 +25,6 @@ import androidx.compose.runtime.getValue
 import androidx.compose.runtime.mutableStateListOf
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.setValue
-import androidx.core.util.Pair
 import androidx.fragment.app.Fragment
 import androidx.lifecycle.lifecycleScope
 import com.google.android.material.appbar.MaterialToolbar
@@ -37,13 +37,11 @@ import kotlinx.coroutines.withContext
 abstract class BaseEpisodesFragment : Fragment(), Toolbar.OnMenuItemClickListener {
     val TAG = this::class.simpleName ?: "Anonymous"
 
-    @JvmField
-    protected var page: Int = 1
-    private var displayUpArrow = false
-
     var _binding: ComposeFragmentBinding? = null
     protected val binding get() = _binding!!
 
+    private var displayUpArrow = false
+
     protected var infoBarText = mutableStateOf("")
     private var leftActionState = mutableStateOf<SwipeAction>(NoActionSwipeAction())
     private var rightActionState = mutableStateOf<SwipeAction>(NoActionSwipeAction())
@@ -52,11 +50,13 @@ abstract class BaseEpisodesFragment : Fragment(), Toolbar.OnMenuItemClickListene
     lateinit var swipeActions: SwipeActions
 
     val episodes = mutableListOf<Episode>()
-    private val vms = mutableStateListOf<EpisodeVM>()
+    protected val vms = mutableStateListOf<EpisodeVM>()
     var showFilterDialog by mutableStateOf(false)
     var showSortDialog by mutableStateOf(false)
     var sortOrder by mutableStateOf(EpisodeSortOrder.DATE_NEW_OLD)
 
+    var actionButtonToPass by mutableStateOf<((Episode) -> EpisodeActionButton)?>(null)
+
     override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
         super.onCreateView(inflater, container, savedInstanceState)
 
@@ -65,24 +65,17 @@ abstract class BaseEpisodesFragment : Fragment(), Toolbar.OnMenuItemClickListene
 
         toolbar = binding.toolbar
         toolbar.setOnMenuItemClickListener(this)
-//        toolbar.setOnLongClickListener {
-//            recyclerView.scrollToPosition(5)
-//            recyclerView.post { recyclerView.smoothScrollToPosition(0) }
-//            false
-//        }
         displayUpArrow = parentFragmentManager.backStackEntryCount != 0
         if (savedInstanceState != null) displayUpArrow = savedInstanceState.getBoolean(KEY_UP_ARROW)
 
         (activity as MainActivity).setupToolbarToggle(toolbar, displayUpArrow)
 
-//        recyclerView.setRecycledViewPool((activity as MainActivity).recycledViewPool)
-//        recyclerView.addOnScrollListener(LiftOnScrollListener(binding.appbar))
-
         swipeActions = SwipeActions(this, TAG)
         lifecycle.addObserver(swipeActions)
         binding.mainView.setContent {
             CustomTheme(requireContext()) {
-                if (showFilterDialog) EpisodesFilterDialog(filter = getFilter(), onDismissRequest = { showFilterDialog = false } ) { onFilterChanged(it) }
+                if (showFilterDialog) EpisodesFilterDialog(filter = getFilter(), filtersDisabled = filtersDisabled(),
+                    onDismissRequest = { showFilterDialog = false } ) { onFilterChanged(it) }
                 if (showSortDialog) EpisodeSortDialog(initOrder = sortOrder, onDismissRequest = {showSortDialog = false}) { order, _ -> onSort(order) }
 
                 Column {
@@ -91,24 +84,29 @@ abstract class BaseEpisodesFragment : Fragment(), Toolbar.OnMenuItemClickListene
                         activity as MainActivity, vms = vms,
                         leftSwipeCB = {
                             if (leftActionState.value is NoActionSwipeAction) swipeActions.showDialog()
-                            else leftActionState.value.performAction(it, this@BaseEpisodesFragment, swipeActions.filter ?: EpisodeFilter())
+                            else leftActionState.value.performAction(it, this@BaseEpisodesFragment)
                         },
                         rightSwipeCB = {
                             if (rightActionState.value is NoActionSwipeAction) swipeActions.showDialog()
-                            else rightActionState.value.performAction(it, this@BaseEpisodesFragment, swipeActions.filter ?: EpisodeFilter())
+                            else rightActionState.value.performAction(it, this@BaseEpisodesFragment)
                         },
+                        actionButton_ = actionButtonToPass
                     )
                 }
             }
         }
 
-        swipeActions.setFilter(getFilter())
+//        swipeActions.setFilter(getFilter())
         refreshSwipeTelltale()
         return binding.root
     }
 
     open fun onFilterChanged(filterValues: Set<String>) {}
 
+    open fun filtersDisabled(): MutableSet<EpisodeFilter.EpisodesFilterGroup> {
+        return mutableSetOf()
+    }
+
     open fun onSort(order: EpisodeSortOrder) {}
 
     override fun onStart() {
@@ -122,15 +120,8 @@ abstract class BaseEpisodesFragment : Fragment(), Toolbar.OnMenuItemClickListene
         cancelFlowEvents()
     }
 
-//    override fun onPause() {
-//        super.onPause()
-////        recyclerView.saveScrollPosition(getPrefName())
-////        unregisterForContextMenu(recyclerView)
-//    }
-
-    @Deprecated("Deprecated in Java")
-     override fun onOptionsItemSelected(item: MenuItem): Boolean {
-        if (super.onOptionsItemSelected(item)) return true
+     override fun onMenuItemClick(item: MenuItem): Boolean {
+//        if (super.onMenuItemClick(item)) return true
         val itemId = item.itemId
         when (itemId) {
             R.id.action_search -> {
@@ -164,7 +155,6 @@ abstract class BaseEpisodesFragment : Fragment(), Toolbar.OnMenuItemClickListene
 //            if (!event.isCompleted(url)) continue
             val pos: Int = Episodes.indexOfItemWithDownloadUrl(episodes, url)
             if (pos >= 0) {
-//                episodes[pos].downloadState.value = event.map[url]?.state ?: DownloadStatus.State.UNKNOWN.ordinal
                 vms[pos].downloadState = event.map[url]?.state ?: DownloadStatus.State.UNKNOWN.ordinal
             }
         }
@@ -187,6 +177,9 @@ abstract class BaseEpisodesFragment : Fragment(), Toolbar.OnMenuItemClickListene
                 Logd(TAG, "Received event: ${event.TAG}")
                 when (event) {
                     is FlowEvent.SwipeActionsChangedEvent -> refreshSwipeTelltale()
+                    is FlowEvent.EpisodeEvent -> onEpisodeEvent(event)
+                    is FlowEvent.EpisodeMediaEvent -> onEpisodeMediaEvent(event)
+                    is FlowEvent.HistoryEvent -> onHistoryEvent(event)
                     is FlowEvent.FeedListEvent, is FlowEvent.EpisodePlayedEvent, is FlowEvent.PlayerSettingsEvent, is FlowEvent.RatingEvent -> loadItems()
                     else -> {}
                 }
@@ -210,6 +203,12 @@ abstract class BaseEpisodesFragment : Fragment(), Toolbar.OnMenuItemClickListene
         }
     }
 
+    protected open fun onHistoryEvent(event: FlowEvent.HistoryEvent) {}
+
+    protected open fun onEpisodeEvent(event: FlowEvent.EpisodeEvent) { }
+
+    protected open fun onEpisodeMediaEvent(event: FlowEvent.EpisodeMediaEvent) {}
+
     private fun refreshSwipeTelltale() {
         leftActionState.value = swipeActions.actions.left[0]
         rightActionState.value = swipeActions.actions.right[0]
@@ -222,14 +221,13 @@ abstract class BaseEpisodesFragment : Fragment(), Toolbar.OnMenuItemClickListene
             Logd(TAG, "loadItems() called")
             lifecycleScope.launch {
                 try {
-                    val data = withContext(Dispatchers.IO) { Pair(loadData().toMutableList(), loadTotalItemCount()) }
-                    val restoreScrollPosition = episodes.isEmpty()
-                    episodes.clear()
-                    episodes.addAll(data.first)
+                    withContext(Dispatchers.IO) {
+                        episodes.clear()
+                        episodes.addAll(loadData())
+                    }
                     withContext(Dispatchers.Main) {
                         vms.clear()
-                        for (e in data.first) { vms.add(EpisodeVM(e)) }
-//                        if (restoreScrollPosition) recyclerView.restoreScrollPosition(getPrefName())
+                        for (e in episodes) { vms.add(EpisodeVM(e)) }
                         updateToolbar()
                     }
                 } catch (e: Throwable) { Log.e(TAG, Log.getStackTraceString(e))
@@ -240,8 +238,6 @@ abstract class BaseEpisodesFragment : Fragment(), Toolbar.OnMenuItemClickListene
 
     protected abstract fun loadData(): List<Episode>
 
-    protected abstract fun loadTotalItemCount(): Int
-
     open fun getFilter(): EpisodeFilter {
         return EpisodeFilter.unfiltered()
     }
@@ -257,6 +253,5 @@ abstract class BaseEpisodesFragment : Fragment(), Toolbar.OnMenuItemClickListene
 
     companion object {
         private const val KEY_UP_ARROW = "up_arrow"
-        const val EPISODES_PER_PAGE: Int = 50
     }
 }
diff --git a/app/src/main/kotlin/ac/mdiq/podcini/ui/fragment/DownloadsFragment.kt b/app/src/main/kotlin/ac/mdiq/podcini/ui/fragment/DownloadsFragment.kt
deleted file mode 100644
index b55d8d1a..00000000
--- a/app/src/main/kotlin/ac/mdiq/podcini/ui/fragment/DownloadsFragment.kt
+++ /dev/null
@@ -1,426 +0,0 @@
-package ac.mdiq.podcini.ui.fragment
-
-import ac.mdiq.podcini.R
-import ac.mdiq.podcini.databinding.ComposeFragmentBinding
-import ac.mdiq.podcini.net.download.service.DownloadServiceInterface
-import ac.mdiq.podcini.preferences.UserPreferences
-import ac.mdiq.podcini.preferences.UserPreferences.appPrefs
-import ac.mdiq.podcini.storage.database.Episodes
-import ac.mdiq.podcini.storage.database.Episodes.getEpisodes
-import ac.mdiq.podcini.storage.database.RealmDB.realm
-import ac.mdiq.podcini.storage.database.RealmDB.runOnIOScope
-import ac.mdiq.podcini.storage.database.RealmDB.upsert
-import ac.mdiq.podcini.storage.database.RealmDB.upsertBlk
-import ac.mdiq.podcini.storage.model.Episode
-import ac.mdiq.podcini.storage.model.EpisodeFilter
-import ac.mdiq.podcini.storage.model.EpisodeMedia
-import ac.mdiq.podcini.storage.model.EpisodeSortOrder
-import ac.mdiq.podcini.ui.actions.DeleteActionButton
-import ac.mdiq.podcini.ui.actions.SwipeAction
-import ac.mdiq.podcini.ui.actions.SwipeActions
-import ac.mdiq.podcini.ui.actions.SwipeActions.NoActionSwipeAction
-import ac.mdiq.podcini.ui.activity.MainActivity
-import ac.mdiq.podcini.ui.compose.*
-import ac.mdiq.podcini.util.EventFlow
-import ac.mdiq.podcini.util.FlowEvent
-import ac.mdiq.podcini.util.Logd
-import android.os.Bundle
-import android.util.Log
-import android.view.LayoutInflater
-import android.view.MenuItem
-import android.view.View
-import android.view.ViewGroup
-import android.widget.Toast
-import androidx.appcompat.widget.Toolbar
-import androidx.compose.foundation.layout.Column
-import androidx.compose.runtime.getValue
-import androidx.compose.runtime.mutableStateListOf
-import androidx.compose.runtime.mutableStateOf
-import androidx.compose.runtime.setValue
-import androidx.fragment.app.Fragment
-import androidx.lifecycle.lifecycleScope
-import com.google.android.material.appbar.MaterialToolbar
-import kotlinx.coroutines.Dispatchers
-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.*
-
-/**
- * Displays all completed downloads and provides a button to delete them.
- */
- class DownloadsFragment : Fragment(), Toolbar.OnMenuItemClickListener {
-
-    private var _binding: ComposeFragmentBinding? = null
-    private val binding get() = _binding!!
-
-    private var runningDownloads: Set<String> = HashSet()
-    private val episodes = mutableListOf<Episode>()
-    private val vms = mutableStateListOf<EpisodeVM>()
-
-    private var infoBarText = mutableStateOf("")
-    private var leftActionState = mutableStateOf<SwipeAction>(NoActionSwipeAction())
-    private var rightActionState = mutableStateOf<SwipeAction>(NoActionSwipeAction())
-    var showFilterDialog by mutableStateOf(false)
-    var showSortDialog by mutableStateOf(false)
-    var sortOrder by mutableStateOf(EpisodeSortOrder.DATE_NEW_OLD)
-
-    private lateinit var toolbar: MaterialToolbar
-    private lateinit var swipeActions: SwipeActions
-
-    private var displayUpArrow = false
-
-    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
-        _binding = ComposeFragmentBinding.inflate(inflater)
-
-        sortOrder = downloadsSortedOrder ?: EpisodeSortOrder.DATE_NEW_OLD
-
-        Logd(TAG, "fragment onCreateView")
-        toolbar = binding.toolbar
-        toolbar.setTitle(R.string.downloads_label)
-        toolbar.inflateMenu(R.menu.downloads_completed)
-        toolbar.setOnMenuItemClickListener(this)
-//        toolbar.setOnLongClickListener {
-////            recyclerView.scrollToPosition(5)
-////            recyclerView.post { recyclerView.smoothScrollToPosition(0) }
-//            false
-//        }
-        displayUpArrow = parentFragmentManager.backStackEntryCount != 0
-        if (savedInstanceState != null) displayUpArrow = savedInstanceState.getBoolean(KEY_UP_ARROW)
-        (activity as MainActivity).setupToolbarToggle(toolbar, displayUpArrow)
-
-        swipeActions = SwipeActions(this, TAG)
-        swipeActions.setFilter(EpisodeFilter(EpisodeFilter.States.downloaded.name))
-        binding.mainView.setContent {
-            CustomTheme(requireContext()) {
-                if (showFilterDialog) EpisodesFilterDialog(filter = getFilter(),
-                    filtersDisabled = mutableSetOf(EpisodeFilter.EpisodesFilterGroup.DOWNLOADED, EpisodeFilter.EpisodesFilterGroup.MEDIA),
-                    onDismissRequest = { showFilterDialog = false } ) {
-//                    EventFlow.postEvent(FlowEvent.DownloadsFilterEvent(it))
-                    val fSet = it.toMutableSet()
-                    fSet.add(EpisodeFilter.States.downloaded.name)
-                    prefFilterDownloads = StringUtils.join(fSet, ",")
-                    Logd(TAG, "onFilterChanged: $prefFilterDownloads")
-                    loadItems()
-                }
-                if (showSortDialog) EpisodeSortDialog(initOrder = sortOrder, onDismissRequest = {showSortDialog = false}) { order, _ ->
-                    downloadsSortedOrder = order
-                    loadItems()
-                }
-
-                Column {
-                    InforBar(infoBarText, leftAction = leftActionState, rightAction = rightActionState, actionConfig = {swipeActions.showDialog()})
-                    EpisodeLazyColumn(activity as MainActivity, vms = vms,
-                        leftSwipeCB = {
-                            if (leftActionState.value is NoActionSwipeAction) swipeActions.showDialog()
-                            else leftActionState.value.performAction(it, this@DownloadsFragment, swipeActions.filter ?: EpisodeFilter())
-                        },
-                        rightSwipeCB = {
-                            if (rightActionState.value is NoActionSwipeAction) swipeActions.showDialog()
-                            else rightActionState.value.performAction(it, this@DownloadsFragment, swipeActions.filter ?: EpisodeFilter())
-                        },
-                        actionButton_ = { DeleteActionButton(it) })
-                }
-            }
-        }
-//        recyclerView.setRecycledViewPool((activity as MainActivity).recycledViewPool)
-//        recyclerView.addOnScrollListener(LiftOnScrollListener(binding.appbar))
-
-        lifecycle.addObserver(swipeActions)
-        refreshSwipeTelltale()
-//        if (arguments != null && requireArguments().getBoolean(ARG_SHOW_LOGS, false)) DownloadLogFragment().show(childFragmentManager, null)
-
-//        addEmptyView()
-        return binding.root
-    }
-
-    override fun onStart() {
-        super.onStart()
-        procFlowEvents()
-        loadItems()
-    }
-
-    override fun onStop() {
-        super.onStop()
-        cancelFlowEvents()
-    }
-
-    override fun onSaveInstanceState(outState: Bundle) {
-        outState.putBoolean(KEY_UP_ARROW, displayUpArrow)
-        super.onSaveInstanceState(outState)
-    }
-
-    override fun onDestroyView() {
-        Logd(TAG, "onDestroyView")
-        _binding = null
-        toolbar.setOnMenuItemClickListener(null)
-        toolbar.setOnLongClickListener(null)
-        episodes.clear()
-        vms.clear()
-        super.onDestroyView()
-    }
-
-     override fun onMenuItemClick(item: MenuItem): Boolean {
-        when (item.itemId) {
-            R.id.filter_items -> showFilterDialog = true
-            R.id.action_search -> (activity as MainActivity).loadChildFragment(SearchFragment.newInstance())
-            R.id.downloads_sort -> showSortDialog = true
-            R.id.reconcile -> reconcile()
-            else -> return false
-        }
-        return true
-    }
-
-    private fun getFilter(): EpisodeFilter {
-        return EpisodeFilter(prefFilterDownloads)
-    }
-
-    private val nameEpisodeMap: MutableMap<String, Episode> = mutableMapOf()
-    private val filesRemoved: MutableList<String> = mutableListOf()
-    private fun reconcile() {
-        runOnIOScope {
-            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) {
-                if (item.media != null ) upsert(item) { it.media!!.episode = it }
-            }
-            nameEpisodeMap.clear()
-            for (e in episodes) {
-                var fileUrl = e.media?.fileUrl ?: continue
-                fileUrl = fileUrl.substring(fileUrl.lastIndexOf('/') + 1)
-                Logd(TAG, "reconcile: fileUrl: $fileUrl")
-                nameEpisodeMap[fileUrl] = e
-            }
-            val mediaDir = requireContext().getExternalFilesDir("media") ?: return@runOnIOScope
-            mediaDir.listFiles()?.forEach { file -> traverse(file, mediaDir) }
-            Logd(TAG, "reconcile: end, episodes missing file: ${nameEpisodeMap.size}")
-            if (nameEpisodeMap.isNotEmpty()) {
-                for (e in nameEpisodeMap.values) {
-                    upsertBlk(e) { it.media?.setfileUrlOrNull(null) }
-                }
-            }
-            loadItems()
-            Logd(TAG, "Episodes reconsiled: ${nameEpisodeMap.size}\nFiles removed: ${filesRemoved.size}")
-            withContext(Dispatchers.Main) {
-                Toast.makeText(requireContext(), "Episodes reconsiled: ${nameEpisodeMap.size}\nFiles removed: ${filesRemoved.size}", Toast.LENGTH_LONG).show()
-            }
-        }
-    }
-
-    private fun traverse(srcFile: File, srcRootDir: File) {
-        val relativePath = srcFile.absolutePath.substring(srcRootDir.absolutePath.length+1)
-        if (srcFile.isDirectory) {
-            Logd(TAG, "traverse folder title: $relativePath")
-            val dirFiles = srcFile.listFiles()
-            dirFiles?.forEach { file -> traverse(file, srcFile) }
-        } else {
-            Logd(TAG, "traverse: $srcFile")
-            val episode = nameEpisodeMap.remove(relativePath)
-            if (episode == null) {
-                Logd(TAG, "traverse: error: episode not exist in map: $relativePath")
-                filesRemoved.add(relativePath)
-                srcFile.delete()
-                return
-            }
-            Logd(TAG, "traverse found episode: ${episode.title}")
-        }
-    }
-
-    private fun onEpisodeDownloadEvent(event: FlowEvent.EpisodeDownloadEvent) {
-        val newRunningDownloads: MutableSet<String> = HashSet()
-        for (url in event.urls) {
-            if (DownloadServiceInterface.get()?.isDownloadingEpisode(url) == true) newRunningDownloads.add(url)
-        }
-        if (newRunningDownloads != runningDownloads) {
-            runningDownloads = newRunningDownloads
-            loadItems()
-            return  // Refreshed anyway
-        }
-//        for (downloadUrl in event.urls) {
-//            val pos = Episodes.indexOfItemWithDownloadUrl(episodes.toList(), downloadUrl)
-//            if (pos >= 0) adapter.notifyItemChangedCompat(pos)
-//        }
-    }
-
-    private var eventSink: Job?     = null
-    private var eventStickySink: Job? = null
-    private fun cancelFlowEvents() {
-        eventSink?.cancel()
-        eventSink = null
-        eventStickySink?.cancel()
-        eventStickySink = null
-    }
-    private fun procFlowEvents() {
-        if (eventSink == null) eventSink = lifecycleScope.launch {
-            EventFlow.events.collectLatest { event ->
-                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()
-                    is FlowEvent.QueueEvent -> loadItems()
-                    is FlowEvent.SwipeActionsChangedEvent -> refreshSwipeTelltale()
-                    is FlowEvent.EpisodeDownloadEvent -> onEpisodeDownloadEvent(event)
-                    else -> {}
-                }
-            }
-        }
-//        if (eventStickySink == null) eventStickySink = lifecycleScope.launch {
-//            EventFlow.stickyEvents.collectLatest { event ->
-//                Logd(TAG, "Received sticky event: ${event.TAG}")
-//                when (event) {
-//                    is FlowEvent.EpisodeDownloadEvent -> onEpisodeDownloadEvent(event)
-//                    else -> {}
-//                }
-//            }
-//        }
-    }
-
-//    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 onEpisodeEvent(event: FlowEvent.EpisodeEvent) {
-//        Logd(TAG, "onEpisodeEvent() called with ${event.TAG}")
-        var i = 0
-        val size: Int = event.episodes.size
-        while (i < size) {
-            val item: Episode = event.episodes[i++]
-            val pos = Episodes.indexOfItemWithId(episodes, item.id)
-            if (pos >= 0) {
-                episodes.removeAt(pos)
-                vms.removeAt(pos)
-                val media = item.media
-                if (media != null && media.downloaded) {
-                    episodes.add(pos, item)
-                    vms.add(pos, EpisodeVM(item))
-                }
-            }
-        }
-//        have to do this as adapter.notifyItemRemoved(pos) when pos == 0 causes crash
-//        if (size > 0) adapter.updateItems(episodes)
-        refreshInfoBar()
-    }
-
-    private fun onEpisodeMediaEvent(event: FlowEvent.EpisodeMediaEvent) {
-//        Logd(TAG, "onEpisodeEvent() called with ${event.TAG}")
-        var i = 0
-        val size: Int = event.episodes.size
-        while (i < size) {
-            val item: Episode = event.episodes[i++]
-            val pos = Episodes.indexOfItemWithId(episodes, item.id)
-            if (pos >= 0) {
-                episodes.removeAt(pos)
-                vms.removeAt(pos)
-                val media = item.media
-                if (media != null && media.downloaded) {
-                    episodes.add(pos, item)
-                    vms.add(pos, EpisodeVM(item))
-                }
-            }
-        }
-//        have to do this as adapter.notifyItemRemoved(pos) when pos == 0 causes crash
-//        if (size > 0) adapter.updateItems(episodes)
-        refreshInfoBar()
-    }
-
-    private fun refreshSwipeTelltale() {
-        leftActionState.value = swipeActions.actions.left[0]
-        rightActionState.value = swipeActions.actions.right[0]
-    }
-
-    private var loadItemsRunning = false
-    private fun loadItems() {
-//        emptyView.hide()
-        Logd(TAG, "loadItems() called")
-        if (!loadItemsRunning) {
-            loadItemsRunning = true
-            lifecycleScope.launch {
-                try {
-                    withContext(Dispatchers.IO) {
-                        val sortOrder: EpisodeSortOrder? = downloadsSortedOrder
-                        val filter = getFilter()
-                        val downloadedItems = getEpisodes(0, Int.MAX_VALUE, filter, sortOrder, false)
-                        if (runningDownloads.isEmpty()) {
-                            episodes.clear()
-                            episodes.addAll(downloadedItems)
-                        } else {
-                            val mediaUrls: MutableList<String> = ArrayList()
-                            for (url in runningDownloads) {
-                                if (Episodes.indexOfItemWithDownloadUrl(downloadedItems, url) != -1) continue
-                                mediaUrls.add(url)
-                            }
-                            val currentDownloads = getEpisdesWithUrl(mediaUrls).toMutableList()
-                            currentDownloads.addAll(downloadedItems)
-                            episodes.clear()
-                            episodes.addAll(currentDownloads)
-                        }
-//                        episodes.retainAll { filter.matchesForQueues(it) }
-                        withContext(Dispatchers.Main) {
-                            vms.clear()
-                            for (e in episodes) vms.add(EpisodeVM(e))
-                        }
-                    }
-                    withContext(Dispatchers.Main) { refreshInfoBar() }
-                } catch (e: Throwable) { Log.e(TAG, Log.getStackTraceString(e))
-                } finally { loadItemsRunning = false }
-            }
-        }
-    }
-
-    private fun getEpisdesWithUrl(urls: List<String>): List<Episode> {
-        Logd(TAG, "getEpisdesWithUrl() called ")
-        if (urls.isEmpty()) return listOf()
-        val episodes_: MutableList<Episode> = mutableListOf()
-        for (url in urls) {
-            val media = realm.query(EpisodeMedia::class).query("downloadUrl == $0", url).first().find() ?: continue
-            val item_ = media.episodeOrFetch()
-            if (item_ != null) episodes_.add(item_)
-        }
-        return realm.copyFromRealm(episodes_)
-    }
-
-    private fun refreshInfoBar() {
-        var info = String.format(Locale.getDefault(), "%d%s", episodes.size, getString(R.string.episodes_suffix))
-        if (episodes.isNotEmpty()) {
-            var sizeMB: Long = 0
-            for (item in episodes) sizeMB += item.media?.size ?: 0
-            info += " • " + (sizeMB / 1000000) + " MB"
-        }
-        Logd(TAG, "refreshInfoBar filter value: ${getFilter().properties.size} ${getFilter().properties.joinToString()}")
-        if (getFilter().properties.size > 1) info += " - ${getString(R.string.filtered_label)}"
-        infoBarText.value = info
-    }
-
-    companion object {
-        val TAG = DownloadsFragment::class.simpleName ?: "Anonymous"
-
-        const val ARG_SHOW_LOGS: String = "show_logs"
-        private const val KEY_UP_ARROW = "up_arrow"
-
-        //    the sort order for the downloads.
-        var downloadsSortedOrder: EpisodeSortOrder?
-            get() {
-                val sortOrderStr = appPrefs.getString(UserPreferences.Prefs.prefDownloadSortedOrder.name, "" + EpisodeSortOrder.DATE_NEW_OLD.code)
-                return EpisodeSortOrder.fromCodeString(sortOrderStr)
-            }
-            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()
-            }
-    }
-}
diff --git a/app/src/main/kotlin/ac/mdiq/podcini/ui/fragment/EpisodesFragment.kt b/app/src/main/kotlin/ac/mdiq/podcini/ui/fragment/EpisodesFragment.kt
new file mode 100644
index 00000000..84adf7ea
--- /dev/null
+++ b/app/src/main/kotlin/ac/mdiq/podcini/ui/fragment/EpisodesFragment.kt
@@ -0,0 +1,354 @@
+package ac.mdiq.podcini.ui.fragment
+
+import ac.mdiq.podcini.R
+import ac.mdiq.podcini.preferences.UserPreferences
+import ac.mdiq.podcini.preferences.UserPreferences.appPrefs
+import ac.mdiq.podcini.storage.database.Episodes
+import ac.mdiq.podcini.storage.database.Episodes.getEpisodes
+import ac.mdiq.podcini.storage.database.RealmDB.realm
+import ac.mdiq.podcini.storage.database.RealmDB.runOnIOScope
+import ac.mdiq.podcini.storage.database.RealmDB.upsert
+import ac.mdiq.podcini.storage.database.RealmDB.upsertBlk
+import ac.mdiq.podcini.storage.model.Episode
+import ac.mdiq.podcini.storage.model.EpisodeFilter
+import ac.mdiq.podcini.storage.model.EpisodeMedia
+import ac.mdiq.podcini.storage.model.EpisodeSortOrder
+import ac.mdiq.podcini.storage.model.EpisodeSortOrder.Companion.getPermutor
+import ac.mdiq.podcini.ui.actions.DeleteActionButton
+import ac.mdiq.podcini.ui.compose.CustomTheme
+import ac.mdiq.podcini.ui.compose.EpisodeVM
+import ac.mdiq.podcini.ui.compose.SpinnerExternalSet
+import ac.mdiq.podcini.ui.dialog.ConfirmationDialog
+import ac.mdiq.podcini.ui.dialog.DatesFilterDialog
+import ac.mdiq.podcini.util.EventFlow
+import ac.mdiq.podcini.util.FlowEvent
+import ac.mdiq.podcini.util.Logd
+import android.content.Context
+import android.content.DialogInterface
+import android.content.SharedPreferences
+import android.os.Bundle
+import android.view.LayoutInflater
+import android.view.MenuItem
+import android.view.View
+import android.view.ViewGroup
+import android.widget.Toast
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableIntStateOf
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.platform.ComposeView
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.Job
+import kotlinx.coroutines.withContext
+import org.apache.commons.lang3.StringUtils
+import java.io.File
+import java.util.*
+import kotlin.math.min
+
+class EpisodesFragment : BaseEpisodesFragment() {
+    val prefs: SharedPreferences by lazy { requireContext().getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE) }
+
+    private val spinnerTexts = QuickAccess.entries.map { it.name }
+    private var curIndex by mutableIntStateOf(0)
+    private lateinit var spinnerView:  ComposeView
+
+    private var startDate : Long = 0L
+    private var endDate : Long = Date().time
+
+    private var episodesSortOrder: EpisodeSortOrder
+        get() = EpisodeSortOrder.fromCodeString(appPrefs.getString(UserPreferences.Prefs.prefEpisodesSort.name, "" + EpisodeSortOrder.DATE_NEW_OLD.code))
+        set(s) {
+            appPrefs.edit().putString(UserPreferences.Prefs.prefEpisodesSort.name, "" + s.code).apply()
+        }
+    private var prefFilterEpisodes: String
+        get() = appPrefs.getString(UserPreferences.Prefs.prefEpisodesFilter.name, "")?:""
+        set(filter) {
+            appPrefs.edit().putString(UserPreferences.Prefs.prefEpisodesFilter.name, filter).apply()
+        }
+    private 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()
+        }
+
+    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
+        val root = super.onCreateView(inflater, container, savedInstanceState)
+        Logd(TAG, "fragment onCreateView")
+
+        curIndex = prefs.getInt("curIndex", 0)
+        spinnerView = ComposeView(requireContext()).apply {
+            setContent {
+                CustomTheme(requireContext()) {
+                    SpinnerExternalSet(items = spinnerTexts, selectedIndex = curIndex) { index: Int ->
+                        Logd(QueuesFragment.Companion.TAG, "Item selected: $index")
+                        curIndex = index
+                        prefs.edit().putInt("curIndex", index).apply()
+                        actionButtonToPass = if (spinnerTexts[curIndex] == QuickAccess.Downloaded.name)  {it -> DeleteActionButton(it) } else null
+                        loadItems()
+                    }
+                }
+            }
+        }
+        toolbar.addView(spinnerView)
+
+        toolbar.inflateMenu(R.menu.episodes)
+        sortOrder = episodesSortOrder
+        updateToolbar()
+        return root
+    }
+
+    /**
+     * Loads the playback history from the database. A FeedItem is in the playback history if playback of the correpsonding episode
+     * has been played ot completed at least once.
+     * @param limit The maximum number of episodes to return.
+     * @return The playback history. The FeedItems are sorted by their media's playbackCompletionDate in descending order.
+     */
+    fun getHistory(offset: Int, limit: Int, start: Long = 0L, end: Long = Date().time,
+                   sortOrder: EpisodeSortOrder = EpisodeSortOrder.PLAYED_DATE_NEW_OLD): List<Episode> {
+        Logd(TAG, "getHistory() called")
+        val medias = realm.query(EpisodeMedia::class).query("(playbackCompletionTime > 0) OR (lastPlayedTime > \$0 AND lastPlayedTime <= \$1)", start, end).find()
+        var episodes: MutableList<Episode> = mutableListOf()
+        for (m in medias) {
+            val item_ = m.episodeOrFetch()
+            if (item_ != null) episodes.add(item_)
+            else Logd(TAG, "getHistory: media has null episode: ${m.id}")
+        }
+        getPermutor(sortOrder).reorder(episodes)
+        if (offset > 0 && episodes.size > offset) episodes = episodes.subList(offset, min(episodes.size, offset+limit))
+        return episodes
+    }
+
+    override fun loadData(): List<Episode> {
+        return when (spinnerTexts[curIndex]) {
+            QuickAccess.New.name -> getEpisodes(0, Int.MAX_VALUE, EpisodeFilter(EpisodeFilter.States.new.name), episodesSortOrder, false)
+            QuickAccess.Planned.name -> getEpisodes(0, Int.MAX_VALUE, EpisodeFilter(EpisodeFilter.States.soon.name, EpisodeFilter.States.later.name), episodesSortOrder, false)
+            QuickAccess.Repeats.name -> getEpisodes(0, Int.MAX_VALUE, EpisodeFilter(EpisodeFilter.States.again.name, EpisodeFilter.States.forever.name), episodesSortOrder, false)
+            QuickAccess.Liked.name -> getEpisodes(0, Int.MAX_VALUE, EpisodeFilter(EpisodeFilter.States.good.name, EpisodeFilter.States.superb.name), episodesSortOrder, false)
+            QuickAccess.Commented.name -> getEpisodes(0, Int.MAX_VALUE, EpisodeFilter(EpisodeFilter.States.has_comments.name), episodesSortOrder, false)
+            QuickAccess.History.name -> getHistory(0, Int.MAX_VALUE, sortOrder = episodesSortOrder).toMutableList()
+            QuickAccess.Downloaded.name -> getEpisodes(0, Int.MAX_VALUE, EpisodeFilter(prefFilterDownloads), episodesSortOrder, false)
+            QuickAccess.All.name -> getEpisodes(0, Int.MAX_VALUE, getFilter(), episodesSortOrder, false)
+            else -> getEpisodes(0, Int.MAX_VALUE, getFilter(), episodesSortOrder, false)
+        }
+    }
+
+    override fun getFilter(): EpisodeFilter {
+        return EpisodeFilter(prefFilterEpisodes)
+    }
+
+    override fun getPrefName(): String {
+        return PREF_NAME
+    }
+
+    var progressing by mutableStateOf(false)
+    override fun updateToolbar() {
+        toolbar.menu.findItem(R.id.clear_new).isVisible = episodes.isNotEmpty() && spinnerTexts[curIndex] == QuickAccess.New.name
+        toolbar.menu.findItem(R.id.filter_items).isVisible = episodes.isNotEmpty() && spinnerTexts[curIndex] == QuickAccess.All.name
+        toolbar.menu.findItem(R.id.clear_history_item).isVisible = episodes.isNotEmpty() && spinnerTexts[curIndex] == QuickAccess.History.name
+        toolbar.menu.findItem(R.id.reconcile).isVisible = episodes.isNotEmpty() && spinnerTexts[curIndex] == QuickAccess.Downloaded.name
+
+        var info = "${episodes.size} episodes"
+        if (spinnerTexts[curIndex] == QuickAccess.All.name && getFilter().properties.isNotEmpty()) info += " - ${getString(R.string.filtered_label)}"
+        else if (spinnerTexts[curIndex] == QuickAccess.Downloaded.name && episodes.isNotEmpty()) {
+            var sizeMB: Long = 0
+            for (item in episodes) sizeMB += item.media?.size ?: 0
+            info += " • " + (sizeMB / 1000000) + " MB"
+        }
+        if (progressing) info += " - ${getString(R.string.progressing_label)}"
+        infoBarText.value = info
+    }
+
+    override fun onMenuItemClick(item: MenuItem): Boolean {
+        if (super.onMenuItemClick(item)) return true
+
+        when (item.itemId) {
+            R.id.filter_items -> {
+                if (spinnerTexts[curIndex] == QuickAccess.History.name) {
+                    val dialog = object: DatesFilterDialog(requireContext(), 0L) {
+                        override fun initParams() {
+                            val calendar = Calendar.getInstance()
+                            calendar.add(Calendar.YEAR, -1) // subtract 1 year
+                            timeFilterFrom = calendar.timeInMillis
+                            showMarkPlayed = false
+                        }
+                        override fun callback(timeFilterFrom: Long, timeFilterTo: Long, includeMarkedAsPlayed: Boolean) {
+                            EventFlow.postEvent(FlowEvent.HistoryEvent(sortOrder, timeFilterFrom, timeFilterTo))
+                        }
+                    }
+                    dialog.show()
+                } else showFilterDialog = true
+            }
+            R.id.episodes_sort -> showSortDialog = true
+            R.id.clear_history_item -> {
+                val conDialog: ConfirmationDialog = object : ConfirmationDialog(requireContext(), R.string.clear_history_label, R.string.clear_playback_history_msg) {
+                    override fun onConfirmButtonPressed(dialog: DialogInterface) {
+                        dialog.dismiss()
+                        clearHistory()
+                    }
+                }
+                conDialog.createNewDialog().show()
+            }
+            R.id.reconcile -> reconcile()
+            R.id.clear_new -> clearNew()
+            else -> return false
+        }
+        return true
+    }
+
+    private fun clearNew() {
+        runOnIOScope {
+            progressing = true
+            for (e in episodes) if (e.isNew) upsert(e) { it.setPlayed(false) }
+            withContext(Dispatchers.Main) {
+                progressing = false
+                Toast.makeText(requireContext(), "History cleared", Toast.LENGTH_LONG).show()
+            }
+            loadItems()
+        }
+    }
+
+    private val nameEpisodeMap: MutableMap<String, Episode> = mutableMapOf()
+    private val filesRemoved: MutableList<String> = mutableListOf()
+    private fun reconcile() {
+        fun traverse(srcFile: File, srcRootDir: File) {
+            val relativePath = srcFile.absolutePath.substring(srcRootDir.absolutePath.length+1)
+            if (srcFile.isDirectory) {
+                Logd(TAG, "traverse folder title: $relativePath")
+                val dirFiles = srcFile.listFiles()
+                dirFiles?.forEach { file -> traverse(file, srcFile) }
+            } else {
+                Logd(TAG, "traverse: $srcFile")
+                val episode = nameEpisodeMap.remove(relativePath)
+                if (episode == null) {
+                    Logd(TAG, "traverse: error: episode not exist in map: $relativePath")
+                    filesRemoved.add(relativePath)
+                    srcFile.delete()
+                    return
+                }
+                Logd(TAG, "traverse found episode: ${episode.title}")
+            }
+        }
+        runOnIOScope {
+            progressing = true
+            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) if (item.media != null) upsert(item) { it.media!!.episode = it }
+            nameEpisodeMap.clear()
+            for (e in episodes) {
+                var fileUrl = e.media?.fileUrl ?: continue
+                fileUrl = fileUrl.substring(fileUrl.lastIndexOf('/') + 1)
+                Logd(TAG, "reconcile: fileUrl: $fileUrl")
+                nameEpisodeMap[fileUrl] = e
+            }
+            val mediaDir = requireContext().getExternalFilesDir("media") ?: return@runOnIOScope
+            mediaDir.listFiles()?.forEach { file -> traverse(file, mediaDir) }
+            Logd(TAG, "reconcile: end, episodes missing file: ${nameEpisodeMap.size}")
+            if (nameEpisodeMap.isNotEmpty()) for (e in nameEpisodeMap.values) upsertBlk(e) { it.media?.setfileUrlOrNull(null) }
+            loadItems()
+            Logd(TAG, "Episodes reconsiled: ${nameEpisodeMap.size}\nFiles removed: ${filesRemoved.size}")
+            withContext(Dispatchers.Main) {
+                progressing = false
+                Toast.makeText(requireContext(), "Episodes reconsiled: ${nameEpisodeMap.size}\nFiles removed: ${filesRemoved.size}", Toast.LENGTH_LONG).show()
+            }
+        }
+    }
+
+    fun clearHistory() : Job {
+        Logd(TAG, "clearHistory called")
+        return runOnIOScope {
+            progressing = true
+            val episodes = realm.query(Episode::class).query("media.playbackCompletionTime > 0 || media.lastPlayedTime > 0").find()
+            for (e in episodes) {
+                upsert(e) {
+                    it.media?.playbackCompletionDate = null
+                    it.media?.lastPlayedTime = 0
+                }
+            }
+            withContext(Dispatchers.Main) {
+                progressing = false
+                Toast.makeText(requireContext(), "History cleared", Toast.LENGTH_LONG).show()
+            }
+            EventFlow.postEvent(FlowEvent.HistoryEvent())
+        }
+    }
+
+    override fun onFilterChanged(filterValues: Set<String>) {
+        if (spinnerTexts[curIndex] == QuickAccess.Downloaded.name || spinnerTexts[curIndex] == QuickAccess.All.name) {
+            val fSet = filterValues.toMutableSet()
+            if (spinnerTexts[curIndex] == QuickAccess.Downloaded.name) fSet.add(EpisodeFilter.States.downloaded.name)
+            prefFilterEpisodes = StringUtils.join(fSet, ",")
+            loadItems()
+        }
+    }
+
+    override fun onSort(order: EpisodeSortOrder) {
+        episodesSortOrder = order
+        loadItems()
+    }
+
+    override fun filtersDisabled(): MutableSet<EpisodeFilter.EpisodesFilterGroup> {
+        return if (spinnerTexts[curIndex] == QuickAccess.Downloaded.name) mutableSetOf(EpisodeFilter.EpisodesFilterGroup.DOWNLOADED, EpisodeFilter.EpisodesFilterGroup.MEDIA)
+        else mutableSetOf()
+    }
+
+    override fun onHistoryEvent(event: FlowEvent.HistoryEvent) {
+        if (spinnerTexts[curIndex] == QuickAccess.History.name) {
+            sortOrder = event.sortOrder
+            if (event.startDate > 0) startDate = event.startDate
+            endDate = event.endDate
+            loadItems()
+            updateToolbar()
+        }
+    }
+
+    override fun onEpisodeEvent(event: FlowEvent.EpisodeEvent) {
+        if (spinnerTexts[curIndex] == QuickAccess.Downloaded.name) {
+            var i = 0
+            val size: Int = event.episodes.size
+            while (i < size) {
+                val item: Episode = event.episodes[i++]
+                val pos = Episodes.indexOfItemWithId(episodes, item.id)
+                if (pos >= 0) {
+                    episodes.removeAt(pos)
+                    vms.removeAt(pos)
+                    val media = item.media
+                    if (media != null && media.downloaded) {
+                        episodes.add(pos, item)
+                        vms.add(pos, EpisodeVM(item))
+                    }
+                }
+            }
+            updateToolbar()
+        }
+    }
+
+    override fun onEpisodeMediaEvent(event: FlowEvent.EpisodeMediaEvent) {
+        if (spinnerTexts[curIndex] == QuickAccess.Downloaded.name) {
+            var i = 0
+            val size: Int = event.episodes.size
+            while (i < size) {
+                val item: Episode = event.episodes[i++]
+                val pos = Episodes.indexOfItemWithId(episodes, item.id)
+                if (pos >= 0) {
+                    episodes.removeAt(pos)
+                    vms.removeAt(pos)
+                    val media = item.media
+                    if (media != null && media.downloaded) {
+                        episodes.add(pos, item)
+                        vms.add(pos, EpisodeVM(item))
+                    }
+                }
+            }
+            updateToolbar()
+        }
+    }
+
+    enum class QuickAccess {
+        New, Planned, Repeats, Liked, Commented, Downloaded, History, All
+    }
+
+    companion object {
+        val TAG = EpisodesFragment::class.simpleName ?: "Anonymous"
+        const val PREF_NAME: String = "PrefEpisodesFragment"
+    }
+}
diff --git a/app/src/main/kotlin/ac/mdiq/podcini/ui/fragment/FeedEpisodesFragment.kt b/app/src/main/kotlin/ac/mdiq/podcini/ui/fragment/FeedEpisodesFragment.kt
index 3b4ff928..253ce25b 100644
--- a/app/src/main/kotlin/ac/mdiq/podcini/ui/fragment/FeedEpisodesFragment.kt
+++ b/app/src/main/kotlin/ac/mdiq/podcini/ui/fragment/FeedEpisodesFragment.kt
@@ -204,12 +204,12 @@ class FeedEpisodesFragment : Fragment(), Toolbar.OnMenuItemClickListener {
                         refreshCB = { FeedUpdateManager.runOnceOrAsk(requireContext(), feed) },
                         leftSwipeCB = {
                             if (leftActionState.value is NoActionSwipeAction) swipeActions.showDialog()
-                            else leftActionState.value.performAction(it, this@FeedEpisodesFragment, swipeActions.filter ?: EpisodeFilter())
+                            else leftActionState.value.performAction(it, this@FeedEpisodesFragment)
                         },
                         rightSwipeCB = {
                             Logd(TAG, "rightActionState: ${rightActionState.value.getId()}")
                             if (rightActionState.value is NoActionSwipeAction) swipeActions.showDialog()
-                            else rightActionState.value.performAction(it, this@FeedEpisodesFragment, swipeActions.filter ?: EpisodeFilter())
+                            else rightActionState.value.performAction(it, this@FeedEpisodesFragment)
                         },
                     )
                 }
@@ -635,7 +635,7 @@ class FeedEpisodesFragment : Fragment(), Toolbar.OnMenuItemClickListener {
                     withContext(Dispatchers.Main) {
                         Logd(TAG, "loadItems subscribe called ${feed?.title}")
                         rating = feed?.rating ?: Rating.UNRATED.code
-                        swipeActions.setFilter(feed?.episodeFilter)
+//                        swipeActions.setFilter(feed?.episodeFilter)
                         refreshHeaderView()
 //                        if (feed != null) {
 //                            adapter.updateItems(episodes, feed)
diff --git a/app/src/main/kotlin/ac/mdiq/podcini/ui/fragment/HistoryFragment.kt b/app/src/main/kotlin/ac/mdiq/podcini/ui/fragment/HistoryFragment.kt
deleted file mode 100644
index 50c102ac..00000000
--- a/app/src/main/kotlin/ac/mdiq/podcini/ui/fragment/HistoryFragment.kt
+++ /dev/null
@@ -1,199 +0,0 @@
-package ac.mdiq.podcini.ui.fragment
-
-import ac.mdiq.podcini.R
-import ac.mdiq.podcini.storage.database.RealmDB.realm
-import ac.mdiq.podcini.storage.database.RealmDB.runOnIOScope
-import ac.mdiq.podcini.storage.database.RealmDB.upsert
-import ac.mdiq.podcini.storage.model.Episode
-import ac.mdiq.podcini.storage.model.EpisodeMedia
-import ac.mdiq.podcini.storage.model.EpisodeSortOrder
-import ac.mdiq.podcini.storage.model.EpisodeSortOrder.Companion.getPermutor
-import ac.mdiq.podcini.ui.dialog.ConfirmationDialog
-import ac.mdiq.podcini.ui.dialog.DatesFilterDialog
-import ac.mdiq.podcini.util.EventFlow
-import ac.mdiq.podcini.util.FlowEvent
-import ac.mdiq.podcini.util.Logd
-import android.content.DialogInterface
-import android.os.Bundle
-import android.view.LayoutInflater
-import android.view.MenuItem
-import android.view.View
-import android.view.ViewGroup
-import androidx.lifecycle.lifecycleScope
-import kotlinx.coroutines.Job
-import kotlinx.coroutines.flow.collectLatest
-import kotlinx.coroutines.launch
-import java.util.*
-import kotlin.math.min
-
-class HistoryFragment : BaseEpisodesFragment() {
-//    private var sortOrder : EpisodeSortOrder = EpisodeSortOrder.PLAYED_DATE_NEW_OLD
-    private var startDate : Long = 0L
-    private var endDate : Long = Date().time
-    private var allHistory: List<Episode> = listOf()
-
-    override fun getPrefName(): String {
-        return TAG
-    }
-
-    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
-        val root = super.onCreateView(inflater, container, savedInstanceState)
-        Logd(TAG, "fragment onCreateView")
-        sortOrder = EpisodeSortOrder.PLAYED_DATE_NEW_OLD
-        toolbar.inflateMenu(R.menu.playback_history)
-        toolbar.setTitle(R.string.playback_history_label)
-        updateToolbar()
-        return root
-    }
-
-    override fun onStart() {
-        super.onStart()
-        procFlowEvents()
-    }
-
-    override fun onStop() {
-        super.onStop()
-        cancelFlowEvents()
-    }
-
-    override fun onDestroyView() {
-        allHistory = listOf()
-        super.onDestroyView()
-    }
-
-    override fun onSort(order: EpisodeSortOrder) {
-//        EventFlow.postEvent(FlowEvent.HistoryEvent(sortOrder))
-        sortOrder = order
-        loadItems()
-        updateToolbar()
-    }
-
-     override fun onMenuItemClick(item: MenuItem): Boolean {
-        if (super.onOptionsItemSelected(item)) return true
-        when (item.itemId) {
-            R.id.episodes_sort -> showSortDialog = true
-            R.id.filter_items -> {
-                val dialog = object: DatesFilterDialog(requireContext(), 0L) {
-                    override fun initParams() {
-                        val calendar = Calendar.getInstance()
-                        calendar.add(Calendar.YEAR, -1) // subtract 1 year
-                        timeFilterFrom = calendar.timeInMillis
-                        showMarkPlayed = false
-                    }
-                    override fun callback(timeFilterFrom: Long, timeFilterTo: Long, includeMarkedAsPlayed: Boolean) {
-                        EventFlow.postEvent(FlowEvent.HistoryEvent(sortOrder, timeFilterFrom, timeFilterTo))
-                    }
-                }
-                dialog.show()
-            }
-            R.id.clear_history_item -> {
-                val conDialog: ConfirmationDialog = object : ConfirmationDialog(requireContext(), R.string.clear_history_label, R.string.clear_playback_history_msg) {
-                    override fun onConfirmButtonPressed(dialog: DialogInterface) {
-                        dialog.dismiss()
-                        clearHistory()
-                    }
-                }
-                conDialog.createNewDialog().show()
-            }
-            else -> return false
-        }
-        return true
-    }
-
-    override fun updateToolbar() {
-        // Not calling super, as we do not have a refresh button that could be updated
-        toolbar.menu.findItem(R.id.episodes_sort).isVisible = episodes.isNotEmpty()
-        toolbar.menu.findItem(R.id.filter_items).isVisible = episodes.isNotEmpty()
-        toolbar.menu.findItem(R.id.clear_history_item).isVisible = episodes.isNotEmpty()
-
-        swipeActions.setFilter(getFilter())
-        var info = "${episodes.size} episodes"
-        if (getFilter().properties.isNotEmpty()) {
-            info += " - ${getString(R.string.filtered_label)}"
-        }
-        infoBarText.value = info
-    }
-
-    private var eventSink: Job?     = null
-    private fun cancelFlowEvents() {
-        eventSink?.cancel()
-        eventSink = null
-    }
-    private fun procFlowEvents() {
-        if (eventSink != null) return
-        eventSink = lifecycleScope.launch {
-            EventFlow.events.collectLatest { event ->
-                Logd(TAG, "Received event: ${event.TAG}")
-                when (event) {
-                    is FlowEvent.HistoryEvent -> {
-                        sortOrder = event.sortOrder
-                        if (event.startDate > 0) startDate = event.startDate
-                        endDate = event.endDate
-                        loadItems()
-                        updateToolbar()
-                    }
-                    else -> {}
-                }
-            }
-        }
-    }
-
-    private var loadItemsRunning = false
-    override fun loadData(): List<Episode> {
-        if (!loadItemsRunning) {
-            loadItemsRunning = true
-            allHistory = getHistory(0, Int.MAX_VALUE, startDate, endDate, sortOrder).toMutableList()
-            loadItemsRunning = false
-        }
-        if (allHistory.isEmpty()) return listOf()
-        return allHistory
-    }
-
-    override fun loadTotalItemCount(): Int {
-        return getNumberOfPlayed().toInt()
-    }
-
-    fun clearHistory() : Job {
-        Logd(TAG, "clearHistory called")
-        return runOnIOScope {
-            val episodes = realm.query(Episode::class).query("media.playbackCompletionTime > 0 || media.lastPlayedTime > 0").find()
-            for (e in episodes) {
-                upsert(e) {
-                    it.media?.playbackCompletionDate = null
-                    it.media?.lastPlayedTime = 0
-                }
-            }
-            EventFlow.postEvent(FlowEvent.HistoryEvent())
-        }
-    }
-
-    companion object {
-        val TAG = HistoryFragment::class.simpleName ?: "Anonymous"
-
-        fun getNumberOfPlayed(): Long {
-            Logd(TAG, "getNumberOfPlayed called")
-            return realm.query(EpisodeMedia::class).query("lastPlayedTime > 0 || playbackCompletionTime > 0").count().find()
-        }
-
-        /**
-         * Loads the playback history from the database. A FeedItem is in the playback history if playback of the correpsonding episode
-         * has been played ot completed at least once.
-         * @param limit The maximum number of episodes to return.
-         * @return The playback history. The FeedItems are sorted by their media's playbackCompletionDate in descending order.
-         */
-        fun getHistory(offset: Int, limit: Int, start: Long = 0L, end: Long = Date().time,
-                       sortOrder: EpisodeSortOrder = EpisodeSortOrder.PLAYED_DATE_NEW_OLD): List<Episode> {
-            Logd(TAG, "getHistory() called")
-            val medias = realm.query(EpisodeMedia::class).query("(playbackCompletionTime > 0) OR (lastPlayedTime > \$0 AND lastPlayedTime <= \$1)", start, end).find()
-            var episodes: MutableList<Episode> = mutableListOf()
-            for (m in medias) {
-                val item_ = m.episodeOrFetch()
-                if (item_ != null) episodes.add(item_)
-                else Logd(TAG, "getHistory: media has null episode: ${m.id}")
-            }
-            getPermutor(sortOrder).reorder(episodes)
-            if (offset > 0 && episodes.size > offset) episodes = episodes.subList(offset, min(episodes.size, offset+limit))
-            return episodes
-        }
-    }
-}
diff --git a/app/src/main/kotlin/ac/mdiq/podcini/ui/fragment/LogsFragment.kt b/app/src/main/kotlin/ac/mdiq/podcini/ui/fragment/LogsFragment.kt
index e3865d3e..17a3d044 100644
--- a/app/src/main/kotlin/ac/mdiq/podcini/ui/fragment/LogsFragment.kt
+++ b/app/src/main/kotlin/ac/mdiq/podcini/ui/fragment/LogsFragment.kt
@@ -13,7 +13,7 @@ import ac.mdiq.podcini.storage.model.Rating.Companion.fromCode
 import ac.mdiq.podcini.ui.actions.DownloadActionButton
 import ac.mdiq.podcini.ui.activity.MainActivity
 import ac.mdiq.podcini.ui.activity.ShareReceiverActivity.Companion.receiveShared
-import ac.mdiq.podcini.ui.compose.ConfirmAddYoutubeEpisode
+import ac.mdiq.podcini.ui.compose.ConfirmAddYoutubeEpisode1
 import ac.mdiq.podcini.ui.compose.CustomTheme
 import ac.mdiq.podcini.util.EventFlow
 import ac.mdiq.podcini.util.FlowEvent
@@ -120,7 +120,7 @@ class LogsFragment : Fragment(), Toolbar.OnMenuItemClickListener {
         var showYTMediaConfirmDialog by remember { mutableStateOf(false) }
         var sharedUrl by remember { mutableStateOf("") }
         if (showYTMediaConfirmDialog)
-            ConfirmAddYoutubeEpisode(listOf(sharedUrl), showYTMediaConfirmDialog, onDismissRequest = { showYTMediaConfirmDialog = false })
+            ConfirmAddYoutubeEpisode1(listOf(sharedUrl), showYTMediaConfirmDialog, onDismissRequest = { showYTMediaConfirmDialog = false })
 
         LazyColumn(state = lazyListState, modifier = Modifier.padding(start = 10.dp, end = 6.dp, top = 5.dp, bottom = 5.dp),
         verticalArrangement = Arrangement.spacedBy(8.dp)) {
diff --git a/app/src/main/kotlin/ac/mdiq/podcini/ui/fragment/NavDrawerFragment.kt b/app/src/main/kotlin/ac/mdiq/podcini/ui/fragment/NavDrawerFragment.kt
index ab2fa432..89c6e231 100644
--- a/app/src/main/kotlin/ac/mdiq/podcini/ui/fragment/NavDrawerFragment.kt
+++ b/app/src/main/kotlin/ac/mdiq/podcini/ui/fragment/NavDrawerFragment.kt
@@ -1,6 +1,7 @@
 package ac.mdiq.podcini.ui.fragment
 
 import ac.mdiq.podcini.R
+import ac.mdiq.podcini.playback.base.InTheatre.curQueue
 import ac.mdiq.podcini.preferences.UserPreferences.hiddenDrawerItems
 import ac.mdiq.podcini.storage.database.Episodes.getEpisodesCount
 import ac.mdiq.podcini.storage.database.Feeds.getFeedCount
@@ -11,7 +12,6 @@ import ac.mdiq.podcini.ui.activity.MainActivity
 import ac.mdiq.podcini.ui.activity.PreferenceActivity
 import ac.mdiq.podcini.ui.compose.CustomTheme
 import ac.mdiq.podcini.ui.fragment.FeedEpisodesFragment.Companion.ARGUMENT_FEED_ID
-import ac.mdiq.podcini.ui.fragment.HistoryFragment.Companion.getNumberOfPlayed
 import ac.mdiq.podcini.ui.utils.ThemeUtils
 import ac.mdiq.podcini.util.IntentUtils.openInBrowser
 import ac.mdiq.podcini.util.Logd
@@ -98,8 +98,7 @@ class NavDrawerFragment : Fragment(), OnSharedPreferenceChangeListener {
 
     private fun getRecentPodcasts() {
         var feeds_ = realm.query(Feed::class).sort("lastPlayed", sortOrder = Sort.DESCENDING).find().toMutableList()
-        if (feeds_.size > 3) feeds_ = feeds_.subList(0, 3)
-//        for (f in feeds_) Logd(TAG, "getRecentPodcasts ${f.title}")
+        if (feeds_.size > 5) feeds_ = feeds_.subList(0, 5)
         feeds.clear()
         feeds.addAll(feeds_)
     }
@@ -114,6 +113,7 @@ class NavDrawerFragment : Fragment(), OnSharedPreferenceChangeListener {
                     (activity as MainActivity).bottomSheet.state = BottomSheetBehavior.STATE_COLLAPSED
                 }) {
                     Icon(imageVector = ImageVector.vectorResource(nav.iconRes), tint = textColor, contentDescription = nav.tag, modifier = Modifier.padding(start = 10.dp))
+//                    val nametag = if (nav.tag != QueuesFragment.TAG) stringResource(nav.nameRes) else curQueue.name
                     Text(stringResource(nav.nameRes), color = textColor, style = MaterialTheme.typography.titleLarge, modifier = Modifier.padding(start = 20.dp))
                     Spacer(Modifier.weight(1f))
                     if (nav.count > 0) Text(nav.count.toString(), color = textColor, modifier = Modifier.padding(end = 10.dp))
@@ -219,11 +219,11 @@ class NavDrawerFragment : Fragment(), OnSharedPreferenceChangeListener {
         val navMap: LinkedHashMap<String, NavItem> = linkedMapOf(
             SubscriptionsFragment.TAG to NavItem(SubscriptionsFragment.TAG, R.drawable.ic_subscriptions, R.string.subscriptions_label),
             QueuesFragment.TAG to NavItem(QueuesFragment.TAG, R.drawable.ic_playlist_play, R.string.queue_label),
-            AllEpisodesFragment.TAG to NavItem(AllEpisodesFragment.TAG, R.drawable.ic_feed, R.string.episodes_label),
-            DownloadsFragment.TAG to NavItem(DownloadsFragment.TAG, R.drawable.ic_download, R.string.downloads_label),
-            HistoryFragment.TAG to NavItem(HistoryFragment.TAG, R.drawable.baseline_work_history_24, R.string.playback_history_label),
+            EpisodesFragment.TAG to NavItem(EpisodesFragment.TAG, R.drawable.ic_feed, R.string.episodes_label),
+//            AllEpisodesFragment.TAG to NavItem(AllEpisodesFragment.TAG, R.drawable.ic_feed, R.string.episodes_label),
+//            DownloadsFragment.TAG to NavItem(DownloadsFragment.TAG, R.drawable.ic_download, R.string.downloads_label),
+//            HistoryFragment.TAG to NavItem(HistoryFragment.TAG, R.drawable.baseline_work_history_24, R.string.playback_history_label),
             LogsFragment.TAG to NavItem(LogsFragment.TAG, R.drawable.ic_history, R.string.logs_label),
-//            SubscriptionLogFragment.TAG to NavItem(SubscriptionLogFragment.TAG, R.drawable.ic_subscriptions, R.string.subscriptions_log_label),
             StatisticsFragment.TAG to NavItem(StatisticsFragment.TAG, R.drawable.ic_chart_box, R.string.statistics_label),
             OnlineSearchFragment.TAG to NavItem(OnlineSearchFragment.TAG, R.drawable.ic_add, R.string.add_feed_label)
         )
@@ -251,9 +251,10 @@ class NavDrawerFragment : Fragment(), OnSharedPreferenceChangeListener {
             feedCount = getFeedCount()
             navMap[QueuesFragment.TAG]?.count = realm.query(PlayQueue::class).find().sumOf { it.size()}
             navMap[SubscriptionsFragment.TAG]?.count = feedCount
-            navMap[HistoryFragment.TAG]?.count = getNumberOfPlayed().toInt()
-            navMap[DownloadsFragment.TAG]?.count = getEpisodesCount(EpisodeFilter(EpisodeFilter.States.downloaded.name))
-            navMap[AllEpisodesFragment.TAG]?.count = numItems
+//            navMap[HistoryFragment.TAG]?.count = getNumberOfPlayed().toInt()
+//            navMap[DownloadsFragment.TAG]?.count = getEpisodesCount(EpisodeFilter(EpisodeFilter.States.downloaded.name))
+//            navMap[AllEpisodesFragment.TAG]?.count = numItems
+            navMap[EpisodesFragment.TAG]?.count = numItems
             navMap[LogsFragment.TAG]?.count = realm.query(ShareLog::class).count().find().toInt() +
                     realm.query(SubscriptionLog::class).count().find().toInt() +
                     realm.query(DownloadResult::class).count().find().toInt()
diff --git a/app/src/main/kotlin/ac/mdiq/podcini/ui/fragment/OnlineFeedFragment.kt b/app/src/main/kotlin/ac/mdiq/podcini/ui/fragment/OnlineFeedFragment.kt
index d1ab0d1e..84869c24 100644
--- a/app/src/main/kotlin/ac/mdiq/podcini/ui/fragment/OnlineFeedFragment.kt
+++ b/app/src/main/kotlin/ac/mdiq/podcini/ui/fragment/OnlineFeedFragment.kt
@@ -709,7 +709,7 @@ class OnlineFeedFragment : Fragment() {
     }
 
     class RemoteEpisodesFragment : BaseEpisodesFragment() {
-        private val episodeList: MutableList<Episode> = mutableListOf()
+        private var episodeList: MutableList<Episode> = mutableListOf()
 
          override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
             val root = super.onCreateView(inflater, container, savedInstanceState)
@@ -720,29 +720,21 @@ class OnlineFeedFragment : Fragment() {
             return root
         }
         override fun onDestroyView() {
-            episodeList.clear()
             super.onDestroyView()
         }
         fun setEpisodes(episodeList_: MutableList<Episode>) {
-            episodeList.clear()
-            episodeList.addAll(episodeList_)
+            episodeList = episodeList_
         }
         override fun loadData(): List<Episode> {
-            if (episodeList.isEmpty()) return listOf()
             return episodeList
         }
-        override fun loadTotalItemCount(): Int {
-            return episodeList.size
-        }
         override fun getPrefName(): String {
             return PREF_NAME
         }
         override fun updateToolbar() {
-            binding.toolbar.menu.findItem(R.id.episodes_sort).isVisible = false
-//        binding.toolbar.menu.findItem(R.id.refresh_item).setVisible(false)
-            binding.toolbar.menu.findItem(R.id.action_search).isVisible = false
-//            binding.toolbar.menu.findItem(R.id.action_favorites).setVisible(false)
-            binding.toolbar.menu.findItem(R.id.filter_items).isVisible = false
+            toolbar.menu.findItem(R.id.episodes_sort).isVisible = false
+            toolbar.menu.findItem(R.id.action_search).isVisible = false
+            toolbar.menu.findItem(R.id.filter_items).isVisible = false
             infoBarText.value = "${episodes.size} episodes"
         }
          override fun onMenuItemClick(item: MenuItem): Boolean {
@@ -772,11 +764,6 @@ class OnlineFeedFragment : Fragment() {
         private const val PREF_LAST_AUTO_DOWNLOAD = "lastAutoDownload"
         private const val KEY_UP_ARROW = "up_arrow"
 
-//        var prefs: SharedPreferences? = null
-//        fun getSharedPrefs(context: Context) {
-//            if (prefs == null) prefs = context.getSharedPreferences(PREFS, Context.MODE_PRIVATE)
-//        }
-
         fun newInstance(feedUrl: String, isShared: Boolean = false): OnlineFeedFragment {
             val fragment = OnlineFeedFragment()
             val b = Bundle()
diff --git a/app/src/main/kotlin/ac/mdiq/podcini/ui/fragment/OnlineSearchFragment.kt b/app/src/main/kotlin/ac/mdiq/podcini/ui/fragment/OnlineSearchFragment.kt
index a0123adf..17836250 100644
--- a/app/src/main/kotlin/ac/mdiq/podcini/ui/fragment/OnlineSearchFragment.kt
+++ b/app/src/main/kotlin/ac/mdiq/podcini/ui/fragment/OnlineSearchFragment.kt
@@ -10,15 +10,17 @@ import ac.mdiq.podcini.net.feed.FeedUpdateManager
 import ac.mdiq.podcini.net.feed.searcher.*
 import ac.mdiq.podcini.preferences.OpmlBackupAgent.Companion.isOPMLRestared
 import ac.mdiq.podcini.preferences.OpmlBackupAgent.Companion.performRestore
+import ac.mdiq.podcini.preferences.OpmlTransporter
+import ac.mdiq.podcini.preferences.OpmlTransporter.OpmlElement
 import ac.mdiq.podcini.storage.database.Feeds.getFeedList
 import ac.mdiq.podcini.storage.database.Feeds.updateFeed
 import ac.mdiq.podcini.storage.model.EpisodeSortOrder
 import ac.mdiq.podcini.storage.model.Feed
 import ac.mdiq.podcini.ui.activity.MainActivity
-import ac.mdiq.podcini.ui.activity.OpmlImportActivity
 import ac.mdiq.podcini.ui.compose.CustomTheme
 import ac.mdiq.podcini.ui.compose.NonlazyGrid
 import ac.mdiq.podcini.ui.compose.OnlineFeedItem
+import ac.mdiq.podcini.ui.compose.OpmlImportSelectionDialog
 import ac.mdiq.podcini.ui.fragment.NavDrawerFragment.Companion.feedCount
 import ac.mdiq.podcini.util.EventFlow
 import ac.mdiq.podcini.util.FlowEvent
@@ -98,10 +100,46 @@ class OnlineSearchFragment : Fragment() {
     private var numColumns by mutableIntStateOf(4)
     private val searchResult = mutableStateListOf<PodcastSearchResult>()
 
+    private var showOpmlImportSelectionDialog by mutableStateOf(false)
+    private val readElements = mutableStateListOf<OpmlElement>()
     private val chooseOpmlImportPathLauncher = registerForActivityResult<String, Uri>(ActivityResultContracts.GetContent()) { uri: Uri? ->
-        this.chooseOpmlImportPathResult(uri) }
+        if (uri == null) return@registerForActivityResult
+        OpmlTransporter.startImport(requireContext(), uri) { readElements.addAll(it) }
+        showOpmlImportSelectionDialog = true
+    }
 
-    private val addLocalFolderLauncher = registerForActivityResult<Uri?, Uri>(AddLocalFolder()) { uri: Uri? -> this.addLocalFolderResult(uri) }
+    private val addLocalFolderLauncher = registerForActivityResult<Uri?, Uri>(AddLocalFolder()) { uri: Uri? ->
+        if (uri == null) return@registerForActivityResult
+        val scope = CoroutineScope(Dispatchers.Main)
+        scope.launch {
+            try {
+                val feed = withContext(Dispatchers.IO) {
+//                    addLocalFolder(uri)
+                    requireActivity().contentResolver.takePersistableUriPermission(uri, Intent.FLAG_GRANT_READ_URI_PERMISSION or Intent.FLAG_GRANT_WRITE_URI_PERMISSION)
+                    val documentFile = DocumentFile.fromTreeUri(requireContext(), uri)
+                    requireNotNull(documentFile) { "Unable to retrieve document tree" }
+                    var title = documentFile.name
+                    if (title == null) title = getString(R.string.local_folder)
+
+                    val dirFeed = Feed(Feed.PREFIX_LOCAL_FOLDER + uri.toString(), null, title)
+                    dirFeed.episodes.clear()
+                    dirFeed.sortOrder = EpisodeSortOrder.EPISODE_TITLE_A_Z
+                    val fromDatabase: Feed? = updateFeed(requireContext(), dirFeed, false)
+                    FeedUpdateManager.runOnce(requireContext(), fromDatabase)
+                    fromDatabase
+                }
+                withContext(Dispatchers.Main) {
+                    if (feed != null) {
+                        val fragment: Fragment = FeedEpisodesFragment.newInstance(feed.id)
+                        mainAct?.loadChildFragment(fragment)
+                    }
+                }
+            } catch (e: Throwable) {
+                Log.e(TAG, Log.getStackTraceString(e))
+                mainAct?.showSnackbarAbovePlayer(e.localizedMessage?: "No messaage", Snackbar.LENGTH_LONG)
+            }
+        }
+    }
 
     override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
         super.onCreateView(inflater, container, savedInstanceState)
@@ -170,6 +208,7 @@ class OnlineSearchFragment : Fragment() {
             Text(stringResource(R.string.search_fyyd_label), color = textColor, modifier = Modifier.padding(start = 10.dp, top = 5.dp).clickable(onClick = { mainAct?.loadChildFragment(SearchResultsFragment.newInstance(FyydPodcastSearcher::class.java)) }))
             Text(stringResource(R.string.gpodnet_search_hint), color = textColor, modifier = Modifier.padding(start = 10.dp, top = 5.dp).clickable(onClick = { mainAct?.loadChildFragment(SearchResultsFragment.newInstance(GpodnetPodcastSearcher::class.java)) }))
             Text(stringResource(R.string.search_podcastindex_label), color = textColor, modifier = Modifier.padding(start = 10.dp, top = 5.dp).clickable(onClick = { mainAct?.loadChildFragment(SearchResultsFragment.newInstance(PodcastIndexPodcastSearcher::class.java)) }))
+            if (showOpmlImportSelectionDialog) OpmlImportSelectionDialog(readElements) { showOpmlImportSelectionDialog = false }
             Text(stringResource(R.string.opml_add_podcast_label), color = textColor, modifier = Modifier.padding(start = 10.dp, top = 5.dp).clickable(onClick = {
                 try { chooseOpmlImportPathLauncher.launch("*/*")
                 } catch (e: ActivityNotFoundException) {
@@ -332,48 +371,6 @@ class OnlineSearchFragment : Fragment() {
         super.onDestroyView()
     }
 
-    private fun chooseOpmlImportPathResult(uri: Uri?) {
-        if (uri == null) return
-
-        val intent = Intent(context, OpmlImportActivity::class.java)
-        intent.setData(uri)
-        startActivity(intent)
-    }
-
-     private fun addLocalFolderResult(uri: Uri?) {
-        if (uri == null) return
-        val scope = CoroutineScope(Dispatchers.Main)
-        scope.launch {
-            try {
-                val feed = withContext(Dispatchers.IO) { addLocalFolder(uri) }
-                withContext(Dispatchers.Main) {
-                    if (feed != null) {
-                        val fragment: Fragment = FeedEpisodesFragment.newInstance(feed.id)
-                        mainAct?.loadChildFragment(fragment)
-                    }
-                }
-            } catch (e: Throwable) {
-                Log.e(TAG, Log.getStackTraceString(e))
-                mainAct?.showSnackbarAbovePlayer(e.localizedMessage?: "No messaage", Snackbar.LENGTH_LONG)
-            }
-        }
-    }
-
-     private fun addLocalFolder(uri: Uri): Feed? {
-        requireActivity().contentResolver.takePersistableUriPermission(uri, Intent.FLAG_GRANT_READ_URI_PERMISSION or Intent.FLAG_GRANT_WRITE_URI_PERMISSION)
-        val documentFile = DocumentFile.fromTreeUri(requireContext(), uri)
-        requireNotNull(documentFile) { "Unable to retrieve document tree" }
-        var title = documentFile.name
-        if (title == null) title = getString(R.string.local_folder)
-
-        val dirFeed = Feed(Feed.PREFIX_LOCAL_FOLDER + uri.toString(), null, title)
-        dirFeed.episodes.clear()
-        dirFeed.sortOrder = EpisodeSortOrder.EPISODE_TITLE_A_Z
-        val fromDatabase: Feed? = updateFeed(requireContext(), dirFeed, false)
-        FeedUpdateManager.runOnce(requireContext(), fromDatabase)
-        return fromDatabase
-    }
-
     private class AddLocalFolder : ActivityResultContracts.OpenDocumentTree() {
         override fun createIntent(context: Context, input: Uri?): Intent {
             return super.createIntent(context, input).addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
diff --git a/app/src/main/kotlin/ac/mdiq/podcini/ui/fragment/QueuesFragment.kt b/app/src/main/kotlin/ac/mdiq/podcini/ui/fragment/QueuesFragment.kt
index 1aa6ce0d..6a543feb 100644
--- a/app/src/main/kotlin/ac/mdiq/podcini/ui/fragment/QueuesFragment.kt
+++ b/app/src/main/kotlin/ac/mdiq/podcini/ui/fragment/QueuesFragment.kt
@@ -168,9 +168,9 @@ class QueuesFragment : Fragment(), Toolbar.OnMenuItemClickListener {
         toolbar.addView(spinnerView)
 
         swipeActions = SwipeActions(this, TAG)
-        swipeActions.setFilter(EpisodeFilter(EpisodeFilter.States.queued.name))
+//        swipeActions.setFilter(EpisodeFilter(EpisodeFilter.States.queued.name))
         swipeActionsBin = SwipeActions(this, "$TAG.Bin")
-        swipeActionsBin.setFilter(EpisodeFilter(EpisodeFilter.States.queued.name))
+//        swipeActionsBin.setFilter(EpisodeFilter(EpisodeFilter.States.queued.name))
 
         binding.mainView.setContent {
             CustomTheme(requireContext()) {
@@ -179,11 +179,11 @@ class QueuesFragment : Fragment(), Toolbar.OnMenuItemClickListener {
                         InforBar(infoBarText, leftAction = leftActionStateBin, rightAction = rightActionStateBin, actionConfig = { swipeActionsBin.showDialog() })
                         val leftCB = { episode: Episode ->
                             if (leftActionStateBin.value is NoActionSwipeAction) swipeActionsBin.showDialog()
-                            else leftActionStateBin.value.performAction(episode, this@QueuesFragment, swipeActionsBin.filter ?: EpisodeFilter())
+                            else leftActionStateBin.value.performAction(episode, this@QueuesFragment)
                         }
                         val rightCB = { episode: Episode ->
                             if (rightActionStateBin.value is NoActionSwipeAction) swipeActionsBin.showDialog()
-                            else rightActionStateBin.value.performAction(episode, this@QueuesFragment, swipeActionsBin.filter ?: EpisodeFilter())
+                            else rightActionStateBin.value.performAction(episode, this@QueuesFragment)
                         }
                         EpisodeLazyColumn(activity as MainActivity, vms = vms, leftSwipeCB = { leftCB(it) }, rightSwipeCB = { rightCB(it) })
                     }
@@ -200,11 +200,11 @@ class QueuesFragment : Fragment(), Toolbar.OnMenuItemClickListener {
                             InforBar(infoBarText, leftAction = leftActionState, rightAction = rightActionState, actionConfig = { swipeActions.showDialog() })
                             val leftCB = { episode: Episode ->
                                 if (leftActionState.value is NoActionSwipeAction) swipeActions.showDialog()
-                                else leftActionState.value.performAction(episode, this@QueuesFragment, swipeActions.filter ?: EpisodeFilter())
+                                else leftActionState.value.performAction(episode, this@QueuesFragment)
                             }
                             val rightCB = { episode: Episode ->
                                 if (rightActionState.value is NoActionSwipeAction) swipeActions.showDialog()
-                                else rightActionState.value.performAction(episode, this@QueuesFragment, swipeActions.filter ?: EpisodeFilter())
+                                else rightActionState.value.performAction(episode, this@QueuesFragment)
                             }
                             EpisodeLazyColumn(activity as MainActivity, vms = vms,
                                 isDraggable = dragDropEnabled, dragCB = { iFrom, iTo -> runOnIOScope { moveInQueueSync(iFrom, iTo, true) } },
diff --git a/app/src/main/kotlin/ac/mdiq/podcini/ui/fragment/SearchFragment.kt b/app/src/main/kotlin/ac/mdiq/podcini/ui/fragment/SearchFragment.kt
index 6f64e705..2eaddbb0 100644
--- a/app/src/main/kotlin/ac/mdiq/podcini/ui/fragment/SearchFragment.kt
+++ b/app/src/main/kotlin/ac/mdiq/podcini/ui/fragment/SearchFragment.kt
@@ -7,7 +7,6 @@ import ac.mdiq.podcini.net.feed.searcher.CombinedSearcher
 import ac.mdiq.podcini.storage.database.Episodes
 import ac.mdiq.podcini.storage.database.RealmDB.realm
 import ac.mdiq.podcini.storage.model.Episode
-import ac.mdiq.podcini.storage.model.EpisodeFilter
 import ac.mdiq.podcini.storage.model.Feed
 import ac.mdiq.podcini.storage.model.Rating
 import ac.mdiq.podcini.ui.actions.SwipeAction
@@ -117,11 +116,11 @@ class SearchFragment : Fragment() {
                     EpisodeLazyColumn(activity as MainActivity, vms = vms,
                         leftSwipeCB = {
                             if (leftActionState.value is NoActionSwipeAction) swipeActions.showDialog()
-                            else leftActionState.value.performAction(it, this@SearchFragment, swipeActions.filter ?: EpisodeFilter())
+                            else leftActionState.value.performAction(it, this@SearchFragment)
                         },
                         rightSwipeCB = {
                             if (rightActionState.value is NoActionSwipeAction) swipeActions.showDialog()
-                            else rightActionState.value.performAction(it, this@SearchFragment, swipeActions.filter ?: EpisodeFilter())
+                            else rightActionState.value.performAction(it, this@SearchFragment)
                         },
                     )
                 }
diff --git a/app/src/main/kotlin/ac/mdiq/podcini/ui/fragment/SubscriptionsFragment.kt b/app/src/main/kotlin/ac/mdiq/podcini/ui/fragment/SubscriptionsFragment.kt
index 4f1d9c85..557bf8df 100644
--- a/app/src/main/kotlin/ac/mdiq/podcini/ui/fragment/SubscriptionsFragment.kt
+++ b/app/src/main/kotlin/ac/mdiq/podcini/ui/fragment/SubscriptionsFragment.kt
@@ -2,7 +2,6 @@ package ac.mdiq.podcini.ui.fragment
 
 import ac.mdiq.podcini.R
 import ac.mdiq.podcini.databinding.ComposeFragmentBinding
-import ac.mdiq.podcini.databinding.DialogSwitchPreferenceBinding
 import ac.mdiq.podcini.net.feed.FeedUpdateManager
 import ac.mdiq.podcini.preferences.DocumentFileExportWorker
 import ac.mdiq.podcini.preferences.ExportTypes
@@ -28,7 +27,10 @@ import ac.mdiq.podcini.util.FlowEvent
 import ac.mdiq.podcini.util.Logd
 import ac.mdiq.podcini.util.MiscFormatter.formatDateTimeFlex
 import android.app.Activity.RESULT_OK
-import android.content.*
+import android.content.ActivityNotFoundException
+import android.content.Context
+import android.content.Intent
+import android.content.SharedPreferences
 import android.net.Uri
 import android.os.Bundle
 import android.util.Log
@@ -75,7 +77,6 @@ import coil.compose.AsyncImage
 import coil.request.CachePolicy
 import coil.request.ImageRequest
 import com.google.android.material.appbar.MaterialToolbar
-import com.google.android.material.dialog.MaterialAlertDialogBuilder
 import com.google.android.material.snackbar.Snackbar
 import kotlinx.coroutines.*
 import kotlinx.coroutines.flow.collectLatest
@@ -532,9 +533,13 @@ class SubscriptionsFragment : Fragment(), Toolbar.OnMenuItemClickListener {
         if (showSpeedDialog) PlaybackSpeedDialog(selected, initSpeed = 1f, maxSpeed = 3f, onDismiss = {showSpeedDialog = false}) { newSpeed ->
             saveFeedPreferences { it: FeedPreferences -> it.playSpeed = newSpeed }
         }
+        var showAutoDownloadSwitchDialog by remember { mutableStateOf(false) }
+        if (showAutoDownloadSwitchDialog) SimpleSwitchDialog(stringResource(R.string.auto_download_settings_label), stringResource(R.string.auto_download_label), onDismissRequest = { showAutoDownloadSwitchDialog = false }) { enabled ->
+            saveFeedPreferences { it: FeedPreferences -> it.autoDownload = enabled }
+        }
 
         @Composable
-        fun EpisodeSpeedDial(activity: MainActivity, selected: SnapshotStateList<Feed>, modifier: Modifier = Modifier) {
+        fun EpisodeSpeedDial(selected: SnapshotStateList<Feed>, modifier: Modifier = Modifier) {
             val TAG = "EpisodeSpeedDial ${selected.size}"
             var isExpanded by remember { mutableStateOf(false) }
             val options = listOf<@Composable () -> Unit>(
@@ -558,13 +563,7 @@ class SubscriptionsFragment : Fragment(), Toolbar.OnMenuItemClickListener {
                     isExpanded = false
                     selectMode = false
                     Logd(TAG, "ic_download: ${selected.size}")
-                    val preferenceSwitchDialog = PreferenceSwitchDialog(activity, activity.getString(R.string.auto_download_settings_label), activity.getString(R.string.auto_download_label))
-                    preferenceSwitchDialog.setOnPreferenceChangedListener( object: PreferenceSwitchDialog.OnPreferenceChangedListener {
-                        override fun preferenceChanged(enabled: Boolean) {
-                            saveFeedPreferences { it: FeedPreferences -> it.autoDownload = enabled }
-                        }
-                    })
-                    preferenceSwitchDialog.openDialog()
+                    showAutoDownloadSwitchDialog = true
                 }) {
                     Icon(imageVector = ImageVector.vectorResource(id = R.drawable.ic_download), "")
                     Text(stringResource(id = R.string.auto_download_label)) } },
@@ -834,7 +833,7 @@ class SubscriptionsFragment : Fragment(), Toolbar.OnMenuItemClickListener {
                             Logd(TAG, "selectedIds: ${selected.size}")
                         }))
                 }
-                EpisodeSpeedDial(activity as MainActivity, selected.toMutableStateList(), modifier = Modifier.align(Alignment.BottomStart).padding(bottom = 16.dp, start = 16.dp))
+                EpisodeSpeedDial(selected.toMutableStateList(), modifier = Modifier.align(Alignment.BottomStart).padding(bottom = 16.dp, start = 16.dp))
             }
         }
     }
@@ -1430,33 +1429,6 @@ class SubscriptionsFragment : Fragment(), Toolbar.OnMenuItemClickListener {
         }
     }
 
-    class PreferenceSwitchDialog(private var context: Context, private val title: String, private val text: String) {
-        private var onPreferenceChangedListener: OnPreferenceChangedListener? = null
-        interface OnPreferenceChangedListener {
-            fun preferenceChanged(enabled: Boolean)
-        }
-        fun openDialog() {
-            val builder = MaterialAlertDialogBuilder(context)
-            builder.setTitle(title)
-
-            val inflater = LayoutInflater.from(this.context)
-            val layout = inflater.inflate(R.layout.dialog_switch_preference, null, false)
-            val binding = DialogSwitchPreferenceBinding.bind(layout)
-            val switchButton = binding.dialogSwitch
-            switchButton.text = text
-            builder.setView(layout)
-
-            builder.setPositiveButton(R.string.confirm_label) { _: DialogInterface?, _: Int ->
-                onPreferenceChangedListener?.preferenceChanged(switchButton.isChecked)
-            }
-            builder.setNegativeButton(R.string.cancel_label, null)
-            builder.create().show()
-        }
-        fun setOnPreferenceChangedListener(onPreferenceChangedListener: OnPreferenceChangedListener?) {
-            this.onPreferenceChangedListener = onPreferenceChangedListener
-        }
-    }
-
     companion object {
         val TAG = SubscriptionsFragment::class.simpleName ?: "Anonymous"
 
diff --git a/app/src/main/res/layout/dialog_switch_preference.xml b/app/src/main/res/layout/dialog_switch_preference.xml
deleted file mode 100644
index 49882cad..00000000
--- a/app/src/main/res/layout/dialog_switch_preference.xml
+++ /dev/null
@@ -1,16 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<LinearLayout
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:tools="http://schemas.android.com/tools"
-    android:layout_width="match_parent"
-    android:layout_height="match_parent"
-    android:id="@+id/dialog_switch_preference"
-    android:padding="24dp">
-
-    <androidx.appcompat.widget.SwitchCompat
-        android:id="@+id/dialogSwitch"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        tools:text="Switch" />
-
-</LinearLayout>
diff --git a/app/src/main/res/layout/opml_selection.xml b/app/src/main/res/layout/opml_selection.xml
deleted file mode 100644
index 735c7f87..00000000
--- a/app/src/main/res/layout/opml_selection.xml
+++ /dev/null
@@ -1,44 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<RelativeLayout
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:tools="http://schemas.android.com/tools"
-    android:layout_width="match_parent"
-    android:layout_height="match_parent">
-
-    <Button
-        android:id="@+id/butConfirm"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:layout_alignParentBottom="true"
-        android:layout_alignParentRight="true"
-        android:layout_alignParentEnd="true"
-        android:layout_margin="8dp"
-        android:text="@string/confirm_label"
-        style="@style/Widget.MaterialComponents.Button.TextButton" />
-
-    <Button
-        android:id="@+id/butCancel"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:layout_alignParentBottom="true"
-        android:layout_toLeftOf="@+id/butConfirm"
-        android:layout_toStartOf="@+id/butConfirm"
-        android:layout_margin="8dp"
-        android:text="@string/cancel_label"
-        style="@style/Widget.MaterialComponents.Button.TextButton" />
-
-    <ListView
-        android:id="@+id/feedlist"
-        android:layout_width="match_parent"
-        android:layout_height="0dp"
-        android:layout_above="@id/butConfirm"
-        android:layout_alignParentTop="true"
-        tools:listitem="@android:layout/simple_list_item_multiple_choice" />
-
-    <ProgressBar
-        android:id="@+id/progressBar"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:layout_centerInParent="true" />
-
-</RelativeLayout>
diff --git a/app/src/main/res/menu/episodes.xml b/app/src/main/res/menu/episodes.xml
index 578b57c1..19935c79 100644
--- a/app/src/main/res/menu/episodes.xml
+++ b/app/src/main/res/menu/episodes.xml
@@ -6,8 +6,14 @@
     <item
         android:id="@+id/action_search"
         android:icon="@drawable/ic_search"
-        custom:showAsAction="always"
-        android:title="@string/search_label"/>
+        android:title="@string/search_label"
+        custom:showAsAction="always"/>
+
+    <item
+        android:id="@+id/episodes_sort"
+        android:icon="@drawable/arrows_sort"
+        android:title="@string/sort"
+        custom:showAsAction="always" />
 
     <item
         android:id="@+id/filter_items"
@@ -17,19 +23,19 @@
         custom:showAsAction="always"/>
 
     <item
-        android:id="@+id/episodes_sort"
-        android:icon="@drawable/arrows_sort"
-        android:title="@string/sort"
-        custom:showAsAction="ifRoom" />
+        android:id="@+id/clear_history_item"
+        android:icon="@drawable/ic_delete"
+        android:title="@string/clear_history_label"
+        custom:showAsAction="never"/>
 
-<!--    <item-->
-<!--        android:id="@+id/refresh_item"-->
-<!--        android:title="@string/refresh_label"-->
-<!--        android:menuCategory="container"-->
-<!--        custom:showAsAction="never" />-->
+    <item
+        android:id="@+id/reconcile"
+        android:title="@string/reconcile_label"
+        custom:showAsAction="never" />
 
-<!--    <item-->
-<!--        android:id="@+id/switch_queue"-->
-<!--        android:title="@string/switch_queue" />-->
+    <item
+        android:id="@+id/clear_new"
+        android:title="@string/clear_new_label"
+        custom:showAsAction="never" />
 
 </menu>
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 772f37bc..7a7dda45 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -127,6 +127,7 @@
     <string name="new_synth_yt_label">New synthetic Youtube</string>
     <string name="toggle_grid_list">Toggle grid list</string>
     <string name="refreshing_label">Refreshing</string>
+    <string name="clear_new_label">Clear new</string>
     <string name="reconcile_label">Reconcile</string>
     <string name="chapters_label">Chapters</string>
 
@@ -213,6 +214,7 @@
 
 
     <string name="filtered_label">Filtered</string>
+    <string name="progressing_label">Processing</string>
 
     <string name="open_podcast">Open podcast</string>
     <string name="open">Open</string>
@@ -441,6 +443,8 @@
     <string name="import_export_search_keywords">backup, restore</string>
     <string name="appearance">Appearance</string>
     <string name="external_elements">External elements</string>
+    <string name="create_YT_syndicates">Create YT syndicates</string>
+    <string name="add_to_feed">Add to feed</string>
     <string name="interruptions">Interruptions</string>
     <string name="playback_control">Playback control</string>
     <string name="reassign_hardware_buttons">Reassign hardware buttons</string>
diff --git a/changelog.md b/changelog.md
index 3c5a47c4..82fb4bd4 100644
--- a/changelog.md
+++ b/changelog.md
@@ -1,3 +1,19 @@
+# 6.15.0
+
+* added a combo Epiosdes fragment with easy access to various filters
+	* merged AllEpisodes, History and Download into Episodes
+	* other easy accesses include: New, Planned (for Soon and Later), Repeats (for Again and Forever), Liked (for Good and Super)
+	* New episodes can be cleared via the manu
+* drawer items customization is disabled in Settings
+* drawer includes 5 most recent feeds
+* rearranged routines in ImportExportPreferences
+* importing OPML file is done within the activity, no longer starts OpmlImportActivity
+* removed OpmlImportActivity
+* getting shared youtube media has a new interface to select feed
+* cleaned up SwipeActions, removed the need for filter
+* more Compose migrations
+* media3 update to 1.5.0
+
 # 6.14.8
 
 * fixed issues in tags setting
diff --git a/fastlane/metadata/android/en-US/changelogs/3020308.txt b/fastlane/metadata/android/en-US/changelogs/3020308.txt
new file mode 100644
index 00000000..f2824b0a
--- /dev/null
+++ b/fastlane/metadata/android/en-US/changelogs/3020308.txt
@@ -0,0 +1,15 @@
+ Version 6.15.0
+
+* added a combo Epiosdes fragment with easy access to various filters
+	* merged AllEpisodes, History and Download into Episodes
+	* other easy accesses include: New, Planned (for Soon and Later), Repeats (for Again and Forever), Liked (for Good and Super)
+	* New episodes can be cleared via the manu
+* drawer items customization is disabled in Settings
+* drawer includes 5 most recent feeds
+* rearranged routines in ImportExportPreferences
+* importing OPML file is done within the activity, no longer starts OpmlImportActivity
+* removed OpmlImportActivity
+* getting shared youtube media has a new interface to select feed
+* cleaned up SwipeActions, removed the need for filter
+* more Compose migrations
+* media3 update to 1.5.0
diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml
index 3bbe0d7e..d8c0f21b 100644
--- a/gradle/libs.versions.toml
+++ b/gradle/libs.versions.toml
@@ -30,10 +30,10 @@ libraryBase = "2.1.0"
 lifecycleRuntimeKtx = "2.8.7"
 material3 = "1.3.1"
 materialVersion = "1.12.0"
-media3Common = "1.4.1"
-media3Session = "1.4.1"
-media3Ui = "1.4.1"
-media3Exoplayer = "1.4.1"
+media3Common = "1.5.0"
+media3Session = "1.5.0"
+media3Ui = "1.5.0"
+media3Exoplayer = "1.5.0"
 mediarouter = "1.7.0"
 okhttp = "4.12.0"
 okhttpUrlconnection = "4.12.0"