Skip to content

Commit

Permalink
6.1.2 commit
Browse files Browse the repository at this point in the history
  • Loading branch information
XilinJia committed Jul 20, 2024
1 parent d47bdab commit 3c2618a
Show file tree
Hide file tree
Showing 40 changed files with 656 additions and 224 deletions.
2 changes: 0 additions & 2 deletions Licenses_and_permissions.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,6 @@ Apache License 2.0

[com.squareup.okhttp3](https://github.com/square/okhttp/blob/master/LICENSE.txt) Apache License 2.0

[//]: # ([io.reactivex.rxjava2](https://github.com/ReactiveX/RxJava/blob/3.x/LICENSE) Apache License 2.0)

[com.mikepenz:iconics-core](https://github.com/mikepenz/Android-Iconics/blob/develop/LICENSE) Apache License 2.0

[com.leinardi.android](https://github.com/leinardi/FloatingActionButtonSpeedDial/blob/release/LICENSE) Apache License 2.0
Expand Down
10 changes: 4 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,6 @@ While podcast subscriptions' OPML files (from AntennaPod or any other sources) c

* More convenient player control displayed on all pages
* Revamped and more efficient expanded player view showing episode description on the front
* External player class is merged into the player
* Playback speed setting has been straightened up, three speed can be set separately or combined: current audio, podcast, and global
* Added preference "Fast Forward Speed" under "Playback" in settings with default value of 0.0, dialog allows setting a number between 0.0 and 10.0
* The "Skip to next episode" button on the player
Expand All @@ -50,9 +49,7 @@ While podcast subscriptions' OPML files (from AntennaPod or any other sources) c
* single tap not during play has no effect
* Added preference "Fallback Speed" under "Playback" in settings with default value of 0.0, dialog allows setting a float number (capped between 0.0 and 1.5)
* if the user customizes "Fallback speed" to a value greater than 0.1, long-press the Play button during play enters the fallback mode and plays at the set fallback speed, single tap exits the fallback mode
* Various efficiency improvements, including removal of:
* redundant media loadings and ui updates
* frequent list search during audio play
* Various efficiency improvements
* streamed media somewhat equivalent to downloaded media
* enabled episode description on player detailed view
* enabled intro- and end- skipping
Expand All @@ -77,6 +74,7 @@ While podcast subscriptions' OPML files (from AntennaPod or any other sources) c
* Played or new episodes have clearer markings
* Sort dialog no longer dims the main view
* download date can be used to sort both feeds and episodes
* Subscriptions view has a filter based on feed preferences, in the same style as episodes filter
* Subscriptions sorting is now bi-directional based on various explicit measures
* in episode list view, if episode has no media, TTS button is shown for fetching transcript (if not exist) and then generating audio file from the transcript. TTS audio files are playable in the same way as local media (with speed setting, pause and rewind/forward)
* Long-press filter button in FeedEpisode view enables/disables filters without changing filter settings
Expand Down Expand Up @@ -106,8 +104,6 @@ While podcast subscriptions' OPML files (from AntennaPod or any other sources) c
* Ability to open podcast from webpage address
* Online feed info display is handled in similar ways as any local feed, and offers options to subscribe or view episodes
* Online feed episodes can be freely played (streamed) without a subscription
* externally shared feed opens in the new online feed view fragment
* OnlineFeedView` activity is stripped down to only receive externally shared feeds
* Youtube channels are accepted from external share or paste of address in podcast search view, and can be subscribed as a normal podcast, though video play is handled externally

### Instant (or Wifi) sync
Expand All @@ -122,6 +118,8 @@ While podcast subscriptions' OPML files (from AntennaPod or any other sources) c
* When auto download is enabled in the Settings, feeds to be auto-downloaded need to be separately enabled in the feed settings.
* Each feed also has its own download policy (only new episodes, newest episodes, and oldest episodes. "newest episodes" meaning most recent episodes, new or old)
* Each feed has its own limit (Episode cache) for number of episodes downloaded, this limit rules in combination of the overall limit for the app.
* Auto downloads run feeds or feed refreshes, scheduled or manual
* auto download always includes any undownloaded episodes (regardless of feeds) added in the current queue
* After auto download run, episodes with New status is changed to Unplayed.
* auto download feed setting dialog is also changed:
* there are now separate dialogs for inclusive and exclusive filters where filter tokens can be specified independently
Expand Down
4 changes: 2 additions & 2 deletions app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -126,8 +126,8 @@ android {
buildConfig true
}
defaultConfig {
versionCode 3020215
versionName "6.1.1"
versionCode 3020216
versionName "6.1.2"

applicationId "ac.mdiq.podcini.R"
def commit = ""
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import ac.mdiq.podcini.net.download.serviceinterface.DownloadRequest
import ac.mdiq.podcini.net.download.serviceinterface.DownloadServiceInterface
import ac.mdiq.podcini.net.sync.model.EpisodeAction
import ac.mdiq.podcini.net.sync.queue.SynchronizationQueueSink
import ac.mdiq.podcini.net.sync.queue.SynchronizationQueueSink.needSynch
import ac.mdiq.podcini.net.utils.NetworkUtils.isAllowMobileEpisodeDownload
import ac.mdiq.podcini.preferences.UserPreferences
import ac.mdiq.podcini.preferences.UserPreferences.appPrefs
Expand Down Expand Up @@ -83,7 +84,7 @@ class DownloadServiceInterfaceImpl : DownloadServiceInterface() {
val workInfoList = future.get() // Wait for the completion of the future operation and retrieve the result
workInfoList.forEach { workInfo ->
if (workInfo.tags.contains(WORK_DATA_WAS_QUEUED)) {
if (media.episode != null) Queues.removeFromQueue(null, media.episode!!)
if (media.episode != null) Queues.removeFromQueue(media.episode!!)
}
}
WorkManager.getInstance(context).cancelAllWorkByTag(tag)
Expand Down Expand Up @@ -383,7 +384,7 @@ class DownloadServiceInterfaceImpl : DownloadServiceInterface() {
Log.e(TAG, "ExecutionException in MediaHandlerThread: " + e.message)
updatedStatus = DownloadResult(media.id, media.getEpisodeTitle(), DownloadError.ERROR_DB_ACCESS_ERROR, false, e.message?:"")
}
if (item != null) {
if (needSynch() && item != null) {
val action = EpisodeAction.Builder(item, EpisodeAction.DOWNLOAD)
.currentTimestamp()
.build()
Expand Down
10 changes: 0 additions & 10 deletions app/src/main/kotlin/ac/mdiq/podcini/net/feed/FeedUpdateManager.kt
Original file line number Diff line number Diff line change
Expand Up @@ -356,15 +356,9 @@ object FeedUpdateManager {
private const val serialVersionUID = 1L
}
}

companion object {
private val TAG: String = FeedParserTask::class.simpleName ?: "Anonymous"
}
}

class FeedSyncTask(private val context: Context, request: DownloadRequest) {
// var savedFeed: Feed? = null
// private set
private val task = FeedParserTask(request)
private var feedHandlerResult: FeedHandlerResult? = null
val downloadStatus: DownloadResult
Expand All @@ -379,9 +373,5 @@ object FeedUpdateManager {
return true
}
}

companion object {
private val TAG: String = FeedUpdateWorker::class.simpleName ?: "Anonymous"
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -287,7 +287,7 @@ open class SyncService(context: Context, params: WorkerParameters) : Worker(cont
// if (result.first != null) queueToBeRemoved.add(result.second)
updatedItems.add(result.second)
}
removeFromQueue(null, *updatedItems.toTypedArray())
removeFromQueue(*updatedItems.toTypedArray())
// loadAdditionalFeedItemListData(updatedItems)
persistEpisodes(updatedItems)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@ object SynchronizationQueueSink {
// To avoid a dependency loop of every class to SyncService, and from SyncService back to every class.
private var serviceStarterImpl = Runnable {}

fun needSynch() : Boolean {
return isProviderConnected
}

fun setServiceStarterImpl(serviceStarter: Runnable) {
serviceStarterImpl = serviceStarter
}
Expand Down Expand Up @@ -57,9 +61,9 @@ object SynchronizationQueueSink {

fun enqueueEpisodePlayedIfSyncActive(context: Context, media: EpisodeMedia, completed: Boolean) {
if (!isProviderConnected) return

if (media.episode?.feed == null || media.episode!!.feed!!.isLocalFeed) return
if (media.startPosition < 0 || (!completed && media.startPosition >= media.getPosition())) return

val action = EpisodeAction.Builder(media.episode!!, EpisodeAction.PLAY)
.currentTimestamp()
.started(media.startPosition / 1000)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package ac.mdiq.podcini.playback.base
import ac.mdiq.podcini.playback.service.PlaybackService
import ac.mdiq.podcini.storage.database.Episodes.getEpisodeMedia
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.model.*
Expand Down Expand Up @@ -45,7 +46,7 @@ object InTheatre {
Logd(TAG, "starting curQueue")
var curQueue_ = realm.query(PlayQueue::class).sort("updated", Sort.DESCENDING).first().find()
if (curQueue_ != null) {
curQueue = realm.copyFromRealm(curQueue_)
curQueue = unmanaged(curQueue_)
curQueue.episodes.addAll(realm.copyFromRealm(realm.query(Episode::class, "id IN $0", curQueue.episodeIds)
.find().sortedBy { curQueue.episodeIds.indexOf(it.id) }))
}
Expand All @@ -68,7 +69,7 @@ object InTheatre {

Logd(TAG, "starting curState")
var curState_ = realm.query(CurrentState::class).first().find()
if (curState_ != null) curState = realm.copyFromRealm(curState_)
if (curState_ != null) curState = unmanaged(curState_)
else {
Logd(TAG, "creating new curState")
curState_ = CurrentState()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -312,7 +312,7 @@ class PlaybackService : MediaSessionService() {
(action == AutoDeleteAction.GLOBAL && item?.feed != null && shouldAutoDeleteItem(item!!.feed!!)))
if (playable is EpisodeMedia && shouldAutoDelete && (item?.isFavorite != true || !shouldFavoriteKeepEpisode())) {
item = deleteMediaSync(this@PlaybackService, item!!)
if (shouldDeleteRemoveFromQueue()) removeFromQueueSync(null, null, item!!)
if (shouldDeleteRemoveFromQueue()) removeFromQueueSync(null, item!!)
}
}
if (playable is EpisodeMedia && (ended || skipped || playingNext)) addToHistory(item!!)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@ object UserPreferences {
prefDefaultPage,
prefBackButtonOpensDrawer,

prefFeedFilter,

prefQueueKeepSorted,
prefQueueKeepSortedOrder,
prefDownloadSortedOrder,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,9 @@ import java.util.*
import java.util.concurrent.ExecutionException

object AutoCleanups {
private val TAG: String = AutoCleanups::class.simpleName ?: "Anonymous"

var episodeCleanupValue: Int
private var episodeCleanupValue: Int
get() = appPrefs.getString(UserPreferences.Prefs.prefEpisodeCleanup.name, "" + EPISODE_CLEANUP_NULL)!!.toInt()
set(episodeCleanupValue) {
appPrefs.edit().putString(UserPreferences.Prefs.prefEpisodeCleanup.name, episodeCleanupValue.toString()).apply()
Expand Down Expand Up @@ -102,9 +103,6 @@ object AutoCleanups {
}
return 0
}
companion object {
private val TAG: String = ExceptFavoriteCleanupAlgorithm::class.simpleName ?: "Anonymous"
}
}

/**
Expand Down Expand Up @@ -154,9 +152,6 @@ object AutoCleanups {
public override fun getDefaultCleanupParameter(): Int {
return getNumEpisodesToCleanup(0)
}
companion object {
private val TAG: String = APQueueCleanupAlgorithm::class.simpleName ?: "Anonymous"
}
}

/**
Expand All @@ -174,9 +169,6 @@ object AutoCleanups {
override fun getReclaimableItems(): Int {
return 0
}
companion object {
private val TAG: String = APNullCleanupAlgorithm::class.simpleName ?: "Anonymous"
}
}

/** the number of days after playback to wait before an item is eligible to be cleaned up.
Expand Down Expand Up @@ -232,7 +224,6 @@ object AutoCleanups {
return getNumEpisodesToCleanup(0)
}
companion object {
private val TAG: String = APCleanupAlgorithm::class.simpleName ?: "Anonymous"
private fun minusHours(baseDate: Date, numberOfHours: Int): Date {
val cal = Calendar.getInstance()
cal.time = baseDate
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -127,9 +127,6 @@ object AutoDownloads {
val status = batteryStatus!!.getIntExtra(BatteryManager.EXTRA_STATUS, -1)
return (status == BatteryManager.BATTERY_STATUS_CHARGING || status == BatteryManager.BATTERY_STATUS_FULL)
}
companion object {
private val TAG: String = AutoDownloadAlgorithm::class.simpleName ?: "Anonymous"
}
}

class FeedBasedAutoDLAlgorithm : AutoDownloadAlgorithm() {
Expand All @@ -141,10 +138,10 @@ object AutoDownloads {
val networkShouldAutoDl = (isAutoDownloadAllowed && isEnableAutodownload)
// true if we should auto download based on power status
val powerShouldAutoDl = (deviceCharging(context) || isEnableAutodownloadOnBattery)
Logd(Companion.TAG, "autoDownloadEpisodeMedia prepare $networkShouldAutoDl $powerShouldAutoDl")
Logd(TAG, "autoDownloadEpisodeMedia prepare $networkShouldAutoDl $powerShouldAutoDl")
// we should only auto download if both network AND power are happy
if (networkShouldAutoDl && powerShouldAutoDl) {
Logd(Companion.TAG, "autoDownloadEpisodeMedia Performing auto-dl of undownloaded episodes")
Logd(TAG, "autoDownloadEpisodeMedia Performing auto-dl of undownloaded episodes")
val candidates: MutableSet<Episode> = mutableSetOf()
val queueItems = realm.query(Episode::class).query("id IN $0 AND media.downloaded == false", curQueue.episodeIds).find()
Logd(TAG, "autoDownloadEpisodeMedia add from queue: ${queueItems.size}")
Expand All @@ -155,6 +152,7 @@ object AutoDownloads {
var episodes = mutableListOf<Episode>()
val downloadedCount = getEpisodesCount(EpisodeFilter(EpisodeFilter.States.downloaded.name), f.id)
val allowedDLCount = (f.preferences?.autoDLMaxEpisodes?:0) - downloadedCount
Logd(TAG, "autoDownloadEpisodeMedia ${f.preferences?.autoDLMaxEpisodes} downloadedCount: $downloadedCount allowedDLCount: $allowedDLCount")
if (allowedDLCount > 0) {
var queryString = "feedId == ${f.id} AND isAutoDownloadEnabled == true AND media != nil AND media.downloaded == false"
when (f.preferences?.autoDLPolicy) {
Expand Down Expand Up @@ -225,8 +223,5 @@ object AutoDownloads {
else Logd(TAG, "not auto downloaded networkShouldAutoDl: $networkShouldAutoDl powerShouldAutoDl $powerShouldAutoDl")
}
}
companion object {
private val TAG: String = FeedBasedAutoDLAlgorithm::class.simpleName ?: "Anonymous"
}
}
}
13 changes: 8 additions & 5 deletions app/src/main/kotlin/ac/mdiq/podcini/storage/database/Episodes.kt
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import ac.mdiq.podcini.net.download.serviceinterface.DownloadServiceInterface
import ac.mdiq.podcini.net.feed.LocalFeedUpdater.updateFeed
import ac.mdiq.podcini.net.sync.model.EpisodeAction
import ac.mdiq.podcini.net.sync.queue.SynchronizationQueueSink
import ac.mdiq.podcini.net.sync.queue.SynchronizationQueueSink.needSynch
import ac.mdiq.podcini.playback.base.InTheatre.curQueue
import ac.mdiq.podcini.playback.base.InTheatre.curState
import ac.mdiq.podcini.playback.base.InTheatre.writeNoMediaPlaying
Expand Down Expand Up @@ -99,7 +100,7 @@ object Episodes {
return runOnIOScope {
if (episode.media == null) return@runOnIOScope
val episode_ = deleteMediaSync(context, episode)
if (shouldDeleteRemoveFromQueue()) removeFromQueueSync(null, null, episode_)
if (shouldDeleteRemoveFromQueue()) removeFromQueueSync(null, episode_)
}
}

Expand Down Expand Up @@ -158,9 +159,11 @@ object Episodes {
// Do full update of this feed to get rid of the episode
if (episode.feed != null) updateFeed(episode.feed!!, context.applicationContext, null)
} else {
// Gpodder: queue delete action for synchronization
val action = EpisodeAction.Builder(episode, EpisodeAction.DELETE).currentTimestamp().build()
SynchronizationQueueSink.enqueueEpisodeActionIfSyncActive(context, action)
if (needSynch()) {
// Gpodder: queue delete action for synchronization
val action = EpisodeAction.Builder(episode, EpisodeAction.DELETE).currentTimestamp().build()
SynchronizationQueueSink.enqueueEpisodeActionIfSyncActive(context, action)
}
EventFlow.postEvent(FlowEvent.EpisodeEvent.updated(episode))
}
return episode
Expand Down Expand Up @@ -292,6 +295,6 @@ object Episodes {
}

private fun shouldRemoveFromQueuesMarkPlayed(): Boolean {
return appPrefs.getBoolean(UserPreferences.Prefs.prefRemoveFromQueueMarkedPlayed.name, true)
return appPrefs.getBoolean(Prefs.prefRemoveFromQueueMarkedPlayed.name, true)
}
}
10 changes: 7 additions & 3 deletions app/src/main/kotlin/ac/mdiq/podcini/storage/database/Feeds.kt
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import ac.mdiq.podcini.BuildConfig
import ac.mdiq.podcini.net.download.DownloadError
import ac.mdiq.podcini.net.sync.model.EpisodeAction
import ac.mdiq.podcini.net.sync.queue.SynchronizationQueueSink
import ac.mdiq.podcini.net.sync.queue.SynchronizationQueueSink.needSynch
import ac.mdiq.podcini.preferences.UserPreferences.isAutoDelete
import ac.mdiq.podcini.preferences.UserPreferences.isAutoDeleteLocal
import ac.mdiq.podcini.storage.database.Episodes.deleteEpisodes
Expand Down Expand Up @@ -39,8 +40,11 @@ object Feeds {
private val tags: MutableList<String> = mutableListOf()

@Synchronized
fun getFeedList(fromDB: Boolean = true): List<Feed> {
if (fromDB) return realm.query(Feed::class).find()
fun getFeedList(queryString: String = "", fromDB: Boolean = true): List<Feed> {
if (fromDB) {
return if (queryString.isEmpty()) realm.query(Feed::class).find()
else realm.query(Feed::class, queryString).find()
}
return feedMap.values.toList()
}

Expand Down Expand Up @@ -278,7 +282,7 @@ object Feeds {
""".trimIndent()))
oldItem.identifier = episode.identifier

if (oldItem.isPlayed() && oldItem.media != null) {
if (needSynch() && oldItem.isPlayed() && oldItem.media != null) {
val durs = oldItem.media!!.getDuration() / 1000
val action = EpisodeAction.Builder(oldItem, EpisodeAction.PLAY)
.currentTimestamp()
Expand Down
Loading

0 comments on commit 3c2618a

Please sign in to comment.