6.3.3 commit
This commit is contained in:
parent
abdbf3dabd
commit
e5188bc998
|
@ -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
|
||||
|
|
|
@ -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 = ""
|
||||
|
|
|
@ -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))
|
||||
}
|
||||
|
||||
|
|
|
@ -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())
|
||||
}
|
||||
|
|
@ -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))
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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")
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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)) {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 -> {
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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()) {
|
||||
|
|
|
@ -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"
|
||||
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
android:targetPackage="ac.mdiq.podcini">
|
||||
<extra
|
||||
android:name="fragment_tag"
|
||||
android:value="QueueFragment" />
|
||||
android:value="QueuesFragment" />
|
||||
</intent>
|
||||
</shortcut>
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
Loading…
Reference in New Issue