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
}
defaultConfig {
versionCode 3020207
versionName "6.0.7"
versionCode 3020208
versionName "6.0.8"
applicationId "ac.mdiq.podcini.R"
def commit = ""

View File

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

View File

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

View File

@ -605,8 +605,8 @@ class SynchronizationPreferencesFragment : PreferenceFragmentCompat() {
}
override fun onDestroy() {
super.onDestroy()
cancelFlowEvents()
super.onDestroy()
}
@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))
var localDelete = false
val url = media.fileUrl
var episode = episode
when {
url != null && url.startsWith("content://") -> {
// Local feed
@ -116,8 +117,8 @@ object Episodes {
EventFlow.postEvent(FlowEvent.MessageEvent(context.getString(R.string.delete_local_failed)))
return episode
}
upsertBlk(episode) {
it.media?.fileUrl = null
episode = upsertBlk(episode) {
it.media?.setfileUrlOrNull(null)
if (media.downloadUrl.isNullOrEmpty()) it.media = null
}
localDelete = true
@ -131,9 +132,9 @@ object Episodes {
EventFlow.postEvent(evt)
return episode
}
upsertBlk(episode) {
episode = upsertBlk(episode) {
it.media?.downloaded = false
it.media?.fileUrl = null
it.media?.setfileUrlOrNull(null)
it.media?.hasEmbeddedPicture = false
if (media.downloadUrl.isNullOrEmpty()) it.media = null
}
@ -209,10 +210,10 @@ object Episodes {
fun persistEpisodeMedia(media: EpisodeMedia) : Job {
Logd(TAG, "persistEpisodeMedia called")
return runOnIOScope {
val episode = media.episode
var episode = media.episode
if (episode != null) {
episode.media = media
upsert(episode) {}
episode = upsert(episode) {}
EventFlow.postEvent(FlowEvent.EpisodeEvent.updated(episode))
} else Log.e(TAG, "persistEpisodeMedia media.episode is null")
}

View File

@ -45,7 +45,7 @@ object RealmDB {
if (BuildConfig.DEBUG) {
val stackTrace = Thread.currentThread().stackTrace
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
}

View File

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

View File

@ -40,10 +40,10 @@ class Feed : RealmObject {
*/
@FullText
var customTitle: String? = null
set(value) {
field = if (value == null || value == eigenTitle) null
else value
}
// set(value) {
// field = if (value == null || value == eigenTitle) null
// else value
// }
var link: String? = null
@ -163,7 +163,7 @@ class Feed : RealmObject {
this.fileUrl = fileUrl
this.downloadUrl = downloadUrl
this.eigenTitle = title
this.customTitle = customTitle
setCustomTitle1(customTitle)
this.lastUpdate = lastUpdate
this.link = link
this.description = description
@ -210,6 +210,10 @@ class Feed : RealmObject {
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? {
return when {
!customTitle.isNullOrEmpty() -> customTitle

View File

@ -246,5 +246,4 @@ object ChapterUtils {
}
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)
}
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")
}
}
@ -57,18 +57,6 @@ class EpisodeMultiSelectHandler(private val activity: MainActivity, private val
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) {
for (item in items) {
Episodes.setFavorite(item, true)
@ -85,14 +73,8 @@ class EpisodeMultiSelectHandler(private val activity: MainActivity, private val
}
private fun deleteChecked(items: List<Episode>) {
var countHasMedia = 0
for (feedItem in items) {
if (feedItem.media != null && feedItem.media!!.downloaded) {
countHasMedia++
deleteMediaOfEpisode(activity, feedItem)
}
}
showMessage(R.plurals.deleted_multi_episode_batch_label, countHasMedia)
LocalDeleteModal.deleteEpisodesWarnLocal(activity, items)
showMessage(R.plurals.deleted_multi_episode_batch_label, items.size)
}
private fun showMessage(@PluralsRes msgId: Int, numItems: Int) {

View File

@ -1,11 +1,10 @@
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.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
class DeleteActionButton(item: Episode) : EpisodeActionButton(item) {
@ -16,7 +15,7 @@ class DeleteActionButton(item: Episode) : EpisodeActionButton(item) {
return R.drawable.ic_delete
}
@UnstableApi override fun onClick(context: Context) {
showLocalFeedDeleteWarningIfNecessary(context, listOf(item)) { deleteMediaOfEpisode(context, item) }
deleteEpisodesWarnLocal(context, listOf(item))
}
override val visibility: Int

View File

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

View File

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

View File

@ -1,13 +1,12 @@
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 androidx.fragment.app.Fragment
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 {
override fun getId(): String {
@ -29,9 +28,7 @@ class DeleteSwipeAction : SwipeAction {
@UnstableApi override fun performAction(item: Episode, fragment: Fragment, filter: EpisodeFilter) {
if (!item.isDownloaded && item.feed?.isLocalFeed != true) return
showLocalFeedDeleteWarningIfNecessary(fragment.requireContext(), listOf(item)) {
deleteMediaOfEpisode(fragment.requireContext(), item)
}
deleteEpisodesWarnLocal(fragment.requireContext(), listOf(item))
}
override fun willRemove(filter: EpisodeFilter, item: Episode): Boolean {

View File

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

View File

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

View File

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

View File

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

View File

@ -138,8 +138,8 @@ class SelectSubscriptionActivity : AppCompatActivity() {
}
override fun onDestroy() {
super.onDestroy()
_binding = null
super.onDestroy()
}
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.VariableSpeedDialog
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.utils.PictureInPictureUtil
import ac.mdiq.podcini.util.IntentUtils.openInBrowser
@ -136,12 +138,12 @@ class VideoplayerActivity : CastEnabledActivity() {
}
override fun onDestroy() {
super.onDestroy()
window.clearFlags(WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN)
window.clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
window.clearFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN)
window.clearFlags(WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN)
_binding = null
super.onDestroy()
}
@ -419,8 +421,9 @@ class VideoplayerActivity : CastEnabledActivity() {
}
override fun onDestroyView() {
super.onDestroyView()
Logd(TAG, "onDestroyView")
_binding = null
super.onDestroyView()
}
@UnstableApi private fun setupAudioTracks() {

View File

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

View File

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

View File

@ -17,7 +17,7 @@ class OnlineFeedsAdapter(private val context: Context, objects: List<PodcastSear
: ArrayAdapter<PodcastSearchResult?>(context, 0, objects) {
// 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 {
val podcast: PodcastSearchResult = data[position]
@ -65,6 +65,10 @@ class OnlineFeedsAdapter(private val context: Context, objects: List<PodcastSear
return view
}
fun clearData() {
data.clear()
}
internal class PodcastViewHolder(view: 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 ->
feed = unmanaged(feed)
val newTitle = binding.editText.text.toString()
feed.customTitle = newTitle
feed.setCustomTitle1(newTitle)
upsertBlk(feed) {}
}
.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.FilterDialogRowBinding
import ac.mdiq.podcini.storage.model.EpisodeFilter
import ac.mdiq.podcini.ui.fragment.SubscriptionsFragment.Companion.TAG
import ac.mdiq.podcini.util.Logd
import android.app.Dialog
import android.content.DialogInterface
@ -98,8 +99,9 @@ abstract class EpisodeFilterDialog : BottomSheetDialogFragment() {
}
override fun onDestroyView() {
super.onDestroyView()
Logd(TAG, "onDestroyView")
_binding = null
super.onDestroyView()
}
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.SortDialogItemBinding
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.content.DialogInterface
import android.os.Bundle
@ -98,8 +100,9 @@ open class EpisodeSortDialog : BottomSheetDialogFragment() {
}
override fun onDestroyView() {
super.onDestroyView()
Logd(TAG, "onDestroyView")
_binding = null
super.onDestroyView()
}
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.storage.model.FeedSortOrder
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.event.EventFlow
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? {
_binding = SortDialogBinding.inflate(inflater)
binding.gridLayout.columnCount = 1
populateList()
binding.keepSortedCheckbox.setOnCheckedChangeListener { _: CompoundButton?, _: Boolean -> this@FeedSortDialogNew.onSelectionChanged() }
return binding.root
@ -117,8 +120,9 @@ open class FeedSortDialogNew : BottomSheetDialogFragment() {
}
override fun onDestroyView() {
super.onDestroyView()
Logd(TAG, "onDestroyView")
_binding = null
super.onDestroyView()
}
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.databinding.ShareEpisodeDialogBinding
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() {
private lateinit var ctx: Context
@ -83,8 +85,9 @@ class ShareDialog : BottomSheetDialogFragment() {
}
override fun onDestroyView() {
super.onDestroyView()
Logd(TAG, "onDestroyView")
_binding = null
super.onDestroyView()
}
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.vibrate
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.util.Converter.getDurationStringLong
import ac.mdiq.podcini.util.Logd
import ac.mdiq.podcini.util.event.EventFlow
import ac.mdiq.podcini.util.event.FlowEvent
import android.app.Activity
@ -160,8 +162,9 @@ class SleepTimerDialog : DialogFragment() {
}
override fun onDestroyView() {
super.onDestroyView()
Logd(TAG, "onDestroyView")
_binding = null
super.onDestroyView()
}
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.FeedPreferences
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.util.Logd
import android.app.Dialog
@ -97,8 +99,9 @@ class TagSettingsDialog : DialogFragment() {
}
override fun onDestroyView() {
super.onDestroyView()
Logd(TAG, "onDestroyView")
_binding = null
super.onDestroyView()
}
@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.model.EpisodeMedia
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.view.PlaybackSpeedSeekBar
import ac.mdiq.podcini.util.Logd
@ -158,8 +160,9 @@ import java.util.*
}
override fun onDestroyView() {
super.onDestroyView()
Logd(TAG, "onDestroyView")
_binding = null
super.onDestroyView()
}
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.ui.activity.MainActivity
import ac.mdiq.podcini.ui.activity.OpmlImportActivity
import ac.mdiq.podcini.ui.fragment.SubscriptionsFragment.Companion
import ac.mdiq.podcini.util.Logd
import android.content.*
import android.net.Uri
@ -150,8 +151,9 @@ class AddFeedFragment : Fragment() {
}
override fun onDestroyView() {
super.onDestroyView()
Logd(TAG, "onDestroyView")
_binding = null
super.onDestroyView()
}
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.EpisodeSortDialog
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.event.EventFlow
import ac.mdiq.podcini.util.event.FlowEvent
@ -49,6 +50,11 @@ import kotlin.math.min
return root
}
override fun onDestroyView() {
allEpisodes = listOf()
super.onDestroyView()
}
override fun onStart() {
super.onStart()
procFlowEvents()
@ -115,6 +121,10 @@ import kotlin.math.min
Logd(TAG, "Received event: ${event.TAG}")
when (event) {
is FlowEvent.AllEpisodesFilterEvent -> onFilterChanged(event)
is FlowEvent.AllEpisodesSortEvent -> {
page = 1
loadItems()
}
else -> {}
}
}
@ -148,13 +158,14 @@ import kotlin.math.min
}
override fun onAddItem(title: Int, ascending: EpisodeSortOrder, descending: EpisodeSortOrder, ascendingIsDefault: Boolean) {
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)
}
override fun onSelectionChanged() {
super.onSelectionChanged()
allEpisodesSortOrder = sortOrder
EventFlow.postEvent(FlowEvent.FeedsSortedEvent())
EventFlow.postEvent(FlowEvent.AllEpisodesSortEvent())
}
}
@ -170,7 +181,6 @@ import kotlin.math.min
}
}
}
companion object {
val TAG = AllEpisodesFragment::class.simpleName ?: "Anonymous"
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.SleepTimerDialog
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.PlayButton
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))
onCollaped()
cardViewSeek = binding.cardViewSeek
initDetailedView()
return binding.root
}
// 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()")
fun initDetailedView() {
if (playerDetailsFragment == null) {
val fm = requireActivity().supportFragmentManager
val transaction = fm.beginTransaction()
playerDetailsFragment = PlayerDetailsFragment()
transaction.replace(R.id.itemDescription, playerDetailsFragment!!).commit()
}
isCollapsed = false
playerUI = playerUI2
playerUI?.updateUi(currentMedia)
playerUI?.butPlay?.setIsShowPlay(isShowPlay)
playerDetailsFragment?.updateInfo()
}
override fun onDestroyView() {
Logd(TAG, "Fragment destroyed")
_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() {
@ -340,7 +342,10 @@ 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)
is FlowEvent.PlaybackPositionEvent -> {
playerUI?.onPositionUpdate(event)
if (!isCollapsed) playerDetailsFragment?.onPlaybackPositionEvent(event)
}
is FlowEvent.SpeedChangedEvent -> playerUI?.updatePlaybackSpeedButton(event)
else -> {}
}
@ -528,8 +533,9 @@ class AudioPlayerFragment : Fragment(), SeekBar.OnSeekBarChangeListener, Toolbar
return binding.root
}
@OptIn(UnstableApi::class) override fun onDestroyView() {
super.onDestroyView()
Logd(TAG, "onDestroyView")
_binding = null
super.onDestroyView()
}
@UnstableApi
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.utils.LiftOnScrollListener
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.event.EventFlow
import ac.mdiq.podcini.util.event.FlowEvent
@ -305,9 +306,12 @@ import kotlinx.coroutines.flow.collectLatest
}
override fun onDestroyView() {
super.onDestroyView()
Logd(TAG, "onDestroyView")
_binding = null
adapter.clearData()
adapter.endSelectMode()
episodes.clear()
super.onDestroyView()
}
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.storage.model.Chapter
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.util.Converter.getDurationStringLocalized
import ac.mdiq.podcini.util.Converter.getDurationStringLong
@ -133,11 +134,11 @@ class ChaptersFragment : AppCompatDialogFragment() {
}
override fun onDestroyView() {
super.onDestroyView()
Logd(TAG, "onDestroyView")
_binding = null
controller?.release()
controller = null
super.onDestroyView()
}
private var eventSink: Job? = null

View File

@ -64,7 +64,6 @@ class DiscoveryFragment : Fragment(), Toolbar.OnMenuItemClickListener {
private var hidden = false
private var needsConfirm = false
/**
* Replace adapter data with provided search results from SearchTask.
*
@ -131,9 +130,12 @@ class DiscoveryFragment : Fragment(), Toolbar.OnMenuItemClickListener {
}
override fun onDestroy() {
super.onDestroy()
_binding = null
adapter?.clearData()
adapter = null
searchResults = null
topList = null
super.onDestroy()
}
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.activity.MainActivity
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.ThemeUtils
import ac.mdiq.podcini.util.Logd
@ -86,8 +87,10 @@ class DownloadLogFragment : BottomSheetDialogFragment(), OnItemClickListener, To
}
override fun onDestroyView() {
super.onDestroyView()
Logd(TAG, "onDestroyView")
_binding = null
downloadLog = listOf()
super.onDestroyView()
}
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.dialog.EpisodeSortDialog
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.LiftOnScrollListener
import ac.mdiq.podcini.ui.view.EpisodesRecyclerView
@ -163,10 +164,13 @@ import java.util.*
}
override fun onDestroyView() {
Logd(TAG, "onDestroyView")
_binding = null
adapter.endSelectMode()
adapter.clearData()
toolbar.setOnMenuItemClickListener(null)
toolbar.setOnLongClickListener(null)
episodes = mutableListOf()
super.onDestroyView()
}
@ -279,7 +283,7 @@ import java.util.*
val size: Int = event.episodes.size
while (i < size) {
val item: Episode = event.episodes[i]
val pos = EpisodeUtil.indexOfItemWithId(episodes.toList(), item.id)
val pos = EpisodeUtil.indexOfItemWithId(episodes, item.id)
if (pos >= 0) {
episodes.removeAt(pos)
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.storage.database.Episodes.persistEpisode
import ac.mdiq.podcini.storage.database.RealmDB.runOnIOScope
import ac.mdiq.podcini.ui.fragment.SubscriptionsFragment.Companion
import android.content.Context
import android.os.Bundle
import android.speech.tts.TextToSpeech
@ -257,7 +258,6 @@ class EpisodeHomeFragment : Fragment() {
}
@OptIn(UnstableApi::class) override fun onDestroyView() {
super.onDestroyView()
Logd(TAG, "onDestroyView")
cleatWebview(binding.webView)
cleatWebview(binding.readerView)
@ -265,6 +265,7 @@ class EpisodeHomeFragment : Fragment() {
tts?.stop()
tts?.shutdown()
tts = null
super.onDestroyView()
}
@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.menuhandler.EpisodeMenuHandler
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.ThemeUtils
import ac.mdiq.podcini.ui.view.ShownotesWebView
@ -230,14 +231,15 @@ import kotlin.math.max
}
@OptIn(UnstableApi::class) override fun onDestroyView() {
super.onDestroyView()
Logd(TAG, "onDestroyView")
binding.root.removeView(webvDescription)
episode = null
webvDescription.clearHistory()
webvDescription.clearCache(true)
webvDescription.clearView()
webvDescription.destroy()
_binding = null
super.onDestroyView()
}
@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.SelectableAdapter
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.TransitionEffect
import ac.mdiq.podcini.ui.view.viewholder.EpisodeViewHolder
@ -226,18 +227,23 @@ import java.util.concurrent.Semaphore
}
override fun onDestroyView() {
super.onDestroyView()
Logd(TAG, "onDestroyView")
_binding = null
_dialBinding = null
ioScope.cancel()
adapter.endSelectMode()
adapter.clearData()
feed = null
episodes = mutableListOf()
tts?.stop()
tts?.shutdown()
ttsWorking = false
ttsReady = false
tts = null
super.onDestroyView()
}
override fun onSaveInstanceState(outState: Bundle) {
@ -335,8 +341,8 @@ import java.util.concurrent.Semaphore
if (item.feedId != feed!!.id) continue
val pos: Int = EpisodeUtil.indexOfItemWithId(episodes, item.id)
if (pos >= 0) {
episodes.removeAt(pos)
episodes.add(pos, item)
Logd(TAG, "episode event: ${item.title} ${item.playState}")
episodes[pos] = item
adapter.notifyItemChangedCompat(pos)
}
i++
@ -352,8 +358,7 @@ import java.util.concurrent.Semaphore
if (item.feedId != feed!!.id) continue
val pos: Int = EpisodeUtil.indexOfItemWithId(episodes, item.id)
if (pos >= 0) {
episodes.removeAt(pos)
episodes.add(pos, item)
episodes[pos] = item
adapter.notifyItemChangedCompat(pos)
// episodes[pos].playState = item.playState
// adapter.notifyItemChangedCompat(pos)
@ -375,6 +380,7 @@ import java.util.concurrent.Semaphore
val item = event.episode
val pos: Int = EpisodeUtil.indexOfItemWithId(episodes, item.id)
if (pos >= 0) {
Logd(TAG, "played item: ${item.title} ${item.playState}")
episodes[pos] = item
adapter.notifyItemChangedCompat(pos)
// 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.FeedFunding
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.StatisticsFragment
import ac.mdiq.podcini.ui.utils.ToolbarIconTintManager
@ -204,9 +205,10 @@ class FeedInfoFragment : Fragment(), Toolbar.OnMenuItemClickListener {
}
override fun onDestroyView() {
super.onDestroyView()
Logd(TAG, "onDestroyView")
_binding = null
// feed = null
feed = Feed()
super.onDestroyView()
}
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.dialog.AuthenticationDialog
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.util.Logd
import android.content.Context
@ -65,8 +66,10 @@ class FeedSettingsFragment : Fragment() {
}
override fun onDestroyView() {
super.onDestroyView()
Logd(TAG, "onDestroyView")
_binding = null
feed = null
super.onDestroyView()
}
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.DatesFilterDialog
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.util.DateFormatter
import ac.mdiq.podcini.util.Logd
@ -86,6 +87,12 @@ import kotlin.math.min
cancelFlowEvents()
}
override fun onDestroyView() {
allHistory = listOf()
adapter.clearData()
super.onDestroyView()
}
@OptIn(UnstableApi::class) override fun onMenuItemClick(item: MenuItem): Boolean {
if (super.onOptionsItemSelected(item)) return true
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.starter.MainActivityStarter
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.utils.ThemeUtils
import ac.mdiq.podcini.util.Logd
@ -127,9 +128,10 @@ class NavDrawerFragment : Fragment(), OnSharedPreferenceChangeListener {
}
override fun onDestroyView() {
super.onDestroyView()
Logd(TAG, "onDestroyView")
_binding = null
prefs!!.unregisterOnSharedPreferenceChangeListener(this)
super.onDestroyView()
}
override fun onResume() {
@ -276,7 +278,6 @@ class NavDrawerFragment : Fragment(), OnSharedPreferenceChangeListener {
} else return false
}
@UnstableApi private fun bindNavView(title: String, position: Int, holder: NavHolder) {
Logd(TAG, "bindNavView called")
val context = activity ?: return
holder.title.text = title
// reset for re-use

View File

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

View File

@ -97,10 +97,11 @@ class OnlineSearchFragment : Fragment() {
}
override fun onDestroy() {
super.onDestroy()
_binding = null
// disposable?.dispose()
searchResults = null
adapter?.clearData()
adapter = null
super.onDestroy()
}
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.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.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.curSpeedMultiplier
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.RealmDB.runOnIOScope
import ac.mdiq.podcini.storage.model.Chapter
import ac.mdiq.podcini.storage.model.Episode
import ac.mdiq.podcini.storage.model.EpisodeMedia
import ac.mdiq.podcini.storage.model.Playable
import ac.mdiq.podcini.storage.model.EmbeddedChapterImage
import ac.mdiq.podcini.storage.model.*
import ac.mdiq.podcini.storage.utils.ChapterUtils
import ac.mdiq.podcini.storage.utils.ImageResourceUtils
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.view.ShownotesWebView
import ac.mdiq.podcini.util.DateFormatter
import ac.mdiq.podcini.util.Logd
import ac.mdiq.podcini.util.event.EventFlow
import ac.mdiq.podcini.util.event.FlowEvent
import android.animation.Animator
import android.animation.AnimatorListenerAdapter
@ -50,8 +46,9 @@ import coil.request.ErrorResult
import coil.request.ImageRequest
import com.google.android.material.bottomsheet.BottomSheetBehavior
import com.google.android.material.snackbar.Snackbar
import kotlinx.coroutines.*
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import net.dankito.readability4j.Readability4J
import org.apache.commons.lang3.StringUtils
@ -117,21 +114,24 @@ class PlayerDetailsFragment : Fragment() {
override fun onStart() {
Logd(TAG, "onStart()")
super.onStart()
procFlowEvents()
// procFlowEvents()
}
override fun onStop() {
Logd(TAG, "onStop()")
super.onStop()
cancelFlowEvents()
// cancelFlowEvents()
}
override fun onDestroyView() {
super.onDestroyView()
Logd(TAG, "onDestroyView")
_binding = null
prevItem = null
currentItem = null
Logd(TAG, "Fragment destroyed")
shownoteView.removeAllViews()
shownoteView.destroy()
super.onDestroyView()
}
override fun onContextItemSelected(item: MenuItem): Boolean {
@ -280,6 +280,7 @@ class PlayerDetailsFragment : Fragment() {
}
private fun refreshChapterData(chapterIndex: Int) {
Logd(TAG, "in refreshChapterData $chapterIndex")
if (playable != null && chapterIndex > -1) {
if (playable!!.getPosition() > playable!!.getDuration() || chapterIndex >= playable!!.getChapters().size - 1) {
displayedChapterIndex = playable!!.getChapters().size - 1
@ -409,25 +410,25 @@ class PlayerDetailsFragment : Fragment() {
savePreference()
}
private var eventSink: Job? = null
private fun cancelFlowEvents() {
eventSink?.cancel()
eventSink = null
}
private fun procFlowEvents() {
if (eventSink != null) return
eventSink = lifecycleScope.launch {
EventFlow.events.collectLatest { event ->
Logd(TAG, "Received event: ${event.TAG}")
when (event) {
is FlowEvent.PlaybackPositionEvent -> onEventMainThread(event)
else -> {}
}
}
}
}
// private var eventSink: Job? = null
// private fun cancelFlowEvents() {
// eventSink?.cancel()
// eventSink = null
// }
// private fun procFlowEvents() {
// if (eventSink != null) return
// eventSink = lifecycleScope.launch {
// EventFlow.events.collectLatest { event ->
// Logd(TAG, "Received event: ${event.TAG}")
// when (event) {
// is FlowEvent.PlaybackPositionEvent -> onPlaybackPositionEvent(event)
// else -> {}
// }
// }
// }
// }
private fun onEventMainThread(event: FlowEvent.PlaybackPositionEvent) {
fun onPlaybackPositionEvent(event: FlowEvent.PlaybackPositionEvent) {
if (playable?.getIdentifier() != event.media?.getIdentifier()) return
val newChapterIndex: Int = ChapterUtils.getCurrentChapterIndex(playable, event.position)
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.EpisodeSortDialog
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.LiftOnScrollListener
import ac.mdiq.podcini.ui.view.EpisodesRecyclerView
@ -301,8 +302,7 @@ import java.util.*
val item: Episode = event.episodes[i]
val pos: Int = EpisodeUtil.indexOfItemWithId(queueItems, item.id)
if (pos >= 0) {
queueItems.removeAt(pos)
queueItems.add(pos, item)
queueItems[pos] = item
adapter?.notifyItemChangedCompat(pos)
refreshInfoBar()
}
@ -322,7 +322,8 @@ import java.util.*
val pos: Int = EpisodeUtil.indexOfItemWithDownloadUrl(queueItems.toList(), downloadUrl)
if (pos >= 0) {
val item = queueItems[pos]
item.media?.downloaded = true
// item.media?.downloaded = true
item.media?.setIsDownloaded()
adapter?.notifyItemChangedCompat(pos)
}
}
@ -380,12 +381,15 @@ import java.util.*
}
override fun onDestroyView() {
super.onDestroyView()
Logd(TAG, "onDestroyView")
_binding = null
queueItems = mutableListOf()
adapter?.endSelectMode()
adapter?.clearData()
adapter = null
toolbar.setOnMenuItemClickListener(null)
toolbar.setOnLongClickListener(null)
super.onDestroyView()
}
private fun refreshToolbarState() {

View File

@ -90,8 +90,8 @@ class QuickDiscoveryFragment : Fragment(), AdapterView.OnItemClickListener {
}
override fun onDestroy() {
super.onDestroy()
_binding = null
super.onDestroy()
}
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.storage.model.Episode
import ac.mdiq.podcini.ui.fragment.SubscriptionsFragment.Companion
import ac.mdiq.podcini.util.Logd
import ac.mdiq.podcini.util.event.EventFlow
import ac.mdiq.podcini.util.event.FlowEvent
@ -46,6 +47,11 @@ import kotlin.math.min
cancelFlowEvents()
}
override fun onDestroyView() {
episodeList.clear()
super.onDestroyView()
}
fun setEpisodes(episodeList_: MutableList<Episode>) {
episodeList.clear()
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.adapter.EpisodesAdapter
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.LiftOnScrollListener
import ac.mdiq.podcini.ui.view.EpisodesRecyclerView
@ -185,8 +186,11 @@ import java.lang.ref.WeakReference
}
override fun onDestroyView() {
super.onDestroyView()
Logd(TAG, "onDestroyView")
_binding = null
adapter.clearData()
results = mutableListOf()
super.onDestroyView()
}
private fun setupToolbar(toolbar: MaterialToolbar) {
@ -282,8 +286,7 @@ import java.lang.ref.WeakReference
val item: Episode = event.episodes[i]
val pos: Int = EpisodeUtil.indexOfItemWithId(results, item.id)
if (pos >= 0) {
results.removeAt(pos)
results.add(pos, item)
results[pos] = item
adapter.notifyItemChangedCompat(pos)
}
i++

View File

@ -210,8 +210,12 @@ class SubscriptionsFragment : Fragment(), Toolbar.OnMenuItemClickListener, Selec
}
override fun onDestroyView() {
super.onDestroyView()
Logd(TAG, "onDestroyView")
feedList = mutableListOf()
feedListFiltered = mutableListOf()
adapter.clearData()
_binding = null
super.onDestroyView()
}
override fun onSaveInstanceState(outState: Bundle) {
@ -309,7 +313,6 @@ class SubscriptionsFragment : Fragment(), Toolbar.OnMenuItemClickListener, Selec
lifecycleScope.launch {
try {
withContext(Dispatchers.IO) {
feedList = getFeedList().toMutableList()
sortFeeds()
resetTags()
}
@ -396,7 +399,8 @@ class SubscriptionsFragment : Fragment(), Toolbar.OnMenuItemClickListener, Selec
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> {
@ -552,6 +556,9 @@ class SubscriptionsFragment : Fragment(), Toolbar.OnMenuItemClickListener, Selec
this.feedList = ArrayList()
setHasStableIds(true)
}
fun clearData() {
feedList = listOf()
}
fun getItem(position: Int): Any {
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.starter.MainActivityStarter
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.ShownotesCleaner
import ac.mdiq.podcini.ui.view.ShownotesWebView
@ -166,12 +167,13 @@ class VideoEpisodeFragment : Fragment(), OnSeekBarChangeListener {
}
override fun onDestroyView() {
super.onDestroyView()
Logd(TAG, "onDestroyView")
root.removeView(webvDescription)
webvDescription.destroy()
_binding = null
controller?.release()
controller = null // prevent leak
super.onDestroyView()
}
private var eventSink: Job? = null

View File

@ -66,8 +66,8 @@ class FeedStatisticsFragment : Fragment() {
}
override fun onDestroy() {
super.onDestroy()
_binding = null
super.onDestroy()
}
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.dialog.ConfirmationDialog
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.util.Converter.shortLocalizedDuration
import ac.mdiq.podcini.util.Logd
@ -92,8 +94,9 @@ class StatisticsFragment : Fragment() {
}
override fun onDestroyView() {
super.onDestroyView()
Logd(TAG, "onDestroyView")
_binding = null
super.onDestroyView()
}
@Deprecated("Deprecated in Java")
@ -218,8 +221,9 @@ class StatisticsFragment : Fragment() {
cancelFlowEvents()
}
override fun onDestroyView() {
super.onDestroyView()
Logd(TAG, "onDestroyView")
_binding = null
super.onDestroyView()
}
private var eventSink: Job? = null
private fun cancelFlowEvents() {
@ -370,8 +374,9 @@ class StatisticsFragment : Fragment() {
cancelFlowEvents()
}
override fun onDestroyView() {
super.onDestroyView()
Logd(TAG, "onDestroyView")
_binding = null
super.onDestroyView()
}
private var eventSink: Job? = null
private fun cancelFlowEvents() {
@ -561,8 +566,9 @@ class StatisticsFragment : Fragment() {
return binding.root
}
override fun onDestroyView() {
super.onDestroyView()
Logd(TAG, "onDestroyView")
_binding = null
super.onDestroyView()
}
@Deprecated("Deprecated in Java")
override fun onPrepareOptionsMenu(menu: Menu) {

View File

@ -1,6 +1,7 @@
package ac.mdiq.podcini.ui.utils
import ac.mdiq.podcini.R
import ac.mdiq.podcini.storage.database.Episodes.deleteMediaOfEpisode
import android.content.Context
import android.content.DialogInterface
import com.google.android.material.dialog.MaterialAlertDialogBuilder
@ -8,25 +9,25 @@ import ac.mdiq.podcini.storage.model.Episode
object LocalDeleteModal {
fun showLocalFeedDeleteWarningIfNecessary(context: Context, items: Iterable<Episode>, deleteCommand: Runnable) {
var anyLocalFeed = false
fun deleteEpisodesWarnLocal(context: Context, items: Iterable<Episode>) {
val localItems: MutableList<Episode> = mutableListOf()
for (item in items) {
if (item.feed?.isLocalFeed == true) {
anyLocalFeed = true
break
}
localItems.add(item)
} else deleteMediaOfEpisode(context, item)
}
if (!anyLocalFeed) {
deleteCommand.run()
return
if (localItems.isNotEmpty()) {
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 ->
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
leftPadding.contentDescription = item.title
binding.playedMark.visibility = View.GONE
binding.txtvPubDate.setTextColor(getColorFromAttr(activity, com.google.android.material.R.attr.colorOnSurfaceVariant))
when {
item.isPlayed() -> {
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 AllEpisodesSortEvent(val dummy: Unit = Unit) : FlowEvent()
data class EpisodeEvent(val episodes: List<Episode>) : FlowEvent() {
companion object {
fun updated(episodes: List<Episode>): EpisodeEvent {

View File

@ -1,5 +1,7 @@
<vector android:height="24dp"
android:viewportHeight="24" android:viewportWidth="24"
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:height="24dp"
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"/>
</vector>

View File

@ -1,5 +1,7 @@
<vector android:height="24dp"
android:viewportHeight="24.0" android:viewportWidth="24.0"
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:height="24dp"
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"/>
</vector>

View File

@ -1,5 +1,9 @@
<vector android:height="24dp" android:tint="#000000"
android:viewportHeight="24" android:viewportWidth="24"
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<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 xmlns:android="http://schemas.android.com/apk/res/android"
android:height="24dp"
android:width="24dp"
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>

View File

@ -271,7 +271,7 @@
<string name="skip_episode_label">Skip episode</string>
<string name="reset_position">Reset playback position</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 -->
<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
* 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