5.4.0 commit
|
@ -17,7 +17,7 @@ Compared to AntennaPod this project:
|
||||||
2. Plays in `AudioOffloadMode`, kind to device battery,
|
2. Plays in `AudioOffloadMode`, kind to device battery,
|
||||||
3. Is purely `Kotlin` based and mono-modular,
|
3. Is purely `Kotlin` based and mono-modular,
|
||||||
4. Targets Android 14 with updated dependencies,
|
4. Targets Android 14 with updated dependencies,
|
||||||
5. Outfits with Viewbinding, Coil replacing Glide, coroutines replacing RxJava, and SharedFlow replacing EventBus,
|
5. Outfits with Viewbinding, Coil replacing Glide, coroutines replacing RxJava and threads, and SharedFlow replacing EventBus,
|
||||||
6. Boasts new UI's including streamlined drawer, subscriptions view and player controller,
|
6. Boasts new UI's including streamlined drawer, subscriptions view and player controller,
|
||||||
7. Accepts podcast as well as plain RSS and YouTube feeds,
|
7. Accepts podcast as well as plain RSS and YouTube feeds,
|
||||||
8. Offers Readability and Text-to-Speech for RSS contents,
|
8. Offers Readability and Text-to-Speech for RSS contents,
|
||||||
|
|
|
@ -159,8 +159,8 @@ android {
|
||||||
// Version code schema (not used):
|
// Version code schema (not used):
|
||||||
// "1.2.3-beta4" -> 1020304
|
// "1.2.3-beta4" -> 1020304
|
||||||
// "1.2.3" -> 1020395
|
// "1.2.3" -> 1020395
|
||||||
versionCode 3020146
|
versionCode 3020147
|
||||||
versionName "5.3.1"
|
versionName "5.4.0"
|
||||||
|
|
||||||
def commit = ""
|
def commit = ""
|
||||||
try {
|
try {
|
||||||
|
|
|
@ -30,6 +30,7 @@ import ac.mdiq.podcini.preferences.UserPreferences
|
||||||
import de.test.podcini.EspressoTestUtils
|
import de.test.podcini.EspressoTestUtils
|
||||||
import de.test.podcini.IgnoreOnCi
|
import de.test.podcini.IgnoreOnCi
|
||||||
import de.test.podcini.ui.UITestUtils
|
import de.test.podcini.ui.UITestUtils
|
||||||
|
import kotlinx.coroutines.runBlocking
|
||||||
import org.awaitility.Awaitility
|
import org.awaitility.Awaitility
|
||||||
import org.hamcrest.Matchers
|
import org.hamcrest.Matchers
|
||||||
import org.junit.*
|
import org.junit.*
|
||||||
|
@ -166,7 +167,7 @@ class PlaybackTest {
|
||||||
fun testStartLocal() {
|
fun testStartLocal() {
|
||||||
uiTestUtils!!.addLocalFeedData(true)
|
uiTestUtils!!.addLocalFeedData(true)
|
||||||
activityTestRule.launchActivity(Intent())
|
activityTestRule.launchActivity(Intent())
|
||||||
clearQueue().get()
|
runBlocking { clearQueue().join() }
|
||||||
startLocalPlayback()
|
startLocalPlayback()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -175,7 +176,7 @@ class PlaybackTest {
|
||||||
fun testPlayingItemAddsToQueue() {
|
fun testPlayingItemAddsToQueue() {
|
||||||
uiTestUtils!!.addLocalFeedData(true)
|
uiTestUtils!!.addLocalFeedData(true)
|
||||||
activityTestRule.launchActivity(Intent())
|
activityTestRule.launchActivity(Intent())
|
||||||
clearQueue().get()
|
runBlocking { clearQueue().join() }
|
||||||
val queue = getQueue()
|
val queue = getQueue()
|
||||||
Assert.assertEquals(0, queue.size.toLong())
|
Assert.assertEquals(0, queue.size.toLong())
|
||||||
startLocalPlayback()
|
startLocalPlayback()
|
||||||
|
@ -188,7 +189,7 @@ class PlaybackTest {
|
||||||
setContinuousPlaybackPreference(false)
|
setContinuousPlaybackPreference(false)
|
||||||
uiTestUtils!!.addLocalFeedData(true)
|
uiTestUtils!!.addLocalFeedData(true)
|
||||||
activityTestRule.launchActivity(Intent())
|
activityTestRule.launchActivity(Intent())
|
||||||
clearQueue().get()
|
runBlocking { clearQueue().join() }
|
||||||
startLocalPlayback()
|
startLocalPlayback()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -261,10 +262,9 @@ class PlaybackTest {
|
||||||
protected fun replayEpisodeCheck(followQueue: Boolean) {
|
protected fun replayEpisodeCheck(followQueue: Boolean) {
|
||||||
setContinuousPlaybackPreference(followQueue)
|
setContinuousPlaybackPreference(followQueue)
|
||||||
uiTestUtils!!.addLocalFeedData(true)
|
uiTestUtils!!.addLocalFeedData(true)
|
||||||
clearQueue().get()
|
runBlocking { clearQueue().join() }
|
||||||
activityTestRule.launchActivity(Intent())
|
activityTestRule.launchActivity(Intent())
|
||||||
val episodes = getEpisodes(0, 10,
|
val episodes = getEpisodes(0, 10, unfiltered(), SortOrder.DATE_NEW_OLD)
|
||||||
unfiltered(), SortOrder.DATE_NEW_OLD)
|
|
||||||
|
|
||||||
startLocalPlayback()
|
startLocalPlayback()
|
||||||
val media = episodes[0].media
|
val media = episodes[0].media
|
||||||
|
|
|
@ -147,12 +147,12 @@ class NavigationDrawerTest {
|
||||||
val hidden = hiddenDrawerItems
|
val hidden = hiddenDrawerItems
|
||||||
Assert.assertEquals(2, hidden!!.size.toLong())
|
Assert.assertEquals(2, hidden!!.size.toLong())
|
||||||
Assert.assertTrue(hidden.contains(AllEpisodesFragment.TAG))
|
Assert.assertTrue(hidden.contains(AllEpisodesFragment.TAG))
|
||||||
Assert.assertTrue(hidden.contains(PlaybackHistoryFragment.TAG))
|
Assert.assertTrue(hidden.contains(HistoryFragment.TAG))
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testDrawerPreferencesUnhideSomeElements() {
|
fun testDrawerPreferencesUnhideSomeElements() {
|
||||||
var hidden = listOf(PlaybackHistoryFragment.TAG, DownloadsFragment.TAG)
|
var hidden = listOf(HistoryFragment.TAG, DownloadsFragment.TAG)
|
||||||
hiddenDrawerItems = hidden
|
hiddenDrawerItems = hidden
|
||||||
activityRule.launchActivity(Intent())
|
activityRule.launchActivity(Intent())
|
||||||
openNavDrawer()
|
openNavDrawer()
|
||||||
|
@ -166,7 +166,7 @@ class NavigationDrawerTest {
|
||||||
hidden = hiddenDrawerItems?.filterNotNull()?: listOf()
|
hidden = hiddenDrawerItems?.filterNotNull()?: listOf()
|
||||||
Assert.assertEquals(2, hidden.size.toLong())
|
Assert.assertEquals(2, hidden.size.toLong())
|
||||||
Assert.assertTrue(hidden.contains(QueueFragment.TAG))
|
Assert.assertTrue(hidden.contains(QueueFragment.TAG))
|
||||||
Assert.assertTrue(hidden.contains(PlaybackHistoryFragment.TAG))
|
Assert.assertTrue(hidden.contains(HistoryFragment.TAG))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -17,6 +17,9 @@ import com.google.android.material.color.DynamicColors
|
||||||
import com.joanzapata.iconify.Iconify
|
import com.joanzapata.iconify.Iconify
|
||||||
import com.joanzapata.iconify.fonts.FontAwesomeModule
|
import com.joanzapata.iconify.fonts.FontAwesomeModule
|
||||||
import com.joanzapata.iconify.fonts.MaterialModule
|
import com.joanzapata.iconify.fonts.MaterialModule
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.runBlocking
|
||||||
|
import kotlinx.coroutines.withContext
|
||||||
|
|
||||||
/** Main application class. */
|
/** Main application class. */
|
||||||
class PodciniApp : Application() {
|
class PodciniApp : Application() {
|
||||||
|
@ -42,9 +45,12 @@ class PodciniApp : Application() {
|
||||||
|
|
||||||
singleton = this
|
singleton = this
|
||||||
|
|
||||||
ClientConfigurator.initialize(this)
|
runBlocking {
|
||||||
PreferenceUpgrader.checkUpgrades(this)
|
withContext(Dispatchers.IO) {
|
||||||
|
ClientConfigurator.initialize(this@PodciniApp)
|
||||||
|
PreferenceUpgrader.checkUpgrades(this@PodciniApp)
|
||||||
|
}
|
||||||
|
}
|
||||||
Iconify.with(FontAwesomeModule())
|
Iconify.with(FontAwesomeModule())
|
||||||
Iconify.with(MaterialModule())
|
Iconify.with(MaterialModule())
|
||||||
|
|
||||||
|
|
|
@ -7,6 +7,7 @@ import android.util.Log
|
||||||
import ac.mdiq.podcini.net.download.service.PodciniHttpClient.getHttpClient
|
import ac.mdiq.podcini.net.download.service.PodciniHttpClient.getHttpClient
|
||||||
import ac.mdiq.podcini.storage.model.feed.Feed
|
import ac.mdiq.podcini.storage.model.feed.Feed
|
||||||
import ac.mdiq.podcini.util.Logd
|
import ac.mdiq.podcini.util.Logd
|
||||||
|
import android.content.SharedPreferences
|
||||||
import okhttp3.CacheControl
|
import okhttp3.CacheControl
|
||||||
import okhttp3.OkHttpClient
|
import okhttp3.OkHttpClient
|
||||||
import okhttp3.Request
|
import okhttp3.Request
|
||||||
|
@ -81,6 +82,11 @@ class ItunesTopListLoader(private val context: Context) {
|
||||||
const val COUNTRY_CODE_UNSET: String = "99"
|
const val COUNTRY_CODE_UNSET: String = "99"
|
||||||
private const val NUM_LOADED = 25
|
private const val NUM_LOADED = 25
|
||||||
|
|
||||||
|
var prefs: SharedPreferences? = null
|
||||||
|
fun getSharedPrefs(context: Context) {
|
||||||
|
if (prefs == null) prefs = context.getSharedPreferences(PREFS, Context.MODE_PRIVATE)
|
||||||
|
}
|
||||||
|
|
||||||
private fun removeSubscribed(suggestedPodcasts: List<PodcastSearchResult>, subscribedFeeds: List<Feed>, limit: Int): List<PodcastSearchResult> {
|
private fun removeSubscribed(suggestedPodcasts: List<PodcastSearchResult>, subscribedFeeds: List<Feed>, limit: Int): List<PodcastSearchResult> {
|
||||||
val subscribedPodcastsSet: MutableSet<String> = HashSet()
|
val subscribedPodcastsSet: MutableSet<String> = HashSet()
|
||||||
for (subscribedFeed in subscribedFeeds) {
|
for (subscribedFeed in subscribedFeeds) {
|
||||||
|
|
|
@ -29,6 +29,7 @@ import androidx.work.Worker
|
||||||
import androidx.work.WorkerParameters
|
import androidx.work.WorkerParameters
|
||||||
import com.google.common.util.concurrent.Futures
|
import com.google.common.util.concurrent.Futures
|
||||||
import com.google.common.util.concurrent.ListenableFuture
|
import com.google.common.util.concurrent.ListenableFuture
|
||||||
|
import kotlinx.coroutines.runBlocking
|
||||||
import org.apache.commons.io.FileUtils
|
import org.apache.commons.io.FileUtils
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
|
@ -115,7 +116,7 @@ class EpisodeDownloadWorker(context: Context, params: WorkerParameters) : Worker
|
||||||
if (dest.exists()) {
|
if (dest.exists()) {
|
||||||
media.file_url = request.destination
|
media.file_url = request.destination
|
||||||
try {
|
try {
|
||||||
DBWriter.persistFeedMedia(media).get()
|
runBlocking { DBWriter.persistFeedMedia(media).join() }
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
Log.e(TAG, "ExecutionException in writeFileUrl: " + e.message)
|
Log.e(TAG, "ExecutionException in writeFileUrl: " + e.message)
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,6 +16,7 @@ import android.content.Context
|
||||||
import android.media.MediaMetadataRetriever
|
import android.media.MediaMetadataRetriever
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import androidx.media3.common.util.UnstableApi
|
import androidx.media3.common.util.UnstableApi
|
||||||
|
import kotlinx.coroutines.runBlocking
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.util.concurrent.ExecutionException
|
import java.util.concurrent.ExecutionException
|
||||||
|
|
||||||
|
@ -60,7 +61,7 @@ class MediaDownloadedHandler(private val context: Context, var updatedStatus: Do
|
||||||
val item = media.item
|
val item = media.item
|
||||||
|
|
||||||
try {
|
try {
|
||||||
DBWriter.persistFeedMedia(media).get()
|
runBlocking { DBWriter.persistFeedMedia(media).join() }
|
||||||
|
|
||||||
// we've received the media, we don't want to autodownload it again
|
// we've received the media, we don't want to autodownload it again
|
||||||
if (item != null) {
|
if (item != null) {
|
||||||
|
@ -68,7 +69,7 @@ class MediaDownloadedHandler(private val context: Context, var updatedStatus: Do
|
||||||
// setFeedItem() signals (via EventBus) that the item has been updated,
|
// setFeedItem() signals (via EventBus) that the item has been updated,
|
||||||
// so we do it after the enclosing media has been updated above,
|
// so we do it after the enclosing media has been updated above,
|
||||||
// to ensure subscribers will get the updated FeedMedia as well
|
// to ensure subscribers will get the updated FeedMedia as well
|
||||||
DBWriter.persistFeedItem(item).get()
|
runBlocking { DBWriter.persistFeedItem(item).join() }
|
||||||
if (broadcastUnreadStateUpdate) EventFlow.postEvent(FlowEvent.UnreadItemsUpdateEvent())
|
if (broadcastUnreadStateUpdate) EventFlow.postEvent(FlowEvent.UnreadItemsUpdateEvent())
|
||||||
}
|
}
|
||||||
} catch (e: InterruptedException) {
|
} catch (e: InterruptedException) {
|
||||||
|
|
|
@ -46,6 +46,7 @@ class CompositeX509TrustManager(private val trustManagers: List<X509TrustManager
|
||||||
override fun getAcceptedIssuers(): Array<X509Certificate> {
|
override fun getAcceptedIssuers(): Array<X509Certificate> {
|
||||||
val certificates: MutableList<X509Certificate> = ArrayList()
|
val certificates: MutableList<X509Certificate> = ArrayList()
|
||||||
for (trustManager in trustManagers) {
|
for (trustManager in trustManagers) {
|
||||||
|
// TODO: appears time consuming
|
||||||
certificates.addAll(listOf(*trustManager.acceptedIssuers))
|
certificates.addAll(listOf(*trustManager.acceptedIssuers))
|
||||||
}
|
}
|
||||||
return certificates.toTypedArray<X509Certificate>()
|
return certificates.toTypedArray<X509Certificate>()
|
||||||
|
|
|
@ -57,6 +57,7 @@ open class SyncService(context: Context, params: WorkerParameters) : Worker(cont
|
||||||
@UnstableApi override fun doWork(): Result {
|
@UnstableApi override fun doWork(): Result {
|
||||||
Logd(TAG, "doWork() called")
|
Logd(TAG, "doWork() called")
|
||||||
val activeSyncProvider = getActiveSyncProvider() ?: return Result.failure()
|
val activeSyncProvider = getActiveSyncProvider() ?: return Result.failure()
|
||||||
|
Logd(TAG, "doWork() got syn provider")
|
||||||
|
|
||||||
SynchronizationSettings.updateLastSynchronizationAttempt()
|
SynchronizationSettings.updateLastSynchronizationAttempt()
|
||||||
setCurrentlyActive(true)
|
setCurrentlyActive(true)
|
||||||
|
@ -165,15 +166,6 @@ open class SyncService(context: Context, params: WorkerParameters) : Worker(cont
|
||||||
return@launch
|
return@launch
|
||||||
}
|
}
|
||||||
scope.cancel()
|
scope.cancel()
|
||||||
// try {
|
|
||||||
// while (true) {
|
|
||||||
// Thread.sleep(1000)
|
|
||||||
// val event = EventBus.getDefault().getStickyEvent(FlowEvent.FeedUpdateRunningEvent::class.java)
|
|
||||||
// if (event == null || !event.isFeedUpdateRunning) return
|
|
||||||
// }
|
|
||||||
// } catch (e: InterruptedException) {
|
|
||||||
// e.printStackTrace()
|
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getEpisodeActions(syncServiceImpl: ISyncService) : Pair<Long, Long> {
|
fun getEpisodeActions(syncServiceImpl: ISyncService) : Pair<Long, Long> {
|
||||||
|
@ -315,11 +307,6 @@ open class SyncService(context: Context, params: WorkerParameters) : Worker(cont
|
||||||
if (selectedService == null) return null
|
if (selectedService == null) return null
|
||||||
|
|
||||||
return when (selectedService) {
|
return when (selectedService) {
|
||||||
// SynchronizationProviderViewData.WIFI -> {
|
|
||||||
//// if (hosturl != null) WifiImplSyncService(hosturl!!, hostport)
|
|
||||||
//// else null
|
|
||||||
// null
|
|
||||||
// }
|
|
||||||
SynchronizationProviderViewData.GPODDER_NET -> GpodnetService(getHttpClient(), hosturl, deviceID?:"", username?:"", password?:"")
|
SynchronizationProviderViewData.GPODDER_NET -> GpodnetService(getHttpClient(), hosturl, deviceID?:"", username?:"", password?:"")
|
||||||
SynchronizationProviderViewData.NEXTCLOUD_GPODDER -> NextcloudSyncService(getHttpClient(), hosturl, username?:"", password?:"")
|
SynchronizationProviderViewData.NEXTCLOUD_GPODDER -> NextcloudSyncService(getHttpClient(), hosturl, username?:"", password?:"")
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,6 +7,7 @@ import ac.mdiq.podcini.playback.service.PlaybackService
|
||||||
import ac.mdiq.podcini.playback.service.PlaybackService.LocalBinder
|
import ac.mdiq.podcini.playback.service.PlaybackService.LocalBinder
|
||||||
import ac.mdiq.podcini.playback.service.PlaybackServiceConstants
|
import ac.mdiq.podcini.playback.service.PlaybackServiceConstants
|
||||||
import ac.mdiq.podcini.preferences.PlaybackPreferences
|
import ac.mdiq.podcini.preferences.PlaybackPreferences
|
||||||
|
import ac.mdiq.podcini.preferences.PlaybackPreferences.Companion.loadPlayableFromPreferences
|
||||||
import ac.mdiq.podcini.preferences.UserPreferences
|
import ac.mdiq.podcini.preferences.UserPreferences
|
||||||
import ac.mdiq.podcini.storage.DBWriter
|
import ac.mdiq.podcini.storage.DBWriter
|
||||||
import ac.mdiq.podcini.storage.model.feed.FeedMedia
|
import ac.mdiq.podcini.storage.model.feed.FeedMedia
|
||||||
|
@ -24,8 +25,11 @@ import android.view.SurfaceHolder
|
||||||
import androidx.fragment.app.FragmentActivity
|
import androidx.fragment.app.FragmentActivity
|
||||||
import androidx.lifecycle.lifecycleScope
|
import androidx.lifecycle.lifecycleScope
|
||||||
import androidx.media3.common.util.UnstableApi
|
import androidx.media3.common.util.UnstableApi
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.flow.collectLatest
|
import kotlinx.coroutines.flow.collectLatest
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
import kotlinx.coroutines.runBlocking
|
||||||
|
import kotlinx.coroutines.withContext
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Communicates with the playback service. GUI classes should use this class to
|
* Communicates with the playback service. GUI classes should use this class to
|
||||||
|
@ -73,6 +77,7 @@ abstract class PlaybackController(private val activity: FragmentActivity) {
|
||||||
private fun procFlowEvents() {
|
private fun procFlowEvents() {
|
||||||
activity.lifecycleScope.launch {
|
activity.lifecycleScope.launch {
|
||||||
EventFlow.events.collectLatest { event ->
|
EventFlow.events.collectLatest { event ->
|
||||||
|
Logd(TAG, "Received event: $event")
|
||||||
when (event) {
|
when (event) {
|
||||||
is FlowEvent.PlaybackServiceEvent -> if (event.action == FlowEvent.PlaybackServiceEvent.Action.SERVICE_STARTED) init()
|
is FlowEvent.PlaybackServiceEvent -> if (event.action == FlowEvent.PlaybackServiceEvent.Action.SERVICE_STARTED) init()
|
||||||
else -> {}
|
else -> {}
|
||||||
|
@ -305,9 +310,13 @@ abstract class PlaybackController(private val activity: FragmentActivity) {
|
||||||
|
|
||||||
fun getMedia(): Playable? {
|
fun getMedia(): Playable? {
|
||||||
if (media == null && playbackService != null) media = playbackService!!.mPlayerInfo.playable
|
if (media == null && playbackService != null) media = playbackService!!.mPlayerInfo.playable
|
||||||
if (media == null) media = PlaybackPreferences.createInstanceFromPreferences(activity)
|
if (media != null) return media
|
||||||
|
return runBlocking {
|
||||||
return media
|
media = withContext(Dispatchers.IO) {
|
||||||
|
loadPlayableFromPreferences()
|
||||||
|
}
|
||||||
|
media
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun seekTo(time: Int) {
|
fun seekTo(time: Int) {
|
||||||
|
|
|
@ -8,6 +8,7 @@ import ac.mdiq.podcini.playback.base.MediaPlayerBase
|
||||||
import ac.mdiq.podcini.playback.base.PlayerStatus
|
import ac.mdiq.podcini.playback.base.PlayerStatus
|
||||||
import ac.mdiq.podcini.playback.base.RewindAfterPauseUtils
|
import ac.mdiq.podcini.playback.base.RewindAfterPauseUtils
|
||||||
import ac.mdiq.podcini.preferences.UserPreferences
|
import ac.mdiq.podcini.preferences.UserPreferences
|
||||||
|
import ac.mdiq.podcini.storage.DBWriter.ioScope
|
||||||
import ac.mdiq.podcini.storage.model.feed.FeedMedia
|
import ac.mdiq.podcini.storage.model.feed.FeedMedia
|
||||||
import ac.mdiq.podcini.storage.model.playback.MediaType
|
import ac.mdiq.podcini.storage.model.playback.MediaType
|
||||||
import ac.mdiq.podcini.storage.model.playback.Playable
|
import ac.mdiq.podcini.storage.model.playback.Playable
|
||||||
|
@ -159,15 +160,15 @@ class LocalMediaPlayer(context: Context, callback: MediaPlayerCallback) : MediaP
|
||||||
private fun setDataSource(m: MediaMetadata, s: String, user: String?, password: String?) {
|
private fun setDataSource(m: MediaMetadata, s: String, user: String?, password: String?) {
|
||||||
Logd(TAG, "setDataSource: $s")
|
Logd(TAG, "setDataSource: $s")
|
||||||
|
|
||||||
val httpDataSourceFactory = OkHttpDataSource.Factory(PodciniHttpClient.getHttpClient() as okhttp3.Call.Factory)
|
if (httpDataSourceFactory == null)
|
||||||
.setUserAgent(ClientConfig.USER_AGENT)
|
httpDataSourceFactory = OkHttpDataSource.Factory(PodciniHttpClient.getHttpClient() as okhttp3.Call.Factory).setUserAgent(ClientConfig.USER_AGENT)
|
||||||
|
|
||||||
if (!user.isNullOrEmpty() && !password.isNullOrEmpty()) {
|
if (!user.isNullOrEmpty() && !password.isNullOrEmpty()) {
|
||||||
val requestProperties = HashMap<String, String>()
|
val requestProperties = HashMap<String, String>()
|
||||||
requestProperties["Authorization"] = HttpCredentialEncoder.encode(user, password, "ISO-8859-1")
|
requestProperties["Authorization"] = HttpCredentialEncoder.encode(user, password, "ISO-8859-1")
|
||||||
httpDataSourceFactory.setDefaultRequestProperties(requestProperties)
|
httpDataSourceFactory!!.setDefaultRequestProperties(requestProperties)
|
||||||
}
|
}
|
||||||
val dataSourceFactory: DataSource.Factory = DefaultDataSourceFactory(context, null, httpDataSourceFactory)
|
val dataSourceFactory: DataSource.Factory = DefaultDataSourceFactory(context, null, httpDataSourceFactory!!)
|
||||||
val extractorsFactory = DefaultExtractorsFactory()
|
val extractorsFactory = DefaultExtractorsFactory()
|
||||||
extractorsFactory.setConstantBitrateSeekingEnabled(true)
|
extractorsFactory.setConstantBitrateSeekingEnabled(true)
|
||||||
extractorsFactory.setMp3ExtractorFlags(Mp3Extractor.FLAG_DISABLE_ID3_METADATA)
|
extractorsFactory.setMp3ExtractorFlags(Mp3Extractor.FLAG_DISABLE_ID3_METADATA)
|
||||||
|
@ -282,6 +283,7 @@ class LocalMediaPlayer(context: Context, callback: MediaPlayerCallback) : MediaP
|
||||||
}
|
}
|
||||||
else -> {
|
else -> {
|
||||||
val localMediaurl = this.playable!!.getLocalMediaUrl()
|
val localMediaurl = this.playable!!.getLocalMediaUrl()
|
||||||
|
// TODO: File(localMediaurl).canRead() time consuming
|
||||||
if (!localMediaurl.isNullOrEmpty() && File(localMediaurl).canRead()) setDataSource(metadata, localMediaurl, null, null)
|
if (!localMediaurl.isNullOrEmpty() && File(localMediaurl).canRead()) setDataSource(metadata, localMediaurl, null, null)
|
||||||
else throw IOException("Unable to read local file $localMediaurl")
|
else throw IOException("Unable to read local file $localMediaurl")
|
||||||
}
|
}
|
||||||
|
@ -603,8 +605,7 @@ class LocalMediaPlayer(context: Context, callback: MediaPlayerCallback) : MediaP
|
||||||
* invalid values.
|
* invalid values.
|
||||||
*/
|
*/
|
||||||
override fun getVideoSize(): Pair<Int, Int>? {
|
override fun getVideoSize(): Pair<Int, Int>? {
|
||||||
if (status != PlayerStatus.ERROR && mediaType == MediaType.VIDEO)
|
if (status != PlayerStatus.ERROR && mediaType == MediaType.VIDEO) videoSize = Pair(videoWidth, videoHeight)
|
||||||
videoSize = Pair(videoWidth, videoHeight)
|
|
||||||
return videoSize
|
return videoSize
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -664,6 +665,12 @@ class LocalMediaPlayer(context: Context, callback: MediaPlayerCallback) : MediaP
|
||||||
init {
|
init {
|
||||||
mediaType = MediaType.UNKNOWN
|
mediaType = MediaType.UNKNOWN
|
||||||
|
|
||||||
|
if (httpDataSourceFactory == null) {
|
||||||
|
ioScope.launch {
|
||||||
|
httpDataSourceFactory = OkHttpDataSource.Factory(PodciniHttpClient.getHttpClient() as okhttp3.Call.Factory)
|
||||||
|
.setUserAgent(ClientConfig.USER_AGENT)
|
||||||
|
}
|
||||||
|
}
|
||||||
if (exoPlayer == null) {
|
if (exoPlayer == null) {
|
||||||
setupPlayerListener()
|
setupPlayerListener()
|
||||||
createStaticPlayer(context)
|
createStaticPlayer(context)
|
||||||
|
@ -837,6 +844,8 @@ class LocalMediaPlayer(context: Context, callback: MediaPlayerCallback) : MediaP
|
||||||
const val BUFFERING_ENDED: Int = -2
|
const val BUFFERING_ENDED: Int = -2
|
||||||
const val ERROR_CODE_OFFSET: Int = 1000
|
const val ERROR_CODE_OFFSET: Int = 1000
|
||||||
|
|
||||||
|
private var httpDataSourceFactory: OkHttpDataSource.Factory? = null
|
||||||
|
|
||||||
private var trackSelector: DefaultTrackSelector? = null
|
private var trackSelector: DefaultTrackSelector? = null
|
||||||
var exoPlayer: ExoPlayer? = null
|
var exoPlayer: ExoPlayer? = null
|
||||||
|
|
||||||
|
@ -897,6 +906,7 @@ class LocalMediaPlayer(context: Context, callback: MediaPlayerCallback) : MediaP
|
||||||
audioErrorListener = null
|
audioErrorListener = null
|
||||||
bufferingUpdateListener = null
|
bufferingUpdateListener = null
|
||||||
loudnessEnhancer = null
|
loudnessEnhancer = null
|
||||||
|
httpDataSourceFactory = null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,7 +11,7 @@ import ac.mdiq.podcini.playback.cast.CastPsmp
|
||||||
import ac.mdiq.podcini.playback.cast.CastStateListener
|
import ac.mdiq.podcini.playback.cast.CastStateListener
|
||||||
import ac.mdiq.podcini.playback.service.PlaybackServiceTaskManager.PSTMCallback
|
import ac.mdiq.podcini.playback.service.PlaybackServiceTaskManager.PSTMCallback
|
||||||
import ac.mdiq.podcini.preferences.PlaybackPreferences.Companion.clearCurrentlyPlayingTemporaryPlaybackSpeed
|
import ac.mdiq.podcini.preferences.PlaybackPreferences.Companion.clearCurrentlyPlayingTemporaryPlaybackSpeed
|
||||||
import ac.mdiq.podcini.preferences.PlaybackPreferences.Companion.createInstanceFromPreferences
|
import ac.mdiq.podcini.preferences.PlaybackPreferences.Companion.loadPlayableFromPreferences
|
||||||
import ac.mdiq.podcini.preferences.PlaybackPreferences.Companion.currentEpisodeIsVideo
|
import ac.mdiq.podcini.preferences.PlaybackPreferences.Companion.currentEpisodeIsVideo
|
||||||
import ac.mdiq.podcini.preferences.PlaybackPreferences.Companion.currentlyPlayingFeedMediaId
|
import ac.mdiq.podcini.preferences.PlaybackPreferences.Companion.currentlyPlayingFeedMediaId
|
||||||
import ac.mdiq.podcini.preferences.PlaybackPreferences.Companion.currentlyPlayingTemporaryPlaybackSpeed
|
import ac.mdiq.podcini.preferences.PlaybackPreferences.Companion.currentlyPlayingTemporaryPlaybackSpeed
|
||||||
|
@ -612,7 +612,7 @@ class PlaybackService : MediaSessionService() {
|
||||||
scope.launch {
|
scope.launch {
|
||||||
try {
|
try {
|
||||||
val playable = withContext(Dispatchers.IO) {
|
val playable = withContext(Dispatchers.IO) {
|
||||||
createInstanceFromPreferences(applicationContext)
|
loadPlayableFromPreferences()
|
||||||
}
|
}
|
||||||
withContext(Dispatchers.Main) {
|
withContext(Dispatchers.Main) {
|
||||||
startPlaying(playable, false)
|
startPlaying(playable, false)
|
||||||
|
@ -783,6 +783,7 @@ class PlaybackService : MediaSessionService() {
|
||||||
private fun procFlowEvents() {
|
private fun procFlowEvents() {
|
||||||
scope.launch {
|
scope.launch {
|
||||||
EventFlow.events.collectLatest { event ->
|
EventFlow.events.collectLatest { event ->
|
||||||
|
Logd(TAG, "Received event: $event")
|
||||||
when (event) {
|
when (event) {
|
||||||
is FlowEvent.PlayerErrorEvent -> playerError(event)
|
is FlowEvent.PlayerErrorEvent -> playerError(event)
|
||||||
is FlowEvent.BufferUpdateEvent -> bufferUpdate(event)
|
is FlowEvent.BufferUpdateEvent -> bufferUpdate(event)
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package ac.mdiq.podcini.preferences
|
package ac.mdiq.podcini.preferences
|
||||||
|
|
||||||
import ac.mdiq.podcini.playback.base.PlayerStatus
|
import ac.mdiq.podcini.playback.base.PlayerStatus
|
||||||
|
import ac.mdiq.podcini.preferences.UserPreferences.appPrefs
|
||||||
import ac.mdiq.podcini.storage.DBReader
|
import ac.mdiq.podcini.storage.DBReader
|
||||||
import ac.mdiq.podcini.storage.model.feed.FeedItem
|
import ac.mdiq.podcini.storage.model.feed.FeedItem
|
||||||
import ac.mdiq.podcini.storage.model.feed.FeedMedia
|
import ac.mdiq.podcini.storage.model.feed.FeedMedia
|
||||||
|
@ -14,7 +15,6 @@ import android.content.Context
|
||||||
import android.content.SharedPreferences
|
import android.content.SharedPreferences
|
||||||
import android.content.SharedPreferences.OnSharedPreferenceChangeListener
|
import android.content.SharedPreferences.OnSharedPreferenceChangeListener
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import androidx.preference.PreferenceManager
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Provides access to preferences set by the playback service. A private
|
* Provides access to preferences set by the playback service. A private
|
||||||
|
@ -88,43 +88,43 @@ class PlaybackPreferences private constructor() : OnSharedPreferenceChangeListen
|
||||||
const val PLAYER_STATUS_OTHER: Int = 3
|
const val PLAYER_STATUS_OTHER: Int = 3
|
||||||
|
|
||||||
private var instance: PlaybackPreferences? = null
|
private var instance: PlaybackPreferences? = null
|
||||||
private lateinit var prefs: SharedPreferences
|
// private lateinit var prefs: SharedPreferences
|
||||||
|
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun init(context: Context) {
|
fun init(context: Context) {
|
||||||
instance = PlaybackPreferences()
|
instance = PlaybackPreferences()
|
||||||
prefs = PreferenceManager.getDefaultSharedPreferences(context)
|
// prefs = PreferenceManager.getDefaultSharedPreferences(context)
|
||||||
prefs.registerOnSharedPreferenceChangeListener(instance)
|
appPrefs.registerOnSharedPreferenceChangeListener(instance)
|
||||||
}
|
}
|
||||||
|
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
val currentlyPlayingMediaType: Long
|
val currentlyPlayingMediaType: Long
|
||||||
get() = prefs.getLong(PREF_CURRENTLY_PLAYING_MEDIA_TYPE, NO_MEDIA_PLAYING)
|
get() = appPrefs.getLong(PREF_CURRENTLY_PLAYING_MEDIA_TYPE, NO_MEDIA_PLAYING)
|
||||||
|
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
val currentlyPlayingFeedMediaId: Long
|
val currentlyPlayingFeedMediaId: Long
|
||||||
get() = prefs.getLong(PREF_CURRENTLY_PLAYING_FEEDMEDIA_ID, NO_MEDIA_PLAYING)
|
get() = appPrefs.getLong(PREF_CURRENTLY_PLAYING_FEEDMEDIA_ID, NO_MEDIA_PLAYING)
|
||||||
|
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
val currentEpisodeIsVideo: Boolean
|
val currentEpisodeIsVideo: Boolean
|
||||||
get() = prefs.getBoolean(PREF_CURRENT_EPISODE_IS_VIDEO, false)
|
get() = appPrefs.getBoolean(PREF_CURRENT_EPISODE_IS_VIDEO, false)
|
||||||
|
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
val currentPlayerStatus: Int
|
val currentPlayerStatus: Int
|
||||||
get() = prefs.getInt(PREF_CURRENT_PLAYER_STATUS, PLAYER_STATUS_OTHER)
|
get() = appPrefs.getInt(PREF_CURRENT_PLAYER_STATUS, PLAYER_STATUS_OTHER)
|
||||||
|
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
var currentlyPlayingTemporaryPlaybackSpeed: Float
|
var currentlyPlayingTemporaryPlaybackSpeed: Float
|
||||||
get() = prefs.getFloat(PREF_CURRENTLY_PLAYING_TEMPORARY_PLAYBACK_SPEED, FeedPreferences.SPEED_USE_GLOBAL)
|
get() = appPrefs.getFloat(PREF_CURRENTLY_PLAYING_TEMPORARY_PLAYBACK_SPEED, FeedPreferences.SPEED_USE_GLOBAL)
|
||||||
set(speed) {
|
set(speed) {
|
||||||
val editor = prefs.edit()
|
val editor = appPrefs.edit()
|
||||||
editor.putFloat(PREF_CURRENTLY_PLAYING_TEMPORARY_PLAYBACK_SPEED, speed)
|
editor.putFloat(PREF_CURRENTLY_PLAYING_TEMPORARY_PLAYBACK_SPEED, speed)
|
||||||
editor.apply()
|
editor.apply()
|
||||||
}
|
}
|
||||||
|
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun writeNoMediaPlaying() {
|
fun writeNoMediaPlaying() {
|
||||||
val editor = prefs.edit()
|
val editor = appPrefs.edit()
|
||||||
editor.putLong(PREF_CURRENTLY_PLAYING_MEDIA_TYPE, NO_MEDIA_PLAYING)
|
editor.putLong(PREF_CURRENTLY_PLAYING_MEDIA_TYPE, NO_MEDIA_PLAYING)
|
||||||
editor.putLong(PREF_CURRENTLY_PLAYING_FEED_ID, NO_MEDIA_PLAYING)
|
editor.putLong(PREF_CURRENTLY_PLAYING_FEED_ID, NO_MEDIA_PLAYING)
|
||||||
editor.putLong(PREF_CURRENTLY_PLAYING_FEEDMEDIA_ID, NO_MEDIA_PLAYING)
|
editor.putLong(PREF_CURRENTLY_PLAYING_FEEDMEDIA_ID, NO_MEDIA_PLAYING)
|
||||||
|
@ -135,7 +135,7 @@ class PlaybackPreferences private constructor() : OnSharedPreferenceChangeListen
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun writeMediaPlaying(playable: Playable?, playerStatus: PlayerStatus, item: FeedItem? = null) {
|
fun writeMediaPlaying(playable: Playable?, playerStatus: PlayerStatus, item: FeedItem? = null) {
|
||||||
Logd(TAG, "Writing playback preferences ${playable?.getIdentifier()}")
|
Logd(TAG, "Writing playback preferences ${playable?.getIdentifier()}")
|
||||||
val editor = prefs.edit()
|
val editor = appPrefs.edit()
|
||||||
|
|
||||||
if (playable == null) {
|
if (playable == null) {
|
||||||
writeNoMediaPlaying()
|
writeNoMediaPlaying()
|
||||||
|
@ -162,14 +162,14 @@ class PlaybackPreferences private constructor() : OnSharedPreferenceChangeListen
|
||||||
fun writePlayerStatus(playerStatus: PlayerStatus) {
|
fun writePlayerStatus(playerStatus: PlayerStatus) {
|
||||||
Logd(TAG, "Writing player status playback preferences")
|
Logd(TAG, "Writing player status playback preferences")
|
||||||
|
|
||||||
val editor = prefs.edit()
|
val editor = appPrefs.edit()
|
||||||
editor.putInt(PREF_CURRENT_PLAYER_STATUS, getCurrentPlayerStatusAsInt(playerStatus))
|
editor.putInt(PREF_CURRENT_PLAYER_STATUS, getCurrentPlayerStatusAsInt(playerStatus))
|
||||||
editor.apply()
|
editor.apply()
|
||||||
}
|
}
|
||||||
|
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun clearCurrentlyPlayingTemporaryPlaybackSpeed() {
|
fun clearCurrentlyPlayingTemporaryPlaybackSpeed() {
|
||||||
val editor = prefs.edit()
|
val editor = appPrefs.edit()
|
||||||
editor.remove(PREF_CURRENTLY_PLAYING_TEMPORARY_PLAYBACK_SPEED)
|
editor.remove(PREF_CURRENTLY_PLAYING_TEMPORARY_PLAYBACK_SPEED)
|
||||||
editor.apply()
|
editor.apply()
|
||||||
}
|
}
|
||||||
|
@ -190,40 +190,23 @@ class PlaybackPreferences private constructor() : OnSharedPreferenceChangeListen
|
||||||
* @return The restored Playable object
|
* @return The restored Playable object
|
||||||
*/
|
*/
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun createInstanceFromPreferences(context: Context): Playable? {
|
fun loadPlayableFromPreferences(): Playable? {
|
||||||
val currentlyPlayingMedia = currentlyPlayingMediaType
|
val currentlyPlayingType = currentlyPlayingMediaType
|
||||||
Logd(TAG, "currentlyPlayingMedia: $currentlyPlayingMedia")
|
Logd(TAG, "loadPlayableFromPreferences currentlyPlayingType: $currentlyPlayingType")
|
||||||
if (currentlyPlayingMedia != NO_MEDIA_PLAYING) {
|
if (currentlyPlayingType != NO_MEDIA_PLAYING) {
|
||||||
val prefs = PreferenceManager.getDefaultSharedPreferences(context.applicationContext)
|
val type = currentlyPlayingType.toInt()
|
||||||
return createInstanceFromPreferences(currentlyPlayingMedia.toInt(), prefs)
|
if (type == FeedMedia.PLAYABLE_TYPE_FEEDMEDIA) {
|
||||||
|
var result: Playable? = null
|
||||||
|
val mediaId = appPrefs.getLong(FeedMedia.PREF_MEDIA_ID, -1)
|
||||||
|
if (mediaId != -1L) result = DBReader.getFeedMedia(mediaId)
|
||||||
|
Logd(TAG, "playable loaded: ${result?.getIdentifier()}")
|
||||||
|
return result
|
||||||
|
} else {
|
||||||
|
Log.e(TAG, "Could not restore Playable object from preferences")
|
||||||
|
return null
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Restores a playable object from a sharedPreferences file. This method might load data from the database,
|
|
||||||
* depending on the type of playable that was restored.
|
|
||||||
*
|
|
||||||
* @param type An integer that represents the type of the Playable object
|
|
||||||
* that is restored.
|
|
||||||
* @param pref The SharedPreferences file from which the Playable object
|
|
||||||
* is restored
|
|
||||||
* @return The restored Playable object
|
|
||||||
*/
|
|
||||||
private fun createInstanceFromPreferences(type: Int, pref: SharedPreferences): Playable? {
|
|
||||||
if (type == FeedMedia.PLAYABLE_TYPE_FEEDMEDIA) {
|
|
||||||
return createFeedMediaInstance(pref)
|
|
||||||
} else {
|
|
||||||
Log.e(TAG, "Could not restore Playable object from preferences")
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun createFeedMediaInstance(pref: SharedPreferences): Playable? {
|
|
||||||
var result: Playable? = null
|
|
||||||
val mediaId = pref.getLong(FeedMedia.PREF_MEDIA_ID, -1)
|
|
||||||
if (mediaId != -1L) result = DBReader.getFeedMedia(mediaId)
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,6 +16,8 @@ import ac.mdiq.podcini.preferences.UserPreferences.isStreamOverDownload
|
||||||
import ac.mdiq.podcini.preferences.UserPreferences.theme
|
import ac.mdiq.podcini.preferences.UserPreferences.theme
|
||||||
import ac.mdiq.podcini.ui.actions.swipeactions.SwipeAction
|
import ac.mdiq.podcini.ui.actions.swipeactions.SwipeAction
|
||||||
import ac.mdiq.podcini.ui.actions.swipeactions.SwipeActions
|
import ac.mdiq.podcini.ui.actions.swipeactions.SwipeActions
|
||||||
|
import ac.mdiq.podcini.ui.actions.swipeactions.SwipeActions.Companion.getSharedPrefs
|
||||||
|
import ac.mdiq.podcini.ui.actions.swipeactions.SwipeActions.Companion.prefs
|
||||||
import ac.mdiq.podcini.ui.fragment.AllEpisodesFragment
|
import ac.mdiq.podcini.ui.fragment.AllEpisodesFragment
|
||||||
import ac.mdiq.podcini.ui.fragment.QueueFragment
|
import ac.mdiq.podcini.ui.fragment.QueueFragment
|
||||||
import ac.mdiq.podcini.util.error.CrashReportWriter.Companion.file
|
import ac.mdiq.podcini.util.error.CrashReportWriter.Companion.file
|
||||||
|
@ -96,8 +98,9 @@ object PreferenceUpgrader {
|
||||||
prefs.edit().putString(UserPreferences.PREF_HARDWARE_PREVIOUS_BUTTON, KeyEvent.KEYCODE_MEDIA_PREVIOUS.toString()).apply()
|
prefs.edit().putString(UserPreferences.PREF_HARDWARE_PREVIOUS_BUTTON, KeyEvent.KEYCODE_MEDIA_PREVIOUS.toString()).apply()
|
||||||
}
|
}
|
||||||
if (oldVersion < 2040000) {
|
if (oldVersion < 2040000) {
|
||||||
val swipePrefs = context.getSharedPreferences(SwipeActions.PREF_NAME, Context.MODE_PRIVATE)
|
getSharedPrefs(context)
|
||||||
swipePrefs.edit().putString(SwipeActions.KEY_PREFIX_SWIPEACTIONS + QueueFragment.TAG,
|
// val swipePrefs = context.getSharedPreferences(SwipeActions.SWIPE_ACTIONS_PREF_NAME, Context.MODE_PRIVATE)
|
||||||
|
SwipeActions.prefs!!.edit().putString(SwipeActions.KEY_PREFIX_SWIPEACTIONS + QueueFragment.TAG,
|
||||||
SwipeAction.REMOVE_FROM_QUEUE + "," + SwipeAction.REMOVE_FROM_QUEUE).apply()
|
SwipeAction.REMOVE_FROM_QUEUE + "," + SwipeAction.REMOVE_FROM_QUEUE).apply()
|
||||||
}
|
}
|
||||||
if (oldVersion < 2050000) prefs.edit().putBoolean(UserPreferences.PREF_PAUSE_PLAYBACK_FOR_FOCUS_LOSS, true).apply()
|
if (oldVersion < 2050000) prefs.edit().putBoolean(UserPreferences.PREF_PAUSE_PLAYBACK_FOR_FOCUS_LOSS, true).apply()
|
||||||
|
|
|
@ -138,7 +138,7 @@ object UserPreferences {
|
||||||
const val DEFAULT_PAGE_REMEMBER: String = "remember"
|
const val DEFAULT_PAGE_REMEMBER: String = "remember"
|
||||||
|
|
||||||
private lateinit var context: Context
|
private lateinit var context: Context
|
||||||
private lateinit var prefs: SharedPreferences
|
lateinit var appPrefs: SharedPreferences
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets up the UserPreferences class.
|
* Sets up the UserPreferences class.
|
||||||
|
@ -150,41 +150,41 @@ object UserPreferences {
|
||||||
Logd(TAG, "Creating new instance of UserPreferences")
|
Logd(TAG, "Creating new instance of UserPreferences")
|
||||||
|
|
||||||
UserPreferences.context = context.applicationContext
|
UserPreferences.context = context.applicationContext
|
||||||
prefs = PreferenceManager.getDefaultSharedPreferences(context)
|
appPrefs = PreferenceManager.getDefaultSharedPreferences(context)
|
||||||
|
|
||||||
createNoMediaFile()
|
createNoMediaFile()
|
||||||
}
|
}
|
||||||
|
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
var theme: ThemePreference
|
var theme: ThemePreference
|
||||||
get() = when (prefs.getString(PREF_THEME, "system")) {
|
get() = when (appPrefs.getString(PREF_THEME, "system")) {
|
||||||
"0" -> ThemePreference.LIGHT
|
"0" -> ThemePreference.LIGHT
|
||||||
"1" -> ThemePreference.DARK
|
"1" -> ThemePreference.DARK
|
||||||
else -> ThemePreference.SYSTEM
|
else -> ThemePreference.SYSTEM
|
||||||
}
|
}
|
||||||
set(theme) {
|
set(theme) {
|
||||||
when (theme) {
|
when (theme) {
|
||||||
ThemePreference.LIGHT -> prefs.edit().putString(PREF_THEME, "0").apply()
|
ThemePreference.LIGHT -> appPrefs.edit().putString(PREF_THEME, "0").apply()
|
||||||
ThemePreference.DARK -> prefs.edit().putString(PREF_THEME, "1").apply()
|
ThemePreference.DARK -> appPrefs.edit().putString(PREF_THEME, "1").apply()
|
||||||
else -> prefs.edit().putString(PREF_THEME, "system").apply()
|
else -> appPrefs.edit().putString(PREF_THEME, "system").apply()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val isBlackTheme: Boolean
|
val isBlackTheme: Boolean
|
||||||
get() = prefs.getBoolean(PREF_THEME_BLACK, false)
|
get() = appPrefs.getBoolean(PREF_THEME_BLACK, false)
|
||||||
|
|
||||||
val isThemeColorTinted: Boolean
|
val isThemeColorTinted: Boolean
|
||||||
get() = Build.VERSION.SDK_INT >= 31 && prefs.getBoolean(PREF_TINTED_COLORS, false)
|
get() = Build.VERSION.SDK_INT >= 31 && appPrefs.getBoolean(PREF_TINTED_COLORS, false)
|
||||||
|
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
var hiddenDrawerItems: List<String>
|
var hiddenDrawerItems: List<String>
|
||||||
get() {
|
get() {
|
||||||
val hiddenItems = prefs.getString(PREF_HIDDEN_DRAWER_ITEMS, "")
|
val hiddenItems = appPrefs.getString(PREF_HIDDEN_DRAWER_ITEMS, "")
|
||||||
return ArrayList(listOf(*TextUtils.split(hiddenItems, ",")))
|
return ArrayList(listOf(*TextUtils.split(hiddenItems, ",")))
|
||||||
}
|
}
|
||||||
set(items) {
|
set(items) {
|
||||||
val str = TextUtils.join(",", items)
|
val str = TextUtils.join(",", items)
|
||||||
prefs.edit()
|
appPrefs.edit()
|
||||||
.putString(PREF_HIDDEN_DRAWER_ITEMS, str)
|
.putString(PREF_HIDDEN_DRAWER_ITEMS, str)
|
||||||
.apply()
|
.apply()
|
||||||
}
|
}
|
||||||
|
@ -192,7 +192,7 @@ object UserPreferences {
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
var fullNotificationButtons: List<Int>
|
var fullNotificationButtons: List<Int>
|
||||||
get() {
|
get() {
|
||||||
val buttons = TextUtils.split(prefs.getString(PREF_FULL_NOTIFICATION_BUTTONS, "$NOTIFICATION_BUTTON_SKIP,$NOTIFICATION_BUTTON_PLAYBACK_SPEED"), ",")
|
val buttons = TextUtils.split(appPrefs.getString(PREF_FULL_NOTIFICATION_BUTTONS, "$NOTIFICATION_BUTTON_SKIP,$NOTIFICATION_BUTTON_PLAYBACK_SPEED"), ",")
|
||||||
val notificationButtons: MutableList<Int> = ArrayList()
|
val notificationButtons: MutableList<Int> = ArrayList()
|
||||||
for (button in buttons) {
|
for (button in buttons) {
|
||||||
notificationButtons.add(button.toInt())
|
notificationButtons.add(button.toInt())
|
||||||
|
@ -200,8 +200,8 @@ object UserPreferences {
|
||||||
return notificationButtons
|
return notificationButtons
|
||||||
}
|
}
|
||||||
set(items) {
|
set(items) {
|
||||||
val str = TextUtils.join(",", items!!)
|
val str = TextUtils.join(",", items)
|
||||||
prefs.edit()
|
appPrefs.edit()
|
||||||
.putString(PREF_FULL_NOTIFICATION_BUTTONS, str)
|
.putString(PREF_FULL_NOTIFICATION_BUTTONS, str)
|
||||||
.apply()
|
.apply()
|
||||||
}
|
}
|
||||||
|
@ -216,7 +216,7 @@ object UserPreferences {
|
||||||
* @return `true` if button should be shown, `false` otherwise
|
* @return `true` if button should be shown, `false` otherwise
|
||||||
*/
|
*/
|
||||||
private fun showButtonOnFullNotification(buttonId: Int): Boolean {
|
private fun showButtonOnFullNotification(buttonId: Int): Boolean {
|
||||||
return fullNotificationButtons!!.contains(buttonId)
|
return fullNotificationButtons.contains(buttonId)
|
||||||
}
|
}
|
||||||
|
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
|
@ -237,13 +237,13 @@ object UserPreferences {
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
val feedOrder: Int
|
val feedOrder: Int
|
||||||
get() {
|
get() {
|
||||||
val value = prefs.getString(PREF_DRAWER_FEED_ORDER, "" + FEED_ORDER_COUNTER)
|
val value = appPrefs.getString(PREF_DRAWER_FEED_ORDER, "" + FEED_ORDER_COUNTER)
|
||||||
return value!!.toInt()
|
return value!!.toInt()
|
||||||
}
|
}
|
||||||
|
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun setFeedOrder(selected: String?) {
|
fun setFeedOrder(selected: String?) {
|
||||||
prefs.edit()
|
appPrefs.edit()
|
||||||
.putString(PREF_DRAWER_FEED_ORDER, selected)
|
.putString(PREF_DRAWER_FEED_ORDER, selected)
|
||||||
.apply()
|
.apply()
|
||||||
}
|
}
|
||||||
|
@ -251,7 +251,7 @@ object UserPreferences {
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
val feedCounterSetting: FeedCounter
|
val feedCounterSetting: FeedCounter
|
||||||
get() {
|
get() {
|
||||||
val value = prefs.getString(PREF_DRAWER_FEED_COUNTER, "" + FeedCounter.SHOW_UNPLAYED.id)
|
val value = appPrefs.getString(PREF_DRAWER_FEED_COUNTER, "" + FeedCounter.SHOW_UNPLAYED.id)
|
||||||
return FeedCounter.fromOrdinal(value!!.toInt())
|
return FeedCounter.fromOrdinal(value!!.toInt())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -259,14 +259,14 @@ object UserPreferences {
|
||||||
/**
|
/**
|
||||||
* @return `true` if episodes should use their own cover, `false` otherwise
|
* @return `true` if episodes should use their own cover, `false` otherwise
|
||||||
*/
|
*/
|
||||||
get() = prefs.getBoolean(PREF_USE_EPISODE_COVER, true)
|
get() = appPrefs.getBoolean(PREF_USE_EPISODE_COVER, true)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return `true` if we should show remaining time or the duration
|
* @return `true` if we should show remaining time or the duration
|
||||||
*/
|
*/
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun shouldShowRemainingTime(): Boolean {
|
fun shouldShowRemainingTime(): Boolean {
|
||||||
return prefs.getBoolean(PREF_SHOW_TIME_LEFT, false)
|
return appPrefs.getBoolean(PREF_SHOW_TIME_LEFT, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -277,7 +277,7 @@ object UserPreferences {
|
||||||
*/
|
*/
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun setShowRemainTimeSetting(showRemain: Boolean?) {
|
fun setShowRemainTimeSetting(showRemain: Boolean?) {
|
||||||
prefs.edit().putBoolean(PREF_SHOW_TIME_LEFT, showRemain!!).apply()
|
appPrefs.edit().putBoolean(PREF_SHOW_TIME_LEFT, showRemain!!).apply()
|
||||||
}
|
}
|
||||||
|
|
||||||
val notifyPriority: Int
|
val notifyPriority: Int
|
||||||
|
@ -286,7 +286,7 @@ object UserPreferences {
|
||||||
*
|
*
|
||||||
* @return NotificationCompat.PRIORITY_MAX or NotificationCompat.PRIORITY_DEFAULT
|
* @return NotificationCompat.PRIORITY_MAX or NotificationCompat.PRIORITY_DEFAULT
|
||||||
*/
|
*/
|
||||||
get() = if (prefs.getBoolean(PREF_EXPANDED_NOTIFICATION, false)) NotificationCompat.PRIORITY_MAX else NotificationCompat.PRIORITY_DEFAULT
|
get() = if (appPrefs.getBoolean(PREF_EXPANDED_NOTIFICATION, false)) NotificationCompat.PRIORITY_MAX else NotificationCompat.PRIORITY_DEFAULT
|
||||||
|
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
val isPersistNotify: Boolean
|
val isPersistNotify: Boolean
|
||||||
|
@ -295,23 +295,23 @@ object UserPreferences {
|
||||||
*
|
*
|
||||||
* @return `true` if notifications are persistent, `false` otherwise
|
* @return `true` if notifications are persistent, `false` otherwise
|
||||||
*/
|
*/
|
||||||
get() = prefs.getBoolean(PREF_PERSISTENT_NOTIFICATION, true)
|
get() = appPrefs.getBoolean(PREF_PERSISTENT_NOTIFICATION, true)
|
||||||
|
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
val showDownloadReportRaw: Boolean
|
val showDownloadReportRaw: Boolean
|
||||||
/**
|
/**
|
||||||
* Used for migration of the preference to system notification channels.
|
* Used for migration of the preference to system notification channels.
|
||||||
*/
|
*/
|
||||||
get() = prefs.getBoolean(PREF_SHOW_DOWNLOAD_REPORT, true)
|
get() = appPrefs.getBoolean(PREF_SHOW_DOWNLOAD_REPORT, true)
|
||||||
|
|
||||||
fun enqueueDownloadedEpisodes(): Boolean {
|
fun enqueueDownloadedEpisodes(): Boolean {
|
||||||
return prefs.getBoolean(PREF_ENQUEUE_DOWNLOADED, true)
|
return appPrefs.getBoolean(PREF_ENQUEUE_DOWNLOADED, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
var enqueueLocation: EnqueueLocation
|
var enqueueLocation: EnqueueLocation
|
||||||
get() {
|
get() {
|
||||||
val valStr = prefs.getString(PREF_ENQUEUE_LOCATION, EnqueueLocation.BACK.name)
|
val valStr = appPrefs.getString(PREF_ENQUEUE_LOCATION, EnqueueLocation.BACK.name)
|
||||||
try {
|
try {
|
||||||
return EnqueueLocation.valueOf(valStr!!)
|
return EnqueueLocation.valueOf(valStr!!)
|
||||||
} catch (t: Throwable) {
|
} catch (t: Throwable) {
|
||||||
|
@ -321,64 +321,64 @@ object UserPreferences {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
set(location) {
|
set(location) {
|
||||||
prefs.edit().putString(PREF_ENQUEUE_LOCATION, location.name).apply()
|
appPrefs.edit().putString(PREF_ENQUEUE_LOCATION, location.name).apply()
|
||||||
}
|
}
|
||||||
|
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
val isPauseOnHeadsetDisconnect: Boolean
|
val isPauseOnHeadsetDisconnect: Boolean
|
||||||
get() = prefs.getBoolean(PREF_PAUSE_ON_HEADSET_DISCONNECT, true)
|
get() = appPrefs.getBoolean(PREF_PAUSE_ON_HEADSET_DISCONNECT, true)
|
||||||
|
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
val isUnpauseOnHeadsetReconnect: Boolean
|
val isUnpauseOnHeadsetReconnect: Boolean
|
||||||
get() = prefs.getBoolean(PREF_UNPAUSE_ON_HEADSET_RECONNECT, true)
|
get() = appPrefs.getBoolean(PREF_UNPAUSE_ON_HEADSET_RECONNECT, true)
|
||||||
|
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
val isUnpauseOnBluetoothReconnect: Boolean
|
val isUnpauseOnBluetoothReconnect: Boolean
|
||||||
get() = prefs.getBoolean(PREF_UNPAUSE_ON_BLUETOOTH_RECONNECT, false)
|
get() = appPrefs.getBoolean(PREF_UNPAUSE_ON_BLUETOOTH_RECONNECT, false)
|
||||||
|
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
val hardwareForwardButton: Int
|
val hardwareForwardButton: Int
|
||||||
get() = prefs.getString(PREF_HARDWARE_FORWARD_BUTTON, KeyEvent.KEYCODE_MEDIA_FAST_FORWARD.toString())!!.toInt()
|
get() = appPrefs.getString(PREF_HARDWARE_FORWARD_BUTTON, KeyEvent.KEYCODE_MEDIA_FAST_FORWARD.toString())!!.toInt()
|
||||||
|
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
val hardwarePreviousButton: Int
|
val hardwarePreviousButton: Int
|
||||||
get() = prefs.getString(PREF_HARDWARE_PREVIOUS_BUTTON, KeyEvent.KEYCODE_MEDIA_REWIND.toString())!!.toInt()
|
get() = appPrefs.getString(PREF_HARDWARE_PREVIOUS_BUTTON, KeyEvent.KEYCODE_MEDIA_REWIND.toString())!!.toInt()
|
||||||
|
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
@set:VisibleForTesting
|
@set:VisibleForTesting
|
||||||
var isFollowQueue: Boolean
|
var isFollowQueue: Boolean
|
||||||
get() = prefs.getBoolean(PREF_FOLLOW_QUEUE, true)
|
get() = appPrefs.getBoolean(PREF_FOLLOW_QUEUE, true)
|
||||||
/**
|
/**
|
||||||
* Set to true to enable Continuous Playback
|
* Set to true to enable Continuous Playback
|
||||||
*/
|
*/
|
||||||
set(value) {
|
set(value) {
|
||||||
prefs.edit().putBoolean(PREF_FOLLOW_QUEUE, value).apply()
|
appPrefs.edit().putBoolean(PREF_FOLLOW_QUEUE, value).apply()
|
||||||
}
|
}
|
||||||
|
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun shouldSkipKeepEpisode(): Boolean {
|
fun shouldSkipKeepEpisode(): Boolean {
|
||||||
return prefs.getBoolean(PREF_SKIP_KEEPS_EPISODE, true)
|
return appPrefs.getBoolean(PREF_SKIP_KEEPS_EPISODE, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun shouldFavoriteKeepEpisode(): Boolean {
|
fun shouldFavoriteKeepEpisode(): Boolean {
|
||||||
return prefs.getBoolean(PREF_FAVORITE_KEEPS_EPISODE, true)
|
return appPrefs.getBoolean(PREF_FAVORITE_KEEPS_EPISODE, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
val isAutoDelete: Boolean
|
val isAutoDelete: Boolean
|
||||||
get() = prefs.getBoolean(PREF_AUTO_DELETE, false)
|
get() = appPrefs.getBoolean(PREF_AUTO_DELETE, false)
|
||||||
|
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
val isAutoDeleteLocal: Boolean
|
val isAutoDeleteLocal: Boolean
|
||||||
get() = prefs.getBoolean(PREF_AUTO_DELETE_LOCAL, false)
|
get() = appPrefs.getBoolean(PREF_AUTO_DELETE_LOCAL, false)
|
||||||
|
|
||||||
val smartMarkAsPlayedSecs: Int
|
val smartMarkAsPlayedSecs: Int
|
||||||
get() = prefs.getString(PREF_SMART_MARK_AS_PLAYED_SECS, "30")!!.toInt()
|
get() = appPrefs.getString(PREF_SMART_MARK_AS_PLAYED_SECS, "30")!!.toInt()
|
||||||
|
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun shouldDeleteRemoveFromQueue(): Boolean {
|
fun shouldDeleteRemoveFromQueue(): Boolean {
|
||||||
return prefs.getBoolean(PREF_DELETE_REMOVES_FROM_QUEUE, false)
|
return appPrefs.getBoolean(PREF_DELETE_REMOVES_FROM_QUEUE, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
|
@ -389,7 +389,7 @@ object UserPreferences {
|
||||||
private val audioPlaybackSpeed: Float
|
private val audioPlaybackSpeed: Float
|
||||||
get() {
|
get() {
|
||||||
try {
|
try {
|
||||||
return prefs.getString(PREF_PLAYBACK_SPEED, "1.00")!!.toFloat()
|
return appPrefs.getString(PREF_PLAYBACK_SPEED, "1.00")!!.toFloat()
|
||||||
} catch (e: NumberFormatException) {
|
} catch (e: NumberFormatException) {
|
||||||
Log.e(TAG, Log.getStackTraceString(e))
|
Log.e(TAG, Log.getStackTraceString(e))
|
||||||
setPlaybackSpeed(1.0f)
|
setPlaybackSpeed(1.0f)
|
||||||
|
@ -400,7 +400,7 @@ object UserPreferences {
|
||||||
val videoPlayMode: Int
|
val videoPlayMode: Int
|
||||||
get() {
|
get() {
|
||||||
try {
|
try {
|
||||||
return prefs.getString(PREF_VIDEO_MODE, "1")!!.toInt()
|
return appPrefs.getString(PREF_VIDEO_MODE, "1")!!.toInt()
|
||||||
} catch (e: NumberFormatException) {
|
} catch (e: NumberFormatException) {
|
||||||
Log.e(TAG, Log.getStackTraceString(e))
|
Log.e(TAG, Log.getStackTraceString(e))
|
||||||
setVideoMode(1)
|
setVideoMode(1)
|
||||||
|
@ -412,7 +412,7 @@ object UserPreferences {
|
||||||
var videoPlaybackSpeed: Float
|
var videoPlaybackSpeed: Float
|
||||||
get() {
|
get() {
|
||||||
try {
|
try {
|
||||||
return prefs.getString(PREF_VIDEO_PLAYBACK_SPEED, "1.00")!!.toFloat()
|
return appPrefs.getString(PREF_VIDEO_PLAYBACK_SPEED, "1.00")!!.toFloat()
|
||||||
} catch (e: NumberFormatException) {
|
} catch (e: NumberFormatException) {
|
||||||
Log.e(TAG, Log.getStackTraceString(e))
|
Log.e(TAG, Log.getStackTraceString(e))
|
||||||
videoPlaybackSpeed = 1.0f
|
videoPlaybackSpeed = 1.0f
|
||||||
|
@ -420,21 +420,21 @@ object UserPreferences {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
set(speed) {
|
set(speed) {
|
||||||
prefs.edit()
|
appPrefs.edit()
|
||||||
.putString(PREF_VIDEO_PLAYBACK_SPEED, speed.toString())
|
.putString(PREF_VIDEO_PLAYBACK_SPEED, speed.toString())
|
||||||
.apply()
|
.apply()
|
||||||
}
|
}
|
||||||
|
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
var isSkipSilence: Boolean
|
var isSkipSilence: Boolean
|
||||||
get() = prefs.getBoolean(PREF_PLAYBACK_SKIP_SILENCE, false)
|
get() = appPrefs.getBoolean(PREF_PLAYBACK_SKIP_SILENCE, false)
|
||||||
set(skipSilence) {
|
set(skipSilence) {
|
||||||
prefs.edit().putBoolean(PREF_PLAYBACK_SKIP_SILENCE, skipSilence).apply()
|
appPrefs.edit().putBoolean(PREF_PLAYBACK_SKIP_SILENCE, skipSilence).apply()
|
||||||
}
|
}
|
||||||
|
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
var playbackSpeedArray: List<Float>
|
var playbackSpeedArray: List<Float>
|
||||||
get() = readPlaybackSpeedArray(prefs.getString(PREF_PLAYBACK_SPEED_ARRAY, null))
|
get() = readPlaybackSpeedArray(appPrefs.getString(PREF_PLAYBACK_SPEED_ARRAY, null))
|
||||||
set(speeds) {
|
set(speeds) {
|
||||||
val format = DecimalFormatSymbols(Locale.US)
|
val format = DecimalFormatSymbols(Locale.US)
|
||||||
format.decimalSeparator = '.'
|
format.decimalSeparator = '.'
|
||||||
|
@ -443,16 +443,16 @@ object UserPreferences {
|
||||||
for (speed in speeds) {
|
for (speed in speeds) {
|
||||||
jsonArray.put(speedFormat.format(speed.toDouble()))
|
jsonArray.put(speedFormat.format(speed.toDouble()))
|
||||||
}
|
}
|
||||||
prefs.edit().putString(PREF_PLAYBACK_SPEED_ARRAY, jsonArray.toString()).apply()
|
appPrefs.edit().putString(PREF_PLAYBACK_SPEED_ARRAY, jsonArray.toString()).apply()
|
||||||
}
|
}
|
||||||
|
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun shouldPauseForFocusLoss(): Boolean {
|
fun shouldPauseForFocusLoss(): Boolean {
|
||||||
return prefs.getBoolean(PREF_PAUSE_PLAYBACK_FOR_FOCUS_LOSS, true)
|
return appPrefs.getBoolean(PREF_PAUSE_PLAYBACK_FOR_FOCUS_LOSS, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
val updateInterval: Long
|
val updateInterval: Long
|
||||||
get() = prefs.getString(PREF_UPDATE_INTERVAL, "12")!!.toInt().toLong()
|
get() = appPrefs.getString(PREF_UPDATE_INTERVAL, "12")!!.toInt().toLong()
|
||||||
|
|
||||||
val isAutoUpdateDisabled: Boolean
|
val isAutoUpdateDisabled: Boolean
|
||||||
get() = updateInterval == 0L
|
get() = updateInterval == 0L
|
||||||
|
@ -460,7 +460,7 @@ object UserPreferences {
|
||||||
private fun isAllowMobileFor(type: String): Boolean {
|
private fun isAllowMobileFor(type: String): Boolean {
|
||||||
val defaultValue = HashSet<String>()
|
val defaultValue = HashSet<String>()
|
||||||
defaultValue.add("images")
|
defaultValue.add("images")
|
||||||
val allowed = prefs.getStringSet(PREF_MOBILE_UPDATE, defaultValue)
|
val allowed = appPrefs.getStringSet(PREF_MOBILE_UPDATE, defaultValue)
|
||||||
return allowed!!.contains(type)
|
return allowed!!.contains(type)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -509,12 +509,12 @@ object UserPreferences {
|
||||||
private fun setAllowMobileFor(type: String, allow: Boolean) {
|
private fun setAllowMobileFor(type: String, allow: Boolean) {
|
||||||
val defaultValue = HashSet<String>()
|
val defaultValue = HashSet<String>()
|
||||||
defaultValue.add("images")
|
defaultValue.add("images")
|
||||||
val getValueStringSet = prefs.getStringSet(PREF_MOBILE_UPDATE, defaultValue)
|
val getValueStringSet = appPrefs.getStringSet(PREF_MOBILE_UPDATE, defaultValue)
|
||||||
val allowed: MutableSet<String> = HashSet(getValueStringSet!!)
|
val allowed: MutableSet<String> = HashSet(getValueStringSet!!)
|
||||||
if (allow) allowed.add(type)
|
if (allow) allowed.add(type)
|
||||||
else allowed.remove(type)
|
else allowed.remove(type)
|
||||||
|
|
||||||
prefs.edit().putStringSet(PREF_MOBILE_UPDATE, allowed).apply()
|
appPrefs.edit().putStringSet(PREF_MOBILE_UPDATE, allowed).apply()
|
||||||
}
|
}
|
||||||
|
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
|
@ -524,29 +524,29 @@ object UserPreferences {
|
||||||
* negative integer EPISODE_CACHE_SIZE_UNLIMITED if the cache size is set to
|
* negative integer EPISODE_CACHE_SIZE_UNLIMITED if the cache size is set to
|
||||||
* 'unlimited'.
|
* 'unlimited'.
|
||||||
*/
|
*/
|
||||||
get() = prefs.getString(PREF_EPISODE_CACHE_SIZE, "20")!!.toInt()
|
get() = appPrefs.getString(PREF_EPISODE_CACHE_SIZE, "20")!!.toInt()
|
||||||
|
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
@set:VisibleForTesting
|
@set:VisibleForTesting
|
||||||
var isEnableAutodownload: Boolean
|
var isEnableAutodownload: Boolean
|
||||||
get() = prefs.getBoolean(PREF_ENABLE_AUTODL, false)
|
get() = appPrefs.getBoolean(PREF_ENABLE_AUTODL, false)
|
||||||
set(enabled) {
|
set(enabled) {
|
||||||
prefs.edit().putBoolean(PREF_ENABLE_AUTODL, enabled).apply()
|
appPrefs.edit().putBoolean(PREF_ENABLE_AUTODL, enabled).apply()
|
||||||
}
|
}
|
||||||
|
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
val isEnableAutodownloadOnBattery: Boolean
|
val isEnableAutodownloadOnBattery: Boolean
|
||||||
get() = prefs.getBoolean(PREF_ENABLE_AUTODL_ON_BATTERY, true)
|
get() = appPrefs.getBoolean(PREF_ENABLE_AUTODL_ON_BATTERY, true)
|
||||||
|
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
val isEnableAutodownloadWifiFilter: Boolean
|
val isEnableAutodownloadWifiFilter: Boolean
|
||||||
get() = Build.VERSION.SDK_INT < 29 && prefs.getBoolean(PREF_ENABLE_AUTODL_WIFI_FILTER, false)
|
get() = Build.VERSION.SDK_INT < 29 && appPrefs.getBoolean(PREF_ENABLE_AUTODL_WIFI_FILTER, false)
|
||||||
|
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
var speedforwardSpeed: Float
|
var speedforwardSpeed: Float
|
||||||
get() {
|
get() {
|
||||||
try {
|
try {
|
||||||
return prefs.getString(PREF_SPEEDFORWRD_SPEED, "0.00")!!.toFloat()
|
return appPrefs.getString(PREF_SPEEDFORWRD_SPEED, "0.00")!!.toFloat()
|
||||||
} catch (e: NumberFormatException) {
|
} catch (e: NumberFormatException) {
|
||||||
Log.e(TAG, Log.getStackTraceString(e))
|
Log.e(TAG, Log.getStackTraceString(e))
|
||||||
speedforwardSpeed = 0.0f
|
speedforwardSpeed = 0.0f
|
||||||
|
@ -554,14 +554,14 @@ object UserPreferences {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
set(speed) {
|
set(speed) {
|
||||||
prefs.edit().putString(PREF_SPEEDFORWRD_SPEED, speed.toString()).apply()
|
appPrefs.edit().putString(PREF_SPEEDFORWRD_SPEED, speed.toString()).apply()
|
||||||
}
|
}
|
||||||
|
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
var fallbackSpeed: Float
|
var fallbackSpeed: Float
|
||||||
get() {
|
get() {
|
||||||
try {
|
try {
|
||||||
return prefs.getString(PREF_FALLBACK_SPEED, "0.00")!!.toFloat()
|
return appPrefs.getString(PREF_FALLBACK_SPEED, "0.00")!!.toFloat()
|
||||||
} catch (e: NumberFormatException) {
|
} catch (e: NumberFormatException) {
|
||||||
Log.e(TAG, Log.getStackTraceString(e))
|
Log.e(TAG, Log.getStackTraceString(e))
|
||||||
fallbackSpeed = 0.0f
|
fallbackSpeed = 0.0f
|
||||||
|
@ -569,42 +569,42 @@ object UserPreferences {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
set(speed) {
|
set(speed) {
|
||||||
prefs.edit().putString(PREF_FALLBACK_SPEED, speed.toString()).apply()
|
appPrefs.edit().putString(PREF_FALLBACK_SPEED, speed.toString()).apply()
|
||||||
}
|
}
|
||||||
|
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
var fastForwardSecs: Int
|
var fastForwardSecs: Int
|
||||||
get() = prefs.getInt(PREF_FAST_FORWARD_SECS, 30)
|
get() = appPrefs.getInt(PREF_FAST_FORWARD_SECS, 30)
|
||||||
set(secs) {
|
set(secs) {
|
||||||
prefs.edit().putInt(PREF_FAST_FORWARD_SECS, secs).apply()
|
appPrefs.edit().putInt(PREF_FAST_FORWARD_SECS, secs).apply()
|
||||||
}
|
}
|
||||||
|
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
var rewindSecs: Int
|
var rewindSecs: Int
|
||||||
get() = prefs.getInt(PREF_REWIND_SECS, 10)
|
get() = appPrefs.getInt(PREF_REWIND_SECS, 10)
|
||||||
set(secs) {
|
set(secs) {
|
||||||
prefs.edit().putInt(PREF_REWIND_SECS, secs).apply()
|
appPrefs.edit().putInt(PREF_REWIND_SECS, secs).apply()
|
||||||
}
|
}
|
||||||
|
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
val autodownloadSelectedNetworks: Array<String>
|
val autodownloadSelectedNetworks: Array<String>
|
||||||
get() {
|
get() {
|
||||||
val selectedNetWorks = prefs.getString(PREF_AUTODL_SELECTED_NETWORKS, "")
|
val selectedNetWorks = appPrefs.getString(PREF_AUTODL_SELECTED_NETWORKS, "")
|
||||||
return TextUtils.split(selectedNetWorks, ",")
|
return TextUtils.split(selectedNetWorks, ",")
|
||||||
}
|
}
|
||||||
|
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
var proxyConfig: ProxyConfig
|
var proxyConfig: ProxyConfig
|
||||||
get() {
|
get() {
|
||||||
val type = Proxy.Type.valueOf(prefs.getString(PREF_PROXY_TYPE, Proxy.Type.DIRECT.name)!!)
|
val type = Proxy.Type.valueOf(appPrefs.getString(PREF_PROXY_TYPE, Proxy.Type.DIRECT.name)!!)
|
||||||
val host = prefs.getString(PREF_PROXY_HOST, null)
|
val host = appPrefs.getString(PREF_PROXY_HOST, null)
|
||||||
val port = prefs.getInt(PREF_PROXY_PORT, 0)
|
val port = appPrefs.getInt(PREF_PROXY_PORT, 0)
|
||||||
val username = prefs.getString(PREF_PROXY_USER, null)
|
val username = appPrefs.getString(PREF_PROXY_USER, null)
|
||||||
val password = prefs.getString(PREF_PROXY_PASSWORD, null)
|
val password = appPrefs.getString(PREF_PROXY_PASSWORD, null)
|
||||||
return ProxyConfig(type, host, port, username, password)
|
return ProxyConfig(type, host, port, username, password)
|
||||||
}
|
}
|
||||||
set(config) {
|
set(config) {
|
||||||
val editor = prefs.edit()
|
val editor = appPrefs.edit()
|
||||||
editor.putString(PREF_PROXY_TYPE, config.type.name)
|
editor.putString(PREF_PROXY_TYPE, config.type.name)
|
||||||
if (config.host.isNullOrEmpty()) editor.remove(PREF_PROXY_HOST)
|
if (config.host.isNullOrEmpty()) editor.remove(PREF_PROXY_HOST)
|
||||||
else editor.putString(PREF_PROXY_HOST, config.host)
|
else editor.putString(PREF_PROXY_HOST, config.host)
|
||||||
|
@ -623,31 +623,31 @@ object UserPreferences {
|
||||||
|
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
var isQueueLocked: Boolean
|
var isQueueLocked: Boolean
|
||||||
get() = prefs.getBoolean(PREF_QUEUE_LOCKED, false)
|
get() = appPrefs.getBoolean(PREF_QUEUE_LOCKED, false)
|
||||||
set(locked) {
|
set(locked) {
|
||||||
prefs.edit().putBoolean(PREF_QUEUE_LOCKED, locked).apply()
|
appPrefs.edit().putBoolean(PREF_QUEUE_LOCKED, locked).apply()
|
||||||
}
|
}
|
||||||
|
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun setPlaybackSpeed(speed: Float) {
|
fun setPlaybackSpeed(speed: Float) {
|
||||||
prefs.edit().putString(PREF_PLAYBACK_SPEED, speed.toString()).apply()
|
appPrefs.edit().putString(PREF_PLAYBACK_SPEED, speed.toString()).apply()
|
||||||
}
|
}
|
||||||
|
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun setVideoMode(mode: Int) {
|
fun setVideoMode(mode: Int) {
|
||||||
prefs.edit().putString(PREF_VIDEO_MODE, mode.toString()).apply()
|
appPrefs.edit().putString(PREF_VIDEO_MODE, mode.toString()).apply()
|
||||||
}
|
}
|
||||||
|
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun setAutodownloadSelectedNetworks(value: Array<String?>?) {
|
fun setAutodownloadSelectedNetworks(value: Array<String?>?) {
|
||||||
prefs.edit().putString(PREF_AUTODL_SELECTED_NETWORKS, TextUtils.join(",", value!!)).apply()
|
appPrefs.edit().putString(PREF_AUTODL_SELECTED_NETWORKS, TextUtils.join(",", value!!)).apply()
|
||||||
}
|
}
|
||||||
|
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun gpodnetNotificationsEnabled(): Boolean {
|
fun gpodnetNotificationsEnabled(): Boolean {
|
||||||
if (Build.VERSION.SDK_INT >= 26) return true // System handles notification preferences
|
if (Build.VERSION.SDK_INT >= 26) return true // System handles notification preferences
|
||||||
|
|
||||||
return prefs.getBoolean(PREF_GPODNET_NOTIFICATIONS, true)
|
return appPrefs.getBoolean(PREF_GPODNET_NOTIFICATIONS, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
|
@ -655,11 +655,11 @@ object UserPreferences {
|
||||||
/**
|
/**
|
||||||
* Used for migration of the preference to system notification channels.
|
* Used for migration of the preference to system notification channels.
|
||||||
*/
|
*/
|
||||||
get() = prefs.getBoolean(PREF_GPODNET_NOTIFICATIONS, true)
|
get() = appPrefs.getBoolean(PREF_GPODNET_NOTIFICATIONS, true)
|
||||||
|
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun setGpodnetNotificationsEnabled() {
|
fun setGpodnetNotificationsEnabled() {
|
||||||
prefs.edit().putBoolean(PREF_GPODNET_NOTIFICATIONS, true).apply()
|
appPrefs.edit().putBoolean(PREF_GPODNET_NOTIFICATIONS, true).apply()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun readPlaybackSpeedArray(valueFromPrefs: String?): List<Float> {
|
private fun readPlaybackSpeedArray(valueFromPrefs: String?): List<Float> {
|
||||||
|
@ -682,9 +682,9 @@ object UserPreferences {
|
||||||
|
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
var episodeCleanupValue: Int
|
var episodeCleanupValue: Int
|
||||||
get() = prefs.getString(PREF_EPISODE_CLEANUP, "" + EPISODE_CLEANUP_NULL)!!.toInt()
|
get() = appPrefs.getString(PREF_EPISODE_CLEANUP, "" + EPISODE_CLEANUP_NULL)!!.toInt()
|
||||||
set(episodeCleanupValue) {
|
set(episodeCleanupValue) {
|
||||||
prefs.edit().putString(PREF_EPISODE_CLEANUP, episodeCleanupValue.toString()).apply()
|
appPrefs.edit().putString(PREF_EPISODE_CLEANUP, episodeCleanupValue.toString()).apply()
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -697,7 +697,7 @@ object UserPreferences {
|
||||||
*/
|
*/
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun getDataFolder(type: String?): File? {
|
fun getDataFolder(type: String?): File? {
|
||||||
var dataFolder = getTypeDir(prefs.getString(PREF_DATA_FOLDER, null), type)
|
var dataFolder = getTypeDir(appPrefs.getString(PREF_DATA_FOLDER, null), type)
|
||||||
if (dataFolder == null || !dataFolder.canWrite()) {
|
if (dataFolder == null || !dataFolder.canWrite()) {
|
||||||
Logd(TAG, "User data folder not writable or not set. Trying default.")
|
Logd(TAG, "User data folder not writable or not set. Trying default.")
|
||||||
dataFolder = context.getExternalFilesDir(type)
|
dataFolder = context.getExternalFilesDir(type)
|
||||||
|
@ -730,7 +730,7 @@ object UserPreferences {
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun setDataFolder(dir: String) {
|
fun setDataFolder(dir: String) {
|
||||||
Logd(TAG, "setDataFolder(dir: $dir)")
|
Logd(TAG, "setDataFolder(dir: $dir)")
|
||||||
prefs.edit().putString(PREF_DATA_FOLDER, dir).apply()
|
appPrefs.edit().putString(PREF_DATA_FOLDER, dir).apply()
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -751,26 +751,26 @@ object UserPreferences {
|
||||||
|
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
var defaultPage: String?
|
var defaultPage: String?
|
||||||
get() = prefs.getString(PREF_DEFAULT_PAGE, "SubscriptionFragment")
|
get() = appPrefs.getString(PREF_DEFAULT_PAGE, "SubscriptionFragment")
|
||||||
set(defaultPage) {
|
set(defaultPage) {
|
||||||
prefs.edit().putString(PREF_DEFAULT_PAGE, defaultPage).apply()
|
appPrefs.edit().putString(PREF_DEFAULT_PAGE, defaultPage).apply()
|
||||||
}
|
}
|
||||||
|
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun backButtonOpensDrawer(): Boolean {
|
fun backButtonOpensDrawer(): Boolean {
|
||||||
return prefs.getBoolean(PREF_BACK_OPENS_DRAWER, false)
|
return appPrefs.getBoolean(PREF_BACK_OPENS_DRAWER, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun timeRespectsSpeed(): Boolean {
|
fun timeRespectsSpeed(): Boolean {
|
||||||
return prefs.getBoolean(PREF_TIME_RESPECTS_SPEED, false)
|
return appPrefs.getBoolean(PREF_TIME_RESPECTS_SPEED, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
var isStreamOverDownload: Boolean
|
var isStreamOverDownload: Boolean
|
||||||
get() = prefs.getBoolean(PREF_STREAM_OVER_DOWNLOAD, false)
|
get() = appPrefs.getBoolean(PREF_STREAM_OVER_DOWNLOAD, false)
|
||||||
set(stream) {
|
set(stream) {
|
||||||
prefs.edit().putBoolean(PREF_STREAM_OVER_DOWNLOAD, stream).apply()
|
appPrefs.edit().putBoolean(PREF_STREAM_OVER_DOWNLOAD, stream).apply()
|
||||||
}
|
}
|
||||||
|
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
|
@ -780,14 +780,14 @@ object UserPreferences {
|
||||||
*
|
*
|
||||||
* @see .getQueueKeepSortedOrder
|
* @see .getQueueKeepSortedOrder
|
||||||
*/
|
*/
|
||||||
get() = prefs.getBoolean(PREF_QUEUE_KEEP_SORTED, false)
|
get() = appPrefs.getBoolean(PREF_QUEUE_KEEP_SORTED, false)
|
||||||
/**
|
/**
|
||||||
* Enables/disables the keep sorted mode of the queue.
|
* Enables/disables the keep sorted mode of the queue.
|
||||||
*
|
*
|
||||||
* @see .setQueueKeepSortedOrder
|
* @see .setQueueKeepSortedOrder
|
||||||
*/
|
*/
|
||||||
set(keepSorted) {
|
set(keepSorted) {
|
||||||
prefs.edit().putBoolean(PREF_QUEUE_KEEP_SORTED, keepSorted).apply()
|
appPrefs.edit().putBoolean(PREF_QUEUE_KEEP_SORTED, keepSorted).apply()
|
||||||
}
|
}
|
||||||
|
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
|
@ -799,7 +799,7 @@ object UserPreferences {
|
||||||
* @see .isQueueKeepSorted
|
* @see .isQueueKeepSorted
|
||||||
*/
|
*/
|
||||||
get() {
|
get() {
|
||||||
val sortOrderStr = prefs.getString(PREF_QUEUE_KEEP_SORTED_ORDER, "use-default")
|
val sortOrderStr = appPrefs.getString(PREF_QUEUE_KEEP_SORTED_ORDER, "use-default")
|
||||||
return SortOrder.parseWithDefault(sortOrderStr, SortOrder.DATE_NEW_OLD)
|
return SortOrder.parseWithDefault(sortOrderStr, SortOrder.DATE_NEW_OLD)
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
|
@ -809,13 +809,13 @@ object UserPreferences {
|
||||||
*/
|
*/
|
||||||
set(sortOrder) {
|
set(sortOrder) {
|
||||||
if (sortOrder == null) return
|
if (sortOrder == null) return
|
||||||
prefs.edit().putString(PREF_QUEUE_KEEP_SORTED_ORDER, sortOrder.name).apply()
|
appPrefs.edit().putString(PREF_QUEUE_KEEP_SORTED_ORDER, sortOrder.name).apply()
|
||||||
}
|
}
|
||||||
|
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
val newEpisodesAction: NewEpisodesAction
|
val newEpisodesAction: NewEpisodesAction
|
||||||
get() {
|
get() {
|
||||||
val str = prefs.getString(PREF_NEW_EPISODES_ACTION, "" + NewEpisodesAction.GLOBAL.code)
|
val str = appPrefs.getString(PREF_NEW_EPISODES_ACTION, "" + NewEpisodesAction.GLOBAL.code)
|
||||||
return NewEpisodesAction.fromCode(str!!.toInt())
|
return NewEpisodesAction.fromCode(str!!.toInt())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -825,14 +825,14 @@ object UserPreferences {
|
||||||
* Returns the sort order for the downloads.
|
* Returns the sort order for the downloads.
|
||||||
*/
|
*/
|
||||||
get() {
|
get() {
|
||||||
val sortOrderStr = prefs.getString(PREF_DOWNLOADS_SORTED_ORDER, "" + SortOrder.DATE_NEW_OLD.code)
|
val sortOrderStr = appPrefs.getString(PREF_DOWNLOADS_SORTED_ORDER, "" + SortOrder.DATE_NEW_OLD.code)
|
||||||
return SortOrder.fromCodeString(sortOrderStr)
|
return SortOrder.fromCodeString(sortOrderStr)
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
* Sets the sort order for the downloads.
|
* Sets the sort order for the downloads.
|
||||||
*/
|
*/
|
||||||
set(sortOrder) {
|
set(sortOrder) {
|
||||||
prefs.edit().putString(PREF_DOWNLOADS_SORTED_ORDER, "" + sortOrder!!.code).apply()
|
appPrefs.edit().putString(PREF_DOWNLOADS_SORTED_ORDER, "" + sortOrder!!.code).apply()
|
||||||
}
|
}
|
||||||
|
|
||||||
// @JvmStatic
|
// @JvmStatic
|
||||||
|
@ -855,11 +855,11 @@ object UserPreferences {
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
var subscriptionsFilter: SubscriptionsFilter
|
var subscriptionsFilter: SubscriptionsFilter
|
||||||
get() {
|
get() {
|
||||||
val value = prefs.getString(PREF_FILTER_FEED, "")
|
val value = appPrefs.getString(PREF_FILTER_FEED, "")
|
||||||
return SubscriptionsFilter(value)
|
return SubscriptionsFilter(value)
|
||||||
}
|
}
|
||||||
set(value) {
|
set(value) {
|
||||||
prefs.edit().putString(PREF_FILTER_FEED, value.serialize()).apply()
|
appPrefs.edit().putString(PREF_FILTER_FEED, value.serialize()).apply()
|
||||||
}
|
}
|
||||||
|
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
|
@ -870,16 +870,16 @@ object UserPreferences {
|
||||||
|
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
var allEpisodesSortOrder: SortOrder?
|
var allEpisodesSortOrder: SortOrder?
|
||||||
get() = SortOrder.fromCodeString(prefs.getString(PREF_SORT_ALL_EPISODES, "" + SortOrder.DATE_NEW_OLD.code))
|
get() = SortOrder.fromCodeString(appPrefs.getString(PREF_SORT_ALL_EPISODES, "" + SortOrder.DATE_NEW_OLD.code))
|
||||||
set(s) {
|
set(s) {
|
||||||
prefs.edit().putString(PREF_SORT_ALL_EPISODES, "" + s!!.code).apply()
|
appPrefs.edit().putString(PREF_SORT_ALL_EPISODES, "" + s!!.code).apply()
|
||||||
}
|
}
|
||||||
|
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
var prefFilterAllEpisodes: String
|
var prefFilterAllEpisodes: String
|
||||||
get() = prefs.getString(PREF_FILTER_ALL_EPISODES, "")?:""
|
get() = appPrefs.getString(PREF_FILTER_ALL_EPISODES, "")?:""
|
||||||
set(filter) {
|
set(filter) {
|
||||||
prefs.edit().putString(PREF_FILTER_ALL_EPISODES, filter).apply()
|
appPrefs.edit().putString(PREF_FILTER_ALL_EPISODES, filter).apply()
|
||||||
}
|
}
|
||||||
|
|
||||||
enum class ThemePreference {
|
enum class ThemePreference {
|
||||||
|
|
|
@ -15,6 +15,7 @@ import ac.mdiq.podcini.net.download.FeedUpdateManager.restartUpdateAlarm
|
||||||
import ac.mdiq.podcini.ui.dialog.ChooseDataFolderDialog
|
import ac.mdiq.podcini.ui.dialog.ChooseDataFolderDialog
|
||||||
import ac.mdiq.podcini.ui.dialog.ProxyDialog
|
import ac.mdiq.podcini.ui.dialog.ProxyDialog
|
||||||
import ac.mdiq.podcini.preferences.UserPreferences
|
import ac.mdiq.podcini.preferences.UserPreferences
|
||||||
|
import ac.mdiq.podcini.preferences.UserPreferences.appPrefs
|
||||||
import ac.mdiq.podcini.preferences.UserPreferences.getDataFolder
|
import ac.mdiq.podcini.preferences.UserPreferences.getDataFolder
|
||||||
import ac.mdiq.podcini.preferences.UserPreferences.setDataFolder
|
import ac.mdiq.podcini.preferences.UserPreferences.setDataFolder
|
||||||
import kotlin.Any
|
import kotlin.Any
|
||||||
|
@ -32,12 +33,12 @@ class DownloadsPreferencesFragment : PreferenceFragmentCompat(), OnSharedPrefere
|
||||||
override fun onStart() {
|
override fun onStart() {
|
||||||
super.onStart()
|
super.onStart()
|
||||||
(activity as PreferenceActivity).supportActionBar!!.setTitle(R.string.downloads_pref)
|
(activity as PreferenceActivity).supportActionBar!!.setTitle(R.string.downloads_pref)
|
||||||
PreferenceManager.getDefaultSharedPreferences(requireContext()).registerOnSharedPreferenceChangeListener(this)
|
appPrefs.registerOnSharedPreferenceChangeListener(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onStop() {
|
override fun onStop() {
|
||||||
super.onStop()
|
super.onStop()
|
||||||
PreferenceManager.getDefaultSharedPreferences(requireContext()).unregisterOnSharedPreferenceChangeListener(this)
|
appPrefs.unregisterOnSharedPreferenceChangeListener(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onResume() {
|
override fun onResume() {
|
||||||
|
|
|
@ -21,6 +21,7 @@ class MainPreferencesFragment : PreferenceFragmentCompat() {
|
||||||
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
|
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
|
||||||
Logd("MainPreferencesFragment", "onCreatePreferences")
|
Logd("MainPreferencesFragment", "onCreatePreferences")
|
||||||
|
|
||||||
|
// TODO: this can be expensive
|
||||||
addPreferencesFromResource(R.xml.preferences)
|
addPreferencesFromResource(R.xml.preferences)
|
||||||
setupMainScreen()
|
setupMainScreen()
|
||||||
setupSearch()
|
setupSearch()
|
||||||
|
|
|
@ -37,7 +37,7 @@ class SwipePreferencesFragment : PreferenceFragmentCompat() {
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
findPreference<Preference>(PREF_SWIPE_HISTORY)?.onPreferenceClickListener = Preference.OnPreferenceClickListener {
|
findPreference<Preference>(PREF_SWIPE_HISTORY)?.onPreferenceClickListener = Preference.OnPreferenceClickListener {
|
||||||
SwipeActionsDialog(requireContext(), PlaybackHistoryFragment.TAG).show (object : SwipeActionsDialog.Callback {
|
SwipeActionsDialog(requireContext(), HistoryFragment.TAG).show (object : SwipeActionsDialog.Callback {
|
||||||
override fun onCall() {}
|
override fun onCall() {}
|
||||||
})
|
})
|
||||||
true
|
true
|
||||||
|
|
|
@ -10,6 +10,9 @@ import ac.mdiq.podcini.net.sync.SynchronizationSettings.isProviderConnected
|
||||||
import ac.mdiq.podcini.net.sync.SynchronizationSettings.wifiSyncEnabledKey
|
import ac.mdiq.podcini.net.sync.SynchronizationSettings.wifiSyncEnabledKey
|
||||||
import ac.mdiq.podcini.ui.activity.PreferenceActivity
|
import ac.mdiq.podcini.ui.activity.PreferenceActivity
|
||||||
import ac.mdiq.podcini.ui.dialog.AuthenticationDialog
|
import ac.mdiq.podcini.ui.dialog.AuthenticationDialog
|
||||||
|
import ac.mdiq.podcini.ui.fragment.AudioPlayerFragment.InternalPlayerFragment
|
||||||
|
import ac.mdiq.podcini.ui.fragment.AudioPlayerFragment.InternalPlayerFragment.Companion
|
||||||
|
import ac.mdiq.podcini.util.Logd
|
||||||
import ac.mdiq.podcini.util.event.EventFlow
|
import ac.mdiq.podcini.util.event.EventFlow
|
||||||
import ac.mdiq.podcini.util.event.FlowEvent
|
import ac.mdiq.podcini.util.event.FlowEvent
|
||||||
import android.app.Activity
|
import android.app.Activity
|
||||||
|
@ -56,6 +59,7 @@ class SynchronizationPreferencesFragment : PreferenceFragmentCompat() {
|
||||||
private fun procFlowEvents() {
|
private fun procFlowEvents() {
|
||||||
lifecycleScope.launch {
|
lifecycleScope.launch {
|
||||||
EventFlow.events.collectLatest { event ->
|
EventFlow.events.collectLatest { event ->
|
||||||
|
Logd("SynchronizationPreferencesFragment", "Received event: ${event}")
|
||||||
when (event) {
|
when (event) {
|
||||||
is FlowEvent.SyncServiceEvent -> syncStatusChanged(event)
|
is FlowEvent.SyncServiceEvent -> syncStatusChanged(event)
|
||||||
else -> {}
|
else -> {}
|
||||||
|
|
|
@ -6,6 +6,8 @@ import ac.mdiq.podcini.net.sync.SynchronizationCredentials
|
||||||
import ac.mdiq.podcini.net.sync.SynchronizationSettings.setWifiSyncEnabled
|
import ac.mdiq.podcini.net.sync.SynchronizationSettings.setWifiSyncEnabled
|
||||||
import ac.mdiq.podcini.net.sync.wifi.WifiSyncService.Companion.hostPort
|
import ac.mdiq.podcini.net.sync.wifi.WifiSyncService.Companion.hostPort
|
||||||
import ac.mdiq.podcini.net.sync.wifi.WifiSyncService.Companion.startInstantSync
|
import ac.mdiq.podcini.net.sync.wifi.WifiSyncService.Companion.startInstantSync
|
||||||
|
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.Logd
|
||||||
import ac.mdiq.podcini.util.event.EventFlow
|
import ac.mdiq.podcini.util.event.EventFlow
|
||||||
import ac.mdiq.podcini.util.event.FlowEvent
|
import ac.mdiq.podcini.util.event.FlowEvent
|
||||||
|
@ -96,6 +98,7 @@ import java.util.*
|
||||||
private fun procFlowEvents() {
|
private fun procFlowEvents() {
|
||||||
lifecycleScope.launch {
|
lifecycleScope.launch {
|
||||||
EventFlow.events.collectLatest { event ->
|
EventFlow.events.collectLatest { event ->
|
||||||
|
Logd(TAG, "Received event: $event")
|
||||||
when (event) {
|
when (event) {
|
||||||
is FlowEvent.SyncServiceEvent -> syncStatusChanged(event)
|
is FlowEvent.SyncServiceEvent -> syncStatusChanged(event)
|
||||||
else -> {}
|
else -> {}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
package ac.mdiq.podcini.receiver
|
package ac.mdiq.podcini.receiver
|
||||||
|
|
||||||
|
import ac.mdiq.podcini.ui.actions.swipeactions.SwipeActions.Companion.SWIPE_ACTIONS_PREF_NAME
|
||||||
import android.appwidget.AppWidgetManager
|
import android.appwidget.AppWidgetManager
|
||||||
import android.appwidget.AppWidgetProvider
|
import android.appwidget.AppWidgetProvider
|
||||||
import android.content.ComponentName
|
import android.content.ComponentName
|
||||||
|
@ -10,6 +11,7 @@ import androidx.work.OneTimeWorkRequest
|
||||||
import androidx.work.WorkManager
|
import androidx.work.WorkManager
|
||||||
import ac.mdiq.podcini.ui.widget.WidgetUpdaterWorker
|
import ac.mdiq.podcini.ui.widget.WidgetUpdaterWorker
|
||||||
import ac.mdiq.podcini.util.Logd
|
import ac.mdiq.podcini.util.Logd
|
||||||
|
import android.content.SharedPreferences
|
||||||
import java.util.concurrent.TimeUnit
|
import java.util.concurrent.TimeUnit
|
||||||
|
|
||||||
class PlayerWidget : AppWidgetProvider() {
|
class PlayerWidget : AppWidgetProvider() {
|
||||||
|
@ -25,10 +27,9 @@ class PlayerWidget : AppWidgetProvider() {
|
||||||
Logd(TAG, "onUpdate() called with: context = [$context], appWidgetManager = [$appWidgetManager], appWidgetIds = [${appWidgetIds.contentToString()}]")
|
Logd(TAG, "onUpdate() called with: context = [$context], appWidgetManager = [$appWidgetManager], appWidgetIds = [${appWidgetIds.contentToString()}]")
|
||||||
WidgetUpdaterWorker.enqueueWork(context)
|
WidgetUpdaterWorker.enqueueWork(context)
|
||||||
|
|
||||||
val prefs = context.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE)
|
if (!prefs!!.getBoolean(KEY_WORKAROUND_ENABLED, false)) {
|
||||||
if (!prefs.getBoolean(KEY_WORKAROUND_ENABLED, false)) {
|
|
||||||
scheduleWorkaround(context)
|
scheduleWorkaround(context)
|
||||||
prefs.edit().putBoolean(KEY_WORKAROUND_ENABLED, true).apply()
|
prefs!!.edit().putBoolean(KEY_WORKAROUND_ENABLED, true).apply()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -40,26 +41,24 @@ class PlayerWidget : AppWidgetProvider() {
|
||||||
|
|
||||||
override fun onDeleted(context: Context, appWidgetIds: IntArray) {
|
override fun onDeleted(context: Context, appWidgetIds: IntArray) {
|
||||||
Logd(TAG, "OnDeleted")
|
Logd(TAG, "OnDeleted")
|
||||||
val prefs = context.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE)
|
|
||||||
for (appWidgetId in appWidgetIds) {
|
for (appWidgetId in appWidgetIds) {
|
||||||
prefs.edit().remove(KEY_WIDGET_COLOR + appWidgetId).apply()
|
prefs!!.edit().remove(KEY_WIDGET_COLOR + appWidgetId).apply()
|
||||||
prefs.edit().remove(KEY_WIDGET_PLAYBACK_SPEED + appWidgetId).apply()
|
prefs!!.edit().remove(KEY_WIDGET_PLAYBACK_SPEED + appWidgetId).apply()
|
||||||
prefs.edit().remove(KEY_WIDGET_REWIND + appWidgetId).apply()
|
prefs!!.edit().remove(KEY_WIDGET_REWIND + appWidgetId).apply()
|
||||||
prefs.edit().remove(KEY_WIDGET_FAST_FORWARD + appWidgetId).apply()
|
prefs!!.edit().remove(KEY_WIDGET_FAST_FORWARD + appWidgetId).apply()
|
||||||
prefs.edit().remove(KEY_WIDGET_SKIP + appWidgetId).apply()
|
prefs!!.edit().remove(KEY_WIDGET_SKIP + appWidgetId).apply()
|
||||||
}
|
}
|
||||||
val manager = AppWidgetManager.getInstance(context)
|
val manager = AppWidgetManager.getInstance(context)
|
||||||
val widgetIds = manager.getAppWidgetIds(ComponentName(context, PlayerWidget::class.java))
|
val widgetIds = manager.getAppWidgetIds(ComponentName(context, PlayerWidget::class.java))
|
||||||
if (widgetIds.isEmpty()) {
|
if (widgetIds.isEmpty()) {
|
||||||
prefs.edit().putBoolean(KEY_WORKAROUND_ENABLED, false).apply()
|
prefs!!.edit().putBoolean(KEY_WORKAROUND_ENABLED, false).apply()
|
||||||
WorkManager.getInstance(context).cancelUniqueWork(WORKAROUND_WORK_NAME)
|
WorkManager.getInstance(context).cancelUniqueWork(WORKAROUND_WORK_NAME)
|
||||||
}
|
}
|
||||||
super.onDeleted(context, appWidgetIds)
|
super.onDeleted(context, appWidgetIds)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun setEnabled(context: Context, enabled: Boolean) {
|
private fun setEnabled(context: Context, enabled: Boolean) {
|
||||||
val prefs = context.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE)
|
prefs!!.edit().putBoolean(KEY_ENABLED, enabled).apply()
|
||||||
prefs.edit().putBoolean(KEY_ENABLED, enabled).apply()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
@ -75,6 +74,12 @@ class PlayerWidget : AppWidgetProvider() {
|
||||||
const val DEFAULT_COLOR: Int = -0xd9d3cf
|
const val DEFAULT_COLOR: Int = -0xd9d3cf
|
||||||
private const val WORKAROUND_WORK_NAME = "WidgetUpdaterWorkaround"
|
private const val WORKAROUND_WORK_NAME = "WidgetUpdaterWorkaround"
|
||||||
|
|
||||||
|
var prefs: SharedPreferences? = null
|
||||||
|
|
||||||
|
fun getSharedPrefs(context: Context) {
|
||||||
|
if (prefs == null) prefs = context.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE)
|
||||||
|
}
|
||||||
|
|
||||||
private fun scheduleWorkaround(context: Context) {
|
private fun scheduleWorkaround(context: Context) {
|
||||||
// Enqueueing work enables a BOOT_COMPLETED receiver, which in turn makes Android refresh widgets.
|
// Enqueueing work enables a BOOT_COMPLETED receiver, which in turn makes Android refresh widgets.
|
||||||
// This creates an endless loop with a flickering widget.
|
// This creates an endless loop with a flickering widget.
|
||||||
|
@ -87,8 +92,8 @@ class PlayerWidget : AppWidgetProvider() {
|
||||||
|
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun isEnabled(context: Context): Boolean {
|
fun isEnabled(context: Context): Boolean {
|
||||||
val prefs = context.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE)
|
// val prefs = context.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE)
|
||||||
return prefs.getBoolean(KEY_ENABLED, false)
|
return prefs!!.getBoolean(KEY_ENABLED, false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,17 +9,17 @@ import ac.mdiq.podcini.storage.model.feed.FeedItemFilter
|
||||||
import ac.mdiq.podcini.storage.model.feed.SortOrder
|
import ac.mdiq.podcini.storage.model.feed.SortOrder
|
||||||
import androidx.annotation.OptIn
|
import androidx.annotation.OptIn
|
||||||
import androidx.media3.common.util.UnstableApi
|
import androidx.media3.common.util.UnstableApi
|
||||||
|
import kotlinx.coroutines.runBlocking
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import java.util.concurrent.ExecutionException
|
import java.util.concurrent.ExecutionException
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Implementation of the EpisodeCleanupAlgorithm interface used by Podcini.
|
* Implementation of the EpisodeCleanupAlgorithm interface used by Podcini.
|
||||||
*/
|
*/
|
||||||
class APCleanupAlgorithm(
|
/** the number of days after playback to wait before an item is eligible to be cleaned up.
|
||||||
/** the number of days after playback to wait before an item is eligible to be cleaned up.
|
* Fractional for number of hours, e.g., 0.5 = 12 hours, 0.0416 = 1 hour. */
|
||||||
* Fractional for number of hours, e.g., 0.5 = 12 hours, 0.0416 = 1 hour. */
|
|
||||||
@JvmField @get:VisibleForTesting val numberOfHoursAfterPlayback: Int
|
class APCleanupAlgorithm(@JvmField @get:VisibleForTesting val numberOfHoursAfterPlayback: Int) : EpisodeCleanupAlgorithm() {
|
||||||
) : EpisodeCleanupAlgorithm() {
|
|
||||||
/**
|
/**
|
||||||
* @return the number of episodes that *could* be cleaned up, if needed
|
* @return the number of episodes that *could* be cleaned up, if needed
|
||||||
*/
|
*/
|
||||||
|
@ -44,7 +44,7 @@ class APCleanupAlgorithm(
|
||||||
|
|
||||||
for (item in delete) {
|
for (item in delete) {
|
||||||
try {
|
try {
|
||||||
DBWriter.deleteFeedMediaOfItem(context!!, item.media!!.id).get()
|
runBlocking { DBWriter.deleteFeedMediaOfItem(context, item.media!!.id).join() }
|
||||||
} catch (e: InterruptedException) {
|
} catch (e: InterruptedException) {
|
||||||
e.printStackTrace()
|
e.printStackTrace()
|
||||||
} catch (e: ExecutionException) {
|
} catch (e: ExecutionException) {
|
||||||
|
@ -53,7 +53,6 @@ class APCleanupAlgorithm(
|
||||||
}
|
}
|
||||||
|
|
||||||
val counter = delete.size
|
val counter = delete.size
|
||||||
|
|
||||||
Log.i(TAG, String.format(Locale.US, "Auto-delete deleted %d episodes (%d requested)", counter, numberOfEpisodesToDelete))
|
Log.i(TAG, String.format(Locale.US, "Auto-delete deleted %d episodes (%d requested)", counter, numberOfEpisodesToDelete))
|
||||||
|
|
||||||
return counter
|
return counter
|
||||||
|
|
|
@ -6,6 +6,9 @@ import ac.mdiq.podcini.storage.DBReader.getEpisodes
|
||||||
import ac.mdiq.podcini.storage.model.feed.FeedItem
|
import ac.mdiq.podcini.storage.model.feed.FeedItem
|
||||||
import ac.mdiq.podcini.storage.model.feed.FeedItemFilter
|
import ac.mdiq.podcini.storage.model.feed.FeedItemFilter
|
||||||
import ac.mdiq.podcini.storage.model.feed.SortOrder
|
import ac.mdiq.podcini.storage.model.feed.SortOrder
|
||||||
|
import androidx.annotation.OptIn
|
||||||
|
import androidx.media3.common.util.UnstableApi
|
||||||
|
import kotlinx.coroutines.runBlocking
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import java.util.concurrent.ExecutionException
|
import java.util.concurrent.ExecutionException
|
||||||
|
|
||||||
|
@ -21,7 +24,7 @@ class APQueueCleanupAlgorithm : EpisodeCleanupAlgorithm() {
|
||||||
return candidates.size
|
return candidates.size
|
||||||
}
|
}
|
||||||
|
|
||||||
public override fun performCleanup(context: Context, numberOfEpisodesToDelete: Int): Int {
|
@OptIn(UnstableApi::class) public override fun performCleanup(context: Context, numberOfEpisodesToDelete: Int): Int {
|
||||||
var candidates = candidates
|
var candidates = candidates
|
||||||
|
|
||||||
// in the absence of better data, we'll sort by item publication date
|
// in the absence of better data, we'll sort by item publication date
|
||||||
|
@ -38,8 +41,9 @@ class APQueueCleanupAlgorithm : EpisodeCleanupAlgorithm() {
|
||||||
val delete = if (candidates.size > numberOfEpisodesToDelete) candidates.subList(0, numberOfEpisodesToDelete) else candidates
|
val delete = if (candidates.size > numberOfEpisodesToDelete) candidates.subList(0, numberOfEpisodesToDelete) else candidates
|
||||||
|
|
||||||
for (item in delete) {
|
for (item in delete) {
|
||||||
|
if (item.media == null) continue
|
||||||
try {
|
try {
|
||||||
DBWriter.deleteFeedMediaOfItem(context!!, item.media!!.id).get()
|
runBlocking { DBWriter.deleteFeedMediaOfItem(context, item.media!!.id).join() }
|
||||||
} catch (e: InterruptedException) {
|
} catch (e: InterruptedException) {
|
||||||
e.printStackTrace()
|
e.printStackTrace()
|
||||||
} catch (e: ExecutionException) {
|
} catch (e: ExecutionException) {
|
||||||
|
|
|
@ -25,6 +25,7 @@ import android.text.TextUtils
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import androidx.annotation.VisibleForTesting
|
import androidx.annotation.VisibleForTesting
|
||||||
import androidx.media3.common.util.UnstableApi
|
import androidx.media3.common.util.UnstableApi
|
||||||
|
import kotlinx.coroutines.runBlocking
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import java.util.concurrent.*
|
import java.util.concurrent.*
|
||||||
|
|
||||||
|
@ -68,15 +69,13 @@ import java.util.concurrent.*
|
||||||
|
|
||||||
if (feedID != 0L) {
|
if (feedID != 0L) {
|
||||||
try {
|
try {
|
||||||
DBWriter.deleteFeed(context, feedID).get()
|
runBlocking { DBWriter.deleteFeed(context, feedID).join() }
|
||||||
} catch (e: InterruptedException) {
|
} catch (e: InterruptedException) {
|
||||||
e.printStackTrace()
|
e.printStackTrace()
|
||||||
} catch (e: ExecutionException) {
|
} catch (e: ExecutionException) {
|
||||||
e.printStackTrace()
|
e.printStackTrace()
|
||||||
}
|
}
|
||||||
} else {
|
} else Log.w(TAG, "removeFeedWithDownloadUrl: Could not find feed with url: $downloadUrl")
|
||||||
Log.w(TAG, "removeFeedWithDownloadUrl: Could not find feed with url: $downloadUrl")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -132,15 +131,13 @@ import java.util.concurrent.*
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun searchFeedByIdentifyingValueOrID(feed: Feed): Feed? {
|
private fun searchFeedByIdentifyingValueOrID(feed: Feed): Feed? {
|
||||||
if (feed.id != 0L) {
|
if (feed.id != 0L) return getFeed(feed.id)
|
||||||
return getFeed(feed.id)
|
|
||||||
} else {
|
val feeds = getFeedList()
|
||||||
val feeds = getFeedList()
|
for (f in feeds.toList()) {
|
||||||
for (f in feeds.toList()) {
|
if (f != null && f.identifyingValue == feed.identifyingValue) {
|
||||||
if (f != null && f.identifyingValue == feed.identifyingValue) {
|
f.items = getFeedItemList(f).toMutableList()
|
||||||
f.items = getFeedItemList(f).toMutableList()
|
return f
|
||||||
return f
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return null
|
return null
|
||||||
|
@ -275,9 +272,8 @@ import java.util.concurrent.*
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (oldItem != null) {
|
if (oldItem != null) oldItem.updateFromOther(item)
|
||||||
oldItem.updateFromOther(item)
|
else {
|
||||||
} else {
|
|
||||||
Logd(TAG, "Found new item: " + item.title)
|
Logd(TAG, "Found new item: " + item.title)
|
||||||
item.feed = savedFeed
|
item.feed = savedFeed
|
||||||
|
|
||||||
|
@ -314,13 +310,13 @@ import java.util.concurrent.*
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (savedFeed == null) {
|
if (savedFeed == null) {
|
||||||
DBWriter.addNewFeed(context, newFeed).get()
|
runBlocking { DBWriter.addNewFeed(context, newFeed).join() }
|
||||||
// Update with default values that are set in database
|
// Update with default values that are set in database
|
||||||
resultFeed = searchFeedByIdentifyingValueOrID(newFeed)
|
resultFeed = searchFeedByIdentifyingValueOrID(newFeed)
|
||||||
} else DBWriter.persistCompleteFeed(savedFeed).get()
|
} else runBlocking { DBWriter.persistCompleteFeed(savedFeed).join() }
|
||||||
|
|
||||||
DBReader.updateFeedList(adapter)
|
DBReader.updateFeedList(adapter)
|
||||||
if (removeUnlistedItems) DBWriter.deleteFeedItems(context, unlistedItems).get()
|
if (removeUnlistedItems) runBlocking { DBWriter.deleteFeedItems(context, unlistedItems).join() }
|
||||||
} catch (e: InterruptedException) {
|
} catch (e: InterruptedException) {
|
||||||
e.printStackTrace()
|
e.printStackTrace()
|
||||||
} catch (e: ExecutionException) {
|
} catch (e: ExecutionException) {
|
||||||
|
|
|
@ -6,7 +6,7 @@ import ac.mdiq.podcini.net.download.serviceinterface.DownloadServiceInterface
|
||||||
import ac.mdiq.podcini.net.sync.model.EpisodeAction
|
import ac.mdiq.podcini.net.sync.model.EpisodeAction
|
||||||
import ac.mdiq.podcini.net.sync.queue.SynchronizationQueueSink
|
import ac.mdiq.podcini.net.sync.queue.SynchronizationQueueSink
|
||||||
import ac.mdiq.podcini.playback.service.PlaybackServiceConstants
|
import ac.mdiq.podcini.playback.service.PlaybackServiceConstants
|
||||||
import ac.mdiq.podcini.preferences.PlaybackPreferences.Companion.createInstanceFromPreferences
|
import ac.mdiq.podcini.preferences.PlaybackPreferences.Companion.loadPlayableFromPreferences
|
||||||
import ac.mdiq.podcini.preferences.PlaybackPreferences.Companion.currentlyPlayingFeedMediaId
|
import ac.mdiq.podcini.preferences.PlaybackPreferences.Companion.currentlyPlayingFeedMediaId
|
||||||
import ac.mdiq.podcini.preferences.PlaybackPreferences.Companion.writeNoMediaPlaying
|
import ac.mdiq.podcini.preferences.PlaybackPreferences.Companion.writeNoMediaPlaying
|
||||||
import ac.mdiq.podcini.preferences.UserPreferences.enqueueLocation
|
import ac.mdiq.podcini.preferences.UserPreferences.enqueueLocation
|
||||||
|
@ -29,7 +29,6 @@ import ac.mdiq.podcini.util.Logd
|
||||||
import ac.mdiq.podcini.util.LongList
|
import ac.mdiq.podcini.util.LongList
|
||||||
import ac.mdiq.podcini.util.event.EventFlow
|
import ac.mdiq.podcini.util.event.EventFlow
|
||||||
import ac.mdiq.podcini.util.event.FlowEvent
|
import ac.mdiq.podcini.util.event.FlowEvent
|
||||||
import ac.mdiq.podcini.util.showStackTrace
|
|
||||||
import android.app.backup.BackupManager
|
import android.app.backup.BackupManager
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
|
@ -37,16 +36,13 @@ import android.util.Log
|
||||||
import androidx.core.app.NotificationManagerCompat
|
import androidx.core.app.NotificationManagerCompat
|
||||||
import androidx.documentfile.provider.DocumentFile
|
import androidx.documentfile.provider.DocumentFile
|
||||||
import androidx.media3.common.util.UnstableApi
|
import androidx.media3.common.util.UnstableApi
|
||||||
import com.google.common.util.concurrent.Futures
|
import kotlinx.coroutines.*
|
||||||
import kotlinx.coroutines.Dispatchers
|
|
||||||
import kotlinx.coroutines.async
|
|
||||||
import kotlinx.coroutines.withContext
|
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import java.util.concurrent.ExecutorService
|
import java.util.concurrent.ExecutorService
|
||||||
import java.util.concurrent.Executors
|
import java.util.concurrent.Executors
|
||||||
import java.util.concurrent.Future
|
|
||||||
import java.util.concurrent.TimeUnit
|
import java.util.concurrent.TimeUnit
|
||||||
|
import kotlin.coroutines.ContinuationInterceptor
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Provides methods for writing data to Podcini's database.
|
* Provides methods for writing data to Podcini's database.
|
||||||
|
@ -57,6 +53,8 @@ import java.util.concurrent.TimeUnit
|
||||||
@UnstableApi object DBWriter {
|
@UnstableApi object DBWriter {
|
||||||
private const val TAG = "DBWriter"
|
private const val TAG = "DBWriter"
|
||||||
|
|
||||||
|
val ioScope = CoroutineScope(Dispatchers.IO)
|
||||||
|
|
||||||
private val dbExec: ExecutorService = Executors.newSingleThreadExecutor { r: Runnable? ->
|
private val dbExec: ExecutorService = Executors.newSingleThreadExecutor { r: Runnable? ->
|
||||||
val t = Thread(r)
|
val t = Thread(r)
|
||||||
t.name = "DatabaseExecutor"
|
t.name = "DatabaseExecutor"
|
||||||
|
@ -77,8 +75,8 @@ import java.util.concurrent.TimeUnit
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun deleteItemsMedia(items: List<FeedItem>) {
|
fun deleteItemsMedia(items: List<FeedItem>) : Job {
|
||||||
runOnDbThread {
|
return runOnDbThread {
|
||||||
val adapter = getInstance()
|
val adapter = getInstance()
|
||||||
adapter.open()
|
adapter.open()
|
||||||
adapter.removeItemMedia(items)
|
adapter.removeItemMedia(items)
|
||||||
|
@ -93,7 +91,7 @@ import java.util.concurrent.TimeUnit
|
||||||
* @param mediaId ID of the FeedMedia object whose downloaded file should be deleted.
|
* @param mediaId ID of the FeedMedia object whose downloaded file should be deleted.
|
||||||
*/
|
*/
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun deleteFeedMediaOfItem(context: Context, mediaId: Long): Future<*> {
|
fun deleteFeedMediaOfItem(context: Context, mediaId: Long) : Job {
|
||||||
Logd(TAG, "deleteFeedMediaOfItem called")
|
Logd(TAG, "deleteFeedMediaOfItem called")
|
||||||
return runOnDbThread {
|
return runOnDbThread {
|
||||||
val media = getFeedMedia(mediaId)
|
val media = getFeedMedia(mediaId)
|
||||||
|
@ -179,7 +177,7 @@ import java.util.concurrent.TimeUnit
|
||||||
* @param feedId ID of the Feed that should be deleted.
|
* @param feedId ID of the Feed that should be deleted.
|
||||||
*/
|
*/
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun deleteFeed(context: Context, feedId: Long): Future<*> {
|
fun deleteFeed(context: Context, feedId: Long) : Job {
|
||||||
return runOnDbThread {
|
return runOnDbThread {
|
||||||
val feed = getFeed(feedId) ?: return@runOnDbThread
|
val feed = getFeed(feedId) ?: return@runOnDbThread
|
||||||
// delete stored media files and mark them as read
|
// delete stored media files and mark them as read
|
||||||
|
@ -203,7 +201,7 @@ import java.util.concurrent.TimeUnit
|
||||||
* Remove the listed items and their FeedMedia entries.
|
* Remove the listed items and their FeedMedia entries.
|
||||||
* Deleting media also removes the download log entries.
|
* Deleting media also removes the download log entries.
|
||||||
*/
|
*/
|
||||||
fun deleteFeedItems(context: Context, items: List<FeedItem>): Future<*> {
|
fun deleteFeedItems(context: Context, items: List<FeedItem>) : Job {
|
||||||
Logd(TAG, "deleteFeedItems called")
|
Logd(TAG, "deleteFeedItems called")
|
||||||
return runOnDbThread { deleteFeedItemsSynchronous(context, items) }
|
return runOnDbThread { deleteFeedItemsSynchronous(context, items) }
|
||||||
}
|
}
|
||||||
|
@ -252,7 +250,7 @@ import java.util.concurrent.TimeUnit
|
||||||
/**
|
/**
|
||||||
* Deletes the entire playback history.
|
* Deletes the entire playback history.
|
||||||
*/
|
*/
|
||||||
fun clearPlaybackHistory(): Future<*> {
|
fun clearPlaybackHistory() : Job {
|
||||||
return runOnDbThread {
|
return runOnDbThread {
|
||||||
val adapter = getInstance()
|
val adapter = getInstance()
|
||||||
adapter.open()
|
adapter.open()
|
||||||
|
@ -265,7 +263,7 @@ import java.util.concurrent.TimeUnit
|
||||||
/**
|
/**
|
||||||
* Deletes the entire download log.
|
* Deletes the entire download log.
|
||||||
*/
|
*/
|
||||||
fun clearDownloadLog(): Future<*> {
|
fun clearDownloadLog() : Job {
|
||||||
return runOnDbThread {
|
return runOnDbThread {
|
||||||
val adapter = getInstance()
|
val adapter = getInstance()
|
||||||
adapter.open()
|
adapter.open()
|
||||||
|
@ -275,8 +273,8 @@ import java.util.concurrent.TimeUnit
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun deleteFromPlaybackHistory(feedItem: FeedItem): Future<*> {
|
fun deleteFromPlaybackHistory(feedItem: FeedItem) {
|
||||||
return addItemToPlaybackHistory(feedItem.media, Date(0))
|
addItemToPlaybackHistory(feedItem.media, Date(0))
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -288,7 +286,7 @@ import java.util.concurrent.TimeUnit
|
||||||
* @param date PlaybackCompletionDate for `media`
|
* @param date PlaybackCompletionDate for `media`
|
||||||
*/
|
*/
|
||||||
@JvmOverloads
|
@JvmOverloads
|
||||||
fun addItemToPlaybackHistory(media: FeedMedia?, date: Date? = Date()): Future<*> {
|
fun addItemToPlaybackHistory(media: FeedMedia?, date: Date? = Date()) : Job {
|
||||||
return runOnDbThread {
|
return runOnDbThread {
|
||||||
if (media != null) {
|
if (media != null) {
|
||||||
Logd(TAG, "Adding item to playback history")
|
Logd(TAG, "Adding item to playback history")
|
||||||
|
@ -308,7 +306,7 @@ import java.util.concurrent.TimeUnit
|
||||||
*
|
*
|
||||||
* @param status The DownloadStatus object.
|
* @param status The DownloadStatus object.
|
||||||
*/
|
*/
|
||||||
fun addDownloadStatus(status: DownloadResult?): Future<*> {
|
fun addDownloadStatus(status: DownloadResult?) : Job {
|
||||||
Logd(TAG, "addDownloadStatus called")
|
Logd(TAG, "addDownloadStatus called")
|
||||||
return runOnDbThread {
|
return runOnDbThread {
|
||||||
if (status != null) {
|
if (status != null) {
|
||||||
|
@ -331,7 +329,7 @@ import java.util.concurrent.TimeUnit
|
||||||
* @param performAutoDownload True if an auto-download process should be started after the operation
|
* @param performAutoDownload True if an auto-download process should be started after the operation
|
||||||
* @throws IndexOutOfBoundsException if index < 0 || index >= queue.size()
|
* @throws IndexOutOfBoundsException if index < 0 || index >= queue.size()
|
||||||
*/
|
*/
|
||||||
@UnstableApi fun addQueueItemAt(context: Context, itemId: Long, index: Int, performAutoDownload: Boolean): Future<*> {
|
@UnstableApi fun addQueueItemAt(context: Context, itemId: Long, index: Int, performAutoDownload: Boolean) : Job {
|
||||||
Logd(TAG, "addQueueItemAt called")
|
Logd(TAG, "addQueueItemAt called")
|
||||||
return runOnDbThread {
|
return runOnDbThread {
|
||||||
val adapter = getInstance()
|
val adapter = getInstance()
|
||||||
|
@ -357,11 +355,11 @@ import java.util.concurrent.TimeUnit
|
||||||
}
|
}
|
||||||
|
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun addQueueItem(context: Context, vararg items: FeedItem): Future<*> {
|
fun addQueueItem(context: Context, vararg items: FeedItem) : Job {
|
||||||
return addQueueItem(context, true, *items)
|
return addQueueItem(context, true, *items)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun addQueueItem(context: Context, markAsUnplayed: Boolean, vararg items: FeedItem): Future<*> {
|
fun addQueueItem(context: Context, markAsUnplayed: Boolean, vararg items: FeedItem) : Job {
|
||||||
Logd(TAG, "addQueueItem called")
|
Logd(TAG, "addQueueItem called")
|
||||||
val itemIds = LongList(items.size)
|
val itemIds = LongList(items.size)
|
||||||
for (item in items) {
|
for (item in items) {
|
||||||
|
@ -380,7 +378,7 @@ import java.util.concurrent.TimeUnit
|
||||||
* @param performAutoDownload true if an auto-download process should be started after the operation.
|
* @param performAutoDownload true if an auto-download process should be started after the operation.
|
||||||
* @param itemIds IDs of the FeedItem objects that should be added to the queue.
|
* @param itemIds IDs of the FeedItem objects that should be added to the queue.
|
||||||
*/
|
*/
|
||||||
@UnstableApi fun addQueueItem(context: Context, performAutoDownload: Boolean, vararg itemIds: Long): Future<*> {
|
@UnstableApi fun addQueueItem(context: Context, performAutoDownload: Boolean, vararg itemIds: Long) : Job {
|
||||||
return addQueueItem(context, performAutoDownload, true, *itemIds)
|
return addQueueItem(context, performAutoDownload, true, *itemIds)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -393,7 +391,7 @@ import java.util.concurrent.TimeUnit
|
||||||
* @param markAsUnplayed true if the items should be marked as unplayed when enqueueing
|
* @param markAsUnplayed true if the items should be marked as unplayed when enqueueing
|
||||||
* @param itemIds IDs of the FeedItem objects that should be added to the queue.
|
* @param itemIds IDs of the FeedItem objects that should be added to the queue.
|
||||||
*/
|
*/
|
||||||
@UnstableApi fun addQueueItem(context: Context, performAutoDownload: Boolean, markAsUnplayed: Boolean, vararg itemIds: Long): Future<*> {
|
@UnstableApi fun addQueueItem(context: Context, performAutoDownload: Boolean, markAsUnplayed: Boolean, vararg itemIds: Long) : Job {
|
||||||
Logd(TAG, "addQueueItem(context ...) called")
|
Logd(TAG, "addQueueItem(context ...) called")
|
||||||
return runOnDbThread {
|
return runOnDbThread {
|
||||||
if (itemIds.isEmpty()) return@runOnDbThread
|
if (itemIds.isEmpty()) return@runOnDbThread
|
||||||
|
@ -406,9 +404,8 @@ import java.util.concurrent.TimeUnit
|
||||||
val markAsUnplayedIds = LongList()
|
val markAsUnplayedIds = LongList()
|
||||||
val events: MutableList<FlowEvent.QueueEvent> = ArrayList()
|
val events: MutableList<FlowEvent.QueueEvent> = ArrayList()
|
||||||
val updatedItems: MutableList<FeedItem> = ArrayList()
|
val updatedItems: MutableList<FeedItem> = ArrayList()
|
||||||
val positionCalculator =
|
val positionCalculator = ItemEnqueuePositionCalculator(enqueueLocation)
|
||||||
ItemEnqueuePositionCalculator(enqueueLocation)
|
val currentlyPlaying = loadPlayableFromPreferences()
|
||||||
val currentlyPlaying = createInstanceFromPreferences(context)
|
|
||||||
var insertPosition = positionCalculator.calcPosition(queue, currentlyPlaying)
|
var insertPosition = positionCalculator.calcPosition(queue, currentlyPlaying)
|
||||||
for (itemId in itemIds) {
|
for (itemId in itemIds) {
|
||||||
if (!itemListContains(queue, itemId)) {
|
if (!itemListContains(queue, itemId)) {
|
||||||
|
@ -468,7 +465,7 @@ import java.util.concurrent.TimeUnit
|
||||||
* Removes all FeedItem objects from the queue.
|
* Removes all FeedItem objects from the queue.
|
||||||
*/
|
*/
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun clearQueue(): Future<*> {
|
fun clearQueue() : Job {
|
||||||
return runOnDbThread {
|
return runOnDbThread {
|
||||||
val adapter = getInstance()
|
val adapter = getInstance()
|
||||||
adapter.open()
|
adapter.open()
|
||||||
|
@ -486,19 +483,19 @@ import java.util.concurrent.TimeUnit
|
||||||
* @param item FeedItem that should be removed.
|
* @param item FeedItem that should be removed.
|
||||||
*/
|
*/
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun removeQueueItem(context: Context, performAutoDownload: Boolean, item: FeedItem): Future<*> {
|
fun removeQueueItem(context: Context, performAutoDownload: Boolean, item: FeedItem) : Job {
|
||||||
return runOnDbThread { removeQueueItemSynchronous(context, performAutoDownload, item.id) }
|
return runOnDbThread { removeQueueItemSynchronous(context, performAutoDownload, item.id) }
|
||||||
}
|
}
|
||||||
|
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun removeQueueItem(context: Context, performAutoDownload: Boolean, vararg itemIds: Long): Future<*> {
|
fun removeQueueItem(context: Context, performAutoDownload: Boolean, vararg itemIds: Long) : Job {
|
||||||
return runOnDbThread { removeQueueItemSynchronous(context, performAutoDownload, *itemIds) }
|
return runOnDbThread { removeQueueItemSynchronous(context, performAutoDownload, *itemIds) }
|
||||||
}
|
}
|
||||||
|
|
||||||
@UnstableApi private fun removeQueueItemSynchronous(context: Context, performAutoDownload: Boolean, vararg itemIds: Long) {
|
@UnstableApi private fun removeQueueItemSynchronous(context: Context, performAutoDownload: Boolean, vararg itemIds: Long) {
|
||||||
Logd(TAG, "removeQueueItemSynchronous called $itemIds")
|
Logd(TAG, "removeQueueItemSynchronous called $itemIds")
|
||||||
if (itemIds.isEmpty()) return
|
if (itemIds.isEmpty()) return
|
||||||
showStackTrace()
|
// showStackTrace()
|
||||||
|
|
||||||
val adapter = getInstance()
|
val adapter = getInstance()
|
||||||
adapter.open()
|
adapter.open()
|
||||||
|
@ -535,11 +532,12 @@ import java.util.concurrent.TimeUnit
|
||||||
if (performAutoDownload) autodownloadUndownloadedItems(context)
|
if (performAutoDownload) autodownloadUndownloadedItems(context)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun toggleFavoriteItem(item: FeedItem): Future<*> {
|
|
||||||
return if (item.isTagged(FeedItem.TAG_FAVORITE)) removeFavoriteItem(item) else addFavoriteItem(item)
|
fun toggleFavoriteItem(item: FeedItem) {
|
||||||
|
if (item.isTagged(FeedItem.TAG_FAVORITE)) removeFavoriteItem(item) else addFavoriteItem(item)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun addFavoriteItem(item: FeedItem): Future<*> {
|
fun addFavoriteItem(item: FeedItem) : Job {
|
||||||
return runOnDbThread {
|
return runOnDbThread {
|
||||||
val adapter = getInstance().open()
|
val adapter = getInstance().open()
|
||||||
adapter.addFavoriteItem(item)
|
adapter.addFavoriteItem(item)
|
||||||
|
@ -550,7 +548,7 @@ import java.util.concurrent.TimeUnit
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun removeFavoriteItem(item: FeedItem): Future<*> {
|
fun removeFavoriteItem(item: FeedItem) : Job {
|
||||||
return runOnDbThread {
|
return runOnDbThread {
|
||||||
val adapter = getInstance().open()
|
val adapter = getInstance().open()
|
||||||
adapter.removeFavoriteItem(item)
|
adapter.removeFavoriteItem(item)
|
||||||
|
@ -567,7 +565,7 @@ import java.util.concurrent.TimeUnit
|
||||||
* @param itemId The item to move to the top of the queue
|
* @param itemId The item to move to the top of the queue
|
||||||
* @param broadcastUpdate true if this operation should trigger a QueueUpdateBroadcast. This option should be set to
|
* @param broadcastUpdate true if this operation should trigger a QueueUpdateBroadcast. This option should be set to
|
||||||
*/
|
*/
|
||||||
fun moveQueueItemToTop(itemId: Long, broadcastUpdate: Boolean): Future<*> {
|
fun moveQueueItemToTop(itemId: Long, broadcastUpdate: Boolean) : Job {
|
||||||
return runOnDbThread {
|
return runOnDbThread {
|
||||||
val queueIdList = getQueueIDList()
|
val queueIdList = getQueueIDList()
|
||||||
val index = queueIdList.indexOf(itemId)
|
val index = queueIdList.indexOf(itemId)
|
||||||
|
@ -582,7 +580,7 @@ import java.util.concurrent.TimeUnit
|
||||||
* @param itemId The item to move to the bottom of the queue
|
* @param itemId The item to move to the bottom of the queue
|
||||||
* @param broadcastUpdate true if this operation should trigger a QueueUpdateBroadcast. This option should be set to
|
* @param broadcastUpdate true if this operation should trigger a QueueUpdateBroadcast. This option should be set to
|
||||||
*/
|
*/
|
||||||
fun moveQueueItemToBottom(itemId: Long, broadcastUpdate: Boolean): Future<*> {
|
fun moveQueueItemToBottom(itemId: Long, broadcastUpdate: Boolean) : Job {
|
||||||
return runOnDbThread {
|
return runOnDbThread {
|
||||||
val queueIdList = getQueueIDList()
|
val queueIdList = getQueueIDList()
|
||||||
val index = queueIdList.indexOf(itemId)
|
val index = queueIdList.indexOf(itemId)
|
||||||
|
@ -601,7 +599,7 @@ import java.util.concurrent.TimeUnit
|
||||||
* @throws IndexOutOfBoundsException if (to < 0 || to >= queue.size()) || (from < 0 || from >= queue.size())
|
* @throws IndexOutOfBoundsException if (to < 0 || to >= queue.size()) || (from < 0 || from >= queue.size())
|
||||||
*/
|
*/
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun moveQueueItem(from: Int, to: Int, broadcastUpdate: Boolean): Future<*> {
|
fun moveQueueItem(from: Int, to: Int, broadcastUpdate: Boolean) : Job {
|
||||||
return runOnDbThread { moveQueueItemHelper(from, to, broadcastUpdate) }
|
return runOnDbThread { moveQueueItemHelper(from, to, broadcastUpdate) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -634,7 +632,7 @@ import java.util.concurrent.TimeUnit
|
||||||
adapter.close()
|
adapter.close()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun resetPagedFeedPage(feed: Feed?): Future<*> {
|
fun resetPagedFeedPage(feed: Feed?) : Job {
|
||||||
return runOnDbThread {
|
return runOnDbThread {
|
||||||
if (feed != null) {
|
if (feed != null) {
|
||||||
val adapter = getInstance()
|
val adapter = getInstance()
|
||||||
|
@ -652,7 +650,7 @@ import java.util.concurrent.TimeUnit
|
||||||
* FeedItem.UNPLAYED
|
* FeedItem.UNPLAYED
|
||||||
* @param itemIds IDs of the FeedItems.
|
* @param itemIds IDs of the FeedItems.
|
||||||
*/
|
*/
|
||||||
fun markItemPlayed(played: Int, vararg itemIds: Long): Future<*> {
|
fun markItemPlayed(played: Int, vararg itemIds: Long) : Job {
|
||||||
return markItemPlayed(played, true, *itemIds)
|
return markItemPlayed(played, true, *itemIds)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -665,7 +663,7 @@ import java.util.concurrent.TimeUnit
|
||||||
* This option is usually set to true
|
* This option is usually set to true
|
||||||
* @param itemIds IDs of the FeedItems.
|
* @param itemIds IDs of the FeedItems.
|
||||||
*/
|
*/
|
||||||
fun markItemPlayed(played: Int, broadcastUpdate: Boolean, vararg itemIds: Long): Future<*> {
|
fun markItemPlayed(played: Int, broadcastUpdate: Boolean, vararg itemIds: Long) : Job {
|
||||||
return runOnDbThread {
|
return runOnDbThread {
|
||||||
val adapter = getInstance()
|
val adapter = getInstance()
|
||||||
adapter.open()
|
adapter.open()
|
||||||
|
@ -683,12 +681,12 @@ import java.util.concurrent.TimeUnit
|
||||||
* FeedItem.NEW, FeedItem.UNPLAYED
|
* FeedItem.NEW, FeedItem.UNPLAYED
|
||||||
* @param resetMediaPosition true if this method should also reset the position of the FeedItem's FeedMedia object.
|
* @param resetMediaPosition true if this method should also reset the position of the FeedItem's FeedMedia object.
|
||||||
*/
|
*/
|
||||||
fun markItemPlayed(item: FeedItem, played: Int, resetMediaPosition: Boolean): Future<*> {
|
fun markItemPlayed(item: FeedItem, played: Int, resetMediaPosition: Boolean) : Job {
|
||||||
val mediaId = if (item.media != null) item.media!!.id else 0
|
val mediaId = if (item.media != null) item.media!!.id else 0
|
||||||
return markItemPlayed(item.id, played, mediaId, resetMediaPosition)
|
return markItemPlayed(item.id, played, mediaId, resetMediaPosition)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun markItemPlayed(itemId: Long, played: Int, mediaId: Long, resetMediaPosition: Boolean): Future<*> {
|
private fun markItemPlayed(itemId: Long, played: Int, mediaId: Long, resetMediaPosition: Boolean) : Job {
|
||||||
return runOnDbThread {
|
return runOnDbThread {
|
||||||
val adapter = getInstance()
|
val adapter = getInstance()
|
||||||
adapter.open()
|
adapter.open()
|
||||||
|
@ -703,7 +701,7 @@ import java.util.concurrent.TimeUnit
|
||||||
*
|
*
|
||||||
* @param feedId ID of the Feed.
|
* @param feedId ID of the Feed.
|
||||||
*/
|
*/
|
||||||
fun removeFeedNewFlag(feedId: Long): Future<*> {
|
fun removeFeedNewFlag(feedId: Long) : Job {
|
||||||
return runOnDbThread {
|
return runOnDbThread {
|
||||||
val adapter = getInstance()
|
val adapter = getInstance()
|
||||||
adapter.open()
|
adapter.open()
|
||||||
|
@ -717,7 +715,7 @@ import java.util.concurrent.TimeUnit
|
||||||
* Sets the 'read'-attribute of all NEW FeedItems to UNPLAYED.
|
* Sets the 'read'-attribute of all NEW FeedItems to UNPLAYED.
|
||||||
*/
|
*/
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun removeAllNewFlags(): Future<*> {
|
fun removeAllNewFlags() : Job {
|
||||||
return runOnDbThread {
|
return runOnDbThread {
|
||||||
val adapter = getInstance()
|
val adapter = getInstance()
|
||||||
adapter.open()
|
adapter.open()
|
||||||
|
@ -727,7 +725,7 @@ import java.util.concurrent.TimeUnit
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun addNewFeed(context: Context, vararg feeds: Feed): Future<*> {
|
fun addNewFeed(context: Context, vararg feeds: Feed) : Job {
|
||||||
return runOnDbThread {
|
return runOnDbThread {
|
||||||
val adapter = getInstance()
|
val adapter = getInstance()
|
||||||
adapter.open()
|
adapter.open()
|
||||||
|
@ -744,7 +742,7 @@ import java.util.concurrent.TimeUnit
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun persistCompleteFeed(vararg feeds: Feed): Future<*> {
|
fun persistCompleteFeed(vararg feeds: Feed) : Job {
|
||||||
return runOnDbThread {
|
return runOnDbThread {
|
||||||
val adapter = getInstance()
|
val adapter = getInstance()
|
||||||
adapter.open()
|
adapter.open()
|
||||||
|
@ -753,7 +751,7 @@ import java.util.concurrent.TimeUnit
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun persistItemList(items: List<FeedItem>): Future<*> {
|
fun persistItemList(items: List<FeedItem>) : Job {
|
||||||
return runOnDbThread {
|
return runOnDbThread {
|
||||||
val adapter = getInstance()
|
val adapter = getInstance()
|
||||||
adapter.open()
|
adapter.open()
|
||||||
|
@ -769,7 +767,7 @@ import java.util.concurrent.TimeUnit
|
||||||
*
|
*
|
||||||
* @param media The FeedMedia object.
|
* @param media The FeedMedia object.
|
||||||
*/
|
*/
|
||||||
fun persistFeedMedia(media: FeedMedia): Future<*> {
|
fun persistFeedMedia(media: FeedMedia) : Job {
|
||||||
Logd(TAG, "persistFeedMedia called")
|
Logd(TAG, "persistFeedMedia called")
|
||||||
return runOnDbThread {
|
return runOnDbThread {
|
||||||
val adapter = getInstance()
|
val adapter = getInstance()
|
||||||
|
@ -785,7 +783,7 @@ import java.util.concurrent.TimeUnit
|
||||||
* @param media The FeedMedia object.
|
* @param media The FeedMedia object.
|
||||||
*/
|
*/
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun persistFeedMediaPlaybackInfo(media: FeedMedia?): Future<*> {
|
fun persistFeedMediaPlaybackInfo(media: FeedMedia?) : Job {
|
||||||
Logd(TAG, "persistFeedMediaPlaybackInfo called")
|
Logd(TAG, "persistFeedMediaPlaybackInfo called")
|
||||||
return runOnDbThread {
|
return runOnDbThread {
|
||||||
if (media != null) {
|
if (media != null) {
|
||||||
|
@ -804,7 +802,7 @@ import java.util.concurrent.TimeUnit
|
||||||
* @param item The FeedItem object.
|
* @param item The FeedItem object.
|
||||||
*/
|
*/
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun persistFeedItem(item: FeedItem?): Future<*> {
|
fun persistFeedItem(item: FeedItem?) : Job {
|
||||||
Logd(TAG, "persistFeedItem called")
|
Logd(TAG, "persistFeedItem called")
|
||||||
return runOnDbThread {
|
return runOnDbThread {
|
||||||
if (item != null) {
|
if (item != null) {
|
||||||
|
@ -820,7 +818,7 @@ import java.util.concurrent.TimeUnit
|
||||||
/**
|
/**
|
||||||
* Updates download URL of a feed
|
* Updates download URL of a feed
|
||||||
*/
|
*/
|
||||||
fun updateFeedDownloadURL(original: String, updated: String): Future<*> {
|
fun updateFeedDownloadURL(original: String, updated: String) : Job {
|
||||||
Logd(TAG, "updateFeedDownloadURL(original: $original, updated: $updated)")
|
Logd(TAG, "updateFeedDownloadURL(original: $original, updated: $updated)")
|
||||||
return runOnDbThread {
|
return runOnDbThread {
|
||||||
val adapter = getInstance()
|
val adapter = getInstance()
|
||||||
|
@ -835,7 +833,7 @@ import java.util.concurrent.TimeUnit
|
||||||
*
|
*
|
||||||
* @param preferences The FeedPreferences object.
|
* @param preferences The FeedPreferences object.
|
||||||
*/
|
*/
|
||||||
fun persistFeedPreferences(preferences: FeedPreferences): Future<*> {
|
fun persistFeedPreferences(preferences: FeedPreferences) : Job {
|
||||||
return runOnDbThread {
|
return runOnDbThread {
|
||||||
val adapter = getInstance()
|
val adapter = getInstance()
|
||||||
adapter.open()
|
adapter.open()
|
||||||
|
@ -863,7 +861,7 @@ import java.util.concurrent.TimeUnit
|
||||||
*
|
*
|
||||||
* @param lastUpdateFailed true if last update failed
|
* @param lastUpdateFailed true if last update failed
|
||||||
*/
|
*/
|
||||||
fun persistFeedLastUpdateFailed(feedId: Long, lastUpdateFailed: Boolean): Future<*> {
|
fun persistFeedLastUpdateFailed(feedId: Long, lastUpdateFailed: Boolean) : Job {
|
||||||
return runOnDbThread {
|
return runOnDbThread {
|
||||||
val adapter = getInstance()
|
val adapter = getInstance()
|
||||||
adapter.open()
|
adapter.open()
|
||||||
|
@ -873,7 +871,7 @@ import java.util.concurrent.TimeUnit
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun persistFeedCustomTitle(feed: Feed): Future<*> {
|
fun persistFeedCustomTitle(feed: Feed) : Job {
|
||||||
return runOnDbThread {
|
return runOnDbThread {
|
||||||
val adapter = getInstance()
|
val adapter = getInstance()
|
||||||
adapter.open()
|
adapter.open()
|
||||||
|
@ -890,10 +888,10 @@ import java.util.concurrent.TimeUnit
|
||||||
* QueueUpdateBroadcast. This option should be set to `false`
|
* QueueUpdateBroadcast. This option should be set to `false`
|
||||||
* if the caller wants to avoid unexpected updates of the GUI.
|
* if the caller wants to avoid unexpected updates of the GUI.
|
||||||
*/
|
*/
|
||||||
fun reorderQueue(sortOrder: SortOrder?, broadcastUpdate: Boolean): Future<*> {
|
fun reorderQueue(sortOrder: SortOrder?, broadcastUpdate: Boolean) : Job {
|
||||||
if (sortOrder == null) {
|
if (sortOrder == null) {
|
||||||
Log.w(TAG, "reorderQueue() - sortOrder is null. Do nothing.")
|
Log.w(TAG, "reorderQueue() - sortOrder is null. Do nothing.")
|
||||||
return runOnDbThread {}
|
return Job()
|
||||||
}
|
}
|
||||||
val permutor = getPermutor(sortOrder)
|
val permutor = getPermutor(sortOrder)
|
||||||
return runOnDbThread {
|
return runOnDbThread {
|
||||||
|
@ -914,7 +912,7 @@ import java.util.concurrent.TimeUnit
|
||||||
* @param feedId The feed's ID
|
* @param feedId The feed's ID
|
||||||
* @param filterValues Values that represent properties to filter by
|
* @param filterValues Values that represent properties to filter by
|
||||||
*/
|
*/
|
||||||
fun persistFeedItemsFilter(feedId: Long, filterValues: Set<String>): Future<*> {
|
fun persistFeedItemsFilter(feedId: Long, filterValues: Set<String>) : Job {
|
||||||
Logd(TAG, "persistFeedItemsFilter() called with: feedId = [$feedId], filterValues = [$filterValues]")
|
Logd(TAG, "persistFeedItemsFilter() called with: feedId = [$feedId], filterValues = [$filterValues]")
|
||||||
return runOnDbThread {
|
return runOnDbThread {
|
||||||
val adapter = getInstance()
|
val adapter = getInstance()
|
||||||
|
@ -929,7 +927,7 @@ import java.util.concurrent.TimeUnit
|
||||||
* Set item sort order of the feed
|
* Set item sort order of the feed
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
fun persistFeedItemSortOrder(feedId: Long, sortOrder: SortOrder?): Future<*> {
|
fun persistFeedItemSortOrder(feedId: Long, sortOrder: SortOrder?) : Job {
|
||||||
return runOnDbThread {
|
return runOnDbThread {
|
||||||
val adapter = getInstance()
|
val adapter = getInstance()
|
||||||
adapter.open()
|
adapter.open()
|
||||||
|
@ -942,32 +940,36 @@ import java.util.concurrent.TimeUnit
|
||||||
/**
|
/**
|
||||||
* Reset the statistics in DB
|
* Reset the statistics in DB
|
||||||
*/
|
*/
|
||||||
// fun resetStatistics(): Future<*> {
|
fun resetStatistics() : Job {
|
||||||
// return runOnDbThread {
|
return runOnDbThread {
|
||||||
// val adapter = getInstance()
|
|
||||||
// adapter.open()
|
|
||||||
// adapter.resetAllMediaPlayedDuration()
|
|
||||||
// adapter.close()
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
suspend fun resetStatistics(): Unit = withContext(Dispatchers.IO) {
|
|
||||||
val result = async {
|
|
||||||
val adapter = getInstance()
|
val adapter = getInstance()
|
||||||
adapter.open()
|
adapter.open()
|
||||||
adapter.resetAllMediaPlayedDuration()
|
adapter.resetAllMediaPlayedDuration()
|
||||||
adapter.close()
|
adapter.close()
|
||||||
}
|
}
|
||||||
result.await()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Submit to the DB thread only if caller is not already on the DB thread. Otherwise,
|
* Submit to the DB thread only if caller is not already on the DB thread. Otherwise,
|
||||||
* just execute synchronously
|
* just execute synchronously
|
||||||
*/
|
*/
|
||||||
private fun runOnDbThread(runnable: Runnable): Future<*> {
|
// private fun runOnDbThread(runnable: Runnable): Future<*> {
|
||||||
if ("DatabaseExecutor" == Thread.currentThread().name) {
|
// if ("DatabaseExecutor" == Thread.currentThread().name) {
|
||||||
runnable.run()
|
// runnable.run()
|
||||||
return Futures.immediateFuture<Any?>(null)
|
// return Futures.immediateFuture<Any?>(null)
|
||||||
} else return dbExec.submit(runnable)
|
// } else return dbExec.submit(runnable)
|
||||||
|
// }
|
||||||
|
|
||||||
|
private fun runOnDbThread(block: suspend () -> Unit) : Job {
|
||||||
|
Logd(TAG, "DBWriter runOnDbThread")
|
||||||
|
return ioScope.launch {
|
||||||
|
if (Dispatchers.IO == coroutineContext[ContinuationInterceptor]) {
|
||||||
|
block()
|
||||||
|
} else {
|
||||||
|
withContext(Dispatchers.IO) {
|
||||||
|
block()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,6 +11,7 @@ import ac.mdiq.podcini.preferences.UserPreferences
|
||||||
import ac.mdiq.podcini.preferences.UserPreferences.episodeCacheSize
|
import ac.mdiq.podcini.preferences.UserPreferences.episodeCacheSize
|
||||||
import androidx.annotation.OptIn
|
import androidx.annotation.OptIn
|
||||||
import androidx.media3.common.util.UnstableApi
|
import androidx.media3.common.util.UnstableApi
|
||||||
|
import kotlinx.coroutines.runBlocking
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import java.util.concurrent.ExecutionException
|
import java.util.concurrent.ExecutionException
|
||||||
|
|
||||||
|
@ -41,8 +42,9 @@ class ExceptFavoriteCleanupAlgorithm : EpisodeCleanupAlgorithm() {
|
||||||
val delete = if (candidates.size > numberOfEpisodesToDelete) candidates.subList(0, numberOfEpisodesToDelete) else candidates
|
val delete = if (candidates.size > numberOfEpisodesToDelete) candidates.subList(0, numberOfEpisodesToDelete) else candidates
|
||||||
|
|
||||||
for (item in delete) {
|
for (item in delete) {
|
||||||
|
if (item.media == null) continue
|
||||||
try {
|
try {
|
||||||
DBWriter.deleteFeedMediaOfItem(context!!, item.media!!.id).get()
|
runBlocking { DBWriter.deleteFeedMediaOfItem(context, item.media!!.id).join() }
|
||||||
} catch (e: InterruptedException) {
|
} catch (e: InterruptedException) {
|
||||||
e.printStackTrace()
|
e.printStackTrace()
|
||||||
} catch (e: ExecutionException) {
|
} catch (e: ExecutionException) {
|
||||||
|
|
|
@ -105,7 +105,7 @@ class Feed : FeedFile {
|
||||||
var sortOrder: SortOrder? = null
|
var sortOrder: SortOrder? = null
|
||||||
set(sortOrder) {
|
set(sortOrder) {
|
||||||
if (sortOrder == null) {
|
if (sortOrder == null) {
|
||||||
Log.w("Feed sortOrder", "The specified sortOrder $sortOrder is invalid.")
|
// Log.w("Feed sortOrder", "The specified sortOrder $sortOrder is invalid.")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
field = sortOrder
|
field = sortOrder
|
||||||
|
|
|
@ -9,7 +9,7 @@ import ac.mdiq.podcini.storage.DBWriter
|
||||||
import ac.mdiq.podcini.util.LongList
|
import ac.mdiq.podcini.util.LongList
|
||||||
import ac.mdiq.podcini.storage.model.feed.FeedItem
|
import ac.mdiq.podcini.storage.model.feed.FeedItem
|
||||||
import ac.mdiq.podcini.net.download.serviceinterface.DownloadServiceInterface
|
import ac.mdiq.podcini.net.download.serviceinterface.DownloadServiceInterface
|
||||||
import ac.mdiq.podcini.ui.view.LocalDeleteModal
|
import ac.mdiq.podcini.ui.utils.LocalDeleteModal
|
||||||
import androidx.media3.common.util.UnstableApi
|
import androidx.media3.common.util.UnstableApi
|
||||||
|
|
||||||
@UnstableApi
|
@UnstableApi
|
||||||
|
|
|
@ -5,7 +5,7 @@ import android.view.View
|
||||||
import ac.mdiq.podcini.R
|
import ac.mdiq.podcini.R
|
||||||
import ac.mdiq.podcini.storage.DBWriter
|
import ac.mdiq.podcini.storage.DBWriter
|
||||||
import ac.mdiq.podcini.storage.model.feed.FeedItem
|
import ac.mdiq.podcini.storage.model.feed.FeedItem
|
||||||
import ac.mdiq.podcini.ui.view.LocalDeleteModal.showLocalFeedDeleteWarningIfNecessary
|
import ac.mdiq.podcini.ui.utils.LocalDeleteModal.showLocalFeedDeleteWarningIfNecessary
|
||||||
import androidx.media3.common.util.UnstableApi
|
import androidx.media3.common.util.UnstableApi
|
||||||
|
|
||||||
class DeleteActionButton(item: FeedItem) : ItemActionButton(item) {
|
class DeleteActionButton(item: FeedItem) : ItemActionButton(item) {
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
package ac.mdiq.podcini.ui.actions.menuhandler
|
package ac.mdiq.podcini.ui.actions.menuhandler
|
||||||
|
|
||||||
import ac.mdiq.podcini.R
|
import ac.mdiq.podcini.R
|
||||||
import ac.mdiq.podcini.net.sync.SynchronizationSettings
|
|
||||||
import ac.mdiq.podcini.net.sync.SynchronizationSettings.isProviderConnected
|
import ac.mdiq.podcini.net.sync.SynchronizationSettings.isProviderConnected
|
||||||
import ac.mdiq.podcini.net.sync.SynchronizationSettings.wifiSyncEnabledKey
|
import ac.mdiq.podcini.net.sync.SynchronizationSettings.wifiSyncEnabledKey
|
||||||
import ac.mdiq.podcini.net.sync.model.EpisodeAction
|
import ac.mdiq.podcini.net.sync.model.EpisodeAction
|
||||||
|
@ -14,16 +13,19 @@ import ac.mdiq.podcini.storage.model.feed.FeedItem
|
||||||
import ac.mdiq.podcini.storage.model.feed.FeedMedia
|
import ac.mdiq.podcini.storage.model.feed.FeedMedia
|
||||||
import ac.mdiq.podcini.ui.activity.MainActivity
|
import ac.mdiq.podcini.ui.activity.MainActivity
|
||||||
import ac.mdiq.podcini.ui.dialog.ShareDialog
|
import ac.mdiq.podcini.ui.dialog.ShareDialog
|
||||||
import ac.mdiq.podcini.ui.view.LocalDeleteModal
|
import ac.mdiq.podcini.ui.utils.LocalDeleteModal
|
||||||
import ac.mdiq.podcini.util.*
|
import ac.mdiq.podcini.util.*
|
||||||
import android.os.Handler
|
import android.os.Handler
|
||||||
import android.util.Log
|
|
||||||
import android.view.KeyEvent
|
import android.view.KeyEvent
|
||||||
import android.view.Menu
|
import android.view.Menu
|
||||||
import androidx.annotation.OptIn
|
import androidx.annotation.OptIn
|
||||||
import androidx.fragment.app.Fragment
|
import androidx.fragment.app.Fragment
|
||||||
import androidx.media3.common.util.UnstableApi
|
import androidx.media3.common.util.UnstableApi
|
||||||
import com.google.android.material.snackbar.Snackbar
|
import com.google.android.material.snackbar.Snackbar
|
||||||
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import kotlinx.coroutines.withContext
|
||||||
import kotlin.math.ceil
|
import kotlin.math.ceil
|
||||||
|
|
||||||
|
|
||||||
|
@ -49,7 +51,6 @@ object FeedItemMenuHandler {
|
||||||
val hasMedia = selectedItem.media != null
|
val hasMedia = selectedItem.media != null
|
||||||
val isPlaying = hasMedia && PlaybackStatus.isPlaying(selectedItem.media)
|
val isPlaying = hasMedia && PlaybackStatus.isPlaying(selectedItem.media)
|
||||||
val isInQueue: Boolean = selectedItem.isTagged(FeedItem.TAG_QUEUE)
|
val isInQueue: Boolean = selectedItem.isTagged(FeedItem.TAG_QUEUE)
|
||||||
val fileDownloaded = hasMedia && selectedItem.media?.fileExists()?:false
|
|
||||||
val isLocalFile = hasMedia && selectedItem.feed?.isLocalFeed?:false
|
val isLocalFile = hasMedia && selectedItem.feed?.isLocalFeed?:false
|
||||||
val isFavorite: Boolean = selectedItem.isTagged(FeedItem.TAG_FAVORITE)
|
val isFavorite: Boolean = selectedItem.isTagged(FeedItem.TAG_FAVORITE)
|
||||||
|
|
||||||
|
@ -73,7 +74,11 @@ object FeedItemMenuHandler {
|
||||||
|
|
||||||
setItemVisibility(menu, R.id.add_to_favorites_item, !isFavorite)
|
setItemVisibility(menu, R.id.add_to_favorites_item, !isFavorite)
|
||||||
setItemVisibility(menu, R.id.remove_from_favorites_item, isFavorite)
|
setItemVisibility(menu, R.id.remove_from_favorites_item, isFavorite)
|
||||||
setItemVisibility(menu, R.id.remove_item, fileDownloaded || isLocalFile)
|
|
||||||
|
CoroutineScope(Dispatchers.Main).launch {
|
||||||
|
val fileDownloaded = withContext(Dispatchers.IO) { hasMedia && selectedItem.media?.fileExists() ?: false }
|
||||||
|
setItemVisibility(menu, R.id.remove_item, fileDownloaded || isLocalFile)
|
||||||
|
}
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -7,7 +7,7 @@ import ac.mdiq.podcini.R
|
||||||
import ac.mdiq.podcini.storage.DBWriter
|
import ac.mdiq.podcini.storage.DBWriter
|
||||||
import ac.mdiq.podcini.storage.model.feed.FeedItem
|
import ac.mdiq.podcini.storage.model.feed.FeedItem
|
||||||
import ac.mdiq.podcini.storage.model.feed.FeedItemFilter
|
import ac.mdiq.podcini.storage.model.feed.FeedItemFilter
|
||||||
import ac.mdiq.podcini.ui.view.LocalDeleteModal.showLocalFeedDeleteWarningIfNecessary
|
import ac.mdiq.podcini.ui.utils.LocalDeleteModal.showLocalFeedDeleteWarningIfNecessary
|
||||||
|
|
||||||
class DeleteSwipeAction : SwipeAction {
|
class DeleteSwipeAction : SwipeAction {
|
||||||
override fun getId(): String {
|
override fun getId(): String {
|
||||||
|
|
|
@ -3,18 +3,21 @@ package ac.mdiq.podcini.ui.actions.swipeactions
|
||||||
import ac.mdiq.podcini.R
|
import ac.mdiq.podcini.R
|
||||||
import ac.mdiq.podcini.storage.model.feed.FeedItemFilter
|
import ac.mdiq.podcini.storage.model.feed.FeedItemFilter
|
||||||
import ac.mdiq.podcini.ui.dialog.SwipeActionsDialog
|
import ac.mdiq.podcini.ui.dialog.SwipeActionsDialog
|
||||||
import ac.mdiq.podcini.ui.fragment.*
|
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.utils.ThemeUtils.getColorFromAttr
|
import ac.mdiq.podcini.ui.utils.ThemeUtils.getColorFromAttr
|
||||||
import ac.mdiq.podcini.ui.view.viewholder.EpisodeItemViewHolder
|
import ac.mdiq.podcini.ui.view.viewholder.EpisodeItemViewHolder
|
||||||
import ac.mdiq.podcini.util.event.EventFlow
|
import ac.mdiq.podcini.util.event.EventFlow
|
||||||
import ac.mdiq.podcini.util.event.FlowEvent
|
import ac.mdiq.podcini.util.event.FlowEvent
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
|
import android.content.SharedPreferences
|
||||||
import android.graphics.Canvas
|
import android.graphics.Canvas
|
||||||
|
import androidx.annotation.OptIn
|
||||||
import androidx.core.graphics.ColorUtils
|
import androidx.core.graphics.ColorUtils
|
||||||
import androidx.fragment.app.Fragment
|
import androidx.fragment.app.Fragment
|
||||||
import androidx.lifecycle.Lifecycle
|
import androidx.lifecycle.*
|
||||||
import androidx.lifecycle.LifecycleObserver
|
|
||||||
import androidx.lifecycle.OnLifecycleEvent
|
|
||||||
import androidx.media3.common.util.UnstableApi
|
import androidx.media3.common.util.UnstableApi
|
||||||
import androidx.recyclerview.widget.ItemTouchHelper
|
import androidx.recyclerview.widget.ItemTouchHelper
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
|
@ -26,7 +29,7 @@ import kotlin.math.min
|
||||||
import kotlin.math.sin
|
import kotlin.math.sin
|
||||||
|
|
||||||
open class SwipeActions(dragDirs: Int, private val fragment: Fragment, private val tag: String) :
|
open class SwipeActions(dragDirs: Int, private val fragment: Fragment, private val tag: String) :
|
||||||
ItemTouchHelper.SimpleCallback(dragDirs, ItemTouchHelper.RIGHT or ItemTouchHelper.LEFT), LifecycleObserver {
|
ItemTouchHelper.SimpleCallback(dragDirs, ItemTouchHelper.RIGHT or ItemTouchHelper.LEFT), DefaultLifecycleObserver {
|
||||||
|
|
||||||
private var filter: FeedItemFilter? = null
|
private var filter: FeedItemFilter? = null
|
||||||
|
|
||||||
|
@ -36,17 +39,19 @@ open class SwipeActions(dragDirs: Int, private val fragment: Fragment, private v
|
||||||
private val itemTouchHelper = ItemTouchHelper(this)
|
private val itemTouchHelper = ItemTouchHelper(this)
|
||||||
|
|
||||||
init {
|
init {
|
||||||
reloadPreference()
|
actions = getPrefs(fragment.requireContext(), tag)
|
||||||
fragment.lifecycle.addObserver(this)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor(fragment: Fragment, tag: String) : this(0, fragment, tag)
|
constructor(fragment: Fragment, tag: String) : this(0, fragment, tag)
|
||||||
|
|
||||||
@OnLifecycleEvent(Lifecycle.Event.ON_START)
|
override fun onStart(owner: LifecycleOwner) {
|
||||||
fun reloadPreference() {
|
|
||||||
actions = getPrefs(fragment.requireContext(), tag)
|
actions = getPrefs(fragment.requireContext(), tag)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun onStop(owner: LifecycleOwner) {
|
||||||
|
actions = null
|
||||||
|
}
|
||||||
|
|
||||||
fun setFilter(filter: FeedItemFilter?) {
|
fun setFilter(filter: FeedItemFilter?) {
|
||||||
this.filter = filter
|
this.filter = filter
|
||||||
}
|
}
|
||||||
|
@ -69,19 +74,10 @@ open class SwipeActions(dragDirs: Int, private val fragment: Fragment, private v
|
||||||
|
|
||||||
@UnstableApi override fun onSwiped(viewHolder: RecyclerView.ViewHolder, swipeDir: Int) {
|
@UnstableApi override fun onSwiped(viewHolder: RecyclerView.ViewHolder, swipeDir: Int) {
|
||||||
if (actions != null && !actions!!.hasActions()) {
|
if (actions != null && !actions!!.hasActions()) {
|
||||||
//open settings dialog if no prefs are set
|
|
||||||
showDialog()
|
showDialog()
|
||||||
// SwipeActionsDialog(fragment.requireContext(), tag).show(object : SwipeActionsDialog.Callback {
|
|
||||||
// override fun onCall() {
|
|
||||||
// this@SwipeActions.reloadPreference()
|
|
||||||
// EventBus.getDefault().post(SwipeActionsChangedEvent())
|
|
||||||
// }
|
|
||||||
// })
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
val item = (viewHolder as EpisodeItemViewHolder).feedItem
|
val item = (viewHolder as EpisodeItemViewHolder).feedItem
|
||||||
|
|
||||||
if (actions != null && item != null && filter != null)
|
if (actions != null && item != null && filter != null)
|
||||||
(if (swipeDir == ItemTouchHelper.RIGHT) actions!!.right else actions!!.left)?.performAction(item, fragment, filter!!)
|
(if (swipeDir == ItemTouchHelper.RIGHT) actions!!.right else actions!!.left)?.performAction(item, fragment, filter!!)
|
||||||
}
|
}
|
||||||
|
@ -89,7 +85,7 @@ open class SwipeActions(dragDirs: Int, private val fragment: Fragment, private v
|
||||||
fun showDialog() {
|
fun showDialog() {
|
||||||
SwipeActionsDialog(fragment.requireContext(), tag).show(object : SwipeActionsDialog.Callback {
|
SwipeActionsDialog(fragment.requireContext(), tag).show(object : SwipeActionsDialog.Callback {
|
||||||
override fun onCall() {
|
override fun onCall() {
|
||||||
this@SwipeActions.reloadPreference()
|
actions = getPrefs(fragment.requireContext(), tag)
|
||||||
EventFlow.postEvent(FlowEvent.SwipeActionsChangedEvent())
|
EventFlow.postEvent(FlowEvent.SwipeActionsChangedEvent())
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@ -124,12 +120,10 @@ open class SwipeActions(dragDirs: Int, private val fragment: Fragment, private v
|
||||||
|
|
||||||
if (actionState == ItemTouchHelper.ACTION_STATE_SWIPE && wontLeave) {
|
if (actionState == ItemTouchHelper.ACTION_STATE_SWIPE && wontLeave) {
|
||||||
swipeOutEnabled = false
|
swipeOutEnabled = false
|
||||||
|
|
||||||
val swipeThresholdReached = displacementPercentage == 1f
|
val swipeThresholdReached = displacementPercentage == 1f
|
||||||
|
|
||||||
// Move slower when getting near the maxMovement
|
// Move slower when getting near the maxMovement
|
||||||
dx = sign * maxMovement * sin((Math.PI / 2) * displacementPercentage)
|
dx = sign * maxMovement * sin((Math.PI / 2) * displacementPercentage).toFloat()
|
||||||
.toFloat()
|
|
||||||
|
|
||||||
if (isCurrentlyActive) {
|
if (isCurrentlyActive) {
|
||||||
val dir = if (dx > 0) ItemTouchHelper.RIGHT else ItemTouchHelper.LEFT
|
val dir = if (dx > 0) ItemTouchHelper.RIGHT else ItemTouchHelper.LEFT
|
||||||
|
@ -202,10 +196,16 @@ open class SwipeActions(dragDirs: Int, private val fragment: Fragment, private v
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
const val PREF_NAME: String = "SwipeActionsPrefs"
|
const val SWIPE_ACTIONS_PREF_NAME: String = "SwipeActionsPrefs"
|
||||||
const val KEY_PREFIX_SWIPEACTIONS: String = "PrefSwipeActions"
|
const val KEY_PREFIX_SWIPEACTIONS: String = "PrefSwipeActions"
|
||||||
const val KEY_PREFIX_NO_ACTION: String = "PrefNoSwipeAction"
|
const val KEY_PREFIX_NO_ACTION: String = "PrefNoSwipeAction"
|
||||||
|
|
||||||
|
var prefs: SharedPreferences? = null
|
||||||
|
|
||||||
|
fun getSharedPrefs(context: Context) {
|
||||||
|
if (prefs == null) prefs = context.getSharedPreferences(SWIPE_ACTIONS_PREF_NAME, Context.MODE_PRIVATE)
|
||||||
|
}
|
||||||
|
|
||||||
@JvmField
|
@JvmField
|
||||||
val swipeActions: List<SwipeAction> = Collections.unmodifiableList(
|
val swipeActions: List<SwipeAction> = Collections.unmodifiableList(
|
||||||
listOf(NoActionSwipeAction(), AddToQueueSwipeAction(), StartDownloadSwipeAction(), MarkFavoriteSwipeAction(),
|
listOf(NoActionSwipeAction(), AddToQueueSwipeAction(), StartDownloadSwipeAction(), MarkFavoriteSwipeAction(),
|
||||||
|
@ -214,9 +214,7 @@ open class SwipeActions(dragDirs: Int, private val fragment: Fragment, private v
|
||||||
)
|
)
|
||||||
|
|
||||||
private fun getPrefs(context: Context, tag: String, defaultActions: String): Actions {
|
private fun getPrefs(context: Context, tag: String, defaultActions: String): Actions {
|
||||||
val prefs = context.getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE)
|
val prefsString = prefs!!.getString(KEY_PREFIX_SWIPEACTIONS + tag, defaultActions)
|
||||||
val prefsString = prefs.getString(KEY_PREFIX_SWIPEACTIONS + tag, defaultActions)
|
|
||||||
|
|
||||||
return Actions(prefsString)
|
return Actions(prefsString)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -224,12 +222,12 @@ open class SwipeActions(dragDirs: Int, private val fragment: Fragment, private v
|
||||||
return getPrefs(context, tag, "")
|
return getPrefs(context, tag, "")
|
||||||
}
|
}
|
||||||
|
|
||||||
@JvmStatic
|
@OptIn(UnstableApi::class) @JvmStatic
|
||||||
fun getPrefsWithDefaults(context: Context, tag: String): Actions {
|
fun getPrefsWithDefaults(context: Context, tag: String): Actions {
|
||||||
val defaultActions = when (tag) {
|
val defaultActions = when (tag) {
|
||||||
QueueFragment.TAG -> SwipeAction.NO_ACTION + "," + SwipeAction.NO_ACTION
|
QueueFragment.TAG -> SwipeAction.NO_ACTION + "," + SwipeAction.NO_ACTION
|
||||||
DownloadsFragment.TAG -> SwipeAction.NO_ACTION + "," + SwipeAction.NO_ACTION
|
DownloadsFragment.TAG -> SwipeAction.NO_ACTION + "," + SwipeAction.NO_ACTION
|
||||||
PlaybackHistoryFragment.TAG -> SwipeAction.NO_ACTION + "," + SwipeAction.NO_ACTION
|
HistoryFragment.TAG -> SwipeAction.NO_ACTION + "," + SwipeAction.NO_ACTION
|
||||||
AllEpisodesFragment.TAG -> SwipeAction.NO_ACTION + "," + SwipeAction.NO_ACTION
|
AllEpisodesFragment.TAG -> SwipeAction.NO_ACTION + "," + SwipeAction.NO_ACTION
|
||||||
else -> SwipeAction.NO_ACTION + "," + SwipeAction.NO_ACTION
|
else -> SwipeAction.NO_ACTION + "," + SwipeAction.NO_ACTION
|
||||||
}
|
}
|
||||||
|
@ -238,8 +236,7 @@ open class SwipeActions(dragDirs: Int, private val fragment: Fragment, private v
|
||||||
|
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun isSwipeActionEnabled(context: Context, tag: String): Boolean {
|
fun isSwipeActionEnabled(context: Context, tag: String): Boolean {
|
||||||
val prefs = context.getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE)
|
return prefs!!.getBoolean(KEY_PREFIX_NO_ACTION + tag, true)
|
||||||
return prefs.getBoolean(KEY_PREFIX_NO_ACTION + tag, true)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,6 +3,7 @@ package ac.mdiq.podcini.ui.activity
|
||||||
import ac.mdiq.podcini.BuildConfig
|
import ac.mdiq.podcini.BuildConfig
|
||||||
import ac.mdiq.podcini.R
|
import ac.mdiq.podcini.R
|
||||||
import ac.mdiq.podcini.databinding.MainActivityBinding
|
import ac.mdiq.podcini.databinding.MainActivityBinding
|
||||||
|
import ac.mdiq.podcini.net.discovery.ItunesTopListLoader
|
||||||
import ac.mdiq.podcini.net.download.FeedUpdateManager
|
import ac.mdiq.podcini.net.download.FeedUpdateManager
|
||||||
import ac.mdiq.podcini.net.download.FeedUpdateManager.restartUpdateAlarm
|
import ac.mdiq.podcini.net.download.FeedUpdateManager.restartUpdateAlarm
|
||||||
import ac.mdiq.podcini.net.download.FeedUpdateManager.runOnceOrAsk
|
import ac.mdiq.podcini.net.download.FeedUpdateManager.runOnceOrAsk
|
||||||
|
@ -16,14 +17,19 @@ import ac.mdiq.podcini.preferences.UserPreferences.backButtonOpensDrawer
|
||||||
import ac.mdiq.podcini.preferences.UserPreferences.defaultPage
|
import ac.mdiq.podcini.preferences.UserPreferences.defaultPage
|
||||||
import ac.mdiq.podcini.preferences.UserPreferences.hiddenDrawerItems
|
import ac.mdiq.podcini.preferences.UserPreferences.hiddenDrawerItems
|
||||||
import ac.mdiq.podcini.receiver.MediaButtonReceiver.Companion.createIntent
|
import ac.mdiq.podcini.receiver.MediaButtonReceiver.Companion.createIntent
|
||||||
|
import ac.mdiq.podcini.receiver.PlayerWidget
|
||||||
import ac.mdiq.podcini.storage.DBReader
|
import ac.mdiq.podcini.storage.DBReader
|
||||||
|
import ac.mdiq.podcini.storage.DBWriter.ioScope
|
||||||
import ac.mdiq.podcini.storage.model.download.DownloadStatus
|
import ac.mdiq.podcini.storage.model.download.DownloadStatus
|
||||||
|
import ac.mdiq.podcini.ui.actions.swipeactions.SwipeActions
|
||||||
import ac.mdiq.podcini.ui.activity.appstartintent.MainActivityStarter
|
import ac.mdiq.podcini.ui.activity.appstartintent.MainActivityStarter
|
||||||
import ac.mdiq.podcini.ui.dialog.RatingDialog
|
import ac.mdiq.podcini.ui.dialog.RatingDialog
|
||||||
import ac.mdiq.podcini.ui.fragment.*
|
import ac.mdiq.podcini.ui.fragment.*
|
||||||
import ac.mdiq.podcini.ui.statistics.StatisticsFragment
|
import ac.mdiq.podcini.ui.statistics.StatisticsFragment
|
||||||
import ac.mdiq.podcini.ui.utils.ThemeUtils.getDrawableFromAttr
|
import ac.mdiq.podcini.ui.utils.ThemeUtils.getDrawableFromAttr
|
||||||
import ac.mdiq.podcini.ui.view.LockableBottomSheetBehavior
|
import ac.mdiq.podcini.ui.utils.TransitionEffect
|
||||||
|
import ac.mdiq.podcini.ui.view.EpisodeItemListRecyclerView
|
||||||
|
import ac.mdiq.podcini.ui.utils.LockableBottomSheetBehavior
|
||||||
import ac.mdiq.podcini.util.Logd
|
import ac.mdiq.podcini.util.Logd
|
||||||
import ac.mdiq.podcini.util.event.EventFlow
|
import ac.mdiq.podcini.util.event.EventFlow
|
||||||
import ac.mdiq.podcini.util.event.FlowEvent
|
import ac.mdiq.podcini.util.event.FlowEvent
|
||||||
|
@ -110,7 +116,18 @@ class MainActivity : CastEnabledActivity() {
|
||||||
StrictMode.setThreadPolicy(builder.build())
|
StrictMode.setThreadPolicy(builder.build())
|
||||||
}
|
}
|
||||||
|
|
||||||
DBReader.updateFeedList()
|
ioScope.launch {
|
||||||
|
NavDrawerFragment.getSharedPrefs(this@MainActivity)
|
||||||
|
SwipeActions.getSharedPrefs(this@MainActivity)
|
||||||
|
QueueFragment.getSharedPrefs(this@MainActivity)
|
||||||
|
DBReader.updateFeedList()
|
||||||
|
EpisodeItemListRecyclerView.getSharedPrefs(this@MainActivity)
|
||||||
|
PlayerDetailsFragment.getSharedPrefs(this@MainActivity)
|
||||||
|
PlayerWidget.getSharedPrefs(this@MainActivity)
|
||||||
|
StatisticsFragment.getSharedPrefs(this@MainActivity)
|
||||||
|
OnlineFeedViewFragment.getSharedPrefs(this@MainActivity)
|
||||||
|
ItunesTopListLoader.getSharedPrefs(this@MainActivity)
|
||||||
|
}
|
||||||
|
|
||||||
if (savedInstanceState != null) ensureGeneratedViewIdGreaterThan(savedInstanceState.getInt(KEY_GENERATED_VIEW_ID, 0))
|
if (savedInstanceState != null) ensureGeneratedViewIdGreaterThan(savedInstanceState.getInt(KEY_GENERATED_VIEW_ID, 0))
|
||||||
|
|
||||||
|
@ -145,9 +162,8 @@ class MainActivity : CastEnabledActivity() {
|
||||||
|
|
||||||
val fm = supportFragmentManager
|
val fm = supportFragmentManager
|
||||||
if (fm.findFragmentByTag(MAIN_FRAGMENT_TAG) == null) {
|
if (fm.findFragmentByTag(MAIN_FRAGMENT_TAG) == null) {
|
||||||
if (UserPreferences.DEFAULT_PAGE_REMEMBER != defaultPage) {
|
if (UserPreferences.DEFAULT_PAGE_REMEMBER != defaultPage) loadFragment(defaultPage, null)
|
||||||
loadFragment(defaultPage, null)
|
else {
|
||||||
} else {
|
|
||||||
val lastFragment = NavDrawerFragment.getLastNavFragment(this)
|
val lastFragment = NavDrawerFragment.getLastNavFragment(this)
|
||||||
if (ArrayUtils.contains(NavDrawerFragment.NAV_DRAWER_TAGS, lastFragment)) {
|
if (ArrayUtils.contains(NavDrawerFragment.NAV_DRAWER_TAGS, lastFragment)) {
|
||||||
loadFragment(lastFragment, null)
|
loadFragment(lastFragment, null)
|
||||||
|
@ -173,14 +189,15 @@ class MainActivity : CastEnabledActivity() {
|
||||||
navDrawer = findViewById(R.id.navDrawerFragment)
|
navDrawer = findViewById(R.id.navDrawerFragment)
|
||||||
audioPlayerFragmentView = findViewById(R.id.audioplayerFragment)
|
audioPlayerFragmentView = findViewById(R.id.audioplayerFragment)
|
||||||
|
|
||||||
checkFirstLaunch()
|
ioScope.launch { checkFirstLaunch() }
|
||||||
|
|
||||||
this.bottomSheet = BottomSheetBehavior.from(audioPlayerFragmentView) as LockableBottomSheetBehavior<*>
|
this.bottomSheet = BottomSheetBehavior.from(audioPlayerFragmentView) as LockableBottomSheetBehavior<*>
|
||||||
this.bottomSheet.isHideable = false
|
this.bottomSheet.isHideable = false
|
||||||
this.bottomSheet.isDraggable = false
|
this.bottomSheet.isDraggable = false
|
||||||
this.bottomSheet.setBottomSheetCallback(bottomSheetCallback)
|
this.bottomSheet.setBottomSheetCallback(bottomSheetCallback)
|
||||||
|
|
||||||
restartUpdateAlarm(this, false)
|
restartUpdateAlarm(this, false)
|
||||||
SynchronizationQueueSink.syncNowIfNotSyncedRecently()
|
ioScope.launch { SynchronizationQueueSink.syncNowIfNotSyncedRecently() }
|
||||||
|
|
||||||
WorkManager.getInstance(this)
|
WorkManager.getInstance(this)
|
||||||
.getWorkInfosByTagLiveData(FeedUpdateManager.WORK_TAG_FEED_UPDATE)
|
.getWorkInfosByTagLiveData(FeedUpdateManager.WORK_TAG_FEED_UPDATE)
|
||||||
|
@ -376,7 +393,7 @@ class MainActivity : CastEnabledActivity() {
|
||||||
QueueFragment.TAG -> fragment = QueueFragment()
|
QueueFragment.TAG -> fragment = QueueFragment()
|
||||||
AllEpisodesFragment.TAG -> fragment = AllEpisodesFragment()
|
AllEpisodesFragment.TAG -> fragment = AllEpisodesFragment()
|
||||||
DownloadsFragment.TAG -> fragment = DownloadsFragment()
|
DownloadsFragment.TAG -> fragment = DownloadsFragment()
|
||||||
PlaybackHistoryFragment.TAG -> fragment = PlaybackHistoryFragment()
|
HistoryFragment.TAG -> fragment = HistoryFragment()
|
||||||
AddFeedFragment.TAG -> fragment = AddFeedFragment()
|
AddFeedFragment.TAG -> fragment = AddFeedFragment()
|
||||||
SubscriptionFragment.TAG -> fragment = SubscriptionFragment()
|
SubscriptionFragment.TAG -> fragment = SubscriptionFragment()
|
||||||
StatisticsFragment.TAG -> fragment = StatisticsFragment()
|
StatisticsFragment.TAG -> fragment = StatisticsFragment()
|
||||||
|
@ -389,7 +406,7 @@ class MainActivity : CastEnabledActivity() {
|
||||||
}
|
}
|
||||||
if (args != null) fragment.arguments = args
|
if (args != null) fragment.arguments = args
|
||||||
|
|
||||||
NavDrawerFragment.saveLastNavFragment(this, tag)
|
ioScope.launch { NavDrawerFragment.saveLastNavFragment(this@MainActivity, tag) }
|
||||||
loadFragment(fragment)
|
loadFragment(fragment)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -560,6 +577,7 @@ class MainActivity : CastEnabledActivity() {
|
||||||
private fun procFlowEvents() {
|
private fun procFlowEvents() {
|
||||||
lifecycleScope.launch {
|
lifecycleScope.launch {
|
||||||
EventFlow.events.collectLatest { event ->
|
EventFlow.events.collectLatest { event ->
|
||||||
|
Logd(TAG, "Received event: $event")
|
||||||
when (event) {
|
when (event) {
|
||||||
is FlowEvent.MessageEvent -> onEventMainThread(event)
|
is FlowEvent.MessageEvent -> onEventMainThread(event)
|
||||||
else -> {}
|
else -> {}
|
||||||
|
@ -660,7 +678,7 @@ class MainActivity : CastEnabledActivity() {
|
||||||
val feature = uri.getQueryParameter("page") ?: return
|
val feature = uri.getQueryParameter("page") ?: return
|
||||||
when (feature) {
|
when (feature) {
|
||||||
"DOWNLOADS" -> loadFragment(DownloadsFragment.TAG, null)
|
"DOWNLOADS" -> loadFragment(DownloadsFragment.TAG, null)
|
||||||
"HISTORY" -> loadFragment(PlaybackHistoryFragment.TAG, null)
|
"HISTORY" -> loadFragment(HistoryFragment.TAG, null)
|
||||||
"EPISODES" -> loadFragment(AllEpisodesFragment.TAG, null)
|
"EPISODES" -> loadFragment(AllEpisodesFragment.TAG, null)
|
||||||
"QUEUE" -> loadFragment(QueueFragment.TAG, null)
|
"QUEUE" -> loadFragment(QueueFragment.TAG, null)
|
||||||
"SUBSCRIPTIONS" -> loadFragment(SubscriptionFragment.TAG, null)
|
"SUBSCRIPTIONS" -> loadFragment(SubscriptionFragment.TAG, null)
|
||||||
|
|
|
@ -161,6 +161,7 @@ class PreferenceActivity : AppCompatActivity(), SearchPreferenceResultListener {
|
||||||
private fun procFlowEvents() {
|
private fun procFlowEvents() {
|
||||||
lifecycleScope.launch {
|
lifecycleScope.launch {
|
||||||
EventFlow.events.collectLatest { event ->
|
EventFlow.events.collectLatest { event ->
|
||||||
|
Logd("PreferenceActivity", "Received event: ${event}")
|
||||||
when (event) {
|
when (event) {
|
||||||
is FlowEvent.MessageEvent -> onEventMainThread(event)
|
is FlowEvent.MessageEvent -> onEventMainThread(event)
|
||||||
else -> {}
|
else -> {}
|
||||||
|
|
|
@ -13,6 +13,8 @@ import ac.mdiq.podcini.storage.model.feed.FeedItem
|
||||||
import ac.mdiq.podcini.storage.model.feed.FeedMedia
|
import ac.mdiq.podcini.storage.model.feed.FeedMedia
|
||||||
import ac.mdiq.podcini.storage.model.playback.Playable
|
import ac.mdiq.podcini.storage.model.playback.Playable
|
||||||
import ac.mdiq.podcini.ui.dialog.*
|
import ac.mdiq.podcini.ui.dialog.*
|
||||||
|
import ac.mdiq.podcini.ui.fragment.AudioPlayerFragment.InternalPlayerFragment
|
||||||
|
import ac.mdiq.podcini.ui.fragment.AudioPlayerFragment.InternalPlayerFragment.Companion
|
||||||
import ac.mdiq.podcini.ui.fragment.ChaptersFragment
|
import ac.mdiq.podcini.ui.fragment.ChaptersFragment
|
||||||
import ac.mdiq.podcini.ui.fragment.VideoEpisodeFragment
|
import ac.mdiq.podcini.ui.fragment.VideoEpisodeFragment
|
||||||
import ac.mdiq.podcini.ui.utils.PictureInPictureUtil
|
import ac.mdiq.podcini.ui.utils.PictureInPictureUtil
|
||||||
|
@ -169,6 +171,7 @@ class VideoplayerActivity : CastEnabledActivity() {
|
||||||
private fun procFlowEvents() {
|
private fun procFlowEvents() {
|
||||||
lifecycleScope.launch {
|
lifecycleScope.launch {
|
||||||
EventFlow.events.collectLatest { event ->
|
EventFlow.events.collectLatest { event ->
|
||||||
|
Logd(TAG, "Received event: $event")
|
||||||
when (event) {
|
when (event) {
|
||||||
is FlowEvent.SleepTimerUpdatedEvent -> if (event.isCancelled || event.wasJustEnabled()) supportInvalidateOptionsMenu()
|
is FlowEvent.SleepTimerUpdatedEvent -> if (event.isCancelled || event.wasJustEnabled()) supportInvalidateOptionsMenu()
|
||||||
is FlowEvent.PlaybackServiceEvent -> if (event.action == FlowEvent.PlaybackServiceEvent.Action.SERVICE_SHUT_DOWN) finish()
|
is FlowEvent.PlaybackServiceEvent -> if (event.action == FlowEvent.PlaybackServiceEvent.Action.SERVICE_SHUT_DOWN) finish()
|
||||||
|
|
|
@ -16,6 +16,7 @@ import ac.mdiq.podcini.databinding.ActivityWidgetConfigBinding
|
||||||
import ac.mdiq.podcini.databinding.PlayerWidgetBinding
|
import ac.mdiq.podcini.databinding.PlayerWidgetBinding
|
||||||
import ac.mdiq.podcini.preferences.ThemeSwitcher.getTheme
|
import ac.mdiq.podcini.preferences.ThemeSwitcher.getTheme
|
||||||
import ac.mdiq.podcini.receiver.PlayerWidget
|
import ac.mdiq.podcini.receiver.PlayerWidget
|
||||||
|
import ac.mdiq.podcini.receiver.PlayerWidget.Companion.prefs
|
||||||
import ac.mdiq.podcini.ui.widget.WidgetUpdaterWorker
|
import ac.mdiq.podcini.ui.widget.WidgetUpdaterWorker
|
||||||
|
|
||||||
class WidgetConfigActivity : AppCompatActivity() {
|
class WidgetConfigActivity : AppCompatActivity() {
|
||||||
|
@ -95,13 +96,13 @@ class WidgetConfigActivity : AppCompatActivity() {
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun setInitialState() {
|
private fun setInitialState() {
|
||||||
val prefs = getSharedPreferences(PlayerWidget.PREFS_NAME, MODE_PRIVATE)
|
// val prefs = getSharedPreferences(PlayerWidget.PREFS_NAME, MODE_PRIVATE)
|
||||||
ckPlaybackSpeed.isChecked = prefs.getBoolean(PlayerWidget.KEY_WIDGET_PLAYBACK_SPEED + appWidgetId, true)
|
ckPlaybackSpeed.isChecked = prefs!!.getBoolean(PlayerWidget.KEY_WIDGET_PLAYBACK_SPEED + appWidgetId, true)
|
||||||
ckRewind.isChecked = prefs.getBoolean(PlayerWidget.KEY_WIDGET_REWIND + appWidgetId, true)
|
ckRewind.isChecked = prefs!!.getBoolean(PlayerWidget.KEY_WIDGET_REWIND + appWidgetId, true)
|
||||||
ckFastForward.isChecked = prefs.getBoolean(PlayerWidget.KEY_WIDGET_FAST_FORWARD + appWidgetId, true)
|
ckFastForward.isChecked = prefs!!.getBoolean(PlayerWidget.KEY_WIDGET_FAST_FORWARD + appWidgetId, true)
|
||||||
ckSkip.isChecked = prefs.getBoolean(PlayerWidget.KEY_WIDGET_SKIP + appWidgetId, true)
|
ckSkip.isChecked = prefs!!.getBoolean(PlayerWidget.KEY_WIDGET_SKIP + appWidgetId, true)
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
|
||||||
val color = prefs.getInt(PlayerWidget.KEY_WIDGET_COLOR + appWidgetId, PlayerWidget.DEFAULT_COLOR)
|
val color = prefs!!.getInt(PlayerWidget.KEY_WIDGET_COLOR + appWidgetId, PlayerWidget.DEFAULT_COLOR)
|
||||||
val opacity = Color.alpha(color) * 100 / 0xFF
|
val opacity = Color.alpha(color) * 100 / 0xFF
|
||||||
|
|
||||||
opacitySeekBar.setProgress(opacity, false)
|
opacitySeekBar.setProgress(opacity, false)
|
||||||
|
@ -122,8 +123,8 @@ class WidgetConfigActivity : AppCompatActivity() {
|
||||||
private fun confirmCreateWidget() {
|
private fun confirmCreateWidget() {
|
||||||
val backgroundColor = getColorWithAlpha(PlayerWidget.DEFAULT_COLOR, opacitySeekBar.progress)
|
val backgroundColor = getColorWithAlpha(PlayerWidget.DEFAULT_COLOR, opacitySeekBar.progress)
|
||||||
|
|
||||||
val prefs = getSharedPreferences(PlayerWidget.PREFS_NAME, MODE_PRIVATE)
|
// val prefs = getSharedPreferences(PlayerWidget.PREFS_NAME, MODE_PRIVATE)
|
||||||
val editor = prefs.edit()
|
val editor = prefs!!.edit()
|
||||||
editor.putInt(PlayerWidget.KEY_WIDGET_COLOR + appWidgetId, backgroundColor)
|
editor.putInt(PlayerWidget.KEY_WIDGET_COLOR + appWidgetId, backgroundColor)
|
||||||
editor.putBoolean(PlayerWidget.KEY_WIDGET_PLAYBACK_SPEED + appWidgetId, ckPlaybackSpeed.isChecked)
|
editor.putBoolean(PlayerWidget.KEY_WIDGET_PLAYBACK_SPEED + appWidgetId, ckPlaybackSpeed.isChecked)
|
||||||
editor.putBoolean(PlayerWidget.KEY_WIDGET_SKIP + appWidgetId, ckSkip.isChecked)
|
editor.putBoolean(PlayerWidget.KEY_WIDGET_SKIP + appWidgetId, ckSkip.isChecked)
|
||||||
|
|
|
@ -7,6 +7,7 @@ import ac.mdiq.podcini.ui.activity.MainActivity
|
||||||
import ac.mdiq.podcini.ui.fragment.EpisodeInfoFragment
|
import ac.mdiq.podcini.ui.fragment.EpisodeInfoFragment
|
||||||
import ac.mdiq.podcini.ui.utils.ThemeUtils
|
import ac.mdiq.podcini.ui.utils.ThemeUtils
|
||||||
import ac.mdiq.podcini.ui.view.viewholder.EpisodeItemViewHolder
|
import ac.mdiq.podcini.ui.view.viewholder.EpisodeItemViewHolder
|
||||||
|
import ac.mdiq.podcini.util.Logd
|
||||||
import android.R.color
|
import android.R.color
|
||||||
import android.app.Activity
|
import android.app.Activity
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
|
@ -18,9 +19,10 @@ import java.lang.ref.WeakReference
|
||||||
/**
|
/**
|
||||||
* List adapter for the list of new episodes.
|
* List adapter for the list of new episodes.
|
||||||
*/
|
*/
|
||||||
open class EpisodeItemListAdapter(mainActivity: MainActivity) :
|
open class EpisodeItemListAdapter(mainActivity: MainActivity)
|
||||||
SelectableAdapter<EpisodeItemViewHolder?>(mainActivity), View.OnCreateContextMenuListener {
|
: SelectableAdapter<EpisodeItemViewHolder?>(mainActivity), View.OnCreateContextMenuListener {
|
||||||
|
|
||||||
|
val TAG = "EpisodeItemListAdapter"
|
||||||
val mainActivityRef: WeakReference<MainActivity> = WeakReference<MainActivity>(mainActivity)
|
val mainActivityRef: WeakReference<MainActivity> = WeakReference<MainActivity>(mainActivity)
|
||||||
|
|
||||||
private var episodes: List<FeedItem> = ArrayList()
|
private var episodes: List<FeedItem> = ArrayList()
|
||||||
|
@ -49,6 +51,8 @@ open class EpisodeItemListAdapter(mainActivity: MainActivity) :
|
||||||
}
|
}
|
||||||
|
|
||||||
@UnstableApi override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): EpisodeItemViewHolder {
|
@UnstableApi override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): EpisodeItemViewHolder {
|
||||||
|
// TODO: the Invalid resource ID 0x00000000 on Android 14 occurs after this and before onBindViewHolder,
|
||||||
|
// somehow, only on the first time EpisodeItemListAdapter is called
|
||||||
return EpisodeItemViewHolder(mainActivityRef.get()!!, parent)
|
return EpisodeItemViewHolder(mainActivityRef.get()!!, parent)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -79,24 +83,13 @@ open class EpisodeItemListAdapter(mainActivity: MainActivity) :
|
||||||
}
|
}
|
||||||
holder.infoCard.setOnClickListener {
|
holder.infoCard.setOnClickListener {
|
||||||
val activity: MainActivity? = mainActivityRef.get()
|
val activity: MainActivity? = mainActivityRef.get()
|
||||||
if (!inActionMode()) {
|
if (!inActionMode()) activity?.loadChildFragment(EpisodeInfoFragment.newInstance(episodes[pos]))
|
||||||
// val ids: LongArray = FeedItemUtil.getIds(episodes)
|
else toggleSelection(holder.bindingAdapterPosition)
|
||||||
// val position = ArrayUtils.indexOf(ids, item.id)
|
|
||||||
activity?.loadChildFragment(EpisodeInfoFragment.newInstance(episodes[pos]))
|
|
||||||
} else {
|
|
||||||
toggleSelection(holder.bindingAdapterPosition)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
holder.coverHolder.setOnClickListener {
|
holder.coverHolder.setOnClickListener {
|
||||||
val activity: MainActivity? = mainActivityRef.get()
|
val activity: MainActivity? = mainActivityRef.get()
|
||||||
if (!inActionMode()) {
|
if (!inActionMode()) activity?.loadChildFragment(EpisodeInfoFragment.newInstance(episodes[pos]))
|
||||||
// val ids: LongArray = FeedItemUtil.getIds(episodes)
|
else toggleSelection(holder.bindingAdapterPosition)
|
||||||
// val position = ArrayUtils.indexOf(ids, item.id)
|
|
||||||
activity?.loadChildFragment(EpisodeInfoFragment.newInstance(episodes[pos]))
|
|
||||||
} else {
|
|
||||||
toggleSelection(holder.bindingAdapterPosition)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
holder.itemView.setOnTouchListener(View.OnTouchListener { _: View?, e: MotionEvent ->
|
holder.itemView.setOnTouchListener(View.OnTouchListener { _: View?, e: MotionEvent ->
|
||||||
if (e.isFromSource(InputDevice.SOURCE_MOUSE) && e.buttonState == MotionEvent.BUTTON_SECONDARY) {
|
if (e.isFromSource(InputDevice.SOURCE_MOUSE) && e.buttonState == MotionEvent.BUTTON_SECONDARY) {
|
||||||
|
@ -106,12 +99,11 @@ open class EpisodeItemListAdapter(mainActivity: MainActivity) :
|
||||||
}
|
}
|
||||||
false
|
false
|
||||||
})
|
})
|
||||||
|
|
||||||
if (inActionMode()) {
|
if (inActionMode()) {
|
||||||
holder.secondaryActionButton.setOnClickListener(null)
|
holder.secondaryActionButton.setOnClickListener(null)
|
||||||
if (isSelected(pos)) {
|
if (isSelected(pos))
|
||||||
holder.itemView.setBackgroundColor(-0x78000000 + (0xffffff and ThemeUtils.getColorFromAttr(mainActivityRef.get()!!, R.attr.colorAccent)))
|
holder.itemView.setBackgroundColor(-0x78000000 + (0xffffff and ThemeUtils.getColorFromAttr(mainActivityRef.get()!!, R.attr.colorAccent)))
|
||||||
} else holder.itemView.setBackgroundResource(color.transparent)
|
else holder.itemView.setBackgroundResource(color.transparent)
|
||||||
}
|
}
|
||||||
|
|
||||||
afterBindViewHolder(holder, pos)
|
afterBindViewHolder(holder, pos)
|
||||||
|
|
|
@ -4,13 +4,14 @@ import ac.mdiq.podcini.R
|
||||||
import ac.mdiq.podcini.databinding.NavListitemBinding
|
import ac.mdiq.podcini.databinding.NavListitemBinding
|
||||||
import ac.mdiq.podcini.databinding.NavSectionItemBinding
|
import ac.mdiq.podcini.databinding.NavSectionItemBinding
|
||||||
import ac.mdiq.podcini.preferences.UserPreferences
|
import ac.mdiq.podcini.preferences.UserPreferences
|
||||||
|
import ac.mdiq.podcini.preferences.UserPreferences.appPrefs
|
||||||
import ac.mdiq.podcini.preferences.UserPreferences.episodeCacheSize
|
import ac.mdiq.podcini.preferences.UserPreferences.episodeCacheSize
|
||||||
import ac.mdiq.podcini.preferences.UserPreferences.hiddenDrawerItems
|
import ac.mdiq.podcini.preferences.UserPreferences.hiddenDrawerItems
|
||||||
|
import ac.mdiq.podcini.storage.DBWriter.ioScope
|
||||||
import ac.mdiq.podcini.storage.NavDrawerData.FeedDrawerItem
|
import ac.mdiq.podcini.storage.NavDrawerData.FeedDrawerItem
|
||||||
import ac.mdiq.podcini.ui.activity.PreferenceActivity
|
import ac.mdiq.podcini.ui.activity.PreferenceActivity
|
||||||
import ac.mdiq.podcini.ui.fragment.*
|
import ac.mdiq.podcini.ui.fragment.*
|
||||||
import ac.mdiq.podcini.ui.statistics.StatisticsFragment
|
import ac.mdiq.podcini.ui.statistics.StatisticsFragment
|
||||||
import ac.mdiq.podcini.util.Logd
|
|
||||||
import android.app.Activity
|
import android.app.Activity
|
||||||
import android.content.DialogInterface
|
import android.content.DialogInterface
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
|
@ -28,6 +29,9 @@ import androidx.media3.common.util.UnstableApi
|
||||||
import androidx.preference.PreferenceManager
|
import androidx.preference.PreferenceManager
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import kotlinx.coroutines.withContext
|
||||||
import org.apache.commons.lang3.ArrayUtils
|
import org.apache.commons.lang3.ArrayUtils
|
||||||
import java.lang.ref.WeakReference
|
import java.lang.ref.WeakReference
|
||||||
import java.text.NumberFormat
|
import java.text.NumberFormat
|
||||||
|
@ -49,9 +53,7 @@ class NavListAdapter(private val itemAccess: ItemAccess, context: Activity) :
|
||||||
|
|
||||||
init {
|
init {
|
||||||
loadItems()
|
loadItems()
|
||||||
|
appPrefs.registerOnSharedPreferenceChangeListener(this@NavListAdapter)
|
||||||
val prefs = PreferenceManager.getDefaultSharedPreferences(context)
|
|
||||||
prefs.registerOnSharedPreferenceChangeListener(this)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences, key: String?) {
|
override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences, key: String?) {
|
||||||
|
@ -78,7 +80,7 @@ class NavListAdapter(private val itemAccess: ItemAccess, context: Activity) :
|
||||||
QueueFragment.TAG -> R.drawable.ic_playlist_play
|
QueueFragment.TAG -> R.drawable.ic_playlist_play
|
||||||
AllEpisodesFragment.TAG -> R.drawable.ic_feed
|
AllEpisodesFragment.TAG -> R.drawable.ic_feed
|
||||||
DownloadsFragment.TAG -> R.drawable.ic_download
|
DownloadsFragment.TAG -> R.drawable.ic_download
|
||||||
PlaybackHistoryFragment.TAG -> R.drawable.ic_history
|
HistoryFragment.TAG -> R.drawable.ic_history
|
||||||
SubscriptionFragment.TAG -> R.drawable.ic_subscriptions
|
SubscriptionFragment.TAG -> R.drawable.ic_subscriptions
|
||||||
StatisticsFragment.TAG -> R.drawable.ic_chart_box
|
StatisticsFragment.TAG -> R.drawable.ic_chart_box
|
||||||
AddFeedFragment.TAG -> R.drawable.ic_add
|
AddFeedFragment.TAG -> R.drawable.ic_add
|
||||||
|
@ -200,7 +202,7 @@ class NavListAdapter(private val itemAccess: ItemAccess, context: Activity) :
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Logd("NavListAdapter", "bindNavView getting drawable for: ${fragmentTags[position]}")
|
// Logd("NavListAdapter", "bindNavView getting drawable for: ${fragmentTags[position]}")
|
||||||
holder.image.setImageResource(getDrawable(fragmentTags[position]))
|
holder.image.setImageResource(getDrawable(fragmentTags[position]))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -11,8 +11,7 @@ import android.util.Log
|
||||||
/**
|
/**
|
||||||
* Used by Recyclerviews that need to provide ability to select items.
|
* Used by Recyclerviews that need to provide ability to select items.
|
||||||
*/
|
*/
|
||||||
abstract class SelectableAdapter<T : RecyclerView.ViewHolder?>(private val activity: Activity) :
|
abstract class SelectableAdapter<T : RecyclerView.ViewHolder?>(private val activity: Activity) : RecyclerView.Adapter<T>() {
|
||||||
RecyclerView.Adapter<T>() {
|
|
||||||
|
|
||||||
private var actionMode: ActionMode? = null
|
private var actionMode: ActionMode? = null
|
||||||
private val selectedIds = HashSet<Long>()
|
private val selectedIds = HashSet<Long>()
|
||||||
|
|
|
@ -6,10 +6,10 @@ import ac.mdiq.podcini.storage.NavDrawerData
|
||||||
import ac.mdiq.podcini.storage.model.feed.Feed
|
import ac.mdiq.podcini.storage.model.feed.Feed
|
||||||
import ac.mdiq.podcini.ui.activity.MainActivity
|
import ac.mdiq.podcini.ui.activity.MainActivity
|
||||||
import ac.mdiq.podcini.ui.fragment.FeedItemlistFragment
|
import ac.mdiq.podcini.ui.fragment.FeedItemlistFragment
|
||||||
|
import ac.mdiq.podcini.ui.utils.CoverLoader
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.graphics.Rect
|
import android.graphics.Rect
|
||||||
import android.graphics.drawable.Drawable
|
import android.graphics.drawable.Drawable
|
||||||
import android.os.Build
|
|
||||||
import android.view.*
|
import android.view.*
|
||||||
import android.widget.*
|
import android.widget.*
|
||||||
import androidx.appcompat.content.res.AppCompatResources
|
import androidx.appcompat.content.res.AppCompatResources
|
||||||
|
@ -153,6 +153,7 @@ open class SubscriptionsAdapter(mainActivity: MainActivity)
|
||||||
}
|
}
|
||||||
|
|
||||||
inner class SubscriptionViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
|
inner class SubscriptionViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
|
||||||
|
|
||||||
val binding = SubscriptionItemBinding.bind(itemView)
|
val binding = SubscriptionItemBinding.bind(itemView)
|
||||||
private val title = binding.titleLabel
|
private val title = binding.titleLabel
|
||||||
private val producer = binding.producerLabel
|
private val producer = binding.producerLabel
|
||||||
|
|
|
@ -12,6 +12,7 @@ import ac.mdiq.podcini.net.download.FeedUpdateManager.runOnce
|
||||||
import ac.mdiq.podcini.databinding.EditTextDialogBinding
|
import ac.mdiq.podcini.databinding.EditTextDialogBinding
|
||||||
import ac.mdiq.podcini.storage.model.feed.Feed
|
import ac.mdiq.podcini.storage.model.feed.Feed
|
||||||
import androidx.media3.common.util.UnstableApi
|
import androidx.media3.common.util.UnstableApi
|
||||||
|
import kotlinx.coroutines.runBlocking
|
||||||
import java.lang.ref.WeakReference
|
import java.lang.ref.WeakReference
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import java.util.concurrent.ExecutionException
|
import java.util.concurrent.ExecutionException
|
||||||
|
@ -37,7 +38,7 @@ import java.util.concurrent.ExecutionException
|
||||||
|
|
||||||
@UnstableApi private fun onConfirmed(original: String, updated: String) {
|
@UnstableApi private fun onConfirmed(original: String, updated: String) {
|
||||||
try {
|
try {
|
||||||
DBWriter.updateFeedDownloadURL(original, updated).get()
|
runBlocking { DBWriter.updateFeedDownloadURL(original, updated).join() }
|
||||||
feed.download_url = updated
|
feed.download_url = updated
|
||||||
runOnce(activityRef.get()!!, feed)
|
runOnce(activityRef.get()!!, feed)
|
||||||
} catch (e: ExecutionException) {
|
} catch (e: ExecutionException) {
|
||||||
|
|
|
@ -4,7 +4,7 @@ import ac.mdiq.podcini.R
|
||||||
import ac.mdiq.podcini.databinding.EpisodeFilterDialogBinding
|
import ac.mdiq.podcini.databinding.EpisodeFilterDialogBinding
|
||||||
import ac.mdiq.podcini.storage.model.feed.FeedFilter
|
import ac.mdiq.podcini.storage.model.feed.FeedFilter
|
||||||
import ac.mdiq.podcini.ui.adapter.SimpleChipAdapter
|
import ac.mdiq.podcini.ui.adapter.SimpleChipAdapter
|
||||||
import ac.mdiq.podcini.ui.view.ItemOffsetDecoration
|
import ac.mdiq.podcini.ui.utils.ItemOffsetDecoration
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.DialogInterface
|
import android.content.DialogInterface
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
|
|
|
@ -10,10 +10,8 @@ import android.content.DialogInterface
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import androidx.annotation.OptIn
|
import androidx.annotation.OptIn
|
||||||
import androidx.media3.common.util.UnstableApi
|
import androidx.media3.common.util.UnstableApi
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.*
|
||||||
import kotlinx.coroutines.Dispatchers
|
import java.lang.Runnable
|
||||||
import kotlinx.coroutines.launch
|
|
||||||
import kotlinx.coroutines.withContext
|
|
||||||
|
|
||||||
object RemoveFeedDialog {
|
object RemoveFeedDialog {
|
||||||
private const val TAG = "RemoveFeedDialog"
|
private const val TAG = "RemoveFeedDialog"
|
||||||
|
@ -63,7 +61,7 @@ object RemoveFeedDialog {
|
||||||
try {
|
try {
|
||||||
withContext(Dispatchers.IO) {
|
withContext(Dispatchers.IO) {
|
||||||
for (feed in feeds) {
|
for (feed in feeds) {
|
||||||
DBWriter.deleteFeed(context, feed.id).get()
|
runBlocking { DBWriter.deleteFeed(context, feed.id).join() }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
withContext(Dispatchers.Main) {
|
withContext(Dispatchers.Main) {
|
||||||
|
|
|
@ -17,10 +17,13 @@ import ac.mdiq.podcini.ui.fragment.*
|
||||||
import ac.mdiq.podcini.ui.actions.swipeactions.SwipeAction
|
import ac.mdiq.podcini.ui.actions.swipeactions.SwipeAction
|
||||||
import ac.mdiq.podcini.ui.actions.swipeactions.SwipeActions
|
import ac.mdiq.podcini.ui.actions.swipeactions.SwipeActions
|
||||||
import ac.mdiq.podcini.ui.actions.swipeactions.SwipeActions.Companion.getPrefsWithDefaults
|
import ac.mdiq.podcini.ui.actions.swipeactions.SwipeActions.Companion.getPrefsWithDefaults
|
||||||
|
import ac.mdiq.podcini.ui.actions.swipeactions.SwipeActions.Companion.getSharedPrefs
|
||||||
import ac.mdiq.podcini.ui.actions.swipeactions.SwipeActions.Companion.isSwipeActionEnabled
|
import ac.mdiq.podcini.ui.actions.swipeactions.SwipeActions.Companion.isSwipeActionEnabled
|
||||||
import ac.mdiq.podcini.ui.utils.ThemeUtils.getColorFromAttr
|
import ac.mdiq.podcini.ui.utils.ThemeUtils.getColorFromAttr
|
||||||
|
import androidx.annotation.OptIn
|
||||||
|
import androidx.media3.common.util.UnstableApi
|
||||||
|
|
||||||
class SwipeActionsDialog(private val context: Context, private val tag: String) {
|
@OptIn(UnstableApi::class) class SwipeActionsDialog(private val context: Context, private val tag: String) {
|
||||||
private lateinit var keys: List<SwipeAction>
|
private lateinit var keys: List<SwipeAction>
|
||||||
|
|
||||||
private var rightAction: SwipeAction? = null
|
private var rightAction: SwipeAction? = null
|
||||||
|
@ -55,15 +58,13 @@ class SwipeActionsDialog(private val context: Context, private val tag: String)
|
||||||
keys = Stream.of(keys).filter { a: SwipeAction ->
|
keys = Stream.of(keys).filter { a: SwipeAction ->
|
||||||
(!a.getId().equals(SwipeAction.ADD_TO_QUEUE) && !a.getId().equals(SwipeAction.REMOVE_FROM_HISTORY)) }.toList()
|
(!a.getId().equals(SwipeAction.ADD_TO_QUEUE) && !a.getId().equals(SwipeAction.REMOVE_FROM_HISTORY)) }.toList()
|
||||||
}
|
}
|
||||||
PlaybackHistoryFragment.TAG -> {
|
HistoryFragment.TAG -> {
|
||||||
forFragment = context.getString(R.string.playback_history_label)
|
forFragment = context.getString(R.string.playback_history_label)
|
||||||
keys = Stream.of(keys).toList()
|
keys = Stream.of(keys).toList()
|
||||||
}
|
}
|
||||||
else -> {}
|
else -> {}
|
||||||
}
|
}
|
||||||
if (tag != QueueFragment.TAG) {
|
if (tag != QueueFragment.TAG) keys = Stream.of(keys).filter { a: SwipeAction -> !a.getId().equals(SwipeAction.REMOVE_FROM_QUEUE) }.toList()
|
||||||
keys = Stream.of(keys).filter { a: SwipeAction -> !a.getId().equals(SwipeAction.REMOVE_FROM_QUEUE) }.toList()
|
|
||||||
}
|
|
||||||
|
|
||||||
builder.setTitle(context.getString(R.string.swipeactions_label) + " - " + forFragment)
|
builder.setTitle(context.getString(R.string.swipeactions_label) + " - " + forFragment)
|
||||||
val binding = SwipeactionsDialogBinding.inflate(LayoutInflater.from(context))
|
val binding = SwipeactionsDialogBinding.inflate(LayoutInflater.from(context))
|
||||||
|
@ -127,11 +128,9 @@ class SwipeActionsDialog(private val context: Context, private val tag: String)
|
||||||
if ((direction == LEFT && leftAction === action) || (direction == RIGHT && rightAction === action)) {
|
if ((direction == LEFT && leftAction === action) || (direction == RIGHT && rightAction === action)) {
|
||||||
icon.setTint(getColorFromAttr(context, action.getActionColor()))
|
icon.setTint(getColorFromAttr(context, action.getActionColor()))
|
||||||
item.swipeActionLabel.setTextColor(getColorFromAttr(context, action.getActionColor()))
|
item.swipeActionLabel.setTextColor(getColorFromAttr(context, action.getActionColor()))
|
||||||
} else {
|
} else icon.setTint(getColorFromAttr(context, R.attr.action_icon_color))
|
||||||
icon.setTint(getColorFromAttr(context, R.attr.action_icon_color))
|
|
||||||
}
|
|
||||||
item.swipeIcon.setImageDrawable(icon)
|
|
||||||
|
|
||||||
|
item.swipeIcon.setImageDrawable(icon)
|
||||||
item.root.setOnClickListener {
|
item.root.setOnClickListener {
|
||||||
if (direction == LEFT) leftAction = keys[i]
|
if (direction == LEFT) leftAction = keys[i]
|
||||||
else rightAction = keys[i]
|
else rightAction = keys[i]
|
||||||
|
@ -158,13 +157,13 @@ class SwipeActionsDialog(private val context: Context, private val tag: String)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun savePrefs(tag: String, right: String?, left: String?) {
|
private fun savePrefs(tag: String, right: String?, left: String?) {
|
||||||
val prefs = context.getSharedPreferences(SwipeActions.PREF_NAME, Context.MODE_PRIVATE)
|
getSharedPrefs(context)
|
||||||
prefs.edit().putString(SwipeActions.KEY_PREFIX_SWIPEACTIONS + tag, "$right,$left").apply()
|
SwipeActions.prefs!!.edit().putString(SwipeActions.KEY_PREFIX_SWIPEACTIONS + tag, "$right,$left").apply()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun saveActionsEnabledPrefs(enabled: Boolean) {
|
private fun saveActionsEnabledPrefs(enabled: Boolean) {
|
||||||
val prefs = context.getSharedPreferences(SwipeActions.PREF_NAME, Context.MODE_PRIVATE)
|
getSharedPrefs(context)
|
||||||
prefs.edit().putBoolean(SwipeActions.KEY_PREFIX_NO_ACTION + tag, enabled).apply()
|
SwipeActions.prefs!!.edit().putBoolean(SwipeActions.KEY_PREFIX_NO_ACTION + tag, enabled).apply()
|
||||||
}
|
}
|
||||||
|
|
||||||
interface Callback {
|
interface Callback {
|
||||||
|
|
|
@ -6,7 +6,7 @@ import ac.mdiq.podcini.storage.DBReader
|
||||||
import ac.mdiq.podcini.storage.DBWriter
|
import ac.mdiq.podcini.storage.DBWriter
|
||||||
import ac.mdiq.podcini.storage.model.feed.FeedPreferences
|
import ac.mdiq.podcini.storage.model.feed.FeedPreferences
|
||||||
import ac.mdiq.podcini.ui.adapter.SimpleChipAdapter
|
import ac.mdiq.podcini.ui.adapter.SimpleChipAdapter
|
||||||
import ac.mdiq.podcini.ui.view.ItemOffsetDecoration
|
import ac.mdiq.podcini.ui.utils.ItemOffsetDecoration
|
||||||
import ac.mdiq.podcini.util.event.EventFlow
|
import ac.mdiq.podcini.util.event.EventFlow
|
||||||
import ac.mdiq.podcini.util.event.FlowEvent
|
import ac.mdiq.podcini.util.event.FlowEvent
|
||||||
import android.app.Dialog
|
import android.app.Dialog
|
||||||
|
|
|
@ -8,7 +8,7 @@ import ac.mdiq.podcini.playback.PlaybackController.Companion.setPlaybackSpeed
|
||||||
import ac.mdiq.podcini.playback.PlaybackController.Companion.setSkipSilence
|
import ac.mdiq.podcini.playback.PlaybackController.Companion.setSkipSilence
|
||||||
import ac.mdiq.podcini.preferences.UserPreferences.isSkipSilence
|
import ac.mdiq.podcini.preferences.UserPreferences.isSkipSilence
|
||||||
import ac.mdiq.podcini.preferences.UserPreferences.playbackSpeedArray
|
import ac.mdiq.podcini.preferences.UserPreferences.playbackSpeedArray
|
||||||
import ac.mdiq.podcini.ui.view.ItemOffsetDecoration
|
import ac.mdiq.podcini.ui.utils.ItemOffsetDecoration
|
||||||
import ac.mdiq.podcini.ui.view.PlaybackSpeedSeekBar
|
import ac.mdiq.podcini.ui.view.PlaybackSpeedSeekBar
|
||||||
import ac.mdiq.podcini.util.Logd
|
import ac.mdiq.podcini.util.Logd
|
||||||
import ac.mdiq.podcini.util.event.EventFlow
|
import ac.mdiq.podcini.util.event.EventFlow
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
package ac.mdiq.podcini.ui.fragment
|
package ac.mdiq.podcini.ui.fragment
|
||||||
|
|
||||||
import ac.mdiq.podcini.R
|
import ac.mdiq.podcini.R
|
||||||
|
import ac.mdiq.podcini.playback.service.PlaybackService
|
||||||
|
import ac.mdiq.podcini.playback.service.PlaybackService.Companion
|
||||||
import ac.mdiq.podcini.preferences.UserPreferences.allEpisodesSortOrder
|
import ac.mdiq.podcini.preferences.UserPreferences.allEpisodesSortOrder
|
||||||
import ac.mdiq.podcini.preferences.UserPreferences.prefFilterAllEpisodes
|
import ac.mdiq.podcini.preferences.UserPreferences.prefFilterAllEpisodes
|
||||||
import ac.mdiq.podcini.storage.DBReader
|
import ac.mdiq.podcini.storage.DBReader
|
||||||
|
@ -99,6 +101,7 @@ import org.apache.commons.lang3.StringUtils
|
||||||
private fun procFlowEvents() {
|
private fun procFlowEvents() {
|
||||||
lifecycleScope.launch {
|
lifecycleScope.launch {
|
||||||
EventFlow.events.collectLatest { event ->
|
EventFlow.events.collectLatest { event ->
|
||||||
|
Logd(TAG, "Received event: $event")
|
||||||
when (event) {
|
when (event) {
|
||||||
is FlowEvent.AllEpisodesFilterChangedEvent -> onFilterChanged(event)
|
is FlowEvent.AllEpisodesFilterChangedEvent -> onFilterChanged(event)
|
||||||
else -> {}
|
else -> {}
|
||||||
|
|
|
@ -14,6 +14,7 @@ import ac.mdiq.podcini.playback.base.MediaPlayerBase
|
||||||
import ac.mdiq.podcini.playback.base.PlayerStatus
|
import ac.mdiq.podcini.playback.base.PlayerStatus
|
||||||
import ac.mdiq.podcini.playback.cast.CastEnabledActivity
|
import ac.mdiq.podcini.playback.cast.CastEnabledActivity
|
||||||
import ac.mdiq.podcini.playback.service.PlaybackService
|
import ac.mdiq.podcini.playback.service.PlaybackService
|
||||||
|
import ac.mdiq.podcini.playback.service.PlaybackService.Companion
|
||||||
import ac.mdiq.podcini.preferences.UserPreferences
|
import ac.mdiq.podcini.preferences.UserPreferences
|
||||||
import ac.mdiq.podcini.preferences.UserPreferences.videoPlayMode
|
import ac.mdiq.podcini.preferences.UserPreferences.videoPlayMode
|
||||||
import ac.mdiq.podcini.receiver.MediaButtonReceiver
|
import ac.mdiq.podcini.receiver.MediaButtonReceiver
|
||||||
|
@ -309,9 +310,8 @@ class AudioPlayerFragment : Fragment(), SeekBar.OnSeekBarChangeListener, Toolbar
|
||||||
|
|
||||||
private fun procFlowEvents() {
|
private fun procFlowEvents() {
|
||||||
lifecycleScope.launch {
|
lifecycleScope.launch {
|
||||||
Logd(TAG, "subscribing PositionFlowEvent")
|
|
||||||
EventFlow.events.collectLatest { event ->
|
EventFlow.events.collectLatest { event ->
|
||||||
// Logd(TAG, "PositionFlowEvent: ${event}")
|
Logd(TAG, "Received event: $event")
|
||||||
when (event) {
|
when (event) {
|
||||||
is FlowEvent.PlaybackServiceEvent ->
|
is FlowEvent.PlaybackServiceEvent ->
|
||||||
if (event.action == FlowEvent.PlaybackServiceEvent.Action.SERVICE_SHUT_DOWN)
|
if (event.action == FlowEvent.PlaybackServiceEvent.Action.SERVICE_SHUT_DOWN)
|
||||||
|
@ -546,7 +546,7 @@ class AudioPlayerFragment : Fragment(), SeekBar.OnSeekBarChangeListener, Toolbar
|
||||||
private fun procFlowEvents() {
|
private fun procFlowEvents() {
|
||||||
lifecycleScope.launch {
|
lifecycleScope.launch {
|
||||||
EventFlow.events.collectLatest { event ->
|
EventFlow.events.collectLatest { event ->
|
||||||
// Logd(TAG, "PositionFlowEvent: ${event}")
|
Logd(TAG, "Received event: $event")
|
||||||
when (event) {
|
when (event) {
|
||||||
is FlowEvent.PlaybackPositionEvent -> onPositionObserverUpdate(event)
|
is FlowEvent.PlaybackPositionEvent -> onPositionObserverUpdate(event)
|
||||||
is FlowEvent.SpeedChangedEvent -> updatePlaybackSpeedButton(event)
|
is FlowEvent.SpeedChangedEvent -> updatePlaybackSpeedButton(event)
|
||||||
|
|
|
@ -14,9 +14,9 @@ import ac.mdiq.podcini.ui.activity.MainActivity
|
||||||
import ac.mdiq.podcini.ui.adapter.EpisodeItemListAdapter
|
import ac.mdiq.podcini.ui.adapter.EpisodeItemListAdapter
|
||||||
import ac.mdiq.podcini.ui.adapter.SelectableAdapter
|
import ac.mdiq.podcini.ui.adapter.SelectableAdapter
|
||||||
import ac.mdiq.podcini.ui.dialog.ConfirmationDialog
|
import ac.mdiq.podcini.ui.dialog.ConfirmationDialog
|
||||||
import ac.mdiq.podcini.ui.view.EmptyViewHandler
|
import ac.mdiq.podcini.ui.utils.EmptyViewHandler
|
||||||
import ac.mdiq.podcini.ui.view.EpisodeItemListRecyclerView
|
import ac.mdiq.podcini.ui.view.EpisodeItemListRecyclerView
|
||||||
import ac.mdiq.podcini.ui.view.LiftOnScrollListener
|
import ac.mdiq.podcini.ui.utils.LiftOnScrollListener
|
||||||
import ac.mdiq.podcini.ui.view.viewholder.EpisodeItemViewHolder
|
import ac.mdiq.podcini.ui.view.viewholder.EpisodeItemViewHolder
|
||||||
import ac.mdiq.podcini.util.FeedItemUtil
|
import ac.mdiq.podcini.util.FeedItemUtil
|
||||||
import ac.mdiq.podcini.util.Logd
|
import ac.mdiq.podcini.util.Logd
|
||||||
|
@ -104,6 +104,7 @@ import kotlinx.coroutines.withContext
|
||||||
recyclerView.addOnScrollListener(LiftOnScrollListener(binding.appbar))
|
recyclerView.addOnScrollListener(LiftOnScrollListener(binding.appbar))
|
||||||
|
|
||||||
swipeActions = SwipeActions(this, getFragmentTag()).attachTo(recyclerView)
|
swipeActions = SwipeActions(this, getFragmentTag()).attachTo(recyclerView)
|
||||||
|
lifecycle.addObserver(swipeActions)
|
||||||
swipeActions.setFilter(getFilter())
|
swipeActions.setFilter(getFilter())
|
||||||
refreshSwipeTelltale()
|
refreshSwipeTelltale()
|
||||||
binding.leftActionIcon.setOnClickListener {
|
binding.leftActionIcon.setOnClickListener {
|
||||||
|
@ -423,6 +424,7 @@ import kotlinx.coroutines.withContext
|
||||||
private fun procFlowEvents() {
|
private fun procFlowEvents() {
|
||||||
lifecycleScope.launch {
|
lifecycleScope.launch {
|
||||||
EventFlow.events.collectLatest { event ->
|
EventFlow.events.collectLatest { event ->
|
||||||
|
Logd(TAG, "Received event: $event")
|
||||||
when (event) {
|
when (event) {
|
||||||
is FlowEvent.SwipeActionsChangedEvent -> refreshSwipeTelltale()
|
is FlowEvent.SwipeActionsChangedEvent -> refreshSwipeTelltale()
|
||||||
is FlowEvent.FeedListUpdateEvent, is FlowEvent.UnreadItemsUpdateEvent, is FlowEvent.PlayerStatusEvent -> loadItems()
|
is FlowEvent.FeedListUpdateEvent, is FlowEvent.UnreadItemsUpdateEvent, is FlowEvent.PlayerStatusEvent -> loadItems()
|
||||||
|
|
|
@ -5,6 +5,8 @@ import ac.mdiq.podcini.databinding.SimpleListFragmentBinding
|
||||||
import ac.mdiq.podcini.playback.PlaybackController
|
import ac.mdiq.podcini.playback.PlaybackController
|
||||||
import ac.mdiq.podcini.playback.base.MediaPlayerBase
|
import ac.mdiq.podcini.playback.base.MediaPlayerBase
|
||||||
import ac.mdiq.podcini.playback.base.PlayerStatus
|
import ac.mdiq.podcini.playback.base.PlayerStatus
|
||||||
|
import ac.mdiq.podcini.playback.service.PlaybackService
|
||||||
|
import ac.mdiq.podcini.playback.service.PlaybackService.Companion
|
||||||
import ac.mdiq.podcini.storage.model.feed.FeedMedia
|
import ac.mdiq.podcini.storage.model.feed.FeedMedia
|
||||||
import ac.mdiq.podcini.storage.model.playback.Playable
|
import ac.mdiq.podcini.storage.model.playback.Playable
|
||||||
import ac.mdiq.podcini.ui.adapter.ChaptersListAdapter
|
import ac.mdiq.podcini.ui.adapter.ChaptersListAdapter
|
||||||
|
@ -124,6 +126,7 @@ class ChaptersFragment : AppCompatDialogFragment() {
|
||||||
private fun procFlowEvents() {
|
private fun procFlowEvents() {
|
||||||
lifecycleScope.launch {
|
lifecycleScope.launch {
|
||||||
EventFlow.events.collectLatest { event ->
|
EventFlow.events.collectLatest { event ->
|
||||||
|
Logd(TAG, "Received event: $event")
|
||||||
when (event) {
|
when (event) {
|
||||||
is FlowEvent.PlaybackPositionEvent -> onEventMainThread(event)
|
is FlowEvent.PlaybackPositionEvent -> onEventMainThread(event)
|
||||||
else -> {}
|
else -> {}
|
||||||
|
|
|
@ -5,6 +5,7 @@ import ac.mdiq.podcini.R
|
||||||
import ac.mdiq.podcini.databinding.FragmentItunesSearchBinding
|
import ac.mdiq.podcini.databinding.FragmentItunesSearchBinding
|
||||||
import ac.mdiq.podcini.databinding.SelectCountryDialogBinding
|
import ac.mdiq.podcini.databinding.SelectCountryDialogBinding
|
||||||
import ac.mdiq.podcini.net.discovery.ItunesTopListLoader
|
import ac.mdiq.podcini.net.discovery.ItunesTopListLoader
|
||||||
|
import ac.mdiq.podcini.net.discovery.ItunesTopListLoader.Companion.prefs
|
||||||
import ac.mdiq.podcini.net.discovery.PodcastSearchResult
|
import ac.mdiq.podcini.net.discovery.PodcastSearchResult
|
||||||
import ac.mdiq.podcini.storage.DBReader
|
import ac.mdiq.podcini.storage.DBReader
|
||||||
import ac.mdiq.podcini.ui.activity.MainActivity
|
import ac.mdiq.podcini.ui.activity.MainActivity
|
||||||
|
@ -42,7 +43,7 @@ class DiscoveryFragment : Fragment(), Toolbar.OnMenuItemClickListener {
|
||||||
private var _binding: FragmentItunesSearchBinding? = null
|
private var _binding: FragmentItunesSearchBinding? = null
|
||||||
private val binding get() = _binding!!
|
private val binding get() = _binding!!
|
||||||
|
|
||||||
private lateinit var prefs: SharedPreferences
|
// private lateinit var prefs: SharedPreferences
|
||||||
private lateinit var gridView: GridView
|
private lateinit var gridView: GridView
|
||||||
private lateinit var progressBar: ProgressBar
|
private lateinit var progressBar: ProgressBar
|
||||||
private lateinit var txtvError: TextView
|
private lateinit var txtvError: TextView
|
||||||
|
@ -91,10 +92,10 @@ class DiscoveryFragment : Fragment(), Toolbar.OnMenuItemClickListener {
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
prefs = requireActivity().getSharedPreferences(ItunesTopListLoader.PREFS, Context.MODE_PRIVATE)
|
// prefs = requireActivity().getSharedPreferences(ItunesTopListLoader.PREFS, Context.MODE_PRIVATE)
|
||||||
countryCode = prefs.getString(ItunesTopListLoader.PREF_KEY_COUNTRY_CODE, Locale.getDefault().country)
|
countryCode = prefs!!.getString(ItunesTopListLoader.PREF_KEY_COUNTRY_CODE, Locale.getDefault().country)
|
||||||
hidden = prefs.getBoolean(ItunesTopListLoader.PREF_KEY_HIDDEN_DISCOVERY_COUNTRY, false)
|
hidden = prefs!!.getBoolean(ItunesTopListLoader.PREF_KEY_HIDDEN_DISCOVERY_COUNTRY, false)
|
||||||
needsConfirm = prefs.getBoolean(ItunesTopListLoader.PREF_KEY_NEEDS_CONFIRM, true)
|
needsConfirm = prefs!!.getBoolean(ItunesTopListLoader.PREF_KEY_NEEDS_CONFIRM, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
@OptIn(UnstableApi::class) override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
|
@OptIn(UnstableApi::class) override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
|
||||||
|
@ -167,7 +168,7 @@ class DiscoveryFragment : Fragment(), Toolbar.OnMenuItemClickListener {
|
||||||
butRetry.visibility = View.VISIBLE
|
butRetry.visibility = View.VISIBLE
|
||||||
butRetry.setText(R.string.discover_confirm)
|
butRetry.setText(R.string.discover_confirm)
|
||||||
butRetry.setOnClickListener {
|
butRetry.setOnClickListener {
|
||||||
prefs.edit().putBoolean(ItunesTopListLoader.PREF_KEY_NEEDS_CONFIRM, false).apply()
|
prefs!!.edit().putBoolean(ItunesTopListLoader.PREF_KEY_NEEDS_CONFIRM, false).apply()
|
||||||
needsConfirm = false
|
needsConfirm = false
|
||||||
loadToplist(country)
|
loadToplist(country)
|
||||||
}
|
}
|
||||||
|
@ -225,7 +226,7 @@ class DiscoveryFragment : Fragment(), Toolbar.OnMenuItemClickListener {
|
||||||
R.id.discover_hide_item -> {
|
R.id.discover_hide_item -> {
|
||||||
item.setChecked(!item.isChecked)
|
item.setChecked(!item.isChecked)
|
||||||
hidden = item.isChecked
|
hidden = item.isChecked
|
||||||
prefs.edit().putBoolean(ItunesTopListLoader.PREF_KEY_HIDDEN_DISCOVERY_COUNTRY, hidden).apply()
|
prefs!!.edit().putBoolean(ItunesTopListLoader.PREF_KEY_HIDDEN_DISCOVERY_COUNTRY, hidden).apply()
|
||||||
|
|
||||||
EventFlow.postEvent(FlowEvent.DiscoveryDefaultUpdateEvent())
|
EventFlow.postEvent(FlowEvent.DiscoveryDefaultUpdateEvent())
|
||||||
loadToplist(countryCode)
|
loadToplist(countryCode)
|
||||||
|
@ -278,8 +279,8 @@ class DiscoveryFragment : Fragment(), Toolbar.OnMenuItemClickListener {
|
||||||
hidden = false
|
hidden = false
|
||||||
}
|
}
|
||||||
|
|
||||||
prefs.edit().putBoolean(ItunesTopListLoader.PREF_KEY_HIDDEN_DISCOVERY_COUNTRY, hidden).apply()
|
prefs!!.edit().putBoolean(ItunesTopListLoader.PREF_KEY_HIDDEN_DISCOVERY_COUNTRY, hidden).apply()
|
||||||
prefs.edit().putString(ItunesTopListLoader.PREF_KEY_COUNTRY_CODE, countryCode).apply()
|
prefs!!.edit().putString(ItunesTopListLoader.PREF_KEY_COUNTRY_CODE, countryCode).apply()
|
||||||
|
|
||||||
EventFlow.postEvent(FlowEvent.DiscoveryDefaultUpdateEvent())
|
EventFlow.postEvent(FlowEvent.DiscoveryDefaultUpdateEvent())
|
||||||
loadToplist(countryCode)
|
loadToplist(countryCode)
|
||||||
|
|
|
@ -7,7 +7,7 @@ import ac.mdiq.podcini.storage.DBWriter
|
||||||
import ac.mdiq.podcini.storage.model.download.DownloadResult
|
import ac.mdiq.podcini.storage.model.download.DownloadResult
|
||||||
import ac.mdiq.podcini.ui.adapter.DownloadLogAdapter
|
import ac.mdiq.podcini.ui.adapter.DownloadLogAdapter
|
||||||
import ac.mdiq.podcini.ui.dialog.DownloadLogDetailsDialog
|
import ac.mdiq.podcini.ui.dialog.DownloadLogDetailsDialog
|
||||||
import ac.mdiq.podcini.ui.view.EmptyViewHandler
|
import ac.mdiq.podcini.ui.utils.EmptyViewHandler
|
||||||
import ac.mdiq.podcini.util.Logd
|
import ac.mdiq.podcini.util.Logd
|
||||||
import ac.mdiq.podcini.util.event.EventFlow
|
import ac.mdiq.podcini.util.event.EventFlow
|
||||||
import ac.mdiq.podcini.util.event.FlowEvent
|
import ac.mdiq.podcini.util.event.FlowEvent
|
||||||
|
@ -83,6 +83,7 @@ class DownloadLogFragment : BottomSheetDialogFragment(), OnItemClickListener, To
|
||||||
private fun procFlowEvents() {
|
private fun procFlowEvents() {
|
||||||
lifecycleScope.launch {
|
lifecycleScope.launch {
|
||||||
EventFlow.events.collectLatest { event ->
|
EventFlow.events.collectLatest { event ->
|
||||||
|
Logd(TAG, "Received event: $event")
|
||||||
when (event) {
|
when (event) {
|
||||||
is FlowEvent.DownloadLogEvent -> loadDownloadLog()
|
is FlowEvent.DownloadLogEvent -> loadDownloadLog()
|
||||||
else -> {}
|
else -> {}
|
||||||
|
|
|
@ -19,9 +19,9 @@ import ac.mdiq.podcini.ui.activity.MainActivity
|
||||||
import ac.mdiq.podcini.ui.adapter.EpisodeItemListAdapter
|
import ac.mdiq.podcini.ui.adapter.EpisodeItemListAdapter
|
||||||
import ac.mdiq.podcini.ui.adapter.SelectableAdapter
|
import ac.mdiq.podcini.ui.adapter.SelectableAdapter
|
||||||
import ac.mdiq.podcini.ui.dialog.ItemSortDialog
|
import ac.mdiq.podcini.ui.dialog.ItemSortDialog
|
||||||
import ac.mdiq.podcini.ui.view.EmptyViewHandler
|
import ac.mdiq.podcini.ui.utils.EmptyViewHandler
|
||||||
import ac.mdiq.podcini.ui.view.EpisodeItemListRecyclerView
|
import ac.mdiq.podcini.ui.view.EpisodeItemListRecyclerView
|
||||||
import ac.mdiq.podcini.ui.view.LiftOnScrollListener
|
import ac.mdiq.podcini.ui.utils.LiftOnScrollListener
|
||||||
import ac.mdiq.podcini.ui.view.viewholder.EpisodeItemViewHolder
|
import ac.mdiq.podcini.ui.view.viewholder.EpisodeItemViewHolder
|
||||||
import ac.mdiq.podcini.util.FeedItemUtil
|
import ac.mdiq.podcini.util.FeedItemUtil
|
||||||
import ac.mdiq.podcini.util.Logd
|
import ac.mdiq.podcini.util.Logd
|
||||||
|
@ -97,6 +97,7 @@ import java.util.*
|
||||||
recyclerView.addOnScrollListener(LiftOnScrollListener(binding.appbar))
|
recyclerView.addOnScrollListener(LiftOnScrollListener(binding.appbar))
|
||||||
|
|
||||||
swipeActions = SwipeActions(this, TAG).attachTo(recyclerView)
|
swipeActions = SwipeActions(this, TAG).attachTo(recyclerView)
|
||||||
|
lifecycle.addObserver(swipeActions)
|
||||||
swipeActions.setFilter(FeedItemFilter(FeedItemFilter.DOWNLOADED))
|
swipeActions.setFilter(FeedItemFilter(FeedItemFilter.DOWNLOADED))
|
||||||
refreshSwipeTelltale()
|
refreshSwipeTelltale()
|
||||||
binding.leftActionIcon.setOnClickListener {
|
binding.leftActionIcon.setOnClickListener {
|
||||||
|
@ -213,6 +214,7 @@ import java.util.*
|
||||||
private fun procFlowEvents() {
|
private fun procFlowEvents() {
|
||||||
lifecycleScope.launch {
|
lifecycleScope.launch {
|
||||||
EventFlow.events.collectLatest { event ->
|
EventFlow.events.collectLatest { event ->
|
||||||
|
Logd(TAG, "Received event: $event")
|
||||||
when (event) {
|
when (event) {
|
||||||
is FlowEvent.FeedItemEvent -> onEventMainThread(event)
|
is FlowEvent.FeedItemEvent -> onEventMainThread(event)
|
||||||
is FlowEvent.PlaybackPositionEvent -> onEventMainThread(event)
|
is FlowEvent.PlaybackPositionEvent -> onEventMainThread(event)
|
||||||
|
|
|
@ -66,7 +66,7 @@ import kotlin.math.max
|
||||||
|
|
||||||
private var homeFragment: EpisodeHomeFragment? = null
|
private var homeFragment: EpisodeHomeFragment? = null
|
||||||
|
|
||||||
private var itemsLoaded = false
|
private var itemLoaded = false
|
||||||
private var item: FeedItem? = null
|
private var item: FeedItem? = null
|
||||||
private var webviewData: String? = null
|
private var webviewData: String? = null
|
||||||
|
|
||||||
|
@ -89,16 +89,8 @@ import kotlin.math.max
|
||||||
private var actionButton1: ItemActionButton? = null
|
private var actionButton1: ItemActionButton? = null
|
||||||
private var actionButton2: ItemActionButton? = null
|
private var actionButton2: ItemActionButton? = null
|
||||||
|
|
||||||
// private var disposable: Disposable? = null
|
|
||||||
private var controller: PlaybackController? = null
|
private var controller: PlaybackController? = null
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
|
||||||
super.onCreate(savedInstanceState)
|
|
||||||
|
|
||||||
// item = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) requireArguments().getSerializable(ARG_FEEDITEM, FeedItem::class.java)
|
|
||||||
// else requireArguments().getSerializable(ARG_FEEDITEM) as? FeedItem
|
|
||||||
}
|
|
||||||
|
|
||||||
@UnstableApi override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
|
@UnstableApi override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
|
||||||
super.onCreateView(inflater, container, savedInstanceState)
|
super.onCreateView(inflater, container, savedInstanceState)
|
||||||
|
|
||||||
|
@ -140,9 +132,7 @@ import kotlin.math.max
|
||||||
if (!item?.link.isNullOrEmpty()) {
|
if (!item?.link.isNullOrEmpty()) {
|
||||||
homeFragment = EpisodeHomeFragment.newInstance(item!!)
|
homeFragment = EpisodeHomeFragment.newInstance(item!!)
|
||||||
(activity as MainActivity).loadChildFragment(homeFragment!!)
|
(activity as MainActivity).loadChildFragment(homeFragment!!)
|
||||||
} else {
|
} else Toast.makeText(context, "Episode link is not valid ${item?.link}", Toast.LENGTH_LONG).show()
|
||||||
Toast.makeText(context, "Episode link is not valid ${item?.link}", Toast.LENGTH_LONG).show()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
butAction1.setOnClickListener(View.OnClickListener {
|
butAction1.setOnClickListener(View.OnClickListener {
|
||||||
|
@ -169,9 +159,7 @@ import kotlin.math.max
|
||||||
})
|
})
|
||||||
|
|
||||||
controller = object : PlaybackController(requireActivity()) {
|
controller = object : PlaybackController(requireActivity()) {
|
||||||
override fun loadMediaInfo() {
|
override fun loadMediaInfo() {}
|
||||||
// Do nothing
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
controller?.init()
|
controller?.init()
|
||||||
load()
|
load()
|
||||||
|
@ -246,7 +234,7 @@ import kotlin.math.max
|
||||||
|
|
||||||
@UnstableApi override fun onResume() {
|
@UnstableApi override fun onResume() {
|
||||||
super.onResume()
|
super.onResume()
|
||||||
if (itemsLoaded) {
|
if (itemLoaded) {
|
||||||
progbarLoading.visibility = View.GONE
|
progbarLoading.visibility = View.GONE
|
||||||
updateAppearance()
|
updateAppearance()
|
||||||
}
|
}
|
||||||
|
@ -258,7 +246,6 @@ import kotlin.math.max
|
||||||
_binding = null
|
_binding = null
|
||||||
|
|
||||||
controller?.release()
|
controller?.release()
|
||||||
// disposable?.dispose()
|
|
||||||
root.removeView(webvDescription)
|
root.removeView(webvDescription)
|
||||||
webvDescription.clearHistory()
|
webvDescription.clearHistory()
|
||||||
webvDescription.clearCache(true)
|
webvDescription.clearCache(true)
|
||||||
|
@ -267,9 +254,9 @@ import kotlin.math.max
|
||||||
}
|
}
|
||||||
|
|
||||||
@UnstableApi private fun onFragmentLoaded() {
|
@UnstableApi private fun onFragmentLoaded() {
|
||||||
if (webviewData != null && !itemsLoaded) {
|
if (webviewData != null && !itemLoaded)
|
||||||
webvDescription.loadDataWithBaseURL("https://127.0.0.1", webviewData!!, "text/html", "utf-8", "about:blank")
|
webvDescription.loadDataWithBaseURL("https://127.0.0.1", webviewData!!, "text/html", "utf-8", "about:blank")
|
||||||
}
|
|
||||||
// if (item?.link != null) binding.webView.loadUrl(item!!.link!!)
|
// if (item?.link != null) binding.webView.loadUrl(item!!.link!!)
|
||||||
updateAppearance()
|
updateAppearance()
|
||||||
}
|
}
|
||||||
|
@ -279,14 +266,13 @@ import kotlin.math.max
|
||||||
Logd(TAG, "updateAppearance item is null")
|
Logd(TAG, "updateAppearance item is null")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if (item!!.hasMedia()) {
|
if (item!!.hasMedia()) FeedItemMenuHandler.onPrepareMenu(toolbar.menu, item, R.id.open_podcast)
|
||||||
FeedItemMenuHandler.onPrepareMenu(toolbar.menu, item, R.id.open_podcast)
|
// these are already available via button1 and button2
|
||||||
} else {
|
else FeedItemMenuHandler.onPrepareMenu(toolbar.menu, item, R.id.open_podcast, R.id.mark_read_item, R.id.visit_website_item)
|
||||||
// these are already available via button1 and button2
|
|
||||||
FeedItemMenuHandler.onPrepareMenu(toolbar.menu, item, R.id.open_podcast, R.id.mark_read_item, R.id.visit_website_item)
|
|
||||||
}
|
|
||||||
if (item!!.feed != null) txtvPodcast.text = item!!.feed!!.title
|
if (item!!.feed != null) txtvPodcast.text = item!!.feed!!.title
|
||||||
txtvTitle.text = item!!.title
|
txtvTitle.text = item!!.title
|
||||||
|
binding.itemLink.text = item!!.link
|
||||||
|
|
||||||
if (item?.pubDate != null) {
|
if (item?.pubDate != null) {
|
||||||
val pubDateStr = DateFormatter.formatAbbrev(context, item!!.pubDate)
|
val pubDateStr = DateFormatter.formatAbbrev(context, item!!.pubDate)
|
||||||
|
@ -385,6 +371,7 @@ import kotlin.math.max
|
||||||
private fun procFlowEvents() {
|
private fun procFlowEvents() {
|
||||||
lifecycleScope.launch {
|
lifecycleScope.launch {
|
||||||
EventFlow.events.collectLatest { event ->
|
EventFlow.events.collectLatest { event ->
|
||||||
|
Logd(TAG, "Received event: $event")
|
||||||
when (event) {
|
when (event) {
|
||||||
is FlowEvent.FeedItemEvent -> onEventMainThread(event)
|
is FlowEvent.FeedItemEvent -> onEventMainThread(event)
|
||||||
is FlowEvent.PlayerStatusEvent -> updateButtons()
|
is FlowEvent.PlayerStatusEvent -> updateButtons()
|
||||||
|
@ -404,7 +391,7 @@ import kotlin.math.max
|
||||||
}
|
}
|
||||||
|
|
||||||
fun onEventMainThread(event: FlowEvent.FeedItemEvent) {
|
fun onEventMainThread(event: FlowEvent.FeedItemEvent) {
|
||||||
Logd(TAG, "onEventMainThread() called with: event = [$event]")
|
Logd(TAG, "onEventMainThread() called with: FeedItemEvent")
|
||||||
if (this.item == null) return
|
if (this.item == null) return
|
||||||
for (item in event.items) {
|
for (item in event.items) {
|
||||||
if (this.item!!.id == item.id) {
|
if (this.item!!.id == item.id) {
|
||||||
|
@ -417,33 +404,13 @@ import kotlin.math.max
|
||||||
fun onEventMainThread(event: FlowEvent.EpisodeDownloadEvent) {
|
fun onEventMainThread(event: FlowEvent.EpisodeDownloadEvent) {
|
||||||
if (item == null || item!!.media == null) return
|
if (item == null || item!!.media == null) return
|
||||||
if (!event.urls.contains(item!!.media!!.download_url)) return
|
if (!event.urls.contains(item!!.media!!.download_url)) return
|
||||||
if (itemsLoaded && activity != null) updateButtons()
|
if (itemLoaded && activity != null) updateButtons()
|
||||||
}
|
}
|
||||||
|
|
||||||
// @UnstableApi private fun load0() {
|
|
||||||
// disposable?.dispose()
|
|
||||||
// if (!itemsLoaded) progbarLoading.visibility = View.VISIBLE
|
|
||||||
//
|
|
||||||
// Logd(TAG, "load() called")
|
|
||||||
// disposable = Observable.fromCallable<FeedItem?> { this.loadInBackground() }
|
|
||||||
// .subscribeOn(Schedulers.io())
|
|
||||||
// .observeOn(AndroidSchedulers.mainThread())
|
|
||||||
// .subscribe({ result: FeedItem? ->
|
|
||||||
// progbarLoading.visibility = View.GONE
|
|
||||||
// item = result
|
|
||||||
// onFragmentLoaded()
|
|
||||||
// itemsLoaded = true
|
|
||||||
// },
|
|
||||||
// { error: Throwable? ->
|
|
||||||
// Log.e(TAG, Log.getStackTraceString(error))
|
|
||||||
// })
|
|
||||||
// }
|
|
||||||
|
|
||||||
@UnstableApi private fun load() {
|
@UnstableApi private fun load() {
|
||||||
if (!itemsLoaded) progbarLoading.visibility = View.VISIBLE
|
if (!itemLoaded) progbarLoading.visibility = View.VISIBLE
|
||||||
|
|
||||||
Logd(TAG, "load() called")
|
Logd(TAG, "load() called")
|
||||||
// val scope = CoroutineScope(Dispatchers.Main)
|
|
||||||
lifecycleScope.launch {
|
lifecycleScope.launch {
|
||||||
try {
|
try {
|
||||||
val result = withContext(Dispatchers.IO) {
|
val result = withContext(Dispatchers.IO) {
|
||||||
|
@ -459,7 +426,7 @@ import kotlin.math.max
|
||||||
progbarLoading.visibility = View.GONE
|
progbarLoading.visibility = View.GONE
|
||||||
item = result
|
item = result
|
||||||
onFragmentLoaded()
|
onFragmentLoaded()
|
||||||
itemsLoaded = true
|
itemLoaded = true
|
||||||
}
|
}
|
||||||
} catch (e: Throwable) {
|
} catch (e: Throwable) {
|
||||||
Log.e(TAG, Log.getStackTraceString(e))
|
Log.e(TAG, Log.getStackTraceString(e))
|
||||||
|
|
|
@ -105,6 +105,7 @@ import kotlin.math.min
|
||||||
private fun procFlowEvents() {
|
private fun procFlowEvents() {
|
||||||
lifecycleScope.launch {
|
lifecycleScope.launch {
|
||||||
EventFlow.events.collectLatest { event ->
|
EventFlow.events.collectLatest { event ->
|
||||||
|
Logd(TAG, "Received event: $event")
|
||||||
when (event) {
|
when (event) {
|
||||||
is FlowEvent.AllEpisodesFilterChangedEvent -> page = 1
|
is FlowEvent.AllEpisodesFilterChangedEvent -> page = 1
|
||||||
else -> {}
|
else -> {}
|
||||||
|
|
|
@ -10,7 +10,8 @@ import ac.mdiq.podcini.ui.activity.MainActivity
|
||||||
import ac.mdiq.podcini.ui.dialog.EditUrlSettingsDialog
|
import ac.mdiq.podcini.ui.dialog.EditUrlSettingsDialog
|
||||||
import ac.mdiq.podcini.ui.statistics.StatisticsFragment
|
import ac.mdiq.podcini.ui.statistics.StatisticsFragment
|
||||||
import ac.mdiq.podcini.ui.statistics.feed.FeedStatisticsFragment
|
import ac.mdiq.podcini.ui.statistics.feed.FeedStatisticsFragment
|
||||||
import ac.mdiq.podcini.ui.view.ToolbarIconTintManager
|
import ac.mdiq.podcini.ui.utils.TransitionEffect
|
||||||
|
import ac.mdiq.podcini.ui.utils.ToolbarIconTintManager
|
||||||
import ac.mdiq.podcini.util.IntentUtils
|
import ac.mdiq.podcini.util.IntentUtils
|
||||||
import ac.mdiq.podcini.util.Logd
|
import ac.mdiq.podcini.util.Logd
|
||||||
import ac.mdiq.podcini.util.ShareUtils
|
import ac.mdiq.podcini.util.ShareUtils
|
||||||
|
@ -45,7 +46,6 @@ import com.google.android.material.appbar.CollapsingToolbarLayout
|
||||||
import com.google.android.material.appbar.MaterialToolbar
|
import com.google.android.material.appbar.MaterialToolbar
|
||||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||||
import com.google.android.material.snackbar.Snackbar
|
import com.google.android.material.snackbar.Snackbar
|
||||||
import kotlinx.coroutines.CoroutineScope
|
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
|
|
|
@ -21,7 +21,8 @@ import ac.mdiq.podcini.ui.adapter.EpisodeItemListAdapter
|
||||||
import ac.mdiq.podcini.ui.adapter.SelectableAdapter
|
import ac.mdiq.podcini.ui.adapter.SelectableAdapter
|
||||||
import ac.mdiq.podcini.ui.dialog.*
|
import ac.mdiq.podcini.ui.dialog.*
|
||||||
import ac.mdiq.podcini.ui.utils.MoreContentListFooterUtil
|
import ac.mdiq.podcini.ui.utils.MoreContentListFooterUtil
|
||||||
import ac.mdiq.podcini.ui.view.ToolbarIconTintManager
|
import ac.mdiq.podcini.ui.utils.TransitionEffect
|
||||||
|
import ac.mdiq.podcini.ui.utils.ToolbarIconTintManager
|
||||||
import ac.mdiq.podcini.ui.view.viewholder.EpisodeItemViewHolder
|
import ac.mdiq.podcini.ui.view.viewholder.EpisodeItemViewHolder
|
||||||
import ac.mdiq.podcini.util.*
|
import ac.mdiq.podcini.util.*
|
||||||
import ac.mdiq.podcini.util.event.EventFlow
|
import ac.mdiq.podcini.util.event.EventFlow
|
||||||
|
@ -112,6 +113,7 @@ import java.util.concurrent.Semaphore
|
||||||
binding.recyclerView.adapter = adapter
|
binding.recyclerView.adapter = adapter
|
||||||
|
|
||||||
swipeActions = SwipeActions(this, TAG).attachTo(binding.recyclerView)
|
swipeActions = SwipeActions(this, TAG).attachTo(binding.recyclerView)
|
||||||
|
lifecycle.addObserver(swipeActions)
|
||||||
refreshSwipeTelltale()
|
refreshSwipeTelltale()
|
||||||
binding.header.leftActionIcon.setOnClickListener {
|
binding.header.leftActionIcon.setOnClickListener {
|
||||||
swipeActions.showDialog()
|
swipeActions.showDialog()
|
||||||
|
@ -257,7 +259,7 @@ import java.util.concurrent.Semaphore
|
||||||
feed!!.nextPageLink = feed!!.download_url
|
feed!!.nextPageLink = feed!!.download_url
|
||||||
feed!!.pageNr = 0
|
feed!!.pageNr = 0
|
||||||
try {
|
try {
|
||||||
DBWriter.resetPagedFeedPage(feed).get()
|
runBlocking { DBWriter.resetPagedFeedPage(feed).join() }
|
||||||
FeedUpdateManager.runOnce(requireContext(), feed)
|
FeedUpdateManager.runOnce(requireContext(), feed)
|
||||||
} catch (e: ExecutionException) {
|
} catch (e: ExecutionException) {
|
||||||
throw RuntimeException(e)
|
throw RuntimeException(e)
|
||||||
|
@ -352,6 +354,7 @@ import java.util.concurrent.Semaphore
|
||||||
private fun procFlowEvents() {
|
private fun procFlowEvents() {
|
||||||
lifecycleScope.launch {
|
lifecycleScope.launch {
|
||||||
EventFlow.events.collectLatest { event ->
|
EventFlow.events.collectLatest { event ->
|
||||||
|
Logd(TAG, "Received event: $event")
|
||||||
when (event) {
|
when (event) {
|
||||||
is FlowEvent.QueueEvent -> loadItems()
|
is FlowEvent.QueueEvent -> loadItems()
|
||||||
is FlowEvent.FavoritesEvent -> loadItems()
|
is FlowEvent.FavoritesEvent -> loadItems()
|
||||||
|
|
|
@ -286,7 +286,7 @@ class FeedSettingsFragment : Fragment() {
|
||||||
val setPreferencesFuture = DBWriter.persistFeedPreferences(feedPreferences!!)
|
val setPreferencesFuture = DBWriter.persistFeedPreferences(feedPreferences!!)
|
||||||
Thread({
|
Thread({
|
||||||
try {
|
try {
|
||||||
setPreferencesFuture.get()
|
runBlocking { setPreferencesFuture.join() }
|
||||||
} catch (e: InterruptedException) {
|
} catch (e: InterruptedException) {
|
||||||
e.printStackTrace()
|
e.printStackTrace()
|
||||||
} catch (e: ExecutionException) {
|
} catch (e: ExecutionException) {
|
||||||
|
|
|
@ -11,7 +11,6 @@ import ac.mdiq.podcini.ui.activity.MainActivity
|
||||||
import ac.mdiq.podcini.ui.adapter.EpisodeItemListAdapter
|
import ac.mdiq.podcini.ui.adapter.EpisodeItemListAdapter
|
||||||
import ac.mdiq.podcini.ui.dialog.ConfirmationDialog
|
import ac.mdiq.podcini.ui.dialog.ConfirmationDialog
|
||||||
import ac.mdiq.podcini.ui.dialog.ItemSortDialog
|
import ac.mdiq.podcini.ui.dialog.ItemSortDialog
|
||||||
import ac.mdiq.podcini.ui.statistics.StatisticsFragment
|
|
||||||
import ac.mdiq.podcini.ui.statistics.subscriptions.DatesFilterDialog
|
import ac.mdiq.podcini.ui.statistics.subscriptions.DatesFilterDialog
|
||||||
import ac.mdiq.podcini.ui.view.viewholder.EpisodeItemViewHolder
|
import ac.mdiq.podcini.ui.view.viewholder.EpisodeItemViewHolder
|
||||||
import ac.mdiq.podcini.util.DateFormatter
|
import ac.mdiq.podcini.util.DateFormatter
|
||||||
|
@ -28,18 +27,18 @@ import kotlinx.coroutines.flow.collectLatest
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
@UnstableApi class PlaybackHistoryFragment : BaseEpisodesListFragment() {
|
@UnstableApi class HistoryFragment : BaseEpisodesListFragment() {
|
||||||
|
|
||||||
private var sortOrder : SortOrder = SortOrder.PLAYED_DATE_NEW_OLD
|
private var sortOrder : SortOrder = SortOrder.PLAYED_DATE_NEW_OLD
|
||||||
private var startDate : Long = 0L
|
private var startDate : Long = 0L
|
||||||
private var endDate : Long = Date().time
|
private var endDate : Long = Date().time
|
||||||
|
|
||||||
override fun getFragmentTag(): String {
|
override fun getFragmentTag(): String {
|
||||||
return "PlaybackHistoryFragment"
|
return TAG
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getPrefName(): String {
|
override fun getPrefName(): String {
|
||||||
return "PlaybackHistoryFragment"
|
return TAG
|
||||||
}
|
}
|
||||||
|
|
||||||
@UnstableApi override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
|
@UnstableApi override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
|
||||||
|
@ -69,7 +68,7 @@ import java.util.*
|
||||||
}
|
}
|
||||||
override fun onCreateContextMenu(menu: ContextMenu, v: View, menuInfo: ContextMenu.ContextMenuInfo?) {
|
override fun onCreateContextMenu(menu: ContextMenu, v: View, menuInfo: ContextMenu.ContextMenuInfo?) {
|
||||||
super.onCreateContextMenu(menu, v, menuInfo)
|
super.onCreateContextMenu(menu, v, menuInfo)
|
||||||
MenuItemUtils.setOnClickListeners(menu) { item: MenuItem -> this@PlaybackHistoryFragment.onContextItemSelected(item) }
|
MenuItemUtils.setOnClickListeners(menu) { item: MenuItem -> this@HistoryFragment.onContextItemSelected(item) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
listAdapter.setOnSelectModeListener(this)
|
listAdapter.setOnSelectModeListener(this)
|
||||||
|
@ -131,6 +130,7 @@ import java.util.*
|
||||||
private fun procFlowEvents() {
|
private fun procFlowEvents() {
|
||||||
lifecycleScope.launch {
|
lifecycleScope.launch {
|
||||||
EventFlow.events.collectLatest { event ->
|
EventFlow.events.collectLatest { event ->
|
||||||
|
Logd(TAG, "Received event: $event")
|
||||||
when (event) {
|
when (event) {
|
||||||
is FlowEvent.HistoryEvent -> {
|
is FlowEvent.HistoryEvent -> {
|
||||||
sortOrder = event.sortOrder
|
sortOrder = event.sortOrder
|
||||||
|
@ -177,6 +177,6 @@ import java.util.*
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
const val TAG: String = "PlaybackHistoryFragment"
|
const val TAG: String = "HistoryFragment"
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -82,9 +82,9 @@ class NavDrawerFragment : Fragment(), SharedPreferences.OnSharedPreferenceChange
|
||||||
insets
|
insets
|
||||||
}
|
}
|
||||||
|
|
||||||
val preferences: SharedPreferences = requireContext().getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE)
|
// val preferences: SharedPreferences = requireContext().getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE)
|
||||||
// TODO: what is this?
|
// TODO: what is this?
|
||||||
openFolders = HashSet(preferences.getStringSet(PREF_OPEN_FOLDERS, HashSet<String>())!!) // Must not modify
|
openFolders = HashSet(prefs!!.getStringSet(PREF_OPEN_FOLDERS, HashSet<String>())!!) // Must not modify
|
||||||
|
|
||||||
val navList = binding.navRecycler
|
val navList = binding.navRecycler
|
||||||
navAdapter = NavListAdapter(itemAccess, requireActivity())
|
navAdapter = NavListAdapter(itemAccess, requireActivity())
|
||||||
|
@ -96,7 +96,7 @@ class NavDrawerFragment : Fragment(), SharedPreferences.OnSharedPreferenceChange
|
||||||
startActivity(Intent(activity, PreferenceActivity::class.java))
|
startActivity(Intent(activity, PreferenceActivity::class.java))
|
||||||
}
|
}
|
||||||
|
|
||||||
preferences.registerOnSharedPreferenceChangeListener(this)
|
prefs!!.registerOnSharedPreferenceChangeListener(this)
|
||||||
return binding.root
|
return binding.root
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -125,12 +125,13 @@ class NavDrawerFragment : Fragment(), SharedPreferences.OnSharedPreferenceChange
|
||||||
_binding = null
|
_binding = null
|
||||||
|
|
||||||
// scope.cancel()
|
// scope.cancel()
|
||||||
requireContext().getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE).unregisterOnSharedPreferenceChangeListener(this)
|
prefs!!.unregisterOnSharedPreferenceChangeListener(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun procFlowEvents() {
|
private fun procFlowEvents() {
|
||||||
lifecycleScope.launch {
|
lifecycleScope.launch {
|
||||||
EventFlow.events.collectLatest { event ->
|
EventFlow.events.collectLatest { event ->
|
||||||
|
Logd(TAG, "Received event: $event")
|
||||||
when (event) {
|
when (event) {
|
||||||
is FlowEvent.UnreadItemsUpdateEvent, is FlowEvent.FeedListUpdateEvent -> loadData()
|
is FlowEvent.UnreadItemsUpdateEvent, is FlowEvent.FeedListUpdateEvent -> loadData()
|
||||||
is FlowEvent.QueueEvent -> onQueueChanged(event)
|
is FlowEvent.QueueEvent -> onQueueChanged(event)
|
||||||
|
@ -292,6 +293,10 @@ class NavDrawerFragment : Fragment(), SharedPreferences.OnSharedPreferenceChange
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
const val PREF_NAME: String = "NavDrawerPrefs"
|
const val PREF_NAME: String = "NavDrawerPrefs"
|
||||||
const val TAG: String = "NavDrawerFragment"
|
const val TAG: String = "NavDrawerFragment"
|
||||||
|
var prefs: SharedPreferences? = null
|
||||||
|
fun getSharedPrefs(context: Context) {
|
||||||
|
if (prefs == null) prefs = context.getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE)
|
||||||
|
}
|
||||||
|
|
||||||
// caution: an array in re/values/arrays.xml relates to this
|
// caution: an array in re/values/arrays.xml relates to this
|
||||||
@JvmField
|
@JvmField
|
||||||
|
@ -301,15 +306,15 @@ class NavDrawerFragment : Fragment(), SharedPreferences.OnSharedPreferenceChange
|
||||||
QueueFragment.TAG,
|
QueueFragment.TAG,
|
||||||
AllEpisodesFragment.TAG,
|
AllEpisodesFragment.TAG,
|
||||||
DownloadsFragment.TAG,
|
DownloadsFragment.TAG,
|
||||||
PlaybackHistoryFragment.TAG,
|
HistoryFragment.TAG,
|
||||||
StatisticsFragment.TAG,
|
StatisticsFragment.TAG,
|
||||||
AddFeedFragment.TAG,
|
AddFeedFragment.TAG,
|
||||||
)
|
)
|
||||||
|
|
||||||
fun saveLastNavFragment(context: Context, tag: String?) {
|
fun saveLastNavFragment(context: Context, tag: String?) {
|
||||||
Logd(TAG, "saveLastNavFragment(tag: $tag)")
|
Logd(TAG, "saveLastNavFragment(tag: $tag)")
|
||||||
val prefs: SharedPreferences = context.getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE)
|
// val prefs: SharedPreferences = context.getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE)
|
||||||
val edit: SharedPreferences.Editor = prefs.edit()
|
val edit: SharedPreferences.Editor = prefs!!.edit()
|
||||||
if (tag != null) edit.putString(PREF_LAST_FRAGMENT_TAG, tag)
|
if (tag != null) edit.putString(PREF_LAST_FRAGMENT_TAG, tag)
|
||||||
else edit.remove(PREF_LAST_FRAGMENT_TAG)
|
else edit.remove(PREF_LAST_FRAGMENT_TAG)
|
||||||
|
|
||||||
|
@ -317,9 +322,9 @@ class NavDrawerFragment : Fragment(), SharedPreferences.OnSharedPreferenceChange
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getLastNavFragment(context: Context): String {
|
fun getLastNavFragment(context: Context): String {
|
||||||
val prefs: SharedPreferences = context.getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE)
|
// val prefs: SharedPreferences = context.getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE)
|
||||||
val lastFragment: String = prefs.getString(PREF_LAST_FRAGMENT_TAG, SubscriptionFragment.TAG)?:""
|
val lastFragment: String = prefs!!.getString(PREF_LAST_FRAGMENT_TAG, SubscriptionFragment.TAG)?:""
|
||||||
Logd(TAG, "getLastNavFragment() -> $lastFragment")
|
// Logd(TAG, "getLastNavFragment() -> $lastFragment")
|
||||||
return lastFragment
|
return lastFragment
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,7 +14,10 @@ import ac.mdiq.podcini.net.download.service.DownloadRequestCreator.create
|
||||||
import ac.mdiq.podcini.net.download.service.Downloader
|
import ac.mdiq.podcini.net.download.service.Downloader
|
||||||
import ac.mdiq.podcini.net.download.service.HttpDownloader
|
import ac.mdiq.podcini.net.download.service.HttpDownloader
|
||||||
import ac.mdiq.podcini.net.download.serviceinterface.DownloadServiceInterface
|
import ac.mdiq.podcini.net.download.serviceinterface.DownloadServiceInterface
|
||||||
|
import ac.mdiq.podcini.playback.service.PlaybackService
|
||||||
|
import ac.mdiq.podcini.playback.service.PlaybackService.Companion
|
||||||
import ac.mdiq.podcini.preferences.UserPreferences.isEnableAutodownload
|
import ac.mdiq.podcini.preferences.UserPreferences.isEnableAutodownload
|
||||||
|
import ac.mdiq.podcini.receiver.PlayerWidget.Companion.PREFS_NAME
|
||||||
import ac.mdiq.podcini.storage.DBReader
|
import ac.mdiq.podcini.storage.DBReader
|
||||||
import ac.mdiq.podcini.storage.DBTasks
|
import ac.mdiq.podcini.storage.DBTasks
|
||||||
import ac.mdiq.podcini.storage.DBWriter
|
import ac.mdiq.podcini.storage.DBWriter
|
||||||
|
@ -35,6 +38,7 @@ import android.app.Dialog
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Context.MODE_PRIVATE
|
import android.content.Context.MODE_PRIVATE
|
||||||
import android.content.DialogInterface
|
import android.content.DialogInterface
|
||||||
|
import android.content.SharedPreferences
|
||||||
import android.graphics.LightingColorFilter
|
import android.graphics.LightingColorFilter
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.text.Spannable
|
import android.text.Spannable
|
||||||
|
@ -330,6 +334,7 @@ import kotlin.concurrent.Volatile
|
||||||
@OptIn(UnstableApi::class) private fun procFlowEvents() {
|
@OptIn(UnstableApi::class) private fun procFlowEvents() {
|
||||||
lifecycleScope.launch {
|
lifecycleScope.launch {
|
||||||
EventFlow.events.collectLatest { event ->
|
EventFlow.events.collectLatest { event ->
|
||||||
|
Logd(TAG, "Received event: $event")
|
||||||
when (event) {
|
when (event) {
|
||||||
is FlowEvent.FeedListUpdateEvent -> onFeedListChanged(event)
|
is FlowEvent.FeedListUpdateEvent -> onFeedListChanged(event)
|
||||||
else -> {}
|
else -> {}
|
||||||
|
@ -507,8 +512,8 @@ import kotlin.concurrent.Volatile
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isEnableAutodownload) {
|
if (isEnableAutodownload) {
|
||||||
val preferences = requireContext().getSharedPreferences(PREFS, MODE_PRIVATE)
|
// val preferences = requireContext().getSharedPreferences(PREFS, MODE_PRIVATE)
|
||||||
binding.autoDownloadCheckBox.isChecked = preferences.getBoolean(PREF_LAST_AUTO_DOWNLOAD, true)
|
binding.autoDownloadCheckBox.isChecked = prefs!!.getBoolean(PREF_LAST_AUTO_DOWNLOAD, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (alternateFeedUrls.isEmpty()) {
|
if (alternateFeedUrls.isEmpty()) {
|
||||||
|
@ -587,8 +592,8 @@ import kotlin.concurrent.Volatile
|
||||||
val autoDownload = binding.autoDownloadCheckBox.isChecked
|
val autoDownload = binding.autoDownloadCheckBox.isChecked
|
||||||
feedPreferences.autoDownload = autoDownload
|
feedPreferences.autoDownload = autoDownload
|
||||||
|
|
||||||
val preferences = requireContext().getSharedPreferences(PREFS, MODE_PRIVATE)
|
// val preferences = requireContext().getSharedPreferences(PREFS, MODE_PRIVATE)
|
||||||
val editor = preferences.edit()
|
val editor = prefs!!.edit()
|
||||||
editor.putBoolean(PREF_LAST_AUTO_DOWNLOAD, autoDownload)
|
editor.putBoolean(PREF_LAST_AUTO_DOWNLOAD, autoDownload)
|
||||||
editor.apply()
|
editor.apply()
|
||||||
}
|
}
|
||||||
|
@ -738,6 +743,12 @@ import kotlin.concurrent.Volatile
|
||||||
private const val PREF_LAST_AUTO_DOWNLOAD = "lastAutoDownload"
|
private const val PREF_LAST_AUTO_DOWNLOAD = "lastAutoDownload"
|
||||||
private const val KEY_UP_ARROW = "up_arrow"
|
private const val KEY_UP_ARROW = "up_arrow"
|
||||||
|
|
||||||
|
var prefs: SharedPreferences? = null
|
||||||
|
|
||||||
|
fun getSharedPrefs(context: Context) {
|
||||||
|
if (prefs == null) prefs = context.getSharedPreferences(PREFS, Context.MODE_PRIVATE)
|
||||||
|
}
|
||||||
|
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun newInstance(feedUrl: String): OnlineFeedViewFragment {
|
fun newInstance(feedUrl: String): OnlineFeedViewFragment {
|
||||||
val fragment = OnlineFeedViewFragment()
|
val fragment = OnlineFeedViewFragment()
|
||||||
|
|
|
@ -4,6 +4,8 @@ import ac.mdiq.podcini.R
|
||||||
import ac.mdiq.podcini.databinding.PlayerDetailsFragmentBinding
|
import ac.mdiq.podcini.databinding.PlayerDetailsFragmentBinding
|
||||||
import ac.mdiq.podcini.feed.util.ImageResourceUtils
|
import ac.mdiq.podcini.feed.util.ImageResourceUtils
|
||||||
import ac.mdiq.podcini.playback.PlaybackController
|
import ac.mdiq.podcini.playback.PlaybackController
|
||||||
|
import ac.mdiq.podcini.playback.service.PlaybackService
|
||||||
|
import ac.mdiq.podcini.playback.service.PlaybackService.Companion
|
||||||
import ac.mdiq.podcini.storage.DBReader
|
import ac.mdiq.podcini.storage.DBReader
|
||||||
import ac.mdiq.podcini.storage.DBWriter.persistFeedItem
|
import ac.mdiq.podcini.storage.DBWriter.persistFeedItem
|
||||||
import ac.mdiq.podcini.storage.model.feed.Chapter
|
import ac.mdiq.podcini.storage.model.feed.Chapter
|
||||||
|
@ -11,6 +13,8 @@ import ac.mdiq.podcini.storage.model.feed.EmbeddedChapterImage
|
||||||
import ac.mdiq.podcini.storage.model.feed.FeedItem
|
import ac.mdiq.podcini.storage.model.feed.FeedItem
|
||||||
import ac.mdiq.podcini.storage.model.feed.FeedMedia
|
import ac.mdiq.podcini.storage.model.feed.FeedMedia
|
||||||
import ac.mdiq.podcini.storage.model.playback.Playable
|
import ac.mdiq.podcini.storage.model.playback.Playable
|
||||||
|
import ac.mdiq.podcini.ui.actions.swipeactions.SwipeActions
|
||||||
|
import ac.mdiq.podcini.ui.actions.swipeactions.SwipeActions.Companion.SWIPE_ACTIONS_PREF_NAME
|
||||||
import ac.mdiq.podcini.ui.activity.MainActivity
|
import ac.mdiq.podcini.ui.activity.MainActivity
|
||||||
import ac.mdiq.podcini.ui.utils.ShownotesCleaner
|
import ac.mdiq.podcini.ui.utils.ShownotesCleaner
|
||||||
import ac.mdiq.podcini.ui.view.ShownotesWebView
|
import ac.mdiq.podcini.ui.view.ShownotesWebView
|
||||||
|
@ -25,9 +29,7 @@ import android.animation.AnimatorListenerAdapter
|
||||||
import android.animation.AnimatorSet
|
import android.animation.AnimatorSet
|
||||||
import android.animation.ObjectAnimator
|
import android.animation.ObjectAnimator
|
||||||
import android.app.Activity
|
import android.app.Activity
|
||||||
import android.content.ClipData
|
import android.content.*
|
||||||
import android.content.ClipboardManager
|
|
||||||
import android.content.Intent
|
|
||||||
import android.graphics.ColorFilter
|
import android.graphics.ColorFilter
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
|
@ -409,8 +411,8 @@ class PlayerDetailsFragment : Fragment() {
|
||||||
|
|
||||||
@UnstableApi private fun savePreference() {
|
@UnstableApi private fun savePreference() {
|
||||||
Logd(TAG, "Saving preferences")
|
Logd(TAG, "Saving preferences")
|
||||||
val prefs = requireActivity().getSharedPreferences(PREF, Activity.MODE_PRIVATE)
|
// val prefs = requireActivity().getSharedPreferences(PREF, Activity.MODE_PRIVATE)
|
||||||
val editor = prefs.edit()
|
val editor = prefs!!.edit()
|
||||||
if (controller?.getMedia() != null) {
|
if (controller?.getMedia() != null) {
|
||||||
Logd(TAG, "Saving scroll position: " + binding.itemDescriptionFragment.scrollY)
|
Logd(TAG, "Saving scroll position: " + binding.itemDescriptionFragment.scrollY)
|
||||||
editor.putInt(PREF_SCROLL_Y, binding.itemDescriptionFragment.scrollY)
|
editor.putInt(PREF_SCROLL_Y, binding.itemDescriptionFragment.scrollY)
|
||||||
|
@ -429,9 +431,9 @@ class PlayerDetailsFragment : Fragment() {
|
||||||
Logd(TAG, "Restoring from preferences")
|
Logd(TAG, "Restoring from preferences")
|
||||||
val activity: Activity? = activity
|
val activity: Activity? = activity
|
||||||
if (activity != null) {
|
if (activity != null) {
|
||||||
val prefs = activity.getSharedPreferences(PREF, Activity.MODE_PRIVATE)
|
// val prefs = activity.getSharedPreferences(PREF, Activity.MODE_PRIVATE)
|
||||||
val id = prefs.getString(PREF_PLAYABLE_ID, "")
|
val id = prefs!!.getString(PREF_PLAYABLE_ID, "")
|
||||||
val scrollY = prefs.getInt(PREF_SCROLL_Y, -1)
|
val scrollY = prefs!!.getInt(PREF_SCROLL_Y, -1)
|
||||||
if (scrollY != -1) {
|
if (scrollY != -1) {
|
||||||
if (id == controller?.getMedia()?.getIdentifier()?.toString()) {
|
if (id == controller?.getMedia()?.getIdentifier()?.toString()) {
|
||||||
Logd(TAG, "Restored scroll Position: $scrollY")
|
Logd(TAG, "Restored scroll Position: $scrollY")
|
||||||
|
@ -455,6 +457,7 @@ class PlayerDetailsFragment : Fragment() {
|
||||||
private fun procFlowEvents() {
|
private fun procFlowEvents() {
|
||||||
lifecycleScope.launch {
|
lifecycleScope.launch {
|
||||||
EventFlow.events.collectLatest { event ->
|
EventFlow.events.collectLatest { event ->
|
||||||
|
Logd(TAG, "Received event: $event")
|
||||||
when (event) {
|
when (event) {
|
||||||
is FlowEvent.PlaybackPositionEvent -> onEventMainThread(event)
|
is FlowEvent.PlaybackPositionEvent -> onEventMainThread(event)
|
||||||
else -> {}
|
else -> {}
|
||||||
|
@ -525,5 +528,10 @@ class PlayerDetailsFragment : Fragment() {
|
||||||
private const val PREF = "ItemDescriptionFragmentPrefs"
|
private const val PREF = "ItemDescriptionFragmentPrefs"
|
||||||
private const val PREF_SCROLL_Y = "prefScrollY"
|
private const val PREF_SCROLL_Y = "prefScrollY"
|
||||||
private const val PREF_PLAYABLE_ID = "prefPlayableId"
|
private const val PREF_PLAYABLE_ID = "prefPlayableId"
|
||||||
|
|
||||||
|
var prefs: SharedPreferences? = null
|
||||||
|
fun getSharedPrefs(context: Context) {
|
||||||
|
if (prefs == null) prefs = context.getSharedPreferences(PREF, Context.MODE_PRIVATE)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,9 +21,9 @@ import ac.mdiq.podcini.ui.adapter.QueueRecyclerAdapter
|
||||||
import ac.mdiq.podcini.ui.adapter.SelectableAdapter
|
import ac.mdiq.podcini.ui.adapter.SelectableAdapter
|
||||||
import ac.mdiq.podcini.ui.dialog.ConfirmationDialog
|
import ac.mdiq.podcini.ui.dialog.ConfirmationDialog
|
||||||
import ac.mdiq.podcini.ui.dialog.ItemSortDialog
|
import ac.mdiq.podcini.ui.dialog.ItemSortDialog
|
||||||
import ac.mdiq.podcini.ui.view.EmptyViewHandler
|
import ac.mdiq.podcini.ui.utils.EmptyViewHandler
|
||||||
import ac.mdiq.podcini.ui.view.EpisodeItemListRecyclerView
|
import ac.mdiq.podcini.ui.view.EpisodeItemListRecyclerView
|
||||||
import ac.mdiq.podcini.ui.view.LiftOnScrollListener
|
import ac.mdiq.podcini.ui.utils.LiftOnScrollListener
|
||||||
import ac.mdiq.podcini.ui.view.viewholder.EpisodeItemViewHolder
|
import ac.mdiq.podcini.ui.view.viewholder.EpisodeItemViewHolder
|
||||||
import ac.mdiq.podcini.util.Converter
|
import ac.mdiq.podcini.util.Converter
|
||||||
import ac.mdiq.podcini.util.FeedItemUtil
|
import ac.mdiq.podcini.util.FeedItemUtil
|
||||||
|
@ -72,7 +72,6 @@ import java.util.*
|
||||||
private lateinit var toolbar: MaterialToolbar
|
private lateinit var toolbar: MaterialToolbar
|
||||||
private lateinit var swipeRefreshLayout: SwipeRefreshLayout
|
private lateinit var swipeRefreshLayout: SwipeRefreshLayout
|
||||||
private lateinit var swipeActions: SwipeActions
|
private lateinit var swipeActions: SwipeActions
|
||||||
private lateinit var prefs: SharedPreferences
|
|
||||||
private lateinit var speedDialView: SpeedDialView
|
private lateinit var speedDialView: SpeedDialView
|
||||||
private lateinit var progressBar: ProgressBar
|
private lateinit var progressBar: ProgressBar
|
||||||
|
|
||||||
|
@ -88,7 +87,7 @@ import java.util.*
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
retainInstance = true
|
retainInstance = true
|
||||||
prefs = requireActivity().getSharedPreferences(PREFS, Context.MODE_PRIVATE)
|
// ioScope.launch { prefs = requireActivity().getSharedPreferences(PREFS, Context.MODE_PRIVATE) }
|
||||||
}
|
}
|
||||||
|
|
||||||
@UnstableApi override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
|
@UnstableApi override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
|
||||||
|
@ -122,15 +121,12 @@ import java.util.*
|
||||||
recyclerView.addOnScrollListener(LiftOnScrollListener(binding.appbar))
|
recyclerView.addOnScrollListener(LiftOnScrollListener(binding.appbar))
|
||||||
|
|
||||||
swipeActions = QueueSwipeActions()
|
swipeActions = QueueSwipeActions()
|
||||||
|
lifecycle.addObserver(swipeActions)
|
||||||
swipeActions.setFilter(FeedItemFilter(FeedItemFilter.QUEUED))
|
swipeActions.setFilter(FeedItemFilter(FeedItemFilter.QUEUED))
|
||||||
swipeActions.attachTo(recyclerView)
|
swipeActions.attachTo(recyclerView)
|
||||||
refreshSwipeTelltale()
|
refreshSwipeTelltale()
|
||||||
binding.leftActionIcon.setOnClickListener {
|
binding.leftActionIcon.setOnClickListener { swipeActions.showDialog() }
|
||||||
swipeActions.showDialog()
|
binding.rightActionIcon.setOnClickListener { swipeActions.showDialog() }
|
||||||
}
|
|
||||||
binding.rightActionIcon.setOnClickListener {
|
|
||||||
swipeActions.showDialog()
|
|
||||||
}
|
|
||||||
|
|
||||||
recyclerAdapter = object : QueueRecyclerAdapter(activity as MainActivity, swipeActions) {
|
recyclerAdapter = object : QueueRecyclerAdapter(activity as MainActivity, swipeActions) {
|
||||||
override fun onCreateContextMenu(menu: ContextMenu, v: View, menuInfo: ContextMenu.ContextMenuInfo?) {
|
override fun onCreateContextMenu(menu: ContextMenu, v: View, menuInfo: ContextMenu.ContextMenuInfo?) {
|
||||||
|
@ -143,9 +139,7 @@ import java.util.*
|
||||||
|
|
||||||
swipeRefreshLayout = binding.swipeRefresh
|
swipeRefreshLayout = binding.swipeRefresh
|
||||||
swipeRefreshLayout.setDistanceToTriggerSync(resources.getInteger(R.integer.swipe_refresh_distance))
|
swipeRefreshLayout.setDistanceToTriggerSync(resources.getInteger(R.integer.swipe_refresh_distance))
|
||||||
swipeRefreshLayout.setOnRefreshListener {
|
swipeRefreshLayout.setOnRefreshListener { FeedUpdateManager.runOnceOrAsk(requireContext()) }
|
||||||
FeedUpdateManager.runOnceOrAsk(requireContext())
|
|
||||||
}
|
|
||||||
|
|
||||||
emptyView = EmptyViewHandler(requireContext())
|
emptyView = EmptyViewHandler(requireContext())
|
||||||
emptyView.attachToRecyclerView(recyclerView)
|
emptyView.attachToRecyclerView(recyclerView)
|
||||||
|
@ -200,6 +194,7 @@ import java.util.*
|
||||||
private fun procFlowEvents() {
|
private fun procFlowEvents() {
|
||||||
lifecycleScope.launch {
|
lifecycleScope.launch {
|
||||||
EventFlow.events.collectLatest { event ->
|
EventFlow.events.collectLatest { event ->
|
||||||
|
Logd(TAG, "Received event: $event")
|
||||||
when (event) {
|
when (event) {
|
||||||
is FlowEvent.QueueEvent -> onEventMainThread(event)
|
is FlowEvent.QueueEvent -> onEventMainThread(event)
|
||||||
is FlowEvent.FeedItemEvent -> onEventMainThread(event)
|
is FlowEvent.FeedItemEvent -> onEventMainThread(event)
|
||||||
|
@ -406,7 +401,7 @@ import java.util.*
|
||||||
val isLocked: Boolean = UserPreferences.isQueueLocked
|
val isLocked: Boolean = UserPreferences.isQueueLocked
|
||||||
if (isLocked) setQueueLocked(false)
|
if (isLocked) setQueueLocked(false)
|
||||||
else {
|
else {
|
||||||
val shouldShowLockWarning: Boolean = prefs.getBoolean(PREF_SHOW_LOCK_WARNING, true)
|
val shouldShowLockWarning: Boolean = prefs!!.getBoolean(PREF_SHOW_LOCK_WARNING, true)
|
||||||
if (!shouldShowLockWarning) setQueueLocked(true)
|
if (!shouldShowLockWarning) setQueueLocked(true)
|
||||||
else {
|
else {
|
||||||
val builder = MaterialAlertDialogBuilder(requireContext())
|
val builder = MaterialAlertDialogBuilder(requireContext())
|
||||||
|
@ -419,7 +414,7 @@ import java.util.*
|
||||||
builder.setView(view)
|
builder.setView(view)
|
||||||
|
|
||||||
builder.setPositiveButton(R.string.lock_queue) { _: DialogInterface?, _: Int ->
|
builder.setPositiveButton(R.string.lock_queue) { _: DialogInterface?, _: Int ->
|
||||||
prefs.edit().putBoolean(PREF_SHOW_LOCK_WARNING, !checkDoNotShowAgain.isChecked).apply()
|
prefs!!.edit().putBoolean(PREF_SHOW_LOCK_WARNING, !checkDoNotShowAgain.isChecked).apply()
|
||||||
setQueueLocked(true)
|
setQueueLocked(true)
|
||||||
}
|
}
|
||||||
builder.setNegativeButton(R.string.cancel_label, null)
|
builder.setNegativeButton(R.string.cancel_label, null)
|
||||||
|
@ -586,7 +581,6 @@ import java.util.*
|
||||||
|
|
||||||
// Update tracked position
|
// Update tracked position
|
||||||
if (dragFrom == -1) dragFrom = fromPosition
|
if (dragFrom == -1) dragFrom = fromPosition
|
||||||
|
|
||||||
dragTo = toPosition
|
dragTo = toPosition
|
||||||
|
|
||||||
val from = viewHolder.bindingAdapterPosition
|
val from = viewHolder.bindingAdapterPosition
|
||||||
|
@ -632,5 +626,11 @@ import java.util.*
|
||||||
|
|
||||||
private const val PREFS = "QueueFragment"
|
private const val PREFS = "QueueFragment"
|
||||||
private const val PREF_SHOW_LOCK_WARNING = "show_lock_warning"
|
private const val PREF_SHOW_LOCK_WARNING = "show_lock_warning"
|
||||||
|
|
||||||
|
private var prefs: SharedPreferences? = null
|
||||||
|
fun getSharedPrefs(context: Context) {
|
||||||
|
if (prefs == null) prefs = context.getSharedPreferences(PREFS, Context.MODE_PRIVATE)
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,6 +4,7 @@ import ac.mdiq.podcini.BuildConfig
|
||||||
import ac.mdiq.podcini.R
|
import ac.mdiq.podcini.R
|
||||||
import ac.mdiq.podcini.databinding.QuickFeedDiscoveryBinding
|
import ac.mdiq.podcini.databinding.QuickFeedDiscoveryBinding
|
||||||
import ac.mdiq.podcini.net.discovery.ItunesTopListLoader
|
import ac.mdiq.podcini.net.discovery.ItunesTopListLoader
|
||||||
|
import ac.mdiq.podcini.net.discovery.ItunesTopListLoader.Companion.prefs
|
||||||
import ac.mdiq.podcini.net.discovery.PodcastSearchResult
|
import ac.mdiq.podcini.net.discovery.PodcastSearchResult
|
||||||
import ac.mdiq.podcini.storage.DBReader
|
import ac.mdiq.podcini.storage.DBReader
|
||||||
import ac.mdiq.podcini.ui.activity.MainActivity
|
import ac.mdiq.podcini.ui.activity.MainActivity
|
||||||
|
@ -11,8 +12,6 @@ import ac.mdiq.podcini.ui.adapter.FeedDiscoverAdapter
|
||||||
import ac.mdiq.podcini.util.Logd
|
import ac.mdiq.podcini.util.Logd
|
||||||
import ac.mdiq.podcini.util.event.EventFlow
|
import ac.mdiq.podcini.util.event.EventFlow
|
||||||
import ac.mdiq.podcini.util.event.FlowEvent
|
import ac.mdiq.podcini.util.event.FlowEvent
|
||||||
import android.content.Context
|
|
||||||
import android.content.SharedPreferences
|
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.util.DisplayMetrics
|
import android.util.DisplayMetrics
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
|
@ -96,6 +95,7 @@ class QuickFeedDiscoveryFragment : Fragment(), AdapterView.OnItemClickListener {
|
||||||
private fun procFlowEvents() {
|
private fun procFlowEvents() {
|
||||||
lifecycleScope.launch {
|
lifecycleScope.launch {
|
||||||
EventFlow.events.collectLatest { event ->
|
EventFlow.events.collectLatest { event ->
|
||||||
|
Logd(TAG, "Received event: $event")
|
||||||
when (event) {
|
when (event) {
|
||||||
is FlowEvent.DiscoveryDefaultUpdateEvent -> loadToplist()
|
is FlowEvent.DiscoveryDefaultUpdateEvent -> loadToplist()
|
||||||
else -> {}
|
else -> {}
|
||||||
|
@ -111,9 +111,9 @@ class QuickFeedDiscoveryFragment : Fragment(), AdapterView.OnItemClickListener {
|
||||||
poweredByTextView.visibility = View.VISIBLE
|
poweredByTextView.visibility = View.VISIBLE
|
||||||
|
|
||||||
val loader = ItunesTopListLoader(requireContext())
|
val loader = ItunesTopListLoader(requireContext())
|
||||||
val prefs: SharedPreferences = requireActivity().getSharedPreferences(ItunesTopListLoader.PREFS, Context.MODE_PRIVATE)
|
// val prefs: SharedPreferences = requireActivity().getSharedPreferences(ItunesTopListLoader.PREFS, Context.MODE_PRIVATE)
|
||||||
val countryCode: String = prefs.getString(ItunesTopListLoader.PREF_KEY_COUNTRY_CODE, Locale.getDefault().country)!!
|
val countryCode: String = prefs!!.getString(ItunesTopListLoader.PREF_KEY_COUNTRY_CODE, Locale.getDefault().country)!!
|
||||||
if (prefs.getBoolean(ItunesTopListLoader.PREF_KEY_HIDDEN_DISCOVERY_COUNTRY, false)) {
|
if (prefs!!.getBoolean(ItunesTopListLoader.PREF_KEY_HIDDEN_DISCOVERY_COUNTRY, false)) {
|
||||||
errorTextView.setText(R.string.discover_is_hidden)
|
errorTextView.setText(R.string.discover_is_hidden)
|
||||||
errorView.visibility = View.VISIBLE
|
errorView.visibility = View.VISIBLE
|
||||||
discoverGridLayout.visibility = View.GONE
|
discoverGridLayout.visibility = View.GONE
|
||||||
|
@ -121,7 +121,7 @@ class QuickFeedDiscoveryFragment : Fragment(), AdapterView.OnItemClickListener {
|
||||||
poweredByTextView.visibility = View.GONE
|
poweredByTextView.visibility = View.GONE
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if (BuildConfig.FLAVOR == "free" && prefs.getBoolean(ItunesTopListLoader.PREF_KEY_NEEDS_CONFIRM, true)) {
|
if (BuildConfig.FLAVOR == "free" && prefs!!.getBoolean(ItunesTopListLoader.PREF_KEY_NEEDS_CONFIRM, true)) {
|
||||||
errorTextView.text = ""
|
errorTextView.text = ""
|
||||||
errorView.visibility = View.VISIBLE
|
errorView.visibility = View.VISIBLE
|
||||||
discoverGridLayout.visibility = View.VISIBLE
|
discoverGridLayout.visibility = View.VISIBLE
|
||||||
|
@ -129,7 +129,7 @@ class QuickFeedDiscoveryFragment : Fragment(), AdapterView.OnItemClickListener {
|
||||||
errorRetry.setText(R.string.discover_confirm)
|
errorRetry.setText(R.string.discover_confirm)
|
||||||
poweredByTextView.visibility = View.VISIBLE
|
poweredByTextView.visibility = View.VISIBLE
|
||||||
errorRetry.setOnClickListener {
|
errorRetry.setOnClickListener {
|
||||||
prefs.edit().putBoolean(ItunesTopListLoader.PREF_KEY_NEEDS_CONFIRM, false).apply()
|
prefs!!.edit().putBoolean(ItunesTopListLoader.PREF_KEY_NEEDS_CONFIRM, false).apply()
|
||||||
loadToplist()
|
loadToplist()
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
|
@ -185,7 +185,6 @@ class QuickFeedDiscoveryFragment : Fragment(), AdapterView.OnItemClickListener {
|
||||||
errorRetry.setOnClickListener { loadToplist() }
|
errorRetry.setOnClickListener { loadToplist() }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@OptIn(UnstableApi::class) override fun onItemClick(parent: AdapterView<*>?, view: View, position: Int, id: Long) {
|
@OptIn(UnstableApi::class) override fun onItemClick(parent: AdapterView<*>?, view: View, position: Int, id: Long) {
|
||||||
|
|
|
@ -16,9 +16,9 @@ import ac.mdiq.podcini.ui.activity.MainActivity
|
||||||
import ac.mdiq.podcini.ui.adapter.EpisodeItemListAdapter
|
import ac.mdiq.podcini.ui.adapter.EpisodeItemListAdapter
|
||||||
import ac.mdiq.podcini.ui.adapter.HorizontalFeedListAdapter
|
import ac.mdiq.podcini.ui.adapter.HorizontalFeedListAdapter
|
||||||
import ac.mdiq.podcini.ui.adapter.SelectableAdapter
|
import ac.mdiq.podcini.ui.adapter.SelectableAdapter
|
||||||
import ac.mdiq.podcini.ui.view.EmptyViewHandler
|
import ac.mdiq.podcini.ui.utils.EmptyViewHandler
|
||||||
import ac.mdiq.podcini.ui.view.EpisodeItemListRecyclerView
|
import ac.mdiq.podcini.ui.view.EpisodeItemListRecyclerView
|
||||||
import ac.mdiq.podcini.ui.view.LiftOnScrollListener
|
import ac.mdiq.podcini.ui.utils.LiftOnScrollListener
|
||||||
import ac.mdiq.podcini.ui.view.viewholder.EpisodeItemViewHolder
|
import ac.mdiq.podcini.ui.view.viewholder.EpisodeItemViewHolder
|
||||||
import ac.mdiq.podcini.util.FeedItemUtil
|
import ac.mdiq.podcini.util.FeedItemUtil
|
||||||
import ac.mdiq.podcini.util.Logd
|
import ac.mdiq.podcini.util.Logd
|
||||||
|
@ -239,6 +239,7 @@ import kotlinx.coroutines.withContext
|
||||||
private fun procFlowEvents() {
|
private fun procFlowEvents() {
|
||||||
lifecycleScope.launch {
|
lifecycleScope.launch {
|
||||||
EventFlow.events.collectLatest { event ->
|
EventFlow.events.collectLatest { event ->
|
||||||
|
Logd(TAG, "Received event: $event")
|
||||||
when (event) {
|
when (event) {
|
||||||
is FlowEvent.FeedListUpdateEvent, is FlowEvent.UnreadItemsUpdateEvent, is FlowEvent.PlayerStatusEvent -> search()
|
is FlowEvent.FeedListUpdateEvent, is FlowEvent.UnreadItemsUpdateEvent, is FlowEvent.PlayerStatusEvent -> search()
|
||||||
is FlowEvent.FeedItemEvent -> onEventMainThread(event)
|
is FlowEvent.FeedItemEvent -> onEventMainThread(event)
|
||||||
|
|
|
@ -16,13 +16,11 @@ import ac.mdiq.podcini.ui.adapter.SelectableAdapter
|
||||||
import ac.mdiq.podcini.ui.adapter.SubscriptionsAdapter
|
import ac.mdiq.podcini.ui.adapter.SubscriptionsAdapter
|
||||||
import ac.mdiq.podcini.ui.dialog.FeedSortDialog
|
import ac.mdiq.podcini.ui.dialog.FeedSortDialog
|
||||||
import ac.mdiq.podcini.ui.dialog.SubscriptionsFilterDialog
|
import ac.mdiq.podcini.ui.dialog.SubscriptionsFilterDialog
|
||||||
import ac.mdiq.podcini.ui.view.EmptyViewHandler
|
import ac.mdiq.podcini.ui.utils.EmptyViewHandler
|
||||||
import ac.mdiq.podcini.ui.view.LiftOnScrollListener
|
import ac.mdiq.podcini.ui.utils.LiftOnScrollListener
|
||||||
import ac.mdiq.podcini.util.Logd
|
import ac.mdiq.podcini.util.Logd
|
||||||
import ac.mdiq.podcini.util.event.EventFlow
|
import ac.mdiq.podcini.util.event.EventFlow
|
||||||
import ac.mdiq.podcini.util.event.FlowEvent
|
import ac.mdiq.podcini.util.event.FlowEvent
|
||||||
import android.content.Context
|
|
||||||
import android.content.SharedPreferences
|
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import android.view.*
|
import android.view.*
|
||||||
|
@ -62,7 +60,7 @@ class SubscriptionFragment : Fragment(), Toolbar.OnMenuItemClickListener, Select
|
||||||
private lateinit var toolbar: MaterialToolbar
|
private lateinit var toolbar: MaterialToolbar
|
||||||
private lateinit var swipeRefreshLayout: SwipeRefreshLayout
|
private lateinit var swipeRefreshLayout: SwipeRefreshLayout
|
||||||
private lateinit var progressBar: ProgressBar
|
private lateinit var progressBar: ProgressBar
|
||||||
private lateinit var prefs: SharedPreferences
|
// private lateinit var prefs: SharedPreferences
|
||||||
private lateinit var speedDialView: SpeedDialView
|
private lateinit var speedDialView: SpeedDialView
|
||||||
|
|
||||||
private var tagFilterIndex = 1
|
private var tagFilterIndex = 1
|
||||||
|
@ -78,7 +76,7 @@ class SubscriptionFragment : Fragment(), Toolbar.OnMenuItemClickListener, Select
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
retainInstance = true
|
retainInstance = true
|
||||||
|
|
||||||
prefs = requireActivity().getSharedPreferences(PREFS, Context.MODE_PRIVATE)
|
// prefs = requireActivity().getSharedPreferences(PREFS, Context.MODE_PRIVATE)
|
||||||
}
|
}
|
||||||
|
|
||||||
@UnstableApi override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
|
@UnstableApi override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
|
||||||
|
@ -236,6 +234,7 @@ class SubscriptionFragment : Fragment(), Toolbar.OnMenuItemClickListener, Select
|
||||||
private fun procFlowEvents() {
|
private fun procFlowEvents() {
|
||||||
lifecycleScope.launch {
|
lifecycleScope.launch {
|
||||||
EventFlow.events.collectLatest { event ->
|
EventFlow.events.collectLatest { event ->
|
||||||
|
Logd(TAG, "Received event: $event")
|
||||||
when (event) {
|
when (event) {
|
||||||
is FlowEvent.FeedListUpdateEvent -> onFeedListChanged(event)
|
is FlowEvent.FeedListUpdateEvent -> onFeedListChanged(event)
|
||||||
is FlowEvent.UnreadItemsUpdateEvent -> loadSubscriptions()
|
is FlowEvent.UnreadItemsUpdateEvent -> loadSubscriptions()
|
||||||
|
|
|
@ -8,6 +8,8 @@ import ac.mdiq.podcini.playback.PlaybackController.Companion.setVideoSurface
|
||||||
import ac.mdiq.podcini.playback.PlaybackController.Companion.videoSize
|
import ac.mdiq.podcini.playback.PlaybackController.Companion.videoSize
|
||||||
import ac.mdiq.podcini.playback.base.MediaPlayerBase
|
import ac.mdiq.podcini.playback.base.MediaPlayerBase
|
||||||
import ac.mdiq.podcini.playback.base.PlayerStatus
|
import ac.mdiq.podcini.playback.base.PlayerStatus
|
||||||
|
import ac.mdiq.podcini.playback.service.PlaybackService
|
||||||
|
import ac.mdiq.podcini.playback.service.PlaybackService.Companion
|
||||||
import ac.mdiq.podcini.preferences.UserPreferences.fastForwardSecs
|
import ac.mdiq.podcini.preferences.UserPreferences.fastForwardSecs
|
||||||
import ac.mdiq.podcini.preferences.UserPreferences.rewindSecs
|
import ac.mdiq.podcini.preferences.UserPreferences.rewindSecs
|
||||||
import ac.mdiq.podcini.preferences.UserPreferences.setShowRemainTimeSetting
|
import ac.mdiq.podcini.preferences.UserPreferences.setShowRemainTimeSetting
|
||||||
|
@ -155,6 +157,7 @@ class VideoEpisodeFragment : Fragment(), OnSeekBarChangeListener {
|
||||||
private fun procFlowEvents() {
|
private fun procFlowEvents() {
|
||||||
lifecycleScope.launch {
|
lifecycleScope.launch {
|
||||||
EventFlow.events.collectLatest { event ->
|
EventFlow.events.collectLatest { event ->
|
||||||
|
Logd(TAG, "Received event: $event")
|
||||||
when (event) {
|
when (event) {
|
||||||
is FlowEvent.BufferUpdateEvent -> bufferUpdate(event)
|
is FlowEvent.BufferUpdateEvent -> bufferUpdate(event)
|
||||||
is FlowEvent.PlaybackPositionEvent -> onPositionObserverUpdate()
|
is FlowEvent.PlaybackPositionEvent -> onPositionObserverUpdate()
|
||||||
|
|
|
@ -3,6 +3,7 @@ package ac.mdiq.podcini.ui.statistics
|
||||||
|
|
||||||
import ac.mdiq.podcini.R
|
import ac.mdiq.podcini.R
|
||||||
import ac.mdiq.podcini.databinding.PagerFragmentBinding
|
import ac.mdiq.podcini.databinding.PagerFragmentBinding
|
||||||
|
import ac.mdiq.podcini.receiver.PlayerWidget.Companion.PREFS_NAME
|
||||||
import ac.mdiq.podcini.storage.DBWriter
|
import ac.mdiq.podcini.storage.DBWriter
|
||||||
import ac.mdiq.podcini.ui.activity.MainActivity
|
import ac.mdiq.podcini.ui.activity.MainActivity
|
||||||
import ac.mdiq.podcini.ui.dialog.ConfirmationDialog
|
import ac.mdiq.podcini.ui.dialog.ConfirmationDialog
|
||||||
|
@ -14,6 +15,7 @@ import ac.mdiq.podcini.util.event.EventFlow
|
||||||
import ac.mdiq.podcini.util.event.FlowEvent
|
import ac.mdiq.podcini.util.event.FlowEvent
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.DialogInterface
|
import android.content.DialogInterface
|
||||||
|
import android.content.SharedPreferences
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
|
@ -98,7 +100,7 @@ class StatisticsFragment : PagedToolbarFragment() {
|
||||||
}
|
}
|
||||||
|
|
||||||
@UnstableApi private fun doResetStatistics() {
|
@UnstableApi private fun doResetStatistics() {
|
||||||
requireContext().getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE).edit()
|
prefs!!.edit()
|
||||||
.putBoolean(PREF_INCLUDE_MARKED_PLAYED, false)
|
.putBoolean(PREF_INCLUDE_MARKED_PLAYED, false)
|
||||||
.putLong(PREF_FILTER_FROM, 0)
|
.putLong(PREF_FILTER_FROM, 0)
|
||||||
.putLong(PREF_FILTER_TO, Long.MAX_VALUE)
|
.putLong(PREF_FILTER_TO, Long.MAX_VALUE)
|
||||||
|
@ -146,10 +148,16 @@ class StatisticsFragment : PagedToolbarFragment() {
|
||||||
const val PREF_FILTER_FROM: String = "filterFrom"
|
const val PREF_FILTER_FROM: String = "filterFrom"
|
||||||
const val PREF_FILTER_TO: String = "filterTo"
|
const val PREF_FILTER_TO: String = "filterTo"
|
||||||
|
|
||||||
|
|
||||||
private const val POS_SUBSCRIPTIONS = 0
|
private const val POS_SUBSCRIPTIONS = 0
|
||||||
private const val POS_YEARS = 1
|
private const val POS_YEARS = 1
|
||||||
private const val POS_SPACE_TAKEN = 2
|
private const val POS_SPACE_TAKEN = 2
|
||||||
private const val TOTAL_COUNT = 3
|
private const val TOTAL_COUNT = 3
|
||||||
|
|
||||||
|
var prefs: SharedPreferences? = null
|
||||||
|
|
||||||
|
fun getSharedPrefs(context: Context) {
|
||||||
|
if (prefs == null) prefs = context.getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE)
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,7 +28,6 @@ abstract class DatesFilterDialog(private val context: Context, oldestDate: Long)
|
||||||
protected val filterDatesFrom: Pair<Array<String>, Array<Long>>
|
protected val filterDatesFrom: Pair<Array<String>, Array<Long>>
|
||||||
protected val filterDatesTo: Pair<Array<String>, Array<Long>>
|
protected val filterDatesTo: Pair<Array<String>, Array<Long>>
|
||||||
|
|
||||||
|
|
||||||
init {
|
init {
|
||||||
initParams()
|
initParams()
|
||||||
filterDatesFrom = makeMonthlyList(oldestDate, false)
|
filterDatesFrom = makeMonthlyList(oldestDate, false)
|
||||||
|
|
|
@ -4,15 +4,13 @@ package ac.mdiq.podcini.ui.statistics.subscriptions
|
||||||
import ac.mdiq.podcini.R
|
import ac.mdiq.podcini.R
|
||||||
import ac.mdiq.podcini.databinding.StatisticsFragmentBinding
|
import ac.mdiq.podcini.databinding.StatisticsFragmentBinding
|
||||||
import ac.mdiq.podcini.storage.DBReader
|
import ac.mdiq.podcini.storage.DBReader
|
||||||
import ac.mdiq.podcini.storage.DBReader.MonthlyStatisticsItem
|
|
||||||
import ac.mdiq.podcini.storage.DBReader.StatisticsResult
|
import ac.mdiq.podcini.storage.DBReader.StatisticsResult
|
||||||
import ac.mdiq.podcini.storage.StatisticsItem
|
import ac.mdiq.podcini.storage.StatisticsItem
|
||||||
import ac.mdiq.podcini.ui.statistics.StatisticsFragment
|
import ac.mdiq.podcini.ui.statistics.StatisticsFragment
|
||||||
import ac.mdiq.podcini.ui.statistics.years.YearsStatisticsFragment
|
import ac.mdiq.podcini.ui.statistics.StatisticsFragment.Companion.prefs
|
||||||
import ac.mdiq.podcini.ui.statistics.years.YearsStatisticsFragment.Companion
|
import ac.mdiq.podcini.util.Logd
|
||||||
import ac.mdiq.podcini.util.event.EventFlow
|
import ac.mdiq.podcini.util.event.EventFlow
|
||||||
import ac.mdiq.podcini.util.event.FlowEvent
|
import ac.mdiq.podcini.util.event.FlowEvent
|
||||||
import android.content.Context
|
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import android.view.*
|
import android.view.*
|
||||||
|
@ -21,10 +19,6 @@ import androidx.fragment.app.Fragment
|
||||||
import androidx.lifecycle.lifecycleScope
|
import androidx.lifecycle.lifecycleScope
|
||||||
import androidx.recyclerview.widget.LinearLayoutManager
|
import androidx.recyclerview.widget.LinearLayoutManager
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import io.reactivex.Observable
|
|
||||||
import io.reactivex.android.schedulers.AndroidSchedulers
|
|
||||||
import io.reactivex.disposables.Disposable
|
|
||||||
import io.reactivex.schedulers.Schedulers
|
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.flow.collectLatest
|
import kotlinx.coroutines.flow.collectLatest
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
@ -77,6 +71,7 @@ class SubscriptionStatisticsFragment : Fragment() {
|
||||||
private fun procFlowEvents() {
|
private fun procFlowEvents() {
|
||||||
lifecycleScope.launch {
|
lifecycleScope.launch {
|
||||||
EventFlow.events.collectLatest { event ->
|
EventFlow.events.collectLatest { event ->
|
||||||
|
Logd(TAG, "Received event: $event")
|
||||||
when (event) {
|
when (event) {
|
||||||
is FlowEvent.StatisticsEvent -> refreshStatistics()
|
is FlowEvent.StatisticsEvent -> refreshStatistics()
|
||||||
else -> {}
|
else -> {}
|
||||||
|
@ -96,7 +91,7 @@ class SubscriptionStatisticsFragment : Fragment() {
|
||||||
if (statisticsResult != null) {
|
if (statisticsResult != null) {
|
||||||
val dialog = object: DatesFilterDialog(requireContext(), statisticsResult!!.oldestDate) {
|
val dialog = object: DatesFilterDialog(requireContext(), statisticsResult!!.oldestDate) {
|
||||||
override fun initParams() {
|
override fun initParams() {
|
||||||
prefs = requireContext().getSharedPreferences(StatisticsFragment.PREF_NAME, Context.MODE_PRIVATE)
|
prefs = StatisticsFragment.prefs
|
||||||
includeMarkedAsPlayed = prefs!!.getBoolean(StatisticsFragment.PREF_INCLUDE_MARKED_PLAYED, false)
|
includeMarkedAsPlayed = prefs!!.getBoolean(StatisticsFragment.PREF_INCLUDE_MARKED_PLAYED, false)
|
||||||
timeFilterFrom = prefs!!.getLong(StatisticsFragment.PREF_FILTER_FROM, 0)
|
timeFilterFrom = prefs!!.getLong(StatisticsFragment.PREF_FILTER_FROM, 0)
|
||||||
timeFilterTo = prefs!!.getLong(StatisticsFragment.PREF_FILTER_TO, Long.MAX_VALUE)
|
timeFilterTo = prefs!!.getLong(StatisticsFragment.PREF_FILTER_TO, Long.MAX_VALUE)
|
||||||
|
@ -126,10 +121,10 @@ class SubscriptionStatisticsFragment : Fragment() {
|
||||||
private fun loadStatistics() {
|
private fun loadStatistics() {
|
||||||
// disposable?.dispose()
|
// disposable?.dispose()
|
||||||
|
|
||||||
val prefs = requireContext().getSharedPreferences(StatisticsFragment.PREF_NAME, Context.MODE_PRIVATE)
|
// val prefs = requireContext().getSharedPreferences(StatisticsFragment.PREF_NAME, Context.MODE_PRIVATE)
|
||||||
val includeMarkedAsPlayed = prefs.getBoolean(StatisticsFragment.PREF_INCLUDE_MARKED_PLAYED, false)
|
val includeMarkedAsPlayed = prefs!!.getBoolean(StatisticsFragment.PREF_INCLUDE_MARKED_PLAYED, false)
|
||||||
val timeFilterFrom = prefs.getLong(StatisticsFragment.PREF_FILTER_FROM, 0)
|
val timeFilterFrom = prefs!!.getLong(StatisticsFragment.PREF_FILTER_FROM, 0)
|
||||||
val timeFilterTo = prefs.getLong(StatisticsFragment.PREF_FILTER_TO, Long.MAX_VALUE)
|
val timeFilterTo = prefs!!.getLong(StatisticsFragment.PREF_FILTER_TO, Long.MAX_VALUE)
|
||||||
|
|
||||||
// disposable = Observable.fromCallable {
|
// disposable = Observable.fromCallable {
|
||||||
// val statisticsData = DBReader.getStatistics(
|
// val statisticsData = DBReader.getStatistics(
|
||||||
|
|
|
@ -3,8 +3,11 @@ package ac.mdiq.podcini.ui.statistics.years
|
||||||
|
|
||||||
import ac.mdiq.podcini.R
|
import ac.mdiq.podcini.R
|
||||||
import ac.mdiq.podcini.databinding.StatisticsFragmentBinding
|
import ac.mdiq.podcini.databinding.StatisticsFragmentBinding
|
||||||
|
import ac.mdiq.podcini.playback.service.PlaybackService
|
||||||
|
import ac.mdiq.podcini.playback.service.PlaybackService.Companion
|
||||||
import ac.mdiq.podcini.storage.DBReader
|
import ac.mdiq.podcini.storage.DBReader
|
||||||
import ac.mdiq.podcini.storage.DBReader.MonthlyStatisticsItem
|
import ac.mdiq.podcini.storage.DBReader.MonthlyStatisticsItem
|
||||||
|
import ac.mdiq.podcini.util.Logd
|
||||||
import ac.mdiq.podcini.util.event.EventFlow
|
import ac.mdiq.podcini.util.event.EventFlow
|
||||||
import ac.mdiq.podcini.util.event.FlowEvent
|
import ac.mdiq.podcini.util.event.FlowEvent
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
|
@ -65,6 +68,7 @@ class YearsStatisticsFragment : Fragment() {
|
||||||
private fun procFlowEvents() {
|
private fun procFlowEvents() {
|
||||||
lifecycleScope.launch {
|
lifecycleScope.launch {
|
||||||
EventFlow.events.collectLatest { event ->
|
EventFlow.events.collectLatest { event ->
|
||||||
|
Logd(TAG, "Received event: $event")
|
||||||
when (event) {
|
when (event) {
|
||||||
is FlowEvent.StatisticsEvent -> refreshStatistics()
|
is FlowEvent.StatisticsEvent -> refreshStatistics()
|
||||||
else -> {}
|
else -> {}
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
package ac.mdiq.podcini.ui.adapter
|
package ac.mdiq.podcini.ui.utils
|
||||||
|
|
||||||
import ac.mdiq.podcini.R
|
import ac.mdiq.podcini.R
|
||||||
import ac.mdiq.podcini.ui.activity.MainActivity
|
import ac.mdiq.podcini.ui.activity.MainActivity
|
||||||
|
import ac.mdiq.podcini.util.Logd
|
||||||
import android.graphics.drawable.Drawable
|
import android.graphics.drawable.Drawable
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.widget.ImageView
|
import android.widget.ImageView
|
||||||
|
@ -75,6 +76,7 @@ class CoverLoader(private val activity: MainActivity) {
|
||||||
.setHeader("User-Agent", "Mozilla/5.0")
|
.setHeader("User-Agent", "Mozilla/5.0")
|
||||||
.listener(object : ImageRequest.Listener {
|
.listener(object : ImageRequest.Listener {
|
||||||
override fun onError(request: ImageRequest, throwable: ErrorResult) {
|
override fun onError(request: ImageRequest, throwable: ErrorResult) {
|
||||||
|
Logd("CoverLoader", "Trying to get fallback image")
|
||||||
val fallbackImageRequest = ImageRequest.Builder(activity)
|
val fallbackImageRequest = ImageRequest.Builder(activity)
|
||||||
.data(fallbackUri)
|
.data(fallbackUri)
|
||||||
.setHeader("User-Agent", "Mozilla/5.0")
|
.setHeader("User-Agent", "Mozilla/5.0")
|
||||||
|
@ -86,9 +88,7 @@ class CoverLoader(private val activity: MainActivity) {
|
||||||
})
|
})
|
||||||
.target(coverTargetCoil)
|
.target(coverTargetCoil)
|
||||||
.build()
|
.build()
|
||||||
activity.imageLoader
|
activity.imageLoader.enqueue(request)
|
||||||
.enqueue(request)
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
internal class CoilCoverTarget(fallbackTitle: TextView?, coverImage: ImageView, private val textAndImageCombined: Boolean) : Target {
|
internal class CoilCoverTarget(fallbackTitle: TextView?, coverImage: ImageView, private val textAndImageCombined: Boolean) : Target {
|
|
@ -1,4 +1,4 @@
|
||||||
package ac.mdiq.podcini.ui.view
|
package ac.mdiq.podcini.ui.utils
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.database.DataSetObserver
|
import android.database.DataSetObserver
|
|
@ -1,4 +1,4 @@
|
||||||
package ac.mdiq.podcini.ui.view
|
package ac.mdiq.podcini.ui.utils
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.graphics.Rect
|
import android.graphics.Rect
|
|
@ -1,4 +1,4 @@
|
||||||
package ac.mdiq.podcini.ui.view
|
package ac.mdiq.podcini.ui.utils
|
||||||
|
|
||||||
import android.animation.ValueAnimator
|
import android.animation.ValueAnimator
|
||||||
import android.view.View
|
import android.view.View
|
|
@ -1,4 +1,4 @@
|
||||||
package ac.mdiq.podcini.ui.view
|
package ac.mdiq.podcini.ui.utils
|
||||||
|
|
||||||
import ac.mdiq.podcini.R
|
import ac.mdiq.podcini.R
|
||||||
import android.content.Context
|
import android.content.Context
|
|
@ -1,4 +1,4 @@
|
||||||
package ac.mdiq.podcini.ui.view
|
package ac.mdiq.podcini.ui.utils
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.util.AttributeSet
|
import android.util.AttributeSet
|
|
@ -1,4 +1,4 @@
|
||||||
package ac.mdiq.podcini.ui.view
|
package ac.mdiq.podcini.ui.utils
|
||||||
|
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
package ac.mdiq.podcini.ui.view
|
package ac.mdiq.podcini.ui.utils
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.graphics.PorterDuff
|
import android.graphics.PorterDuff
|
|
@ -1,4 +1,4 @@
|
||||||
package ac.mdiq.podcini.ui.fragment
|
package ac.mdiq.podcini.ui.utils
|
||||||
|
|
||||||
enum class TransitionEffect {
|
enum class TransitionEffect {
|
||||||
NONE, FADE, SLIDE
|
NONE, FADE, SLIDE
|
|
@ -8,6 +8,8 @@ import androidx.recyclerview.widget.DividerItemDecoration
|
||||||
import androidx.recyclerview.widget.LinearLayoutManager
|
import androidx.recyclerview.widget.LinearLayoutManager
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import ac.mdiq.podcini.R
|
import ac.mdiq.podcini.R
|
||||||
|
import ac.mdiq.podcini.receiver.PlayerWidget.Companion.PREFS_NAME
|
||||||
|
import android.content.SharedPreferences
|
||||||
|
|
||||||
class EpisodeItemListRecyclerView : RecyclerView {
|
class EpisodeItemListRecyclerView : RecyclerView {
|
||||||
private lateinit var layoutManager: LinearLayoutManager
|
private lateinit var layoutManager: LinearLayoutManager
|
||||||
|
@ -46,16 +48,16 @@ class EpisodeItemListRecyclerView : RecyclerView {
|
||||||
val firstItemView = layoutManager.findViewByPosition(firstItem)
|
val firstItemView = layoutManager.findViewByPosition(firstItem)
|
||||||
val topOffset = firstItemView?.top?.toFloat() ?: 0f
|
val topOffset = firstItemView?.top?.toFloat() ?: 0f
|
||||||
|
|
||||||
context.getSharedPreferences(TAG, Context.MODE_PRIVATE).edit()
|
prefs!!.edit()
|
||||||
.putInt(PREF_PREFIX_SCROLL_POSITION + tag, firstItem)
|
.putInt(PREF_PREFIX_SCROLL_POSITION + tag, firstItem)
|
||||||
.putInt(PREF_PREFIX_SCROLL_OFFSET + tag, topOffset.toInt())
|
.putInt(PREF_PREFIX_SCROLL_OFFSET + tag, topOffset.toInt())
|
||||||
.apply()
|
.apply()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun restoreScrollPosition(tag: String) {
|
fun restoreScrollPosition(tag: String) {
|
||||||
val prefs = context.getSharedPreferences(TAG, Context.MODE_PRIVATE)
|
// val prefs = context.getSharedPreferences(TAG, Context.MODE_PRIVATE)
|
||||||
val position = prefs.getInt(PREF_PREFIX_SCROLL_POSITION + tag, 0)
|
val position = prefs!!.getInt(PREF_PREFIX_SCROLL_POSITION + tag, 0)
|
||||||
val offset = prefs.getInt(PREF_PREFIX_SCROLL_OFFSET + tag, 0)
|
val offset = prefs!!.getInt(PREF_PREFIX_SCROLL_OFFSET + tag, 0)
|
||||||
if (position > 0 || offset > 0) layoutManager.scrollToPositionWithOffset(position, offset)
|
if (position > 0 || offset > 0) layoutManager.scrollToPositionWithOffset(position, offset)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -71,5 +73,11 @@ class EpisodeItemListRecyclerView : RecyclerView {
|
||||||
private const val TAG = "EpisodeItemListRecyclerView"
|
private const val TAG = "EpisodeItemListRecyclerView"
|
||||||
private const val PREF_PREFIX_SCROLL_POSITION = "scroll_position_"
|
private const val PREF_PREFIX_SCROLL_POSITION = "scroll_position_"
|
||||||
private const val PREF_PREFIX_SCROLL_OFFSET = "scroll_offset_"
|
private const val PREF_PREFIX_SCROLL_OFFSET = "scroll_offset_"
|
||||||
|
|
||||||
|
var prefs: SharedPreferences? = null
|
||||||
|
|
||||||
|
fun getSharedPrefs(context: Context) {
|
||||||
|
if (prefs == null) prefs = context.getSharedPreferences(TAG, Context.MODE_PRIVATE)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
package ac.mdiq.podcini.ui.view.viewholder
|
package ac.mdiq.podcini.ui.view.viewholder
|
||||||
|
|
||||||
import ac.mdiq.podcini.ui.activity.MainActivity
|
import ac.mdiq.podcini.ui.activity.MainActivity
|
||||||
import android.os.Build
|
|
||||||
import android.text.Layout
|
import android.text.Layout
|
||||||
import android.text.format.Formatter
|
import android.text.format.Formatter
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
|
@ -18,7 +17,7 @@ import com.google.android.material.elevation.SurfaceColors
|
||||||
import com.joanzapata.iconify.Iconify
|
import com.joanzapata.iconify.Iconify
|
||||||
import ac.mdiq.podcini.R
|
import ac.mdiq.podcini.R
|
||||||
import ac.mdiq.podcini.databinding.FeeditemlistItemBinding
|
import ac.mdiq.podcini.databinding.FeeditemlistItemBinding
|
||||||
import ac.mdiq.podcini.ui.adapter.CoverLoader
|
import ac.mdiq.podcini.ui.utils.CoverLoader
|
||||||
import ac.mdiq.podcini.feed.util.ImageResourceUtils
|
import ac.mdiq.podcini.feed.util.ImageResourceUtils
|
||||||
import ac.mdiq.podcini.net.download.MediaSizeLoader
|
import ac.mdiq.podcini.net.download.MediaSizeLoader
|
||||||
import ac.mdiq.podcini.storage.model.feed.FeedItem
|
import ac.mdiq.podcini.storage.model.feed.FeedItem
|
||||||
|
@ -37,7 +36,6 @@ import ac.mdiq.podcini.util.*
|
||||||
import ac.mdiq.podcini.util.event.FlowEvent
|
import ac.mdiq.podcini.util.event.FlowEvent
|
||||||
import android.widget.LinearLayout
|
import android.widget.LinearLayout
|
||||||
import androidx.appcompat.content.res.AppCompatResources
|
import androidx.appcompat.content.res.AppCompatResources
|
||||||
import androidx.core.content.ContextCompat.getDrawable
|
|
||||||
import io.reactivex.functions.Consumer
|
import io.reactivex.functions.Consumer
|
||||||
import kotlin.math.max
|
import kotlin.math.max
|
||||||
|
|
||||||
|
@ -45,8 +43,8 @@ import kotlin.math.max
|
||||||
* Holds the view which shows FeedItems.
|
* Holds the view which shows FeedItems.
|
||||||
*/
|
*/
|
||||||
@UnstableApi
|
@UnstableApi
|
||||||
open class EpisodeItemViewHolder(private val activity: MainActivity, parent: ViewGroup?) :
|
open class EpisodeItemViewHolder(private val activity: MainActivity, parent: ViewGroup)
|
||||||
RecyclerView.ViewHolder(LayoutInflater.from(activity).inflate(R.layout.feeditemlist_item, parent, false)) {
|
: RecyclerView.ViewHolder(LayoutInflater.from(parent.context).inflate(R.layout.feeditemlist_item, parent, false)) {
|
||||||
|
|
||||||
val binding: FeeditemlistItemBinding = FeeditemlistItemBinding.bind(itemView)
|
val binding: FeeditemlistItemBinding = FeeditemlistItemBinding.bind(itemView)
|
||||||
|
|
||||||
|
@ -108,7 +106,7 @@ open class EpisodeItemViewHolder(private val activity: MainActivity, parent: Vie
|
||||||
container.alpha = if (item.isPlayed()) 0.75f else 1.0f
|
container.alpha = if (item.isPlayed()) 0.75f else 1.0f
|
||||||
|
|
||||||
val newButton = ItemActionButton.forItem(item)
|
val newButton = ItemActionButton.forItem(item)
|
||||||
Logd(TAG, "bind ${actionButton?.TAG} ${newButton.TAG} ${item.title}")
|
Logd(TAG, "Trying to bind button ${actionButton?.TAG} ${newButton.TAG} ${item.title}")
|
||||||
// not using a new button to ensure valid progress values, for TTS audio generation
|
// not using a new button to ensure valid progress values, for TTS audio generation
|
||||||
if (!(actionButton?.TAG == TTSActionButton::class.simpleName && newButton.TAG == TTSActionButton::class.simpleName)) {
|
if (!(actionButton?.TAG == TTSActionButton::class.simpleName && newButton.TAG == TTSActionButton::class.simpleName)) {
|
||||||
actionButton = newButton
|
actionButton = newButton
|
||||||
|
@ -118,11 +116,9 @@ open class EpisodeItemViewHolder(private val activity: MainActivity, parent: Vie
|
||||||
|
|
||||||
// Log.d(TAG, "bind called ${item.media}")
|
// Log.d(TAG, "bind called ${item.media}")
|
||||||
when {
|
when {
|
||||||
item.media != null -> {
|
item.media != null -> bind(item.media!!)
|
||||||
bind(item.media!!)
|
// for generating TTS files for episode without media
|
||||||
}
|
|
||||||
item.playState == BUILDING -> {
|
item.playState == BUILDING -> {
|
||||||
// for generating TTS files for episode without media
|
|
||||||
secondaryActionProgress.setPercentage(actionButton!!.processing, item)
|
secondaryActionProgress.setPercentage(actionButton!!.processing, item)
|
||||||
secondaryActionProgress.setIndeterminate(false)
|
secondaryActionProgress.setIndeterminate(false)
|
||||||
}
|
}
|
||||||
|
@ -139,15 +135,16 @@ open class EpisodeItemViewHolder(private val activity: MainActivity, parent: Vie
|
||||||
|
|
||||||
if (coverHolder.visibility == View.VISIBLE) {
|
if (coverHolder.visibility == View.VISIBLE) {
|
||||||
val imgLoc = ImageResourceUtils.getEpisodeListImageLocation(item)
|
val imgLoc = ImageResourceUtils.getEpisodeListImageLocation(item)
|
||||||
// Logd(TAG, "imgLoc $imgLoc ${item.feed?.imageUrl} ${item.title}")
|
Logd(TAG, "imgLoc $imgLoc ${item.feed?.imageUrl} ${item.title}")
|
||||||
if (!imgLoc.isNullOrBlank() && !imgLoc.contains(PREFIX_GENERATIVE_COVER)) CoverLoader(activity)
|
if (!imgLoc.isNullOrBlank() && !imgLoc.contains(PREFIX_GENERATIVE_COVER))
|
||||||
.withUri(imgLoc)
|
CoverLoader(activity)
|
||||||
.withFallbackUri(item.feed?.imageUrl)
|
.withUri(imgLoc)
|
||||||
.withPlaceholderView(placeholder)
|
.withFallbackUri(item.feed?.imageUrl)
|
||||||
.withCoverView(cover)
|
.withPlaceholderView(placeholder)
|
||||||
.load()
|
.withCoverView(cover)
|
||||||
|
.load()
|
||||||
else {
|
else {
|
||||||
Logd(TAG, "setting to ic_launcher")
|
Logd(TAG, "setting cover to ic_launcher")
|
||||||
cover.setImageDrawable(AppCompatResources.getDrawable(activity, R.drawable.ic_launcher_foreground))
|
cover.setImageDrawable(AppCompatResources.getDrawable(activity, R.drawable.ic_launcher_foreground))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -211,12 +208,11 @@ open class EpisodeItemViewHolder(private val activity: MainActivity, parent: Vie
|
||||||
NetworkUtils.isEpisodeHeadDownloadAllowed && !media.checkedOnSizeButUnknown() -> {
|
NetworkUtils.isEpisodeHeadDownloadAllowed && !media.checkedOnSizeButUnknown() -> {
|
||||||
size.text = "{fa-spinner}"
|
size.text = "{fa-spinner}"
|
||||||
Iconify.addIcons(size)
|
Iconify.addIcons(size)
|
||||||
MediaSizeLoader.getFeedMediaSizeObservable(media).subscribe(
|
MediaSizeLoader.getFeedMediaSizeObservable(media).subscribe(Consumer<Long?> { sizeValue: Long? ->
|
||||||
Consumer<Long?> { sizeValue: Long? ->
|
if (sizeValue == null) return@Consumer
|
||||||
if (sizeValue == null) return@Consumer
|
if (sizeValue > 0) size.text = Formatter.formatShortFileSize(activity, sizeValue)
|
||||||
if (sizeValue > 0) size.text = Formatter.formatShortFileSize(activity, sizeValue)
|
else size.text = ""
|
||||||
else size.text = ""
|
}) { error: Throwable? ->
|
||||||
}) { error: Throwable? ->
|
|
||||||
size.text = ""
|
size.text = ""
|
||||||
Log.e(TAG, Log.getStackTraceString(error))
|
Log.e(TAG, Log.getStackTraceString(error))
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,6 +7,7 @@ import ac.mdiq.podcini.preferences.UserPreferences.shouldShowRemainingTime
|
||||||
import ac.mdiq.podcini.receiver.MediaButtonReceiver.Companion.createPendingIntent
|
import ac.mdiq.podcini.receiver.MediaButtonReceiver.Companion.createPendingIntent
|
||||||
import ac.mdiq.podcini.receiver.PlayerWidget
|
import ac.mdiq.podcini.receiver.PlayerWidget
|
||||||
import ac.mdiq.podcini.receiver.PlayerWidget.Companion.isEnabled
|
import ac.mdiq.podcini.receiver.PlayerWidget.Companion.isEnabled
|
||||||
|
import ac.mdiq.podcini.receiver.PlayerWidget.Companion.prefs
|
||||||
import ac.mdiq.podcini.storage.model.playback.MediaType
|
import ac.mdiq.podcini.storage.model.playback.MediaType
|
||||||
import ac.mdiq.podcini.storage.model.playback.Playable
|
import ac.mdiq.podcini.storage.model.playback.Playable
|
||||||
import ac.mdiq.podcini.ui.activity.appstartintent.MainActivityStarter
|
import ac.mdiq.podcini.ui.activity.appstartintent.MainActivityStarter
|
||||||
|
@ -14,13 +15,10 @@ import ac.mdiq.podcini.ui.activity.appstartintent.PlaybackSpeedActivityStarter
|
||||||
import ac.mdiq.podcini.ui.activity.appstartintent.VideoPlayerActivityStarter
|
import ac.mdiq.podcini.ui.activity.appstartintent.VideoPlayerActivityStarter
|
||||||
import ac.mdiq.podcini.util.Converter.getDurationStringLong
|
import ac.mdiq.podcini.util.Converter.getDurationStringLong
|
||||||
import ac.mdiq.podcini.util.TimeSpeedConverter
|
import ac.mdiq.podcini.util.TimeSpeedConverter
|
||||||
import android.R.attr.bitmap
|
|
||||||
import android.appwidget.AppWidgetManager
|
import android.appwidget.AppWidgetManager
|
||||||
import android.content.ComponentName
|
import android.content.ComponentName
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.graphics.Bitmap
|
import android.graphics.Bitmap
|
||||||
import android.graphics.Canvas
|
|
||||||
import android.graphics.Matrix
|
|
||||||
import android.graphics.drawable.BitmapDrawable
|
import android.graphics.drawable.BitmapDrawable
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import android.view.KeyEvent
|
import android.view.KeyEvent
|
||||||
|
@ -165,16 +163,16 @@ object WidgetUpdater {
|
||||||
|
|
||||||
for (id in widgetIds) {
|
for (id in widgetIds) {
|
||||||
val options = manager.getAppWidgetOptions(id)
|
val options = manager.getAppWidgetOptions(id)
|
||||||
val prefs = context.getSharedPreferences(PlayerWidget.PREFS_NAME, Context.MODE_PRIVATE)
|
// val prefs = context.getSharedPreferences(PlayerWidget.PREFS_NAME, Context.MODE_PRIVATE)
|
||||||
val minWidth = options.getInt(AppWidgetManager.OPTION_APPWIDGET_MIN_WIDTH)
|
val minWidth = options.getInt(AppWidgetManager.OPTION_APPWIDGET_MIN_WIDTH)
|
||||||
val columns = getCellsForSize(minWidth)
|
val columns = getCellsForSize(minWidth)
|
||||||
if (columns < 3) views.setViewVisibility(R.id.layout_center, View.INVISIBLE)
|
if (columns < 3) views.setViewVisibility(R.id.layout_center, View.INVISIBLE)
|
||||||
else views.setViewVisibility(R.id.layout_center, View.VISIBLE)
|
else views.setViewVisibility(R.id.layout_center, View.VISIBLE)
|
||||||
|
|
||||||
val showPlaybackSpeed = prefs.getBoolean(PlayerWidget.KEY_WIDGET_PLAYBACK_SPEED + id, true)
|
val showPlaybackSpeed = prefs!!.getBoolean(PlayerWidget.KEY_WIDGET_PLAYBACK_SPEED + id, true)
|
||||||
val showRewind = prefs.getBoolean(PlayerWidget.KEY_WIDGET_REWIND + id, true)
|
val showRewind = prefs!!.getBoolean(PlayerWidget.KEY_WIDGET_REWIND + id, true)
|
||||||
val showFastForward = prefs.getBoolean(PlayerWidget.KEY_WIDGET_FAST_FORWARD + id, true)
|
val showFastForward = prefs!!.getBoolean(PlayerWidget.KEY_WIDGET_FAST_FORWARD + id, true)
|
||||||
val showSkip = prefs.getBoolean(PlayerWidget.KEY_WIDGET_SKIP + id, true)
|
val showSkip = prefs!!.getBoolean(PlayerWidget.KEY_WIDGET_SKIP + id, true)
|
||||||
|
|
||||||
if (showPlaybackSpeed || showRewind || showSkip || showFastForward) {
|
if (showPlaybackSpeed || showRewind || showSkip || showFastForward) {
|
||||||
views.setInt(R.id.extendedButtonsContainer, "setVisibility", View.VISIBLE)
|
views.setInt(R.id.extendedButtonsContainer, "setVisibility", View.VISIBLE)
|
||||||
|
@ -188,7 +186,7 @@ object WidgetUpdater {
|
||||||
views.setInt(R.id.butPlay, "setVisibility", View.VISIBLE)
|
views.setInt(R.id.butPlay, "setVisibility", View.VISIBLE)
|
||||||
}
|
}
|
||||||
|
|
||||||
val backgroundColor = prefs.getInt(PlayerWidget.KEY_WIDGET_COLOR + id, PlayerWidget.DEFAULT_COLOR)
|
val backgroundColor = prefs!!.getInt(PlayerWidget.KEY_WIDGET_COLOR + id, PlayerWidget.DEFAULT_COLOR)
|
||||||
views.setInt(R.id.widgetLayout, "setBackgroundColor", backgroundColor)
|
views.setInt(R.id.widgetLayout, "setBackgroundColor", backgroundColor)
|
||||||
|
|
||||||
manager.updateAppWidget(id, views)
|
manager.updateAppWidget(id, views)
|
||||||
|
|
|
@ -2,7 +2,7 @@ package ac.mdiq.podcini.ui.widget
|
||||||
|
|
||||||
import ac.mdiq.podcini.feed.util.PlaybackSpeedUtils.getCurrentPlaybackSpeed
|
import ac.mdiq.podcini.feed.util.PlaybackSpeedUtils.getCurrentPlaybackSpeed
|
||||||
import ac.mdiq.podcini.playback.base.PlayerStatus
|
import ac.mdiq.podcini.playback.base.PlayerStatus
|
||||||
import ac.mdiq.podcini.preferences.PlaybackPreferences.Companion.createInstanceFromPreferences
|
import ac.mdiq.podcini.preferences.PlaybackPreferences.Companion.loadPlayableFromPreferences
|
||||||
import ac.mdiq.podcini.ui.widget.WidgetUpdater.WidgetState
|
import ac.mdiq.podcini.ui.widget.WidgetUpdater.WidgetState
|
||||||
import ac.mdiq.podcini.util.Logd
|
import ac.mdiq.podcini.util.Logd
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
|
@ -24,7 +24,7 @@ class WidgetUpdaterWorker(context: Context, workerParams: WorkerParameters) : Wo
|
||||||
* Loads the current media from the database and updates the widget in a background job.
|
* Loads the current media from the database and updates the widget in a background job.
|
||||||
*/
|
*/
|
||||||
private fun updateWidget() {
|
private fun updateWidget() {
|
||||||
val media = createInstanceFromPreferences(applicationContext)
|
val media = loadPlayableFromPreferences()
|
||||||
if (media != null) WidgetUpdater.updateWidget(applicationContext, WidgetState(media, PlayerStatus.STOPPED, media.getPosition(), media.getDuration(), getCurrentPlaybackSpeed(media)))
|
if (media != null) WidgetUpdater.updateWidget(applicationContext, WidgetState(media, PlayerStatus.STOPPED, media.getPosition(), media.getDuration(), getCurrentPlaybackSpeed(media)))
|
||||||
else WidgetUpdater.updateWidget(applicationContext, WidgetState(PlayerStatus.STOPPED))
|
else WidgetUpdater.updateWidget(applicationContext, WidgetState(PlayerStatus.STOPPED))
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
package ac.mdiq.podcini.util
|
package ac.mdiq.podcini.util
|
||||||
|
|
||||||
|
import ac.mdiq.podcini.preferences.UserPreferences.appPrefs
|
||||||
import ac.mdiq.podcini.receiver.SPAReceiver
|
import ac.mdiq.podcini.receiver.SPAReceiver
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
|
@ -31,12 +32,12 @@ object SPAUtil {
|
||||||
Log.wtf(TAG, "Unable to get application context")
|
Log.wtf(TAG, "Unable to get application context")
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
val prefs = PreferenceManager.getDefaultSharedPreferences(appContext)
|
// val prefs = PreferenceManager.getDefaultSharedPreferences(appContext)
|
||||||
if (!prefs.getBoolean(PREF_HAS_QUERIED_SP_APPS, false)) {
|
if (!appPrefs.getBoolean(PREF_HAS_QUERIED_SP_APPS, false)) {
|
||||||
appContext.sendBroadcast(Intent(SPAReceiver.ACTION_SP_APPS_QUERY_FEEDS))
|
appContext.sendBroadcast(Intent(SPAReceiver.ACTION_SP_APPS_QUERY_FEEDS))
|
||||||
Logd(TAG, "Sending SP_APPS_QUERY_FEEDS intent")
|
Logd(TAG, "Sending SP_APPS_QUERY_FEEDS intent")
|
||||||
|
|
||||||
val editor = prefs.edit()
|
val editor = appPrefs.edit()
|
||||||
editor.putBoolean(PREF_HAS_QUERIED_SP_APPS, true)
|
editor.putBoolean(PREF_HAS_QUERIED_SP_APPS, true)
|
||||||
editor.apply()
|
editor.apply()
|
||||||
|
|
||||||
|
|
|
@ -18,6 +18,9 @@ import ac.mdiq.podcini.storage.database.PodDBAdapter
|
||||||
import ac.mdiq.podcini.preferences.UserPreferences
|
import ac.mdiq.podcini.preferences.UserPreferences
|
||||||
import ac.mdiq.podcini.preferences.UserPreferences.proxyConfig
|
import ac.mdiq.podcini.preferences.UserPreferences.proxyConfig
|
||||||
import ac.mdiq.podcini.net.download.service.DownloadServiceInterfaceImpl
|
import ac.mdiq.podcini.net.download.service.DownloadServiceInterfaceImpl
|
||||||
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
|
||||||
@UnstableApi
|
@UnstableApi
|
||||||
|
|
Before Width: | Height: | Size: 590 KiB |
Before Width: | Height: | Size: 1.0 MiB |
Before Width: | Height: | Size: 953 KiB |
Before Width: | Height: | Size: 910 KiB |
Before Width: | Height: | Size: 619 KiB |
Before Width: | Height: | Size: 813 KiB |
Before Width: | Height: | Size: 1.0 MiB |
Before Width: | Height: | Size: 67 KiB |
Before Width: | Height: | Size: 198 KiB |
Before Width: | Height: | Size: 130 KiB |
Before Width: | Height: | Size: 225 KiB |
Before Width: | Height: | Size: 219 KiB |
Before Width: | Height: | Size: 375 KiB |