diff --git a/README.md b/README.md index 3ad531df..f8230ca2 100644 --- a/README.md +++ b/README.md @@ -17,7 +17,7 @@ Compared to AntennaPod this project: 2. Plays in `AudioOffloadMode`, kind to device battery, 3. Is purely `Kotlin` based and mono-modular, 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, 7. Accepts podcast as well as plain RSS and YouTube feeds, 8. Offers Readability and Text-to-Speech for RSS contents, diff --git a/app/build.gradle b/app/build.gradle index b372fd83..00bb0aa3 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -159,8 +159,8 @@ android { // Version code schema (not used): // "1.2.3-beta4" -> 1020304 // "1.2.3" -> 1020395 - versionCode 3020146 - versionName "5.3.1" + versionCode 3020147 + versionName "5.4.0" def commit = "" try { diff --git a/app/src/androidTest/java/ac/test/podcini/playback/PlaybackTest.kt b/app/src/androidTest/java/ac/test/podcini/playback/PlaybackTest.kt index 15634645..79b48c78 100644 --- a/app/src/androidTest/java/ac/test/podcini/playback/PlaybackTest.kt +++ b/app/src/androidTest/java/ac/test/podcini/playback/PlaybackTest.kt @@ -30,6 +30,7 @@ import ac.mdiq.podcini.preferences.UserPreferences import de.test.podcini.EspressoTestUtils import de.test.podcini.IgnoreOnCi import de.test.podcini.ui.UITestUtils +import kotlinx.coroutines.runBlocking import org.awaitility.Awaitility import org.hamcrest.Matchers import org.junit.* @@ -166,7 +167,7 @@ class PlaybackTest { fun testStartLocal() { uiTestUtils!!.addLocalFeedData(true) activityTestRule.launchActivity(Intent()) - clearQueue().get() + runBlocking { clearQueue().join() } startLocalPlayback() } @@ -175,7 +176,7 @@ class PlaybackTest { fun testPlayingItemAddsToQueue() { uiTestUtils!!.addLocalFeedData(true) activityTestRule.launchActivity(Intent()) - clearQueue().get() + runBlocking { clearQueue().join() } val queue = getQueue() Assert.assertEquals(0, queue.size.toLong()) startLocalPlayback() @@ -188,7 +189,7 @@ class PlaybackTest { setContinuousPlaybackPreference(false) uiTestUtils!!.addLocalFeedData(true) activityTestRule.launchActivity(Intent()) - clearQueue().get() + runBlocking { clearQueue().join() } startLocalPlayback() } @@ -261,10 +262,9 @@ class PlaybackTest { protected fun replayEpisodeCheck(followQueue: Boolean) { setContinuousPlaybackPreference(followQueue) uiTestUtils!!.addLocalFeedData(true) - clearQueue().get() + runBlocking { clearQueue().join() } activityTestRule.launchActivity(Intent()) - val episodes = getEpisodes(0, 10, - unfiltered(), SortOrder.DATE_NEW_OLD) + val episodes = getEpisodes(0, 10, unfiltered(), SortOrder.DATE_NEW_OLD) startLocalPlayback() val media = episodes[0].media diff --git a/app/src/androidTest/java/ac/test/podcini/ui/NavigationDrawerTest.kt b/app/src/androidTest/java/ac/test/podcini/ui/NavigationDrawerTest.kt index ddfc9769..fcad844b 100644 --- a/app/src/androidTest/java/ac/test/podcini/ui/NavigationDrawerTest.kt +++ b/app/src/androidTest/java/ac/test/podcini/ui/NavigationDrawerTest.kt @@ -147,12 +147,12 @@ class NavigationDrawerTest { val hidden = hiddenDrawerItems Assert.assertEquals(2, hidden!!.size.toLong()) Assert.assertTrue(hidden.contains(AllEpisodesFragment.TAG)) - Assert.assertTrue(hidden.contains(PlaybackHistoryFragment.TAG)) + Assert.assertTrue(hidden.contains(HistoryFragment.TAG)) } @Test fun testDrawerPreferencesUnhideSomeElements() { - var hidden = listOf(PlaybackHistoryFragment.TAG, DownloadsFragment.TAG) + var hidden = listOf(HistoryFragment.TAG, DownloadsFragment.TAG) hiddenDrawerItems = hidden activityRule.launchActivity(Intent()) openNavDrawer() @@ -166,7 +166,7 @@ class NavigationDrawerTest { hidden = hiddenDrawerItems?.filterNotNull()?: listOf() Assert.assertEquals(2, hidden.size.toLong()) Assert.assertTrue(hidden.contains(QueueFragment.TAG)) - Assert.assertTrue(hidden.contains(PlaybackHistoryFragment.TAG)) + Assert.assertTrue(hidden.contains(HistoryFragment.TAG)) } diff --git a/app/src/main/java/ac/mdiq/podcini/PodciniApp.kt b/app/src/main/java/ac/mdiq/podcini/PodciniApp.kt index 06a36eaf..1eeda000 100644 --- a/app/src/main/java/ac/mdiq/podcini/PodciniApp.kt +++ b/app/src/main/java/ac/mdiq/podcini/PodciniApp.kt @@ -17,6 +17,9 @@ import com.google.android.material.color.DynamicColors import com.joanzapata.iconify.Iconify import com.joanzapata.iconify.fonts.FontAwesomeModule import com.joanzapata.iconify.fonts.MaterialModule +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.runBlocking +import kotlinx.coroutines.withContext /** Main application class. */ class PodciniApp : Application() { @@ -42,9 +45,12 @@ class PodciniApp : Application() { singleton = this - ClientConfigurator.initialize(this) - PreferenceUpgrader.checkUpgrades(this) - + runBlocking { + withContext(Dispatchers.IO) { + ClientConfigurator.initialize(this@PodciniApp) + PreferenceUpgrader.checkUpgrades(this@PodciniApp) + } + } Iconify.with(FontAwesomeModule()) Iconify.with(MaterialModule()) diff --git a/app/src/main/java/ac/mdiq/podcini/net/discovery/ItunesTopListLoader.kt b/app/src/main/java/ac/mdiq/podcini/net/discovery/ItunesTopListLoader.kt index adc13366..48e4ab3c 100644 --- a/app/src/main/java/ac/mdiq/podcini/net/discovery/ItunesTopListLoader.kt +++ b/app/src/main/java/ac/mdiq/podcini/net/discovery/ItunesTopListLoader.kt @@ -7,6 +7,7 @@ import android.util.Log import ac.mdiq.podcini.net.download.service.PodciniHttpClient.getHttpClient import ac.mdiq.podcini.storage.model.feed.Feed import ac.mdiq.podcini.util.Logd +import android.content.SharedPreferences import okhttp3.CacheControl import okhttp3.OkHttpClient import okhttp3.Request @@ -81,6 +82,11 @@ class ItunesTopListLoader(private val context: Context) { const val COUNTRY_CODE_UNSET: String = "99" 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, subscribedFeeds: List, limit: Int): List { val subscribedPodcastsSet: MutableSet = HashSet() for (subscribedFeed in subscribedFeeds) { diff --git a/app/src/main/java/ac/mdiq/podcini/net/download/service/EpisodeDownloadWorker.kt b/app/src/main/java/ac/mdiq/podcini/net/download/service/EpisodeDownloadWorker.kt index 2e43eb84..ad3631cb 100644 --- a/app/src/main/java/ac/mdiq/podcini/net/download/service/EpisodeDownloadWorker.kt +++ b/app/src/main/java/ac/mdiq/podcini/net/download/service/EpisodeDownloadWorker.kt @@ -29,6 +29,7 @@ import androidx.work.Worker import androidx.work.WorkerParameters import com.google.common.util.concurrent.Futures import com.google.common.util.concurrent.ListenableFuture +import kotlinx.coroutines.runBlocking import org.apache.commons.io.FileUtils import java.io.File import java.io.IOException @@ -115,7 +116,7 @@ class EpisodeDownloadWorker(context: Context, params: WorkerParameters) : Worker if (dest.exists()) { media.file_url = request.destination try { - DBWriter.persistFeedMedia(media).get() + runBlocking { DBWriter.persistFeedMedia(media).join() } } catch (e: Exception) { Log.e(TAG, "ExecutionException in writeFileUrl: " + e.message) } diff --git a/app/src/main/java/ac/mdiq/podcini/net/download/service/handler/MediaDownloadedHandler.kt b/app/src/main/java/ac/mdiq/podcini/net/download/service/handler/MediaDownloadedHandler.kt index dc990db6..a873f6c6 100644 --- a/app/src/main/java/ac/mdiq/podcini/net/download/service/handler/MediaDownloadedHandler.kt +++ b/app/src/main/java/ac/mdiq/podcini/net/download/service/handler/MediaDownloadedHandler.kt @@ -16,6 +16,7 @@ import android.content.Context import android.media.MediaMetadataRetriever import android.util.Log import androidx.media3.common.util.UnstableApi +import kotlinx.coroutines.runBlocking import java.io.File import java.util.concurrent.ExecutionException @@ -60,7 +61,7 @@ class MediaDownloadedHandler(private val context: Context, var updatedStatus: Do val item = media.item try { - DBWriter.persistFeedMedia(media).get() + runBlocking { DBWriter.persistFeedMedia(media).join() } // we've received the media, we don't want to autodownload it again 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, // so we do it after the enclosing media has been updated above, // 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()) } } catch (e: InterruptedException) { diff --git a/app/src/main/java/ac/mdiq/podcini/net/ssl/CompositeX509TrustManager.kt b/app/src/main/java/ac/mdiq/podcini/net/ssl/CompositeX509TrustManager.kt index 7f4a51ad..17f3bed3 100644 --- a/app/src/main/java/ac/mdiq/podcini/net/ssl/CompositeX509TrustManager.kt +++ b/app/src/main/java/ac/mdiq/podcini/net/ssl/CompositeX509TrustManager.kt @@ -46,6 +46,7 @@ class CompositeX509TrustManager(private val trustManagers: List { val certificates: MutableList = ArrayList() for (trustManager in trustManagers) { +// TODO: appears time consuming certificates.addAll(listOf(*trustManager.acceptedIssuers)) } return certificates.toTypedArray() diff --git a/app/src/main/java/ac/mdiq/podcini/net/sync/SyncService.kt b/app/src/main/java/ac/mdiq/podcini/net/sync/SyncService.kt index 6fcecb2b..f0211afc 100644 --- a/app/src/main/java/ac/mdiq/podcini/net/sync/SyncService.kt +++ b/app/src/main/java/ac/mdiq/podcini/net/sync/SyncService.kt @@ -57,6 +57,7 @@ open class SyncService(context: Context, params: WorkerParameters) : Worker(cont @UnstableApi override fun doWork(): Result { Logd(TAG, "doWork() called") val activeSyncProvider = getActiveSyncProvider() ?: return Result.failure() + Logd(TAG, "doWork() got syn provider") SynchronizationSettings.updateLastSynchronizationAttempt() setCurrentlyActive(true) @@ -165,15 +166,6 @@ open class SyncService(context: Context, params: WorkerParameters) : Worker(cont return@launch } 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 { @@ -315,11 +307,6 @@ open class SyncService(context: Context, params: WorkerParameters) : Worker(cont if (selectedService == null) return null return when (selectedService) { -// SynchronizationProviderViewData.WIFI -> { -//// if (hosturl != null) WifiImplSyncService(hosturl!!, hostport) -//// else null -// null -// } SynchronizationProviderViewData.GPODDER_NET -> GpodnetService(getHttpClient(), hosturl, deviceID?:"", username?:"", password?:"") SynchronizationProviderViewData.NEXTCLOUD_GPODDER -> NextcloudSyncService(getHttpClient(), hosturl, username?:"", password?:"") } diff --git a/app/src/main/java/ac/mdiq/podcini/playback/PlaybackController.kt b/app/src/main/java/ac/mdiq/podcini/playback/PlaybackController.kt index 2a567f55..25553bab 100644 --- a/app/src/main/java/ac/mdiq/podcini/playback/PlaybackController.kt +++ b/app/src/main/java/ac/mdiq/podcini/playback/PlaybackController.kt @@ -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.PlaybackServiceConstants import ac.mdiq.podcini.preferences.PlaybackPreferences +import ac.mdiq.podcini.preferences.PlaybackPreferences.Companion.loadPlayableFromPreferences import ac.mdiq.podcini.preferences.UserPreferences import ac.mdiq.podcini.storage.DBWriter import ac.mdiq.podcini.storage.model.feed.FeedMedia @@ -24,8 +25,11 @@ import android.view.SurfaceHolder import androidx.fragment.app.FragmentActivity import androidx.lifecycle.lifecycleScope import androidx.media3.common.util.UnstableApi +import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.launch +import kotlinx.coroutines.runBlocking +import kotlinx.coroutines.withContext /** * 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() { activity.lifecycleScope.launch { EventFlow.events.collectLatest { event -> + Logd(TAG, "Received event: $event") when (event) { is FlowEvent.PlaybackServiceEvent -> if (event.action == FlowEvent.PlaybackServiceEvent.Action.SERVICE_STARTED) init() else -> {} @@ -305,9 +310,13 @@ abstract class PlaybackController(private val activity: FragmentActivity) { fun getMedia(): Playable? { if (media == null && playbackService != null) media = playbackService!!.mPlayerInfo.playable - if (media == null) media = PlaybackPreferences.createInstanceFromPreferences(activity) - - return media + if (media != null) return media + return runBlocking { + media = withContext(Dispatchers.IO) { + loadPlayableFromPreferences() + } + media + } } fun seekTo(time: Int) { diff --git a/app/src/main/java/ac/mdiq/podcini/playback/service/LocalMediaPlayer.kt b/app/src/main/java/ac/mdiq/podcini/playback/service/LocalMediaPlayer.kt index aa9f8299..e5136d78 100644 --- a/app/src/main/java/ac/mdiq/podcini/playback/service/LocalMediaPlayer.kt +++ b/app/src/main/java/ac/mdiq/podcini/playback/service/LocalMediaPlayer.kt @@ -8,6 +8,7 @@ import ac.mdiq.podcini.playback.base.MediaPlayerBase import ac.mdiq.podcini.playback.base.PlayerStatus import ac.mdiq.podcini.playback.base.RewindAfterPauseUtils 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.playback.MediaType 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?) { Logd(TAG, "setDataSource: $s") - val httpDataSourceFactory = OkHttpDataSource.Factory(PodciniHttpClient.getHttpClient() as okhttp3.Call.Factory) - .setUserAgent(ClientConfig.USER_AGENT) + if (httpDataSourceFactory == null) + httpDataSourceFactory = OkHttpDataSource.Factory(PodciniHttpClient.getHttpClient() as okhttp3.Call.Factory).setUserAgent(ClientConfig.USER_AGENT) if (!user.isNullOrEmpty() && !password.isNullOrEmpty()) { val requestProperties = HashMap() 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() extractorsFactory.setConstantBitrateSeekingEnabled(true) extractorsFactory.setMp3ExtractorFlags(Mp3Extractor.FLAG_DISABLE_ID3_METADATA) @@ -282,6 +283,7 @@ class LocalMediaPlayer(context: Context, callback: MediaPlayerCallback) : MediaP } else -> { val localMediaurl = this.playable!!.getLocalMediaUrl() +// TODO: File(localMediaurl).canRead() time consuming if (!localMediaurl.isNullOrEmpty() && File(localMediaurl).canRead()) setDataSource(metadata, localMediaurl, null, null) else throw IOException("Unable to read local file $localMediaurl") } @@ -603,8 +605,7 @@ class LocalMediaPlayer(context: Context, callback: MediaPlayerCallback) : MediaP * invalid values. */ override fun getVideoSize(): Pair? { - if (status != PlayerStatus.ERROR && mediaType == MediaType.VIDEO) - videoSize = Pair(videoWidth, videoHeight) + if (status != PlayerStatus.ERROR && mediaType == MediaType.VIDEO) videoSize = Pair(videoWidth, videoHeight) return videoSize } @@ -664,6 +665,12 @@ class LocalMediaPlayer(context: Context, callback: MediaPlayerCallback) : MediaP init { mediaType = MediaType.UNKNOWN + if (httpDataSourceFactory == null) { + ioScope.launch { + httpDataSourceFactory = OkHttpDataSource.Factory(PodciniHttpClient.getHttpClient() as okhttp3.Call.Factory) + .setUserAgent(ClientConfig.USER_AGENT) + } + } if (exoPlayer == null) { setupPlayerListener() createStaticPlayer(context) @@ -837,6 +844,8 @@ class LocalMediaPlayer(context: Context, callback: MediaPlayerCallback) : MediaP const val BUFFERING_ENDED: Int = -2 const val ERROR_CODE_OFFSET: Int = 1000 + private var httpDataSourceFactory: OkHttpDataSource.Factory? = null + private var trackSelector: DefaultTrackSelector? = null var exoPlayer: ExoPlayer? = null @@ -897,6 +906,7 @@ class LocalMediaPlayer(context: Context, callback: MediaPlayerCallback) : MediaP audioErrorListener = null bufferingUpdateListener = null loudnessEnhancer = null + httpDataSourceFactory = null } } } diff --git a/app/src/main/java/ac/mdiq/podcini/playback/service/PlaybackService.kt b/app/src/main/java/ac/mdiq/podcini/playback/service/PlaybackService.kt index 66c253d0..d637a2fe 100644 --- a/app/src/main/java/ac/mdiq/podcini/playback/service/PlaybackService.kt +++ b/app/src/main/java/ac/mdiq/podcini/playback/service/PlaybackService.kt @@ -11,7 +11,7 @@ import ac.mdiq.podcini.playback.cast.CastPsmp import ac.mdiq.podcini.playback.cast.CastStateListener import ac.mdiq.podcini.playback.service.PlaybackServiceTaskManager.PSTMCallback 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.currentlyPlayingFeedMediaId import ac.mdiq.podcini.preferences.PlaybackPreferences.Companion.currentlyPlayingTemporaryPlaybackSpeed @@ -612,7 +612,7 @@ class PlaybackService : MediaSessionService() { scope.launch { try { val playable = withContext(Dispatchers.IO) { - createInstanceFromPreferences(applicationContext) + loadPlayableFromPreferences() } withContext(Dispatchers.Main) { startPlaying(playable, false) @@ -783,6 +783,7 @@ class PlaybackService : MediaSessionService() { private fun procFlowEvents() { scope.launch { EventFlow.events.collectLatest { event -> + Logd(TAG, "Received event: $event") when (event) { is FlowEvent.PlayerErrorEvent -> playerError(event) is FlowEvent.BufferUpdateEvent -> bufferUpdate(event) diff --git a/app/src/main/java/ac/mdiq/podcini/preferences/PlaybackPreferences.kt b/app/src/main/java/ac/mdiq/podcini/preferences/PlaybackPreferences.kt index db3e25d5..5b770574 100644 --- a/app/src/main/java/ac/mdiq/podcini/preferences/PlaybackPreferences.kt +++ b/app/src/main/java/ac/mdiq/podcini/preferences/PlaybackPreferences.kt @@ -1,6 +1,7 @@ package ac.mdiq.podcini.preferences 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.model.feed.FeedItem import ac.mdiq.podcini.storage.model.feed.FeedMedia @@ -14,7 +15,6 @@ import android.content.Context import android.content.SharedPreferences import android.content.SharedPreferences.OnSharedPreferenceChangeListener import android.util.Log -import androidx.preference.PreferenceManager /** * 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 private var instance: PlaybackPreferences? = null - private lateinit var prefs: SharedPreferences +// private lateinit var prefs: SharedPreferences @JvmStatic fun init(context: Context) { instance = PlaybackPreferences() - prefs = PreferenceManager.getDefaultSharedPreferences(context) - prefs.registerOnSharedPreferenceChangeListener(instance) +// prefs = PreferenceManager.getDefaultSharedPreferences(context) + appPrefs.registerOnSharedPreferenceChangeListener(instance) } @JvmStatic 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 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 val currentEpisodeIsVideo: Boolean - get() = prefs.getBoolean(PREF_CURRENT_EPISODE_IS_VIDEO, false) + get() = appPrefs.getBoolean(PREF_CURRENT_EPISODE_IS_VIDEO, false) @JvmStatic val currentPlayerStatus: Int - get() = prefs.getInt(PREF_CURRENT_PLAYER_STATUS, PLAYER_STATUS_OTHER) + get() = appPrefs.getInt(PREF_CURRENT_PLAYER_STATUS, PLAYER_STATUS_OTHER) @JvmStatic 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) { - val editor = prefs.edit() + val editor = appPrefs.edit() editor.putFloat(PREF_CURRENTLY_PLAYING_TEMPORARY_PLAYBACK_SPEED, speed) editor.apply() } @JvmStatic 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_FEED_ID, NO_MEDIA_PLAYING) editor.putLong(PREF_CURRENTLY_PLAYING_FEEDMEDIA_ID, NO_MEDIA_PLAYING) @@ -135,7 +135,7 @@ class PlaybackPreferences private constructor() : OnSharedPreferenceChangeListen @JvmStatic fun writeMediaPlaying(playable: Playable?, playerStatus: PlayerStatus, item: FeedItem? = null) { Logd(TAG, "Writing playback preferences ${playable?.getIdentifier()}") - val editor = prefs.edit() + val editor = appPrefs.edit() if (playable == null) { writeNoMediaPlaying() @@ -162,14 +162,14 @@ class PlaybackPreferences private constructor() : OnSharedPreferenceChangeListen fun writePlayerStatus(playerStatus: PlayerStatus) { Logd(TAG, "Writing player status playback preferences") - val editor = prefs.edit() + val editor = appPrefs.edit() editor.putInt(PREF_CURRENT_PLAYER_STATUS, getCurrentPlayerStatusAsInt(playerStatus)) editor.apply() } @JvmStatic fun clearCurrentlyPlayingTemporaryPlaybackSpeed() { - val editor = prefs.edit() + val editor = appPrefs.edit() editor.remove(PREF_CURRENTLY_PLAYING_TEMPORARY_PLAYBACK_SPEED) editor.apply() } @@ -190,40 +190,23 @@ class PlaybackPreferences private constructor() : OnSharedPreferenceChangeListen * @return The restored Playable object */ @JvmStatic - fun createInstanceFromPreferences(context: Context): Playable? { - val currentlyPlayingMedia = currentlyPlayingMediaType - Logd(TAG, "currentlyPlayingMedia: $currentlyPlayingMedia") - if (currentlyPlayingMedia != NO_MEDIA_PLAYING) { - val prefs = PreferenceManager.getDefaultSharedPreferences(context.applicationContext) - return createInstanceFromPreferences(currentlyPlayingMedia.toInt(), prefs) + fun loadPlayableFromPreferences(): Playable? { + val currentlyPlayingType = currentlyPlayingMediaType + Logd(TAG, "loadPlayableFromPreferences currentlyPlayingType: $currentlyPlayingType") + if (currentlyPlayingType != NO_MEDIA_PLAYING) { + val type = currentlyPlayingType.toInt() + 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 } - - /** - * 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 - } } } diff --git a/app/src/main/java/ac/mdiq/podcini/preferences/PreferenceUpgrader.kt b/app/src/main/java/ac/mdiq/podcini/preferences/PreferenceUpgrader.kt index 10627a02..65fa41d0 100644 --- a/app/src/main/java/ac/mdiq/podcini/preferences/PreferenceUpgrader.kt +++ b/app/src/main/java/ac/mdiq/podcini/preferences/PreferenceUpgrader.kt @@ -16,6 +16,8 @@ import ac.mdiq.podcini.preferences.UserPreferences.isStreamOverDownload import ac.mdiq.podcini.preferences.UserPreferences.theme import ac.mdiq.podcini.ui.actions.swipeactions.SwipeAction 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.QueueFragment 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() } if (oldVersion < 2040000) { - val swipePrefs = context.getSharedPreferences(SwipeActions.PREF_NAME, Context.MODE_PRIVATE) - swipePrefs.edit().putString(SwipeActions.KEY_PREFIX_SWIPEACTIONS + QueueFragment.TAG, + getSharedPrefs(context) +// 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() } if (oldVersion < 2050000) prefs.edit().putBoolean(UserPreferences.PREF_PAUSE_PLAYBACK_FOR_FOCUS_LOSS, true).apply() diff --git a/app/src/main/java/ac/mdiq/podcini/preferences/UserPreferences.kt b/app/src/main/java/ac/mdiq/podcini/preferences/UserPreferences.kt index e5d064b8..032984a5 100644 --- a/app/src/main/java/ac/mdiq/podcini/preferences/UserPreferences.kt +++ b/app/src/main/java/ac/mdiq/podcini/preferences/UserPreferences.kt @@ -138,7 +138,7 @@ object UserPreferences { const val DEFAULT_PAGE_REMEMBER: String = "remember" private lateinit var context: Context - private lateinit var prefs: SharedPreferences + lateinit var appPrefs: SharedPreferences /** * Sets up the UserPreferences class. @@ -150,41 +150,41 @@ object UserPreferences { Logd(TAG, "Creating new instance of UserPreferences") UserPreferences.context = context.applicationContext - prefs = PreferenceManager.getDefaultSharedPreferences(context) + appPrefs = PreferenceManager.getDefaultSharedPreferences(context) createNoMediaFile() } @JvmStatic var theme: ThemePreference - get() = when (prefs.getString(PREF_THEME, "system")) { + get() = when (appPrefs.getString(PREF_THEME, "system")) { "0" -> ThemePreference.LIGHT "1" -> ThemePreference.DARK else -> ThemePreference.SYSTEM } set(theme) { when (theme) { - ThemePreference.LIGHT -> prefs.edit().putString(PREF_THEME, "0").apply() - ThemePreference.DARK -> prefs.edit().putString(PREF_THEME, "1").apply() - else -> prefs.edit().putString(PREF_THEME, "system").apply() + ThemePreference.LIGHT -> appPrefs.edit().putString(PREF_THEME, "0").apply() + ThemePreference.DARK -> appPrefs.edit().putString(PREF_THEME, "1").apply() + else -> appPrefs.edit().putString(PREF_THEME, "system").apply() } } val isBlackTheme: Boolean - get() = prefs.getBoolean(PREF_THEME_BLACK, false) + get() = appPrefs.getBoolean(PREF_THEME_BLACK, false) 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 var hiddenDrawerItems: List get() { - val hiddenItems = prefs.getString(PREF_HIDDEN_DRAWER_ITEMS, "") + val hiddenItems = appPrefs.getString(PREF_HIDDEN_DRAWER_ITEMS, "") return ArrayList(listOf(*TextUtils.split(hiddenItems, ","))) } set(items) { val str = TextUtils.join(",", items) - prefs.edit() + appPrefs.edit() .putString(PREF_HIDDEN_DRAWER_ITEMS, str) .apply() } @@ -192,7 +192,7 @@ object UserPreferences { @JvmStatic var fullNotificationButtons: List 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 = ArrayList() for (button in buttons) { notificationButtons.add(button.toInt()) @@ -200,8 +200,8 @@ object UserPreferences { return notificationButtons } set(items) { - val str = TextUtils.join(",", items!!) - prefs.edit() + val str = TextUtils.join(",", items) + appPrefs.edit() .putString(PREF_FULL_NOTIFICATION_BUTTONS, str) .apply() } @@ -216,7 +216,7 @@ object UserPreferences { * @return `true` if button should be shown, `false` otherwise */ private fun showButtonOnFullNotification(buttonId: Int): Boolean { - return fullNotificationButtons!!.contains(buttonId) + return fullNotificationButtons.contains(buttonId) } @JvmStatic @@ -237,13 +237,13 @@ object UserPreferences { @JvmStatic val feedOrder: Int 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() } @JvmStatic fun setFeedOrder(selected: String?) { - prefs.edit() + appPrefs.edit() .putString(PREF_DRAWER_FEED_ORDER, selected) .apply() } @@ -251,7 +251,7 @@ object UserPreferences { @JvmStatic val feedCounterSetting: FeedCounter 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()) } @@ -259,14 +259,14 @@ object UserPreferences { /** * @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 */ @JvmStatic 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 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 @@ -286,7 +286,7 @@ object UserPreferences { * * @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 val isPersistNotify: Boolean @@ -295,23 +295,23 @@ object UserPreferences { * * @return `true` if notifications are persistent, `false` otherwise */ - get() = prefs.getBoolean(PREF_PERSISTENT_NOTIFICATION, true) + get() = appPrefs.getBoolean(PREF_PERSISTENT_NOTIFICATION, true) @JvmStatic val showDownloadReportRaw: Boolean /** * 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 { - return prefs.getBoolean(PREF_ENQUEUE_DOWNLOADED, true) + return appPrefs.getBoolean(PREF_ENQUEUE_DOWNLOADED, true) } @JvmStatic var enqueueLocation: EnqueueLocation get() { - val valStr = prefs.getString(PREF_ENQUEUE_LOCATION, EnqueueLocation.BACK.name) + val valStr = appPrefs.getString(PREF_ENQUEUE_LOCATION, EnqueueLocation.BACK.name) try { return EnqueueLocation.valueOf(valStr!!) } catch (t: Throwable) { @@ -321,64 +321,64 @@ object UserPreferences { } } set(location) { - prefs.edit().putString(PREF_ENQUEUE_LOCATION, location.name).apply() + appPrefs.edit().putString(PREF_ENQUEUE_LOCATION, location.name).apply() } @JvmStatic val isPauseOnHeadsetDisconnect: Boolean - get() = prefs.getBoolean(PREF_PAUSE_ON_HEADSET_DISCONNECT, true) + get() = appPrefs.getBoolean(PREF_PAUSE_ON_HEADSET_DISCONNECT, true) @JvmStatic val isUnpauseOnHeadsetReconnect: Boolean - get() = prefs.getBoolean(PREF_UNPAUSE_ON_HEADSET_RECONNECT, true) + get() = appPrefs.getBoolean(PREF_UNPAUSE_ON_HEADSET_RECONNECT, true) @JvmStatic val isUnpauseOnBluetoothReconnect: Boolean - get() = prefs.getBoolean(PREF_UNPAUSE_ON_BLUETOOTH_RECONNECT, false) + get() = appPrefs.getBoolean(PREF_UNPAUSE_ON_BLUETOOTH_RECONNECT, false) @JvmStatic 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 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 @set:VisibleForTesting 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(value) { - prefs.edit().putBoolean(PREF_FOLLOW_QUEUE, value).apply() + appPrefs.edit().putBoolean(PREF_FOLLOW_QUEUE, value).apply() } @JvmStatic fun shouldSkipKeepEpisode(): Boolean { - return prefs.getBoolean(PREF_SKIP_KEEPS_EPISODE, true) + return appPrefs.getBoolean(PREF_SKIP_KEEPS_EPISODE, true) } @JvmStatic fun shouldFavoriteKeepEpisode(): Boolean { - return prefs.getBoolean(PREF_FAVORITE_KEEPS_EPISODE, true) + return appPrefs.getBoolean(PREF_FAVORITE_KEEPS_EPISODE, true) } @JvmStatic val isAutoDelete: Boolean - get() = prefs.getBoolean(PREF_AUTO_DELETE, false) + get() = appPrefs.getBoolean(PREF_AUTO_DELETE, false) @JvmStatic val isAutoDeleteLocal: Boolean - get() = prefs.getBoolean(PREF_AUTO_DELETE_LOCAL, false) + get() = appPrefs.getBoolean(PREF_AUTO_DELETE_LOCAL, false) 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 fun shouldDeleteRemoveFromQueue(): Boolean { - return prefs.getBoolean(PREF_DELETE_REMOVES_FROM_QUEUE, false) + return appPrefs.getBoolean(PREF_DELETE_REMOVES_FROM_QUEUE, false) } @JvmStatic @@ -389,7 +389,7 @@ object UserPreferences { private val audioPlaybackSpeed: Float get() { try { - return prefs.getString(PREF_PLAYBACK_SPEED, "1.00")!!.toFloat() + return appPrefs.getString(PREF_PLAYBACK_SPEED, "1.00")!!.toFloat() } catch (e: NumberFormatException) { Log.e(TAG, Log.getStackTraceString(e)) setPlaybackSpeed(1.0f) @@ -400,7 +400,7 @@ object UserPreferences { val videoPlayMode: Int get() { try { - return prefs.getString(PREF_VIDEO_MODE, "1")!!.toInt() + return appPrefs.getString(PREF_VIDEO_MODE, "1")!!.toInt() } catch (e: NumberFormatException) { Log.e(TAG, Log.getStackTraceString(e)) setVideoMode(1) @@ -412,7 +412,7 @@ object UserPreferences { var videoPlaybackSpeed: Float get() { try { - return prefs.getString(PREF_VIDEO_PLAYBACK_SPEED, "1.00")!!.toFloat() + return appPrefs.getString(PREF_VIDEO_PLAYBACK_SPEED, "1.00")!!.toFloat() } catch (e: NumberFormatException) { Log.e(TAG, Log.getStackTraceString(e)) videoPlaybackSpeed = 1.0f @@ -420,21 +420,21 @@ object UserPreferences { } } set(speed) { - prefs.edit() + appPrefs.edit() .putString(PREF_VIDEO_PLAYBACK_SPEED, speed.toString()) .apply() } @JvmStatic var isSkipSilence: Boolean - get() = prefs.getBoolean(PREF_PLAYBACK_SKIP_SILENCE, false) + get() = appPrefs.getBoolean(PREF_PLAYBACK_SKIP_SILENCE, false) set(skipSilence) { - prefs.edit().putBoolean(PREF_PLAYBACK_SKIP_SILENCE, skipSilence).apply() + appPrefs.edit().putBoolean(PREF_PLAYBACK_SKIP_SILENCE, skipSilence).apply() } @JvmStatic var playbackSpeedArray: List - get() = readPlaybackSpeedArray(prefs.getString(PREF_PLAYBACK_SPEED_ARRAY, null)) + get() = readPlaybackSpeedArray(appPrefs.getString(PREF_PLAYBACK_SPEED_ARRAY, null)) set(speeds) { val format = DecimalFormatSymbols(Locale.US) format.decimalSeparator = '.' @@ -443,16 +443,16 @@ object UserPreferences { for (speed in speeds) { 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 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 - get() = prefs.getString(PREF_UPDATE_INTERVAL, "12")!!.toInt().toLong() + get() = appPrefs.getString(PREF_UPDATE_INTERVAL, "12")!!.toInt().toLong() val isAutoUpdateDisabled: Boolean get() = updateInterval == 0L @@ -460,7 +460,7 @@ object UserPreferences { private fun isAllowMobileFor(type: String): Boolean { val defaultValue = HashSet() defaultValue.add("images") - val allowed = prefs.getStringSet(PREF_MOBILE_UPDATE, defaultValue) + val allowed = appPrefs.getStringSet(PREF_MOBILE_UPDATE, defaultValue) return allowed!!.contains(type) } @@ -509,12 +509,12 @@ object UserPreferences { private fun setAllowMobileFor(type: String, allow: Boolean) { val defaultValue = HashSet() defaultValue.add("images") - val getValueStringSet = prefs.getStringSet(PREF_MOBILE_UPDATE, defaultValue) + val getValueStringSet = appPrefs.getStringSet(PREF_MOBILE_UPDATE, defaultValue) val allowed: MutableSet = HashSet(getValueStringSet!!) if (allow) allowed.add(type) else allowed.remove(type) - prefs.edit().putStringSet(PREF_MOBILE_UPDATE, allowed).apply() + appPrefs.edit().putStringSet(PREF_MOBILE_UPDATE, allowed).apply() } @JvmStatic @@ -524,29 +524,29 @@ object UserPreferences { * negative integer EPISODE_CACHE_SIZE_UNLIMITED if the cache size is set to * 'unlimited'. */ - get() = prefs.getString(PREF_EPISODE_CACHE_SIZE, "20")!!.toInt() + get() = appPrefs.getString(PREF_EPISODE_CACHE_SIZE, "20")!!.toInt() @JvmStatic @set:VisibleForTesting var isEnableAutodownload: Boolean - get() = prefs.getBoolean(PREF_ENABLE_AUTODL, false) + get() = appPrefs.getBoolean(PREF_ENABLE_AUTODL, false) set(enabled) { - prefs.edit().putBoolean(PREF_ENABLE_AUTODL, enabled).apply() + appPrefs.edit().putBoolean(PREF_ENABLE_AUTODL, enabled).apply() } @JvmStatic val isEnableAutodownloadOnBattery: Boolean - get() = prefs.getBoolean(PREF_ENABLE_AUTODL_ON_BATTERY, true) + get() = appPrefs.getBoolean(PREF_ENABLE_AUTODL_ON_BATTERY, true) @JvmStatic 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 var speedforwardSpeed: Float get() { try { - return prefs.getString(PREF_SPEEDFORWRD_SPEED, "0.00")!!.toFloat() + return appPrefs.getString(PREF_SPEEDFORWRD_SPEED, "0.00")!!.toFloat() } catch (e: NumberFormatException) { Log.e(TAG, Log.getStackTraceString(e)) speedforwardSpeed = 0.0f @@ -554,14 +554,14 @@ object UserPreferences { } } set(speed) { - prefs.edit().putString(PREF_SPEEDFORWRD_SPEED, speed.toString()).apply() + appPrefs.edit().putString(PREF_SPEEDFORWRD_SPEED, speed.toString()).apply() } @JvmStatic var fallbackSpeed: Float get() { try { - return prefs.getString(PREF_FALLBACK_SPEED, "0.00")!!.toFloat() + return appPrefs.getString(PREF_FALLBACK_SPEED, "0.00")!!.toFloat() } catch (e: NumberFormatException) { Log.e(TAG, Log.getStackTraceString(e)) fallbackSpeed = 0.0f @@ -569,42 +569,42 @@ object UserPreferences { } } set(speed) { - prefs.edit().putString(PREF_FALLBACK_SPEED, speed.toString()).apply() + appPrefs.edit().putString(PREF_FALLBACK_SPEED, speed.toString()).apply() } @JvmStatic var fastForwardSecs: Int - get() = prefs.getInt(PREF_FAST_FORWARD_SECS, 30) + get() = appPrefs.getInt(PREF_FAST_FORWARD_SECS, 30) set(secs) { - prefs.edit().putInt(PREF_FAST_FORWARD_SECS, secs).apply() + appPrefs.edit().putInt(PREF_FAST_FORWARD_SECS, secs).apply() } @JvmStatic var rewindSecs: Int - get() = prefs.getInt(PREF_REWIND_SECS, 10) + get() = appPrefs.getInt(PREF_REWIND_SECS, 10) set(secs) { - prefs.edit().putInt(PREF_REWIND_SECS, secs).apply() + appPrefs.edit().putInt(PREF_REWIND_SECS, secs).apply() } @JvmStatic val autodownloadSelectedNetworks: Array get() { - val selectedNetWorks = prefs.getString(PREF_AUTODL_SELECTED_NETWORKS, "") + val selectedNetWorks = appPrefs.getString(PREF_AUTODL_SELECTED_NETWORKS, "") return TextUtils.split(selectedNetWorks, ",") } @JvmStatic var proxyConfig: ProxyConfig get() { - val type = Proxy.Type.valueOf(prefs.getString(PREF_PROXY_TYPE, Proxy.Type.DIRECT.name)!!) - val host = prefs.getString(PREF_PROXY_HOST, null) - val port = prefs.getInt(PREF_PROXY_PORT, 0) - val username = prefs.getString(PREF_PROXY_USER, null) - val password = prefs.getString(PREF_PROXY_PASSWORD, null) + val type = Proxy.Type.valueOf(appPrefs.getString(PREF_PROXY_TYPE, Proxy.Type.DIRECT.name)!!) + val host = appPrefs.getString(PREF_PROXY_HOST, null) + val port = appPrefs.getInt(PREF_PROXY_PORT, 0) + val username = appPrefs.getString(PREF_PROXY_USER, null) + val password = appPrefs.getString(PREF_PROXY_PASSWORD, null) return ProxyConfig(type, host, port, username, password) } set(config) { - val editor = prefs.edit() + val editor = appPrefs.edit() editor.putString(PREF_PROXY_TYPE, config.type.name) if (config.host.isNullOrEmpty()) editor.remove(PREF_PROXY_HOST) else editor.putString(PREF_PROXY_HOST, config.host) @@ -623,31 +623,31 @@ object UserPreferences { @JvmStatic var isQueueLocked: Boolean - get() = prefs.getBoolean(PREF_QUEUE_LOCKED, false) + get() = appPrefs.getBoolean(PREF_QUEUE_LOCKED, false) set(locked) { - prefs.edit().putBoolean(PREF_QUEUE_LOCKED, locked).apply() + appPrefs.edit().putBoolean(PREF_QUEUE_LOCKED, locked).apply() } @JvmStatic fun setPlaybackSpeed(speed: Float) { - prefs.edit().putString(PREF_PLAYBACK_SPEED, speed.toString()).apply() + appPrefs.edit().putString(PREF_PLAYBACK_SPEED, speed.toString()).apply() } @JvmStatic fun setVideoMode(mode: Int) { - prefs.edit().putString(PREF_VIDEO_MODE, mode.toString()).apply() + appPrefs.edit().putString(PREF_VIDEO_MODE, mode.toString()).apply() } @JvmStatic fun setAutodownloadSelectedNetworks(value: Array?) { - prefs.edit().putString(PREF_AUTODL_SELECTED_NETWORKS, TextUtils.join(",", value!!)).apply() + appPrefs.edit().putString(PREF_AUTODL_SELECTED_NETWORKS, TextUtils.join(",", value!!)).apply() } @JvmStatic fun gpodnetNotificationsEnabled(): Boolean { 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 @@ -655,11 +655,11 @@ object UserPreferences { /** * 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 fun setGpodnetNotificationsEnabled() { - prefs.edit().putBoolean(PREF_GPODNET_NOTIFICATIONS, true).apply() + appPrefs.edit().putBoolean(PREF_GPODNET_NOTIFICATIONS, true).apply() } private fun readPlaybackSpeedArray(valueFromPrefs: String?): List { @@ -682,9 +682,9 @@ object UserPreferences { @JvmStatic 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) { - 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 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()) { Logd(TAG, "User data folder not writable or not set. Trying default.") dataFolder = context.getExternalFilesDir(type) @@ -730,7 +730,7 @@ object UserPreferences { @JvmStatic fun setDataFolder(dir: String) { 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 var defaultPage: String? - get() = prefs.getString(PREF_DEFAULT_PAGE, "SubscriptionFragment") + get() = appPrefs.getString(PREF_DEFAULT_PAGE, "SubscriptionFragment") set(defaultPage) { - prefs.edit().putString(PREF_DEFAULT_PAGE, defaultPage).apply() + appPrefs.edit().putString(PREF_DEFAULT_PAGE, defaultPage).apply() } @JvmStatic fun backButtonOpensDrawer(): Boolean { - return prefs.getBoolean(PREF_BACK_OPENS_DRAWER, false) + return appPrefs.getBoolean(PREF_BACK_OPENS_DRAWER, false) } @JvmStatic fun timeRespectsSpeed(): Boolean { - return prefs.getBoolean(PREF_TIME_RESPECTS_SPEED, false) + return appPrefs.getBoolean(PREF_TIME_RESPECTS_SPEED, false) } @JvmStatic var isStreamOverDownload: Boolean - get() = prefs.getBoolean(PREF_STREAM_OVER_DOWNLOAD, false) + get() = appPrefs.getBoolean(PREF_STREAM_OVER_DOWNLOAD, false) set(stream) { - prefs.edit().putBoolean(PREF_STREAM_OVER_DOWNLOAD, stream).apply() + appPrefs.edit().putBoolean(PREF_STREAM_OVER_DOWNLOAD, stream).apply() } @JvmStatic @@ -780,14 +780,14 @@ object UserPreferences { * * @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. * * @see .setQueueKeepSortedOrder */ set(keepSorted) { - prefs.edit().putBoolean(PREF_QUEUE_KEEP_SORTED, keepSorted).apply() + appPrefs.edit().putBoolean(PREF_QUEUE_KEEP_SORTED, keepSorted).apply() } @JvmStatic @@ -799,7 +799,7 @@ object UserPreferences { * @see .isQueueKeepSorted */ 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) } /** @@ -809,13 +809,13 @@ object UserPreferences { */ set(sortOrder) { 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 val newEpisodesAction: NewEpisodesAction 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()) } @@ -825,14 +825,14 @@ object UserPreferences { * Returns the sort order for the downloads. */ 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) } /** * Sets the sort order for the downloads. */ set(sortOrder) { - prefs.edit().putString(PREF_DOWNLOADS_SORTED_ORDER, "" + sortOrder!!.code).apply() + appPrefs.edit().putString(PREF_DOWNLOADS_SORTED_ORDER, "" + sortOrder!!.code).apply() } // @JvmStatic @@ -855,11 +855,11 @@ object UserPreferences { @JvmStatic var subscriptionsFilter: SubscriptionsFilter get() { - val value = prefs.getString(PREF_FILTER_FEED, "") + val value = appPrefs.getString(PREF_FILTER_FEED, "") return SubscriptionsFilter(value) } set(value) { - prefs.edit().putString(PREF_FILTER_FEED, value.serialize()).apply() + appPrefs.edit().putString(PREF_FILTER_FEED, value.serialize()).apply() } @JvmStatic @@ -870,16 +870,16 @@ object UserPreferences { @JvmStatic 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) { - prefs.edit().putString(PREF_SORT_ALL_EPISODES, "" + s!!.code).apply() + appPrefs.edit().putString(PREF_SORT_ALL_EPISODES, "" + s!!.code).apply() } @JvmStatic var prefFilterAllEpisodes: String - get() = prefs.getString(PREF_FILTER_ALL_EPISODES, "")?:"" + get() = appPrefs.getString(PREF_FILTER_ALL_EPISODES, "")?:"" set(filter) { - prefs.edit().putString(PREF_FILTER_ALL_EPISODES, filter).apply() + appPrefs.edit().putString(PREF_FILTER_ALL_EPISODES, filter).apply() } enum class ThemePreference { diff --git a/app/src/main/java/ac/mdiq/podcini/preferences/fragments/DownloadsPreferencesFragment.kt b/app/src/main/java/ac/mdiq/podcini/preferences/fragments/DownloadsPreferencesFragment.kt index 16f262ed..156c633a 100644 --- a/app/src/main/java/ac/mdiq/podcini/preferences/fragments/DownloadsPreferencesFragment.kt +++ b/app/src/main/java/ac/mdiq/podcini/preferences/fragments/DownloadsPreferencesFragment.kt @@ -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.ProxyDialog 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.setDataFolder import kotlin.Any @@ -32,12 +33,12 @@ class DownloadsPreferencesFragment : PreferenceFragmentCompat(), OnSharedPrefere override fun onStart() { super.onStart() (activity as PreferenceActivity).supportActionBar!!.setTitle(R.string.downloads_pref) - PreferenceManager.getDefaultSharedPreferences(requireContext()).registerOnSharedPreferenceChangeListener(this) + appPrefs.registerOnSharedPreferenceChangeListener(this) } override fun onStop() { super.onStop() - PreferenceManager.getDefaultSharedPreferences(requireContext()).unregisterOnSharedPreferenceChangeListener(this) + appPrefs.unregisterOnSharedPreferenceChangeListener(this) } override fun onResume() { diff --git a/app/src/main/java/ac/mdiq/podcini/preferences/fragments/MainPreferencesFragment.kt b/app/src/main/java/ac/mdiq/podcini/preferences/fragments/MainPreferencesFragment.kt index c62456b6..ebd01673 100644 --- a/app/src/main/java/ac/mdiq/podcini/preferences/fragments/MainPreferencesFragment.kt +++ b/app/src/main/java/ac/mdiq/podcini/preferences/fragments/MainPreferencesFragment.kt @@ -21,6 +21,7 @@ class MainPreferencesFragment : PreferenceFragmentCompat() { override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { Logd("MainPreferencesFragment", "onCreatePreferences") +// TODO: this can be expensive addPreferencesFromResource(R.xml.preferences) setupMainScreen() setupSearch() diff --git a/app/src/main/java/ac/mdiq/podcini/preferences/fragments/SwipePreferencesFragment.kt b/app/src/main/java/ac/mdiq/podcini/preferences/fragments/SwipePreferencesFragment.kt index cc083eff..851aa5fc 100644 --- a/app/src/main/java/ac/mdiq/podcini/preferences/fragments/SwipePreferencesFragment.kt +++ b/app/src/main/java/ac/mdiq/podcini/preferences/fragments/SwipePreferencesFragment.kt @@ -37,7 +37,7 @@ class SwipePreferencesFragment : PreferenceFragmentCompat() { true } findPreference(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() {} }) true diff --git a/app/src/main/java/ac/mdiq/podcini/preferences/fragments/synchronization/SynchronizationPreferencesFragment.kt b/app/src/main/java/ac/mdiq/podcini/preferences/fragments/synchronization/SynchronizationPreferencesFragment.kt index bf7de1c5..00e647ed 100644 --- a/app/src/main/java/ac/mdiq/podcini/preferences/fragments/synchronization/SynchronizationPreferencesFragment.kt +++ b/app/src/main/java/ac/mdiq/podcini/preferences/fragments/synchronization/SynchronizationPreferencesFragment.kt @@ -10,6 +10,9 @@ import ac.mdiq.podcini.net.sync.SynchronizationSettings.isProviderConnected import ac.mdiq.podcini.net.sync.SynchronizationSettings.wifiSyncEnabledKey import ac.mdiq.podcini.ui.activity.PreferenceActivity 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.FlowEvent import android.app.Activity @@ -56,6 +59,7 @@ class SynchronizationPreferencesFragment : PreferenceFragmentCompat() { private fun procFlowEvents() { lifecycleScope.launch { EventFlow.events.collectLatest { event -> + Logd("SynchronizationPreferencesFragment", "Received event: ${event}") when (event) { is FlowEvent.SyncServiceEvent -> syncStatusChanged(event) else -> {} diff --git a/app/src/main/java/ac/mdiq/podcini/preferences/fragments/synchronization/WifiAuthenticationFragment.kt b/app/src/main/java/ac/mdiq/podcini/preferences/fragments/synchronization/WifiAuthenticationFragment.kt index dd371a92..f78b72e6 100644 --- a/app/src/main/java/ac/mdiq/podcini/preferences/fragments/synchronization/WifiAuthenticationFragment.kt +++ b/app/src/main/java/ac/mdiq/podcini/preferences/fragments/synchronization/WifiAuthenticationFragment.kt @@ -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.wifi.WifiSyncService.Companion.hostPort 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.event.EventFlow import ac.mdiq.podcini.util.event.FlowEvent @@ -96,6 +98,7 @@ import java.util.* private fun procFlowEvents() { lifecycleScope.launch { EventFlow.events.collectLatest { event -> + Logd(TAG, "Received event: $event") when (event) { is FlowEvent.SyncServiceEvent -> syncStatusChanged(event) else -> {} diff --git a/app/src/main/java/ac/mdiq/podcini/receiver/PlayerWidget.kt b/app/src/main/java/ac/mdiq/podcini/receiver/PlayerWidget.kt index 2af3dbf7..024f7124 100644 --- a/app/src/main/java/ac/mdiq/podcini/receiver/PlayerWidget.kt +++ b/app/src/main/java/ac/mdiq/podcini/receiver/PlayerWidget.kt @@ -1,5 +1,6 @@ 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.AppWidgetProvider import android.content.ComponentName @@ -10,6 +11,7 @@ import androidx.work.OneTimeWorkRequest import androidx.work.WorkManager import ac.mdiq.podcini.ui.widget.WidgetUpdaterWorker import ac.mdiq.podcini.util.Logd +import android.content.SharedPreferences import java.util.concurrent.TimeUnit class PlayerWidget : AppWidgetProvider() { @@ -25,10 +27,9 @@ class PlayerWidget : AppWidgetProvider() { Logd(TAG, "onUpdate() called with: context = [$context], appWidgetManager = [$appWidgetManager], appWidgetIds = [${appWidgetIds.contentToString()}]") 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) - 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) { Logd(TAG, "OnDeleted") - val prefs = context.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE) for (appWidgetId in appWidgetIds) { - prefs.edit().remove(KEY_WIDGET_COLOR + appWidgetId).apply() - prefs.edit().remove(KEY_WIDGET_PLAYBACK_SPEED + appWidgetId).apply() - prefs.edit().remove(KEY_WIDGET_REWIND + appWidgetId).apply() - prefs.edit().remove(KEY_WIDGET_FAST_FORWARD + appWidgetId).apply() - prefs.edit().remove(KEY_WIDGET_SKIP + appWidgetId).apply() + prefs!!.edit().remove(KEY_WIDGET_COLOR + appWidgetId).apply() + prefs!!.edit().remove(KEY_WIDGET_PLAYBACK_SPEED + appWidgetId).apply() + prefs!!.edit().remove(KEY_WIDGET_REWIND + appWidgetId).apply() + prefs!!.edit().remove(KEY_WIDGET_FAST_FORWARD + appWidgetId).apply() + prefs!!.edit().remove(KEY_WIDGET_SKIP + appWidgetId).apply() } val manager = AppWidgetManager.getInstance(context) val widgetIds = manager.getAppWidgetIds(ComponentName(context, PlayerWidget::class.java)) 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) } super.onDeleted(context, appWidgetIds) } 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 { @@ -75,6 +74,12 @@ class PlayerWidget : AppWidgetProvider() { const val DEFAULT_COLOR: Int = -0xd9d3cf 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) { // Enqueueing work enables a BOOT_COMPLETED receiver, which in turn makes Android refresh widgets. // This creates an endless loop with a flickering widget. @@ -87,8 +92,8 @@ class PlayerWidget : AppWidgetProvider() { @JvmStatic fun isEnabled(context: Context): Boolean { - val prefs = context.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE) - return prefs.getBoolean(KEY_ENABLED, false) +// val prefs = context.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE) + return prefs!!.getBoolean(KEY_ENABLED, false) } } } diff --git a/app/src/main/java/ac/mdiq/podcini/storage/APCleanupAlgorithm.kt b/app/src/main/java/ac/mdiq/podcini/storage/APCleanupAlgorithm.kt index d05c3316..0fccbb8b 100644 --- a/app/src/main/java/ac/mdiq/podcini/storage/APCleanupAlgorithm.kt +++ b/app/src/main/java/ac/mdiq/podcini/storage/APCleanupAlgorithm.kt @@ -9,17 +9,17 @@ import ac.mdiq.podcini.storage.model.feed.FeedItemFilter 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.concurrent.ExecutionException /** * 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. - * Fractional for number of hours, e.g., 0.5 = 12 hours, 0.0416 = 1 hour. */ - @JvmField @get:VisibleForTesting val numberOfHoursAfterPlayback: Int -) : EpisodeCleanupAlgorithm() { +/** 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. */ + +class APCleanupAlgorithm(@JvmField @get:VisibleForTesting val numberOfHoursAfterPlayback: Int) : EpisodeCleanupAlgorithm() { /** * @return the number of episodes that *could* be cleaned up, if needed */ @@ -44,7 +44,7 @@ class APCleanupAlgorithm( for (item in delete) { try { - DBWriter.deleteFeedMediaOfItem(context!!, item.media!!.id).get() + runBlocking { DBWriter.deleteFeedMediaOfItem(context, item.media!!.id).join() } } catch (e: InterruptedException) { e.printStackTrace() } catch (e: ExecutionException) { @@ -53,7 +53,6 @@ class APCleanupAlgorithm( } val counter = delete.size - Log.i(TAG, String.format(Locale.US, "Auto-delete deleted %d episodes (%d requested)", counter, numberOfEpisodesToDelete)) return counter diff --git a/app/src/main/java/ac/mdiq/podcini/storage/APQueueCleanupAlgorithm.kt b/app/src/main/java/ac/mdiq/podcini/storage/APQueueCleanupAlgorithm.kt index 7364260d..05ca97f4 100644 --- a/app/src/main/java/ac/mdiq/podcini/storage/APQueueCleanupAlgorithm.kt +++ b/app/src/main/java/ac/mdiq/podcini/storage/APQueueCleanupAlgorithm.kt @@ -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.FeedItemFilter 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.concurrent.ExecutionException @@ -21,7 +24,7 @@ class APQueueCleanupAlgorithm : EpisodeCleanupAlgorithm() { 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 // 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 for (item in delete) { + if (item.media == null) continue try { - DBWriter.deleteFeedMediaOfItem(context!!, item.media!!.id).get() + runBlocking { DBWriter.deleteFeedMediaOfItem(context, item.media!!.id).join() } } catch (e: InterruptedException) { e.printStackTrace() } catch (e: ExecutionException) { diff --git a/app/src/main/java/ac/mdiq/podcini/storage/DBTasks.kt b/app/src/main/java/ac/mdiq/podcini/storage/DBTasks.kt index 8a2a17ef..5ef4c86d 100644 --- a/app/src/main/java/ac/mdiq/podcini/storage/DBTasks.kt +++ b/app/src/main/java/ac/mdiq/podcini/storage/DBTasks.kt @@ -25,6 +25,7 @@ import android.text.TextUtils import android.util.Log import androidx.annotation.VisibleForTesting import androidx.media3.common.util.UnstableApi +import kotlinx.coroutines.runBlocking import java.util.* import java.util.concurrent.* @@ -68,15 +69,13 @@ import java.util.concurrent.* if (feedID != 0L) { try { - DBWriter.deleteFeed(context, feedID).get() + runBlocking { DBWriter.deleteFeed(context, feedID).join() } } catch (e: InterruptedException) { e.printStackTrace() } catch (e: ExecutionException) { e.printStackTrace() } - } else { - Log.w(TAG, "removeFeedWithDownloadUrl: Could not find feed with url: $downloadUrl") - } + } else 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? { - if (feed.id != 0L) { - return getFeed(feed.id) - } else { - val feeds = getFeedList() - for (f in feeds.toList()) { - if (f != null && f.identifyingValue == feed.identifyingValue) { - f.items = getFeedItemList(f).toMutableList() - return f - } + if (feed.id != 0L) return getFeed(feed.id) + + val feeds = getFeedList() + for (f in feeds.toList()) { + if (f != null && f.identifyingValue == feed.identifyingValue) { + f.items = getFeedItemList(f).toMutableList() + return f } } return null @@ -275,9 +272,8 @@ import java.util.concurrent.* } } - if (oldItem != null) { - oldItem.updateFromOther(item) - } else { + if (oldItem != null) oldItem.updateFromOther(item) + else { Logd(TAG, "Found new item: " + item.title) item.feed = savedFeed @@ -314,13 +310,13 @@ import java.util.concurrent.* try { if (savedFeed == null) { - DBWriter.addNewFeed(context, newFeed).get() + runBlocking { DBWriter.addNewFeed(context, newFeed).join() } // Update with default values that are set in database resultFeed = searchFeedByIdentifyingValueOrID(newFeed) - } else DBWriter.persistCompleteFeed(savedFeed).get() + } else runBlocking { DBWriter.persistCompleteFeed(savedFeed).join() } DBReader.updateFeedList(adapter) - if (removeUnlistedItems) DBWriter.deleteFeedItems(context, unlistedItems).get() + if (removeUnlistedItems) runBlocking { DBWriter.deleteFeedItems(context, unlistedItems).join() } } catch (e: InterruptedException) { e.printStackTrace() } catch (e: ExecutionException) { diff --git a/app/src/main/java/ac/mdiq/podcini/storage/DBWriter.kt b/app/src/main/java/ac/mdiq/podcini/storage/DBWriter.kt index df00fc41..302498a6 100644 --- a/app/src/main/java/ac/mdiq/podcini/storage/DBWriter.kt +++ b/app/src/main/java/ac/mdiq/podcini/storage/DBWriter.kt @@ -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.queue.SynchronizationQueueSink 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.writeNoMediaPlaying 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.event.EventFlow import ac.mdiq.podcini.util.event.FlowEvent -import ac.mdiq.podcini.util.showStackTrace import android.app.backup.BackupManager import android.content.Context import android.net.Uri @@ -37,16 +36,13 @@ import android.util.Log import androidx.core.app.NotificationManagerCompat import androidx.documentfile.provider.DocumentFile import androidx.media3.common.util.UnstableApi -import com.google.common.util.concurrent.Futures -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.async -import kotlinx.coroutines.withContext +import kotlinx.coroutines.* import java.io.File import java.util.* import java.util.concurrent.ExecutorService import java.util.concurrent.Executors -import java.util.concurrent.Future import java.util.concurrent.TimeUnit +import kotlin.coroutines.ContinuationInterceptor /** * Provides methods for writing data to Podcini's database. @@ -57,6 +53,8 @@ import java.util.concurrent.TimeUnit @UnstableApi object DBWriter { private const val TAG = "DBWriter" + val ioScope = CoroutineScope(Dispatchers.IO) + private val dbExec: ExecutorService = Executors.newSingleThreadExecutor { r: Runnable? -> val t = Thread(r) t.name = "DatabaseExecutor" @@ -77,8 +75,8 @@ import java.util.concurrent.TimeUnit } } - fun deleteItemsMedia(items: List) { - runOnDbThread { + fun deleteItemsMedia(items: List) : Job { + return runOnDbThread { val adapter = getInstance() adapter.open() 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. */ @JvmStatic - fun deleteFeedMediaOfItem(context: Context, mediaId: Long): Future<*> { + fun deleteFeedMediaOfItem(context: Context, mediaId: Long) : Job { Logd(TAG, "deleteFeedMediaOfItem called") return runOnDbThread { val media = getFeedMedia(mediaId) @@ -179,7 +177,7 @@ import java.util.concurrent.TimeUnit * @param feedId ID of the Feed that should be deleted. */ @JvmStatic - fun deleteFeed(context: Context, feedId: Long): Future<*> { + fun deleteFeed(context: Context, feedId: Long) : Job { return runOnDbThread { val feed = getFeed(feedId) ?: return@runOnDbThread // 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. * Deleting media also removes the download log entries. */ - fun deleteFeedItems(context: Context, items: List): Future<*> { + fun deleteFeedItems(context: Context, items: List) : Job { Logd(TAG, "deleteFeedItems called") return runOnDbThread { deleteFeedItemsSynchronous(context, items) } } @@ -252,7 +250,7 @@ import java.util.concurrent.TimeUnit /** * Deletes the entire playback history. */ - fun clearPlaybackHistory(): Future<*> { + fun clearPlaybackHistory() : Job { return runOnDbThread { val adapter = getInstance() adapter.open() @@ -265,7 +263,7 @@ import java.util.concurrent.TimeUnit /** * Deletes the entire download log. */ - fun clearDownloadLog(): Future<*> { + fun clearDownloadLog() : Job { return runOnDbThread { val adapter = getInstance() adapter.open() @@ -275,8 +273,8 @@ import java.util.concurrent.TimeUnit } } - fun deleteFromPlaybackHistory(feedItem: FeedItem): Future<*> { - return addItemToPlaybackHistory(feedItem.media, Date(0)) + fun deleteFromPlaybackHistory(feedItem: FeedItem) { + addItemToPlaybackHistory(feedItem.media, Date(0)) } /** @@ -288,7 +286,7 @@ import java.util.concurrent.TimeUnit * @param date PlaybackCompletionDate for `media` */ @JvmOverloads - fun addItemToPlaybackHistory(media: FeedMedia?, date: Date? = Date()): Future<*> { + fun addItemToPlaybackHistory(media: FeedMedia?, date: Date? = Date()) : Job { return runOnDbThread { if (media != null) { Logd(TAG, "Adding item to playback history") @@ -308,7 +306,7 @@ import java.util.concurrent.TimeUnit * * @param status The DownloadStatus object. */ - fun addDownloadStatus(status: DownloadResult?): Future<*> { + fun addDownloadStatus(status: DownloadResult?) : Job { Logd(TAG, "addDownloadStatus called") return runOnDbThread { 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 * @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") return runOnDbThread { val adapter = getInstance() @@ -357,11 +355,11 @@ import java.util.concurrent.TimeUnit } @JvmStatic - fun addQueueItem(context: Context, vararg items: FeedItem): Future<*> { + fun addQueueItem(context: Context, vararg items: FeedItem) : Job { 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") val itemIds = LongList(items.size) 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 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) } @@ -393,7 +391,7 @@ import java.util.concurrent.TimeUnit * @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. */ - @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") return runOnDbThread { if (itemIds.isEmpty()) return@runOnDbThread @@ -406,9 +404,8 @@ import java.util.concurrent.TimeUnit val markAsUnplayedIds = LongList() val events: MutableList = ArrayList() val updatedItems: MutableList = ArrayList() - val positionCalculator = - ItemEnqueuePositionCalculator(enqueueLocation) - val currentlyPlaying = createInstanceFromPreferences(context) + val positionCalculator = ItemEnqueuePositionCalculator(enqueueLocation) + val currentlyPlaying = loadPlayableFromPreferences() var insertPosition = positionCalculator.calcPosition(queue, currentlyPlaying) for (itemId in itemIds) { if (!itemListContains(queue, itemId)) { @@ -468,7 +465,7 @@ import java.util.concurrent.TimeUnit * Removes all FeedItem objects from the queue. */ @JvmStatic - fun clearQueue(): Future<*> { + fun clearQueue() : Job { return runOnDbThread { val adapter = getInstance() adapter.open() @@ -486,19 +483,19 @@ import java.util.concurrent.TimeUnit * @param item FeedItem that should be removed. */ @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) } } @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) } } @UnstableApi private fun removeQueueItemSynchronous(context: Context, performAutoDownload: Boolean, vararg itemIds: Long) { Logd(TAG, "removeQueueItemSynchronous called $itemIds") if (itemIds.isEmpty()) return - showStackTrace() +// showStackTrace() val adapter = getInstance() adapter.open() @@ -535,11 +532,12 @@ import java.util.concurrent.TimeUnit 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 { val adapter = getInstance().open() adapter.addFavoriteItem(item) @@ -550,7 +548,7 @@ import java.util.concurrent.TimeUnit } } - fun removeFavoriteItem(item: FeedItem): Future<*> { + fun removeFavoriteItem(item: FeedItem) : Job { return runOnDbThread { val adapter = getInstance().open() 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 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 { val queueIdList = getQueueIDList() 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 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 { val queueIdList = getQueueIDList() 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()) */ @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) } } @@ -634,7 +632,7 @@ import java.util.concurrent.TimeUnit adapter.close() } - fun resetPagedFeedPage(feed: Feed?): Future<*> { + fun resetPagedFeedPage(feed: Feed?) : Job { return runOnDbThread { if (feed != null) { val adapter = getInstance() @@ -652,7 +650,7 @@ import java.util.concurrent.TimeUnit * FeedItem.UNPLAYED * @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) } @@ -665,7 +663,7 @@ import java.util.concurrent.TimeUnit * This option is usually set to true * @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 { val adapter = getInstance() adapter.open() @@ -683,12 +681,12 @@ import java.util.concurrent.TimeUnit * FeedItem.NEW, FeedItem.UNPLAYED * @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 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 { val adapter = getInstance() adapter.open() @@ -703,7 +701,7 @@ import java.util.concurrent.TimeUnit * * @param feedId ID of the Feed. */ - fun removeFeedNewFlag(feedId: Long): Future<*> { + fun removeFeedNewFlag(feedId: Long) : Job { return runOnDbThread { val adapter = getInstance() adapter.open() @@ -717,7 +715,7 @@ import java.util.concurrent.TimeUnit * Sets the 'read'-attribute of all NEW FeedItems to UNPLAYED. */ @JvmStatic - fun removeAllNewFlags(): Future<*> { + fun removeAllNewFlags() : Job { return runOnDbThread { val adapter = getInstance() 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 { val adapter = getInstance() 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 { val adapter = getInstance() adapter.open() @@ -753,7 +751,7 @@ import java.util.concurrent.TimeUnit } } - fun persistItemList(items: List): Future<*> { + fun persistItemList(items: List) : Job { return runOnDbThread { val adapter = getInstance() adapter.open() @@ -769,7 +767,7 @@ import java.util.concurrent.TimeUnit * * @param media The FeedMedia object. */ - fun persistFeedMedia(media: FeedMedia): Future<*> { + fun persistFeedMedia(media: FeedMedia) : Job { Logd(TAG, "persistFeedMedia called") return runOnDbThread { val adapter = getInstance() @@ -785,7 +783,7 @@ import java.util.concurrent.TimeUnit * @param media The FeedMedia object. */ @JvmStatic - fun persistFeedMediaPlaybackInfo(media: FeedMedia?): Future<*> { + fun persistFeedMediaPlaybackInfo(media: FeedMedia?) : Job { Logd(TAG, "persistFeedMediaPlaybackInfo called") return runOnDbThread { if (media != null) { @@ -804,7 +802,7 @@ import java.util.concurrent.TimeUnit * @param item The FeedItem object. */ @JvmStatic - fun persistFeedItem(item: FeedItem?): Future<*> { + fun persistFeedItem(item: FeedItem?) : Job { Logd(TAG, "persistFeedItem called") return runOnDbThread { if (item != null) { @@ -820,7 +818,7 @@ import java.util.concurrent.TimeUnit /** * 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)") return runOnDbThread { val adapter = getInstance() @@ -835,7 +833,7 @@ import java.util.concurrent.TimeUnit * * @param preferences The FeedPreferences object. */ - fun persistFeedPreferences(preferences: FeedPreferences): Future<*> { + fun persistFeedPreferences(preferences: FeedPreferences) : Job { return runOnDbThread { val adapter = getInstance() adapter.open() @@ -863,7 +861,7 @@ import java.util.concurrent.TimeUnit * * @param lastUpdateFailed true if last update failed */ - fun persistFeedLastUpdateFailed(feedId: Long, lastUpdateFailed: Boolean): Future<*> { + fun persistFeedLastUpdateFailed(feedId: Long, lastUpdateFailed: Boolean) : Job { return runOnDbThread { val adapter = getInstance() adapter.open() @@ -873,7 +871,7 @@ import java.util.concurrent.TimeUnit } } - fun persistFeedCustomTitle(feed: Feed): Future<*> { + fun persistFeedCustomTitle(feed: Feed) : Job { return runOnDbThread { val adapter = getInstance() adapter.open() @@ -890,10 +888,10 @@ import java.util.concurrent.TimeUnit * QueueUpdateBroadcast. This option should be set to `false` * 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) { Log.w(TAG, "reorderQueue() - sortOrder is null. Do nothing.") - return runOnDbThread {} + return Job() } val permutor = getPermutor(sortOrder) return runOnDbThread { @@ -914,7 +912,7 @@ import java.util.concurrent.TimeUnit * @param feedId The feed's ID * @param filterValues Values that represent properties to filter by */ - fun persistFeedItemsFilter(feedId: Long, filterValues: Set): Future<*> { + fun persistFeedItemsFilter(feedId: Long, filterValues: Set) : Job { Logd(TAG, "persistFeedItemsFilter() called with: feedId = [$feedId], filterValues = [$filterValues]") return runOnDbThread { val adapter = getInstance() @@ -929,7 +927,7 @@ import java.util.concurrent.TimeUnit * Set item sort order of the feed * */ - fun persistFeedItemSortOrder(feedId: Long, sortOrder: SortOrder?): Future<*> { + fun persistFeedItemSortOrder(feedId: Long, sortOrder: SortOrder?) : Job { return runOnDbThread { val adapter = getInstance() adapter.open() @@ -942,32 +940,36 @@ import java.util.concurrent.TimeUnit /** * Reset the statistics in DB */ -// fun resetStatistics(): Future<*> { -// return runOnDbThread { -// val adapter = getInstance() -// adapter.open() -// adapter.resetAllMediaPlayedDuration() -// adapter.close() -// } -// } - - suspend fun resetStatistics(): Unit = withContext(Dispatchers.IO) { - val result = async { + fun resetStatistics() : Job { + return runOnDbThread { val adapter = getInstance() adapter.open() adapter.resetAllMediaPlayedDuration() adapter.close() } - result.await() } + /** * Submit to the DB thread only if caller is not already on the DB thread. Otherwise, * just execute synchronously */ - private fun runOnDbThread(runnable: Runnable): Future<*> { - if ("DatabaseExecutor" == Thread.currentThread().name) { - runnable.run() - return Futures.immediateFuture(null) - } else return dbExec.submit(runnable) +// private fun runOnDbThread(runnable: Runnable): Future<*> { +// if ("DatabaseExecutor" == Thread.currentThread().name) { +// runnable.run() +// return Futures.immediateFuture(null) +// } 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() + } + } + } } } diff --git a/app/src/main/java/ac/mdiq/podcini/storage/ExceptFavoriteCleanupAlgorithm.kt b/app/src/main/java/ac/mdiq/podcini/storage/ExceptFavoriteCleanupAlgorithm.kt index 38f5c7b6..df4ce4f0 100644 --- a/app/src/main/java/ac/mdiq/podcini/storage/ExceptFavoriteCleanupAlgorithm.kt +++ b/app/src/main/java/ac/mdiq/podcini/storage/ExceptFavoriteCleanupAlgorithm.kt @@ -11,6 +11,7 @@ import ac.mdiq.podcini.preferences.UserPreferences import ac.mdiq.podcini.preferences.UserPreferences.episodeCacheSize import androidx.annotation.OptIn import androidx.media3.common.util.UnstableApi +import kotlinx.coroutines.runBlocking import java.util.* import java.util.concurrent.ExecutionException @@ -41,8 +42,9 @@ class ExceptFavoriteCleanupAlgorithm : EpisodeCleanupAlgorithm() { val delete = if (candidates.size > numberOfEpisodesToDelete) candidates.subList(0, numberOfEpisodesToDelete) else candidates for (item in delete) { + if (item.media == null) continue try { - DBWriter.deleteFeedMediaOfItem(context!!, item.media!!.id).get() + runBlocking { DBWriter.deleteFeedMediaOfItem(context, item.media!!.id).join() } } catch (e: InterruptedException) { e.printStackTrace() } catch (e: ExecutionException) { diff --git a/app/src/main/java/ac/mdiq/podcini/storage/model/feed/Feed.kt b/app/src/main/java/ac/mdiq/podcini/storage/model/feed/Feed.kt index 57cf3bc0..d594590d 100644 --- a/app/src/main/java/ac/mdiq/podcini/storage/model/feed/Feed.kt +++ b/app/src/main/java/ac/mdiq/podcini/storage/model/feed/Feed.kt @@ -105,7 +105,7 @@ class Feed : FeedFile { var sortOrder: SortOrder? = null set(sortOrder) { if (sortOrder == null) { - Log.w("Feed sortOrder", "The specified sortOrder $sortOrder is invalid.") +// Log.w("Feed sortOrder", "The specified sortOrder $sortOrder is invalid.") return } field = sortOrder diff --git a/app/src/main/java/ac/mdiq/podcini/ui/actions/EpisodeMultiSelectActionHandler.kt b/app/src/main/java/ac/mdiq/podcini/ui/actions/EpisodeMultiSelectActionHandler.kt index 4522c62b..edba8e58 100644 --- a/app/src/main/java/ac/mdiq/podcini/ui/actions/EpisodeMultiSelectActionHandler.kt +++ b/app/src/main/java/ac/mdiq/podcini/ui/actions/EpisodeMultiSelectActionHandler.kt @@ -9,7 +9,7 @@ import ac.mdiq.podcini.storage.DBWriter import ac.mdiq.podcini.util.LongList import ac.mdiq.podcini.storage.model.feed.FeedItem 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 @UnstableApi diff --git a/app/src/main/java/ac/mdiq/podcini/ui/actions/actionbutton/DeleteActionButton.kt b/app/src/main/java/ac/mdiq/podcini/ui/actions/actionbutton/DeleteActionButton.kt index 1fd4e1bb..ba481e45 100644 --- a/app/src/main/java/ac/mdiq/podcini/ui/actions/actionbutton/DeleteActionButton.kt +++ b/app/src/main/java/ac/mdiq/podcini/ui/actions/actionbutton/DeleteActionButton.kt @@ -5,7 +5,7 @@ import android.view.View import ac.mdiq.podcini.R import ac.mdiq.podcini.storage.DBWriter 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 class DeleteActionButton(item: FeedItem) : ItemActionButton(item) { diff --git a/app/src/main/java/ac/mdiq/podcini/ui/actions/menuhandler/FeedItemMenuHandler.kt b/app/src/main/java/ac/mdiq/podcini/ui/actions/menuhandler/FeedItemMenuHandler.kt index 95f6a909..b2afba9d 100644 --- a/app/src/main/java/ac/mdiq/podcini/ui/actions/menuhandler/FeedItemMenuHandler.kt +++ b/app/src/main/java/ac/mdiq/podcini/ui/actions/menuhandler/FeedItemMenuHandler.kt @@ -1,7 +1,6 @@ package ac.mdiq.podcini.ui.actions.menuhandler 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.wifiSyncEnabledKey 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.ui.activity.MainActivity 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 android.os.Handler -import android.util.Log import android.view.KeyEvent import android.view.Menu import androidx.annotation.OptIn import androidx.fragment.app.Fragment import androidx.media3.common.util.UnstableApi 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 @@ -49,7 +51,6 @@ object FeedItemMenuHandler { val hasMedia = selectedItem.media != null val isPlaying = hasMedia && PlaybackStatus.isPlaying(selectedItem.media) val isInQueue: Boolean = selectedItem.isTagged(FeedItem.TAG_QUEUE) - val fileDownloaded = hasMedia && selectedItem.media?.fileExists()?:false val isLocalFile = hasMedia && selectedItem.feed?.isLocalFeed?:false 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.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 } diff --git a/app/src/main/java/ac/mdiq/podcini/ui/actions/swipeactions/DeleteSwipeAction.kt b/app/src/main/java/ac/mdiq/podcini/ui/actions/swipeactions/DeleteSwipeAction.kt index a817c5f4..ee27ea6e 100644 --- a/app/src/main/java/ac/mdiq/podcini/ui/actions/swipeactions/DeleteSwipeAction.kt +++ b/app/src/main/java/ac/mdiq/podcini/ui/actions/swipeactions/DeleteSwipeAction.kt @@ -7,7 +7,7 @@ import ac.mdiq.podcini.R import ac.mdiq.podcini.storage.DBWriter import ac.mdiq.podcini.storage.model.feed.FeedItem 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 { override fun getId(): String { diff --git a/app/src/main/java/ac/mdiq/podcini/ui/actions/swipeactions/SwipeActions.kt b/app/src/main/java/ac/mdiq/podcini/ui/actions/swipeactions/SwipeActions.kt index bf118dae..db48dc97 100644 --- a/app/src/main/java/ac/mdiq/podcini/ui/actions/swipeactions/SwipeActions.kt +++ b/app/src/main/java/ac/mdiq/podcini/ui/actions/swipeactions/SwipeActions.kt @@ -3,18 +3,21 @@ package ac.mdiq.podcini.ui.actions.swipeactions import ac.mdiq.podcini.R import ac.mdiq.podcini.storage.model.feed.FeedItemFilter 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.view.viewholder.EpisodeItemViewHolder import ac.mdiq.podcini.util.event.EventFlow import ac.mdiq.podcini.util.event.FlowEvent import android.content.Context +import android.content.SharedPreferences import android.graphics.Canvas +import androidx.annotation.OptIn import androidx.core.graphics.ColorUtils import androidx.fragment.app.Fragment -import androidx.lifecycle.Lifecycle -import androidx.lifecycle.LifecycleObserver -import androidx.lifecycle.OnLifecycleEvent +import androidx.lifecycle.* import androidx.media3.common.util.UnstableApi import androidx.recyclerview.widget.ItemTouchHelper import androidx.recyclerview.widget.RecyclerView @@ -26,7 +29,7 @@ import kotlin.math.min import kotlin.math.sin 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 @@ -36,17 +39,19 @@ open class SwipeActions(dragDirs: Int, private val fragment: Fragment, private v private val itemTouchHelper = ItemTouchHelper(this) init { - reloadPreference() - fragment.lifecycle.addObserver(this) + actions = getPrefs(fragment.requireContext(), tag) } constructor(fragment: Fragment, tag: String) : this(0, fragment, tag) - @OnLifecycleEvent(Lifecycle.Event.ON_START) - fun reloadPreference() { + override fun onStart(owner: LifecycleOwner) { actions = getPrefs(fragment.requireContext(), tag) } + override fun onStop(owner: LifecycleOwner) { + actions = null + } + fun setFilter(filter: FeedItemFilter?) { 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) { if (actions != null && !actions!!.hasActions()) { - //open settings dialog if no prefs are set showDialog() -// SwipeActionsDialog(fragment.requireContext(), tag).show(object : SwipeActionsDialog.Callback { -// override fun onCall() { -// this@SwipeActions.reloadPreference() -// EventBus.getDefault().post(SwipeActionsChangedEvent()) -// } -// }) return } - val item = (viewHolder as EpisodeItemViewHolder).feedItem - if (actions != null && item != null && filter != null) (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() { SwipeActionsDialog(fragment.requireContext(), tag).show(object : SwipeActionsDialog.Callback { override fun onCall() { - this@SwipeActions.reloadPreference() + actions = getPrefs(fragment.requireContext(), tag) 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) { swipeOutEnabled = false - val swipeThresholdReached = displacementPercentage == 1f // Move slower when getting near the maxMovement - dx = sign * maxMovement * sin((Math.PI / 2) * displacementPercentage) - .toFloat() + dx = sign * maxMovement * sin((Math.PI / 2) * displacementPercentage).toFloat() if (isCurrentlyActive) { 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 { - 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_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 val swipeActions: List = Collections.unmodifiableList( 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 { - 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) } @@ -224,12 +222,12 @@ open class SwipeActions(dragDirs: Int, private val fragment: Fragment, private v return getPrefs(context, tag, "") } - @JvmStatic + @OptIn(UnstableApi::class) @JvmStatic fun getPrefsWithDefaults(context: Context, tag: String): Actions { val defaultActions = when (tag) { QueueFragment.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 else -> SwipeAction.NO_ACTION + "," + SwipeAction.NO_ACTION } @@ -238,8 +236,7 @@ open class SwipeActions(dragDirs: Int, private val fragment: Fragment, private v @JvmStatic 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) } } } diff --git a/app/src/main/java/ac/mdiq/podcini/ui/activity/MainActivity.kt b/app/src/main/java/ac/mdiq/podcini/ui/activity/MainActivity.kt index a8ca7cfb..8b4f3122 100644 --- a/app/src/main/java/ac/mdiq/podcini/ui/activity/MainActivity.kt +++ b/app/src/main/java/ac/mdiq/podcini/ui/activity/MainActivity.kt @@ -3,6 +3,7 @@ package ac.mdiq.podcini.ui.activity import ac.mdiq.podcini.BuildConfig import ac.mdiq.podcini.R 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.restartUpdateAlarm 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.hiddenDrawerItems 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.DBWriter.ioScope 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.dialog.RatingDialog import ac.mdiq.podcini.ui.fragment.* import ac.mdiq.podcini.ui.statistics.StatisticsFragment 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.event.EventFlow import ac.mdiq.podcini.util.event.FlowEvent @@ -110,7 +116,18 @@ class MainActivity : CastEnabledActivity() { 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)) @@ -145,9 +162,8 @@ class MainActivity : CastEnabledActivity() { val fm = supportFragmentManager if (fm.findFragmentByTag(MAIN_FRAGMENT_TAG) == null) { - if (UserPreferences.DEFAULT_PAGE_REMEMBER != defaultPage) { - loadFragment(defaultPage, null) - } else { + if (UserPreferences.DEFAULT_PAGE_REMEMBER != defaultPage) loadFragment(defaultPage, null) + else { val lastFragment = NavDrawerFragment.getLastNavFragment(this) if (ArrayUtils.contains(NavDrawerFragment.NAV_DRAWER_TAGS, lastFragment)) { loadFragment(lastFragment, null) @@ -173,14 +189,15 @@ class MainActivity : CastEnabledActivity() { navDrawer = findViewById(R.id.navDrawerFragment) audioPlayerFragmentView = findViewById(R.id.audioplayerFragment) - checkFirstLaunch() + ioScope.launch { checkFirstLaunch() } + this.bottomSheet = BottomSheetBehavior.from(audioPlayerFragmentView) as LockableBottomSheetBehavior<*> this.bottomSheet.isHideable = false this.bottomSheet.isDraggable = false this.bottomSheet.setBottomSheetCallback(bottomSheetCallback) restartUpdateAlarm(this, false) - SynchronizationQueueSink.syncNowIfNotSyncedRecently() + ioScope.launch { SynchronizationQueueSink.syncNowIfNotSyncedRecently() } WorkManager.getInstance(this) .getWorkInfosByTagLiveData(FeedUpdateManager.WORK_TAG_FEED_UPDATE) @@ -376,7 +393,7 @@ class MainActivity : CastEnabledActivity() { QueueFragment.TAG -> fragment = QueueFragment() AllEpisodesFragment.TAG -> fragment = AllEpisodesFragment() DownloadsFragment.TAG -> fragment = DownloadsFragment() - PlaybackHistoryFragment.TAG -> fragment = PlaybackHistoryFragment() + HistoryFragment.TAG -> fragment = HistoryFragment() AddFeedFragment.TAG -> fragment = AddFeedFragment() SubscriptionFragment.TAG -> fragment = SubscriptionFragment() StatisticsFragment.TAG -> fragment = StatisticsFragment() @@ -389,7 +406,7 @@ class MainActivity : CastEnabledActivity() { } if (args != null) fragment.arguments = args - NavDrawerFragment.saveLastNavFragment(this, tag) + ioScope.launch { NavDrawerFragment.saveLastNavFragment(this@MainActivity, tag) } loadFragment(fragment) } @@ -560,6 +577,7 @@ class MainActivity : CastEnabledActivity() { private fun procFlowEvents() { lifecycleScope.launch { EventFlow.events.collectLatest { event -> + Logd(TAG, "Received event: $event") when (event) { is FlowEvent.MessageEvent -> onEventMainThread(event) else -> {} @@ -660,7 +678,7 @@ class MainActivity : CastEnabledActivity() { val feature = uri.getQueryParameter("page") ?: return when (feature) { "DOWNLOADS" -> loadFragment(DownloadsFragment.TAG, null) - "HISTORY" -> loadFragment(PlaybackHistoryFragment.TAG, null) + "HISTORY" -> loadFragment(HistoryFragment.TAG, null) "EPISODES" -> loadFragment(AllEpisodesFragment.TAG, null) "QUEUE" -> loadFragment(QueueFragment.TAG, null) "SUBSCRIPTIONS" -> loadFragment(SubscriptionFragment.TAG, null) diff --git a/app/src/main/java/ac/mdiq/podcini/ui/activity/PreferenceActivity.kt b/app/src/main/java/ac/mdiq/podcini/ui/activity/PreferenceActivity.kt index 3d7838e5..edda047a 100644 --- a/app/src/main/java/ac/mdiq/podcini/ui/activity/PreferenceActivity.kt +++ b/app/src/main/java/ac/mdiq/podcini/ui/activity/PreferenceActivity.kt @@ -161,6 +161,7 @@ class PreferenceActivity : AppCompatActivity(), SearchPreferenceResultListener { private fun procFlowEvents() { lifecycleScope.launch { EventFlow.events.collectLatest { event -> + Logd("PreferenceActivity", "Received event: ${event}") when (event) { is FlowEvent.MessageEvent -> onEventMainThread(event) else -> {} diff --git a/app/src/main/java/ac/mdiq/podcini/ui/activity/VideoplayerActivity.kt b/app/src/main/java/ac/mdiq/podcini/ui/activity/VideoplayerActivity.kt index bb9c1b40..dab0c015 100644 --- a/app/src/main/java/ac/mdiq/podcini/ui/activity/VideoplayerActivity.kt +++ b/app/src/main/java/ac/mdiq/podcini/ui/activity/VideoplayerActivity.kt @@ -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.playback.Playable 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.VideoEpisodeFragment import ac.mdiq.podcini.ui.utils.PictureInPictureUtil @@ -169,6 +171,7 @@ class VideoplayerActivity : CastEnabledActivity() { private fun procFlowEvents() { lifecycleScope.launch { EventFlow.events.collectLatest { event -> + Logd(TAG, "Received event: $event") when (event) { is FlowEvent.SleepTimerUpdatedEvent -> if (event.isCancelled || event.wasJustEnabled()) supportInvalidateOptionsMenu() is FlowEvent.PlaybackServiceEvent -> if (event.action == FlowEvent.PlaybackServiceEvent.Action.SERVICE_SHUT_DOWN) finish() diff --git a/app/src/main/java/ac/mdiq/podcini/ui/activity/WidgetConfigActivity.kt b/app/src/main/java/ac/mdiq/podcini/ui/activity/WidgetConfigActivity.kt index fd5d2f87..f94efcdb 100644 --- a/app/src/main/java/ac/mdiq/podcini/ui/activity/WidgetConfigActivity.kt +++ b/app/src/main/java/ac/mdiq/podcini/ui/activity/WidgetConfigActivity.kt @@ -16,6 +16,7 @@ import ac.mdiq.podcini.databinding.ActivityWidgetConfigBinding import ac.mdiq.podcini.databinding.PlayerWidgetBinding import ac.mdiq.podcini.preferences.ThemeSwitcher.getTheme import ac.mdiq.podcini.receiver.PlayerWidget +import ac.mdiq.podcini.receiver.PlayerWidget.Companion.prefs import ac.mdiq.podcini.ui.widget.WidgetUpdaterWorker class WidgetConfigActivity : AppCompatActivity() { @@ -95,13 +96,13 @@ class WidgetConfigActivity : AppCompatActivity() { } private fun setInitialState() { - val prefs = getSharedPreferences(PlayerWidget.PREFS_NAME, MODE_PRIVATE) - ckPlaybackSpeed.isChecked = prefs.getBoolean(PlayerWidget.KEY_WIDGET_PLAYBACK_SPEED + appWidgetId, true) - ckRewind.isChecked = prefs.getBoolean(PlayerWidget.KEY_WIDGET_REWIND + appWidgetId, true) - ckFastForward.isChecked = prefs.getBoolean(PlayerWidget.KEY_WIDGET_FAST_FORWARD + appWidgetId, true) - ckSkip.isChecked = prefs.getBoolean(PlayerWidget.KEY_WIDGET_SKIP + appWidgetId, true) +// val prefs = getSharedPreferences(PlayerWidget.PREFS_NAME, MODE_PRIVATE) + ckPlaybackSpeed.isChecked = prefs!!.getBoolean(PlayerWidget.KEY_WIDGET_PLAYBACK_SPEED + appWidgetId, true) + ckRewind.isChecked = prefs!!.getBoolean(PlayerWidget.KEY_WIDGET_REWIND + appWidgetId, true) + ckFastForward.isChecked = prefs!!.getBoolean(PlayerWidget.KEY_WIDGET_FAST_FORWARD + appWidgetId, true) + ckSkip.isChecked = prefs!!.getBoolean(PlayerWidget.KEY_WIDGET_SKIP + appWidgetId, true) 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 opacitySeekBar.setProgress(opacity, false) @@ -122,8 +123,8 @@ class WidgetConfigActivity : AppCompatActivity() { private fun confirmCreateWidget() { val backgroundColor = getColorWithAlpha(PlayerWidget.DEFAULT_COLOR, opacitySeekBar.progress) - val prefs = getSharedPreferences(PlayerWidget.PREFS_NAME, MODE_PRIVATE) - val editor = prefs.edit() +// val prefs = getSharedPreferences(PlayerWidget.PREFS_NAME, MODE_PRIVATE) + val editor = prefs!!.edit() editor.putInt(PlayerWidget.KEY_WIDGET_COLOR + appWidgetId, backgroundColor) editor.putBoolean(PlayerWidget.KEY_WIDGET_PLAYBACK_SPEED + appWidgetId, ckPlaybackSpeed.isChecked) editor.putBoolean(PlayerWidget.KEY_WIDGET_SKIP + appWidgetId, ckSkip.isChecked) diff --git a/app/src/main/java/ac/mdiq/podcini/ui/adapter/EpisodeItemListAdapter.kt b/app/src/main/java/ac/mdiq/podcini/ui/adapter/EpisodeItemListAdapter.kt index b50bde0c..eae3bd26 100644 --- a/app/src/main/java/ac/mdiq/podcini/ui/adapter/EpisodeItemListAdapter.kt +++ b/app/src/main/java/ac/mdiq/podcini/ui/adapter/EpisodeItemListAdapter.kt @@ -7,6 +7,7 @@ import ac.mdiq.podcini.ui.activity.MainActivity import ac.mdiq.podcini.ui.fragment.EpisodeInfoFragment import ac.mdiq.podcini.ui.utils.ThemeUtils import ac.mdiq.podcini.ui.view.viewholder.EpisodeItemViewHolder +import ac.mdiq.podcini.util.Logd import android.R.color import android.app.Activity import android.util.Log @@ -18,9 +19,10 @@ import java.lang.ref.WeakReference /** * List adapter for the list of new episodes. */ -open class EpisodeItemListAdapter(mainActivity: MainActivity) : - SelectableAdapter(mainActivity), View.OnCreateContextMenuListener { +open class EpisodeItemListAdapter(mainActivity: MainActivity) + : SelectableAdapter(mainActivity), View.OnCreateContextMenuListener { + val TAG = "EpisodeItemListAdapter" val mainActivityRef: WeakReference = WeakReference(mainActivity) private var episodes: List = ArrayList() @@ -49,6 +51,8 @@ open class EpisodeItemListAdapter(mainActivity: MainActivity) : } @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) } @@ -79,24 +83,13 @@ open class EpisodeItemListAdapter(mainActivity: MainActivity) : } holder.infoCard.setOnClickListener { val activity: MainActivity? = mainActivityRef.get() - if (!inActionMode()) { -// val ids: LongArray = FeedItemUtil.getIds(episodes) -// val position = ArrayUtils.indexOf(ids, item.id) - activity?.loadChildFragment(EpisodeInfoFragment.newInstance(episodes[pos])) - } else { - toggleSelection(holder.bindingAdapterPosition) - } + if (!inActionMode()) activity?.loadChildFragment(EpisodeInfoFragment.newInstance(episodes[pos])) + else toggleSelection(holder.bindingAdapterPosition) } - holder.coverHolder.setOnClickListener { val activity: MainActivity? = mainActivityRef.get() - if (!inActionMode()) { -// val ids: LongArray = FeedItemUtil.getIds(episodes) -// val position = ArrayUtils.indexOf(ids, item.id) - activity?.loadChildFragment(EpisodeInfoFragment.newInstance(episodes[pos])) - } else { - toggleSelection(holder.bindingAdapterPosition) - } + if (!inActionMode()) activity?.loadChildFragment(EpisodeInfoFragment.newInstance(episodes[pos])) + else toggleSelection(holder.bindingAdapterPosition) } holder.itemView.setOnTouchListener(View.OnTouchListener { _: View?, e: MotionEvent -> if (e.isFromSource(InputDevice.SOURCE_MOUSE) && e.buttonState == MotionEvent.BUTTON_SECONDARY) { @@ -106,12 +99,11 @@ open class EpisodeItemListAdapter(mainActivity: MainActivity) : } false }) - if (inActionMode()) { holder.secondaryActionButton.setOnClickListener(null) - if (isSelected(pos)) { + if (isSelected(pos)) 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) diff --git a/app/src/main/java/ac/mdiq/podcini/ui/adapter/NavListAdapter.kt b/app/src/main/java/ac/mdiq/podcini/ui/adapter/NavListAdapter.kt index d191d240..f686e2ef 100644 --- a/app/src/main/java/ac/mdiq/podcini/ui/adapter/NavListAdapter.kt +++ b/app/src/main/java/ac/mdiq/podcini/ui/adapter/NavListAdapter.kt @@ -4,13 +4,14 @@ import ac.mdiq.podcini.R import ac.mdiq.podcini.databinding.NavListitemBinding import ac.mdiq.podcini.databinding.NavSectionItemBinding 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.hiddenDrawerItems +import ac.mdiq.podcini.storage.DBWriter.ioScope import ac.mdiq.podcini.storage.NavDrawerData.FeedDrawerItem import ac.mdiq.podcini.ui.activity.PreferenceActivity import ac.mdiq.podcini.ui.fragment.* import ac.mdiq.podcini.ui.statistics.StatisticsFragment -import ac.mdiq.podcini.util.Logd import android.app.Activity import android.content.DialogInterface import android.content.Intent @@ -28,6 +29,9 @@ import androidx.media3.common.util.UnstableApi import androidx.preference.PreferenceManager import androidx.recyclerview.widget.RecyclerView 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 java.lang.ref.WeakReference import java.text.NumberFormat @@ -49,9 +53,7 @@ class NavListAdapter(private val itemAccess: ItemAccess, context: Activity) : init { loadItems() - - val prefs = PreferenceManager.getDefaultSharedPreferences(context) - prefs.registerOnSharedPreferenceChangeListener(this) + appPrefs.registerOnSharedPreferenceChangeListener(this@NavListAdapter) } 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 AllEpisodesFragment.TAG -> R.drawable.ic_feed DownloadsFragment.TAG -> R.drawable.ic_download - PlaybackHistoryFragment.TAG -> R.drawable.ic_history + HistoryFragment.TAG -> R.drawable.ic_history SubscriptionFragment.TAG -> R.drawable.ic_subscriptions StatisticsFragment.TAG -> R.drawable.ic_chart_box 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])) } diff --git a/app/src/main/java/ac/mdiq/podcini/ui/adapter/SelectableAdapter.kt b/app/src/main/java/ac/mdiq/podcini/ui/adapter/SelectableAdapter.kt index b08b74c1..57ec55ae 100644 --- a/app/src/main/java/ac/mdiq/podcini/ui/adapter/SelectableAdapter.kt +++ b/app/src/main/java/ac/mdiq/podcini/ui/adapter/SelectableAdapter.kt @@ -11,8 +11,7 @@ import android.util.Log /** * Used by Recyclerviews that need to provide ability to select items. */ -abstract class SelectableAdapter(private val activity: Activity) : - RecyclerView.Adapter() { +abstract class SelectableAdapter(private val activity: Activity) : RecyclerView.Adapter() { private var actionMode: ActionMode? = null private val selectedIds = HashSet() diff --git a/app/src/main/java/ac/mdiq/podcini/ui/adapter/SubscriptionsAdapter.kt b/app/src/main/java/ac/mdiq/podcini/ui/adapter/SubscriptionsAdapter.kt index 7c0b9d53..2da6fcc9 100644 --- a/app/src/main/java/ac/mdiq/podcini/ui/adapter/SubscriptionsAdapter.kt +++ b/app/src/main/java/ac/mdiq/podcini/ui/adapter/SubscriptionsAdapter.kt @@ -6,10 +6,10 @@ import ac.mdiq.podcini.storage.NavDrawerData import ac.mdiq.podcini.storage.model.feed.Feed import ac.mdiq.podcini.ui.activity.MainActivity import ac.mdiq.podcini.ui.fragment.FeedItemlistFragment +import ac.mdiq.podcini.ui.utils.CoverLoader import android.content.Context import android.graphics.Rect import android.graphics.drawable.Drawable -import android.os.Build import android.view.* import android.widget.* import androidx.appcompat.content.res.AppCompatResources @@ -153,6 +153,7 @@ open class SubscriptionsAdapter(mainActivity: MainActivity) } inner class SubscriptionViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { + val binding = SubscriptionItemBinding.bind(itemView) private val title = binding.titleLabel private val producer = binding.producerLabel diff --git a/app/src/main/java/ac/mdiq/podcini/ui/dialog/EditUrlSettingsDialog.kt b/app/src/main/java/ac/mdiq/podcini/ui/dialog/EditUrlSettingsDialog.kt index 8a638ef7..e0b8e919 100644 --- a/app/src/main/java/ac/mdiq/podcini/ui/dialog/EditUrlSettingsDialog.kt +++ b/app/src/main/java/ac/mdiq/podcini/ui/dialog/EditUrlSettingsDialog.kt @@ -12,6 +12,7 @@ import ac.mdiq.podcini.net.download.FeedUpdateManager.runOnce import ac.mdiq.podcini.databinding.EditTextDialogBinding import ac.mdiq.podcini.storage.model.feed.Feed import androidx.media3.common.util.UnstableApi +import kotlinx.coroutines.runBlocking import java.lang.ref.WeakReference import java.util.* import java.util.concurrent.ExecutionException @@ -37,7 +38,7 @@ import java.util.concurrent.ExecutionException @UnstableApi private fun onConfirmed(original: String, updated: String) { try { - DBWriter.updateFeedDownloadURL(original, updated).get() + runBlocking { DBWriter.updateFeedDownloadURL(original, updated).join() } feed.download_url = updated runOnce(activityRef.get()!!, feed) } catch (e: ExecutionException) { diff --git a/app/src/main/java/ac/mdiq/podcini/ui/dialog/EpisodeFilterDialog.kt b/app/src/main/java/ac/mdiq/podcini/ui/dialog/EpisodeFilterDialog.kt index 70e51c41..24bb8995 100644 --- a/app/src/main/java/ac/mdiq/podcini/ui/dialog/EpisodeFilterDialog.kt +++ b/app/src/main/java/ac/mdiq/podcini/ui/dialog/EpisodeFilterDialog.kt @@ -4,7 +4,7 @@ import ac.mdiq.podcini.R import ac.mdiq.podcini.databinding.EpisodeFilterDialogBinding import ac.mdiq.podcini.storage.model.feed.FeedFilter 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.DialogInterface import android.view.LayoutInflater diff --git a/app/src/main/java/ac/mdiq/podcini/ui/dialog/RemoveFeedDialog.kt b/app/src/main/java/ac/mdiq/podcini/ui/dialog/RemoveFeedDialog.kt index 4c0e49fe..6a054211 100644 --- a/app/src/main/java/ac/mdiq/podcini/ui/dialog/RemoveFeedDialog.kt +++ b/app/src/main/java/ac/mdiq/podcini/ui/dialog/RemoveFeedDialog.kt @@ -10,10 +10,8 @@ import android.content.DialogInterface import android.util.Log import androidx.annotation.OptIn import androidx.media3.common.util.UnstableApi -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.launch -import kotlinx.coroutines.withContext +import kotlinx.coroutines.* +import java.lang.Runnable object RemoveFeedDialog { private const val TAG = "RemoveFeedDialog" @@ -63,7 +61,7 @@ object RemoveFeedDialog { try { withContext(Dispatchers.IO) { for (feed in feeds) { - DBWriter.deleteFeed(context, feed.id).get() + runBlocking { DBWriter.deleteFeed(context, feed.id).join() } } } withContext(Dispatchers.Main) { diff --git a/app/src/main/java/ac/mdiq/podcini/ui/dialog/SwipeActionsDialog.kt b/app/src/main/java/ac/mdiq/podcini/ui/dialog/SwipeActionsDialog.kt index e94b514f..e20b9a58 100644 --- a/app/src/main/java/ac/mdiq/podcini/ui/dialog/SwipeActionsDialog.kt +++ b/app/src/main/java/ac/mdiq/podcini/ui/dialog/SwipeActionsDialog.kt @@ -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.SwipeActions 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.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 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 -> (!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) keys = Stream.of(keys).toList() } else -> {} } - if (tag != QueueFragment.TAG) { - keys = Stream.of(keys).filter { a: SwipeAction -> !a.getId().equals(SwipeAction.REMOVE_FROM_QUEUE) }.toList() - } + if (tag != QueueFragment.TAG) keys = Stream.of(keys).filter { a: SwipeAction -> !a.getId().equals(SwipeAction.REMOVE_FROM_QUEUE) }.toList() builder.setTitle(context.getString(R.string.swipeactions_label) + " - " + forFragment) val binding = SwipeactionsDialogBinding.inflate(LayoutInflater.from(context)) @@ -127,11 +128,9 @@ class SwipeActionsDialog(private val context: Context, private val tag: String) if ((direction == LEFT && leftAction === action) || (direction == RIGHT && rightAction === action)) { icon.setTint(getColorFromAttr(context, action.getActionColor())) item.swipeActionLabel.setTextColor(getColorFromAttr(context, action.getActionColor())) - } else { - icon.setTint(getColorFromAttr(context, R.attr.action_icon_color)) - } - item.swipeIcon.setImageDrawable(icon) + } else icon.setTint(getColorFromAttr(context, R.attr.action_icon_color)) + item.swipeIcon.setImageDrawable(icon) item.root.setOnClickListener { if (direction == LEFT) leftAction = 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?) { - val prefs = context.getSharedPreferences(SwipeActions.PREF_NAME, Context.MODE_PRIVATE) - prefs.edit().putString(SwipeActions.KEY_PREFIX_SWIPEACTIONS + tag, "$right,$left").apply() + getSharedPrefs(context) + SwipeActions.prefs!!.edit().putString(SwipeActions.KEY_PREFIX_SWIPEACTIONS + tag, "$right,$left").apply() } private fun saveActionsEnabledPrefs(enabled: Boolean) { - val prefs = context.getSharedPreferences(SwipeActions.PREF_NAME, Context.MODE_PRIVATE) - prefs.edit().putBoolean(SwipeActions.KEY_PREFIX_NO_ACTION + tag, enabled).apply() + getSharedPrefs(context) + SwipeActions.prefs!!.edit().putBoolean(SwipeActions.KEY_PREFIX_NO_ACTION + tag, enabled).apply() } interface Callback { diff --git a/app/src/main/java/ac/mdiq/podcini/ui/dialog/TagSettingsDialog.kt b/app/src/main/java/ac/mdiq/podcini/ui/dialog/TagSettingsDialog.kt index d696ab3e..4e148eae 100644 --- a/app/src/main/java/ac/mdiq/podcini/ui/dialog/TagSettingsDialog.kt +++ b/app/src/main/java/ac/mdiq/podcini/ui/dialog/TagSettingsDialog.kt @@ -6,7 +6,7 @@ import ac.mdiq.podcini.storage.DBReader import ac.mdiq.podcini.storage.DBWriter import ac.mdiq.podcini.storage.model.feed.FeedPreferences 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.FlowEvent import android.app.Dialog diff --git a/app/src/main/java/ac/mdiq/podcini/ui/dialog/VariableSpeedDialog.kt b/app/src/main/java/ac/mdiq/podcini/ui/dialog/VariableSpeedDialog.kt index c3956d3e..64bc8de6 100644 --- a/app/src/main/java/ac/mdiq/podcini/ui/dialog/VariableSpeedDialog.kt +++ b/app/src/main/java/ac/mdiq/podcini/ui/dialog/VariableSpeedDialog.kt @@ -8,7 +8,7 @@ import ac.mdiq.podcini.playback.PlaybackController.Companion.setPlaybackSpeed import ac.mdiq.podcini.playback.PlaybackController.Companion.setSkipSilence import ac.mdiq.podcini.preferences.UserPreferences.isSkipSilence 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.util.Logd import ac.mdiq.podcini.util.event.EventFlow diff --git a/app/src/main/java/ac/mdiq/podcini/ui/fragment/AllEpisodesFragment.kt b/app/src/main/java/ac/mdiq/podcini/ui/fragment/AllEpisodesFragment.kt index 21c20224..8622f930 100644 --- a/app/src/main/java/ac/mdiq/podcini/ui/fragment/AllEpisodesFragment.kt +++ b/app/src/main/java/ac/mdiq/podcini/ui/fragment/AllEpisodesFragment.kt @@ -1,6 +1,8 @@ package ac.mdiq.podcini.ui.fragment 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.prefFilterAllEpisodes import ac.mdiq.podcini.storage.DBReader @@ -99,6 +101,7 @@ import org.apache.commons.lang3.StringUtils private fun procFlowEvents() { lifecycleScope.launch { EventFlow.events.collectLatest { event -> + Logd(TAG, "Received event: $event") when (event) { is FlowEvent.AllEpisodesFilterChangedEvent -> onFilterChanged(event) else -> {} diff --git a/app/src/main/java/ac/mdiq/podcini/ui/fragment/AudioPlayerFragment.kt b/app/src/main/java/ac/mdiq/podcini/ui/fragment/AudioPlayerFragment.kt index e44f93f1..8c2ee71d 100644 --- a/app/src/main/java/ac/mdiq/podcini/ui/fragment/AudioPlayerFragment.kt +++ b/app/src/main/java/ac/mdiq/podcini/ui/fragment/AudioPlayerFragment.kt @@ -14,6 +14,7 @@ import ac.mdiq.podcini.playback.base.MediaPlayerBase import ac.mdiq.podcini.playback.base.PlayerStatus import ac.mdiq.podcini.playback.cast.CastEnabledActivity 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.videoPlayMode import ac.mdiq.podcini.receiver.MediaButtonReceiver @@ -309,9 +310,8 @@ class AudioPlayerFragment : Fragment(), SeekBar.OnSeekBarChangeListener, Toolbar private fun procFlowEvents() { lifecycleScope.launch { - Logd(TAG, "subscribing PositionFlowEvent") EventFlow.events.collectLatest { event -> -// Logd(TAG, "PositionFlowEvent: ${event}") + Logd(TAG, "Received event: $event") when (event) { is FlowEvent.PlaybackServiceEvent -> if (event.action == FlowEvent.PlaybackServiceEvent.Action.SERVICE_SHUT_DOWN) @@ -546,7 +546,7 @@ class AudioPlayerFragment : Fragment(), SeekBar.OnSeekBarChangeListener, Toolbar private fun procFlowEvents() { lifecycleScope.launch { EventFlow.events.collectLatest { event -> -// Logd(TAG, "PositionFlowEvent: ${event}") + Logd(TAG, "Received event: $event") when (event) { is FlowEvent.PlaybackPositionEvent -> onPositionObserverUpdate(event) is FlowEvent.SpeedChangedEvent -> updatePlaybackSpeedButton(event) diff --git a/app/src/main/java/ac/mdiq/podcini/ui/fragment/BaseEpisodesListFragment.kt b/app/src/main/java/ac/mdiq/podcini/ui/fragment/BaseEpisodesListFragment.kt index ed6f7041..784fe542 100644 --- a/app/src/main/java/ac/mdiq/podcini/ui/fragment/BaseEpisodesListFragment.kt +++ b/app/src/main/java/ac/mdiq/podcini/ui/fragment/BaseEpisodesListFragment.kt @@ -14,9 +14,9 @@ import ac.mdiq.podcini.ui.activity.MainActivity import ac.mdiq.podcini.ui.adapter.EpisodeItemListAdapter import ac.mdiq.podcini.ui.adapter.SelectableAdapter 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.LiftOnScrollListener +import ac.mdiq.podcini.ui.utils.LiftOnScrollListener import ac.mdiq.podcini.ui.view.viewholder.EpisodeItemViewHolder import ac.mdiq.podcini.util.FeedItemUtil import ac.mdiq.podcini.util.Logd @@ -104,6 +104,7 @@ import kotlinx.coroutines.withContext recyclerView.addOnScrollListener(LiftOnScrollListener(binding.appbar)) swipeActions = SwipeActions(this, getFragmentTag()).attachTo(recyclerView) + lifecycle.addObserver(swipeActions) swipeActions.setFilter(getFilter()) refreshSwipeTelltale() binding.leftActionIcon.setOnClickListener { @@ -423,6 +424,7 @@ import kotlinx.coroutines.withContext private fun procFlowEvents() { lifecycleScope.launch { EventFlow.events.collectLatest { event -> + Logd(TAG, "Received event: $event") when (event) { is FlowEvent.SwipeActionsChangedEvent -> refreshSwipeTelltale() is FlowEvent.FeedListUpdateEvent, is FlowEvent.UnreadItemsUpdateEvent, is FlowEvent.PlayerStatusEvent -> loadItems() diff --git a/app/src/main/java/ac/mdiq/podcini/ui/fragment/ChaptersFragment.kt b/app/src/main/java/ac/mdiq/podcini/ui/fragment/ChaptersFragment.kt index 2ba21408..36b5c7ef 100644 --- a/app/src/main/java/ac/mdiq/podcini/ui/fragment/ChaptersFragment.kt +++ b/app/src/main/java/ac/mdiq/podcini/ui/fragment/ChaptersFragment.kt @@ -5,6 +5,8 @@ import ac.mdiq.podcini.databinding.SimpleListFragmentBinding import ac.mdiq.podcini.playback.PlaybackController import ac.mdiq.podcini.playback.base.MediaPlayerBase 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.playback.Playable import ac.mdiq.podcini.ui.adapter.ChaptersListAdapter @@ -124,6 +126,7 @@ class ChaptersFragment : AppCompatDialogFragment() { private fun procFlowEvents() { lifecycleScope.launch { EventFlow.events.collectLatest { event -> + Logd(TAG, "Received event: $event") when (event) { is FlowEvent.PlaybackPositionEvent -> onEventMainThread(event) else -> {} diff --git a/app/src/main/java/ac/mdiq/podcini/ui/fragment/DiscoveryFragment.kt b/app/src/main/java/ac/mdiq/podcini/ui/fragment/DiscoveryFragment.kt index 1ad8cde3..8db1d18c 100644 --- a/app/src/main/java/ac/mdiq/podcini/ui/fragment/DiscoveryFragment.kt +++ b/app/src/main/java/ac/mdiq/podcini/ui/fragment/DiscoveryFragment.kt @@ -5,6 +5,7 @@ import ac.mdiq.podcini.R import ac.mdiq.podcini.databinding.FragmentItunesSearchBinding import ac.mdiq.podcini.databinding.SelectCountryDialogBinding 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.storage.DBReader import ac.mdiq.podcini.ui.activity.MainActivity @@ -42,7 +43,7 @@ class DiscoveryFragment : Fragment(), Toolbar.OnMenuItemClickListener { private var _binding: FragmentItunesSearchBinding? = null private val binding get() = _binding!! - private lateinit var prefs: SharedPreferences +// private lateinit var prefs: SharedPreferences private lateinit var gridView: GridView private lateinit var progressBar: ProgressBar private lateinit var txtvError: TextView @@ -91,10 +92,10 @@ class DiscoveryFragment : Fragment(), Toolbar.OnMenuItemClickListener { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - prefs = requireActivity().getSharedPreferences(ItunesTopListLoader.PREFS, Context.MODE_PRIVATE) - countryCode = prefs.getString(ItunesTopListLoader.PREF_KEY_COUNTRY_CODE, Locale.getDefault().country) - hidden = prefs.getBoolean(ItunesTopListLoader.PREF_KEY_HIDDEN_DISCOVERY_COUNTRY, false) - needsConfirm = prefs.getBoolean(ItunesTopListLoader.PREF_KEY_NEEDS_CONFIRM, true) +// prefs = requireActivity().getSharedPreferences(ItunesTopListLoader.PREFS, Context.MODE_PRIVATE) + countryCode = prefs!!.getString(ItunesTopListLoader.PREF_KEY_COUNTRY_CODE, Locale.getDefault().country) + hidden = prefs!!.getBoolean(ItunesTopListLoader.PREF_KEY_HIDDEN_DISCOVERY_COUNTRY, false) + needsConfirm = prefs!!.getBoolean(ItunesTopListLoader.PREF_KEY_NEEDS_CONFIRM, true) } @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.setText(R.string.discover_confirm) butRetry.setOnClickListener { - prefs.edit().putBoolean(ItunesTopListLoader.PREF_KEY_NEEDS_CONFIRM, false).apply() + prefs!!.edit().putBoolean(ItunesTopListLoader.PREF_KEY_NEEDS_CONFIRM, false).apply() needsConfirm = false loadToplist(country) } @@ -225,7 +226,7 @@ class DiscoveryFragment : Fragment(), Toolbar.OnMenuItemClickListener { R.id.discover_hide_item -> { item.setChecked(!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()) loadToplist(countryCode) @@ -278,8 +279,8 @@ class DiscoveryFragment : Fragment(), Toolbar.OnMenuItemClickListener { hidden = false } - prefs.edit().putBoolean(ItunesTopListLoader.PREF_KEY_HIDDEN_DISCOVERY_COUNTRY, hidden).apply() - prefs.edit().putString(ItunesTopListLoader.PREF_KEY_COUNTRY_CODE, countryCode).apply() + prefs!!.edit().putBoolean(ItunesTopListLoader.PREF_KEY_HIDDEN_DISCOVERY_COUNTRY, hidden).apply() + prefs!!.edit().putString(ItunesTopListLoader.PREF_KEY_COUNTRY_CODE, countryCode).apply() EventFlow.postEvent(FlowEvent.DiscoveryDefaultUpdateEvent()) loadToplist(countryCode) diff --git a/app/src/main/java/ac/mdiq/podcini/ui/fragment/DownloadLogFragment.kt b/app/src/main/java/ac/mdiq/podcini/ui/fragment/DownloadLogFragment.kt index 06657e60..175e3d89 100644 --- a/app/src/main/java/ac/mdiq/podcini/ui/fragment/DownloadLogFragment.kt +++ b/app/src/main/java/ac/mdiq/podcini/ui/fragment/DownloadLogFragment.kt @@ -7,7 +7,7 @@ import ac.mdiq.podcini.storage.DBWriter import ac.mdiq.podcini.storage.model.download.DownloadResult import ac.mdiq.podcini.ui.adapter.DownloadLogAdapter 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.event.EventFlow import ac.mdiq.podcini.util.event.FlowEvent @@ -83,6 +83,7 @@ class DownloadLogFragment : BottomSheetDialogFragment(), OnItemClickListener, To private fun procFlowEvents() { lifecycleScope.launch { EventFlow.events.collectLatest { event -> + Logd(TAG, "Received event: $event") when (event) { is FlowEvent.DownloadLogEvent -> loadDownloadLog() else -> {} diff --git a/app/src/main/java/ac/mdiq/podcini/ui/fragment/DownloadsFragment.kt b/app/src/main/java/ac/mdiq/podcini/ui/fragment/DownloadsFragment.kt index 247e3af8..f4123406 100644 --- a/app/src/main/java/ac/mdiq/podcini/ui/fragment/DownloadsFragment.kt +++ b/app/src/main/java/ac/mdiq/podcini/ui/fragment/DownloadsFragment.kt @@ -19,9 +19,9 @@ import ac.mdiq.podcini.ui.activity.MainActivity import ac.mdiq.podcini.ui.adapter.EpisodeItemListAdapter import ac.mdiq.podcini.ui.adapter.SelectableAdapter 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.LiftOnScrollListener +import ac.mdiq.podcini.ui.utils.LiftOnScrollListener import ac.mdiq.podcini.ui.view.viewholder.EpisodeItemViewHolder import ac.mdiq.podcini.util.FeedItemUtil import ac.mdiq.podcini.util.Logd @@ -97,6 +97,7 @@ import java.util.* recyclerView.addOnScrollListener(LiftOnScrollListener(binding.appbar)) swipeActions = SwipeActions(this, TAG).attachTo(recyclerView) + lifecycle.addObserver(swipeActions) swipeActions.setFilter(FeedItemFilter(FeedItemFilter.DOWNLOADED)) refreshSwipeTelltale() binding.leftActionIcon.setOnClickListener { @@ -213,6 +214,7 @@ import java.util.* private fun procFlowEvents() { lifecycleScope.launch { EventFlow.events.collectLatest { event -> + Logd(TAG, "Received event: $event") when (event) { is FlowEvent.FeedItemEvent -> onEventMainThread(event) is FlowEvent.PlaybackPositionEvent -> onEventMainThread(event) diff --git a/app/src/main/java/ac/mdiq/podcini/ui/fragment/EpisodeInfoFragment.kt b/app/src/main/java/ac/mdiq/podcini/ui/fragment/EpisodeInfoFragment.kt index a9ff5a7c..071de318 100644 --- a/app/src/main/java/ac/mdiq/podcini/ui/fragment/EpisodeInfoFragment.kt +++ b/app/src/main/java/ac/mdiq/podcini/ui/fragment/EpisodeInfoFragment.kt @@ -66,7 +66,7 @@ import kotlin.math.max private var homeFragment: EpisodeHomeFragment? = null - private var itemsLoaded = false + private var itemLoaded = false private var item: FeedItem? = null private var webviewData: String? = null @@ -89,16 +89,8 @@ import kotlin.math.max private var actionButton1: ItemActionButton? = null private var actionButton2: ItemActionButton? = null -// private var disposable: Disposable? = 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 { super.onCreateView(inflater, container, savedInstanceState) @@ -140,9 +132,7 @@ import kotlin.math.max if (!item?.link.isNullOrEmpty()) { homeFragment = EpisodeHomeFragment.newInstance(item!!) (activity as MainActivity).loadChildFragment(homeFragment!!) - } else { - Toast.makeText(context, "Episode link is not valid ${item?.link}", Toast.LENGTH_LONG).show() - } + } else Toast.makeText(context, "Episode link is not valid ${item?.link}", Toast.LENGTH_LONG).show() } butAction1.setOnClickListener(View.OnClickListener { @@ -169,9 +159,7 @@ import kotlin.math.max }) controller = object : PlaybackController(requireActivity()) { - override fun loadMediaInfo() { - // Do nothing - } + override fun loadMediaInfo() {} } controller?.init() load() @@ -246,7 +234,7 @@ import kotlin.math.max @UnstableApi override fun onResume() { super.onResume() - if (itemsLoaded) { + if (itemLoaded) { progbarLoading.visibility = View.GONE updateAppearance() } @@ -258,7 +246,6 @@ import kotlin.math.max _binding = null controller?.release() -// disposable?.dispose() root.removeView(webvDescription) webvDescription.clearHistory() webvDescription.clearCache(true) @@ -267,9 +254,9 @@ import kotlin.math.max } @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") - } + // if (item?.link != null) binding.webView.loadUrl(item!!.link!!) updateAppearance() } @@ -279,14 +266,13 @@ import kotlin.math.max Logd(TAG, "updateAppearance item is null") return } - if (item!!.hasMedia()) { - FeedItemMenuHandler.onPrepareMenu(toolbar.menu, item, R.id.open_podcast) - } else { - // 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!!.hasMedia()) FeedItemMenuHandler.onPrepareMenu(toolbar.menu, item, R.id.open_podcast) + // these are already available via button1 and button2 + else 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 txtvTitle.text = item!!.title + binding.itemLink.text = item!!.link if (item?.pubDate != null) { val pubDateStr = DateFormatter.formatAbbrev(context, item!!.pubDate) @@ -385,6 +371,7 @@ import kotlin.math.max private fun procFlowEvents() { lifecycleScope.launch { EventFlow.events.collectLatest { event -> + Logd(TAG, "Received event: $event") when (event) { is FlowEvent.FeedItemEvent -> onEventMainThread(event) is FlowEvent.PlayerStatusEvent -> updateButtons() @@ -404,7 +391,7 @@ import kotlin.math.max } fun onEventMainThread(event: FlowEvent.FeedItemEvent) { - Logd(TAG, "onEventMainThread() called with: event = [$event]") + Logd(TAG, "onEventMainThread() called with: FeedItemEvent") if (this.item == null) return for (item in event.items) { if (this.item!!.id == item.id) { @@ -417,33 +404,13 @@ import kotlin.math.max fun onEventMainThread(event: FlowEvent.EpisodeDownloadEvent) { if (item == null || item!!.media == null) 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 { 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() { - if (!itemsLoaded) progbarLoading.visibility = View.VISIBLE + if (!itemLoaded) progbarLoading.visibility = View.VISIBLE Logd(TAG, "load() called") -// val scope = CoroutineScope(Dispatchers.Main) lifecycleScope.launch { try { val result = withContext(Dispatchers.IO) { @@ -459,7 +426,7 @@ import kotlin.math.max progbarLoading.visibility = View.GONE item = result onFragmentLoaded() - itemsLoaded = true + itemLoaded = true } } catch (e: Throwable) { Log.e(TAG, Log.getStackTraceString(e)) diff --git a/app/src/main/java/ac/mdiq/podcini/ui/fragment/ExternalEpisodesListFragment.kt b/app/src/main/java/ac/mdiq/podcini/ui/fragment/ExternalEpisodesListFragment.kt index 989a56df..57047337 100644 --- a/app/src/main/java/ac/mdiq/podcini/ui/fragment/ExternalEpisodesListFragment.kt +++ b/app/src/main/java/ac/mdiq/podcini/ui/fragment/ExternalEpisodesListFragment.kt @@ -105,6 +105,7 @@ import kotlin.math.min private fun procFlowEvents() { lifecycleScope.launch { EventFlow.events.collectLatest { event -> + Logd(TAG, "Received event: $event") when (event) { is FlowEvent.AllEpisodesFilterChangedEvent -> page = 1 else -> {} diff --git a/app/src/main/java/ac/mdiq/podcini/ui/fragment/FeedInfoFragment.kt b/app/src/main/java/ac/mdiq/podcini/ui/fragment/FeedInfoFragment.kt index b0a32a74..9ec16326 100644 --- a/app/src/main/java/ac/mdiq/podcini/ui/fragment/FeedInfoFragment.kt +++ b/app/src/main/java/ac/mdiq/podcini/ui/fragment/FeedInfoFragment.kt @@ -10,7 +10,8 @@ import ac.mdiq.podcini.ui.activity.MainActivity import ac.mdiq.podcini.ui.dialog.EditUrlSettingsDialog import ac.mdiq.podcini.ui.statistics.StatisticsFragment 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.Logd 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.dialog.MaterialAlertDialogBuilder import com.google.android.material.snackbar.Snackbar -import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import kotlinx.coroutines.withContext diff --git a/app/src/main/java/ac/mdiq/podcini/ui/fragment/FeedItemlistFragment.kt b/app/src/main/java/ac/mdiq/podcini/ui/fragment/FeedItemlistFragment.kt index c5f84161..e1770857 100644 --- a/app/src/main/java/ac/mdiq/podcini/ui/fragment/FeedItemlistFragment.kt +++ b/app/src/main/java/ac/mdiq/podcini/ui/fragment/FeedItemlistFragment.kt @@ -21,7 +21,8 @@ import ac.mdiq.podcini.ui.adapter.EpisodeItemListAdapter import ac.mdiq.podcini.ui.adapter.SelectableAdapter import ac.mdiq.podcini.ui.dialog.* 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.util.* import ac.mdiq.podcini.util.event.EventFlow @@ -112,6 +113,7 @@ import java.util.concurrent.Semaphore binding.recyclerView.adapter = adapter swipeActions = SwipeActions(this, TAG).attachTo(binding.recyclerView) + lifecycle.addObserver(swipeActions) refreshSwipeTelltale() binding.header.leftActionIcon.setOnClickListener { swipeActions.showDialog() @@ -257,7 +259,7 @@ import java.util.concurrent.Semaphore feed!!.nextPageLink = feed!!.download_url feed!!.pageNr = 0 try { - DBWriter.resetPagedFeedPage(feed).get() + runBlocking { DBWriter.resetPagedFeedPage(feed).join() } FeedUpdateManager.runOnce(requireContext(), feed) } catch (e: ExecutionException) { throw RuntimeException(e) @@ -352,6 +354,7 @@ import java.util.concurrent.Semaphore private fun procFlowEvents() { lifecycleScope.launch { EventFlow.events.collectLatest { event -> + Logd(TAG, "Received event: $event") when (event) { is FlowEvent.QueueEvent -> loadItems() is FlowEvent.FavoritesEvent -> loadItems() diff --git a/app/src/main/java/ac/mdiq/podcini/ui/fragment/FeedSettingsFragment.kt b/app/src/main/java/ac/mdiq/podcini/ui/fragment/FeedSettingsFragment.kt index d462772e..10892217 100644 --- a/app/src/main/java/ac/mdiq/podcini/ui/fragment/FeedSettingsFragment.kt +++ b/app/src/main/java/ac/mdiq/podcini/ui/fragment/FeedSettingsFragment.kt @@ -286,7 +286,7 @@ class FeedSettingsFragment : Fragment() { val setPreferencesFuture = DBWriter.persistFeedPreferences(feedPreferences!!) Thread({ try { - setPreferencesFuture.get() + runBlocking { setPreferencesFuture.join() } } catch (e: InterruptedException) { e.printStackTrace() } catch (e: ExecutionException) { diff --git a/app/src/main/java/ac/mdiq/podcini/ui/fragment/PlaybackHistoryFragment.kt b/app/src/main/java/ac/mdiq/podcini/ui/fragment/HistoryFragment.kt similarity index 95% rename from app/src/main/java/ac/mdiq/podcini/ui/fragment/PlaybackHistoryFragment.kt rename to app/src/main/java/ac/mdiq/podcini/ui/fragment/HistoryFragment.kt index 02af8db8..3d808b37 100644 --- a/app/src/main/java/ac/mdiq/podcini/ui/fragment/PlaybackHistoryFragment.kt +++ b/app/src/main/java/ac/mdiq/podcini/ui/fragment/HistoryFragment.kt @@ -11,7 +11,6 @@ import ac.mdiq.podcini.ui.activity.MainActivity import ac.mdiq.podcini.ui.adapter.EpisodeItemListAdapter import ac.mdiq.podcini.ui.dialog.ConfirmationDialog 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.view.viewholder.EpisodeItemViewHolder import ac.mdiq.podcini.util.DateFormatter @@ -28,18 +27,18 @@ import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.launch import java.util.* -@UnstableApi class PlaybackHistoryFragment : BaseEpisodesListFragment() { +@UnstableApi class HistoryFragment : BaseEpisodesListFragment() { private var sortOrder : SortOrder = SortOrder.PLAYED_DATE_NEW_OLD private var startDate : Long = 0L private var endDate : Long = Date().time override fun getFragmentTag(): String { - return "PlaybackHistoryFragment" + return TAG } override fun getPrefName(): String { - return "PlaybackHistoryFragment" + return TAG } @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?) { 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) @@ -131,6 +130,7 @@ import java.util.* private fun procFlowEvents() { lifecycleScope.launch { EventFlow.events.collectLatest { event -> + Logd(TAG, "Received event: $event") when (event) { is FlowEvent.HistoryEvent -> { sortOrder = event.sortOrder @@ -177,6 +177,6 @@ import java.util.* } companion object { - const val TAG: String = "PlaybackHistoryFragment" + const val TAG: String = "HistoryFragment" } } diff --git a/app/src/main/java/ac/mdiq/podcini/ui/fragment/NavDrawerFragment.kt b/app/src/main/java/ac/mdiq/podcini/ui/fragment/NavDrawerFragment.kt index 513a9065..30a5b46f 100644 --- a/app/src/main/java/ac/mdiq/podcini/ui/fragment/NavDrawerFragment.kt +++ b/app/src/main/java/ac/mdiq/podcini/ui/fragment/NavDrawerFragment.kt @@ -82,9 +82,9 @@ class NavDrawerFragment : Fragment(), SharedPreferences.OnSharedPreferenceChange 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? - openFolders = HashSet(preferences.getStringSet(PREF_OPEN_FOLDERS, HashSet())!!) // Must not modify + openFolders = HashSet(prefs!!.getStringSet(PREF_OPEN_FOLDERS, HashSet())!!) // Must not modify val navList = binding.navRecycler navAdapter = NavListAdapter(itemAccess, requireActivity()) @@ -96,7 +96,7 @@ class NavDrawerFragment : Fragment(), SharedPreferences.OnSharedPreferenceChange startActivity(Intent(activity, PreferenceActivity::class.java)) } - preferences.registerOnSharedPreferenceChangeListener(this) + prefs!!.registerOnSharedPreferenceChangeListener(this) return binding.root } @@ -125,12 +125,13 @@ class NavDrawerFragment : Fragment(), SharedPreferences.OnSharedPreferenceChange _binding = null // scope.cancel() - requireContext().getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE).unregisterOnSharedPreferenceChangeListener(this) + prefs!!.unregisterOnSharedPreferenceChangeListener(this) } private fun procFlowEvents() { lifecycleScope.launch { EventFlow.events.collectLatest { event -> + Logd(TAG, "Received event: $event") when (event) { is FlowEvent.UnreadItemsUpdateEvent, is FlowEvent.FeedListUpdateEvent -> loadData() is FlowEvent.QueueEvent -> onQueueChanged(event) @@ -292,6 +293,10 @@ class NavDrawerFragment : Fragment(), SharedPreferences.OnSharedPreferenceChange @VisibleForTesting const val PREF_NAME: String = "NavDrawerPrefs" 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 @JvmField @@ -301,15 +306,15 @@ class NavDrawerFragment : Fragment(), SharedPreferences.OnSharedPreferenceChange QueueFragment.TAG, AllEpisodesFragment.TAG, DownloadsFragment.TAG, - PlaybackHistoryFragment.TAG, + HistoryFragment.TAG, StatisticsFragment.TAG, AddFeedFragment.TAG, ) fun saveLastNavFragment(context: Context, tag: String?) { Logd(TAG, "saveLastNavFragment(tag: $tag)") - val prefs: SharedPreferences = context.getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE) - val edit: SharedPreferences.Editor = prefs.edit() +// val prefs: SharedPreferences = context.getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE) + val edit: SharedPreferences.Editor = prefs!!.edit() if (tag != null) edit.putString(PREF_LAST_FRAGMENT_TAG, tag) else edit.remove(PREF_LAST_FRAGMENT_TAG) @@ -317,9 +322,9 @@ class NavDrawerFragment : Fragment(), SharedPreferences.OnSharedPreferenceChange } fun getLastNavFragment(context: Context): String { - val prefs: SharedPreferences = context.getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE) - val lastFragment: String = prefs.getString(PREF_LAST_FRAGMENT_TAG, SubscriptionFragment.TAG)?:"" - Logd(TAG, "getLastNavFragment() -> $lastFragment") +// val prefs: SharedPreferences = context.getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE) + val lastFragment: String = prefs!!.getString(PREF_LAST_FRAGMENT_TAG, SubscriptionFragment.TAG)?:"" +// Logd(TAG, "getLastNavFragment() -> $lastFragment") return lastFragment } } diff --git a/app/src/main/java/ac/mdiq/podcini/ui/fragment/OnlineFeedViewFragment.kt b/app/src/main/java/ac/mdiq/podcini/ui/fragment/OnlineFeedViewFragment.kt index 2eddd094..d42cf24e 100644 --- a/app/src/main/java/ac/mdiq/podcini/ui/fragment/OnlineFeedViewFragment.kt +++ b/app/src/main/java/ac/mdiq/podcini/ui/fragment/OnlineFeedViewFragment.kt @@ -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.HttpDownloader 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.receiver.PlayerWidget.Companion.PREFS_NAME import ac.mdiq.podcini.storage.DBReader import ac.mdiq.podcini.storage.DBTasks import ac.mdiq.podcini.storage.DBWriter @@ -35,6 +38,7 @@ import android.app.Dialog import android.content.Context import android.content.Context.MODE_PRIVATE import android.content.DialogInterface +import android.content.SharedPreferences import android.graphics.LightingColorFilter import android.os.Bundle import android.text.Spannable @@ -330,6 +334,7 @@ import kotlin.concurrent.Volatile @OptIn(UnstableApi::class) private fun procFlowEvents() { lifecycleScope.launch { EventFlow.events.collectLatest { event -> + Logd(TAG, "Received event: $event") when (event) { is FlowEvent.FeedListUpdateEvent -> onFeedListChanged(event) else -> {} @@ -507,8 +512,8 @@ import kotlin.concurrent.Volatile } if (isEnableAutodownload) { - val preferences = requireContext().getSharedPreferences(PREFS, MODE_PRIVATE) - binding.autoDownloadCheckBox.isChecked = preferences.getBoolean(PREF_LAST_AUTO_DOWNLOAD, true) +// val preferences = requireContext().getSharedPreferences(PREFS, MODE_PRIVATE) + binding.autoDownloadCheckBox.isChecked = prefs!!.getBoolean(PREF_LAST_AUTO_DOWNLOAD, true) } if (alternateFeedUrls.isEmpty()) { @@ -587,8 +592,8 @@ import kotlin.concurrent.Volatile val autoDownload = binding.autoDownloadCheckBox.isChecked feedPreferences.autoDownload = autoDownload - val preferences = requireContext().getSharedPreferences(PREFS, MODE_PRIVATE) - val editor = preferences.edit() +// val preferences = requireContext().getSharedPreferences(PREFS, MODE_PRIVATE) + val editor = prefs!!.edit() editor.putBoolean(PREF_LAST_AUTO_DOWNLOAD, autoDownload) editor.apply() } @@ -738,6 +743,12 @@ import kotlin.concurrent.Volatile private const val PREF_LAST_AUTO_DOWNLOAD = "lastAutoDownload" 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 fun newInstance(feedUrl: String): OnlineFeedViewFragment { val fragment = OnlineFeedViewFragment() diff --git a/app/src/main/java/ac/mdiq/podcini/ui/fragment/PlayerDetailsFragment.kt b/app/src/main/java/ac/mdiq/podcini/ui/fragment/PlayerDetailsFragment.kt index 478bd6e8..74225fbd 100644 --- a/app/src/main/java/ac/mdiq/podcini/ui/fragment/PlayerDetailsFragment.kt +++ b/app/src/main/java/ac/mdiq/podcini/ui/fragment/PlayerDetailsFragment.kt @@ -4,6 +4,8 @@ import ac.mdiq.podcini.R import ac.mdiq.podcini.databinding.PlayerDetailsFragmentBinding import ac.mdiq.podcini.feed.util.ImageResourceUtils 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.DBWriter.persistFeedItem 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.FeedMedia 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.utils.ShownotesCleaner import ac.mdiq.podcini.ui.view.ShownotesWebView @@ -25,9 +29,7 @@ import android.animation.AnimatorListenerAdapter import android.animation.AnimatorSet import android.animation.ObjectAnimator import android.app.Activity -import android.content.ClipData -import android.content.ClipboardManager -import android.content.Intent +import android.content.* import android.graphics.ColorFilter import android.os.Build import android.os.Bundle @@ -409,8 +411,8 @@ class PlayerDetailsFragment : Fragment() { @UnstableApi private fun savePreference() { Logd(TAG, "Saving preferences") - val prefs = requireActivity().getSharedPreferences(PREF, Activity.MODE_PRIVATE) - val editor = prefs.edit() +// val prefs = requireActivity().getSharedPreferences(PREF, Activity.MODE_PRIVATE) + val editor = prefs!!.edit() if (controller?.getMedia() != null) { Logd(TAG, "Saving scroll position: " + binding.itemDescriptionFragment.scrollY) editor.putInt(PREF_SCROLL_Y, binding.itemDescriptionFragment.scrollY) @@ -429,9 +431,9 @@ class PlayerDetailsFragment : Fragment() { Logd(TAG, "Restoring from preferences") val activity: Activity? = activity if (activity != null) { - val prefs = activity.getSharedPreferences(PREF, Activity.MODE_PRIVATE) - val id = prefs.getString(PREF_PLAYABLE_ID, "") - val scrollY = prefs.getInt(PREF_SCROLL_Y, -1) +// val prefs = activity.getSharedPreferences(PREF, Activity.MODE_PRIVATE) + val id = prefs!!.getString(PREF_PLAYABLE_ID, "") + val scrollY = prefs!!.getInt(PREF_SCROLL_Y, -1) if (scrollY != -1) { if (id == controller?.getMedia()?.getIdentifier()?.toString()) { Logd(TAG, "Restored scroll Position: $scrollY") @@ -455,6 +457,7 @@ class PlayerDetailsFragment : Fragment() { private fun procFlowEvents() { lifecycleScope.launch { EventFlow.events.collectLatest { event -> + Logd(TAG, "Received event: $event") when (event) { is FlowEvent.PlaybackPositionEvent -> onEventMainThread(event) else -> {} @@ -525,5 +528,10 @@ class PlayerDetailsFragment : Fragment() { private const val PREF = "ItemDescriptionFragmentPrefs" private const val PREF_SCROLL_Y = "prefScrollY" 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) + } } } diff --git a/app/src/main/java/ac/mdiq/podcini/ui/fragment/QueueFragment.kt b/app/src/main/java/ac/mdiq/podcini/ui/fragment/QueueFragment.kt index 3bd28f88..aefb22f9 100644 --- a/app/src/main/java/ac/mdiq/podcini/ui/fragment/QueueFragment.kt +++ b/app/src/main/java/ac/mdiq/podcini/ui/fragment/QueueFragment.kt @@ -21,9 +21,9 @@ import ac.mdiq.podcini.ui.adapter.QueueRecyclerAdapter import ac.mdiq.podcini.ui.adapter.SelectableAdapter import ac.mdiq.podcini.ui.dialog.ConfirmationDialog 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.LiftOnScrollListener +import ac.mdiq.podcini.ui.utils.LiftOnScrollListener import ac.mdiq.podcini.ui.view.viewholder.EpisodeItemViewHolder import ac.mdiq.podcini.util.Converter import ac.mdiq.podcini.util.FeedItemUtil @@ -72,7 +72,6 @@ import java.util.* private lateinit var toolbar: MaterialToolbar private lateinit var swipeRefreshLayout: SwipeRefreshLayout private lateinit var swipeActions: SwipeActions - private lateinit var prefs: SharedPreferences private lateinit var speedDialView: SpeedDialView private lateinit var progressBar: ProgressBar @@ -88,7 +87,7 @@ import java.util.* override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) 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 { @@ -122,15 +121,12 @@ import java.util.* recyclerView.addOnScrollListener(LiftOnScrollListener(binding.appbar)) swipeActions = QueueSwipeActions() + lifecycle.addObserver(swipeActions) swipeActions.setFilter(FeedItemFilter(FeedItemFilter.QUEUED)) swipeActions.attachTo(recyclerView) refreshSwipeTelltale() - binding.leftActionIcon.setOnClickListener { - swipeActions.showDialog() - } - binding.rightActionIcon.setOnClickListener { - swipeActions.showDialog() - } + binding.leftActionIcon.setOnClickListener { swipeActions.showDialog() } + binding.rightActionIcon.setOnClickListener { swipeActions.showDialog() } recyclerAdapter = object : QueueRecyclerAdapter(activity as MainActivity, swipeActions) { override fun onCreateContextMenu(menu: ContextMenu, v: View, menuInfo: ContextMenu.ContextMenuInfo?) { @@ -143,9 +139,7 @@ import java.util.* swipeRefreshLayout = binding.swipeRefresh swipeRefreshLayout.setDistanceToTriggerSync(resources.getInteger(R.integer.swipe_refresh_distance)) - swipeRefreshLayout.setOnRefreshListener { - FeedUpdateManager.runOnceOrAsk(requireContext()) - } + swipeRefreshLayout.setOnRefreshListener { FeedUpdateManager.runOnceOrAsk(requireContext()) } emptyView = EmptyViewHandler(requireContext()) emptyView.attachToRecyclerView(recyclerView) @@ -200,6 +194,7 @@ import java.util.* private fun procFlowEvents() { lifecycleScope.launch { EventFlow.events.collectLatest { event -> + Logd(TAG, "Received event: $event") when (event) { is FlowEvent.QueueEvent -> onEventMainThread(event) is FlowEvent.FeedItemEvent -> onEventMainThread(event) @@ -406,7 +401,7 @@ import java.util.* val isLocked: Boolean = UserPreferences.isQueueLocked if (isLocked) setQueueLocked(false) 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) else { val builder = MaterialAlertDialogBuilder(requireContext()) @@ -419,7 +414,7 @@ import java.util.* builder.setView(view) 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) } builder.setNegativeButton(R.string.cancel_label, null) @@ -586,7 +581,6 @@ import java.util.* // Update tracked position if (dragFrom == -1) dragFrom = fromPosition - dragTo = toPosition val from = viewHolder.bindingAdapterPosition @@ -632,5 +626,11 @@ import java.util.* private const val PREFS = "QueueFragment" 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) + } + } } diff --git a/app/src/main/java/ac/mdiq/podcini/ui/fragment/QuickFeedDiscoveryFragment.kt b/app/src/main/java/ac/mdiq/podcini/ui/fragment/QuickFeedDiscoveryFragment.kt index 528f997f..dc32cb30 100644 --- a/app/src/main/java/ac/mdiq/podcini/ui/fragment/QuickFeedDiscoveryFragment.kt +++ b/app/src/main/java/ac/mdiq/podcini/ui/fragment/QuickFeedDiscoveryFragment.kt @@ -4,6 +4,7 @@ import ac.mdiq.podcini.BuildConfig import ac.mdiq.podcini.R import ac.mdiq.podcini.databinding.QuickFeedDiscoveryBinding 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.storage.DBReader 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.event.EventFlow import ac.mdiq.podcini.util.event.FlowEvent -import android.content.Context -import android.content.SharedPreferences import android.os.Bundle import android.util.DisplayMetrics import android.util.Log @@ -96,6 +95,7 @@ class QuickFeedDiscoveryFragment : Fragment(), AdapterView.OnItemClickListener { private fun procFlowEvents() { lifecycleScope.launch { EventFlow.events.collectLatest { event -> + Logd(TAG, "Received event: $event") when (event) { is FlowEvent.DiscoveryDefaultUpdateEvent -> loadToplist() else -> {} @@ -111,9 +111,9 @@ class QuickFeedDiscoveryFragment : Fragment(), AdapterView.OnItemClickListener { poweredByTextView.visibility = View.VISIBLE val loader = ItunesTopListLoader(requireContext()) - val prefs: SharedPreferences = requireActivity().getSharedPreferences(ItunesTopListLoader.PREFS, Context.MODE_PRIVATE) - val countryCode: String = prefs.getString(ItunesTopListLoader.PREF_KEY_COUNTRY_CODE, Locale.getDefault().country)!! - if (prefs.getBoolean(ItunesTopListLoader.PREF_KEY_HIDDEN_DISCOVERY_COUNTRY, false)) { +// val prefs: SharedPreferences = requireActivity().getSharedPreferences(ItunesTopListLoader.PREFS, Context.MODE_PRIVATE) + val countryCode: String = prefs!!.getString(ItunesTopListLoader.PREF_KEY_COUNTRY_CODE, Locale.getDefault().country)!! + if (prefs!!.getBoolean(ItunesTopListLoader.PREF_KEY_HIDDEN_DISCOVERY_COUNTRY, false)) { errorTextView.setText(R.string.discover_is_hidden) errorView.visibility = View.VISIBLE discoverGridLayout.visibility = View.GONE @@ -121,7 +121,7 @@ class QuickFeedDiscoveryFragment : Fragment(), AdapterView.OnItemClickListener { poweredByTextView.visibility = View.GONE 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 = "" errorView.visibility = View.VISIBLE discoverGridLayout.visibility = View.VISIBLE @@ -129,7 +129,7 @@ class QuickFeedDiscoveryFragment : Fragment(), AdapterView.OnItemClickListener { errorRetry.setText(R.string.discover_confirm) poweredByTextView.visibility = View.VISIBLE errorRetry.setOnClickListener { - prefs.edit().putBoolean(ItunesTopListLoader.PREF_KEY_NEEDS_CONFIRM, false).apply() + prefs!!.edit().putBoolean(ItunesTopListLoader.PREF_KEY_NEEDS_CONFIRM, false).apply() loadToplist() } return @@ -185,7 +185,6 @@ class QuickFeedDiscoveryFragment : Fragment(), AdapterView.OnItemClickListener { errorRetry.setOnClickListener { loadToplist() } } } - } @OptIn(UnstableApi::class) override fun onItemClick(parent: AdapterView<*>?, view: View, position: Int, id: Long) { diff --git a/app/src/main/java/ac/mdiq/podcini/ui/fragment/SearchFragment.kt b/app/src/main/java/ac/mdiq/podcini/ui/fragment/SearchFragment.kt index 1ac3f8df..0fde744d 100644 --- a/app/src/main/java/ac/mdiq/podcini/ui/fragment/SearchFragment.kt +++ b/app/src/main/java/ac/mdiq/podcini/ui/fragment/SearchFragment.kt @@ -16,9 +16,9 @@ import ac.mdiq.podcini.ui.activity.MainActivity import ac.mdiq.podcini.ui.adapter.EpisodeItemListAdapter import ac.mdiq.podcini.ui.adapter.HorizontalFeedListAdapter 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.LiftOnScrollListener +import ac.mdiq.podcini.ui.utils.LiftOnScrollListener import ac.mdiq.podcini.ui.view.viewholder.EpisodeItemViewHolder import ac.mdiq.podcini.util.FeedItemUtil import ac.mdiq.podcini.util.Logd @@ -239,6 +239,7 @@ import kotlinx.coroutines.withContext private fun procFlowEvents() { lifecycleScope.launch { EventFlow.events.collectLatest { event -> + Logd(TAG, "Received event: $event") when (event) { is FlowEvent.FeedListUpdateEvent, is FlowEvent.UnreadItemsUpdateEvent, is FlowEvent.PlayerStatusEvent -> search() is FlowEvent.FeedItemEvent -> onEventMainThread(event) diff --git a/app/src/main/java/ac/mdiq/podcini/ui/fragment/SubscriptionFragment.kt b/app/src/main/java/ac/mdiq/podcini/ui/fragment/SubscriptionFragment.kt index 7932a2ea..e214d919 100644 --- a/app/src/main/java/ac/mdiq/podcini/ui/fragment/SubscriptionFragment.kt +++ b/app/src/main/java/ac/mdiq/podcini/ui/fragment/SubscriptionFragment.kt @@ -16,13 +16,11 @@ import ac.mdiq.podcini.ui.adapter.SelectableAdapter import ac.mdiq.podcini.ui.adapter.SubscriptionsAdapter import ac.mdiq.podcini.ui.dialog.FeedSortDialog import ac.mdiq.podcini.ui.dialog.SubscriptionsFilterDialog -import ac.mdiq.podcini.ui.view.EmptyViewHandler -import ac.mdiq.podcini.ui.view.LiftOnScrollListener +import ac.mdiq.podcini.ui.utils.EmptyViewHandler +import ac.mdiq.podcini.ui.utils.LiftOnScrollListener import ac.mdiq.podcini.util.Logd import ac.mdiq.podcini.util.event.EventFlow import ac.mdiq.podcini.util.event.FlowEvent -import android.content.Context -import android.content.SharedPreferences import android.os.Bundle import android.util.Log import android.view.* @@ -62,7 +60,7 @@ class SubscriptionFragment : Fragment(), Toolbar.OnMenuItemClickListener, Select private lateinit var toolbar: MaterialToolbar private lateinit var swipeRefreshLayout: SwipeRefreshLayout private lateinit var progressBar: ProgressBar - private lateinit var prefs: SharedPreferences +// private lateinit var prefs: SharedPreferences private lateinit var speedDialView: SpeedDialView private var tagFilterIndex = 1 @@ -78,7 +76,7 @@ class SubscriptionFragment : Fragment(), Toolbar.OnMenuItemClickListener, Select super.onCreate(savedInstanceState) 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 { @@ -236,6 +234,7 @@ class SubscriptionFragment : Fragment(), Toolbar.OnMenuItemClickListener, Select private fun procFlowEvents() { lifecycleScope.launch { EventFlow.events.collectLatest { event -> + Logd(TAG, "Received event: $event") when (event) { is FlowEvent.FeedListUpdateEvent -> onFeedListChanged(event) is FlowEvent.UnreadItemsUpdateEvent -> loadSubscriptions() diff --git a/app/src/main/java/ac/mdiq/podcini/ui/fragment/VideoEpisodeFragment.kt b/app/src/main/java/ac/mdiq/podcini/ui/fragment/VideoEpisodeFragment.kt index fe48884c..062c1f05 100644 --- a/app/src/main/java/ac/mdiq/podcini/ui/fragment/VideoEpisodeFragment.kt +++ b/app/src/main/java/ac/mdiq/podcini/ui/fragment/VideoEpisodeFragment.kt @@ -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.base.MediaPlayerBase 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.rewindSecs import ac.mdiq.podcini.preferences.UserPreferences.setShowRemainTimeSetting @@ -155,6 +157,7 @@ class VideoEpisodeFragment : Fragment(), OnSeekBarChangeListener { private fun procFlowEvents() { lifecycleScope.launch { EventFlow.events.collectLatest { event -> + Logd(TAG, "Received event: $event") when (event) { is FlowEvent.BufferUpdateEvent -> bufferUpdate(event) is FlowEvent.PlaybackPositionEvent -> onPositionObserverUpdate() diff --git a/app/src/main/java/ac/mdiq/podcini/ui/statistics/StatisticsFragment.kt b/app/src/main/java/ac/mdiq/podcini/ui/statistics/StatisticsFragment.kt index 41f2fb43..e8acd124 100644 --- a/app/src/main/java/ac/mdiq/podcini/ui/statistics/StatisticsFragment.kt +++ b/app/src/main/java/ac/mdiq/podcini/ui/statistics/StatisticsFragment.kt @@ -3,6 +3,7 @@ package ac.mdiq.podcini.ui.statistics import ac.mdiq.podcini.R 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.ui.activity.MainActivity 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 android.content.Context import android.content.DialogInterface +import android.content.SharedPreferences import android.os.Bundle import android.util.Log import android.view.LayoutInflater @@ -98,7 +100,7 @@ class StatisticsFragment : PagedToolbarFragment() { } @UnstableApi private fun doResetStatistics() { - requireContext().getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE).edit() + prefs!!.edit() .putBoolean(PREF_INCLUDE_MARKED_PLAYED, false) .putLong(PREF_FILTER_FROM, 0) .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_TO: String = "filterTo" - private const val POS_SUBSCRIPTIONS = 0 private const val POS_YEARS = 1 private const val POS_SPACE_TAKEN = 2 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) + } + } } diff --git a/app/src/main/java/ac/mdiq/podcini/ui/statistics/subscriptions/DatesFilterDialog.kt b/app/src/main/java/ac/mdiq/podcini/ui/statistics/subscriptions/DatesFilterDialog.kt index 83ca93e2..ad55f3dc 100644 --- a/app/src/main/java/ac/mdiq/podcini/ui/statistics/subscriptions/DatesFilterDialog.kt +++ b/app/src/main/java/ac/mdiq/podcini/ui/statistics/subscriptions/DatesFilterDialog.kt @@ -28,7 +28,6 @@ abstract class DatesFilterDialog(private val context: Context, oldestDate: Long) protected val filterDatesFrom: Pair, Array> protected val filterDatesTo: Pair, Array> - init { initParams() filterDatesFrom = makeMonthlyList(oldestDate, false) diff --git a/app/src/main/java/ac/mdiq/podcini/ui/statistics/subscriptions/SubscriptionStatisticsFragment.kt b/app/src/main/java/ac/mdiq/podcini/ui/statistics/subscriptions/SubscriptionStatisticsFragment.kt index 3fbbad10..a00c5478 100644 --- a/app/src/main/java/ac/mdiq/podcini/ui/statistics/subscriptions/SubscriptionStatisticsFragment.kt +++ b/app/src/main/java/ac/mdiq/podcini/ui/statistics/subscriptions/SubscriptionStatisticsFragment.kt @@ -4,15 +4,13 @@ package ac.mdiq.podcini.ui.statistics.subscriptions import ac.mdiq.podcini.R import ac.mdiq.podcini.databinding.StatisticsFragmentBinding 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.StatisticsItem import ac.mdiq.podcini.ui.statistics.StatisticsFragment -import ac.mdiq.podcini.ui.statistics.years.YearsStatisticsFragment -import ac.mdiq.podcini.ui.statistics.years.YearsStatisticsFragment.Companion +import ac.mdiq.podcini.ui.statistics.StatisticsFragment.Companion.prefs +import ac.mdiq.podcini.util.Logd import ac.mdiq.podcini.util.event.EventFlow import ac.mdiq.podcini.util.event.FlowEvent -import android.content.Context import android.os.Bundle import android.util.Log import android.view.* @@ -21,10 +19,6 @@ import androidx.fragment.app.Fragment import androidx.lifecycle.lifecycleScope import androidx.recyclerview.widget.LinearLayoutManager 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.flow.collectLatest import kotlinx.coroutines.launch @@ -77,6 +71,7 @@ class SubscriptionStatisticsFragment : Fragment() { private fun procFlowEvents() { lifecycleScope.launch { EventFlow.events.collectLatest { event -> + Logd(TAG, "Received event: $event") when (event) { is FlowEvent.StatisticsEvent -> refreshStatistics() else -> {} @@ -96,7 +91,7 @@ class SubscriptionStatisticsFragment : Fragment() { if (statisticsResult != null) { val dialog = object: DatesFilterDialog(requireContext(), statisticsResult!!.oldestDate) { override fun initParams() { - prefs = requireContext().getSharedPreferences(StatisticsFragment.PREF_NAME, Context.MODE_PRIVATE) + prefs = StatisticsFragment.prefs includeMarkedAsPlayed = prefs!!.getBoolean(StatisticsFragment.PREF_INCLUDE_MARKED_PLAYED, false) timeFilterFrom = prefs!!.getLong(StatisticsFragment.PREF_FILTER_FROM, 0) timeFilterTo = prefs!!.getLong(StatisticsFragment.PREF_FILTER_TO, Long.MAX_VALUE) @@ -126,10 +121,10 @@ class SubscriptionStatisticsFragment : Fragment() { private fun loadStatistics() { // disposable?.dispose() - val prefs = requireContext().getSharedPreferences(StatisticsFragment.PREF_NAME, Context.MODE_PRIVATE) - val includeMarkedAsPlayed = prefs.getBoolean(StatisticsFragment.PREF_INCLUDE_MARKED_PLAYED, false) - val timeFilterFrom = prefs.getLong(StatisticsFragment.PREF_FILTER_FROM, 0) - val timeFilterTo = prefs.getLong(StatisticsFragment.PREF_FILTER_TO, Long.MAX_VALUE) +// val prefs = requireContext().getSharedPreferences(StatisticsFragment.PREF_NAME, Context.MODE_PRIVATE) + val includeMarkedAsPlayed = prefs!!.getBoolean(StatisticsFragment.PREF_INCLUDE_MARKED_PLAYED, false) + val timeFilterFrom = prefs!!.getLong(StatisticsFragment.PREF_FILTER_FROM, 0) + val timeFilterTo = prefs!!.getLong(StatisticsFragment.PREF_FILTER_TO, Long.MAX_VALUE) // disposable = Observable.fromCallable { // val statisticsData = DBReader.getStatistics( diff --git a/app/src/main/java/ac/mdiq/podcini/ui/statistics/years/YearsStatisticsFragment.kt b/app/src/main/java/ac/mdiq/podcini/ui/statistics/years/YearsStatisticsFragment.kt index 74ecbc42..2aaa456a 100644 --- a/app/src/main/java/ac/mdiq/podcini/ui/statistics/years/YearsStatisticsFragment.kt +++ b/app/src/main/java/ac/mdiq/podcini/ui/statistics/years/YearsStatisticsFragment.kt @@ -3,8 +3,11 @@ package ac.mdiq.podcini.ui.statistics.years import ac.mdiq.podcini.R 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.MonthlyStatisticsItem +import ac.mdiq.podcini.util.Logd import ac.mdiq.podcini.util.event.EventFlow import ac.mdiq.podcini.util.event.FlowEvent import android.os.Bundle @@ -65,6 +68,7 @@ class YearsStatisticsFragment : Fragment() { private fun procFlowEvents() { lifecycleScope.launch { EventFlow.events.collectLatest { event -> + Logd(TAG, "Received event: $event") when (event) { is FlowEvent.StatisticsEvent -> refreshStatistics() else -> {} diff --git a/app/src/main/java/ac/mdiq/podcini/ui/adapter/CoverLoader.kt b/app/src/main/java/ac/mdiq/podcini/ui/utils/CoverLoader.kt similarity index 95% rename from app/src/main/java/ac/mdiq/podcini/ui/adapter/CoverLoader.kt rename to app/src/main/java/ac/mdiq/podcini/ui/utils/CoverLoader.kt index 627251e4..9825aed3 100644 --- a/app/src/main/java/ac/mdiq/podcini/ui/adapter/CoverLoader.kt +++ b/app/src/main/java/ac/mdiq/podcini/ui/utils/CoverLoader.kt @@ -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.ui.activity.MainActivity +import ac.mdiq.podcini.util.Logd import android.graphics.drawable.Drawable import android.view.View import android.widget.ImageView @@ -75,6 +76,7 @@ class CoverLoader(private val activity: MainActivity) { .setHeader("User-Agent", "Mozilla/5.0") .listener(object : ImageRequest.Listener { override fun onError(request: ImageRequest, throwable: ErrorResult) { + Logd("CoverLoader", "Trying to get fallback image") val fallbackImageRequest = ImageRequest.Builder(activity) .data(fallbackUri) .setHeader("User-Agent", "Mozilla/5.0") @@ -86,9 +88,7 @@ class CoverLoader(private val activity: MainActivity) { }) .target(coverTargetCoil) .build() - activity.imageLoader - .enqueue(request) - + activity.imageLoader.enqueue(request) } internal class CoilCoverTarget(fallbackTitle: TextView?, coverImage: ImageView, private val textAndImageCombined: Boolean) : Target { diff --git a/app/src/main/java/ac/mdiq/podcini/ui/view/EmptyViewHandler.kt b/app/src/main/java/ac/mdiq/podcini/ui/utils/EmptyViewHandler.kt similarity index 96% rename from app/src/main/java/ac/mdiq/podcini/ui/view/EmptyViewHandler.kt rename to app/src/main/java/ac/mdiq/podcini/ui/utils/EmptyViewHandler.kt index 5e8ea337..b4549e5c 100644 --- a/app/src/main/java/ac/mdiq/podcini/ui/view/EmptyViewHandler.kt +++ b/app/src/main/java/ac/mdiq/podcini/ui/utils/EmptyViewHandler.kt @@ -1,4 +1,4 @@ -package ac.mdiq.podcini.ui.view +package ac.mdiq.podcini.ui.utils import android.content.Context import android.database.DataSetObserver diff --git a/app/src/main/java/ac/mdiq/podcini/ui/view/ItemOffsetDecoration.kt b/app/src/main/java/ac/mdiq/podcini/ui/utils/ItemOffsetDecoration.kt similarity index 95% rename from app/src/main/java/ac/mdiq/podcini/ui/view/ItemOffsetDecoration.kt rename to app/src/main/java/ac/mdiq/podcini/ui/utils/ItemOffsetDecoration.kt index 0e7ace60..d02f4a2f 100644 --- a/app/src/main/java/ac/mdiq/podcini/ui/view/ItemOffsetDecoration.kt +++ b/app/src/main/java/ac/mdiq/podcini/ui/utils/ItemOffsetDecoration.kt @@ -1,4 +1,4 @@ -package ac.mdiq.podcini.ui.view +package ac.mdiq.podcini.ui.utils import android.content.Context import android.graphics.Rect diff --git a/app/src/main/java/ac/mdiq/podcini/ui/view/LiftOnScrollListener.kt b/app/src/main/java/ac/mdiq/podcini/ui/utils/LiftOnScrollListener.kt similarity index 98% rename from app/src/main/java/ac/mdiq/podcini/ui/view/LiftOnScrollListener.kt rename to app/src/main/java/ac/mdiq/podcini/ui/utils/LiftOnScrollListener.kt index bb490319..8537b6e3 100644 --- a/app/src/main/java/ac/mdiq/podcini/ui/view/LiftOnScrollListener.kt +++ b/app/src/main/java/ac/mdiq/podcini/ui/utils/LiftOnScrollListener.kt @@ -1,4 +1,4 @@ -package ac.mdiq.podcini.ui.view +package ac.mdiq.podcini.ui.utils import android.animation.ValueAnimator import android.view.View diff --git a/app/src/main/java/ac/mdiq/podcini/ui/view/LocalDeleteModal.kt b/app/src/main/java/ac/mdiq/podcini/ui/utils/LocalDeleteModal.kt similarity index 96% rename from app/src/main/java/ac/mdiq/podcini/ui/view/LocalDeleteModal.kt rename to app/src/main/java/ac/mdiq/podcini/ui/utils/LocalDeleteModal.kt index b9f9fce1..376a69eb 100644 --- a/app/src/main/java/ac/mdiq/podcini/ui/view/LocalDeleteModal.kt +++ b/app/src/main/java/ac/mdiq/podcini/ui/utils/LocalDeleteModal.kt @@ -1,4 +1,4 @@ -package ac.mdiq.podcini.ui.view +package ac.mdiq.podcini.ui.utils import ac.mdiq.podcini.R import android.content.Context diff --git a/app/src/main/java/ac/mdiq/podcini/ui/view/LockableBottomSheetBehavior.kt b/app/src/main/java/ac/mdiq/podcini/ui/utils/LockableBottomSheetBehavior.kt similarity index 98% rename from app/src/main/java/ac/mdiq/podcini/ui/view/LockableBottomSheetBehavior.kt rename to app/src/main/java/ac/mdiq/podcini/ui/utils/LockableBottomSheetBehavior.kt index 21aa67d6..67e8e5a9 100644 --- a/app/src/main/java/ac/mdiq/podcini/ui/view/LockableBottomSheetBehavior.kt +++ b/app/src/main/java/ac/mdiq/podcini/ui/utils/LockableBottomSheetBehavior.kt @@ -1,4 +1,4 @@ -package ac.mdiq.podcini.ui.view +package ac.mdiq.podcini.ui.utils import android.content.Context import android.util.AttributeSet diff --git a/app/src/main/java/ac/mdiq/podcini/ui/view/SimpleAdapterDataObserver.kt b/app/src/main/java/ac/mdiq/podcini/ui/utils/SimpleAdapterDataObserver.kt similarity index 96% rename from app/src/main/java/ac/mdiq/podcini/ui/view/SimpleAdapterDataObserver.kt rename to app/src/main/java/ac/mdiq/podcini/ui/utils/SimpleAdapterDataObserver.kt index 26f3c25d..3c9ae389 100644 --- a/app/src/main/java/ac/mdiq/podcini/ui/view/SimpleAdapterDataObserver.kt +++ b/app/src/main/java/ac/mdiq/podcini/ui/utils/SimpleAdapterDataObserver.kt @@ -1,4 +1,4 @@ -package ac.mdiq.podcini.ui.view +package ac.mdiq.podcini.ui.utils import androidx.recyclerview.widget.RecyclerView diff --git a/app/src/main/java/ac/mdiq/podcini/ui/view/ToolbarIconTintManager.kt b/app/src/main/java/ac/mdiq/podcini/ui/utils/ToolbarIconTintManager.kt similarity index 98% rename from app/src/main/java/ac/mdiq/podcini/ui/view/ToolbarIconTintManager.kt rename to app/src/main/java/ac/mdiq/podcini/ui/utils/ToolbarIconTintManager.kt index c4021b27..397431f2 100644 --- a/app/src/main/java/ac/mdiq/podcini/ui/view/ToolbarIconTintManager.kt +++ b/app/src/main/java/ac/mdiq/podcini/ui/utils/ToolbarIconTintManager.kt @@ -1,4 +1,4 @@ -package ac.mdiq.podcini.ui.view +package ac.mdiq.podcini.ui.utils import android.content.Context import android.graphics.PorterDuff diff --git a/app/src/main/java/ac/mdiq/podcini/ui/fragment/TransitionEffect.kt b/app/src/main/java/ac/mdiq/podcini/ui/utils/TransitionEffect.kt similarity index 60% rename from app/src/main/java/ac/mdiq/podcini/ui/fragment/TransitionEffect.kt rename to app/src/main/java/ac/mdiq/podcini/ui/utils/TransitionEffect.kt index 7a76a47b..b2bbdcd7 100644 --- a/app/src/main/java/ac/mdiq/podcini/ui/fragment/TransitionEffect.kt +++ b/app/src/main/java/ac/mdiq/podcini/ui/utils/TransitionEffect.kt @@ -1,4 +1,4 @@ -package ac.mdiq.podcini.ui.fragment +package ac.mdiq.podcini.ui.utils enum class TransitionEffect { NONE, FADE, SLIDE diff --git a/app/src/main/java/ac/mdiq/podcini/ui/view/EpisodeItemListRecyclerView.kt b/app/src/main/java/ac/mdiq/podcini/ui/view/EpisodeItemListRecyclerView.kt index ec746d41..e957858a 100644 --- a/app/src/main/java/ac/mdiq/podcini/ui/view/EpisodeItemListRecyclerView.kt +++ b/app/src/main/java/ac/mdiq/podcini/ui/view/EpisodeItemListRecyclerView.kt @@ -8,6 +8,8 @@ import androidx.recyclerview.widget.DividerItemDecoration import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView import ac.mdiq.podcini.R +import ac.mdiq.podcini.receiver.PlayerWidget.Companion.PREFS_NAME +import android.content.SharedPreferences class EpisodeItemListRecyclerView : RecyclerView { private lateinit var layoutManager: LinearLayoutManager @@ -46,16 +48,16 @@ class EpisodeItemListRecyclerView : RecyclerView { val firstItemView = layoutManager.findViewByPosition(firstItem) 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_OFFSET + tag, topOffset.toInt()) .apply() } fun restoreScrollPosition(tag: String) { - val prefs = context.getSharedPreferences(TAG, Context.MODE_PRIVATE) - val position = prefs.getInt(PREF_PREFIX_SCROLL_POSITION + tag, 0) - val offset = prefs.getInt(PREF_PREFIX_SCROLL_OFFSET + tag, 0) +// val prefs = context.getSharedPreferences(TAG, Context.MODE_PRIVATE) + val position = prefs!!.getInt(PREF_PREFIX_SCROLL_POSITION + tag, 0) + val offset = prefs!!.getInt(PREF_PREFIX_SCROLL_OFFSET + tag, 0) if (position > 0 || offset > 0) layoutManager.scrollToPositionWithOffset(position, offset) } @@ -71,5 +73,11 @@ class EpisodeItemListRecyclerView : RecyclerView { private const val TAG = "EpisodeItemListRecyclerView" private const val PREF_PREFIX_SCROLL_POSITION = "scroll_position_" 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) + } } } diff --git a/app/src/main/java/ac/mdiq/podcini/ui/view/viewholder/EpisodeItemViewHolder.kt b/app/src/main/java/ac/mdiq/podcini/ui/view/viewholder/EpisodeItemViewHolder.kt index 64141d5b..6a3805dd 100644 --- a/app/src/main/java/ac/mdiq/podcini/ui/view/viewholder/EpisodeItemViewHolder.kt +++ b/app/src/main/java/ac/mdiq/podcini/ui/view/viewholder/EpisodeItemViewHolder.kt @@ -1,7 +1,6 @@ package ac.mdiq.podcini.ui.view.viewholder import ac.mdiq.podcini.ui.activity.MainActivity -import android.os.Build import android.text.Layout import android.text.format.Formatter import android.util.Log @@ -18,7 +17,7 @@ import com.google.android.material.elevation.SurfaceColors import com.joanzapata.iconify.Iconify import ac.mdiq.podcini.R 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.net.download.MediaSizeLoader 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 android.widget.LinearLayout import androidx.appcompat.content.res.AppCompatResources -import androidx.core.content.ContextCompat.getDrawable import io.reactivex.functions.Consumer import kotlin.math.max @@ -45,8 +43,8 @@ import kotlin.math.max * Holds the view which shows FeedItems. */ @UnstableApi -open class EpisodeItemViewHolder(private val activity: MainActivity, parent: ViewGroup?) : - RecyclerView.ViewHolder(LayoutInflater.from(activity).inflate(R.layout.feeditemlist_item, parent, false)) { +open class EpisodeItemViewHolder(private val activity: MainActivity, parent: ViewGroup) + : RecyclerView.ViewHolder(LayoutInflater.from(parent.context).inflate(R.layout.feeditemlist_item, parent, false)) { 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 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 if (!(actionButton?.TAG == TTSActionButton::class.simpleName && newButton.TAG == TTSActionButton::class.simpleName)) { actionButton = newButton @@ -118,11 +116,9 @@ open class EpisodeItemViewHolder(private val activity: MainActivity, parent: Vie // Log.d(TAG, "bind called ${item.media}") when { - item.media != null -> { - bind(item.media!!) - } + item.media != null -> bind(item.media!!) + // for generating TTS files for episode without media item.playState == BUILDING -> { - // for generating TTS files for episode without media secondaryActionProgress.setPercentage(actionButton!!.processing, item) secondaryActionProgress.setIndeterminate(false) } @@ -139,15 +135,16 @@ open class EpisodeItemViewHolder(private val activity: MainActivity, parent: Vie if (coverHolder.visibility == View.VISIBLE) { val imgLoc = ImageResourceUtils.getEpisodeListImageLocation(item) -// Logd(TAG, "imgLoc $imgLoc ${item.feed?.imageUrl} ${item.title}") - if (!imgLoc.isNullOrBlank() && !imgLoc.contains(PREFIX_GENERATIVE_COVER)) CoverLoader(activity) - .withUri(imgLoc) - .withFallbackUri(item.feed?.imageUrl) - .withPlaceholderView(placeholder) - .withCoverView(cover) - .load() + Logd(TAG, "imgLoc $imgLoc ${item.feed?.imageUrl} ${item.title}") + if (!imgLoc.isNullOrBlank() && !imgLoc.contains(PREFIX_GENERATIVE_COVER)) + CoverLoader(activity) + .withUri(imgLoc) + .withFallbackUri(item.feed?.imageUrl) + .withPlaceholderView(placeholder) + .withCoverView(cover) + .load() else { - Logd(TAG, "setting to ic_launcher") + Logd(TAG, "setting cover to ic_launcher") 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() -> { size.text = "{fa-spinner}" Iconify.addIcons(size) - MediaSizeLoader.getFeedMediaSizeObservable(media).subscribe( - Consumer { sizeValue: Long? -> - if (sizeValue == null) return@Consumer - if (sizeValue > 0) size.text = Formatter.formatShortFileSize(activity, sizeValue) - else size.text = "" - }) { error: Throwable? -> + MediaSizeLoader.getFeedMediaSizeObservable(media).subscribe(Consumer { sizeValue: Long? -> + if (sizeValue == null) return@Consumer + if (sizeValue > 0) size.text = Formatter.formatShortFileSize(activity, sizeValue) + else size.text = "" + }) { error: Throwable? -> size.text = "" Log.e(TAG, Log.getStackTraceString(error)) } diff --git a/app/src/main/java/ac/mdiq/podcini/ui/widget/WidgetUpdater.kt b/app/src/main/java/ac/mdiq/podcini/ui/widget/WidgetUpdater.kt index dd43279f..233f3fe3 100644 --- a/app/src/main/java/ac/mdiq/podcini/ui/widget/WidgetUpdater.kt +++ b/app/src/main/java/ac/mdiq/podcini/ui/widget/WidgetUpdater.kt @@ -7,6 +7,7 @@ import ac.mdiq.podcini.preferences.UserPreferences.shouldShowRemainingTime import ac.mdiq.podcini.receiver.MediaButtonReceiver.Companion.createPendingIntent import ac.mdiq.podcini.receiver.PlayerWidget 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.Playable 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.util.Converter.getDurationStringLong import ac.mdiq.podcini.util.TimeSpeedConverter -import android.R.attr.bitmap import android.appwidget.AppWidgetManager import android.content.ComponentName import android.content.Context import android.graphics.Bitmap -import android.graphics.Canvas -import android.graphics.Matrix import android.graphics.drawable.BitmapDrawable import android.util.Log import android.view.KeyEvent @@ -165,16 +163,16 @@ object WidgetUpdater { for (id in widgetIds) { 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 columns = getCellsForSize(minWidth) if (columns < 3) views.setViewVisibility(R.id.layout_center, View.INVISIBLE) else views.setViewVisibility(R.id.layout_center, View.VISIBLE) - val showPlaybackSpeed = prefs.getBoolean(PlayerWidget.KEY_WIDGET_PLAYBACK_SPEED + id, true) - val showRewind = prefs.getBoolean(PlayerWidget.KEY_WIDGET_REWIND + id, true) - val showFastForward = prefs.getBoolean(PlayerWidget.KEY_WIDGET_FAST_FORWARD + id, true) - val showSkip = prefs.getBoolean(PlayerWidget.KEY_WIDGET_SKIP + id, true) + val showPlaybackSpeed = prefs!!.getBoolean(PlayerWidget.KEY_WIDGET_PLAYBACK_SPEED + id, true) + val showRewind = prefs!!.getBoolean(PlayerWidget.KEY_WIDGET_REWIND + id, true) + val showFastForward = prefs!!.getBoolean(PlayerWidget.KEY_WIDGET_FAST_FORWARD + id, true) + val showSkip = prefs!!.getBoolean(PlayerWidget.KEY_WIDGET_SKIP + id, true) if (showPlaybackSpeed || showRewind || showSkip || showFastForward) { views.setInt(R.id.extendedButtonsContainer, "setVisibility", View.VISIBLE) @@ -188,7 +186,7 @@ object WidgetUpdater { 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) manager.updateAppWidget(id, views) diff --git a/app/src/main/java/ac/mdiq/podcini/ui/widget/WidgetUpdaterWorker.kt b/app/src/main/java/ac/mdiq/podcini/ui/widget/WidgetUpdaterWorker.kt index 884c0f23..b8ac96bc 100644 --- a/app/src/main/java/ac/mdiq/podcini/ui/widget/WidgetUpdaterWorker.kt +++ b/app/src/main/java/ac/mdiq/podcini/ui/widget/WidgetUpdaterWorker.kt @@ -2,7 +2,7 @@ package ac.mdiq.podcini.ui.widget import ac.mdiq.podcini.feed.util.PlaybackSpeedUtils.getCurrentPlaybackSpeed 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.util.Logd 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. */ 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))) else WidgetUpdater.updateWidget(applicationContext, WidgetState(PlayerStatus.STOPPED)) } diff --git a/app/src/main/java/ac/mdiq/podcini/util/SPAUtil.kt b/app/src/main/java/ac/mdiq/podcini/util/SPAUtil.kt index b6912eb7..f3207dab 100644 --- a/app/src/main/java/ac/mdiq/podcini/util/SPAUtil.kt +++ b/app/src/main/java/ac/mdiq/podcini/util/SPAUtil.kt @@ -1,5 +1,6 @@ package ac.mdiq.podcini.util +import ac.mdiq.podcini.preferences.UserPreferences.appPrefs import ac.mdiq.podcini.receiver.SPAReceiver import android.content.Context import android.content.Intent @@ -31,12 +32,12 @@ object SPAUtil { Log.wtf(TAG, "Unable to get application context") return false } - val prefs = PreferenceManager.getDefaultSharedPreferences(appContext) - if (!prefs.getBoolean(PREF_HAS_QUERIED_SP_APPS, false)) { +// val prefs = PreferenceManager.getDefaultSharedPreferences(appContext) + if (!appPrefs.getBoolean(PREF_HAS_QUERIED_SP_APPS, false)) { appContext.sendBroadcast(Intent(SPAReceiver.ACTION_SP_APPS_QUERY_FEEDS)) 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.apply() diff --git a/app/src/main/java/ac/mdiq/podcini/util/config/ClientConfigurator.kt b/app/src/main/java/ac/mdiq/podcini/util/config/ClientConfigurator.kt index d0dcfe02..e40af0db 100644 --- a/app/src/main/java/ac/mdiq/podcini/util/config/ClientConfigurator.kt +++ b/app/src/main/java/ac/mdiq/podcini/util/config/ClientConfigurator.kt @@ -18,6 +18,9 @@ import ac.mdiq.podcini.storage.database.PodDBAdapter import ac.mdiq.podcini.preferences.UserPreferences import ac.mdiq.podcini.preferences.UserPreferences.proxyConfig import ac.mdiq.podcini.net.download.service.DownloadServiceInterfaceImpl +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch import java.io.File @UnstableApi diff --git a/app/src/main/play/listings/de-DE/graphics/large-tablet-screenshots/tablet.png b/app/src/main/play/listings/de-DE/graphics/large-tablet-screenshots/tablet.png deleted file mode 100644 index 5ff81f1e..00000000 Binary files a/app/src/main/play/listings/de-DE/graphics/large-tablet-screenshots/tablet.png and /dev/null differ diff --git a/app/src/main/play/listings/de-DE/graphics/phone-screenshots/00.png b/app/src/main/play/listings/de-DE/graphics/phone-screenshots/00.png deleted file mode 100644 index b45199a3..00000000 Binary files a/app/src/main/play/listings/de-DE/graphics/phone-screenshots/00.png and /dev/null differ diff --git a/app/src/main/play/listings/de-DE/graphics/phone-screenshots/01.png b/app/src/main/play/listings/de-DE/graphics/phone-screenshots/01.png deleted file mode 100644 index e7bfdc2d..00000000 Binary files a/app/src/main/play/listings/de-DE/graphics/phone-screenshots/01.png and /dev/null differ diff --git a/app/src/main/play/listings/de-DE/graphics/phone-screenshots/02.png b/app/src/main/play/listings/de-DE/graphics/phone-screenshots/02.png deleted file mode 100644 index 1d7ec8bf..00000000 Binary files a/app/src/main/play/listings/de-DE/graphics/phone-screenshots/02.png and /dev/null differ diff --git a/app/src/main/play/listings/de-DE/graphics/phone-screenshots/03.png b/app/src/main/play/listings/de-DE/graphics/phone-screenshots/03.png deleted file mode 100644 index 7a7ede19..00000000 Binary files a/app/src/main/play/listings/de-DE/graphics/phone-screenshots/03.png and /dev/null differ diff --git a/app/src/main/play/listings/de-DE/graphics/phone-screenshots/04.png b/app/src/main/play/listings/de-DE/graphics/phone-screenshots/04.png deleted file mode 100644 index 2120934c..00000000 Binary files a/app/src/main/play/listings/de-DE/graphics/phone-screenshots/04.png and /dev/null differ diff --git a/app/src/main/play/listings/de-DE/graphics/phone-screenshots/05.png b/app/src/main/play/listings/de-DE/graphics/phone-screenshots/05.png deleted file mode 100644 index 1eedf13f..00000000 Binary files a/app/src/main/play/listings/de-DE/graphics/phone-screenshots/05.png and /dev/null differ diff --git a/app/src/main/play/listings/en-US/graphics/icon/icon.png b/app/src/main/play/listings/en-US/graphics/icon/icon.png deleted file mode 100644 index 991e7439..00000000 Binary files a/app/src/main/play/listings/en-US/graphics/icon/icon.png and /dev/null differ diff --git a/app/src/main/play/listings/en-US/graphics/phone-screenshots/10_player.jpg b/app/src/main/play/listings/en-US/graphics/phone-screenshots/10_player.jpg deleted file mode 100644 index ec3dc6a5..00000000 Binary files a/app/src/main/play/listings/en-US/graphics/phone-screenshots/10_player.jpg and /dev/null differ diff --git a/app/src/main/play/listings/en-US/graphics/phone-screenshots/1_drawer.jpg b/app/src/main/play/listings/en-US/graphics/phone-screenshots/1_drawer.jpg deleted file mode 100644 index 895e58a2..00000000 Binary files a/app/src/main/play/listings/en-US/graphics/phone-screenshots/1_drawer.jpg and /dev/null differ diff --git a/app/src/main/play/listings/en-US/graphics/phone-screenshots/2_setting.jpg b/app/src/main/play/listings/en-US/graphics/phone-screenshots/2_setting.jpg deleted file mode 100644 index a3804756..00000000 Binary files a/app/src/main/play/listings/en-US/graphics/phone-screenshots/2_setting.jpg and /dev/null differ diff --git a/app/src/main/play/listings/en-US/graphics/phone-screenshots/3_setting.jpg b/app/src/main/play/listings/en-US/graphics/phone-screenshots/3_setting.jpg deleted file mode 100644 index b7bcba4c..00000000 Binary files a/app/src/main/play/listings/en-US/graphics/phone-screenshots/3_setting.jpg and /dev/null differ diff --git a/app/src/main/play/listings/en-US/graphics/phone-screenshots/4_home.jpg b/app/src/main/play/listings/en-US/graphics/phone-screenshots/4_home.jpg deleted file mode 100644 index 895db71c..00000000 Binary files a/app/src/main/play/listings/en-US/graphics/phone-screenshots/4_home.jpg and /dev/null differ diff --git a/app/src/main/play/listings/en-US/graphics/phone-screenshots/5_queue.jpg b/app/src/main/play/listings/en-US/graphics/phone-screenshots/5_queue.jpg deleted file mode 100644 index 55893b4d..00000000 Binary files a/app/src/main/play/listings/en-US/graphics/phone-screenshots/5_queue.jpg and /dev/null differ diff --git a/app/src/main/play/listings/en-US/graphics/phone-screenshots/6_podcast.jpg b/app/src/main/play/listings/en-US/graphics/phone-screenshots/6_podcast.jpg deleted file mode 100644 index c2f29fb4..00000000 Binary files a/app/src/main/play/listings/en-US/graphics/phone-screenshots/6_podcast.jpg and /dev/null differ diff --git a/app/src/main/play/listings/en-US/graphics/phone-screenshots/7_podcast.jpg b/app/src/main/play/listings/en-US/graphics/phone-screenshots/7_podcast.jpg deleted file mode 100644 index 36ba875c..00000000 Binary files a/app/src/main/play/listings/en-US/graphics/phone-screenshots/7_podcast.jpg and /dev/null differ diff --git a/app/src/main/play/listings/en-US/graphics/phone-screenshots/8_episode.jpg b/app/src/main/play/listings/en-US/graphics/phone-screenshots/8_episode.jpg deleted file mode 100644 index f8e81f9c..00000000 Binary files a/app/src/main/play/listings/en-US/graphics/phone-screenshots/8_episode.jpg and /dev/null differ diff --git a/app/src/main/play/listings/en-US/graphics/phone-screenshots/9_speed.jpg b/app/src/main/play/listings/en-US/graphics/phone-screenshots/9_speed.jpg deleted file mode 100644 index 0d372337..00000000 Binary files a/app/src/main/play/listings/en-US/graphics/phone-screenshots/9_speed.jpg and /dev/null differ diff --git a/app/src/main/play/listings/en-US/graphics/tv-banner/tv-banner.png b/app/src/main/play/listings/en-US/graphics/tv-banner/tv-banner.png deleted file mode 100644 index 860a1889..00000000 Binary files a/app/src/main/play/listings/en-US/graphics/tv-banner/tv-banner.png and /dev/null differ diff --git a/app/src/main/play/listings/es-ES/graphics/phone-screenshots/00.png b/app/src/main/play/listings/es-ES/graphics/phone-screenshots/00.png deleted file mode 100644 index 6c3ed912..00000000 Binary files a/app/src/main/play/listings/es-ES/graphics/phone-screenshots/00.png and /dev/null differ diff --git a/app/src/main/play/listings/es-ES/graphics/phone-screenshots/01.png b/app/src/main/play/listings/es-ES/graphics/phone-screenshots/01.png deleted file mode 100644 index 7bd0730a..00000000 Binary files a/app/src/main/play/listings/es-ES/graphics/phone-screenshots/01.png and /dev/null differ diff --git a/app/src/main/play/listings/es-ES/graphics/phone-screenshots/02.png b/app/src/main/play/listings/es-ES/graphics/phone-screenshots/02.png deleted file mode 100644 index 2c02ee1d..00000000 Binary files a/app/src/main/play/listings/es-ES/graphics/phone-screenshots/02.png and /dev/null differ diff --git a/app/src/main/play/listings/es-ES/graphics/phone-screenshots/03.png b/app/src/main/play/listings/es-ES/graphics/phone-screenshots/03.png deleted file mode 100644 index 306de8f3..00000000 Binary files a/app/src/main/play/listings/es-ES/graphics/phone-screenshots/03.png and /dev/null differ diff --git a/app/src/main/play/listings/es-ES/graphics/phone-screenshots/04.png b/app/src/main/play/listings/es-ES/graphics/phone-screenshots/04.png deleted file mode 100644 index c1a09170..00000000 Binary files a/app/src/main/play/listings/es-ES/graphics/phone-screenshots/04.png and /dev/null differ diff --git a/app/src/main/play/listings/es-ES/graphics/phone-screenshots/05.png b/app/src/main/play/listings/es-ES/graphics/phone-screenshots/05.png deleted file mode 100644 index 2698d5d2..00000000 Binary files a/app/src/main/play/listings/es-ES/graphics/phone-screenshots/05.png and /dev/null differ diff --git a/app/src/main/play/listings/fr-FR/graphics/phone-screenshots/00.png b/app/src/main/play/listings/fr-FR/graphics/phone-screenshots/00.png deleted file mode 100644 index c51243a7..00000000 Binary files a/app/src/main/play/listings/fr-FR/graphics/phone-screenshots/00.png and /dev/null differ diff --git a/app/src/main/play/listings/fr-FR/graphics/phone-screenshots/01.png b/app/src/main/play/listings/fr-FR/graphics/phone-screenshots/01.png deleted file mode 100644 index 58e14b54..00000000 Binary files a/app/src/main/play/listings/fr-FR/graphics/phone-screenshots/01.png and /dev/null differ diff --git a/app/src/main/play/listings/fr-FR/graphics/phone-screenshots/02.png b/app/src/main/play/listings/fr-FR/graphics/phone-screenshots/02.png deleted file mode 100644 index 16d6d2f3..00000000 Binary files a/app/src/main/play/listings/fr-FR/graphics/phone-screenshots/02.png and /dev/null differ diff --git a/app/src/main/play/listings/fr-FR/graphics/phone-screenshots/03.png b/app/src/main/play/listings/fr-FR/graphics/phone-screenshots/03.png deleted file mode 100644 index abbe2565..00000000 Binary files a/app/src/main/play/listings/fr-FR/graphics/phone-screenshots/03.png and /dev/null differ diff --git a/app/src/main/play/listings/fr-FR/graphics/phone-screenshots/04.png b/app/src/main/play/listings/fr-FR/graphics/phone-screenshots/04.png deleted file mode 100644 index 884d91a6..00000000 Binary files a/app/src/main/play/listings/fr-FR/graphics/phone-screenshots/04.png and /dev/null differ diff --git a/app/src/main/play/listings/fr-FR/graphics/phone-screenshots/05.png b/app/src/main/play/listings/fr-FR/graphics/phone-screenshots/05.png deleted file mode 100644 index 14f04a32..00000000 Binary files a/app/src/main/play/listings/fr-FR/graphics/phone-screenshots/05.png and /dev/null differ diff --git a/app/src/main/play/listings/it-IT/graphics/phone-screenshots/00.png b/app/src/main/play/listings/it-IT/graphics/phone-screenshots/00.png deleted file mode 100644 index a860cc8c..00000000 Binary files a/app/src/main/play/listings/it-IT/graphics/phone-screenshots/00.png and /dev/null differ diff --git a/app/src/main/play/listings/it-IT/graphics/phone-screenshots/01.png b/app/src/main/play/listings/it-IT/graphics/phone-screenshots/01.png deleted file mode 100644 index b28200a8..00000000 Binary files a/app/src/main/play/listings/it-IT/graphics/phone-screenshots/01.png and /dev/null differ diff --git a/app/src/main/play/listings/it-IT/graphics/phone-screenshots/02.png b/app/src/main/play/listings/it-IT/graphics/phone-screenshots/02.png deleted file mode 100644 index 5fd46f51..00000000 Binary files a/app/src/main/play/listings/it-IT/graphics/phone-screenshots/02.png and /dev/null differ diff --git a/app/src/main/play/listings/it-IT/graphics/phone-screenshots/03.png b/app/src/main/play/listings/it-IT/graphics/phone-screenshots/03.png deleted file mode 100644 index 1fcaaba6..00000000 Binary files a/app/src/main/play/listings/it-IT/graphics/phone-screenshots/03.png and /dev/null differ diff --git a/app/src/main/play/listings/it-IT/graphics/phone-screenshots/04.png b/app/src/main/play/listings/it-IT/graphics/phone-screenshots/04.png deleted file mode 100644 index f7749048..00000000 Binary files a/app/src/main/play/listings/it-IT/graphics/phone-screenshots/04.png and /dev/null differ diff --git a/app/src/main/play/listings/it-IT/graphics/phone-screenshots/05.png b/app/src/main/play/listings/it-IT/graphics/phone-screenshots/05.png deleted file mode 100644 index 14259aa8..00000000 Binary files a/app/src/main/play/listings/it-IT/graphics/phone-screenshots/05.png and /dev/null differ diff --git a/app/src/main/play/listings/nl-NL/graphics/phone-screenshots/00.png b/app/src/main/play/listings/nl-NL/graphics/phone-screenshots/00.png deleted file mode 100644 index df59503e..00000000 Binary files a/app/src/main/play/listings/nl-NL/graphics/phone-screenshots/00.png and /dev/null differ diff --git a/app/src/main/play/listings/nl-NL/graphics/phone-screenshots/01.png b/app/src/main/play/listings/nl-NL/graphics/phone-screenshots/01.png deleted file mode 100644 index c9a93bb1..00000000 Binary files a/app/src/main/play/listings/nl-NL/graphics/phone-screenshots/01.png and /dev/null differ diff --git a/app/src/main/play/listings/nl-NL/graphics/phone-screenshots/02.png b/app/src/main/play/listings/nl-NL/graphics/phone-screenshots/02.png deleted file mode 100644 index ac6f2d65..00000000 Binary files a/app/src/main/play/listings/nl-NL/graphics/phone-screenshots/02.png and /dev/null differ diff --git a/app/src/main/play/listings/nl-NL/graphics/phone-screenshots/03.png b/app/src/main/play/listings/nl-NL/graphics/phone-screenshots/03.png deleted file mode 100644 index 464efedc..00000000 Binary files a/app/src/main/play/listings/nl-NL/graphics/phone-screenshots/03.png and /dev/null differ diff --git a/app/src/main/play/listings/nl-NL/graphics/phone-screenshots/04.png b/app/src/main/play/listings/nl-NL/graphics/phone-screenshots/04.png deleted file mode 100644 index 4db1bdfa..00000000 Binary files a/app/src/main/play/listings/nl-NL/graphics/phone-screenshots/04.png and /dev/null differ diff --git a/app/src/main/play/listings/nl-NL/graphics/phone-screenshots/05.png b/app/src/main/play/listings/nl-NL/graphics/phone-screenshots/05.png deleted file mode 100644 index 95cee64e..00000000 Binary files a/app/src/main/play/listings/nl-NL/graphics/phone-screenshots/05.png and /dev/null differ diff --git a/app/src/main/res/layout/episode_info_fragment.xml b/app/src/main/res/layout/episode_info_fragment.xml index 681a8b33..67e01f2f 100644 --- a/app/src/main/res/layout/episode_info_fragment.xml +++ b/app/src/main/res/layout/episode_info_fragment.xml @@ -76,7 +76,8 @@ + android:layout_height="wrap_content" + android:orientation="horizontal"> - - + android:scrollbars="vertical"> - - - - - - + + + + + + + - diff --git a/app/src/main/res/layout/feeditemlist_item.xml b/app/src/main/res/layout/feeditemlist_item.xml index 2160bdb8..2cabcfbd 100644 --- a/app/src/main/res/layout/feeditemlist_item.xml +++ b/app/src/main/res/layout/feeditemlist_item.xml @@ -3,6 +3,7 @@ xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" + android:id="@+id/feeditemlist_item" android:layout_width="match_parent" android:layout_height="wrap_content" tools:ignore="MergeRootFrame"> @@ -80,6 +81,7 @@ android:layout_height="@dimen/thumbnail_length_queue_item" android:layout_centerVertical="true" android:importantForAccessibility="no" + android:src="@drawable/ic_launcher_foreground" tools:src="@tools:sample/avatars" /> + android:imeOptions="actionSearch" + android:background="?attr/background_color"/> + app:layout_behavior="ac.mdiq.podcini.ui.utils.LockableBottomSheetBehavior" /> diff --git a/app/src/main/res/layout/secondary_action.xml b/app/src/main/res/layout/secondary_action.xml index 66185429..c545fcd9 100644 --- a/app/src/main/res/layout/secondary_action.xml +++ b/app/src/main/res/layout/secondary_action.xml @@ -13,12 +13,12 @@ android:focusableInTouchMode="false" > + android:id="@+id/secondaryActionIcon" + android:layout_width="18dp" + android:layout_height="21dp" + android:layout_gravity="center" + tools:ignore="ContentDescription" + tools:src="@sample/secondaryaction"/> - QueueFragment - EpisodesFragment SubscriptionFragment + QueueFragment + AllEpisodesFragment + DownloadsFragment + PlaybackHistoryFragment + AddFeedFragment + StatisticsFragment remember + @string/subscriptions_label @string/queue_label @string/episodes_label - @string/subscriptions_label + @string/downloads_label + @string/playback_history_label + @string/add_feed_label + @string/statistics_label @string/remember_last_page diff --git a/app/src/test/java/ac/mdiq/podcini/storage/DbWriterTest.kt b/app/src/test/java/ac/mdiq/podcini/storage/DbWriterTest.kt index 2993313b..ba5ce4e9 100644 --- a/app/src/test/java/ac/mdiq/podcini/storage/DbWriterTest.kt +++ b/app/src/test/java/ac/mdiq/podcini/storage/DbWriterTest.kt @@ -1,14 +1,11 @@ package ac.mdiq.podcini.storage -import android.app.Application -import android.content.Context -import android.util.Log -import androidx.core.util.Consumer -import androidx.preference.PreferenceManager -import androidx.test.platform.app.InstrumentationRegistry -import ac.mdiq.podcini.util.config.ApplicationCallbacks -import ac.mdiq.podcini.util.config.ClientConfig +import ac.mdiq.podcini.net.download.serviceinterface.DownloadServiceInterface +import ac.mdiq.podcini.net.download.serviceinterface.DownloadServiceInterfaceStub import ac.mdiq.podcini.preferences.PlaybackPreferences.Companion.init +import ac.mdiq.podcini.preferences.UserPreferences +import ac.mdiq.podcini.preferences.UserPreferences.enqueueLocation +import ac.mdiq.podcini.preferences.UserPreferences.shouldDeleteRemoveFromQueue import ac.mdiq.podcini.storage.DBReader.getFeedItem import ac.mdiq.podcini.storage.DBReader.getFeedItemList import ac.mdiq.podcini.storage.DBReader.getFeedMedia @@ -20,23 +17,27 @@ import ac.mdiq.podcini.storage.DBWriter.deleteFeed import ac.mdiq.podcini.storage.DBWriter.deleteFeedItems import ac.mdiq.podcini.storage.DBWriter.deleteFeedMediaOfItem import ac.mdiq.podcini.storage.DBWriter.moveQueueItem -import ac.mdiq.podcini.storage.DBWriter.removeAllNewFlags -import ac.mdiq.podcini.storage.DBWriter.removeQueueItem import ac.mdiq.podcini.storage.DBWriter.persistFeedItem import ac.mdiq.podcini.storage.DBWriter.persistFeedMediaPlaybackInfo -import ac.mdiq.podcini.util.FeedItemUtil.getIdList -import ac.mdiq.podcini.storage.model.feed.Feed -import ac.mdiq.podcini.storage.model.feed.FeedItem -import ac.mdiq.podcini.storage.model.feed.FeedMedia -import ac.mdiq.podcini.net.download.serviceinterface.DownloadServiceInterface -import ac.mdiq.podcini.net.download.serviceinterface.DownloadServiceInterfaceStub +import ac.mdiq.podcini.storage.DBWriter.removeAllNewFlags +import ac.mdiq.podcini.storage.DBWriter.removeQueueItem import ac.mdiq.podcini.storage.database.PodDBAdapter import ac.mdiq.podcini.storage.database.PodDBAdapter.Companion.deleteDatabase import ac.mdiq.podcini.storage.database.PodDBAdapter.Companion.getInstance -import ac.mdiq.podcini.preferences.UserPreferences -import ac.mdiq.podcini.preferences.UserPreferences.enqueueLocation -import ac.mdiq.podcini.preferences.UserPreferences.shouldDeleteRemoveFromQueue +import ac.mdiq.podcini.storage.model.feed.Feed +import ac.mdiq.podcini.storage.model.feed.FeedItem +import ac.mdiq.podcini.storage.model.feed.FeedMedia +import ac.mdiq.podcini.util.FeedItemUtil.getIdList import ac.mdiq.podcini.util.Logd +import ac.mdiq.podcini.util.config.ApplicationCallbacks +import ac.mdiq.podcini.util.config.ClientConfig +import android.app.Application +import android.content.Context +import androidx.core.util.Consumer +import androidx.preference.PreferenceManager +import androidx.test.platform.app.InstrumentationRegistry +import kotlinx.coroutines.runBlocking +import kotlinx.coroutines.withTimeout import org.awaitility.Awaitility import org.junit.After import org.junit.Assert @@ -109,13 +110,19 @@ class DbWriterTest { "dummy path", "download_url", true, null, 0, 0) item.setMedia(media) - persistFeedItem(item)[TIMEOUT, TimeUnit.SECONDS] + runBlocking { + val job = persistFeedItem(item) + withTimeout(TIMEOUT*1000) { job.join() } + } media.setPosition(position) media.setLastPlayedTime(lastPlayedTime) media.playedDuration = playedDuration - persistFeedMediaPlaybackInfo(item.media)[TIMEOUT, TimeUnit.SECONDS] + runBlocking { + val job = persistFeedMediaPlaybackInfo(item.media) + withTimeout(TIMEOUT*1000) { job.join() } + } val itemFromDb = getFeedItem(item.id) val mediaFromDb = itemFromDb!!.media @@ -151,7 +158,10 @@ class DbWriterTest { Assert.assertTrue(media!!.id != 0L) Assert.assertTrue(item.id != 0L) - deleteFeedMediaOfItem(context, media.id)[TIMEOUT, TimeUnit.SECONDS] + runBlocking { + val job = deleteFeedMediaOfItem(context, media!!.id) + withTimeout(TIMEOUT*1000) { job.join() } + } media = getFeedMedia(media.id) Assert.assertNotNull(media) Assert.assertFalse(dest.exists()) @@ -236,7 +246,10 @@ class DbWriterTest { Assert.assertTrue(item.media!!.id != 0L) } - deleteFeed(context, feed.id)[TIMEOUT, TimeUnit.SECONDS] + runBlocking { + val job = deleteFeed(context, feed.id) + withTimeout(TIMEOUT*1000) { job.join() } + } // check if files still exist for (f in itemFiles) { @@ -276,7 +289,10 @@ class DbWriterTest { Assert.assertTrue(feed.id != 0L) - deleteFeed(context, feed.id)[TIMEOUT, TimeUnit.SECONDS] + runBlocking { + val job = deleteFeed(context, feed.id) + withTimeout(TIMEOUT*1000) { job.join() } + } adapter = getInstance() adapter.open() @@ -313,7 +329,10 @@ class DbWriterTest { Assert.assertTrue(item.id != 0L) } - deleteFeed(context, feed.id)[TIMEOUT, TimeUnit.SECONDS] + runBlocking { + val job = deleteFeed(context, feed.id) + withTimeout(TIMEOUT*1000) { job.join() } + } adapter = getInstance() adapter.open() @@ -369,7 +388,10 @@ class DbWriterTest { queueCursor.close() adapter.close() - deleteFeed(context, feed.id)[TIMEOUT, TimeUnit.SECONDS] + runBlocking { + val job = deleteFeed(context, feed.id) + withTimeout(TIMEOUT*1000) { job.join() } + } adapter.open() var c = adapter.getFeedCursor(feed.id) @@ -421,7 +443,10 @@ class DbWriterTest { Assert.assertTrue(item.media!!.id != 0L) } - deleteFeed(context, feed.id)[TIMEOUT, TimeUnit.SECONDS] + runBlocking { + val job = deleteFeed(context, feed.id) + withTimeout(TIMEOUT*1000) { job.join() } + } adapter = getInstance() adapter.open() @@ -459,7 +484,10 @@ class DbWriterTest { adapter.close() val itemsToDelete: List = feed.items.subList(0, 2) - deleteFeedItems(context, itemsToDelete)[TIMEOUT, TimeUnit.SECONDS] + runBlocking { + val job = deleteFeedItems(context, itemsToDelete) + withTimeout(TIMEOUT*1000) { job.join() } + } adapter = getInstance() adapter.open() @@ -493,7 +521,10 @@ class DbWriterTest { @Throws(Exception::class) fun testAddItemToPlaybackHistoryNotPlayedYet() { var media: FeedMedia? = playbackHistorySetup(null) - addItemToPlaybackHistory(media)[TIMEOUT, TimeUnit.SECONDS] + runBlocking { + val job = addItemToPlaybackHistory(media) + withTimeout(TIMEOUT*1000) { job.join() } + } val adapter = getInstance() adapter.open() media = getFeedMedia(media!!.id) @@ -509,7 +540,10 @@ class DbWriterTest { val oldDate: Long = 0 var media: FeedMedia? = playbackHistorySetup(Date(oldDate)) - addItemToPlaybackHistory(media)[TIMEOUT, TimeUnit.SECONDS] + runBlocking { + val job = addItemToPlaybackHistory(media) + withTimeout(TIMEOUT*1000) { job.join() } + } val adapter = getInstance() adapter.open() media = getFeedMedia(media!!.id) @@ -540,12 +574,13 @@ class DbWriterTest { Assert.assertTrue(item.id != 0L) } val futures: MutableList> = ArrayList() - for (item in feed.items) { - futures.add(addQueueItem(context, item)) - } - for (f in futures) { - f[TIMEOUT, TimeUnit.SECONDS] - } + // TODO: +// for (item in feed.items) { +// futures.add(addQueueItem(context, item)) +// } +// for (f in futures) { +// f[TIMEOUT, TimeUnit.SECONDS] +// } return feed } @@ -564,7 +599,10 @@ class DbWriterTest { adapter.close() Assert.assertTrue(item.id != 0L) - addQueueItem(context, item)[TIMEOUT, TimeUnit.SECONDS] + runBlocking { + val job = addQueueItem(context, item) + withTimeout(TIMEOUT*1000) { job.join() } + } adapter = getInstance() adapter.open() @@ -590,7 +628,10 @@ class DbWriterTest { adapter.close() Assert.assertTrue(item.id != 0L) - addQueueItem(context, item)[TIMEOUT, TimeUnit.SECONDS] + runBlocking { + val job = addQueueItem(context, item) + withTimeout(TIMEOUT*1000) { job.join() } + } adapter = getInstance() adapter.open() @@ -600,7 +641,10 @@ class DbWriterTest { cursor.close() adapter.close() - addQueueItem(context, item)[TIMEOUT, TimeUnit.SECONDS] + runBlocking { + val job = addQueueItem(context, item) + withTimeout(TIMEOUT*1000) { job.join() } + } adapter = getInstance() adapter.open() cursor = adapter.queueIDCursor @@ -639,7 +683,10 @@ class DbWriterTest { val numItems = 10 queueTestSetupMultipleItems(numItems) - clearQueue()[TIMEOUT, TimeUnit.SECONDS] + runBlocking { + val job = clearQueue() + withTimeout(TIMEOUT*1000) { job.join() } + } val adapter = getInstance() adapter.open() val cursor = adapter.queueIDCursor @@ -661,7 +708,10 @@ class DbWriterTest { adapter.setQueue(feed.items.toList()) adapter.close() - removeQueueItem(context, false, item)[TIMEOUT, TimeUnit.SECONDS] + runBlocking { + val job = removeQueueItem(context, false, item) + withTimeout(TIMEOUT*1000) { job.join() } + } adapter = getInstance() adapter.open() val queue = adapter.queueIDCursor @@ -697,16 +747,28 @@ class DbWriterTest { // Use array rather than List to make codes more succinct val itemIds = toItemIds(feed.items).toTypedArray() - removeQueueItem(context, false, itemIds[1], itemIds[3])[TIMEOUT, TimeUnit.SECONDS] + runBlocking { + val job = removeQueueItem(context, false, itemIds[1], itemIds[3]) + withTimeout(TIMEOUT*1000) { job.join() } + } assertQueueByItemIds("Average case - 2 items removed successfully", itemIds[0], itemIds[2]) - removeQueueItem(context, false)[TIMEOUT, TimeUnit.SECONDS] + runBlocking { + val job = removeQueueItem(context, false) + withTimeout(TIMEOUT*1000) { job.join() } + } assertQueueByItemIds("Boundary case - no items supplied. queue should see no change", itemIds[0], itemIds[2]) - removeQueueItem(context, false, itemIds[0], itemIds[4], -1L)[TIMEOUT, TimeUnit.SECONDS] + runBlocking { + val job = removeQueueItem(context, false, itemIds[0], itemIds[4], -1L) + withTimeout(TIMEOUT*1000) { job.join() } + } assertQueueByItemIds("Boundary case - items not in queue ignored", itemIds[2]) - removeQueueItem(context, false, itemIds[2], -1L)[TIMEOUT, TimeUnit.SECONDS] + runBlocking { + val job = removeQueueItem(context, false, itemIds[2], -1L) + withTimeout(TIMEOUT*1000) { job.join() } + } assertQueueByItemIds("Boundary case - invalid itemIds ignored") // the queue is empty } @@ -743,7 +805,10 @@ class DbWriterTest { adapter.setQueue(feed.items) adapter.close() - moveQueueItem(from, to, false)[TIMEOUT, TimeUnit.SECONDS] + runBlocking { + val job = moveQueueItem(from, to, false) + withTimeout(TIMEOUT*1000) { job.join() } + } adapter = getInstance() adapter.open() val queue = adapter.queueIDCursor @@ -781,7 +846,7 @@ class DbWriterTest { Assert.assertTrue(item.id != 0L) } - removeAllNewFlags().get() + runBlocking { removeAllNewFlags().join() } val loadedItems = getFeedItemList(feed) for (item in loadedItems) { Assert.assertFalse(item.isNew) diff --git a/changelog.md b/changelog.md index 25d396ef..db7b72ee 100644 --- a/changelog.md +++ b/changelog.md @@ -1,3 +1,13 @@ + +## 5.4.0 + +* replaced thread with coroutines in DBWrite +* reduced startup lags of the app, various views, and media playing +* tidied up preferences accesses +* updated SwipeActions to the new DefaultLifecycleObserver +* added more options for default page +* added link info in episode info view + ## 5.3.1 * fixed crash issue on some device when attempting to remove item from queue multiple times diff --git a/fastlane/metadata/android/en-US/changelogs/3020147.txt b/fastlane/metadata/android/en-US/changelogs/3020147.txt new file mode 100644 index 00000000..43f39e40 --- /dev/null +++ b/fastlane/metadata/android/en-US/changelogs/3020147.txt @@ -0,0 +1,9 @@ + +Version 5.4.0 brings several changes: + +* replaced thread with coroutines in DBWrite +* reduced startup lags of the app, various views, and media playing +* tidied up preferences accesses +* updated SwipeActions to the new DefaultLifecycleObserver +* added more options for default page +* added link info in episode info view