6.1.1 commit
This commit is contained in:
parent
76e350ff72
commit
d47bdab971
|
@ -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 = ""
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -110,6 +110,7 @@ class AddFeedFragment : Fragment() {
|
|||
.setPositiveButton("Yes") { dialog, _ ->
|
||||
performRestore(requireContext())
|
||||
dialog.dismiss()
|
||||
parentFragmentManager.popBackStack()
|
||||
}
|
||||
.setNegativeButton("No") { dialog, _ ->
|
||||
dialog.dismiss()
|
||||
|
|
|
@ -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 -> {}
|
||||
}
|
||||
|
|
|
@ -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") })
|
||||
}
|
||||
|
|
|
@ -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") })
|
||||
}
|
||||
|
|
|
@ -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") })
|
||||
}
|
||||
|
|
|
@ -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") })
|
||||
}
|
||||
|
|
|
@ -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") })
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
Loading…
Reference in New Issue