6.0.8 commit

This commit is contained in:
Xilin Jia 2024-07-10 22:01:01 +01:00
parent 8eee3644a8
commit 0f99aa2dd7
66 changed files with 370 additions and 232 deletions

View File

@ -125,8 +125,8 @@ android {
buildConfig true buildConfig true
} }
defaultConfig { defaultConfig {
versionCode 3020207 versionCode 3020208
versionName "6.0.7" versionName "6.0.8"
applicationId "ac.mdiq.podcini.R" applicationId "ac.mdiq.podcini.R"
def commit = "" def commit = ""

View File

@ -52,7 +52,7 @@ class DownloadServiceInterfaceImpl : DownloadServiceInterface() {
override fun downloadNow(context: Context, item: Episode, ignoreConstraints: Boolean) { override fun downloadNow(context: Context, item: Episode, ignoreConstraints: Boolean) {
Logd(TAG, "starting downloadNow") Logd(TAG, "starting downloadNow")
val workRequest: OneTimeWorkRequest.Builder = getRequest(context, item) val workRequest: OneTimeWorkRequest.Builder = getRequest(item)
workRequest.setExpedited(OutOfQuotaPolicy.RUN_AS_NON_EXPEDITED_WORK_REQUEST) workRequest.setExpedited(OutOfQuotaPolicy.RUN_AS_NON_EXPEDITED_WORK_REQUEST)
if (ignoreConstraints) workRequest.setConstraints(Builder().setRequiredNetworkType(NetworkType.CONNECTED).build()) if (ignoreConstraints) workRequest.setConstraints(Builder().setRequiredNetworkType(NetworkType.CONNECTED).build())
else workRequest.setConstraints(constraints) else workRequest.setConstraints(constraints)
@ -63,7 +63,7 @@ class DownloadServiceInterfaceImpl : DownloadServiceInterface() {
override fun download(context: Context, item: Episode) { override fun download(context: Context, item: Episode) {
Logd(TAG, "starting download") Logd(TAG, "starting download")
val workRequest: OneTimeWorkRequest.Builder = getRequest(context, item) val workRequest: OneTimeWorkRequest.Builder = getRequest(item)
workRequest.setConstraints(constraints) workRequest.setConstraints(constraints)
if (item.media?.downloadUrl != null) if (item.media?.downloadUrl != null)
WorkManager.getInstance(context).enqueueUniqueWork(item.media!!.downloadUrl!!, ExistingWorkPolicy.KEEP, workRequest.build()) WorkManager.getInstance(context).enqueueUniqueWork(item.media!!.downloadUrl!!, ExistingWorkPolicy.KEEP, workRequest.build())
@ -108,7 +108,7 @@ class DownloadServiceInterfaceImpl : DownloadServiceInterface() {
return constraints.build() return constraints.build()
} }
@OptIn(UnstableApi::class) private fun getRequest(context: Context, item: Episode): OneTimeWorkRequest.Builder { @OptIn(UnstableApi::class) private fun getRequest(item: Episode): OneTimeWorkRequest.Builder {
Logd(TAG, "starting getRequest") Logd(TAG, "starting getRequest")
val workRequest: OneTimeWorkRequest.Builder = OneTimeWorkRequest.Builder(EpisodeDownloadWorker::class.java) val workRequest: OneTimeWorkRequest.Builder = OneTimeWorkRequest.Builder(EpisodeDownloadWorker::class.java)
.setInitialDelay(0L, TimeUnit.MILLISECONDS) .setInitialDelay(0L, TimeUnit.MILLISECONDS)
@ -208,7 +208,7 @@ class DownloadServiceInterfaceImpl : DownloadServiceInterface() {
if (dest.exists()) { if (dest.exists()) {
try { try {
media.fileUrl = request.destination media.setfileUrlOrNull(request.destination)
Episodes.persistEpisodeMedia(media) Episodes.persistEpisodeMedia(media)
} catch (e: Exception) { } catch (e: Exception) {
Log.e(TAG, "performDownload Exception in writeFileUrl: " + e.message) Log.e(TAG, "performDownload Exception in writeFileUrl: " + e.message)
@ -353,8 +353,10 @@ class DownloadServiceInterfaceImpl : DownloadServiceInterface() {
} }
// media.setDownloaded modifies played state // media.setDownloaded modifies played state
val broadcastUnreadStateUpdate = media.episode != null && media.episode!!.isNew val broadcastUnreadStateUpdate = media.episode != null && media.episode!!.isNew
media.downloaded = true // media.downloaded = true
media.fileUrl = request.destination media.setIsDownloaded()
Logd(TAG, "media.episode.isNew: ${media.episode?.isNew} ${media.episode?.playState}")
media.setfileUrlOrNull(request.destination)
if (request.destination != null) media.size = File(request.destination).length() if (request.destination != null) media.size = File(request.destination).length()
media.checkEmbeddedPicture() // enforce check media.checkEmbeddedPicture() // enforce check
@ -388,7 +390,7 @@ class DownloadServiceInterfaceImpl : DownloadServiceInterface() {
// we've received the media, we don't want to autodownload it again // we've received the media, we don't want to autodownload it again
if (item != null) { if (item != null) {
item.disableAutoDownload() item.disableAutoDownload()
Logd(TAG, "persisting episode downloaded ${item.title} ${item.media?.fileUrl} ${item.media?.downloaded}") Logd(TAG, "persisting episode downloaded ${item.title} ${item.media?.fileUrl} ${item.media?.downloaded} ${item.isNew}")
// setFeedItem() signals that the item has been updated, // setFeedItem() signals that the item has been updated,
// so we do it after the enclosing media has been updated above, // so we do it after the enclosing media has been updated above,
// to ensure subscribers will get the updated EpisodeMedia as well // to ensure subscribers will get the updated EpisodeMedia as well

View File

@ -550,13 +550,14 @@ class PlaybackService : MediaSessionService() {
} }
override fun onDestroy() { override fun onDestroy() {
super.onDestroy()
Logd(TAG, "Service is about to be destroyed") Logd(TAG, "Service is about to be destroyed")
isRunning = false isRunning = false
currentMediaType = MediaType.UNKNOWN currentMediaType = MediaType.UNKNOWN
castStateListener.destroy() castStateListener.destroy()
currentitem = null
LocalMediaPlayer.cleanup() LocalMediaPlayer.cleanup()
mediaSession?.run { mediaSession?.run {
player.release() player.release()
@ -573,6 +574,8 @@ class PlaybackService : MediaSessionService() {
unregisterReceiver(bluetoothStateUpdated) unregisterReceiver(bluetoothStateUpdated)
unregisterReceiver(audioBecomingNoisy) unregisterReceiver(audioBecomingNoisy)
taskManager.shutdown() taskManager.shutdown()
super.onDestroy()
} }
fun isServiceReady(): Boolean { fun isServiceReady(): Boolean {

View File

@ -605,8 +605,8 @@ class SynchronizationPreferencesFragment : PreferenceFragmentCompat() {
} }
override fun onDestroy() { override fun onDestroy() {
super.onDestroy()
cancelFlowEvents() cancelFlowEvents()
super.onDestroy()
} }
@OptIn(UnstableApi::class) override fun onResume() { @OptIn(UnstableApi::class) override fun onResume() {

View File

@ -108,6 +108,7 @@ object Episodes {
Logd(TAG, String.format(Locale.US, "Requested to delete EpisodeMedia [id=%d, title=%s, downloaded=%s", media.id, media.getEpisodeTitle(), media.downloaded)) Logd(TAG, String.format(Locale.US, "Requested to delete EpisodeMedia [id=%d, title=%s, downloaded=%s", media.id, media.getEpisodeTitle(), media.downloaded))
var localDelete = false var localDelete = false
val url = media.fileUrl val url = media.fileUrl
var episode = episode
when { when {
url != null && url.startsWith("content://") -> { url != null && url.startsWith("content://") -> {
// Local feed // Local feed
@ -116,8 +117,8 @@ object Episodes {
EventFlow.postEvent(FlowEvent.MessageEvent(context.getString(R.string.delete_local_failed))) EventFlow.postEvent(FlowEvent.MessageEvent(context.getString(R.string.delete_local_failed)))
return episode return episode
} }
upsertBlk(episode) { episode = upsertBlk(episode) {
it.media?.fileUrl = null it.media?.setfileUrlOrNull(null)
if (media.downloadUrl.isNullOrEmpty()) it.media = null if (media.downloadUrl.isNullOrEmpty()) it.media = null
} }
localDelete = true localDelete = true
@ -131,9 +132,9 @@ object Episodes {
EventFlow.postEvent(evt) EventFlow.postEvent(evt)
return episode return episode
} }
upsertBlk(episode) { episode = upsertBlk(episode) {
it.media?.downloaded = false it.media?.downloaded = false
it.media?.fileUrl = null it.media?.setfileUrlOrNull(null)
it.media?.hasEmbeddedPicture = false it.media?.hasEmbeddedPicture = false
if (media.downloadUrl.isNullOrEmpty()) it.media = null if (media.downloadUrl.isNullOrEmpty()) it.media = null
} }
@ -209,10 +210,10 @@ object Episodes {
fun persistEpisodeMedia(media: EpisodeMedia) : Job { fun persistEpisodeMedia(media: EpisodeMedia) : Job {
Logd(TAG, "persistEpisodeMedia called") Logd(TAG, "persistEpisodeMedia called")
return runOnIOScope { return runOnIOScope {
val episode = media.episode var episode = media.episode
if (episode != null) { if (episode != null) {
episode.media = media episode.media = media
upsert(episode) {} episode = upsert(episode) {}
EventFlow.postEvent(FlowEvent.EpisodeEvent.updated(episode)) EventFlow.postEvent(FlowEvent.EpisodeEvent.updated(episode))
} else Log.e(TAG, "persistEpisodeMedia media.episode is null") } else Log.e(TAG, "persistEpisodeMedia media.episode is null")
} }

View File

@ -45,7 +45,7 @@ object RealmDB {
if (BuildConfig.DEBUG) { if (BuildConfig.DEBUG) {
val stackTrace = Thread.currentThread().stackTrace val stackTrace = Thread.currentThread().stackTrace
val caller = if (stackTrace.size > 3) stackTrace[3] else null val caller = if (stackTrace.size > 3) stackTrace[3] else null
Logd(TAG, "${caller?.className}.${caller?.methodName} upsert: ${entity.javaClass.simpleName}") Logd(TAG, "${caller?.className}.${caller?.methodName} unmanaged: ${entity.javaClass.simpleName}")
} }
return if (entity.isManaged()) realm.copyFromRealm(entity) else entity return if (entity.isManaged()) realm.copyFromRealm(entity) else entity
} }

View File

@ -19,18 +19,19 @@ class EpisodeMedia: EmbeddedRealmObject, Playable {
var id: Long = 0L // same as the episode id var id: Long = 0L // same as the episode id
var fileUrl: String? = null var fileUrl: String? = null
set(value) { // set(value) {
field = value // field = value
if (value == null) downloaded = false // if (value == null) downloaded = false
} // }
var downloadUrl: String? = null var downloadUrl: String? = null
var downloaded: Boolean = false var downloaded: Boolean = false
set(value) { // set(value) {
field = value // Logd(TAG, "setting downloaded: $value ${episode?.isNew}")
if (value && episode?.isNew == true) episode!!.setPlayed(false) // field = value
} // if (value && episode?.isNew == true) episode!!.setPlayed(false)
// }
@get:JvmName("getDurationProperty") @get:JvmName("getDurationProperty")
@set:JvmName("setDurationProperty") @set:JvmName("setDurationProperty")
@ -88,9 +89,8 @@ class EpisodeMedia: EmbeddedRealmObject, Playable {
this.episode = i this.episode = i
this.size = size this.size = size
this.mimeType = mime_type this.mimeType = mime_type
fileUrl = null setfileUrlOrNull(null)
this.downloadUrl = download_url this.downloadUrl = download_url
downloaded = false
} }
constructor(id: Long, item: Episode?, duration: Int, position: Int, constructor(id: Long, item: Episode?, duration: Int, position: Int,
@ -108,20 +108,12 @@ class EpisodeMedia: EmbeddedRealmObject, Playable {
this.playbackCompletionDate = playbackCompletionDate?.clone() as? Date this.playbackCompletionDate = playbackCompletionDate?.clone() as? Date
this.playbackCompletionTime = playbackCompletionDate?.time ?: 0 this.playbackCompletionTime = playbackCompletionDate?.time ?: 0
this.lastPlayedTime = lastPlayedTime this.lastPlayedTime = lastPlayedTime
this.fileUrl = file_url setfileUrlOrNull(file_url)
this.downloadUrl = download_url this.downloadUrl = download_url
this.downloaded = downloaded if (downloaded) setIsDownloaded()
else this.downloaded = downloaded
} }
// constructor(id: Long, item: Episode?, duration: Int, position: Int,
// size: Long, mime_type: String?, file_url: String?, download_url: String?,
// downloaded: Boolean, playbackCompletionDate: Date?, played_duration: Int,
// hasEmbeddedPicture: Boolean?, lastPlayedTime: Long)
// : this(id, item, duration, position, size, mime_type, file_url, download_url, downloaded, playbackCompletionDate, played_duration, lastPlayedTime) {
//
// this.hasEmbeddedPicture = hasEmbeddedPicture
// }
fun getHumanReadableIdentifier(): String? { fun getHumanReadableIdentifier(): String? {
return if (episode?.title != null) episode!!.title else downloadUrl return if (episode?.title != null) episode!!.title else downloadUrl
} }
@ -158,6 +150,16 @@ class EpisodeMedia: EmbeddedRealmObject, Playable {
return FEEDFILETYPE_FEEDMEDIA return FEEDFILETYPE_FEEDMEDIA
} }
fun setIsDownloaded() {
downloaded = true
if (episode?.isNew == true) episode!!.setPlayed(false)
}
fun setfileUrlOrNull(url: String?) {
fileUrl = url
if (url == null) downloaded = false
}
override fun getDuration(): Int { override fun getDuration(): Int {
return duration return duration
} }

View File

@ -40,10 +40,10 @@ class Feed : RealmObject {
*/ */
@FullText @FullText
var customTitle: String? = null var customTitle: String? = null
set(value) { // set(value) {
field = if (value == null || value == eigenTitle) null // field = if (value == null || value == eigenTitle) null
else value // else value
} // }
var link: String? = null var link: String? = null
@ -163,7 +163,7 @@ class Feed : RealmObject {
this.fileUrl = fileUrl this.fileUrl = fileUrl
this.downloadUrl = downloadUrl this.downloadUrl = downloadUrl
this.eigenTitle = title this.eigenTitle = title
this.customTitle = customTitle setCustomTitle1(customTitle)
this.lastUpdate = lastUpdate this.lastUpdate = lastUpdate
this.link = link this.link = link
this.description = description this.description = description
@ -210,6 +210,10 @@ class Feed : RealmObject {
preferences = FeedPreferences(0, false, FeedPreferences.AutoDeleteAction.GLOBAL, VolumeAdaptionSetting.OFF, username, password) preferences = FeedPreferences(0, false, FeedPreferences.AutoDeleteAction.GLOBAL, VolumeAdaptionSetting.OFF, username, password)
} }
fun setCustomTitle1(value: String?) {
customTitle = if (value == null || value == eigenTitle) null else value
}
fun getTextIdentifier(): String? { fun getTextIdentifier(): String? {
return when { return when {
!customTitle.isNullOrEmpty() -> customTitle !customTitle.isNullOrEmpty() -> customTitle

View File

@ -246,5 +246,4 @@ object ChapterUtils {
} }
return listOf() return listOf()
} }
} }

View File

@ -36,7 +36,7 @@ class EpisodeMultiSelectHandler(private val activity: MainActivity, private val
showMessage(R.plurals.marked_unread_batch_label, items.size) showMessage(R.plurals.marked_unread_batch_label, items.size)
} }
R.id.download_batch -> downloadChecked(items) R.id.download_batch -> downloadChecked(items)
R.id.delete_batch -> LocalDeleteModal.showLocalFeedDeleteWarningIfNecessary(activity, items) { deleteChecked(items) } R.id.delete_batch -> deleteChecked(items)
else -> Log.e(TAG, "Unrecognized speed dial action item. Do nothing. id=$actionId") else -> Log.e(TAG, "Unrecognized speed dial action item. Do nothing. id=$actionId")
} }
} }
@ -57,18 +57,6 @@ class EpisodeMultiSelectHandler(private val activity: MainActivity, private val
showMessage(R.plurals.removed_from_queue_batch_label, checkedIds.size) showMessage(R.plurals.removed_from_queue_batch_label, checkedIds.size)
} }
// private fun markedCheckedPlayed(items: List<FeedItem>) {
//// val checkedIds = getSelectedIds(items)
// DBWriter.markItemsPlayed(FeedItem.PLAYED, *items.toTypedArray())
// showMessage(R.plurals.marked_read_batch_label, items.size)
// }
//
// private fun markedCheckedUnplayed(items: List<FeedItem>) {
//// val checkedIds = getSelectedIds(items)
// DBWriter.markItemsPlayed(FeedItem.UNPLAYED, *items.toTypedArray())
// showMessage(R.plurals.marked_unread_batch_label, items.size)
// }
private fun markFavorite(items: List<Episode>, stat: Boolean) { private fun markFavorite(items: List<Episode>, stat: Boolean) {
for (item in items) { for (item in items) {
Episodes.setFavorite(item, true) Episodes.setFavorite(item, true)
@ -85,14 +73,8 @@ class EpisodeMultiSelectHandler(private val activity: MainActivity, private val
} }
private fun deleteChecked(items: List<Episode>) { private fun deleteChecked(items: List<Episode>) {
var countHasMedia = 0 LocalDeleteModal.deleteEpisodesWarnLocal(activity, items)
for (feedItem in items) { showMessage(R.plurals.deleted_multi_episode_batch_label, items.size)
if (feedItem.media != null && feedItem.media!!.downloaded) {
countHasMedia++
deleteMediaOfEpisode(activity, feedItem)
}
}
showMessage(R.plurals.deleted_multi_episode_batch_label, countHasMedia)
} }
private fun showMessage(@PluralsRes msgId: Int, numItems: Int) { private fun showMessage(@PluralsRes msgId: Int, numItems: Int) {

View File

@ -1,11 +1,10 @@
package ac.mdiq.podcini.ui.actions.actionbutton package ac.mdiq.podcini.ui.actions.actionbutton
import ac.mdiq.podcini.R
import ac.mdiq.podcini.storage.model.Episode
import ac.mdiq.podcini.ui.utils.LocalDeleteModal.deleteEpisodesWarnLocal
import android.content.Context import android.content.Context
import android.view.View import android.view.View
import ac.mdiq.podcini.R
import ac.mdiq.podcini.storage.database.Episodes.deleteMediaOfEpisode
import ac.mdiq.podcini.storage.model.Episode
import ac.mdiq.podcini.ui.utils.LocalDeleteModal.showLocalFeedDeleteWarningIfNecessary
import androidx.media3.common.util.UnstableApi import androidx.media3.common.util.UnstableApi
class DeleteActionButton(item: Episode) : EpisodeActionButton(item) { class DeleteActionButton(item: Episode) : EpisodeActionButton(item) {
@ -16,7 +15,7 @@ class DeleteActionButton(item: Episode) : EpisodeActionButton(item) {
return R.drawable.ic_delete return R.drawable.ic_delete
} }
@UnstableApi override fun onClick(context: Context) { @UnstableApi override fun onClick(context: Context) {
showLocalFeedDeleteWarningIfNecessary(context, listOf(item)) { deleteMediaOfEpisode(context, item) } deleteEpisodesWarnLocal(context, listOf(item))
} }
override val visibility: Int override val visibility: Int

View File

@ -132,7 +132,8 @@ class TTSActionButton(item: Episode) : EpisodeActionButton(item) {
Logd(TAG, "saving TTS to file $mFilename") Logd(TAG, "saving TTS to file $mFilename")
val media = EpisodeMedia(item, null, 0, "audio/*") val media = EpisodeMedia(item, null, 0, "audio/*")
media.fileUrl = mFilename media.fileUrl = mFilename
media.downloaded = true // media.downloaded = true
media.setIsDownloaded()
item.media = media item.media = media
// DBWriter.persistFeedMedia(media) // DBWriter.persistFeedMedia(media)
item.setTranscriptIfLonger(readerText) item.setTranscriptIfLonger(readerText)

View File

@ -137,9 +137,7 @@ object EpisodeMenuHandler {
when (menuItemId) { when (menuItemId) {
R.id.skip_episode_item -> context.sendBroadcast(MediaButtonReceiver.createIntent(context, KeyEvent.KEYCODE_MEDIA_NEXT)) R.id.skip_episode_item -> context.sendBroadcast(MediaButtonReceiver.createIntent(context, KeyEvent.KEYCODE_MEDIA_NEXT))
R.id.remove_item -> { R.id.remove_item -> {
LocalDeleteModal.showLocalFeedDeleteWarningIfNecessary(context, listOf(selectedItem)) { LocalDeleteModal.deleteEpisodesWarnLocal(context, listOf(selectedItem))
if (selectedItem.media != null) deleteMediaOfEpisode(context, selectedItem)
}
} }
R.id.mark_read_item -> { R.id.mark_read_item -> {
selectedItem.setPlayed(true) selectedItem.setPlayed(true)

View File

@ -1,13 +1,12 @@
package ac.mdiq.podcini.ui.actions.swipeactions package ac.mdiq.podcini.ui.actions.swipeactions
import ac.mdiq.podcini.R
import ac.mdiq.podcini.storage.model.Episode
import ac.mdiq.podcini.storage.model.EpisodeFilter
import ac.mdiq.podcini.ui.utils.LocalDeleteModal.deleteEpisodesWarnLocal
import android.content.Context import android.content.Context
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import androidx.media3.common.util.UnstableApi import androidx.media3.common.util.UnstableApi
import ac.mdiq.podcini.R
import ac.mdiq.podcini.storage.database.Episodes.deleteMediaOfEpisode
import ac.mdiq.podcini.storage.model.Episode
import ac.mdiq.podcini.storage.model.EpisodeFilter
import ac.mdiq.podcini.ui.utils.LocalDeleteModal.showLocalFeedDeleteWarningIfNecessary
class DeleteSwipeAction : SwipeAction { class DeleteSwipeAction : SwipeAction {
override fun getId(): String { override fun getId(): String {
@ -29,9 +28,7 @@ class DeleteSwipeAction : SwipeAction {
@UnstableApi override fun performAction(item: Episode, fragment: Fragment, filter: EpisodeFilter) { @UnstableApi override fun performAction(item: Episode, fragment: Fragment, filter: EpisodeFilter) {
if (!item.isDownloaded && item.feed?.isLocalFeed != true) return if (!item.isDownloaded && item.feed?.isLocalFeed != true) return
showLocalFeedDeleteWarningIfNecessary(fragment.requireContext(), listOf(item)) { deleteEpisodesWarnLocal(fragment.requireContext(), listOf(item))
deleteMediaOfEpisode(fragment.requireContext(), item)
}
} }
override fun willRemove(filter: EpisodeFilter, item: Episode): Boolean { override fun willRemove(filter: EpisodeFilter, item: Episode): Boolean {

View File

@ -70,8 +70,8 @@ class BugReportActivity : AppCompatActivity() {
} }
override fun onDestroy() { override fun onDestroy() {
super.onDestroy()
_binding = null _binding = null
super.onDestroy()
} }
override fun onCreateOptionsMenu(menu: Menu): Boolean { override fun onCreateOptionsMenu(menu: Menu): Boolean {

View File

@ -364,11 +364,11 @@ class MainActivity : CastEnabledActivity() {
override fun onDestroy() { override fun onDestroy() {
Logd(TAG, "onDestroy") Logd(TAG, "onDestroy")
super.onDestroy()
// WorkManager.getInstance(this).pruneWork() // WorkManager.getInstance(this).pruneWork()
_binding = null _binding = null
// realm.close() // realm.close()
drawerLayout?.removeDrawerListener(drawerToggle!!) drawerLayout?.removeDrawerListener(drawerToggle!!)
super.onDestroy()
} }
private fun checkFirstLaunch() { private fun checkFirstLaunch() {
@ -391,6 +391,7 @@ class MainActivity : CastEnabledActivity() {
} }
fun setPlayerVisible(visible_: Boolean?) { fun setPlayerVisible(visible_: Boolean?) {
Logd(TAG, "setPlayerVisible $visible_")
val visible = visible_ ?: (bottomSheet.state != BottomSheetBehavior.STATE_COLLAPSED) val visible = visible_ ?: (bottomSheet.state != BottomSheetBehavior.STATE_COLLAPSED)
bottomSheet.setLocked(!visible) bottomSheet.setLocked(!visible)

View File

@ -251,8 +251,8 @@ class OpmlImportActivity : AppCompatActivity() {
} }
override fun onDestroy() { override fun onDestroy() {
super.onDestroy()
_binding = null _binding = null
super.onDestroy()
} }
companion object { companion object {

View File

@ -139,8 +139,8 @@ class PreferenceActivity : AppCompatActivity(), SearchPreferenceResultListener {
} }
override fun onDestroy() { override fun onDestroy() {
super.onDestroy()
_binding = null _binding = null
super.onDestroy()
} }
private var eventSink: Job? = null private var eventSink: Job? = null

View File

@ -138,8 +138,8 @@ class SelectSubscriptionActivity : AppCompatActivity() {
} }
override fun onDestroy() { override fun onDestroy() {
super.onDestroy()
_binding = null _binding = null
super.onDestroy()
} }
companion object { companion object {

View File

@ -21,6 +21,8 @@ import ac.mdiq.podcini.ui.dialog.ShareDialog
import ac.mdiq.podcini.ui.dialog.SleepTimerDialog import ac.mdiq.podcini.ui.dialog.SleepTimerDialog
import ac.mdiq.podcini.ui.dialog.VariableSpeedDialog import ac.mdiq.podcini.ui.dialog.VariableSpeedDialog
import ac.mdiq.podcini.ui.fragment.ChaptersFragment import ac.mdiq.podcini.ui.fragment.ChaptersFragment
import ac.mdiq.podcini.ui.fragment.SubscriptionsFragment
import ac.mdiq.podcini.ui.fragment.SubscriptionsFragment.Companion
import ac.mdiq.podcini.ui.fragment.VideoEpisodeFragment import ac.mdiq.podcini.ui.fragment.VideoEpisodeFragment
import ac.mdiq.podcini.ui.utils.PictureInPictureUtil import ac.mdiq.podcini.ui.utils.PictureInPictureUtil
import ac.mdiq.podcini.util.IntentUtils.openInBrowser import ac.mdiq.podcini.util.IntentUtils.openInBrowser
@ -136,12 +138,12 @@ class VideoplayerActivity : CastEnabledActivity() {
} }
override fun onDestroy() { override fun onDestroy() {
super.onDestroy()
window.clearFlags(WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN) window.clearFlags(WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN)
window.clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON) window.clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
window.clearFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN) window.clearFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN)
window.clearFlags(WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN) window.clearFlags(WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN)
_binding = null _binding = null
super.onDestroy()
} }
@ -419,8 +421,9 @@ class VideoplayerActivity : CastEnabledActivity() {
} }
override fun onDestroyView() { override fun onDestroyView() {
super.onDestroyView() Logd(TAG, "onDestroyView")
_binding = null _binding = null
super.onDestroyView()
} }
@UnstableApi private fun setupAudioTracks() { @UnstableApi private fun setupAudioTracks() {

View File

@ -89,9 +89,9 @@ class WidgetConfigActivity : AppCompatActivity() {
} }
override fun onDestroy() { override fun onDestroy() {
super.onDestroy()
_binding = null _binding = null
_wpBinding = null _wpBinding = null
super.onDestroy()
} }
private fun setInitialState() { private fun setInitialState() {

View File

@ -14,7 +14,6 @@ import android.app.Activity
import android.os.Bundle import android.os.Bundle
import android.view.* import android.view.*
import androidx.media3.common.util.UnstableApi import androidx.media3.common.util.UnstableApi
import java.lang.ref.WeakReference import java.lang.ref.WeakReference
/** /**
@ -51,6 +50,12 @@ open class EpisodesAdapter(mainActivity: MainActivity)
setHasStableIds(true) setHasStableIds(true)
} }
fun clearData() {
episodes = listOf()
feed = null
notifyDataSetChanged()
}
fun setDummyViews(dummyViews: Int) { fun setDummyViews(dummyViews: Int) {
this.dummyViews = dummyViews this.dummyViews = dummyViews
notifyDataSetChanged() notifyDataSetChanged()
@ -90,8 +95,7 @@ open class EpisodesAdapter(mainActivity: MainActivity)
val item: Episode = unmanaged(episodes[pos]) val item: Episode = unmanaged(episodes[pos])
// val item: Episode = episodes[pos] // val item: Episode = episodes[pos]
if (feed != null) item.feed = feed item.feed = feed ?: episodes[pos].feed
else item.feed = episodes[pos].feed
holder.bind(item) holder.bind(item)
// holder.infoCard.setOnCreateContextMenuListener(this) // holder.infoCard.setOnCreateContextMenuListener(this)

View File

@ -17,7 +17,7 @@ class OnlineFeedsAdapter(private val context: Context, objects: List<PodcastSear
: ArrayAdapter<PodcastSearchResult?>(context, 0, objects) { : ArrayAdapter<PodcastSearchResult?>(context, 0, objects) {
// List holding the podcasts found in the search // List holding the podcasts found in the search
private val data: List<PodcastSearchResult> = objects private val data: MutableList<PodcastSearchResult> = objects.toMutableList()
@UnstableApi override fun getView(position: Int, convertView: View?, parent: ViewGroup): View { @UnstableApi override fun getView(position: Int, convertView: View?, parent: ViewGroup): View {
val podcast: PodcastSearchResult = data[position] val podcast: PodcastSearchResult = data[position]
@ -65,6 +65,10 @@ class OnlineFeedsAdapter(private val context: Context, objects: List<PodcastSear
return view return view
} }
fun clearData() {
data.clear()
}
internal class PodcastViewHolder(view: View) { internal class PodcastViewHolder(view: View) {
val binding = OnlinePodcastListitemBinding.bind(view) val binding = OnlinePodcastListitemBinding.bind(view)

View File

@ -31,7 +31,7 @@ class CustomFeedNameDialog(activity: Activity, private var feed: Feed) {
.setPositiveButton(android.R.string.ok) { _: DialogInterface?, _: Int -> .setPositiveButton(android.R.string.ok) { _: DialogInterface?, _: Int ->
feed = unmanaged(feed) feed = unmanaged(feed)
val newTitle = binding.editText.text.toString() val newTitle = binding.editText.text.toString()
feed.customTitle = newTitle feed.setCustomTitle1(newTitle)
upsertBlk(feed) {} upsertBlk(feed) {}
} }
.setNeutralButton(R.string.reset, null) .setNeutralButton(R.string.reset, null)

View File

@ -4,6 +4,7 @@ import ac.mdiq.podcini.R
import ac.mdiq.podcini.databinding.FilterDialogBinding import ac.mdiq.podcini.databinding.FilterDialogBinding
import ac.mdiq.podcini.databinding.FilterDialogRowBinding import ac.mdiq.podcini.databinding.FilterDialogRowBinding
import ac.mdiq.podcini.storage.model.EpisodeFilter import ac.mdiq.podcini.storage.model.EpisodeFilter
import ac.mdiq.podcini.ui.fragment.SubscriptionsFragment.Companion.TAG
import ac.mdiq.podcini.util.Logd import ac.mdiq.podcini.util.Logd
import android.app.Dialog import android.app.Dialog
import android.content.DialogInterface import android.content.DialogInterface
@ -98,8 +99,9 @@ abstract class EpisodeFilterDialog : BottomSheetDialogFragment() {
} }
override fun onDestroyView() { override fun onDestroyView() {
super.onDestroyView() Logd(TAG, "onDestroyView")
_binding = null _binding = null
super.onDestroyView()
} }
private fun setupFullHeight(bottomSheetDialog: BottomSheetDialog) { private fun setupFullHeight(bottomSheetDialog: BottomSheetDialog) {

View File

@ -5,6 +5,8 @@ import ac.mdiq.podcini.databinding.SortDialogBinding
import ac.mdiq.podcini.databinding.SortDialogItemActiveBinding import ac.mdiq.podcini.databinding.SortDialogItemActiveBinding
import ac.mdiq.podcini.databinding.SortDialogItemBinding import ac.mdiq.podcini.databinding.SortDialogItemBinding
import ac.mdiq.podcini.storage.model.EpisodeSortOrder import ac.mdiq.podcini.storage.model.EpisodeSortOrder
import ac.mdiq.podcini.ui.fragment.SubscriptionsFragment.Companion.TAG
import ac.mdiq.podcini.util.Logd
import android.app.Dialog import android.app.Dialog
import android.content.DialogInterface import android.content.DialogInterface
import android.os.Bundle import android.os.Bundle
@ -98,8 +100,9 @@ open class EpisodeSortDialog : BottomSheetDialogFragment() {
} }
override fun onDestroyView() { override fun onDestroyView() {
super.onDestroyView() Logd(TAG, "onDestroyView")
_binding = null _binding = null
super.onDestroyView()
} }
private fun setupFullHeight(bottomSheetDialog: BottomSheetDialog) { private fun setupFullHeight(bottomSheetDialog: BottomSheetDialog) {

View File

@ -9,6 +9,8 @@ import ac.mdiq.podcini.preferences.UserPreferences.feedOrderDir
import ac.mdiq.podcini.preferences.UserPreferences.setFeedOrder import ac.mdiq.podcini.preferences.UserPreferences.setFeedOrder
import ac.mdiq.podcini.storage.model.FeedSortOrder import ac.mdiq.podcini.storage.model.FeedSortOrder
import ac.mdiq.podcini.storage.model.FeedSortOrder.Companion.getSortOrder import ac.mdiq.podcini.storage.model.FeedSortOrder.Companion.getSortOrder
import ac.mdiq.podcini.ui.fragment.SubscriptionsFragment
import ac.mdiq.podcini.ui.fragment.SubscriptionsFragment.Companion
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
@ -40,6 +42,7 @@ open class FeedSortDialogNew : BottomSheetDialogFragment() {
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
_binding = SortDialogBinding.inflate(inflater) _binding = SortDialogBinding.inflate(inflater)
binding.gridLayout.columnCount = 1
populateList() populateList()
binding.keepSortedCheckbox.setOnCheckedChangeListener { _: CompoundButton?, _: Boolean -> this@FeedSortDialogNew.onSelectionChanged() } binding.keepSortedCheckbox.setOnCheckedChangeListener { _: CompoundButton?, _: Boolean -> this@FeedSortDialogNew.onSelectionChanged() }
return binding.root return binding.root
@ -117,8 +120,9 @@ open class FeedSortDialogNew : BottomSheetDialogFragment() {
} }
override fun onDestroyView() { override fun onDestroyView() {
super.onDestroyView() Logd(TAG, "onDestroyView")
_binding = null _binding = null
super.onDestroyView()
} }
private fun setupFullHeight(bottomSheetDialog: BottomSheetDialog) { private fun setupFullHeight(bottomSheetDialog: BottomSheetDialog) {

View File

@ -13,6 +13,8 @@ import ac.mdiq.podcini.util.ShareUtils.shareFeedItemLinkWithDownloadLink
import ac.mdiq.podcini.util.ShareUtils.shareMediaDownloadLink import ac.mdiq.podcini.util.ShareUtils.shareMediaDownloadLink
import ac.mdiq.podcini.databinding.ShareEpisodeDialogBinding import ac.mdiq.podcini.databinding.ShareEpisodeDialogBinding
import ac.mdiq.podcini.storage.model.Episode import ac.mdiq.podcini.storage.model.Episode
import ac.mdiq.podcini.ui.fragment.SubscriptionsFragment.Companion.TAG
import ac.mdiq.podcini.util.Logd
class ShareDialog : BottomSheetDialogFragment() { class ShareDialog : BottomSheetDialogFragment() {
private lateinit var ctx: Context private lateinit var ctx: Context
@ -83,8 +85,9 @@ class ShareDialog : BottomSheetDialogFragment() {
} }
override fun onDestroyView() { override fun onDestroyView() {
super.onDestroyView() Logd(TAG, "onDestroyView")
_binding = null _binding = null
super.onDestroyView()
} }
fun setItem(item_: Episode) { fun setItem(item_: Episode) {

View File

@ -18,8 +18,10 @@ import ac.mdiq.podcini.preferences.SleepTimerPreferences.shakeToReset
import ac.mdiq.podcini.preferences.SleepTimerPreferences.timerMillis import ac.mdiq.podcini.preferences.SleepTimerPreferences.timerMillis
import ac.mdiq.podcini.preferences.SleepTimerPreferences.vibrate import ac.mdiq.podcini.preferences.SleepTimerPreferences.vibrate
import ac.mdiq.podcini.storage.model.Playable import ac.mdiq.podcini.storage.model.Playable
import ac.mdiq.podcini.ui.fragment.SubscriptionsFragment.Companion.TAG
import ac.mdiq.podcini.ui.utils.ThemeUtils.getColorFromAttr import ac.mdiq.podcini.ui.utils.ThemeUtils.getColorFromAttr
import ac.mdiq.podcini.util.Converter.getDurationStringLong import ac.mdiq.podcini.util.Converter.getDurationStringLong
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
import android.app.Activity import android.app.Activity
@ -160,8 +162,9 @@ class SleepTimerDialog : DialogFragment() {
} }
override fun onDestroyView() { override fun onDestroyView() {
super.onDestroyView() Logd(TAG, "onDestroyView")
_binding = null _binding = null
super.onDestroyView()
} }
fun extendSleepTimer(extendTime: Long) { fun extendSleepTimer(extendTime: Long) {

View File

@ -11,6 +11,8 @@ import ac.mdiq.podcini.storage.database.RealmDB.upsertBlk
import ac.mdiq.podcini.storage.model.Feed import ac.mdiq.podcini.storage.model.Feed
import ac.mdiq.podcini.storage.model.FeedPreferences import ac.mdiq.podcini.storage.model.FeedPreferences
import ac.mdiq.podcini.ui.adapter.SimpleChipAdapter import ac.mdiq.podcini.ui.adapter.SimpleChipAdapter
import ac.mdiq.podcini.ui.fragment.SubscriptionsFragment
import ac.mdiq.podcini.ui.fragment.SubscriptionsFragment.Companion
import ac.mdiq.podcini.ui.utils.ItemOffsetDecoration import ac.mdiq.podcini.ui.utils.ItemOffsetDecoration
import ac.mdiq.podcini.util.Logd import ac.mdiq.podcini.util.Logd
import android.app.Dialog import android.app.Dialog
@ -97,8 +99,9 @@ class TagSettingsDialog : DialogFragment() {
} }
override fun onDestroyView() { override fun onDestroyView() {
super.onDestroyView() Logd(TAG, "onDestroyView")
_binding = null _binding = null
super.onDestroyView()
} }
@OptIn(UnstableApi::class) private fun updatePreferencesTags(commonTags: Set<String>) { @OptIn(UnstableApi::class) private fun updatePreferencesTags(commonTags: Set<String>) {

View File

@ -16,6 +16,8 @@ import ac.mdiq.podcini.storage.database.Feeds.persistFeedPreferences
import ac.mdiq.podcini.storage.database.RealmDB.unmanaged import ac.mdiq.podcini.storage.database.RealmDB.unmanaged
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.fragment.SubscriptionsFragment
import ac.mdiq.podcini.ui.fragment.SubscriptionsFragment.Companion
import ac.mdiq.podcini.ui.utils.ItemOffsetDecoration import ac.mdiq.podcini.ui.utils.ItemOffsetDecoration
import ac.mdiq.podcini.ui.view.PlaybackSpeedSeekBar import ac.mdiq.podcini.ui.view.PlaybackSpeedSeekBar
import ac.mdiq.podcini.util.Logd import ac.mdiq.podcini.util.Logd
@ -158,8 +160,9 @@ import java.util.*
} }
override fun onDestroyView() { override fun onDestroyView() {
super.onDestroyView() Logd(TAG, "onDestroyView")
_binding = null _binding = null
super.onDestroyView()
} }
private fun addCurrentSpeed() { private fun addCurrentSpeed() {

View File

@ -10,6 +10,7 @@ import ac.mdiq.podcini.storage.model.Feed
import ac.mdiq.podcini.storage.model.EpisodeSortOrder import ac.mdiq.podcini.storage.model.EpisodeSortOrder
import ac.mdiq.podcini.ui.activity.MainActivity import ac.mdiq.podcini.ui.activity.MainActivity
import ac.mdiq.podcini.ui.activity.OpmlImportActivity import ac.mdiq.podcini.ui.activity.OpmlImportActivity
import ac.mdiq.podcini.ui.fragment.SubscriptionsFragment.Companion
import ac.mdiq.podcini.util.Logd import ac.mdiq.podcini.util.Logd
import android.content.* import android.content.*
import android.net.Uri import android.net.Uri
@ -150,8 +151,9 @@ class AddFeedFragment : Fragment() {
} }
override fun onDestroyView() { override fun onDestroyView() {
super.onDestroyView() Logd(TAG, "onDestroyView")
_binding = null _binding = null
super.onDestroyView()
} }
private fun chooseOpmlImportPathResult(uri: Uri?) { private fun chooseOpmlImportPathResult(uri: Uri?) {

View File

@ -12,6 +12,7 @@ import ac.mdiq.podcini.ui.activity.MainActivity
import ac.mdiq.podcini.ui.dialog.EpisodeFilterDialog import ac.mdiq.podcini.ui.dialog.EpisodeFilterDialog
import ac.mdiq.podcini.ui.dialog.EpisodeSortDialog import ac.mdiq.podcini.ui.dialog.EpisodeSortDialog
import ac.mdiq.podcini.ui.dialog.SwitchQueueDialog import ac.mdiq.podcini.ui.dialog.SwitchQueueDialog
import ac.mdiq.podcini.ui.fragment.SubscriptionsFragment.Companion
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
@ -49,6 +50,11 @@ import kotlin.math.min
return root return root
} }
override fun onDestroyView() {
allEpisodes = listOf()
super.onDestroyView()
}
override fun onStart() { override fun onStart() {
super.onStart() super.onStart()
procFlowEvents() procFlowEvents()
@ -115,6 +121,10 @@ import kotlin.math.min
Logd(TAG, "Received event: ${event.TAG}") Logd(TAG, "Received event: ${event.TAG}")
when (event) { when (event) {
is FlowEvent.AllEpisodesFilterEvent -> onFilterChanged(event) is FlowEvent.AllEpisodesFilterEvent -> onFilterChanged(event)
is FlowEvent.AllEpisodesSortEvent -> {
page = 1
loadItems()
}
else -> {} else -> {}
} }
} }
@ -148,13 +158,14 @@ import kotlin.math.min
} }
override fun onAddItem(title: Int, ascending: EpisodeSortOrder, descending: EpisodeSortOrder, ascendingIsDefault: Boolean) { override fun onAddItem(title: Int, ascending: EpisodeSortOrder, descending: EpisodeSortOrder, ascendingIsDefault: Boolean) {
if (ascending == EpisodeSortOrder.DATE_OLD_NEW || ascending == EpisodeSortOrder.DURATION_SHORT_LONG if (ascending == EpisodeSortOrder.DATE_OLD_NEW || ascending == EpisodeSortOrder.DURATION_SHORT_LONG
|| ascending == EpisodeSortOrder.PLAYED_DATE_OLD_NEW || ascending == EpisodeSortOrder.COMPLETED_DATE_OLD_NEW) || ascending == EpisodeSortOrder.PLAYED_DATE_OLD_NEW || ascending == EpisodeSortOrder.COMPLETED_DATE_OLD_NEW
|| ascending == EpisodeSortOrder.EPISODE_TITLE_A_Z)
super.onAddItem(title, ascending, descending, ascendingIsDefault) super.onAddItem(title, ascending, descending, ascendingIsDefault)
} }
override fun onSelectionChanged() { override fun onSelectionChanged() {
super.onSelectionChanged() super.onSelectionChanged()
allEpisodesSortOrder = sortOrder allEpisodesSortOrder = sortOrder
EventFlow.postEvent(FlowEvent.FeedsSortedEvent()) EventFlow.postEvent(FlowEvent.AllEpisodesSortEvent())
} }
} }
@ -170,7 +181,6 @@ import kotlin.math.min
} }
} }
} }
companion object { companion object {
val TAG = AllEpisodesFragment::class.simpleName ?: "Anonymous" val TAG = AllEpisodesFragment::class.simpleName ?: "Anonymous"
const val PREF_NAME: String = "PrefAllEpisodesFragment" const val PREF_NAME: String = "PrefAllEpisodesFragment"

View File

@ -38,6 +38,7 @@ import ac.mdiq.podcini.ui.dialog.MediaPlayerErrorDialog
import ac.mdiq.podcini.ui.dialog.SkipPreferenceDialog import ac.mdiq.podcini.ui.dialog.SkipPreferenceDialog
import ac.mdiq.podcini.ui.dialog.SleepTimerDialog import ac.mdiq.podcini.ui.dialog.SleepTimerDialog
import ac.mdiq.podcini.ui.dialog.VariableSpeedDialog import ac.mdiq.podcini.ui.dialog.VariableSpeedDialog
import ac.mdiq.podcini.ui.fragment.SubscriptionsFragment.Companion
import ac.mdiq.podcini.ui.view.ChapterSeekBar import ac.mdiq.podcini.ui.view.ChapterSeekBar
import ac.mdiq.podcini.ui.view.PlayButton import ac.mdiq.podcini.ui.view.PlayButton
import ac.mdiq.podcini.util.Converter import ac.mdiq.podcini.util.Converter
@ -145,39 +146,40 @@ class AudioPlayerFragment : Fragment(), SeekBar.OnSeekBarChangeListener, Toolbar
playerUIView2?.setBackgroundColor(SurfaceColors.getColorForElevation(requireContext(), 8 * resources.displayMetrics.density)) playerUIView2?.setBackgroundColor(SurfaceColors.getColorForElevation(requireContext(), 8 * resources.displayMetrics.density))
onCollaped() onCollaped()
cardViewSeek = binding.cardViewSeek cardViewSeek = binding.cardViewSeek
initDetailedView()
return binding.root return binding.root
} }
// fun initDetailedView() { fun initDetailedView() {
// if (playerDetailsFragment == null) {
// val fm = requireActivity().supportFragmentManager
// val transaction = fm.beginTransaction()
// playerDetailsFragment = PlayerDetailsFragment()
// transaction.replace(R.id.itemDescription, playerDetailsFragment!!).commit()
// }
// }
override fun onDestroyView() {
Logd(TAG, "Fragment destroyed")
super.onDestroyView()
_binding = null
controller?.release()
controller = null
}
fun onExpanded() {
Logd(TAG, "onExpanded()")
if (playerDetailsFragment == null) { if (playerDetailsFragment == null) {
val fm = requireActivity().supportFragmentManager val fm = requireActivity().supportFragmentManager
val transaction = fm.beginTransaction() val transaction = fm.beginTransaction()
playerDetailsFragment = PlayerDetailsFragment() playerDetailsFragment = PlayerDetailsFragment()
transaction.replace(R.id.itemDescription, playerDetailsFragment!!).commit() transaction.replace(R.id.itemDescription, playerDetailsFragment!!).commit()
} }
isCollapsed = false }
playerUI = playerUI2
playerUI?.updateUi(currentMedia) override fun onDestroyView() {
playerUI?.butPlay?.setIsShowPlay(isShowPlay) Logd(TAG, "Fragment destroyed")
playerDetailsFragment?.updateInfo() _binding = null
controller?.release()
controller = null
super.onDestroyView()
}
fun onExpanded() {
Logd(TAG, "onExpanded()")
initDetailedView()
// the function can also be called from MainActivity when a select menu pops up and closes
if (isCollapsed) {
isCollapsed = false
playerUI = playerUI2
playerUI?.updateUi(currentMedia)
playerUI?.butPlay?.setIsShowPlay(isShowPlay)
playerDetailsFragment?.updateInfo()
}
} }
fun onCollaped() { fun onCollaped() {
@ -340,7 +342,10 @@ class AudioPlayerFragment : Fragment(), SeekBar.OnSeekBarChangeListener, Toolbar
is FlowEvent.FavoritesEvent -> onFavoriteEvent(event) is FlowEvent.FavoritesEvent -> onFavoriteEvent(event)
is FlowEvent.PlayerErrorEvent -> MediaPlayerErrorDialog.show(activity as Activity, event) is FlowEvent.PlayerErrorEvent -> MediaPlayerErrorDialog.show(activity as Activity, event)
is FlowEvent.SleepTimerUpdatedEvent -> if (event.isCancelled || event.wasJustEnabled()) loadMediaInfo(false) is FlowEvent.SleepTimerUpdatedEvent -> if (event.isCancelled || event.wasJustEnabled()) loadMediaInfo(false)
is FlowEvent.PlaybackPositionEvent -> playerUI?.onPositionUpdate(event) is FlowEvent.PlaybackPositionEvent -> {
playerUI?.onPositionUpdate(event)
if (!isCollapsed) playerDetailsFragment?.onPlaybackPositionEvent(event)
}
is FlowEvent.SpeedChangedEvent -> playerUI?.updatePlaybackSpeedButton(event) is FlowEvent.SpeedChangedEvent -> playerUI?.updatePlaybackSpeedButton(event)
else -> {} else -> {}
} }
@ -528,8 +533,9 @@ class AudioPlayerFragment : Fragment(), SeekBar.OnSeekBarChangeListener, Toolbar
return binding.root return binding.root
} }
@OptIn(UnstableApi::class) override fun onDestroyView() { @OptIn(UnstableApi::class) override fun onDestroyView() {
super.onDestroyView() Logd(TAG, "onDestroyView")
_binding = null _binding = null
super.onDestroyView()
} }
@UnstableApi @UnstableApi
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {

View File

@ -20,6 +20,7 @@ import ac.mdiq.podcini.ui.utils.EmptyViewHandler
import ac.mdiq.podcini.ui.view.EpisodesRecyclerView import ac.mdiq.podcini.ui.view.EpisodesRecyclerView
import ac.mdiq.podcini.ui.utils.LiftOnScrollListener import ac.mdiq.podcini.ui.utils.LiftOnScrollListener
import ac.mdiq.podcini.storage.utils.EpisodeUtil import ac.mdiq.podcini.storage.utils.EpisodeUtil
import ac.mdiq.podcini.ui.fragment.SubscriptionsFragment.Companion
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
@ -305,9 +306,12 @@ import kotlinx.coroutines.flow.collectLatest
} }
override fun onDestroyView() { override fun onDestroyView() {
super.onDestroyView() Logd(TAG, "onDestroyView")
_binding = null _binding = null
adapter.clearData()
adapter.endSelectMode() adapter.endSelectMode()
episodes.clear()
super.onDestroyView()
} }
override fun onStartSelectMode() { override fun onStartSelectMode() {

View File

@ -15,6 +15,7 @@ import ac.mdiq.podcini.playback.PlaybackController.Companion.curPosition
import ac.mdiq.podcini.playback.PlaybackController.Companion.seekTo import ac.mdiq.podcini.playback.PlaybackController.Companion.seekTo
import ac.mdiq.podcini.storage.model.Chapter import ac.mdiq.podcini.storage.model.Chapter
import ac.mdiq.podcini.storage.model.EmbeddedChapterImage import ac.mdiq.podcini.storage.model.EmbeddedChapterImage
import ac.mdiq.podcini.ui.fragment.SubscriptionsFragment.Companion
import ac.mdiq.podcini.ui.view.CircularProgressBar import ac.mdiq.podcini.ui.view.CircularProgressBar
import ac.mdiq.podcini.util.Converter.getDurationStringLocalized import ac.mdiq.podcini.util.Converter.getDurationStringLocalized
import ac.mdiq.podcini.util.Converter.getDurationStringLong import ac.mdiq.podcini.util.Converter.getDurationStringLong
@ -133,11 +134,11 @@ class ChaptersFragment : AppCompatDialogFragment() {
} }
override fun onDestroyView() { override fun onDestroyView() {
super.onDestroyView() Logd(TAG, "onDestroyView")
_binding = null _binding = null
controller?.release() controller?.release()
controller = null controller = null
super.onDestroyView()
} }
private var eventSink: Job? = null private var eventSink: Job? = null

View File

@ -64,7 +64,6 @@ class DiscoveryFragment : Fragment(), Toolbar.OnMenuItemClickListener {
private var hidden = false private var hidden = false
private var needsConfirm = false private var needsConfirm = false
/** /**
* Replace adapter data with provided search results from SearchTask. * Replace adapter data with provided search results from SearchTask.
* *
@ -131,9 +130,12 @@ class DiscoveryFragment : Fragment(), Toolbar.OnMenuItemClickListener {
} }
override fun onDestroy() { override fun onDestroy() {
super.onDestroy()
_binding = null _binding = null
adapter?.clearData()
adapter = null adapter = null
searchResults = null
topList = null
super.onDestroy()
} }
private fun loadToplist(country: String?) { private fun loadToplist(country: String?) {

View File

@ -15,6 +15,7 @@ import ac.mdiq.podcini.storage.model.Feed
import ac.mdiq.podcini.ui.actions.actionbutton.DownloadActionButton import ac.mdiq.podcini.ui.actions.actionbutton.DownloadActionButton
import ac.mdiq.podcini.ui.activity.MainActivity import ac.mdiq.podcini.ui.activity.MainActivity
import ac.mdiq.podcini.ui.dialog.DownloadLogDetailsDialog import ac.mdiq.podcini.ui.dialog.DownloadLogDetailsDialog
import ac.mdiq.podcini.ui.fragment.SubscriptionsFragment.Companion
import ac.mdiq.podcini.ui.utils.EmptyViewHandler import ac.mdiq.podcini.ui.utils.EmptyViewHandler
import ac.mdiq.podcini.ui.utils.ThemeUtils import ac.mdiq.podcini.ui.utils.ThemeUtils
import ac.mdiq.podcini.util.Logd import ac.mdiq.podcini.util.Logd
@ -86,8 +87,10 @@ class DownloadLogFragment : BottomSheetDialogFragment(), OnItemClickListener, To
} }
override fun onDestroyView() { override fun onDestroyView() {
super.onDestroyView() Logd(TAG, "onDestroyView")
_binding = null _binding = null
downloadLog = listOf()
super.onDestroyView()
} }
override fun onItemClick(parent: AdapterView<*>?, view: View, position: Int, id: Long) { override fun onItemClick(parent: AdapterView<*>?, view: View, position: Int, id: Long) {

View File

@ -24,6 +24,7 @@ import ac.mdiq.podcini.ui.adapter.EpisodesAdapter
import ac.mdiq.podcini.ui.adapter.SelectableAdapter import ac.mdiq.podcini.ui.adapter.SelectableAdapter
import ac.mdiq.podcini.ui.dialog.EpisodeSortDialog import ac.mdiq.podcini.ui.dialog.EpisodeSortDialog
import ac.mdiq.podcini.ui.dialog.SwitchQueueDialog import ac.mdiq.podcini.ui.dialog.SwitchQueueDialog
import ac.mdiq.podcini.ui.fragment.SubscriptionsFragment.Companion
import ac.mdiq.podcini.ui.utils.EmptyViewHandler import ac.mdiq.podcini.ui.utils.EmptyViewHandler
import ac.mdiq.podcini.ui.utils.LiftOnScrollListener import ac.mdiq.podcini.ui.utils.LiftOnScrollListener
import ac.mdiq.podcini.ui.view.EpisodesRecyclerView import ac.mdiq.podcini.ui.view.EpisodesRecyclerView
@ -163,10 +164,13 @@ import java.util.*
} }
override fun onDestroyView() { override fun onDestroyView() {
Logd(TAG, "onDestroyView")
_binding = null _binding = null
adapter.endSelectMode() adapter.endSelectMode()
adapter.clearData()
toolbar.setOnMenuItemClickListener(null) toolbar.setOnMenuItemClickListener(null)
toolbar.setOnLongClickListener(null) toolbar.setOnLongClickListener(null)
episodes = mutableListOf()
super.onDestroyView() super.onDestroyView()
} }
@ -279,7 +283,7 @@ import java.util.*
val size: Int = event.episodes.size val size: Int = event.episodes.size
while (i < size) { while (i < size) {
val item: Episode = event.episodes[i] val item: Episode = event.episodes[i]
val pos = EpisodeUtil.indexOfItemWithId(episodes.toList(), item.id) val pos = EpisodeUtil.indexOfItemWithId(episodes, item.id)
if (pos >= 0) { if (pos >= 0) {
episodes.removeAt(pos) episodes.removeAt(pos)
val media = item.media val media = item.media

View File

@ -8,6 +8,7 @@ import ac.mdiq.podcini.util.Logd
import ac.mdiq.podcini.net.utils.NetworkUtils.fetchHtmlSource import ac.mdiq.podcini.net.utils.NetworkUtils.fetchHtmlSource
import ac.mdiq.podcini.storage.database.Episodes.persistEpisode import ac.mdiq.podcini.storage.database.Episodes.persistEpisode
import ac.mdiq.podcini.storage.database.RealmDB.runOnIOScope import ac.mdiq.podcini.storage.database.RealmDB.runOnIOScope
import ac.mdiq.podcini.ui.fragment.SubscriptionsFragment.Companion
import android.content.Context import android.content.Context
import android.os.Bundle import android.os.Bundle
import android.speech.tts.TextToSpeech import android.speech.tts.TextToSpeech
@ -257,7 +258,6 @@ class EpisodeHomeFragment : Fragment() {
} }
@OptIn(UnstableApi::class) override fun onDestroyView() { @OptIn(UnstableApi::class) override fun onDestroyView() {
super.onDestroyView()
Logd(TAG, "onDestroyView") Logd(TAG, "onDestroyView")
cleatWebview(binding.webView) cleatWebview(binding.webView)
cleatWebview(binding.readerView) cleatWebview(binding.readerView)
@ -265,6 +265,7 @@ class EpisodeHomeFragment : Fragment() {
tts?.stop() tts?.stop()
tts?.shutdown() tts?.shutdown()
tts = null tts = null
super.onDestroyView()
} }
@UnstableApi private fun updateAppearance() { @UnstableApi private fun updateAppearance() {

View File

@ -19,6 +19,7 @@ import ac.mdiq.podcini.storage.model.MediaType
import ac.mdiq.podcini.ui.actions.actionbutton.* import ac.mdiq.podcini.ui.actions.actionbutton.*
import ac.mdiq.podcini.ui.actions.menuhandler.EpisodeMenuHandler import ac.mdiq.podcini.ui.actions.menuhandler.EpisodeMenuHandler
import ac.mdiq.podcini.ui.activity.MainActivity import ac.mdiq.podcini.ui.activity.MainActivity
import ac.mdiq.podcini.ui.fragment.SubscriptionsFragment.Companion
import ac.mdiq.podcini.ui.utils.ShownotesCleaner import ac.mdiq.podcini.ui.utils.ShownotesCleaner
import ac.mdiq.podcini.ui.utils.ThemeUtils import ac.mdiq.podcini.ui.utils.ThemeUtils
import ac.mdiq.podcini.ui.view.ShownotesWebView import ac.mdiq.podcini.ui.view.ShownotesWebView
@ -230,14 +231,15 @@ import kotlin.math.max
} }
@OptIn(UnstableApi::class) override fun onDestroyView() { @OptIn(UnstableApi::class) override fun onDestroyView() {
super.onDestroyView()
Logd(TAG, "onDestroyView") Logd(TAG, "onDestroyView")
binding.root.removeView(webvDescription) binding.root.removeView(webvDescription)
episode = null
webvDescription.clearHistory() webvDescription.clearHistory()
webvDescription.clearCache(true) webvDescription.clearCache(true)
webvDescription.clearView() webvDescription.clearView()
webvDescription.destroy() webvDescription.destroy()
_binding = null _binding = null
super.onDestroyView()
} }
@UnstableApi private fun onFragmentLoaded() { @UnstableApi private fun onFragmentLoaded() {

View File

@ -30,6 +30,7 @@ import ac.mdiq.podcini.ui.activity.MainActivity
import ac.mdiq.podcini.ui.adapter.EpisodesAdapter import ac.mdiq.podcini.ui.adapter.EpisodesAdapter
import ac.mdiq.podcini.ui.adapter.SelectableAdapter import ac.mdiq.podcini.ui.adapter.SelectableAdapter
import ac.mdiq.podcini.ui.dialog.* import ac.mdiq.podcini.ui.dialog.*
import ac.mdiq.podcini.ui.fragment.SubscriptionsFragment.Companion
import ac.mdiq.podcini.ui.utils.ToolbarIconTintManager import ac.mdiq.podcini.ui.utils.ToolbarIconTintManager
import ac.mdiq.podcini.ui.utils.TransitionEffect import ac.mdiq.podcini.ui.utils.TransitionEffect
import ac.mdiq.podcini.ui.view.viewholder.EpisodeViewHolder import ac.mdiq.podcini.ui.view.viewholder.EpisodeViewHolder
@ -226,18 +227,23 @@ import java.util.concurrent.Semaphore
} }
override fun onDestroyView() { override fun onDestroyView() {
super.onDestroyView() Logd(TAG, "onDestroyView")
_binding = null _binding = null
_dialBinding = null _dialBinding = null
ioScope.cancel() ioScope.cancel()
adapter.endSelectMode() adapter.endSelectMode()
adapter.clearData()
feed = null
episodes = mutableListOf()
tts?.stop() tts?.stop()
tts?.shutdown() tts?.shutdown()
ttsWorking = false ttsWorking = false
ttsReady = false ttsReady = false
tts = null tts = null
super.onDestroyView()
} }
override fun onSaveInstanceState(outState: Bundle) { override fun onSaveInstanceState(outState: Bundle) {
@ -335,8 +341,8 @@ import java.util.concurrent.Semaphore
if (item.feedId != feed!!.id) continue if (item.feedId != feed!!.id) continue
val pos: Int = EpisodeUtil.indexOfItemWithId(episodes, item.id) val pos: Int = EpisodeUtil.indexOfItemWithId(episodes, item.id)
if (pos >= 0) { if (pos >= 0) {
episodes.removeAt(pos) Logd(TAG, "episode event: ${item.title} ${item.playState}")
episodes.add(pos, item) episodes[pos] = item
adapter.notifyItemChangedCompat(pos) adapter.notifyItemChangedCompat(pos)
} }
i++ i++
@ -352,8 +358,7 @@ import java.util.concurrent.Semaphore
if (item.feedId != feed!!.id) continue if (item.feedId != feed!!.id) continue
val pos: Int = EpisodeUtil.indexOfItemWithId(episodes, item.id) val pos: Int = EpisodeUtil.indexOfItemWithId(episodes, item.id)
if (pos >= 0) { if (pos >= 0) {
episodes.removeAt(pos) episodes[pos] = item
episodes.add(pos, item)
adapter.notifyItemChangedCompat(pos) adapter.notifyItemChangedCompat(pos)
// episodes[pos].playState = item.playState // episodes[pos].playState = item.playState
// adapter.notifyItemChangedCompat(pos) // adapter.notifyItemChangedCompat(pos)
@ -375,6 +380,7 @@ import java.util.concurrent.Semaphore
val item = event.episode val item = event.episode
val pos: Int = EpisodeUtil.indexOfItemWithId(episodes, item.id) val pos: Int = EpisodeUtil.indexOfItemWithId(episodes, item.id)
if (pos >= 0) { if (pos >= 0) {
Logd(TAG, "played item: ${item.title} ${item.playState}")
episodes[pos] = item episodes[pos] = item
adapter.notifyItemChangedCompat(pos) adapter.notifyItemChangedCompat(pos)
// episodes[pos].playState = item.playState // episodes[pos].playState = item.playState

View File

@ -11,6 +11,7 @@ import ac.mdiq.podcini.storage.database.Feeds.updateFeedDownloadURL
import ac.mdiq.podcini.storage.model.Feed import ac.mdiq.podcini.storage.model.Feed
import ac.mdiq.podcini.storage.model.FeedFunding import ac.mdiq.podcini.storage.model.FeedFunding
import ac.mdiq.podcini.ui.activity.MainActivity import ac.mdiq.podcini.ui.activity.MainActivity
import ac.mdiq.podcini.ui.fragment.SubscriptionsFragment.Companion
import ac.mdiq.podcini.ui.statistics.FeedStatisticsFragment import ac.mdiq.podcini.ui.statistics.FeedStatisticsFragment
import ac.mdiq.podcini.ui.statistics.StatisticsFragment import ac.mdiq.podcini.ui.statistics.StatisticsFragment
import ac.mdiq.podcini.ui.utils.ToolbarIconTintManager import ac.mdiq.podcini.ui.utils.ToolbarIconTintManager
@ -204,9 +205,10 @@ class FeedInfoFragment : Fragment(), Toolbar.OnMenuItemClickListener {
} }
override fun onDestroyView() { override fun onDestroyView() {
super.onDestroyView() Logd(TAG, "onDestroyView")
_binding = null _binding = null
// feed = null feed = Feed()
super.onDestroyView()
} }
private fun refreshToolbarState() { private fun refreshToolbarState() {

View File

@ -17,6 +17,7 @@ import ac.mdiq.podcini.storage.model.VolumeAdaptionSetting
import ac.mdiq.podcini.ui.adapter.SimpleChipAdapter import ac.mdiq.podcini.ui.adapter.SimpleChipAdapter
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.fragment.SubscriptionsFragment.Companion
import ac.mdiq.podcini.ui.utils.ItemOffsetDecoration import ac.mdiq.podcini.ui.utils.ItemOffsetDecoration
import ac.mdiq.podcini.util.Logd import ac.mdiq.podcini.util.Logd
import android.content.Context import android.content.Context
@ -65,8 +66,10 @@ class FeedSettingsFragment : Fragment() {
} }
override fun onDestroyView() { override fun onDestroyView() {
super.onDestroyView() Logd(TAG, "onDestroyView")
_binding = null _binding = null
feed = null
super.onDestroyView()
} }
class FeedSettingsPreferenceFragment : PreferenceFragmentCompat() { class FeedSettingsPreferenceFragment : PreferenceFragmentCompat() {

View File

@ -12,6 +12,7 @@ import ac.mdiq.podcini.ui.adapter.EpisodesAdapter
import ac.mdiq.podcini.ui.dialog.ConfirmationDialog import ac.mdiq.podcini.ui.dialog.ConfirmationDialog
import ac.mdiq.podcini.ui.dialog.DatesFilterDialog import ac.mdiq.podcini.ui.dialog.DatesFilterDialog
import ac.mdiq.podcini.ui.dialog.EpisodeSortDialog import ac.mdiq.podcini.ui.dialog.EpisodeSortDialog
import ac.mdiq.podcini.ui.fragment.SubscriptionsFragment.Companion
import ac.mdiq.podcini.ui.view.viewholder.EpisodeViewHolder import ac.mdiq.podcini.ui.view.viewholder.EpisodeViewHolder
import ac.mdiq.podcini.util.DateFormatter import ac.mdiq.podcini.util.DateFormatter
import ac.mdiq.podcini.util.Logd import ac.mdiq.podcini.util.Logd
@ -86,6 +87,12 @@ import kotlin.math.min
cancelFlowEvents() cancelFlowEvents()
} }
override fun onDestroyView() {
allHistory = listOf()
adapter.clearData()
super.onDestroyView()
}
@OptIn(UnstableApi::class) override fun onMenuItemClick(item: MenuItem): Boolean { @OptIn(UnstableApi::class) override fun onMenuItemClick(item: MenuItem): Boolean {
if (super.onOptionsItemSelected(item)) return true if (super.onOptionsItemSelected(item)) return true
when (item.itemId) { when (item.itemId) {

View File

@ -19,6 +19,7 @@ import ac.mdiq.podcini.ui.activity.MainActivity
import ac.mdiq.podcini.ui.activity.PreferenceActivity import ac.mdiq.podcini.ui.activity.PreferenceActivity
import ac.mdiq.podcini.ui.activity.starter.MainActivityStarter import ac.mdiq.podcini.ui.activity.starter.MainActivityStarter
import ac.mdiq.podcini.ui.dialog.DrawerPreferencesDialog import ac.mdiq.podcini.ui.dialog.DrawerPreferencesDialog
import ac.mdiq.podcini.ui.fragment.SubscriptionsFragment.Companion
import ac.mdiq.podcini.ui.statistics.StatisticsFragment import ac.mdiq.podcini.ui.statistics.StatisticsFragment
import ac.mdiq.podcini.ui.utils.ThemeUtils import ac.mdiq.podcini.ui.utils.ThemeUtils
import ac.mdiq.podcini.util.Logd import ac.mdiq.podcini.util.Logd
@ -127,9 +128,10 @@ class NavDrawerFragment : Fragment(), OnSharedPreferenceChangeListener {
} }
override fun onDestroyView() { override fun onDestroyView() {
super.onDestroyView() Logd(TAG, "onDestroyView")
_binding = null _binding = null
prefs!!.unregisterOnSharedPreferenceChangeListener(this) prefs!!.unregisterOnSharedPreferenceChangeListener(this)
super.onDestroyView()
} }
override fun onResume() { override fun onResume() {
@ -276,7 +278,6 @@ class NavDrawerFragment : Fragment(), OnSharedPreferenceChangeListener {
} else return false } else return false
} }
@UnstableApi private fun bindNavView(title: String, position: Int, holder: NavHolder) { @UnstableApi private fun bindNavView(title: String, position: Int, holder: NavHolder) {
Logd(TAG, "bindNavView called")
val context = activity ?: return val context = activity ?: return
holder.title.text = title holder.title.text = title
// reset for re-use // reset for re-use

View File

@ -164,8 +164,9 @@ import kotlin.concurrent.Volatile
} }
override fun onDestroy() { override fun onDestroy() {
super.onDestroy()
_binding = null _binding = null
feeds = null
super.onDestroy()
} }
@OptIn(UnstableApi::class) override fun onSaveInstanceState(outState: Bundle) { @OptIn(UnstableApi::class) override fun onSaveInstanceState(outState: Bundle) {

View File

@ -97,10 +97,11 @@ class OnlineSearchFragment : Fragment() {
} }
override fun onDestroy() { override fun onDestroy() {
super.onDestroy()
_binding = null _binding = null
// disposable?.dispose() searchResults = null
adapter?.clearData()
adapter = null adapter = null
super.onDestroy()
} }
private fun setupToolbar(toolbar: MaterialToolbar) { private fun setupToolbar(toolbar: MaterialToolbar) {

View File

@ -2,26 +2,22 @@ package ac.mdiq.podcini.ui.fragment
import ac.mdiq.podcini.R import ac.mdiq.podcini.R
import ac.mdiq.podcini.databinding.PlayerDetailsFragmentBinding import ac.mdiq.podcini.databinding.PlayerDetailsFragmentBinding
import ac.mdiq.podcini.storage.utils.ChapterUtils
import ac.mdiq.podcini.storage.utils.ImageResourceUtils
import ac.mdiq.podcini.net.utils.NetworkUtils.fetchHtmlSource import ac.mdiq.podcini.net.utils.NetworkUtils.fetchHtmlSource
import ac.mdiq.podcini.playback.PlaybackController.Companion.curSpeedMultiplier
import ac.mdiq.podcini.playback.base.InTheatre.curMedia
import ac.mdiq.podcini.playback.PlaybackController.Companion.curPosition import ac.mdiq.podcini.playback.PlaybackController.Companion.curPosition
import ac.mdiq.podcini.playback.PlaybackController.Companion.curSpeedMultiplier
import ac.mdiq.podcini.playback.PlaybackController.Companion.seekTo import ac.mdiq.podcini.playback.PlaybackController.Companion.seekTo
import ac.mdiq.podcini.playback.base.InTheatre.curMedia
import ac.mdiq.podcini.storage.database.Episodes.persistEpisode import ac.mdiq.podcini.storage.database.Episodes.persistEpisode
import ac.mdiq.podcini.storage.database.RealmDB.runOnIOScope import ac.mdiq.podcini.storage.database.RealmDB.runOnIOScope
import ac.mdiq.podcini.storage.model.Chapter import ac.mdiq.podcini.storage.model.*
import ac.mdiq.podcini.storage.model.Episode import ac.mdiq.podcini.storage.utils.ChapterUtils
import ac.mdiq.podcini.storage.model.EpisodeMedia import ac.mdiq.podcini.storage.utils.ImageResourceUtils
import ac.mdiq.podcini.storage.model.Playable
import ac.mdiq.podcini.storage.model.EmbeddedChapterImage
import ac.mdiq.podcini.ui.activity.MainActivity import ac.mdiq.podcini.ui.activity.MainActivity
import ac.mdiq.podcini.ui.fragment.SubscriptionsFragment.Companion
import ac.mdiq.podcini.ui.utils.ShownotesCleaner import ac.mdiq.podcini.ui.utils.ShownotesCleaner
import ac.mdiq.podcini.ui.view.ShownotesWebView import ac.mdiq.podcini.ui.view.ShownotesWebView
import ac.mdiq.podcini.util.DateFormatter import ac.mdiq.podcini.util.DateFormatter
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.FlowEvent import ac.mdiq.podcini.util.event.FlowEvent
import android.animation.Animator import android.animation.Animator
import android.animation.AnimatorListenerAdapter import android.animation.AnimatorListenerAdapter
@ -50,8 +46,9 @@ import coil.request.ErrorResult
import coil.request.ImageRequest import coil.request.ImageRequest
import com.google.android.material.bottomsheet.BottomSheetBehavior import com.google.android.material.bottomsheet.BottomSheetBehavior
import com.google.android.material.snackbar.Snackbar import com.google.android.material.snackbar.Snackbar
import kotlinx.coroutines.* import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import net.dankito.readability4j.Readability4J import net.dankito.readability4j.Readability4J
import org.apache.commons.lang3.StringUtils import org.apache.commons.lang3.StringUtils
@ -117,21 +114,24 @@ class PlayerDetailsFragment : Fragment() {
override fun onStart() { override fun onStart() {
Logd(TAG, "onStart()") Logd(TAG, "onStart()")
super.onStart() super.onStart()
procFlowEvents() // procFlowEvents()
} }
override fun onStop() { override fun onStop() {
Logd(TAG, "onStop()") Logd(TAG, "onStop()")
super.onStop() super.onStop()
cancelFlowEvents() // cancelFlowEvents()
} }
override fun onDestroyView() { override fun onDestroyView() {
super.onDestroyView() Logd(TAG, "onDestroyView")
_binding = null _binding = null
prevItem = null
currentItem = null
Logd(TAG, "Fragment destroyed") Logd(TAG, "Fragment destroyed")
shownoteView.removeAllViews() shownoteView.removeAllViews()
shownoteView.destroy() shownoteView.destroy()
super.onDestroyView()
} }
override fun onContextItemSelected(item: MenuItem): Boolean { override fun onContextItemSelected(item: MenuItem): Boolean {
@ -280,6 +280,7 @@ class PlayerDetailsFragment : Fragment() {
} }
private fun refreshChapterData(chapterIndex: Int) { private fun refreshChapterData(chapterIndex: Int) {
Logd(TAG, "in refreshChapterData $chapterIndex")
if (playable != null && chapterIndex > -1) { if (playable != null && chapterIndex > -1) {
if (playable!!.getPosition() > playable!!.getDuration() || chapterIndex >= playable!!.getChapters().size - 1) { if (playable!!.getPosition() > playable!!.getDuration() || chapterIndex >= playable!!.getChapters().size - 1) {
displayedChapterIndex = playable!!.getChapters().size - 1 displayedChapterIndex = playable!!.getChapters().size - 1
@ -409,25 +410,25 @@ class PlayerDetailsFragment : Fragment() {
savePreference() savePreference()
} }
private var eventSink: Job? = null // private var eventSink: Job? = null
private fun cancelFlowEvents() { // private fun cancelFlowEvents() {
eventSink?.cancel() // eventSink?.cancel()
eventSink = null // eventSink = null
} // }
private fun procFlowEvents() { // private fun procFlowEvents() {
if (eventSink != null) return // if (eventSink != null) return
eventSink = lifecycleScope.launch { // eventSink = lifecycleScope.launch {
EventFlow.events.collectLatest { event -> // EventFlow.events.collectLatest { event ->
Logd(TAG, "Received event: ${event.TAG}") // Logd(TAG, "Received event: ${event.TAG}")
when (event) { // when (event) {
is FlowEvent.PlaybackPositionEvent -> onEventMainThread(event) // is FlowEvent.PlaybackPositionEvent -> onPlaybackPositionEvent(event)
else -> {} // else -> {}
} // }
} // }
} // }
} // }
private fun onEventMainThread(event: FlowEvent.PlaybackPositionEvent) { fun onPlaybackPositionEvent(event: FlowEvent.PlaybackPositionEvent) {
if (playable?.getIdentifier() != event.media?.getIdentifier()) return if (playable?.getIdentifier() != event.media?.getIdentifier()) return
val newChapterIndex: Int = ChapterUtils.getCurrentChapterIndex(playable, event.position) val newChapterIndex: Int = ChapterUtils.getCurrentChapterIndex(playable, event.position)
if (newChapterIndex >= 0 && newChapterIndex != displayedChapterIndex) { if (newChapterIndex >= 0 && newChapterIndex != displayedChapterIndex) {

View File

@ -30,6 +30,7 @@ import ac.mdiq.podcini.ui.adapter.SelectableAdapter
import ac.mdiq.podcini.ui.dialog.ConfirmationDialog import ac.mdiq.podcini.ui.dialog.ConfirmationDialog
import ac.mdiq.podcini.ui.dialog.EpisodeSortDialog import ac.mdiq.podcini.ui.dialog.EpisodeSortDialog
import ac.mdiq.podcini.ui.dialog.SwitchQueueDialog import ac.mdiq.podcini.ui.dialog.SwitchQueueDialog
import ac.mdiq.podcini.ui.fragment.SubscriptionsFragment.Companion
import ac.mdiq.podcini.ui.utils.EmptyViewHandler import ac.mdiq.podcini.ui.utils.EmptyViewHandler
import ac.mdiq.podcini.ui.utils.LiftOnScrollListener import ac.mdiq.podcini.ui.utils.LiftOnScrollListener
import ac.mdiq.podcini.ui.view.EpisodesRecyclerView import ac.mdiq.podcini.ui.view.EpisodesRecyclerView
@ -301,8 +302,7 @@ import java.util.*
val item: Episode = event.episodes[i] val item: Episode = event.episodes[i]
val pos: Int = EpisodeUtil.indexOfItemWithId(queueItems, item.id) val pos: Int = EpisodeUtil.indexOfItemWithId(queueItems, item.id)
if (pos >= 0) { if (pos >= 0) {
queueItems.removeAt(pos) queueItems[pos] = item
queueItems.add(pos, item)
adapter?.notifyItemChangedCompat(pos) adapter?.notifyItemChangedCompat(pos)
refreshInfoBar() refreshInfoBar()
} }
@ -322,7 +322,8 @@ import java.util.*
val pos: Int = EpisodeUtil.indexOfItemWithDownloadUrl(queueItems.toList(), downloadUrl) val pos: Int = EpisodeUtil.indexOfItemWithDownloadUrl(queueItems.toList(), downloadUrl)
if (pos >= 0) { if (pos >= 0) {
val item = queueItems[pos] val item = queueItems[pos]
item.media?.downloaded = true // item.media?.downloaded = true
item.media?.setIsDownloaded()
adapter?.notifyItemChangedCompat(pos) adapter?.notifyItemChangedCompat(pos)
} }
} }
@ -380,12 +381,15 @@ import java.util.*
} }
override fun onDestroyView() { override fun onDestroyView() {
super.onDestroyView() Logd(TAG, "onDestroyView")
_binding = null _binding = null
queueItems = mutableListOf()
adapter?.endSelectMode() adapter?.endSelectMode()
adapter?.clearData()
adapter = null adapter = null
toolbar.setOnMenuItemClickListener(null) toolbar.setOnMenuItemClickListener(null)
toolbar.setOnLongClickListener(null) toolbar.setOnLongClickListener(null)
super.onDestroyView()
} }
private fun refreshToolbarState() { private fun refreshToolbarState() {

View File

@ -90,8 +90,8 @@ class QuickDiscoveryFragment : Fragment(), AdapterView.OnItemClickListener {
} }
override fun onDestroy() { override fun onDestroy() {
super.onDestroy()
_binding = null _binding = null
super.onDestroy()
} }
private var eventSink: Job? = null private var eventSink: Job? = null

View File

@ -2,6 +2,7 @@ package ac.mdiq.podcini.ui.fragment
import ac.mdiq.podcini.R import ac.mdiq.podcini.R
import ac.mdiq.podcini.storage.model.Episode import ac.mdiq.podcini.storage.model.Episode
import ac.mdiq.podcini.ui.fragment.SubscriptionsFragment.Companion
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
@ -46,6 +47,11 @@ import kotlin.math.min
cancelFlowEvents() cancelFlowEvents()
} }
override fun onDestroyView() {
episodeList.clear()
super.onDestroyView()
}
fun setEpisodes(episodeList_: MutableList<Episode>) { fun setEpisodes(episodeList_: MutableList<Episode>) {
episodeList.clear() episodeList.clear()
episodeList.addAll(episodeList_) episodeList.addAll(episodeList_)

View File

@ -19,6 +19,7 @@ import ac.mdiq.podcini.ui.actions.menuhandler.MenuItemUtils
import ac.mdiq.podcini.ui.activity.MainActivity import ac.mdiq.podcini.ui.activity.MainActivity
import ac.mdiq.podcini.ui.adapter.EpisodesAdapter import ac.mdiq.podcini.ui.adapter.EpisodesAdapter
import ac.mdiq.podcini.ui.adapter.SelectableAdapter import ac.mdiq.podcini.ui.adapter.SelectableAdapter
import ac.mdiq.podcini.ui.fragment.SubscriptionsFragment.Companion
import ac.mdiq.podcini.ui.utils.EmptyViewHandler import ac.mdiq.podcini.ui.utils.EmptyViewHandler
import ac.mdiq.podcini.ui.utils.LiftOnScrollListener import ac.mdiq.podcini.ui.utils.LiftOnScrollListener
import ac.mdiq.podcini.ui.view.EpisodesRecyclerView import ac.mdiq.podcini.ui.view.EpisodesRecyclerView
@ -185,8 +186,11 @@ import java.lang.ref.WeakReference
} }
override fun onDestroyView() { override fun onDestroyView() {
super.onDestroyView() Logd(TAG, "onDestroyView")
_binding = null _binding = null
adapter.clearData()
results = mutableListOf()
super.onDestroyView()
} }
private fun setupToolbar(toolbar: MaterialToolbar) { private fun setupToolbar(toolbar: MaterialToolbar) {
@ -282,8 +286,7 @@ import java.lang.ref.WeakReference
val item: Episode = event.episodes[i] val item: Episode = event.episodes[i]
val pos: Int = EpisodeUtil.indexOfItemWithId(results, item.id) val pos: Int = EpisodeUtil.indexOfItemWithId(results, item.id)
if (pos >= 0) { if (pos >= 0) {
results.removeAt(pos) results[pos] = item
results.add(pos, item)
adapter.notifyItemChangedCompat(pos) adapter.notifyItemChangedCompat(pos)
} }
i++ i++

View File

@ -210,8 +210,12 @@ class SubscriptionsFragment : Fragment(), Toolbar.OnMenuItemClickListener, Selec
} }
override fun onDestroyView() { override fun onDestroyView() {
super.onDestroyView() Logd(TAG, "onDestroyView")
feedList = mutableListOf()
feedListFiltered = mutableListOf()
adapter.clearData()
_binding = null _binding = null
super.onDestroyView()
} }
override fun onSaveInstanceState(outState: Bundle) { override fun onSaveInstanceState(outState: Bundle) {
@ -309,7 +313,6 @@ class SubscriptionsFragment : Fragment(), Toolbar.OnMenuItemClickListener, Selec
lifecycleScope.launch { lifecycleScope.launch {
try { try {
withContext(Dispatchers.IO) { withContext(Dispatchers.IO) {
feedList = getFeedList().toMutableList()
sortFeeds() sortFeeds()
resetTags() resetTags()
} }
@ -396,7 +399,8 @@ class SubscriptionsFragment : Fragment(), Toolbar.OnMenuItemClickListener, Selec
comparator(counterMap, dir) comparator(counterMap, dir)
} }
} }
synchronized(feedList) { feedList.sortWith(comparator) } val feedList_ = getFeedList().toMutableList()
synchronized(feedList_) { feedList = feedList_.sortedWith(comparator).toMutableList() }
} }
private fun counterMap(episodes: RealmResults<Episode>): Map<Long, Long> { private fun counterMap(episodes: RealmResults<Episode>): Map<Long, Long> {
@ -552,6 +556,9 @@ class SubscriptionsFragment : Fragment(), Toolbar.OnMenuItemClickListener, Selec
this.feedList = ArrayList() this.feedList = ArrayList()
setHasStableIds(true) setHasStableIds(true)
} }
fun clearData() {
feedList = listOf()
}
fun getItem(position: Int): Any { fun getItem(position: Int): Any {
return feedList[position] return feedList[position]
} }

View File

@ -23,6 +23,7 @@ import ac.mdiq.podcini.ui.activity.VideoplayerActivity
import ac.mdiq.podcini.ui.activity.VideoplayerActivity.Companion.videoMode import ac.mdiq.podcini.ui.activity.VideoplayerActivity.Companion.videoMode
import ac.mdiq.podcini.ui.activity.starter.MainActivityStarter import ac.mdiq.podcini.ui.activity.starter.MainActivityStarter
import ac.mdiq.podcini.ui.dialog.SkipPreferenceDialog import ac.mdiq.podcini.ui.dialog.SkipPreferenceDialog
import ac.mdiq.podcini.ui.fragment.SubscriptionsFragment.Companion
import ac.mdiq.podcini.ui.utils.PictureInPictureUtil import ac.mdiq.podcini.ui.utils.PictureInPictureUtil
import ac.mdiq.podcini.ui.utils.ShownotesCleaner import ac.mdiq.podcini.ui.utils.ShownotesCleaner
import ac.mdiq.podcini.ui.view.ShownotesWebView import ac.mdiq.podcini.ui.view.ShownotesWebView
@ -166,12 +167,13 @@ class VideoEpisodeFragment : Fragment(), OnSeekBarChangeListener {
} }
override fun onDestroyView() { override fun onDestroyView() {
super.onDestroyView() Logd(TAG, "onDestroyView")
root.removeView(webvDescription) root.removeView(webvDescription)
webvDescription.destroy() webvDescription.destroy()
_binding = null _binding = null
controller?.release() controller?.release()
controller = null // prevent leak controller = null // prevent leak
super.onDestroyView()
} }
private var eventSink: Job? = null private var eventSink: Job? = null

View File

@ -66,8 +66,8 @@ class FeedStatisticsFragment : Fragment() {
} }
override fun onDestroy() { override fun onDestroy() {
super.onDestroy()
_binding = null _binding = null
super.onDestroy()
} }
companion object { companion object {

View File

@ -12,6 +12,8 @@ import ac.mdiq.podcini.ui.activity.MainActivity
import ac.mdiq.podcini.ui.activity.starter.MainActivityStarter import ac.mdiq.podcini.ui.activity.starter.MainActivityStarter
import ac.mdiq.podcini.ui.dialog.ConfirmationDialog import ac.mdiq.podcini.ui.dialog.ConfirmationDialog
import ac.mdiq.podcini.ui.dialog.DatesFilterDialog import ac.mdiq.podcini.ui.dialog.DatesFilterDialog
import ac.mdiq.podcini.ui.fragment.SubscriptionsFragment
import ac.mdiq.podcini.ui.fragment.SubscriptionsFragment.Companion
import ac.mdiq.podcini.ui.statistics.PieChartView.PieChartData import ac.mdiq.podcini.ui.statistics.PieChartView.PieChartData
import ac.mdiq.podcini.util.Converter.shortLocalizedDuration import ac.mdiq.podcini.util.Converter.shortLocalizedDuration
import ac.mdiq.podcini.util.Logd import ac.mdiq.podcini.util.Logd
@ -92,8 +94,9 @@ class StatisticsFragment : Fragment() {
} }
override fun onDestroyView() { override fun onDestroyView() {
super.onDestroyView() Logd(TAG, "onDestroyView")
_binding = null _binding = null
super.onDestroyView()
} }
@Deprecated("Deprecated in Java") @Deprecated("Deprecated in Java")
@ -218,8 +221,9 @@ class StatisticsFragment : Fragment() {
cancelFlowEvents() cancelFlowEvents()
} }
override fun onDestroyView() { override fun onDestroyView() {
super.onDestroyView() Logd(TAG, "onDestroyView")
_binding = null _binding = null
super.onDestroyView()
} }
private var eventSink: Job? = null private var eventSink: Job? = null
private fun cancelFlowEvents() { private fun cancelFlowEvents() {
@ -370,8 +374,9 @@ class StatisticsFragment : Fragment() {
cancelFlowEvents() cancelFlowEvents()
} }
override fun onDestroyView() { override fun onDestroyView() {
super.onDestroyView() Logd(TAG, "onDestroyView")
_binding = null _binding = null
super.onDestroyView()
} }
private var eventSink: Job? = null private var eventSink: Job? = null
private fun cancelFlowEvents() { private fun cancelFlowEvents() {
@ -561,8 +566,9 @@ class StatisticsFragment : Fragment() {
return binding.root return binding.root
} }
override fun onDestroyView() { override fun onDestroyView() {
super.onDestroyView() Logd(TAG, "onDestroyView")
_binding = null _binding = null
super.onDestroyView()
} }
@Deprecated("Deprecated in Java") @Deprecated("Deprecated in Java")
override fun onPrepareOptionsMenu(menu: Menu) { override fun onPrepareOptionsMenu(menu: Menu) {

View File

@ -1,6 +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 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
@ -8,25 +9,25 @@ import ac.mdiq.podcini.storage.model.Episode
object LocalDeleteModal { object LocalDeleteModal {
fun showLocalFeedDeleteWarningIfNecessary(context: Context, items: Iterable<Episode>, deleteCommand: Runnable) { fun deleteEpisodesWarnLocal(context: Context, items: Iterable<Episode>) {
var anyLocalFeed = false val localItems: MutableList<Episode> = mutableListOf()
for (item in items) { for (item in items) {
if (item.feed?.isLocalFeed == true) { if (item.feed?.isLocalFeed == true) {
anyLocalFeed = true localItems.add(item)
break } else deleteMediaOfEpisode(context, item)
}
} }
if (!anyLocalFeed) { if (localItems.isNotEmpty()) {
deleteCommand.run() MaterialAlertDialogBuilder(context)
return .setTitle(R.string.delete_episode_label)
.setMessage(R.string.delete_local_feed_warning_body)
.setPositiveButton(R.string.delete_label) { dialog: DialogInterface?, which: Int ->
for (item in localItems) {
deleteMediaOfEpisode(context, item)
}
}
.setNegativeButton(R.string.cancel_label, null)
.show()
} }
MaterialAlertDialogBuilder(context)
.setTitle(R.string.delete_episode_label)
.setMessage(R.string.delete_local_feed_warning_body)
.setPositiveButton(R.string.delete_label) { dialog: DialogInterface?, which: Int -> deleteCommand.run() }
.setNegativeButton(R.string.cancel_label, null)
.show()
} }
} }

View File

@ -99,6 +99,7 @@ open class EpisodeViewHolder(private val activity: MainActivity, parent: ViewGro
container.alpha = if (item.isPlayed()) 0.7f else 1.0f container.alpha = if (item.isPlayed()) 0.7f else 1.0f
leftPadding.contentDescription = item.title leftPadding.contentDescription = item.title
binding.playedMark.visibility = View.GONE binding.playedMark.visibility = View.GONE
binding.txtvPubDate.setTextColor(getColorFromAttr(activity, com.google.android.material.R.attr.colorOnSurfaceVariant))
when { when {
item.isPlayed() -> { item.isPlayed() -> {
leftPadding.contentDescription = item.title + ". " + activity.getString(R.string.is_played) leftPadding.contentDescription = item.title + ". " + activity.getString(R.string.is_played)

View File

@ -155,6 +155,8 @@ sealed class FlowEvent {
data class AllEpisodesFilterEvent(val filterValues: Set<String?>?) : FlowEvent() data class AllEpisodesFilterEvent(val filterValues: Set<String?>?) : FlowEvent()
data class AllEpisodesSortEvent(val dummy: Unit = Unit) : FlowEvent()
data class EpisodeEvent(val episodes: List<Episode>) : FlowEvent() { data class EpisodeEvent(val episodes: List<Episode>) : FlowEvent() {
companion object { companion object {
fun updated(episodes: List<Episode>): EpisodeEvent { fun updated(episodes: List<Episode>): EpisodeEvent {

View File

@ -1,5 +1,7 @@
<vector android:height="24dp" <vector xmlns:android="http://schemas.android.com/apk/res/android"
android:viewportHeight="24" android:viewportWidth="24" android:height="24dp"
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android"> android:width="24dp"
android:viewportHeight="24"
android:viewportWidth="24">
<path android:fillColor="?attr/action_icon_color" android:pathData="M10,20v-6h4v6h5v-8h3L12,3 2,12h3v8z"/> <path android:fillColor="?attr/action_icon_color" android:pathData="M10,20v-6h4v6h5v-8h3L12,3 2,12h3v8z"/>
</vector> </vector>

View File

@ -1,5 +1,7 @@
<vector android:height="24dp" <vector xmlns:android="http://schemas.android.com/apk/res/android"
android:viewportHeight="24.0" android:viewportWidth="24.0" android:height="24dp"
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android"> android:width="24dp"
android:viewportHeight="24.0"
android:viewportWidth="24.0">
<path android:fillColor="#FFFFFFFF" android:pathData="M11,17h2v-6h-2v6zM12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM12,20c-4.41,0 -8,-3.59 -8,-8s3.59,-8 8,-8 8,3.59 8,8 -3.59,8 -8,8zM11,9h2L13,7h-2v2z"/> <path android:fillColor="#FFFFFFFF" android:pathData="M11,17h2v-6h-2v6zM12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM12,20c-4.41,0 -8,-3.59 -8,-8s3.59,-8 8,-8 8,3.59 8,8 -3.59,8 -8,8zM11,9h2L13,7h-2v2z"/>
</vector> </vector>

View File

@ -1,5 +1,9 @@
<vector android:height="24dp" android:tint="#000000" <vector xmlns:android="http://schemas.android.com/apk/res/android"
android:viewportHeight="24" android:viewportWidth="24" android:height="24dp"
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android"> android:width="24dp"
<path android:fillColor="?attr/action_icon_color" android:pathData="M12,5.69l5,4.5V18h-2v-6H9v6H7v-7.81l5,-4.5M12,3L2,12h3v8h6v-6h2v6h6v-8h3L12,3z"/> android:viewportHeight="24"
android:viewportWidth="24">
<path
android:fillColor="?attr/action_icon_color"
android:pathData="M12,5.69l5,4.5V18h-2v-6H9v6H7v-7.81l5,-4.5M12,3L2,12h3v8h6v-6h2v6h6v-8h3L12,3z"/>
</vector> </vector>

View File

@ -271,7 +271,7 @@
<string name="skip_episode_label">Skip episode</string> <string name="skip_episode_label">Skip episode</string>
<string name="reset_position">Reset playback position</string> <string name="reset_position">Reset playback position</string>
<string name="no_items_selected">No items selected</string> <string name="no_items_selected">No items selected</string>
<string name="delete_local_feed_warning_body">Deleting removes the episode from Podcini and deletes the media file from your device storage. It cannot be downloaded again through Podcini.</string> <string name="delete_local_feed_warning_body">Warning: you are deleting local episodes. It will delete the media files from your device storage. It cannot be downloaded again through Podcini. Continue?</string>
<!-- Download messages and labels --> <!-- Download messages and labels -->
<string name="download_successful">successful</string> <string name="download_successful">successful</string>

View File

@ -1,3 +1,13 @@
## 6.0.8
* feeds sorting dialog layout is set to single column due to potential long text issue
* fixed issue of not being able to copy text from player detailed view
* fixed issue of some menu icon not correctly shown in dark theme
* fixed issue of results not updated in views when deleting episodes
* fixed issue of downloaded episode still showing as New
* fixed sorting on All Episodes view
* added more garbage collections
## 6.0.7 ## 6.0.7
* feeds sorting is bi-directional and in the same style as episodes sorting * feeds sorting is bi-directional and in the same style as episodes sorting

View File

@ -0,0 +1,10 @@
Version 6.0.8 brings several changes:
* feeds sorting dialog layout is set to single column due to potential long text issue
* fixed issue of not being able to copy text from player detailed view
* fixed issue of some menu icon not correctly shown in dark theme
* fixed issue of results not updated in views when deleting episodes
* fixed issue of downloaded episode still showing as New
* fixed sorting on All Episodes view
* added more garbage collections