6.1.1 commit

This commit is contained in:
Xilin Jia 2024-07-19 22:41:20 +01:00
parent 76e350ff72
commit d47bdab971
14 changed files with 86 additions and 34 deletions

View File

@ -126,8 +126,8 @@ android {
buildConfig true
}
defaultConfig {
versionCode 3020214
versionName "6.1.0"
versionCode 3020215
versionName "6.1.1"
applicationId "ac.mdiq.podcini.R"
def commit = ""

View File

@ -95,7 +95,8 @@ class OpmlBackupAgent : BackupAgentHelper() {
IOUtils.closeQuietly(writer)
}
}
@OptIn(UnstableApi::class) override fun restoreEntity(data: BackupDataInputStream) {
@OptIn(UnstableApi::class)
override fun restoreEntity(data: BackupDataInputStream) {
Logd(TAG, "Backup restore")
if (OPML_ENTITY_KEY != data.key) {
Logd(TAG, "Unknown entity key: " + data.key)
@ -103,6 +104,7 @@ class OpmlBackupAgent : BackupAgentHelper() {
}
var digester: MessageDigest? = null
var reader: Reader
var linesRead = 0
try {
digester = MessageDigest.getInstance("MD5")
reader = InputStreamReader(DigestInputStream(data, digester), Charset.forName("UTF-8"))
@ -112,25 +114,30 @@ class OpmlBackupAgent : BackupAgentHelper() {
try {
mChecksum = digester?.digest() ?: byteArrayOf()
BufferedReader(reader).use { bufferedReader ->
val tempFile = File.createTempFile("opml_restored", ".tmp", mContext.filesDir)
val tempFile = File(mContext.filesDir, "opml_restored.txt")
// val tempFile = File.createTempFile("opml_restored", ".tmp", mContext.filesDir)
FileWriter(tempFile).use { fileWriter ->
while (true) {
val line = bufferedReader.readLine() ?: break
Logd(TAG, "restoreEntity: $linesRead $line")
linesRead++
fileWriter.write(line)
fileWriter.write(System.lineSeparator()) // Write a newline character
}
}
}
val sharedPreferences = PreferenceManager.getDefaultSharedPreferences(mContext)
with(sharedPreferences.edit()) {
putBoolean(UserPreferences.Prefs.prefOPMLRestore.name, true)
apply()
}
} catch (e: XmlPullParserException) {
Log.e(TAG, "Error while parsing the OPML file", e)
} catch (e: IOException) {
Log.e(TAG, "Failed to restore OPML backup", e)
} finally {
if (linesRead > 0) {
val sharedPreferences = PreferenceManager.getDefaultSharedPreferences(mContext)
with(sharedPreferences.edit()) {
putBoolean(UserPreferences.Prefs.prefOPMLRestore.name, true)
apply()
}
}
IOUtils.closeQuietly(reader)
}
}
@ -166,11 +173,12 @@ class OpmlBackupAgent : BackupAgentHelper() {
private const val OPML_BACKUP_KEY = "opml"
val isOPMLRestared: Boolean
get() = appPrefs.getBoolean(UserPreferences.Prefs.prefOPMLRestore.name, true)
get() = appPrefs.getBoolean(UserPreferences.Prefs.prefOPMLRestore.name, false)
fun performRestore(context: Context) {
Logd(TAG, "performRestore")
val tempFile = File.createTempFile("opml_restored", ".tmp", context.filesDir)
val tempFile = File(context.filesDir, "opml_restored.txt")
// val tempFile = File.createTempFile("opml_restored", ".tmp", context.filesDir)
if (tempFile.exists()) {
val reader = FileReader(tempFile)
val opmlElements = OpmlReader().readDocument(reader)
@ -179,6 +187,7 @@ class OpmlBackupAgent : BackupAgentHelper() {
feed.episodes.clear()
updateFeed(context, feed, false)
}
Toast.makeText(context, "${opmlElements.size} feeds were restored", Toast.LENGTH_SHORT).show()
runOnce(context)
} else {
Toast.makeText(context, "No backup data found", Toast.LENGTH_SHORT).show()

View File

@ -12,6 +12,7 @@ import ac.mdiq.podcini.storage.database.Queues.getInQueueEpisodeIds
import ac.mdiq.podcini.storage.model.Episode
import ac.mdiq.podcini.storage.model.EpisodeFilter
import ac.mdiq.podcini.storage.model.EpisodeSortOrder
import ac.mdiq.podcini.util.Logd
import android.content.Context
import android.util.Log
import androidx.annotation.OptIn
@ -68,7 +69,8 @@ object AutoCleanups {
override fun getReclaimableItems(): Int {
return candidates.size
}
@OptIn(UnstableApi::class) public override fun performCleanup(context: Context, numToRemove: Int): Int {
@OptIn(UnstableApi::class)
public override fun performCleanup(context: Context, numToRemove: Int): Int {
var candidates = candidates
// in the absence of better data, we'll sort by item publication date
candidates = candidates.sortedWith { lhs: Episode, rhs: Episode ->
@ -250,8 +252,12 @@ object AutoCleanups {
* @return The number of episodes that were deleted.
*/
protected abstract fun performCleanup(context: Context, numToRemove: Int): Int
// only used in tests
fun performCleanup(context: Context): Int {
return performCleanup(context, getDefaultCleanupParameter())
val numToRemove = getDefaultCleanupParameter()
if (numToRemove <= 0) return 0
return performCleanup(context, numToRemove)
}
/**
* Returns a parameter for performCleanup. The implementation of this interface should decide how much
@ -266,7 +272,10 @@ object AutoCleanups {
* @return The number of epiosdes that were deleted
*/
fun makeRoomForEpisodes(context: Context, amountOfRoomNeeded: Int): Int {
return performCleanup(context, getNumEpisodesToCleanup(amountOfRoomNeeded))
val numToRemove = getNumEpisodesToCleanup(amountOfRoomNeeded)
Logd("EpisodeCleanupAlgorithm", "makeRoomForEpisodes: $numToRemove")
if (numToRemove <= 0) return 0
return performCleanup(context, numToRemove)
}
/**
* @return the number of episodes/items that *could* be cleaned up, if needed

View File

@ -10,20 +10,17 @@ import ac.mdiq.podcini.preferences.UserPreferences.isEnableAutodownload
import ac.mdiq.podcini.preferences.UserPreferences.isEnableAutodownloadOnBattery
import ac.mdiq.podcini.storage.database.Episodes.getEpisodes
import ac.mdiq.podcini.storage.database.Episodes.getEpisodesCount
import ac.mdiq.podcini.storage.database.Episodes.setPlayState
import ac.mdiq.podcini.storage.database.Feeds.getFeedList
import ac.mdiq.podcini.storage.database.RealmDB.realm
import ac.mdiq.podcini.storage.database.RealmDB.runOnIOScope
import ac.mdiq.podcini.storage.database.RealmDB.upsertBlk
import ac.mdiq.podcini.storage.model.*
import ac.mdiq.podcini.storage.utils.EpisodesPermutors.getPermutor
import ac.mdiq.podcini.util.Logd
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.os.BatteryManager
import androidx.media3.common.util.UnstableApi
import io.realm.kotlin.Realm
import io.realm.kotlin.UpdatePolicy
import java.util.concurrent.ExecutorService
import java.util.concurrent.Executors
@ -149,27 +146,28 @@ object AutoDownloads {
if (networkShouldAutoDl && powerShouldAutoDl) {
Logd(Companion.TAG, "autoDownloadEpisodeMedia Performing auto-dl of undownloaded episodes")
val candidates: MutableSet<Episode> = mutableSetOf()
val queueItems = curQueue.episodes.filter { it.media?.downloaded != true }
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}")
if (queueItems.isNotEmpty()) candidates.addAll(queueItems)
val feeds = feeds ?: getFeedList()
feeds.forEach { f ->
if (f.preferences?.autoDownload == true && !f.isLocalFeed) {
var episodes = mutableListOf<Episode>()
val downloadedCount = getEpisodesCount(EpisodeFilter(EpisodeFilter.States.downloaded.name), f.id)
val toDLCount = (f.preferences?.autoDLMaxEpisodes?:0) - downloadedCount
if (toDLCount > 0) {
val allowedDLCount = (f.preferences?.autoDLMaxEpisodes?:0) - downloadedCount
if (allowedDLCount > 0) {
var queryString = "feedId == ${f.id} AND isAutoDownloadEnabled == true AND media != nil AND media.downloaded == false"
when (f.preferences?.autoDLPolicy) {
FeedPreferences.AutoDLPolicy.ONLY_NEW -> {
queryString += " AND playState == -1 SORT(pubDate DESC) LIMIT(${3*toDLCount})"
queryString += " AND playState == -1 SORT(pubDate DESC) LIMIT(${3*allowedDLCount})"
episodes = realm.query(Episode::class).query(queryString).find().toMutableList()
}
FeedPreferences.AutoDLPolicy.NEWER -> {
queryString += " AND playState != 1 SORT(pubDate DESC) LIMIT(${3*toDLCount})"
queryString += " AND playState != 1 SORT(pubDate DESC) LIMIT(${3*allowedDLCount})"
episodes = realm.query(Episode::class).query(queryString).find().toMutableList()
}
FeedPreferences.AutoDLPolicy.OLDER -> {
queryString += " AND playState != 1 SORT(pubDate ASC) LIMIT(${3*toDLCount})"
queryString += " AND playState != 1 SORT(pubDate ASC) LIMIT(${3*allowedDLCount})"
episodes = realm.query(Episode::class).query(queryString).find().toMutableList()
}
else -> {}
@ -177,9 +175,11 @@ object AutoDownloads {
if (episodes.isNotEmpty()) {
var count = 0
for (e in episodes) {
if (isCurMedia(e.media)) continue
if (f.preferences?.autoDownloadFilter?.shouldAutoDownload(e) == true) {
Logd(TAG, "autoDownloadEpisodeMedia add to cadidates: ${e.title} ${e.isDownloaded}")
candidates.add(e)
if (++count >= toDLCount) break
if (++count >= allowedDLCount) break
} else upsertBlk(e) { it.setPlayed(true)}
}
}

View File

@ -110,6 +110,7 @@ class AddFeedFragment : Fragment() {
.setPositiveButton("Yes") { dialog, _ ->
performRestore(requireContext())
dialog.dismiss()
parentFragmentManager.popBackStack()
}
.setNegativeButton("No") { dialog, _ ->
dialog.dismiss()

View File

@ -324,6 +324,17 @@ class AudioPlayerFragment : Fragment(), SeekBar.OnSeekBarChangeListener, Toolbar
(activity as MainActivity).setPlayerVisible(true)
}
private fun onPlaybackPositionEvent(event: FlowEvent.PlaybackPositionEvent) {
// Logd(TAG, "onPlayEvent ${event.episode.title}")
val media = event.media
if (currentMedia?.getIdentifier() == null || media?.getIdentifier() != currentMedia?.getIdentifier()) {
currentMedia = media
playerDetailsFragment?.setItem(curEpisode!!)
}
playerUI?.onPositionUpdate(event)
if (!isCollapsed) playerDetailsFragment?.onPlaybackPositionEvent(event)
}
private var eventSink: Job? = null
private fun cancelFlowEvents() {
Logd(TAG, "cancelFlowEvents")
@ -346,10 +357,7 @@ class AudioPlayerFragment : Fragment(), SeekBar.OnSeekBarChangeListener, Toolbar
is FlowEvent.FavoritesEvent -> onFavoriteEvent(event)
is FlowEvent.PlayerErrorEvent -> MediaPlayerErrorDialog.show(activity as Activity, event)
is FlowEvent.SleepTimerUpdatedEvent -> if (event.isCancelled || event.wasJustEnabled()) loadMediaInfo(false)
is FlowEvent.PlaybackPositionEvent -> {
playerUI?.onPositionUpdate(event)
if (!isCollapsed) playerDetailsFragment?.onPlaybackPositionEvent(event)
}
is FlowEvent.PlaybackPositionEvent -> onPlaybackPositionEvent(event)
is FlowEvent.SpeedChangedEvent -> playerUI?.updatePlaybackSpeedButton(event)
else -> {}
}

View File

@ -5,6 +5,7 @@ import ac.mdiq.podcini.databinding.BaseEpisodesListFragmentBinding
import ac.mdiq.podcini.databinding.MultiSelectSpeedDialBinding
import ac.mdiq.podcini.net.feed.FeedUpdateManager
import ac.mdiq.podcini.playback.base.InTheatre.isCurMedia
import ac.mdiq.podcini.storage.database.RealmDB.unmanaged
import ac.mdiq.podcini.storage.model.Episode
import ac.mdiq.podcini.storage.model.EpisodeMedia
import ac.mdiq.podcini.storage.model.EpisodeFilter
@ -343,7 +344,8 @@ import kotlinx.coroutines.flow.collectLatest
curIndex else EpisodeUtil.indexOfItemWithId(episodes, item.id)
if (pos >= 0) {
episodes[pos] = item
episodes[pos] = unmanaged(episodes[pos])
episodes[pos].media?.position = event.media.position
curIndex = pos
adapter.notifyItemChanged(pos, Bundle().apply { putString("PositionUpdate", "PlaybackPositionEvent") })
}

View File

@ -10,6 +10,7 @@ import ac.mdiq.podcini.preferences.UserPreferences
import ac.mdiq.podcini.preferences.UserPreferences.appPrefs
import ac.mdiq.podcini.storage.database.Episodes.getEpisodes
import ac.mdiq.podcini.storage.database.RealmDB.realm
import ac.mdiq.podcini.storage.database.RealmDB.unmanaged
import ac.mdiq.podcini.storage.model.Episode
import ac.mdiq.podcini.storage.model.EpisodeFilter
import ac.mdiq.podcini.storage.model.EpisodeMedia
@ -310,7 +311,8 @@ import java.util.*
curIndex else EpisodeUtil.indexOfItemWithId(episodes, item.id)
if (pos >= 0) {
episodes[pos] = item
episodes[pos] = unmanaged(episodes[pos])
episodes[pos].media?.position = event.media.position
curIndex = pos
adapter.notifyItemChanged(pos, Bundle().apply { putString("PositionUpdate", "PlaybackPositionEvent") })
}

View File

@ -421,7 +421,8 @@ import java.util.concurrent.Semaphore
curIndex else EpisodeUtil.indexOfItemWithId(episodes, item.id)
if (pos >= 0) {
episodes[pos] = item
episodes[pos] = unmanaged(episodes[pos])
episodes[pos].media?.position = event.media.position
curIndex = pos
adapter.notifyItemChanged(pos, Bundle().apply { putString("PositionUpdate", "PlaybackPositionEvent") })
}

View File

@ -17,6 +17,7 @@ import ac.mdiq.podcini.storage.database.Queues.moveInQueueSync
import ac.mdiq.podcini.storage.database.Queues.queueKeepSortedOrder
import ac.mdiq.podcini.storage.database.RealmDB.realm
import ac.mdiq.podcini.storage.database.RealmDB.runOnIOScope
import ac.mdiq.podcini.storage.database.RealmDB.unmanaged
import ac.mdiq.podcini.storage.database.RealmDB.upsert
import ac.mdiq.podcini.storage.model.Episode
import ac.mdiq.podcini.storage.model.EpisodeFilter
@ -337,7 +338,8 @@ import java.util.*
curIndex else EpisodeUtil.indexOfItemWithId(queueItems, item.id)
if (pos >= 0) {
queueItems[pos] = item
queueItems[pos] = unmanaged(queueItems[pos])
queueItems[pos].media?.position = event.media.position
curIndex = pos
adapter?.notifyItemChanged(pos, Bundle().apply { putString("PositionUpdate", "PlaybackPositionEvent") })
}

View File

@ -8,6 +8,7 @@ import ac.mdiq.podcini.databinding.SearchFragmentBinding
import ac.mdiq.podcini.net.feed.discovery.CombinedSearcher
import ac.mdiq.podcini.playback.base.InTheatre.isCurMedia
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.EpisodeMedia
import ac.mdiq.podcini.storage.model.Feed
@ -300,7 +301,8 @@ import java.lang.ref.WeakReference
curIndex else EpisodeUtil.indexOfItemWithId(results, item.id)
if (pos >= 0) {
results[pos] = item
results[pos] = unmanaged(results[pos])
results[pos].media?.position = event.media.position
curIndex = pos
adapter.notifyItemChanged(pos, Bundle().apply { putString("PositionUpdate", "PlaybackPositionEvent") })
}

View File

@ -1,3 +1,11 @@
# 6.1.1
* fixed player UI not updating on change of episode
* fixed the mal-function of restoring previously backed-up OPML
* reduced reactions to PlaybackPositionEvent
* tuned AutoCleanup a bit
* tuned and fixed some some issues in audo-downloaded
# 6.1.0
* in FeedEpisode view fixed filtering after an episode's play state is changed

View File

@ -1,5 +1,5 @@
Version 6.0.14 brings several changes:
Version 6.1.0 brings several changes:
* in FeedEpisode view fixed filtering after an episode's play state is changed
* fixed refreshing a feed causes duplicate episodes in FeedEpisodes view

View File

@ -0,0 +1,8 @@
Version 6.1.1 brings several changes:
* fixed player UI not updating on change of episode
* fixed the mal-function of restoring previously backed-up OPML
* reduced reactions to PlaybackPositionEvent
* tuned AutoCleanup a bit
* tuned and fixed some some issues in audo-downloaded