From dccc54b674a634288a181d0bc12a53cd2384f99d Mon Sep 17 00:00:00 2001 From: Xilin Jia <6257601+XilinJia@users.noreply.github.com> Date: Sun, 3 Nov 2024 13:02:29 +0100 Subject: [PATCH] 6.13.3 commit --- .github/workflows/runEmulatorTests.sh | 11 ---- CONTRIBUTING.md | 19 ------- app/build.gradle | 4 +- .../main/assets/LICENSE_ANDROID_ICONIFY.txt | 18 ------ app/src/main/assets/licenses.xml | 2 +- .../main/kotlin/ac/mdiq/podcini/PodciniApp.kt | 4 +- .../service/DownloadServiceInterfaceImpl.kt | 11 ++-- .../podcini/net/feed/FeedUpdateManager.kt | 10 ++-- .../mdiq/podcini/net/feed/LocalFeedUpdater.kt | 10 ++-- .../ac/mdiq/podcini/net/sync/SyncService.kt | 13 ++--- .../podcini/net/sync/wifi/WifiSyncService.kt | 12 ++-- .../playback/PlaybackServiceStarter.kt | 4 +- .../podcini/playback/ServiceStatusHandler.kt | 4 +- .../mdiq/podcini/playback/base/InTheatre.kt | 8 +-- .../podcini/playback/base/LocalMediaPlayer.kt | 8 ++- .../playback/service/PlaybackService.kt | 8 +-- .../service/QuickSettingsTileService.kt | 4 +- .../podcini/preferences/OpmlBackupAgent.kt | 4 +- .../podcini/preferences/PreferenceUpgrader.kt | 4 +- .../ImportExportPreferencesFragment.kt | 4 +- .../fragments/PlaybackPreferencesFragment.kt | 11 ++-- .../fragments/SwipePreferencesFragment.kt | 12 ++-- .../SynchronizationPreferencesFragment.kt | 10 ++-- .../receiver/ConnectivityActionReceiver.kt | 4 +- .../podcini/receiver/FeedUpdateReceiver.kt | 4 +- .../podcini/receiver/MediaButtonReceiver.kt | 4 +- .../receiver/PowerConnectionReceiver.kt | 4 +- .../ac/mdiq/podcini/receiver/SPAReceiver.kt | 4 +- .../storage/algorithms/AutoCleanups.kt | 8 +-- .../storage/algorithms/AutoDownloads.kt | 8 +-- .../mdiq/podcini/storage/database/Episodes.kt | 8 +-- .../ac/mdiq/podcini/storage/database/Feeds.kt | 28 +++------ .../mdiq/podcini/storage/database/Queues.kt | 30 +--------- .../podcini/ui/actions/EpisodeActionButton.kt | 22 ++++--- .../ac/mdiq/podcini/ui/actions/SwipeAction.kt | 1 + .../mdiq/podcini/ui/actions/SwipeActions.kt | 57 +++++++++++++++---- .../mdiq/podcini/ui/activity/MainActivity.kt | 8 +-- .../podcini/ui/activity/OpmlImportActivity.kt | 4 +- .../ui/activity/SelectSubscriptionActivity.kt | 8 +-- .../ui/activity/ShareReceiverActivity.kt | 6 +- .../podcini/ui/activity/SplashActivity.kt | 4 +- .../ui/activity/VideoplayerActivity.kt | 38 ++++++------- .../starter/VideoPlayerActivityStarter.kt | 5 +- .../ac/mdiq/podcini/ui/compose/EpisodesVM.kt | 4 +- .../ac/mdiq/podcini/ui/compose/Feeds.kt | 6 +- .../podcini/ui/dialog/CustomFeedNameDialog.kt | 5 +- .../ui/dialog/MediaPlayerErrorDialog.kt | 8 +-- .../podcini/ui/dialog/SleepTimerDialog.kt | 8 +-- .../podcini/ui/dialog/TagSettingsDialog.kt | 4 +- .../podcini/ui/dialog/VariableSpeedDialog.kt | 15 +++-- .../ui/fragment/AllEpisodesFragment.kt | 8 +-- .../ui/fragment/AudioPlayerFragment.kt | 19 +++---- .../ui/fragment/BaseEpisodesFragment.kt | 8 +-- .../podcini/ui/fragment/DownloadsFragment.kt | 8 +-- .../ui/fragment/EpisodeInfoFragment.kt | 37 ++++++------ .../ui/fragment/FeedEpisodesFragment.kt | 39 ++++++------- .../podcini/ui/fragment/FeedInfoFragment.kt | 14 ++--- .../ui/fragment/FeedSettingsFragment.kt | 6 +- .../podcini/ui/fragment/HistoryFragment.kt | 8 +-- .../podcini/ui/fragment/OnlineFeedFragment.kt | 22 ++++--- .../ui/fragment/OnlineSearchFragment.kt | 8 +-- .../podcini/ui/fragment/QueuesFragment.kt | 16 +++--- .../ui/fragment/QuickDiscoveryFragment.kt | 8 +-- .../podcini/ui/fragment/SearchFragment.kt | 16 +++--- .../ui/fragment/SearchResultsFragment.kt | 4 +- .../ui/fragment/SubscriptionsFragment.kt | 15 ++--- .../ui/statistics/StatisticsFragment.kt | 14 ++--- .../mdiq/podcini/ui/view/ShownotesWebView.kt | 4 +- .../podcini/util/config/ClientConfigurator.kt | 4 +- .../main/res/drawable/baseline_comment_24.xml | 5 ++ app/src/main/res/values/strings.xml | 1 + changelog.md | 10 ++++ .../android/en-US/changelogs/3020289.txt | 2 +- .../android/en-US/changelogs/3020290.txt | 9 +++ gradle/libs.versions.toml | 22 +++---- 75 files changed, 361 insertions(+), 446 deletions(-) delete mode 100644 .github/workflows/runEmulatorTests.sh delete mode 100644 app/src/main/assets/LICENSE_ANDROID_ICONIFY.txt create mode 100644 app/src/main/res/drawable/baseline_comment_24.xml create mode 100644 fastlane/metadata/android/en-US/changelogs/3020290.txt diff --git a/.github/workflows/runEmulatorTests.sh b/.github/workflows/runEmulatorTests.sh deleted file mode 100644 index 4d048a33..00000000 --- a/.github/workflows/runEmulatorTests.sh +++ /dev/null @@ -1,11 +0,0 @@ -#!/bin/zsh - -set -o pipefail - -runTests() { - ./gradlew connectedPlayDebugAndroidTest connectedDebugAndroidTest \ - -Pandroid.testInstrumentationRunnerArguments.notAnnotation=de.test.podcini.IgnoreOnCi -} - -# Retry tests to make them less flaky -runTests || runTests || runTests diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 814c03c4..82fa7905 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -53,22 +53,3 @@ Testing and Verifying -------------------------- As a developer contributing to Podcini, we ask that you test the feature yourself manually and better yet, add unit and functional tests to any feature of bug you fix. - -### Running Unit Tests - -- `./gradlew :core:testPlayDebugUnitTest` - -### Running Integration Tests - -#### Using Android Studio - -- Create a configuration via 'Run->Edit Configurations...' - -podcini-run-tests - -#### Using the command line - -- Start an AVD or plug in your phone - -- `sh .github/workflows/runTests.sh` diff --git a/app/build.gradle b/app/build.gradle index 78166d4d..1fee2d96 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -31,8 +31,8 @@ android { // testApplicationId "ac.mdiq.podcini.tests" // testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" - versionCode 3020289 - versionName "6.13.2" + versionCode 3020290 + versionName "6.13.3" applicationId "ac.mdiq.podcini.R" def commit = "" diff --git a/app/src/main/assets/LICENSE_ANDROID_ICONIFY.txt b/app/src/main/assets/LICENSE_ANDROID_ICONIFY.txt deleted file mode 100644 index 954402c9..00000000 --- a/app/src/main/assets/LICENSE_ANDROID_ICONIFY.txt +++ /dev/null @@ -1,18 +0,0 @@ -Copyright 2015 Joan Zapata - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. - -It uses FontAwesome font, licensed under OFL 1.1, which is compatible -with this library's license. - - http://scripts.sil.org/cms/scripts/render_download.php?format=file&media_id=OFL_plaintext&filename=OFL.txt diff --git a/app/src/main/assets/licenses.xml b/app/src/main/assets/licenses.xml index cc12699b..92f44463 100644 --- a/app/src/main/assets/licenses.xml +++ b/app/src/main/assets/licenses.xml @@ -65,7 +65,7 @@ author="Mike Penz and Peter Gulko" website="https://github.com/mikepenz/Android-Iconics" license="Apache 2.0" - licenseText="LICENSE_ANDROID_ICONIFY.txt" /> + licenseText="LICENSE_APACHE-2.0.txt" /> = 2 - @UnstableApi + override fun doWork(): Result { Logd(TAG, "starting doWork") ClientConfigurator.initialize(applicationContext) @@ -163,7 +160,7 @@ class DownloadServiceInterfaceImpl : DownloadServiceInterface() { override fun getForegroundInfoAsync(): ListenableFuture { return Futures.immediateFuture(ForegroundInfo(R.id.notification_downloading, generateProgressNotification())) } - @OptIn(UnstableApi::class) + private fun performDownload(media: EpisodeMedia, request: DownloadRequest): Result { Logd(TAG, "starting performDownload") if (request.destination == null) { @@ -295,7 +292,7 @@ class DownloadServiceInterfaceImpl : DownloadServiceInterface() { } class MediaDownloadedHandler(private val context: Context, var updatedStatus: DownloadResult, private val request: DownloadRequest) : Runnable { - @UnstableApi override fun run() { + override fun run() { var item = realm.query(Episode::class).query("id == ${request.feedfileId}").first().find() if (item == null) { Log.e(TAG, "Could not find downloaded episode object in database") @@ -356,7 +353,7 @@ class DownloadServiceInterfaceImpl : DownloadServiceInterface() { return constraints.build() } - @OptIn(UnstableApi::class) + private fun getRequest(item: Episode): OneTimeWorkRequest.Builder { Logd(TAG, "starting getRequest") val workRequest: OneTimeWorkRequest.Builder = OneTimeWorkRequest.Builder(EpisodeDownloadWorker::class.java) diff --git a/app/src/main/kotlin/ac/mdiq/podcini/net/feed/FeedUpdateManager.kt b/app/src/main/kotlin/ac/mdiq/podcini/net/feed/FeedUpdateManager.kt index 53486775..58001203 100644 --- a/app/src/main/kotlin/ac/mdiq/podcini/net/feed/FeedUpdateManager.kt +++ b/app/src/main/kotlin/ac/mdiq/podcini/net/feed/FeedUpdateManager.kt @@ -39,11 +39,9 @@ import android.content.DialogInterface import android.content.pm.PackageManager import android.os.Build import android.util.Log -import androidx.annotation.OptIn import androidx.core.app.ActivityCompat import androidx.core.app.NotificationCompat import androidx.core.app.NotificationManagerCompat -import androidx.media3.common.util.UnstableApi import androidx.work.* import androidx.work.Constraints.Builder import com.google.android.material.dialog.MaterialAlertDialogBuilder @@ -139,11 +137,11 @@ object FeedUpdateManager { builder.show() } - @OptIn(UnstableApi::class) + class FeedUpdateWorker(context: Context, params: WorkerParameters) : Worker(context, params) { private val notificationManager = NotificationManagerCompat.from(context) - @UnstableApi + override fun doWork(): Result { ClientConfigurator.initialize(applicationContext) val feedsToUpdate: MutableList @@ -200,7 +198,7 @@ object FeedUpdateManager { override fun getForegroundInfoAsync(): ListenableFuture { return Futures.immediateFuture(ForegroundInfo(R.id.notification_updating_feeds, createNotification(null))) } - @UnstableApi + private fun refreshFeeds(feedsToUpdate: MutableList, force: Boolean) { if (Build.VERSION.SDK_INT >= 33 && ActivityCompat.checkSelfPermission(this.applicationContext, Manifest.permission.POST_NOTIFICATIONS) != PackageManager.PERMISSION_GRANTED) { @@ -264,7 +262,7 @@ object FeedUpdateManager { } catch (e: Throwable) { Logd(TAG, "refreshYoutubeFeed error1 ${e.message}") } } catch (e: Throwable) { Logd(TAG, "refreshYoutubeFeed error ${e.message}") } } - @UnstableApi + @Throws(Exception::class) fun refreshFeed(feed: Feed, force: Boolean) { val nextPage = (inputData.getBoolean(EXTRA_NEXT_PAGE, false) && feed.nextPageLink != null) diff --git a/app/src/main/kotlin/ac/mdiq/podcini/net/feed/LocalFeedUpdater.kt b/app/src/main/kotlin/ac/mdiq/podcini/net/feed/LocalFeedUpdater.kt index aa89b6c7..f7b2991b 100644 --- a/app/src/main/kotlin/ac/mdiq/podcini/net/feed/LocalFeedUpdater.kt +++ b/app/src/main/kotlin/ac/mdiq/podcini/net/feed/LocalFeedUpdater.kt @@ -19,7 +19,7 @@ import android.net.Uri import android.provider.DocumentsContract import androidx.annotation.VisibleForTesting import androidx.documentfile.provider.DocumentFile -import androidx.media3.common.util.UnstableApi + import org.apache.commons.io.input.CountingInputStream import java.io.BufferedInputStream import java.io.IOException @@ -33,7 +33,7 @@ object LocalFeedUpdater { @JvmField val PREFERRED_FEED_IMAGE_FILENAMES: Array = arrayOf("folder.jpg", "Folder.jpg", "folder.png", "Folder.png") - @UnstableApi @JvmStatic + @JvmStatic fun updateFeed(feed: Feed, context: Context, updaterProgressListener: UpdaterProgressListener?) { if (feed.downloadUrl.isNullOrEmpty()) return try { @@ -51,7 +51,7 @@ object LocalFeedUpdater { } } - @UnstableApi @JvmStatic + @JvmStatic @VisibleForTesting @Throws(IOException::class) fun tryUpdateFeed(feed: Feed, context: Context, folderUri: Uri?, updaterProgressListener: UpdaterProgressListener?) { @@ -188,7 +188,7 @@ object LocalFeedUpdater { } } - @UnstableApi private fun reportError(feed: Feed, reasonDetailed: String?) { + private fun reportError(feed: Feed, reasonDetailed: String?) { val status = DownloadResult(feed.id, feed.title?:"", DownloadError.ERROR_IO_ERROR, false, reasonDetailed?:"") LogsAndStats.addDownloadStatus(status) Feeds.persistFeedLastUpdateFailed(feed, true) @@ -197,7 +197,7 @@ object LocalFeedUpdater { /** * Reports a successful download status. */ - @UnstableApi private fun reportSuccess(feed: Feed) { + private fun reportSuccess(feed: Feed) { val status = DownloadResult(feed.id, feed.title?:"", DownloadError.SUCCESS, true, "") LogsAndStats.addDownloadStatus(status) Feeds.persistFeedLastUpdateFailed(feed, false) diff --git a/app/src/main/kotlin/ac/mdiq/podcini/net/sync/SyncService.kt b/app/src/main/kotlin/ac/mdiq/podcini/net/sync/SyncService.kt index be097f52..bfb432b5 100644 --- a/app/src/main/kotlin/ac/mdiq/podcini/net/sync/SyncService.kt +++ b/app/src/main/kotlin/ac/mdiq/podcini/net/sync/SyncService.kt @@ -44,10 +44,8 @@ import android.app.PendingIntent import android.content.Context import android.os.Build import android.util.Log -import androidx.annotation.OptIn import androidx.collection.ArrayMap import androidx.core.app.NotificationCompat -import androidx.media3.common.util.UnstableApi import androidx.work.* import androidx.work.Constraints.Builder import kotlinx.coroutines.* @@ -56,13 +54,13 @@ import org.apache.commons.lang3.StringUtils import java.util.concurrent.ExecutionException import java.util.concurrent.TimeUnit -@OptIn(UnstableApi::class) + open class SyncService(context: Context, params: WorkerParameters) : Worker(context, params) { val TAG = this::class.simpleName ?: "Anonymous" protected val synchronizationQueueStorage = SynchronizationQueueStorage(context) - @UnstableApi override fun doWork(): Result { + override fun doWork(): Result { Logd(TAG, "doWork() called") val activeSyncProvider = getActiveSyncProvider() ?: return Result.failure() Logd(TAG, "doWork() got syn provider") @@ -104,7 +102,7 @@ open class SyncService(context: Context, params: WorkerParameters) : Worker(cont } } - @UnstableApi @Throws(SyncServiceException::class) + @Throws(SyncServiceException::class) private fun syncSubscriptions(syncServiceImpl: ISyncService) { Logd(TAG, "syncSubscriptions called") val lastSync = SynchronizationSettings.lastSubscriptionSynchronizationTimestamp @@ -160,7 +158,6 @@ open class SyncService(context: Context, params: WorkerParameters) : Worker(cont SynchronizationSettings.setLastSubscriptionSynchronizationAttemptTimestamp(newTimeStamp) } - @UnstableApi private fun removeFeedWithDownloadUrl(context: Context, downloadUrl: String) { Logd(TAG, "removeFeedWithDownloadUrl called") var feedID: Long? = null @@ -244,7 +241,7 @@ open class SyncService(context: Context, params: WorkerParameters) : Worker(cont return newTimeStamp } - @UnstableApi @Throws(SyncServiceException::class) + @Throws(SyncServiceException::class) private fun syncEpisodeActions(syncServiceImpl: ISyncService) { Logd(TAG, "syncEpisodeActions called") var (lastSync, newTimeStamp) = getEpisodeActions(syncServiceImpl) @@ -277,7 +274,7 @@ open class SyncService(context: Context, params: WorkerParameters) : Worker(cont return if (idRemove != null) Pair(idRemove, feedItem) else null } - @UnstableApi @Synchronized + @Synchronized fun processEpisodeActions(remoteActions: List) { Logd(TAG, "Processing " + remoteActions.size + " actions") if (remoteActions.isEmpty()) return diff --git a/app/src/main/kotlin/ac/mdiq/podcini/net/sync/wifi/WifiSyncService.kt b/app/src/main/kotlin/ac/mdiq/podcini/net/sync/wifi/WifiSyncService.kt index aa9c3872..9f5a2670 100644 --- a/app/src/main/kotlin/ac/mdiq/podcini/net/sync/wifi/WifiSyncService.kt +++ b/app/src/main/kotlin/ac/mdiq/podcini/net/sync/wifi/WifiSyncService.kt @@ -12,17 +12,15 @@ import ac.mdiq.podcini.storage.database.Episodes.getEpisodes import ac.mdiq.podcini.storage.database.RealmDB.upsertBlk import ac.mdiq.podcini.storage.model.Episode import ac.mdiq.podcini.storage.model.EpisodeFilter -import ac.mdiq.podcini.storage.utils.EpisodeUtil.hasAlmostEnded import ac.mdiq.podcini.storage.model.EpisodeSortOrder import ac.mdiq.podcini.storage.model.Rating -import ac.mdiq.podcini.util.Logd +import ac.mdiq.podcini.storage.utils.EpisodeUtil.hasAlmostEnded import ac.mdiq.podcini.util.EventFlow import ac.mdiq.podcini.util.FlowEvent +import ac.mdiq.podcini.util.Logd import android.content.Context import android.util.Log -import androidx.annotation.OptIn import androidx.core.content.ContextCompat.getString -import androidx.media3.common.util.UnstableApi import androidx.work.* import org.apache.commons.lang3.StringUtils import org.json.JSONArray @@ -34,7 +32,7 @@ import java.util.* import java.util.concurrent.TimeUnit import kotlin.math.min -@UnstableApi class WifiSyncService(val context: Context, params: WorkerParameters) : SyncService(context, params), ISyncService { +class WifiSyncService(val context: Context, params: WorkerParameters) : SyncService(context, params), ISyncService { // val TAG = this::class.simpleName ?: "Anonymous" @@ -109,7 +107,7 @@ import kotlin.math.min private var socket: Socket? = null - @OptIn(UnstableApi::class) override fun login() { + override fun login() { Logd(TAG, "serverIp: $hostIp serverPort: $hostPort $isGuest") EventFlow.postEvent(FlowEvent.SyncServiceEvent(R.string.sync_status_in_progress, "2")) if (!isPortInUse(hostPort)) { @@ -164,7 +162,7 @@ import kotlin.math.min EventFlow.postEvent(FlowEvent.SyncServiceEvent(R.string.sync_status_in_progress, "5")) } - @OptIn(UnstableApi::class) private fun isPortInUse(port: Int): Boolean { + private fun isPortInUse(port: Int): Boolean { val command = "netstat -tlnp" val process = Runtime.getRuntime().exec(command) val output = process.inputStream.bufferedReader().use { it.readText() } diff --git a/app/src/main/kotlin/ac/mdiq/podcini/playback/PlaybackServiceStarter.kt b/app/src/main/kotlin/ac/mdiq/podcini/playback/PlaybackServiceStarter.kt index 3483229e..ebef792a 100644 --- a/app/src/main/kotlin/ac/mdiq/podcini/playback/PlaybackServiceStarter.kt +++ b/app/src/main/kotlin/ac/mdiq/podcini/playback/PlaybackServiceStarter.kt @@ -3,7 +3,7 @@ package ac.mdiq.podcini.playback import android.content.Context import android.content.Intent import androidx.core.content.ContextCompat -import androidx.media3.common.util.UnstableApi + import ac.mdiq.podcini.playback.service.PlaybackService import ac.mdiq.podcini.storage.model.EpisodeMedia import ac.mdiq.podcini.playback.base.InTheatre.curEpisode @@ -14,7 +14,7 @@ import ac.mdiq.podcini.util.Logd import ac.mdiq.podcini.util.EventFlow import ac.mdiq.podcini.util.FlowEvent -@UnstableApi + class PlaybackServiceStarter(private val context: Context, private val media: Playable) { private var shouldStreamThisTime = false diff --git a/app/src/main/kotlin/ac/mdiq/podcini/playback/ServiceStatusHandler.kt b/app/src/main/kotlin/ac/mdiq/podcini/playback/ServiceStatusHandler.kt index 1f485b40..2797500e 100644 --- a/app/src/main/kotlin/ac/mdiq/podcini/playback/ServiceStatusHandler.kt +++ b/app/src/main/kotlin/ac/mdiq/podcini/playback/ServiceStatusHandler.kt @@ -18,7 +18,7 @@ import android.os.Build import android.util.Log import androidx.fragment.app.FragmentActivity import androidx.lifecycle.lifecycleScope -import androidx.media3.common.util.UnstableApi + import kotlinx.coroutines.Job import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.launch @@ -27,7 +27,7 @@ import kotlinx.coroutines.launch * Communicates with the playback service. GUI classes should use this class to * control playback instead of communicating with the PlaybackService directly. */ -@UnstableApi + abstract class ServiceStatusHandler(private val activity: FragmentActivity) { private var mediaInfoLoaded = false diff --git a/app/src/main/kotlin/ac/mdiq/podcini/playback/base/InTheatre.kt b/app/src/main/kotlin/ac/mdiq/podcini/playback/base/InTheatre.kt index 365c306f..b803a29c 100644 --- a/app/src/main/kotlin/ac/mdiq/podcini/playback/base/InTheatre.kt +++ b/app/src/main/kotlin/ac/mdiq/podcini/playback/base/InTheatre.kt @@ -11,10 +11,10 @@ import ac.mdiq.podcini.storage.model.CurrentState.Companion.NO_MEDIA_PLAYING import ac.mdiq.podcini.storage.model.CurrentState.Companion.PLAYER_STATUS_OTHER import ac.mdiq.podcini.util.Logd import android.util.Log -import androidx.annotation.OptIn -import androidx.media3.common.util.UnstableApi import io.realm.kotlin.query.Sort -import kotlinx.coroutines.* +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch object InTheatre { val TAG: String = InTheatre::class.simpleName ?: "Anonymous" @@ -139,7 +139,7 @@ object InTheatre { } } - @OptIn(UnstableApi::class) @JvmStatic + @JvmStatic fun isCurrentlyPlaying(media: EpisodeMedia?): Boolean { return isCurMedia(media) && PlaybackService.isRunning && MediaPlayerBase.status == PlayerStatus.PLAYING } diff --git a/app/src/main/kotlin/ac/mdiq/podcini/playback/base/LocalMediaPlayer.kt b/app/src/main/kotlin/ac/mdiq/podcini/playback/base/LocalMediaPlayer.kt index 982c8359..79ce5b48 100644 --- a/app/src/main/kotlin/ac/mdiq/podcini/playback/base/LocalMediaPlayer.kt +++ b/app/src/main/kotlin/ac/mdiq/podcini/playback/base/LocalMediaPlayer.kt @@ -12,6 +12,7 @@ import ac.mdiq.podcini.playback.base.InTheatre.curQueue import ac.mdiq.podcini.preferences.UserPreferences.isSkipSilence import ac.mdiq.podcini.preferences.UserPreferences.prefLowQualityMedia import ac.mdiq.podcini.preferences.UserPreferences.rewindSecs +import ac.mdiq.podcini.storage.database.Episodes.setPlayStateSync import ac.mdiq.podcini.storage.database.RealmDB.runOnIOScope import ac.mdiq.podcini.storage.model.* import ac.mdiq.podcini.storage.utils.EpisodeUtil @@ -324,7 +325,8 @@ class LocalMediaPlayer(context: Context, callback: MediaPlayerCallback) : MediaP curMedia = playable if (curMedia is EpisodeMedia) { val media_ = curMedia as EpisodeMedia - val item = media_.episodeOrFetch() + var item = media_.episodeOrFetch() + if (item != null && item.playState < PlayState.INPROGRESS.code) item = runBlocking { setPlayStateSync(PlayState.INPROGRESS.code, item!!, false) } val eList = if (item?.feed?.preferences?.queue != null) curQueue.episodes else item?.feed?.getVirtualQueueItems() ?: listOf() curIndexInQueue = EpisodeUtil.indexOfItemWithId(eList, media_.id) } else curIndexInQueue = -1 @@ -710,7 +712,7 @@ class LocalMediaPlayer(context: Context, callback: MediaPlayerCallback) : MediaP } private fun setupPlayerListener() { - exoplayerListener = object : Player.Listener { + exoplayerListener = object : Listener { override fun onPlaybackStateChanged(playbackState: @State Int) { Logd(TAG, "onPlaybackStateChanged $playbackState") when (playbackState) { @@ -762,7 +764,7 @@ class LocalMediaPlayer(context: Context, callback: MediaPlayerCallback) : MediaP var exoPlayer: ExoPlayer? = null - private var exoplayerListener: Player.Listener? = null + private var exoplayerListener: Listener? = null private var audioSeekCompleteListener: java.lang.Runnable? = null private var audioCompletionListener: java.lang.Runnable? = null private var audioErrorListener: Consumer? = null diff --git a/app/src/main/kotlin/ac/mdiq/podcini/playback/service/PlaybackService.kt b/app/src/main/kotlin/ac/mdiq/podcini/playback/service/PlaybackService.kt index 93d734a5..69add402 100644 --- a/app/src/main/kotlin/ac/mdiq/podcini/playback/service/PlaybackService.kt +++ b/app/src/main/kotlin/ac/mdiq/podcini/playback/service/PlaybackService.kt @@ -94,7 +94,7 @@ import androidx.media3.common.MediaItem import androidx.media3.common.MediaMetadata import androidx.media3.common.Player.STATE_ENDED import androidx.media3.common.Player.STATE_IDLE -import androidx.media3.common.util.UnstableApi + import androidx.media3.session.* import androidx.work.impl.utils.futures.SettableFuture import com.google.common.collect.ImmutableList @@ -113,7 +113,7 @@ import kotlin.math.sqrt /** * Controls the MediaPlayer that plays a EpisodeMedia-file */ -@UnstableApi + class PlaybackService : MediaLibraryService() { private var mediaSession: MediaLibrarySession? = null @@ -376,7 +376,7 @@ class PlaybackService : MediaLibraryService() { val isItemdeletable = (!shouldKeepSuperEpisode || (item?.isSUPER != true && item?.playState != PlayState.AGAIN.code && item?.playState != PlayState.FOREVER.code)) if (playable is EpisodeMedia && shouldAutoDelete && isItemdeletable) { if (playable.localFileAvailable()) item = deleteMediaSync(this@PlaybackService, item!!) - if (prefDeleteRemovesFromQueue) removeFromQueueSync(null, item!!) + if (prefDeleteRemovesFromQueue) removeFromAllQueuesSync( item!!) } else if (prefRemoveFromQueueMarkedPlayed) removeFromAllQueuesSync(item!!) } } @@ -1247,7 +1247,7 @@ class PlaybackService : MediaLibraryService() { ), } - @UnstableApi + class CustomMediaNotificationProvider(context: Context) : DefaultMediaNotificationProvider(context) { override fun addNotificationActions(mediaSession: MediaSession, mediaButtons: ImmutableList, builder: NotificationCompat.Builder, actionFactory: MediaNotification.ActionFactory): IntArray { diff --git a/app/src/main/kotlin/ac/mdiq/podcini/playback/service/QuickSettingsTileService.kt b/app/src/main/kotlin/ac/mdiq/podcini/playback/service/QuickSettingsTileService.kt index b7ae8031..277d270f 100644 --- a/app/src/main/kotlin/ac/mdiq/podcini/playback/service/QuickSettingsTileService.kt +++ b/app/src/main/kotlin/ac/mdiq/podcini/playback/service/QuickSettingsTileService.kt @@ -12,9 +12,9 @@ import android.service.quicksettings.Tile import android.service.quicksettings.TileService import android.view.KeyEvent import androidx.annotation.RequiresApi -import androidx.media3.common.util.UnstableApi -@UnstableApi + + @RequiresApi(api = Build.VERSION_CODES.N) class QuickSettingsTileService : TileService() { override fun onTileAdded() { diff --git a/app/src/main/kotlin/ac/mdiq/podcini/preferences/OpmlBackupAgent.kt b/app/src/main/kotlin/ac/mdiq/podcini/preferences/OpmlBackupAgent.kt index ec27d996..cf9806dc 100644 --- a/app/src/main/kotlin/ac/mdiq/podcini/preferences/OpmlBackupAgent.kt +++ b/app/src/main/kotlin/ac/mdiq/podcini/preferences/OpmlBackupAgent.kt @@ -16,8 +16,6 @@ import android.content.Context import android.os.ParcelFileDescriptor import android.util.Log import android.widget.Toast -import androidx.annotation.OptIn -import androidx.media3.common.util.UnstableApi import androidx.preference.PreferenceManager import org.apache.commons.io.IOUtils import org.xmlpull.v1.XmlPullParserException @@ -95,7 +93,7 @@ class OpmlBackupAgent : BackupAgentHelper() { IOUtils.closeQuietly(writer) } } - @OptIn(UnstableApi::class) + override fun restoreEntity(data: BackupDataInputStream) { Logd(TAG, "Backup restore") if (OPML_ENTITY_KEY != data.key) { diff --git a/app/src/main/kotlin/ac/mdiq/podcini/preferences/PreferenceUpgrader.kt b/app/src/main/kotlin/ac/mdiq/podcini/preferences/PreferenceUpgrader.kt index 5f1ffc7e..0a787539 100644 --- a/app/src/main/kotlin/ac/mdiq/podcini/preferences/PreferenceUpgrader.kt +++ b/app/src/main/kotlin/ac/mdiq/podcini/preferences/PreferenceUpgrader.kt @@ -4,8 +4,6 @@ import ac.mdiq.podcini.BuildConfig import ac.mdiq.podcini.util.error.CrashReportWriter.Companion.file import android.content.Context import android.content.SharedPreferences -import androidx.annotation.OptIn -import androidx.media3.common.util.UnstableApi import androidx.preference.PreferenceManager object PreferenceUpgrader { @@ -28,7 +26,7 @@ object PreferenceUpgrader { } } - @OptIn(UnstableApi::class) private fun upgrade(oldVersion: Int, context: Context) { + private fun upgrade(oldVersion: Int, context: Context) { //New installation if (oldVersion == -1) return } diff --git a/app/src/main/kotlin/ac/mdiq/podcini/preferences/fragments/ImportExportPreferencesFragment.kt b/app/src/main/kotlin/ac/mdiq/podcini/preferences/fragments/ImportExportPreferencesFragment.kt index 1cd226e1..f41fa22d 100644 --- a/app/src/main/kotlin/ac/mdiq/podcini/preferences/fragments/ImportExportPreferencesFragment.kt +++ b/app/src/main/kotlin/ac/mdiq/podcini/preferences/fragments/ImportExportPreferencesFragment.kt @@ -36,13 +36,11 @@ import androidx.activity.result.ActivityResult import androidx.activity.result.ActivityResultLauncher import androidx.activity.result.contract.ActivityResultContracts import androidx.activity.result.contract.ActivityResultContracts.CreateDocument -import androidx.annotation.OptIn import androidx.annotation.StringRes import androidx.core.app.ShareCompat.IntentBuilder import androidx.core.content.FileProvider import androidx.documentfile.provider.DocumentFile import androidx.lifecycle.lifecycleScope -import androidx.media3.common.util.UnstableApi import androidx.preference.Preference import androidx.preference.PreferenceFragmentCompat import com.google.android.material.dialog.MaterialAlertDialogBuilder @@ -912,7 +910,7 @@ class ImportExportPreferencesFragment : PreferenceFragmentCompat() { /** Reads OPML documents. */ object EpisodeProgressReader { private const val TAG = "EpisodeProgressReader" - @OptIn(UnstableApi::class) + fun readDocument(reader: Reader) { val jsonString = reader.readText() val jsonArray = JSONArray(jsonString) diff --git a/app/src/main/kotlin/ac/mdiq/podcini/preferences/fragments/PlaybackPreferencesFragment.kt b/app/src/main/kotlin/ac/mdiq/podcini/preferences/fragments/PlaybackPreferencesFragment.kt index 010aaba5..0f37fa2a 100644 --- a/app/src/main/kotlin/ac/mdiq/podcini/preferences/fragments/PlaybackPreferencesFragment.kt +++ b/app/src/main/kotlin/ac/mdiq/podcini/preferences/fragments/PlaybackPreferencesFragment.kt @@ -10,7 +10,8 @@ import ac.mdiq.podcini.preferences.UserPreferences.setVideoMode import ac.mdiq.podcini.preferences.UserPreferences.speedforwardSpeed import ac.mdiq.podcini.preferences.UserPreferences.videoPlayMode import ac.mdiq.podcini.ui.activity.PreferenceActivity -import ac.mdiq.podcini.ui.dialog.* +import ac.mdiq.podcini.ui.dialog.SkipPreferenceDialog +import ac.mdiq.podcini.ui.dialog.VariableSpeedDialog import ac.mdiq.podcini.util.EventFlow import ac.mdiq.podcini.util.FlowEvent import android.app.Activity @@ -21,9 +22,7 @@ import android.os.Bundle import android.text.Editable import android.text.InputType import android.view.LayoutInflater -import androidx.annotation.OptIn import androidx.collection.ArrayMap -import androidx.media3.common.util.UnstableApi import androidx.preference.ListPreference import androidx.preference.Preference import androidx.preference.PreferenceFragmentCompat @@ -44,7 +43,7 @@ class PlaybackPreferencesFragment : PreferenceFragmentCompat() { (activity as PreferenceActivity).supportActionBar!!.setTitle(R.string.playback_pref) } - @OptIn(UnstableApi::class) + private fun setupPlaybackScreen() { findPreference(Prefs.prefPlaybackSpeedLauncher.name)!!.onPreferenceClickListener = Preference.OnPreferenceClickListener { VariableSpeedDialog.newInstance(booleanArrayOf(false, false, true),2)?.show(childFragmentManager, null) @@ -133,7 +132,7 @@ class PlaybackPreferencesFragment : PreferenceFragmentCompat() { // pref!!.entries = entries // } - @UnstableApi + class EditFallbackSpeedDialog(activity: Activity) { val TAG = this::class.simpleName ?: "Anonymous" private val activityRef = WeakReference(activity) @@ -159,7 +158,7 @@ class PlaybackPreferencesFragment : PreferenceFragmentCompat() { } } - @UnstableApi + class EditForwardSpeedDialog(activity: Activity) { val TAG = this::class.simpleName ?: "Anonymous" private val activityRef = WeakReference(activity) diff --git a/app/src/main/kotlin/ac/mdiq/podcini/preferences/fragments/SwipePreferencesFragment.kt b/app/src/main/kotlin/ac/mdiq/podcini/preferences/fragments/SwipePreferencesFragment.kt index cfb44faa..0681872f 100644 --- a/app/src/main/kotlin/ac/mdiq/podcini/preferences/fragments/SwipePreferencesFragment.kt +++ b/app/src/main/kotlin/ac/mdiq/podcini/preferences/fragments/SwipePreferencesFragment.kt @@ -1,18 +1,16 @@ package ac.mdiq.podcini.preferences.fragments -import android.os.Bundle -import androidx.preference.Preference -import androidx.preference.PreferenceFragmentCompat import ac.mdiq.podcini.R import ac.mdiq.podcini.ui.actions.SwipeActions.Companion.showSettingDialog import ac.mdiq.podcini.ui.activity.PreferenceActivity -//import ac.mdiq.podcini.ui.dialog.SwipeActionsDialog import ac.mdiq.podcini.ui.fragment.* -import androidx.annotation.OptIn -import androidx.media3.common.util.UnstableApi +import android.os.Bundle +import androidx.preference.Preference +import androidx.preference.PreferenceFragmentCompat + class SwipePreferencesFragment : PreferenceFragmentCompat() { - @OptIn(UnstableApi::class) override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { + override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { addPreferencesFromResource(R.xml.preferences_swipe) findPreference(Prefs.prefSwipeQueue.name)?.onPreferenceClickListener = Preference.OnPreferenceClickListener { diff --git a/app/src/main/kotlin/ac/mdiq/podcini/preferences/fragments/SynchronizationPreferencesFragment.kt b/app/src/main/kotlin/ac/mdiq/podcini/preferences/fragments/SynchronizationPreferencesFragment.kt index f857ddb5..eb87416e 100644 --- a/app/src/main/kotlin/ac/mdiq/podcini/preferences/fragments/SynchronizationPreferencesFragment.kt +++ b/app/src/main/kotlin/ac/mdiq/podcini/preferences/fragments/SynchronizationPreferencesFragment.kt @@ -18,9 +18,9 @@ import ac.mdiq.podcini.net.sync.wifi.WifiSyncService.Companion.hostPort import ac.mdiq.podcini.net.sync.wifi.WifiSyncService.Companion.startInstantSync import ac.mdiq.podcini.storage.utils.FileNameGenerator.generateFileName import ac.mdiq.podcini.ui.activity.PreferenceActivity -import ac.mdiq.podcini.util.Logd import ac.mdiq.podcini.util.EventFlow import ac.mdiq.podcini.util.FlowEvent +import ac.mdiq.podcini.util.Logd import android.app.Activity import android.app.Dialog import android.content.Context @@ -39,12 +39,10 @@ import android.view.ViewGroup import android.view.inputmethod.EditorInfo import android.view.inputmethod.InputMethodManager import android.widget.* -import androidx.annotation.OptIn import androidx.appcompat.app.AlertDialog import androidx.core.text.HtmlCompat import androidx.fragment.app.DialogFragment import androidx.lifecycle.lifecycleScope -import androidx.media3.common.util.UnstableApi import androidx.preference.Preference import androidx.preference.PreferenceFragmentCompat import com.google.android.material.dialog.MaterialAlertDialogBuilder @@ -578,12 +576,12 @@ class SynchronizationPreferencesFragment : PreferenceFragmentCompat() { } } - @OptIn(UnstableApi::class) class WifiAuthenticationFragment : DialogFragment() { + class WifiAuthenticationFragment : DialogFragment() { private var binding: WifiSyncDialogBinding? = null private var portNum = 0 private var isGuest: Boolean? = null - @OptIn(UnstableApi::class) override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { + override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { val dialog = MaterialAlertDialogBuilder(requireContext()) dialog.setTitle(R.string.connect_to_peer) dialog.setNegativeButton(R.string.cancel_label, null) @@ -626,7 +624,7 @@ class SynchronizationPreferencesFragment : PreferenceFragmentCompat() { cancelFlowEvents() super.onDestroy() } - @OptIn(UnstableApi::class) override fun onResume() { + override fun onResume() { super.onResume() val d = dialog as? AlertDialog if (d != null) { diff --git a/app/src/main/kotlin/ac/mdiq/podcini/receiver/ConnectivityActionReceiver.kt b/app/src/main/kotlin/ac/mdiq/podcini/receiver/ConnectivityActionReceiver.kt index 52a8cd9b..23c36745 100644 --- a/app/src/main/kotlin/ac/mdiq/podcini/receiver/ConnectivityActionReceiver.kt +++ b/app/src/main/kotlin/ac/mdiq/podcini/receiver/ConnectivityActionReceiver.kt @@ -11,10 +11,10 @@ import android.content.Context import android.content.Intent import android.net.ConnectivityManager import android.util.Log -import androidx.media3.common.util.UnstableApi + class ConnectivityActionReceiver : BroadcastReceiver() { - @UnstableApi override fun onReceive(context: Context, intent: Intent) { + override fun onReceive(context: Context, intent: Intent) { Log.d(TAG, "onReceive called with action: ${intent.action}") if (intent.action == ConnectivityManager.CONNECTIVITY_ACTION) { Logd(TAG, "Received intent") diff --git a/app/src/main/kotlin/ac/mdiq/podcini/receiver/FeedUpdateReceiver.kt b/app/src/main/kotlin/ac/mdiq/podcini/receiver/FeedUpdateReceiver.kt index 9f4fe514..7b35352d 100644 --- a/app/src/main/kotlin/ac/mdiq/podcini/receiver/FeedUpdateReceiver.kt +++ b/app/src/main/kotlin/ac/mdiq/podcini/receiver/FeedUpdateReceiver.kt @@ -3,7 +3,7 @@ package ac.mdiq.podcini.receiver import android.content.BroadcastReceiver import android.content.Context import android.content.Intent -import androidx.media3.common.util.UnstableApi + import ac.mdiq.podcini.util.config.ClientConfigurator import ac.mdiq.podcini.net.feed.FeedUpdateManager import ac.mdiq.podcini.util.Logd @@ -12,7 +12,7 @@ import ac.mdiq.podcini.util.Logd * Refreshes all feeds when it receives an intent */ class FeedUpdateReceiver : BroadcastReceiver() { - @UnstableApi + override fun onReceive(context: Context, intent: Intent) { Logd(TAG, "Received intent") ClientConfigurator.initialize(context) diff --git a/app/src/main/kotlin/ac/mdiq/podcini/receiver/MediaButtonReceiver.kt b/app/src/main/kotlin/ac/mdiq/podcini/receiver/MediaButtonReceiver.kt index dc5acebf..39b7afbc 100644 --- a/app/src/main/kotlin/ac/mdiq/podcini/receiver/MediaButtonReceiver.kt +++ b/app/src/main/kotlin/ac/mdiq/podcini/receiver/MediaButtonReceiver.kt @@ -9,13 +9,13 @@ import android.os.Build import android.util.Log import android.view.KeyEvent import androidx.core.content.ContextCompat -import androidx.media3.common.util.UnstableApi + /** * Receives media button events. */ class MediaButtonReceiver : BroadcastReceiver() { - @UnstableApi + override fun onReceive(context: Context, intent: Intent) { Log.d(TAG, "onReceive Received intent: $intent") Log.d(TAG, "onReceive Action: ${intent.action}") diff --git a/app/src/main/kotlin/ac/mdiq/podcini/receiver/PowerConnectionReceiver.kt b/app/src/main/kotlin/ac/mdiq/podcini/receiver/PowerConnectionReceiver.kt index c19f4eee..f0ae49c5 100644 --- a/app/src/main/kotlin/ac/mdiq/podcini/receiver/PowerConnectionReceiver.kt +++ b/app/src/main/kotlin/ac/mdiq/podcini/receiver/PowerConnectionReceiver.kt @@ -3,7 +3,7 @@ package ac.mdiq.podcini.receiver import android.content.BroadcastReceiver import android.content.Context import android.content.Intent -import androidx.media3.common.util.UnstableApi + import ac.mdiq.podcini.util.config.ClientConfigurator import ac.mdiq.podcini.net.download.service.DownloadServiceInterface import ac.mdiq.podcini.preferences.UserPreferences.isEnableAutodownloadOnBattery @@ -17,7 +17,7 @@ import android.util.Log // Since the intent doesn't have the EXTRA_STATUS like the android.com article says it does // (though it used to) class PowerConnectionReceiver : BroadcastReceiver() { - @UnstableApi override fun onReceive(context: Context, intent: Intent) { + override fun onReceive(context: Context, intent: Intent) { val action = intent.action Log.d(TAG, "onReceive charging intent: $action") ClientConfigurator.initialize(context) diff --git a/app/src/main/kotlin/ac/mdiq/podcini/receiver/SPAReceiver.kt b/app/src/main/kotlin/ac/mdiq/podcini/receiver/SPAReceiver.kt index 0729afd0..d012ce5c 100644 --- a/app/src/main/kotlin/ac/mdiq/podcini/receiver/SPAReceiver.kt +++ b/app/src/main/kotlin/ac/mdiq/podcini/receiver/SPAReceiver.kt @@ -12,13 +12,13 @@ import android.content.Context import android.content.Intent import android.util.Log import android.widget.Toast -import androidx.media3.common.util.UnstableApi + /** * Receives intents from Podcini Single Purpose apps */ class SPAReceiver : BroadcastReceiver() { - @UnstableApi override fun onReceive(context: Context, intent: Intent) { + override fun onReceive(context: Context, intent: Intent) { if (intent.action != ACTION_SP_APPS_QUERY_FEEDS_REPSONSE) return Log.d(TAG, "onReceive called with action: ${intent.action}") diff --git a/app/src/main/kotlin/ac/mdiq/podcini/storage/algorithms/AutoCleanups.kt b/app/src/main/kotlin/ac/mdiq/podcini/storage/algorithms/AutoCleanups.kt index 637f51a3..2f11f318 100644 --- a/app/src/main/kotlin/ac/mdiq/podcini/storage/algorithms/AutoCleanups.kt +++ b/app/src/main/kotlin/ac/mdiq/podcini/storage/algorithms/AutoCleanups.kt @@ -16,9 +16,7 @@ import ac.mdiq.podcini.storage.model.PlayState import ac.mdiq.podcini.util.Logd import android.content.Context import android.util.Log -import androidx.annotation.OptIn import androidx.annotation.VisibleForTesting -import androidx.media3.common.util.UnstableApi import kotlinx.coroutines.runBlocking import java.util.* import java.util.concurrent.ExecutionException @@ -71,7 +69,7 @@ object AutoCleanups { override fun getReclaimableItems(): Int { return candidates.size } - @OptIn(UnstableApi::class) + public override fun performCleanup(context: Context, numToRemove: Int): Int { var candidates = candidates // in the absence of better data, we'll sort by item publication date @@ -125,7 +123,7 @@ object AutoCleanups { override fun getReclaimableItems(): Int { return candidates.size } - @OptIn(UnstableApi::class) public override fun performCleanup(context: Context, numToRemove: Int): Int { + public override fun performCleanup(context: Context, numToRemove: Int): Int { var candidates = candidates // in the absence of better data, we'll sort by item publication date candidates = candidates.sortedWith { lhs: Episode, rhs: Episode -> @@ -194,7 +192,7 @@ object AutoCleanups { override fun getReclaimableItems(): Int { return candidates.size } - @OptIn(UnstableApi::class) public override fun performCleanup(context: Context, numToRemove: Int): Int { + public override fun performCleanup(context: Context, numToRemove: Int): Int { val candidates = candidates.toMutableList() candidates.sortWith { lhs: Episode, rhs: Episode -> var l = lhs.media!!.playbackCompletionDate diff --git a/app/src/main/kotlin/ac/mdiq/podcini/storage/algorithms/AutoDownloads.kt b/app/src/main/kotlin/ac/mdiq/podcini/storage/algorithms/AutoDownloads.kt index 692fa7c8..993e2611 100644 --- a/app/src/main/kotlin/ac/mdiq/podcini/storage/algorithms/AutoDownloads.kt +++ b/app/src/main/kotlin/ac/mdiq/podcini/storage/algorithms/AutoDownloads.kt @@ -19,7 +19,7 @@ import android.content.Context import android.content.Intent import android.content.IntentFilter import android.os.BatteryManager -import androidx.media3.common.util.UnstableApi + import io.realm.kotlin.UpdatePolicy import java.util.concurrent.ExecutorService import java.util.concurrent.Executors @@ -48,7 +48,7 @@ object AutoDownloads { * @param context Used for accessing the DB. * @return A Future that can be used for waiting for the methods completion. */ - @UnstableApi + fun autodownloadEpisodeMedia(context: Context, feeds: List? = null): Future<*> { Logd(TAG, "autodownloadEpisodeMedia") return autodownloadExec.submit(downloadAlgorithm.autoDownloadEpisodeMedia(context, feeds)) @@ -69,7 +69,7 @@ object AutoDownloads { * @return A Runnable that will be submitted to an ExecutorService. */ // likely not needed - @UnstableApi + open fun autoDownloadEpisodeMedia(context: Context, feeds: List? = null): Runnable? { return Runnable {} } @@ -89,7 +89,7 @@ object AutoDownloads { class FeedBasedAutoDLAlgorithm : AutoDownloadAlgorithm() { - @UnstableApi + override fun autoDownloadEpisodeMedia(context: Context, feeds: List?): Runnable { return Runnable { // true if we should auto download based on network status diff --git a/app/src/main/kotlin/ac/mdiq/podcini/storage/database/Episodes.kt b/app/src/main/kotlin/ac/mdiq/podcini/storage/database/Episodes.kt index 2db7128e..dfa80d0d 100644 --- a/app/src/main/kotlin/ac/mdiq/podcini/storage/database/Episodes.kt +++ b/app/src/main/kotlin/ac/mdiq/podcini/storage/database/Episodes.kt @@ -31,10 +31,8 @@ import android.app.backup.BackupManager import android.content.Context import android.net.Uri import android.util.Log -import androidx.annotation.OptIn import androidx.core.app.NotificationManagerCompat import androidx.documentfile.provider.DocumentFile -import androidx.media3.common.util.UnstableApi import io.realm.kotlin.ext.isManaged import kotlinx.coroutines.Job import java.io.File @@ -102,7 +100,7 @@ object Episodes { return runOnIOScope { if (episode.media == null) return@runOnIOScope val episode_ = deleteMediaSync(context, episode) - if (prefDeleteRemovesFromQueue) removeFromQueueSync(null, episode_) + if (prefDeleteRemovesFromQueue) removeFromAllQueuesSync(episode_) } } @@ -181,7 +179,7 @@ object Episodes { * Remove the listed episodes and their EpisodeMedia entries. * Deleting media also removes the download log entries. */ - @UnstableApi + fun deleteEpisodes(context: Context, episodes: List) : Job { return runOnIOScope { val removedFromQueue: MutableList = mutableListOf() @@ -226,7 +224,7 @@ object Episodes { * @param episodes the FeedItems. * @param resetMediaPosition true if this method should also reset the position of the Episode's EpisodeMedia object. */ - @OptIn(UnstableApi::class) + fun setPlayState(played: Int, resetMediaPosition: Boolean, vararg episodes: Episode) : Job { Logd(TAG, "setPlayState called") return runOnIOScope { diff --git a/app/src/main/kotlin/ac/mdiq/podcini/storage/database/Feeds.kt b/app/src/main/kotlin/ac/mdiq/podcini/storage/database/Feeds.kt index a1f2043e..dd4c4b46 100644 --- a/app/src/main/kotlin/ac/mdiq/podcini/storage/database/Feeds.kt +++ b/app/src/main/kotlin/ac/mdiq/podcini/storage/database/Feeds.kt @@ -161,11 +161,6 @@ object Feeds { } fun getFeed(feedId: Long, copy: Boolean = false): Feed? { -// if (BuildConfig.DEBUG) { -// val stackTrace = Thread.currentThread().stackTrace -// val caller = if (stackTrace.size > 3) stackTrace[3] else null -// Logd(TAG, "${caller?.className}.${caller?.methodName} getFeed called") -// } val f = realm.query(Feed::class, "id == $feedId").first().find() return if (f != null) { if (copy) realm.copyFromRealm(f) @@ -348,13 +343,6 @@ object Feeds { } } - fun setRating(feed: Feed, rating: Int) { - Logd(TAG, "setRating called $rating") -// return runOnIOScope { - val result = upsertBlk(feed) { it.rating = rating } -// } - } - fun updateFeedDownloadURL(original: String, updated: String) : Job { Logd(TAG, "updateFeedDownloadURL(original: $original, updated: $updated)") return runOnIOScope { @@ -678,14 +666,14 @@ object Feeds { */ object EpisodeDuplicateGuesser { // only used in test - fun seemDuplicates(item1: Episode, item2: Episode): Boolean { - if (sameAndNotEmpty(item1.identifier, item2.identifier)) return true - val media1 = item1.media - val media2 = item2.media - if (media1 == null || media2 == null) return false - if (sameAndNotEmpty(media1.getStreamUrl(), media2.getStreamUrl())) return true - return (titlesLookSimilar(item1, item2) && datesLookSimilar(item1, item2) && durationsLookSimilar(media1, media2) && mimeTypeLooksSimilar(media1, media2)) - } +// fun seemDuplicates(item1: Episode, item2: Episode): Boolean { +// if (sameAndNotEmpty(item1.identifier, item2.identifier)) return true +// val media1 = item1.media +// val media2 = item2.media +// if (media1 == null || media2 == null) return false +// if (sameAndNotEmpty(media1.getStreamUrl(), media2.getStreamUrl())) return true +// return (titlesLookSimilar(item1, item2) && datesLookSimilar(item1, item2) && durationsLookSimilar(media1, media2) && mimeTypeLooksSimilar(media1, media2)) +// } private fun sameAndNotEmpty(string1: String?, string2: String?): Boolean { if (string1.isNullOrEmpty() || string2.isNullOrEmpty()) return false return string1 == string2 diff --git a/app/src/main/kotlin/ac/mdiq/podcini/storage/database/Queues.kt b/app/src/main/kotlin/ac/mdiq/podcini/storage/database/Queues.kt index fe35b670..af8e630d 100644 --- a/app/src/main/kotlin/ac/mdiq/podcini/storage/database/Queues.kt +++ b/app/src/main/kotlin/ac/mdiq/podcini/storage/database/Queues.kt @@ -17,7 +17,7 @@ import ac.mdiq.podcini.util.EventFlow import ac.mdiq.podcini.util.FlowEvent import ac.mdiq.podcini.util.Logd import android.util.Log -import androidx.media3.common.util.UnstableApi + import kotlinx.coroutines.Job import java.util.* @@ -68,11 +68,7 @@ object Queues { set(location) { appPrefs.edit().putString(UserPreferences.Prefs.prefEnqueueLocation.name, location.name).apply() } - -// fun queueFromName(name: String): PlayQueue? { -// return realm.query(PlayQueue::class).query("name == $0", name).first().find() -// } - + fun getInQueueEpisodeIds(): Set { Logd(TAG, "getQueueIDList() called") val queues = realm.query(PlayQueue::class).find() @@ -192,15 +188,6 @@ object Queues { } } -// /** -// * Removes a Episode object from the queue. -// * @param episodes FeedItems that should be removed. -// */ -// @JvmStatic -// fun removeFromQueue(vararg episodes: Episode) : Job { -// return runOnIOScope { removeFromQueueSync(curQueue, *episodes) } -// } - fun removeFromAllQueuesSync(vararg episodes: Episode) { Logd(TAG, "removeFromAllQueuesSync called ") val queues = realm.query(PlayQueue::class).find() @@ -215,7 +202,6 @@ object Queues { /** * @param queue_ if null, use curQueue */ - @UnstableApi internal fun removeFromQueueSync(queue_: PlayQueue?, vararg episodes: Episode) { Logd(TAG, "removeFromQueueSync called ") if (episodes.isEmpty()) return @@ -300,18 +286,6 @@ object Queues { } } - /** - * Changes the position of a Episode in the queue. - * @param from Source index. Must be in range 0..queue.size()-1. - * @param to Destination index. Must be in range 0..queue.size()-1. - * @param broadcastUpdate true if this operation should trigger a QueueUpdateBroadcast. This option should be set to - * false if the caller wants to avoid unexpected updates of the GUI. - * @throws IndexOutOfBoundsException if (to < 0 || to >= queue.size()) || (from < 0 || from >= queue.size()) - */ -// fun moveInQueue(from: Int, to: Int, broadcastUpdate: Boolean) : Job { -// return runOnIOScope { moveInQueueSync(from, to, broadcastUpdate) } -// } - /** * Changes the position of a Episode in the queue. * This function must be run using the ExecutorService (dbExec). diff --git a/app/src/main/kotlin/ac/mdiq/podcini/ui/actions/EpisodeActionButton.kt b/app/src/main/kotlin/ac/mdiq/podcini/ui/actions/EpisodeActionButton.kt index 7ab1b860..3401d535 100644 --- a/app/src/main/kotlin/ac/mdiq/podcini/ui/actions/EpisodeActionButton.kt +++ b/app/src/main/kotlin/ac/mdiq/podcini/ui/actions/EpisodeActionButton.kt @@ -34,7 +34,6 @@ import android.util.Log import android.view.KeyEvent import android.widget.Toast import androidx.annotation.DrawableRes -import androidx.annotation.OptIn import androidx.annotation.StringRes import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Row @@ -53,7 +52,6 @@ import androidx.compose.ui.res.vectorResource import androidx.compose.ui.unit.dp import androidx.compose.ui.window.Dialog import androidx.core.text.HtmlCompat -import androidx.media3.common.util.UnstableApi import com.google.android.material.dialog.MaterialAlertDialogBuilder import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.delay @@ -144,7 +142,7 @@ abstract class EpisodeActionButton internal constructor(@JvmField var item: Epis } } - @UnstableApi + companion object { // fun forItem(episode: Episode): EpisodeActionButton { // val media = episode.media ?: return TTSActionButton(episode) @@ -208,7 +206,7 @@ class CancelDownloadActionButton(item: Episode) : EpisodeActionButton(item) { return R.drawable.ic_cancel } - @UnstableApi + override fun onClick(context: Context) { val media = item.media if (media != null) DownloadServiceInterface.get()?.cancel(context, media) @@ -231,7 +229,7 @@ class PlayActionButton(item: Episode) : EpisodeActionButton(item) { return R.drawable.ic_play_24dp } - @UnstableApi + override fun onClick(context: Context) { Logd("PlayActionButton", "onClick called") val media = item.media @@ -290,7 +288,7 @@ class DeleteActionButton(item: Episode) : EpisodeActionButton(item) { override fun getDrawable(): Int { return R.drawable.ic_delete } - @UnstableApi + override fun onClick(context: Context) { LocalDeleteModal.deleteEpisodesWarnLocal(context, listOf(item)) actionState.value = getLabel() @@ -304,7 +302,7 @@ class NullActionButton(item: Episode) : EpisodeActionButton(item) { override fun getDrawable(): Int { return R.drawable.ic_questionmark } - @UnstableApi + override fun onClick(context: Context) {} } @@ -315,7 +313,7 @@ class PauseActionButton(item: Episode) : EpisodeActionButton(item) { override fun getDrawable(): Int { return R.drawable.ic_pause } - @UnstableApi + override fun onClick(context: Context) { Logd("PauseActionButton", "onClick called") val media = item.media ?: return @@ -390,7 +388,7 @@ class StreamActionButton(item: Episode) : EpisodeActionButton(item) { return R.drawable.ic_stream } - @UnstableApi + override fun onClick(context: Context) { if (item.media == null) return // Logd("StreamActionButton", "item.feed: ${item.feedId}") @@ -405,7 +403,7 @@ class StreamActionButton(item: Episode) : EpisodeActionButton(item) { } class StreamingConfirmationDialog(private val context: Context, private val playable: Playable) { - @UnstableApi + fun show() { MaterialAlertDialogBuilder(context) .setTitle(R.string.stream_label) @@ -447,7 +445,7 @@ class TTSActionButton(item: Episode) : EpisodeActionButton(item) { return R.drawable.text_to_speech } - @OptIn(UnstableApi::class) override fun onClick(context: Context) { + override fun onClick(context: Context) { Logd("TTSActionButton", "onClick called") if (item.link.isNullOrEmpty()) { Toast.makeText(context, R.string.episode_has_no_content, Toast.LENGTH_LONG).show() @@ -577,7 +575,7 @@ class PlayLocalActionButton(item: Episode) : EpisodeActionButton(item) { override fun getDrawable(): Int { return R.drawable.ic_play_24dp } - @UnstableApi + override fun onClick(context: Context) { Logd("PlayLocalActionButton", "onClick called") val media = item.media diff --git a/app/src/main/kotlin/ac/mdiq/podcini/ui/actions/SwipeAction.kt b/app/src/main/kotlin/ac/mdiq/podcini/ui/actions/SwipeAction.kt index 500d2eb5..d2dfc921 100644 --- a/app/src/main/kotlin/ac/mdiq/podcini/ui/actions/SwipeAction.kt +++ b/app/src/main/kotlin/ac/mdiq/podcini/ui/actions/SwipeAction.kt @@ -28,6 +28,7 @@ interface SwipeAction { NO_ACTION, COMBO, RATING, + COMMENT, SET_PLAY_STATE, ADD_TO_QUEUE, PUT_TO_QUEUE, diff --git a/app/src/main/kotlin/ac/mdiq/podcini/ui/actions/SwipeActions.kt b/app/src/main/kotlin/ac/mdiq/podcini/ui/actions/SwipeActions.kt index d2330f35..79dfd01c 100644 --- a/app/src/main/kotlin/ac/mdiq/podcini/ui/actions/SwipeActions.kt +++ b/app/src/main/kotlin/ac/mdiq/podcini/ui/actions/SwipeActions.kt @@ -27,7 +27,6 @@ import android.content.Context import android.content.SharedPreferences import android.util.TypedValue import android.view.ViewGroup -import androidx.annotation.OptIn import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.* import androidx.compose.foundation.lazy.grid.GridCells @@ -43,13 +42,13 @@ import androidx.compose.ui.platform.ComposeView import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.vectorResource +import androidx.compose.ui.text.input.TextFieldValue import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.dp import androidx.compose.ui.window.Dialog import androidx.fragment.app.Fragment import androidx.lifecycle.DefaultLifecycleObserver import androidx.lifecycle.LifecycleOwner -import androidx.media3.common.util.UnstableApi import com.google.android.material.snackbar.Snackbar import kotlinx.coroutines.Job import kotlinx.coroutines.runBlocking @@ -124,7 +123,6 @@ open class SwipeActions(private val fragment: Fragment, private val tag: String) override fun getTitle(context: Context): String { return context.getString(R.string.add_to_queue_label) } - @OptIn(UnstableApi::class) override fun performAction(item: Episode, fragment: Fragment, filter: EpisodeFilter) { addToQueue(item) } @@ -143,7 +141,6 @@ open class SwipeActions(private val fragment: Fragment, private val tag: String) override fun getTitle(context: Context): String { return context.getString(R.string.combo_action) } - @OptIn(UnstableApi::class) override fun performAction(item: Episode, fragment: Fragment, filter: EpisodeFilter) { val composeView = ComposeView(fragment.requireContext()).apply { setContent { @@ -195,7 +192,6 @@ open class SwipeActions(private val fragment: Fragment, private val tag: String) override fun getTitle(context: Context): String { return context.getString(R.string.delete_episode_label) } - @UnstableApi override fun performAction(item_: Episode, fragment: Fragment, filter: EpisodeFilter) { var item = item_ if (!item.isDownloaded && item.feed?.isLocalFeed != true) return @@ -225,7 +221,6 @@ open class SwipeActions(private val fragment: Fragment, private val tag: String) override fun getTitle(context: Context): String { return context.getString(R.string.set_rating_label) } - @OptIn(UnstableApi::class) override fun performAction(item: Episode, fragment: Fragment, filter: EpisodeFilter) { var showChooseRatingDialog by mutableStateOf(true) val composeView = ComposeView(fragment.requireContext()).apply { @@ -242,6 +237,46 @@ open class SwipeActions(private val fragment: Fragment, private val tag: String) } } + class AddCommentSwipeAction : SwipeAction { + override fun getId(): String { + return ActionTypes.COMMENT.name + } + override fun getActionIcon(): Int { + return R.drawable.baseline_comment_24 + } + override fun getActionColor(): Int { + return R.attr.icon_yellow + } + override fun getTitle(context: Context): String { + return context.getString(R.string.add_opinion_label) + } + override fun performAction(item: Episode, fragment: Fragment, filter: EpisodeFilter) { + var showEditComment by mutableStateOf(true) + val composeView = ComposeView(fragment.requireContext()).apply { + setContent { + CustomTheme(fragment.requireContext()) { + if (showEditComment) { + ChooseRatingDialog(listOf(item)) { + showEditComment = false + (fragment.view as? ViewGroup)?.removeView(this@apply) + } + var commentTextState by remember { mutableStateOf(TextFieldValue(item.comment)) } + LargeTextEditingDialog(textState = commentTextState, onTextChange = { commentTextState = it }, + onDismissRequest = { + showEditComment = false + (fragment.view as? ViewGroup)?.removeView(this@apply) + }, + onSave = { + runOnIOScope { upsert(item) { it.comment = commentTextState.text } } + }) + } + } + } + } + (fragment.view as? ViewGroup)?.addView(composeView) + } + } + class NoActionSwipeAction : SwipeAction { override fun getId(): String { return NO_ACTION.name @@ -273,7 +308,6 @@ open class SwipeActions(private val fragment: Fragment, private val tag: String) override fun getTitle(context: Context): String { return context.getString(R.string.remove_history_label) } - @OptIn(UnstableApi::class) override fun performAction(item: Episode, fragment: Fragment, filter: EpisodeFilter) { val playbackCompletionDate: Date? = item.media?.playbackCompletionDate val lastPlayedDate = item.media?.lastPlayedTime @@ -314,7 +348,6 @@ open class SwipeActions(private val fragment: Fragment, private val tag: String) override fun getTitle(context: Context): String { return context.getString(R.string.remove_from_queue_label) } - @OptIn(UnstableApi::class) override fun performAction(item_: Episode, fragment: Fragment, filter: EpisodeFilter) { val position: Int = curQueue.episodes.indexOf(item_) var item = item_ @@ -344,8 +377,8 @@ open class SwipeActions(private val fragment: Fragment, private val tag: String) * @param index Destination index. Must be in range 0..queue.size() * @throws IndexOutOfBoundsException if index < 0 || index >= queue.size() */ - @UnstableApi - fun addToQueueAt(episode: Episode, index: Int) : Job { + + private fun addToQueueAt(episode: Episode, index: Int) : Job { return runOnIOScope { if (curQueue.episodeIds.contains(episode.id)) return@runOnIOScope if (episode.isNew) setPlayState(PlayState.UNPLAYED.code, false, episode) @@ -519,7 +552,7 @@ open class SwipeActions(private val fragment: Fragment, private val tag: String) val swipeActions: List = listOf( NoActionSwipeAction(), ComboSwipeAction(), AddToQueueSwipeAction(), PutToQueueSwipeAction(), - StartDownloadSwipeAction(), SetRatingSwipeAction(), + StartDownloadSwipeAction(), SetRatingSwipeAction(), AddCommentSwipeAction(), SetPlaybackStateSwipeAction(), RemoveFromQueueSwipeAction(), DeleteSwipeAction(), RemoveFromHistorySwipeAction(), ShelveSwipeAction(), EraseSwipeAction()) @@ -533,7 +566,7 @@ open class SwipeActions(private val fragment: Fragment, private val tag: String) return getPrefs(tag, "") } - @OptIn(UnstableApi::class) @JvmStatic + @JvmStatic fun getPrefsWithDefaults(tag: String): Actions { val defaultActions = when (tag) { QueuesFragment.TAG -> "${NO_ACTION.name},${NO_ACTION.name}" diff --git a/app/src/main/kotlin/ac/mdiq/podcini/ui/activity/MainActivity.kt b/app/src/main/kotlin/ac/mdiq/podcini/ui/activity/MainActivity.kt index 71918cc9..e6b674e2 100644 --- a/app/src/main/kotlin/ac/mdiq/podcini/ui/activity/MainActivity.kt +++ b/app/src/main/kotlin/ac/mdiq/podcini/ui/activity/MainActivity.kt @@ -69,7 +69,7 @@ import androidx.core.view.WindowInsetsCompat import androidx.drawerlayout.widget.DrawerLayout import androidx.fragment.app.Fragment import androidx.lifecycle.lifecycleScope -import androidx.media3.common.util.UnstableApi + import androidx.media3.session.MediaController import androidx.media3.session.SessionToken import androidx.recyclerview.widget.RecyclerView @@ -89,7 +89,7 @@ import kotlin.math.min /** * The activity that is shown when the user launches the app. */ -@UnstableApi + class MainActivity : CastEnabledActivity() { private var drawerLayout: DrawerLayout? = null @@ -178,7 +178,7 @@ class MainActivity : CastEnabledActivity() { } private var prevState: Int = 0 - private val bottomSheetCallback: BottomSheetCallback = @UnstableApi object : BottomSheetCallback() { + private val bottomSheetCallback: BottomSheetCallback = object : BottomSheetCallback() { override fun onStateChanged(view: View, state: Int) { Logd(TAG, "bottomSheet onStateChanged $state ${view.id}") when (state) { @@ -214,7 +214,7 @@ class MainActivity : CastEnabledActivity() { return displayMetrics.widthPixels } - @UnstableApi public override fun onCreate(savedInstanceState: Bundle?) { + public override fun onCreate(savedInstanceState: Bundle?) { lastTheme = getNoTitleTheme(this) setTheme(lastTheme) diff --git a/app/src/main/kotlin/ac/mdiq/podcini/ui/activity/OpmlImportActivity.kt b/app/src/main/kotlin/ac/mdiq/podcini/ui/activity/OpmlImportActivity.kt index fe359d38..72b56014 100644 --- a/app/src/main/kotlin/ac/mdiq/podcini/ui/activity/OpmlImportActivity.kt +++ b/app/src/main/kotlin/ac/mdiq/podcini/ui/activity/OpmlImportActivity.kt @@ -31,7 +31,7 @@ import androidx.activity.result.contract.ActivityResultContracts import androidx.appcompat.app.AppCompatActivity import androidx.core.app.ActivityCompat import androidx.lifecycle.lifecycleScope -import androidx.media3.common.util.UnstableApi + import com.google.android.material.dialog.MaterialAlertDialogBuilder import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch @@ -73,7 +73,7 @@ class OpmlImportActivity : AppCompatActivity() { } } - @UnstableApi override fun onCreate(savedInstanceState: Bundle?) { + override fun onCreate(savedInstanceState: Bundle?) { setTheme(getTheme(this)) super.onCreate(savedInstanceState) supportActionBar?.setDisplayHomeAsUpEnabled(true) diff --git a/app/src/main/kotlin/ac/mdiq/podcini/ui/activity/SelectSubscriptionActivity.kt b/app/src/main/kotlin/ac/mdiq/podcini/ui/activity/SelectSubscriptionActivity.kt index 72456277..3802d8d4 100644 --- a/app/src/main/kotlin/ac/mdiq/podcini/ui/activity/SelectSubscriptionActivity.kt +++ b/app/src/main/kotlin/ac/mdiq/podcini/ui/activity/SelectSubscriptionActivity.kt @@ -15,13 +15,11 @@ import android.view.View import android.widget.AdapterView import android.widget.ArrayAdapter import android.widget.ListView -import androidx.annotation.OptIn import androidx.appcompat.app.AppCompatActivity import androidx.core.content.pm.ShortcutInfoCompat import androidx.core.content.pm.ShortcutManagerCompat import androidx.core.graphics.drawable.IconCompat import androidx.lifecycle.lifecycleScope -import androidx.media3.common.util.UnstableApi import coil.imageLoader import coil.request.ErrorResult import coil.request.ImageRequest @@ -72,7 +70,7 @@ class SelectSubscriptionActivity : AppCompatActivity() { return result } - @UnstableApi private fun addShortcut(feed: Feed, bitmap: Bitmap?) { + private fun addShortcut(feed: Feed, bitmap: Bitmap?) { val intent = Intent(this, MainActivity::class.java) intent.setAction(Intent.ACTION_MAIN) intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK) @@ -100,10 +98,10 @@ class SelectSubscriptionActivity : AppCompatActivity() { .setHeader("User-Agent", "Mozilla/5.0") .placeholder(R.color.light_gray) .listener(object : ImageRequest.Listener { - @OptIn(UnstableApi::class) override fun onError(request: ImageRequest, throwable: ErrorResult) { + override fun onError(request: ImageRequest, throwable: ErrorResult) { addShortcut(feed, null) } - @OptIn(UnstableApi::class) override fun onSuccess(request: ImageRequest, result: SuccessResult) { + override fun onSuccess(request: ImageRequest, result: SuccessResult) { addShortcut(feed, (result.drawable as BitmapDrawable).bitmap) } }) diff --git a/app/src/main/kotlin/ac/mdiq/podcini/ui/activity/ShareReceiverActivity.kt b/app/src/main/kotlin/ac/mdiq/podcini/ui/activity/ShareReceiverActivity.kt index cb800ede..d93037cb 100644 --- a/app/src/main/kotlin/ac/mdiq/podcini/ui/activity/ShareReceiverActivity.kt +++ b/app/src/main/kotlin/ac/mdiq/podcini/ui/activity/ShareReceiverActivity.kt @@ -4,8 +4,8 @@ import ac.mdiq.podcini.R import ac.mdiq.podcini.storage.database.RealmDB.realm import ac.mdiq.podcini.storage.database.RealmDB.upsertBlk import ac.mdiq.podcini.storage.model.ShareLog -import ac.mdiq.podcini.ui.compose.CustomTheme import ac.mdiq.podcini.ui.compose.ConfirmAddYoutubeEpisode +import ac.mdiq.podcini.ui.compose.CustomTheme import ac.mdiq.podcini.util.Logd import ac.mdiq.vista.extractor.services.youtube.YoutubeParsingHelper.isYoutubeServiceURL import ac.mdiq.vista.extractor.services.youtube.YoutubeParsingHelper.isYoutubeURL @@ -15,11 +15,9 @@ import android.net.Uri import android.os.Bundle import android.util.Log import androidx.activity.compose.setContent -import androidx.annotation.OptIn import androidx.appcompat.app.AppCompatActivity import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember -import androidx.media3.common.util.UnstableApi import com.google.android.material.dialog.MaterialAlertDialogBuilder import java.net.URL import java.net.URLDecoder @@ -27,7 +25,7 @@ import java.net.URLDecoder class ShareReceiverActivity : AppCompatActivity() { private var sharedUrl: String? = null - @OptIn(UnstableApi::class) override fun onCreate(savedInstanceState: Bundle?) { + override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) Logd(TAG, "intent: $intent") diff --git a/app/src/main/kotlin/ac/mdiq/podcini/ui/activity/SplashActivity.kt b/app/src/main/kotlin/ac/mdiq/podcini/ui/activity/SplashActivity.kt index 07875693..e1791e6f 100644 --- a/app/src/main/kotlin/ac/mdiq/podcini/ui/activity/SplashActivity.kt +++ b/app/src/main/kotlin/ac/mdiq/podcini/ui/activity/SplashActivity.kt @@ -7,7 +7,7 @@ import android.content.Intent import android.os.Bundle import android.view.View import android.widget.Toast -import androidx.media3.common.util.UnstableApi + import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch @@ -18,7 +18,7 @@ import kotlinx.coroutines.withContext */ @SuppressLint("CustomSplashScreen") class SplashActivity : Activity() { - @UnstableApi override fun onCreate(savedInstanceState: Bundle?) { + override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) val content = findViewById(android.R.id.content) content.viewTreeObserver.addOnPreDrawListener { false } // Keep splash screen active diff --git a/app/src/main/kotlin/ac/mdiq/podcini/ui/activity/VideoplayerActivity.kt b/app/src/main/kotlin/ac/mdiq/podcini/ui/activity/VideoplayerActivity.kt index 7a4a21a6..e5afdaab 100644 --- a/app/src/main/kotlin/ac/mdiq/podcini/ui/activity/VideoplayerActivity.kt +++ b/app/src/main/kotlin/ac/mdiq/podcini/ui/activity/VideoplayerActivity.kt @@ -64,7 +64,6 @@ import android.widget.EditText import android.widget.FrameLayout import android.widget.SeekBar import android.widget.SeekBar.OnSeekBarChangeListener -import androidx.annotation.OptIn import androidx.appcompat.app.AlertDialog import androidx.appcompat.app.AppCompatActivity import androidx.compose.runtime.mutableStateOf @@ -75,7 +74,6 @@ import androidx.fragment.app.DialogFragment import androidx.fragment.app.Fragment import androidx.interpolator.view.animation.FastOutSlowInInterpolator import androidx.lifecycle.lifecycleScope -import androidx.media3.common.util.UnstableApi import androidx.window.layout.WindowMetricsCalculator import com.google.android.material.dialog.MaterialAlertDialogBuilder import kotlinx.coroutines.Dispatchers @@ -89,7 +87,7 @@ import kotlin.math.min /** * Activity for playing video files. */ -@UnstableApi + class VideoplayerActivity : CastEnabledActivity() { private var _binding: VideoplayerActivityBinding? = null @@ -162,7 +160,7 @@ class VideoplayerActivity : CastEnabledActivity() { if (::videoEpisodeFragment.isInitialized) videoEpisodeFragment.setForVideoMode() } - @UnstableApi + override fun onResume() { super.onResume() setForVideoMode() @@ -190,7 +188,7 @@ class VideoplayerActivity : CastEnabledActivity() { if (!PictureInPictureUtil.isInPictureInPictureMode(this)) compatEnterPictureInPicture() } - @UnstableApi + override fun onStart() { super.onStart() procFlowEvents() @@ -244,7 +242,7 @@ class VideoplayerActivity : CastEnabledActivity() { return true } - @UnstableApi + override fun onPrepareOptionsMenu(menu: Menu): Boolean { super.onPrepareOptionsMenu(menu) @@ -426,7 +424,7 @@ class VideoplayerActivity : CastEnabledActivity() { private val binding get() = _binding!! private var statusHandler: ServiceStatusHandler? = null - @UnstableApi override fun onStart() { + override fun onStart() { super.onStart() statusHandler = object : ServiceStatusHandler(requireActivity()) { override fun loadMediaInfo() { @@ -435,7 +433,7 @@ class VideoplayerActivity : CastEnabledActivity() { } statusHandler?.init() } - @UnstableApi override fun onStop() { + override fun onStop() { super.onStop() statusHandler?.release() statusHandler = null @@ -453,7 +451,7 @@ class VideoplayerActivity : CastEnabledActivity() { _binding = null super.onDestroyView() } - @UnstableApi private fun setupAudioTracks() { + private fun setupAudioTracks() { val butAudioTracks = binding.audioTracks if (audioTracks.size < 2 || selectedAudioTrack < 0) { butAudioTracks.visibility = View.GONE @@ -476,7 +474,7 @@ class VideoplayerActivity : CastEnabledActivity() { } } - @UnstableApi + class VideoEpisodeFragment : Fragment(), OnSeekBarChangeListener { private var _binding: VideoEpisodeFragmentBinding? = null private val binding get() = _binding!! @@ -527,7 +525,7 @@ class VideoplayerActivity : CastEnabledActivity() { override fun surfaceChanged(holder: SurfaceHolder, format: Int, width: Int, height: Int) { holder.setFixedSize(width, height) } - @UnstableApi + override fun surfaceCreated(holder: SurfaceHolder) { Logd(TAG, "Videoview holder created") videoSurfaceCreated = true @@ -549,7 +547,7 @@ class VideoplayerActivity : CastEnabledActivity() { } } - @OptIn(UnstableApi::class) + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View { super.onCreateView(inflater, container, savedInstanceState) Logd(TAG, "fragment onCreateView") @@ -561,7 +559,7 @@ class VideoplayerActivity : CastEnabledActivity() { return root } - @OptIn(UnstableApi::class) + private fun newStatusHandler(): ServiceStatusHandler { return object : ServiceStatusHandler(requireActivity()) { override fun updatePlayButton(showPlay: Boolean) { @@ -586,14 +584,14 @@ class VideoplayerActivity : CastEnabledActivity() { } } - @UnstableApi + override fun onStart() { super.onStart() onPositionObserverUpdate() procFlowEvents() } - @UnstableApi + override fun onStop() { super.onStop() cancelFlowEvents() @@ -689,7 +687,7 @@ class VideoplayerActivity : CastEnabledActivity() { } private var loadItemsRunning = false - @OptIn(UnstableApi::class) + private fun loadMediaInfo() { Logd(TAG, "loadMediaInfo called") if (curMedia == null) return @@ -749,7 +747,7 @@ class VideoplayerActivity : CastEnabledActivity() { } } - @UnstableApi + private fun setupView() { showTimeLeft = shouldShowRemainingTime() Logd(TAG, "setupView showTimeLeft: $showTimeLeft") @@ -843,19 +841,19 @@ class VideoplayerActivity : CastEnabledActivity() { }) } - @UnstableApi + fun onRewind() { playbackService?.mPlayer?.seekDelta(-rewindSecs * 1000) setupVideoControlsToggler() } - @UnstableApi + fun onPlayPause() { playPause() setupVideoControlsToggler() } - @UnstableApi + fun onFastForward() { playbackService?.mPlayer?.seekDelta(fastForwardSecs * 1000) setupVideoControlsToggler() diff --git a/app/src/main/kotlin/ac/mdiq/podcini/ui/activity/starter/VideoPlayerActivityStarter.kt b/app/src/main/kotlin/ac/mdiq/podcini/ui/activity/starter/VideoPlayerActivityStarter.kt index 346711be..e9cd498d 100644 --- a/app/src/main/kotlin/ac/mdiq/podcini/ui/activity/starter/VideoPlayerActivityStarter.kt +++ b/app/src/main/kotlin/ac/mdiq/podcini/ui/activity/starter/VideoPlayerActivityStarter.kt @@ -1,18 +1,15 @@ package ac.mdiq.podcini.ui.activity.starter - import ac.mdiq.podcini.R import android.app.PendingIntent import android.content.Context import android.content.Intent -import androidx.annotation.OptIn -import androidx.media3.common.util.UnstableApi /** * Launches the video player activity of the app with specific arguments. * Does not require a dependency on the actual implementation of the activity. */ -@OptIn(UnstableApi::class) class VideoPlayerActivityStarter(private val context: Context) { + class VideoPlayerActivityStarter(private val context: Context) { val intent: Intent = Intent(INTENT) val pendingIntent: PendingIntent get() = PendingIntent.getActivity(context, R.id.pending_intent_video_player, intent, diff --git a/app/src/main/kotlin/ac/mdiq/podcini/ui/compose/EpisodesVM.kt b/app/src/main/kotlin/ac/mdiq/podcini/ui/compose/EpisodesVM.kt index b1e20bd8..bdf7f648 100644 --- a/app/src/main/kotlin/ac/mdiq/podcini/ui/compose/EpisodesVM.kt +++ b/app/src/main/kotlin/ac/mdiq/podcini/ui/compose/EpisodesVM.kt @@ -278,7 +278,7 @@ fun PlayStateDialog(selected: List, onDismissRequest: () -> Unit) { media = item_.media if (media != null && hasAlmostEnded && shouldAutoDelete) { item_ = deleteMediaSync(context, item_) - if (prefDeleteRemovesFromQueue) removeFromQueueSync(null, item_) + if (prefDeleteRemovesFromQueue) removeFromAllQueuesSync(item_) } else if (prefRemoveFromQueueMarkedPlayed) removeFromAllQueuesSync(item_) if (item_.feed?.isLocalFeed != true && (isProviderConnected || wifiSyncEnabledKey)) { val media_: EpisodeMedia? = item_.media @@ -781,7 +781,7 @@ fun EpisodeLazyColumn(activity: MainActivity, vms: MutableList, feed: LaunchedEffect(key1 = status, key2 = vm.downloadState) { if (index >= vms.size) return@LaunchedEffect if (isDownloading()) vm.dlPercent = dls?.getProgress(vms[index].episode.media?.downloadUrl ?: "") ?: 0 - Logd(TAG, "LaunchedEffect $index isPlayingState: ${vms[index].isPlayingState} ${vms[index].episode.title}") + Logd(TAG, "LaunchedEffect $index isPlayingState: ${vms[index].isPlayingState} ${vm.episode.playState} ${vms[index].episode.title}") Logd(TAG, "LaunchedEffect $index downloadState: ${vms[index].downloadState} ${vm.episode.media?.downloaded} ${vm.dlPercent}") vm.actionButton = vm.actionButton.forItem(vm.episode) if (vm.actionButton.getLabel() != actionButton.getLabel()) actionButton = vm.actionButton diff --git a/app/src/main/kotlin/ac/mdiq/podcini/ui/compose/Feeds.kt b/app/src/main/kotlin/ac/mdiq/podcini/ui/compose/Feeds.kt index 04b681b8..43a3d2f3 100644 --- a/app/src/main/kotlin/ac/mdiq/podcini/ui/compose/Feeds.kt +++ b/app/src/main/kotlin/ac/mdiq/podcini/ui/compose/Feeds.kt @@ -6,6 +6,7 @@ import ac.mdiq.podcini.net.feed.discovery.PodcastSearchResult import ac.mdiq.podcini.storage.database.Feeds import ac.mdiq.podcini.storage.database.Feeds.deleteFeedSync import ac.mdiq.podcini.storage.database.RealmDB.upsert +import ac.mdiq.podcini.storage.database.RealmDB.upsertBlk import ac.mdiq.podcini.storage.model.Feed import ac.mdiq.podcini.storage.model.Feed.Companion.MAX_SYNTHETIC_ID import ac.mdiq.podcini.storage.model.Rating @@ -54,7 +55,10 @@ fun ChooseRatingDialog(selected: List, onDismissRequest: () -> Unit) { Column(modifier = Modifier.padding(16.dp), verticalArrangement = Arrangement.spacedBy(16.dp)) { for (rating in Rating.entries.reversed()) { Row(verticalAlignment = Alignment.CenterVertically, modifier = Modifier.padding(4.dp).clickable { - for (item in selected) Feeds.setRating(item, rating.code) + for (item in selected) { +// Feeds.setRating(item, rating.code) + upsertBlk(item) { it.rating = rating.code } + } onDismissRequest() }) { Icon(imageVector = ImageVector.vectorResource(id = rating.res), "") diff --git a/app/src/main/kotlin/ac/mdiq/podcini/ui/dialog/CustomFeedNameDialog.kt b/app/src/main/kotlin/ac/mdiq/podcini/ui/dialog/CustomFeedNameDialog.kt index 3535e685..f8495bd3 100644 --- a/app/src/main/kotlin/ac/mdiq/podcini/ui/dialog/CustomFeedNameDialog.kt +++ b/app/src/main/kotlin/ac/mdiq/podcini/ui/dialog/CustomFeedNameDialog.kt @@ -2,19 +2,16 @@ package ac.mdiq.podcini.ui.dialog import ac.mdiq.podcini.R import ac.mdiq.podcini.databinding.EditTextDialogBinding -import ac.mdiq.podcini.storage.database.RealmDB.unmanaged import ac.mdiq.podcini.storage.database.RealmDB.upsertBlk import ac.mdiq.podcini.storage.model.Feed import android.app.Activity import android.content.DialogInterface import android.view.LayoutInflater -import androidx.annotation.OptIn import androidx.appcompat.app.AlertDialog -import androidx.media3.common.util.UnstableApi import com.google.android.material.dialog.MaterialAlertDialogBuilder import java.lang.ref.WeakReference -@OptIn(UnstableApi::class) + class CustomFeedNameDialog(activity: Activity, private var feed: Feed) { private val activityRef: WeakReference = WeakReference(activity) diff --git a/app/src/main/kotlin/ac/mdiq/podcini/ui/dialog/MediaPlayerErrorDialog.kt b/app/src/main/kotlin/ac/mdiq/podcini/ui/dialog/MediaPlayerErrorDialog.kt index 65edfa51..d28063b8 100644 --- a/app/src/main/kotlin/ac/mdiq/podcini/ui/dialog/MediaPlayerErrorDialog.kt +++ b/app/src/main/kotlin/ac/mdiq/podcini/ui/dialog/MediaPlayerErrorDialog.kt @@ -1,6 +1,8 @@ package ac.mdiq.podcini.ui.dialog +import ac.mdiq.podcini.R import ac.mdiq.podcini.ui.activity.MainActivity +import ac.mdiq.podcini.util.FlowEvent import android.app.Activity import android.content.DialogInterface import android.text.Spannable @@ -8,12 +10,8 @@ import android.text.SpannableString import android.text.style.ForegroundColorSpan import com.google.android.material.bottomsheet.BottomSheetBehavior import com.google.android.material.dialog.MaterialAlertDialogBuilder -import ac.mdiq.podcini.R -import ac.mdiq.podcini.util.FlowEvent -import androidx.annotation.OptIn -import androidx.media3.common.util.UnstableApi -@OptIn(UnstableApi::class) + object MediaPlayerErrorDialog { fun show(activity: Activity, event: FlowEvent.PlayerErrorEvent) { val errorDialog = MaterialAlertDialogBuilder(activity) diff --git a/app/src/main/kotlin/ac/mdiq/podcini/ui/dialog/SleepTimerDialog.kt b/app/src/main/kotlin/ac/mdiq/podcini/ui/dialog/SleepTimerDialog.kt index d881fe58..ba4c2efb 100644 --- a/app/src/main/kotlin/ac/mdiq/podcini/ui/dialog/SleepTimerDialog.kt +++ b/app/src/main/kotlin/ac/mdiq/podcini/ui/dialog/SleepTimerDialog.kt @@ -42,7 +42,7 @@ import android.view.inputmethod.InputMethodManager import android.widget.* import androidx.fragment.app.DialogFragment import androidx.lifecycle.lifecycleScope -import androidx.media3.common.util.UnstableApi + import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.google.android.material.snackbar.Snackbar import kotlinx.coroutines.Job @@ -59,17 +59,17 @@ class SleepTimerDialog : DialogFragment() { private lateinit var etxtTime: EditText private lateinit var chAutoEnable: CheckBox - @UnstableApi override fun onStart() { + override fun onStart() { super.onStart() procFlowEvents() } - @UnstableApi override fun onStop() { + override fun onStop() { super.onStop() cancelFlowEvents() } - @UnstableApi override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { + override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { _binding = TimeDialogBinding.inflate(layoutInflater) val content = binding.root val builder = MaterialAlertDialogBuilder(requireContext()) diff --git a/app/src/main/kotlin/ac/mdiq/podcini/ui/dialog/TagSettingsDialog.kt b/app/src/main/kotlin/ac/mdiq/podcini/ui/dialog/TagSettingsDialog.kt index c16e005b..4fa7474a 100644 --- a/app/src/main/kotlin/ac/mdiq/podcini/ui/dialog/TagSettingsDialog.kt +++ b/app/src/main/kotlin/ac/mdiq/podcini/ui/dialog/TagSettingsDialog.kt @@ -15,9 +15,7 @@ import android.content.DialogInterface import android.os.Bundle import android.view.View import android.widget.ArrayAdapter -import androidx.annotation.OptIn import androidx.fragment.app.DialogFragment -import androidx.media3.common.util.UnstableApi import androidx.recyclerview.widget.GridLayoutManager import com.google.android.material.dialog.MaterialAlertDialogBuilder @@ -95,7 +93,7 @@ class TagSettingsDialog : DialogFragment() { super.onDestroyView() } - @OptIn(UnstableApi::class) private fun updatePreferencesTags(commonTags: Set) { + private fun updatePreferencesTags(commonTags: Set) { for (i in 0..feedList.size-1) { val f = feedList[i] Logd(TAG, "${f.title} $displayedTags") diff --git a/app/src/main/kotlin/ac/mdiq/podcini/ui/dialog/VariableSpeedDialog.kt b/app/src/main/kotlin/ac/mdiq/podcini/ui/dialog/VariableSpeedDialog.kt index b0685f9c..595fc7b4 100644 --- a/app/src/main/kotlin/ac/mdiq/podcini/ui/dialog/VariableSpeedDialog.kt +++ b/app/src/main/kotlin/ac/mdiq/podcini/ui/dialog/VariableSpeedDialog.kt @@ -1,6 +1,7 @@ package ac.mdiq.podcini.ui.dialog //import ac.mdiq.podcini.preferences.UserPreferences.videoPlaybackSpeed + import ac.mdiq.podcini.R import ac.mdiq.podcini.databinding.SpeedSelectDialogBinding import ac.mdiq.podcini.playback.base.InTheatre.curEpisode @@ -26,9 +27,7 @@ import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import android.widget.CompoundButton -import androidx.annotation.OptIn import androidx.lifecycle.lifecycleScope -import androidx.media3.common.util.UnstableApi import androidx.recyclerview.widget.GridLayoutManager import androidx.recyclerview.widget.RecyclerView import com.google.android.material.bottomsheet.BottomSheetDialogFragment @@ -43,7 +42,7 @@ import java.text.DecimalFormat import java.text.DecimalFormatSymbols import java.util.* -@OptIn(UnstableApi::class) + open class VariableSpeedDialog : BottomSheetDialogFragment() { private var _binding: SpeedSelectDialogBinding? = null private val binding get() = _binding!! @@ -60,7 +59,7 @@ open class VariableSpeedDialog : BottomSheetDialogFragment() { selectedSpeeds = ArrayList(playbackSpeedArray) } - @UnstableApi override fun onStart() { + override fun onStart() { super.onStart() Logd(TAG, "onStart: playbackService ready: ${playbackService?.isServiceReady()}") @@ -75,7 +74,7 @@ open class VariableSpeedDialog : BottomSheetDialogFragment() { updateSpeed(FlowEvent.SpeedChangedEvent(curSpeedFB)) } - @UnstableApi override fun onStop() { + override fun onStop() { super.onStop() cancelFlowEvents() } @@ -102,7 +101,7 @@ open class VariableSpeedDialog : BottomSheetDialogFragment() { addCurrentSpeedChip.text = String.format(Locale.getDefault(), "%1$.2f", event.newSpeed) } - @UnstableApi override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { _binding = SpeedSelectDialogBinding.inflate(inflater) settingCode = (arguments?.getBooleanArray("settingCode") ?: BooleanArray(3) {true}) @@ -144,7 +143,7 @@ open class VariableSpeedDialog : BottomSheetDialogFragment() { return binding.root } - @OptIn(UnstableApi::class) override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) if (playbackService?.isServiceReady() == false) { binding.currentAudio.visibility = View.INVISIBLE @@ -210,7 +209,7 @@ open class VariableSpeedDialog : BottomSheetDialogFragment() { return ViewHolder(chip) } - @UnstableApi override fun onBindViewHolder(holder: ViewHolder, position: Int) { + override fun onBindViewHolder(holder: ViewHolder, position: Int) { val speed = selectedSpeeds[position] holder.chip.text = String.format(Locale.getDefault(), "%1$.2f", speed) diff --git a/app/src/main/kotlin/ac/mdiq/podcini/ui/fragment/AllEpisodesFragment.kt b/app/src/main/kotlin/ac/mdiq/podcini/ui/fragment/AllEpisodesFragment.kt index 2e8613c8..ffb1b7b0 100644 --- a/app/src/main/kotlin/ac/mdiq/podcini/ui/fragment/AllEpisodesFragment.kt +++ b/app/src/main/kotlin/ac/mdiq/podcini/ui/fragment/AllEpisodesFragment.kt @@ -19,19 +19,17 @@ import android.view.LayoutInflater import android.view.MenuItem import android.view.View import android.view.ViewGroup -import androidx.annotation.OptIn import androidx.lifecycle.lifecycleScope -import androidx.media3.common.util.UnstableApi import kotlinx.coroutines.Job import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.launch import org.apache.commons.lang3.StringUtils -@UnstableApi + class AllEpisodesFragment : BaseEpisodesFragment() { private var allEpisodes: List = listOf() - @UnstableApi override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View { + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View { val root = super.onCreateView(inflater, container, savedInstanceState) Logd(TAG, "fragment onCreateView") @@ -85,7 +83,7 @@ class AllEpisodesFragment : BaseEpisodesFragment() { return PREF_NAME } - @OptIn(UnstableApi::class) override fun onMenuItemClick(item: MenuItem): Boolean { + override fun onMenuItemClick(item: MenuItem): Boolean { if (super.onOptionsItemSelected(item)) return true when (item.itemId) { diff --git a/app/src/main/kotlin/ac/mdiq/podcini/ui/fragment/AudioPlayerFragment.kt b/app/src/main/kotlin/ac/mdiq/podcini/ui/fragment/AudioPlayerFragment.kt index bac26485..712a96cc 100644 --- a/app/src/main/kotlin/ac/mdiq/podcini/ui/fragment/AudioPlayerFragment.kt +++ b/app/src/main/kotlin/ac/mdiq/podcini/ui/fragment/AudioPlayerFragment.kt @@ -80,7 +80,7 @@ import androidx.core.content.ContextCompat import androidx.core.text.HtmlCompat import androidx.fragment.app.Fragment import androidx.lifecycle.lifecycleScope -import androidx.media3.common.util.UnstableApi + import androidx.media3.session.MediaController import coil.compose.AsyncImage import coil.request.CachePolicy @@ -95,7 +95,7 @@ import java.text.DecimalFormat import java.text.NumberFormat import kotlin.math.max -@UnstableApi + class AudioPlayerFragment : Fragment() { private var isCollapsed by mutableStateOf(true) @@ -556,8 +556,7 @@ class AudioPlayerFragment : Fragment() { txtvPlaybackSpeed = speedStr } - @UnstableApi - fun onPositionUpdate(event: FlowEvent.PlaybackPositionEvent) { + private fun onPositionUpdate(event: FlowEvent.PlaybackPositionEvent) { Logd(TAG, "onPositionUpdate") if (!playButInit && playButRes == R.drawable.ic_play_48dp && curMedia is EpisodeMedia) { if (isCurrentlyPlaying(curMedia as? EpisodeMedia)) playButRes = R.drawable.ic_pause @@ -588,8 +587,8 @@ class AudioPlayerFragment : Fragment() { } } - @UnstableApi - fun updateUi(media: Playable) { + + private fun updateUi(media: Playable) { Logd(TAG, "updateUi called $media") titleText = media.getEpisodeTitle() txtvPlaybackSpeed = DecimalFormat("0.00").format(curSpeedFB.toDouble()) @@ -726,7 +725,7 @@ class AudioPlayerFragment : Fragment() { Logd(TAG, "displayCoverImage: imgLoc: $imgLoc") } - @UnstableApi private fun seekToPrevChapter() { + private fun seekToPrevChapter() { val curr: Chapter? = currentChapter if (curr == null || displayedChapterIndex == -1) return when { @@ -739,13 +738,13 @@ class AudioPlayerFragment : Fragment() { } } - @UnstableApi private fun seekToNextChapter() { + private fun seekToNextChapter() { if (currentMedia == null || currentMedia!!.getChapters().isEmpty() || displayedChapterIndex == -1 || displayedChapterIndex + 1 >= currentMedia!!.getChapters().size) return refreshChapterData(displayedChapterIndex + 1) seekTo(currentMedia!!.getChapters()[displayedChapterIndex].start.toInt()) } - @UnstableApi private fun savePreference() { + private fun savePreference() { Logd(TAG, "Saving preferences") val editor = prefs?.edit() ?: return if (curMedia != null) { @@ -898,7 +897,7 @@ class AudioPlayerFragment : Fragment() { cancelFlowEvents() } - @UnstableApi override fun onPause() { + override fun onPause() { super.onPause() savePreference() } diff --git a/app/src/main/kotlin/ac/mdiq/podcini/ui/fragment/BaseEpisodesFragment.kt b/app/src/main/kotlin/ac/mdiq/podcini/ui/fragment/BaseEpisodesFragment.kt index fe2d05b6..70dd41d5 100644 --- a/app/src/main/kotlin/ac/mdiq/podcini/ui/fragment/BaseEpisodesFragment.kt +++ b/app/src/main/kotlin/ac/mdiq/podcini/ui/fragment/BaseEpisodesFragment.kt @@ -26,7 +26,7 @@ import androidx.compose.runtime.setValue import androidx.core.util.Pair import androidx.fragment.app.Fragment import androidx.lifecycle.lifecycleScope -import androidx.media3.common.util.UnstableApi + import com.google.android.material.appbar.MaterialToolbar import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Job @@ -35,7 +35,7 @@ import kotlinx.coroutines.launch import kotlinx.coroutines.withContext -@UnstableApi + abstract class BaseEpisodesFragment : Fragment(), Toolbar.OnMenuItemClickListener { val TAG = this::class.simpleName ?: "Anonymous" @@ -57,7 +57,7 @@ abstract class BaseEpisodesFragment : Fragment(), Toolbar.OnMenuItemClickListene private val vms = mutableStateListOf() var showFilterDialog by mutableStateOf(false) - @UnstableApi override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View { + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View { super.onCreateView(inflater, container, savedInstanceState) _binding = ComposeFragmentBinding.inflate(inflater) @@ -130,7 +130,7 @@ abstract class BaseEpisodesFragment : Fragment(), Toolbar.OnMenuItemClickListene // } @Deprecated("Deprecated in Java") - @UnstableApi override fun onOptionsItemSelected(item: MenuItem): Boolean { + override fun onOptionsItemSelected(item: MenuItem): Boolean { if (super.onOptionsItemSelected(item)) return true val itemId = item.itemId when (itemId) { diff --git a/app/src/main/kotlin/ac/mdiq/podcini/ui/fragment/DownloadsFragment.kt b/app/src/main/kotlin/ac/mdiq/podcini/ui/fragment/DownloadsFragment.kt index bfdbae10..cd76f73a 100644 --- a/app/src/main/kotlin/ac/mdiq/podcini/ui/fragment/DownloadsFragment.kt +++ b/app/src/main/kotlin/ac/mdiq/podcini/ui/fragment/DownloadsFragment.kt @@ -41,7 +41,7 @@ import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.setValue import androidx.fragment.app.Fragment import androidx.lifecycle.lifecycleScope -import androidx.media3.common.util.UnstableApi + import com.google.android.material.appbar.MaterialToolbar import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Job @@ -55,7 +55,7 @@ import java.util.* /** * Displays all completed downloads and provides a button to delete them. */ -@UnstableApi class DownloadsFragment : Fragment(), Toolbar.OnMenuItemClickListener { + class DownloadsFragment : Fragment(), Toolbar.OnMenuItemClickListener { private var _binding: ComposeFragmentBinding? = null private val binding get() = _binding!! @@ -74,7 +74,7 @@ import java.util.* private var displayUpArrow = false - @UnstableApi override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View { + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View { _binding = ComposeFragmentBinding.inflate(inflater) Logd(TAG, "fragment onCreateView") @@ -163,7 +163,7 @@ import java.util.* super.onDestroyView() } - @UnstableApi override fun onMenuItemClick(item: MenuItem): Boolean { + override fun onMenuItemClick(item: MenuItem): Boolean { when (item.itemId) { R.id.filter_items -> { showFilterDialog = true diff --git a/app/src/main/kotlin/ac/mdiq/podcini/ui/fragment/EpisodeInfoFragment.kt b/app/src/main/kotlin/ac/mdiq/podcini/ui/fragment/EpisodeInfoFragment.kt index 20a0eb37..865ba6ed 100644 --- a/app/src/main/kotlin/ac/mdiq/podcini/ui/fragment/EpisodeInfoFragment.kt +++ b/app/src/main/kotlin/ac/mdiq/podcini/ui/fragment/EpisodeInfoFragment.kt @@ -46,7 +46,6 @@ import android.webkit.WebViewClient import android.widget.Button import android.widget.TextView import android.widget.Toast -import androidx.annotation.OptIn import androidx.appcompat.widget.Toolbar import androidx.compose.foundation.background import androidx.compose.foundation.clickable @@ -74,7 +73,6 @@ import androidx.core.view.MenuProvider import androidx.fragment.app.Fragment import androidx.lifecycle.Lifecycle import androidx.lifecycle.lifecycleScope -import androidx.media3.common.util.UnstableApi import coil.compose.AsyncImage import com.google.android.material.appbar.MaterialToolbar import com.google.android.material.snackbar.Snackbar @@ -95,7 +93,6 @@ import java.util.* /** * Displays information about an Episode (FeedItem) and actions. */ -@UnstableApi class EpisodeInfoFragment : Fragment(), Toolbar.OnMenuItemClickListener { private var _binding: ComposeFragmentBinding? = null private val binding get() = _binding!! @@ -125,7 +122,7 @@ class EpisodeInfoFragment : Fragment(), Toolbar.OnMenuItemClickListener { private var actionButton1 by mutableStateOf(null) private var actionButton2 by mutableStateOf(null) - @UnstableApi + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View { super.onCreateView(inflater, container, savedInstanceState) @@ -307,7 +304,7 @@ class EpisodeInfoFragment : Fragment(), Toolbar.OnMenuItemClickListener { cancelFlowEvents() } - @OptIn(UnstableApi::class) + private fun showOnDemandConfigBalloon(offerStreaming: Boolean) { val isLocaleRtl = (TextUtils.getLayoutDirectionFromLocale(Locale.getDefault()) == View.LAYOUT_DIRECTION_RTL) val balloon: Balloon = Balloon.Builder(requireContext()) @@ -344,7 +341,7 @@ class EpisodeInfoFragment : Fragment(), Toolbar.OnMenuItemClickListener { // balloon.showAlignBottom(butAction1, 0, (-12 * resources.displayMetrics.density).toInt()) } - @UnstableApi + override fun onMenuItemClick(menuItem: MenuItem): Boolean { when (menuItem.itemId) { R.id.share_notes -> { @@ -383,7 +380,7 @@ class EpisodeInfoFragment : Fragment(), Toolbar.OnMenuItemClickListener { } } - @UnstableApi + override fun onResume() { super.onResume() if (itemLoaded) { @@ -392,7 +389,7 @@ class EpisodeInfoFragment : Fragment(), Toolbar.OnMenuItemClickListener { } } - @OptIn(UnstableApi::class) override fun onDestroyView() { + override fun onDestroyView() { Logd(TAG, "onDestroyView") episode = null // webvDescription.clearHistory() @@ -403,7 +400,7 @@ class EpisodeInfoFragment : Fragment(), Toolbar.OnMenuItemClickListener { super.onDestroyView() } - @UnstableApi + private fun onFragmentLoaded() { // if (!itemLoaded) // webvDescription.loadDataWithBaseURL("https://127.0.0.1", webviewData, "text/html", "utf-8", "about:blank") @@ -417,7 +414,7 @@ class EpisodeInfoFragment : Fragment(), Toolbar.OnMenuItemClickListener { // else EpisodeMenuHandler.onPrepareMenu(toolbar.menu, episode, R.id.open_podcast, R.id.mark_read_item, R.id.visit_website_item) // } - @UnstableApi + private fun updateAppearance() { if (episode == null) { Logd(TAG, "updateAppearance item is null") @@ -453,7 +450,7 @@ class EpisodeInfoFragment : Fragment(), Toolbar.OnMenuItemClickListener { updateButtons() } - @UnstableApi + private fun updateButtons() { // binding.circularProgressBar.visibility = View.GONE val dls = DownloadServiceInterface.get() @@ -503,7 +500,7 @@ class EpisodeInfoFragment : Fragment(), Toolbar.OnMenuItemClickListener { // return webvDescription.onContextItemSelected(item) // } - @OptIn(UnstableApi::class) private fun openPodcast() { + private fun openPodcast() { if (episode?.feedId == null) return val fragment: Fragment = FeedEpisodesFragment.newInstance(episode!!.feedId!!) @@ -585,7 +582,7 @@ class EpisodeInfoFragment : Fragment(), Toolbar.OnMenuItemClickListener { } private var loadItemsRunning = false - @UnstableApi + private fun load() { // if (!itemLoaded) binding.progbarLoading.visibility = View.VISIBLE Logd(TAG, "load() called") @@ -654,7 +651,7 @@ class EpisodeInfoFragment : Fragment(), Toolbar.OnMenuItemClickListener { private var tts: TextToSpeech? = null private var ttsReady = false - @UnstableApi + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View { super.onCreateView(inflater, container, savedInstanceState) _binding = EpisodeHomeFragmentBinding.inflate(inflater, container, false) @@ -681,13 +678,13 @@ class EpisodeInfoFragment : Fragment(), Toolbar.OnMenuItemClickListener { return binding.root } - @OptIn(UnstableApi::class) private fun switchMode() { + private fun switchMode() { readMode = !readMode showContent() updateAppearance() } - @OptIn(UnstableApi::class) private fun showReaderContent() { + private fun showReaderContent() { runOnIOScope { if (!episode?.link.isNullOrEmpty()) { if (cleanedNotes == null) { @@ -789,7 +786,7 @@ class EpisodeInfoFragment : Fragment(), Toolbar.OnMenuItemClickListener { onPrepareMenu(menu) } - @OptIn(UnstableApi::class) override fun onMenuItemSelected(menuItem: MenuItem): Boolean { + override fun onMenuItemSelected(menuItem: MenuItem): Boolean { when (menuItem.itemId) { R.id.switch_home -> { switchMode() @@ -843,7 +840,7 @@ class EpisodeInfoFragment : Fragment(), Toolbar.OnMenuItemClickListener { } } - @UnstableApi + override fun onResume() { super.onResume() updateAppearance() @@ -857,7 +854,7 @@ class EpisodeInfoFragment : Fragment(), Toolbar.OnMenuItemClickListener { webview.destroy() } - @OptIn(UnstableApi::class) override fun onDestroyView() { + override fun onDestroyView() { Logd(TAG, "onDestroyView") cleatWebview(binding.webView) cleatWebview(binding.readerView) @@ -868,7 +865,7 @@ class EpisodeInfoFragment : Fragment(), Toolbar.OnMenuItemClickListener { super.onDestroyView() } - @UnstableApi + private fun updateAppearance() { if (episode == null) { Logd(TAG, "updateAppearance currentItem is null") diff --git a/app/src/main/kotlin/ac/mdiq/podcini/ui/fragment/FeedEpisodesFragment.kt b/app/src/main/kotlin/ac/mdiq/podcini/ui/fragment/FeedEpisodesFragment.kt index ddd65c73..abc27526 100644 --- a/app/src/main/kotlin/ac/mdiq/podcini/ui/fragment/FeedEpisodesFragment.kt +++ b/app/src/main/kotlin/ac/mdiq/podcini/ui/fragment/FeedEpisodesFragment.kt @@ -56,7 +56,7 @@ import androidx.constraintlayout.compose.ConstraintLayout import androidx.constraintlayout.compose.Dimension import androidx.fragment.app.Fragment import androidx.lifecycle.lifecycleScope -import androidx.media3.common.util.UnstableApi + import coil.compose.AsyncImage import com.google.android.material.bottomsheet.BottomSheetBehavior import kotlinx.coroutines.* @@ -69,7 +69,7 @@ import java.util.concurrent.Semaphore /** * Displays a list of FeedItems. */ -@UnstableApi class FeedEpisodesFragment : Fragment(), Toolbar.OnMenuItemClickListener { + class FeedEpisodesFragment : Fragment(), Toolbar.OnMenuItemClickListener { private var _binding: ComposeFragmentBinding? = null private val binding get() = _binding!! @@ -110,7 +110,7 @@ import java.util.concurrent.Semaphore if (args != null) feedID = args.getLong(ARGUMENT_FEED_ID) } - @UnstableApi override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View { + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View { Logd(TAG, "fragment onCreateView") _binding = ComposeFragmentBinding.inflate(inflater) @@ -178,7 +178,10 @@ import java.util.concurrent.Semaphore Logd(TAG, "persist Episode Filter(): feedId = [${feed?.id}], filterValues = [$filterValues]") runOnIOScope { val feed_ = realm.query(Feed::class, "id == ${feed!!.id}").first().find() - if (feed_ != null) upsert(feed_) { it.preferences?.filterString = filterValues.joinToString() } + if (feed_ != null) { + upsert(feed_) { it.preferences?.filterString = filterValues.joinToString() } + loadFeed() + } } } } @@ -367,7 +370,7 @@ import java.util.concurrent.Semaphore super.onDestroyView() } -// @UnstableApi +// // private fun refreshPosCallback(pos: Int, episode: Episode) { // Logd(TAG, "FeedEpisode refreshPosCallback: $pos ${episode.title}") //// if (pos >= 0 && pos < episodes.size) episodes[pos] = episode @@ -396,7 +399,7 @@ import java.util.concurrent.Semaphore //// horizontalSpacing, binding.header.headerContainer.paddingBottom) // } - @UnstableApi override fun onMenuItemClick(item: MenuItem): Boolean { + override fun onMenuItemClick(item: MenuItem): Boolean { if (feed == null) { (activity as MainActivity).showSnackbarAbovePlayer(R.string.please_wait_for_data, Toast.LENGTH_LONG) return true @@ -547,7 +550,7 @@ import java.util.concurrent.Semaphore rightActionState.value = swipeActions.actions.right[0] } - @UnstableApi private fun refreshHeaderView() { + private fun refreshHeaderView() { setupHeaderView() if (feed == null) { Log.e(TAG, "Unable to refresh header view") @@ -577,7 +580,7 @@ import java.util.concurrent.Semaphore infoBarText.value = "$infoTextFiltered $infoTextUpdate" } - @UnstableApi private fun setupHeaderView() { + private fun setupHeaderView() { if (feed == null || headerCreated) return // binding.imgvBackground.colorFilter = LightingColorFilter(-0x99999a, 0x000000) @@ -602,7 +605,7 @@ import java.util.concurrent.Semaphore // } // } -// @UnstableApi private fun showFeedInfo() { +// private fun showFeedInfo() { // if (feed != null) { // val fragment = FeedInfoFragment.newInstance(feed!!) // (activity as MainActivity).loadChildFragment(fragment, TransitionEffect.SLIDE) @@ -627,18 +630,7 @@ import java.util.concurrent.Semaphore } return false } - -// private fun redoFilter(list: List? = null) { -// if (enableFilter && !feed?.preferences?.filterString.isNullOrEmpty()) { -// val episodes_ = list ?: episodes.toList() -// episodes.clear() -// episodes.addAll(episodes_.filter { feed!!.episodeFilter.matches(it) }) -// ieMap = episodes.withIndex().associate { (index, episode) -> episode.id to index } -// ueMap = episodes.mapIndexedNotNull { index, episode -> episode.media?.downloadUrl?.let { it to index } }.toMap() -// } -// } - - @UnstableApi + private fun loadFeed() { if (!loadItemsRunning) { loadItemsRunning = true @@ -650,6 +642,7 @@ import java.util.concurrent.Semaphore Logd(TAG, "loadItems feed_.episodes.size: ${feed_.episodes.size}") val etmp = mutableListOf() if (enableFilter && !feed_.preferences?.filterString.isNullOrEmpty()) { + Logd(TAG, "episodeFilter: ${feed_.episodeFilter.queryString()}") val episodes_ = realm.query(Episode::class).query("feedId == ${feed_.id}").query(feed_.episodeFilter.queryString()).find() // val episodes_ = feed_.episodes.filter { feed_.episodeFilter.matches(it) } etmp.addAll(episodes_) @@ -719,7 +712,7 @@ import java.util.concurrent.Semaphore } // class FeedEpisodeFilterDialog(val feed: Feed?) : EpisodeFilterDialog() { -// @OptIn(UnstableApi::class) override fun onFilterChanged(newFilterValues: Set) { +// override fun onFilterChanged(newFilterValues: Set) { // if (feed != null) { // Logd(TAG, "persist Episode Filter(): feedId = [$feed.id], filterValues = [$newFilterValues]") // runOnIOScope { @@ -747,7 +740,7 @@ import java.util.concurrent.Semaphore super.onAddItem(title, ascending, descending, ascendingIsDefault) } } - @UnstableApi override fun onSelectionChanged() { + override fun onSelectionChanged() { super.onSelectionChanged() if (feed != null) { Logd(TAG, "persist Episode SortOrder") diff --git a/app/src/main/kotlin/ac/mdiq/podcini/ui/fragment/FeedInfoFragment.kt b/app/src/main/kotlin/ac/mdiq/podcini/ui/fragment/FeedInfoFragment.kt index e5d43c08..05105775 100644 --- a/app/src/main/kotlin/ac/mdiq/podcini/ui/fragment/FeedInfoFragment.kt +++ b/app/src/main/kotlin/ac/mdiq/podcini/ui/fragment/FeedInfoFragment.kt @@ -70,7 +70,7 @@ import androidx.documentfile.provider.DocumentFile import androidx.fragment.app.Fragment import androidx.fragment.compose.AndroidFragment import androidx.lifecycle.lifecycleScope -import androidx.media3.common.util.UnstableApi + import coil.compose.AsyncImage import com.google.android.material.appbar.MaterialToolbar import com.google.android.material.dialog.MaterialAlertDialogBuilder @@ -82,7 +82,7 @@ import java.lang.ref.WeakReference import java.util.* import java.util.concurrent.ExecutionException -@UnstableApi + class FeedInfoFragment : Fragment(), Toolbar.OnMenuItemClickListener { private var _binding: ComposeFragmentBinding? = null private val binding get() = _binding!! @@ -382,7 +382,7 @@ class FeedInfoFragment : Fragment(), Toolbar.OnMenuItemClickListener { return true } - @UnstableApi private fun addLocalFolderResult(uri: Uri?) { + private fun addLocalFolderResult(uri: Uri?) { if (uri == null) return // reconnectLocalFolder(uri) lifecycleScope.launch { @@ -403,7 +403,7 @@ class FeedInfoFragment : Fragment(), Toolbar.OnMenuItemClickListener { } } -// @UnstableApi private fun reconnectLocalFolder(uri: Uri) { +// private fun reconnectLocalFolder(uri: Uri) { // lifecycleScope.launch { // try { // withContext(Dispatchers.IO) { @@ -450,7 +450,7 @@ class FeedInfoFragment : Fragment(), Toolbar.OnMenuItemClickListener { } } - @UnstableApi + abstract class EditUrlSettingsDialog(activity: Activity, private val feed: Feed) { val TAG = this::class.simpleName ?: "Anonymous" private val activityRef = WeakReference(activity) @@ -466,7 +466,7 @@ class FeedInfoFragment : Fragment(), Toolbar.OnMenuItemClickListener { .setNegativeButton(R.string.cancel_label, null) .show() } - @UnstableApi private fun onConfirmed(original: String, updated: String) { + private fun onConfirmed(original: String, updated: String) { try { runBlocking { updateFeedDownloadURL(original, updated).join() } feed.downloadUrl = updated @@ -474,7 +474,7 @@ class FeedInfoFragment : Fragment(), Toolbar.OnMenuItemClickListener { } catch (e: ExecutionException) { throw RuntimeException(e) } catch (e: InterruptedException) { throw RuntimeException(e) } } - @UnstableApi private fun showConfirmAlertDialog(url: String) { + private fun showConfirmAlertDialog(url: String) { val activity = activityRef.get() val alertDialog = MaterialAlertDialogBuilder(activity!!) .setTitle(R.string.edit_url_menu) diff --git a/app/src/main/kotlin/ac/mdiq/podcini/ui/fragment/FeedSettingsFragment.kt b/app/src/main/kotlin/ac/mdiq/podcini/ui/fragment/FeedSettingsFragment.kt index 531e2349..a5ff3374 100644 --- a/app/src/main/kotlin/ac/mdiq/podcini/ui/fragment/FeedSettingsFragment.kt +++ b/app/src/main/kotlin/ac/mdiq/podcini/ui/fragment/FeedSettingsFragment.kt @@ -54,7 +54,7 @@ import androidx.compose.ui.text.input.KeyboardType import androidx.compose.ui.unit.dp import androidx.compose.ui.window.Dialog import androidx.fragment.app.Fragment -import androidx.media3.common.util.UnstableApi + import androidx.recyclerview.widget.GridLayoutManager import com.google.android.material.dialog.MaterialAlertDialogBuilder import java.util.* @@ -344,7 +344,7 @@ class FeedSettingsFragment : Fragment() { object : AutoDownloadFilterPrefDialog(requireContext(), feed?.preferences!!.autoDownloadFilter!!, 1) { - @UnstableApi + override fun onConfirmed(filter: FeedAutoDownloadFilter) { feed = upsertBlk(feed!!) { it.preferences?.autoDownloadFilter = filter @@ -364,7 +364,7 @@ class FeedSettingsFragment : Fragment() { object : AutoDownloadFilterPrefDialog(requireContext(), feed?.preferences!!.autoDownloadFilter!!, -1) { - @UnstableApi + override fun onConfirmed(filter: FeedAutoDownloadFilter) { feed = upsertBlk(feed!!) { it.preferences?.autoDownloadFilter = filter diff --git a/app/src/main/kotlin/ac/mdiq/podcini/ui/fragment/HistoryFragment.kt b/app/src/main/kotlin/ac/mdiq/podcini/ui/fragment/HistoryFragment.kt index ca01e382..920bf9d2 100644 --- a/app/src/main/kotlin/ac/mdiq/podcini/ui/fragment/HistoryFragment.kt +++ b/app/src/main/kotlin/ac/mdiq/podcini/ui/fragment/HistoryFragment.kt @@ -20,16 +20,14 @@ import android.view.LayoutInflater import android.view.MenuItem import android.view.View import android.view.ViewGroup -import androidx.annotation.OptIn import androidx.lifecycle.lifecycleScope -import androidx.media3.common.util.UnstableApi import kotlinx.coroutines.Job import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.launch import java.util.* import kotlin.math.min -@UnstableApi class HistoryFragment : BaseEpisodesFragment() { +class HistoryFragment : BaseEpisodesFragment() { private var sortOrder : EpisodeSortOrder = EpisodeSortOrder.PLAYED_DATE_NEW_OLD private var startDate : Long = 0L @@ -40,7 +38,7 @@ import kotlin.math.min return TAG } - @UnstableApi override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View { + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View { val root = super.onCreateView(inflater, container, savedInstanceState) Logd(TAG, "fragment onCreateView") toolbar.inflateMenu(R.menu.playback_history) @@ -83,7 +81,7 @@ import kotlin.math.min super.onDestroyView() } - @OptIn(UnstableApi::class) override fun onMenuItemClick(item: MenuItem): Boolean { + override fun onMenuItemClick(item: MenuItem): Boolean { if (super.onOptionsItemSelected(item)) return true when (item.itemId) { R.id.episodes_sort -> HistorySortDialog().show(childFragmentManager.beginTransaction(), "SortDialog") diff --git a/app/src/main/kotlin/ac/mdiq/podcini/ui/fragment/OnlineFeedFragment.kt b/app/src/main/kotlin/ac/mdiq/podcini/ui/fragment/OnlineFeedFragment.kt index 82de7a54..234417a5 100644 --- a/app/src/main/kotlin/ac/mdiq/podcini/ui/fragment/OnlineFeedFragment.kt +++ b/app/src/main/kotlin/ac/mdiq/podcini/ui/fragment/OnlineFeedFragment.kt @@ -39,7 +39,6 @@ import android.view.LayoutInflater import android.view.MenuItem import android.view.View import android.view.ViewGroup -import androidx.annotation.OptIn import androidx.annotation.UiThread import androidx.collection.ArrayMap import androidx.compose.foundation.* @@ -59,7 +58,6 @@ import androidx.compose.ui.unit.dp import androidx.constraintlayout.compose.ConstraintLayout import androidx.fragment.app.Fragment import androidx.lifecycle.lifecycleScope -import androidx.media3.common.util.UnstableApi import coil.compose.AsyncImage import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.google.android.material.snackbar.Snackbar @@ -79,7 +77,7 @@ import kotlin.concurrent.Volatile * If the feed cannot be downloaded or parsed, an error dialog will be displayed * and the activity will finish as soon as the error dialog is closed. */ -@OptIn(UnstableApi::class) + class OnlineFeedFragment : Fragment() { private var _binding: ComposeFragmentBinding? = null private val binding get() = _binding!! @@ -122,7 +120,7 @@ class OnlineFeedFragment : Fragment() { private var dialog: Dialog? = null - @OptIn(UnstableApi::class) override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View { + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View { Logd(TAG, "fragment onCreateView") _binding = ComposeFragmentBinding.inflate(layoutInflater) displayUpArrow = parentFragmentManager.backStackEntryCount != 0 @@ -176,7 +174,7 @@ class OnlineFeedFragment : Fragment() { super.onDestroy() } - @OptIn(UnstableApi::class) override fun onSaveInstanceState(outState: Bundle) { + override fun onSaveInstanceState(outState: Bundle) { outState.putBoolean(KEY_UP_ARROW, displayUpArrow) super.onSaveInstanceState(outState) outState.putString("username", username) @@ -267,7 +265,7 @@ class OnlineFeedFragment : Fragment() { eventStickySink?.cancel() eventStickySink = null } - @OptIn(UnstableApi::class) private fun procFlowEvents() { + private fun procFlowEvents() { if (eventSink == null) eventSink = lifecycleScope.launch { EventFlow.events.collectLatest { event -> Logd(TAG, "Received event: ${event.TAG}") @@ -307,7 +305,7 @@ class OnlineFeedFragment : Fragment() { * Called when feed parsed successfully. * This method is executed on the GUI thread. */ - @UnstableApi private fun showFeedInformation(feed: Feed, alternateFeedUrls: Map) { + private fun showFeedInformation(feed: Feed, alternateFeedUrls: Map) { showProgress = false // binding.feedDisplayContainer.visibility = View.VISIBLE showFeedDisplay = true @@ -480,7 +478,7 @@ class OnlineFeedFragment : Fragment() { } } - @UnstableApi private fun showEpisodes(episodes: MutableList) { + private fun showEpisodes(episodes: MutableList) { Logd(TAG, "showEpisodes ${episodes.size}") if (episodes.isEmpty()) return episodes.sortByDescending { it.pubDate } @@ -494,7 +492,7 @@ class OnlineFeedFragment : Fragment() { (activity as MainActivity).loadChildFragment(fragment) } - @UnstableApi private fun handleUpdatedFeedStatus() { + private fun handleUpdatedFeedStatus() { val dli = DownloadServiceInterface.get() if (dli == null || selectedDownloadUrl == null) return @@ -708,11 +706,11 @@ class OnlineFeedFragment : Fragment() { } } - @UnstableApi + class RemoteEpisodesFragment : BaseEpisodesFragment() { private val episodeList: MutableList = mutableListOf() - @UnstableApi override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View { + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View { val root = super.onCreateView(inflater, container, savedInstanceState) Logd(TAG, "fragment onCreateView") toolbar.inflateMenu(R.menu.episodes) @@ -746,7 +744,7 @@ class OnlineFeedFragment : Fragment() { binding.toolbar.menu.findItem(R.id.filter_items).setVisible(false) infoBarText.value = "${episodes.size} episodes" } - @OptIn(UnstableApi::class) override fun onMenuItemClick(item: MenuItem): Boolean { + override fun onMenuItemClick(item: MenuItem): Boolean { if (super.onOptionsItemSelected(item)) return true when (item.itemId) { else -> return false diff --git a/app/src/main/kotlin/ac/mdiq/podcini/ui/fragment/OnlineSearchFragment.kt b/app/src/main/kotlin/ac/mdiq/podcini/ui/fragment/OnlineSearchFragment.kt index 5a52dd8e..ce420eee 100644 --- a/app/src/main/kotlin/ac/mdiq/podcini/ui/fragment/OnlineSearchFragment.kt +++ b/app/src/main/kotlin/ac/mdiq/podcini/ui/fragment/OnlineSearchFragment.kt @@ -28,7 +28,7 @@ import androidx.activity.result.contract.ActivityResultContracts import androidx.appcompat.app.AlertDialog import androidx.documentfile.provider.DocumentFile import androidx.fragment.app.Fragment -import androidx.media3.common.util.UnstableApi + import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.google.android.material.snackbar.Snackbar import kotlinx.coroutines.CoroutineScope @@ -39,7 +39,7 @@ import kotlinx.coroutines.withContext /** * Provides actions for adding new podcast subscriptions. */ -@UnstableApi + class OnlineSearchFragment : Fragment() { private var _binding: AddfeedBinding? = null @@ -163,7 +163,7 @@ class OnlineSearchFragment : Fragment() { startActivity(intent) } - @UnstableApi private fun addLocalFolderResult(uri: Uri?) { + private fun addLocalFolderResult(uri: Uri?) { if (uri == null) return val scope = CoroutineScope(Dispatchers.Main) scope.launch { @@ -182,7 +182,7 @@ class OnlineSearchFragment : Fragment() { } } - @UnstableApi private fun addLocalFolder(uri: Uri): Feed? { + private fun addLocalFolder(uri: Uri): Feed? { requireActivity().contentResolver.takePersistableUriPermission(uri, Intent.FLAG_GRANT_READ_URI_PERMISSION or Intent.FLAG_GRANT_WRITE_URI_PERMISSION) val documentFile = DocumentFile.fromTreeUri(requireContext(), uri) requireNotNull(documentFile) { "Unable to retrieve document tree" } diff --git a/app/src/main/kotlin/ac/mdiq/podcini/ui/fragment/QueuesFragment.kt b/app/src/main/kotlin/ac/mdiq/podcini/ui/fragment/QueuesFragment.kt index 2cbfeb29..4e1e57d4 100644 --- a/app/src/main/kotlin/ac/mdiq/podcini/ui/fragment/QueuesFragment.kt +++ b/app/src/main/kotlin/ac/mdiq/podcini/ui/fragment/QueuesFragment.kt @@ -67,7 +67,7 @@ import androidx.compose.ui.unit.dp import androidx.compose.ui.window.Dialog import androidx.fragment.app.Fragment import androidx.lifecycle.lifecycleScope -import androidx.media3.common.util.UnstableApi + import androidx.media3.session.MediaBrowser import androidx.media3.session.SessionToken import com.google.android.material.appbar.MaterialToolbar @@ -83,7 +83,7 @@ import kotlinx.coroutines.runBlocking import java.util.* import kotlin.math.max -@UnstableApi class QueuesFragment : Fragment(), Toolbar.OnMenuItemClickListener { + class QueuesFragment : Fragment(), Toolbar.OnMenuItemClickListener { private var _binding: ComposeFragmentBinding? = null private val binding get() = _binding!! @@ -125,7 +125,7 @@ import kotlin.math.max retainInstance = true } - @UnstableApi override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View { + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View { super.onCreateView(inflater, container, savedInstanceState) _binding = ComposeFragmentBinding.inflate(inflater) @@ -448,7 +448,7 @@ import kotlin.math.max } } - @UnstableApi override fun onMenuItemClick(item: MenuItem): Boolean { + override fun onMenuItemClick(item: MenuItem): Boolean { val itemId = item.itemId when (itemId) { R.id.show_bin -> { @@ -478,7 +478,7 @@ import kotlin.math.max R.id.clear_queue -> { // make sure the user really wants to clear the queue val conDialog: ConfirmationDialog = object : ConfirmationDialog(requireContext(), R.string.clear_queue_label, R.string.clear_queue_confirmation_msg) { - @UnstableApi override fun onConfirmButtonPressed(dialog: DialogInterface) { + override fun onConfirmButtonPressed(dialog: DialogInterface) { dialog.dismiss() clearQueue() } @@ -609,7 +609,7 @@ import kotlin.math.max } } - @UnstableApi private fun toggleQueueLock() { + private fun toggleQueueLock() { val isLocked: Boolean = isQueueLocked if (isLocked) setQueueLock(false) else { @@ -635,7 +635,7 @@ import kotlin.math.max } } - @UnstableApi private fun setQueueLock(locked: Boolean) { + private fun setQueueLock(locked: Boolean) { isQueueLocked = locked appPrefs.edit().putBoolean(UserPreferences.Prefs.prefQueueLocked.name, locked).apply() dragDropEnabled = !(isQueueKeepSorted || isQueueLocked) @@ -711,7 +711,7 @@ import kotlin.math.max if (ascending != EpisodeSortOrder.EPISODE_FILENAME_A_Z && ascending != EpisodeSortOrder.SIZE_SMALL_LARGE) super.onAddItem(title, ascending, descending, ascendingIsDefault) } - @UnstableApi override fun onSelectionChanged() { + override fun onSelectionChanged() { super.onSelectionChanged() binding.keepSortedCheckbox.setEnabled(sortOrder != EpisodeSortOrder.RANDOM) if (sortOrder == EpisodeSortOrder.RANDOM) binding.keepSortedCheckbox.setChecked(false) diff --git a/app/src/main/kotlin/ac/mdiq/podcini/ui/fragment/QuickDiscoveryFragment.kt b/app/src/main/kotlin/ac/mdiq/podcini/ui/fragment/QuickDiscoveryFragment.kt index de1d35a6..7730ea8b 100644 --- a/app/src/main/kotlin/ac/mdiq/podcini/ui/fragment/QuickDiscoveryFragment.kt +++ b/app/src/main/kotlin/ac/mdiq/podcini/ui/fragment/QuickDiscoveryFragment.kt @@ -25,7 +25,6 @@ import android.view.MenuItem import android.view.View import android.view.ViewGroup import android.widget.* -import androidx.annotation.OptIn import androidx.appcompat.widget.Toolbar import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.fillMaxSize @@ -44,7 +43,6 @@ import androidx.compose.ui.unit.dp import androidx.constraintlayout.compose.ConstraintLayout import androidx.fragment.app.Fragment import androidx.lifecycle.lifecycleScope -import androidx.media3.common.util.UnstableApi import coil.load import com.google.android.material.appbar.MaterialToolbar import com.google.android.material.dialog.MaterialAlertDialogBuilder @@ -68,7 +66,7 @@ class QuickDiscoveryFragment : Fragment(), AdapterView.OnItemClickListener { private lateinit var errorView: LinearLayout private lateinit var errorRetry: Button - @OptIn(UnstableApi::class) override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View { + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View { super.onCreateView(inflater, container, savedInstanceState) _binding = QuickFeedDiscoveryBinding.inflate(inflater) @@ -195,7 +193,7 @@ class QuickDiscoveryFragment : Fragment(), AdapterView.OnItemClickListener { } } - @OptIn(UnstableApi::class) override fun onItemClick(parent: AdapterView<*>?, view: View, position: Int, id: Long) { + override fun onItemClick(parent: AdapterView<*>?, view: View, position: Int, id: Long) { val podcast: PodcastSearchResult? = adapter.getItem(position) if (podcast?.feedUrl.isNullOrEmpty()) return @@ -294,7 +292,7 @@ class QuickDiscoveryFragment : Fragment(), AdapterView.OnItemClickListener { needsConfirm = prefs!!.getBoolean(ItunesTopListLoader.PREF_KEY_NEEDS_CONFIRM, true) } - @OptIn(UnstableApi::class) override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View { + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View { // Inflate the layout for this fragment _binding = ComposeFragmentBinding.inflate(inflater) Logd(TAG, "fragment onCreateView") diff --git a/app/src/main/kotlin/ac/mdiq/podcini/ui/fragment/SearchFragment.kt b/app/src/main/kotlin/ac/mdiq/podcini/ui/fragment/SearchFragment.kt index be975542..8ae38f81 100644 --- a/app/src/main/kotlin/ac/mdiq/podcini/ui/fragment/SearchFragment.kt +++ b/app/src/main/kotlin/ac/mdiq/podcini/ui/fragment/SearchFragment.kt @@ -49,7 +49,7 @@ import androidx.compose.ui.unit.dp import androidx.constraintlayout.compose.ConstraintLayout import androidx.fragment.app.Fragment import androidx.lifecycle.lifecycleScope -import androidx.media3.common.util.UnstableApi + import coil.compose.AsyncImage import coil.request.CachePolicy import coil.request.ImageRequest @@ -65,7 +65,7 @@ import java.text.NumberFormat /** * Performs a search operation on all feeds or one specific feed and displays the search result. */ -@UnstableApi + class SearchFragment : Fragment() { private var _binding: SearchFragmentBinding? = null private val binding get() = _binding!! @@ -87,7 +87,7 @@ class SearchFragment : Fragment() { automaticSearchDebouncer = Handler(Looper.getMainLooper()) } - @UnstableApi override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View { + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View { _binding = SearchFragmentBinding.inflate(inflater) Logd(TAG, "fragment onCreateView") setupToolbar(binding.toolbar) @@ -148,12 +148,12 @@ class SearchFragment : Fragment() { searchView.setQuery(requireArguments().getString(ARG_QUERY), true) searchView.requestFocus() searchView.setOnQueryTextListener(object : SearchView.OnQueryTextListener { - @UnstableApi override fun onQueryTextSubmit(s: String): Boolean { + override fun onQueryTextSubmit(s: String): Boolean { searchView.clearFocus() search() return true } - @UnstableApi override fun onQueryTextChange(s: String): Boolean { + override fun onQueryTextChange(s: String): Boolean { automaticSearchDebouncer.removeCallbacksAndMessages(null) if (s.isEmpty() || s.endsWith(" ") || (lastQueryChange != 0L && System.currentTimeMillis() > lastQueryChange + SEARCH_DEBOUNCE_INTERVAL)) search() @@ -233,7 +233,7 @@ class SearchFragment : Fragment() { } @SuppressLint("StringFormatMatches") - @UnstableApi private fun search() { + private fun search() { // adapterFeeds.setEndButton(R.string.search_online) { this.searchOnline() } chip.visibility = if ((requireArguments().getLong(ARG_FEED, 0) == 0L)) View.GONE else View.VISIBLE @@ -344,7 +344,7 @@ class SearchFragment : Fragment() { } } - @UnstableApi private fun performSearch(): Pair, List> { + private fun performSearch(): Pair, List> { val query = searchView.query.toString() if (query.isEmpty()) return Pair, List>(emptyList(), emptyList()) @@ -433,7 +433,7 @@ class SearchFragment : Fragment() { imm.showSoftInput(view, 0) } - @UnstableApi private fun searchOnline() { + private fun searchOnline() { searchView.clearFocus() val inVal = requireActivity().getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager inVal.hideSoftInputFromWindow(searchView.windowToken, 0) diff --git a/app/src/main/kotlin/ac/mdiq/podcini/ui/fragment/SearchResultsFragment.kt b/app/src/main/kotlin/ac/mdiq/podcini/ui/fragment/SearchResultsFragment.kt index 2b494745..896b5f76 100644 --- a/app/src/main/kotlin/ac/mdiq/podcini/ui/fragment/SearchResultsFragment.kt +++ b/app/src/main/kotlin/ac/mdiq/podcini/ui/fragment/SearchResultsFragment.kt @@ -40,7 +40,7 @@ import androidx.compose.ui.unit.dp import androidx.constraintlayout.compose.ConstraintLayout import androidx.fragment.app.Fragment import androidx.lifecycle.lifecycleScope -import androidx.media3.common.util.UnstableApi + import com.google.android.material.appbar.MaterialToolbar import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch @@ -73,7 +73,7 @@ class SearchResultsFragment : Fragment() { if (searchProvider == null) Logd(TAG,"Podcast searcher not found") } - @UnstableApi override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View { + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View { _binding = ComposeFragmentBinding.inflate(inflater) Logd(TAG, "fragment onCreateView") binding.mainView.setContent { diff --git a/app/src/main/kotlin/ac/mdiq/podcini/ui/fragment/SubscriptionsFragment.kt b/app/src/main/kotlin/ac/mdiq/podcini/ui/fragment/SubscriptionsFragment.kt index 366bc59c..d0e40cd0 100644 --- a/app/src/main/kotlin/ac/mdiq/podcini/ui/fragment/SubscriptionsFragment.kt +++ b/app/src/main/kotlin/ac/mdiq/podcini/ui/fragment/SubscriptionsFragment.kt @@ -17,6 +17,7 @@ import ac.mdiq.podcini.storage.database.Feeds.getTags import ac.mdiq.podcini.storage.database.RealmDB.realm import ac.mdiq.podcini.storage.database.RealmDB.runOnIOScope import ac.mdiq.podcini.storage.database.RealmDB.upsert +import ac.mdiq.podcini.storage.database.RealmDB.upsertBlk import ac.mdiq.podcini.storage.model.* import ac.mdiq.podcini.storage.model.FeedPreferences.AutoDeleteAction import ac.mdiq.podcini.storage.model.FeedPreferences.Companion.FeedAutoDeleteOptions @@ -49,7 +50,6 @@ import android.widget.ArrayAdapter import android.widget.CompoundButton import androidx.activity.result.ActivityResult import androidx.activity.result.contract.ActivityResultContracts -import androidx.annotation.OptIn import androidx.appcompat.widget.Toolbar import androidx.compose.foundation.* import androidx.compose.foundation.layout.* @@ -88,7 +88,6 @@ import androidx.constraintlayout.compose.ConstraintLayout import androidx.core.util.Consumer import androidx.fragment.app.Fragment import androidx.lifecycle.lifecycleScope -import androidx.media3.common.util.UnstableApi import coil.compose.AsyncImage import coil.request.CachePolicy import coil.request.ImageRequest @@ -145,7 +144,7 @@ class SubscriptionsFragment : Fragment(), Toolbar.OnMenuItemClickListener { retainInstance = true } - @UnstableApi override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View { + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View { _binding = FragmentSubscriptionsBinding.inflate(inflater) Logd(TAG, "fragment onCreateView") @@ -306,7 +305,7 @@ class SubscriptionsFragment : Fragment(), Toolbar.OnMenuItemClickListener { } } - @UnstableApi override fun onMenuItemClick(item: MenuItem): Boolean { + override fun onMenuItemClick(item: MenuItem): Boolean { val itemId = item.itemId when (itemId) { R.id.subscriptions_filter -> { @@ -334,7 +333,6 @@ class SubscriptionsFragment : Fragment(), Toolbar.OnMenuItemClickListener { } private var loadItemsRunning = false - @OptIn(UnstableApi::class) private fun loadSubscriptions() { // emptyView.hide() if (!loadItemsRunning) { @@ -653,7 +651,10 @@ class SubscriptionsFragment : Fragment(), Toolbar.OnMenuItemClickListener { Column(modifier = Modifier.padding(16.dp), verticalArrangement = Arrangement.spacedBy(16.dp)) { for (rating in Rating.entries.reversed()) { Row(verticalAlignment = Alignment.CenterVertically, modifier = Modifier.padding(4.dp).clickable { - for (item in selected) Feeds.setRating(item, rating.code) + for (item in selected) { +// Feeds.setRating(item, rating.code) + upsertBlk(item) { it.rating = rating.code } + } onDismissRequest() }) { Icon(imageVector = ImageVector.vectorResource(id = rating.res), "") @@ -711,7 +712,7 @@ class SubscriptionsFragment : Fragment(), Toolbar.OnMenuItemClickListener { selectMode = false Logd(TAG, "ic_download: ${selected.size}") val preferenceSwitchDialog = PreferenceSwitchDialog(activity, activity.getString(R.string.auto_download_settings_label), activity.getString(R.string.auto_download_label)) - preferenceSwitchDialog.setOnPreferenceChangedListener(@UnstableApi object: PreferenceSwitchDialog.OnPreferenceChangedListener { + preferenceSwitchDialog.setOnPreferenceChangedListener( object: PreferenceSwitchDialog.OnPreferenceChangedListener { override fun preferenceChanged(enabled: Boolean) { saveFeedPreferences { it: FeedPreferences -> it.autoDownload = enabled } } diff --git a/app/src/main/kotlin/ac/mdiq/podcini/ui/statistics/StatisticsFragment.kt b/app/src/main/kotlin/ac/mdiq/podcini/ui/statistics/StatisticsFragment.kt index b547b772..6f3b4f50 100644 --- a/app/src/main/kotlin/ac/mdiq/podcini/ui/statistics/StatisticsFragment.kt +++ b/app/src/main/kotlin/ac/mdiq/podcini/ui/statistics/StatisticsFragment.kt @@ -8,15 +8,15 @@ import ac.mdiq.podcini.storage.database.RealmDB.realm import ac.mdiq.podcini.storage.database.RealmDB.runOnIOScope import ac.mdiq.podcini.storage.database.RealmDB.update import ac.mdiq.podcini.storage.model.* +import ac.mdiq.podcini.storage.utils.DurationConverter.shortLocalizedDuration import ac.mdiq.podcini.ui.activity.MainActivity import ac.mdiq.podcini.ui.activity.starter.MainActivityStarter import ac.mdiq.podcini.ui.dialog.ConfirmationDialog import ac.mdiq.podcini.ui.dialog.DatesFilterDialog import ac.mdiq.podcini.ui.statistics.PieChartView.PieChartData -import ac.mdiq.podcini.storage.utils.DurationConverter.shortLocalizedDuration -import ac.mdiq.podcini.util.Logd import ac.mdiq.podcini.util.EventFlow import ac.mdiq.podcini.util.FlowEvent +import ac.mdiq.podcini.util.Logd import android.annotation.SuppressLint import android.app.Dialog import android.content.Context @@ -30,11 +30,9 @@ import android.view.* import android.widget.ImageView import android.widget.ProgressBar import android.widget.TextView -import androidx.annotation.OptIn import androidx.fragment.app.DialogFragment import androidx.fragment.app.Fragment import androidx.lifecycle.lifecycleScope -import androidx.media3.common.util.UnstableApi import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView import androidx.viewpager2.adapter.FragmentStateAdapter @@ -64,7 +62,7 @@ class StatisticsFragment : Fragment() { private var _binding: PagerFragmentBinding? = null private val binding get() = _binding!! - @OptIn(UnstableApi::class) override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View { + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View { super.onCreateView(inflater, container, savedInstanceState) setHasOptionsMenu(true) _binding = PagerFragmentBinding.inflate(inflater) @@ -98,7 +96,7 @@ class StatisticsFragment : Fragment() { } @Deprecated("Deprecated in Java") - @UnstableApi override fun onOptionsItemSelected(item: MenuItem): Boolean { + override fun onOptionsItemSelected(item: MenuItem): Boolean { if (item.itemId == R.id.statistics_reset) { confirmResetStatistics() return true @@ -133,7 +131,7 @@ class StatisticsFragment : Fragment() { }) } - @UnstableApi private fun confirmResetStatistics() { + private fun confirmResetStatistics() { val conDialog: ConfirmationDialog = object : ConfirmationDialog(requireContext(), R.string.statistics_reset_data, R.string.statistics_reset_data_msg) { override fun onConfirmButtonPressed(dialog: DialogInterface) { @@ -144,7 +142,7 @@ class StatisticsFragment : Fragment() { conDialog.createNewDialog().show() } - @UnstableApi private fun doResetStatistics() { + private fun doResetStatistics() { prefs!!.edit() .putBoolean(PREF_INCLUDE_MARKED_PLAYED, false) .putLong(PREF_FILTER_FROM, 0) diff --git a/app/src/main/kotlin/ac/mdiq/podcini/ui/view/ShownotesWebView.kt b/app/src/main/kotlin/ac/mdiq/podcini/ui/view/ShownotesWebView.kt index c6095b13..c2d57de2 100644 --- a/app/src/main/kotlin/ac/mdiq/podcini/ui/view/ShownotesWebView.kt +++ b/app/src/main/kotlin/ac/mdiq/podcini/ui/view/ShownotesWebView.kt @@ -25,7 +25,7 @@ import android.webkit.WebView import android.webkit.WebViewClient import androidx.core.content.ContextCompat import androidx.core.util.Consumer -import androidx.media3.common.util.UnstableApi + import com.google.android.material.snackbar.Snackbar import kotlin.math.max @@ -75,7 +75,7 @@ class ShownotesWebView : WebView, View.OnLongClickListener { }) } - @UnstableApi override fun onLongClick(v: View): Boolean { + override fun onLongClick(v: View): Boolean { val r: HitTestResult = getHitTestResult() when (r.type) { HitTestResult.SRC_ANCHOR_TYPE -> { diff --git a/app/src/main/kotlin/ac/mdiq/podcini/util/config/ClientConfigurator.kt b/app/src/main/kotlin/ac/mdiq/podcini/util/config/ClientConfigurator.kt index 4b011c5a..b9c2c5b2 100644 --- a/app/src/main/kotlin/ac/mdiq/podcini/util/config/ClientConfigurator.kt +++ b/app/src/main/kotlin/ac/mdiq/podcini/util/config/ClientConfigurator.kt @@ -14,10 +14,10 @@ import ac.mdiq.podcini.preferences.UserPreferences import ac.mdiq.podcini.preferences.UserPreferences.proxyConfig import ac.mdiq.podcini.ui.utils.NotificationUtils import android.content.Context -import androidx.media3.common.util.UnstableApi + import java.io.File -@UnstableApi + object ClientConfigurator { private var initialized = false diff --git a/app/src/main/res/drawable/baseline_comment_24.xml b/app/src/main/res/drawable/baseline_comment_24.xml new file mode 100644 index 00000000..5972c48a --- /dev/null +++ b/app/src/main/res/drawable/baseline_comment_24.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 918dd22a..fd86cc38 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -273,6 +273,7 @@ JavaScript No action My opinion + Add opinion Cancelled on Set played state diff --git a/changelog.md b/changelog.md index ed25973e..afc9c50f 100644 --- a/changelog.md +++ b/changelog.md @@ -1,3 +1,13 @@ +# 6.13.3 + +* on playing the next episode in queue, if its state is lower than InProgress, the state is set as such +* added "add commment" in swipe actions +* in FeedEpisodes, fixed incorrect ordering of episodes after filtering +* when removing episode media, if remove-from-queue is set, the episode is removed from all queues rather than only the active queue +* removed the @UnstableAPI annotations previously required for Media3 +* updated Contributing.md +* various dependencies update + # 6.13.2 * replaced the setting of prefSmartMarkAsPlayedSecs by an adaptive internal val smartMarkAsPlayedPercent = 95 diff --git a/fastlane/metadata/android/en-US/changelogs/3020289.txt b/fastlane/metadata/android/en-US/changelogs/3020289.txt index e7bd4062..fb1120d6 100644 --- a/fastlane/metadata/android/en-US/changelogs/3020289.txt +++ b/fastlane/metadata/android/en-US/changelogs/3020289.txt @@ -1,4 +1,4 @@ - Version 6.13.1 + Version 6.13.2 * replaced the setting of prefSmartMarkAsPlayedSecs by an adaptive internal val smartMarkAsPlayedPercent = 95 * if the episode is played to over 95% of its duration, it's considered played diff --git a/fastlane/metadata/android/en-US/changelogs/3020290.txt b/fastlane/metadata/android/en-US/changelogs/3020290.txt new file mode 100644 index 00000000..7efe30b1 --- /dev/null +++ b/fastlane/metadata/android/en-US/changelogs/3020290.txt @@ -0,0 +1,9 @@ + Version 6.13.3 + +* on playing the next episode in queue, if its state is lower than InProgress, the state is set as such +* added "add commment" in swipe actions +* in FeedEpisodes, fixed incorrect ordering of episodes after filtering +* when removing episode media, if remove-from-queue is set, the episode is removed from all queues rather than only the active queue +* removed the @UnstableAPI annotations previously required for Media3 +* updated Contributing.md +* various dependencies update diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index e590a954..b82895d2 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,18 +1,18 @@ [versions] activityCompose = "1.9.3" -annotation = "1.9.0" +annotation = "1.9.1" appcompat = "1.7.0" #awaitility = "4.2.1" balloon = "1.6.6" coil = "2.7.0" commonsLang3 = "3.15.0" commonsIo = "2.16.1" -composeBom = "2024.10.00" +composeBom = "2024.10.01" conscryptAndroid = "2.5.2" -constraintlayoutCompose = "1.0.1" +constraintlayoutCompose = "1.1.0" coordinatorlayout = "1.2.0" -coreKtx = "1.13.1" +coreKtx = "1.15.0" coreKtxVersion = "1.8.1" coreSplashscreen = "1.0.1" desugar_jdk_libs_nio = "2.1.2" @@ -34,9 +34,9 @@ jsoup = "1.18.1" kotlin = "2.0.20" kotlinxCoroutinesAndroid = "1.8.1" libraryBase = "2.1.0" -lifecycleRuntimeKtx = "2.8.6" +lifecycleRuntimeKtx = "2.8.7" #material = "1.7.2" -material3 = "1.3.0" +material3 = "1.3.1" #material3Android = "1.3.0" #materialIconsExtended = "1.7.3" materialVersion = "1.12.0" @@ -66,16 +66,16 @@ rxjavaVersion = "3.1.8" #speedDial = "3.3.0" searchpreference = "v2.5.0" stream = "1.2.2" -uiToolingPreview = "1.7.4" -uiTooling = "1.7.4" +uiToolingPreview = "1.7.5" +uiTooling = "1.7.5" viewpager2 = "1.1.0" vistaguide = "lv0.24.2.6" wearable = "2.9.0" webkit = "1.12.1" window = "1.3.0" -workRuntime = "2.9.1" +workRuntime = "2.10.0" #uiViewbinding = "1.7.2" -fragmentCompose = "1.8.4" +fragmentCompose = "1.8.5" #fragment-ktx = "1.8.4" [libraries] androidx-activity-compose = { module = "androidx.activity:activity-compose", version.ref = "activityCompose" } @@ -158,7 +158,7 @@ desugar_jdk_libs_nio = { module = "com.android.tools:desugar_jdk_libs_nio", vers wearable = { module = "com.google.android.wearable:wearable", version.ref = "wearable" } #androidx-ui-viewbinding = { group = "androidx.compose.ui", name = "ui-viewbinding", version.ref = "uiViewbinding" } androidx-fragment-compose = { group = "androidx.fragment", name = "fragment-compose", version.ref = "fragmentCompose" } -androidx-fragment-fragment-ktx = { module = "androidx.fragment:fragment-ktx", version = "1.8.4" } +androidx-fragment-fragment-ktx = { module = "androidx.fragment:fragment-ktx", version = "1.8.5" } [plugins] org-jetbrains-kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" }