6.3.3 commit

This commit is contained in:
Xilin Jia 2024-08-03 12:51:17 +01:00
parent abdbf3dabd
commit e5188bc998
30 changed files with 120 additions and 87 deletions

View File

@ -6,7 +6,7 @@ An open source podcast instrument, attuned to Puccini ![Puccini](./images/Puccin
[<img src="https://fdroid.gitlab.io/artwork/badge/get-it-on.png"
alt="Get it on F-Droid"
height="80">](https://f-droid.org/packages/ac.mdiq.podcini/)
height="80">](https://f-droid.org/packages/ac.mdiq.podcini.R/)
Or download the latest APK from the [Releases Section](https://github.com/XilinJia/Podcini/releases/latest).
## Announcement

View File

@ -31,8 +31,8 @@ android {
testApplicationId "ac.mdiq.podcini.tests"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
versionCode 3020226
versionName "6.3.2"
versionCode 3020227
versionName "6.3.3"
applicationId "ac.mdiq.podcini.R"
def commit = ""

View File

@ -165,7 +165,7 @@ class NavigationDrawerTest {
hidden = hiddenDrawerItems?.filterNotNull()?: listOf()
Assert.assertEquals(2, hidden.size.toLong())
Assert.assertTrue(hidden.contains(QueueFragment.TAG))
Assert.assertTrue(hidden.contains(QueuesFragment.TAG))
Assert.assertTrue(hidden.contains(HistoryFragment.TAG))
}

View File

@ -8,7 +8,7 @@ import androidx.test.espresso.matcher.ViewMatchers
import androidx.test.ext.junit.runners.AndroidJUnit4
import ac.mdiq.podcini.R
import ac.mdiq.podcini.ui.activity.MainActivity
import ac.mdiq.podcini.ui.fragment.QueueFragment
import ac.mdiq.podcini.ui.fragment.QueuesFragment
import de.test.podcini.EspressoTestUtils
import de.test.podcini.NthMatcher
import org.hamcrest.CoreMatchers
@ -22,7 +22,7 @@ import org.junit.runner.RunWith
* User interface tests for queue fragment.
*/
@RunWith(AndroidJUnit4::class)
class PlayQueueFragmentTest {
class PlayQueuesFragmentTest {
@Rule
var activityRule: IntentsTestRule<MainActivity> = IntentsTestRule(MainActivity::class.java, false, false)
@ -30,7 +30,7 @@ class PlayQueueFragmentTest {
fun setUp() {
EspressoTestUtils.clearPreferences()
EspressoTestUtils.clearDatabase()
EspressoTestUtils.setLaunchScreen(QueueFragment.TAG)
EspressoTestUtils.setLaunchScreen(QueuesFragment.TAG)
activityRule.launchActivity(Intent())
}

View File

@ -14,6 +14,8 @@ import ac.mdiq.podcini.preferences.UserPreferences.appPrefs
import ac.mdiq.podcini.storage.database.Episodes
import ac.mdiq.podcini.storage.database.LogsAndStats
import ac.mdiq.podcini.storage.database.Queues
import ac.mdiq.podcini.storage.database.RealmDB.upsert
import ac.mdiq.podcini.storage.database.RealmDB.upsertBlk
import ac.mdiq.podcini.storage.model.DownloadResult
import ac.mdiq.podcini.storage.model.Episode
import ac.mdiq.podcini.storage.model.EpisodeMedia
@ -377,12 +379,15 @@ class DownloadServiceInterfaceImpl : DownloadServiceInterface() {
try {
// we've received the media, we don't want to autodownload it again
if (item != null) {
item.disableAutoDownload()
item = upsertBlk(item) {
it.disableAutoDownload()
}
EventFlow.postEvent(FlowEvent.EpisodeEvent.updated(item))
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
Episodes.persistEpisode(item)
// Episodes.persistEpisode(item)
// TODO: should use different event?
if (broadcastUnreadStateUpdate) EventFlow.postEvent(FlowEvent.EpisodePlayedEvent(item))
}

View File

@ -65,7 +65,7 @@ abstract class PlaybackController(private val activity: FragmentActivity) {
private var prevStatus = PlayerStatus.STOPPED
private val statusUpdate: BroadcastReceiver = object : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
Logd(TAG, "BroadcastReceiver onReceive")
Log.d(TAG, "statusUpdate onReceive called with action: ${intent.action}")
if (playbackService != null && mPlayerInfo != null) {
val info = mPlayerInfo!!
Logd(TAG, "statusUpdate onReceive $prevStatus ${MediaPlayerBase.status} ${info.playerStatus} ${curMedia?.getIdentifier()} ${info.playable?.getIdentifier()}.")
@ -88,6 +88,7 @@ abstract class PlaybackController(private val activity: FragmentActivity) {
private val notificationReceiver: BroadcastReceiver = object : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
Log.d(TAG, "notificationReceiver onReceive called with action: ${intent.action}")
val type = intent.getIntExtra(PlaybackService.EXTRA_NOTIFICATION_TYPE, -1)
val code = intent.getIntExtra(PlaybackService.EXTRA_NOTIFICATION_CODE, -1)
if (code == -1 || type == -1) {

View File

@ -132,6 +132,7 @@ class PlaybackService : MediaSessionService() {
private val autoStateUpdated: BroadcastReceiver = object : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
Log.d(TAG, "autoStateUpdated onReceive called with action: ${intent.action}")
val status = intent.getStringExtra("media_connection_status")
val isConnectedToCar = "media_connected" == status
Logd(TAG, "Received Auto Connection update: $status")
@ -164,6 +165,7 @@ class PlaybackService : MediaSessionService() {
// Don't pause playback after we just started, just because the receiver
// delivers the current headset state (instead of a change)
if (isInitialStickyBroadcast) return
Log.d(TAG, "headsetDisconnected onReceive called with action: ${intent.action}")
if (intent.action == Intent.ACTION_HEADSET_PLUG) {
val state = intent.getIntExtra("state", -1)
@ -183,6 +185,7 @@ class PlaybackService : MediaSessionService() {
private val bluetoothStateUpdated: BroadcastReceiver = object : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
Log.d(TAG, "bluetoothStateUpdated onReceive called with action: ${intent.action}")
if (intent.action == BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED) {
val state = intent.getIntExtra(BluetoothA2dp.EXTRA_STATE, -1)
if (state == BluetoothA2dp.STATE_CONNECTED) {
@ -196,6 +199,7 @@ class PlaybackService : MediaSessionService() {
private val audioBecomingNoisy: BroadcastReceiver = object : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
// sound is about to change, eg. bluetooth -> speaker
Log.d(TAG, "audioBecomingNoisy onReceive called with action: ${intent.action}")
Logd(TAG, "Pausing playback because audio is becoming noisy")
pauseIfPauseOnDisconnect()
}
@ -468,6 +472,7 @@ class PlaybackService : MediaSessionService() {
private val shutdownReceiver: BroadcastReceiver = object : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
Log.d(TAG, "shutdownReceiver onReceive called with action: ${intent.action}")
if (intent.action == ACTION_SHUTDOWN_PLAYBACK_SERVICE)
EventFlow.postEvent(FlowEvent.PlaybackServiceEvent(FlowEvent.PlaybackServiceEvent.Action.SERVICE_SHUT_DOWN))
}
@ -524,7 +529,7 @@ class PlaybackService : MediaSessionService() {
val pendingIntent = PendingIntent.getActivity(this, 0, intent, FLAG_IMMUTABLE or FLAG_UPDATE_CURRENT)
mediaSession = MediaSession.Builder(applicationContext, LocalMediaPlayer.exoPlayer!!)
.setSessionActivity(pendingIntent)
.setCallback(MyCallback())
.setCallback(MyMediaSessionCallback())
.setCustomLayout(notificationCustomButtons)
.build()
}
@ -589,18 +594,18 @@ class PlaybackService : MediaSessionService() {
return mediaSession?.player?.playbackState != STATE_IDLE && mediaSession?.player?.playbackState != STATE_ENDED
}
private inner class MyCallback : MediaSession.Callback {
private inner class MyMediaSessionCallback : MediaSession.Callback {
override fun onConnect(session: MediaSession, controller: MediaSession.ControllerInfo): MediaSession.ConnectionResult {
Logd(TAG, "in MyCallback onConnect")
Logd(TAG, "in MyMediaSessionCallback onConnect")
val sessionCommands = MediaSession.ConnectionResult.DEFAULT_SESSION_COMMANDS.buildUpon()
// .add(NotificationCustomButton.REWIND)
// .add(NotificationCustomButton.FORWARD)
when {
session.isMediaNotificationController(controller) -> {
Logd(TAG, "MyCallback onConnect isMediaNotificationController")
Logd(TAG, "MyMediaSessionCallback onConnect isMediaNotificationController")
val playerCommands = MediaSession.ConnectionResult.DEFAULT_PLAYER_COMMANDS.buildUpon()
notificationCustomButtons.forEach { commandButton ->
Logd(TAG, "MyCallback onConnect commandButton ${commandButton.displayName}")
Logd(TAG, "MyMediaSessionCallback onConnect commandButton ${commandButton.displayName}")
commandButton.sessionCommand?.let(sessionCommands::add)
}
return MediaSession.ConnectionResult.accept(
@ -609,29 +614,27 @@ class PlaybackService : MediaSessionService() {
)
}
session.isAutoCompanionController(controller) -> {
Logd(TAG, "MyCallback onConnect isAutoCompanionController")
Logd(TAG, "MyMediaSessionCallback onConnect isAutoCompanionController")
return MediaSession.ConnectionResult.AcceptedResultBuilder(session)
.setAvailableSessionCommands(sessionCommands.build())
.build()
}
else -> {
Logd(TAG, "MyCallback onConnect other controller")
Logd(TAG, "MyMediaSessionCallback onConnect other controller")
return MediaSession.ConnectionResult.AcceptedResultBuilder(session).build()
}
}
}
override fun onPostConnect(session: MediaSession, controller: MediaSession.ControllerInfo) {
super.onPostConnect(session, controller)
Logd(TAG, "MyCallback onPostConnect")
Logd(TAG, "MyMediaSessionCallback onPostConnect")
if (notificationCustomButtons.isNotEmpty()) {
mediaSession?.setCustomLayout(notificationCustomButtons)
// mediaSession?.setCustomLayout(customMediaNotificationProvider.notificationMediaButtons)
}
}
override fun onCustomCommand(session: MediaSession, controller: MediaSession.ControllerInfo, customCommand: SessionCommand, args: Bundle): ListenableFuture<SessionResult> {
Logd(TAG, "MyCallback onCustomCommand ${customCommand.customAction}")
Log.d(TAG, "onCustomCommand ${customCommand.customAction}")
/* Handling custom command buttons from player notification. */
when (customCommand.customAction) {
NotificationCustomButton.REWIND.customAction -> mPlayer?.seekDelta(-rewindSecs * 1000)
@ -640,9 +643,8 @@ class PlaybackService : MediaSessionService() {
}
return Futures.immediateFuture(SessionResult(SessionResult.RESULT_SUCCESS))
}
override fun onPlaybackResumption(mediaSession: MediaSession, controller: MediaSession.ControllerInfo): ListenableFuture<MediaSession.MediaItemsWithStartPosition> {
Logd(TAG, "MyCallback onPlaybackResumption ")
Logd(TAG, "MyMediaSessionCallback onPlaybackResumption ")
val settable = SettableFuture.create<MediaSession.MediaItemsWithStartPosition>()
// scope.launch {
// // Your app is responsible for storing the playlist and the start position
@ -652,11 +654,10 @@ class PlaybackService : MediaSessionService() {
// }
return settable
}
override fun onMediaButtonEvent(mediaSession: MediaSession, controller: MediaSession.ControllerInfo, intent: Intent): Boolean {
val keyEvent = if (Build.VERSION.SDK_INT >= VERSION_CODES.TIRAMISU) intent.extras!!.getParcelable(EXTRA_KEY_EVENT, KeyEvent::class.java)
else intent.extras!!.getParcelable(EXTRA_KEY_EVENT) as? KeyEvent
Logd(TAG, "MyCallback onMediaButtonEvent ${keyEvent?.keyCode}")
Log.d(TAG, "onMediaButtonEvent ${keyEvent?.keyCode}")
if (keyEvent != null && keyEvent.action == KeyEvent.ACTION_DOWN && keyEvent.repeatCount == 0) {
val keyCode = keyEvent.keyCode
@ -693,7 +694,7 @@ class PlaybackService : MediaSessionService() {
val hardwareButton = intent?.getBooleanExtra(MediaButtonReceiver.EXTRA_HARDWAREBUTTON, false) ?: false
val playable = curMedia
Logd(TAG, "onStartCommand flags=$flags startId=$startId keycode=$keycode customAction=$customAction hardwareButton=$hardwareButton action=${intent?.action.toString()} ${playable?.getEpisodeTitle()}")
Log.d(TAG, "onStartCommand flags=$flags startId=$startId keycode=$keycode customAction=$customAction hardwareButton=$hardwareButton action=${intent?.action.toString()} ${playable?.getEpisodeTitle()}")
if (keycode == -1 && playable == null && customAction == null) {
Log.e(TAG, "onStartCommand PlaybackService was started with no arguments, return")
return START_NOT_STICKY

View File

@ -15,7 +15,7 @@ class SwipePreferencesFragment : PreferenceFragmentCompat() {
addPreferencesFromResource(R.xml.preferences_swipe)
findPreference<Preference>(Prefs.prefSwipeQueue.name)?.onPreferenceClickListener = Preference.OnPreferenceClickListener {
SwipeActionsDialog(requireContext(), QueueFragment.TAG).show(object : SwipeActionsDialog.Callback {
SwipeActionsDialog(requireContext(), QueuesFragment.TAG).show(object : SwipeActionsDialog.Callback {
override fun onCall() {}
})
true

View File

@ -1,16 +1,20 @@
package ac.mdiq.podcini.receiver
import ac.mdiq.podcini.net.download.NetworkConnectionChangeHandler.networkChangedDetected
import ac.mdiq.podcini.playback.service.PlaybackService
import ac.mdiq.podcini.playback.service.PlaybackService.Companion
import ac.mdiq.podcini.util.Logd
import ac.mdiq.podcini.util.config.ClientConfigurator
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.net.ConnectivityManager
import android.util.Log
import androidx.media3.common.util.UnstableApi
class ConnectivityActionReceiver : BroadcastReceiver() {
@UnstableApi override fun onReceive(context: Context, intent: Intent) {
Log.d(TAG, "onReceive called with action: ${intent.action}")
if (intent.action == ConnectivityManager.CONNECTIVITY_ACTION) {
Logd(TAG, "Received intent")

View File

@ -1,5 +1,7 @@
package ac.mdiq.podcini.receiver
import ac.mdiq.podcini.playback.service.PlaybackService
import ac.mdiq.podcini.playback.service.PlaybackService.Companion
import ac.mdiq.podcini.util.Logd
import android.app.PendingIntent
import android.content.BroadcastReceiver
@ -18,7 +20,7 @@ import ac.mdiq.podcini.util.config.ClientConfigurator
class MediaButtonReceiver : BroadcastReceiver() {
@UnstableApi
override fun onReceive(context: Context, intent: Intent) {
Logd(TAG, "Received intent")
Log.d(TAG, "onReceive called with action: ${intent.action}")
if (intent.extras == null) return
val event = intent.extras!![Intent.EXTRA_KEY_EVENT] as? KeyEvent

View File

@ -9,6 +9,7 @@ import ac.mdiq.podcini.net.download.serviceinterface.DownloadServiceInterface
import ac.mdiq.podcini.preferences.UserPreferences.isEnableAutodownloadOnBattery
import ac.mdiq.podcini.storage.algorithms.AutoDownloads.autodownloadEpisodeMedia
import ac.mdiq.podcini.util.Logd
import android.util.Log
// modified from http://developer.android.com/training/monitoring-device-state/battery-monitoring.html
// and ConnectivityActionReceiver.java
@ -19,7 +20,7 @@ class PowerConnectionReceiver : BroadcastReceiver() {
@UnstableApi override fun onReceive(context: Context, intent: Intent) {
val action = intent.action
Logd(TAG, "charging intent: $action")
Log.d(TAG, "onReceive charging intent: $action")
ClientConfigurator.initialize(context)
if (Intent.ACTION_POWER_CONNECTED == action) {

View File

@ -2,6 +2,7 @@ package ac.mdiq.podcini.receiver
import ac.mdiq.podcini.R
import ac.mdiq.podcini.net.feed.FeedUpdateManager.runOnce
import ac.mdiq.podcini.receiver.MediaButtonReceiver.Companion
import ac.mdiq.podcini.storage.database.Feeds.updateFeed
import ac.mdiq.podcini.storage.model.Feed
import ac.mdiq.podcini.util.Logd
@ -19,6 +20,7 @@ import androidx.media3.common.util.UnstableApi
class SPAReceiver : BroadcastReceiver() {
@UnstableApi override fun onReceive(context: Context, intent: Intent) {
if (intent.action != ACTION_SP_APPS_QUERY_FEEDS_REPSONSE) return
Log.d(TAG, "onReceive called with action: ${intent.action}")
Logd(TAG, "Received SP_APPS_QUERY_RESPONSE")
if (!intent.hasExtra(ACTION_SP_APPS_QUERY_FEEDS_REPSONSE_FEEDS_EXTRA)) {

View File

@ -1,18 +1,16 @@
package ac.mdiq.podcini.ui.actions.actionbutton
import ac.mdiq.podcini.R
import ac.mdiq.podcini.net.download.serviceinterface.DownloadServiceInterface
import ac.mdiq.podcini.preferences.UserPreferences.isEnableAutodownload
import ac.mdiq.podcini.storage.database.RealmDB.upsertBlk
import ac.mdiq.podcini.storage.model.Episode
import ac.mdiq.podcini.util.event.EventFlow
import ac.mdiq.podcini.util.event.FlowEvent
import android.content.Context
import androidx.annotation.DrawableRes
import androidx.annotation.StringRes
import androidx.media3.common.util.UnstableApi
import ac.mdiq.podcini.R
import ac.mdiq.podcini.storage.model.Episode
import ac.mdiq.podcini.net.download.serviceinterface.DownloadServiceInterface
import ac.mdiq.podcini.preferences.UserPreferences.isEnableAutodownload
import ac.mdiq.podcini.storage.database.Episodes.persistEpisode
import ac.mdiq.podcini.storage.database.RealmDB.upsert
import ac.mdiq.podcini.storage.database.RealmDB.upsertBlk
import ac.mdiq.podcini.util.event.EventFlow
import ac.mdiq.podcini.util.event.FlowEvent
class CancelDownloadActionButton(item: Episode) : EpisodeActionButton(item) {
@StringRes

View File

@ -2,16 +2,16 @@ package ac.mdiq.podcini.ui.actions.actionbutton
import ac.mdiq.podcini.R
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.storage.database.RealmDB.upsertBlk
import ac.mdiq.podcini.storage.model.Episode
import ac.mdiq.podcini.storage.model.EpisodeMedia
import ac.mdiq.podcini.storage.utils.AudioMediaTools.mergeAudios
import ac.mdiq.podcini.storage.utils.FilesUtils.getMediafilePath
import ac.mdiq.podcini.storage.utils.FilesUtils.getMediafilename
import ac.mdiq.podcini.ui.fragment.FeedEpisodesFragment.Companion.tts
import ac.mdiq.podcini.ui.fragment.FeedEpisodesFragment.Companion.ttsReady
import ac.mdiq.podcini.ui.fragment.FeedEpisodesFragment.Companion.ttsWorking
import ac.mdiq.podcini.storage.utils.AudioMediaTools.mergeAudios
import ac.mdiq.podcini.util.Logd
import ac.mdiq.podcini.util.event.EventFlow
import ac.mdiq.podcini.util.event.FlowEvent
@ -64,8 +64,10 @@ class TTSActionButton(item: Episode) : EpisodeActionButton(item) {
val htmlSource = fetchHtmlSource(url)
val article = Readability4J(item.link!!, htmlSource).parse()
readerText = article.textContent
item.setTranscriptIfLonger(article.contentWithDocumentsCharsetOrUtf8)
persistEpisode(item)
item = upsertBlk(item) {
it.setTranscriptIfLonger(article.contentWithDocumentsCharsetOrUtf8)
}
// persistEpisode(item)
Logd(TAG, "readability4J: ${readerText?.substring(max(0, readerText!!.length-100), readerText!!.length)}")
} else readerText = HtmlCompat.fromHtml(item.transcript!!, HtmlCompat.FROM_HTML_MODE_COMPACT).toString()
processing = 0.1f
@ -139,10 +141,11 @@ class TTSActionButton(item: Episode) : EpisodeActionButton(item) {
media.fileUrl = mFilename
// media.downloaded = true
media.setIsDownloaded()
item.media = media
// DBWriter.persistFeedMedia(media)
item.setTranscriptIfLonger(readerText)
persistEpisode(item)
item = upsertBlk(item) {
it.media = media
it.setTranscriptIfLonger(readerText)
}
// persistEpisode(item)
}
for (p in parts) {
val f = File(p)

View File

@ -140,7 +140,7 @@ object EpisodeMenuHandler {
LocalDeleteModal.deleteEpisodesWarnLocal(context, listOf(selectedItem))
}
R.id.mark_read_item -> {
selectedItem.setPlayed(true)
// selectedItem.setPlayed(true)
setPlayState(Episode.PLAYED, true, selectedItem)
if (selectedItem.feed?.isLocalFeed != true && (isProviderConnected || wifiSyncEnabledKey)) {
val media: EpisodeMedia? = selectedItem.media
@ -157,7 +157,7 @@ object EpisodeMenuHandler {
}
}
R.id.mark_unread_item -> {
selectedItem.setPlayed(false)
// selectedItem.setPlayed(false)
setPlayState(Episode.UNPLAYED, false, selectedItem)
if (needSynch() && selectedItem.feed?.isLocalFeed != true && selectedItem.media != null) {
val actionNew: EpisodeAction = EpisodeAction.Builder(selectedItem, EpisodeAction.NEW)

View File

@ -7,7 +7,7 @@ import ac.mdiq.podcini.ui.dialog.SwipeActionsDialog
import ac.mdiq.podcini.ui.fragment.AllEpisodesFragment
import ac.mdiq.podcini.ui.fragment.DownloadsFragment
import ac.mdiq.podcini.ui.fragment.HistoryFragment
import ac.mdiq.podcini.ui.fragment.QueueFragment
import ac.mdiq.podcini.ui.fragment.QueuesFragment
import ac.mdiq.podcini.ui.utils.ThemeUtils.getColorFromAttr
import ac.mdiq.podcini.ui.view.EpisodeViewHolder
import ac.mdiq.podcini.util.event.EventFlow
@ -233,7 +233,7 @@ open class SwipeActions(dragDirs: Int, private val fragment: Fragment, private v
@OptIn(UnstableApi::class) @JvmStatic
fun getPrefsWithDefaults(tag: String): Actions {
val defaultActions = when (tag) {
QueueFragment.TAG -> NO_ACTION + "," + NO_ACTION
QueuesFragment.TAG -> NO_ACTION + "," + NO_ACTION
DownloadsFragment.TAG -> NO_ACTION + "," + NO_ACTION
HistoryFragment.TAG -> NO_ACTION + "," + NO_ACTION
AllEpisodesFragment.TAG -> NO_ACTION + "," + NO_ACTION

View File

@ -128,7 +128,7 @@ class MainActivity : CastEnabledActivity() {
EpisodesRecyclerView.getSharedPrefs(this@MainActivity)
NavDrawerFragment.getSharedPrefs(this@MainActivity)
SwipeActions.getSharedPrefs(this@MainActivity)
QueueFragment.getSharedPrefs(this@MainActivity)
QueuesFragment.getSharedPrefs(this@MainActivity)
// updateFeedMap()
buildTags()
monitorFeeds()
@ -419,7 +419,7 @@ class MainActivity : CastEnabledActivity() {
Logd(TAG, "loadFragment(tag: $tag, args: $args)")
val fragment: Fragment
when (tag) {
QueueFragment.TAG -> fragment = QueueFragment()
QueuesFragment.TAG -> fragment = QueuesFragment()
AllEpisodesFragment.TAG -> fragment = AllEpisodesFragment()
DownloadsFragment.TAG -> fragment = DownloadsFragment()
HistoryFragment.TAG -> fragment = HistoryFragment()
@ -719,7 +719,7 @@ class MainActivity : CastEnabledActivity() {
"DOWNLOADS" -> loadFragment(DownloadsFragment.TAG, null)
"HISTORY" -> loadFragment(HistoryFragment.TAG, null)
"EPISODES" -> loadFragment(AllEpisodesFragment.TAG, null)
"QUEUE" -> loadFragment(QueueFragment.TAG, null)
"QUEUE" -> loadFragment(QueuesFragment.TAG, null)
"SUBSCRIPTIONS" -> loadFragment(SubscriptionsFragment.TAG, null)
"STATISTCS" -> loadFragment(StatisticsFragment.TAG, null)
else -> {

View File

@ -53,7 +53,7 @@ import androidx.media3.common.util.UnstableApi
forFragment = context.getString(R.string.individual_subscription)
keys = Stream.of(keys).filter { a: SwipeAction -> !a.getId().equals(SwipeAction.REMOVE_FROM_HISTORY) }.toList()
}
QueueFragment.TAG -> {
QueuesFragment.TAG -> {
forFragment = context.getString(R.string.queue_label)
keys = Stream.of(keys).filter { a: SwipeAction ->
(!a.getId().equals(SwipeAction.ADD_TO_QUEUE) && !a.getId().equals(SwipeAction.REMOVE_FROM_HISTORY)) }.toList()
@ -64,7 +64,7 @@ import androidx.media3.common.util.UnstableApi
}
else -> {}
}
if (tag != QueueFragment.TAG) keys = Stream.of(keys).filter { a: SwipeAction -> !a.getId().equals(SwipeAction.REMOVE_FROM_QUEUE) }.toList()
if (tag != QueuesFragment.TAG) keys = Stream.of(keys).filter { a: SwipeAction -> !a.getId().equals(SwipeAction.REMOVE_FROM_QUEUE) }.toList()
builder.setTitle(context.getString(R.string.swipeactions_label) + " - " + forFragment)
val binding = SwipeactionsDialogBinding.inflate(LayoutInflater.from(context))

View File

@ -24,7 +24,6 @@ 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.QueueFragment.Companion
import ac.mdiq.podcini.ui.utils.EmptyViewHandler
import ac.mdiq.podcini.ui.utils.LiftOnScrollListener
import ac.mdiq.podcini.ui.view.EpisodeViewHolder
@ -76,7 +75,6 @@ import java.util.*
private lateinit var emptyView: EmptyViewHandler
private var displayUpArrow = false
// private var curIndex = -1
@UnstableApi override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
_binding = SimpleListFragmentBinding.inflate(inflater)

View File

@ -2,15 +2,12 @@ package ac.mdiq.podcini.ui.fragment
import ac.mdiq.podcini.R
import ac.mdiq.podcini.databinding.EpisodeHomeFragmentBinding
import ac.mdiq.podcini.net.utils.NetworkUtils.fetchHtmlSource
import ac.mdiq.podcini.storage.database.RealmDB.runOnIOScope
import ac.mdiq.podcini.storage.database.RealmDB.upsertBlk
import ac.mdiq.podcini.storage.model.Episode
import ac.mdiq.podcini.ui.utils.ShownotesCleaner
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.storage.database.RealmDB.upsert
import ac.mdiq.podcini.storage.database.RealmDB.upsertBlk
import ac.mdiq.podcini.ui.fragment.SubscriptionsFragment.Companion
import android.content.Context
import android.os.Bundle
import android.speech.tts.TextToSpeech
@ -27,7 +24,8 @@ import androidx.fragment.app.Fragment
import androidx.lifecycle.Lifecycle
import androidx.media3.common.util.UnstableApi
import com.google.android.material.appbar.MaterialToolbar
import kotlinx.coroutines.*
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import net.dankito.readability4j.extended.Readability4JExtended
import java.util.*
@ -113,11 +111,8 @@ class EpisodeHomeFragment : Fragment() {
if (!cleanedNotes.isNullOrEmpty()) {
if (!ttsReady) initializeTTS(requireContext())
withContext(Dispatchers.Main) {
binding.readerView.loadDataWithBaseURL("https://127.0.0.1",
cleanedNotes ?: "No notes",
"text/html",
"UTF-8",
null)
binding.readerView.loadDataWithBaseURL("https://127.0.0.1", cleanedNotes ?: "No notes",
"text/html", "UTF-8", null)
binding.readerView.visibility = View.VISIBLE
binding.webView.visibility = View.GONE
}

View File

@ -74,7 +74,7 @@ import kotlin.math.max
private var homeFragment: EpisodeHomeFragment? = null
private var itemLoaded = false
private var episode: Episode? = null // unmanaged
private var episode: Episode? = null // managed
private var webviewData: String? = null
private lateinit var shownotesCleaner: ShownotesCleaner

View File

@ -309,7 +309,7 @@ import java.util.concurrent.Semaphore
R.id.action_search -> (activity as MainActivity).loadChildFragment(SearchFragment.newInstance(feed!!.id, feed!!.title))
R.id.switch_queue -> SwitchQueueDialog(activity as MainActivity).show()
R.id.open_queue -> {
val qFrag = QueueFragment()
val qFrag = QueuesFragment()
(activity as MainActivity).loadChildFragment(qFrag)
(activity as MainActivity).bottomSheet.state = BottomSheetBehavior.STATE_COLLAPSED
}

View File

@ -186,7 +186,7 @@ class NavDrawerFragment : Fragment(), OnSharedPreferenceChangeListener {
@UnstableApi @DrawableRes
private fun getDrawable(tag: String?): Int {
return when (tag) {
QueueFragment.TAG -> R.drawable.ic_playlist_play
QueuesFragment.TAG -> R.drawable.ic_playlist_play
AllEpisodesFragment.TAG -> R.drawable.ic_feed
DownloadsFragment.TAG -> R.drawable.ic_download
HistoryFragment.TAG -> R.drawable.ic_history
@ -293,7 +293,7 @@ class NavDrawerFragment : Fragment(), OnSharedPreferenceChangeListener {
holder.count.visibility = View.VISIBLE
}
}
QueueFragment.TAG -> {
QueuesFragment.TAG -> {
val queueSize = datasetStats?.queueSize ?: 0
if (queueSize > 0) {
holder.count.text = NumberFormat.getInstance().format(queueSize.toLong())
@ -378,7 +378,7 @@ class NavDrawerFragment : Fragment(), OnSharedPreferenceChangeListener {
@UnstableApi
val NAV_DRAWER_TAGS: Array<String> = arrayOf(
SubscriptionsFragment.TAG,
QueueFragment.TAG,
QueuesFragment.TAG,
AllEpisodesFragment.TAG,
DownloadsFragment.TAG,
HistoryFragment.TAG,

View File

@ -7,13 +7,12 @@ 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.database.RealmDB.upsertBlk
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
@ -187,9 +186,11 @@ class PlayerDetailsFragment : Fragment() {
val article = readability4J.parse()
readerhtml = article.contentWithDocumentsCharsetOrUtf8
if (!readerhtml.isNullOrEmpty()) {
currentItem!!.setTranscriptIfLonger(readerhtml)
currentItem = upsertBlk(currentItem!!) {
it.setTranscriptIfLonger(readerhtml)
}
homeText = currentItem!!.transcript
persistEpisode(currentItem)
// persistEpisode(currentItem)
}
}
if (!homeText.isNullOrEmpty()) {

View File

@ -14,7 +14,6 @@ import ac.mdiq.podcini.storage.database.Queues.moveInQueue
import ac.mdiq.podcini.storage.database.Queues.queueKeepSortedOrder
import ac.mdiq.podcini.storage.database.RealmDB.realm
import ac.mdiq.podcini.storage.database.RealmDB.runOnIOScope
import ac.mdiq.podcini.storage.database.RealmDB.unmanaged
import ac.mdiq.podcini.storage.database.RealmDB.upsert
import ac.mdiq.podcini.storage.database.RealmDB.upsertBlk
import ac.mdiq.podcini.storage.model.Episode
@ -80,7 +79,7 @@ import java.util.*
/**
* Shows all items in the queue.
*/
@UnstableApi class QueueFragment : Fragment(), Toolbar.OnMenuItemClickListener, SelectableAdapter.OnSelectModeListener {
@UnstableApi class QueuesFragment : Fragment(), Toolbar.OnMenuItemClickListener, SelectableAdapter.OnSelectModeListener {
private var _binding: QueueFragmentBinding? = null
private val binding get() = _binding!!
@ -91,6 +90,7 @@ import java.util.*
private lateinit var swipeActions: SwipeActions
private lateinit var speedDialView: SpeedDialView
private lateinit var spinnerLayout: View
private lateinit var queueNames: Array<String>
private lateinit var spinnerTexts: MutableList<String>
private lateinit var queueSpinner: Spinner
@ -127,7 +127,7 @@ import java.util.*
val queues = realm.query(PlayQueue::class).find()
queueNames = queues.map { it.name }.toTypedArray()
spinnerTexts = queues.map { "${it.name} : ${it.episodeIds.size}" }.toMutableList()
val spinnerLayout = inflater.inflate(R.layout.queue_title_spinner, toolbar, false)
spinnerLayout = inflater.inflate(R.layout.queue_title_spinner, toolbar, false)
queueSpinner = spinnerLayout.findViewById(R.id.queue_spinner)
val params = Toolbar.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT)
params.gravity = Gravity.CENTER_VERTICAL
@ -415,6 +415,13 @@ import java.util.*
when (itemId) {
R.id.show_bin -> {
showBin = !showBin
if (showBin) {
toolbar.removeView(spinnerLayout)
toolbar.title = curQueue.name + " Bin"
} else {
toolbar.title = ""
toolbar.addView(spinnerLayout)
}
refreshMenuItems()
if (showBin) {
item.setIcon(R.drawable.playlist_play)
@ -635,7 +642,6 @@ import java.util.*
while (curQueue.name.isEmpty()) runBlocking { delay(100) }
if (queueItems.isEmpty()) emptyView.hide()
queueItems.clear()
Logd(TAG, "loadCurQueue() showBin: $showBin")
if (showBin) {
queueItems.addAll(realm.query(Episode::class, "id IN $0", curQueue.idsBinList)
.find().sortedByDescending { curQueue.idsBinList.indexOf(it.id) })
@ -718,7 +724,7 @@ import java.util.*
}
}
private inner class QueueSwipeActions : SwipeActions(ItemTouchHelper.UP or ItemTouchHelper.DOWN, this@QueueFragment, TAG) {
private inner class QueueSwipeActions : SwipeActions(ItemTouchHelper.UP or ItemTouchHelper.DOWN, this@QueuesFragment, TAG) {
// Position tracking whilst dragging
var dragFrom: Int = -1
var dragTo: Int = -1
@ -801,7 +807,7 @@ import java.util.*
}
companion object {
val TAG = QueueFragment::class.simpleName ?: "Anonymous"
val TAG = QueuesFragment::class.simpleName ?: "Anonymous"
private const val KEY_UP_ARROW = "up_arrow"

View File

@ -228,7 +228,7 @@
<string-array name="default_page_values">
<item>SubscriptionsFragment</item>
<item>QueueFragment</item>
<item>QueuesFragment</item>
<item>AllEpisodesFragment</item>
<item>DownloadsFragment</item>
<item>PlaybackHistoryFragment</item>

View File

@ -11,7 +11,7 @@
<string name="add_feed_label">Add podcast</string>
<string name="episodes_label">Episodes</string>
<string name="home_label">Home</string>
<string name="queue_label">Queue</string>
<string name="queue_label">Queues</string>
<string name="favorite_episodes_label">Favorites</string>
<string name="settings_label">Settings</string>

View File

@ -11,7 +11,7 @@
android:targetPackage="ac.mdiq.podcini">
<extra
android:name="fragment_tag"
android:value="QueueFragment" />
android:value="QueuesFragment" />
</intent>
</shortcut>

View File

@ -1,3 +1,11 @@
# 6.3.3
* fixed crash when setting as Played/Unplayed in EpisodeInfo view
* various changes in writing to DB in write block
* Queue view is renamed to Queues view
* in Queues view, when opening/closing Bin, the queues spinner on ToolBar is toggled with a title
* added various Log.d statements in seeking to trace down the occasional random playing behavior
# 6.3.2
* fixed crash of opening FeedEpisode view when "Use episode cover" is set

View File

@ -0,0 +1,8 @@
Version 6.3.3 brings several changes:
* fixed crash when setting as Played/Unplayed in EpisodeInfo view
* various changes in writing to DB in write block
* Queue view is renamed to Queues view
* in Queues view, when opening/closing Bin, the queues spinner on ToolBar is toggled with a title
* added various Log.d statements in seeking to trace down the occasional random playing behavior