6.3.4 commit
This commit is contained in:
parent
e5188bc998
commit
5fe3f049d2
11
README.md
11
README.md
|
@ -22,7 +22,7 @@ Compared to AntennaPod this project:
|
||||||
3. Iron-age celebrity SQLite is replaced with modern object-base Realm DB (Podcini.R),
|
3. Iron-age celebrity SQLite is replaced with modern object-base Realm DB (Podcini.R),
|
||||||
4. Outfits with Viewbinding, Coil replacing Glide, coroutines replacing RxJava and threads, SharedFlow replacing EventBus, and jetifier removed,
|
4. Outfits with Viewbinding, Coil replacing Glide, coroutines replacing RxJava and threads, SharedFlow replacing EventBus, and jetifier removed,
|
||||||
5. Boasts new UI's including streamlined drawer, subscriptions view and player controller,
|
5. Boasts new UI's including streamlined drawer, subscriptions view and player controller,
|
||||||
6. Supports multiple and circular play queues associable to any podcast
|
6. Supports multiple, virtual and circular play queues associable to any podcast
|
||||||
7. Auto-download is governed by policy and limit settings of individual feed
|
7. Auto-download is governed by policy and limit settings of individual feed
|
||||||
8. Accepts podcast as well as plain RSS and YouTube feeds,
|
8. Accepts podcast as well as plain RSS and YouTube feeds,
|
||||||
9. Offers Readability and Text-to-Speech for RSS contents,
|
9. Offers Readability and Text-to-Speech for RSS contents,
|
||||||
|
@ -59,13 +59,20 @@ While podcast subscriptions' OPML files (from AntennaPod or any other sources) c
|
||||||
* easy switches on video player to other video mode or audio only
|
* easy switches on video player to other video mode or audio only
|
||||||
* default video player mode setting in preferences
|
* default video player mode setting in preferences
|
||||||
* when video mode is set to audio only, click on image on audio player on a video episode brings up the normal player detailed view
|
* when video mode is set to audio only, click on image on audio player on a video episode brings up the normal player detailed view
|
||||||
|
* "Prefer streaming over download" is now on setting of individual feed
|
||||||
* Multiple queues can be used: 5 queues are provided by default, user can rename or add up to 10 queues
|
* Multiple queues can be used: 5 queues are provided by default, user can rename or add up to 10 queues
|
||||||
* on app startup, the most recently updated queue is set to curQueue
|
* on app startup, the most recently updated queue is set to curQueue
|
||||||
* any episodes can be easily added/moved to the active or any designated queues
|
* any episodes can be easily added/moved to the active or any designated queues
|
||||||
* any queue can be associated with any feed for customized playing experience
|
* any queue can be associated with any feed for customized playing experience
|
||||||
* Every queue is circular: if the final item in queue finished, the first item in queue (if exists) will get played
|
* Every queue is circular: if the final item in queue finished, the first item in queue (if exists) will get played
|
||||||
* Every queue has a bin containing past episodes removed from the queue
|
* Every queue has a bin containing past episodes removed from the queue
|
||||||
* Episode played from a list other than the queue is now a one-off play, unless the episode is on the active queue, in which case, the next episode in the queue will be played
|
* Feed associated queue can be set to None, in which case:
|
||||||
|
* episodes in the feed are not automatically added to any queue, but are used as a natural queue for getting the next episode to play
|
||||||
|
* the next episode is determined in such a way:
|
||||||
|
* if the currently playing episode had been (manually) added to the active queue, then it's the next in queue
|
||||||
|
* else if "prefer streaming" is set, it's the next unplayed episode in the feed episodes list based on the current sort order
|
||||||
|
* else it's the next downloaded unplayed episode
|
||||||
|
* Otherwise, episode played from a list other than the queue is now a one-off play, unless the episode is on the active queue, in which case, the next episode in the queue will be played
|
||||||
|
|
||||||
|
|
||||||
### Podcast/Episode list
|
### Podcast/Episode list
|
||||||
|
|
|
@ -31,8 +31,8 @@ android {
|
||||||
testApplicationId "ac.mdiq.podcini.tests"
|
testApplicationId "ac.mdiq.podcini.tests"
|
||||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||||
|
|
||||||
versionCode 3020227
|
versionCode 3020228
|
||||||
versionName "6.3.3"
|
versionName "6.3.4"
|
||||||
|
|
||||||
applicationId "ac.mdiq.podcini.R"
|
applicationId "ac.mdiq.podcini.R"
|
||||||
def commit = ""
|
def commit = ""
|
||||||
|
|
|
@ -105,7 +105,7 @@ class MediaPlayerBaseTest {
|
||||||
VolumeAdaptionSetting.OFF, null, null)
|
VolumeAdaptionSetting.OFF, null, null)
|
||||||
f.preferences = prefs
|
f.preferences = prefs
|
||||||
f.episodes.clear()
|
f.episodes.clear()
|
||||||
val i = Episode(0, "t", "i", "l", Date(), Episode.UNPLAYED, f)
|
val i = Episode(0, "t", "i", "l", Date(), Episode.PlayState.UNPLAYED.code, f)
|
||||||
f.episodes.add(i)
|
f.episodes.add(i)
|
||||||
val media = EpisodeMedia(0, i, 0, 0, 0, "audio/wav", fileUrl, downloadUrl, fileUrl != null, null, 0, 0)
|
val media = EpisodeMedia(0, i, 0, 0, 0, "audio/wav", fileUrl, downloadUrl, fileUrl != null, null, 0, 0)
|
||||||
i.setMedia(media)
|
i.setMedia(media)
|
||||||
|
|
|
@ -65,7 +65,7 @@ class TaskManagerTest {
|
||||||
val f = Feed(0, null, "title", "link", "d", null, null, null, null, "id", null, "null", "url")
|
val f = Feed(0, null, "title", "link", "d", null, null, null, null, "id", null, "null", "url")
|
||||||
f.episodes.clear()
|
f.episodes.clear()
|
||||||
for (i in 0 until NUM_ITEMS) {
|
for (i in 0 until NUM_ITEMS) {
|
||||||
f.episodes.add(Episode(0, pref + i, pref + i, "link", Date(), Episode.PLAYED, f))
|
f.episodes.add(Episode(0, pref + i, pref + i, "link", Date(), Episode.PlayState.PLAYED.code, f))
|
||||||
}
|
}
|
||||||
// val adapter = getInstance()
|
// val adapter = getInstance()
|
||||||
// adapter.open()
|
// adapter.open()
|
||||||
|
|
|
@ -113,7 +113,7 @@ class UITestUtils(private val context: Context) {
|
||||||
val items: MutableList<Episode> = ArrayList()
|
val items: MutableList<Episode> = ArrayList()
|
||||||
for (j in 0 until NUM_ITEMS_PER_FEED) {
|
for (j in 0 until NUM_ITEMS_PER_FEED) {
|
||||||
val item = Episode(j.toLong(), "Feed " + (i + 1) + ": Item " + (j + 1), "item$j",
|
val item = Episode(j.toLong(), "Feed " + (i + 1) + ": Item " + (j + 1), "item$j",
|
||||||
"http://example.com/feed$i/item/$j", Date(), Episode.UNPLAYED, feed)
|
"http://example.com/feed$i/item/$j", Date(), Episode.PlayState.UNPLAYED.code, feed)
|
||||||
items.add(item)
|
items.add(item)
|
||||||
|
|
||||||
if (!hostTextOnlyFeeds) {
|
if (!hostTextOnlyFeeds) {
|
||||||
|
|
|
@ -14,7 +14,6 @@ import ac.mdiq.podcini.preferences.UserPreferences.appPrefs
|
||||||
import ac.mdiq.podcini.storage.database.Episodes
|
import ac.mdiq.podcini.storage.database.Episodes
|
||||||
import ac.mdiq.podcini.storage.database.LogsAndStats
|
import ac.mdiq.podcini.storage.database.LogsAndStats
|
||||||
import ac.mdiq.podcini.storage.database.Queues
|
import ac.mdiq.podcini.storage.database.Queues
|
||||||
import ac.mdiq.podcini.storage.database.RealmDB.upsert
|
|
||||||
import ac.mdiq.podcini.storage.database.RealmDB.upsertBlk
|
import ac.mdiq.podcini.storage.database.RealmDB.upsertBlk
|
||||||
import ac.mdiq.podcini.storage.model.DownloadResult
|
import ac.mdiq.podcini.storage.model.DownloadResult
|
||||||
import ac.mdiq.podcini.storage.model.Episode
|
import ac.mdiq.podcini.storage.model.Episode
|
||||||
|
@ -78,7 +77,7 @@ class DownloadServiceInterfaceImpl : DownloadServiceInterface() {
|
||||||
Logd(TAG, "starting cancel")
|
Logd(TAG, "starting cancel")
|
||||||
// This needs to be done here, not in the worker. Reason: The worker might or might not be running.
|
// This needs to be done here, not in the worker. Reason: The worker might or might not be running.
|
||||||
val item_ = media.episodeOrFetch()
|
val item_ = media.episodeOrFetch()
|
||||||
if (item_ != null) Episodes.deleteMediaOfEpisode(context, item_) // Remove partially downloaded file
|
if (item_ != null) Episodes.deleteEpisodeMedia(context, item_) // Remove partially downloaded file
|
||||||
val tag = WORK_TAG_EPISODE_URL + media.downloadUrl
|
val tag = WORK_TAG_EPISODE_URL + media.downloadUrl
|
||||||
val future: Future<List<WorkInfo>> = WorkManager.getInstance(context).getWorkInfosByTag(tag)
|
val future: Future<List<WorkInfo>> = WorkManager.getInstance(context).getWorkInfosByTag(tag)
|
||||||
|
|
||||||
|
@ -124,7 +123,8 @@ class DownloadServiceInterfaceImpl : DownloadServiceInterface() {
|
||||||
.addTag(WORK_TAG)
|
.addTag(WORK_TAG)
|
||||||
.addTag(WORK_TAG_EPISODE_URL + item.media!!.downloadUrl)
|
.addTag(WORK_TAG_EPISODE_URL + item.media!!.downloadUrl)
|
||||||
if (enqueueDownloadedEpisodes()) {
|
if (enqueueDownloadedEpisodes()) {
|
||||||
runBlocking { Queues.addToQueueSync(false, item, item.feed?.preferences?.queue) }
|
if (item.feed?.preferences?.queue != null)
|
||||||
|
runBlocking { Queues.addToQueueSync(false, item, item.feed?.preferences?.queue) }
|
||||||
workRequest.addTag(WORK_DATA_WAS_QUEUED)
|
workRequest.addTag(WORK_DATA_WAS_QUEUED)
|
||||||
}
|
}
|
||||||
workRequest.setInputData(Data.Builder().putLong(WORK_DATA_MEDIA_ID, item.media!!.id).build())
|
workRequest.setInputData(Data.Builder().putLong(WORK_DATA_MEDIA_ID, item.media!!.id).build())
|
||||||
|
|
|
@ -132,7 +132,7 @@ object LocalFeedUpdater {
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun createFeedItem(feed: Feed, file: FastDocumentFile, context: Context): Episode {
|
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.UNPLAYED, feed)
|
val item = Episode(0L, file.name, UUID.randomUUID().toString(), file.name, Date(file.lastModified), Episode.PlayState.UNPLAYED.code, feed)
|
||||||
item.disableAutoDownload()
|
item.disableAutoDownload()
|
||||||
|
|
||||||
val size = file.length
|
val size = file.length
|
||||||
|
|
|
@ -253,8 +253,11 @@ class LocalMediaPlayer(context: Context, callback: MediaPlayerCallback) : MediaP
|
||||||
curMedia = playable
|
curMedia = playable
|
||||||
if (curMedia is EpisodeMedia) {
|
if (curMedia is EpisodeMedia) {
|
||||||
val media_ = curMedia as EpisodeMedia
|
val media_ = curMedia as EpisodeMedia
|
||||||
curIndexInQueue = EpisodeUtil.indexOfItemWithId(curQueue.episodes, media_.id)
|
val item = media_.episodeOrFetch()
|
||||||
|
val eList = if (item?.feed?.preferences?.queue != null) curQueue.episodes else item?.feed?.getVirtualQueueItems() ?: listOf()
|
||||||
|
curIndexInQueue = EpisodeUtil.indexOfItemWithId(eList, media_.id)
|
||||||
} else curIndexInQueue = -1
|
} else curIndexInQueue = -1
|
||||||
|
|
||||||
prevMedia = curMedia
|
prevMedia = curMedia
|
||||||
this.isStreaming = stream
|
this.isStreaming = stream
|
||||||
mediaType = curMedia!!.getMediaType()
|
mediaType = curMedia!!.getMediaType()
|
||||||
|
|
|
@ -311,7 +311,7 @@ class PlaybackService : MediaSessionService() {
|
||||||
if (ended || smartMarkAsPlayed || autoSkipped || (skipped && !shouldSkipKeepEpisode())) {
|
if (ended || smartMarkAsPlayed || autoSkipped || (skipped && !shouldSkipKeepEpisode())) {
|
||||||
Logd(TAG, "onPostPlayback ended: $ended smartMarkAsPlayed: $smartMarkAsPlayed autoSkipped: $autoSkipped skipped: $skipped")
|
Logd(TAG, "onPostPlayback ended: $ended smartMarkAsPlayed: $smartMarkAsPlayed autoSkipped: $autoSkipped skipped: $skipped")
|
||||||
// only mark the item as played if we're not keeping it anyways
|
// only mark the item as played if we're not keeping it anyways
|
||||||
item = setPlayStateSync(Episode.PLAYED, ended || (skipped && smartMarkAsPlayed), item!!)
|
item = setPlayStateSync(Episode.PlayState.PLAYED.code, ended || (skipped && smartMarkAsPlayed), item!!)
|
||||||
val action = item?.feed?.preferences?.autoDeleteAction
|
val action = item?.feed?.preferences?.autoDeleteAction
|
||||||
val shouldAutoDelete = (action == AutoDeleteAction.ALWAYS ||
|
val shouldAutoDelete = (action == AutoDeleteAction.ALWAYS ||
|
||||||
(action == AutoDeleteAction.GLOBAL && item?.feed != null && shouldAutoDeleteItem(item!!.feed!!)))
|
(action == AutoDeleteAction.GLOBAL && item?.feed != null && shouldAutoDeleteItem(item!!.feed!!)))
|
||||||
|
@ -355,11 +355,6 @@ class PlaybackService : MediaSessionService() {
|
||||||
|
|
||||||
override fun getNextInQueue(currentMedia: Playable?): Playable? {
|
override fun getNextInQueue(currentMedia: Playable?): Playable? {
|
||||||
Logd(TAG, "call getNextInQueue currentMedia: ${currentMedia?.getEpisodeTitle()}")
|
Logd(TAG, "call getNextInQueue currentMedia: ${currentMedia?.getEpisodeTitle()}")
|
||||||
if (curIndexInQueue < 0) {
|
|
||||||
Logd(TAG, "getNextInQueue(), curMedia is not in curQueue")
|
|
||||||
writeNoMediaPlaying()
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
if (currentMedia !is EpisodeMedia) {
|
if (currentMedia !is EpisodeMedia) {
|
||||||
Logd(TAG, "getNextInQueue(), but playable not an instance of EpisodeMedia, so not proceeding")
|
Logd(TAG, "getNextInQueue(), but playable not an instance of EpisodeMedia, so not proceeding")
|
||||||
writeNoMediaPlaying()
|
writeNoMediaPlaying()
|
||||||
|
@ -371,20 +366,29 @@ class PlaybackService : MediaSessionService() {
|
||||||
writeNoMediaPlaying()
|
writeNoMediaPlaying()
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
// val nextItem = getNextInQueue(item)
|
if (curIndexInQueue < 0 && item.feed?.preferences?.queue != null) {
|
||||||
if (curQueue.episodes.isEmpty()) {
|
Logd(TAG, "getNextInQueue(), curMedia is not in curQueue")
|
||||||
|
writeNoMediaPlaying()
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
val eList = if (item.feed?.preferences?.queue == null) item.feed?.getVirtualQueueItems() else curQueue.episodes
|
||||||
|
if (eList.isNullOrEmpty()) {
|
||||||
Logd(TAG, "getNextInQueue queue is empty")
|
Logd(TAG, "getNextInQueue queue is empty")
|
||||||
writeNoMediaPlaying()
|
writeNoMediaPlaying()
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
Logd(TAG, "getNextInQueue eList: ${eList.size}")
|
||||||
var j = 0
|
var j = 0
|
||||||
val i = EpisodeUtil.indexOfItemWithId(curQueue.episodes, item.id)
|
val i = EpisodeUtil.indexOfItemWithId(eList, item.id)
|
||||||
|
Logd(TAG, "getNextInQueue current i: $i curIndexInQueue: $curIndexInQueue")
|
||||||
if (i < 0) {
|
if (i < 0) {
|
||||||
if (curIndexInQueue < curQueue.episodes.size) j = curIndexInQueue
|
if (curIndexInQueue >= 0 && curIndexInQueue < eList.size) j = curIndexInQueue
|
||||||
else j = curQueue.episodes.size-1
|
else j = eList.size-1
|
||||||
} else if (i < curQueue.episodes.size-1) j = i+1
|
} else if (i < eList.size-1) j = i+1
|
||||||
|
Logd(TAG, "getNextInQueue next j: $j")
|
||||||
|
|
||||||
val nextItem = unmanaged(curQueue.episodes[j])
|
val nextItem = unmanaged(eList[j])
|
||||||
|
Logd(TAG, "getNextInQueue nextItem ${nextItem.title}")
|
||||||
if (nextItem.media == null) {
|
if (nextItem.media == null) {
|
||||||
Logd(TAG, "getNextInQueue nextItem: $nextItem media is null")
|
Logd(TAG, "getNextInQueue nextItem: $nextItem media is null")
|
||||||
writeNoMediaPlaying()
|
writeNoMediaPlaying()
|
||||||
|
@ -397,7 +401,7 @@ class PlaybackService : MediaSessionService() {
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!nextItem.media!!.localFileAvailable() && !isStreamingAllowed && isFollowQueue && nextItem.feed != null && !nextItem.feed!!.isLocalFeed) {
|
if (!nextItem.media!!.localFileAvailable() && !isStreamingAllowed && isFollowQueue && nextItem.feed?.isLocalFeed != true) {
|
||||||
Logd(TAG, "getNextInQueue nextItem has no local file ${nextItem.title}")
|
Logd(TAG, "getNextInQueue nextItem has no local file ${nextItem.title}")
|
||||||
displayStreamingNotAllowedNotification(PlaybackServiceStarter(this@PlaybackService, nextItem.media!!).intent)
|
displayStreamingNotAllowedNotification(PlaybackServiceStarter(this@PlaybackService, nextItem.media!!).intent)
|
||||||
writeNoMediaPlaying()
|
writeNoMediaPlaying()
|
||||||
|
@ -405,7 +409,7 @@ class PlaybackService : MediaSessionService() {
|
||||||
}
|
}
|
||||||
EventFlow.postEvent(FlowEvent.PlayEvent(item, FlowEvent.PlayEvent.Action.END))
|
EventFlow.postEvent(FlowEvent.PlayEvent(item, FlowEvent.PlayEvent.Action.END))
|
||||||
EventFlow.postEvent(FlowEvent.PlayEvent(nextItem))
|
EventFlow.postEvent(FlowEvent.PlayEvent(nextItem))
|
||||||
return if (nextItem.media == null) nextItem.media else unmanaged(nextItem.media!!)
|
return if (nextItem.media == null) null else unmanaged(nextItem.media!!)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun findMedia(url: String): Playable? {
|
override fun findMedia(url: String): Playable? {
|
||||||
|
@ -419,14 +423,12 @@ class PlaybackService : MediaSessionService() {
|
||||||
if (stopPlaying) taskManager.cancelPositionSaver()
|
if (stopPlaying) taskManager.cancelPositionSaver()
|
||||||
|
|
||||||
if (mediaType == null) sendNotificationBroadcast(NOTIFICATION_TYPE_PLAYBACK_END, 0)
|
if (mediaType == null) sendNotificationBroadcast(NOTIFICATION_TYPE_PLAYBACK_END, 0)
|
||||||
else {
|
else sendNotificationBroadcast(NOTIFICATION_TYPE_RELOAD,
|
||||||
sendNotificationBroadcast(NOTIFICATION_TYPE_RELOAD,
|
when {
|
||||||
when {
|
isCasting -> EXTRA_CODE_CAST
|
||||||
isCasting -> EXTRA_CODE_CAST
|
mediaType == MediaType.VIDEO -> EXTRA_CODE_VIDEO
|
||||||
mediaType == MediaType.VIDEO -> EXTRA_CODE_VIDEO
|
else -> EXTRA_CODE_AUDIO
|
||||||
else -> EXTRA_CODE_AUDIO
|
})
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun ensureMediaInfoLoaded(media: Playable) {
|
override fun ensureMediaInfoLoaded(media: Playable) {
|
||||||
|
@ -962,7 +964,7 @@ class PlaybackService : MediaSessionService() {
|
||||||
if (event.action == FlowEvent.QueueEvent.Action.REMOVED) {
|
if (event.action == FlowEvent.QueueEvent.Action.REMOVED) {
|
||||||
Logd(TAG, "onQueueEvent: ending playback curEpisode ${curEpisode?.title}")
|
Logd(TAG, "onQueueEvent: ending playback curEpisode ${curEpisode?.title}")
|
||||||
for (e in event.episodes) {
|
for (e in event.episodes) {
|
||||||
Logd(TAG, "onQueueEvent: ending playback event ${e?.title}")
|
Logd(TAG, "onQueueEvent: ending playback event ${e.title}")
|
||||||
if (e.id == curEpisode?.id) {
|
if (e.id == curEpisode?.id) {
|
||||||
mPlayer?.endPlayback(hasEnded = false, wasSkipped = true, shouldContinue = true, toStoppedState = true)
|
mPlayer?.endPlayback(hasEnded = false, wasSkipped = true, shouldContinue = true, toStoppedState = true)
|
||||||
break
|
break
|
||||||
|
@ -1075,7 +1077,7 @@ class PlaybackService : MediaSessionService() {
|
||||||
if (media != null) {
|
if (media != null) {
|
||||||
media.setPosition(position)
|
media.setPosition(position)
|
||||||
media.setLastPlayedTime(System.currentTimeMillis())
|
media.setLastPlayedTime(System.currentTimeMillis())
|
||||||
if (it.isNew) it.playState = Episode.UNPLAYED
|
if (it.isNew) it.playState = Episode.PlayState.UNPLAYED.code
|
||||||
if (media.startPosition >= 0 && media.getPosition() > media.startPosition)
|
if (media.startPosition >= 0 && media.getPosition() > media.startPosition)
|
||||||
media.playedDuration = (media.playedDurationWhenStarted + media.getPosition() - media.startPosition)
|
media.playedDuration = (media.playedDurationWhenStarted + media.getPosition() - media.startPosition)
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,7 +5,7 @@ import ac.mdiq.podcini.preferences.UserPreferences.EPISODE_CLEANUP_NULL
|
||||||
import ac.mdiq.podcini.preferences.UserPreferences.appPrefs
|
import ac.mdiq.podcini.preferences.UserPreferences.appPrefs
|
||||||
import ac.mdiq.podcini.preferences.UserPreferences.episodeCacheSize
|
import ac.mdiq.podcini.preferences.UserPreferences.episodeCacheSize
|
||||||
import ac.mdiq.podcini.preferences.UserPreferences.isEnableAutodownload
|
import ac.mdiq.podcini.preferences.UserPreferences.isEnableAutodownload
|
||||||
import ac.mdiq.podcini.storage.database.Episodes.deleteMediaOfEpisode
|
import ac.mdiq.podcini.storage.database.Episodes.deleteEpisodeMedia
|
||||||
import ac.mdiq.podcini.storage.database.Episodes.getEpisodes
|
import ac.mdiq.podcini.storage.database.Episodes.getEpisodes
|
||||||
import ac.mdiq.podcini.storage.database.Episodes.getEpisodesCount
|
import ac.mdiq.podcini.storage.database.Episodes.getEpisodesCount
|
||||||
import ac.mdiq.podcini.storage.database.Queues.getInQueueEpisodeIds
|
import ac.mdiq.podcini.storage.database.Queues.getInQueueEpisodeIds
|
||||||
|
@ -84,7 +84,7 @@ object AutoCleanups {
|
||||||
for (item in delete) {
|
for (item in delete) {
|
||||||
if (item.media == null) continue
|
if (item.media == null) continue
|
||||||
try {
|
try {
|
||||||
runBlocking { deleteMediaOfEpisode(context, item).join() }
|
runBlocking { deleteEpisodeMedia(context, item).join() }
|
||||||
} catch (e: InterruptedException) {
|
} catch (e: InterruptedException) {
|
||||||
e.printStackTrace()
|
e.printStackTrace()
|
||||||
} catch (e: ExecutionException) {
|
} catch (e: ExecutionException) {
|
||||||
|
@ -138,7 +138,7 @@ object AutoCleanups {
|
||||||
for (item in delete) {
|
for (item in delete) {
|
||||||
if (item.media == null) continue
|
if (item.media == null) continue
|
||||||
try {
|
try {
|
||||||
runBlocking { deleteMediaOfEpisode(context, item).join() }
|
runBlocking { deleteEpisodeMedia(context, item).join() }
|
||||||
} catch (e: InterruptedException) {
|
} catch (e: InterruptedException) {
|
||||||
e.printStackTrace()
|
e.printStackTrace()
|
||||||
} catch (e: ExecutionException) {
|
} catch (e: ExecutionException) {
|
||||||
|
@ -205,7 +205,7 @@ object AutoCleanups {
|
||||||
val delete = if (candidates.size > numToRemove) candidates.subList(0, numToRemove) else candidates
|
val delete = if (candidates.size > numToRemove) candidates.subList(0, numToRemove) else candidates
|
||||||
for (item in delete) {
|
for (item in delete) {
|
||||||
try {
|
try {
|
||||||
runBlocking { deleteMediaOfEpisode(context, item).join() }
|
runBlocking { deleteEpisodeMedia(context, item).join() }
|
||||||
} catch (e: InterruptedException) {
|
} catch (e: InterruptedException) {
|
||||||
e.printStackTrace()
|
e.printStackTrace()
|
||||||
} catch (e: ExecutionException) {
|
} catch (e: ExecutionException) {
|
||||||
|
|
|
@ -19,10 +19,7 @@ import ac.mdiq.podcini.storage.database.RealmDB.runOnIOScope
|
||||||
import ac.mdiq.podcini.storage.database.RealmDB.upsert
|
import ac.mdiq.podcini.storage.database.RealmDB.upsert
|
||||||
import ac.mdiq.podcini.storage.database.RealmDB.upsertBlk
|
import ac.mdiq.podcini.storage.database.RealmDB.upsertBlk
|
||||||
import ac.mdiq.podcini.storage.model.Episode
|
import ac.mdiq.podcini.storage.model.Episode
|
||||||
import ac.mdiq.podcini.storage.model.Episode.Companion.BUILDING
|
import ac.mdiq.podcini.storage.model.Episode.PlayState
|
||||||
import ac.mdiq.podcini.storage.model.Episode.Companion.NEW
|
|
||||||
import ac.mdiq.podcini.storage.model.Episode.Companion.PLAYED
|
|
||||||
import ac.mdiq.podcini.storage.model.Episode.Companion.UNPLAYED
|
|
||||||
import ac.mdiq.podcini.storage.model.EpisodeFilter
|
import ac.mdiq.podcini.storage.model.EpisodeFilter
|
||||||
import ac.mdiq.podcini.storage.model.EpisodeMedia
|
import ac.mdiq.podcini.storage.model.EpisodeMedia
|
||||||
import ac.mdiq.podcini.storage.model.EpisodeSortOrder
|
import ac.mdiq.podcini.storage.model.EpisodeSortOrder
|
||||||
|
@ -98,7 +95,7 @@ object Episodes {
|
||||||
|
|
||||||
// @JvmStatic is needed because some Runnable blocks call this
|
// @JvmStatic is needed because some Runnable blocks call this
|
||||||
@OptIn(UnstableApi::class) @JvmStatic
|
@OptIn(UnstableApi::class) @JvmStatic
|
||||||
fun deleteMediaOfEpisode(context: Context, episode: Episode) : Job {
|
fun deleteEpisodeMedia(context: Context, episode: Episode) : Job {
|
||||||
Logd(TAG, "deleteMediaOfEpisode called ${episode.title}")
|
Logd(TAG, "deleteMediaOfEpisode called ${episode.title}")
|
||||||
return runOnIOScope {
|
return runOnIOScope {
|
||||||
if (episode.media == null) return@runOnIOScope
|
if (episode.media == null) return@runOnIOScope
|
||||||
|
@ -136,7 +133,7 @@ object Episodes {
|
||||||
url != null -> {
|
url != null -> {
|
||||||
// delete downloaded media file
|
// delete downloaded media file
|
||||||
val mediaFile = File(url)
|
val mediaFile = File(url)
|
||||||
if (mediaFile.exists() && !mediaFile.delete()) {
|
if (!mediaFile.delete()) {
|
||||||
Log.e(TAG, "delete media file failed: $url")
|
Log.e(TAG, "delete media file failed: $url")
|
||||||
val evt = FlowEvent.MessageEvent(context.getString(R.string.delete_failed))
|
val evt = FlowEvent.MessageEvent(context.getString(R.string.delete_failed))
|
||||||
EventFlow.postEvent(evt)
|
EventFlow.postEvent(evt)
|
||||||
|
@ -176,6 +173,7 @@ object Episodes {
|
||||||
* Remove the listed episodes and their EpisodeMedia entries.
|
* Remove the listed episodes and their EpisodeMedia entries.
|
||||||
* Deleting media also removes the download log entries.
|
* Deleting media also removes the download log entries.
|
||||||
*/
|
*/
|
||||||
|
@UnstableApi
|
||||||
fun deleteEpisodes(context: Context, episodes: List<Episode>) : Job {
|
fun deleteEpisodes(context: Context, episodes: List<Episode>) : Job {
|
||||||
return runOnIOScope {
|
return runOnIOScope {
|
||||||
val removedFromQueue: MutableList<Episode> = ArrayList()
|
val removedFromQueue: MutableList<Episode> = ArrayList()
|
||||||
|
@ -290,19 +288,19 @@ object Episodes {
|
||||||
suspend fun setPlayStateSync(played: Int, resetMediaPosition: Boolean, episode: Episode) : Episode {
|
suspend fun setPlayStateSync(played: Int, resetMediaPosition: Boolean, episode: Episode) : Episode {
|
||||||
Logd(TAG, "setPlayStateSync called resetMediaPosition: $resetMediaPosition")
|
Logd(TAG, "setPlayStateSync called resetMediaPosition: $resetMediaPosition")
|
||||||
val result = upsert(episode) {
|
val result = upsert(episode) {
|
||||||
if (played >= NEW && played <= BUILDING) it.playState = played
|
if (played >= PlayState.NEW.code && played <= PlayState.BUILDING.code) it.playState = played
|
||||||
else {
|
else {
|
||||||
if (it.playState == PLAYED) it.playState = UNPLAYED
|
if (it.playState == PlayState.PLAYED.code) it.playState = PlayState.UNPLAYED.code
|
||||||
else it.playState = PLAYED
|
else it.playState = PlayState.PLAYED.code
|
||||||
}
|
}
|
||||||
if (resetMediaPosition) it.media?.setPosition(0)
|
if (resetMediaPosition) it.media?.setPosition(0)
|
||||||
}
|
}
|
||||||
if (played == PLAYED && shouldRemoveFromQueuesMarkPlayed()) removeFromAllQueuesSync(result)
|
if (played == PlayState.PLAYED.code && shouldMarkedPlayedRemoveFromQueues()) removeFromAllQueuesSync(result)
|
||||||
EventFlow.postEvent(FlowEvent.EpisodePlayedEvent(result))
|
EventFlow.postEvent(FlowEvent.EpisodePlayedEvent(result))
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun shouldRemoveFromQueuesMarkPlayed(): Boolean {
|
private fun shouldMarkedPlayedRemoveFromQueues(): Boolean {
|
||||||
return appPrefs.getBoolean(Prefs.prefRemoveFromQueueMarkedPlayed.name, true)
|
return appPrefs.getBoolean(Prefs.prefRemoveFromQueueMarkedPlayed.name, true)
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -377,13 +377,6 @@ object Feeds {
|
||||||
backupManager.dataChanged()
|
backupManager.dataChanged()
|
||||||
}
|
}
|
||||||
|
|
||||||
// private fun persistFeedsSync(vararg feeds: Feed) {
|
|
||||||
// Logd(TAG, "persistFeedsSync called")
|
|
||||||
// for (feed in feeds) {
|
|
||||||
// upsertBlk(feed) {}
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
fun persistFeedPreferences(feed: Feed) : Job {
|
fun persistFeedPreferences(feed: Feed) : Job {
|
||||||
Logd(TAG, "persistFeedPreferences called")
|
Logd(TAG, "persistFeedPreferences called")
|
||||||
return runOnIOScope {
|
return runOnIOScope {
|
||||||
|
|
|
@ -56,7 +56,7 @@ object LogsAndStats {
|
||||||
feedTotalTime += m.duration
|
feedTotalTime += m.duration
|
||||||
if (m.lastPlayedTime in timeFilterFrom..<timeFilterTo) {
|
if (m.lastPlayedTime in timeFilterFrom..<timeFilterTo) {
|
||||||
if (includeMarkedAsPlayed) {
|
if (includeMarkedAsPlayed) {
|
||||||
if ((m.playbackCompletionTime > 0 && m.playedDuration > 0) || m.episodeOrFetch()?.playState == Episode.PLAYED || m.position > 0) {
|
if ((m.playbackCompletionTime > 0 && m.playedDuration > 0) || m.episodeOrFetch()?.playState == Episode.PlayState.PLAYED.code || m.position > 0) {
|
||||||
episodesStarted += 1
|
episodesStarted += 1
|
||||||
feedPlayedTime += m.duration
|
feedPlayedTime += m.duration
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,6 +11,7 @@ import ac.mdiq.podcini.storage.database.RealmDB.runOnIOScope
|
||||||
import ac.mdiq.podcini.storage.database.RealmDB.upsert
|
import ac.mdiq.podcini.storage.database.RealmDB.upsert
|
||||||
import ac.mdiq.podcini.storage.database.RealmDB.upsertBlk
|
import ac.mdiq.podcini.storage.database.RealmDB.upsertBlk
|
||||||
import ac.mdiq.podcini.storage.model.*
|
import ac.mdiq.podcini.storage.model.*
|
||||||
|
import ac.mdiq.podcini.storage.utils.EpisodeUtil.indexOfItemWithId
|
||||||
import ac.mdiq.podcini.storage.utils.EpisodesPermutors.getPermutor
|
import ac.mdiq.podcini.storage.utils.EpisodesPermutors.getPermutor
|
||||||
import ac.mdiq.podcini.util.Logd
|
import ac.mdiq.podcini.util.Logd
|
||||||
import ac.mdiq.podcini.util.event.EventFlow
|
import ac.mdiq.podcini.util.event.EventFlow
|
||||||
|
@ -144,7 +145,7 @@ object Queues {
|
||||||
for (event in events) EventFlow.postEvent(event)
|
for (event in events) EventFlow.postEvent(event)
|
||||||
|
|
||||||
// EventFlow.postEvent(FlowEvent.EpisodeEvent.updated(updatedItems))
|
// EventFlow.postEvent(FlowEvent.EpisodeEvent.updated(updatedItems))
|
||||||
if (markAsUnplayed && markAsUnplayeds.size > 0) setPlayState(Episode.UNPLAYED, false, *markAsUnplayeds.toTypedArray())
|
if (markAsUnplayed && markAsUnplayeds.size > 0) setPlayState(Episode.PlayState.UNPLAYED.code, false, *markAsUnplayeds.toTypedArray())
|
||||||
// if (performAutoDownload) autodownloadEpisodeMedia(context)
|
// if (performAutoDownload) autodownloadEpisodeMedia(context)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -153,7 +154,6 @@ object Queues {
|
||||||
suspend fun addToQueueSync(markAsUnplayed: Boolean, episode: Episode, queue_: PlayQueue? = null) {
|
suspend fun addToQueueSync(markAsUnplayed: Boolean, episode: Episode, queue_: PlayQueue? = null) {
|
||||||
Logd(TAG, "addToQueueSync( ... ) called")
|
Logd(TAG, "addToQueueSync( ... ) called")
|
||||||
|
|
||||||
// val queue = if (queue_ != null) unmanaged(queue_) else curQueue
|
|
||||||
val queue = queue_ ?: curQueue
|
val queue = queue_ ?: curQueue
|
||||||
val currentlyPlaying = curMedia
|
val currentlyPlaying = curMedia
|
||||||
val positionCalculator = EnqueuePositionCalculator(enqueueLocation)
|
val positionCalculator = EnqueuePositionCalculator(enqueueLocation)
|
||||||
|
@ -166,11 +166,9 @@ object Queues {
|
||||||
insertPosition++
|
insertPosition++
|
||||||
it.update()
|
it.update()
|
||||||
}
|
}
|
||||||
// queueNew.episodes.addAll(queue.episodes)
|
|
||||||
// queueNew.episodes.add(insertPosition, episode)
|
|
||||||
if (queue.id == curQueue.id) curQueue = queueNew
|
if (queue.id == curQueue.id) curQueue = queueNew
|
||||||
|
|
||||||
if (markAsUnplayed && episode.isNew) setPlayState(Episode.UNPLAYED, false, episode)
|
if (markAsUnplayed && episode.isNew) setPlayState(Episode.PlayState.UNPLAYED.code, false, episode)
|
||||||
if (queue.id == curQueue.id) EventFlow.postEvent(FlowEvent.QueueEvent.added(episode, insertPosition))
|
if (queue.id == curQueue.id) EventFlow.postEvent(FlowEvent.QueueEvent.added(episode, insertPosition))
|
||||||
// if (performAutoDownload) autodownloadEpisodeMedia(context)
|
// if (performAutoDownload) autodownloadEpisodeMedia(context)
|
||||||
}
|
}
|
||||||
|
@ -246,9 +244,10 @@ object Queues {
|
||||||
val events: MutableList<FlowEvent.QueueEvent> = ArrayList()
|
val events: MutableList<FlowEvent.QueueEvent> = ArrayList()
|
||||||
val indicesToRemove: MutableList<Int> = mutableListOf()
|
val indicesToRemove: MutableList<Int> = mutableListOf()
|
||||||
val qItems = queue.episodes.toMutableList()
|
val qItems = queue.episodes.toMutableList()
|
||||||
|
val eList = episodes.toList()
|
||||||
for (i in qItems.indices) {
|
for (i in qItems.indices) {
|
||||||
val episode = qItems[i]
|
val episode = qItems[i]
|
||||||
if (episodes.contains(episode)) {
|
if (indexOfItemWithId(eList, episode.id) >= 0) {
|
||||||
Logd(TAG, "removing from queue: ${episode.id} ${episode.title}")
|
Logd(TAG, "removing from queue: ${episode.id} ${episode.title}")
|
||||||
indicesToRemove.add(i)
|
indicesToRemove.add(i)
|
||||||
if (queue.id == curQueue.id) events.add(FlowEvent.QueueEvent.removed(episode))
|
if (queue.id == curQueue.id) events.add(FlowEvent.QueueEvent.removed(episode))
|
||||||
|
|
|
@ -18,7 +18,7 @@ import kotlin.coroutines.ContinuationInterceptor
|
||||||
object RealmDB {
|
object RealmDB {
|
||||||
private val TAG: String = RealmDB::class.simpleName ?: "Anonymous"
|
private val TAG: String = RealmDB::class.simpleName ?: "Anonymous"
|
||||||
|
|
||||||
private const val SCHEMA_VERSION_NUMBER = 17L
|
private const val SCHEMA_VERSION_NUMBER = 18L
|
||||||
|
|
||||||
private val ioScope = CoroutineScope(Dispatchers.IO)
|
private val ioScope = CoroutineScope(Dispatchers.IO)
|
||||||
|
|
||||||
|
|
|
@ -86,7 +86,7 @@ class Episode : RealmObject {
|
||||||
|
|
||||||
@Ignore
|
@Ignore
|
||||||
val isNew: Boolean
|
val isNew: Boolean
|
||||||
get() = playState == NEW
|
get() = playState == PlayState.NEW.code
|
||||||
|
|
||||||
@Ignore
|
@Ignore
|
||||||
val isInProgress: Boolean
|
val isInProgress: Boolean
|
||||||
|
@ -123,7 +123,7 @@ class Episode : RealmObject {
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
this.playState = UNPLAYED
|
this.playState = PlayState.UNPLAYED.code
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -187,19 +187,19 @@ class Episode : RealmObject {
|
||||||
}
|
}
|
||||||
|
|
||||||
fun setNew() {
|
fun setNew() {
|
||||||
playState = NEW
|
playState = PlayState.NEW.code
|
||||||
}
|
}
|
||||||
|
|
||||||
fun isPlayed(): Boolean {
|
fun isPlayed(): Boolean {
|
||||||
return playState == PLAYED
|
return playState == PlayState.PLAYED.code
|
||||||
}
|
}
|
||||||
|
|
||||||
fun setPlayed(played: Boolean) {
|
fun setPlayed(played: Boolean) {
|
||||||
playState = if (played) PLAYED else UNPLAYED
|
playState = if (played) PlayState.PLAYED.code else PlayState.UNPLAYED.code
|
||||||
}
|
}
|
||||||
|
|
||||||
fun setBuilding() {
|
fun setBuilding() {
|
||||||
playState = BUILDING
|
playState = PlayState.BUILDING.code
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -252,13 +252,15 @@ class Episode : RealmObject {
|
||||||
return (id xor (id ushr 32)).toInt()
|
return (id xor (id ushr 32)).toInt()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum class PlayState(val code: Int) {
|
||||||
|
UNSPECIFIED(-2),
|
||||||
|
NEW(-1),
|
||||||
|
UNPLAYED(0),
|
||||||
|
PLAYED(1),
|
||||||
|
BUILDING(2),
|
||||||
|
ABANDONED(3)
|
||||||
|
}
|
||||||
companion object {
|
companion object {
|
||||||
val TAG: String = Episode::class.simpleName ?: "Anonymous"
|
val TAG: String = Episode::class.simpleName ?: "Anonymous"
|
||||||
|
|
||||||
const val UNSPECIFIED: Int = -2
|
|
||||||
const val NEW: Int = -1
|
|
||||||
const val UNPLAYED: Int = 0
|
|
||||||
const val PLAYED: Int = 1
|
|
||||||
const val BUILDING: Int = 2
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,6 +3,7 @@ package ac.mdiq.podcini.storage.model
|
||||||
import ac.mdiq.podcini.storage.database.RealmDB.realm
|
import ac.mdiq.podcini.storage.database.RealmDB.realm
|
||||||
import ac.mdiq.podcini.storage.model.FeedFunding.Companion.extractPaymentLinks
|
import ac.mdiq.podcini.storage.model.FeedFunding.Companion.extractPaymentLinks
|
||||||
import ac.mdiq.podcini.storage.model.EpisodeSortOrder.Companion.fromCode
|
import ac.mdiq.podcini.storage.model.EpisodeSortOrder.Companion.fromCode
|
||||||
|
import ac.mdiq.podcini.storage.utils.EpisodesPermutors.getPermutor
|
||||||
import io.realm.kotlin.ext.realmListOf
|
import io.realm.kotlin.ext.realmListOf
|
||||||
import io.realm.kotlin.types.RealmList
|
import io.realm.kotlin.types.RealmList
|
||||||
import io.realm.kotlin.types.RealmObject
|
import io.realm.kotlin.types.RealmObject
|
||||||
|
@ -144,20 +145,7 @@ class Feed : RealmObject {
|
||||||
|
|
||||||
@Ignore
|
@Ignore
|
||||||
val mostRecentItem: Episode?
|
val mostRecentItem: Episode?
|
||||||
get() {
|
get() = realm.query(Episode::class).query("feedId == $id SORT(pubDate DESC)").first().find()
|
||||||
// // we could sort, but we don't need to, a simple search is fine...
|
|
||||||
// var mostRecentDate = Date(0)
|
|
||||||
// var mostRecentItem: Episode? = null
|
|
||||||
// for (item in episodes) {
|
|
||||||
// val date = item.getPubDate()
|
|
||||||
// if (date != null && date.after(mostRecentDate)) {
|
|
||||||
// mostRecentDate = date
|
|
||||||
// mostRecentItem = item
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// return mostRecentItem
|
|
||||||
return realm.query(Episode::class).query("feedId == $id SORT(pubDate DESC)").first().find()
|
|
||||||
}
|
|
||||||
|
|
||||||
@Ignore
|
@Ignore
|
||||||
var title: String?
|
var title: String?
|
||||||
|
@ -297,6 +285,14 @@ class Feed : RealmObject {
|
||||||
paymentLinks.add(funding)
|
paymentLinks.add(funding)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun getVirtualQueueItems(): List<Episode> {
|
||||||
|
var qString = "feedId == $id AND playState != ${Episode.PlayState.PLAYED.code}"
|
||||||
|
if (preferences?.prefStreamOverDownload != true) qString += " AND media.downloaded == true"
|
||||||
|
val eList_ = realm.query(Episode::class, qString).find().toMutableList()
|
||||||
|
if (sortOrder != null) getPermutor(sortOrder!!).reorder(eList_)
|
||||||
|
return eList_
|
||||||
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
val TAG: String = Feed::class.simpleName ?: "Anonymous"
|
val TAG: String = Feed::class.simpleName ?: "Anonymous"
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,9 @@
|
||||||
package ac.mdiq.podcini.storage.model
|
package ac.mdiq.podcini.storage.model
|
||||||
|
|
||||||
|
import ac.mdiq.podcini.playback.base.InTheatre.curQueue
|
||||||
import ac.mdiq.podcini.storage.database.RealmDB.realm
|
import ac.mdiq.podcini.storage.database.RealmDB.realm
|
||||||
import ac.mdiq.podcini.storage.model.VolumeAdaptionSetting.Companion.fromInteger
|
import ac.mdiq.podcini.storage.model.VolumeAdaptionSetting.Companion.fromInteger
|
||||||
|
import androidx.compose.runtime.mutableStateOf
|
||||||
import io.realm.kotlin.ext.realmSetOf
|
import io.realm.kotlin.ext.realmSetOf
|
||||||
import io.realm.kotlin.types.EmbeddedRealmObject
|
import io.realm.kotlin.types.EmbeddedRealmObject
|
||||||
import io.realm.kotlin.types.RealmSet
|
import io.realm.kotlin.types.RealmSet
|
||||||
|
@ -46,6 +48,8 @@ class FeedPreferences : EmbeddedRealmObject {
|
||||||
}
|
}
|
||||||
var volumeAdaption: Int = 0
|
var volumeAdaption: Int = 0
|
||||||
|
|
||||||
|
var prefStreamOverDownload: Boolean = false
|
||||||
|
|
||||||
var filterString: String = ""
|
var filterString: String = ""
|
||||||
|
|
||||||
var sortOrderCode: Int = 0 // in EpisodeSortOrder
|
var sortOrderCode: Int = 0 // in EpisodeSortOrder
|
||||||
|
@ -62,11 +66,31 @@ class FeedPreferences : EmbeddedRealmObject {
|
||||||
|
|
||||||
@Ignore
|
@Ignore
|
||||||
var queue: PlayQueue? = null
|
var queue: PlayQueue? = null
|
||||||
get() = if(queueId >= 0) realm.query(PlayQueue::class).query("id == $queueId").first().find() else null
|
get() = when {
|
||||||
|
queueId >= 0 -> realm.query(PlayQueue::class).query("id == $queueId").first().find()
|
||||||
|
queueId == -1L -> curQueue
|
||||||
|
queueId == -2L -> null
|
||||||
|
else -> null
|
||||||
|
}
|
||||||
set(value) {
|
set(value) {
|
||||||
field = value
|
field = value
|
||||||
queueId = value?.id ?: -1L
|
queueId = value?.id ?: -1L
|
||||||
}
|
}
|
||||||
|
@Ignore
|
||||||
|
var queueText: String = "Default"
|
||||||
|
get() = when (queueId) {
|
||||||
|
0L -> "Default"
|
||||||
|
-1L -> "Active"
|
||||||
|
-2L -> "None"
|
||||||
|
else -> "Custom"
|
||||||
|
}
|
||||||
|
@Ignore
|
||||||
|
val queueTextExt: String
|
||||||
|
get() = when (queueId) {
|
||||||
|
-1L -> "Active"
|
||||||
|
-2L -> "None"
|
||||||
|
else -> queue?.name ?: "Default"
|
||||||
|
}
|
||||||
var queueId: Long = 0L
|
var queueId: Long = 0L
|
||||||
|
|
||||||
@Ignore
|
@Ignore
|
||||||
|
|
|
@ -11,9 +11,7 @@ import ac.mdiq.podcini.storage.database.Queues.addToQueueSync
|
||||||
import ac.mdiq.podcini.storage.database.Queues.removeFromAllQueuesQuiet
|
import ac.mdiq.podcini.storage.database.Queues.removeFromAllQueuesQuiet
|
||||||
import ac.mdiq.podcini.storage.database.Queues.removeFromQueue
|
import ac.mdiq.podcini.storage.database.Queues.removeFromQueue
|
||||||
import ac.mdiq.podcini.storage.database.RealmDB.realm
|
import ac.mdiq.podcini.storage.database.RealmDB.realm
|
||||||
import ac.mdiq.podcini.storage.database.RealmDB.unmanaged
|
|
||||||
import ac.mdiq.podcini.storage.model.Episode
|
import ac.mdiq.podcini.storage.model.Episode
|
||||||
import ac.mdiq.podcini.storage.model.Episode.Companion.UNSPECIFIED
|
|
||||||
import ac.mdiq.podcini.storage.model.PlayQueue
|
import ac.mdiq.podcini.storage.model.PlayQueue
|
||||||
import ac.mdiq.podcini.ui.activity.MainActivity
|
import ac.mdiq.podcini.ui.activity.MainActivity
|
||||||
import ac.mdiq.podcini.ui.utils.LocalDeleteModal
|
import ac.mdiq.podcini.ui.utils.LocalDeleteModal
|
||||||
|
@ -47,7 +45,7 @@ class EpisodeMultiSelectHandler(private val activity: MainActivity, private val
|
||||||
R.id.put_in_queue_batch -> PutToQueueDialog(activity, items).show()
|
R.id.put_in_queue_batch -> PutToQueueDialog(activity, items).show()
|
||||||
R.id.remove_from_queue_batch -> removeFromQueueChecked(items)
|
R.id.remove_from_queue_batch -> removeFromQueueChecked(items)
|
||||||
R.id.toggle_played_batch -> {
|
R.id.toggle_played_batch -> {
|
||||||
setPlayState(UNSPECIFIED, false, *items.toTypedArray())
|
setPlayState(Episode.PlayState.UNSPECIFIED.code, false, *items.toTypedArray())
|
||||||
// showMessage(R.plurals.marked_read_batch_label, items.size)
|
// showMessage(R.plurals.marked_read_batch_label, items.size)
|
||||||
}
|
}
|
||||||
// R.id.mark_read_batch -> {
|
// R.id.mark_read_batch -> {
|
||||||
|
|
|
@ -46,7 +46,8 @@ abstract class EpisodeActionButton internal constructor(@JvmField var item: Epis
|
||||||
episode.feed != null && episode.feed!!.isLocalFeed -> PlayLocalActionButton(episode)
|
episode.feed != null && episode.feed!!.isLocalFeed -> PlayLocalActionButton(episode)
|
||||||
media.downloaded -> PlayActionButton(episode)
|
media.downloaded -> PlayActionButton(episode)
|
||||||
isDownloadingMedia -> CancelDownloadActionButton(episode)
|
isDownloadingMedia -> CancelDownloadActionButton(episode)
|
||||||
isStreamOverDownload || episode.feed == null || episode.feedId == null -> StreamActionButton(episode)
|
isStreamOverDownload || episode.feed == null || episode.feedId == null || episode.feed?.preferences?.prefStreamOverDownload == true ->
|
||||||
|
StreamActionButton(episode)
|
||||||
else -> DownloadActionButton(episode)
|
else -> DownloadActionButton(episode)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,7 +20,7 @@ class MarkAsPlayedActionButton(item: Episode) : EpisodeActionButton(item) {
|
||||||
}
|
}
|
||||||
|
|
||||||
@UnstableApi override fun onClick(context: Context) {
|
@UnstableApi override fun onClick(context: Context) {
|
||||||
if (!item.isPlayed()) setPlayState(Episode.PLAYED, true, item)
|
if (!item.isPlayed()) setPlayState(Episode.PlayState.PLAYED.code, true, item)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,14 +1,17 @@
|
||||||
package ac.mdiq.podcini.ui.actions.actionbutton
|
package ac.mdiq.podcini.ui.actions.actionbutton
|
||||||
|
|
||||||
import ac.mdiq.podcini.R
|
import ac.mdiq.podcini.R
|
||||||
|
import ac.mdiq.podcini.net.utils.NetworkUtils.isStreamingAllowed
|
||||||
import ac.mdiq.podcini.playback.PlaybackController.Companion.getPlayerActivityIntent
|
import ac.mdiq.podcini.playback.PlaybackController.Companion.getPlayerActivityIntent
|
||||||
import ac.mdiq.podcini.playback.PlaybackController.Companion.playbackService
|
import ac.mdiq.podcini.playback.PlaybackController.Companion.playbackService
|
||||||
import ac.mdiq.podcini.playback.PlaybackServiceStarter
|
import ac.mdiq.podcini.playback.PlaybackServiceStarter
|
||||||
import ac.mdiq.podcini.playback.base.InTheatre
|
import ac.mdiq.podcini.playback.base.InTheatre
|
||||||
|
import ac.mdiq.podcini.storage.database.RealmDB.realm
|
||||||
import ac.mdiq.podcini.storage.database.RealmDB.upsertBlk
|
import ac.mdiq.podcini.storage.database.RealmDB.upsertBlk
|
||||||
import ac.mdiq.podcini.storage.model.Episode
|
import ac.mdiq.podcini.storage.model.Episode
|
||||||
import ac.mdiq.podcini.storage.model.EpisodeMedia
|
import ac.mdiq.podcini.storage.model.EpisodeMedia
|
||||||
import ac.mdiq.podcini.storage.model.MediaType
|
import ac.mdiq.podcini.storage.model.MediaType
|
||||||
|
import ac.mdiq.podcini.ui.actions.actionbutton.StreamActionButton.StreamingConfirmationDialog
|
||||||
import ac.mdiq.podcini.util.Logd
|
import ac.mdiq.podcini.util.Logd
|
||||||
import ac.mdiq.podcini.util.event.EventFlow
|
import ac.mdiq.podcini.util.event.EventFlow
|
||||||
import ac.mdiq.podcini.util.event.FlowEvent
|
import ac.mdiq.podcini.util.event.FlowEvent
|
||||||
|
@ -57,10 +60,11 @@ class PlayActionButton(item: Episode) : EpisodeActionButton(item) {
|
||||||
fun notifyMissingEpisodeMediaFile(context: Context, media: EpisodeMedia) {
|
fun notifyMissingEpisodeMediaFile(context: Context, media: EpisodeMedia) {
|
||||||
Logd(TAG, "notifyMissingEpisodeMediaFile called")
|
Logd(TAG, "notifyMissingEpisodeMediaFile called")
|
||||||
Log.i(TAG, "The feedmanager was notified about a missing episode. It will update its database now.")
|
Log.i(TAG, "The feedmanager was notified about a missing episode. It will update its database now.")
|
||||||
val episode = media.episodeOrFetch()
|
val episode = realm.query(Episode::class).query("id == media.id").first().find()
|
||||||
|
// val episode = media.episodeOrFetch()
|
||||||
if (episode != null) {
|
if (episode != null) {
|
||||||
val episode_ = upsertBlk(episode) {
|
val episode_ = upsertBlk(episode) {
|
||||||
it.media = media
|
// it.media = media
|
||||||
it.media?.downloaded = false
|
it.media?.downloaded = false
|
||||||
it.media?.fileUrl = null
|
it.media?.fileUrl = null
|
||||||
}
|
}
|
||||||
|
|
|
@ -141,7 +141,7 @@ object EpisodeMenuHandler {
|
||||||
}
|
}
|
||||||
R.id.mark_read_item -> {
|
R.id.mark_read_item -> {
|
||||||
// selectedItem.setPlayed(true)
|
// selectedItem.setPlayed(true)
|
||||||
setPlayState(Episode.PLAYED, true, selectedItem)
|
setPlayState(Episode.PlayState.PLAYED.code, true, selectedItem)
|
||||||
if (selectedItem.feed?.isLocalFeed != true && (isProviderConnected || wifiSyncEnabledKey)) {
|
if (selectedItem.feed?.isLocalFeed != true && (isProviderConnected || wifiSyncEnabledKey)) {
|
||||||
val media: EpisodeMedia? = selectedItem.media
|
val media: EpisodeMedia? = selectedItem.media
|
||||||
// not all items have media, Gpodder only cares about those that do
|
// not all items have media, Gpodder only cares about those that do
|
||||||
|
@ -158,7 +158,7 @@ object EpisodeMenuHandler {
|
||||||
}
|
}
|
||||||
R.id.mark_unread_item -> {
|
R.id.mark_unread_item -> {
|
||||||
// selectedItem.setPlayed(false)
|
// selectedItem.setPlayed(false)
|
||||||
setPlayState(Episode.UNPLAYED, false, selectedItem)
|
setPlayState(Episode.PlayState.UNPLAYED.code, false, selectedItem)
|
||||||
if (needSynch() && selectedItem.feed?.isLocalFeed != true && selectedItem.media != null) {
|
if (needSynch() && selectedItem.feed?.isLocalFeed != true && selectedItem.media != null) {
|
||||||
val actionNew: EpisodeAction = EpisodeAction.Builder(selectedItem, EpisodeAction.NEW)
|
val actionNew: EpisodeAction = EpisodeAction.Builder(selectedItem, EpisodeAction.NEW)
|
||||||
.currentTimestamp()
|
.currentTimestamp()
|
||||||
|
@ -176,7 +176,7 @@ object EpisodeMenuHandler {
|
||||||
writeNoMediaPlaying()
|
writeNoMediaPlaying()
|
||||||
IntentUtils.sendLocalBroadcast(context, ACTION_SHUTDOWN_PLAYBACK_SERVICE)
|
IntentUtils.sendLocalBroadcast(context, ACTION_SHUTDOWN_PLAYBACK_SERVICE)
|
||||||
}
|
}
|
||||||
setPlayState(Episode.UNPLAYED, true, selectedItem)
|
setPlayState(Episode.PlayState.UNPLAYED.code, true, selectedItem)
|
||||||
}
|
}
|
||||||
R.id.visit_website_item -> {
|
R.id.visit_website_item -> {
|
||||||
val url = selectedItem.getLinkWithFallback()
|
val url = selectedItem.getLinkWithFallback()
|
||||||
|
|
|
@ -61,7 +61,7 @@ class RemoveFromQueueSwipeAction : SwipeAction {
|
||||||
fun addToQueueAt(episode: Episode, index: Int) : Job {
|
fun addToQueueAt(episode: Episode, index: Int) : Job {
|
||||||
return runOnIOScope {
|
return runOnIOScope {
|
||||||
if (curQueue.episodeIds.contains(episode.id)) return@runOnIOScope
|
if (curQueue.episodeIds.contains(episode.id)) return@runOnIOScope
|
||||||
if (episode.isNew) setPlayState(Episode.UNPLAYED, false, episode)
|
if (episode.isNew) setPlayState(Episode.PlayState.UNPLAYED.code, false, episode)
|
||||||
curQueue = upsert(curQueue) {
|
curQueue = upsert(curQueue) {
|
||||||
it.episodeIds.add(index, episode.id)
|
it.episodeIds.add(index, episode.id)
|
||||||
it.update()
|
it.update()
|
||||||
|
|
|
@ -39,7 +39,7 @@ class TogglePlaybackStateSwipeAction : SwipeAction {
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun performAction(item: Episode, fragment: Fragment, filter: EpisodeFilter) {
|
override fun performAction(item: Episode, fragment: Fragment, filter: EpisodeFilter) {
|
||||||
val newState = if (item.playState == Episode.UNPLAYED) Episode.PLAYED else Episode.UNPLAYED
|
val newState = if (item.playState == Episode.PlayState.UNPLAYED.code) Episode.PlayState.PLAYED.code else Episode.PlayState.UNPLAYED.code
|
||||||
|
|
||||||
Logd("TogglePlaybackStateSwipeAction", "performAction( ${item.id} )")
|
Logd("TogglePlaybackStateSwipeAction", "performAction( ${item.id} )")
|
||||||
// we're marking it as unplayed since the user didn't actually play it
|
// we're marking it as unplayed since the user didn't actually play it
|
||||||
|
@ -55,10 +55,10 @@ class TogglePlaybackStateSwipeAction : SwipeAction {
|
||||||
if (shouldDeleteRemoveFromQueue()) removeFromQueueSync(null, item) }
|
if (shouldDeleteRemoveFromQueue()) removeFromQueueSync(null, item) }
|
||||||
}
|
}
|
||||||
val playStateStringRes: Int = when (newState) {
|
val playStateStringRes: Int = when (newState) {
|
||||||
Episode.UNPLAYED -> if (item.playState == Episode.NEW) R.string.removed_inbox_label //was new
|
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
|
else R.string.marked_as_unplayed_label //was played
|
||||||
Episode.PLAYED -> R.string.marked_as_played_label
|
Episode.PlayState.PLAYED.code -> R.string.marked_as_played_label
|
||||||
else -> if (item.playState == Episode.NEW) R.string.removed_inbox_label
|
else -> if (item.playState == Episode.PlayState.NEW.code) R.string.removed_inbox_label
|
||||||
else R.string.marked_as_unplayed_label
|
else R.string.marked_as_unplayed_label
|
||||||
}
|
}
|
||||||
val duration: Int = Snackbar.LENGTH_LONG
|
val duration: Int = Snackbar.LENGTH_LONG
|
||||||
|
@ -87,7 +87,7 @@ class TogglePlaybackStateSwipeAction : SwipeAction {
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun willRemove(filter: EpisodeFilter, item: Episode): Boolean {
|
override fun willRemove(filter: EpisodeFilter, item: Episode): Boolean {
|
||||||
return if (item.playState == Episode.NEW) filter.showPlayed || filter.showNew
|
return if (item.playState == Episode.PlayState.NEW.code) filter.showPlayed || filter.showNew
|
||||||
else filter.showUnplayed || filter.showPlayed || filter.showNew
|
else filter.showUnplayed || filter.showPlayed || filter.showNew
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
package ac.mdiq.podcini.ui.adapter
|
package ac.mdiq.podcini.ui.adapter
|
||||||
|
|
||||||
import ac.mdiq.podcini.R
|
import ac.mdiq.podcini.R
|
||||||
import ac.mdiq.podcini.storage.database.RealmDB.unmanaged
|
|
||||||
import ac.mdiq.podcini.storage.model.Episode
|
import ac.mdiq.podcini.storage.model.Episode
|
||||||
import ac.mdiq.podcini.storage.model.Feed
|
import ac.mdiq.podcini.storage.model.Feed
|
||||||
import ac.mdiq.podcini.ui.activity.MainActivity
|
import ac.mdiq.podcini.ui.activity.MainActivity
|
||||||
|
@ -92,6 +91,7 @@ open class EpisodesAdapter(mainActivity: MainActivity, var refreshFragPosCallbac
|
||||||
|
|
||||||
@UnstableApi
|
@UnstableApi
|
||||||
override fun onBindViewHolder(holder: EpisodeViewHolder, pos: Int) {
|
override fun onBindViewHolder(holder: EpisodeViewHolder, pos: Int) {
|
||||||
|
// Logd(TAG, "onBindViewHolder $pos ${holder.episode?.title}")
|
||||||
if (pos >= episodes.size || pos < 0) {
|
if (pos >= episodes.size || pos < 0) {
|
||||||
beforeBindViewHolder(holder, pos)
|
beforeBindViewHolder(holder, pos)
|
||||||
holder.bindDummy()
|
holder.bindDummy()
|
||||||
|
@ -150,8 +150,10 @@ open class EpisodesAdapter(mainActivity: MainActivity, var refreshFragPosCallbac
|
||||||
|
|
||||||
@UnstableApi
|
@UnstableApi
|
||||||
override fun onBindViewHolder(holder: EpisodeViewHolder, pos: Int, payloads: MutableList<Any>) {
|
override fun onBindViewHolder(holder: EpisodeViewHolder, pos: Int, payloads: MutableList<Any>) {
|
||||||
|
// Logd(TAG, "onBindViewHolder payloads $pos ${holder.episode?.title}")
|
||||||
if (payloads.isEmpty()) onBindViewHolder(holder, pos)
|
if (payloads.isEmpty()) onBindViewHolder(holder, pos)
|
||||||
else {
|
else {
|
||||||
|
holder.refreshAdapterPosCallback = ::refreshPosCallback
|
||||||
val payload = payloads[0]
|
val payload = payloads[0]
|
||||||
when {
|
when {
|
||||||
payload is String && payload == "foo" -> onBindViewHolder(holder, pos)
|
payload is String && payload == "foo" -> onBindViewHolder(holder, pos)
|
||||||
|
|
|
@ -213,13 +213,14 @@ import java.util.*
|
||||||
if (nameEpisodeMap.isNotEmpty()) {
|
if (nameEpisodeMap.isNotEmpty()) {
|
||||||
for (e in nameEpisodeMap.values) {
|
for (e in nameEpisodeMap.values) {
|
||||||
upsertBlk(e) {
|
upsertBlk(e) {
|
||||||
e.media?.setfileUrlOrNull(null)
|
it.media?.setfileUrlOrNull(null)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
loadItems()
|
||||||
Logd(TAG, "Episodes reconsiled: ${nameEpisodeMap.size}\nFiles removed: ${filesRemoved.size}")
|
Logd(TAG, "Episodes reconsiled: ${nameEpisodeMap.size}\nFiles removed: ${filesRemoved.size}")
|
||||||
withContext(Dispatchers.Main) {
|
withContext(Dispatchers.Main) {
|
||||||
Toast.makeText(requireContext().applicationContext, "Episodes reconsiled: ${nameEpisodeMap.size}\nFiles removed: ${filesRemoved.size}", Toast.LENGTH_LONG).show()
|
Toast.makeText(requireContext(), "Episodes reconsiled: ${nameEpisodeMap.size}\nFiles removed: ${filesRemoved.size}", Toast.LENGTH_LONG).show()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -573,7 +573,7 @@ import java.util.concurrent.Semaphore
|
||||||
val episodes_ = feed_.episodes.filter { feed_.episodeFilter.matches(it) }
|
val episodes_ = feed_.episodes.filter { feed_.episodeFilter.matches(it) }
|
||||||
episodes.addAll(episodes_)
|
episodes.addAll(episodes_)
|
||||||
} else episodes.addAll(feed_.episodes)
|
} else episodes.addAll(feed_.episodes)
|
||||||
val sortOrder = fromCode(feed_.preferences?.sortOrderCode ?: 0)
|
val sortOrder = feed_.sortOrder
|
||||||
if (sortOrder != null) getPermutor(sortOrder).reorder(episodes)
|
if (sortOrder != null) getPermutor(sortOrder).reorder(episodes)
|
||||||
if (onInit) {
|
if (onInit) {
|
||||||
var hasNonMediaItems = false
|
var hasNonMediaItems = false
|
||||||
|
|
|
@ -18,6 +18,8 @@ import ac.mdiq.podcini.ui.utils.TransitionEffect
|
||||||
import ac.mdiq.podcini.util.IntentUtils
|
import ac.mdiq.podcini.util.IntentUtils
|
||||||
import ac.mdiq.podcini.util.Logd
|
import ac.mdiq.podcini.util.Logd
|
||||||
import ac.mdiq.podcini.util.ShareUtils
|
import ac.mdiq.podcini.util.ShareUtils
|
||||||
|
import ac.mdiq.podcini.util.event.EventFlow
|
||||||
|
import ac.mdiq.podcini.util.event.FlowEvent
|
||||||
import android.R.string
|
import android.R.string
|
||||||
import android.app.Activity
|
import android.app.Activity
|
||||||
import android.content.*
|
import android.content.*
|
||||||
|
@ -46,10 +48,8 @@ import com.google.android.material.appbar.CollapsingToolbarLayout
|
||||||
import com.google.android.material.appbar.MaterialToolbar
|
import com.google.android.material.appbar.MaterialToolbar
|
||||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||||
import com.google.android.material.snackbar.Snackbar
|
import com.google.android.material.snackbar.Snackbar
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.*
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.flow.collectLatest
|
||||||
import kotlinx.coroutines.runBlocking
|
|
||||||
import kotlinx.coroutines.withContext
|
|
||||||
import org.apache.commons.lang3.StringUtils
|
import org.apache.commons.lang3.StringUtils
|
||||||
import java.lang.ref.WeakReference
|
import java.lang.ref.WeakReference
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
@ -64,8 +64,6 @@ class FeedInfoFragment : Fragment(), Toolbar.OnMenuItemClickListener {
|
||||||
private val binding get() = _binding!!
|
private val binding get() = _binding!!
|
||||||
|
|
||||||
private lateinit var feed: Feed
|
private lateinit var feed: Feed
|
||||||
// private lateinit var imgvCover: ImageView
|
|
||||||
// private lateinit var imgvBackground: ImageView
|
|
||||||
private lateinit var toolbar: MaterialToolbar
|
private lateinit var toolbar: MaterialToolbar
|
||||||
|
|
||||||
private val addLocalFolderLauncher = registerForActivityResult<Uri?, Uri>(AddLocalFolder()) {
|
private val addLocalFolderLauncher = registerForActivityResult<Uri?, Uri>(AddLocalFolder()) {
|
||||||
|
@ -131,6 +129,18 @@ class FeedInfoFragment : Fragment(), Toolbar.OnMenuItemClickListener {
|
||||||
return binding.root
|
return binding.root
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun onStart() {
|
||||||
|
Logd(TAG, "onStart() called")
|
||||||
|
super.onStart()
|
||||||
|
procFlowEvents()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onStop() {
|
||||||
|
Logd(TAG, "onStop() called")
|
||||||
|
super.onStop()
|
||||||
|
cancelFlowEvents()
|
||||||
|
}
|
||||||
|
|
||||||
override fun onConfigurationChanged(newConfig: Configuration) {
|
override fun onConfigurationChanged(newConfig: Configuration) {
|
||||||
super.onConfigurationChanged(newConfig)
|
super.onConfigurationChanged(newConfig)
|
||||||
val horizontalSpacing = resources.getDimension(R.dimen.additional_horizontal_spacing).toInt()
|
val horizontalSpacing = resources.getDimension(R.dimen.additional_horizontal_spacing).toInt()
|
||||||
|
@ -269,6 +279,23 @@ class FeedInfoFragment : Fragment(), Toolbar.OnMenuItemClickListener {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private var eventSink: Job? = null
|
||||||
|
private fun cancelFlowEvents() {
|
||||||
|
eventSink?.cancel()
|
||||||
|
eventSink = null
|
||||||
|
}
|
||||||
|
private fun procFlowEvents() {
|
||||||
|
if (eventSink == null) eventSink = lifecycleScope.launch {
|
||||||
|
EventFlow.events.collectLatest { event ->
|
||||||
|
Logd(TAG, "Received event: ${event.TAG}")
|
||||||
|
when (event) {
|
||||||
|
is FlowEvent.FeedPrefsChangeEvent -> feed = event.feed
|
||||||
|
else -> {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private class AddLocalFolder : ActivityResultContracts.OpenDocumentTree() {
|
private class AddLocalFolder : ActivityResultContracts.OpenDocumentTree() {
|
||||||
override fun createIntent(context: Context, input: Uri?): Intent {
|
override fun createIntent(context: Context, input: Uri?): Intent {
|
||||||
return super.createIntent(context, input).addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
|
return super.createIntent(context, input).addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
|
||||||
|
|
|
@ -9,18 +9,16 @@ import ac.mdiq.podcini.net.feed.FeedUpdateManager.runOnce
|
||||||
import ac.mdiq.podcini.preferences.UserPreferences.isEnableAutodownload
|
import ac.mdiq.podcini.preferences.UserPreferences.isEnableAutodownload
|
||||||
import ac.mdiq.podcini.storage.database.Feeds.persistFeedPreferences
|
import ac.mdiq.podcini.storage.database.Feeds.persistFeedPreferences
|
||||||
import ac.mdiq.podcini.storage.database.RealmDB.realm
|
import ac.mdiq.podcini.storage.database.RealmDB.realm
|
||||||
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.database.RealmDB.upsertBlk
|
||||||
import ac.mdiq.podcini.storage.model.*
|
import ac.mdiq.podcini.storage.model.*
|
||||||
import ac.mdiq.podcini.storage.model.FeedPreferences.AutoDeleteAction
|
import ac.mdiq.podcini.storage.model.FeedPreferences.AutoDeleteAction
|
||||||
import ac.mdiq.podcini.storage.model.FeedPreferences.Companion.FeedAutoDeleteOptions
|
import ac.mdiq.podcini.storage.model.FeedPreferences.Companion.FeedAutoDeleteOptions
|
||||||
import ac.mdiq.podcini.ui.adapter.SimpleChipAdapter
|
import ac.mdiq.podcini.ui.adapter.SimpleChipAdapter
|
||||||
|
import ac.mdiq.podcini.ui.compose.CustomTheme
|
||||||
|
import ac.mdiq.podcini.ui.compose.Spinner
|
||||||
import ac.mdiq.podcini.ui.dialog.AuthenticationDialog
|
import ac.mdiq.podcini.ui.dialog.AuthenticationDialog
|
||||||
import ac.mdiq.podcini.ui.dialog.TagSettingsDialog
|
import ac.mdiq.podcini.ui.dialog.TagSettingsDialog
|
||||||
import ac.mdiq.podcini.ui.utils.ItemOffsetDecoration
|
import ac.mdiq.podcini.ui.utils.ItemOffsetDecoration
|
||||||
import ac.mdiq.podcini.ui.compose.CustomTheme
|
|
||||||
import ac.mdiq.podcini.ui.compose.Spinner
|
|
||||||
import ac.mdiq.podcini.util.Logd
|
import ac.mdiq.podcini.util.Logd
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.DialogInterface
|
import android.content.DialogInterface
|
||||||
|
@ -40,11 +38,6 @@ import androidx.compose.foundation.layout.*
|
||||||
import androidx.compose.foundation.selection.selectable
|
import androidx.compose.foundation.selection.selectable
|
||||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||||
import androidx.compose.material.*
|
import androidx.compose.material.*
|
||||||
import androidx.compose.material.icons.Icons
|
|
||||||
import androidx.compose.material.icons.filled.AddCircle
|
|
||||||
import androidx.compose.material.icons.filled.CheckCircle
|
|
||||||
import androidx.compose.material.icons.outlined.AccountCircle
|
|
||||||
import androidx.compose.material.icons.outlined.CheckCircle
|
|
||||||
import androidx.compose.runtime.*
|
import androidx.compose.runtime.*
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
|
@ -70,6 +63,7 @@ class FeedSettingsFragment : Fragment() {
|
||||||
private val binding get() = _binding!!
|
private val binding get() = _binding!!
|
||||||
private var feed: Feed? = null
|
private var feed: Feed? = null
|
||||||
private var autoDeleteSummaryResId by mutableIntStateOf(R.string.global_default)
|
private var autoDeleteSummaryResId by mutableIntStateOf(R.string.global_default)
|
||||||
|
private var curPrefQueue by mutableStateOf(feed?.preferences?.queueTextExt ?: "Default")
|
||||||
private var autoDeletePolicy = "global"
|
private var autoDeletePolicy = "global"
|
||||||
private var queues: List<PlayQueue>? = null
|
private var queues: List<PlayQueue>? = null
|
||||||
|
|
||||||
|
@ -102,6 +96,7 @@ class FeedSettingsFragment : Fragment() {
|
||||||
var checked by remember { mutableStateOf(feed?.preferences?.keepUpdated ?: true) }
|
var checked by remember { mutableStateOf(feed?.preferences?.keepUpdated ?: true) }
|
||||||
Switch(
|
Switch(
|
||||||
checked = checked,
|
checked = checked,
|
||||||
|
modifier = Modifier.height(24.dp),
|
||||||
onCheckedChange = {
|
onCheckedChange = {
|
||||||
checked = it
|
checked = it
|
||||||
feed = upsertBlk(feed!!) {
|
feed = upsertBlk(feed!!) {
|
||||||
|
@ -117,6 +112,35 @@ class FeedSettingsFragment : Fragment() {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
Column {
|
Column {
|
||||||
|
Row(Modifier.fillMaxWidth()) {
|
||||||
|
Icon(ImageVector.vectorResource(id = R.drawable.ic_stream), "", tint = textColor)
|
||||||
|
Spacer(modifier = Modifier.width(20.dp))
|
||||||
|
Text(
|
||||||
|
text = stringResource(R.string.pref_stream_over_download_title),
|
||||||
|
style = MaterialTheme.typography.h6,
|
||||||
|
color = textColor
|
||||||
|
)
|
||||||
|
Spacer(modifier = Modifier.weight(1f))
|
||||||
|
var checked by remember { mutableStateOf(feed?.preferences?.prefStreamOverDownload ?: false) }
|
||||||
|
Switch(
|
||||||
|
checked = checked,
|
||||||
|
modifier = Modifier.height(24.dp),
|
||||||
|
onCheckedChange = {
|
||||||
|
checked = it
|
||||||
|
feed = upsertBlk(feed!!) {
|
||||||
|
it.preferences?.prefStreamOverDownload = checked
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
Text(
|
||||||
|
text = stringResource(R.string.pref_stream_over_download_sum),
|
||||||
|
style = MaterialTheme.typography.body2,
|
||||||
|
color = textColor
|
||||||
|
)
|
||||||
|
}
|
||||||
|
Column {
|
||||||
|
curPrefQueue = feed?.preferences?.queueTextExt ?: "Default"
|
||||||
Row(Modifier.fillMaxWidth()) {
|
Row(Modifier.fillMaxWidth()) {
|
||||||
Icon(ImageVector.vectorResource(id = R.drawable.ic_playlist_play), "", tint = textColor)
|
Icon(ImageVector.vectorResource(id = R.drawable.ic_playlist_play), "", tint = textColor)
|
||||||
Spacer(modifier = Modifier.width(20.dp))
|
Spacer(modifier = Modifier.width(20.dp))
|
||||||
|
@ -125,27 +149,21 @@ class FeedSettingsFragment : Fragment() {
|
||||||
style = MaterialTheme.typography.h6,
|
style = MaterialTheme.typography.h6,
|
||||||
color = textColor,
|
color = textColor,
|
||||||
modifier = Modifier.clickable(onClick = {
|
modifier = Modifier.clickable(onClick = {
|
||||||
// queues = realm.query(PlayQueue::class).find()
|
val selectedOption = feed?.preferences?.queueText ?: "Default"
|
||||||
val selectedOption = when (feed?.preferences?.queueId) {
|
val composeView = ComposeView(requireContext()).apply {
|
||||||
null, 0L -> "Default"
|
setContent {
|
||||||
-1L -> "Active"
|
val showDialog = remember { mutableStateOf(true) }
|
||||||
else -> "Custom"
|
CustomTheme(requireContext()) {
|
||||||
}
|
SetAssociatedQueue(showDialog.value, selectedOption = selectedOption, onDismissRequest = { showDialog.value = false })
|
||||||
val composeView = ComposeView(requireContext()).apply {
|
|
||||||
setContent {
|
|
||||||
val showDialog = remember { mutableStateOf(true) }
|
|
||||||
CustomTheme(requireContext()) {
|
|
||||||
SetAssociatedQueue(showDialog.value, selectedOption = selectedOption, onDismissRequest = { showDialog.value = false })
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
(view as? ViewGroup)?.addView(composeView)
|
}
|
||||||
|
(view as? ViewGroup)?.addView(composeView)
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
Text(
|
Text(
|
||||||
text = stringResource(R.string.pref_feed_associated_queue_sum),
|
text = curPrefQueue + " : " + stringResource(R.string.pref_feed_associated_queue_sum),
|
||||||
style = MaterialTheme.typography.body2,
|
style = MaterialTheme.typography.body2,
|
||||||
color = textColor
|
color = textColor
|
||||||
)
|
)
|
||||||
|
@ -159,16 +177,15 @@ class FeedSettingsFragment : Fragment() {
|
||||||
style = MaterialTheme.typography.h6,
|
style = MaterialTheme.typography.h6,
|
||||||
color = textColor,
|
color = textColor,
|
||||||
modifier = Modifier.clickable(onClick = {
|
modifier = Modifier.clickable(onClick = {
|
||||||
val composeView = ComposeView(requireContext()).apply {
|
val composeView = ComposeView(requireContext()).apply {
|
||||||
setContent {
|
setContent {
|
||||||
val showDialog = remember { mutableStateOf(true) }
|
val showDialog = remember { mutableStateOf(true) }
|
||||||
CustomTheme(requireContext()) {
|
CustomTheme(requireContext()) {
|
||||||
AutoDeleteDialog(showDialog.value, onDismissRequest = { showDialog.value = false })
|
AutoDeleteDialog(showDialog.value, onDismissRequest = { showDialog.value = false })
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
(view as? ViewGroup)?.addView(composeView)
|
}
|
||||||
|
(view as? ViewGroup)?.addView(composeView)
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -231,35 +248,29 @@ class FeedSettingsFragment : Fragment() {
|
||||||
) {
|
) {
|
||||||
Column {
|
Column {
|
||||||
FeedAutoDeleteOptions.forEach { text ->
|
FeedAutoDeleteOptions.forEach { text ->
|
||||||
Row(
|
Row(Modifier
|
||||||
Modifier
|
.fillMaxWidth()
|
||||||
.fillMaxWidth()
|
.padding(horizontal = 16.dp),
|
||||||
.selectable(
|
|
||||||
selected = (text == selectedOption),
|
|
||||||
onClick = {
|
|
||||||
Logd(TAG, "row clicked: $text $selectedOption")
|
|
||||||
if (text != selectedOption) {
|
|
||||||
onOptionSelected(text)
|
|
||||||
val action_ = when (text) {
|
|
||||||
"global" -> AutoDeleteAction.GLOBAL
|
|
||||||
"always" -> AutoDeleteAction.ALWAYS
|
|
||||||
"never" -> AutoDeleteAction.NEVER
|
|
||||||
else -> AutoDeleteAction.GLOBAL
|
|
||||||
}
|
|
||||||
feed = upsertBlk(feed!!) {
|
|
||||||
it.preferences?.autoDeleteAction = action_
|
|
||||||
}
|
|
||||||
getAutoDeletePolicy()
|
|
||||||
onDismissRequest()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
.padding(horizontal = 16.dp),
|
|
||||||
verticalAlignment = Alignment.CenterVertically
|
verticalAlignment = Alignment.CenterVertically
|
||||||
) {
|
) {
|
||||||
RadioButton(
|
Checkbox(checked = (text == selectedOption),
|
||||||
selected = (text == selectedOption),
|
onCheckedChange = { isChecked ->
|
||||||
onClick = { }
|
Logd(TAG, "row clicked: $text $selectedOption")
|
||||||
|
if (text != selectedOption) {
|
||||||
|
onOptionSelected(text)
|
||||||
|
val action_ = when (text) {
|
||||||
|
"global" -> AutoDeleteAction.GLOBAL
|
||||||
|
"always" -> AutoDeleteAction.ALWAYS
|
||||||
|
"never" -> AutoDeleteAction.NEVER
|
||||||
|
else -> AutoDeleteAction.GLOBAL
|
||||||
|
}
|
||||||
|
feed = upsertBlk(feed!!) {
|
||||||
|
it.preferences?.autoDeleteAction = action_
|
||||||
|
}
|
||||||
|
getAutoDeletePolicy()
|
||||||
|
onDismissRequest()
|
||||||
|
}
|
||||||
|
}
|
||||||
)
|
)
|
||||||
Text(
|
Text(
|
||||||
text = text,
|
text = text,
|
||||||
|
@ -281,37 +292,36 @@ class FeedSettingsFragment : Fragment() {
|
||||||
var selected by remember {mutableStateOf(selectedOption)}
|
var selected by remember {mutableStateOf(selectedOption)}
|
||||||
if (showDialog) {
|
if (showDialog) {
|
||||||
Dialog(onDismissRequest = { onDismissRequest() }) {
|
Dialog(onDismissRequest = { onDismissRequest() }) {
|
||||||
Card(
|
Card(modifier = Modifier
|
||||||
modifier = Modifier
|
.wrapContentSize(align = Alignment.Center)
|
||||||
.wrapContentSize(align = Alignment.Center)
|
.padding(16.dp),
|
||||||
.padding(16.dp),
|
|
||||||
shape = RoundedCornerShape(16.dp),
|
shape = RoundedCornerShape(16.dp),
|
||||||
) {
|
) {
|
||||||
Column(
|
Column(modifier = Modifier.padding(16.dp),
|
||||||
modifier = Modifier.padding(16.dp),
|
|
||||||
verticalArrangement = Arrangement.spacedBy(8.dp)
|
verticalArrangement = Arrangement.spacedBy(8.dp)
|
||||||
) {
|
) {
|
||||||
queueSettingOptions.forEach { option ->
|
queueSettingOptions.forEach { option ->
|
||||||
Row(
|
Row(modifier = Modifier.fillMaxWidth(),
|
||||||
modifier = Modifier.fillMaxWidth(),
|
|
||||||
verticalAlignment = Alignment.CenterVertically
|
verticalAlignment = Alignment.CenterVertically
|
||||||
) {
|
) {
|
||||||
Checkbox(
|
Checkbox(checked = option == selected,
|
||||||
checked = option == selected,
|
|
||||||
onCheckedChange = { isChecked ->
|
onCheckedChange = { isChecked ->
|
||||||
selected = option
|
selected = option
|
||||||
if (isChecked) Logd(TAG, "$option is checked")
|
if (isChecked) Logd(TAG, "$option is checked")
|
||||||
when (selected) {
|
when (selected) {
|
||||||
"Default" -> {
|
"Default" -> {
|
||||||
feed = upsertBlk(feed!!) {
|
feed = upsertBlk(feed!!) { it.preferences?.queueId = 0L }
|
||||||
it.preferences?.queueId = 0L
|
curPrefQueue = selected
|
||||||
}
|
|
||||||
onDismissRequest()
|
onDismissRequest()
|
||||||
}
|
}
|
||||||
"Active" -> {
|
"Active" -> {
|
||||||
feed = upsertBlk(feed!!) {
|
feed = upsertBlk(feed!!) { it.preferences?.queueId = -1L }
|
||||||
it.preferences?.queueId = 1L
|
curPrefQueue = selected
|
||||||
}
|
onDismissRequest()
|
||||||
|
}
|
||||||
|
"None" -> {
|
||||||
|
feed = upsertBlk(feed!!) { it.preferences?.queueId = -2L }
|
||||||
|
curPrefQueue = selected
|
||||||
onDismissRequest()
|
onDismissRequest()
|
||||||
}
|
}
|
||||||
"Custom" -> {}
|
"Custom" -> {}
|
||||||
|
@ -326,9 +336,8 @@ class FeedSettingsFragment : Fragment() {
|
||||||
Spinner(items = queues!!.map { it.name }, selectedItem = feed?.preferences?.queue?.name ?: "Default") { name ->
|
Spinner(items = queues!!.map { it.name }, selectedItem = feed?.preferences?.queue?.name ?: "Default") { name ->
|
||||||
Logd(TAG, "Queue selected: $name")
|
Logd(TAG, "Queue selected: $name")
|
||||||
val q = queues?.firstOrNull { it.name == name }
|
val q = queues?.firstOrNull { it.name == name }
|
||||||
feed = upsertBlk(feed!!) {
|
feed = upsertBlk(feed!!) { it.preferences?.queue = q }
|
||||||
it.preferences?.queue = q
|
if (q != null) curPrefQueue = q.name
|
||||||
}
|
|
||||||
onDismissRequest()
|
onDismissRequest()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -753,7 +762,7 @@ class FeedSettingsFragment : Fragment() {
|
||||||
private val TAG: String = FeedSettingsFragment::class.simpleName ?: "Anonymous"
|
private val TAG: String = FeedSettingsFragment::class.simpleName ?: "Anonymous"
|
||||||
private const val EXTRA_FEED_ID = "ac.mdiq.podcini.extra.feedId"
|
private const val EXTRA_FEED_ID = "ac.mdiq.podcini.extra.feedId"
|
||||||
|
|
||||||
val queueSettingOptions = listOf("Default", "Active", "Custom")
|
val queueSettingOptions = listOf("Default", "Active", "None", "Custom")
|
||||||
|
|
||||||
fun newInstance(feed: Feed): FeedSettingsFragment {
|
fun newInstance(feed: Feed): FeedSettingsFragment {
|
||||||
val fragment = FeedSettingsFragment()
|
val fragment = FeedSettingsFragment()
|
||||||
|
|
|
@ -85,7 +85,7 @@ import java.util.*
|
||||||
private val binding get() = _binding!!
|
private val binding get() = _binding!!
|
||||||
|
|
||||||
private lateinit var recyclerView: EpisodesRecyclerView
|
private lateinit var recyclerView: EpisodesRecyclerView
|
||||||
private lateinit var emptyView: EmptyViewHandler
|
private lateinit var emptyViewHandler: EmptyViewHandler
|
||||||
private lateinit var toolbar: MaterialToolbar
|
private lateinit var toolbar: MaterialToolbar
|
||||||
private lateinit var swipeActions: SwipeActions
|
private lateinit var swipeActions: SwipeActions
|
||||||
private lateinit var speedDialView: SpeedDialView
|
private lateinit var speedDialView: SpeedDialView
|
||||||
|
@ -179,12 +179,12 @@ import java.util.*
|
||||||
adapter?.setOnSelectModeListener(this)
|
adapter?.setOnSelectModeListener(this)
|
||||||
recyclerView.adapter = adapter
|
recyclerView.adapter = adapter
|
||||||
|
|
||||||
emptyView = EmptyViewHandler(requireContext())
|
emptyViewHandler = EmptyViewHandler(requireContext())
|
||||||
emptyView.attachToRecyclerView(recyclerView)
|
emptyViewHandler.attachToRecyclerView(recyclerView)
|
||||||
emptyView.setIcon(R.drawable.ic_playlist_play)
|
emptyViewHandler.setIcon(R.drawable.ic_playlist_play)
|
||||||
emptyView.setTitle(R.string.no_items_header_label)
|
emptyViewHandler.setTitle(R.string.no_items_header_label)
|
||||||
emptyView.setMessage(R.string.no_items_label)
|
emptyViewHandler.setMessage(R.string.no_items_label)
|
||||||
emptyView.updateAdapter(adapter)
|
emptyViewHandler.updateAdapter(adapter)
|
||||||
|
|
||||||
val multiSelectDial = MultiSelectSpeedDialBinding.bind(binding.root)
|
val multiSelectDial = MultiSelectSpeedDialBinding.bind(binding.root)
|
||||||
speedDialView = multiSelectDial.fabSD
|
speedDialView = multiSelectDial.fabSD
|
||||||
|
@ -290,8 +290,12 @@ import java.util.*
|
||||||
}
|
}
|
||||||
when (event.action) {
|
when (event.action) {
|
||||||
FlowEvent.QueueEvent.Action.ADDED -> {
|
FlowEvent.QueueEvent.Action.ADDED -> {
|
||||||
if (event.episodes.isNotEmpty() && !curQueue.isInQueue(event.episodes[0])) queueItems.add(event.position, event.episodes[0])
|
if (event.episodes.isNotEmpty() && !curQueue.isInQueue(event.episodes[0])) {
|
||||||
adapter?.notifyItemInserted(event.position)
|
val pos = queueItems.size
|
||||||
|
queueItems.addAll(event.episodes)
|
||||||
|
adapter?.notifyItemRangeInserted(pos, queueItems.size)
|
||||||
|
adapter?.notifyItemRangeChanged(pos, event.episodes.size);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
FlowEvent.QueueEvent.Action.SET_QUEUE, FlowEvent.QueueEvent.Action.SORTED -> {
|
FlowEvent.QueueEvent.Action.SET_QUEUE, FlowEvent.QueueEvent.Action.SORTED -> {
|
||||||
queueItems.clear()
|
queueItems.clear()
|
||||||
|
@ -303,9 +307,12 @@ import java.util.*
|
||||||
for (e in event.episodes) {
|
for (e in event.episodes) {
|
||||||
val pos: Int = EpisodeUtil.indexOfItemWithId(queueItems, e.id)
|
val pos: Int = EpisodeUtil.indexOfItemWithId(queueItems, e.id)
|
||||||
if (pos >= 0) {
|
if (pos >= 0) {
|
||||||
Logd(TAG, "removing episode $pos ${queueItems[pos]} $e")
|
Logd(TAG, "removing episode $pos ${queueItems[pos].title} $e")
|
||||||
|
val holder = recyclerView.findViewHolderForLayoutPosition(pos) as? EpisodeViewHolder
|
||||||
|
holder?.unbind()
|
||||||
queueItems.removeAt(pos)
|
queueItems.removeAt(pos)
|
||||||
adapter?.notifyItemRemoved(pos)
|
adapter?.notifyItemRemoved(pos)
|
||||||
|
adapter?.notifyItemRangeChanged(pos, adapter!!.getItemCount()-pos);
|
||||||
} else {
|
} else {
|
||||||
Log.e(TAG, "Trying to remove item non-existent from queue ${e.id} ${e.title}")
|
Log.e(TAG, "Trying to remove item non-existent from queue ${e.id} ${e.title}")
|
||||||
continue
|
continue
|
||||||
|
@ -637,18 +644,16 @@ import java.util.*
|
||||||
private fun loadCurQueue(restoreScrollPosition: Boolean) {
|
private fun loadCurQueue(restoreScrollPosition: Boolean) {
|
||||||
if (!loadItemsRunning) {
|
if (!loadItemsRunning) {
|
||||||
loadItemsRunning = true
|
loadItemsRunning = true
|
||||||
adapter?.updateItems(mutableListOf())
|
// adapter?.updateItems(mutableListOf())
|
||||||
Logd(TAG, "loadCurQueue() called ${curQueue.name}")
|
Logd(TAG, "loadCurQueue() called ${curQueue.name}")
|
||||||
while (curQueue.name.isEmpty()) runBlocking { delay(100) }
|
while (curQueue.name.isEmpty()) runBlocking { delay(100) }
|
||||||
if (queueItems.isEmpty()) emptyView.hide()
|
if (queueItems.isNotEmpty()) emptyViewHandler.hide()
|
||||||
queueItems.clear()
|
queueItems.clear()
|
||||||
if (showBin) {
|
if (showBin) {
|
||||||
queueItems.addAll(realm.query(Episode::class, "id IN $0", curQueue.idsBinList)
|
queueItems.addAll(realm.query(Episode::class, "id IN $0", curQueue.idsBinList)
|
||||||
.find().sortedByDescending { curQueue.idsBinList.indexOf(it.id) })
|
.find().sortedByDescending { curQueue.idsBinList.indexOf(it.id) })
|
||||||
} else {
|
} else {
|
||||||
curQueue.episodes.clear()
|
curQueue.episodes.clear()
|
||||||
// curQueue.episodes.addAll(realm.query(Episode::class, "id IN $0", curQueue.episodeIds)
|
|
||||||
// .find().sortedBy { curQueue.episodeIds.indexOf(it.id) })
|
|
||||||
queueItems.addAll(curQueue.episodes)
|
queueItems.addAll(curQueue.episodes)
|
||||||
}
|
}
|
||||||
Logd(TAG, "loadCurQueue() curQueue.episodes: ${curQueue.episodes.size}")
|
Logd(TAG, "loadCurQueue() curQueue.episodes: ${curQueue.episodes.size}")
|
||||||
|
|
|
@ -388,7 +388,7 @@ class SubscriptionsFragment : Fragment(), Toolbar.OnMenuItemClickListener, Selec
|
||||||
val dir = 1 - 2*feedOrderDir // get from 0, 1 to 1, -1
|
val dir = 1 - 2*feedOrderDir // get from 0, 1 to 1, -1
|
||||||
val comparator: Comparator<Feed> = when (feedOrder) {
|
val comparator: Comparator<Feed> = when (feedOrder) {
|
||||||
FeedSortOrder.UNPLAYED_NEW_OLD.index -> {
|
FeedSortOrder.UNPLAYED_NEW_OLD.index -> {
|
||||||
val queryString = "feedId == $0 AND (playState == ${Episode.NEW} OR playState == ${Episode.UNPLAYED})"
|
val queryString = "feedId == $0 AND (playState == ${Episode.PlayState.NEW.code} OR playState == ${Episode.PlayState.UNPLAYED.code})"
|
||||||
val counterMap: MutableMap<Long, Long> = mutableMapOf()
|
val counterMap: MutableMap<Long, Long> = mutableMapOf()
|
||||||
for (f in feedList_) {
|
for (f in feedList_) {
|
||||||
val c = realm.query(Episode::class).query(queryString, f.id).count().find()
|
val c = realm.query(Episode::class).query(queryString, f.id).count().find()
|
||||||
|
@ -410,7 +410,7 @@ class SubscriptionsFragment : Fragment(), Toolbar.OnMenuItemClickListener, Selec
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
FeedSortOrder.MOST_PLAYED.index -> {
|
FeedSortOrder.MOST_PLAYED.index -> {
|
||||||
val queryString = "feedId == $0 AND playState == ${Episode.PLAYED}"
|
val queryString = "feedId == $0 AND playState == ${Episode.PlayState.PLAYED.code}"
|
||||||
val counterMap: MutableMap<Long, Long> = mutableMapOf()
|
val counterMap: MutableMap<Long, Long> = mutableMapOf()
|
||||||
for (f in feedList_) {
|
for (f in feedList_) {
|
||||||
val c = realm.query(Episode::class).query(queryString, f.id).count().find()
|
val c = realm.query(Episode::class).query(queryString, f.id).count().find()
|
||||||
|
@ -444,7 +444,7 @@ class SubscriptionsFragment : Fragment(), Toolbar.OnMenuItemClickListener, Selec
|
||||||
}
|
}
|
||||||
FeedSortOrder.LAST_UPDATED_UNPLAYED_NEW_OLD.index -> {
|
FeedSortOrder.LAST_UPDATED_UNPLAYED_NEW_OLD.index -> {
|
||||||
val queryString =
|
val queryString =
|
||||||
"feedId == $0 AND (playState == ${Episode.NEW} OR playState == ${Episode.UNPLAYED}) SORT(pubDate DESC)"
|
"feedId == $0 AND (playState == ${Episode.PlayState.NEW.code} OR playState == ${Episode.PlayState.UNPLAYED.code}) SORT(pubDate DESC)"
|
||||||
val counterMap: MutableMap<Long, Long> = mutableMapOf()
|
val counterMap: MutableMap<Long, Long> = mutableMapOf()
|
||||||
for (f in feedList_) {
|
for (f in feedList_) {
|
||||||
val d = realm.query(Episode::class).query(queryString, f.id).first().find()?.pubDate ?: 0L
|
val d = realm.query(Episode::class).query(queryString, f.id).first().find()?.pubDate ?: 0L
|
||||||
|
@ -466,7 +466,7 @@ class SubscriptionsFragment : Fragment(), Toolbar.OnMenuItemClickListener, Selec
|
||||||
}
|
}
|
||||||
FeedSortOrder.MOST_DOWNLOADED_UNPLAYED.index -> {
|
FeedSortOrder.MOST_DOWNLOADED_UNPLAYED.index -> {
|
||||||
val queryString =
|
val queryString =
|
||||||
"feedId == $0 AND (playState == ${Episode.NEW} OR playState == ${Episode.UNPLAYED}) AND media.downloaded == true"
|
"feedId == $0 AND (playState == ${Episode.PlayState.NEW.code} OR playState == ${Episode.PlayState.UNPLAYED.code}) AND media.downloaded == true"
|
||||||
val counterMap: MutableMap<Long, Long> = mutableMapOf()
|
val counterMap: MutableMap<Long, Long> = mutableMapOf()
|
||||||
for (f in feedList_) {
|
for (f in feedList_) {
|
||||||
val c = realm.query(Episode::class).query(queryString, f.id).count().find()
|
val c = realm.query(Episode::class).query(queryString, f.id).count().find()
|
||||||
|
@ -477,7 +477,7 @@ class SubscriptionsFragment : Fragment(), Toolbar.OnMenuItemClickListener, Selec
|
||||||
}
|
}
|
||||||
// doing FEED_ORDER_NEW
|
// doing FEED_ORDER_NEW
|
||||||
else -> {
|
else -> {
|
||||||
val queryString = "feedId == $0 AND playState == ${Episode.NEW}"
|
val queryString = "feedId == $0 AND playState == ${Episode.PlayState.NEW.code}"
|
||||||
val counterMap: MutableMap<Long, Long> = mutableMapOf()
|
val counterMap: MutableMap<Long, Long> = mutableMapOf()
|
||||||
for (f in feedList_) {
|
for (f in feedList_) {
|
||||||
val c = realm.query(Episode::class).query(queryString, f.id).count().find()
|
val c = realm.query(Episode::class).query(queryString, f.id).count().find()
|
||||||
|
@ -738,37 +738,33 @@ class SubscriptionsFragment : Fragment(), Toolbar.OnMenuItemClickListener, Selec
|
||||||
var selected by remember {mutableStateOf("")}
|
var selected by remember {mutableStateOf("")}
|
||||||
if (showDialog) {
|
if (showDialog) {
|
||||||
Dialog(onDismissRequest = { onDismissRequest() }) {
|
Dialog(onDismissRequest = { onDismissRequest() }) {
|
||||||
Card(
|
Card(modifier = Modifier
|
||||||
modifier = Modifier
|
.wrapContentSize(align = Alignment.Center)
|
||||||
.wrapContentSize(align = Alignment.Center)
|
.padding(16.dp),
|
||||||
.padding(16.dp),
|
|
||||||
shape = RoundedCornerShape(16.dp),
|
shape = RoundedCornerShape(16.dp),
|
||||||
) {
|
) {
|
||||||
Column(
|
Column(modifier = Modifier.padding(16.dp),
|
||||||
modifier = Modifier.padding(16.dp),
|
|
||||||
verticalArrangement = Arrangement.spacedBy(8.dp)
|
verticalArrangement = Arrangement.spacedBy(8.dp)
|
||||||
) {
|
) {
|
||||||
queueSettingOptions.forEach { option ->
|
queueSettingOptions.forEach { option ->
|
||||||
Row(
|
Row(modifier = Modifier.fillMaxWidth(),
|
||||||
modifier = Modifier.fillMaxWidth(),
|
|
||||||
verticalAlignment = Alignment.CenterVertically
|
verticalAlignment = Alignment.CenterVertically
|
||||||
) {
|
) {
|
||||||
Checkbox(
|
Checkbox(checked = option == selected,
|
||||||
checked = option == selected,
|
|
||||||
onCheckedChange = { isChecked ->
|
onCheckedChange = { isChecked ->
|
||||||
selected = option
|
selected = option
|
||||||
if (isChecked) Logd(TAG, "$option is checked")
|
if (isChecked) Logd(TAG, "$option is checked")
|
||||||
when (selected) {
|
when (selected) {
|
||||||
"Default" -> {
|
"Default" -> {
|
||||||
saveFeedPreferences { it: FeedPreferences ->
|
saveFeedPreferences { it: FeedPreferences -> it.queueId = 0L }
|
||||||
it.queueId = 0L
|
|
||||||
}
|
|
||||||
onDismissRequest()
|
onDismissRequest()
|
||||||
}
|
}
|
||||||
"Active" -> {
|
"Active" -> {
|
||||||
saveFeedPreferences { it: FeedPreferences ->
|
saveFeedPreferences { it: FeedPreferences -> it.queueId = -1L }
|
||||||
it.queueId = -1L
|
onDismissRequest()
|
||||||
}
|
}
|
||||||
|
"None" -> {
|
||||||
|
saveFeedPreferences { it: FeedPreferences -> it.queueId = -2L }
|
||||||
onDismissRequest()
|
onDismissRequest()
|
||||||
}
|
}
|
||||||
"Custom" -> {}
|
"Custom" -> {}
|
||||||
|
@ -784,9 +780,7 @@ class SubscriptionsFragment : Fragment(), Toolbar.OnMenuItemClickListener, Selec
|
||||||
Logd(TAG, "Queue selected: $name")
|
Logd(TAG, "Queue selected: $name")
|
||||||
val q = queues.firstOrNull { it.name == name }
|
val q = queues.firstOrNull { it.name == name }
|
||||||
if (q != null) {
|
if (q != null) {
|
||||||
saveFeedPreferences { it: FeedPreferences ->
|
saveFeedPreferences { it: FeedPreferences -> it.queueId = q.id }
|
||||||
it.queueId = q.id
|
|
||||||
}
|
|
||||||
onDismissRequest()
|
onDismissRequest()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -447,7 +447,7 @@ class StatisticsFragment : Fragment() {
|
||||||
else {
|
else {
|
||||||
// progress import does not include playedDuration
|
// progress import does not include playedDuration
|
||||||
if (includeMarkedAsPlayed) {
|
if (includeMarkedAsPlayed) {
|
||||||
if (m.playbackCompletionTime > 0 || m.episodeOrFetch()?.playState == Episode.PLAYED)
|
if (m.playbackCompletionTime > 0 || m.episodeOrFetch()?.playState == Episode.PlayState.PLAYED.code)
|
||||||
dur += m.duration
|
dur += m.duration
|
||||||
else if (m.position > 0) dur += m.position
|
else if (m.position > 0) dur += m.position
|
||||||
} else dur += m.position
|
} else dur += m.position
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
package ac.mdiq.podcini.ui.utils
|
package ac.mdiq.podcini.ui.utils
|
||||||
|
|
||||||
import ac.mdiq.podcini.R
|
import ac.mdiq.podcini.R
|
||||||
import ac.mdiq.podcini.storage.database.Episodes.deleteMediaOfEpisode
|
import ac.mdiq.podcini.storage.database.Episodes.deleteEpisodeMedia
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.DialogInterface
|
import android.content.DialogInterface
|
||||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||||
|
@ -13,7 +13,7 @@ object LocalDeleteModal {
|
||||||
val localItems: MutableList<Episode> = mutableListOf()
|
val localItems: MutableList<Episode> = mutableListOf()
|
||||||
for (item in items) {
|
for (item in items) {
|
||||||
if (item.feed?.isLocalFeed == true) localItems.add(item)
|
if (item.feed?.isLocalFeed == true) localItems.add(item)
|
||||||
else deleteMediaOfEpisode(context, item)
|
else deleteEpisodeMedia(context, item)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (localItems.isNotEmpty()) {
|
if (localItems.isNotEmpty()) {
|
||||||
|
@ -22,7 +22,7 @@ object LocalDeleteModal {
|
||||||
.setMessage(R.string.delete_local_feed_warning_body)
|
.setMessage(R.string.delete_local_feed_warning_body)
|
||||||
.setPositiveButton(R.string.delete_label) { dialog: DialogInterface?, which: Int ->
|
.setPositiveButton(R.string.delete_label) { dialog: DialogInterface?, which: Int ->
|
||||||
for (item in localItems) {
|
for (item in localItems) {
|
||||||
deleteMediaOfEpisode(context, item)
|
deleteEpisodeMedia(context, item)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.setNegativeButton(R.string.cancel_label, null)
|
.setNegativeButton(R.string.cancel_label, null)
|
||||||
|
|
|
@ -7,9 +7,8 @@ import ac.mdiq.podcini.playback.base.InTheatre
|
||||||
import ac.mdiq.podcini.playback.base.InTheatre.curQueue
|
import ac.mdiq.podcini.playback.base.InTheatre.curQueue
|
||||||
import ac.mdiq.podcini.preferences.UserPreferences
|
import ac.mdiq.podcini.preferences.UserPreferences
|
||||||
import ac.mdiq.podcini.storage.database.RealmDB.realm
|
import ac.mdiq.podcini.storage.database.RealmDB.realm
|
||||||
import ac.mdiq.podcini.storage.database.RealmDB.unmanaged
|
|
||||||
import ac.mdiq.podcini.storage.model.Episode
|
import ac.mdiq.podcini.storage.model.Episode
|
||||||
import ac.mdiq.podcini.storage.model.Episode.Companion.BUILDING
|
import ac.mdiq.podcini.storage.model.Episode.PlayState
|
||||||
import ac.mdiq.podcini.storage.model.EpisodeMedia
|
import ac.mdiq.podcini.storage.model.EpisodeMedia
|
||||||
import ac.mdiq.podcini.storage.model.Feed.Companion.PREFIX_GENERATIVE_COVER
|
import ac.mdiq.podcini.storage.model.Feed.Companion.PREFIX_GENERATIVE_COVER
|
||||||
import ac.mdiq.podcini.storage.model.MediaType
|
import ac.mdiq.podcini.storage.model.MediaType
|
||||||
|
@ -99,7 +98,6 @@ open class EpisodeViewHolder(private val activity: MainActivity, parent: ViewGro
|
||||||
}
|
}
|
||||||
|
|
||||||
fun bind(item: Episode) {
|
fun bind(item: Episode) {
|
||||||
// Logd(TAG, "in bind: ${item.title} ${item.isFavorite} ${item.isPlayed()}")
|
|
||||||
if (episodeMonitor == null) {
|
if (episodeMonitor == null) {
|
||||||
val item_ = realm.query(Episode::class).query("id == ${item.id}").first()
|
val item_ = realm.query(Episode::class).query("id == ${item.id}").first()
|
||||||
episodeMonitor = CoroutineScope(Dispatchers.Default).launch {
|
episodeMonitor = CoroutineScope(Dispatchers.Default).launch {
|
||||||
|
@ -116,6 +114,7 @@ open class EpisodeViewHolder(private val activity: MainActivity, parent: ViewGro
|
||||||
else -> {}
|
else -> {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (mediaMonitor == null) {
|
if (mediaMonitor == null) {
|
||||||
|
@ -174,7 +173,7 @@ open class EpisodeViewHolder(private val activity: MainActivity, parent: ViewGro
|
||||||
when {
|
when {
|
||||||
item.media != null -> bind(item.media!!)
|
item.media != null -> bind(item.media!!)
|
||||||
// for generating TTS files for episode without media
|
// for generating TTS files for episode without media
|
||||||
item.playState == BUILDING -> {
|
item.playState == PlayState.BUILDING.code -> {
|
||||||
secondaryActionProgress.setPercentage(actionButton!!.processing, item)
|
secondaryActionProgress.setPercentage(actionButton!!.processing, item)
|
||||||
secondaryActionProgress.setIndeterminate(false)
|
secondaryActionProgress.setIndeterminate(false)
|
||||||
}
|
}
|
||||||
|
|
|
@ -512,7 +512,7 @@
|
||||||
<string name="pref_feed_skip">Auto skip</string>
|
<string name="pref_feed_skip">Auto skip</string>
|
||||||
<string name="pref_feed_skip_sum">Skip introductions and ending credits.</string>
|
<string name="pref_feed_skip_sum">Skip introductions and ending credits.</string>
|
||||||
<string name="pref_feed_associated_queue">Associated queue</string>
|
<string name="pref_feed_associated_queue">Associated queue</string>
|
||||||
<string name="pref_feed_associated_queue_sum">Set the queue to which epiosdes in the feed will added by default.</string>
|
<string name="pref_feed_associated_queue_sum">The queue to which epiosdes in the feed will added by default</string>
|
||||||
<string name="pref_feed_skip_ending">Skip last</string>
|
<string name="pref_feed_skip_ending">Skip last</string>
|
||||||
<string name="pref_feed_skip_intro">Skip first</string>
|
<string name="pref_feed_skip_intro">Skip first</string>
|
||||||
<string name="pref_feed_skip_ending_toast">Skipped last %d seconds</string>
|
<string name="pref_feed_skip_ending_toast">Skipped last %d seconds</string>
|
||||||
|
|
|
@ -8,7 +8,7 @@ internal object FeedItemMother {
|
||||||
|
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun anyFeedItemWithImage(): Episode {
|
fun anyFeedItemWithImage(): Episode {
|
||||||
val item = Episode(0, "Item", "Item", "url", Date(), Episode.PLAYED, FeedMother.anyFeed())
|
val item = Episode(0, "Item", "Item", "url", Date(), Episode.PlayState.PLAYED.code, FeedMother.anyFeed())
|
||||||
item.imageUrl = (IMAGE_URL)
|
item.imageUrl = (IMAGE_URL)
|
||||||
return item
|
return item
|
||||||
}
|
}
|
||||||
|
|
|
@ -87,7 +87,7 @@ open class DbCleanupTests {
|
||||||
val items: MutableList<Episode> = ArrayList()
|
val items: MutableList<Episode> = ArrayList()
|
||||||
feed.episodes.addAll(items)
|
feed.episodes.addAll(items)
|
||||||
val files: MutableList<File> = ArrayList()
|
val files: MutableList<File> = ArrayList()
|
||||||
populateItems(numItems, feed, items, files, Episode.PLAYED, false, false)
|
populateItems(numItems, feed, items, files, Episode.PlayState.PLAYED.code, false, false)
|
||||||
|
|
||||||
performAutoCleanup(context)
|
performAutoCleanup(context)
|
||||||
for (i in files.indices) {
|
for (i in files.indices) {
|
||||||
|
@ -104,7 +104,7 @@ open class DbCleanupTests {
|
||||||
for (i in 0 until numItems) {
|
for (i in 0 until numItems) {
|
||||||
val itemDate = Date((numItems - i).toLong())
|
val itemDate = Date((numItems - i).toLong())
|
||||||
var playbackCompletionDate: Date? = null
|
var playbackCompletionDate: Date? = null
|
||||||
if (itemState == Episode.PLAYED) {
|
if (itemState == Episode.PlayState.PLAYED.code) {
|
||||||
playbackCompletionDate = itemDate
|
playbackCompletionDate = itemDate
|
||||||
}
|
}
|
||||||
val item = Episode(0, "title", "id$i", "link", itemDate, itemState, feed)
|
val item = Episode(0, "title", "id$i", "link", itemDate, itemState, feed)
|
||||||
|
@ -140,7 +140,7 @@ open class DbCleanupTests {
|
||||||
val items: MutableList<Episode> = ArrayList()
|
val items: MutableList<Episode> = ArrayList()
|
||||||
feed.episodes.addAll(items)
|
feed.episodes.addAll(items)
|
||||||
val files: MutableList<File> = ArrayList()
|
val files: MutableList<File> = ArrayList()
|
||||||
populateItems(numItems, feed, items, files, Episode.UNPLAYED, false, false)
|
populateItems(numItems, feed, items, files, Episode.PlayState.UNPLAYED.code, false, false)
|
||||||
|
|
||||||
performAutoCleanup(context)
|
performAutoCleanup(context)
|
||||||
for (file in files) {
|
for (file in files) {
|
||||||
|
@ -157,7 +157,7 @@ open class DbCleanupTests {
|
||||||
val items: MutableList<Episode> = ArrayList()
|
val items: MutableList<Episode> = ArrayList()
|
||||||
feed.episodes.addAll(items)
|
feed.episodes.addAll(items)
|
||||||
val files: MutableList<File> = ArrayList()
|
val files: MutableList<File> = ArrayList()
|
||||||
populateItems(numItems, feed, items, files, Episode.PLAYED, true, false)
|
populateItems(numItems, feed, items, files, Episode.PlayState.PLAYED.code, true, false)
|
||||||
|
|
||||||
performAutoCleanup(context)
|
performAutoCleanup(context)
|
||||||
for (file in files) {
|
for (file in files) {
|
||||||
|
@ -198,7 +198,7 @@ open class DbCleanupTests {
|
||||||
val items: MutableList<Episode> = ArrayList()
|
val items: MutableList<Episode> = ArrayList()
|
||||||
feed.episodes.addAll(items)
|
feed.episodes.addAll(items)
|
||||||
val files: MutableList<File> = ArrayList()
|
val files: MutableList<File> = ArrayList()
|
||||||
populateItems(numItems, feed, items, files, Episode.PLAYED, false, true)
|
populateItems(numItems, feed, items, files, Episode.PlayState.PLAYED.code, false, true)
|
||||||
|
|
||||||
performAutoCleanup(context)
|
performAutoCleanup(context)
|
||||||
for (file in files) {
|
for (file in files) {
|
||||||
|
|
|
@ -81,7 +81,7 @@ class DbNullCleanupAlgorithmTest {
|
||||||
feed.episodes.addAll(items)
|
feed.episodes.addAll(items)
|
||||||
val files: MutableList<File> = ArrayList()
|
val files: MutableList<File> = ArrayList()
|
||||||
for (i in 0 until numItems) {
|
for (i in 0 until numItems) {
|
||||||
val item = Episode(0, "title", "id$i", "link", Date(), Episode.PLAYED, feed)
|
val item = Episode(0, "title", "id$i", "link", Date(), Episode.PlayState.PLAYED.code, feed)
|
||||||
|
|
||||||
val f = File(destFolder, "file $i")
|
val f = File(destFolder, "file $i")
|
||||||
Assert.assertTrue(f.createNewFile())
|
Assert.assertTrue(f.createNewFile())
|
||||||
|
|
|
@ -33,7 +33,7 @@ class DbPlayQueueCleanupAlgorithmTest : DbCleanupTests() {
|
||||||
val items: MutableList<Episode> = ArrayList()
|
val items: MutableList<Episode> = ArrayList()
|
||||||
feed.episodes.addAll(items)
|
feed.episodes.addAll(items)
|
||||||
val files: MutableList<File> = ArrayList()
|
val files: MutableList<File> = ArrayList()
|
||||||
populateItems(numItems, feed, items, files, Episode.UNPLAYED, false, false)
|
populateItems(numItems, feed, items, files, Episode.PlayState.UNPLAYED.code, false, false)
|
||||||
|
|
||||||
performAutoCleanup(context)
|
performAutoCleanup(context)
|
||||||
for (i in files.indices) {
|
for (i in files.indices) {
|
||||||
|
|
|
@ -61,7 +61,7 @@ class DbTasksTest {
|
||||||
feed.episodes.clear()
|
feed.episodes.clear()
|
||||||
for (i in 0 until numItems) {
|
for (i in 0 until numItems) {
|
||||||
feed.episodes.add(Episode(0, "item $i", "id $i", "link $i",
|
feed.episodes.add(Episode(0, "item $i", "id $i", "link $i",
|
||||||
Date(), Episode.UNPLAYED, feed))
|
Date(), Episode.PlayState.UNPLAYED.code, feed))
|
||||||
}
|
}
|
||||||
val newFeed = updateFeed(context, feed, false)
|
val newFeed = updateFeed(context, feed, false)
|
||||||
|
|
||||||
|
@ -97,7 +97,7 @@ class DbTasksTest {
|
||||||
feed.episodes.clear()
|
feed.episodes.clear()
|
||||||
for (i in 0 until numItemsOld) {
|
for (i in 0 until numItemsOld) {
|
||||||
feed.episodes.add(Episode(0, "item $i", "id $i", "link $i",
|
feed.episodes.add(Episode(0, "item $i", "id $i", "link $i",
|
||||||
Date(i.toLong()), Episode.PLAYED, feed))
|
Date(i.toLong()), Episode.PlayState.PLAYED.code, feed))
|
||||||
}
|
}
|
||||||
// val adapter = getInstance()
|
// val adapter = getInstance()
|
||||||
// adapter.open()
|
// adapter.open()
|
||||||
|
@ -117,7 +117,7 @@ class DbTasksTest {
|
||||||
|
|
||||||
for (i in numItemsOld until numItemsNew + numItemsOld) {
|
for (i in numItemsOld until numItemsNew + numItemsOld) {
|
||||||
feed.episodes.add(0, Episode(0, "item $i", "id $i", "link $i",
|
feed.episodes.add(0, Episode(0, "item $i", "id $i", "link $i",
|
||||||
Date(i.toLong()), Episode.UNPLAYED, feed))
|
Date(i.toLong()), Episode.PlayState.UNPLAYED.code, feed))
|
||||||
}
|
}
|
||||||
|
|
||||||
val newFeed = updateFeed(context, feed, false)
|
val newFeed = updateFeed(context, feed, false)
|
||||||
|
@ -134,7 +134,7 @@ class DbTasksTest {
|
||||||
@Test
|
@Test
|
||||||
fun testUpdateFeedMediaUrlResetState() {
|
fun testUpdateFeedMediaUrlResetState() {
|
||||||
val feed = Feed("url", null, "title")
|
val feed = Feed("url", null, "title")
|
||||||
val item = Episode(0, "item", "id", "link", Date(), Episode.PLAYED, feed)
|
val item = Episode(0, "item", "id", "link", Date(), Episode.PlayState.PLAYED.code, feed)
|
||||||
feed.episodes.add(item)
|
feed.episodes.add(item)
|
||||||
|
|
||||||
// val adapter = getInstance()
|
// val adapter = getInstance()
|
||||||
|
@ -166,7 +166,7 @@ class DbTasksTest {
|
||||||
feed.episodes.clear()
|
feed.episodes.clear()
|
||||||
for (i in 0..9) {
|
for (i in 0..9) {
|
||||||
feed.episodes.add(
|
feed.episodes.add(
|
||||||
Episode(0, "item $i", "id $i", "link $i", Date(i.toLong()), Episode.PLAYED, feed))
|
Episode(0, "item $i", "id $i", "link $i", Date(i.toLong()), Episode.PlayState.PLAYED.code, feed))
|
||||||
}
|
}
|
||||||
// val adapter = getInstance()
|
// val adapter = getInstance()
|
||||||
// adapter.open()
|
// adapter.open()
|
||||||
|
@ -188,7 +188,7 @@ class DbTasksTest {
|
||||||
feed.episodes.clear()
|
feed.episodes.clear()
|
||||||
for (i in 0..9) {
|
for (i in 0..9) {
|
||||||
val item =
|
val item =
|
||||||
Episode(0, "item $i", "id $i", "link $i", Date(i.toLong()), Episode.PLAYED, feed)
|
Episode(0, "item $i", "id $i", "link $i", Date(i.toLong()), Episode.PlayState.PLAYED.code, feed)
|
||||||
val media = EpisodeMedia(item, "download url $i", 123, "media/mp3")
|
val media = EpisodeMedia(item, "download url $i", 123, "media/mp3")
|
||||||
item.setMedia(media)
|
item.setMedia(media)
|
||||||
feed.episodes.add(item)
|
feed.episodes.add(item)
|
||||||
|
@ -244,7 +244,7 @@ class DbTasksTest {
|
||||||
val items: MutableList<Episode> = ArrayList(numFeedItems)
|
val items: MutableList<Episode> = ArrayList(numFeedItems)
|
||||||
for (i in 1..numFeedItems) {
|
for (i in 1..numFeedItems) {
|
||||||
val item = Episode(0, "item $i of $title", "id$title$i", "link",
|
val item = Episode(0, "item $i of $title", "id$title$i", "link",
|
||||||
Date(), Episode.UNPLAYED, feed)
|
Date(), Episode.PlayState.UNPLAYED.code, feed)
|
||||||
items.add(item)
|
items.add(item)
|
||||||
}
|
}
|
||||||
feed.episodes.addAll(items)
|
feed.episodes.addAll(items)
|
||||||
|
|
|
@ -6,7 +6,7 @@ import ac.mdiq.podcini.playback.base.InTheatre.curQueue
|
||||||
import ac.mdiq.podcini.preferences.UserPreferences
|
import ac.mdiq.podcini.preferences.UserPreferences
|
||||||
import ac.mdiq.podcini.storage.database.Episodes.addToHistory
|
import ac.mdiq.podcini.storage.database.Episodes.addToHistory
|
||||||
import ac.mdiq.podcini.storage.database.Episodes.deleteEpisodes
|
import ac.mdiq.podcini.storage.database.Episodes.deleteEpisodes
|
||||||
import ac.mdiq.podcini.storage.database.Episodes.deleteMediaOfEpisode
|
import ac.mdiq.podcini.storage.database.Episodes.deleteEpisodeMedia
|
||||||
import ac.mdiq.podcini.storage.database.Episodes.getEpisode
|
import ac.mdiq.podcini.storage.database.Episodes.getEpisode
|
||||||
import ac.mdiq.podcini.storage.database.Episodes.getEpisodeMedia
|
import ac.mdiq.podcini.storage.database.Episodes.getEpisodeMedia
|
||||||
import ac.mdiq.podcini.storage.database.Episodes.persistEpisode
|
import ac.mdiq.podcini.storage.database.Episodes.persistEpisode
|
||||||
|
@ -97,7 +97,7 @@ class DbWriterTest {
|
||||||
val feed = Feed("url", null, "title")
|
val feed = Feed("url", null, "title")
|
||||||
val items: MutableList<Episode> = ArrayList()
|
val items: MutableList<Episode> = ArrayList()
|
||||||
feed.episodes.addAll(items)
|
feed.episodes.addAll(items)
|
||||||
val item = Episode(0, "Item", "Item", "url", Date(), Episode.PLAYED, feed)
|
val item = Episode(0, "Item", "Item", "url", Date(), Episode.PlayState.PLAYED.code, feed)
|
||||||
items.add(item)
|
items.add(item)
|
||||||
val media = EpisodeMedia(0, item, duration, 1, 1, "mime_type",
|
val media = EpisodeMedia(0, item, duration, 1, 1, "mime_type",
|
||||||
"dummy path", "download_url", true, null, 0, 0)
|
"dummy path", "download_url", true, null, 0, 0)
|
||||||
|
@ -138,7 +138,7 @@ class DbWriterTest {
|
||||||
val feed = Feed("url", null, "title")
|
val feed = Feed("url", null, "title")
|
||||||
val items: MutableList<Episode> = ArrayList()
|
val items: MutableList<Episode> = ArrayList()
|
||||||
feed.episodes.addAll(items)
|
feed.episodes.addAll(items)
|
||||||
val item = Episode(0, "Item", "Item", "url", Date(), Episode.PLAYED, feed)
|
val item = Episode(0, "Item", "Item", "url", Date(), Episode.PlayState.PLAYED.code, feed)
|
||||||
|
|
||||||
var media: EpisodeMedia? = EpisodeMedia(0, item, 1, 1, 1, "mime_type",
|
var media: EpisodeMedia? = EpisodeMedia(0, item, 1, 1, 1, "mime_type",
|
||||||
dest.absolutePath, "download_url", true, null, 0, 0)
|
dest.absolutePath, "download_url", true, null, 0, 0)
|
||||||
|
@ -154,7 +154,7 @@ class DbWriterTest {
|
||||||
Assert.assertTrue(item.id != 0L)
|
Assert.assertTrue(item.id != 0L)
|
||||||
|
|
||||||
runBlocking {
|
runBlocking {
|
||||||
val job = deleteMediaOfEpisode(context, item)
|
val job = deleteEpisodeMedia(context, item)
|
||||||
withTimeout(TIMEOUT*1000) { job.join() }
|
withTimeout(TIMEOUT*1000) { job.join() }
|
||||||
}
|
}
|
||||||
media = getEpisodeMedia(media.id)
|
media = getEpisodeMedia(media.id)
|
||||||
|
@ -176,7 +176,7 @@ class DbWriterTest {
|
||||||
val feed = Feed("url", null, "title")
|
val feed = Feed("url", null, "title")
|
||||||
val items: MutableList<Episode> = ArrayList()
|
val items: MutableList<Episode> = ArrayList()
|
||||||
feed.episodes.addAll(items)
|
feed.episodes.addAll(items)
|
||||||
val item = Episode(0, "Item", "Item", "url", Date(), Episode.UNPLAYED, feed)
|
val item = Episode(0, "Item", "Item", "url", Date(), Episode.PlayState.UNPLAYED.code, feed)
|
||||||
|
|
||||||
var media: EpisodeMedia? = EpisodeMedia(0, item, 1, 1, 1, "mime_type",
|
var media: EpisodeMedia? = EpisodeMedia(0, item, 1, 1, 1, "mime_type",
|
||||||
dest.absolutePath, "download_url", true, null, 0, 0)
|
dest.absolutePath, "download_url", true, null, 0, 0)
|
||||||
|
@ -196,7 +196,7 @@ class DbWriterTest {
|
||||||
queue = curQueue.episodes
|
queue = curQueue.episodes
|
||||||
Assert.assertTrue(queue.size != 0)
|
Assert.assertTrue(queue.size != 0)
|
||||||
|
|
||||||
deleteMediaOfEpisode(context, item)
|
deleteEpisodeMedia(context, item)
|
||||||
Awaitility.await().timeout(2, TimeUnit.SECONDS).until { !dest.exists() }
|
Awaitility.await().timeout(2, TimeUnit.SECONDS).until { !dest.exists() }
|
||||||
media = getEpisodeMedia(media.id)
|
media = getEpisodeMedia(media.id)
|
||||||
Assert.assertNotNull(media)
|
Assert.assertNotNull(media)
|
||||||
|
@ -218,7 +218,7 @@ class DbWriterTest {
|
||||||
val itemFiles: MutableList<File> = ArrayList()
|
val itemFiles: MutableList<File> = ArrayList()
|
||||||
// create items with downloaded media files
|
// create items with downloaded media files
|
||||||
for (i in 0..9) {
|
for (i in 0..9) {
|
||||||
val item = Episode(0, "Item $i", "Item$i", "url", Date(), Episode.PLAYED, feed)
|
val item = Episode(0, "Item $i", "Item$i", "url", Date(), Episode.PlayState.PLAYED.code, feed)
|
||||||
feed.episodes.add(item)
|
feed.episodes.add(item)
|
||||||
|
|
||||||
val enc = File(destFolder, "file $i")
|
val enc = File(destFolder, "file $i")
|
||||||
|
@ -308,7 +308,7 @@ class DbWriterTest {
|
||||||
|
|
||||||
// create items
|
// create items
|
||||||
for (i in 0..9) {
|
for (i in 0..9) {
|
||||||
val item = Episode(0, "Item $i", "Item$i", "url", Date(), Episode.PLAYED, feed)
|
val item = Episode(0, "Item $i", "Item$i", "url", Date(), Episode.PlayState.PLAYED.code, feed)
|
||||||
feed.episodes.add(item)
|
feed.episodes.add(item)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -352,7 +352,7 @@ class DbWriterTest {
|
||||||
|
|
||||||
// create items with downloaded media files
|
// create items with downloaded media files
|
||||||
for (i in 0..9) {
|
for (i in 0..9) {
|
||||||
val item = Episode(0, "Item $i", "Item$i", "url", Date(), Episode.PLAYED, feed)
|
val item = Episode(0, "Item $i", "Item$i", "url", Date(), Episode.PlayState.PLAYED.code, feed)
|
||||||
feed.episodes.add(item)
|
feed.episodes.add(item)
|
||||||
val enc = File(destFolder, "file $i")
|
val enc = File(destFolder, "file $i")
|
||||||
val media = EpisodeMedia(0, item, 1, 1, 1, "mime_type",
|
val media = EpisodeMedia(0, item, 1, 1, 1, "mime_type",
|
||||||
|
@ -415,7 +415,7 @@ class DbWriterTest {
|
||||||
|
|
||||||
// create items with downloaded media files
|
// create items with downloaded media files
|
||||||
for (i in 0..9) {
|
for (i in 0..9) {
|
||||||
val item = Episode(0, "Item $i", "Item$i", "url", Date(), Episode.PLAYED, feed)
|
val item = Episode(0, "Item $i", "Item$i", "url", Date(), Episode.PlayState.PLAYED.code, feed)
|
||||||
feed.episodes.add(item)
|
feed.episodes.add(item)
|
||||||
val enc = File(destFolder, "file $i")
|
val enc = File(destFolder, "file $i")
|
||||||
val media = EpisodeMedia(0, item, 1, 1, 1, "mime_type",
|
val media = EpisodeMedia(0, item, 1, 1, 1, "mime_type",
|
||||||
|
@ -463,7 +463,7 @@ class DbWriterTest {
|
||||||
|
|
||||||
// create items
|
// create items
|
||||||
for (i in 0..9) {
|
for (i in 0..9) {
|
||||||
val item = Episode(0, "Item $i", "Item$i", "url", Date(), Episode.PLAYED, feed)
|
val item = Episode(0, "Item $i", "Item$i", "url", Date(), Episode.PlayState.PLAYED.code, feed)
|
||||||
item.setMedia(EpisodeMedia(item, "", 0, ""))
|
item.setMedia(EpisodeMedia(item, "", 0, ""))
|
||||||
feed.episodes.add(item)
|
feed.episodes.add(item)
|
||||||
}
|
}
|
||||||
|
@ -494,7 +494,7 @@ class DbWriterTest {
|
||||||
private fun playbackHistorySetup(playbackCompletionDate: Date?): EpisodeMedia {
|
private fun playbackHistorySetup(playbackCompletionDate: Date?): EpisodeMedia {
|
||||||
val feed = Feed("url", null, "title")
|
val feed = Feed("url", null, "title")
|
||||||
feed.episodes.clear()
|
feed.episodes.clear()
|
||||||
val item = Episode(0, "title", "id", "link", Date(), Episode.PLAYED, feed)
|
val item = Episode(0, "title", "id", "link", Date(), Episode.PlayState.PLAYED.code, feed)
|
||||||
val media = EpisodeMedia(0, item, 10, 0, 1, "mime", null,
|
val media = EpisodeMedia(0, item, 10, 0, 1, "mime", null,
|
||||||
"url", false, playbackCompletionDate, 0, 0)
|
"url", false, playbackCompletionDate, 0, 0)
|
||||||
feed.episodes.add(item)
|
feed.episodes.add(item)
|
||||||
|
@ -547,7 +547,7 @@ class DbWriterTest {
|
||||||
val feed = Feed("url", null, "title")
|
val feed = Feed("url", null, "title")
|
||||||
feed.episodes.clear()
|
feed.episodes.clear()
|
||||||
for (i in 0 until numItems) {
|
for (i in 0 until numItems) {
|
||||||
val item = Episode(0, "title $i", "id $i", "link $i", Date(), Episode.PLAYED, feed)
|
val item = Episode(0, "title $i", "id $i", "link $i", Date(), Episode.PlayState.PLAYED.code, feed)
|
||||||
item.setMedia(EpisodeMedia(item, "", 0, ""))
|
item.setMedia(EpisodeMedia(item, "", 0, ""))
|
||||||
feed.episodes.add(item)
|
feed.episodes.add(item)
|
||||||
}
|
}
|
||||||
|
@ -576,7 +576,7 @@ class DbWriterTest {
|
||||||
fun testAddQueueItemSingleItem() {
|
fun testAddQueueItemSingleItem() {
|
||||||
val feed = Feed("url", null, "title")
|
val feed = Feed("url", null, "title")
|
||||||
feed.episodes.clear()
|
feed.episodes.clear()
|
||||||
val item = Episode(0, "title", "id", "link", Date(), Episode.PLAYED, feed)
|
val item = Episode(0, "title", "id", "link", Date(), Episode.PlayState.PLAYED.code, feed)
|
||||||
item.setMedia(EpisodeMedia(item, "", 0, ""))
|
item.setMedia(EpisodeMedia(item, "", 0, ""))
|
||||||
feed.episodes.add(item)
|
feed.episodes.add(item)
|
||||||
|
|
||||||
|
@ -605,7 +605,7 @@ class DbWriterTest {
|
||||||
fun testAddQueueItemSingleItemAlreadyInQueue() {
|
fun testAddQueueItemSingleItemAlreadyInQueue() {
|
||||||
val feed = Feed("url", null, "title")
|
val feed = Feed("url", null, "title")
|
||||||
feed.episodes.clear()
|
feed.episodes.clear()
|
||||||
val item = Episode(0, "title", "id", "link", Date(), Episode.PLAYED, feed)
|
val item = Episode(0, "title", "id", "link", Date(), Episode.PlayState.PLAYED.code, feed)
|
||||||
item.setMedia(EpisodeMedia(item, "", 0, ""))
|
item.setMedia(EpisodeMedia(item, "", 0, ""))
|
||||||
feed.episodes.add(item)
|
feed.episodes.add(item)
|
||||||
|
|
||||||
|
@ -766,7 +766,7 @@ class DbWriterTest {
|
||||||
feed.episodes.clear()
|
feed.episodes.clear()
|
||||||
for (i in 0 until numItems) {
|
for (i in 0 until numItems) {
|
||||||
val item = Episode(0, "title $i", "id $i", "link $i",
|
val item = Episode(0, "title $i", "id $i", "link $i",
|
||||||
Date(), Episode.PLAYED, feed)
|
Date(), Episode.PlayState.PLAYED.code, feed)
|
||||||
item.setMedia(EpisodeMedia(item, "", 0, ""))
|
item.setMedia(EpisodeMedia(item, "", 0, ""))
|
||||||
feed.episodes.add(item)
|
feed.episodes.add(item)
|
||||||
}
|
}
|
||||||
|
@ -817,7 +817,7 @@ class DbWriterTest {
|
||||||
val feed = Feed("url", null, "title")
|
val feed = Feed("url", null, "title")
|
||||||
feed.episodes.clear()
|
feed.episodes.clear()
|
||||||
for (i in 0 until numItems) {
|
for (i in 0 until numItems) {
|
||||||
val item = Episode(0, "title $i", "id $i", "link $i", Date(), Episode.NEW, feed)
|
val item = Episode(0, "title $i", "id $i", "link $i", Date(), Episode.PlayState.NEW.code, feed)
|
||||||
item.setMedia(EpisodeMedia(item, "", 0, ""))
|
item.setMedia(EpisodeMedia(item, "", 0, ""))
|
||||||
feed.episodes.add(item)
|
feed.episodes.add(item)
|
||||||
}
|
}
|
||||||
|
@ -848,7 +848,7 @@ class DbWriterTest {
|
||||||
val feed = Feed("url", null, "title")
|
val feed = Feed("url", null, "title")
|
||||||
feed.episodes.clear()
|
feed.episodes.clear()
|
||||||
for (i in 0 until numItems) {
|
for (i in 0 until numItems) {
|
||||||
val item = Episode(0, "title $i", "id $i", "link $i", Date(), Episode.PLAYED, feed)
|
val item = Episode(0, "title $i", "id $i", "link $i", Date(), Episode.PlayState.PLAYED.code, feed)
|
||||||
item.setMedia(EpisodeMedia(item, "", 0, ""))
|
item.setMedia(EpisodeMedia(item, "", 0, ""))
|
||||||
feed.episodes.add(item)
|
feed.episodes.add(item)
|
||||||
}
|
}
|
||||||
|
|
|
@ -57,7 +57,7 @@ class EpisodeDuplicateGuesserTest {
|
||||||
private fun item(guid: String, title: String, downloadUrl: String,
|
private fun item(guid: String, title: String, downloadUrl: String,
|
||||||
date: Long, duration: Long, mime: String
|
date: Long, duration: Long, mime: String
|
||||||
): Episode {
|
): Episode {
|
||||||
val item = Episode(0, title, guid, "link", Date(date), Episode.PLAYED, null)
|
val item = Episode(0, title, guid, "link", Date(date), Episode.PlayState.PLAYED.code, null)
|
||||||
val media = EpisodeMedia(item, downloadUrl, duration, mime)
|
val media = EpisodeMedia(item, downloadUrl, duration, mime)
|
||||||
item.setMedia(media)
|
item.setMedia(media)
|
||||||
return item
|
return item
|
||||||
|
|
|
@ -29,7 +29,7 @@ class ExceptFavoriteCleanupAlgorithmTest : DbCleanupTests() {
|
||||||
val items: MutableList<Episode> = ArrayList()
|
val items: MutableList<Episode> = ArrayList()
|
||||||
feed.episodes.addAll(items)
|
feed.episodes.addAll(items)
|
||||||
val files: MutableList<File> = ArrayList()
|
val files: MutableList<File> = ArrayList()
|
||||||
populateItems(numberOfItems, feed, items, files, Episode.UNPLAYED, false, false)
|
populateItems(numberOfItems, feed, items, files, Episode.PlayState.UNPLAYED.code, false, false)
|
||||||
|
|
||||||
performAutoCleanup(context)
|
performAutoCleanup(context)
|
||||||
for (i in files.indices) {
|
for (i in files.indices) {
|
||||||
|
@ -48,7 +48,7 @@ class ExceptFavoriteCleanupAlgorithmTest : DbCleanupTests() {
|
||||||
val items: MutableList<Episode> = ArrayList()
|
val items: MutableList<Episode> = ArrayList()
|
||||||
feed.episodes.addAll(items)
|
feed.episodes.addAll(items)
|
||||||
val files: MutableList<File> = ArrayList()
|
val files: MutableList<File> = ArrayList()
|
||||||
populateItems(numberOfItems, feed, items, files, Episode.UNPLAYED, true, false)
|
populateItems(numberOfItems, feed, items, files, Episode.PlayState.UNPLAYED.code, true, false)
|
||||||
|
|
||||||
performAutoCleanup(context)
|
performAutoCleanup(context)
|
||||||
for (i in files.indices) {
|
for (i in files.indices) {
|
||||||
|
@ -67,7 +67,7 @@ class ExceptFavoriteCleanupAlgorithmTest : DbCleanupTests() {
|
||||||
val items: MutableList<Episode> = ArrayList()
|
val items: MutableList<Episode> = ArrayList()
|
||||||
feed.episodes.addAll(items)
|
feed.episodes.addAll(items)
|
||||||
val files: MutableList<File> = ArrayList()
|
val files: MutableList<File> = ArrayList()
|
||||||
populateItems(numberOfItems, feed, items, files, Episode.UNPLAYED, false, true)
|
populateItems(numberOfItems, feed, items, files, Episode.PlayState.UNPLAYED.code, false, true)
|
||||||
|
|
||||||
performAutoCleanup(context)
|
performAutoCleanup(context)
|
||||||
for (i in files.indices) {
|
for (i in files.indices) {
|
||||||
|
|
|
@ -57,7 +57,7 @@ object ItemEnqueuePositionCalculatorTest {
|
||||||
|
|
||||||
fun createFeedItem(id: Long): Episode {
|
fun createFeedItem(id: Long): Episode {
|
||||||
val item = Episode(id, "Item$id", "ItemId$id", "url",
|
val item = Episode(id, "Item$id", "ItemId$id", "url",
|
||||||
Date(), Episode.PLAYED, anyFeed())
|
Date(), Episode.PlayState.PLAYED.code, anyFeed())
|
||||||
val media = EpisodeMedia(item, "http://download.url.net/$id", 1234567, "audio/mpeg")
|
val media = EpisodeMedia(item, "http://download.url.net/$id", 1234567, "audio/mpeg")
|
||||||
media.id = item.id
|
media.id = item.id
|
||||||
item.setMedia(media)
|
item.setMedia(media)
|
||||||
|
|
17
changelog.md
17
changelog.md
|
@ -1,3 +1,20 @@
|
||||||
|
# 6.3.4
|
||||||
|
|
||||||
|
* fixed mis-behavior of setting associated queue to Active in FeedSettings
|
||||||
|
* items on dialog for "Auto delete episodes" are changed to checkbox
|
||||||
|
* playState Int variables have been put into enum PlayState
|
||||||
|
* corrected an error in incomplete reconsile
|
||||||
|
* fixed the nasty mis-behavior in Queues view when removing episodes
|
||||||
|
* updated feed in FeedInfo view when feed preferences change
|
||||||
|
* enhanced feed setting UI, added display of current queue preference
|
||||||
|
* added "prefer streaming over download" in feed setting. ruling along the global setting, streaming is preferred when either one is set to true
|
||||||
|
* added None in associated queue setting of any feed
|
||||||
|
* if set, episodes in the feed are not automatically added to any queue, but are used as a natural queue for getting the next episode to play
|
||||||
|
* the next episode is determined in such a way:
|
||||||
|
* if the currently playing episode had been (manually) added to the active queue, then it's the next in queue
|
||||||
|
* else if "prefer streaming" is set, it's the next unplayed episode in the feed episodes list based on the current sort order
|
||||||
|
* else it's the next downloaded unplayed episode
|
||||||
|
|
||||||
# 6.3.3
|
# 6.3.3
|
||||||
|
|
||||||
* fixed crash when setting as Played/Unplayed in EpisodeInfo view
|
* fixed crash when setting as Played/Unplayed in EpisodeInfo view
|
||||||
|
|
|
@ -0,0 +1,17 @@
|
||||||
|
|
||||||
|
Version 6.3.4 brings several changes:
|
||||||
|
|
||||||
|
* fixed mis-behavior of setting associated queue to Active in FeedSettings
|
||||||
|
* items on dialog for "Auto delete episodes" are changed to checkbox
|
||||||
|
* playState Int variables have been put into enum PlayState
|
||||||
|
* corrected an error in incomplete reconsile
|
||||||
|
* fixed the nasty mis-behavior in Queues view when removing episodes
|
||||||
|
* updated feed in FeedInfo view when feed preferences change
|
||||||
|
* enhanced feed setting UI, added display of current queue preference
|
||||||
|
* added "prefer streaming over download" in feed setting. ruling along the global setting, streaming is preferred when either one is set to true
|
||||||
|
* added None in associated queue setting of any feed
|
||||||
|
* if set, episodes in the feed are not automatically added to any queue, but are used as a natural queue for getting the next episode to play
|
||||||
|
* the next episode is determined in such a way:
|
||||||
|
* if the currently playing episode had been (manually) added to the active queue, then it's the next in queue
|
||||||
|
* else if "prefer streaming" is set, it's the next unplayed episode in the feed episodes list based on the current sort order
|
||||||
|
* else it's the next downloaded unplayed episode
|
Loading…
Reference in New Issue