diff --git a/app/build.gradle b/app/build.gradle
index 9d045bd9..193c109f 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -26,8 +26,8 @@ android {
vectorDrawables.useSupportLibrary false
vectorDrawables.generatedDensities = []
- versionCode 3020297
- versionName "6.13.10"
+ versionCode 3020298
+ versionName "6.13.11"
applicationId "ac.mdiq.podcini.R"
def commit = ""
diff --git a/app/src/main/assets/licenses.xml b/app/src/main/assets/licenses.xml
index d933ed38..a4390fb0 100644
--- a/app/src/main/assets/licenses.xml
+++ b/app/src/main/assets/licenses.xml
@@ -114,10 +114,10 @@
website="https://github.com/ByteHamster/SearchPreference"
license="MIT"
licenseText="LICENSE_SEARCHPREFERENCE.txt" />
-
+
+
+
+
+
+
diff --git a/app/src/main/kotlin/ac/mdiq/podcini/net/download/service/DownloadServiceInterfaceImpl.kt b/app/src/main/kotlin/ac/mdiq/podcini/net/download/service/DownloadServiceInterfaceImpl.kt
index 67bd4c2c..5354be0c 100644
--- a/app/src/main/kotlin/ac/mdiq/podcini/net/download/service/DownloadServiceInterfaceImpl.kt
+++ b/app/src/main/kotlin/ac/mdiq/podcini/net/download/service/DownloadServiceInterfaceImpl.kt
@@ -15,13 +15,12 @@ import ac.mdiq.podcini.storage.database.LogsAndStats
import ac.mdiq.podcini.storage.database.Queues
import ac.mdiq.podcini.storage.database.Queues.removeFromQueueSync
import ac.mdiq.podcini.storage.database.RealmDB.realm
-import ac.mdiq.podcini.storage.database.RealmDB.runOnIOScope
import ac.mdiq.podcini.storage.database.RealmDB.upsertBlk
import ac.mdiq.podcini.storage.model.DownloadResult
import ac.mdiq.podcini.storage.model.Episode
import ac.mdiq.podcini.storage.model.EpisodeMedia
+import ac.mdiq.podcini.storage.model.EpisodeMedia.MediaMetadataRetrieverCompat
import ac.mdiq.podcini.storage.utils.ChapterUtils
-import ac.mdiq.podcini.storage.utils.MediaMetadataRetrieverCompat
import ac.mdiq.podcini.ui.activity.starter.MainActivityStarter
import ac.mdiq.podcini.ui.utils.NotificationUtils
import ac.mdiq.podcini.util.EventFlow
@@ -314,7 +313,7 @@ class DownloadServiceInterfaceImpl : DownloadServiceInterface() {
MediaMetadataRetrieverCompat().use { mmr ->
if (it.media != null) mmr.setDataSource(it.media!!.fileUrl)
durationStr = mmr.extractMetadata(MediaMetadataRetriever.METADATA_KEY_DURATION)
- if (durationStr != null) it.media?.setDuration(durationStr!!.toInt())
+ if (durationStr != null) it.media?.setDuration(durationStr.toInt())
}
} catch (e: NumberFormatException) { Logd(TAG, "Invalid file duration: $durationStr")
} catch (e: Exception) {
diff --git a/app/src/main/kotlin/ac/mdiq/podcini/net/feed/FeedBuilder.kt b/app/src/main/kotlin/ac/mdiq/podcini/net/feed/FeedBuilder.kt
index f43e23ad..bdb46e10 100644
--- a/app/src/main/kotlin/ac/mdiq/podcini/net/feed/FeedBuilder.kt
+++ b/app/src/main/kotlin/ac/mdiq/podcini/net/feed/FeedBuilder.kt
@@ -344,9 +344,7 @@ class FeedBuilder(val context: Context, val showError: (String?, String)->Unit)
}
fun subscribe(feed: Feed) {
- while (feed.isBuilding) {
- runBlocking { delay(200) }
- }
+ while (feed.isBuilding) runBlocking { delay(200) }
feed.id = 0L
for (item in feed.episodes) {
item.id = 0L
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 f7b2991b..e919f7b3 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
@@ -11,7 +11,7 @@ import ac.mdiq.podcini.net.feed.parser.utils.MimeTypeUtils
import ac.mdiq.podcini.storage.database.Feeds
import ac.mdiq.podcini.storage.database.LogsAndStats
import ac.mdiq.podcini.storage.model.*
-import ac.mdiq.podcini.storage.utils.MediaMetadataRetrieverCompat
+import ac.mdiq.podcini.storage.model.EpisodeMedia.MediaMetadataRetrieverCompat
import ac.mdiq.podcini.util.Logd
import android.content.Context
import android.media.MediaMetadataRetriever
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 3d004bfb..b523dfaa 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
@@ -193,7 +193,7 @@ class LocalMediaPlayer(context: Context, callback: MediaPlayerCallback) : MediaP
if (curMedia is EpisodeMedia) {
val media_ = curMedia as EpisodeMedia
var item = media_.episodeOrFetch()
- if (item != null && item.playState < PlayState.INPROGRESS.code) item = runBlocking { setPlayStateSync(PlayState.INPROGRESS.code, item!!, false) }
+ 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
@@ -629,8 +629,8 @@ class LocalMediaPlayer(context: Context, callback: MediaPlayerCallback) : MediaP
var exoPlayer: ExoPlayer? = null
private var exoplayerListener: Listener? = null
- private var audioSeekCompleteListener: java.lang.Runnable? = null
- private var audioCompletionListener: java.lang.Runnable? = null
+ private var audioSeekCompleteListener: Runnable? = null
+ private var audioCompletionListener: Runnable? = null
private var audioErrorListener: Consumer? = null
private var bufferingUpdateListener: Consumer? = null
private var loudnessEnhancer: LoudnessEnhancer? = null
diff --git a/app/src/main/kotlin/ac/mdiq/podcini/playback/base/MediaPlayerBase.kt b/app/src/main/kotlin/ac/mdiq/podcini/playback/base/MediaPlayerBase.kt
index ab70b976..d82eba74 100644
--- a/app/src/main/kotlin/ac/mdiq/podcini/playback/base/MediaPlayerBase.kt
+++ b/app/src/main/kotlin/ac/mdiq/podcini/playback/base/MediaPlayerBase.kt
@@ -41,7 +41,6 @@ import androidx.media3.extractor.DefaultExtractorsFactory
import androidx.media3.extractor.mp3.Mp3Extractor
import java.util.concurrent.TimeUnit
import java.util.concurrent.atomic.AtomicBoolean
-import kotlin.concurrent.Volatile
import kotlin.math.max
/*
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 a3c9062d..566d1e7a 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
@@ -92,7 +92,6 @@ 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.session.*
import androidx.work.impl.utils.futures.SettableFuture
import com.google.common.collect.ImmutableList
@@ -104,7 +103,6 @@ import java.util.*
import java.util.concurrent.ScheduledFuture
import java.util.concurrent.ScheduledThreadPoolExecutor
import java.util.concurrent.TimeUnit
-import kotlin.concurrent.Volatile
import kotlin.math.max
import kotlin.math.sqrt
@@ -309,8 +307,7 @@ class PlaybackService : MediaLibraryService() {
else -> {}
}
}
- if (Build.VERSION.SDK_INT >= VERSION_CODES.N)
- TileService.requestListeningState(applicationContext, ComponentName(applicationContext, QuickSettingsTileService::class.java))
+ TileService.requestListeningState(applicationContext, ComponentName(applicationContext, QuickSettingsTileService::class.java))
sendLocalBroadcast(applicationContext, ACTION_PLAYER_STATUS_CHANGED)
bluetoothNotifyChange(newInfo, AVRCP_ACTION_PLAYER_STATUS_CHANGED)
@@ -359,9 +356,38 @@ class PlaybackService : MediaLibraryService() {
if (ended || smartMarkAsPlayed || autoSkipped || (skipped && !shouldSkipKeepEpisode)) {
Logd(TAG, "onPostPlayback ended: $ended smartMarkAsPlayed: $smartMarkAsPlayed autoSkipped: $autoSkipped skipped: $skipped")
// only mark the item as played if we're not keeping it anyways
- item = setPlayStateSync(PlayState.PLAYED.code, item!!, ended || (skipped && smartMarkAsPlayed), false)
- if (playable is EpisodeMedia && (ended || skipped || playingNext)) {
- item = upsert(item!!) { it.media?.playbackCompletionDate = Date() }
+
+// item = setPlayStateSync(PlayState.PLAYED.code, item!!, ended || (skipped && smartMarkAsPlayed), false)
+// if (playable is EpisodeMedia && (ended || skipped || playingNext)) {
+// item = upsert(item!!) {
+// it.media?.playbackCompletionDate = Date()
+// }
+// EventFlow.postEvent(FlowEvent.HistoryEvent())
+// }
+
+ if (playable !is EpisodeMedia)
+ item = setPlayStateSync(PlayState.PLAYED.code, item!!, ended || (skipped && smartMarkAsPlayed), false)
+ else {
+ val item_ = realm.query(Episode::class).query("id == $0", item!!.id).first().find()
+ if (item_ != null) {
+ item = upsert(item_) {
+ it.playState = PlayState.PLAYED.code
+ val media = it.media
+ if (media != null) {
+ media.startPosition = playable.startPosition
+ media.startTime = playable.startTime
+ media.playedDurationWhenStarted = playable.playedDurationWhenStarted
+ media.setPosition(playable.getPosition())
+ media.setLastPlayedTime(System.currentTimeMillis())
+ if (media.startPosition >= 0 && media.getPosition() > media.startPosition)
+ media.playedDuration = (media.playedDurationWhenStarted + media.getPosition() - media.startPosition)
+ media.timeSpent = media.timeSpentOnStart + (System.currentTimeMillis() - media.startTime).toInt()
+ if (ended || (skipped && smartMarkAsPlayed)) media.setPosition(0)
+ if (ended || skipped || playingNext) media.playbackCompletionDate = Date()
+ }
+ }
+ }
+ EventFlow.postEvent(FlowEvent.EpisodePlayedEvent(item))
EventFlow.postEvent(FlowEvent.HistoryEvent())
}
val action = item?.feed?.preferences?.autoDeleteAction
@@ -370,8 +396,8 @@ 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) removeFromAllQueuesSync( item!!)
- } else if (prefRemoveFromQueueMarkedPlayed) removeFromAllQueuesSync(item!!)
+ if (prefDeleteRemovesFromQueue) removeFromAllQueuesSync(item)
+ } else if (prefRemoveFromQueueMarkedPlayed) removeFromAllQueuesSync(item)
}
}
}
@@ -674,7 +700,7 @@ class PlaybackService : MediaLibraryService() {
recreateMediaPlayer()
if (LocalMediaPlayer.exoPlayer == null) LocalMediaPlayer.createStaticPlayer(applicationContext)
val intent = packageManager.getLaunchIntentForPackage(packageName)
- val pendingIntent = PendingIntent.getActivity(this, 0, intent, if (Build.VERSION.SDK_INT >= 23) FLAG_IMMUTABLE else FLAG_UPDATE_CURRENT)
+ val pendingIntent = PendingIntent.getActivity(this, 0, intent, FLAG_IMMUTABLE)
mediaSession = MediaLibrarySession.Builder(applicationContext, LocalMediaPlayer.exoPlayer!!, mediaLibrarySessionCK)
.setId(packageName)
.setSessionActivity(pendingIntent)
@@ -757,7 +783,7 @@ class PlaybackService : MediaLibraryService() {
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
val keycode = intent?.getIntExtra(MediaButtonReceiver.EXTRA_KEYCODE, -1) ?: -1
val customAction = intent?.getStringExtra(MediaButtonReceiver.EXTRA_CUSTOM_ACTION)
- val hardwareButton = intent?.getBooleanExtra(MediaButtonReceiver.EXTRA_HARDWAREBUTTON, false) ?: false
+ val hardwareButton = intent?.getBooleanExtra(MediaButtonReceiver.EXTRA_HARDWAREBUTTON, false) == true
val keyEvent: KeyEvent? = if (Build.VERSION.SDK_INT >= VERSION_CODES.TIRAMISU)
intent?.getParcelableExtra(EXTRA_KEY_EVENT, KeyEvent::class.java)
else {
@@ -793,8 +819,8 @@ class PlaybackService : MediaLibraryService() {
playable != null -> {
recreateMediaSessionIfNeeded()
Logd(TAG, "onStartCommand status: $status")
- val allowStreamThisTime = intent?.getBooleanExtra(EXTRA_ALLOW_STREAM_THIS_TIME, false) ?: false
- val allowStreamAlways = intent?.getBooleanExtra(EXTRA_ALLOW_STREAM_ALWAYS, false) ?: false
+ val allowStreamThisTime = intent?.getBooleanExtra(EXTRA_ALLOW_STREAM_THIS_TIME, false) == true
+ val allowStreamAlways = intent?.getBooleanExtra(EXTRA_ALLOW_STREAM_ALWAYS, false) == true
sendNotificationBroadcast(NOTIFICATION_TYPE_RELOAD, 0)
if (allowStreamAlways) isAllowMobileStreaming = true
startPlaying(allowStreamThisTime)
@@ -849,8 +875,7 @@ class PlaybackService : MediaLibraryService() {
val pendingIntentAlwaysAllow =
if (Build.VERSION.SDK_INT >= VERSION_CODES.O) PendingIntent.getForegroundService(this, R.id.pending_intent_allow_stream_always,
intentAlwaysAllow, FLAG_UPDATE_CURRENT or FLAG_IMMUTABLE)
- else PendingIntent.getService(this, R.id.pending_intent_allow_stream_always, intentAlwaysAllow,
- FLAG_UPDATE_CURRENT or FLAG_IMMUTABLE)
+ else PendingIntent.getService(this, R.id.pending_intent_allow_stream_always, intentAlwaysAllow, FLAG_UPDATE_CURRENT or FLAG_IMMUTABLE)
val builder = Notification.Builder(this, NotificationUtils.CHANNEL_ID.user_action.name)
.setSmallIcon(R.drawable.ic_notification_stream)
@@ -863,7 +888,7 @@ class PlaybackService : MediaLibraryService() {
.addAction(R.drawable.ic_notification_stream, getString(R.string.confirm_mobile_streaming_button_always), pendingIntentAlwaysAllow)
.setAutoCancel(true)
- val notificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
+ val notificationManager = getSystemService(NOTIFICATION_SERVICE) as NotificationManager
notificationManager.notify(5566, builder.build())
}
@@ -941,7 +966,7 @@ class PlaybackService : MediaLibraryService() {
return true
}
}
- KeyEvent.KEYCODE_MEDIA_STOP -> {
+ KEYCODE_MEDIA_STOP -> {
if (status == PlayerStatus.FALLBACK || status == PlayerStatus.PLAYING) mPlayer?.pause(abandonFocus = true, reinit = true)
return true
}
@@ -1154,9 +1179,8 @@ class PlaybackService : MediaLibraryService() {
media.setPosition(position)
media.setLastPlayedTime(System.currentTimeMillis())
if (it.isNew) it.playState = PlayState.UNPLAYED.code
- if (media.startPosition >= 0 && media.getPosition() > media.startPosition) {
+ if (media.startPosition >= 0 && media.getPosition() > media.startPosition)
media.playedDuration = (media.playedDurationWhenStarted + media.getPosition() - media.startPosition)
- }
media.timeSpent = media.timeSpentOnStart + (System.currentTimeMillis() - media.startTime).toInt()
Logd(TAG, "saveCurrentPosition ${media.startTime} timeSpent: ${media.timeSpent} playedDuration: ${media.playedDuration}")
}
@@ -1246,7 +1270,6 @@ class PlaybackService : MediaLibraryService() {
.build(),
),
}
-
class CustomMediaNotificationProvider(context: Context) : DefaultMediaNotificationProvider(context) {
override fun addNotificationActions(mediaSession: MediaSession, mediaButtons: ImmutableList,
@@ -1283,7 +1306,7 @@ class PlaybackService : MediaLibraryService() {
* to notify the PlaybackService about updates from the running tasks.
*/
class TaskManager(private val context: Context, private val callback: PSTMCallback) {
- private val schedExecutor: ScheduledThreadPoolExecutor = ScheduledThreadPoolExecutor(SCHED_EX_POOL_SIZE) { r: java.lang.Runnable? ->
+ private val schedExecutor: ScheduledThreadPoolExecutor = ScheduledThreadPoolExecutor(SCHED_EX_POOL_SIZE) { r: Runnable? ->
val t = Thread(r)
t.priority = Thread.MIN_PRIORITY
t
@@ -1441,7 +1464,7 @@ class PlaybackService : MediaLibraryService() {
schedExecutor.shutdownNow()
}
- private fun useMainThreadIfNecessary(runnable: java.lang.Runnable): java.lang.Runnable {
+ private fun useMainThreadIfNecessary(runnable: Runnable): Runnable {
if (Looper.myLooper() == Looper.getMainLooper()) {
// Called in main thread => ExoPlayer is used
// Run on ui thread even if called from schedExecutor
@@ -1453,7 +1476,7 @@ class PlaybackService : MediaLibraryService() {
/**
* Sleeps for a given time and then pauses playback.
*/
- internal inner class SleepTimer(private val waitingTime: Long) : java.lang.Runnable {
+ internal inner class SleepTimer(private val waitingTime: Long) : Runnable {
private var hasVibrated = false
private var timeLeft = waitingTime
private var shakeListener: ShakeListener? = null
@@ -1479,7 +1502,7 @@ class PlaybackService : MediaLibraryService() {
if (timeLeft < NOTIFICATION_THRESHOLD) {
Logd(TAG, "Sleep timer is about to expire")
if (SleepTimerPreferences.vibrate() && !hasVibrated) {
- val v = context.getSystemService(Context.VIBRATOR_SERVICE) as? Vibrator
+ val v = context.getSystemService(VIBRATOR_SERVICE) as? Vibrator
if (v != null) {
v.vibrate(500)
hasVibrated = true
@@ -1527,7 +1550,7 @@ class PlaybackService : MediaLibraryService() {
private fun resume() {
// only a precaution, the user should actually not be able to activate shake to reset
// when the accelerometer is not available
- mSensorMgr = mContext.getSystemService(Context.SENSOR_SERVICE) as SensorManager
+ mSensorMgr = mContext.getSystemService(SENSOR_SERVICE) as SensorManager
if (mSensorMgr == null) throw UnsupportedOperationException("Sensors not supported")
mAccelerometer = mSensorMgr!!.getDefaultSensor(Sensor.TYPE_ACCELEROMETER)
@@ -1656,7 +1679,7 @@ class PlaybackService : MediaLibraryService() {
}
var isStartWhenPrepared: Boolean
- get() = playbackService?.mPlayer?.startWhenPrepared?.get() ?: false
+ get() = playbackService?.mPlayer?.startWhenPrepared?.get() == true
set(s) {
playbackService?.mPlayer?.startWhenPrepared?.set(s)
}
@@ -1694,7 +1717,7 @@ class PlaybackService : MediaLibraryService() {
}
fun isSleepTimerActive(): Boolean {
- return playbackService?.taskManager?.isSleepTimerActive ?: false
+ return playbackService?.taskManager?.isSleepTimerActive == true
}
fun clearCurTempSpeed() {
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 277d270f..8eaba3bc 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
@@ -1,21 +1,16 @@
package ac.mdiq.podcini.playback.service
+import ac.mdiq.podcini.playback.base.InTheatre.curState
import ac.mdiq.podcini.receiver.MediaButtonReceiver
import ac.mdiq.podcini.storage.model.CurrentState.Companion.PLAYER_STATUS_PLAYING
-import ac.mdiq.podcini.playback.base.InTheatre.curState
import ac.mdiq.podcini.util.Logd
import android.content.ComponentName
import android.content.Intent
-import android.os.Build
import android.os.IBinder
import android.service.quicksettings.Tile
import android.service.quicksettings.TileService
import android.view.KeyEvent
-import androidx.annotation.RequiresApi
-
-
-@RequiresApi(api = Build.VERSION_CODES.N)
class QuickSettingsTileService : TileService() {
override fun onTileAdded() {
super.onTileAdded()
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 89714921..f616ce96 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
@@ -13,7 +13,6 @@ import ac.mdiq.podcini.playback.service.PlaybackService.Companion.ACTION_SHUTDOW
import ac.mdiq.podcini.preferences.UserPreferences.Prefs
import ac.mdiq.podcini.preferences.UserPreferences.appPrefs
import ac.mdiq.podcini.storage.database.Queues.removeFromAllQueuesSync
-import ac.mdiq.podcini.storage.database.Queues.removeFromQueueSync
import ac.mdiq.podcini.storage.database.RealmDB.realm
import ac.mdiq.podcini.storage.database.RealmDB.runOnIOScope
import ac.mdiq.podcini.storage.database.RealmDB.upsert
@@ -215,11 +214,7 @@ object Episodes {
*/
fun setPlayState(played: Int, resetMediaPosition: Boolean, vararg episodes: Episode) : Job {
Logd(TAG, "setPlayState called")
- return runOnIOScope {
- for (episode in episodes) {
- setPlayStateSync(played, episode, resetMediaPosition)
- }
- }
+ return runOnIOScope { for (episode in episodes) setPlayStateSync(played, episode, resetMediaPosition) }
}
suspend fun setPlayStateSync(played: Int, episode: Episode, resetMediaPosition: Boolean, removeFromQueue: Boolean = true) : Episode {
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 9dee91c2..7dae28d1 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
@@ -285,7 +285,7 @@ object Feeds {
else savedFeed.episodes.add(idx, episode)
val pubDate = episode.getPubDate()
- if (pubDate == null || priorMostRecentDate == null || priorMostRecentDate.before(pubDate) || priorMostRecentDate == pubDate) {
+ if (priorMostRecentDate == null || priorMostRecentDate.before(pubDate) || priorMostRecentDate == pubDate) {
Logd(TAG, "Marking episode published on $pubDate new, prior most recent date = $priorMostRecentDate")
episode.playState = PlayState.NEW.code
if (savedFeed.preferences?.autoAddNewToQueue == true) {
@@ -385,8 +385,7 @@ object Feeds {
for (e in savedFeed.episodes) savedFeed.totleDuration += e.media?.duration ?: 0
val resultFeed = savedFeed
- try {
- upsertBlk(savedFeed) {}
+ try { upsertBlk(savedFeed) {}
} catch (e: InterruptedException) { e.printStackTrace()
} catch (e: ExecutionException) { e.printStackTrace() }
return resultFeed
@@ -536,7 +535,7 @@ object Feeds {
feed.downloadUrl = null
feed.hasVideoMedia = video
feed.fileUrl = File(feedfilePath, getFeedfileName(feed)).toString()
- feed.preferences = FeedPreferences(feed.id, false, FeedPreferences.AutoDeleteAction.GLOBAL, VolumeAdaptionSetting.OFF, "", "")
+ feed.preferences = FeedPreferences(feed.id, false, AutoDeleteAction.GLOBAL, VolumeAdaptionSetting.OFF, "", "")
feed.preferences!!.keepUpdated = false
feed.preferences!!.queueId = -2L
return feed
@@ -736,10 +735,10 @@ object Feeds {
return string1 == string2
}
internal fun datesLookSimilar(item1: Episode, item2: Episode): Boolean {
- if (item1.getPubDate() == null || item2.getPubDate() == null) return false
+// if (item1.getPubDate() == null || item2.getPubDate() == null) return false
val dateFormat = DateFormat.getDateInstance(DateFormat.SHORT, Locale.US) // MM/DD/YY
- val dateOriginal = dateFormat.format(item2.getPubDate()!!)
- val dateNew = dateFormat.format(item1.getPubDate()!!)
+ val dateOriginal = dateFormat.format(item2.getPubDate())
+ val dateNew = dateFormat.format(item1.getPubDate())
return dateOriginal == dateNew // Same date; time is ignored.
}
internal fun durationsLookSimilar(media1: EpisodeMedia, media2: EpisodeMedia): Boolean {
diff --git a/app/src/main/kotlin/ac/mdiq/podcini/storage/database/LogsAndStats.kt b/app/src/main/kotlin/ac/mdiq/podcini/storage/database/LogsAndStats.kt
index 3e0b0ed5..36fa09a6 100644
--- a/app/src/main/kotlin/ac/mdiq/podcini/storage/database/LogsAndStats.kt
+++ b/app/src/main/kotlin/ac/mdiq/podcini/storage/database/LogsAndStats.kt
@@ -4,7 +4,6 @@ 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.model.DownloadResult
-import ac.mdiq.podcini.storage.utils.DownloadResultComparator
import ac.mdiq.podcini.util.EventFlow
import ac.mdiq.podcini.util.FlowEvent
import ac.mdiq.podcini.util.Logd
@@ -30,4 +29,11 @@ object LogsAndStats {
}
}
}
+
+ /** Compares the completion date of two DownloadResult objects. */
+ class DownloadResultComparator : Comparator {
+ override fun compare(lhs: DownloadResult, rhs: DownloadResult): Int {
+ return rhs.getCompletionDate().compareTo(lhs.getCompletionDate())
+ }
+ }
}
\ No newline at end of file
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 af8e630d..974439b4 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
@@ -344,7 +344,7 @@ object Queues {
val random = Random()
return random.nextInt(queueItems.size + 1)
}
- else -> throw AssertionError("calcPosition() : unrecognized enqueueLocation option: $enqueueLocation")
+// else -> throw AssertionError("calcPosition() : unrecognized enqueueLocation option: $enqueueLocation")
}
}
private fun getPositionOfFirstNonDownloadingItem(startPosition: Int, queueItems: List): Int {
diff --git a/app/src/main/kotlin/ac/mdiq/podcini/storage/model/EpisodeMedia.kt b/app/src/main/kotlin/ac/mdiq/podcini/storage/model/EpisodeMedia.kt
index fbfebacb..14ee7f23 100644
--- a/app/src/main/kotlin/ac/mdiq/podcini/storage/model/EpisodeMedia.kt
+++ b/app/src/main/kotlin/ac/mdiq/podcini/storage/model/EpisodeMedia.kt
@@ -4,10 +4,9 @@ import ac.mdiq.podcini.storage.database.RealmDB.realm
import ac.mdiq.podcini.storage.database.RealmDB.unmanaged
import ac.mdiq.podcini.storage.database.RealmDB.upsertBlk
import ac.mdiq.podcini.storage.model.VolumeAdaptionSetting.Companion.fromInteger
-import ac.mdiq.podcini.storage.utils.MediaMetadataRetrieverCompat
import ac.mdiq.podcini.util.Logd
-import ac.mdiq.podcini.util.showStackTrace
import android.content.Context
+import android.media.MediaMetadataRetriever
import android.os.Parcel
import android.os.Parcelable
import androidx.compose.runtime.getValue
@@ -18,6 +17,7 @@ import io.realm.kotlin.types.EmbeddedRealmObject
import io.realm.kotlin.types.annotations.Ignore
import io.realm.kotlin.types.annotations.Index
import java.io.File
+import java.io.IOException
import java.util.*
import kotlin.math.max
@@ -229,7 +229,7 @@ class EpisodeMedia: EmbeddedRealmObject, Playable {
fun hasEmbeddedPicture(): Boolean {
// TODO: checkEmbeddedPicture needs to update current copy
if (hasEmbeddedPicture == null) checkEmbeddedPicture()
- return hasEmbeddedPicture ?: false
+ return hasEmbeddedPicture == true
}
override fun writeToParcel(dest: Parcel, flags: Int) {
@@ -411,6 +411,15 @@ class EpisodeMedia: EmbeddedRealmObject, Playable {
return result
}
+ /**
+ * On SDK<29, this class does not have a close method yet, so the app crashes when using try-with-resources.
+ */
+ class MediaMetadataRetrieverCompat : MediaMetadataRetriever(), AutoCloseable {
+ override fun close() {
+ try { release() } catch (e: IOException) { e.printStackTrace() }
+ }
+ }
+
companion object {
private val TAG: String = EpisodeMedia::class.simpleName ?: "Anonymous"
diff --git a/app/src/main/kotlin/ac/mdiq/podcini/storage/utils/DownloadResultComparator.kt b/app/src/main/kotlin/ac/mdiq/podcini/storage/utils/DownloadResultComparator.kt
deleted file mode 100644
index 9e88f180..00000000
--- a/app/src/main/kotlin/ac/mdiq/podcini/storage/utils/DownloadResultComparator.kt
+++ /dev/null
@@ -1,10 +0,0 @@
-package ac.mdiq.podcini.storage.utils
-
-import ac.mdiq.podcini.storage.model.DownloadResult
-
-/** Compares the completion date of two DownloadResult objects. */
-class DownloadResultComparator : Comparator {
- override fun compare(lhs: DownloadResult, rhs: DownloadResult): Int {
- return rhs.getCompletionDate().compareTo(lhs.getCompletionDate())
- }
-}
diff --git a/app/src/main/kotlin/ac/mdiq/podcini/storage/utils/MediaMetadataRetrieverCompat.kt b/app/src/main/kotlin/ac/mdiq/podcini/storage/utils/MediaMetadataRetrieverCompat.kt
deleted file mode 100644
index bff11088..00000000
--- a/app/src/main/kotlin/ac/mdiq/podcini/storage/utils/MediaMetadataRetrieverCompat.kt
+++ /dev/null
@@ -1,17 +0,0 @@
-package ac.mdiq.podcini.storage.utils
-
-import android.media.MediaMetadataRetriever
-import java.io.IOException
-
-/**
- * On SDK<29, this class does not have a close method yet, so the app crashes when using try-with-resources.
- */
-class MediaMetadataRetrieverCompat : MediaMetadataRetriever(), AutoCloseable {
- override fun close() {
- try {
- release()
- } catch (e: IOException) {
- e.printStackTrace()
- }
- }
-}
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 3ed89b5e..5a583668 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
@@ -19,9 +19,9 @@ import ac.mdiq.podcini.storage.database.RealmDB
import ac.mdiq.podcini.storage.model.*
import ac.mdiq.podcini.storage.utils.AudioMediaTools
import ac.mdiq.podcini.storage.utils.FilesUtils
+import ac.mdiq.podcini.ui.actions.SwipeActions.Companion.deleteEpisodesWarnLocal
import ac.mdiq.podcini.ui.activity.VideoplayerActivity.Companion.videoMode
import ac.mdiq.podcini.ui.fragment.FeedEpisodesFragment
-import ac.mdiq.podcini.ui.utils.LocalDeleteModal
import ac.mdiq.podcini.util.EventFlow
import ac.mdiq.podcini.util.FlowEvent
import ac.mdiq.podcini.util.IntentUtils
@@ -86,7 +86,7 @@ abstract class EpisodeActionButton internal constructor(@JvmField var item: Epis
val media = item.media ?: return TTSActionButton(item)
val isDownloadingMedia = when (media.downloadUrl) {
null -> false
- else -> DownloadServiceInterface.get()?.isDownloadingEpisode(media.downloadUrl!!)?:false
+ else -> DownloadServiceInterface.get()?.isDownloadingEpisode(media.downloadUrl!!) == true
}
Logd("ItemActionButton", "forItem: local feed: ${item.feed?.isLocalFeed} downloaded: ${media.downloaded} playing: ${isCurrentlyPlaying(media)} ${item.title} ")
return when {
@@ -292,7 +292,7 @@ class DeleteActionButton(item: Episode) : EpisodeActionButton(item) {
}
override fun onClick(context: Context) {
- LocalDeleteModal.deleteEpisodesWarnLocal(context, listOf(item))
+ deleteEpisodesWarnLocal(context, listOf(item))
actionState.value = getLabel()
}
}
@@ -360,7 +360,7 @@ class DownloadActionButton(item: Episode) : EpisodeActionButton(item) {
private fun shouldNotDownload(media: EpisodeMedia?): Boolean {
if (media?.downloadUrl == null) return true
- val isDownloading = DownloadServiceInterface.get()?.isDownloadingEpisode(media.downloadUrl!!)?:false
+ val isDownloading = DownloadServiceInterface.get()?.isDownloadingEpisode(media.downloadUrl!!) == true
return isDownloading || media.downloaded
}
diff --git a/app/src/main/kotlin/ac/mdiq/podcini/ui/actions/MenuItemUtils.kt b/app/src/main/kotlin/ac/mdiq/podcini/ui/actions/MenuItemUtils.kt
deleted file mode 100644
index 9456d45c..00000000
--- a/app/src/main/kotlin/ac/mdiq/podcini/ui/actions/MenuItemUtils.kt
+++ /dev/null
@@ -1,25 +0,0 @@
-package ac.mdiq.podcini.ui.actions
-
-import android.view.Menu
-import android.view.MenuItem
-
-/**
- * Utilities for menu items
- */
-object MenuItemUtils {
- /**
- * When pressing a context menu item, Android calls onContextItemSelected
- * for ALL fragments in arbitrary order, not just for the fragment that the
- * context menu was created from. This assigns the listener to every menu item,
- * so that the correct fragment is always called first and can consume the click.
- *
- * Note that Android still calls the onContextItemSelected methods of all fragments
- * when the passed listener returns false.
- */
- fun setOnClickListeners(menu: Menu?, listener: MenuItem.OnMenuItemClickListener?) {
- for (i in 0 until menu!!.size()) {
- if (menu.getItem(i).subMenu != null) setOnClickListeners(menu.getItem(i).subMenu, listener)
- menu.getItem(i).setOnMenuItemClickListener(listener)
- }
- }
-}
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
deleted file mode 100644
index d2dfc921..00000000
--- a/app/src/main/kotlin/ac/mdiq/podcini/ui/actions/SwipeAction.kt
+++ /dev/null
@@ -1,42 +0,0 @@
-package ac.mdiq.podcini.ui.actions
-
-import android.content.Context
-import androidx.annotation.AttrRes
-import androidx.annotation.DrawableRes
-import androidx.fragment.app.Fragment
-import ac.mdiq.podcini.storage.model.Episode
-import ac.mdiq.podcini.storage.model.EpisodeFilter
-
-interface SwipeAction {
- fun getId(): String?
- fun getTitle(context: Context): String
-
- @DrawableRes
- fun getActionIcon(): Int
-
- @AttrRes
- @DrawableRes
- fun getActionColor(): Int
-
- fun performAction(item: Episode, fragment: Fragment, filter: EpisodeFilter)
-
- fun willRemove(filter: EpisodeFilter, item: Episode): Boolean {
- return false
- }
-
- enum class ActionTypes {
- NO_ACTION,
- COMBO,
- RATING,
- COMMENT,
- SET_PLAY_STATE,
- ADD_TO_QUEUE,
- PUT_TO_QUEUE,
- REMOVE_FROM_QUEUE,
- START_DOWNLOAD,
- DELETE,
- REMOVE_FROM_HISTORY,
- SHELVE,
- ERASE
- }
-}
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 2accb853..71f5b0be 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
@@ -2,6 +2,7 @@ package ac.mdiq.podcini.ui.actions
import ac.mdiq.podcini.R
import ac.mdiq.podcini.playback.base.InTheatre.curQueue
+import ac.mdiq.podcini.storage.database.Episodes.deleteEpisodeMedia
import ac.mdiq.podcini.storage.database.Episodes.setPlayState
import ac.mdiq.podcini.storage.database.Episodes.setPlayStateSync
import ac.mdiq.podcini.storage.database.Queues.addToQueue
@@ -14,19 +15,19 @@ import ac.mdiq.podcini.storage.model.Episode
import ac.mdiq.podcini.storage.model.EpisodeFilter
import ac.mdiq.podcini.storage.model.PlayState
import ac.mdiq.podcini.storage.utils.EpisodeUtil.hasAlmostEnded
-import ac.mdiq.podcini.ui.actions.SwipeAction.ActionTypes
-import ac.mdiq.podcini.ui.actions.SwipeAction.ActionTypes.NO_ACTION
import ac.mdiq.podcini.ui.activity.MainActivity
import ac.mdiq.podcini.ui.compose.*
import ac.mdiq.podcini.ui.fragment.*
-import ac.mdiq.podcini.ui.utils.LocalDeleteModal.deleteEpisodesWarnLocal
import ac.mdiq.podcini.util.EventFlow
import ac.mdiq.podcini.util.FlowEvent
import ac.mdiq.podcini.util.Logd
import android.content.Context
+import android.content.DialogInterface
import android.content.SharedPreferences
import android.util.TypedValue
import android.view.ViewGroup
+import androidx.annotation.AttrRes
+import androidx.annotation.DrawableRes
import androidx.compose.foundation.BorderStroke
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.*
@@ -50,12 +51,31 @@ import androidx.compose.ui.window.Dialog
import androidx.fragment.app.Fragment
import androidx.lifecycle.DefaultLifecycleObserver
import androidx.lifecycle.LifecycleOwner
+import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.google.android.material.snackbar.Snackbar
import kotlinx.coroutines.Job
import kotlinx.coroutines.runBlocking
import java.util.*
-open class SwipeActions(private val fragment: Fragment, private val tag: String) : DefaultLifecycleObserver {
+interface SwipeAction {
+ fun getId(): String?
+ fun getTitle(context: Context): String
+
+ @DrawableRes
+ fun getActionIcon(): Int
+
+ @AttrRes
+ @DrawableRes
+ fun getActionColor(): Int
+
+ fun performAction(item: Episode, fragment: Fragment, filter: EpisodeFilter)
+
+ fun willRemove(filter: EpisodeFilter, item: Episode): Boolean {
+ return false
+ }
+}
+
+class SwipeActions(private val fragment: Fragment, private val tag: String) : DefaultLifecycleObserver {
@set:JvmName("setFilterProperty")
var filter: EpisodeFilter? = null
@@ -111,6 +131,22 @@ open class SwipeActions(private val fragment: Fragment, private val tag: String)
}
}
+ enum class ActionTypes {
+ NO_ACTION,
+ COMBO,
+ RATING,
+ COMMENT,
+ SET_PLAY_STATE,
+ ADD_TO_QUEUE,
+ PUT_TO_QUEUE,
+ REMOVE_FROM_QUEUE,
+ START_DOWNLOAD,
+ DELETE,
+ REMOVE_FROM_HISTORY,
+ SHELVE,
+ ERASE
+ }
+
class AddToQueueSwipeAction : SwipeAction {
override fun getId(): String {
return ActionTypes.ADD_TO_QUEUE.name
@@ -155,7 +191,7 @@ open class SwipeActions(private val fragment: Fragment, private val tag: String)
Surface(shape = RoundedCornerShape(16.dp), border = BorderStroke(1.dp, Color.Yellow)) {
Column(modifier = Modifier.padding(16.dp), verticalArrangement = Arrangement.spacedBy(16.dp)) {
for (action in swipeActions) {
- if (action.getId() == NO_ACTION.name || action.getId() == ActionTypes.COMBO.name) continue
+ if (action.getId() == ActionTypes.NO_ACTION.name || action.getId() == ActionTypes.COMBO.name) continue
Row(verticalAlignment = Alignment.CenterVertically, modifier = Modifier.padding(4.dp).clickable {
action.performAction(item, fragment, filter)
showDialog = false
@@ -280,7 +316,7 @@ open class SwipeActions(private val fragment: Fragment, private val tag: String)
class NoActionSwipeAction : SwipeAction {
override fun getId(): String {
- return NO_ACTION.name
+ return ActionTypes.NO_ACTION.name
}
override fun getActionIcon(): Int {
return R.drawable.ic_questionmark
@@ -569,7 +605,7 @@ open class SwipeActions(private val fragment: Fragment, private val tag: String)
@JvmStatic
fun getPrefsWithDefaults(tag: String): Actions {
- val defaultActions = "${NO_ACTION.name},${NO_ACTION.name}"
+ val defaultActions = "${ActionTypes.NO_ACTION.name},${ActionTypes.NO_ACTION.name}"
return getPrefs(tag, defaultActions)
}
@@ -577,6 +613,25 @@ open class SwipeActions(private val fragment: Fragment, private val tag: String)
// return prefs!!.getBoolean(KEY_PREFIX_NO_ACTION + tag, true)
// }
+ fun deleteEpisodesWarnLocal(context: Context, items: Iterable) {
+ val localItems: MutableList = mutableListOf()
+ for (item in items) {
+ if (item.feed?.isLocalFeed == true) localItems.add(item)
+ else deleteEpisodeMedia(context, item)
+ }
+
+ if (localItems.isNotEmpty()) {
+ MaterialAlertDialogBuilder(context)
+ .setTitle(R.string.delete_episode_label)
+ .setMessage(R.string.delete_local_feed_warning_body)
+ .setPositiveButton(R.string.delete_label) { dialog: DialogInterface?, which: Int ->
+ for (item in localItems) deleteEpisodeMedia(context, item)
+ }
+ .setNegativeButton(R.string.cancel_label, null)
+ .show()
+ }
+ }
+
fun showSettingDialog(fragment: Fragment, tag: String) {
val composeView = ComposeView(fragment.requireContext()).apply {
setContent {
diff --git a/app/src/main/kotlin/ac/mdiq/podcini/ui/activity/BugReportActivity.kt b/app/src/main/kotlin/ac/mdiq/podcini/ui/activity/BugReportActivity.kt
index 641deb28..45fa96b7 100644
--- a/app/src/main/kotlin/ac/mdiq/podcini/ui/activity/BugReportActivity.kt
+++ b/app/src/main/kotlin/ac/mdiq/podcini/ui/activity/BugReportActivity.kt
@@ -12,7 +12,6 @@ import android.content.ClipboardManager
import android.content.DialogInterface
import android.os.Build
import android.os.Bundle
-import android.util.Log
import android.view.Menu
import android.view.MenuItem
import androidx.appcompat.app.AppCompatActivity
@@ -26,9 +25,6 @@ import java.io.FileInputStream
import java.io.IOException
import java.nio.charset.Charset
-/**
- * Displays the 'crash report' screen
- */
class BugReportActivity : AppCompatActivity() {
private var _binding: BugReportBinding? = null
private val binding get() = _binding!!
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 909c12e3..74188be2 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
@@ -92,10 +92,6 @@ import kotlinx.coroutines.*
import kotlinx.coroutines.flow.collectLatest
import kotlin.math.min
-/**
- * The activity that is shown when the user launches the app.
- */
-
class MainActivity : CastEnabledActivity() {
private var drawerLayout: DrawerLayout? = null
@@ -119,7 +115,7 @@ class MainActivity : CastEnabledActivity() {
private var lastTheme = 0
private var navigationBarInsets = Insets.NONE
- val prefs by lazy { getSharedPreferences(PREF_NAME, MODE_PRIVATE) }
+ val prefs by lazy { getSharedPreferences("MainActivityPrefs", MODE_PRIVATE) }
private val requestPermissionLauncher = registerForActivityResult(ActivityResultContracts.RequestPermission()) { isGranted: Boolean ->
Toast.makeText(this, R.string.notification_permission_text, Toast.LENGTH_LONG).show()
@@ -129,54 +125,11 @@ class MainActivity : CastEnabledActivity() {
}
MaterialAlertDialogBuilder(this)
.setMessage(R.string.notification_permission_text)
- .setPositiveButton(android.R.string.ok) { _: DialogInterface?, _: Int ->
- checkAndRequestUnrestrictedBackgroundActivity(this)
- }
- .setNegativeButton(R.string.cancel_label) { _: DialogInterface?, _: Int ->
- checkAndRequestUnrestrictedBackgroundActivity(this)
- }
+ .setPositiveButton(android.R.string.ok) { _: DialogInterface?, _: Int -> checkAndRequestUnrestrictedBackgroundActivity(this) }
+ .setNegativeButton(R.string.cancel_label) { _: DialogInterface?, _: Int -> checkAndRequestUnrestrictedBackgroundActivity(this) }
.show()
}
- @Composable
- fun UnrestrictedBackgroundPermissionDialog(onDismiss: () -> Unit) {
- var dontAskAgain by remember { mutableStateOf(false) }
- AlertDialog(onDismissRequest = onDismiss, title = { Text("Permission Required") },
- text = {
- Column {
- Text(stringResource(R.string.unrestricted_background_permission_text))
- Row(verticalAlignment = Alignment.CenterVertically) {
- Checkbox(checked = dontAskAgain, onCheckedChange = { dontAskAgain = it })
- Text(stringResource(R.string.dont_ask_again))
- }
- }
- },
- confirmButton = {
- TextButton(onClick = {
- if (dontAskAgain) prefs.edit().putBoolean("dont_ask_again_unrestricted_background", true).apply()
- val intent = Intent()
- intent.action = Settings.ACTION_IGNORE_BATTERY_OPTIMIZATION_SETTINGS
- this@MainActivity.startActivity(intent)
- onDismiss()
- }) { Text("OK") }
- },
- dismissButton = { TextButton(onClick = onDismiss) { Text("Cancel") } }
- )
- }
-
- fun checkAndRequestUnrestrictedBackgroundActivity(context: Context) {
- if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) return
- val powerManager = context.getSystemService(Context.POWER_SERVICE) as PowerManager
- val isIgnoringBatteryOptimizations = powerManager.isIgnoringBatteryOptimizations(context.packageName)
- val dontAskAgain = prefs.getBoolean("dont_ask_again_unrestricted_background", false)
- if (!isIgnoringBatteryOptimizations && !dontAskAgain) {
- val composeView = ComposeView(this).apply {
- setContent { UnrestrictedBackgroundPermissionDialog(onDismiss = { (parent as? ViewGroup)?.removeView(this) }) }
- }
- (window.decorView as? ViewGroup)?.addView(composeView)
- }
- }
-
private var prevState: Int = 0
private val bottomSheetCallback: BottomSheetCallback = object : BottomSheetCallback() {
override fun onStateChanged(view: View, state: Int) {
@@ -205,7 +158,7 @@ class MainActivity : CastEnabledActivity() {
}
private val isDrawerOpen: Boolean
- get() = drawerLayout?.isDrawerOpen(navDrawer)?:false
+ get() = drawerLayout?.isDrawerOpen(navDrawer) == true
private val screenWidth: Int
get() {
@@ -227,19 +180,13 @@ class MainActivity : CastEnabledActivity() {
}
val ioScope = CoroutineScope(Dispatchers.IO)
-// init shared preferences
ioScope.launch {
// RealmDB.apply { }
NavDrawerFragment.getSharedPrefs(this@MainActivity)
SwipeActions.getSharedPrefs(this@MainActivity)
-// QueuesFragment.getSharedPrefs(this@MainActivity)
buildTags()
monitorFeeds()
-// AudioPlayerFragment.getSharedPrefs(this@MainActivity)
PlayerWidget.getSharedPrefs(this@MainActivity)
-// StatisticsFragment.getSharedPrefs(this@MainActivity)
-// OnlineFeedFragment.getSharedPrefs(this@MainActivity)
-// ItunesTopListLoader.getSharedPrefs(this@MainActivity)
}
if (savedInstanceState != null) ensureGeneratedViewIdGreaterThan(savedInstanceState.getInt(Extras.generated_view_id.name, 0))
@@ -247,7 +194,6 @@ class MainActivity : CastEnabledActivity() {
WindowCompat.setDecorFitsSystemWindows(window, false)
super.onCreate(savedInstanceState)
_binding = MainActivityBinding.inflate(layoutInflater)
-// setContentView(R.layout.main_activity)
setContentView(binding.root)
recycledViewPool.setMaxRecycledViews(R.id.view_type_episode_item, 25)
dummyView = object : View(this) {}
@@ -321,6 +267,45 @@ class MainActivity : CastEnabledActivity() {
observeDownloads()
}
+ @Composable
+ fun UnrestrictedBackgroundPermissionDialog(onDismiss: () -> Unit) {
+ var dontAskAgain by remember { mutableStateOf(false) }
+ AlertDialog(onDismissRequest = onDismiss, title = { Text("Permission Required") },
+ text = {
+ Column {
+ Text(stringResource(R.string.unrestricted_background_permission_text))
+ Row(verticalAlignment = Alignment.CenterVertically) {
+ Checkbox(checked = dontAskAgain, onCheckedChange = { dontAskAgain = it })
+ Text(stringResource(R.string.dont_ask_again))
+ }
+ }
+ },
+ confirmButton = {
+ TextButton(onClick = {
+ if (dontAskAgain) prefs.edit().putBoolean("dont_ask_again_unrestricted_background", true).apply()
+ val intent = Intent()
+ intent.action = Settings.ACTION_IGNORE_BATTERY_OPTIMIZATION_SETTINGS
+ this@MainActivity.startActivity(intent)
+ onDismiss()
+ }) { Text("OK") }
+ },
+ dismissButton = { TextButton(onClick = onDismiss) { Text("Cancel") } }
+ )
+ }
+
+ fun checkAndRequestUnrestrictedBackgroundActivity(context: Context) {
+// if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) return
+ val powerManager = context.getSystemService(POWER_SERVICE) as PowerManager
+ val isIgnoringBatteryOptimizations = powerManager.isIgnoringBatteryOptimizations(context.packageName)
+ val dontAskAgain = prefs.getBoolean("dont_ask_again_unrestricted_background", false)
+ if (!isIgnoringBatteryOptimizations && !dontAskAgain) {
+ val composeView = ComposeView(this).apply {
+ setContent { UnrestrictedBackgroundPermissionDialog(onDismiss = { (parent as? ViewGroup)?.removeView(this) }) }
+ }
+ (window.decorView as? ViewGroup)?.addView(composeView)
+ }
+ }
+
private fun observeDownloads() {
lifecycleScope.launch {
withContext(Dispatchers.IO) { WorkManager.getInstance(this@MainActivity).pruneWork().result.get() }
@@ -718,7 +703,7 @@ class MainActivity : CastEnabledActivity() {
val s: Snackbar
if (bottomSheet.state == BottomSheetBehavior.STATE_COLLAPSED) {
s = Snackbar.make(mainView, text, duration)
- if (audioPlayerView.visibility == View.VISIBLE) s.setAnchorView(audioPlayerView)
+ if (audioPlayerView.visibility == View.VISIBLE) s.anchorView = audioPlayerView
} else s = Snackbar.make(binding.root, text, duration)
s.show()
@@ -813,7 +798,7 @@ class MainActivity : CastEnabledActivity() {
companion object {
private val TAG: String = MainActivity::class.simpleName ?: "Anonymous"
const val MAIN_FRAGMENT_TAG: String = "main"
- const val PREF_NAME: String = "MainActivityPrefs"
+// const val PREF_NAME: String = "MainActivityPrefs"
const val REQUEST_CODE_FIRST_PERMISSION = 1001
const val REQUEST_CODE_SECOND_PERMISSION = 1002
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 72b56014..43d5c6e6 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
@@ -90,11 +90,11 @@ class OpmlImportActivity : AppCompatActivity() {
}
if (listAdapter != null) {
if (checkedCount == listAdapter!!.count) {
- selectAll.setVisible(false)
- deselectAll.setVisible(true)
+ selectAll.isVisible = false
+ deselectAll.isVisible = true
} else {
- deselectAll.setVisible(false)
- selectAll.setVisible(true)
+ deselectAll.isVisible = false
+ selectAll.isVisible = true
}
}
}
@@ -160,7 +160,7 @@ class OpmlImportActivity : AppCompatActivity() {
inflater.inflate(R.menu.opml_selection_options, menu)
selectAll = menu.findItem(R.id.select_all_item)
deselectAll = menu.findItem(R.id.deselect_all_item)
- deselectAll.setVisible(false)
+ deselectAll.isVisible = false
return true
}
@@ -168,15 +168,15 @@ class OpmlImportActivity : AppCompatActivity() {
val itemId = item.itemId
when (itemId) {
R.id.select_all_item -> {
- selectAll.setVisible(false)
+ selectAll.isVisible = false
selectAllItems(true)
- deselectAll.setVisible(true)
+ deselectAll.isVisible = true
return true
}
R.id.deselect_all_item -> {
- deselectAll.setVisible(false)
+ deselectAll.isVisible = false
selectAllItems(false)
- selectAll.setVisible(true)
+ selectAll.isVisible = true
return true
}
android.R.id.home -> finish()
diff --git a/app/src/main/kotlin/ac/mdiq/podcini/ui/activity/PreferenceActivity.kt b/app/src/main/kotlin/ac/mdiq/podcini/ui/activity/PreferenceActivity.kt
index 25cc1f16..e66ede71 100644
--- a/app/src/main/kotlin/ac/mdiq/podcini/ui/activity/PreferenceActivity.kt
+++ b/app/src/main/kotlin/ac/mdiq/podcini/ui/activity/PreferenceActivity.kt
@@ -46,15 +46,11 @@ class PreferenceActivity : AppCompatActivity(), SearchPreferenceResultListener {
_binding = SettingsActivityBinding.inflate(layoutInflater)
setContentView(binding.root)
- if (supportFragmentManager.findFragmentByTag(FRAGMENT_TAG) == null) {
- supportFragmentManager.beginTransaction()
- .replace(binding.settingsContainer.id, MainPreferencesFragment(), FRAGMENT_TAG)
- .commit()
- }
+ if (supportFragmentManager.findFragmentByTag(FRAGMENT_TAG) == null)
+ supportFragmentManager.beginTransaction().replace(binding.settingsContainer.id, MainPreferencesFragment(), FRAGMENT_TAG).commit()
+
val intent = intent
- if (intent.getBooleanExtra(OPEN_AUTO_DOWNLOAD_SETTINGS, false)) {
- openScreen(R.xml.preferences_autodownload)
- }
+ if (intent.getBooleanExtra(OPEN_AUTO_DOWNLOAD_SETTINGS, false)) openScreen(R.xml.preferences_autodownload)
}
private fun getPreferenceScreen(screen: Int): PreferenceFragmentCompat? {
@@ -81,27 +77,20 @@ class PreferenceActivity : AppCompatActivity(), SearchPreferenceResultListener {
intent.setAction(Settings.ACTION_APP_NOTIFICATION_SETTINGS)
intent.putExtra(Settings.EXTRA_APP_PACKAGE, packageName)
startActivity(intent)
- } else {
- supportFragmentManager.beginTransaction()
- .replace(binding.settingsContainer.id, fragment!!)
- .addToBackStack(getString(getTitleOfPage(screen)))
- .commit()
- }
+ } else
+ supportFragmentManager.beginTransaction().replace(binding.settingsContainer.id, fragment!!).addToBackStack(getString(getTitleOfPage(screen))).commit()
return fragment
}
override fun onOptionsItemSelected(item: MenuItem): Boolean {
if (item.itemId == android.R.id.home) {
- if (supportFragmentManager.backStackEntryCount == 0) {
- finish()
- } else {
+ if (supportFragmentManager.backStackEntryCount == 0) finish()
+ else {
val imm = getSystemService(INPUT_METHOD_SERVICE) as InputMethodManager
var view = currentFocus
//If no view currently has focus, create a new one, just so we can grab a window token from it
- if (view == null) {
- view = View(this)
- }
+ if (view == null) view = View(this)
imm.hideSoftInputFromWindow(view.windowToken, 0)
supportFragmentManager.popBackStack()
}
@@ -156,9 +145,7 @@ class PreferenceActivity : AppCompatActivity(), SearchPreferenceResultListener {
fun onEventMainThread(event: FlowEvent.MessageEvent) {
// Logd(FRAGMENT_TAG, "onEvent($event)")
val s = Snackbar.make(binding.root, event.message, Snackbar.LENGTH_LONG)
- if (event.action != null) {
- s.setAction(event.actionText) { event.action.accept(this) }
- }
+ if (event.action != null) s.setAction(event.actionText) { event.action.accept(this) }
s.show()
}
@@ -167,35 +154,16 @@ class PreferenceActivity : AppCompatActivity(), SearchPreferenceResultListener {
const val OPEN_AUTO_DOWNLOAD_SETTINGS: String = "OpenAutoDownloadSettings"
@JvmStatic
fun getTitleOfPage(preferences: Int): Int {
- when (preferences) {
- R.xml.preferences_downloads -> {
- return R.string.downloads_pref
- }
- R.xml.preferences_autodownload -> {
- return R.string.pref_automatic_download_title
- }
- R.xml.preferences_playback -> {
- return R.string.playback_pref
- }
- R.xml.preferences_import_export -> {
- return R.string.import_export_pref
- }
- R.xml.preferences_user_interface -> {
- return R.string.user_interface_label
- }
- R.xml.preferences_synchronization -> {
- return R.string.synchronization_pref
- }
- R.xml.preferences_notifications -> {
- return R.string.notification_pref_fragment
- }
-// R.xml.feed_settings -> {
-// return R.string.feed_settings_label
-// }
- R.xml.preferences_swipe -> {
- return R.string.swipeactions_label
- }
- else -> return R.string.settings_label
+ return when (preferences) {
+ R.xml.preferences_downloads -> R.string.downloads_pref
+ R.xml.preferences_autodownload -> R.string.pref_automatic_download_title
+ R.xml.preferences_playback -> R.string.playback_pref
+ R.xml.preferences_import_export -> R.string.import_export_pref
+ R.xml.preferences_user_interface -> R.string.user_interface_label
+ R.xml.preferences_synchronization -> R.string.synchronization_pref
+ R.xml.preferences_notifications -> R.string.notification_pref_fragment
+ R.xml.preferences_swipe -> R.string.swipeactions_label
+ else -> R.string.settings_label
}
}
}
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 3802d8d4..9321903d 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
@@ -87,7 +87,7 @@ class SelectSubscriptionActivity : AppCompatActivity() {
.setIcon(icon)
.build()
- setResult(Activity.RESULT_OK, ShortcutManagerCompat.createShortcutResultIntent(this, shortcut))
+ setResult(RESULT_OK, ShortcutManagerCompat.createShortcutResultIntent(this, shortcut))
finish()
}
@@ -127,11 +127,8 @@ class SelectSubscriptionActivity : AppCompatActivity() {
val adapter: ArrayAdapter = ArrayAdapter(this@SelectSubscriptionActivity, R.layout.simple_list_item_multiple_choice_on_start, titles)
binding.list.adapter = adapter
}
- } catch (e: Throwable) {
- Log.e(TAG, Log.getStackTraceString(e))
- }
+ } catch (e: Throwable) { Log.e(TAG, Log.getStackTraceString(e)) }
}
-
}
override fun onDestroy() {
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 e5afdaab..3ebecba6 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
@@ -35,7 +35,6 @@ import ac.mdiq.podcini.ui.activity.starter.MainActivityStarter
import ac.mdiq.podcini.ui.compose.ChaptersDialog
import ac.mdiq.podcini.ui.compose.CustomTheme
import ac.mdiq.podcini.ui.dialog.*
-import ac.mdiq.podcini.ui.utils.PictureInPictureUtil
import ac.mdiq.podcini.ui.utils.ShownotesCleaner
import ac.mdiq.podcini.ui.view.ShownotesWebView
import ac.mdiq.podcini.util.EventFlow
@@ -48,10 +47,10 @@ import android.app.Dialog
import android.content.DialogInterface
import android.content.Intent
import android.content.pm.ActivityInfo
+import android.content.pm.PackageManager
import android.graphics.PixelFormat
import android.graphics.drawable.ColorDrawable
import android.media.AudioManager
-import android.os.Build
import android.os.Bundle
import android.os.Handler
import android.os.Looper
@@ -84,12 +83,7 @@ import kotlinx.coroutines.withContext
import kotlin.math.max
import kotlin.math.min
-/**
- * Activity for playing video files.
- */
-
class VideoplayerActivity : CastEnabledActivity() {
-
private var _binding: VideoplayerActivityBinding? = null
private val binding get() = _binding!!
private lateinit var videoEpisodeFragment: VideoEpisodeFragment
@@ -185,10 +179,10 @@ class VideoplayerActivity : CastEnabledActivity() {
}
public override fun onUserLeaveHint() {
- if (!PictureInPictureUtil.isInPictureInPictureMode(this)) compatEnterPictureInPicture()
+ super.onUserLeaveHint()
+ if (!isInPictureInPictureMode()) compatEnterPictureInPicture()
}
-
override fun onStart() {
super.onStart()
procFlowEvents()
@@ -249,15 +243,15 @@ class VideoplayerActivity : CastEnabledActivity() {
val media = curMedia
val isEpisodeMedia = (media is EpisodeMedia)
- menu.findItem(R.id.show_home_reader_view).setVisible(false)
- menu.findItem(R.id.open_feed_item).setVisible(isEpisodeMedia) // EpisodeMedia implies it belongs to a Feed
+ menu.findItem(R.id.show_home_reader_view).isVisible = false
+ menu.findItem(R.id.open_feed_item).isVisible = isEpisodeMedia // EpisodeMedia implies it belongs to a Feed
val hasWebsiteLink = getWebsiteLinkWithFallback(media) != null
- menu.findItem(R.id.visit_website_item).setVisible(hasWebsiteLink)
+ menu.findItem(R.id.visit_website_item).isVisible = hasWebsiteLink
- val isItemAndHasLink = isEpisodeMedia && hasLinkToShare((media as EpisodeMedia).episodeOrFetch())
+ val isItemAndHasLink = isEpisodeMedia && hasLinkToShare(media.episodeOrFetch())
val isItemHasDownloadLink = isEpisodeMedia && (media as EpisodeMedia?)?.downloadUrl != null
- menu.findItem(R.id.share_item).setVisible(hasWebsiteLink || isItemAndHasLink || isItemHasDownloadLink)
+ menu.findItem(R.id.share_item).isVisible = hasWebsiteLink || isItemAndHasLink || isItemHasDownloadLink
// menu.findItem(R.id.add_to_favorites_item).setVisible(false)
// menu.findItem(R.id.remove_from_favorites_item).setVisible(false)
@@ -266,13 +260,13 @@ class VideoplayerActivity : CastEnabledActivity() {
// menu.findItem(R.id.remove_from_favorites_item).setVisible(videoEpisodeFragment.isFavorite)
// }
- menu.findItem(R.id.set_sleeptimer_item).setVisible(!isSleepTimerActive())
- menu.findItem(R.id.disable_sleeptimer_item).setVisible(isSleepTimerActive())
- menu.findItem(R.id.player_switch_to_audio_only).setVisible(true)
+ menu.findItem(R.id.set_sleeptimer_item).isVisible = !isSleepTimerActive()
+ menu.findItem(R.id.disable_sleeptimer_item).isVisible = isSleepTimerActive()
+ menu.findItem(R.id.player_switch_to_audio_only).isVisible = true
- menu.findItem(R.id.audio_controls).setVisible(audioTracks.size >= 2)
- menu.findItem(R.id.playback_speed).setVisible(true)
- menu.findItem(R.id.player_show_chapters).setVisible(true)
+ menu.findItem(R.id.audio_controls).isVisible = audioTracks.size >= 2
+ menu.findItem(R.id.playback_speed).isVisible = true
+ menu.findItem(R.id.player_show_chapters).isVisible = true
if (videoMode == VideoMode.WINDOW_VIEW) {
// menu.findItem(R.id.add_to_favorites_item).setShowAsAction(SHOW_AS_ACTION_NEVER)
@@ -360,7 +354,7 @@ class VideoplayerActivity : CastEnabledActivity() {
}
private fun compatEnterPictureInPicture() {
- if (PictureInPictureUtil.supportsPictureInPicture(this) && Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
+ if (packageManager.hasSystemFeature(PackageManager.FEATURE_PICTURE_IN_PICTURE)) {
if (videoMode == VideoMode.FULL_SCREEN_VIEW) supportActionBar?.hide()
videoEpisodeFragment.hideVideoControls(false)
enterPictureInPictureMode()
@@ -418,6 +412,16 @@ class VideoplayerActivity : CastEnabledActivity() {
return super.onKeyUp(keyCode, event)
}
+// fun supportsPictureInPicture(): Boolean {
+//// val packageManager = activity.packageManager
+// return packageManager.hasSystemFeature(PackageManager.FEATURE_PICTURE_IN_PICTURE)
+// }
+
+ override fun isInPictureInPictureMode(): Boolean {
+ return if (packageManager.hasSystemFeature(PackageManager.FEATURE_PICTURE_IN_PICTURE)) super.isInPictureInPictureMode
+ else false
+ }
+
class PlaybackControlsDialog : DialogFragment() {
private lateinit var dialog: AlertDialog
private var _binding: AudioControlsBinding? = null
@@ -474,7 +478,6 @@ class VideoplayerActivity : CastEnabledActivity() {
}
}
-
class VideoEpisodeFragment : Fragment(), OnSeekBarChangeListener {
private var _binding: VideoEpisodeFragmentBinding? = null
private val binding get() = _binding!!
@@ -498,7 +501,7 @@ class VideoplayerActivity : CastEnabledActivity() {
private val onVideoviewTouched = View.OnTouchListener { v: View, event: MotionEvent ->
Logd(TAG, "onVideoviewTouched ${event.action}")
if (event.action != MotionEvent.ACTION_DOWN) return@OnTouchListener false
- if (PictureInPictureUtil.isInPictureInPictureMode(requireActivity())) return@OnTouchListener true
+ if (requireActivity().isInPictureInPictureMode()) return@OnTouchListener true
videoControlsHider.removeCallbacks(hideVideoControls)
Logd(TAG, "onVideoviewTouched $videoControlsVisible ${System.currentTimeMillis() - lastScreenTap}")
if (System.currentTimeMillis() - lastScreenTap < 300) {
@@ -546,7 +549,6 @@ class VideoplayerActivity : CastEnabledActivity() {
videoControlsVisible = false
}
}
-
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
super.onCreateView(inflater, container, savedInstanceState)
@@ -559,7 +561,6 @@ class VideoplayerActivity : CastEnabledActivity() {
return root
}
-
private fun newStatusHandler(): ServiceStatusHandler {
return object : ServiceStatusHandler(requireActivity()) {
override fun updatePlayButton(showPlay: Boolean) {
@@ -583,7 +584,6 @@ class VideoplayerActivity : CastEnabledActivity() {
}
}
}
-
override fun onStart() {
super.onStart()
@@ -591,11 +591,10 @@ class VideoplayerActivity : CastEnabledActivity() {
procFlowEvents()
}
-
override fun onStop() {
super.onStop()
cancelFlowEvents()
- if (!PictureInPictureUtil.isInPictureInPictureMode(requireActivity())) videoControlsHider.removeCallbacks(hideVideoControls)
+ if (!requireActivity().isInPictureInPictureMode()) videoControlsHider.removeCallbacks(hideVideoControls)
// Controller released; we will not receive buffering updates
binding.progressBar.visibility = View.GONE
}
diff --git a/app/src/main/kotlin/ac/mdiq/podcini/ui/activity/WidgetConfigActivity.kt b/app/src/main/kotlin/ac/mdiq/podcini/ui/activity/WidgetConfigActivity.kt
index 12f8839b..c9db5fdd 100644
--- a/app/src/main/kotlin/ac/mdiq/podcini/ui/activity/WidgetConfigActivity.kt
+++ b/app/src/main/kotlin/ac/mdiq/podcini/ui/activity/WidgetConfigActivity.kt
@@ -1,16 +1,5 @@
package ac.mdiq.podcini.ui.activity
-import android.appwidget.AppWidgetManager
-import android.content.Intent
-import android.graphics.Color
-import android.os.Build
-import android.os.Bundle
-import android.view.View
-import android.widget.CheckBox
-import android.widget.SeekBar
-import android.widget.SeekBar.OnSeekBarChangeListener
-import android.widget.TextView
-import androidx.appcompat.app.AppCompatActivity
import ac.mdiq.podcini.R
import ac.mdiq.podcini.databinding.ActivityWidgetConfigBinding
import ac.mdiq.podcini.databinding.PlayerWidgetBinding
@@ -19,6 +8,17 @@ import ac.mdiq.podcini.receiver.PlayerWidget
import ac.mdiq.podcini.receiver.PlayerWidget.Companion.prefs
import ac.mdiq.podcini.ui.widget.WidgetUpdaterWorker
import ac.mdiq.podcini.util.Logd
+import android.appwidget.AppWidgetManager
+import android.content.Intent
+import android.graphics.Color
+import android.os.Bundle
+import android.view.View
+import android.widget.CheckBox
+import android.widget.SeekBar
+import android.widget.SeekBar.OnSeekBarChangeListener
+import android.widget.TextView
+import androidx.appcompat.app.AppCompatActivity
+import kotlin.math.roundToInt
class WidgetConfigActivity : AppCompatActivity() {
@@ -96,18 +96,15 @@ class WidgetConfigActivity : AppCompatActivity() {
private fun setInitialState() {
PlayerWidget.getSharedPrefs(this)
-
// val prefs = getSharedPreferences(PlayerWidget.PREFS_NAME, MODE_PRIVATE)
ckPlaybackSpeed.isChecked = prefs!!.getBoolean(PlayerWidget.Prefs.widget_playback_speed.name + appWidgetId, true)
ckRewind.isChecked = prefs!!.getBoolean(PlayerWidget.Prefs.widget_rewind.name + appWidgetId, true)
ckFastForward.isChecked = prefs!!.getBoolean(PlayerWidget.Prefs.widget_fast_forward.name + appWidgetId, true)
ckSkip.isChecked = prefs!!.getBoolean(PlayerWidget.Prefs.widget_skip.name + appWidgetId, true)
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
- val color = prefs!!.getInt(PlayerWidget.Prefs.widget_color.name + appWidgetId, PlayerWidget.DEFAULT_COLOR)
- val opacity = Color.alpha(color) * 100 / 0xFF
- opacitySeekBar.setProgress(opacity, false)
- }
+ val color = prefs!!.getInt(PlayerWidget.Prefs.widget_color.name + appWidgetId, PlayerWidget.DEFAULT_COLOR)
+ val opacity = Color.alpha(color) * 100 / 0xFF
+ opacitySeekBar.setProgress(opacity, false)
displayPreviewPanel()
}
@@ -142,6 +139,6 @@ class WidgetConfigActivity : AppCompatActivity() {
}
private fun getColorWithAlpha(color: Int, opacity: Int): Int {
- return Math.round(0xFF * (0.01 * opacity)).toInt() * 0x1000000 + (color and 0xffffff)
+ return (0xFF * (0.01 * opacity)).roundToInt() * 0x1000000 + (color and 0xffffff)
}
}
diff --git a/app/src/main/kotlin/ac/mdiq/podcini/ui/compose/ChaptersDialog.kt b/app/src/main/kotlin/ac/mdiq/podcini/ui/compose/ChaptersDialog.kt
index bb298613..6746698e 100644
--- a/app/src/main/kotlin/ac/mdiq/podcini/ui/compose/ChaptersDialog.kt
+++ b/app/src/main/kotlin/ac/mdiq/podcini/ui/compose/ChaptersDialog.kt
@@ -24,7 +24,6 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.platform.LocalContext
-import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.res.vectorResource
import androidx.compose.ui.text.font.FontWeight
@@ -58,7 +57,7 @@ fun ChaptersDialog(media: Playable, onDismissRequest: () -> Unit) {
Text(ch.title ?: "No title", color = textColor, fontWeight = FontWeight.Bold)
// Text(ch.link?: "")
val duration = if (index + 1 < chapters.size) chapters[index + 1].start - ch.start
- else (media.getDuration() ?: 0) - ch.start
+ else media.getDuration() - ch.start
Text(stringResource(R.string.chapter_duration0) + getDurationStringLocalized(LocalContext.current, duration), color = textColor)
}
val playRes = if (index == currentChapterIndex) R.drawable.ic_replay else R.drawable.ic_play_48dp
diff --git a/app/src/main/kotlin/ac/mdiq/podcini/ui/compose/Composables.kt b/app/src/main/kotlin/ac/mdiq/podcini/ui/compose/Composables.kt
index d438e926..7148c519 100644
--- a/app/src/main/kotlin/ac/mdiq/podcini/ui/compose/Composables.kt
+++ b/app/src/main/kotlin/ac/mdiq/podcini/ui/compose/Composables.kt
@@ -6,8 +6,6 @@ import androidx.compose.foundation.border
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.text.BasicTextField
-import androidx.compose.material.icons.Icons
-import androidx.compose.material.icons.filled.ArrowDropDown
import androidx.compose.material3.*
import androidx.compose.runtime.*
import androidx.compose.runtime.saveable.rememberSaveable
@@ -91,9 +89,8 @@ fun Spinner(items: List, selectedItem: String, modifier: Modifier = Modi
@Composable
fun Spinner(items: List, selectedIndex: Int, modifier: Modifier = Modifier, onItemSelected: (Int) -> Unit) {
var expanded by remember { mutableStateOf(false) }
- var currentSelectedIndex by remember { mutableStateOf(selectedIndex) }
ExposedDropdownMenuBox(expanded = expanded, onExpandedChange = { expanded = it }) {
- BasicTextField(readOnly = true, value = items.getOrNull(currentSelectedIndex) ?: "Select Item", onValueChange = { },
+ BasicTextField(readOnly = true, value = items.getOrNull(selectedIndex) ?: "Select Item", onValueChange = { },
textStyle = LocalTextStyle.current.copy(color = MaterialTheme.colorScheme.onSurface, fontSize = MaterialTheme.typography.bodyLarge.fontSize, fontWeight = FontWeight.Bold),
modifier = modifier.menuAnchor(MenuAnchorType.PrimaryNotEditable, true), // Material3 requirement
decorationBox = { innerTextField ->
@@ -106,7 +103,6 @@ fun Spinner(items: List, selectedIndex: Int, modifier: Modifier = Modifi
for (i in items.indices) {
DropdownMenuItem(text = { Text(items[i]) },
onClick = {
- currentSelectedIndex = i
onItemSelected(i)
expanded = false
}
@@ -132,36 +128,6 @@ fun CustomToast(message: String, durationMillis: Long = 2000L, onDismiss: () ->
}
}
-@Composable
-fun AutoCompleteTextView(suggestions: List, onItemSelected: (String) -> Unit, modifier: Modifier = Modifier) {
- var text by remember { mutableStateOf("") }
- var expanded by remember { mutableStateOf(false) }
-
- Column(modifier = modifier) {
- TextField(value = text,
- onValueChange = { text = it },
- label = { Text("Search") },
- trailingIcon = {
- IconButton(onClick = { expanded = !expanded }) {
- Icon(imageVector = Icons.Filled.ArrowDropDown, contentDescription = "Expand")
- }
- }
- )
-
- if (expanded) {
- DropdownMenu(expanded = expanded, onDismissRequest = { expanded = false }) {
- suggestions.forEach { suggestion ->
- DropdownMenuItem(text = {Text(suggestion)}, onClick = {
- onItemSelected(suggestion)
- text = suggestion
- expanded = false
- })
- }
- }
- }
- }
-}
-
@Composable
fun LargeTextEditingDialog(textState: TextFieldValue, onTextChange: (TextFieldValue) -> Unit, onDismissRequest: () -> Unit, onSave: (String) -> Unit) {
Dialog(onDismissRequest = { onDismissRequest() }, properties = DialogProperties(usePlatformDefaultWidth = false)) {
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 7ac5f0c3..ef574d06 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
@@ -112,24 +112,16 @@ fun InforBar(text: MutableState, leftAction: MutableState,
Logd("InforBar", "textState: ${text.value}")
Row {
Icon(imageVector = ImageVector.vectorResource(leftAction.value.getActionIcon()), tint = textColor, contentDescription = "left_action_icon",
- modifier = Modifier
- .width(24.dp)
- .height(24.dp)
- .clickable(onClick = actionConfig))
- Icon(imageVector = ImageVector.vectorResource(R.drawable.baseline_arrow_left_alt_24), tint = textColor, contentDescription = "left_arrow", modifier = Modifier
- .width(24.dp)
- .height(24.dp))
+ modifier = Modifier.width(24.dp).height(24.dp).clickable(onClick = actionConfig))
+ Icon(imageVector = ImageVector.vectorResource(R.drawable.baseline_arrow_left_alt_24), tint = textColor, contentDescription = "left_arrow",
+ modifier = Modifier.width(24.dp).height(24.dp))
Spacer(modifier = Modifier.weight(1f))
Text(text.value, color = textColor, style = MaterialTheme.typography.bodyMedium)
Spacer(modifier = Modifier.weight(1f))
- Icon(imageVector = ImageVector.vectorResource(R.drawable.baseline_arrow_right_alt_24), tint = textColor, contentDescription = "right_arrow", modifier = Modifier
- .width(24.dp)
- .height(24.dp))
+ Icon(imageVector = ImageVector.vectorResource(R.drawable.baseline_arrow_right_alt_24), tint = textColor, contentDescription = "right_arrow",
+ modifier = Modifier.width(24.dp).height(24.dp))
Icon(imageVector = ImageVector.vectorResource(rightAction.value.getActionIcon()), tint = textColor, contentDescription = "right_action_icon",
- modifier = Modifier
- .width(24.dp)
- .height(24.dp)
- .clickable(onClick = actionConfig))
+ modifier = Modifier.width(24.dp).height(24.dp).clickable(onClick = actionConfig))
}
}
@@ -213,20 +205,6 @@ class EpisodeVM(var episode: Episode) {
}
}
}
-
-// override fun equals(other: Any?): Boolean {
-// if (this === other) return true
-// if (javaClass != other?.javaClass) return false
-// other as EpisodeVM
-//
-// if (episode.id != other.episode.id) return false
-// return true
-// }
-//
-// override fun hashCode(): Int {
-// var result = episode.id.hashCode()
-// return result
-// }
}
@Composable
@@ -333,9 +311,7 @@ fun PutToQueueDialog(selected: List, onDismissRequest: () -> Unit) {
if (removeChecked) {
val toRemove = mutableSetOf()
val toRemoveCur = mutableListOf()
- selected.forEach { e ->
- if (curQueue.contains(e)) toRemoveCur.add(e)
- }
+ selected.forEach { e -> if (curQueue.contains(e)) toRemoveCur.add(e) }
selected.forEach { e ->
for (q in queues) {
if (q.contains(e)) {
@@ -347,13 +323,9 @@ fun PutToQueueDialog(selected: List, onDismissRequest: () -> Unit) {
if (toRemove.isNotEmpty()) runBlocking { removeFromAllQueuesQuiet(toRemove.toList()) }
if (toRemoveCur.isNotEmpty()) EventFlow.postEvent(FlowEvent.QueueEvent.removed(toRemoveCur))
}
- selected.forEach { e ->
- runBlocking { addToQueueSync(e, toQueue) }
- }
+ selected.forEach { e -> runBlocking { addToQueueSync(e, toQueue) } }
onDismissRequest()
- }) {
- Text("Confirm")
- }
+ }) { Text("Confirm") }
}
}
}
@@ -395,11 +367,7 @@ fun ShelveDialog(selected: List, onDismissRequest: () -> Unit) {
e_.media?.id = e_.id
} else {
val feed = realm.query(Feed::class).query("id == $0", e_.feedId).first().find()
- if (feed != null) {
- upsertBlk(feed) {
- it.episodes.remove(e_)
- }
- }
+ if (feed != null) upsertBlk(feed) { it.episodes.remove(e_) }
}
upsertBlk(e_) {
it.feed = toFeed
@@ -407,13 +375,9 @@ fun ShelveDialog(selected: List, onDismissRequest: () -> Unit) {
eList.add(it)
}
}
- upsertBlk(toFeed!!) {
- it.episodes.addAll(eList)
- }
+ upsertBlk(toFeed!!) { it.episodes.addAll(eList) }
onDismissRequest()
- }) {
- Text("Confirm")
- }
+ }) { Text("Confirm") }
}
}
}
@@ -464,9 +428,7 @@ fun EraseEpisodesDialog(selected: List, feed: Feed?, onDismissRequest:
} catch (e: Throwable) { Log.e("EraseEpisodesDialog", Log.getStackTraceString(e)) }
}
onDismissRequest()
- }) {
- Text("Confirm")
- }
+ }) { Text("Confirm") }
}
}
}
@@ -490,9 +452,7 @@ fun EpisodeLazyColumn(activity: MainActivity, vms: MutableList, feed:
val showConfirmYoutubeDialog = remember { mutableStateOf(false) }
val youtubeUrls = remember { mutableListOf() }
- ConfirmAddYoutubeEpisode(youtubeUrls, showConfirmYoutubeDialog.value, onDismissRequest = {
- showConfirmYoutubeDialog.value = false
- })
+ ConfirmAddYoutubeEpisode(youtubeUrls, showConfirmYoutubeDialog.value, onDismissRequest = { showConfirmYoutubeDialog.value = false })
var showChooseRatingDialog by remember { mutableStateOf(false) }
if (showChooseRatingDialog) ChooseRatingDialog(selected) { showChooseRatingDialog = false }
@@ -513,152 +473,133 @@ fun EpisodeLazyColumn(activity: MainActivity, vms: MutableList, feed:
fun EpisodeSpeedDial(modifier: Modifier = Modifier) {
var isExpanded by remember { mutableStateOf(false) }
val options = mutableListOf<@Composable () -> Unit>(
- { Row(modifier = Modifier
- .padding(horizontal = 16.dp)
- .clickable {
- isExpanded = false
- selectMode = false
- Logd(TAG, "ic_delete: ${selected.size}")
- runOnIOScope {
- for (item_ in selected) {
- var item = item_
- if (!item.isDownloaded && item.feed?.isLocalFeed != true) continue
- val media = item.media
- if (media != null) {
- val almostEnded = hasAlmostEnded(media)
- if (almostEnded && item.playState < PlayState.PLAYED.code) item = setPlayStateSync(PlayState.PLAYED.code, item, almostEnded, false)
- if (almostEnded) item = upsert(item) { it.media?.playbackCompletionDate = Date() }
- deleteEpisodeMedia(activity, item)
- }
+ { Row(modifier = Modifier.padding(horizontal = 16.dp).clickable {
+ isExpanded = false
+ selectMode = false
+ Logd(TAG, "ic_delete: ${selected.size}")
+ runOnIOScope {
+ for (item_ in selected) {
+ var item = item_
+ if (!item.isDownloaded && item.feed?.isLocalFeed != true) continue
+ val media = item.media
+ if (media != null) {
+ val almostEnded = hasAlmostEnded(media)
+ if (almostEnded && item.playState < PlayState.PLAYED.code) item = setPlayStateSync(PlayState.PLAYED.code, item, almostEnded, false)
+ if (almostEnded) item = upsert(item) { it.media?.playbackCompletionDate = Date() }
+ deleteEpisodeMedia(activity, item)
}
}
+ }
// LocalDeleteModal.deleteEpisodesWarnLocal(activity, selected)
- }, verticalAlignment = Alignment.CenterVertically) {
+ }, verticalAlignment = Alignment.CenterVertically) {
Icon(imageVector = ImageVector.vectorResource(id = R.drawable.ic_delete), "Delete media")
Text(stringResource(id = R.string.delete_episode_label)) } },
- { Row(modifier = Modifier
- .padding(horizontal = 16.dp)
- .clickable {
- isExpanded = false
- selectMode = false
- Logd(TAG, "ic_download: ${selected.size}")
- for (episode in selected) {
- if (episode.media != null && episode.feed != null && !episode.feed!!.isLocalFeed) DownloadServiceInterface.get()?.download(activity, episode)
- }
- }, verticalAlignment = Alignment.CenterVertically) {
+ { Row(modifier = Modifier.padding(horizontal = 16.dp).clickable {
+ isExpanded = false
+ selectMode = false
+ Logd(TAG, "ic_download: ${selected.size}")
+ for (episode in selected) {
+ if (episode.media != null && episode.feed != null && !episode.feed!!.isLocalFeed) DownloadServiceInterface.get()?.download(activity, episode)
+ }
+ }, verticalAlignment = Alignment.CenterVertically) {
Icon(imageVector = ImageVector.vectorResource(id = R.drawable.ic_download), "Download")
Text(stringResource(id = R.string.download_label)) } },
- { Row(modifier = Modifier
- .padding(horizontal = 16.dp)
- .clickable {
- showPlayStateDialog = true
- isExpanded = false
- selectMode = false
- Logd(TAG, "ic_mark_played: ${selected.size}")
+ { Row(modifier = Modifier.padding(horizontal = 16.dp).clickable {
+ showPlayStateDialog = true
+ isExpanded = false
+ selectMode = false
+ Logd(TAG, "ic_mark_played: ${selected.size}")
// setPlayState(PlayState.UNSPECIFIED.code, false, *selected.toTypedArray())
- }, verticalAlignment = Alignment.CenterVertically) {
+ }, verticalAlignment = Alignment.CenterVertically) {
Icon(imageVector = ImageVector.vectorResource(id = R.drawable.ic_mark_played), "Toggle played state")
Text(stringResource(id = R.string.set_play_state_label)) } },
- { Row(modifier = Modifier
- .padding(horizontal = 16.dp)
- .clickable {
- isExpanded = false
- selectMode = false
- Logd(TAG, "ic_playlist_remove: ${selected.size}")
- runOnIOScope {
- for (item_ in selected) {
- var item = item_
- val media = item.media
- if (media != null) {
- val almostEnded = hasAlmostEnded(media)
- if (almostEnded && item.playState < PlayState.PLAYED.code) item = setPlayStateSync(PlayState.PLAYED.code, item, almostEnded, false)
- if (almostEnded) item = upsert(item) { it.media?.playbackCompletionDate = Date() }
- }
- if (item.playState < PlayState.SKIPPED.code) setPlayState(PlayState.SKIPPED.code, false, item)
+ { Row(modifier = Modifier.padding(horizontal = 16.dp).clickable {
+ isExpanded = false
+ selectMode = false
+ Logd(TAG, "ic_playlist_remove: ${selected.size}")
+ runOnIOScope {
+ for (item_ in selected) {
+ var item = item_
+ val media = item.media
+ if (media != null) {
+ val almostEnded = hasAlmostEnded(media)
+ if (almostEnded && item.playState < PlayState.PLAYED.code) item = setPlayStateSync(PlayState.PLAYED.code, item, almostEnded, false)
+ if (almostEnded) item = upsert(item) { it.media?.playbackCompletionDate = Date() }
}
- removeFromQueueSync(curQueue, *selected.toTypedArray())
-// removeFromQueue(*selected.toTypedArray())
+ if (item.playState < PlayState.SKIPPED.code) setPlayState(PlayState.SKIPPED.code, false, item)
}
- }, verticalAlignment = Alignment.CenterVertically) {
+ removeFromQueueSync(curQueue, *selected.toTypedArray())
+// removeFromQueue(*selected.toTypedArray())
+ }
+ }, verticalAlignment = Alignment.CenterVertically) {
Icon(imageVector = ImageVector.vectorResource(id = R.drawable.ic_playlist_remove), "Remove from active queue")
Text(stringResource(id = R.string.remove_from_queue_label)) } },
- { Row(modifier = Modifier
- .padding(horizontal = 16.dp)
- .clickable {
- isExpanded = false
- selectMode = false
- Logd(TAG, "ic_playlist_play: ${selected.size}")
- Queues.addToQueue(*selected.toTypedArray())
- }, verticalAlignment = Alignment.CenterVertically) {
+ { Row(modifier = Modifier.padding(horizontal = 16.dp).clickable {
+ isExpanded = false
+ selectMode = false
+ Logd(TAG, "ic_playlist_play: ${selected.size}")
+ Queues.addToQueue(*selected.toTypedArray())
+ }, verticalAlignment = Alignment.CenterVertically) {
Icon(imageVector = ImageVector.vectorResource(id = R.drawable.ic_playlist_play), "Add to active queue")
Text(stringResource(id = R.string.add_to_queue_label)) } },
- { Row(modifier = Modifier
- .padding(horizontal = 16.dp)
- .clickable {
- isExpanded = false
- selectMode = false
- Logd(TAG, "shelve_label: ${selected.size}")
- showShelveDialog = true
- }, verticalAlignment = Alignment.CenterVertically) {
+ { Row(modifier = Modifier.padding(horizontal = 16.dp).clickable {
+ isExpanded = false
+ selectMode = false
+ Logd(TAG, "shelve_label: ${selected.size}")
+ showShelveDialog = true
+ }, verticalAlignment = Alignment.CenterVertically) {
Icon(imageVector = ImageVector.vectorResource(id = R.drawable.baseline_shelves_24), "Shelve")
Text(stringResource(id = R.string.shelve_label)) } },
- { Row(modifier = Modifier
- .padding(horizontal = 16.dp)
- .clickable {
- isExpanded = false
- selectMode = false
- Logd(TAG, "ic_playlist_play: ${selected.size}")
- showPutToQueueDialog = true
- }, verticalAlignment = Alignment.CenterVertically) {
+ { Row(modifier = Modifier.padding(horizontal = 16.dp).clickable {
+ isExpanded = false
+ selectMode = false
+ Logd(TAG, "ic_playlist_play: ${selected.size}")
+ showPutToQueueDialog = true
+ }, verticalAlignment = Alignment.CenterVertically) {
Icon(imageVector = ImageVector.vectorResource(id = R.drawable.ic_playlist_play), "Add to queue...")
Text(stringResource(id = R.string.put_in_queue_label)) } },
- { Row(modifier = Modifier
- .padding(horizontal = 16.dp)
- .clickable {
- selectMode = false
- Logd(TAG, "ic_star: ${selected.size}")
- showChooseRatingDialog = true
- isExpanded = false
- }, verticalAlignment = Alignment.CenterVertically) {
+ { Row(modifier = Modifier.padding(horizontal = 16.dp).clickable {
+ selectMode = false
+ Logd(TAG, "ic_star: ${selected.size}")
+ showChooseRatingDialog = true
+ isExpanded = false
+ }, verticalAlignment = Alignment.CenterVertically) {
Icon(imageVector = ImageVector.vectorResource(id = R.drawable.ic_star), "Set rating")
Text(stringResource(id = R.string.set_rating_label)) } },
)
if (selected.isNotEmpty() && selected[0].isRemote.value)
options.add {
- Row(modifier = Modifier.padding(horizontal = 16.dp)
- .clickable {
- isExpanded = false
- selectMode = false
- Logd(TAG, "reserve: ${selected.size}")
- CoroutineScope(Dispatchers.IO).launch {
- youtubeUrls.clear()
- for (e in selected) {
- Logd(TAG, "downloadUrl: ${e.media?.downloadUrl}")
- val url = URL(e.media?.downloadUrl ?: "")
- if ((isYoutubeURL(url) && url.path.startsWith("/watch")) || isYoutubeServiceURL(url)) {
- youtubeUrls.add(e.media!!.downloadUrl!!)
- } else addToMiscSyndicate(e)
- }
- Logd(TAG, "youtubeUrls: ${youtubeUrls.size}")
- withContext(Dispatchers.Main) {
- showConfirmYoutubeDialog.value = youtubeUrls.isNotEmpty()
- }
+ Row(modifier = Modifier.padding(horizontal = 16.dp).clickable {
+ isExpanded = false
+ selectMode = false
+ Logd(TAG, "reserve: ${selected.size}")
+ CoroutineScope(Dispatchers.IO).launch {
+ youtubeUrls.clear()
+ for (e in selected) {
+ Logd(TAG, "downloadUrl: ${e.media?.downloadUrl}")
+ val url = URL(e.media?.downloadUrl ?: "")
+ if ((isYoutubeURL(url) && url.path.startsWith("/watch")) || isYoutubeServiceURL(url)) {
+ youtubeUrls.add(e.media!!.downloadUrl!!)
+ } else addToMiscSyndicate(e)
}
- }, verticalAlignment = Alignment.CenterVertically) {
+ Logd(TAG, "youtubeUrls: ${youtubeUrls.size}")
+ withContext(Dispatchers.Main) {
+ showConfirmYoutubeDialog.value = youtubeUrls.isNotEmpty()
+ }
+ }
+ }, verticalAlignment = Alignment.CenterVertically) {
Icon(Icons.Filled.AddCircle, "Reserve episodes")
Text(stringResource(id = R.string.reserve_episodes_label))
}
}
if (feed != null && feed.id <= MAX_SYNTHETIC_ID) {
options.add {
- Row(modifier = Modifier
- .padding(horizontal = 16.dp)
- .clickable {
- isExpanded = false
- selectMode = false
- showEraseDialog = true
- Logd(TAG, "reserve: ${selected.size}")
- }, verticalAlignment = Alignment.CenterVertically) {
+ Row(modifier = Modifier.padding(horizontal = 16.dp).clickable {
+ isExpanded = false
+ selectMode = false
+ showEraseDialog = true
+ Logd(TAG, "reserve: ${selected.size}")
+ }, verticalAlignment = Alignment.CenterVertically) {
Icon(imageVector = ImageVector.vectorResource(id = R.drawable.baseline_delete_forever_24), "Erase episodes")
Text(stringResource(id = R.string.erase_episodes_label))
}
@@ -668,14 +609,9 @@ fun EpisodeLazyColumn(activity: MainActivity, vms: MutableList, feed:
val scrollState = rememberScrollState()
Column(modifier = modifier.verticalScroll(scrollState), verticalArrangement = Arrangement.Bottom) {
if (isExpanded) options.forEachIndexed { _, button ->
- FloatingActionButton(modifier = Modifier
- .padding(start = 4.dp, bottom = 6.dp)
- .height(40.dp),
- containerColor = Color.LightGray,
- onClick = {}) { button() }
+ FloatingActionButton(modifier = Modifier.padding(start = 4.dp, bottom = 6.dp).height(40.dp), containerColor = Color.LightGray, onClick = {}) { button() }
}
- FloatingActionButton(containerColor = MaterialTheme.colorScheme.secondaryContainer,
- contentColor = MaterialTheme.colorScheme.secondary,
+ FloatingActionButton(containerColor = MaterialTheme.colorScheme.secondaryContainer, contentColor = MaterialTheme.colorScheme.secondary,
onClick = { isExpanded = !isExpanded }) { Icon(Icons.Filled.Edit, "Edit") }
}
}
@@ -807,8 +743,7 @@ fun EpisodeLazyColumn(activity: MainActivity, vms: MutableList, feed:
if (isDownloading() && vm.dlPercent >= 0) CircularProgressIndicator(progress = { 0.01f * vm.dlPercent },
strokeWidth = 4.dp, color = textColor, modifier = Modifier.width(30.dp).height(35.dp))
}
- if (vm.showAltActionsDialog) actionButton.AltActionsDialog(activity, vm.showAltActionsDialog,
- onDismiss = { vm.showAltActionsDialog = false })
+ if (vm.showAltActionsDialog) actionButton.AltActionsDialog(activity, vm.showAltActionsDialog, onDismiss = { vm.showAltActionsDialog = false })
}
}
@@ -1037,9 +972,7 @@ fun EpisodesFilterDialog(filter: EpisodeFilter? = null, filtersDisabled: Mutable
}
onFilterChanged(filterValues)
},
- ) {
- Text(text = stringResource(item.values[0].displayName), color = textColor)
- }
+ ) { Text(text = stringResource(item.values[0].displayName), color = textColor) }
Spacer(Modifier.weight(0.1f))
OutlinedButton(
modifier = Modifier.padding(0.dp), border = BorderStroke(2.dp, if (selectedIndex != 1) textColor else Color.Green),
@@ -1055,9 +988,7 @@ fun EpisodesFilterDialog(filter: EpisodeFilter? = null, filtersDisabled: Mutable
}
onFilterChanged(filterValues)
},
- ) {
- Text(text = stringResource(item.values[1].displayName), color = textColor)
- }
+ ) { Text(text = stringResource(item.values[1].displayName), color = textColor) }
Spacer(Modifier.weight(0.5f))
}
} else {
@@ -1133,9 +1064,7 @@ fun EpisodesFilterDialog(filter: EpisodeFilter? = null, filtersDisabled: Mutable
else filterValues.remove(item.values[index].filterId)
onFilterChanged(filterValues)
},
- ) {
- Text(text = stringResource(item.values[index].displayName), maxLines = 1, color = textColor)
- }
+ ) { Text(text = stringResource(item.values[index].displayName), maxLines = 1, color = textColor) }
}
}
}
@@ -1145,15 +1074,9 @@ fun EpisodesFilterDialog(filter: EpisodeFilter? = null, filtersDisabled: Mutable
Button(onClick = {
selectNone = true
onFilterChanged(setOf(""))
- }) {
- Text(stringResource(R.string.reset))
- }
+ }) { Text(stringResource(R.string.reset)) }
Spacer(Modifier.weight(0.4f))
- Button(onClick = {
- onDismissRequest()
- }) {
- Text(stringResource(R.string.close_label))
- }
+ Button(onClick = { onDismissRequest() }) { Text(stringResource(R.string.close_label)) }
Spacer(Modifier.weight(0.3f))
}
}
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 3d46bd7b..3b5edd42 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
@@ -244,7 +244,7 @@ fun RenameOrCreateSyntheticFeed(feed_: Feed? = null, onDismissRequest: () -> Uni
Row {
Button({ onDismissRequest() }) { Text(stringResource(R.string.cancel_label)) }
Button({
- val feed = if (feed_ == null) createSynthetic(0, name, hasVideo) else feed_
+ val feed = feed_ ?: createSynthetic(0, name, hasVideo)
if (feed_ == null) {
feed.type = if (isYoutube) Feed.FeedType.YOUTUBE.name else Feed.FeedType.RSS.name
if (hasVideo) feed.preferences!!.videoModePolicy = VideoMode.WINDOW_VIEW
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 7b6d08dc..cc7fd305 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
@@ -8,7 +8,6 @@ import ac.mdiq.podcini.storage.database.Episodes.getEpisodesCount
import ac.mdiq.podcini.storage.model.Episode
import ac.mdiq.podcini.storage.model.EpisodeFilter
import ac.mdiq.podcini.storage.model.EpisodeSortOrder
-import ac.mdiq.podcini.ui.activity.MainActivity
import ac.mdiq.podcini.ui.dialog.EpisodeSortDialog
import ac.mdiq.podcini.util.EventFlow
import ac.mdiq.podcini.util.FlowEvent
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 422ce67d..0a2a6b1a 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
@@ -97,7 +97,7 @@ import java.text.NumberFormat
import kotlin.math.max
class AudioPlayerFragment : Fragment() {
- val prefs: SharedPreferences by lazy { requireContext().getSharedPreferences(PREF, Context.MODE_PRIVATE) }
+ val prefs: SharedPreferences by lazy { requireContext().getSharedPreferences("AudioPlayerFragmentPrefs", Context.MODE_PRIVATE) }
private var isCollapsed by mutableStateOf(true)
@@ -999,13 +999,7 @@ class AudioPlayerFragment : Fragment() {
val TAG = AudioPlayerFragment::class.simpleName ?: "Anonymous"
var media3Controller: MediaController? = null
- private const val PREF = "ItemDescriptionFragmentPrefs"
private const val PREF_SCROLL_Y = "prefScrollY"
private const val PREF_PLAYABLE_ID = "prefPlayableId"
-
-// var prefs: SharedPreferences? = null
-// fun getSharedPrefs(context: Context) {
-// if (prefs == null) prefs = context.getSharedPreferences(PREF, Context.MODE_PRIVATE)
-// }
}
}
diff --git a/app/src/main/kotlin/ac/mdiq/podcini/ui/fragment/EpisodeInfoFragment.kt b/app/src/main/kotlin/ac/mdiq/podcini/ui/fragment/EpisodeInfoFragment.kt
index 865ba6ed..9a141d52 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
@@ -34,7 +34,6 @@ import ac.mdiq.podcini.util.IntentUtils
import ac.mdiq.podcini.util.Logd
import ac.mdiq.podcini.util.MiscFormatter.formatAbbrev
import android.content.Context
-import android.os.Build
import android.os.Bundle
import android.speech.tts.TextToSpeech
import android.text.TextUtils
@@ -90,9 +89,6 @@ import okhttp3.Request.Builder
import java.io.File
import java.util.*
-/**
- * Displays information about an Episode (FeedItem) and actions.
- */
class EpisodeInfoFragment : Fragment(), Toolbar.OnMenuItemClickListener {
private var _binding: ComposeFragmentBinding? = null
private val binding get() = _binding!!
@@ -348,8 +344,9 @@ class EpisodeInfoFragment : Fragment(), Toolbar.OnMenuItemClickListener {
if (episode == null) return false
val notes = episode!!.description
if (!notes.isNullOrEmpty()) {
- val shareText = if (Build.VERSION.SDK_INT >= 24) HtmlCompat.fromHtml(notes, HtmlCompat.FROM_HTML_MODE_COMPACT).toString()
- else HtmlCompat.fromHtml(notes, HtmlCompat.FROM_HTML_MODE_COMPACT).toString()
+// val shareText = if (Build.VERSION.SDK_INT >= 24) HtmlCompat.fromHtml(notes, HtmlCompat.FROM_HTML_MODE_COMPACT).toString()
+// else HtmlCompat.fromHtml(notes, HtmlCompat.FROM_HTML_MODE_COMPACT).toString()
+ val shareText = HtmlCompat.fromHtml(notes, HtmlCompat.FROM_HTML_MODE_COMPACT).toString()
val context = requireContext()
val intent = ShareCompat.IntentBuilder(context)
.setType("text/plain")
@@ -774,8 +771,8 @@ class EpisodeInfoFragment : Fragment(), Toolbar.OnMenuItemClickListener {
if (textSpeech?.isVisible == true) {
if (ttsPlaying) textSpeech.setIcon(R.drawable.ic_pause) else textSpeech.setIcon(R.drawable.ic_play_24dp)
}
- menu.findItem(R.id.share_notes)?.setVisible(readMode)
- menu.findItem(R.id.switchJS)?.setVisible(!readMode)
+ menu.findItem(R.id.share_notes)?.isVisible = readMode
+ menu.findItem(R.id.switchJS)?.isVisible = !readMode
val btn = menu.findItem(R.id.switch_home)
if (readMode) btn?.setIcon(R.drawable.baseline_home_24)
else btn?.setIcon(R.drawable.outline_home_24)
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 712630a7..eb615f7f 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
@@ -62,9 +62,6 @@ import java.util.*
import java.util.concurrent.ExecutionException
import java.util.concurrent.Semaphore
-/**
- * Displays a list of FeedItems.
- */
class FeedEpisodesFragment : Fragment(), Toolbar.OnMenuItemClickListener {
private var _binding: ComposeFragmentBinding? = null
@@ -384,10 +381,10 @@ import java.util.concurrent.Semaphore
private fun updateToolbar() {
if (feed == null) return
- binding.toolbar.menu.findItem(R.id.visit_website_item).setVisible(feed!!.link != null)
- binding.toolbar.menu.findItem(R.id.refresh_complete_item).setVisible(feed!!.isPaged)
- if (StringUtils.isBlank(feed!!.link)) binding.toolbar.menu.findItem(R.id.visit_website_item).setVisible(false)
- if (feed!!.isLocalFeed) binding.toolbar.menu.findItem(R.id.share_feed).setVisible(false)
+ binding.toolbar.menu.findItem(R.id.visit_website_item).isVisible = feed!!.link != null
+ binding.toolbar.menu.findItem(R.id.refresh_complete_item).isVisible = feed!!.isPaged
+ if (StringUtils.isBlank(feed!!.link)) binding.toolbar.menu.findItem(R.id.visit_website_item).isVisible = false
+ if (feed!!.isLocalFeed) binding.toolbar.menu.findItem(R.id.share_feed).isVisible = false
}
// override fun onConfigurationChanged(newConfig: Configuration) {
@@ -451,20 +448,21 @@ import java.util.concurrent.Semaphore
return true
}
+ // TODO: not really needed
private fun onQueueEvent(event: FlowEvent.QueueEvent) {
if (feed == null || episodes.isEmpty()) return
- var i = 0
- val size: Int = event.episodes.size
- while (i < size) {
- val item = event.episodes[i++]
- if (item.feedId != feed!!.id) continue
- val pos: Int = ieMap[item.id] ?: -1
- if (pos >= 0) {
-// episodes[pos].inQueueState.value = event.inQueue()
-// queueChanged++
- }
- break
- }
+// var i = 0
+// val size: Int = event.episodes.size
+// while (i < size) {
+// val item = event.episodes[i++]
+// if (item.feedId != feed!!.id) continue
+// val pos: Int = ieMap[item.id] ?: -1
+// if (pos >= 0) {
+//// episodes[pos].inQueueState.value = event.inQueue()
+//// queueChanged++
+// }
+// break
+// }
}
private fun onPlayEvent(event: FlowEvent.PlayEvent) {
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 3e188449..e0524bec 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
@@ -318,11 +318,10 @@ class FeedInfoFragment : Fragment(), Toolbar.OnMenuItemClickListener {
}
private fun refreshToolbarState() {
- toolbar.menu?.findItem(R.id.reconnect_local_folder)?.setVisible(feed.isLocalFeed)
- toolbar.menu?.findItem(R.id.share_item)?.setVisible(!feed.isLocalFeed)
- toolbar.menu?.findItem(R.id.visit_website_item)
- ?.setVisible(feed.link != null && IntentUtils.isCallable(requireContext(), Intent(Intent.ACTION_VIEW, Uri.parse(feed.link))))
- toolbar.menu?.findItem(R.id.edit_feed_url_item)?.setVisible(!feed.isLocalFeed)
+ toolbar.menu?.findItem(R.id.reconnect_local_folder)?.isVisible = feed.isLocalFeed
+ toolbar.menu?.findItem(R.id.share_item)?.isVisible = !feed.isLocalFeed
+ toolbar.menu?.findItem(R.id.visit_website_item)?.isVisible = feed.link != null && IntentUtils.isCallable(requireContext(), Intent(Intent.ACTION_VIEW, Uri.parse(feed.link)))
+ toolbar.menu?.findItem(R.id.edit_feed_url_item)?.isVisible = !feed.isLocalFeed
}
override fun onMenuItemClick(item: MenuItem): Boolean {
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 8d6c18d6..bec674d3 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
@@ -104,7 +104,7 @@ class FeedSettingsFragment : Fragment() {
Spacer(modifier = Modifier.width(20.dp))
Text(text = stringResource(R.string.keep_updated), style = MaterialTheme.typography.titleLarge, color = textColor)
Spacer(modifier = Modifier.weight(1f))
- var checked by remember { mutableStateOf(feed?.preferences?.keepUpdated ?: true) }
+ var checked by remember { mutableStateOf(feed?.preferences?.keepUpdated != false) }
Switch(checked = checked, modifier = Modifier.height(24.dp),
onCheckedChange = {
checked = it
@@ -138,7 +138,7 @@ class FeedSettingsFragment : Fragment() {
Spacer(modifier = Modifier.width(20.dp))
Text(text = stringResource(R.string.pref_stream_over_download_title), style = MaterialTheme.typography.titleLarge, color = textColor)
Spacer(modifier = Modifier.weight(1f))
- var checked by remember { mutableStateOf(feed?.preferences?.prefStreamOverDownload ?: false) }
+ var checked by remember { mutableStateOf(feed?.preferences?.prefStreamOverDownload == true) }
Switch(checked = checked, modifier = Modifier.height(24.dp),
onCheckedChange = {
checked = it
@@ -213,7 +213,7 @@ class FeedSettingsFragment : Fragment() {
Spacer(modifier = Modifier.width(20.dp))
Text(text = stringResource(R.string.audo_add_new_queue), style = MaterialTheme.typography.titleLarge, color = textColor)
Spacer(modifier = Modifier.weight(1f))
- var checked by remember { mutableStateOf(feed?.preferences?.autoAddNewToQueue ?: true) }
+ var checked by remember { mutableStateOf(feed?.preferences?.autoAddNewToQueue != false) }
Switch(checked = checked, modifier = Modifier.height(24.dp),
onCheckedChange = {
checked = it
@@ -302,7 +302,7 @@ class FeedSettingsFragment : Fragment() {
}
if (isEnableAutodownload && feed?.type != Feed.FeedType.YOUTUBE.name) {
// auto download
- var audoDownloadChecked by remember { mutableStateOf(feed?.preferences?.autoDownload ?: false) }
+ var audoDownloadChecked by remember { mutableStateOf(feed?.preferences?.autoDownload == true) }
Column {
Row(Modifier.fillMaxWidth()) {
Text(text = stringResource(R.string.auto_download_label), style = MaterialTheme.typography.titleLarge, color = textColor)
@@ -341,7 +341,7 @@ class FeedSettingsFragment : Fragment() {
Row(Modifier.fillMaxWidth()) {
Text(text = stringResource(R.string.pref_auto_download_counting_played_title), style = MaterialTheme.typography.titleLarge, color = textColor)
Spacer(modifier = Modifier.weight(1f))
- var checked by remember { mutableStateOf(feed?.preferences?.countingPlayed ?: true) }
+ var checked by remember { mutableStateOf(feed?.preferences?.countingPlayed != false) }
Switch(checked = checked, modifier = Modifier.height(24.dp),
onCheckedChange = {
checked = it
@@ -800,9 +800,7 @@ class FeedSettingsFragment : Fragment() {
.setPositiveButton(android.R.string.ok) { _: DialogInterface, _: Int ->
val newSpeed = if (binding.useGlobalCheckbox.isChecked) FeedPreferences.SPEED_USE_GLOBAL
else binding.seekBar.currentSpeed
- feed = upsertBlk(feed!!) {
- it.preferences?.playSpeed = newSpeed
- }
+ feed = upsertBlk(feed!!) { it.preferences?.playSpeed = newSpeed }
}
.setNegativeButton(R.string.cancel_label, null)
.create()
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 39841a61..8e0729fc 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
@@ -96,9 +96,9 @@ class HistoryFragment : BaseEpisodesFragment() {
override fun updateToolbar() {
// Not calling super, as we do not have a refresh button that could be updated
- toolbar.menu.findItem(R.id.episodes_sort).setVisible(episodes.isNotEmpty())
- toolbar.menu.findItem(R.id.filter_items).setVisible(episodes.isNotEmpty())
- toolbar.menu.findItem(R.id.clear_history_item).setVisible(episodes.isNotEmpty())
+ toolbar.menu.findItem(R.id.episodes_sort).isVisible = episodes.isNotEmpty()
+ toolbar.menu.findItem(R.id.filter_items).isVisible = episodes.isNotEmpty()
+ toolbar.menu.findItem(R.id.clear_history_item).isVisible = episodes.isNotEmpty()
swipeActions.setFilter(getFilter())
var info = "${episodes.size} episodes"
diff --git a/app/src/main/kotlin/ac/mdiq/podcini/ui/fragment/LogsFragment.kt b/app/src/main/kotlin/ac/mdiq/podcini/ui/fragment/LogsFragment.kt
index 8c3d2caf..50113d21 100644
--- a/app/src/main/kotlin/ac/mdiq/podcini/ui/fragment/LogsFragment.kt
+++ b/app/src/main/kotlin/ac/mdiq/podcini/ui/fragment/LogsFragment.kt
@@ -5,11 +5,11 @@ import ac.mdiq.podcini.databinding.ComposeFragmentBinding
import ac.mdiq.podcini.net.feed.FeedUpdateManager
import ac.mdiq.podcini.storage.database.Feeds.getFeed
import ac.mdiq.podcini.storage.database.Feeds.getFeedByTitleAndAuthor
+import ac.mdiq.podcini.storage.database.LogsAndStats.DownloadResultComparator
import ac.mdiq.podcini.storage.database.RealmDB.realm
import ac.mdiq.podcini.storage.database.RealmDB.runOnIOScope
import ac.mdiq.podcini.storage.model.*
import ac.mdiq.podcini.storage.model.Rating.Companion.fromCode
-import ac.mdiq.podcini.storage.utils.DownloadResultComparator
import ac.mdiq.podcini.ui.actions.DownloadActionButton
import ac.mdiq.podcini.ui.activity.MainActivity
import ac.mdiq.podcini.ui.activity.ShareReceiverActivity.Companion.receiveShared
@@ -301,7 +301,7 @@ class LogsFragment : Fragment(), Toolbar.OnMenuItemClickListener {
@Deprecated("Deprecated in Java")
override fun onPrepareOptionsMenu(menu: Menu) {
- menu.findItem(R.id.clear_logs_item).setVisible(shareLogs.isNotEmpty())
+ menu.findItem(R.id.clear_logs_item).isVisible = shareLogs.isNotEmpty()
}
private fun clearAllLogs() {
diff --git a/app/src/main/kotlin/ac/mdiq/podcini/ui/fragment/NavDrawerFragment.kt b/app/src/main/kotlin/ac/mdiq/podcini/ui/fragment/NavDrawerFragment.kt
index bd6dba3e..ce5d4a41 100644
--- a/app/src/main/kotlin/ac/mdiq/podcini/ui/fragment/NavDrawerFragment.kt
+++ b/app/src/main/kotlin/ac/mdiq/podcini/ui/fragment/NavDrawerFragment.kt
@@ -59,6 +59,7 @@ import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import kotlin.math.max
+import kotlin.math.roundToInt
class NavDrawerFragment : Fragment(), OnSharedPreferenceChangeListener {
val TAG = this::class.simpleName ?: "Anonymous"
@@ -68,13 +69,7 @@ class NavDrawerFragment : Fragment(), OnSharedPreferenceChangeListener {
super.onCreateView(inflater, container, savedInstanceState)
checkHiddenItems()
getRecentPodcasts()
- val composeView = ComposeView(requireContext()).apply {
- setContent {
- CustomTheme(requireContext()) {
- MainView()
- }
- }
- }
+ val composeView = ComposeView(requireContext()).apply { setContent { CustomTheme(requireContext()) { MainView() } } }
Logd(TAG, "fragment onCreateView")
setupDrawerRoundBackground(composeView)
@@ -87,7 +82,7 @@ class NavDrawerFragment : Fragment(), OnSharedPreferenceChangeListener {
navigationBarHeight = if (requireActivity().window.navigationBarDividerColor == Color.TRANSPARENT) 0f
else 1 * resources.displayMetrics.density // Assuming the divider is 1dp in height
}
- val bottomInset = max(0.0, Math.round(bars.bottom - navigationBarHeight).toDouble()).toFloat()
+ val bottomInset = max(0.0, (bars.bottom - navigationBarHeight).roundToInt().toDouble()).toFloat()
(view.layoutParams as ViewGroup.MarginLayoutParams).bottomMargin = bottomInset.toInt()
insets
}
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 243e21b1..d1ab0d1e 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
@@ -41,8 +41,11 @@ import android.view.View
import android.view.ViewGroup
import androidx.annotation.UiThread
import androidx.collection.ArrayMap
-import androidx.compose.foundation.*
+import androidx.compose.foundation.background
+import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.*
+import androidx.compose.foundation.rememberScrollState
+import androidx.compose.foundation.verticalScroll
import androidx.compose.material3.*
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
@@ -68,7 +71,6 @@ import org.jsoup.nodes.Document
import java.io.File
import java.io.IOException
import java.util.*
-import kotlin.concurrent.Volatile
/**
* Downloads a feed from a feed URL and parses it. Subclasses can display the
@@ -736,11 +738,11 @@ class OnlineFeedFragment : Fragment() {
return PREF_NAME
}
override fun updateToolbar() {
- binding.toolbar.menu.findItem(R.id.episodes_sort).setVisible(false)
+ binding.toolbar.menu.findItem(R.id.episodes_sort).isVisible = false
// binding.toolbar.menu.findItem(R.id.refresh_item).setVisible(false)
- binding.toolbar.menu.findItem(R.id.action_search).setVisible(false)
+ binding.toolbar.menu.findItem(R.id.action_search).isVisible = false
// binding.toolbar.menu.findItem(R.id.action_favorites).setVisible(false)
- binding.toolbar.menu.findItem(R.id.filter_items).setVisible(false)
+ binding.toolbar.menu.findItem(R.id.filter_items).isVisible = false
infoBarText.value = "${episodes.size} episodes"
}
override fun onMenuItemClick(item: MenuItem): Boolean {
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 fb532c54..8bc0ad93 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
@@ -36,10 +36,6 @@ import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
-/**
- * Provides actions for adding new podcast subscriptions.
- */
-
class OnlineSearchFragment : Fragment() {
private var _binding: AddfeedBinding? = null
@@ -56,11 +52,11 @@ class OnlineSearchFragment : Fragment() {
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
super.onCreateView(inflater, container, savedInstanceState)
_binding = AddfeedBinding.inflate(inflater)
- activity = getActivity() as? MainActivity
+// activity = activity
Logd(TAG, "fragment onCreateView")
displayUpArrow = parentFragmentManager.backStackEntryCount != 0
if (savedInstanceState != null) displayUpArrow = savedInstanceState.getBoolean(KEY_UP_ARROW)
- (getActivity() as MainActivity).setupToolbarToggle(binding.toolbar, displayUpArrow)
+ (activity as MainActivity).setupToolbarToggle(binding.toolbar, displayUpArrow)
binding.searchButton.setOnClickListener { performSearch() }
binding.searchVistaGuideButton.setOnClickListener { activity?.loadChildFragment(SearchResultsFragment.newInstance(VistaGuidePodcastSearcher::class.java)) }
@@ -172,12 +168,12 @@ class OnlineSearchFragment : Fragment() {
withContext(Dispatchers.Main) {
if (feed != null) {
val fragment: Fragment = FeedEpisodesFragment.newInstance(feed.id)
- (getActivity() as MainActivity).loadChildFragment(fragment)
+ (activity as MainActivity).loadChildFragment(fragment)
}
}
} catch (e: Throwable) {
Log.e(TAG, Log.getStackTraceString(e))
- (getActivity() as MainActivity).showSnackbarAbovePlayer(e.localizedMessage?: "No messaage", Snackbar.LENGTH_LONG)
+ (activity as MainActivity).showSnackbarAbovePlayer(e.localizedMessage?: "No messaage", Snackbar.LENGTH_LONG)
}
}
}
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 89cb5ef1..c0577911 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
@@ -143,7 +143,7 @@ class QueuesFragment : Fragment(), Toolbar.OnMenuItemClickListener {
queueNames = queues.map { it.name }.toTypedArray()
spinnerTexts.clear()
spinnerTexts.addAll(queues.map { "${it.name} : ${it.size()}" })
- curIndex = queues.indexOf(curQueue)
+// curIndex = queues.indexOf(curQueue)
(activity as MainActivity).setupToolbarToggle(toolbar, displayUpArrow)
toolbar.inflateMenu(R.menu.queue)
@@ -156,7 +156,7 @@ class QueuesFragment : Fragment(), Toolbar.OnMenuItemClickListener {
Logd(TAG, "Queue selected: $queues[index].name")
val prevQueueSize = curQueue.size()
curQueue = upsertBlk(queues[index]) { it.update() }
- toolbar.menu?.findItem(R.id.rename_queue)?.setVisible(curQueue.name != "Default")
+ toolbar.menu?.findItem(R.id.rename_queue)?.isVisible = curQueue.name != "Default"
loadCurQueue(true)
playbackService?.notifyCurQueueItemsChanged(max(prevQueueSize, curQueue.size()))
}
@@ -246,7 +246,7 @@ class QueuesFragment : Fragment(), Toolbar.OnMenuItemClickListener {
items(feedsAssociated.size, key = {index -> feedsAssociated[index].id}) { index ->
val feed by remember { mutableStateOf(feedsAssociated[index]) }
ConstraintLayout {
- val (coverImage, episodeCount, rating, error) = createRefs()
+ val (coverImage, episodeCount, rating, _) = createRefs()
val imgLoc = remember(feed) { feed.imageUrl }
AsyncImage(model = ImageRequest.Builder(context).data(imgLoc)
.memoryCachePolicy(CachePolicy.ENABLED).placeholder(R.mipmap.ic_launcher).error(R.mipmap.ic_launcher).build(),
@@ -374,7 +374,7 @@ class QueuesFragment : Fragment(), Toolbar.OnMenuItemClickListener {
}
queues = realm.query(PlayQueue::class).find()
queueNames = queues.map { it.name }.toTypedArray()
- curIndex = queues.indexOf(curQueue)
+ curIndex = queues.indexOfFirst { it.id == curQueue.id }
spinnerTexts.clear()
spinnerTexts.addAll(queues.map { "${it.name} : ${it.size()}" })
refreshMenuItems()
@@ -451,20 +451,20 @@ class QueuesFragment : Fragment(), Toolbar.OnMenuItemClickListener {
private fun refreshMenuItems() {
if (showBin) {
- toolbar.menu?.findItem(R.id.queue_sort)?.setVisible(false)
- toolbar.menu?.findItem(R.id.rename_queue)?.setVisible(false)
- toolbar.menu?.findItem(R.id.associated_feed)?.setVisible(false)
- toolbar.menu?.findItem(R.id.add_queue)?.setVisible(false)
- toolbar.menu?.findItem(R.id.queue_lock)?.setVisible(false)
- toolbar.menu?.findItem(R.id.action_search)?.setVisible(false)
+ toolbar.menu?.findItem(R.id.queue_sort)?.isVisible = false
+ toolbar.menu?.findItem(R.id.rename_queue)?.isVisible = false
+ toolbar.menu?.findItem(R.id.associated_feed)?.isVisible = false
+ toolbar.menu?.findItem(R.id.add_queue)?.isVisible = false
+ toolbar.menu?.findItem(R.id.queue_lock)?.isVisible = false
+ toolbar.menu?.findItem(R.id.action_search)?.isVisible = false
} else {
- toolbar.menu?.findItem(R.id.action_search)?.setVisible(true)
- toolbar.menu?.findItem(R.id.queue_sort)?.setVisible(true)
- toolbar.menu?.findItem(R.id.associated_feed)?.setVisible(true)
- toolbar.menu?.findItem(R.id.queue_lock)?.setChecked(isQueueLocked)
- toolbar.menu?.findItem(R.id.queue_lock)?.setVisible(!isQueueKeepSorted)
- toolbar.menu?.findItem(R.id.rename_queue)?.setVisible(curQueue.name != "Default")
- toolbar.menu?.findItem(R.id.add_queue)?.setVisible(queueNames.size < 9)
+ toolbar.menu?.findItem(R.id.action_search)?.isVisible = true
+ toolbar.menu?.findItem(R.id.queue_sort)?.isVisible = true
+ toolbar.menu?.findItem(R.id.associated_feed)?.isVisible = true
+ toolbar.menu?.findItem(R.id.queue_lock)?.isChecked = isQueueLocked
+ toolbar.menu?.findItem(R.id.queue_lock)?.isVisible = !isQueueKeepSorted
+ toolbar.menu?.findItem(R.id.rename_queue)?.isVisible = curQueue.name != "Default"
+ toolbar.menu?.findItem(R.id.add_queue)?.isVisible = queueNames.size < 9
}
}
@@ -584,7 +584,7 @@ class QueuesFragment : Fragment(), Toolbar.OnMenuItemClickListener {
upsertBlk(newQueue) {}
queues = realm.query(PlayQueue::class).find()
queueNames = queues.map { it.name }.toTypedArray()
- curIndex = queues.indexOf(curQueue)
+ curIndex = queues.indexOfFirst { it.id == curQueue.id }
spinnerTexts.clear()
spinnerTexts.addAll(queues.map { "${it.name} : ${it.episodeIds.size}" })
onDismiss()
@@ -625,7 +625,7 @@ class QueuesFragment : Fragment(), Toolbar.OnMenuItemClickListener {
appPrefs.edit().putBoolean(UserPreferences.Prefs.prefQueueLocked.name, locked).apply()
dragDropEnabled = !(isQueueKeepSorted || isQueueLocked)
refreshMenuItems()
- if (queueItems.size == 0) {
+ if (queueItems.isEmpty()) {
if (locked) (activity as MainActivity).showSnackbarAbovePlayer(R.string.queue_locked, Snackbar.LENGTH_SHORT)
else (activity as MainActivity).showSnackbarAbovePlayer(R.string.queue_unlocked, Snackbar.LENGTH_SHORT)
}
@@ -673,7 +673,7 @@ class QueuesFragment : Fragment(), Toolbar.OnMenuItemClickListener {
for (e in queueItems) vms.add(EpisodeVM(e))
Logd(TAG, "loadCurQueue() curQueue.episodes: ${curQueue.episodes.size}")
queues = realm.query(PlayQueue::class).find()
- curIndex = queues.indexOf(curQueue)
+ curIndex = queues.indexOfFirst { it.id == curQueue.id }
spinnerTexts.clear()
spinnerTexts.addAll(queues.map { "${it.name} : ${it.size()}" })
refreshInfoBar()
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 6a6f2132..38b21be9 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
@@ -207,7 +207,7 @@ class QuickDiscoveryFragment : Fragment(), AdapterView.OnItemClickListener {
val podcast: PodcastSearchResult? = adapter.getItem(position)
if (podcast?.feedUrl.isNullOrEmpty()) return
- val fragment: Fragment = OnlineFeedFragment.newInstance(podcast!!.feedUrl!!)
+ val fragment: Fragment = OnlineFeedFragment.newInstance(podcast.feedUrl)
(activity as MainActivity).loadChildFragment(fragment)
}
@@ -368,7 +368,7 @@ class QuickDiscoveryFragment : Fragment(), AdapterView.OnItemClickListener {
if (result.isNotEmpty()) {
searchResults.addAll(result)
noResultText = ""
- } else noResultText = getString(R.string.no_results_for_query, "")
+ } else noResultText = getString(R.string.no_results_for_query)
showProgress = false
}
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 3ca2586d..6e837f1d 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
@@ -62,9 +62,6 @@ import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import java.text.NumberFormat
-/**
- * Performs a search operation on all feeds or one specific feed and displays the search result.
- */
class SearchFragment : Fragment() {
private var _binding: SearchFragmentBinding? = null
private val binding get() = _binding!!
@@ -76,7 +73,7 @@ class SearchFragment : Fragment() {
private val resultFeeds = mutableStateListOf()
private val results = mutableListOf()
private val vms = mutableStateListOf()
- protected var infoBarText = mutableStateOf("")
+ private var infoBarText = mutableStateOf("")
private var leftActionState = mutableStateOf(NoActionSwipeAction())
private var rightActionState = mutableStateOf(NoActionSwipeAction())
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 e19fa572..23e5ece4 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
@@ -119,7 +119,7 @@ class SearchResultsFragment : Fragment() {
}
if (searchResults.isEmpty()) Text(noResultText, color = textColor, modifier = Modifier.constrainAs(empty) { centerTo(parent) })
if (errorText.isNotEmpty()) Text(errorText, color = textColor, modifier = Modifier.constrainAs(txtvError) { centerTo(parent) })
- if (retryQerry.isNotEmpty()) Button(modifier = Modifier.padding(16.dp).constrainAs(butRetry) { top.linkTo(txtvError.bottom)}, onClick = { search(retryQerry) }, ) {
+ if (retryQerry.isNotEmpty()) Button(modifier = Modifier.padding(16.dp).constrainAs(butRetry) { top.linkTo(txtvError.bottom) }, onClick = { search(retryQerry) }) {
Text(stringResource(id = R.string.retry_label))
}
Text( getString(R.string.search_powered_by, searchProvider!!.name), color = Color.Black, style = MaterialTheme.typography.labelSmall, modifier = Modifier.background(Color.LightGray)
diff --git a/app/src/main/kotlin/ac/mdiq/podcini/ui/fragment/StatisticsFragment.kt b/app/src/main/kotlin/ac/mdiq/podcini/ui/fragment/StatisticsFragment.kt
index 265d490b..0044aa8d 100644
--- a/app/src/main/kotlin/ac/mdiq/podcini/ui/fragment/StatisticsFragment.kt
+++ b/app/src/main/kotlin/ac/mdiq/podcini/ui/fragment/StatisticsFragment.kt
@@ -367,16 +367,16 @@ class StatisticsFragment : Fragment(), Toolbar.OnMenuItemClickListener {
private fun refreshToolbarState() {
when (selectedTabIndex.value) {
0 -> {
- toolbar.menu?.findItem(R.id.statistics_reset)?.setVisible(true)
- toolbar.menu?.findItem(R.id.statistics_filter)?.setVisible(true)
+ toolbar.menu?.findItem(R.id.statistics_reset)?.isVisible = true
+ toolbar.menu?.findItem(R.id.statistics_filter)?.isVisible = true
}
1 -> {
- toolbar.menu?.findItem(R.id.statistics_reset)?.setVisible(true)
- toolbar.menu?.findItem(R.id.statistics_filter)?.setVisible(false)
+ toolbar.menu?.findItem(R.id.statistics_reset)?.isVisible = true
+ toolbar.menu?.findItem(R.id.statistics_filter)?.isVisible = false
}
else -> {
- toolbar.menu?.findItem(R.id.statistics_reset)?.setVisible(false)
- toolbar.menu?.findItem(R.id.statistics_filter)?.setVisible(false)
+ toolbar.menu?.findItem(R.id.statistics_reset)?.isVisible = false
+ toolbar.menu?.findItem(R.id.statistics_filter)?.isVisible = false
}
}
}
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 5aedcb14..e765b694 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
@@ -32,6 +32,7 @@ import android.content.ActivityNotFoundException
import android.content.Context
import android.content.DialogInterface
import android.content.Intent
+import android.content.SharedPreferences
import android.net.Uri
import android.os.Bundle
import android.util.Log
@@ -89,6 +90,7 @@ import java.text.SimpleDateFormat
import java.util.*
class SubscriptionsFragment : Fragment(), Toolbar.OnMenuItemClickListener {
+ val prefs: SharedPreferences by lazy { requireContext().getSharedPreferences("SubscriptionsFragmentPrefs", Context.MODE_PRIVATE) }
private var _binding: ComposeFragmentBinding? = null
private val binding get() = _binding!!
@@ -97,8 +99,38 @@ class SubscriptionsFragment : Fragment(), Toolbar.OnMenuItemClickListener {
private val tags: MutableList = mutableListOf()
private val queueIds: MutableList = mutableListOf()
- private var tagFilterIndex = 1
- private var queueFilterIndex = 0
+
+ private var _feedsFilter: String? = null
+ private var feedsFilter: String
+ get() {
+ if (_feedsFilter == null) _feedsFilter = prefs.getString("feedsFilter", "") ?: ""
+ return _feedsFilter ?: ""
+ }
+ set(filter) {
+ _feedsFilter = filter
+ prefs.edit().putString("feedsFilter", filter).apply()
+ }
+
+ private var _tagFilterIndex: Int = -1
+ private var tagFilterIndex: Int
+ get() {
+ if (_tagFilterIndex < 0) _tagFilterIndex = prefs.getInt("tagFilterIndex", 0)
+ return _tagFilterIndex
+ }
+ set(index) {
+ _tagFilterIndex = index
+ prefs.edit().putInt("tagFilterIndex", index).apply()
+ }
+ private var _queueFilterIndex: Int = -1
+ private var queueFilterIndex: Int
+ get() {
+ if (_queueFilterIndex < 0) _queueFilterIndex = prefs.getInt("queueFilterIndex", 0)
+ return _queueFilterIndex
+ }
+ set(index) {
+ _queueFilterIndex = index
+ prefs.edit().putInt("queueFilterIndex", index).apply()
+ }
private var infoTextFiltered = ""
private var infoTextUpdate = ""
@@ -176,12 +208,12 @@ class SubscriptionsFragment : Fragment(), Toolbar.OnMenuItemClickListener {
Column {
InforBar()
Row(verticalAlignment = Alignment.CenterVertically, modifier = Modifier.padding(start = 20.dp, end = 20.dp)) {
- Spinner(items = spinnerTexts, selectedItem = spinnerTexts[0]) { index: Int ->
+ Spinner(items = spinnerTexts, selectedIndex = queueFilterIndex) { index: Int ->
queueFilterIndex = index
loadSubscriptions()
}
Spacer(Modifier.weight(1f))
- Spinner(items = tags, selectedItem = tags[0]) { index: Int ->
+ Spinner(items = tags, selectedIndex = tagFilterIndex) { index: Int ->
tagFilterIndex = index
loadSubscriptions()
}
@@ -439,7 +471,7 @@ class SubscriptionsFragment : Fragment(), Toolbar.OnMenuItemClickListener {
}
if (selectedOption == "Custom") {
val queues = realm.query(PlayQueue::class).find()
- Spinner(items = queues.map { it.name }, selectedItem = "Default") { index ->
+ Spinner(items = queues.map { it.name }, selectedIndex = 0) { index ->
Logd(TAG, "Queue selected: ${queues[index]}")
saveFeedPreferences { it: FeedPreferences -> it.queueId = queues[index].id }
onDismissRequest()
@@ -793,18 +825,14 @@ class SubscriptionsFragment : Fragment(), Toolbar.OnMenuItemClickListener {
Icon(imageVector = ImageVector.vectorResource(R.drawable.baseline_arrow_upward_24), tint = Color.Black, contentDescription = null,
modifier = Modifier.width(35.dp).height(35.dp).padding(end = 10.dp).clickable(onClick = {
selected.clear()
- for (i in 0..longPressIndex) {
- selected.add(feedListFiltered[i])
- }
+ for (i in 0..longPressIndex) selected.add(feedListFiltered[i])
selectedSize = selected.size
Logd(TAG, "selectedIds: ${selected.size}")
}))
Icon(imageVector = ImageVector.vectorResource(R.drawable.baseline_arrow_downward_24), tint = Color.Black, contentDescription = null,
modifier = Modifier.width(35.dp).height(35.dp).padding(end = 10.dp).clickable(onClick = {
selected.clear()
- for (i in longPressIndex..) {
- val localItems: MutableList = mutableListOf()
- for (item in items) {
- if (item.feed?.isLocalFeed == true) localItems.add(item)
- else deleteEpisodeMedia(context, item)
- }
-
- if (localItems.isNotEmpty()) {
- MaterialAlertDialogBuilder(context)
- .setTitle(R.string.delete_episode_label)
- .setMessage(R.string.delete_local_feed_warning_body)
- .setPositiveButton(R.string.delete_label) { dialog: DialogInterface?, which: Int ->
- for (item in localItems) {
- deleteEpisodeMedia(context, item)
- }
- }
- .setNegativeButton(R.string.cancel_label, null)
- .show()
- }
- }
-}
diff --git a/app/src/main/kotlin/ac/mdiq/podcini/ui/utils/PictureInPictureUtil.kt b/app/src/main/kotlin/ac/mdiq/podcini/ui/utils/PictureInPictureUtil.kt
deleted file mode 100644
index ceed1e16..00000000
--- a/app/src/main/kotlin/ac/mdiq/podcini/ui/utils/PictureInPictureUtil.kt
+++ /dev/null
@@ -1,19 +0,0 @@
-package ac.mdiq.podcini.ui.utils
-
-import android.app.Activity
-import android.content.pm.PackageManager
-import android.os.Build
-
-object PictureInPictureUtil {
- fun supportsPictureInPicture(activity: Activity): Boolean {
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
- val packageManager = activity.packageManager
- return packageManager.hasSystemFeature(PackageManager.FEATURE_PICTURE_IN_PICTURE)
- } else return false
- }
-
- fun isInPictureInPictureMode(activity: Activity): Boolean {
- return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N && supportsPictureInPicture(activity)) activity.isInPictureInPictureMode
- else false
- }
-}
diff --git a/app/src/main/kotlin/ac/mdiq/podcini/ui/utils/ShownotesCleaner.kt b/app/src/main/kotlin/ac/mdiq/podcini/ui/utils/ShownotesCleaner.kt
index 162693e1..7c4c138f 100644
--- a/app/src/main/kotlin/ac/mdiq/podcini/ui/utils/ShownotesCleaner.kt
+++ b/app/src/main/kotlin/ac/mdiq/podcini/ui/utils/ShownotesCleaner.kt
@@ -79,7 +79,7 @@ class ShownotesCleaner(context: Context) {
private fun addTimecodes(document: Document, playableDuration: Int) {
val elementsWithTimeCodes = document.body().getElementsMatchingOwnText(TIMECODE_REGEX)
Logd(TAG, "Recognized " + elementsWithTimeCodes.size + " timecodes")
- if (elementsWithTimeCodes.size == 0) return // No elements with timecodes
+ if (elementsWithTimeCodes.isEmpty()) return // No elements with timecodes
var useHourFormat = true
if (playableDuration != Int.MAX_VALUE) {
diff --git a/app/src/main/kotlin/ac/mdiq/podcini/ui/view/NoRelayoutTextView.kt b/app/src/main/kotlin/ac/mdiq/podcini/ui/view/NoRelayoutTextView.kt
deleted file mode 100644
index fa5e854d..00000000
--- a/app/src/main/kotlin/ac/mdiq/podcini/ui/view/NoRelayoutTextView.kt
+++ /dev/null
@@ -1,30 +0,0 @@
-package ac.mdiq.podcini.ui.view
-
-import android.content.Context
-import android.util.AttributeSet
-import androidx.appcompat.widget.AppCompatTextView
-
-class NoRelayoutTextView : AppCompatTextView {
- private var requestLayoutEnabled = false
- private var maxTextLength = 0f
-
- constructor(context: Context) : super(context)
-
- constructor(context: Context, attrs: AttributeSet?) : super(context, attrs)
-
- constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr)
-
- override fun requestLayout() {
- if (requestLayoutEnabled) super.requestLayout()
- requestLayoutEnabled = false
- }
-
- override fun setText(text: CharSequence, type: BufferType) {
- val textLength = paint.measureText(text.toString())
- if (textLength > maxTextLength) {
- maxTextLength = textLength
- requestLayoutEnabled = true
- }
- super.setText(text, type)
- }
-}
diff --git a/app/src/main/kotlin/ac/mdiq/podcini/ui/view/PlaybackSpeedSeekBar.kt b/app/src/main/kotlin/ac/mdiq/podcini/ui/view/PlaybackSpeedSeekBar.kt
index f161471b..5bf9f514 100644
--- a/app/src/main/kotlin/ac/mdiq/podcini/ui/view/PlaybackSpeedSeekBar.kt
+++ b/app/src/main/kotlin/ac/mdiq/podcini/ui/view/PlaybackSpeedSeekBar.kt
@@ -8,6 +8,7 @@ import android.widget.FrameLayout
import android.widget.SeekBar
import android.widget.SeekBar.OnSeekBarChangeListener
import androidx.core.util.Consumer
+import kotlin.math.roundToInt
class PlaybackSpeedSeekBar : FrameLayout {
private var _binding: PlaybackSpeedSeekBarBinding? = null
@@ -16,6 +17,9 @@ class PlaybackSpeedSeekBar : FrameLayout {
private lateinit var seekBar: SeekBar
private var progressChangedListener: Consumer? = null
+ val currentSpeed: Float
+ get() = (seekBar.progress + 10) / 20.0f
+
constructor(context: Context) : super(context) {
setup()
}
@@ -39,24 +43,19 @@ class PlaybackSpeedSeekBar : FrameLayout {
val playbackSpeed = (progress + 10) / 20.0f
progressChangedListener?.accept(playbackSpeed)
}
-
override fun onStartTrackingTouch(seekBar: SeekBar) {}
-
override fun onStopTrackingTouch(seekBar: SeekBar) {}
})
}
fun updateSpeed(speedMultiplier: Float) {
- seekBar.progress = Math.round((20 * speedMultiplier) - 10)
+ seekBar.progress = ((20 * speedMultiplier) - 10).roundToInt()
}
fun setProgressChangedListener(progressChangedListener: Consumer?) {
this.progressChangedListener = progressChangedListener
}
- val currentSpeed: Float
- get() = (seekBar.progress + 10) / 20.0f
-
override fun setEnabled(enabled: Boolean) {
super.setEnabled(enabled)
seekBar.isEnabled = enabled
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 c2d57de2..61d87602 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
@@ -3,7 +3,6 @@ package ac.mdiq.podcini.ui.view
import ac.mdiq.podcini.R
import ac.mdiq.podcini.net.utils.NetworkUtils
import ac.mdiq.podcini.storage.utils.DurationConverter
-import ac.mdiq.podcini.ui.actions.MenuItemUtils
import ac.mdiq.podcini.ui.activity.MainActivity
import ac.mdiq.podcini.ui.utils.ShownotesCleaner
import ac.mdiq.podcini.util.*
@@ -142,7 +141,23 @@ class ShownotesWebView : WebView, View.OnLongClickListener {
menu.add(Menu.NONE, R.id.share_url_item, Menu.NONE, R.string.share_url_label)
menu.setHeaderTitle(selectedUrl)
}
- MenuItemUtils.setOnClickListeners(menu) { item: MenuItem -> this.onContextItemSelected(item) }
+ setOnClickListeners(menu) { item: MenuItem -> this.onContextItemSelected(item) }
+ }
+
+ /**
+ * When pressing a context menu item, Android calls onContextItemSelected
+ * for ALL fragments in arbitrary order, not just for the fragment that the
+ * context menu was created from. This assigns the listener to every menu item,
+ * so that the correct fragment is always called first and can consume the click.
+ *
+ * Note that Android still calls the onContextItemSelected methods of all fragments
+ * when the passed listener returns false.
+ */
+ fun setOnClickListeners(menu: Menu?, listener: MenuItem.OnMenuItemClickListener?) {
+ for (i in 0 until menu!!.size()) {
+ if (menu.getItem(i).subMenu != null) setOnClickListeners(menu.getItem(i).subMenu, listener)
+ menu.getItem(i).setOnMenuItemClickListener(listener)
+ }
}
fun setTimecodeSelectedListener(timecodeSelectedListener: Consumer?) {
diff --git a/app/src/main/kotlin/ac/mdiq/podcini/ui/view/TriangleLabelView.kt b/app/src/main/kotlin/ac/mdiq/podcini/ui/view/TriangleLabelView.kt
deleted file mode 100644
index 3992f74b..00000000
--- a/app/src/main/kotlin/ac/mdiq/podcini/ui/view/TriangleLabelView.kt
+++ /dev/null
@@ -1,187 +0,0 @@
-/*
- * Copyright (C) 2016 Shota Saito
- *
- * 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.
- *
- * Source: https://github.com/shts/TriangleLabelView
- * Modified for our need; see Podcini #5925 for context
- */
-package ac.mdiq.podcini.ui.view
-
-
-import ac.mdiq.podcini.R
-import android.annotation.SuppressLint
-import android.content.Context
-import android.graphics.*
-import android.util.AttributeSet
-import android.view.View
-import kotlin.math.sqrt
-
-class TriangleLabelView : View {
- private val primary = PaintHolder()
- private var topPadding = 0f
- private var bottomPadding = 0f
- private var centerPadding = 0f
- private var trianglePaint: Paint? = null
- private var width = 0
- private var height = 0
- private var corner: Corner? = null
-
- @JvmOverloads
- constructor(context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0) : super(context, attrs, defStyleAttr) {
- init(context, attrs)
- }
-
- constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int, defStyleRes: Int) : super(context, attrs, defStyleAttr, defStyleRes) {
- init(context, attrs)
- }
-
- private fun init(context: Context, attrs: AttributeSet?) {
- val ta = context.obtainStyledAttributes(attrs, R.styleable.TriangleLabelView)
-
- this.topPadding = ta.getDimension(R.styleable.TriangleLabelView_labelTopPadding, dp2px(7f).toFloat())
- this.centerPadding = ta.getDimension(R.styleable.TriangleLabelView_labelCenterPadding, dp2px(3f).toFloat())
- this.bottomPadding = ta.getDimension(R.styleable.TriangleLabelView_labelBottomPadding, dp2px(3f).toFloat())
-
- val backgroundColor = ta.getColor(R.styleable.TriangleLabelView_backgroundColor, Color.parseColor("#66000000"))
- primary.color = ta.getColor(R.styleable.TriangleLabelView_primaryTextColor, Color.WHITE)
-
- primary.size = ta.getDimension(R.styleable.TriangleLabelView_primaryTextSize, sp2px(11f))
-
- val primary = ta.getString(R.styleable.TriangleLabelView_primaryText)
- if (primary != null) this.primary.text = primary
-
- this.corner = Corner.from(ta.getInt(R.styleable.TriangleLabelView_corner, 1))
-
- ta.recycle()
-
- this.primary.initPaint()
-
- trianglePaint = Paint(Paint.ANTI_ALIAS_FLAG)
- trianglePaint!!.color = backgroundColor
-
- this.primary.resetStatus()
- }
-
- fun setPrimaryText(text: String) {
- primary.text = text
- primary.resetStatus()
- relayout()
- }
-
- fun getCorner(): Corner? {
- return corner
- }
-
- fun setCorner(corner: Corner?) {
- this.corner = corner
- relayout()
- }
-
- override fun onDraw(canvas: Canvas) {
- super.onDraw(canvas)
- canvas.save()
-
- // translate
- canvas.translate(0f, ((height * sqrt(2.0)) - height).toFloat())
-
- // rotate
- if (corner!!.left()) canvas.rotate(DEGREES_LEFT.toFloat(), 0f, height.toFloat())
- else canvas.rotate(DEGREES_RIGHT.toFloat(), width.toFloat(), height.toFloat())
-
- // draw triangle
- @SuppressLint("DrawAllocation") val path = Path()
- path.moveTo(0f, height.toFloat())
- path.lineTo(width / 2f, 0f)
- path.lineTo(width.toFloat(), height.toFloat())
- path.close()
- canvas.drawPath(path, trianglePaint!!)
-
- // draw primaryText
- canvas.drawText(primary.text, (width) / 2f, (topPadding + centerPadding + primary.height), primary.paint!!)
- canvas.restore()
- }
-
- override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
- super.onMeasure(widthMeasureSpec, heightMeasureSpec)
- height = (topPadding + centerPadding + bottomPadding + primary.height).toInt()
- width = 2 * height
- val realHeight = (height * sqrt(2.0)).toInt()
- setMeasuredDimension(width, realHeight)
- }
-
- fun dp2px(dpValue: Float): Int {
- val scale = context.resources.displayMetrics.density
- return (dpValue * scale + 0.5f).toInt()
- }
-
- fun sp2px(spValue: Float): Float {
- val scale = context.resources.displayMetrics.scaledDensity
- return spValue * scale
- }
-
- /**
- * Should be called whenever what we're displaying could have changed.
- */
- private fun relayout() {
- invalidate()
- requestLayout()
- }
-
- enum class Corner(private val type: Int) {
- TOP_LEFT(1),
- TOP_RIGHT(2);
-
- fun left(): Boolean {
- return this == TOP_LEFT
- }
-
- companion object {
- internal fun from(type: Int): Corner {
- for (c in entries) {
- if (c.type == type) return c
- }
- return TOP_LEFT
- }
- }
- }
-
- private class PaintHolder {
- var text: String = ""
- var paint: Paint? = null
- var color: Int = 0
- var size: Float = 0f
- var height: Float = 0f
- var width: Float = 0f
-
- fun initPaint() {
- paint = Paint(Paint.ANTI_ALIAS_FLAG)
- paint!!.color = color
- paint!!.textAlign = Paint.Align.CENTER
- paint!!.textSize = size
- paint!!.setTypeface(Typeface.DEFAULT_BOLD)
- }
-
- fun resetStatus() {
- val rectText = Rect()
- paint!!.getTextBounds(text, 0, text.length, rectText)
- width = rectText.width().toFloat()
- height = rectText.height().toFloat()
- }
- }
-
- companion object {
- private const val DEGREES_LEFT = -45
- private const val DEGREES_RIGHT = 45
- }
-}
diff --git a/app/src/main/kotlin/ac/mdiq/podcini/ui/widget/WidgetUpdater.kt b/app/src/main/kotlin/ac/mdiq/podcini/ui/widget/WidgetUpdater.kt
index f91c2404..24f20ec6 100644
--- a/app/src/main/kotlin/ac/mdiq/podcini/ui/widget/WidgetUpdater.kt
+++ b/app/src/main/kotlin/ac/mdiq/podcini/ui/widget/WidgetUpdater.kt
@@ -36,9 +36,6 @@ import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import kotlin.math.max
-/**
- * Updates the state of the player widget.
- */
object WidgetUpdater {
private val TAG: String = WidgetUpdater::class.simpleName ?: "Anonymous"
diff --git a/app/src/main/kotlin/ac/mdiq/podcini/ui/widget/WidgetUpdaterWorker.kt b/app/src/main/kotlin/ac/mdiq/podcini/ui/widget/WidgetUpdaterWorker.kt
index 36f96ad3..91c85a74 100644
--- a/app/src/main/kotlin/ac/mdiq/podcini/ui/widget/WidgetUpdaterWorker.kt
+++ b/app/src/main/kotlin/ac/mdiq/podcini/ui/widget/WidgetUpdaterWorker.kt
@@ -11,8 +11,7 @@ import androidx.work.*
class WidgetUpdaterWorker(context: Context, workerParams: WorkerParameters) : Worker(context, workerParams) {
override fun doWork(): Result {
- try {
- updateWidget()
+ try { updateWidget()
} catch (e: Exception) {
Logd(TAG, "Failed to update Podcini widget: $e")
return Result.failure()
diff --git a/changelog.md b/changelog.md
index 90854288..8d6757bc 100644
--- a/changelog.md
+++ b/changelog.md
@@ -1,3 +1,12 @@
+# 6.13.11
+
+* created private shared preferences for Subscriptions view and moved related properties there from the apps prefs
+* persisted settings of tag spinner and queue spinner in Subscriptions view
+* fixed again the incorrect initial text on Spinner in Queues view
+* save played duration and time spent when playback of an episode is completed
+* some code cleaning and restructuring
+* gradle update
+
# 6.13.10
* fixed Spinner in Subscriptions: All tags vs Untagged irregularity
diff --git a/fastlane/metadata/android/en-US/changelogs/3020298.txt b/fastlane/metadata/android/en-US/changelogs/3020298.txt
new file mode 100644
index 00000000..74e77072
--- /dev/null
+++ b/fastlane/metadata/android/en-US/changelogs/3020298.txt
@@ -0,0 +1,8 @@
+ Version 6.13.11
+
+* created private shared preferences for Subscriptions view and moved related properties there from the apps prefs
+* persisted settings of tag spinner and queue spinner in Subscriptions view
+* fixed again the incorrect initial text on Spinner in Queues view
+* save played duration and time spent when playback of an episode is completed
+* some code cleaning and restructuring
+* gradle update
diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml
index 6e486e10..ce303c9d 100644
--- a/gradle/libs.versions.toml
+++ b/gradle/libs.versions.toml
@@ -7,7 +7,7 @@ balloon = "1.6.6"
coil = "2.7.0"
commonsLang3 = "3.15.0"
commonsIo = "2.16.1"
-composeBom = "2024.10.01"
+composeBom = "2024.11.00"
conscryptAndroid = "2.5.2"
constraintlayoutCompose = "1.1.0"
coordinatorlayout = "1.2.0"
@@ -19,7 +19,7 @@ documentfile = "1.0.1"
fyydlin = "v0.5.0"
googleMaterialTypeface = "4.0.0.3-kotlin"
googleMaterialTypefaceOutlined = "4.0.0.2-kotlin"
-gradle = "8.5.2"
+gradle = "8.6.1"
gridlayout = "1.0.0"
groovyXml = "3.0.19"
iconicsCore = "5.5.0-b01"
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
index 09523c0e..bb45641c 100644
--- a/gradle/wrapper/gradle-wrapper.properties
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -1,6 +1,7 @@
+#Fri Nov 15 08:33:12 WEST 2024
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-8.9-bin.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-8.11-bin.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME