From 0c801717ac6fd8d4c7a82dac958855d855da15d6 Mon Sep 17 00:00:00 2001 From: Xilin Jia <6257601+XilinJia@users.noreply.github.com> Date: Tue, 22 Oct 2024 18:40:35 +0100 Subject: [PATCH] 6.12.0 commit --- README.md | 2 +- app/build.gradle | 4 +- .../service/playback/MediaPlayerBaseTest.kt | 2 +- .../service/playback/TaskManagerTest.kt | 2 +- .../kotlin/ac/test/podcini/ui/UITestUtils.kt | 3 +- .../service/DownloadServiceInterfaceImpl.kt | 2 +- .../mdiq/podcini/net/feed/LocalFeedUpdater.kt | 2 +- .../mdiq/podcini/playback/base/InTheatre.kt | 6 +- .../playback/service/PlaybackService.kt | 4 +- .../storage/algorithms/AutoCleanups.kt | 3 +- .../mdiq/podcini/storage/database/Episodes.kt | 8 +- .../ac/mdiq/podcini/storage/database/Feeds.kt | 3 +- .../podcini/storage/database/LogsAndStats.kt | 2 +- .../mdiq/podcini/storage/database/Queues.kt | 33 ++++-- .../mdiq/podcini/storage/database/RealmDB.kt | 20 +++- .../ac/mdiq/podcini/storage/model/Episode.kt | 21 +--- .../podcini/storage/model/EpisodeFilter.kt | 20 ++-- .../ac/mdiq/podcini/storage/model/Feed.kt | 2 +- .../mdiq/podcini/storage/model/PlayState.kt | 23 ++++ .../podcini/ui/actions/EpisodeActionButton.kt | 54 ++++----- .../ac/mdiq/podcini/ui/actions/SwipeAction.kt | 1 + .../mdiq/podcini/ui/actions/SwipeActions.kt | 67 ++++------- .../mdiq/podcini/ui/activity/MainActivity.kt | 4 + .../ac/mdiq/podcini/ui/compose/EpisodesVM.kt | 104 +++++++++++++++--- .../ui/fragment/AudioPlayerFragment.kt | 31 ++++-- .../ui/fragment/BaseEpisodesFragment.kt | 13 ++- .../podcini/ui/fragment/DownloadsFragment.kt | 17 +-- .../ui/fragment/EpisodeInfoFragment.kt | 79 ++++++------- .../ui/fragment/FeedEpisodesFragment.kt | 81 +++++++++----- .../podcini/ui/fragment/FeedInfoFragment.kt | 38 ++++--- .../mdiq/podcini/ui/fragment/LogsFragment.kt | 25 +++-- .../podcini/ui/fragment/NavDrawerFragment.kt | 28 +++-- .../podcini/ui/fragment/OnlineFeedFragment.kt | 6 +- .../podcini/ui/fragment/QueuesFragment.kt | 6 +- .../ui/fragment/QuickDiscoveryFragment.kt | 13 ++- .../ui/fragment/SearchResultsFragment.kt | 6 +- .../ui/fragment/SubscriptionsFragment.kt | 33 +++--- .../ui/statistics/StatisticsFragment.kt | 2 +- .../main/res/drawable/baseline_build_24.xml | 5 + .../drawable/baseline_download_done_24.xml | 7 ++ .../res/drawable/baseline_fiber_new_24.xml | 5 + .../res/drawable/baseline_local_play_24.xml | 5 + .../res/drawable/baseline_new_label_24.xml | 5 + .../baseline_play_circle_outline_24.xml | 5 + .../drawable/baseline_play_disabled_24.xml | 5 + .../drawable/baseline_visibility_off_24.xml | 5 + .../res/drawable/baseline_watch_later_24.xml | 5 + app/src/main/res/layout/addfeed.xml | 26 ++--- .../layout/base_episodes_list_fragment.xml | 36 ------ ...info_fragment.xml => compose_fragment.xml} | 13 +-- .../main/res/layout/downloads_fragment.xml | 35 ------ .../res/layout/feed_item_list_fragment.xml | 35 ------ app/src/main/res/layout/feedinfo.xml | 35 ------ .../res/layout/fragment_search_results.xml | 35 ------ app/src/main/res/layout/logs_fragment.xml | 22 ---- app/src/main/res/layout/nav_list.xml | 16 --- .../res/layout/online_feedview_fragment.xml | 33 ------ app/src/main/res/layout/queue_fragment.xml | 36 ------ app/src/main/res/layout/search_fragment.xml | 24 ++-- .../menu/episodes_apply_action_speeddial.xml | 45 -------- app/src/main/res/menu/feed_episodes.xml | 12 +- .../main/res/menu/feeditemlist_context.xml | 59 ---------- .../res/menu/multi_select_context_popup.xml | 11 -- .../main/res/menu/multi_select_options.xml | 18 --- app/src/main/res/menu/queue_context.xml | 16 --- app/src/main/res/values-ar/strings.xml | 29 +---- app/src/main/res/values-ast/strings.xml | 6 +- app/src/main/res/values-br/strings.xml | 28 ++--- app/src/main/res/values-ca/strings.xml | 21 +--- app/src/main/res/values-cs/strings.xml | 26 ++--- app/src/main/res/values-da/strings.xml | 21 +--- app/src/main/res/values-de/strings.xml | 21 +--- app/src/main/res/values-es/strings.xml | 23 +--- app/src/main/res/values-et/strings.xml | 16 +-- app/src/main/res/values-eu/strings.xml | 21 +--- app/src/main/res/values-fa/strings.xml | 21 +--- app/src/main/res/values-fi/strings.xml | 21 +--- app/src/main/res/values-fr/strings.xml | 23 +--- app/src/main/res/values-gl/strings.xml | 20 +--- app/src/main/res/values-hu/strings.xml | 21 +--- app/src/main/res/values-in/strings.xml | 19 +--- app/src/main/res/values-it/strings.xml | 23 +--- app/src/main/res/values-iw/strings.xml | 25 +---- app/src/main/res/values-ja/strings.xml | 18 +-- app/src/main/res/values-ko/strings.xml | 19 +--- app/src/main/res/values-lt/strings.xml | 21 +--- app/src/main/res/values-nb/strings.xml | 21 +--- app/src/main/res/values-nl/strings.xml | 21 +--- app/src/main/res/values-pl/strings.xml | 25 +---- app/src/main/res/values-pt-rBR/strings.xml | 23 +--- app/src/main/res/values-pt/strings.xml | 23 +--- app/src/main/res/values-ro/strings.xml | 23 +--- app/src/main/res/values-ru/strings.xml | 25 +---- app/src/main/res/values-sk/strings.xml | 25 +---- app/src/main/res/values-sl/strings.xml | 21 +--- app/src/main/res/values-sv/strings.xml | 21 +--- app/src/main/res/values-tr/strings.xml | 21 +--- app/src/main/res/values-uk/strings.xml | 25 +---- app/src/main/res/values-zh-rCN/strings.xml | 19 +--- app/src/main/res/values-zh-rTW/strings.xml | 11 +- app/src/main/res/values/strings.xml | 18 +-- .../ac/mdiq/podcini/feed/FeedItemMother.kt | 2 +- .../ac/mdiq/podcini/storage/DbCleanupTests.kt | 10 +- .../storage/DbNullCleanupAlgorithmTest.kt | 2 +- .../DbPlayQueueCleanupAlgorithmTest.kt | 2 +- .../ac/mdiq/podcini/storage/DbTasksTest.kt | 14 +-- .../ac/mdiq/podcini/storage/DbWriterTest.kt | 36 +++--- .../storage/EpisodeDuplicateGuesserTest.kt | 2 +- .../ExceptFavoriteCleanupAlgorithmTest.kt | 6 +- .../ItemEnqueuePositionCalculatorTest.kt | 2 +- changelog.md | 15 +++ .../android/en-US/changelogs/3020278.txt | 14 +++ 112 files changed, 791 insertions(+), 1403 deletions(-) create mode 100644 app/src/main/kotlin/ac/mdiq/podcini/storage/model/PlayState.kt create mode 100644 app/src/main/res/drawable/baseline_build_24.xml create mode 100644 app/src/main/res/drawable/baseline_download_done_24.xml create mode 100644 app/src/main/res/drawable/baseline_fiber_new_24.xml create mode 100644 app/src/main/res/drawable/baseline_local_play_24.xml create mode 100644 app/src/main/res/drawable/baseline_new_label_24.xml create mode 100644 app/src/main/res/drawable/baseline_play_circle_outline_24.xml create mode 100644 app/src/main/res/drawable/baseline_play_disabled_24.xml create mode 100644 app/src/main/res/drawable/baseline_visibility_off_24.xml create mode 100644 app/src/main/res/drawable/baseline_watch_later_24.xml delete mode 100644 app/src/main/res/layout/base_episodes_list_fragment.xml rename app/src/main/res/layout/{episode_info_fragment.xml => compose_fragment.xml} (72%) delete mode 100644 app/src/main/res/layout/downloads_fragment.xml delete mode 100644 app/src/main/res/layout/feed_item_list_fragment.xml delete mode 100644 app/src/main/res/layout/feedinfo.xml delete mode 100644 app/src/main/res/layout/fragment_search_results.xml delete mode 100644 app/src/main/res/layout/logs_fragment.xml delete mode 100644 app/src/main/res/layout/nav_list.xml delete mode 100644 app/src/main/res/layout/online_feedview_fragment.xml delete mode 100644 app/src/main/res/layout/queue_fragment.xml delete mode 100644 app/src/main/res/menu/episodes_apply_action_speeddial.xml delete mode 100644 app/src/main/res/menu/feeditemlist_context.xml delete mode 100644 app/src/main/res/menu/multi_select_context_popup.xml delete mode 100644 app/src/main/res/menu/multi_select_options.xml delete mode 100644 app/src/main/res/menu/queue_context.xml create mode 100644 fastlane/metadata/android/en-US/changelogs/3020278.txt diff --git a/README.md b/README.md index 5f535bbd..aa98a063 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@ An open source podcast instrument, attuned to Puccini ![Puccini](./images/Puccin [Amazon](https://www.amazon.com/%E8%B4%BE%E8%A5%BF%E6%9E%97-Podcini-R/dp/B0D9WR8P13) #### Podcini.R 6.10 allows creating synthetic podcast and shelving any episdes to any synthetic podcasts -#### Podcini.R version 6.5 as a major step forward brings YouTube contents in the app. Channels can be searched, received from share, subscribed. Since 6.6, podcasts, playlists as well as single media from Youtube and YT Music can be shared to Podcini. For more see the Youtube section below or the changelogs +#### Podcini.R version 6.5 as a major step forward brings YouTube contents in the app. Channels can be searched, received from share, subscribed. Podcasts, playlists as well as single media from Youtube and YT Music can be shared to Podcini. For more see the Youtube section below or the changelogs That means finally: [Nessun dorma](https://www.youtube.com/watch?v=cWc7vYjgnTs) #### For Podcini to show up on car's HUD with Android Auto, please read AnroidAuto.md for instructions. #### If you need to cast to an external speaker, you should install the "play" apk, not the "free" apk, that's about the difference between the two. diff --git a/app/build.gradle b/app/build.gradle index ef76a654..c073e74e 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -31,8 +31,8 @@ android { testApplicationId "ac.mdiq.podcini.tests" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" - versionCode 3020277 - versionName "6.11.7" + versionCode 3020278 + versionName "6.12.0" applicationId "ac.mdiq.podcini.R" def commit = "" diff --git a/app/src/androidTest/kotlin/ac/test/podcini/service/playback/MediaPlayerBaseTest.kt b/app/src/androidTest/kotlin/ac/test/podcini/service/playback/MediaPlayerBaseTest.kt index 9be520eb..749b2fbf 100644 --- a/app/src/androidTest/kotlin/ac/test/podcini/service/playback/MediaPlayerBaseTest.kt +++ b/app/src/androidTest/kotlin/ac/test/podcini/service/playback/MediaPlayerBaseTest.kt @@ -106,7 +106,7 @@ class MediaPlayerBaseTest { VolumeAdaptionSetting.OFF, null, null) f.preferences = prefs f.episodes.clear() - val i = Episode(0, "t", "i", "l", Date(), Episode.PlayState.UNPLAYED.code, f) + val i = Episode(0, "t", "i", "l", Date(), PlayState.UNPLAYED.code, f) f.episodes.add(i) val media = EpisodeMedia(0, i, 0, 0, 0, "audio/wav", fileUrl, downloadUrl, fileUrl != null, null, 0, 0) i.setMedia(media) diff --git a/app/src/androidTest/kotlin/ac/test/podcini/service/playback/TaskManagerTest.kt b/app/src/androidTest/kotlin/ac/test/podcini/service/playback/TaskManagerTest.kt index c4ea6406..b048fef6 100644 --- a/app/src/androidTest/kotlin/ac/test/podcini/service/playback/TaskManagerTest.kt +++ b/app/src/androidTest/kotlin/ac/test/podcini/service/playback/TaskManagerTest.kt @@ -65,7 +65,7 @@ class TaskManagerTest { val f = Feed(0, null, "title", "link", "d", null, null, null, null, "id", null, "null", "url") f.episodes.clear() for (i in 0 until NUM_ITEMS) { - f.episodes.add(Episode(0, pref + i, pref + i, "link", Date(), Episode.PlayState.PLAYED.code, f)) + f.episodes.add(Episode(0, pref + i, pref + i, "link", Date(), PlayState.PLAYED.code, f)) } // val adapter = getInstance() // adapter.open() diff --git a/app/src/androidTest/kotlin/ac/test/podcini/ui/UITestUtils.kt b/app/src/androidTest/kotlin/ac/test/podcini/ui/UITestUtils.kt index e8be5fc3..9d776275 100644 --- a/app/src/androidTest/kotlin/ac/test/podcini/ui/UITestUtils.kt +++ b/app/src/androidTest/kotlin/ac/test/podcini/ui/UITestUtils.kt @@ -3,6 +3,7 @@ package de.test.podcini.ui import ac.mdiq.podcini.storage.model.Feed import ac.mdiq.podcini.storage.model.Episode import ac.mdiq.podcini.storage.model.EpisodeMedia +import ac.mdiq.podcini.storage.model.PlayState import ac.mdiq.podcini.util.EventFlow import ac.mdiq.podcini.util.FlowEvent import android.content.Context @@ -113,7 +114,7 @@ class UITestUtils(private val context: Context) { val items: MutableList = ArrayList() for (j in 0 until NUM_ITEMS_PER_FEED) { val item = Episode(j.toLong(), "Feed " + (i + 1) + ": Item " + (j + 1), "item$j", - "http://example.com/feed$i/item/$j", Date(), Episode.PlayState.UNPLAYED.code, feed) + "http://example.com/feed$i/item/$j", Date(), PlayState.UNPLAYED.code, feed) items.add(item) if (!hostTextOnlyFeeds) { 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 5c3a1960..e696bb1b 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 @@ -362,7 +362,7 @@ class DownloadServiceInterfaceImpl : DownloadServiceInterface() { .addTag(WORK_TAG_EPISODE_URL + item.media!!.downloadUrl) if (enqueueDownloadedEpisodes()) { if (item.feed?.preferences?.queue != null) - runBlocking { Queues.addToQueueSync(false, item, item.feed?.preferences?.queue) } + runBlocking { Queues.addToQueueSync(item, item.feed?.preferences?.queue) } workRequest.addTag(WORK_DATA_WAS_QUEUED) } workRequest.setInputData(Data.Builder().putLong(WORK_DATA_MEDIA_ID, item.media!!.id).build()) diff --git a/app/src/main/kotlin/ac/mdiq/podcini/net/feed/LocalFeedUpdater.kt b/app/src/main/kotlin/ac/mdiq/podcini/net/feed/LocalFeedUpdater.kt index ebabf73d..aa89b6c7 100644 --- a/app/src/main/kotlin/ac/mdiq/podcini/net/feed/LocalFeedUpdater.kt +++ b/app/src/main/kotlin/ac/mdiq/podcini/net/feed/LocalFeedUpdater.kt @@ -121,7 +121,7 @@ object LocalFeedUpdater { } private fun createFeedItem(feed: Feed, file: FastDocumentFile, context: Context): Episode { - val item = Episode(0L, file.name, UUID.randomUUID().toString(), file.name, Date(file.lastModified), Episode.PlayState.UNPLAYED.code, feed) + val item = Episode(0L, file.name, UUID.randomUUID().toString(), file.name, Date(file.lastModified), PlayState.UNPLAYED.code, feed) item.disableAutoDownload() val size = file.length val media = EpisodeMedia(0, item, 0, 0, size, file.type, diff --git a/app/src/main/kotlin/ac/mdiq/podcini/playback/base/InTheatre.kt b/app/src/main/kotlin/ac/mdiq/podcini/playback/base/InTheatre.kt index c200519e..68df9b7f 100644 --- a/app/src/main/kotlin/ac/mdiq/podcini/playback/base/InTheatre.kt +++ b/app/src/main/kotlin/ac/mdiq/podcini/playback/base/InTheatre.kt @@ -82,9 +82,7 @@ object InTheatre { } upsert(curQueue_) {} } - upsert(curQueue) { - it.update() - } + upsert(curQueue) { it.update() } } Logd(TAG, "starting curState") @@ -135,10 +133,12 @@ object InTheatre { val type = curState.curMediaType.toInt() if (type == EpisodeMedia.PLAYABLE_TYPE_FEEDMEDIA) { val mediaId = curState.curMediaId + Logd(TAG, "loadPlayableFromPreferences getting mediaId: $mediaId") if (mediaId != 0L) { curMedia = getEpisodeMedia(mediaId) if (curEpisode != null) curEpisode = (curMedia as EpisodeMedia).episodeOrFetch() } + Logd(TAG, "loadPlayableFromPreferences: curMedia: ${curMedia?.getIdentifier()}") } else Log.e(TAG, "Could not restore Playable object from preferences") } } diff --git a/app/src/main/kotlin/ac/mdiq/podcini/playback/service/PlaybackService.kt b/app/src/main/kotlin/ac/mdiq/podcini/playback/service/PlaybackService.kt index a4885558..567fdfb5 100644 --- a/app/src/main/kotlin/ac/mdiq/podcini/playback/service/PlaybackService.kt +++ b/app/src/main/kotlin/ac/mdiq/podcini/playback/service/PlaybackService.kt @@ -393,7 +393,7 @@ class PlaybackService : MediaLibraryService() { if (ended || smartMarkAsPlayed || autoSkipped || (skipped && !shouldSkipKeepEpisode())) { Logd(TAG, "onPostPlayback ended: $ended smartMarkAsPlayed: $smartMarkAsPlayed autoSkipped: $autoSkipped skipped: $skipped") // only mark the item as played if we're not keeping it anyways - item = setPlayStateSync(Episode.PlayState.PLAYED.code, ended || (skipped && smartMarkAsPlayed), item!!) + item = setPlayStateSync(PlayState.PLAYED.code, ended || (skipped && smartMarkAsPlayed), item!!) val action = item?.feed?.preferences?.autoDeleteAction val shouldAutoDelete = (action == AutoDeleteAction.ALWAYS || (action == AutoDeleteAction.GLOBAL && item?.feed != null && shouldAutoDeleteItem(item!!.feed!!))) @@ -1189,7 +1189,7 @@ class PlaybackService : MediaLibraryService() { if (media != null) { media.setPosition(position) media.setLastPlayedTime(System.currentTimeMillis()) - if (it.isNew) it.playState = Episode.PlayState.UNPLAYED.code + if (it.isNew) it.playState = PlayState.UNPLAYED.code if (media.startPosition >= 0 && media.getPosition() > media.startPosition) media.playedDuration = (media.playedDurationWhenStarted + media.getPosition() - media.startPosition) } diff --git a/app/src/main/kotlin/ac/mdiq/podcini/storage/algorithms/AutoCleanups.kt b/app/src/main/kotlin/ac/mdiq/podcini/storage/algorithms/AutoCleanups.kt index c5029cd8..57d311ba 100644 --- a/app/src/main/kotlin/ac/mdiq/podcini/storage/algorithms/AutoCleanups.kt +++ b/app/src/main/kotlin/ac/mdiq/podcini/storage/algorithms/AutoCleanups.kt @@ -12,6 +12,7 @@ import ac.mdiq.podcini.storage.database.Queues.getInQueueEpisodeIds 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.storage.model.PlayState import ac.mdiq.podcini.util.Logd import android.content.Context import android.util.Log @@ -182,7 +183,7 @@ object AutoCleanups { val idsInQueues = getInQueueEpisodeIds() val mostRecentDateForDeletion = calcMostRecentDateForDeletion(Date()) for (item in downloadedItems) { - if (item.media != null && item.media!!.downloaded && !idsInQueues.contains(item.id) && item.isPlayed() && !item.isFavorite) { + if (item.media != null && item.media!!.downloaded && !idsInQueues.contains(item.id) && item.playState >= PlayState.PLAYED.code && !item.isFavorite) { val media = item.media // make sure this candidate was played at least the proper amount of days prior to now if (media?.playbackCompletionDate != null && media.playbackCompletionDate!!.before(mostRecentDateForDeletion)) candidates.add(item) diff --git a/app/src/main/kotlin/ac/mdiq/podcini/storage/database/Episodes.kt b/app/src/main/kotlin/ac/mdiq/podcini/storage/database/Episodes.kt index 8843dea3..fd3bcf38 100644 --- a/app/src/main/kotlin/ac/mdiq/podcini/storage/database/Episodes.kt +++ b/app/src/main/kotlin/ac/mdiq/podcini/storage/database/Episodes.kt @@ -18,11 +18,7 @@ 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.Episode.PlayState -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.* import ac.mdiq.podcini.storage.utils.EpisodesPermutors.getPermutor import ac.mdiq.podcini.storage.utils.FilesUtils.getMediafilename import ac.mdiq.podcini.util.EventFlow @@ -287,7 +283,7 @@ object Episodes { var episode_ = episode if (!episode.isManaged()) episode_ = realm.query(Episode::class).query("id == $0", episode.id).first().find() ?: episode val result = upsert(episode_) { - if (played >= PlayState.NEW.code && played <= PlayState.BUILDING.code) it.playState = played + if (played != PlayState.UNSPECIFIED.code) it.playState = played else { if (it.playState == PlayState.PLAYED.code) it.playState = PlayState.UNPLAYED.code else it.playState = PlayState.PLAYED.code 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 647a3219..6f0bd98a 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 @@ -287,6 +287,7 @@ object Feeds { episode.feed = savedFeed episode.id = idLong++ episode.feedId = savedFeed.id + episode.playState = PlayState.NEW.code if (episode.media != null) { episode.media!!.id = episode.id if (!savedFeed.hasVideoMedia && episode.media!!.getMediaType() == MediaType.VIDEO) savedFeed.hasVideoMedia = true @@ -300,7 +301,7 @@ object Feeds { episode.setNew() if (savedFeed.preferences?.autoAddNewToQueue == true) { val q = savedFeed.preferences?.queue - if (q != null) runOnIOScope { addToQueueSync(false, episode, q) } + if (q != null) runOnIOScope { addToQueueSync(episode, q) } } } } diff --git a/app/src/main/kotlin/ac/mdiq/podcini/storage/database/LogsAndStats.kt b/app/src/main/kotlin/ac/mdiq/podcini/storage/database/LogsAndStats.kt index bf384659..e398969b 100644 --- a/app/src/main/kotlin/ac/mdiq/podcini/storage/database/LogsAndStats.kt +++ b/app/src/main/kotlin/ac/mdiq/podcini/storage/database/LogsAndStats.kt @@ -56,7 +56,7 @@ object LogsAndStats { feedTotalTime += m.duration if (m.lastPlayedTime in timeFilterFrom.. 0 && m.playedDuration > 0) || m.episodeOrFetch()?.playState == Episode.PlayState.PLAYED.code || m.position > 0) { + if ((m.playbackCompletionTime > 0 && m.playedDuration > 0) || m.episodeOrFetch()?.playState == PlayState.PLAYED.code || m.position > 0) { episodesStarted += 1 feedPlayedTime += m.duration } 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 31c8eb6a..52683d0a 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 @@ -6,6 +6,7 @@ import ac.mdiq.podcini.playback.base.InTheatre.curQueue import ac.mdiq.podcini.preferences.UserPreferences import ac.mdiq.podcini.preferences.UserPreferences.appPrefs import ac.mdiq.podcini.storage.database.Episodes.setPlayState +import ac.mdiq.podcini.storage.database.Episodes.setPlayStateSync import ac.mdiq.podcini.storage.database.RealmDB.realm import ac.mdiq.podcini.storage.database.RealmDB.runOnIOScope import ac.mdiq.podcini.storage.database.RealmDB.upsert @@ -77,9 +78,9 @@ object Queues { appPrefs.edit().putString(UserPreferences.Prefs.prefEnqueueLocation.name, location.name).apply() } - fun queueFromName(name: String): PlayQueue? { - return realm.query(PlayQueue::class).query("name == $0", name).first().find() - } +// fun queueFromName(name: String): PlayQueue? { +// return realm.query(PlayQueue::class).query("name == $0", name).first().find() +// } fun getInQueueEpisodeIds(): Set { Logd(TAG, "getQueueIDList() called") @@ -98,13 +99,13 @@ object Queues { * @param episodes the Episode objects that should be added to the queue. */ @UnstableApi @JvmStatic @Synchronized - fun addToQueue(markAsUnplayed: Boolean, vararg episodes: Episode) : Job { + fun addToQueue(vararg episodes: Episode) : Job { Logd(TAG, "addToQueue( ... ) called") return runOnIOScope { if (episodes.isEmpty()) return@runOnIOScope var queueModified = false - val markAsUnplayeds = mutableListOf() + val setInQueue = mutableListOf() val events: MutableList = ArrayList() val updatedItems: MutableList = ArrayList() val positionCalculator = EnqueuePositionPolicy(enqueueLocation) @@ -121,7 +122,7 @@ object Queues { updatedItems.add(episode) qItems.add(insertPosition, episode) queueModified = true - if (episode.isNew) markAsUnplayeds.add(episode) + if (episode.playState < PlayState.INQUEUE.code) setInQueue.add(episode) insertPosition++ } if (queueModified) { @@ -134,13 +135,13 @@ object Queues { } for (event in events) EventFlow.postEvent(event) - if (markAsUnplayed && markAsUnplayeds.size > 0) setPlayState(Episode.PlayState.UNPLAYED.code, false, *markAsUnplayeds.toTypedArray()) + setPlayState(PlayState.INQUEUE.code, false, *setInQueue.toTypedArray()) // if (performAutoDownload) autodownloadEpisodeMedia(context) } } } - suspend fun addToQueueSync(markAsUnplayed: Boolean, episode: Episode, queue_: PlayQueue? = null) { + suspend fun addToQueueSync(episode: Episode, queue_: PlayQueue? = null) { Logd(TAG, "addToQueueSync( ... ) called") val queue = queue_ ?: curQueue if (queue.episodeIds.contains(episode.id)) return @@ -157,7 +158,7 @@ object Queues { } if (queue.id == curQueue.id) curQueue = queueNew - if (markAsUnplayed && episode.isNew) setPlayState(Episode.PlayState.UNPLAYED.code, false, episode) + if (episode.playState < PlayState.INQUEUE.code) setPlayState(PlayState.INQUEUE.code, false, episode) if (queue.id == curQueue.id) EventFlow.postEvent(FlowEvent.QueueEvent.added(episode, insertPosition)) // if (performAutoDownload) autodownloadEpisodeMedia(context) } @@ -194,6 +195,9 @@ object Queues { it.episodeIds.clear() it.update() } + for (e in curQueue.episodes) { + if (e.playState < PlayState.SKIPPED.code) setPlayState(PlayState.SKIPPED.code, false, e) + } curQueue.episodes.clear() EventFlow.postEvent(FlowEvent.QueueEvent.cleared()) } @@ -230,7 +234,7 @@ object Queues { var queue = queue_ ?: curQueue if (queue.size() == 0) return - val events: MutableList = ArrayList() + val events: MutableList = mutableListOf() val indicesToRemove: MutableList = mutableListOf() val qItems = queue.episodes.toMutableList() val eList = episodes.toList() @@ -239,6 +243,7 @@ object Queues { if (indexOfItemWithId(eList, episode.id) >= 0) { Logd(TAG, "removing from queue: ${episode.id} ${episode.title}") indicesToRemove.add(i) + if (episode.playState < PlayState.SKIPPED.code) setPlayState(PlayState.SKIPPED.code, false, episode) if (queue.id == curQueue.id) events.add(FlowEvent.QueueEvent.removed(episode)) } } @@ -270,6 +275,10 @@ object Queues { if (q.size() == 0 || q.id == curQueue.id) continue idsInQueuesToRemove = q.episodeIds.intersect(episodeIds.toSet()).toMutableSet() if (idsInQueuesToRemove.isNotEmpty()) { + val eList = realm.query(Episode::class).query("id IN $0", idsInQueuesToRemove).find() + for (e in eList) { + if (e.playState < PlayState.SKIPPED.code) setPlayState(PlayState.SKIPPED.code, false, e) + } upsert(q) { it.idsBinList.removeAll(idsInQueuesToRemove) it.idsBinList.addAll(idsInQueuesToRemove) @@ -288,6 +297,10 @@ object Queues { } idsInQueuesToRemove = q.episodeIds.intersect(episodeIds.toSet()).toMutableSet() if (idsInQueuesToRemove.isNotEmpty()) { + val eList = realm.query(Episode::class).query("id IN $0", idsInQueuesToRemove).find() + for (e in eList) { + if (e.playState < PlayState.SKIPPED.code) setPlayState(PlayState.SKIPPED.code, false, e) + } curQueue = upsert(q) { it.idsBinList.removeAll(idsInQueuesToRemove) it.idsBinList.addAll(idsInQueuesToRemove) 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 e6ac311a..7e751b6f 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(27) + .schemaVersion(28) .migration({ mContext -> val oldRealm = mContext.oldRealm // old realm using the previous schema val newRealm = mContext.newRealm // new realm using the new schema @@ -104,8 +104,22 @@ object RealmDB { // ) // } } - }) - .build() + if (oldRealm.schemaVersion() < 28) { + Logd(TAG, "migrating DB from below 27") + mContext.enumerate(className = "Episode") { oldObject: DynamicRealmObject, newObject: DynamicMutableRealmObject? -> + newObject?.run { + if (oldObject.getValue(fieldName = "playState") == 1L) { + set("playState", 10L) + } else { + val media = oldObject.getObject(propertyName = "media") + var position = 0L + if (media != null) position = media.getValue(propertyName = "position", Long::class) ?: 0 + if (position > 0) set("playState", 5L) + } + } + } + } + }).build() realm = Realm.open(config) } 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 f289ae98..750aebdd 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 @@ -86,7 +86,7 @@ class Episode : RealmObject { var rating: Int = Rating.UNRATED.code @Ignore - var isFavorite: Boolean = (rating == 2) + var isFavorite: Boolean = (rating == Rating.FAVORITE.code) private set var comment: String = "" @@ -142,7 +142,7 @@ class Episode : RealmObject { val isRemote = mutableStateOf(false) constructor() { - this.playState = PlayState.UNPLAYED.code + this.playState = PlayState.NEW.code } /** @@ -313,19 +313,10 @@ class Episode : RealmObject { return result } - fun shiftRating(): Int { - val nr = rating + 1 - return if (nr <= Rating.FAVORITE.code) nr else Rating.TRASH.code - } - - enum class PlayState(val code: Int) { - UNSPECIFIED(-2), - NEW(-1), - UNPLAYED(0), - PLAYED(1), - BUILDING(2), - ABANDONED(3) - } +// fun shiftRating(): Int { +// val nr = rating + 1 +// return if (nr <= Rating.FAVORITE.code) nr else Rating.TRASH.code +// } companion object { val TAG: String = Episode::class.simpleName ?: "Anonymous" 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 305c7561..cf6af9f1 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 @@ -38,8 +38,8 @@ class EpisodeFilter(vararg properties: String) : Serializable { fun matches(item: Episode): Boolean { when { showNew && !item.isNew -> return false - showPlayed && !item.isPlayed() -> return false - showUnplayed && item.isPlayed() -> return false + showPlayed && item.playState < PlayState.PLAYED.code -> return false + showUnplayed && item.playState >= PlayState.PLAYED.code -> return false showPaused && !item.isInProgress -> return false showNotPaused && item.isInProgress -> return false showDownloaded && !item.isDownloaded -> return false @@ -58,18 +58,18 @@ class EpisodeFilter(vararg properties: String) : Serializable { // filter on queues does not have a query string so it's not applied on query results, need to filter separately fun matchesForQueues(item: Episode): Boolean { - when { - showQueued && !inAnyQueue(item) -> return false - showNotQueued && inAnyQueue(item) -> return false - else -> return true - } + return when { + showQueued && !inAnyQueue(item) -> false + showNotQueued && inAnyQueue(item) -> false + else -> true + } } fun queryString(): String { val statements: MutableList = ArrayList() when { - showPlayed -> statements.add("playState == 1 ") - showUnplayed -> statements.add(" playState != 1 ") // Match "New" items (read = -1) as well + showPlayed -> statements.add("playState >= ${PlayState.PLAYED.code}") + showUnplayed -> statements.add(" playState < ${PlayState.PLAYED.code}> ") // Match "New" items (read = -1) as well showNew -> statements.add("playState == -1 ") } when { @@ -127,8 +127,8 @@ class EpisodeFilter(vararg properties: String) : Serializable { auto_downloadable, not_auto_downloadable } - companion object { + companion object { @JvmStatic fun unfiltered(): EpisodeFilter { return EpisodeFilter("") diff --git a/app/src/main/kotlin/ac/mdiq/podcini/storage/model/Feed.kt b/app/src/main/kotlin/ac/mdiq/podcini/storage/model/Feed.kt index c14331d6..7f044050 100644 --- a/app/src/main/kotlin/ac/mdiq/podcini/storage/model/Feed.kt +++ b/app/src/main/kotlin/ac/mdiq/podcini/storage/model/Feed.kt @@ -283,7 +283,7 @@ class Feed : RealmObject { } fun getVirtualQueueItems(): List { - var qString = "feedId == $id AND playState != ${Episode.PlayState.PLAYED.code}" + var qString = "feedId == $id AND playState != ${PlayState.PLAYED.code}" // TODO: perhaps need to set prefStreamOverDownload for youtube feeds if (type != FeedType.YOUTUBE.name && preferences?.prefStreamOverDownload != true) qString += " AND media.downloaded == true" val eList_ = realm.query(Episode::class, qString).find().toMutableList() diff --git a/app/src/main/kotlin/ac/mdiq/podcini/storage/model/PlayState.kt b/app/src/main/kotlin/ac/mdiq/podcini/storage/model/PlayState.kt new file mode 100644 index 00000000..6f7776ff --- /dev/null +++ b/app/src/main/kotlin/ac/mdiq/podcini/storage/model/PlayState.kt @@ -0,0 +1,23 @@ +package ac.mdiq.podcini.storage.model + +import ac.mdiq.podcini.R + +enum class PlayState(val code: Int, val res: Int, val userSet: Boolean) { + UNSPECIFIED(-10, R.drawable.ic_questionmark, false), + BUILDING(-2, R.drawable.baseline_build_24, false), + NEW(-1, R.drawable.baseline_fiber_new_24, false), + UNPLAYED(0, R.drawable.baseline_new_label_24, true), + LATER(1, R.drawable.baseline_watch_later_24, true), + SOON(2, R.drawable.baseline_local_play_24, true), + INQUEUE(3, R.drawable.ic_playlist_play_black, false), + INPROGRESS(5, R.drawable.baseline_play_circle_outline_24, false), + SKIPPED(6, R.drawable.ic_skip_24dp, true), + PLAYED(10, R.drawable.ic_mark_played, true), // was 1 + IGNORED(20, R.drawable.baseline_visibility_off_24, true); + + companion object { + fun fromCode(code: Int): PlayState { + return enumValues().firstOrNull { it.code == code } ?: UNSPECIFIED + } + } +} diff --git a/app/src/main/kotlin/ac/mdiq/podcini/ui/actions/EpisodeActionButton.kt b/app/src/main/kotlin/ac/mdiq/podcini/ui/actions/EpisodeActionButton.kt index 6f06addb..6fbab966 100644 --- a/app/src/main/kotlin/ac/mdiq/podcini/ui/actions/EpisodeActionButton.kt +++ b/app/src/main/kotlin/ac/mdiq/podcini/ui/actions/EpisodeActionButton.kt @@ -5,16 +5,16 @@ import ac.mdiq.podcini.net.download.service.DownloadServiceInterface import ac.mdiq.podcini.net.utils.NetworkUtils import ac.mdiq.podcini.playback.PlaybackServiceStarter import ac.mdiq.podcini.playback.base.InTheatre -import ac.mdiq.podcini.preferences.UserPreferences.isStreamOverDownload import ac.mdiq.podcini.playback.base.InTheatre.isCurrentlyPlaying import ac.mdiq.podcini.playback.base.VideoMode import ac.mdiq.podcini.playback.service.PlaybackService import ac.mdiq.podcini.playback.service.PlaybackService.Companion.getPlayerActivityIntent import ac.mdiq.podcini.preferences.UsageStatistics import ac.mdiq.podcini.preferences.UserPreferences +import ac.mdiq.podcini.preferences.UserPreferences.isStreamOverDownload import ac.mdiq.podcini.preferences.UserPreferences.videoPlayMode import ac.mdiq.podcini.receiver.MediaButtonReceiver -import ac.mdiq.podcini.storage.database.Episodes +import ac.mdiq.podcini.storage.database.Episodes.setPlayStateSync import ac.mdiq.podcini.storage.database.RealmDB import ac.mdiq.podcini.storage.model.* import ac.mdiq.podcini.storage.utils.AudioMediaTools @@ -36,15 +36,19 @@ import android.widget.Toast import androidx.annotation.DrawableRes import androidx.annotation.OptIn import androidx.annotation.StringRes -import androidx.compose.foundation.Image -import androidx.compose.foundation.layout.* +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.wrapContentSize import androidx.compose.foundation.shape.RoundedCornerShape -import androidx.compose.material3.* -import androidx.compose.runtime.* +import androidx.compose.material3.Card +import androidx.compose.material3.Icon +import androidx.compose.material3.IconButton +import androidx.compose.runtime.Composable +import androidx.compose.runtime.mutableIntStateOf import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.vector.ImageVector -import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.vectorResource import androidx.compose.ui.unit.dp import androidx.compose.ui.window.Dialog @@ -153,7 +157,7 @@ abstract class EpisodeActionButton internal constructor(@JvmField var item: Epis class VisitWebsiteActionButton(item: Episode) : EpisodeActionButton(item) { override val visibility: Boolean - get() = if (item.link.isNullOrEmpty()) false else true + get() = !item.link.isNullOrEmpty() override fun getLabel(): Int { return R.string.visit_website_label @@ -222,6 +226,7 @@ class PlayActionButton(item: Episode) : EpisodeActionButton(item) { } else { PlaybackService.clearCurTempSpeed() PlaybackServiceStarter(context, media).callEvenIfRunning(true).start() + item = runBlocking { setPlayStateSync(PlayState.INPROGRESS.code, false, item) } EventFlow.postEvent(FlowEvent.PlayEvent(item)) } playVideoIfNeeded(context, media) @@ -253,8 +258,7 @@ class DeleteActionButton(item: Episode) : EpisodeActionButton(item) { override val visibility: Boolean get() { - if (item.media != null && (item.media!!.downloaded || item.feed?.isLocalFeed == true)) return true - return false + return item.media != null && (item.media!!.downloaded || item.feed?.isLocalFeed == true) } override fun getLabel(): Int { @@ -291,7 +295,7 @@ class PauseActionButton(item: Episode) : EpisodeActionButton(item) { class DownloadActionButton(item: Episode) : EpisodeActionButton(item) { override val visibility: Boolean - get() = if (item.feed?.isLocalFeed == true) false else true + get() = item.feed?.isLocalFeed != true override fun getLabel(): Int { return R.string.download_label @@ -371,7 +375,10 @@ class StreamActionButton(item: Episode) : EpisodeActionButton(item) { fun stream(context: Context, media: Playable) { if (media !is EpisodeMedia || !InTheatre.isCurMedia(media)) PlaybackService.clearCurTempSpeed() PlaybackServiceStarter(context, media).shouldStreamThisTime(true).callEvenIfRunning(true).start() - if (media is EpisodeMedia && media.episode != null) EventFlow.postEvent(FlowEvent.PlayEvent(media.episode!!)) + if (media is EpisodeMedia && media.episode != null) { + val item = runBlocking { setPlayStateSync(PlayState.INPROGRESS.code, false, media.episode!!) } + EventFlow.postEvent(FlowEvent.PlayEvent(item)) + } playVideoIfNeeded(context, media) } } @@ -382,7 +389,7 @@ class TTSActionButton(item: Episode) : EpisodeActionButton(item) { private var readerText: String? = null override val visibility: Boolean - get() = if (item.link.isNullOrEmpty()) false else true + get() = !item.link.isNullOrEmpty() override fun getLabel(): Int { return R.string.TTS_label @@ -535,6 +542,7 @@ class PlayLocalActionButton(item: Episode) : EpisodeActionButton(item) { } else { PlaybackService.clearCurTempSpeed() PlaybackServiceStarter(context, media).callEvenIfRunning(true).start() + item = runBlocking { setPlayStateSync(PlayState.INPROGRESS.code, false, item) } EventFlow.postEvent(FlowEvent.PlayEvent(item)) } if (media.getMediaType() == MediaType.VIDEO) context.startActivity(getPlayerActivityIntent(context, @@ -542,23 +550,3 @@ class PlayLocalActionButton(item: Episode) : EpisodeActionButton(item) { actionState.value = getLabel() } } - -class MarkAsPlayedActionButton(item: Episode) : EpisodeActionButton(item) { - override val visibility: Boolean - get() = if (item.isPlayed()) false else true - - override fun getLabel(): Int { - return (if (item.media != null) R.string.mark_read_label else R.string.mark_read_no_media_label) - } - - override fun getDrawable(): Int { - return R.drawable.ic_check - } - - @UnstableApi - override fun onClick(context: Context) { - if (!item.isPlayed()) Episodes.setPlayState(Episode.PlayState.PLAYED.code, true, item) - actionState.value = getLabel() - } - -} \ No newline at end of file diff --git a/app/src/main/kotlin/ac/mdiq/podcini/ui/actions/SwipeAction.kt b/app/src/main/kotlin/ac/mdiq/podcini/ui/actions/SwipeAction.kt index bc8dc597..391421b7 100644 --- a/app/src/main/kotlin/ac/mdiq/podcini/ui/actions/SwipeAction.kt +++ b/app/src/main/kotlin/ac/mdiq/podcini/ui/actions/SwipeAction.kt @@ -29,6 +29,7 @@ interface SwipeAction { START_DOWNLOAD, MARK_FAV, TOGGLE_PLAYED, + SET_PLAY_STATE, REMOVE_FROM_QUEUE, DELETE, REMOVE_FROM_HISTORY 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 6e4ab9e4..7eee1fd7 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 @@ -1,10 +1,10 @@ package ac.mdiq.podcini.ui.actions +//import ac.mdiq.podcini.ui.dialog.SwipeActionsDialog import ac.mdiq.podcini.R import ac.mdiq.podcini.playback.base.InTheatre.curQueue import ac.mdiq.podcini.storage.database.Episodes.deleteMediaSync import ac.mdiq.podcini.storage.database.Episodes.setPlayState -import ac.mdiq.podcini.storage.database.Episodes.setPlayStateSync import ac.mdiq.podcini.storage.database.Episodes.shouldDeleteRemoveFromQueue import ac.mdiq.podcini.storage.database.Feeds.shouldAutoDeleteItem import ac.mdiq.podcini.storage.database.Queues.addToQueue @@ -16,21 +16,20 @@ import ac.mdiq.podcini.storage.database.RealmDB.upsert 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.PlayState import ac.mdiq.podcini.storage.utils.EpisodeUtil -import ac.mdiq.podcini.ui.actions.SwipeAction.ActionTypes.NO_ACTION import ac.mdiq.podcini.ui.actions.SwipeAction.ActionTypes +import ac.mdiq.podcini.ui.actions.SwipeAction.ActionTypes.NO_ACTION import ac.mdiq.podcini.ui.activity.MainActivity import ac.mdiq.podcini.ui.compose.ChooseRatingDialog import ac.mdiq.podcini.ui.compose.CustomTheme +import ac.mdiq.podcini.ui.compose.PlayStateDialog import ac.mdiq.podcini.ui.fragment.* -//import ac.mdiq.podcini.ui.dialog.SwipeActionsDialog import ac.mdiq.podcini.ui.utils.LocalDeleteModal.deleteEpisodesWarnLocal 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.SharedPreferences -import android.os.Handler import android.util.TypedValue import android.view.ViewGroup import androidx.annotation.OptIn @@ -47,7 +46,6 @@ import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.platform.ComposeView import androidx.compose.ui.platform.LocalContext -import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.vectorResource import androidx.compose.ui.text.style.TextAlign @@ -147,7 +145,7 @@ open class SwipeActions(private val fragment: Fragment, private val tag: String) @OptIn(UnstableApi::class) override fun performAction(item: Episode, fragment: Fragment, filter: EpisodeFilter) { - addToQueue( true, item) + addToQueue(item) } override fun willRemove(filter: EpisodeFilter, item: Episode): Boolean { @@ -398,7 +396,7 @@ open class SwipeActions(private val fragment: Fragment, private val tag: String) fun addToQueueAt(episode: Episode, index: Int) : Job { return runOnIOScope { if (curQueue.episodeIds.contains(episode.id)) return@runOnIOScope - if (episode.isNew) setPlayState(Episode.PlayState.UNPLAYED.code, false, episode) + if (episode.isNew) setPlayState(PlayState.UNPLAYED.code, false, episode) curQueue = upsert(curQueue) { it.episodeIds.add(index, episode.id) it.update() @@ -438,9 +436,9 @@ open class SwipeActions(private val fragment: Fragment, private val tag: String) } } - class TogglePlaybackStateSwipeAction : SwipeAction { + class SetPlaybackStateSwipeAction : SwipeAction { override fun getId(): String { - return ActionTypes.TOGGLE_PLAYED.name + return ActionTypes.SET_PLAY_STATE.name } override fun getActionIcon(): Int { @@ -452,45 +450,22 @@ open class SwipeActions(private val fragment: Fragment, private val tag: String) } override fun getTitle(context: Context): String { - return context.getString(R.string.toggle_played_label) + return context.getString(R.string.set_play_state_label) } override fun performAction(item: Episode, fragment: Fragment, filter: EpisodeFilter) { - val newState = if (item.playState == Episode.PlayState.UNPLAYED.code) Episode.PlayState.PLAYED.code else Episode.PlayState.UNPLAYED.code - - Logd("TogglePlaybackStateSwipeAction", "performAction( ${item.id} )") - // we're marking it as unplayed since the user didn't actually play it - // but they don't want it considered 'NEW' anymore - var item = runBlocking { setPlayStateSync(newState, false, item) } - - val h = Handler(fragment.requireContext().mainLooper) - val r = Runnable { - val media: EpisodeMedia? = item.media - val shouldAutoDelete = if (item.feed == null) false else shouldAutoDeleteItem(item.feed!!) - if (media != null && EpisodeUtil.hasAlmostEnded(media) && shouldAutoDelete) { - item = deleteMediaSync(fragment.requireContext(), item) - if (shouldDeleteRemoveFromQueue()) removeFromQueueSync(null, item) } - } - val playStateStringRes: Int = when (newState) { - Episode.PlayState.UNPLAYED.code -> if (item.playState == Episode.PlayState.NEW.code) R.string.removed_inbox_label //was new - else R.string.marked_as_unplayed_label //was played - Episode.PlayState.PLAYED.code -> R.string.marked_as_played_label - else -> if (item.playState == Episode.PlayState.NEW.code) R.string.removed_inbox_label - else R.string.marked_as_unplayed_label - } - val duration: Int = Snackbar.LENGTH_LONG - - if (willRemove(filter, item)) { - (fragment.activity as MainActivity).showSnackbarAbovePlayer( - playStateStringRes, duration) - .setAction(fragment.getString(R.string.undo)) { - setPlayState(item.playState, false, item) - // don't forget to cancel the thing that's going to remove the media - h.removeCallbacks(r) + var showPlayStateDialog by mutableStateOf(true) + val composeView = ComposeView(fragment.requireContext()).apply { + setContent { + CustomTheme(fragment.requireContext()) { + if (showPlayStateDialog) PlayStateDialog(listOf(item)) { + showPlayStateDialog = false + (fragment.view as? ViewGroup)?.removeView(this@apply) + } } + } } - - h.postDelayed(r, ceil((duration * 1.05f).toDouble()).toLong()) + (fragment.view as? ViewGroup)?.addView(composeView) } private fun delayedExecution(item: Episode, fragment: Fragment, duration: Float) = runBlocking { @@ -504,7 +479,7 @@ open class SwipeActions(private val fragment: Fragment, private val tag: String) } override fun willRemove(filter: EpisodeFilter, item: Episode): Boolean { - return if (item.playState == Episode.PlayState.NEW.code) filter.showPlayed || filter.showNew + return if (item.playState == PlayState.NEW.code) filter.showPlayed || filter.showNew else filter.showUnplayed || filter.showPlayed || filter.showNew } } @@ -523,7 +498,7 @@ open class SwipeActions(private val fragment: Fragment, private val tag: String) val swipeActions: List = listOf( NoActionSwipeAction(), ComboSwipeAction(), AddToQueueSwipeAction(), StartDownloadSwipeAction(), SetRatingSwipeAction(), - TogglePlaybackStateSwipeAction(), RemoveFromQueueSwipeAction(), + SetPlaybackStateSwipeAction(), RemoveFromQueueSwipeAction(), DeleteSwipeAction(), RemoveFromHistorySwipeAction()) private fun getPrefs(tag: String, defaultActions: String): Actions { 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 4107c485..71918cc9 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 @@ -633,6 +633,10 @@ class MainActivity : CastEnabledActivity() { } } + fun openDrawer() { + drawerLayout?.openDrawer(navDrawer) + } + private var eventSink: Job? = null private var eventStickySink: Job? = null private fun cancelFlowEvents() { 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 84cb9584..7e96cf8a 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 @@ -3,18 +3,27 @@ package ac.mdiq.podcini.ui.compose import ac.mdiq.podcini.R import ac.mdiq.podcini.net.download.DownloadStatus import ac.mdiq.podcini.net.download.service.DownloadServiceInterface +import ac.mdiq.podcini.net.sync.SynchronizationSettings.isProviderConnected +import ac.mdiq.podcini.net.sync.SynchronizationSettings.wifiSyncEnabledKey +import ac.mdiq.podcini.net.sync.model.EpisodeAction +import ac.mdiq.podcini.net.sync.queue.SynchronizationQueueSink import ac.mdiq.podcini.playback.base.InTheatre import ac.mdiq.podcini.playback.base.InTheatre.curQueue import ac.mdiq.podcini.playback.base.MediaPlayerBase.Companion.status import ac.mdiq.podcini.storage.database.Episodes +import ac.mdiq.podcini.storage.database.Episodes.deleteMediaSync import ac.mdiq.podcini.storage.database.Episodes.episodeFromStreamInfo import ac.mdiq.podcini.storage.database.Episodes.setPlayState +import ac.mdiq.podcini.storage.database.Episodes.setPlayStateSync +import ac.mdiq.podcini.storage.database.Episodes.shouldDeleteRemoveFromQueue import ac.mdiq.podcini.storage.database.Feeds.addToMiscSyndicate import ac.mdiq.podcini.storage.database.Feeds.addToYoutubeSyndicate +import ac.mdiq.podcini.storage.database.Feeds.shouldAutoDeleteItem import ac.mdiq.podcini.storage.database.Queues import ac.mdiq.podcini.storage.database.Queues.addToQueueSync import ac.mdiq.podcini.storage.database.Queues.removeFromAllQueuesQuiet import ac.mdiq.podcini.storage.database.Queues.removeFromQueue +import ac.mdiq.podcini.storage.database.Queues.removeFromQueueSync import ac.mdiq.podcini.storage.database.RealmDB.realm import ac.mdiq.podcini.storage.database.RealmDB.upsert import ac.mdiq.podcini.storage.database.RealmDB.upsertBlk @@ -22,6 +31,7 @@ import ac.mdiq.podcini.storage.model.* 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 +import ac.mdiq.podcini.storage.utils.EpisodeUtil.hasAlmostEnded import ac.mdiq.podcini.storage.utils.ImageResourceUtils import ac.mdiq.podcini.ui.actions.EpisodeActionButton import ac.mdiq.podcini.ui.actions.EpisodeActionButton.Companion.forItem @@ -121,7 +131,7 @@ var queueChanged by mutableIntStateOf(0) @Stable class EpisodeVM(var episode: Episode) { var positionState by mutableStateOf(episode.media?.position?:0) - var playedState by mutableStateOf(episode.isPlayed()) + var playedState by mutableIntStateOf(episode.playState) var isPlayingState by mutableStateOf(false) var ratingState by mutableIntStateOf(episode.rating) var inProgressState by mutableStateOf(episode.isInProgress) @@ -157,9 +167,9 @@ class EpisodeVM(var episode: Episode) { Logd("EpisodeVM", "episodeMonitor UpdatedObject ${changes.obj.title} ${changes.changedFields.joinToString()}") if (episode.id == changes.obj.id) { withContext(Dispatchers.Main) { - playedState = changes.obj.isPlayed() + playedState = changes.obj.playState ratingState = changes.obj.rating -// episode = changes.obj // direct assignment doesn't update member like media?? + episode = changes.obj // direct assignment doesn't update member like media?? } Logd("EpisodeVM", "episodeMonitor $playedState $playedState ") } else Logd("EpisodeVM", "episodeMonitor index out bound") @@ -183,7 +193,7 @@ class EpisodeVM(var episode: Episode) { positionState = changes.obj.media?.position ?: 0 inProgressState = changes.obj.isInProgress Logd("EpisodeVM", "mediaMonitor $positionState $inProgressState ${episode.title}") -// episode = changes.obj + episode = changes.obj // Logd("EpisodeVM", "mediaMonitor downloaded: ${changes.obj.media?.downloaded} ${episode.media?.downloaded}") } } else Logd("EpisodeVM", "mediaMonitor index out bound") @@ -231,6 +241,61 @@ fun ChooseRatingDialog(selected: List, onDismissRequest: () -> Unit) { } } +@Composable +fun PlayStateDialog(selected: List, onDismissRequest: () -> Unit) { + val context = LocalContext.current + Dialog(onDismissRequest = onDismissRequest) { + Surface(shape = RoundedCornerShape(16.dp)) { + Column(modifier = Modifier.padding(16.dp), verticalArrangement = Arrangement.spacedBy(16.dp)) { + for (state in PlayState.entries) { + if (state.userSet) { + Row(verticalAlignment = Alignment.CenterVertically, modifier = Modifier.padding(4.dp) + .clickable { + for (item in selected) { + var item_ = runBlocking { setPlayStateSync(state.code, false, item) } + val media: EpisodeMedia? = item_.media + val shouldAutoDelete = if (item_.feed == null) false else shouldAutoDeleteItem(item_.feed!!) + if (media != null && hasAlmostEnded(media) && shouldAutoDelete) { + item_ = deleteMediaSync(context, item_) + if (shouldDeleteRemoveFromQueue()) removeFromQueueSync(null, item_) + } + when (state) { + PlayState.UNPLAYED -> { + if (isProviderConnected && item_?.feed?.isLocalFeed != true && item_?.media != null) { + val actionNew: EpisodeAction = EpisodeAction.Builder(item_!!, EpisodeAction.NEW).currentTimestamp().build() + SynchronizationQueueSink.enqueueEpisodeActionIfSyncActive(context, actionNew) + } + } + PlayState.PLAYED -> { + if (item_?.feed?.isLocalFeed != true && (isProviderConnected || wifiSyncEnabledKey)) { + val media: EpisodeMedia? = item_?.media + // not all items have media, Gpodder only cares about those that do + if (isProviderConnected && media != null) { + val actionPlay: EpisodeAction = EpisodeAction.Builder(item_!!, EpisodeAction.PLAY) + .currentTimestamp() + .started(media.getDuration() / 1000) + .position(media.getDuration() / 1000) + .total(media.getDuration() / 1000) + .build() + SynchronizationQueueSink.enqueueEpisodeActionIfSyncActive(context, actionPlay) + } + } + } + else -> {} + } + } + onDismissRequest() + }) { + Icon(imageVector = ImageVector.vectorResource(id = state.res), "") + Text(state.name, Modifier.padding(start = 4.dp)) + } + } + } + } + } + } +} + @Composable fun PutToQueueDialog(selected: List, onDismissRequest: () -> Unit) { val queues = realm.query(PlayQueue::class).find() @@ -271,7 +336,7 @@ fun PutToQueueDialog(selected: List, onDismissRequest: () -> Unit) { if (toRemoveCur.isNotEmpty()) EventFlow.postEvent(FlowEvent.QueueEvent.removed(toRemoveCur)) } selected.forEach { e -> - runBlocking { addToQueueSync(false, e, toQueue) } + runBlocking { addToQueueSync(e, toQueue) } } onDismissRequest() }) { @@ -366,6 +431,9 @@ fun EpisodeLazyColumn(activity: MainActivity, vms: List, feed: Feed? var showChooseRatingDialog by remember { mutableStateOf(false) } if (showChooseRatingDialog) ChooseRatingDialog(selected) { showChooseRatingDialog = false } + var showPlayStateDialog by remember { mutableStateOf(false) } + if (showPlayStateDialog) PlayStateDialog(selected) { showPlayStateDialog = false } + var showPutToQueueDialog by remember { mutableStateOf(false) } if (showPutToQueueDialog) PutToQueueDialog(selected) { showPutToQueueDialog = false } @@ -460,13 +528,14 @@ fun EpisodeLazyColumn(activity: MainActivity, vms: List, feed: Feed? { Row(modifier = Modifier .padding(horizontal = 16.dp) .clickable { + showPlayStateDialog = true isExpanded = false selectMode = false Logd(TAG, "ic_mark_played: ${selected.size}") - setPlayState(Episode.PlayState.UNSPECIFIED.code, false, *selected.toTypedArray()) +// setPlayState(PlayState.UNSPECIFIED.code, false, *selected.toTypedArray()) }, verticalAlignment = Alignment.CenterVertically) { Icon(imageVector = ImageVector.vectorResource(id = R.drawable.ic_mark_played), "Toggle played state") - Text(stringResource(id = R.string.toggle_played_label)) } }, + Text(stringResource(id = R.string.set_play_state_label)) } }, { Row(modifier = Modifier .padding(horizontal = 16.dp) .clickable { @@ -483,7 +552,7 @@ fun EpisodeLazyColumn(activity: MainActivity, vms: List, feed: Feed? isExpanded = false selectMode = false Logd(TAG, "ic_playlist_play: ${selected.size}") - Queues.addToQueue(true, *selected.toTypedArray()) + Queues.addToQueue(*selected.toTypedArray()) }, verticalAlignment = Alignment.CenterVertically) { Icon(imageVector = ImageVector.vectorResource(id = R.drawable.ic_playlist_play), "Add to active queue") Text(stringResource(id = R.string.add_to_queue_label)) } }, @@ -520,8 +589,7 @@ fun EpisodeLazyColumn(activity: MainActivity, vms: List, feed: Feed? ) if (selected.isNotEmpty() && selected[0].isRemote.value) options.add { - Row(modifier = Modifier - .padding(horizontal = 16.dp) + Row(modifier = Modifier.padding(horizontal = 16.dp) .clickable { isExpanded = false selectMode = false @@ -594,7 +662,7 @@ fun EpisodeLazyColumn(activity: MainActivity, vms: List, feed: Feed? ConstraintLayout(modifier = Modifier.width(56.dp).height(56.dp)) { val (imgvCover, checkMark) = createRefs() val imgLoc = remember(vm) { ImageResourceUtils.getEpisodeListImageLocation(vm.episode) } - Logd(TAG, "imgLoc: $imgLoc") +// Logd(TAG, "imgLoc: $imgLoc") AsyncImage(model = ImageRequest.Builder(context).data(imgLoc) .memoryCachePolicy(CachePolicy.ENABLED).placeholder(R.mipmap.ic_launcher).error(R.mipmap.ic_launcher).build(), contentDescription = "imgvCover", @@ -609,8 +677,8 @@ fun EpisodeLazyColumn(activity: MainActivity, vms: List, feed: Feed? if (selectMode) toggleSelected() else if (vm.episode.feed != null) activity.loadChildFragment(FeedInfoFragment.newInstance(vm.episode.feed!!)) })) - val alpha = if (vm.playedState) 1.0f else 0f - if (vm.playedState) Icon(imageVector = ImageVector.vectorResource(R.drawable.ic_check), tint = textColor, contentDescription = "played_mark", + val alpha = if (vm.playedState >= PlayState.SKIPPED.code) 1.0f else 0f + if (vm.playedState >= PlayState.SKIPPED.code) Icon(imageVector = ImageVector.vectorResource(R.drawable.ic_check), tint = textColor, contentDescription = "played_mark", modifier = Modifier.background(Color.Green).alpha(alpha) .constrainAs(checkMark) { bottom.linkTo(parent.bottom) @@ -640,14 +708,16 @@ fun EpisodeLazyColumn(activity: MainActivity, vms: List, feed: Feed? vms[index].inQueueState = curQueue.contains(vms[index].episode) } Row(verticalAlignment = Alignment.CenterVertically) { - Logd(TAG, "info row") - if (vm.episode.media?.getMediaType() == MediaType.VIDEO) - Icon(imageVector = ImageVector.vectorResource(R.drawable.ic_videocam), tint = textColor, contentDescription = "isVideo", modifier = Modifier.width(14.dp).height(14.dp)) +// Logd(TAG, "info row") val ratingIconRes = Rating.fromCode(vm.ratingState).res if (vm.ratingState != Rating.UNRATED.code) Icon(imageVector = ImageVector.vectorResource(ratingIconRes), tint = MaterialTheme.colorScheme.tertiary, contentDescription = "rating", modifier = Modifier.background(MaterialTheme.colorScheme.tertiaryContainer).width(14.dp).height(14.dp)) + val playStateRes = PlayState.fromCode(vm.playedState).res + Icon(imageVector = ImageVector.vectorResource(playStateRes), tint = textColor, contentDescription = "playState", modifier = Modifier.width(14.dp).height(14.dp)) if (vm.inQueueState) Icon(imageVector = ImageVector.vectorResource(R.drawable.ic_playlist_play), tint = textColor, contentDescription = "ivInPlaylist", modifier = Modifier.width(14.dp).height(14.dp)) + if (vm.episode.media?.getMediaType() == MediaType.VIDEO) + Icon(imageVector = ImageVector.vectorResource(R.drawable.ic_videocam), tint = textColor, contentDescription = "isVideo", modifier = Modifier.width(14.dp).height(14.dp)) val curContext = LocalContext.current val dur = remember { vm.episode.media?.getDuration() ?: 0 } val durText = remember { DurationConverter.getDurationStringLong(dur) } @@ -681,7 +751,7 @@ fun EpisodeLazyColumn(activity: MainActivity, vms: List, feed: Feed? detectTapGestures(onLongPress = { vms[index].showAltActionsDialog = true }, onTap = { vms[index].actionButton.onClick(activity) }) }, ) { - Logd(TAG, "button box") +// Logd(TAG, "button box") vm.actionRes = vm.actionButton.getDrawable() Icon(imageVector = ImageVector.vectorResource(vm.actionRes), tint = textColor, contentDescription = null, modifier = Modifier.width(28.dp).height(32.dp)) if (isDownloading() && vm.dlPercent >= 0) CircularProgressIndicator(progress = { 0.01f * vm.dlPercent }, diff --git a/app/src/main/kotlin/ac/mdiq/podcini/ui/fragment/AudioPlayerFragment.kt b/app/src/main/kotlin/ac/mdiq/podcini/ui/fragment/AudioPlayerFragment.kt index 51c7cf83..bf4ddf0d 100644 --- a/app/src/main/kotlin/ac/mdiq/podcini/ui/fragment/AudioPlayerFragment.kt +++ b/app/src/main/kotlin/ac/mdiq/podcini/ui/fragment/AudioPlayerFragment.kt @@ -6,6 +6,8 @@ import ac.mdiq.podcini.playback.PlaybackServiceStarter import ac.mdiq.podcini.playback.ServiceStatusHandler import ac.mdiq.podcini.playback.base.InTheatre.curEpisode import ac.mdiq.podcini.playback.base.InTheatre.curMedia +import ac.mdiq.podcini.playback.base.InTheatre.curQueue +import ac.mdiq.podcini.playback.base.InTheatre.isCurrentlyPlaying import ac.mdiq.podcini.playback.base.MediaPlayerBase.Companion.status import ac.mdiq.podcini.playback.base.PlayerStatus import ac.mdiq.podcini.playback.base.VideoMode @@ -85,11 +87,8 @@ import coil.request.CachePolicy import coil.request.ImageRequest import com.google.android.material.bottomsheet.BottomSheetBehavior import com.google.android.material.snackbar.Snackbar -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.Job +import kotlinx.coroutines.* import kotlinx.coroutines.flow.collectLatest -import kotlinx.coroutines.launch -import kotlinx.coroutines.withContext import net.dankito.readability4j.Readability4J import org.apache.commons.lang3.StringUtils import java.text.DecimalFormat @@ -108,6 +107,7 @@ class AudioPlayerFragment : Fragment() { private var prevItem: Episode? = null private var currentItem: Episode? = null + private var playButInit = false private var isShowPlay: Boolean = true private var showTimeLeft = false @@ -166,7 +166,10 @@ class AudioPlayerFragment : Fragment() { } } } - (activity as MainActivity).setPlayerVisible(false) + Logd(TAG, "curMedia: ${curMedia?.getIdentifier()}") + (activity as MainActivity).setPlayerVisible(curMedia != null) + if (curMedia != null) updateUi(curMedia!!) +// if (curMedia is EpisodeMedia) setIsShowPlay(isCurrentlyPlaying(curMedia as EpisodeMedia)) return composeView } @@ -188,7 +191,7 @@ class AudioPlayerFragment : Fragment() { if (curMedia == null) return if (playbackService == null) PlaybackServiceStarter(requireContext(), curMedia!!).start() } - val imgLoc_ = remember(currentItem) { imgLoc } + val imgLoc_ = remember(currentMedia) { imgLoc } AsyncImage(model = ImageRequest.Builder(context).data(imgLoc_) .memoryCachePolicy(CachePolicy.ENABLED).placeholder(R.mipmap.ic_launcher).error(R.mipmap.ic_launcher).build(), contentDescription = "imgvCover", @@ -502,9 +505,15 @@ class AudioPlayerFragment : Fragment() { txtvPlaybackSpeed = speedStr // binding.butPlaybackSpeed.setSpeed(event.newSpeed) TODO } + @UnstableApi fun onPositionUpdate(event: FlowEvent.PlaybackPositionEvent) { Logd(TAG, "onPositionUpdate") + if (!playButInit && playButRes == R.drawable.ic_play_48dp && curMedia is EpisodeMedia) { + playButRes = R.drawable.ic_pause + playButInit = true + } + if (curMedia?.getIdentifier() != event.media?.getIdentifier() || controller == null || curPositionFB == Playable.INVALID_TIME || curDurationFB == Playable.INVALID_TIME) return val converter = TimeSpeedConverter(curSpeedFB) currentPosition = converter.convert(event.position) @@ -520,6 +529,7 @@ class AudioPlayerFragment : Fragment() { sliderValue = event.position.toFloat() } + private fun onPlaybackServiceChanged(event: FlowEvent.PlaybackServiceEvent) { when (event.action) { FlowEvent.PlaybackServiceEvent.Action.SERVICE_SHUT_DOWN -> (activity as MainActivity).setPlayerVisible(false) @@ -527,6 +537,7 @@ class AudioPlayerFragment : Fragment() { // PlaybackServiceEvent.Action.SERVICE_RESTARTED -> (activity as MainActivity).setPlayerVisible(true) } } + @UnstableApi fun updateUi(media: Playable) { Logd(TAG, "updateUi called $media") @@ -578,7 +589,8 @@ class AudioPlayerFragment : Fragment() { withContext(Dispatchers.Main) { Logd(TAG, "subscribe: ${currentMedia?.getEpisodeTitle()}") displayMediaInfo(currentMedia!!) -// shownoteView.loadDataWithBaseURL("https://127.0.0.1", cleanedNotes?:"No notes", "text/html", "utf-8", "about:blank") + (activity as MainActivity).setPlayerVisible(curMedia != null) + // shownoteView.loadDataWithBaseURL("https://127.0.0.1", cleanedNotes?:"No notes", "text/html", "utf-8", "about:blank") Logd(TAG, "Webview loaded") } }.invokeOnCompletion { throwable -> @@ -738,7 +750,10 @@ class AudioPlayerFragment : Fragment() { private var loadItemsRunning = false fun loadMediaInfo() { + Logd(TAG, "loadMediaInfo() curMedia: ${curMedia?.getIdentifier()}") val actMain = (activity as MainActivity) + var i = 0 + while (curMedia == null && i++ < 6) runBlocking { delay(500) } if (curMedia == null) { if (actMain.isPlayerVisible()) actMain.setPlayerVisible(false) return @@ -747,6 +762,7 @@ class AudioPlayerFragment : Fragment() { loadItemsRunning = true if (!actMain.isPlayerVisible()) actMain.setPlayerVisible(true) val curMediaChanged = currentMedia == null || curMedia?.getIdentifier() != currentMedia?.getIdentifier() + if (curMedia?.getIdentifier() != currentMedia?.getIdentifier()) updateUi(curMedia!!) if (!isCollapsed && curMediaChanged) { updateDetails() Logd(TAG, "loadMediaInfo loading details ${curMedia?.getIdentifier()}") @@ -761,7 +777,6 @@ class AudioPlayerFragment : Fragment() { if (item != null) setItem(item) setChapterDividers() sleepTimerActive = isSleepTimerActive() - if (currentMedia != null) updateUi(currentMedia!!) // TODO: disable for now // if (!includingChapters) loadMediaInfo(true) }.invokeOnCompletion { throwable -> 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 db1785ae..ebd45a98 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 @@ -1,7 +1,7 @@ package ac.mdiq.podcini.ui.fragment import ac.mdiq.podcini.R -import ac.mdiq.podcini.databinding.BaseEpisodesListFragmentBinding +import ac.mdiq.podcini.databinding.ComposeFragmentBinding import ac.mdiq.podcini.net.download.DownloadStatus import ac.mdiq.podcini.storage.model.Episode import ac.mdiq.podcini.storage.model.EpisodeFilter @@ -10,7 +10,10 @@ 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.ui.compose.CustomTheme +import ac.mdiq.podcini.ui.compose.EpisodeLazyColumn +import ac.mdiq.podcini.ui.compose.EpisodeVM +import ac.mdiq.podcini.ui.compose.InforBar import ac.mdiq.podcini.ui.utils.EmptyViewHandler import ac.mdiq.podcini.util.EventFlow import ac.mdiq.podcini.util.FlowEvent @@ -42,7 +45,7 @@ abstract class BaseEpisodesFragment : Fragment(), Toolbar.OnMenuItemClickListene protected var page: Int = 1 private var displayUpArrow = false - var _binding: BaseEpisodesListFragmentBinding? = null + var _binding: ComposeFragmentBinding? = null protected val binding get() = _binding!! protected var infoBarText = mutableStateOf("") @@ -59,7 +62,7 @@ abstract class BaseEpisodesFragment : Fragment(), Toolbar.OnMenuItemClickListene @UnstableApi override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View { super.onCreateView(inflater, container, savedInstanceState) - _binding = BaseEpisodesListFragmentBinding.inflate(inflater) + _binding = ComposeFragmentBinding.inflate(inflater) Logd(TAG, "fragment onCreateView") toolbar = binding.toolbar @@ -79,7 +82,7 @@ abstract class BaseEpisodesFragment : Fragment(), Toolbar.OnMenuItemClickListene swipeActions = SwipeActions(this, TAG) lifecycle.addObserver(swipeActions) - binding.lazyColumn.setContent { + binding.mainView.setContent { CustomTheme(requireContext()) { Column { InforBar(infoBarText, leftAction = leftActionState, rightAction = rightActionState, actionConfig = {swipeActions.showDialog()}) 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 index 3c936069..03a9267b 100644 --- a/app/src/main/kotlin/ac/mdiq/podcini/ui/fragment/DownloadsFragment.kt +++ b/app/src/main/kotlin/ac/mdiq/podcini/ui/fragment/DownloadsFragment.kt @@ -1,7 +1,7 @@ package ac.mdiq.podcini.ui.fragment import ac.mdiq.podcini.R -import ac.mdiq.podcini.databinding.DownloadsFragmentBinding +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 @@ -20,7 +20,10 @@ 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.ui.compose.CustomTheme +import ac.mdiq.podcini.ui.compose.EpisodeLazyColumn +import ac.mdiq.podcini.ui.compose.EpisodeVM +import ac.mdiq.podcini.ui.compose.InforBar import ac.mdiq.podcini.ui.dialog.EpisodeFilterDialog import ac.mdiq.podcini.ui.dialog.EpisodeSortDialog import ac.mdiq.podcini.ui.dialog.SwitchQueueDialog @@ -37,7 +40,8 @@ import android.view.ViewGroup import android.widget.Toast import androidx.appcompat.widget.Toolbar import androidx.compose.foundation.layout.Column -import androidx.compose.runtime.* +import androidx.compose.runtime.mutableStateListOf +import androidx.compose.runtime.mutableStateOf import androidx.fragment.app.Fragment import androidx.lifecycle.lifecycleScope import androidx.media3.common.util.UnstableApi @@ -56,7 +60,7 @@ import java.util.* */ @UnstableApi class DownloadsFragment : Fragment(), Toolbar.OnMenuItemClickListener { - private var _binding: DownloadsFragmentBinding? = null + private var _binding: ComposeFragmentBinding? = null private val binding get() = _binding!! private var runningDownloads: Set = HashSet() @@ -74,7 +78,7 @@ import java.util.* private var displayUpArrow = false @UnstableApi override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View { - _binding = DownloadsFragmentBinding.inflate(inflater) + _binding = ComposeFragmentBinding.inflate(inflater) Logd(TAG, "fragment onCreateView") toolbar = binding.toolbar @@ -88,12 +92,11 @@ import java.util.* // } 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.lazyColumn.setContent { + binding.mainView.setContent { CustomTheme(requireContext()) { Column { InforBar(infoBarText, leftAction = leftActionState, rightAction = rightActionState, actionConfig = {swipeActions.showDialog()}) diff --git a/app/src/main/kotlin/ac/mdiq/podcini/ui/fragment/EpisodeInfoFragment.kt b/app/src/main/kotlin/ac/mdiq/podcini/ui/fragment/EpisodeInfoFragment.kt index 578304b7..02d6b540 100644 --- a/app/src/main/kotlin/ac/mdiq/podcini/ui/fragment/EpisodeInfoFragment.kt +++ b/app/src/main/kotlin/ac/mdiq/podcini/ui/fragment/EpisodeInfoFragment.kt @@ -1,14 +1,10 @@ package ac.mdiq.podcini.ui.fragment import ac.mdiq.podcini.R +import ac.mdiq.podcini.databinding.ComposeFragmentBinding import ac.mdiq.podcini.databinding.EpisodeHomeFragmentBinding -import ac.mdiq.podcini.databinding.EpisodeInfoFragmentBinding import ac.mdiq.podcini.net.download.service.DownloadServiceInterface import ac.mdiq.podcini.net.download.service.PodciniHttpClient.getHttpClient -import ac.mdiq.podcini.net.sync.SynchronizationSettings.isProviderConnected -import ac.mdiq.podcini.net.sync.SynchronizationSettings.wifiSyncEnabledKey -import ac.mdiq.podcini.net.sync.model.EpisodeAction -import ac.mdiq.podcini.net.sync.queue.SynchronizationQueueSink import ac.mdiq.podcini.net.utils.NetworkUtils.fetchHtmlSource import ac.mdiq.podcini.net.utils.NetworkUtils.isEpisodeHeadDownloadAllowed import ac.mdiq.podcini.playback.base.InTheatre @@ -16,7 +12,6 @@ import ac.mdiq.podcini.playback.base.InTheatre.curQueue import ac.mdiq.podcini.playback.service.PlaybackService.Companion.seekTo import ac.mdiq.podcini.preferences.UsageStatistics import ac.mdiq.podcini.preferences.UserPreferences -import ac.mdiq.podcini.storage.database.Episodes.setPlayState import ac.mdiq.podcini.storage.database.Queues.addToQueue import ac.mdiq.podcini.storage.database.Queues.removeFromQueue import ac.mdiq.podcini.storage.database.RealmDB.realm @@ -24,18 +19,12 @@ import ac.mdiq.podcini.storage.database.RealmDB.runOnIOScope import ac.mdiq.podcini.storage.database.RealmDB.unmanaged 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.EpisodeMedia -import ac.mdiq.podcini.storage.model.Feed -import ac.mdiq.podcini.storage.model.Rating +import ac.mdiq.podcini.storage.model.* import ac.mdiq.podcini.storage.utils.DurationConverter import ac.mdiq.podcini.storage.utils.ImageResourceUtils import ac.mdiq.podcini.ui.actions.* import ac.mdiq.podcini.ui.activity.MainActivity -import ac.mdiq.podcini.ui.compose.ChaptersDialog -import ac.mdiq.podcini.ui.compose.ChooseRatingDialog -import ac.mdiq.podcini.ui.compose.CustomTheme -import ac.mdiq.podcini.ui.compose.LargeTextEditingDialog +import ac.mdiq.podcini.ui.compose.* import ac.mdiq.podcini.ui.dialog.ShareDialog import ac.mdiq.podcini.ui.utils.ShownotesCleaner import ac.mdiq.podcini.ui.utils.ThemeUtils @@ -109,7 +98,7 @@ import java.util.* */ @UnstableApi class EpisodeInfoFragment : Fragment(), Toolbar.OnMenuItemClickListener { - private var _binding: EpisodeInfoFragmentBinding? = null + private var _binding: ComposeFragmentBinding? = null private val binding get() = _binding!! private var homeFragment: EpisodeHomeFragment? = null @@ -126,7 +115,7 @@ class EpisodeInfoFragment : Fragment(), Toolbar.OnMenuItemClickListener { private var hasMedia by mutableStateOf(true) var rating by mutableStateOf(episode?.rating ?: Rating.UNRATED.code) private var inQueue by mutableStateOf(if (episode != null) curQueue.contains(episode!!) else false) - var isPlayed by mutableStateOf(episode?.isPlayed() ?: false) + var isPlayed by mutableIntStateOf(episode?.playState ?: PlayState.UNSPECIFIED.code) private var webviewData by mutableStateOf("") @@ -141,7 +130,7 @@ class EpisodeInfoFragment : Fragment(), Toolbar.OnMenuItemClickListener { override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View { super.onCreateView(inflater, container, savedInstanceState) - _binding = EpisodeInfoFragmentBinding.inflate(inflater, container, false) + _binding = ComposeFragmentBinding.inflate(inflater, container, false) Logd(TAG, "fragment onCreateView") toolbar = binding.toolbar @@ -150,7 +139,7 @@ class EpisodeInfoFragment : Fragment(), Toolbar.OnMenuItemClickListener { toolbar.setNavigationOnClickListener { parentFragmentManager.popBackStack() } toolbar.setOnMenuItemClickListener(this) - binding.composeView.setContent{ + binding.mainView.setContent{ CustomTheme(requireContext()) { MainView() } @@ -188,6 +177,9 @@ class EpisodeInfoFragment : Fragment(), Toolbar.OnMenuItemClickListener { var showChaptersDialog by remember { mutableStateOf(false) } if (showChaptersDialog && episode?.media != null) ChaptersDialog(media = episode!!.media!!, onDismissRequest = {showChaptersDialog = false}) + var showPlayStateDialog by remember { mutableStateOf(false) } + if (showPlayStateDialog) PlayStateDialog(listOf(episode!!)) { showPlayStateDialog = false } + Column { Row(modifier = Modifier.padding(start = 16.dp, end = 16.dp), verticalAlignment = Alignment.CenterVertically) { val imgLoc = if (episode != null) ImageResourceUtils.getEpisodeListImageLocation(episode!!) else null @@ -200,39 +192,40 @@ class EpisodeInfoFragment : Fragment(), Toolbar.OnMenuItemClickListener { } Row(modifier = Modifier.padding(top = 4.dp), verticalAlignment = Alignment.CenterVertically) { Spacer(modifier = Modifier.weight(0.4f)) - val playedIconRes = if (!isPlayed) R.drawable.ic_mark_unplayed else R.drawable.ic_mark_played + val playedIconRes = PlayState.fromCode(isPlayed).res Icon(imageVector = ImageVector.vectorResource(playedIconRes), tint = MaterialTheme.colorScheme.tertiary, contentDescription = "isPlayed", modifier = Modifier.background(MaterialTheme.colorScheme.tertiaryContainer).width(24.dp).height(24.dp) .clickable(onClick = { - if (isPlayed) { - setPlayState(Episode.PlayState.UNPLAYED.code, false, episode!!) - if (isProviderConnected && episode?.feed?.isLocalFeed != true && episode?.media != null) { - val actionNew: EpisodeAction = EpisodeAction.Builder(episode!!, EpisodeAction.NEW).currentTimestamp().build() - SynchronizationQueueSink.enqueueEpisodeActionIfSyncActive(requireContext(), actionNew) - } - } else { - setPlayState(Episode.PlayState.PLAYED.code, true, episode!!) - if (episode?.feed?.isLocalFeed != true && (isProviderConnected || wifiSyncEnabledKey)) { - val media: EpisodeMedia? = episode?.media - // not all items have media, Gpodder only cares about those that do - if (isProviderConnected && media != null) { - val actionPlay: EpisodeAction = EpisodeAction.Builder(episode!!, EpisodeAction.PLAY) - .currentTimestamp() - .started(media.getDuration() / 1000) - .position(media.getDuration() / 1000) - .total(media.getDuration() / 1000) - .build() - SynchronizationQueueSink.enqueueEpisodeActionIfSyncActive(requireContext(), actionPlay) - } - } - } + showPlayStateDialog = true +// if (isPlayed) { +// setPlayState(PlayState.UNPLAYED.code, false, episode!!) +// if (isProviderConnected && episode?.feed?.isLocalFeed != true && episode?.media != null) { +// val actionNew: EpisodeAction = EpisodeAction.Builder(episode!!, EpisodeAction.NEW).currentTimestamp().build() +// SynchronizationQueueSink.enqueueEpisodeActionIfSyncActive(requireContext(), actionNew) +// } +// } else { +// setPlayState(PlayState.PLAYED.code, true, episode!!) +// if (episode?.feed?.isLocalFeed != true && (isProviderConnected || wifiSyncEnabledKey)) { +// val media: EpisodeMedia? = episode?.media +// // not all items have media, Gpodder only cares about those that do +// if (isProviderConnected && media != null) { +// val actionPlay: EpisodeAction = EpisodeAction.Builder(episode!!, EpisodeAction.PLAY) +// .currentTimestamp() +// .started(media.getDuration() / 1000) +// .position(media.getDuration() / 1000) +// .total(media.getDuration() / 1000) +// .build() +// SynchronizationQueueSink.enqueueEpisodeActionIfSyncActive(requireContext(), actionPlay) +// } +// } +// } })) if (episode?.media != null) { Spacer(modifier = Modifier.weight(0.2f)) val inQueueIconRes = if (inQueue) R.drawable.ic_playlist_play else R.drawable.ic_playlist_remove Icon(imageVector = ImageVector.vectorResource(inQueueIconRes), tint = MaterialTheme.colorScheme.tertiary, contentDescription = "inQueue", modifier = Modifier.background(MaterialTheme.colorScheme.tertiaryContainer).width(24.dp).height(24.dp).clickable(onClick = { - if (inQueue) removeFromQueue(episode!!) else addToQueue(true, episode!!) + if (inQueue) removeFromQueue(episode!!) else addToQueue(episode!!) })) } Spacer(modifier = Modifier.weight(0.2f)) @@ -625,7 +618,7 @@ class EpisodeInfoFragment : Fragment(), Toolbar.OnMenuItemClickListener { if (episode != null) { rating = episode!!.rating inQueue = curQueue.contains(episode!!) - isPlayed = episode!!.isPlayed() + isPlayed = episode!!.playState } onFragmentLoaded() itemLoaded = true 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 cf4e3c87..be7ddd51 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 @@ -1,7 +1,7 @@ package ac.mdiq.podcini.ui.fragment import ac.mdiq.podcini.R -import ac.mdiq.podcini.databinding.FeedItemListFragmentBinding +import ac.mdiq.podcini.databinding.ComposeFragmentBinding import ac.mdiq.podcini.net.download.DownloadStatus import ac.mdiq.podcini.net.feed.FeedUpdateManager import ac.mdiq.podcini.preferences.UserPreferences @@ -10,11 +10,8 @@ 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.EpisodeSortOrder +import ac.mdiq.podcini.storage.model.* import ac.mdiq.podcini.storage.model.EpisodeSortOrder.Companion.fromCode -import ac.mdiq.podcini.storage.model.Feed import ac.mdiq.podcini.storage.utils.EpisodesPermutors.getPermutor import ac.mdiq.podcini.ui.actions.SwipeAction import ac.mdiq.podcini.ui.actions.SwipeActions @@ -36,7 +33,10 @@ import android.view.* import android.widget.Toast import androidx.annotation.OptIn import androidx.appcompat.widget.Toolbar -import androidx.compose.foundation.* +import androidx.compose.foundation.ExperimentalFoundationApi +import androidx.compose.foundation.background +import androidx.compose.foundation.clickable +import androidx.compose.foundation.combinedClickable import androidx.compose.foundation.layout.* import androidx.compose.material3.Icon import androidx.compose.material3.MaterialTheme @@ -55,6 +55,7 @@ import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.dp import androidx.constraintlayout.compose.ConstraintLayout +import androidx.constraintlayout.compose.Dimension import androidx.fragment.app.Fragment import androidx.lifecycle.lifecycleScope import androidx.media3.common.util.UnstableApi @@ -72,7 +73,7 @@ import java.util.concurrent.Semaphore */ @UnstableApi class FeedEpisodesFragment : Fragment(), Toolbar.OnMenuItemClickListener { - private var _binding: FeedItemListFragmentBinding? = null + private var _binding: ComposeFragmentBinding? = null private val binding get() = _binding!! private lateinit var swipeActions: SwipeActions @@ -87,6 +88,7 @@ import java.util.concurrent.Semaphore private var headerCreated = false private var feedID: Long = 0 private var feed by mutableStateOf(null) + var rating by mutableStateOf(Rating.UNRATED.code) private val episodes = mutableStateListOf() private val vms = mutableStateListOf() @@ -112,7 +114,7 @@ import java.util.concurrent.Semaphore @UnstableApi override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View { Logd(TAG, "fragment onCreateView") - _binding = FeedItemListFragmentBinding.inflate(inflater) + _binding = ComposeFragmentBinding.inflate(inflater) binding.toolbar.inflateMenu(R.menu.feed_episodes) binding.toolbar.setOnMenuItemClickListener(this) @@ -169,7 +171,7 @@ import java.util.concurrent.Semaphore requireActivity().supportFragmentManager.executePendingTransactions() } Column { - FeedEpisodesHeader(activity = (activity as MainActivity), feed = feed, filterButColor = filterButColor.value, filterClickCB = {filterClick()}, filterLongClickCB = {filterLongClick()}) + FeedEpisodesHeader(activity = (activity as MainActivity), filterButColor = filterButColor.value, filterClickCB = {filterClick()}, filterLongClickCB = {filterLongClick()}) InforBar(infoBarText, leftAction = leftActionState, rightAction = rightActionState, actionConfig = { swipeActions.showDialog() }) @@ -225,8 +227,14 @@ import java.util.concurrent.Semaphore @kotlin.OptIn(ExperimentalFoundationApi::class) @Composable - fun FeedEpisodesHeader(activity: MainActivity, feed: Feed?, filterButColor: Color, filterClickCB: ()->Unit, filterLongClickCB: ()->Unit) { + fun FeedEpisodesHeader(activity: MainActivity, filterButColor: Color, filterClickCB: ()->Unit, filterLongClickCB: ()->Unit) { val textColor = MaterialTheme.colorScheme.onSurface + var showChooseRatingDialog by remember { mutableStateOf(false) } + if (showChooseRatingDialog) ChooseRatingDialog(listOf(feed!!)) { + showChooseRatingDialog = false + feed = realm.query(Feed::class).query("id == $0", feed!!.id).first().find()!! + rating = feed!!.rating + } ConstraintLayout(modifier = Modifier.fillMaxWidth().height(120.dp)) { val (bgImage, bgColor, controlRow, image1, image2, imgvCover, taColumn) = createRefs() AsyncImage(model = feed?.imageUrl?:"", contentDescription = "bgImage", contentScale = ContentScale.FillBounds, @@ -238,7 +246,7 @@ import java.util.concurrent.Semaphore start.linkTo(parent.start) end.linkTo(parent.end) }) - Box(modifier = Modifier.fillMaxSize().background(MaterialTheme.colorScheme.surface.copy(alpha = 0.7f)) + Box(modifier = Modifier.fillMaxSize().background(MaterialTheme.colorScheme.surface.copy(alpha = 0.75f)) .constrainAs(bgColor) { bottom.linkTo(parent.bottom) top.linkTo(parent.top) @@ -249,20 +257,30 @@ import java.util.concurrent.Semaphore .constrainAs(controlRow) { bottom.linkTo(parent.bottom) start.linkTo(parent.start) + width = Dimension.fillToConstraints }, verticalAlignment = Alignment.CenterVertically) { Spacer(modifier = Modifier.weight(0.7f)) + val ratingIconRes = Rating.fromCode(rating).res + Icon(imageVector = ImageVector.vectorResource(ratingIconRes), tint = MaterialTheme.colorScheme.tertiary, contentDescription = "rating", + modifier = Modifier.background(MaterialTheme.colorScheme.tertiaryContainer).width(30.dp).height(30.dp).clickable(onClick = { + showChooseRatingDialog = true + })) + Spacer(modifier = Modifier.weight(0.2f)) + Icon(imageVector = ImageVector.vectorResource(R.drawable.arrows_sort), tint = textColor, contentDescription = "butSort", + modifier = Modifier.width(40.dp).height(40.dp).padding(3.dp).clickable(onClick = { SingleFeedSortDialog(feed).show(childFragmentManager, "SortDialog") })) + Spacer(modifier = Modifier.width(15.dp)) Icon(imageVector = ImageVector.vectorResource(R.drawable.ic_filter_white), tint = if (filterButColor == Color.White) textColor else filterButColor, contentDescription = "butFilter", modifier = Modifier.width(40.dp).height(40.dp).padding(3.dp).combinedClickable(onClick = filterClickCB, onLongClick = filterLongClickCB)) Spacer(modifier = Modifier.width(15.dp)) Icon(imageVector = ImageVector.vectorResource(R.drawable.ic_settings_white), tint = textColor, contentDescription = "butShowSettings", modifier = Modifier.width(40.dp).height(40.dp).padding(3.dp).clickable(onClick = { if (feed != null) { - val fragment = FeedSettingsFragment.newInstance(feed) + val fragment = FeedSettingsFragment.newInstance(feed!!) activity.loadChildFragment(fragment, TransitionEffect.SLIDE) } })) - Spacer(modifier = Modifier.weight(0.5f)) - Text(episodes.size.toString() + " / " + feed?.episodes?.size?.toString(), textAlign = TextAlign.Center, color = Color.White, style = MaterialTheme.typography.bodyLarge) + Spacer(modifier = Modifier.weight(0.4f)) + Text(episodes.size.toString() + " / " + feed?.episodes?.size?.toString(), textAlign = TextAlign.End, color = textColor, fontWeight = FontWeight.Bold, style = MaterialTheme.typography.bodyLarge) } // Icon(imageVector = ImageVector.vectorResource(R.drawable.ic_rounded_corner_left), contentDescription = "left_corner", // Modifier.width(12.dp).height(12.dp).constrainAs(image1) { @@ -274,21 +292,23 @@ import java.util.concurrent.Semaphore // bottom.linkTo(parent.bottom) // end.linkTo(parent.end) // }) - AsyncImage(model = feed?.imageUrl?:"", contentDescription = "imgvCover", error = painterResource(R.mipmap.ic_launcher), - modifier = Modifier.width(120.dp).height(120.dp).padding(start = 16.dp, end = 16.dp, bottom = 12.dp).constrainAs(imgvCover) { - bottom.linkTo(parent.bottom) - start.linkTo(parent.start) - }.clickable(onClick = { - if (feed != null) { - val fragment = FeedInfoFragment.newInstance(feed) - activity.loadChildFragment(fragment, TransitionEffect.SLIDE) - } - })) - Column(Modifier.fillMaxWidth().constrainAs(taColumn) { - top.linkTo(imgvCover.top) - start.linkTo(imgvCover.end) }) { - Text(feed?.title?:"", color = textColor, fontWeight = FontWeight.Bold, style = MaterialTheme.typography.bodyLarge, modifier = Modifier.fillMaxWidth(), maxLines = 2, overflow = TextOverflow.Ellipsis) - Text(feed?.author?:"", color = textColor, fontWeight = FontWeight.Bold, style = MaterialTheme.typography.bodyMedium, modifier = Modifier.fillMaxWidth(), maxLines = 1, overflow = TextOverflow.Ellipsis) + Row(verticalAlignment = Alignment.Top, modifier = Modifier.fillMaxWidth().padding(bottom = 12.dp).constrainAs(imgvCover) { + top.linkTo(parent.top) + start.linkTo(parent.start) + end.linkTo(parent.end) + width = Dimension.fillToConstraints + }) { + AsyncImage(model = feed?.imageUrl ?: "", contentDescription = "imgvCover", error = painterResource(R.mipmap.ic_launcher), + modifier = Modifier.width(100.dp).height(100.dp).padding(start = 16.dp, end = 16.dp).clickable(onClick = { + if (feed != null) { + val fragment = FeedInfoFragment.newInstance(feed!!) + activity.loadChildFragment(fragment, TransitionEffect.SLIDE) + } + })) + Column(Modifier.padding(top = 10.dp)) { + Text(feed?.title ?: "", color = textColor, fontWeight = FontWeight.Bold, style = MaterialTheme.typography.bodyLarge, modifier = Modifier.fillMaxWidth(), maxLines = 2, overflow = TextOverflow.Ellipsis) + Text(feed?.author ?: "", color = textColor, style = MaterialTheme.typography.bodyMedium, modifier = Modifier.fillMaxWidth(), maxLines = 1, overflow = TextOverflow.Ellipsis) + } } } } @@ -386,7 +406,7 @@ import java.util.concurrent.Semaphore } catch (e: InterruptedException) { throw RuntimeException(e) } }.start() } - R.id.sort_items -> SingleFeedSortDialog(feed).show(childFragmentManager, "SortDialog") +// R.id.sort_items -> SingleFeedSortDialog(feed).show(childFragmentManager, "SortDialog") // R.id.filter_items -> {} // R.id.settings -> { // if (feed != null) { @@ -651,6 +671,7 @@ import java.util.concurrent.Semaphore } withContext(Dispatchers.Main) { Logd(TAG, "loadItems subscribe called ${feed?.title}") + rating = feed?.rating ?: Rating.UNRATED.code swipeActions.setFilter(feed?.episodeFilter) refreshHeaderView() // if (feed != null) { diff --git a/app/src/main/kotlin/ac/mdiq/podcini/ui/fragment/FeedInfoFragment.kt b/app/src/main/kotlin/ac/mdiq/podcini/ui/fragment/FeedInfoFragment.kt index cd9ecee7..2327dfbd 100644 --- a/app/src/main/kotlin/ac/mdiq/podcini/ui/fragment/FeedInfoFragment.kt +++ b/app/src/main/kotlin/ac/mdiq/podcini/ui/fragment/FeedInfoFragment.kt @@ -1,8 +1,8 @@ package ac.mdiq.podcini.ui.fragment import ac.mdiq.podcini.R +import ac.mdiq.podcini.databinding.ComposeFragmentBinding import ac.mdiq.podcini.databinding.EditTextDialogBinding -import ac.mdiq.podcini.databinding.FeedinfoBinding import ac.mdiq.podcini.net.feed.FeedUpdateManager.runOnce import ac.mdiq.podcini.net.feed.discovery.CombinedSearcher import ac.mdiq.podcini.net.utils.HtmlToPlainText @@ -42,8 +42,11 @@ import android.view.ViewGroup import androidx.activity.result.contract.ActivityResultContracts import androidx.appcompat.app.AlertDialog import androidx.appcompat.widget.Toolbar -import androidx.compose.foundation.* +import androidx.compose.foundation.background +import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.* +import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.verticalScroll import androidx.compose.material3.Button import androidx.compose.material3.Icon import androidx.compose.material3.MaterialTheme @@ -62,6 +65,7 @@ import androidx.compose.ui.text.input.TextFieldValue import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.dp import androidx.constraintlayout.compose.ConstraintLayout +import androidx.constraintlayout.compose.Dimension import androidx.documentfile.provider.DocumentFile import androidx.fragment.app.Fragment import androidx.fragment.compose.AndroidFragment @@ -80,7 +84,7 @@ import java.util.concurrent.ExecutionException @UnstableApi class FeedInfoFragment : Fragment(), Toolbar.OnMenuItemClickListener { - private var _binding: FeedinfoBinding? = null + private var _binding: ComposeFragmentBinding? = null private val binding get() = _binding!! private lateinit var feed: Feed @@ -96,7 +100,7 @@ class FeedInfoFragment : Fragment(), Toolbar.OnMenuItemClickListener { } override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View { - _binding = FeedinfoBinding.inflate(inflater) + _binding = ComposeFragmentBinding.inflate(inflater) Logd(TAG, "fragment onCreateView") toolbar = binding.toolbar toolbar.title = "" @@ -108,7 +112,7 @@ class FeedInfoFragment : Fragment(), Toolbar.OnMenuItemClickListener { txtvAuthor = feed.author ?: "" txtvUrl = feed.downloadUrl - binding.mainUI.setContent { + binding.mainView.setContent { CustomTheme(requireContext()) { if (showRemoveFeedDialog) RemoveFeedDialog(listOf(feed), onDismissRequest = {showRemoveFeedDialog = false}) { (activity as MainActivity).loadFragment(UserPreferences.defaultPage, null) @@ -200,21 +204,23 @@ class FeedInfoFragment : Fragment(), Toolbar.OnMenuItemClickListener { // bottom.linkTo(parent.bottom) // end.linkTo(parent.end) // }) - AsyncImage(model = feed.imageUrl?:"", contentDescription = "imgvCover", error = painterResource(R.mipmap.ic_launcher), - modifier = Modifier.width(120.dp).height(120.dp).padding(start = 16.dp, end = 16.dp, bottom = 12.dp).constrainAs(imgvCover) { - bottom.linkTo(parent.bottom) - start.linkTo(parent.start) - }.clickable(onClick = { + Row(verticalAlignment = Alignment.Top, modifier = Modifier.fillMaxWidth().padding(bottom = 12.dp).constrainAs(imgvCover) { + top.linkTo(parent.top) + start.linkTo(parent.start) + end.linkTo(parent.end) + width = Dimension.fillToConstraints + }) { + AsyncImage(model = feed.imageUrl ?: "", contentDescription = "imgvCover", error = painterResource(R.mipmap.ic_launcher), + modifier = Modifier.width(100.dp).height(100.dp).padding(start = 16.dp, end = 16.dp).clickable(onClick = { // if (feed != null) { // val fragment = FeedInfoFragment.newInstance(feed) // (activity as MainActivity).loadChildFragment(fragment, TransitionEffect.SLIDE) // } - })) - Column(Modifier.constrainAs(taColumn) { - top.linkTo(imgvCover.top) - start.linkTo(imgvCover.end) }) { - Text(feed.title ?:"", color = textColor, fontWeight = FontWeight.Bold, style = MaterialTheme.typography.bodyLarge, modifier = Modifier.fillMaxWidth(), maxLines = 2, overflow = TextOverflow.Ellipsis) - Text(text = txtvAuthor, color = textColor, fontWeight = FontWeight.Bold, style = MaterialTheme.typography.bodyMedium, modifier = Modifier.fillMaxWidth(), maxLines = 1, overflow = TextOverflow.Ellipsis) + })) + Column(Modifier.padding(top = 10.dp)) { + Text(feed.title ?: "", color = textColor, fontWeight = FontWeight.Bold, style = MaterialTheme.typography.bodyLarge, modifier = Modifier.fillMaxWidth(), maxLines = 2, overflow = TextOverflow.Ellipsis) + Text(text = txtvAuthor, color = textColor, style = MaterialTheme.typography.bodyMedium, modifier = Modifier.fillMaxWidth(), maxLines = 1, overflow = TextOverflow.Ellipsis) + } } } } 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 5a2f6c5f..09229034 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 @@ -1,9 +1,8 @@ package ac.mdiq.podcini.ui.fragment import ac.mdiq.podcini.R -import ac.mdiq.podcini.databinding.LogsFragmentBinding +import ac.mdiq.podcini.databinding.ComposeFragmentBinding import ac.mdiq.podcini.net.feed.FeedUpdateManager -import ac.mdiq.podcini.storage.database.Episodes.getEpisodeByGuidOrUrl import ac.mdiq.podcini.storage.database.Feeds.getFeed import ac.mdiq.podcini.storage.database.Feeds.getFeedByTitleAndAuthor import ac.mdiq.podcini.storage.database.RealmDB.realm @@ -14,8 +13,8 @@ import ac.mdiq.podcini.storage.utils.DownloadResultComparator 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.CustomTheme import ac.mdiq.podcini.ui.compose.ConfirmAddYoutubeEpisode +import ac.mdiq.podcini.ui.compose.CustomTheme import ac.mdiq.podcini.util.EventFlow import ac.mdiq.podcini.util.FlowEvent import ac.mdiq.podcini.util.Logd @@ -51,7 +50,6 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.vector.ImageVector -import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.vectorResource import androidx.compose.ui.text.style.TextOverflow @@ -66,23 +64,26 @@ import kotlinx.coroutines.withContext import java.util.* class LogsFragment : Fragment(), Toolbar.OnMenuItemClickListener { - private var _binding: LogsFragmentBinding? = null + private var _binding: ComposeFragmentBinding? = null private val binding get() = _binding!! private val shareLogs = mutableStateListOf() private val subscriptionLogs = mutableStateListOf() private val downloadLogs = mutableStateListOf() -// private var showShared by mutableStateOf(true) -// private var showSubscription by mutableStateOf(false) + private var displayUpArrow = false override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View { Logd(TAG, "fragment onCreateView") - _binding = LogsFragmentBinding.inflate(inflater) + _binding = ComposeFragmentBinding.inflate(inflater) binding.toolbar.inflateMenu(R.menu.logs) binding.toolbar.setOnMenuItemClickListener(this) - binding.lazyColumn.setContent { + displayUpArrow = parentFragmentManager.backStackEntryCount != 0 + if (savedInstanceState != null) displayUpArrow = savedInstanceState.getBoolean(KEY_UP_ARROW) + (activity as MainActivity).setupToolbarToggle(binding.toolbar, displayUpArrow) + + binding.mainView.setContent { CustomTheme(requireContext()) { when { downloadLogs.isNotEmpty() -> DownloadLogView() @@ -95,6 +96,11 @@ class LogsFragment : Fragment(), Toolbar.OnMenuItemClickListener { return binding.root } + override fun onSaveInstanceState(outState: Bundle) { + outState.putBoolean(KEY_UP_ARROW, displayUpArrow) + super.onSaveInstanceState(outState) + } + override fun onDestroyView() { Logd(TAG, "onDestroyView") _binding = null @@ -489,5 +495,6 @@ class LogsFragment : Fragment(), Toolbar.OnMenuItemClickListener { companion object { val TAG: String = LogsFragment::class.simpleName ?: "Anonymous" + private const val KEY_UP_ARROW = "up_arrow" } } 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 205495b4..bf2abb40 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,8 +1,6 @@ package ac.mdiq.podcini.ui.fragment import ac.mdiq.podcini.R -import ac.mdiq.podcini.databinding.NavListBinding -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 @@ -42,6 +40,7 @@ import androidx.compose.runtime.* import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.vector.ImageVector +import androidx.compose.ui.platform.ComposeView import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.vectorResource @@ -57,30 +56,30 @@ import com.google.android.material.bottomsheet.BottomSheetBehavior import com.google.android.material.shape.MaterialShapeDrawable import com.google.android.material.shape.ShapeAppearanceModel import io.realm.kotlin.query.Sort -import kotlinx.coroutines.* +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext import kotlin.math.max class NavDrawerFragment : Fragment(), OnSharedPreferenceChangeListener { val TAG = this::class.simpleName ?: "Anonymous" - - private var _binding: NavListBinding? = null - private val binding get() = _binding!! private val feeds = mutableStateListOf() override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View { super.onCreateView(inflater, container, savedInstanceState) - _binding = NavListBinding.inflate(inflater) checkHiddenItems() getRecentPodcasts() - binding.mainView.setContent { - CustomTheme(requireContext()) { - MainView() + val composeView = ComposeView(requireContext()).apply { + setContent { + CustomTheme(requireContext()) { + MainView() + } } } Logd(TAG, "fragment onCreateView") - setupDrawerRoundBackground(binding.root) - ViewCompat.setOnApplyWindowInsetsListener(binding.root) { view: View, insets: WindowInsetsCompat -> + setupDrawerRoundBackground(composeView) + ViewCompat.setOnApplyWindowInsetsListener(composeView) { view: View, insets: WindowInsetsCompat -> val bars: Insets = insets.getInsets(WindowInsetsCompat.Type.systemBars()) view.setPadding(bars.left, bars.top, bars.right, 0) var navigationBarHeight = 0f @@ -94,7 +93,7 @@ class NavDrawerFragment : Fragment(), OnSharedPreferenceChangeListener { insets } prefs!!.registerOnSharedPreferenceChangeListener(this) - return binding.root + return composeView } private fun checkHiddenItems() { @@ -171,7 +170,6 @@ class NavDrawerFragment : Fragment(), OnSharedPreferenceChangeListener { override fun onDestroyView() { Logd(TAG, "onDestroyView") - _binding = null prefs!!.unregisterOnSharedPreferenceChangeListener(this) super.onDestroyView() } @@ -239,7 +237,7 @@ class NavDrawerFragment : Fragment(), OnSharedPreferenceChangeListener { } fun getLastNavFragment(): String { - val lastFragment: String = prefs!!.getString(PREF_LAST_FRAGMENT_TAG, SubscriptionsFragment.TAG)?:"" + val lastFragment: String = prefs?.getString(PREF_LAST_FRAGMENT_TAG, SubscriptionsFragment.TAG)?:"" return lastFragment } 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 c0ba7767..96636898 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 @@ -1,7 +1,7 @@ package ac.mdiq.podcini.ui.fragment import ac.mdiq.podcini.R -import ac.mdiq.podcini.databinding.OnlineFeedviewFragmentBinding +import ac.mdiq.podcini.databinding.ComposeFragmentBinding import ac.mdiq.podcini.net.download.service.DownloadServiceInterface import ac.mdiq.podcini.net.feed.FeedBuilder import ac.mdiq.podcini.net.feed.FeedUrlNotFoundException @@ -81,7 +81,7 @@ import kotlin.concurrent.Volatile */ @OptIn(UnstableApi::class) class OnlineFeedFragment : Fragment() { - private var _binding: OnlineFeedviewFragmentBinding? = null + private var _binding: ComposeFragmentBinding? = null private val binding get() = _binding!! private var displayUpArrow = false @@ -124,7 +124,7 @@ class OnlineFeedFragment : Fragment() { @OptIn(UnstableApi::class) override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View { Logd(TAG, "fragment onCreateView") - _binding = OnlineFeedviewFragmentBinding.inflate(layoutInflater) + _binding = ComposeFragmentBinding.inflate(layoutInflater) displayUpArrow = parentFragmentManager.backStackEntryCount != 0 if (savedInstanceState != null) displayUpArrow = savedInstanceState.getBoolean(KEY_UP_ARROW) (activity as MainActivity).setupToolbarToggle(binding.toolbar, displayUpArrow) 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 2e874b3b..a424e76d 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 @@ -2,7 +2,7 @@ package ac.mdiq.podcini.ui.fragment import ac.mdiq.podcini.R import ac.mdiq.podcini.databinding.CheckboxDoNotShowAgainBinding -import ac.mdiq.podcini.databinding.QueueFragmentBinding +import ac.mdiq.podcini.databinding.ComposeFragmentBinding import ac.mdiq.podcini.net.download.DownloadStatus import ac.mdiq.podcini.net.feed.FeedUpdateManager import ac.mdiq.podcini.playback.base.InTheatre.curQueue @@ -85,7 +85,7 @@ import kotlin.math.max @UnstableApi class QueuesFragment : Fragment(), Toolbar.OnMenuItemClickListener { - private var _binding: QueueFragmentBinding? = null + private var _binding: ComposeFragmentBinding? = null private val binding get() = _binding!! private lateinit var emptyViewHandler: EmptyViewHandler @@ -126,7 +126,7 @@ import kotlin.math.max @UnstableApi override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View { super.onCreateView(inflater, container, savedInstanceState) - _binding = QueueFragmentBinding.inflate(inflater) + _binding = ComposeFragmentBinding.inflate(inflater) Logd(TAG, "fragment onCreateView") toolbar = binding.toolbar diff --git a/app/src/main/kotlin/ac/mdiq/podcini/ui/fragment/QuickDiscoveryFragment.kt b/app/src/main/kotlin/ac/mdiq/podcini/ui/fragment/QuickDiscoveryFragment.kt index b08f2f21..de1d35a6 100644 --- a/app/src/main/kotlin/ac/mdiq/podcini/ui/fragment/QuickDiscoveryFragment.kt +++ b/app/src/main/kotlin/ac/mdiq/podcini/ui/fragment/QuickDiscoveryFragment.kt @@ -2,7 +2,10 @@ package ac.mdiq.podcini.ui.fragment import ac.mdiq.podcini.BuildConfig import ac.mdiq.podcini.R -import ac.mdiq.podcini.databinding.* +import ac.mdiq.podcini.databinding.ComposeFragmentBinding +import ac.mdiq.podcini.databinding.QuickFeedDiscoveryBinding +import ac.mdiq.podcini.databinding.QuickFeedDiscoveryItemBinding +import ac.mdiq.podcini.databinding.SelectCountryDialogBinding import ac.mdiq.podcini.net.feed.discovery.ItunesTopListLoader import ac.mdiq.podcini.net.feed.discovery.ItunesTopListLoader.Companion.prefs import ac.mdiq.podcini.net.feed.discovery.PodcastSearchResult @@ -10,9 +13,9 @@ import ac.mdiq.podcini.storage.database.Feeds.getFeedList import ac.mdiq.podcini.ui.activity.MainActivity import ac.mdiq.podcini.ui.compose.CustomTheme import ac.mdiq.podcini.ui.compose.OnlineFeedItem -import ac.mdiq.podcini.util.Logd 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.util.DisplayMetrics @@ -24,7 +27,6 @@ import android.view.ViewGroup import android.widget.* import androidx.annotation.OptIn import androidx.appcompat.widget.Toolbar -import androidx.compose.foundation.background import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.padding @@ -37,7 +39,6 @@ import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text import androidx.compose.runtime.* import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.Color import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp import androidx.constraintlayout.compose.ConstraintLayout @@ -255,7 +256,7 @@ class QuickDiscoveryFragment : Fragment(), AdapterView.OnItemClickListener { * Searches iTunes store for top podcasts and displays results in a list. */ class DiscoveryFragment : Fragment(), Toolbar.OnMenuItemClickListener { - private var _binding: FragmentSearchResultsBinding? = null + private var _binding: ComposeFragmentBinding? = null private val binding get() = _binding!! private lateinit var toolbar: MaterialToolbar @@ -295,7 +296,7 @@ class QuickDiscoveryFragment : Fragment(), AdapterView.OnItemClickListener { @OptIn(UnstableApi::class) override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View { // Inflate the layout for this fragment - _binding = FragmentSearchResultsBinding.inflate(inflater) + _binding = ComposeFragmentBinding.inflate(inflater) Logd(TAG, "fragment onCreateView") binding.mainView.setContent { CustomTheme(requireContext()) { diff --git a/app/src/main/kotlin/ac/mdiq/podcini/ui/fragment/SearchResultsFragment.kt b/app/src/main/kotlin/ac/mdiq/podcini/ui/fragment/SearchResultsFragment.kt index 1b5a8be2..2b494745 100644 --- a/app/src/main/kotlin/ac/mdiq/podcini/ui/fragment/SearchResultsFragment.kt +++ b/app/src/main/kotlin/ac/mdiq/podcini/ui/fragment/SearchResultsFragment.kt @@ -1,7 +1,7 @@ package ac.mdiq.podcini.ui.fragment import ac.mdiq.podcini.R -import ac.mdiq.podcini.databinding.FragmentSearchResultsBinding +import ac.mdiq.podcini.databinding.ComposeFragmentBinding import ac.mdiq.podcini.net.feed.discovery.PodcastSearchResult import ac.mdiq.podcini.net.feed.discovery.PodcastSearcher import ac.mdiq.podcini.net.feed.discovery.PodcastSearcherRegistry @@ -48,7 +48,7 @@ import kotlinx.coroutines.withContext class SearchResultsFragment : Fragment() { - private var _binding: FragmentSearchResultsBinding? = null + private var _binding: ComposeFragmentBinding? = null private val binding get() = _binding!! private var searchProvider: PodcastSearcher? = null @@ -74,7 +74,7 @@ class SearchResultsFragment : Fragment() { } @UnstableApi override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View { - _binding = FragmentSearchResultsBinding.inflate(inflater) + _binding = ComposeFragmentBinding.inflate(inflater) Logd(TAG, "fragment onCreateView") binding.mainView.setContent { CustomTheme(requireContext()) { 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 c0fb25dd..aeb7bd51 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 @@ -381,7 +381,7 @@ class SubscriptionsFragment : Fragment(), Toolbar.OnMenuItemClickListener { val dir = 1 - 2*feedOrderDir // get from 0, 1 to 1, -1 val comparator: Comparator = when (feedOrder) { FeedSortOrder.UNPLAYED_NEW_OLD.index -> { - val queryString = "feedId == $0 AND (playState == ${Episode.PlayState.NEW.code} OR playState == ${Episode.PlayState.UNPLAYED.code})" + val queryString = "feedId == $0 AND (playState == ${PlayState.NEW.code} OR playState == ${PlayState.UNPLAYED.code})" val counterMap: MutableMap = mutableMapOf() for (f in feedList_) { val c = realm.query(Episode::class).query(queryString, f.id).count().find() @@ -403,7 +403,7 @@ class SubscriptionsFragment : Fragment(), Toolbar.OnMenuItemClickListener { } } FeedSortOrder.MOST_PLAYED.index -> { - val queryString = "feedId == $0 AND playState == ${Episode.PlayState.PLAYED.code}" + val queryString = "feedId == $0 AND playState == ${PlayState.PLAYED.code}" val counterMap: MutableMap = mutableMapOf() for (f in feedList_) { val c = realm.query(Episode::class).query(queryString, f.id).count().find() @@ -436,7 +436,7 @@ class SubscriptionsFragment : Fragment(), Toolbar.OnMenuItemClickListener { } FeedSortOrder.LAST_UPDATED_UNPLAYED_NEW_OLD.index -> { val queryString = - "feedId == $0 AND (playState == ${Episode.PlayState.NEW.code} OR playState == ${Episode.PlayState.UNPLAYED.code}) SORT(pubDate DESC)" + "feedId == $0 AND (playState == ${PlayState.NEW.code} OR playState == ${PlayState.UNPLAYED.code}) SORT(pubDate DESC)" val counterMap: MutableMap = mutableMapOf() for (f in feedList_) { val d = realm.query(Episode::class).query(queryString, f.id).first().find()?.pubDate ?: 0L @@ -458,7 +458,7 @@ class SubscriptionsFragment : Fragment(), Toolbar.OnMenuItemClickListener { } FeedSortOrder.MOST_DOWNLOADED_UNPLAYED.index -> { val queryString = - "feedId == $0 AND (playState == ${Episode.PlayState.NEW.code} OR playState == ${Episode.PlayState.UNPLAYED.code}) AND media.downloaded == true" + "feedId == $0 AND (playState == ${PlayState.NEW.code} OR playState == ${PlayState.UNPLAYED.code}) AND media.downloaded == true" val counterMap: MutableMap = mutableMapOf() for (f in feedList_) { val c = realm.query(Episode::class).query(queryString, f.id).count().find() @@ -469,7 +469,7 @@ class SubscriptionsFragment : Fragment(), Toolbar.OnMenuItemClickListener { } // doing FEED_ORDER_NEW else -> { - val queryString = "feedId == $0 AND playState == ${Episode.PlayState.NEW.code}" + val queryString = "feedId == $0 AND playState == ${PlayState.NEW.code}" val counterMap: MutableMap = mutableMapOf() for (f in feedList_) { val c = realm.query(Episode::class).query(queryString, f.id).count().find() @@ -954,13 +954,13 @@ class SubscriptionsFragment : Fragment(), Toolbar.OnMenuItemClickListener { } }) ) - if (feed.rating != Rating.UNRATED.code) - Icon(imageVector = ImageVector.vectorResource(Rating.fromCode(feed.rating).res), tint = MaterialTheme.colorScheme.tertiary, - contentDescription = "rating", - modifier = Modifier.background(MaterialTheme.colorScheme.tertiaryContainer).constrainAs(rating) { - start.linkTo(parent.start) - centerVerticallyTo(coverImage) - }) +// if (feed.rating != Rating.UNRATED.code) +// Icon(imageVector = ImageVector.vectorResource(Rating.fromCode(feed.rating).res), tint = MaterialTheme.colorScheme.tertiary, +// contentDescription = "rating", +// modifier = Modifier.background(MaterialTheme.colorScheme.tertiaryContainer).constrainAs(rating) { +// start.linkTo(parent.start) +// centerVerticallyTo(coverImage) +// }) } val textColor = MaterialTheme.colorScheme.onSurface Column(Modifier.weight(1f).padding(start = 10.dp).combinedClickable(onClick = { @@ -984,8 +984,13 @@ class SubscriptionsFragment : Fragment(), Toolbar.OnMenuItemClickListener { } Logd(TAG, "long clicked: ${feed.title}") })) { - Text(feed.title ?: "No title", color = textColor, maxLines = 1, overflow = TextOverflow.Ellipsis, - style = MaterialTheme.typography.bodyMedium.copy(fontWeight = FontWeight.Bold)) + Row { + if (feed.rating != Rating.UNRATED.code) + Icon(imageVector = ImageVector.vectorResource(Rating.fromCode(feed.rating).res), tint = MaterialTheme.colorScheme.tertiary, contentDescription = "rating", + modifier = Modifier.background(MaterialTheme.colorScheme.tertiaryContainer)) + Text(feed.title ?: "No title", color = textColor, maxLines = 1, overflow = TextOverflow.Ellipsis, + style = MaterialTheme.typography.bodyMedium.copy(fontWeight = FontWeight.Bold)) + } Text(feed.author ?: "No author", color = textColor, maxLines = 1, overflow = TextOverflow.Ellipsis, style = MaterialTheme.typography.bodyMedium) Row(Modifier.padding(top = 5.dp)) { val measureString = remember { NumberFormat.getInstance().format(feed.episodes.size.toLong()) + " : " + diff --git a/app/src/main/kotlin/ac/mdiq/podcini/ui/statistics/StatisticsFragment.kt b/app/src/main/kotlin/ac/mdiq/podcini/ui/statistics/StatisticsFragment.kt index 06bae5ac..fb383644 100644 --- a/app/src/main/kotlin/ac/mdiq/podcini/ui/statistics/StatisticsFragment.kt +++ b/app/src/main/kotlin/ac/mdiq/podcini/ui/statistics/StatisticsFragment.kt @@ -447,7 +447,7 @@ class StatisticsFragment : Fragment() { else { // progress import does not include playedDuration if (includeMarkedAsPlayed) { - if (m.playbackCompletionTime > 0 || m.episodeOrFetch()?.playState == Episode.PlayState.PLAYED.code) + if (m.playbackCompletionTime > 0 || m.episodeOrFetch()?.playState == PlayState.PLAYED.code) dur += m.duration else if (m.position > 0) dur += m.position } else dur += m.position diff --git a/app/src/main/res/drawable/baseline_build_24.xml b/app/src/main/res/drawable/baseline_build_24.xml new file mode 100644 index 00000000..cbc92c8d --- /dev/null +++ b/app/src/main/res/drawable/baseline_build_24.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/app/src/main/res/drawable/baseline_download_done_24.xml b/app/src/main/res/drawable/baseline_download_done_24.xml new file mode 100644 index 00000000..74b5d32e --- /dev/null +++ b/app/src/main/res/drawable/baseline_download_done_24.xml @@ -0,0 +1,7 @@ + + + + + + + diff --git a/app/src/main/res/drawable/baseline_fiber_new_24.xml b/app/src/main/res/drawable/baseline_fiber_new_24.xml new file mode 100644 index 00000000..979c0465 --- /dev/null +++ b/app/src/main/res/drawable/baseline_fiber_new_24.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/app/src/main/res/drawable/baseline_local_play_24.xml b/app/src/main/res/drawable/baseline_local_play_24.xml new file mode 100644 index 00000000..e8e09168 --- /dev/null +++ b/app/src/main/res/drawable/baseline_local_play_24.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/app/src/main/res/drawable/baseline_new_label_24.xml b/app/src/main/res/drawable/baseline_new_label_24.xml new file mode 100644 index 00000000..3eb7690c --- /dev/null +++ b/app/src/main/res/drawable/baseline_new_label_24.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/app/src/main/res/drawable/baseline_play_circle_outline_24.xml b/app/src/main/res/drawable/baseline_play_circle_outline_24.xml new file mode 100644 index 00000000..b1bdebfe --- /dev/null +++ b/app/src/main/res/drawable/baseline_play_circle_outline_24.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/app/src/main/res/drawable/baseline_play_disabled_24.xml b/app/src/main/res/drawable/baseline_play_disabled_24.xml new file mode 100644 index 00000000..fc25b014 --- /dev/null +++ b/app/src/main/res/drawable/baseline_play_disabled_24.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/app/src/main/res/drawable/baseline_visibility_off_24.xml b/app/src/main/res/drawable/baseline_visibility_off_24.xml new file mode 100644 index 00000000..5993ca39 --- /dev/null +++ b/app/src/main/res/drawable/baseline_visibility_off_24.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/app/src/main/res/drawable/baseline_watch_later_24.xml b/app/src/main/res/drawable/baseline_watch_later_24.xml new file mode 100644 index 00000000..711969d7 --- /dev/null +++ b/app/src/main/res/drawable/baseline_watch_later_24.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/app/src/main/res/layout/addfeed.xml b/app/src/main/res/layout/addfeed.xml index d0efb9d0..17a6e5d8 100644 --- a/app/src/main/res/layout/addfeed.xml +++ b/app/src/main/res/layout/addfeed.xml @@ -4,26 +4,18 @@ xmlns:app="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" android:layout_height="match_parent" - android:orientation="vertical"> + android:orientation="vertical" + android:fitsSystemWindows="true"> - - - - - + android:minHeight="?android:attr/actionBarSize" + android:theme="?attr/actionBarTheme" + app:title="@string/add_feed_label" + app:navigationContentDescription="@string/toolbar_back_button_content_description" + app:navigationIcon="?homeAsUpIndicator" /> - - - - - - - - - - - - - - - - diff --git a/app/src/main/res/layout/episode_info_fragment.xml b/app/src/main/res/layout/compose_fragment.xml similarity index 72% rename from app/src/main/res/layout/episode_info_fragment.xml rename to app/src/main/res/layout/compose_fragment.xml index 179afb08..6305ceed 100644 --- a/app/src/main/res/layout/episode_info_fragment.xml +++ b/app/src/main/res/layout/compose_fragment.xml @@ -1,27 +1,24 @@ - + android:id="@+id/compose_fragment"> - \ No newline at end of file + diff --git a/app/src/main/res/layout/downloads_fragment.xml b/app/src/main/res/layout/downloads_fragment.xml deleted file mode 100644 index 2baf5dc7..00000000 --- a/app/src/main/res/layout/downloads_fragment.xml +++ /dev/null @@ -1,35 +0,0 @@ - - - - - - - - - - - - - - - - - diff --git a/app/src/main/res/layout/feed_item_list_fragment.xml b/app/src/main/res/layout/feed_item_list_fragment.xml deleted file mode 100644 index 4b5533f4..00000000 --- a/app/src/main/res/layout/feed_item_list_fragment.xml +++ /dev/null @@ -1,35 +0,0 @@ - - - - - - - - - - - - - - - - - diff --git a/app/src/main/res/layout/feedinfo.xml b/app/src/main/res/layout/feedinfo.xml deleted file mode 100644 index a2a01973..00000000 --- a/app/src/main/res/layout/feedinfo.xml +++ /dev/null @@ -1,35 +0,0 @@ - - - - - - - - - - - - diff --git a/app/src/main/res/layout/fragment_search_results.xml b/app/src/main/res/layout/fragment_search_results.xml deleted file mode 100644 index 7f4aaa2c..00000000 --- a/app/src/main/res/layout/fragment_search_results.xml +++ /dev/null @@ -1,35 +0,0 @@ - - - - - - - - - - - - diff --git a/app/src/main/res/layout/logs_fragment.xml b/app/src/main/res/layout/logs_fragment.xml deleted file mode 100644 index 8d2f2359..00000000 --- a/app/src/main/res/layout/logs_fragment.xml +++ /dev/null @@ -1,22 +0,0 @@ - - - - - - - diff --git a/app/src/main/res/layout/nav_list.xml b/app/src/main/res/layout/nav_list.xml deleted file mode 100644 index efd3b5b7..00000000 --- a/app/src/main/res/layout/nav_list.xml +++ /dev/null @@ -1,16 +0,0 @@ - - - - - - diff --git a/app/src/main/res/layout/online_feedview_fragment.xml b/app/src/main/res/layout/online_feedview_fragment.xml deleted file mode 100644 index dc052e07..00000000 --- a/app/src/main/res/layout/online_feedview_fragment.xml +++ /dev/null @@ -1,33 +0,0 @@ - - - - - - - - - - - - diff --git a/app/src/main/res/layout/queue_fragment.xml b/app/src/main/res/layout/queue_fragment.xml deleted file mode 100644 index 09f13f67..00000000 --- a/app/src/main/res/layout/queue_fragment.xml +++ /dev/null @@ -1,36 +0,0 @@ - - - - - - - - - - - - - - - - - diff --git a/app/src/main/res/layout/search_fragment.xml b/app/src/main/res/layout/search_fragment.xml index 48e719d1..08afe406 100644 --- a/app/src/main/res/layout/search_fragment.xml +++ b/app/src/main/res/layout/search_fragment.xml @@ -5,26 +5,18 @@ android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" + android:fitsSystemWindows="true" android:id="@+id/search_fragment"> - - - - - + android:minHeight="?android:attr/actionBarSize" + android:theme="?attr/actionBarTheme" + app:title="@string/search_label" + app:navigationContentDescription="@string/toolbar_back_button_content_description" + app:navigationIcon="?homeAsUpIndicator" /> - - - - - - - - - - - - - - - - - - diff --git a/app/src/main/res/menu/feed_episodes.xml b/app/src/main/res/menu/feed_episodes.xml index d916899b..caf86e97 100644 --- a/app/src/main/res/menu/feed_episodes.xml +++ b/app/src/main/res/menu/feed_episodes.xml @@ -8,12 +8,12 @@ android:icon="@drawable/playlist_play" custom:showAsAction="always"/> - - + + + + + + diff --git a/app/src/main/res/menu/feeditemlist_context.xml b/app/src/main/res/menu/feeditemlist_context.xml deleted file mode 100644 index 6a2e27d6..00000000 --- a/app/src/main/res/menu/feeditemlist_context.xml +++ /dev/null @@ -1,59 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/app/src/main/res/menu/multi_select_context_popup.xml b/app/src/main/res/menu/multi_select_context_popup.xml deleted file mode 100644 index 730b0101..00000000 --- a/app/src/main/res/menu/multi_select_context_popup.xml +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - - \ No newline at end of file diff --git a/app/src/main/res/menu/multi_select_options.xml b/app/src/main/res/menu/multi_select_options.xml deleted file mode 100644 index 33755cbb..00000000 --- a/app/src/main/res/menu/multi_select_options.xml +++ /dev/null @@ -1,18 +0,0 @@ - - - - - - diff --git a/app/src/main/res/menu/queue_context.xml b/app/src/main/res/menu/queue_context.xml deleted file mode 100644 index 522e712e..00000000 --- a/app/src/main/res/menu/queue_context.xml +++ /dev/null @@ -1,16 +0,0 @@ - - - - - - - - - - \ No newline at end of file diff --git a/app/src/main/res/values-ar/strings.xml b/app/src/main/res/values-ar/strings.xml index 2c7edc35..cbe75c51 100644 --- a/app/src/main/res/values-ar/strings.xml +++ b/app/src/main/res/values-ar/strings.xml @@ -228,31 +228,14 @@ تم مسح %d حلقات منزلة. - تم الإزالة من صندوق الوارد - علمها كمشغلة - فعل حالة التشغيل - تم الاستماع - لم يتم الاستماع بعد + + + + + علمها كمقروءة للإنتقال للتوقيتات, يجب أن تشغل الحلقة - - %d حلقة علمت كـ مقروءة. - %d حلقة علمت كـ مقروءة. - %d حلقتان علمتا كـ مقروءة. - %d حلقات علمت كـ مقروءة. - %d حلقات علمت كـ مقروءة. - %d حلقات علمت كمقروءة. - - علمها كغير مشغلة - علمها كغير مقروءة - - %d حلقة علمت بأنه لم يتم تشغيلها. - %d حلقة علمت بأنه لم يتم تشغيلها. - %d حلقتان علمتا بأنه لم يتم تشغيلها. - %d حلقات علمت بأنه لم يتم تشغيلها. - %d حلقة علمت بأنه لم يتم تشغيلها. - %d حلقات علمت بأنه لم يتم تشغيلها. - + اضف للائحة الاستماع %d حلقة أضيفت إلى لائحة الاستماع. diff --git a/app/src/main/res/values-ast/strings.xml b/app/src/main/res/values-ast/strings.xml index 82576a21..737500e8 100644 --- a/app/src/main/res/values-ast/strings.xml +++ b/app/src/main/res/values-ast/strings.xml @@ -104,9 +104,9 @@ Sentir Desaniciar Nun ye posible desaniciar el ficheru. Reaniciar el preséu podría ayudar. - Marcar como «Reprodúxose» - Marcar como «Ensin reproducir» - Marcar como «Ensin lleer» + + + Amestóse %d episodiu a la cola. Amestáronse %d episodios a la cola. diff --git a/app/src/main/res/values-br/strings.xml b/app/src/main/res/values-br/strings.xml index b1531dcf..2781ee45 100644 --- a/app/src/main/res/values-br/strings.xml +++ b/app/src/main/res/values-br/strings.xml @@ -193,29 +193,15 @@ Dilamet ez eus bet %d rann pellgarget. - Tennet eo bet eus ar voest degemer - Merkañ evel lennet - Lakaat da gemm stad al lenn - Lakaet war-well evel lennet - Lakaet war-well evel lakaet da baouez + + + + + Merkañ evel lennet Evit kemmañ al lec\'hiadur e rankit lenn ar rann - - %d rann merket evel lennet - %d rann merket evel lennet - %d rann merket evel lennet - %d rann merket evel lennet - %d rann merket evel lennet - - Merkañ evel anlennet - Merkañ evel anlennet - - %d rann merket evel anlennet. - %d rann merket evel anlennet. - %d rann merket evel anlennet. - %d rann merket evel anlennet. - %d rann merket evel anlennet. - + + Ouzhpennañ el lost %d rann ouzhpennet el lost diff --git a/app/src/main/res/values-ca/strings.xml b/app/src/main/res/values-ca/strings.xml index c35bc16e..7d5f5e6e 100644 --- a/app/src/main/res/values-ca/strings.xml +++ b/app/src/main/res/values-ca/strings.xml @@ -188,23 +188,14 @@ %d episodis baixats, suprimits. - Eliminat de la Safata d\'entrada - Marca com a reproduït - Commuta l\'estat de reproducció - Marca com a reproduït - Marca com a no reproduït + + + + + Marcar com a llegit Per a botar a posicions deus reproduir l\'episodi. - - %d episodi marcat com a reproduït. - %d episodis marcats com a reproduïts. - - Marca com a pendent - Marcar com a no llegit - - %d episodi marcat com a no llegit. - %d episodis marcats com a no llegits. - + Afegeix a la cua %d episodi afegit a la cua. diff --git a/app/src/main/res/values-cs/strings.xml b/app/src/main/res/values-cs/strings.xml index 085ecbc2..1d457a2e 100644 --- a/app/src/main/res/values-cs/strings.xml +++ b/app/src/main/res/values-cs/strings.xml @@ -217,27 +217,15 @@ %d stažených epizod smazáno. - Odebráno z nových - Označit jako poslechnuté - Přepnutí stavu přehrávání - Označeno jako poslechnuté - Označeno jako neposlechnuté + + + + + Označit jako poslechnuté Pro přeskočení na pozice musíte epizodu přehrát - - %d epizoda označena jako přehraná - %d epizody označeny jako přehrané - %d epizod označeno jako přehrané - %d epizod označeno jako přehrané - - Označit jako neposlechnuté - Označit jako nepřečtené - - %d epizoda označena jako neposlechnutá - %d epizody označeny jako neposlechnuté - %d epizod označeno jako neposlechnuté - %d epizod označeno jako neposlechnuté - + + Přidat do fronty %d epizoda přidána do fronty diff --git a/app/src/main/res/values-da/strings.xml b/app/src/main/res/values-da/strings.xml index 91a39a2a..ba2e4acf 100644 --- a/app/src/main/res/values-da/strings.xml +++ b/app/src/main/res/values-da/strings.xml @@ -199,23 +199,14 @@ %d overførte afsnit slettet. - Fjernet fra indbakken - Markér som afspillet - Skift afspilningstilstand - Markér som afspillet - Markér som uafspillet + + + + + Marker som læst For at springe til positioner, er du nødt til at afspille afsnittet - - %d afsnit markeret som afspillet. - %d afsnit markeret som afspillet. - - Markér som uafspillet - Marker som ulæst - - %d afsnit markeret som uafspillet. - %d afsnit markeret som uafspillede. - + Føj til kø %d afsnit føjet til køen. diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml index d66dbabe..562cbedd 100644 --- a/app/src/main/res/values-de/strings.xml +++ b/app/src/main/res/values-de/strings.xml @@ -203,23 +203,14 @@ %d heruntergeladene Episoden gelöscht. - Aus dem Posteingang entfernt - Als gespielt markieren - Abgespielt-Zustand umschalten - Als gespielt markieren - Als ungespielt markieren + + + + + Als gelesen markieren Um auf eine Position zu springen, musst du die Episode abspielen - - %d Episode als gespielt markiert. - %d Episoden als gespielt markiert. - - Als ungespielt markieren - Als ungelesen markieren - - %d Episode als ungespielt markiert. - %d Episoden als ungespielt markiert. - + Zur Warteschlange hinzufügen %d Episode zur Warteschlange hinzugefügt. diff --git a/app/src/main/res/values-es/strings.xml b/app/src/main/res/values-es/strings.xml index bb6d0964..dadba586 100644 --- a/app/src/main/res/values-es/strings.xml +++ b/app/src/main/res/values-es/strings.xml @@ -211,25 +211,14 @@ Eliminados %d episodios descargados. - Borrado de la bandeja de entrada - Marcar como reproducido - Cambiar estado de reproducción - Marcar como reproducido - Marcar como no reproducido + + + + + Marcar como leído Para saltar a posiciones, necesitas reproducir el episodio - - %depisodio marcado como reproducido. - %depisodios marcados como reproducidos. - %depisodios marcados como reproducidos. - - Marcar como no reproducido - Marcar como no leído - - %d episodio marcado como no reproducido. - %d episodios marcados como no reproducidos. - %d episodios marcados como no reproducidos. - + Añadir a la cola %d episodio añadido a la cola. diff --git a/app/src/main/res/values-et/strings.xml b/app/src/main/res/values-et/strings.xml index 76bd171e..c9f09d0e 100644 --- a/app/src/main/res/values-et/strings.xml +++ b/app/src/main/res/values-et/strings.xml @@ -160,21 +160,11 @@ 1 allalaaditud saade kustutatud. %d allalaaditud saadet kustutatud. - Märgi kuulatuks - Märgitud kuulatuks - Märgitud kui kuulamata + + + Märgi loetuks Asukohale hüppamiseks pead saadet esitama - - %d saade märgiti kuulatuks. - %d saadet märgiti kuulatuks. - - Märgitud kui kuulamata - Märgi mitteloetuks - - %d saade märgiti kui kuulamata. - %d saadet märgiti kui kuulamata. - Lisa järjekorda %d saade lisati järjekorda. diff --git a/app/src/main/res/values-eu/strings.xml b/app/src/main/res/values-eu/strings.xml index 36053bd2..2a9c4f45 100644 --- a/app/src/main/res/values-eu/strings.xml +++ b/app/src/main/res/values-eu/strings.xml @@ -187,23 +187,14 @@ %d deskargatutako kapituluak ezabatu egin dira - Sarrerako ontzitik kenduta - Markatu entzundakotzat - Erreprodukzio-egoera txandakatu - Entzundakotzat markatua - Ez entzundakotzat markatua + + + + + Markatu iraurrita gisa Posizioetara jauzi egiteko, pasartea erreproduzitu behar duzu - - %d saio markatuta ikusita bezala. - %d saio markatuta ikusita bezala. - - Markatu ez entzundakotzat bezala - Markatu ez irakurrita bezala - - %d saio markatuta ikusita bezala. - %d saio markatuta ez ikusita bezala. - + Gehitu ilaran %d saio ilaran gehitua. diff --git a/app/src/main/res/values-fa/strings.xml b/app/src/main/res/values-fa/strings.xml index 95fac2f7..3c0413c5 100644 --- a/app/src/main/res/values-fa/strings.xml +++ b/app/src/main/res/values-fa/strings.xml @@ -194,23 +194,14 @@ %dقسمت بار گرفته حذف شد. - حذف شده از صندوق ورودی - علامت‌گذاری به‌عنوان پخش‌شده - وضعیت پخش را تغییر دهید - علامت‌گذاری شد به‌عنوان پخش‌شده - علامت‌گذاری شد به‌عنوان پخش‌نشده + + + + + علامت زدن به عنوان خوانده شده برای پرش به موقعیت‌ها، می‌توانید قسمت را پخش کنید - - %d قسمت به‌عنوان پخش‌شده علامت‌گذاری شد. - %d قسمت به‌عنوان پخش‌شده علامت‌گذاری شد. - - علامت‌گذاری به‌عنوان پخش‌نشده - علامت‌گذاری به‌عنوان نخوانده‌شده - - %d قسمت به‌عنوان پخش‌نشده علامت‌گذاری شد. - %d قسمت به‌عنوان پخش‌نشده علامت‌گذاری شد. - + افزودن به صف %d قسمت به صف اضافه شد. diff --git a/app/src/main/res/values-fi/strings.xml b/app/src/main/res/values-fi/strings.xml index adadbfd0..83c8bca4 100644 --- a/app/src/main/res/values-fi/strings.xml +++ b/app/src/main/res/values-fi/strings.xml @@ -187,23 +187,14 @@ %d ladattua jaksoa poistettu. - Poistettu saapuneista - Merkitse toistetuksi - Vaihda toistettu-tila - Merkattu toistetuksi - Merkattu toistamattomaksi + + + + + Merkitse luetuksi Jaksoa pitää toistaa siirtyäksesi kohtiin - - %d jakso merkitty soitetuksi. - %d jaksoa merkitty toistetuksi - - Merkitse toistamattomaksi - Merkitse lukemattomaksi - - %d jakso merkitty soittamattomaksi. - %d jaksoa merkitty toistamattomaksi. - + Lisää jonoon %d jakso lisätty jonoon. diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml index 772fb2d8..4bf5625e 100644 --- a/app/src/main/res/values-fr/strings.xml +++ b/app/src/main/res/values-fr/strings.xml @@ -212,25 +212,14 @@ %d épisodes téléchargés supprimés. - Supprimé de la boîte de réception - Marquer comme lu - Modifier l\'état de lecture - Marqué comme lu - Marqué comme non lu + + + + + Marquer comme lu Pour changer la position l\'épisode doit être en cours de lecture - - %d épisode marqué comme lu. - %d épisodes marqués comme lus. - %d épisodes marqués comme lus. - - Marquer comme non lu - Marquer comme non lu - - %d épisode marqué comme non lu. - %d épisodes marqués comme non lus. - %d épisodes marqués comme non lus. - + Ajouter à la liste de lecture %d épisode ajouté à la liste de lecture. diff --git a/app/src/main/res/values-gl/strings.xml b/app/src/main/res/values-gl/strings.xml index fc7c7625..03eeaa03 100644 --- a/app/src/main/res/values-gl/strings.xml +++ b/app/src/main/res/values-gl/strings.xml @@ -199,23 +199,13 @@ Eliminados %d episodios descargados. - Retirado da lista de novidades - Marcar como reproducido - Cambiar estado de reprodución - Marcado como reproducido - Marcado como non reproducido + + + + + Marcar como lido Para ir á posición, debes reproducir o episodio - - %d episodio marcado como reproducido. - %d episodios marcados como reproducidos. - - Marcar como non reproducido - Marcar como non lido - - %d episodio marcado como non reproducido. - %d episodios marcados como non reproducidos. - Engadir á cola %d episodio engadido a cola. diff --git a/app/src/main/res/values-hu/strings.xml b/app/src/main/res/values-hu/strings.xml index 7992398a..1ad65afa 100644 --- a/app/src/main/res/values-hu/strings.xml +++ b/app/src/main/res/values-hu/strings.xml @@ -175,23 +175,14 @@ %d letöltött epizód törölve. - Eltávolítva a beérkezők közül - Megjelölés lejátszottként - Lejátszott állapot be/ki - Megjelölve lejátszottként - Megjelölve nem lejátszottként + + + + + Megjelölés olvasottként A pozíciókra ugráshoz le kell játszania az epizódot - - %d epizód megjelölve lejátszottként. - %d epizód megjelölve lejátszottként. - - Megjelölés nem lejátszottként - Megjelölés olvasatlanként - - %d epizód megjelölve nem lejátszottként. - %d epizód megjelölve nem lejátszottként. - + %d epizód sorbaállítva. %d epizód sorbaállítva. diff --git a/app/src/main/res/values-in/strings.xml b/app/src/main/res/values-in/strings.xml index f3a9c63c..7c403883 100644 --- a/app/src/main/res/values-in/strings.xml +++ b/app/src/main/res/values-in/strings.xml @@ -155,21 +155,14 @@ %depisode terunduh dihapus. - Dihapus dari kotak masuk - Tandai diputar - Aktifkan status diputar - Ditandai sebagai \'diputar\' - Ditandai sebagai \'belum diputar\' + + + + + Tandai dibaca Untuk melompat posisi waktu, Anda perlu memutar episode terlebih dahulu - - %d episode ditandai sebagai telah diputar. - - Tandai belum diputar - Tandai belum dibaca - - %d episode ditandai sebagai belum diputar - + %d episode ditambahkan ke antrian. diff --git a/app/src/main/res/values-it/strings.xml b/app/src/main/res/values-it/strings.xml index 72dc7b1c..c90b5888 100644 --- a/app/src/main/res/values-it/strings.xml +++ b/app/src/main/res/values-it/strings.xml @@ -212,25 +212,14 @@ %d episodi scaricati eliminati. - Rimosso dall\'inbox - Segna come riprodotto - Cambia stato di riproduzione - Segnato come riprodotto - Segnato come non riprodotto + + + + + Segna come letto Per saltare alla posizione devi riprodurre l\'episodio - - %d episodio segnato come riprodotto. - %d di episodi segnati come riprodotti. - %d episodi segnati come riprodotti. - - Segna come non riprodotto - Segna come non letto - - %d episodio segnato come non riprodotto. - %d di episodi segnati come non riprodotti. - %d episodi segnati come non riprodotti. - + Aggiungi alla coda %d episodio aggiunto alla coda. diff --git a/app/src/main/res/values-iw/strings.xml b/app/src/main/res/values-iw/strings.xml index a50bc7d4..94349bb4 100644 --- a/app/src/main/res/values-iw/strings.xml +++ b/app/src/main/res/values-iw/strings.xml @@ -217,27 +217,14 @@ %d פרקים שהורדו נמחקו. - הוסר מהדואר הנכנס - סימון כנצפה - החלפת מצב נגינה - סומן כהתנגן - סומן שלא התנגן + + + + + סימון כנקרא כדי לקפוץ למיקומים, עליך לנגן את הפרק - - פרק אחד סומן שנוגן. - %d פרקים סומנו שנוגנו. - %d פרקים סומנו שנוגנו. - %d פרקים סומנו שנוגנו. - - סימון כלא נוגן - סימון כלא נקרא - - פרק אחד סומן שטרם נוגן. - %d פרקים סומנו שטרם נוגנו. - %d פרקים סומנו שטרם נוגנו. - %d פרקים סומנו שטרם נוגנו. - + הוספה לתור פרק אחד נוסף לתור. diff --git a/app/src/main/res/values-ja/strings.xml b/app/src/main/res/values-ja/strings.xml index 7f0abc0f..c10a6fd5 100644 --- a/app/src/main/res/values-ja/strings.xml +++ b/app/src/main/res/values-ja/strings.xml @@ -147,21 +147,13 @@ ダウンロードした %d 項目のエピソードが削除されました。 - 受信トレイから取り除きました - 再生済としてマーク - 再生状態の切り替え - 再生済みとマーク済み - 未再生としてマーク + + + + + 既読としてマーク 位置にジャンプするには、エピソードを再生する必要があります - - %d エピソードを再生済にしました。 - - 未再生としてマーク - 未読としてマーク - - %d エピソードを未再生にしました。 - %d エピソードをキューに追加しました。 diff --git a/app/src/main/res/values-ko/strings.xml b/app/src/main/res/values-ko/strings.xml index 70e83a69..41f0564c 100644 --- a/app/src/main/res/values-ko/strings.xml +++ b/app/src/main/res/values-ko/strings.xml @@ -178,21 +178,14 @@ 다운로드한 %d개 에피소드 삭제함. - 새로 받음 목록에서 제거함 - 재생했다고 표시 - 재생함 상태 토글 - 재생했다고 표시 - 재생하지 않음으로 표시 + + + + + 읽었다고 표시 특정 재생 위치로 이동하려면, 에피소드를 재생해야 합니다 - - %d개 에피소드를 재생한 것으로 표시했습니다. - - 재생하지 않음으로 표시 - 읽지 않음으로 표시 - - %d개 에피소드를 재생하지 않은 것으로 표시했습니다. - + 대기열에 추가 %d개 에피소드를 대기열에 추가했습니다. diff --git a/app/src/main/res/values-lt/strings.xml b/app/src/main/res/values-lt/strings.xml index f416df91..ba662b65 100644 --- a/app/src/main/res/values-lt/strings.xml +++ b/app/src/main/res/values-lt/strings.xml @@ -151,25 +151,12 @@ %d atsiųsti epizodai ištrinti. %d atsiųsti epizodai ištrinti. - Pažymėti kaip perklausytą - Pažymėtas kaip perklausytas - Pažymėtas kaip neperklausytas + + + Pažymėti kaip perskaitytą Norint peršokti į poziciją reikia pradėti epizodo atkūrimą - - %d epizodas pažymėtas kaip perklausytas. - %d epizodai pažymėti kaip perklausyti. - %d epizodai pažymėti kaip perklausyti. - %d epizodai pažymėti kaip perklausyti. - - Pažymėti kaip neperklausytą - Pažymėti kaip neperskaitytą - - %d epizodas pažymėtas kaip neperklausytas. - %d epizodai pažymėti kaip neperklausyti. - %d epizodai pažymėti kaip neperklausyti. - %d epizodai pažymėti kaip neperklausyti. - + %d epizodas pridėtas į eilę. %d epizodai pridėti į eilę. diff --git a/app/src/main/res/values-nb/strings.xml b/app/src/main/res/values-nb/strings.xml index cd4119fc..01f45fca 100644 --- a/app/src/main/res/values-nb/strings.xml +++ b/app/src/main/res/values-nb/strings.xml @@ -183,23 +183,14 @@ %d nedlasted episoder slettet. - Fjernet fra innboksen - Marker som avspilt - Bytte avspilt status - Marker som avspilt - Marker som ikke avspilt + + + + + Merk som lest For å hoppe til posisjoner, må du spille episoden - - %d episode merket som avspilt. - %d episoder merket som avspilt. - - Marker som ikke avspilt - Merk som ulest - - %d episode merket som ikke avspilt. - %d episoder merket som ikke avspilt. - + Legg til i kø %d episode lagt til i køen. diff --git a/app/src/main/res/values-nl/strings.xml b/app/src/main/res/values-nl/strings.xml index 668746c7..393c257e 100644 --- a/app/src/main/res/values-nl/strings.xml +++ b/app/src/main/res/values-nl/strings.xml @@ -193,23 +193,14 @@ %d gedownloade afleveringen verwijderd. - Verwijderd uit Postvak IN - Als afgespeeld markeren - Afspeelstatus wijzigen - Gemarkeerd als afgespeeld - Gemarkeerd als niet-afgespeeld + + + + + Markeren als gelezen Speel de aflevering af om naar posities te springen - - %d aflevering gemarkeerd als afgespeeld. - %d afleveringen gemarkeerd als afgespeeld. - - Als niet-afgespeeld markeren - Markeren als ongelezen - - %d aflevering gemarkeerd als niet-afgespeeld. - %d afleveringen gemarkeerd als niet-afgespeeld. - + Toevoegen aan wachtrij %d aflevering toegevoegd aan de wachtrij. diff --git a/app/src/main/res/values-pl/strings.xml b/app/src/main/res/values-pl/strings.xml index 2cf70c2f..32015d22 100644 --- a/app/src/main/res/values-pl/strings.xml +++ b/app/src/main/res/values-pl/strings.xml @@ -204,27 +204,14 @@ Usunięto %d pobranych odcinków. - Usunięto ze skrzynki odbiorczej - Oznacz jako odtworzone - Pokaż status odtworzenia - Oznaczone jako odtworzone - Oznaczone jako nieodtworzone + + + + + Oznacz jako przeczytane Aby skoczyć do konkretnej pozycji, musisz odtworzyć odcinek - - Oznaczono %d odcinek jako odtworzony. - Oznaczono %d odcinki(ów) jako odtworzone. - Oznaczono %d odcinki(ów) jako odtworzone. - Oznaczono %d odcinki(ów) jako odtworzone. - - Oznacz jako nieodtworzone - Oznacz jako nieprzeczytane - - Oznaczono %d odcinek jako nieodtworzony. - Oznaczono %d odcinki(ów) jako nieodtworzone. - Oznaczono %d odcinki(ów) jako nieodtworzone. - Oznaczono %d odcinki(ów) jako nieodtworzone. - + Dodaj do kolejki %d odcinek dodano do kolejki. diff --git a/app/src/main/res/values-pt-rBR/strings.xml b/app/src/main/res/values-pt-rBR/strings.xml index 665fe18e..780cabad 100644 --- a/app/src/main/res/values-pt-rBR/strings.xml +++ b/app/src/main/res/values-pt-rBR/strings.xml @@ -199,25 +199,14 @@ %d episódios baixados deletados. - Removido da caixa de entrada - Marcar como reproduzido - Alterar estado de reprodução - Marcado como reproduzido - Marcado como não reproduzido + + + + + Marcar como lido Para pular para as posições, você precisa reproduzir o episódio - - %d episódio marcado como reproduzido. - %d episódios marcados como reproduzidos. - %d episódios marcados como reproduzidos. - - Marcar como não reproduzido - Marcar como não reproduzido - - %d episódio marcado como não reproduzido. - %d episódios marcados como não reproduzidos. - %d episódios marcados como não reproduzidos. - + Adicionar à fila %d episódio adicionado à fila. diff --git a/app/src/main/res/values-pt/strings.xml b/app/src/main/res/values-pt/strings.xml index cfd024d1..26310d62 100644 --- a/app/src/main/res/values-pt/strings.xml +++ b/app/src/main/res/values-pt/strings.xml @@ -211,25 +211,14 @@ %d episódios eliminados - Removido da caixa de entrada - Marcar como reproduzido - Alternar estado de reprodução - Marcado como reproduzido - Marcado como não reproduzido + + + + + Marcar como lido Se quiser ir para uma posição, tem que reproduzir o episódio - - %d episódio marcado como reproduzido - %d episódios marcados como reproduzidos - %d episódios marcados como reproduzidos - - Marcar como não reproduzido - Marcar como não lido - - %d episódio marcado como não reproduzido - %d episódios marcados como não reproduzidos - %d episódios marcados como não reproduzidos - + Adicionar à fila %d episódio adicionado à fila diff --git a/app/src/main/res/values-ro/strings.xml b/app/src/main/res/values-ro/strings.xml index 68c278cf..a82f18ee 100644 --- a/app/src/main/res/values-ro/strings.xml +++ b/app/src/main/res/values-ro/strings.xml @@ -208,25 +208,14 @@ %d de episoade descărcare au fost șterse. - Eliminat din inbox - Marchează ca redat - Comută starea de redare - Marcat ca redat - Marcat ca neredat + + + + + Marcați ca citit Pentru a sări către o poziție, trebuie sa începeți redarea episodului - - Un episod marcat ca redat. - %d episoade marcate ca redate. - %d de episoade marcate ca redate. - - Marchează ca neredat - Marchează ca necitit. - - Un episod marcat ca neredat. - %d episoade marcate ca neredate. - %d de episoade marcate ca neredate. - + Adaugă la coadă Un episod adăugat la coadă. diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml index 2cd53cd3..f31fc2b9 100644 --- a/app/src/main/res/values-ru/strings.xml +++ b/app/src/main/res/values-ru/strings.xml @@ -205,27 +205,14 @@ %d загруженных выпусков удалёно. - Убрано из входящих - Отметить как прослушанное - Переключить состояние \"прослушано\" - Отмечено как прослушанное - Отметить как непрослушанное + + + + + Отметить как прочитанное Для переходов в выпуске нужно слушать выпуск - - %d выпуск отмечен как прослушанный. - %d выпуска отмечены как прослушанные. - %d выпусков отмечены как прослушанные. - %d выпусков отмечено как прослушанные. - - Отметить как непрослушанное - Отметить как непрочитанное - - %d выпуск отмечен непрослушанный. - %d выпуска отмечены непрослушанные. - %d выпусков отмечены непрослушанные. - %d выпусков отмечено непрослушанные. - + Добавить в очередь %d выпуск добавлен в очередь. diff --git a/app/src/main/res/values-sk/strings.xml b/app/src/main/res/values-sk/strings.xml index d818df71..d65494b9 100644 --- a/app/src/main/res/values-sk/strings.xml +++ b/app/src/main/res/values-sk/strings.xml @@ -217,27 +217,14 @@ %dstiahnutých epizód zmazaných. - Odstránené zo schránky - Označiť ako prehrané - Prepnúť stav prehratia - Označené ako prehrané - Označené ako neprehrané + + + + + Označiť ako prečítané Preskočenie na určitú pozíciu funguje len pri prehrávaní epizódy. - - %d epizóda bola označená ako prehraná. - %d epizódy boli označené ako prehrané. - %d epizód bolo označených ako prehrané. - %d epizód bolo označených ako prehrané. - - Označiť ako neprehrané - Označiť ako prehrané - - %d epizóda bola označená ako neprehraná. - %d epizódy bolo označené ako neprehrané. - %d epizód bolo označených ako neprehrané. - %d epizód bolo označených ako neprehrané. - + Pridať do poradia %d epizóda bola pridaná do poradia diff --git a/app/src/main/res/values-sl/strings.xml b/app/src/main/res/values-sl/strings.xml index 0a1c6339..dfe6c18b 100644 --- a/app/src/main/res/values-sl/strings.xml +++ b/app/src/main/res/values-sl/strings.xml @@ -142,25 +142,12 @@ Tok Briši Ni bilo mogoče brisati datoteke. Ponovni zagon naprave bi lahko pomagal. - Označi kot predvajan - Označi kot predvajano. - Označi kot ne predvajano. + + + Označi kot prebrano Če želite skočiti na to mesto, morate predvajati epizodo - - %d označi kot predvajano. - %d označi kot predvajani. - %d označi kot predvajano. - %d označi kot predvajano. - - Označi kot ne predvajano. - Pusti ne prebrano. - - %d epizodo označi kot ne predvajano. - %depizodi označi kot ne predvajani. - %d epizode označi kot ne predvajane. - %d epizode označi kot ne predvajane. - + %d epizoda je dodana v čakalno vrsto. %d epizodi sta dodani v čakalno vrsto. diff --git a/app/src/main/res/values-sv/strings.xml b/app/src/main/res/values-sv/strings.xml index fe186e31..8d20ad5a 100644 --- a/app/src/main/res/values-sv/strings.xml +++ b/app/src/main/res/values-sv/strings.xml @@ -199,23 +199,14 @@ %d nedladdade episoder borttagna. - Tog bort från inkorg - Markera som spelad - Växla spelläge - Markera som spelad - Markera som ospelad + + + + + Markera som läst Spela episoden för att kunna hoppa till olika positioner - - 1%d episod markerad som spelad. - %d episoder markerade som spelade. - - Markera som ospelad - Markera som ospelad - - 1%d episod markerad som ospelad. - %d episoder markerade som ospelade. - + Lägg till i kön 1%d episod tillagd i kön. diff --git a/app/src/main/res/values-tr/strings.xml b/app/src/main/res/values-tr/strings.xml index 4eda2a21..131c8984 100644 --- a/app/src/main/res/values-tr/strings.xml +++ b/app/src/main/res/values-tr/strings.xml @@ -192,23 +192,14 @@ %d indirilmiş bölüm silindi. - Gelen kutusundan silindi - Oynatıldı olarak işaretle - Oynatma durumunu değiştir - Çalındı olarak işaretlendi - Çalınmadı olarak işaretlendi + + + + + Okundu olarak işaretle Başka bir konuma atlamak için bölümü oynatmanız gerekir. - - %d bölüm oynatıldı olarak işaretlendi. - %d bölüm oynatıldı olarak işaretlendi. - - Oynatılmadı olarak işaretle - Okunmadı olarak işaretle - - %d bölüm oynanmamış olarak işaretlendi. - %d bölüm oynanmamış olarak işaretlendi. - + Sıraya ekle %d bölüm kuyruğa eklendi. diff --git a/app/src/main/res/values-uk/strings.xml b/app/src/main/res/values-uk/strings.xml index 92b9b0d0..ee0edc43 100644 --- a/app/src/main/res/values-uk/strings.xml +++ b/app/src/main/res/values-uk/strings.xml @@ -217,27 +217,14 @@ %d завантажених епізодів видалено. - Вилучено з вхідних - Позначити як відтворений - Перемикнути стан прослуханості - Позначено як відтворений - Позначено як невідтворений + + + + + Позначити як прочитане Щоб перейти до позиції, потрібно відтворити епізод - - %d епізод помічено як відтворений - %d епізоди помічено як відтворені - %d епізодів помічено як відтворені - %d епізодів помічено як відтворені - - Позначити як невідтворений - Позначити як непрочитане - - %d епізод помічено як невідтворений. - %d епізоди помічено як невідтворені. - %d епізодів помічено як невідтворені. - %d епізодів помічено як невідтворені. - + Додати до черги %d епізод додано до черги. diff --git a/app/src/main/res/values-zh-rCN/strings.xml b/app/src/main/res/values-zh-rCN/strings.xml index 0bc3b43f..7c727009 100644 --- a/app/src/main/res/values-zh-rCN/strings.xml +++ b/app/src/main/res/values-zh-rCN/strings.xml @@ -194,21 +194,14 @@ 删除了 %d 个已下载的节目。 - 已从收件箱删除 - 标记已播放 - 切换播放状态 - 标记为已播放 - 标记为未播放 + + + + + 标记为已读 要跳转到某处,你需要播放这一集 - - 已将%d个节目标记为已播放 - - 标记未播放 - 标为未读 - - 已将%d个节目标记为未播放 - + 加至队列 已将%d个节目添加到序列中 diff --git a/app/src/main/res/values-zh-rTW/strings.xml b/app/src/main/res/values-zh-rTW/strings.xml index 94d6aae1..3f78856d 100644 --- a/app/src/main/res/values-zh-rTW/strings.xml +++ b/app/src/main/res/values-zh-rTW/strings.xml @@ -105,17 +105,10 @@ 串流播放 刪除 刪除文件失敗。重啟設備試試看。 - 標記為已播放 + 標示為已讀 若想指定播放位置,請先播放該單集 - - 共有 %d 集標示為已播放。 - - 標記為未播放 - 標示為未讀 - - 共有 %d 集標示為未播放。 - + 已將 %d 集加入待播清單。 diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index a9d19dcc..f53418d5 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -270,27 +270,15 @@ My opinion Cancelled on - Removed from inbox - Mark as played - Toggle played state - Marked as played - Marked as unplayed + Set played state + Mark as read To jump to positions, you need to play the episode %d episode marked as favorite. %d episodes marked as favorite. - - %d episode marked as played. - %d episodes marked as played. - - Mark as unplayed - Mark as unread - - %d episode marked as unplayed. - %d episodes marked as unplayed. - + Shelve to synthetic Add to active queue diff --git a/app/src/test/kotlin/ac/mdiq/podcini/feed/FeedItemMother.kt b/app/src/test/kotlin/ac/mdiq/podcini/feed/FeedItemMother.kt index 10ffe102..7c712ba5 100644 --- a/app/src/test/kotlin/ac/mdiq/podcini/feed/FeedItemMother.kt +++ b/app/src/test/kotlin/ac/mdiq/podcini/feed/FeedItemMother.kt @@ -8,7 +8,7 @@ internal object FeedItemMother { @JvmStatic fun anyFeedItemWithImage(): Episode { - val item = Episode(0, "Item", "Item", "url", Date(), Episode.PlayState.PLAYED.code, FeedMother.anyFeed()) + val item = Episode(0, "Item", "Item", "url", Date(), PlayState.PLAYED.code, FeedMother.anyFeed()) item.imageUrl = (IMAGE_URL) return item } diff --git a/app/src/test/kotlin/ac/mdiq/podcini/storage/DbCleanupTests.kt b/app/src/test/kotlin/ac/mdiq/podcini/storage/DbCleanupTests.kt index 67296a9d..b6e39254 100644 --- a/app/src/test/kotlin/ac/mdiq/podcini/storage/DbCleanupTests.kt +++ b/app/src/test/kotlin/ac/mdiq/podcini/storage/DbCleanupTests.kt @@ -87,7 +87,7 @@ open class DbCleanupTests { val items: MutableList = ArrayList() feed.episodes.addAll(items) val files: MutableList = ArrayList() - populateItems(numItems, feed, items, files, Episode.PlayState.PLAYED.code, false, false) + populateItems(numItems, feed, items, files, PlayState.PLAYED.code, false, false) performAutoCleanup(context) for (i in files.indices) { @@ -104,7 +104,7 @@ open class DbCleanupTests { for (i in 0 until numItems) { val itemDate = Date((numItems - i).toLong()) var playbackCompletionDate: Date? = null - if (itemState == Episode.PlayState.PLAYED.code) { + if (itemState == PlayState.PLAYED.code) { playbackCompletionDate = itemDate } val item = Episode(0, "title", "id$i", "link", itemDate, itemState, feed) @@ -140,7 +140,7 @@ open class DbCleanupTests { val items: MutableList = ArrayList() feed.episodes.addAll(items) val files: MutableList = ArrayList() - populateItems(numItems, feed, items, files, Episode.PlayState.UNPLAYED.code, false, false) + populateItems(numItems, feed, items, files, PlayState.UNPLAYED.code, false, false) performAutoCleanup(context) for (file in files) { @@ -157,7 +157,7 @@ open class DbCleanupTests { val items: MutableList = ArrayList() feed.episodes.addAll(items) val files: MutableList = ArrayList() - populateItems(numItems, feed, items, files, Episode.PlayState.PLAYED.code, true, false) + populateItems(numItems, feed, items, files, PlayState.PLAYED.code, true, false) performAutoCleanup(context) for (file in files) { @@ -198,7 +198,7 @@ open class DbCleanupTests { val items: MutableList = ArrayList() feed.episodes.addAll(items) val files: MutableList = ArrayList() - populateItems(numItems, feed, items, files, Episode.PlayState.PLAYED.code, false, true) + populateItems(numItems, feed, items, files, PlayState.PLAYED.code, false, true) performAutoCleanup(context) for (file in files) { diff --git a/app/src/test/kotlin/ac/mdiq/podcini/storage/DbNullCleanupAlgorithmTest.kt b/app/src/test/kotlin/ac/mdiq/podcini/storage/DbNullCleanupAlgorithmTest.kt index e90ffdc8..2d18f4d4 100644 --- a/app/src/test/kotlin/ac/mdiq/podcini/storage/DbNullCleanupAlgorithmTest.kt +++ b/app/src/test/kotlin/ac/mdiq/podcini/storage/DbNullCleanupAlgorithmTest.kt @@ -81,7 +81,7 @@ class DbNullCleanupAlgorithmTest { feed.episodes.addAll(items) val files: MutableList = ArrayList() for (i in 0 until numItems) { - val item = Episode(0, "title", "id$i", "link", Date(), Episode.PlayState.PLAYED.code, feed) + val item = Episode(0, "title", "id$i", "link", Date(), PlayState.PLAYED.code, feed) val f = File(destFolder, "file $i") Assert.assertTrue(f.createNewFile()) diff --git a/app/src/test/kotlin/ac/mdiq/podcini/storage/DbPlayQueueCleanupAlgorithmTest.kt b/app/src/test/kotlin/ac/mdiq/podcini/storage/DbPlayQueueCleanupAlgorithmTest.kt index dcdb50aa..ad7eab37 100644 --- a/app/src/test/kotlin/ac/mdiq/podcini/storage/DbPlayQueueCleanupAlgorithmTest.kt +++ b/app/src/test/kotlin/ac/mdiq/podcini/storage/DbPlayQueueCleanupAlgorithmTest.kt @@ -33,7 +33,7 @@ class DbPlayQueueCleanupAlgorithmTest : DbCleanupTests() { val items: MutableList = ArrayList() feed.episodes.addAll(items) val files: MutableList = ArrayList() - populateItems(numItems, feed, items, files, Episode.PlayState.UNPLAYED.code, false, false) + populateItems(numItems, feed, items, files, PlayState.UNPLAYED.code, false, false) performAutoCleanup(context) for (i in files.indices) { diff --git a/app/src/test/kotlin/ac/mdiq/podcini/storage/DbTasksTest.kt b/app/src/test/kotlin/ac/mdiq/podcini/storage/DbTasksTest.kt index 9915895d..784e2f53 100644 --- a/app/src/test/kotlin/ac/mdiq/podcini/storage/DbTasksTest.kt +++ b/app/src/test/kotlin/ac/mdiq/podcini/storage/DbTasksTest.kt @@ -61,7 +61,7 @@ class DbTasksTest { feed.episodes.clear() for (i in 0 until numItems) { feed.episodes.add(Episode(0, "item $i", "id $i", "link $i", - Date(), Episode.PlayState.UNPLAYED.code, feed)) + Date(), PlayState.UNPLAYED.code, feed)) } val newFeed = updateFeed(context, feed, false) @@ -97,7 +97,7 @@ class DbTasksTest { feed.episodes.clear() for (i in 0 until numItemsOld) { feed.episodes.add(Episode(0, "item $i", "id $i", "link $i", - Date(i.toLong()), Episode.PlayState.PLAYED.code, feed)) + Date(i.toLong()), PlayState.PLAYED.code, feed)) } // val adapter = getInstance() // adapter.open() @@ -117,7 +117,7 @@ class DbTasksTest { for (i in numItemsOld until numItemsNew + numItemsOld) { feed.episodes.add(0, Episode(0, "item $i", "id $i", "link $i", - Date(i.toLong()), Episode.PlayState.UNPLAYED.code, feed)) + Date(i.toLong()), PlayState.UNPLAYED.code, feed)) } val newFeed = updateFeed(context, feed, false) @@ -134,7 +134,7 @@ class DbTasksTest { @Test fun testUpdateFeedMediaUrlResetState() { val feed = Feed("url", null, "title") - val item = Episode(0, "item", "id", "link", Date(), Episode.PlayState.PLAYED.code, feed) + val item = Episode(0, "item", "id", "link", Date(), PlayState.PLAYED.code, feed) feed.episodes.add(item) // val adapter = getInstance() @@ -166,7 +166,7 @@ class DbTasksTest { feed.episodes.clear() for (i in 0..9) { feed.episodes.add( - Episode(0, "item $i", "id $i", "link $i", Date(i.toLong()), Episode.PlayState.PLAYED.code, feed)) + Episode(0, "item $i", "id $i", "link $i", Date(i.toLong()), PlayState.PLAYED.code, feed)) } // val adapter = getInstance() // adapter.open() @@ -188,7 +188,7 @@ class DbTasksTest { feed.episodes.clear() for (i in 0..9) { val item = - Episode(0, "item $i", "id $i", "link $i", Date(i.toLong()), Episode.PlayState.PLAYED.code, feed) + Episode(0, "item $i", "id $i", "link $i", Date(i.toLong()), PlayState.PLAYED.code, feed) val media = EpisodeMedia(item, "download url $i", 123, "media/mp3") item.setMedia(media) feed.episodes.add(item) @@ -244,7 +244,7 @@ class DbTasksTest { val items: MutableList = ArrayList(numFeedItems) for (i in 1..numFeedItems) { val item = Episode(0, "item $i of $title", "id$title$i", "link", - Date(), Episode.PlayState.UNPLAYED.code, feed) + Date(), PlayState.UNPLAYED.code, feed) items.add(item) } feed.episodes.addAll(items) diff --git a/app/src/test/kotlin/ac/mdiq/podcini/storage/DbWriterTest.kt b/app/src/test/kotlin/ac/mdiq/podcini/storage/DbWriterTest.kt index 4a5cd39f..5b59b566 100644 --- a/app/src/test/kotlin/ac/mdiq/podcini/storage/DbWriterTest.kt +++ b/app/src/test/kotlin/ac/mdiq/podcini/storage/DbWriterTest.kt @@ -97,7 +97,7 @@ class DbWriterTest { val feed = Feed("url", null, "title") val items: MutableList = ArrayList() feed.episodes.addAll(items) - val item = Episode(0, "Item", "Item", "url", Date(), Episode.PlayState.PLAYED.code, feed) + val item = Episode(0, "Item", "Item", "url", Date(), PlayState.PLAYED.code, feed) items.add(item) val media = EpisodeMedia(0, item, duration, 1, 1, "mime_type", "dummy path", "download_url", true, null, 0, 0) @@ -138,7 +138,7 @@ class DbWriterTest { val feed = Feed("url", null, "title") val items: MutableList = ArrayList() feed.episodes.addAll(items) - val item = Episode(0, "Item", "Item", "url", Date(), Episode.PlayState.PLAYED.code, feed) + val item = Episode(0, "Item", "Item", "url", Date(), PlayState.PLAYED.code, feed) var media: EpisodeMedia? = EpisodeMedia(0, item, 1, 1, 1, "mime_type", dest.absolutePath, "download_url", true, null, 0, 0) @@ -176,7 +176,7 @@ class DbWriterTest { val feed = Feed("url", null, "title") val items: MutableList = ArrayList() feed.episodes.addAll(items) - val item = Episode(0, "Item", "Item", "url", Date(), Episode.PlayState.UNPLAYED.code, feed) + val item = Episode(0, "Item", "Item", "url", Date(), PlayState.UNPLAYED.code, feed) var media: EpisodeMedia? = EpisodeMedia(0, item, 1, 1, 1, "mime_type", dest.absolutePath, "download_url", true, null, 0, 0) @@ -218,7 +218,7 @@ class DbWriterTest { val itemFiles: MutableList = ArrayList() // create items with downloaded media files for (i in 0..9) { - val item = Episode(0, "Item $i", "Item$i", "url", Date(), Episode.PlayState.PLAYED.code, feed) + val item = Episode(0, "Item $i", "Item$i", "url", Date(), PlayState.PLAYED.code, feed) feed.episodes.add(item) val enc = File(destFolder, "file $i") @@ -308,7 +308,7 @@ class DbWriterTest { // create items for (i in 0..9) { - val item = Episode(0, "Item $i", "Item$i", "url", Date(), Episode.PlayState.PLAYED.code, feed) + val item = Episode(0, "Item $i", "Item$i", "url", Date(), PlayState.PLAYED.code, feed) feed.episodes.add(item) } @@ -352,7 +352,7 @@ class DbWriterTest { // create items with downloaded media files for (i in 0..9) { - val item = Episode(0, "Item $i", "Item$i", "url", Date(), Episode.PlayState.PLAYED.code, feed) + val item = Episode(0, "Item $i", "Item$i", "url", Date(), PlayState.PLAYED.code, feed) feed.episodes.add(item) val enc = File(destFolder, "file $i") val media = EpisodeMedia(0, item, 1, 1, 1, "mime_type", @@ -415,7 +415,7 @@ class DbWriterTest { // create items with downloaded media files for (i in 0..9) { - val item = Episode(0, "Item $i", "Item$i", "url", Date(), Episode.PlayState.PLAYED.code, feed) + val item = Episode(0, "Item $i", "Item$i", "url", Date(), PlayState.PLAYED.code, feed) feed.episodes.add(item) val enc = File(destFolder, "file $i") val media = EpisodeMedia(0, item, 1, 1, 1, "mime_type", @@ -463,7 +463,7 @@ class DbWriterTest { // create items for (i in 0..9) { - val item = Episode(0, "Item $i", "Item$i", "url", Date(), Episode.PlayState.PLAYED.code, feed) + val item = Episode(0, "Item $i", "Item$i", "url", Date(), PlayState.PLAYED.code, feed) item.setMedia(EpisodeMedia(item, "", 0, "")) feed.episodes.add(item) } @@ -494,7 +494,7 @@ class DbWriterTest { private fun playbackHistorySetup(playbackCompletionDate: Date?): EpisodeMedia { val feed = Feed("url", null, "title") feed.episodes.clear() - val item = Episode(0, "title", "id", "link", Date(), Episode.PlayState.PLAYED.code, feed) + val item = Episode(0, "title", "id", "link", Date(), PlayState.PLAYED.code, feed) val media = EpisodeMedia(0, item, 10, 0, 1, "mime", null, "url", false, playbackCompletionDate, 0, 0) feed.episodes.add(item) @@ -547,7 +547,7 @@ class DbWriterTest { val feed = Feed("url", null, "title") feed.episodes.clear() for (i in 0 until numItems) { - val item = Episode(0, "title $i", "id $i", "link $i", Date(), Episode.PlayState.PLAYED.code, feed) + val item = Episode(0, "title $i", "id $i", "link $i", Date(), PlayState.PLAYED.code, feed) item.setMedia(EpisodeMedia(item, "", 0, "")) feed.episodes.add(item) } @@ -576,7 +576,7 @@ class DbWriterTest { fun testAddQueueItemSingleItem() { val feed = Feed("url", null, "title") feed.episodes.clear() - val item = Episode(0, "title", "id", "link", Date(), Episode.PlayState.PLAYED.code, feed) + val item = Episode(0, "title", "id", "link", Date(), PlayState.PLAYED.code, feed) item.setMedia(EpisodeMedia(item, "", 0, "")) feed.episodes.add(item) @@ -587,7 +587,7 @@ class DbWriterTest { Assert.assertTrue(item.id != 0L) runBlocking { - val job = addToQueue(true, item) + val job = addToQueue(item) withTimeout(TIMEOUT*1000) { job.join() } } @@ -605,7 +605,7 @@ class DbWriterTest { fun testAddQueueItemSingleItemAlreadyInQueue() { val feed = Feed("url", null, "title") feed.episodes.clear() - val item = Episode(0, "title", "id", "link", Date(), Episode.PlayState.PLAYED.code, feed) + val item = Episode(0, "title", "id", "link", Date(), PlayState.PLAYED.code, feed) item.setMedia(EpisodeMedia(item, "", 0, "")) feed.episodes.add(item) @@ -616,7 +616,7 @@ class DbWriterTest { Assert.assertTrue(item.id != 0L) runBlocking { - val job = addToQueue(true, item) + val job = addToQueue(item) withTimeout(TIMEOUT*1000) { job.join() } } @@ -629,7 +629,7 @@ class DbWriterTest { // adapter.close() runBlocking { - val job = addToQueue(true, item) + val job = addToQueue(item) withTimeout(TIMEOUT*1000) { job.join() } } // adapter = getInstance() @@ -766,7 +766,7 @@ class DbWriterTest { feed.episodes.clear() for (i in 0 until numItems) { val item = Episode(0, "title $i", "id $i", "link $i", - Date(), Episode.PlayState.PLAYED.code, feed) + Date(), PlayState.PLAYED.code, feed) item.setMedia(EpisodeMedia(item, "", 0, "")) feed.episodes.add(item) } @@ -817,7 +817,7 @@ class DbWriterTest { val feed = Feed("url", null, "title") feed.episodes.clear() for (i in 0 until numItems) { - val item = Episode(0, "title $i", "id $i", "link $i", Date(), Episode.PlayState.NEW.code, feed) + val item = Episode(0, "title $i", "id $i", "link $i", Date(), PlayState.NEW.code, feed) item.setMedia(EpisodeMedia(item, "", 0, "")) feed.episodes.add(item) } @@ -848,7 +848,7 @@ class DbWriterTest { val feed = Feed("url", null, "title") feed.episodes.clear() for (i in 0 until numItems) { - val item = Episode(0, "title $i", "id $i", "link $i", Date(), Episode.PlayState.PLAYED.code, feed) + val item = Episode(0, "title $i", "id $i", "link $i", Date(), PlayState.PLAYED.code, feed) item.setMedia(EpisodeMedia(item, "", 0, "")) feed.episodes.add(item) } diff --git a/app/src/test/kotlin/ac/mdiq/podcini/storage/EpisodeDuplicateGuesserTest.kt b/app/src/test/kotlin/ac/mdiq/podcini/storage/EpisodeDuplicateGuesserTest.kt index 287bc5fd..673717a8 100644 --- a/app/src/test/kotlin/ac/mdiq/podcini/storage/EpisodeDuplicateGuesserTest.kt +++ b/app/src/test/kotlin/ac/mdiq/podcini/storage/EpisodeDuplicateGuesserTest.kt @@ -57,7 +57,7 @@ class EpisodeDuplicateGuesserTest { private fun item(guid: String, title: String, downloadUrl: String, date: Long, duration: Long, mime: String ): Episode { - val item = Episode(0, title, guid, "link", Date(date), Episode.PlayState.PLAYED.code, null) + val item = Episode(0, title, guid, "link", Date(date), PlayState.PLAYED.code, null) val media = EpisodeMedia(item, downloadUrl, duration, mime) item.setMedia(media) return item diff --git a/app/src/test/kotlin/ac/mdiq/podcini/storage/ExceptFavoriteCleanupAlgorithmTest.kt b/app/src/test/kotlin/ac/mdiq/podcini/storage/ExceptFavoriteCleanupAlgorithmTest.kt index d91c9837..3db6cdce 100644 --- a/app/src/test/kotlin/ac/mdiq/podcini/storage/ExceptFavoriteCleanupAlgorithmTest.kt +++ b/app/src/test/kotlin/ac/mdiq/podcini/storage/ExceptFavoriteCleanupAlgorithmTest.kt @@ -29,7 +29,7 @@ class ExceptFavoriteCleanupAlgorithmTest : DbCleanupTests() { val items: MutableList = ArrayList() feed.episodes.addAll(items) val files: MutableList = ArrayList() - populateItems(numberOfItems, feed, items, files, Episode.PlayState.UNPLAYED.code, false, false) + populateItems(numberOfItems, feed, items, files, PlayState.UNPLAYED.code, false, false) performAutoCleanup(context) for (i in files.indices) { @@ -48,7 +48,7 @@ class ExceptFavoriteCleanupAlgorithmTest : DbCleanupTests() { val items: MutableList = ArrayList() feed.episodes.addAll(items) val files: MutableList = ArrayList() - populateItems(numberOfItems, feed, items, files, Episode.PlayState.UNPLAYED.code, true, false) + populateItems(numberOfItems, feed, items, files, PlayState.UNPLAYED.code, true, false) performAutoCleanup(context) for (i in files.indices) { @@ -67,7 +67,7 @@ class ExceptFavoriteCleanupAlgorithmTest : DbCleanupTests() { val items: MutableList = ArrayList() feed.episodes.addAll(items) val files: MutableList = ArrayList() - populateItems(numberOfItems, feed, items, files, Episode.PlayState.UNPLAYED.code, false, true) + populateItems(numberOfItems, feed, items, files, PlayState.UNPLAYED.code, false, true) performAutoCleanup(context) for (i in files.indices) { diff --git a/app/src/test/kotlin/ac/mdiq/podcini/storage/ItemEnqueuePositionCalculatorTest.kt b/app/src/test/kotlin/ac/mdiq/podcini/storage/ItemEnqueuePositionCalculatorTest.kt index 0d23606b..ee0feb53 100644 --- a/app/src/test/kotlin/ac/mdiq/podcini/storage/ItemEnqueuePositionCalculatorTest.kt +++ b/app/src/test/kotlin/ac/mdiq/podcini/storage/ItemEnqueuePositionCalculatorTest.kt @@ -57,7 +57,7 @@ object ItemEnqueuePositionCalculatorTest { fun createFeedItem(id: Long): Episode { val item = Episode(id, "Item$id", "ItemId$id", "url", - Date(), Episode.PlayState.PLAYED.code, anyFeed()) + Date(), PlayState.PLAYED.code, anyFeed()) val media = EpisodeMedia(item, "http://download.url.net/$id", 1234567, "audio/mpeg") media.id = item.id item.setMedia(media) diff --git a/changelog.md b/changelog.md index fed7f72a..801b16de 100644 --- a/changelog.md +++ b/changelog.md @@ -1,3 +1,18 @@ +# 6.12.0 + +* created a new play state system: Unspecified, Building, New, Unplayed, Later, Soon, InQueue, InProgress, Skipped, Played, Ignored, + * among which Unplayed, Later, Soon, Skipped, Played, Ignored are settable by the user + * when an episode is started to play, its state is set to InProgress + * when episode is added to a queue, its state is set to InQueue, when it's removed from a queue, the state (if lower than Skipped) is set to Skipped + * in Episode filter, for now, Unplayed is all lower than Played, while Played includes Played, Ignore +* fixed issues from last release: action button not update, icon at PlayUI not update +* re-worked the header layouts of FeedInfo and FeedEpisodes +* tuned to show PlayerUI in proper conditions +* enabled open NavDrawer in LogsFragment +* in list view of Subscriptions, moved the rating icon to the title +* NavDrawer is fully in Compose +* cleaned out some unused or redundant stuff + # 6.11.7 * added author and title info in SharedLog diff --git a/fastlane/metadata/android/en-US/changelogs/3020278.txt b/fastlane/metadata/android/en-US/changelogs/3020278.txt new file mode 100644 index 00000000..06d6ab73 --- /dev/null +++ b/fastlane/metadata/android/en-US/changelogs/3020278.txt @@ -0,0 +1,14 @@ + Version 6.12.0 + +* created a new play state system: Unspecified, Building, New, Unplayed, Later, Soon, InQueue, InProgress, Skipped, Played, Ignored, + * among which Unplayed, Later, Soon, Skipped, Played, Ignored are settable by the user + * when an episode is started to play, its state is set to InProgress + * when episode is added to a queue, its state is set to InQueue, when it's removed from a queue, the state (if lower than Skipped) is set to Skipped + * in Episode filter, for now, Unplayed is all lower than Played, while Played includes Played, Ignore +* fixed issues from last release: action button not update, icon at PlayUI not update +* re-worked the header layouts of FeedInfo and FeedEpisodes +* tuned to show PlayerUI in proper conditions +* enabled open NavDrawer in LogsFragment +* in list view of Subscriptions, moved the rating icon to the title +* NavDrawer is fully in Compose +* cleaned out some unused or redundant stuff