From 2d614ad5d1053110d7eb458a5d723ef797b4f9d8 Mon Sep 17 00:00:00 2001
From: Xilin Jia <6257601+XilinJia@users.noreply.github.com>
Date: Mon, 4 Nov 2024 19:19:00 +0100
Subject: [PATCH] 6.13.4 commit
---
Licenses_and_permissions.md | 12 -
app/build.gradle | 40 +-
app/src/main/assets/licenses.xml | 18 -
.../net/download/VistaDownloaderImpl.kt | 13 +-
.../service/DownloadServiceInterfaceImpl.kt | 12 +-
.../podcini/net/feed/FeedUpdateManager.kt | 10 +-
.../podcini/net/sync/LockingAsyncExecutor.kt | 6 +-
.../playback/service/PlaybackService.kt | 4 +-
.../preferences/fragments/AboutFragment.kt | 8 +-
.../fragments/DownloadsPreferencesFragment.kt | 4 +-
.../mdiq/podcini/storage/utils/ImageUtils.kt | 277 ---------
.../podcini/ui/actions/EpisodeActionButton.kt | 1 -
.../mdiq/podcini/ui/actions/SwipeActions.kt | 12 +-
.../mdiq/podcini/ui/activity/MainActivity.kt | 10 +-
.../ac/mdiq/podcini/ui/compose/Composables.kt | 60 +-
.../ui/fragment/BaseEpisodesFragment.kt | 2 -
.../ui/fragment/FeedSettingsFragment.kt | 8 +-
.../podcini/ui/fragment/QueuesFragment.kt | 284 ++++-----
.../podcini/ui/fragment/SearchFragment.kt | 52 +-
.../ui/fragment/SubscriptionsFragment.kt | 570 +++++++-----------
.../res/drawable/baseline_dynamic_feed_24.xml | 9 +
.../res/layout/fragment_subscriptions.xml | 74 ---
.../main/res/layout/queue_title_spinner.xml | 17 -
app/src/main/res/menu/queue.xml | 25 +-
app/src/main/res/values/strings.xml | 1 +
changelog.md | 8 +
.../android/en-US/changelogs/3020291.txt | 7 +
gradle/libs.versions.toml | 42 +-
28 files changed, 518 insertions(+), 1068 deletions(-)
delete mode 100644 app/src/main/kotlin/ac/mdiq/podcini/storage/utils/ImageUtils.kt
create mode 100644 app/src/main/res/drawable/baseline_dynamic_feed_24.xml
delete mode 100644 app/src/main/res/layout/fragment_subscriptions.xml
delete mode 100644 app/src/main/res/layout/queue_title_spinner.xml
create mode 100644 fastlane/metadata/android/en-US/changelogs/3020291.txt
diff --git a/Licenses_and_permissions.md b/Licenses_and_permissions.md
index 2f833a1c..cc883a75 100644
--- a/Licenses_and_permissions.md
+++ b/Licenses_and_permissions.md
@@ -26,8 +26,6 @@ Apache License 2.0
[com.mikepenz:iconics-core](https://github.com/mikepenz/Android-Iconics/blob/develop/LICENSE) Apache License 2.0
-[//]: # ([com.leinardi.android](https://github.com/leinardi/FloatingActionButtonSpeedDial/blob/release/LICENSE) Apache License 2.0)
-
[com.github.ByteHamster](https://github.com/ByteHamster/SearchPreference/blob/master/LICENSE) MIT License
[com.github.skydoves](https://github.com/skydoves/Only/blob/master/LICENSE) Apache License 2.0
@@ -36,18 +34,8 @@ Apache License 2.0
[com.annimon](https://github.com/aNNiMON/Lightweight-Stream-API/blob/master/LICENSE) Apache License 2.0
-[com.nanohttpd](https://github.com/NanoHttpd/nanohttpd/blob/master/LICENSE.md) BSD 3-Clause "New" or "Revised" License
-
-[org.awaitility](https://github.com/awaitility/awaitility/blob/master/LICENSE) Apache License 2.0
-
[com.github.mfietz](https://github.com/mfietz/fyydlin/blob/master/LICENSE) Apache License 2.0
-[junit](https://junit.org/junit4/license.html) Eclipse Public License 1.0
-
-[org.mockito](https://github.com/mockito/mockito/blob/main/LICENSE) The MIT License
-
-[org.robolectric](https://github.com/robolectric/robolectric/blob/master/LICENSE) The MIT License
-
[javax.inject](https://github.com/javax-inject/javax-inject) Apache License 2.0
[org.conscrypt](https://github.com/google/conscrypt/blob/master/LICENSE) Apache License 2.0
diff --git a/app/build.gradle b/app/build.gradle
index 1fee2d96..9e050aba 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -7,8 +7,6 @@ plugins {
id('com.github.triplet.play') version '3.9.0' apply false
}
-//apply plugin: 'org.jetbrains.compose'
-
composeCompiler {
enableStrongSkippingMode = true
reportsDestination = layout.buildDirectory.dir("compose_compiler")
@@ -28,11 +26,8 @@ android {
vectorDrawables.useSupportLibrary false
vectorDrawables.generatedDensities = []
-// testApplicationId "ac.mdiq.podcini.tests"
-// testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
-
- versionCode 3020290
- versionName "6.13.3"
+ versionCode 3020291
+ versionName "6.13.4"
applicationId "ac.mdiq.podcini.R"
def commit = ""
@@ -178,8 +173,6 @@ android {
dependencies {
implementation libs.androidx.material3.android
-// implementation libs.androidx.material3
-// implementation libs.androidx.ui.viewbinding
implementation libs.androidx.fragment.compose
// implementation libs.androidx.material.icons.extended
@@ -189,8 +182,7 @@ dependencies {
def composeBom = libs.androidx.compose.bom
implementation composeBom
-// androidTestImplementation composeBom
-// implementation libs.androidx.material
+
implementation libs.androidx.ui.tooling.preview
debugImplementation libs.androidx.ui.tooling
implementation libs.androidx.constraintlayout.compose
@@ -240,18 +232,14 @@ dependencies {
implementation libs.rxjava3.rxjava
implementation libs.rxandroid
-// 5.5.0-b01 is newer than 5.5.0-compose01
implementation libs.iconics.core
implementation libs.iconics.views
implementation libs.google.material.typeface
implementation libs.google.material.typeface.outlined
- implementation libs.fontawesome.typeface
-// implementation libs.speed.dial
implementation libs.searchpreference
implementation libs.balloon
- implementation libs.recyclerviewswipedecorator
- implementation libs.stream
+// implementation libs.stream
implementation libs.fyydlin
@@ -263,26 +251,6 @@ dependencies {
playImplementation libs.core.ktx
compileOnly libs.wearable
-// this one can not be updated? TODO: need to get an alternative
-// androidTestImplementation libs.nanohttpd
-//
-// androidTestImplementation libs.androidx.espresso.core
-// androidTestImplementation libs.androidx.espresso.contrib
-// androidTestImplementation libs.androidx.espresso.intents
-// androidTestImplementation libs.androidx.runner
-// androidTestImplementation libs.androidx.rules
-// androidTestImplementation libs.androidx.junit
-// androidTestImplementation libs.awaitility
-
- // Non-free dependencies:
-
-// testImplementation libs.androidx.core
-// testImplementation libs.awaitility
-// testImplementation libs.junit
-// testImplementation libs.mockito.inline
-// testImplementation libs.robolectric
-// testImplementation libs.javax.inject
-
playImplementation libs.play.services.base
freeImplementation libs.conscrypt.android
diff --git a/app/src/main/assets/licenses.xml b/app/src/main/assets/licenses.xml
index 92f44463..d933ed38 100644
--- a/app/src/main/assets/licenses.xml
+++ b/app/src/main/assets/licenses.xml
@@ -42,12 +42,6 @@
website="https://github.com/google/conscrypt"
license="Apache 2.0"
licenseText="LICENSE_APACHE-2.0.txt" />
-
-
-
Objects.nonNull(obj) }
- .flatMap { cookies: String? -> Arrays.stream(cookies!!.split("; *".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()) }
+ return listOf(youtubeCookie, getCookie("recaptcha_cookies"))
+ .filterNotNull()
+ .flatMap { cookies: String? -> cookies!!.split("; *".toRegex()).dropLastWhile { it.isEmpty() } }
.distinct()
- .collect(Collectors.joining("; "))
+ .joinToString("; ")
+// return Stream.of(youtubeCookie, getCookie("recaptcha_cookies"))
+// .filter { obj: String? -> Objects.nonNull(obj) }
+// .flatMap { cookies: String? -> Arrays.stream(cookies!!.split("; *".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()) }
+// .distinct()
+// .collect(Collectors.joining("; "))
}
private fun getCookie(key: String): String? {
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 c8fb10fd..67bd4c2c 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
@@ -53,21 +53,21 @@ import java.util.concurrent.TimeUnit
class DownloadServiceInterfaceImpl : DownloadServiceInterface() {
override fun downloadNow(context: Context, item: Episode, ignoreConstraints: Boolean) {
+ if (item.media?.downloadUrl.isNullOrEmpty()) return
Logd(TAG, "starting downloadNow")
val workRequest: OneTimeWorkRequest.Builder = getRequest(item)
workRequest.setExpedited(OutOfQuotaPolicy.RUN_AS_NON_EXPEDITED_WORK_REQUEST)
if (ignoreConstraints) workRequest.setConstraints(Builder().setRequiredNetworkType(NetworkType.CONNECTED).build())
else workRequest.setConstraints(constraints)
- if (item.media?.downloadUrl != null)
- WorkManager.getInstance(context).enqueueUniqueWork(item.media!!.downloadUrl!!, ExistingWorkPolicy.KEEP, workRequest.build())
+ WorkManager.getInstance(context).enqueueUniqueWork(item.media!!.downloadUrl!!, ExistingWorkPolicy.KEEP, workRequest.build())
}
override fun download(context: Context, item: Episode) {
+ if (item.media?.downloadUrl.isNullOrEmpty()) return
Logd(TAG, "starting download")
val workRequest: OneTimeWorkRequest.Builder = getRequest(item)
workRequest.setConstraints(constraints)
- if (item.media?.downloadUrl != null)
- WorkManager.getInstance(context).enqueueUniqueWork(item.media!!.downloadUrl!!, ExistingWorkPolicy.KEEP, workRequest.build())
+ WorkManager.getInstance(context).enqueueUniqueWork(item.media!!.downloadUrl!!, ExistingWorkPolicy.KEEP, workRequest.build())
}
override fun cancel(context: Context, media: EpisodeMedia) {
@@ -84,7 +84,7 @@ class DownloadServiceInterfaceImpl : DownloadServiceInterface() {
workInfoList.forEach { workInfo ->
if (workInfo.tags.contains(WORK_DATA_WAS_QUEUED)) {
val item_ = media.episodeOrFetch()
- if (item_ != null) runOnIOScope { removeFromQueueSync(curQueue, item_) }
+ if (item_ != null) removeFromQueueSync(curQueue, item_)
}
}
WorkManager.getInstance(context).cancelAllWorkByTag(tag)
@@ -104,7 +104,6 @@ class DownloadServiceInterfaceImpl : DownloadServiceInterface() {
private val isLastRunAttempt: Boolean
get() = runAttemptCount >= 2
-
override fun doWork(): Result {
Logd(TAG, "starting doWork")
ClientConfigurator.initialize(applicationContext)
@@ -160,7 +159,6 @@ class DownloadServiceInterfaceImpl : DownloadServiceInterface() {
override fun getForegroundInfoAsync(): ListenableFuture {
return Futures.immediateFuture(ForegroundInfo(R.id.notification_downloading, generateProgressNotification()))
}
-
private fun performDownload(media: EpisodeMedia, request: DownloadRequest): Result {
Logd(TAG, "starting performDownload")
if (request.destination == null) {
diff --git a/app/src/main/kotlin/ac/mdiq/podcini/net/feed/FeedUpdateManager.kt b/app/src/main/kotlin/ac/mdiq/podcini/net/feed/FeedUpdateManager.kt
index 58001203..9ff2c81e 100644
--- a/app/src/main/kotlin/ac/mdiq/podcini/net/feed/FeedUpdateManager.kt
+++ b/app/src/main/kotlin/ac/mdiq/podcini/net/feed/FeedUpdateManager.kt
@@ -82,12 +82,10 @@ object FeedUpdateManager {
if (isAutoUpdateDisabled) WorkManager.getInstance(context).cancelUniqueWork(WORK_ID_FEED_UPDATE)
else {
val workRequest: PeriodicWorkRequest = PeriodicWorkRequest.Builder(FeedUpdateWorker::class.java, updateInterval, TimeUnit.HOURS)
- .setConstraints(Builder()
- .setRequiredNetworkType(if (isAllowMobileFeedRefresh) NetworkType.CONNECTED else NetworkType.UNMETERED)
- .build())
+ .setConstraints(Builder().setRequiredNetworkType(if (isAllowMobileFeedRefresh) NetworkType.CONNECTED else NetworkType.UNMETERED).build())
.build()
WorkManager.getInstance(context).enqueueUniquePeriodicWork(WORK_ID_FEED_UPDATE,
- if (replace) ExistingPeriodicWorkPolicy.REPLACE else ExistingPeriodicWorkPolicy.KEEP, workRequest)
+ if (replace) ExistingPeriodicWorkPolicy.UPDATE else ExistingPeriodicWorkPolicy.KEEP, workRequest)
}
}
@@ -137,11 +135,9 @@ object FeedUpdateManager {
builder.show()
}
-
class FeedUpdateWorker(context: Context, params: WorkerParameters) : Worker(context, params) {
private val notificationManager = NotificationManagerCompat.from(context)
-
override fun doWork(): Result {
ClientConfigurator.initialize(applicationContext)
val feedsToUpdate: MutableList
@@ -183,7 +179,7 @@ object FeedUpdateManager {
var bigText: String? = ""
if (titles != null) {
contentText = context.resources.getQuantityString(R.plurals.downloads_left, titles.size, titles.size)
- bigText = titles.map { "• $it" }.joinToString("\n")
+ bigText = titles.joinToString("\n") { "• $it" }
}
return NotificationCompat.Builder(context, NotificationUtils.CHANNEL_ID.downloading.name)
.setContentTitle(context.getString(R.string.download_notification_title_feeds))
diff --git a/app/src/main/kotlin/ac/mdiq/podcini/net/sync/LockingAsyncExecutor.kt b/app/src/main/kotlin/ac/mdiq/podcini/net/sync/LockingAsyncExecutor.kt
index cd0c37b3..1521c19b 100644
--- a/app/src/main/kotlin/ac/mdiq/podcini/net/sync/LockingAsyncExecutor.kt
+++ b/app/src/main/kotlin/ac/mdiq/podcini/net/sync/LockingAsyncExecutor.kt
@@ -36,11 +36,7 @@ object LockingAsyncExecutor {
coroutineScope.launch {
withContext(Dispatchers.IO) {
lock.lock()
- try {
- runnable.run()
- } finally {
- lock.unlock()
- }
+ try { runnable.run() } finally { lock.unlock() }
}
}
}
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 69add402..9af399a9 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
@@ -1415,9 +1415,7 @@ class PlaybackService : MediaLibraryService() {
try {
ChapterUtils.loadChapters(media, context, false)
withContext(Dispatchers.Main) { callback.onChapterLoaded(media) }
- } catch (e: Throwable) {
- Logd(TAG, "Error loading chapters: ${Log.getStackTraceString(e)}")
- }
+ } catch (e: Throwable) { Logd(TAG, "Error loading chapters: ${Log.getStackTraceString(e)}") }
}
}
}
diff --git a/app/src/main/kotlin/ac/mdiq/podcini/preferences/fragments/AboutFragment.kt b/app/src/main/kotlin/ac/mdiq/podcini/preferences/fragments/AboutFragment.kt
index 303c2411..e762153f 100644
--- a/app/src/main/kotlin/ac/mdiq/podcini/preferences/fragments/AboutFragment.kt
+++ b/app/src/main/kotlin/ac/mdiq/podcini/preferences/fragments/AboutFragment.kt
@@ -103,9 +103,7 @@ class AboutFragment : PreferenceFragmentCompat() {
listAdapter = ContributorsPagerFragment.SimpleIconListAdapter(requireContext(), licenses)
}
}.invokeOnCompletion { throwable ->
- if (throwable!= null) {
- Toast.makeText(context, throwable.message, Toast.LENGTH_LONG).show()
- }
+ if (throwable!= null) Toast.makeText(context, throwable.message, Toast.LENGTH_LONG).show()
}
}
@@ -236,9 +234,7 @@ class AboutFragment : PreferenceFragmentCompat() {
listAdapter = SimpleIconListAdapter(requireContext(), translators)
}
}.invokeOnCompletion { throwable ->
- if (throwable!= null) {
- Toast.makeText(context, throwable.message, Toast.LENGTH_LONG).show()
- }
+ if (throwable!= null) Toast.makeText(context, throwable.message, Toast.LENGTH_LONG).show()
}
}
}
diff --git a/app/src/main/kotlin/ac/mdiq/podcini/preferences/fragments/DownloadsPreferencesFragment.kt b/app/src/main/kotlin/ac/mdiq/podcini/preferences/fragments/DownloadsPreferencesFragment.kt
index 4c94e90c..a0257c3d 100644
--- a/app/src/main/kotlin/ac/mdiq/podcini/preferences/fragments/DownloadsPreferencesFragment.kt
+++ b/app/src/main/kotlin/ac/mdiq/podcini/preferences/fragments/DownloadsPreferencesFragment.kt
@@ -344,9 +344,7 @@ class DownloadsPreferencesFragment : PreferenceFragmentCompat(), OnSharedPrefere
client.newCall(request).execute().use { response ->
if (!response.isSuccessful) throw IOException(response.message)
}
- } catch (e: IOException) {
- throw e
- }
+ } catch (e: IOException) { throw e }
withContext(Dispatchers.Main) {
txtvMessage.setTextColor(getColorFromAttr(context, R.attr.icon_green))
val message = String.format("%s %s", "{faw_check}", context.getString(R.string.proxy_test_successful))
diff --git a/app/src/main/kotlin/ac/mdiq/podcini/storage/utils/ImageUtils.kt b/app/src/main/kotlin/ac/mdiq/podcini/storage/utils/ImageUtils.kt
deleted file mode 100644
index 46e5f06c..00000000
--- a/app/src/main/kotlin/ac/mdiq/podcini/storage/utils/ImageUtils.kt
+++ /dev/null
@@ -1,277 +0,0 @@
-package ac.mdiq.podcini.storage.utils
-
-import android.graphics.Bitmap
-import kotlin.math.abs
-import kotlin.math.max
-import kotlin.math.min
-
-class ImageUtils {
-
- companion object {
- private val TAG: String = ImageUtils::class.simpleName ?: "Anonymous"
-
- private const val STACK_BLUR_RADIUS = 10
-
- private fun fastBlur(bitmap: Bitmap, radius: Int): Bitmap? {
- // Stack Blur v1.0 from
- // http://www.quasimondo.com/StackBlurForCanvas/StackBlurDemo.html
- //
- // Java Author: Mario Klingemann
- // http://incubator.quasimondo.com
- // created Feburary 29, 2004
- // Android port : Yahel Bouaziz
- // http://www.kayenko.com
- // ported april 5th, 2012
-
- // This is a compromise between Gaussian Blur and Box blur
- // It creates much better looking blurs than Box Blur, but is
- // 7x faster than my Gaussian Blur implementation.
- //
- // I called it Stack Blur because this describes best how this
- // filter works internally: it creates a kind of moving stack
- // of colors whilst scanning through the image. Thereby it
- // just has to add one new block of color to the right side
- // of the stack and remove the leftmost color. The remaining
- // colors on the topmost layer of the stack are either added on
- // or reduced by one, depending on if they are on the right or
- // on the left side of the stack.
- //
- // If you are using this algorithm in your code please add
- // the following line:
- //
- // Stack Blur Algorithm by Mario Klingemann
-
- if (radius < 1) return null
-
- val w = bitmap.width
- val h = bitmap.height
-
- val pix = IntArray(w * h)
- bitmap.getPixels(pix, 0, w, 0, 0, w, h)
-
- val wm = w - 1
- val hm = h - 1
- val wh = w * h
- val div = radius + radius + 1
-
- val r = IntArray(wh)
- val g = IntArray(wh)
- val b = IntArray(wh)
- var rsum: Int
- var gsum: Int
- var bsum: Int
- var x: Int
- var y: Int
- var i: Int
- var p: Int
- var yp: Int
- var yi: Int
- val vmin = IntArray(max(w.toDouble(), h.toDouble()).toInt())
-
- var divsum = (div + 1) shr 1
- divsum *= divsum
- val dv = IntArray(256 * divsum)
- i = 0
- while (i < 256 * divsum) {
- dv[i] = (i / divsum)
- i++
- }
-
- yi = 0
- var yw = yi
-
- val stack = Array(div) { IntArray(3) }
- var stackpointer: Int
- var stackstart: Int
- var sir: IntArray
- var rbs: Int
- val r1 = radius + 1
- var routsum: Int
- var goutsum: Int
- var boutsum: Int
- var rinsum: Int
- var ginsum: Int
- var binsum: Int
-
- y = 0
- while (y < h) {
- bsum = 0
- gsum = bsum
- rsum = gsum
- boutsum = rsum
- goutsum = boutsum
- routsum = goutsum
- binsum = routsum
- ginsum = binsum
- rinsum = ginsum
- i = -radius
- while (i <= radius) {
- p = pix[(yi + min(wm.toDouble(), max(i.toDouble(), 0.0))).toInt()]
- sir = stack[i + radius]
- sir[0] = (p and 0xff0000) shr 16
- sir[1] = (p and 0x00ff00) shr 8
- sir[2] = (p and 0x0000ff)
- rbs = (r1 - abs(i.toDouble())).toInt()
- rsum += sir[0] * rbs
- gsum += sir[1] * rbs
- bsum += sir[2] * rbs
- if (i > 0) {
- rinsum += sir[0]
- ginsum += sir[1]
- binsum += sir[2]
- } else {
- routsum += sir[0]
- goutsum += sir[1]
- boutsum += sir[2]
- }
- i++
- }
- stackpointer = radius
-
- x = 0
- while (x < w) {
- r[yi] = dv[rsum]
- g[yi] = dv[gsum]
- b[yi] = dv[bsum]
-
- rsum -= routsum
- gsum -= goutsum
- bsum -= boutsum
-
- stackstart = stackpointer - radius + div
- sir = stack[stackstart % div]
-
- routsum -= sir[0]
- goutsum -= sir[1]
- boutsum -= sir[2]
-
- if (y == 0) vmin[x] = min((x + radius + 1).toDouble(), wm.toDouble()).toInt()
-
- p = pix[yw + vmin[x]]
-
- sir[0] = (p and 0xff0000) shr 16
- sir[1] = (p and 0x00ff00) shr 8
- sir[2] = (p and 0x0000ff)
-
- rinsum += sir[0]
- ginsum += sir[1]
- binsum += sir[2]
-
- rsum += rinsum
- gsum += ginsum
- bsum += binsum
-
- stackpointer = (stackpointer + 1) % div
- sir = stack[stackpointer % div]
-
- routsum += sir[0]
- goutsum += sir[1]
- boutsum += sir[2]
-
- rinsum -= sir[0]
- ginsum -= sir[1]
- binsum -= sir[2]
-
- yi++
- x++
- }
- yw += w
- y++
- }
- x = 0
- while (x < w) {
- bsum = 0
- gsum = bsum
- rsum = gsum
- boutsum = rsum
- goutsum = boutsum
- routsum = goutsum
- binsum = routsum
- ginsum = binsum
- rinsum = ginsum
- yp = -radius * w
- i = -radius
- while (i <= radius) {
- yi = (max(0.0, yp.toDouble()) + x).toInt()
-
- sir = stack[i + radius]
-
- sir[0] = r[yi]
- sir[1] = g[yi]
- sir[2] = b[yi]
-
- rbs = (r1 - abs(i.toDouble())).toInt()
-
- rsum += r[yi] * rbs
- gsum += g[yi] * rbs
- bsum += b[yi] * rbs
-
- if (i > 0) {
- rinsum += sir[0]
- ginsum += sir[1]
- binsum += sir[2]
- } else {
- routsum += sir[0]
- goutsum += sir[1]
- boutsum += sir[2]
- }
-
- if (i < hm) yp += w
-
- i++
- }
- yi = x
- stackpointer = radius
- y = 0
- while (y < h) {
- // Set alpha to 1
- pix[yi] = -0x1000000 or (dv[rsum] shl 16) or (dv[gsum] shl 8) or dv[bsum]
-
- rsum -= routsum
- gsum -= goutsum
- bsum -= boutsum
-
- stackstart = stackpointer - radius + div
- sir = stack[stackstart % div]
-
- routsum -= sir[0]
- goutsum -= sir[1]
- boutsum -= sir[2]
-
- if (x == 0) vmin[y] = (min((y + r1).toDouble(), hm.toDouble()) * w).toInt()
-
- p = x + vmin[y]
-
- sir[0] = r[p]
- sir[1] = g[p]
- sir[2] = b[p]
-
- rinsum += sir[0]
- ginsum += sir[1]
- binsum += sir[2]
-
- rsum += rinsum
- gsum += ginsum
- bsum += binsum
-
- stackpointer = (stackpointer + 1) % div
- sir = stack[stackpointer]
-
- routsum += sir[0]
- goutsum += sir[1]
- boutsum += sir[2]
-
- rinsum -= sir[0]
- ginsum -= sir[1]
- binsum -= sir[2]
-
- yi += w
- y++
- }
- x++
- }
- bitmap.setPixels(pix, 0, w, 0, 0, w, h)
- return bitmap
- }
- }
-}
\ No newline at end of file
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 3401d535..7985ab87 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
@@ -351,7 +351,6 @@ class DownloadActionButton(item: Episode) : EpisodeActionButton(item) {
.setNegativeButton(R.string.cancel_label, null)
if (NetworkUtils.isNetworkRestricted && NetworkUtils.isVpnOverWifi) builder.setMessage(R.string.confirm_mobile_download_dialog_message_vpn)
else builder.setMessage(R.string.confirm_mobile_download_dialog_message)
-
builder.show()
}
actionState.value = getLabel()
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 79dfd01c..ff71bd25 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
@@ -198,7 +198,7 @@ open class SwipeActions(private val fragment: Fragment, private val tag: String)
val media = item.media
if (media != null) {
val almostEnded = hasAlmostEnded(media)
- if (almostEnded && item.playState < PlayState.PLAYED.code) item = runBlocking { setPlayStateSync(PlayState.PLAYED.code, item, almostEnded, false) }
+ if (almostEnded && item.playState < PlayState.PLAYED.code) item = runBlocking { setPlayStateSync(PlayState.PLAYED.code, item, true, false) }
if (almostEnded) item = upsertBlk(item) { it.media?.playbackCompletionDate = Date() }
}
deleteEpisodesWarnLocal(fragment.requireContext(), listOf(item))
@@ -354,7 +354,7 @@ open class SwipeActions(private val fragment: Fragment, private val tag: String)
val media = item.media
if (media != null) {
val almostEnded = hasAlmostEnded(media)
- if (almostEnded && item.playState < PlayState.PLAYED.code) item = runBlocking { setPlayStateSync(PlayState.PLAYED.code, item, almostEnded, false) }
+ if (almostEnded && item.playState < PlayState.PLAYED.code) item = runBlocking { setPlayStateSync(PlayState.PLAYED.code, item, true, false) }
if (almostEnded) item = upsertBlk(item) { it.media?.playbackCompletionDate = Date() }
}
if (item.playState < PlayState.SKIPPED.code) item = runBlocking { setPlayStateSync(PlayState.SKIPPED.code, item, false, false) }
@@ -568,13 +568,7 @@ open class SwipeActions(private val fragment: Fragment, private val tag: String)
@JvmStatic
fun getPrefsWithDefaults(tag: String): Actions {
- val defaultActions = when (tag) {
- QueuesFragment.TAG -> "${NO_ACTION.name},${NO_ACTION.name}"
- DownloadsFragment.TAG -> "${NO_ACTION.name},${NO_ACTION.name}"
- HistoryFragment.TAG -> "${NO_ACTION.name},${NO_ACTION.name}"
- AllEpisodesFragment.TAG -> "${NO_ACTION.name},${NO_ACTION.name}"
- else -> "${NO_ACTION.name},${NO_ACTION.name}"
- }
+ val defaultActions = "${NO_ACTION.name},${NO_ACTION.name}"
return getPrefs(tag, defaultActions)
}
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 e6b674e2..d6559dd6 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
@@ -160,19 +160,19 @@ class MainActivity : CastEnabledActivity() {
// Toast.makeText(context, "Please allow unrestricted background activity for this app", Toast.LENGTH_LONG).show()
MaterialAlertDialogBuilder(this)
.setMessage(R.string.unrestricted_background_permission_text)
- .setPositiveButton(android.R.string.ok) { _: DialogInterface?, _: Int ->
- var intent = Intent()
+ .setPositiveButton(android.R.string.ok) { dialog: DialogInterface?, _: Int ->
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
+ val intent = Intent()
intent.action = Settings.ACTION_IGNORE_BATTERY_OPTIMIZATION_SETTINGS
// intent = Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS).also {
// val uri = Uri.parse("package:$packageName")
// it.flags = Intent.FLAG_ACTIVITY_NEW_TASK
// it.data = uri
// }
- }
- context.startActivity(intent)
+ context.startActivity(intent)
+ } else dialog?.dismiss()
}
- .setNegativeButton(R.string.cancel_label) { _: DialogInterface?, _: Int -> }
+ .setNegativeButton(R.string.cancel_label) { dialog, which -> dialog.dismiss() }
.show()
}
}
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 212b8207..df33068b 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
@@ -9,31 +9,75 @@ 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
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.graphics.SolidColor
import androidx.compose.ui.text.TextStyle
+import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.input.TextFieldValue
+import androidx.compose.ui.unit.TextUnit
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.compose.ui.window.Dialog
import androidx.compose.ui.window.DialogProperties
import kotlinx.coroutines.delay
+@Composable
+private fun CustomTextField(
+ modifier: Modifier = Modifier,
+ leadingIcon: (@Composable () -> Unit)? = null,
+ trailingIcon: (@Composable () -> Unit)? = null,
+ placeholderText: String = "Placeholder",
+ fontSize: TextUnit = MaterialTheme.typography.bodyMedium.fontSize
+) {
+ var text by rememberSaveable { mutableStateOf("") }
+ BasicTextField(
+ modifier = modifier.background(MaterialTheme.colorScheme.surface, MaterialTheme.shapes.small).fillMaxWidth(),
+ value = text,
+ onValueChange = { text = it },
+ singleLine = true,
+ cursorBrush = SolidColor(MaterialTheme.colorScheme.primary),
+ textStyle = LocalTextStyle.current.copy(
+ color = MaterialTheme.colorScheme.onSurface,
+ fontSize = fontSize
+ ),
+ decorationBox = { innerTextField ->
+ Row(modifier, verticalAlignment = Alignment.CenterVertically) {
+ if (leadingIcon != null) leadingIcon()
+ Box(Modifier.weight(1f)) {
+ if (text.isEmpty())
+ Text(text = placeholderText, style = LocalTextStyle.current.copy(color = MaterialTheme.colorScheme.onSurface.copy(alpha = 0.3f), fontSize = fontSize))
+ innerTextField()
+ }
+ if (trailingIcon != null) trailingIcon()
+ }
+ }
+ )
+}
+
@OptIn(ExperimentalMaterial3Api::class)
@Composable
-fun Spinner(items: List, selectedItem: String, onItemSelected: (String) -> Unit) {
+fun Spinner(items: List, selectedItem: String, modifier: Modifier = Modifier, onItemSelected: (Int) -> Unit) {
var expanded by remember { mutableStateOf(false) }
-
+ var currentSelectedItem by remember { mutableStateOf(selectedItem) }
ExposedDropdownMenuBox(expanded = expanded, onExpandedChange = { expanded = it }) {
- TextField(readOnly = true, value = selectedItem, onValueChange = {}, label = { Text("Select an item") },
- modifier = Modifier.menuAnchor(MenuAnchorType.PrimaryNotEditable, true), // Material3 requirement
- trailingIcon = { ExposedDropdownMenuDefaults.TrailingIcon(expanded = expanded) }, colors = ExposedDropdownMenuDefaults.textFieldColors())
+ BasicTextField(readOnly = true, value = currentSelectedItem, 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 ->
+ Row(modifier, verticalAlignment = Alignment.CenterVertically) {
+ innerTextField()
+ ExposedDropdownMenuDefaults.TrailingIcon(expanded = expanded)
+ }
+ })
ExposedDropdownMenu(expanded = expanded, onDismissRequest = { expanded = false }) {
- items.forEach { item ->
- DropdownMenuItem(text = { Text(item) },
+ for (i in items.indices) {
+ DropdownMenuItem(text = { Text(items[i]) },
onClick = {
- onItemSelected(item)
+ currentSelectedItem = items[i]
+ onItemSelected(i)
expanded = false
}
)
diff --git a/app/src/main/kotlin/ac/mdiq/podcini/ui/fragment/BaseEpisodesFragment.kt b/app/src/main/kotlin/ac/mdiq/podcini/ui/fragment/BaseEpisodesFragment.kt
index 70dd41d5..b028f812 100644
--- a/app/src/main/kotlin/ac/mdiq/podcini/ui/fragment/BaseEpisodesFragment.kt
+++ b/app/src/main/kotlin/ac/mdiq/podcini/ui/fragment/BaseEpisodesFragment.kt
@@ -34,8 +34,6 @@ import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
-
-
abstract class BaseEpisodesFragment : Fragment(), Toolbar.OnMenuItemClickListener {
val TAG = this::class.simpleName ?: "Anonymous"
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 a5ff3374..440a19d0 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
@@ -634,11 +634,11 @@ class FeedSettingsFragment : Fragment() {
if (selected == "Custom") {
if (queues == null) queues = realm.query(PlayQueue::class).find()
Logd(TAG, "queues: ${queues?.size}")
- Spinner(items = queues!!.map { it.name }, selectedItem = feed?.preferences?.queue?.name ?: "Default") { name ->
- Logd(TAG, "Queue selected: $name")
- val q = queues?.firstOrNull { it.name == name }
+ Spinner(items = queues!!.map { it.name }, selectedItem = feed?.preferences?.queue?.name ?: "Default") { index ->
+ Logd(TAG, "Queue selected: $queues[index].name")
+ val q = queues!![index]
feed = upsertBlk(feed!!) { it.preferences?.queue = q }
- if (q != null) curPrefQueue = q.name
+ curPrefQueue = q.name
onDismissRequest()
}
}
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 4e1e57d4..84720929 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
@@ -20,10 +20,7 @@ import ac.mdiq.podcini.storage.database.RealmDB.realm
import ac.mdiq.podcini.storage.database.RealmDB.runOnIOScope
import ac.mdiq.podcini.storage.database.RealmDB.upsert
import ac.mdiq.podcini.storage.database.RealmDB.upsertBlk
-import ac.mdiq.podcini.storage.model.Episode
-import ac.mdiq.podcini.storage.model.EpisodeFilter
-import ac.mdiq.podcini.storage.model.EpisodeSortOrder
-import ac.mdiq.podcini.storage.model.PlayQueue
+import ac.mdiq.podcini.storage.model.*
import ac.mdiq.podcini.storage.utils.DurationConverter
import ac.mdiq.podcini.storage.utils.EpisodeUtil
import ac.mdiq.podcini.storage.utils.EpisodesPermutors.getPermutor
@@ -45,31 +42,35 @@ import android.content.SharedPreferences
import android.os.Bundle
import android.util.Log
import android.view.*
-import android.widget.AdapterView
-import android.widget.ArrayAdapter
import android.widget.CheckBox
-import android.widget.Spinner
import androidx.appcompat.widget.Toolbar
-import androidx.compose.foundation.layout.Arrangement
-import androidx.compose.foundation.layout.Column
-import androidx.compose.foundation.layout.padding
-import androidx.compose.foundation.layout.wrapContentSize
+import androidx.compose.foundation.ExperimentalFoundationApi
+import androidx.compose.foundation.background
+import androidx.compose.foundation.combinedClickable
+import androidx.compose.foundation.layout.*
+import androidx.compose.foundation.lazy.grid.GridCells
+import androidx.compose.foundation.lazy.grid.LazyVerticalGrid
+import androidx.compose.foundation.lazy.grid.rememberLazyGridState
import androidx.compose.foundation.shape.RoundedCornerShape
-import androidx.compose.material3.Button
-import androidx.compose.material3.Card
-import androidx.compose.material3.Text
-import androidx.compose.material3.TextField
+import androidx.compose.material3.*
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.platform.ComposeView
+import androidx.compose.ui.platform.LocalContext
+import androidx.compose.ui.res.vectorResource
import androidx.compose.ui.unit.dp
import androidx.compose.ui.window.Dialog
+import androidx.constraintlayout.compose.ConstraintLayout
import androidx.fragment.app.Fragment
import androidx.lifecycle.lifecycleScope
-
import androidx.media3.session.MediaBrowser
import androidx.media3.session.SessionToken
+import coil.compose.AsyncImage
+import coil.request.CachePolicy
+import coil.request.ImageRequest
import com.google.android.material.appbar.MaterialToolbar
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.google.android.material.snackbar.Snackbar
@@ -80,10 +81,11 @@ import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
+import java.text.NumberFormat
import java.util.*
import kotlin.math.max
- class QueuesFragment : Fragment(), Toolbar.OnMenuItemClickListener {
+class QueuesFragment : Fragment(), Toolbar.OnMenuItemClickListener {
private var _binding: ComposeFragmentBinding? = null
private val binding get() = _binding!!
@@ -101,21 +103,20 @@ import kotlin.math.max
private var leftActionStateBin = mutableStateOf(NoActionSwipeAction())
private var rightActionStateBin = mutableStateOf(NoActionSwipeAction())
- var isQueueLocked = appPrefs.getBoolean(UserPreferences.Prefs.prefQueueLocked.name, true)
+ private var isQueueLocked = appPrefs.getBoolean(UserPreferences.Prefs.prefQueueLocked.name, true)
- private lateinit var spinnerLayout: View
private lateinit var queueNames: Array
- private lateinit var spinnerTexts: MutableList
- private lateinit var queueSpinner: Spinner
- private lateinit var spinnerAdaptor: ArrayAdapter
+ private val spinnerTexts = mutableStateListOf()
private lateinit var queues: List
+ private lateinit var spinnerView: ComposeView
private var displayUpArrow = false
private val queueItems = mutableListOf()
private val vms = mutableStateListOf()
+ private var feedsAssociated = listOf()
private var showBin by mutableStateOf(false)
-
+ private var showFeeds by mutableStateOf(false)
private var dragDropEnabled by mutableStateOf(!(isQueueKeepSorted || isQueueLocked))
private lateinit var browserFuture: ListenableFuture
@@ -125,60 +126,39 @@ import kotlin.math.max
retainInstance = true
}
- override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
+ override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
super.onCreateView(inflater, container, savedInstanceState)
_binding = ComposeFragmentBinding.inflate(inflater)
Logd(TAG, "fragment onCreateView")
toolbar = binding.toolbar
toolbar.setOnMenuItemClickListener(this)
-// toolbar.setOnLongClickListener {
-// recyclerView.scrollToPosition(5)
-// recyclerView.post { recyclerView.smoothScrollToPosition(0) }
-// false
-// }
displayUpArrow = parentFragmentManager.backStackEntryCount != 0
if (savedInstanceState != null) displayUpArrow = savedInstanceState.getBoolean(KEY_UP_ARROW)
+// toolbar.title = "Queues"
queues = realm.query(PlayQueue::class).find()
queueNames = queues.map { it.name }.toTypedArray()
- spinnerTexts = queues.map { "${it.name} : ${it.episodeIds.size}" }.toMutableList()
- spinnerLayout = inflater.inflate(R.layout.queue_title_spinner, toolbar, false)
- queueSpinner = spinnerLayout.findViewById(R.id.queue_spinner)
- val params = Toolbar.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT)
- params.gravity = Gravity.CENTER_VERTICAL
- toolbar.addView(spinnerLayout)
- spinnerAdaptor = ArrayAdapter(requireContext(), android.R.layout.simple_spinner_item, spinnerTexts)
- spinnerAdaptor.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item)
- queueSpinner.adapter = spinnerAdaptor
- queueSpinner.setSelection(queueNames.indexOf(curQueue.name))
- queueSpinner.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
- override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) {
- val prevQueueSize = curQueue.size()
- curQueue = upsertBlk(queues[position]) { it.update() }
- toolbar.menu?.findItem(R.id.rename_queue)?.setVisible(curQueue.name != "Default")
- loadCurQueue(true)
- playbackService?.notifyCurQueueItemsChanged(max(prevQueueSize, curQueue.size()))
- }
- override fun onNothingSelected(parent: AdapterView<*>?) {}
- }
- queueSpinner.setOnTouchListener { _, event ->
- if (event.action == MotionEvent.ACTION_DOWN) {
- Logd(TAG, "Spinner is opening")
- val queues = realm.query(PlayQueue::class).find()
- spinnerTexts.clear()
- spinnerTexts.addAll(queues.map { "${it.name} : ${it.episodeIds.size}" })
- spinnerAdaptor.notifyDataSetChanged()
- }
- false
- }
+ spinnerTexts.clear()
+ spinnerTexts.addAll(queues.map { "${it.name} : ${it.episodeIds.size}" })
+
(activity as MainActivity).setupToolbarToggle(toolbar, displayUpArrow)
toolbar.inflateMenu(R.menu.queue)
refreshMenuItems()
-// recyclerView.setRecycledViewPool((activity as MainActivity).recycledViewPool)
-// registerForContextMenu(recyclerView)
-// recyclerView.addOnScrollListener(LiftOnScrollListener(binding.appbar))
+ spinnerView = ComposeView(requireContext()).apply {
+ setContent {
+ Spinner(items = spinnerTexts, selectedItem = spinnerTexts[0]) { index:Int ->
+ 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")
+ loadCurQueue(true)
+ playbackService?.notifyCurQueueItemsChanged(max(prevQueueSize, curQueue.size()))
+ }
+ }
+ }
+ toolbar.addView(spinnerView)
swipeActions = SwipeActions(this, TAG)
swipeActions.setFilter(EpisodeFilter(EpisodeFilter.States.queued.name))
@@ -201,22 +181,22 @@ import kotlin.math.max
EpisodeLazyColumn(activity as MainActivity, vms = vms, leftSwipeCB = { leftCB(it) }, rightSwipeCB = { rightCB(it) })
}
} else {
- Column {
- InforBar(infoBarText, leftAction = leftActionState, rightAction = rightActionState, actionConfig = { swipeActions.showDialog() })
- val leftCB = { episode: Episode ->
- if (leftActionState.value is NoActionSwipeAction) swipeActions.showDialog()
- else leftActionState.value.performAction(episode, this@QueuesFragment, swipeActions.filter ?: EpisodeFilter())
- }
- val rightCB = { episode: Episode ->
- if (rightActionState.value is NoActionSwipeAction) swipeActions.showDialog()
- else rightActionState.value.performAction(episode, this@QueuesFragment, swipeActions.filter ?: EpisodeFilter())
+ if (showFeeds) FeedsGrid()
+ else {
+ Column {
+ InforBar(infoBarText, leftAction = leftActionState, rightAction = rightActionState, actionConfig = { swipeActions.showDialog() })
+ val leftCB = { episode: Episode ->
+ if (leftActionState.value is NoActionSwipeAction) swipeActions.showDialog()
+ else leftActionState.value.performAction(episode, this@QueuesFragment, swipeActions.filter ?: EpisodeFilter())
+ }
+ val rightCB = { episode: Episode ->
+ if (rightActionState.value is NoActionSwipeAction) swipeActions.showDialog()
+ else rightActionState.value.performAction(episode, this@QueuesFragment, swipeActions.filter ?: EpisodeFilter())
+ }
+ EpisodeLazyColumn(activity as MainActivity, vms = vms,
+ isDraggable = dragDropEnabled, dragCB = { iFrom, iTo -> runOnIOScope { moveInQueueSync(iFrom, iTo, true) } },
+ leftSwipeCB = { leftCB(it) }, rightSwipeCB = { rightCB(it) })
}
- EpisodeLazyColumn(activity as MainActivity, vms = vms,
- isDraggable = dragDropEnabled, dragCB = { iFrom, iTo ->
-// moveInQueue(iFrom, iTo, true)
- runOnIOScope { moveInQueueSync(iFrom, iTo, true) }
- },
- leftSwipeCB = { leftCB(it) }, rightSwipeCB = { rightCB(it) })
}
}
}
@@ -224,7 +204,6 @@ import kotlin.math.max
lifecycle.addObserver(swipeActions)
refreshSwipeTelltale()
-
return binding.root
}
@@ -251,13 +230,54 @@ import kotlin.math.max
MediaBrowser.releaseFuture(browserFuture)
}
- override fun onPause() {
- Logd(TAG, "onPause() called")
- super.onPause()
-// recyclerView.saveScrollPosition(TAG)
- }
-
- private var eventSink: Job? = null
+ @OptIn(ExperimentalFoundationApi::class)
+ @Composable
+ fun FeedsGrid() {
+ val context = LocalContext.current
+ val lazyGridState = rememberLazyGridState()
+ LazyVerticalGrid(state = lazyGridState, columns = GridCells.Adaptive(80.dp),
+ verticalArrangement = Arrangement.spacedBy(16.dp), horizontalArrangement = Arrangement.spacedBy(16.dp),
+ contentPadding = PaddingValues(start = 12.dp, top = 16.dp, end = 12.dp, bottom = 16.dp)) {
+ items(feedsAssociated.size, key = {index -> feedsAssociated[index].id}) { index ->
+ val feed by remember { mutableStateOf(feedsAssociated[index]) }
+ ConstraintLayout {
+ val (coverImage, episodeCount, rating, error) = 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(),
+ contentDescription = "coverImage",
+ modifier = Modifier.height(100.dp).aspectRatio(1f)
+ .constrainAs(coverImage) {
+ top.linkTo(parent.top)
+ bottom.linkTo(parent.bottom)
+ start.linkTo(parent.start)
+ }.combinedClickable(onClick = {
+ Logd(SubscriptionsFragment.TAG, "clicked: ${feed.title}")
+ (activity as MainActivity).loadChildFragment(FeedEpisodesFragment.newInstance(feed.id))
+ }, onLongClick = {
+ Logd(SubscriptionsFragment.TAG, "long clicked: ${feed.title}")
+// val inflater: MenuInflater = (activity as MainActivity).menuInflater
+// inflater.inflate(R.menu.feed_context, contextMenu)
+// contextMenu.setHeaderTitle(feed.title)
+ })
+ )
+ Text(NumberFormat.getInstance().format(feed.episodes.size.toLong()), color = Color.Green,
+ modifier = Modifier.background(Color.Gray).constrainAs(episodeCount) {
+ end.linkTo(parent.end)
+ top.linkTo(coverImage.top)
+ })
+ if (feed.rating != Rating.UNRATED.code)
+ Icon(imageVector = ImageVector.vectorResource(Rating.fromCode(feed.rating).res), tint = MaterialTheme.colorScheme.tertiary, contentDescription = "rating",
+ modifier = Modifier.background(MaterialTheme.colorScheme.tertiaryContainer).constrainAs(rating) {
+ start.linkTo(parent.start)
+ centerVerticallyTo(coverImage)
+ })
+ }
+ }
+ }
+ }
+
+ private var eventSink: Job? = null
private var eventStickySink: Job? = null
private var eventKeySink: Job? = null
private fun cancelFlowEvents() {
@@ -304,11 +324,6 @@ import kotlin.math.max
}
}
-// private fun refreshPosCallback(pos: Int, episode: Episode) {
-//// Logd(TAG, "Queue refreshPosCallback: $pos ${episode.title}")
-// if (isAdded && activity != null) refreshInfoBar()
-// }
-
private fun onQueueEvent(event: FlowEvent.QueueEvent) {
Logd(TAG, "onQueueEvent() called with ${event.action.name}")
if (showBin) return
@@ -353,9 +368,7 @@ import kotlin.math.max
FlowEvent.QueueEvent.Action.MOVED, FlowEvent.QueueEvent.Action.DELETED_MEDIA -> return
}
queueChanged++
-// adapter?.updateDragDropEnabled()
refreshMenuItems()
-// recyclerView.saveScrollPosition(TAG)
refreshInfoBar()
}
@@ -371,10 +384,8 @@ import kotlin.math.max
for (url in event.urls) {
// if (!event.isCompleted(url)) continue
val pos: Int = EpisodeUtil.indexOfItemWithDownloadUrl(queueItems.toList(), url)
- if (pos >= 0) {
-// queueItems[pos].downloadState.value = event.map[url]?.state ?: DownloadStatus.State.UNKNOWN.ordinal
- vms[pos].downloadState = event.map[url]?.state ?: DownloadStatus.State.UNKNOWN.ordinal
- }
+ if (pos >= 0) vms[pos].downloadState = event.map[url]?.state ?: DownloadStatus.State.UNKNOWN.ordinal
+
}
}
@@ -388,9 +399,7 @@ import kotlin.math.max
private fun onEpisodePlayedEvent(event: FlowEvent.EpisodePlayedEvent) {
// Sent when playback position is reset
Logd(TAG, "onUnreadItemsChanged() called with event = [$event]")
- if (event.episode == null) {
- if (!showBin) loadCurQueue(false)
- }
+ if (event.episode == null && !showBin) loadCurQueue(false)
refreshMenuItems()
}
@@ -435,12 +444,14 @@ import kotlin.math.max
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)
} 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")
@@ -454,23 +465,18 @@ import kotlin.math.max
R.id.show_bin -> {
showBin = !showBin
if (showBin) {
- toolbar.removeView(spinnerLayout)
+ toolbar.removeView(spinnerView)
toolbar.title = curQueue.name + " Bin"
} else {
toolbar.title = ""
- toolbar.addView(spinnerLayout)
+ toolbar.addView(spinnerView)
}
refreshMenuItems()
refreshSwipeTelltale()
- if (showBin) {
- item.setIcon(R.drawable.playlist_play)
-// speedDialView.addActionItem(addToQueueActionItem)
- } else {
- item.setIcon(R.drawable.ic_history)
-// speedDialView.removeActionItem(addToQueueActionItem)
- }
+ item.setIcon(if (showBin) R.drawable.playlist_play else R.drawable.ic_history)
loadCurQueue(false)
}
+ R.id.associated_feed -> showFeeds = !showFeeds
R.id.queue_lock -> toggleQueueLock()
R.id.queue_sort -> QueueSortDialog().show(childFragmentManager.beginTransaction(), "SortDialog")
R.id.rename_queue -> renameQueue()
@@ -533,36 +539,20 @@ import kotlin.math.max
fun RenameQueueDialog(showDialog: Boolean, onDismiss: () -> Unit) {
if (showDialog) {
Dialog(onDismissRequest = onDismiss) {
- Card(modifier = Modifier
- .wrapContentSize(align = Alignment.Center)
- .padding(16.dp),
- shape = RoundedCornerShape(16.dp),
- ) {
- Column(modifier = Modifier.padding(16.dp),
- verticalArrangement = Arrangement.spacedBy(8.dp)
- ) {
+ Card(modifier = Modifier.wrapContentSize(align = Alignment.Center).padding(16.dp), shape = RoundedCornerShape(16.dp)) {
+ Column(modifier = Modifier.padding(16.dp), verticalArrangement = Arrangement.spacedBy(8.dp)) {
var newName by remember { mutableStateOf(curQueue.name) }
- TextField(value = newName,
- onValueChange = { newName = it },
- label = { Text("Rename (Unique name only)") }
- )
+ TextField(value = newName, onValueChange = { newName = it }, label = { Text("Rename (Unique name only)") })
Button(onClick = {
if (newName.isNotEmpty() && curQueue.name != newName && queueNames.indexOf(newName) < 0) {
val oldName = curQueue.name
- runOnIOScope {
- curQueue = upsertBlk(curQueue) {
- it.name = newName
- }
- }
+ curQueue = upsertBlk(curQueue) { it.name = newName }
val index_ = queueNames.indexOf(oldName)
queueNames[index_] = newName
spinnerTexts[index_] = newName + " : " + curQueue.episodeIds.size
- spinnerAdaptor.notifyDataSetChanged()
onDismiss()
}
- }) {
- Text("Confirm")
- }
+ }) { Text("Confirm") }
}
}
}
@@ -573,19 +563,10 @@ import kotlin.math.max
fun AddQueueDialog(showDialog: Boolean, onDismiss: () -> Unit) {
if (showDialog) {
Dialog(onDismissRequest = onDismiss) {
- Card(modifier = Modifier
- .wrapContentSize(align = Alignment.Center)
- .padding(16.dp),
- shape = RoundedCornerShape(16.dp),
- ) {
- Column(modifier = Modifier.padding(16.dp),
- verticalArrangement = Arrangement.spacedBy(8.dp)
- ) {
+ Card(modifier = Modifier.wrapContentSize(align = Alignment.Center).padding(16.dp), shape = RoundedCornerShape(16.dp)) {
+ Column(modifier = Modifier.padding(16.dp), verticalArrangement = Arrangement.spacedBy(8.dp)) {
var newName by remember { mutableStateOf("") }
- TextField(value = newName,
- onValueChange = { newName = it },
- label = { Text("Add queue (Unique name only)") }
- )
+ TextField(value = newName, onValueChange = { newName = it }, label = { Text("Add queue (Unique name only)") })
Button(onClick = {
if (newName.isNotEmpty() && queueNames.indexOf(newName) < 0) {
val newQueue = PlayQueue()
@@ -594,15 +575,11 @@ import kotlin.math.max
upsertBlk(newQueue) {}
queues = realm.query(PlayQueue::class).find()
queueNames = queues.map { it.name }.toTypedArray()
+ spinnerTexts.clear()
spinnerTexts.addAll(queues.map { "${it.name} : ${it.episodeIds.size}" })
- spinnerAdaptor.notifyDataSetChanged()
- queueSpinner.adapter = spinnerAdaptor
- queueSpinner.setSelection(spinnerAdaptor.getPosition(curQueue.name))
onDismiss()
}
- }) {
- Text("Confirm")
- }
+ }) { Text("Confirm") }
}
}
}
@@ -610,8 +587,8 @@ import kotlin.math.max
}
private fun toggleQueueLock() {
- val isLocked: Boolean = isQueueLocked
- if (isLocked) setQueueLock(false)
+// val isLocked: Boolean = isQueueLocked
+ if (isQueueLocked) setQueueLock(false)
else {
val shouldShowLockWarning: Boolean = prefs!!.getBoolean(PREF_SHOW_LOCK_WARNING, true)
if (!shouldShowLockWarning) setQueueLock(true)
@@ -619,12 +596,10 @@ import kotlin.math.max
val builder = MaterialAlertDialogBuilder(requireContext())
builder.setTitle(R.string.lock_queue)
builder.setMessage(R.string.queue_lock_warning)
-
val view = View.inflate(context, R.layout.checkbox_do_not_show_again, null)
val binding_ = CheckboxDoNotShowAgainBinding.bind(view)
val checkDoNotShowAgain: CheckBox = binding_.checkboxDoNotShowAgain
builder.setView(view)
-
builder.setPositiveButton(R.string.lock_queue) { _: DialogInterface?, _: Int ->
prefs!!.edit().putBoolean(PREF_SHOW_LOCK_WARNING, !checkDoNotShowAgain.isChecked).apply()
setQueueLock(true)
@@ -640,8 +615,6 @@ import kotlin.math.max
appPrefs.edit().putBoolean(UserPreferences.Prefs.prefQueueLocked.name, locked).apply()
dragDropEnabled = !(isQueueKeepSorted || isQueueLocked)
refreshMenuItems()
-// adapter?.updateDragDropEnabled()
-
if (queueItems.size == 0) {
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)
@@ -660,7 +633,6 @@ import kotlin.math.max
for (item in queueItems) {
var playbackSpeed = 1f
if (UserPreferences.timeRespectsSpeed()) playbackSpeed = getCurrentPlaybackSpeed(item.media)
-
if (item.media != null) {
val itemTimeLeft: Long = (item.media!!.getDuration() - item.media!!.getPosition()).toLong()
timeLeft = (timeLeft + itemTimeLeft / playbackSpeed).toLong()
@@ -679,6 +651,7 @@ import kotlin.math.max
Logd(TAG, "loadCurQueue() called ${curQueue.name}")
while (curQueue.name.isEmpty()) runBlocking { delay(100) }
// if (queueItems.isNotEmpty()) emptyViewHandler.hide()
+ feedsAssociated = realm.query(Feed::class).query("preferences.queueId == ${curQueue.id}").find()
queueItems.clear()
vms.clear()
if (showBin) queueItems.addAll(realm.query(Episode::class, "id IN $0", curQueue.idsBinList)
@@ -689,10 +662,7 @@ import kotlin.math.max
}
for (e in queueItems) vms.add(EpisodeVM(e))
Logd(TAG, "loadCurQueue() curQueue.episodes: ${curQueue.episodes.size}")
-
-// if (restoreScrollPosition) recyclerView.restoreScrollPosition(TAG)
refreshInfoBar()
-// playbackService?.notifyCurQueueItemsChanged()
loadItemsRunning = 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 8ae38f81..b55f2ffe 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
@@ -6,14 +6,15 @@ import ac.mdiq.podcini.net.download.DownloadStatus
import ac.mdiq.podcini.net.feed.discovery.CombinedSearcher
import ac.mdiq.podcini.storage.database.RealmDB.realm
import ac.mdiq.podcini.storage.model.Episode
+import ac.mdiq.podcini.storage.model.EpisodeFilter
import ac.mdiq.podcini.storage.model.Feed
import ac.mdiq.podcini.storage.model.Rating
import ac.mdiq.podcini.storage.utils.EpisodeUtil
+import ac.mdiq.podcini.ui.actions.SwipeAction
+import ac.mdiq.podcini.ui.actions.SwipeActions
+import ac.mdiq.podcini.ui.actions.SwipeActions.NoActionSwipeAction
import ac.mdiq.podcini.ui.activity.MainActivity
-import ac.mdiq.podcini.ui.compose.CustomTheme
-import ac.mdiq.podcini.ui.compose.EpisodeLazyColumn
-import ac.mdiq.podcini.ui.compose.EpisodeVM
-import ac.mdiq.podcini.ui.compose.NonlazyGrid
+import ac.mdiq.podcini.ui.compose.*
import ac.mdiq.podcini.util.EventFlow
import ac.mdiq.podcini.util.FlowEvent
import ac.mdiq.podcini.util.Logd
@@ -49,7 +50,6 @@ import androidx.compose.ui.unit.dp
import androidx.constraintlayout.compose.ConstraintLayout
import androidx.fragment.app.Fragment
import androidx.lifecycle.lifecycleScope
-
import coil.compose.AsyncImage
import coil.request.CachePolicy
import coil.request.ImageRequest
@@ -77,6 +77,11 @@ class SearchFragment : Fragment() {
private val resultFeeds = mutableStateListOf()
private val results = mutableListOf()
private val vms = mutableStateListOf()
+ protected var infoBarText = mutableStateOf("")
+
+ private var leftActionState = mutableStateOf(NoActionSwipeAction())
+ private var rightActionState = mutableStateOf(NoActionSwipeAction())
+ private lateinit var swipeActions: SwipeActions
private var lastQueryChange: Long = 0
private var isOtherViewInFoucus = false
@@ -87,21 +92,35 @@ class SearchFragment : Fragment() {
automaticSearchDebouncer = Handler(Looper.getMainLooper())
}
- override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
+ override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
_binding = SearchFragmentBinding.inflate(inflater)
Logd(TAG, "fragment onCreateView")
setupToolbar(binding.toolbar)
+ swipeActions = SwipeActions(this, TAG)
+ lifecycle.addObserver(swipeActions)
binding.resultsListView.setContent {
CustomTheme(requireContext()) {
Column {
CriteriaList()
FeedsRow()
- EpisodeLazyColumn(activity as MainActivity, vms = vms)
+ InforBar(infoBarText, leftAction = leftActionState, rightAction = rightActionState, actionConfig = {swipeActions.showDialog()})
+ EpisodeLazyColumn(activity as MainActivity, vms = vms,
+ leftSwipeCB = {
+ if (leftActionState.value is NoActionSwipeAction) swipeActions.showDialog()
+ else leftActionState.value.performAction(it, this@SearchFragment, swipeActions.filter ?: EpisodeFilter())
+ },
+ rightSwipeCB = {
+ if (rightActionState.value is NoActionSwipeAction) swipeActions.showDialog()
+ else rightActionState.value.performAction(it, this@SearchFragment, swipeActions.filter ?: EpisodeFilter())
+ },
+ )
}
}
}
+ refreshSwipeTelltale()
+
chip = binding.feedTitleChip
chip.setOnCloseIconClickListener {
requireArguments().putLong(ARG_FEED, 0)
@@ -136,6 +155,11 @@ class SearchFragment : Fragment() {
super.onDestroyView()
}
+ private fun refreshSwipeTelltale() {
+ leftActionState.value = swipeActions.actions.left[0]
+ rightActionState.value = swipeActions.actions.right[0]
+ }
+
private fun setupToolbar(toolbar: MaterialToolbar) {
toolbar.setTitle(R.string.search_label)
toolbar.setNavigationOnClickListener { parentFragmentManager.popBackStack() }
@@ -207,6 +231,7 @@ class SearchFragment : Fragment() {
Logd(TAG, "Received event: ${event.TAG}")
when (event) {
is FlowEvent.FeedListEvent, is FlowEvent.EpisodePlayedEvent, is FlowEvent.PlayerSettingsEvent -> search()
+ is FlowEvent.SwipeActionsChangedEvent -> refreshSwipeTelltale()
else -> {}
}
}
@@ -236,7 +261,6 @@ class SearchFragment : Fragment() {
private fun search() {
// adapterFeeds.setEndButton(R.string.search_online) { this.searchOnline() }
chip.visibility = if ((requireArguments().getLong(ARG_FEED, 0) == 0L)) View.GONE else View.VISIBLE
-
lifecycleScope.launch {
try {
val results_ = withContext(Dispatchers.IO) { performSearch() }
@@ -245,6 +269,7 @@ class SearchFragment : Fragment() {
val first_ = results_.first!!.toMutableList()
results.clear()
results.addAll(first_)
+ infoBarText.value = "${results.size} episodes"
vms.clear()
for (e in first_) { vms.add(EpisodeVM(e)) }
}
@@ -272,12 +297,8 @@ class SearchFragment : Fragment() {
var showGrid by remember { mutableStateOf(false) }
Column {
Row {
- Button(onClick = {showGrid = !showGrid}) {
- Text(stringResource(R.string.show_criteria))
- }
- Button(onClick = { searchOnline() }) {
- Text(stringResource(R.string.search_online))
- }
+ Button(onClick = {showGrid = !showGrid}) { Text(stringResource(R.string.show_criteria)) }
+ Button(onClick = { searchOnline() }) { Text(stringResource(R.string.search_online)) }
}
if (showGrid) NonlazyGrid(columns = 2, itemCount = SearchBy.entries.size) { index ->
val c = SearchBy.entries[index]
@@ -303,8 +324,7 @@ class SearchFragment : Fragment() {
val context = LocalContext.current
val lazyGridState = rememberLazyListState()
LazyRow (state = lazyGridState, horizontalArrangement = Arrangement.spacedBy(16.dp),
- contentPadding = PaddingValues(start = 12.dp, top = 16.dp, end = 12.dp, bottom = 16.dp)
- ) {
+ contentPadding = PaddingValues(start = 12.dp, top = 16.dp, end = 12.dp, bottom = 16.dp)) {
items(resultFeeds.size, key = {index -> resultFeeds[index].id}) { index ->
val feed by remember { mutableStateOf(resultFeeds[index]) }
ConstraintLayout {
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 d0e40cd0..c2107527 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
@@ -1,8 +1,8 @@
package ac.mdiq.podcini.ui.fragment
import ac.mdiq.podcini.R
+import ac.mdiq.podcini.databinding.ComposeFragmentBinding
import ac.mdiq.podcini.databinding.DialogSwitchPreferenceBinding
-import ac.mdiq.podcini.databinding.FragmentSubscriptionsBinding
import ac.mdiq.podcini.databinding.PlaybackSpeedFeedSettingDialogBinding
import ac.mdiq.podcini.net.feed.FeedUpdateManager
import ac.mdiq.podcini.playback.base.VideoMode
@@ -10,7 +10,6 @@ import ac.mdiq.podcini.preferences.OpmlTransporter.OpmlWriter
import ac.mdiq.podcini.preferences.UserPreferences
import ac.mdiq.podcini.preferences.UserPreferences.appPrefs
import ac.mdiq.podcini.preferences.fragments.ImportExportPreferencesFragment.*
-import ac.mdiq.podcini.storage.database.Feeds
import ac.mdiq.podcini.storage.database.Feeds.createSynthetic
import ac.mdiq.podcini.storage.database.Feeds.getFeedList
import ac.mdiq.podcini.storage.database.Feeds.getTags
@@ -45,8 +44,6 @@ import android.net.Uri
import android.os.Bundle
import android.util.Log
import android.view.*
-import android.widget.AdapterView
-import android.widget.ArrayAdapter
import android.widget.CompoundButton
import androidx.activity.result.ActivityResult
import androidx.activity.result.contract.ActivityResultContracts
@@ -60,11 +57,9 @@ import androidx.compose.foundation.lazy.grid.rememberLazyGridState
import androidx.compose.foundation.lazy.itemsIndexed
import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.foundation.selection.selectable
-import androidx.compose.foundation.shape.CircleShape
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Edit
-import androidx.compose.material.icons.outlined.AddCircle
import androidx.compose.material3.*
import androidx.compose.material3.pulltorefresh.PullToRefreshBox
import androidx.compose.runtime.*
@@ -103,15 +98,13 @@ import java.util.*
class SubscriptionsFragment : Fragment(), Toolbar.OnMenuItemClickListener {
- private var _binding: FragmentSubscriptionsBinding? = null
+ private var _binding: ComposeFragmentBinding? = null
private val binding get() = _binding!!
private lateinit var toolbar: MaterialToolbar
private val tags: MutableList = mutableListOf()
private val queueIds: MutableList = mutableListOf()
- private lateinit var queuesAdapter: ArrayAdapter
- private lateinit var tagsAdapter: ArrayAdapter
private var tagFilterIndex = 1
private var queueFilterIndex = 0
@@ -126,10 +119,9 @@ class SubscriptionsFragment : Fragment(), Toolbar.OnMenuItemClickListener {
private var feedCount by mutableStateOf("")
private var feedSorted by mutableIntStateOf(0)
-// private var feedList: MutableList = mutableListOf()
private var feedListFiltered = mutableStateListOf()
- var showFilterDialog by mutableStateOf(false)
- var noSubscription by mutableStateOf(false)
+ private var showFilterDialog by mutableStateOf(false)
+ private var noSubscription by mutableStateOf(false)
private var useGrid by mutableStateOf(null)
private val useGridLayout by mutableStateOf(appPrefs.getBoolean(UserPreferences.Prefs.prefFeedGridLayout.name, false))
@@ -145,7 +137,7 @@ class SubscriptionsFragment : Fragment(), Toolbar.OnMenuItemClickListener {
}
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
- _binding = FragmentSubscriptionsBinding.inflate(inflater)
+ _binding = ComposeFragmentBinding.inflate(inflater)
Logd(TAG, "fragment onCreateView")
toolbar = binding.toolbar
@@ -155,56 +147,40 @@ class SubscriptionsFragment : Fragment(), Toolbar.OnMenuItemClickListener {
(activity as MainActivity).setupToolbarToggle(toolbar, displayUpArrow)
toolbar.inflateMenu(R.menu.subscriptions)
+ toolbar.title = getString(R.string.subscriptions_label)
if (arguments != null) {
displayedFolder = requireArguments().getString(ARGUMENT_FOLDER, null)
toolbar.title = displayedFolder
}
-
- binding.infobar.setContent {
- CustomTheme(requireContext()) {
- InforBar()
- }
- }
- binding.lazyColumn.setContent {
- CustomTheme(requireContext()) {
- if (showFilterDialog) FilterDialog(FeedFilter(feedsFilter)) { showFilterDialog = false }
- if (noSubscription) Text(stringResource(R.string.no_subscriptions_label))
- LazyList()
- }
- }
-// setupEmptyView()
resetTags()
val queues = realm.query(PlayQueue::class).find()
queueIds.addAll(queues.map { it.id })
val spinnerTexts: MutableList = mutableListOf("Any queue", "No queue")
spinnerTexts.addAll(queues.map { it.name })
- queuesAdapter = ArrayAdapter(requireContext(), android.R.layout.simple_spinner_item, spinnerTexts)
- queuesAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item)
- binding.queuesSpinner.setAdapter(queuesAdapter)
- binding.queuesSpinner.setSelection(queuesAdapter.getPosition("Any queue"))
- binding.queuesSpinner.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
- override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) {
- queueFilterIndex = position
- loadSubscriptions()
- }
- override fun onNothingSelected(parent: AdapterView<*>?) {}
- }
- tagsAdapter = ArrayAdapter(requireContext(), android.R.layout.simple_spinner_item, tags)
- tagsAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item)
- binding.categorySpinner.setAdapter(tagsAdapter)
- binding.categorySpinner.setSelection(tagsAdapter.getPosition("All"))
- binding.categorySpinner.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
- override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) {
- tagFilterIndex = position
-// filterOnTag()
- loadSubscriptions()
+ binding.mainView.setContent {
+ CustomTheme(requireContext()) {
+ if (showFilterDialog) FilterDialog(FeedFilter(feedsFilter)) { showFilterDialog = false }
+ Column {
+ InforBar()
+ Row(verticalAlignment = Alignment.CenterVertically, modifier = Modifier.padding(start = 20.dp, end = 20.dp)) {
+ Spinner(items = spinnerTexts, selectedItem = spinnerTexts[0]) { index: Int ->
+ queueFilterIndex = index
+ loadSubscriptions()
+ }
+ Spacer(Modifier.weight(1f))
+ Spinner(items = tags, selectedItem = tags[0]) { index: Int ->
+ tagFilterIndex = index
+ loadSubscriptions()
+ }
+ }
+ if (noSubscription) Text(stringResource(R.string.no_subscriptions_label))
+ else LazyList()
+ }
}
- override fun onNothingSelected(parent: AdapterView<*>?) {}
}
-
feedCount = feedListFiltered.size.toString() + " / " + NavDrawerFragment.feedCount.toString()
loadSubscriptions()
return binding.root
@@ -224,7 +200,6 @@ class SubscriptionsFragment : Fragment(), Toolbar.OnMenuItemClickListener {
override fun onDestroyView() {
Logd(TAG, "onDestroyView")
-// feedList = mutableListOf()
feedListFiltered.clear()
_binding = null
super.onDestroyView()
@@ -260,8 +235,8 @@ class SubscriptionsFragment : Fragment(), Toolbar.OnMenuItemClickListener {
private fun resetTags() {
tags.clear()
+ tags.add("All tags")
tags.add("Untagged")
- tags.add("All")
tags.addAll(getTags())
}
@@ -308,10 +283,7 @@ class SubscriptionsFragment : Fragment(), Toolbar.OnMenuItemClickListener {
override fun onMenuItemClick(item: MenuItem): Boolean {
val itemId = item.itemId
when (itemId) {
- R.id.subscriptions_filter -> {
- showFilterDialog = true
-// FeedFilterDialog.newInstance(FeedFilter(feedsFilter)).show(childFragmentManager, null)
- }
+ R.id.subscriptions_filter -> showFilterDialog = true
R.id.action_search -> (activity as MainActivity).loadChildFragment(SearchFragment.newInstance())
R.id.subscriptions_sort -> FeedSortDialog().show(childFragmentManager, "FeedSortDialog")
R.id.new_synth -> {
@@ -334,7 +306,6 @@ class SubscriptionsFragment : Fragment(), Toolbar.OnMenuItemClickListener {
private var loadItemsRunning = false
private fun loadSubscriptions() {
-// emptyView.hide()
if (!loadItemsRunning) {
loadItemsRunning = true
lifecycleScope.launch {
@@ -345,9 +316,6 @@ class SubscriptionsFragment : Fragment(), Toolbar.OnMenuItemClickListener {
feedList = filterAndSort()
}
withContext(Dispatchers.Main) {
- // We have fewer items. This can result in items being selected that are no longer visible.
-// if (feedListFiltered.size > feedList.size) adapter.endSelectMode()
-// filterOnTag()
noSubscription = feedList.isEmpty()
feedListFiltered.clear()
feedListFiltered.addAll(feedList)
@@ -355,7 +323,6 @@ class SubscriptionsFragment : Fragment(), Toolbar.OnMenuItemClickListener {
infoTextFiltered = " "
if (feedsFilter.isNotEmpty()) infoTextFiltered = getString(R.string.filtered_label)
txtvInformation = (infoTextFiltered + infoTextUpdate)
-// emptyView.updateVisibility()
}
} catch (e: Throwable) { Log.e(TAG, Log.getStackTraceString(e))
} finally { loadItemsRunning = false }
@@ -427,7 +394,6 @@ class SubscriptionsFragment : Fragment(), Toolbar.OnMenuItemClickListener {
for (f in feedList_) {
val d = realm.query(Episode::class).query(queryString, f.id).first().find()?.pubDate ?: 0L
counterMap[f.id] = d
-// val dateFormat = SimpleDateFormat("yy-MM-dd HH:mm", Locale.getDefault())
f.sortInfo = "Updated: " + formatAbbrev(requireContext(), Date(d))
}
comparator(counterMap, dir)
@@ -438,20 +404,16 @@ class SubscriptionsFragment : Fragment(), Toolbar.OnMenuItemClickListener {
for (f in feedList_) {
val d = realm.query(Episode::class).query(queryString, f.id).first().find()?.media?.downloadTime ?: 0L
counterMap[f.id] = d
-// val dateFormat = SimpleDateFormat("yy-MM-dd HH:mm", Locale.getDefault())
f.sortInfo = "Downloaded: " + formatAbbrev(requireContext(), Date(d))
}
comparator(counterMap, dir)
}
FeedSortOrder.LAST_UPDATED_UNPLAYED_NEW_OLD.index -> {
- val queryString =
-// "feedId == $0 AND (playState == ${PlayState.NEW.code} OR playState == ${PlayState.UNPLAYED.code}) SORT(pubDate DESC)"
- "feedId == $0 AND (playState < ${PlayState.SKIPPED.code}) SORT(pubDate DESC)"
+ val queryString = "feedId == $0 AND (playState < ${PlayState.SKIPPED.code}) SORT(pubDate DESC)"
val counterMap: MutableMap = mutableMapOf()
for (f in feedList_) {
val d = realm.query(Episode::class).query(queryString, f.id).first().find()?.pubDate ?: 0L
counterMap[f.id] = d
-// val dateFormat = SimpleDateFormat("yy-MM-dd HH:mm", Locale.getDefault())
f.sortInfo = "Unplayed: " + formatAbbrev(requireContext(), Date(d))
}
comparator(counterMap, dir)
@@ -467,9 +429,7 @@ class SubscriptionsFragment : Fragment(), Toolbar.OnMenuItemClickListener {
comparator(counterMap, dir)
}
FeedSortOrder.MOST_DOWNLOADED_UNPLAYED.index -> {
- val queryString =
-// "feedId == $0 AND (playState == ${PlayState.NEW.code} OR playState == ${PlayState.UNPLAYED.code}) AND media.downloaded == true"
- "feedId == $0 AND (playState < ${PlayState.SKIPPED.code}) AND media.downloaded == true"
+ val queryString = "feedId == $0 AND (playState < ${PlayState.SKIPPED.code}) AND media.downloaded == true"
val counterMap: MutableMap = mutableMapOf()
for (f in feedList_) {
val c = realm.query(Episode::class).query(queryString, f.id).count().find()
@@ -490,7 +450,6 @@ class SubscriptionsFragment : Fragment(), Toolbar.OnMenuItemClickListener {
comparator(counterMap, dir)
}
}
-// synchronized(feedList_) { feedList = feedList_.sortedWith(comparator).toMutableList() }
feedSorted++
return feedList_.sortedWith(comparator)
}
@@ -502,19 +461,14 @@ class SubscriptionsFragment : Fragment(), Toolbar.OnMenuItemClickListener {
Icon(imageVector = ImageVector.vectorResource(R.drawable.ic_info), contentDescription = "info", tint = textColor)
Spacer(Modifier.weight(1f))
Text(txtvInformation, color = MaterialTheme.colorScheme.onSurfaceVariant, modifier = Modifier.clickable {
- if (feedsFilter.isNotEmpty()) {
- showFilterDialog = true
-// val filter = FeedFilter(feedsFilter)
-// val dialog = FeedFilterDialog.newInstance(filter)
-// dialog.show(childFragmentManager, null)
- }
+ if (feedsFilter.isNotEmpty()) showFilterDialog = true
} )
Spacer(Modifier.weight(1f))
Text(feedCount, color = textColor)
}
}
- @kotlin.OptIn(ExperimentalFoundationApi::class, ExperimentalMaterial3Api::class)
+ @OptIn(ExperimentalFoundationApi::class, ExperimentalMaterial3Api::class)
@Composable
fun LazyList() {
var selectedSize by remember { mutableStateOf(0) }
@@ -526,12 +480,10 @@ class SubscriptionsFragment : Fragment(), Toolbar.OnMenuItemClickListener {
if (showRemoveFeedDialog) RemoveFeedDialog(selected, onDismissRequest = {showRemoveFeedDialog = false}, null)
fun saveFeedPreferences(preferencesConsumer: Consumer) {
- for (feed in selected) {
- if (feed.preferences == null) continue
- runOnIOScope {
- upsert(feed) {
- preferencesConsumer.accept(it.preferences!!)
- }
+ runOnIOScope {
+ for (feed in selected) {
+ if (feed.preferences == null) continue
+ upsert(feed) { preferencesConsumer.accept(it.preferences!!) }
}
}
val numItems = selected.size
@@ -546,20 +498,13 @@ class SubscriptionsFragment : Fragment(), Toolbar.OnMenuItemClickListener {
Column(modifier = Modifier.padding(16.dp), verticalArrangement = Arrangement.spacedBy(8.dp)) {
Column {
FeedAutoDeleteOptions.forEach { text ->
- Row(Modifier.fillMaxWidth().padding(horizontal = 16.dp)
- .selectable(selected = (text == selectedOption),
- onClick = {
- if (text != selectedOption) {
- val autoDeleteAction: AutoDeleteAction = AutoDeleteAction.fromTag(text)
- saveFeedPreferences { it: FeedPreferences ->
- it.autoDeleteAction = autoDeleteAction
- }
- onDismissRequest()
- }
- }
- ),
- verticalAlignment = Alignment.CenterVertically
- ) {
+ Row(verticalAlignment = Alignment.CenterVertically, modifier = Modifier.fillMaxWidth().padding(horizontal = 16.dp).selectable(selected = (text == selectedOption), onClick = {
+ if (text != selectedOption) {
+ val autoDeleteAction: AutoDeleteAction = AutoDeleteAction.fromTag(text)
+ saveFeedPreferences { it: FeedPreferences -> it.autoDeleteAction = autoDeleteAction }
+ onDismissRequest()
+ }
+ })) {
RadioButton(selected = (text == selectedOption), onClick = { })
Text(text = text, style = MaterialTheme.typography.bodyLarge.merge(), modifier = Modifier.padding(start = 16.dp))
}
@@ -604,13 +549,10 @@ class SubscriptionsFragment : Fragment(), Toolbar.OnMenuItemClickListener {
}
if (selectedOption == "Custom") {
val queues = realm.query(PlayQueue::class).find()
- Spinner(items = queues.map { it.name }, selectedItem = "Default") { name ->
- Logd(TAG, "Queue selected: $name")
- val q = queues.firstOrNull { it.name == name }
- if (q != null) {
- saveFeedPreferences { it: FeedPreferences -> it.queueId = q.id }
- onDismissRequest()
- }
+ Spinner(items = queues.map { it.name }, selectedItem = "Default") { index ->
+ Logd(TAG, "Queue selected: ${queues[index]}")
+ saveFeedPreferences { it: FeedPreferences -> it.queueId = queues[index].id }
+ onDismissRequest()
}
}
}
@@ -629,14 +571,10 @@ class SubscriptionsFragment : Fragment(), Toolbar.OnMenuItemClickListener {
Text(text = stringResource(R.string.keep_updated), style = MaterialTheme.typography.titleLarge)
Spacer(modifier = Modifier.weight(1f))
var checked by remember { mutableStateOf(false) }
- Switch(checked = checked,
- onCheckedChange = {
- checked = it
- saveFeedPreferences { pref: FeedPreferences ->
- pref.keepUpdated = checked
- }
- }
- )
+ Switch(checked = checked, onCheckedChange = {
+ checked = it
+ saveFeedPreferences { pref: FeedPreferences -> pref.keepUpdated = checked }
+ })
}
Text(text = stringResource(R.string.keep_updated_summary), style = MaterialTheme.typography.bodyMedium)
}
@@ -651,10 +589,7 @@ class SubscriptionsFragment : Fragment(), Toolbar.OnMenuItemClickListener {
Column(modifier = Modifier.padding(16.dp), verticalArrangement = Arrangement.spacedBy(16.dp)) {
for (rating in Rating.entries.reversed()) {
Row(verticalAlignment = Alignment.CenterVertically, modifier = Modifier.padding(4.dp).clickable {
- for (item in selected) {
-// Feeds.setRating(item, rating.code)
- upsertBlk(item) { it.rating = rating.code }
- }
+ for (item in selected) upsertBlk(item) { it.rating = rating.code }
onDismissRequest()
}) {
Icon(imageVector = ImageVector.vectorResource(id = rating.res), "")
@@ -668,13 +603,10 @@ class SubscriptionsFragment : Fragment(), Toolbar.OnMenuItemClickListener {
var showChooseRatingDialog by remember { mutableStateOf(false) }
if (showChooseRatingDialog) ChooseRatingDialog(selected) { showChooseRatingDialog = false }
-
var showAutoDeleteHandlerDialog by remember { mutableStateOf(false) }
if (showAutoDeleteHandlerDialog) AutoDeleteHandlerDialog {showAutoDeleteHandlerDialog = false}
-
var showAssociateDialog by remember { mutableStateOf(false) }
if (showAssociateDialog) SetAssociateQueueDialog {showAssociateDialog = false}
-
var showKeepUpdateDialog by remember { mutableStateOf(false) }
if (showKeepUpdateDialog) SetKeepUpdateDialog {showKeepUpdateDialog = false}
@@ -683,156 +615,128 @@ class SubscriptionsFragment : Fragment(), Toolbar.OnMenuItemClickListener {
val TAG = "EpisodeSpeedDial ${selected.size}"
var isExpanded by remember { mutableStateOf(false) }
val options = listOf<@Composable () -> Unit>(
- { Row(modifier = Modifier.padding(horizontal = 16.dp)
- .clickable {
- showRemoveFeedDialog = true
- isExpanded = false
- selectMode = false
- Logd(TAG, "ic_delete: ${selected.size}")
-// RemoveFeedDialog.show(activity, selected)
- }, verticalAlignment = Alignment.CenterVertically
- ) {
+ { Row(verticalAlignment = Alignment.CenterVertically, modifier = Modifier.padding(horizontal = 16.dp).clickable {
+ showRemoveFeedDialog = true
+ isExpanded = false
+ selectMode = false
+ Logd(TAG, "ic_delete: ${selected.size}")
+ }) {
Icon(imageVector = ImageVector.vectorResource(id = R.drawable.ic_delete), "")
- Text(stringResource(id = R.string.remove_feed_label))
- } },
- { Row(modifier = Modifier.padding(horizontal = 16.dp)
- .clickable {
- showKeepUpdateDialog = true
- isExpanded = false
- selectMode = false
- Logd(TAG, "ic_refresh: ${selected.size}")
- }, verticalAlignment = Alignment.CenterVertically
- ) {
+ Text(stringResource(id = R.string.remove_feed_label)) } },
+ { Row(verticalAlignment = Alignment.CenterVertically, modifier = Modifier.padding(horizontal = 16.dp).clickable {
+ showKeepUpdateDialog = true
+ isExpanded = false
+ selectMode = false
+ Logd(TAG, "ic_refresh: ${selected.size}")
+ }) {
Icon(imageVector = ImageVector.vectorResource(id = R.drawable.ic_refresh), "")
- Text(stringResource(id = R.string.keep_updated))
- } },
- { Row(modifier = Modifier.padding(horizontal = 16.dp)
- .clickable {
- isExpanded = false
- selectMode = false
- Logd(TAG, "ic_download: ${selected.size}")
- val preferenceSwitchDialog = PreferenceSwitchDialog(activity, activity.getString(R.string.auto_download_settings_label), activity.getString(R.string.auto_download_label))
- preferenceSwitchDialog.setOnPreferenceChangedListener( object: PreferenceSwitchDialog.OnPreferenceChangedListener {
- override fun preferenceChanged(enabled: Boolean) {
- saveFeedPreferences { it: FeedPreferences -> it.autoDownload = enabled }
- }
- })
- preferenceSwitchDialog.openDialog()
- }, verticalAlignment = Alignment.CenterVertically
- ) {
+ Text(stringResource(id = R.string.keep_updated)) } },
+ { Row(verticalAlignment = Alignment.CenterVertically, modifier = Modifier.padding(horizontal = 16.dp).clickable {
+ isExpanded = false
+ selectMode = false
+ Logd(TAG, "ic_download: ${selected.size}")
+ val preferenceSwitchDialog = PreferenceSwitchDialog(activity, activity.getString(R.string.auto_download_settings_label), activity.getString(R.string.auto_download_label))
+ preferenceSwitchDialog.setOnPreferenceChangedListener( object: PreferenceSwitchDialog.OnPreferenceChangedListener {
+ override fun preferenceChanged(enabled: Boolean) {
+ saveFeedPreferences { it: FeedPreferences -> it.autoDownload = enabled }
+ }
+ })
+ preferenceSwitchDialog.openDialog()
+ }) {
Icon(imageVector = ImageVector.vectorResource(id = R.drawable.ic_download), "")
- Text(stringResource(id = R.string.auto_download_label))
- } },
- { Row(modifier = Modifier.padding(horizontal = 16.dp)
- .clickable {
- showAutoDeleteHandlerDialog = true
- isExpanded = false
- selectMode = false
- Logd(TAG, "ic_delete_auto: ${selected.size}")
-// autoDeleteEpisodesPrefHandler()
- }, verticalAlignment = Alignment.CenterVertically
- ) {
+ Text(stringResource(id = R.string.auto_download_label)) } },
+ { Row(verticalAlignment = Alignment.CenterVertically, modifier = Modifier.padding(horizontal = 16.dp).clickable {
+ showAutoDeleteHandlerDialog = true
+ isExpanded = false
+ selectMode = false
+ Logd(TAG, "ic_delete_auto: ${selected.size}")
+ }) {
Icon(imageVector = ImageVector.vectorResource(id = R.drawable.ic_delete_auto), "")
- Text(stringResource(id = R.string.auto_delete_label))
- } },
- { Row(modifier = Modifier.padding(horizontal = 16.dp)
- .clickable {
- isExpanded = false
- selectMode = false
- Logd(TAG, "ic_playback_speed: ${selected.size}")
- val vBinding = PlaybackSpeedFeedSettingDialogBinding.inflate(activity.layoutInflater)
- vBinding.seekBar.setProgressChangedListener { speed: Float? ->
- vBinding.currentSpeedLabel.text = String.format(Locale.getDefault(), "%.2fx", speed)
- }
- vBinding.useGlobalCheckbox.setOnCheckedChangeListener { _: CompoundButton?, isChecked: Boolean ->
- vBinding.seekBar.isEnabled = !isChecked
- vBinding.seekBar.alpha = if (isChecked) 0.4f else 1f
- vBinding.currentSpeedLabel.alpha = if (isChecked) 0.4f else 1f
- }
- vBinding.seekBar.updateSpeed(1.0f)
- MaterialAlertDialogBuilder(activity)
- .setTitle(R.string.playback_speed)
- .setView(vBinding.root)
- .setPositiveButton("OK") { _: DialogInterface?, _: Int ->
- val newSpeed = if (vBinding.useGlobalCheckbox.isChecked) FeedPreferences.SPEED_USE_GLOBAL
- else vBinding.seekBar.currentSpeed
- saveFeedPreferences { it: FeedPreferences ->
- it.playSpeed = newSpeed
- }
+ Text(stringResource(id = R.string.auto_delete_label)) } },
+ { Row(verticalAlignment = Alignment.CenterVertically, modifier = Modifier.padding(horizontal = 16.dp).clickable {
+ isExpanded = false
+ selectMode = false
+ Logd(TAG, "ic_playback_speed: ${selected.size}")
+ val vBinding = PlaybackSpeedFeedSettingDialogBinding.inflate(activity.layoutInflater)
+ vBinding.seekBar.setProgressChangedListener { speed: Float? ->
+ vBinding.currentSpeedLabel.text = String.format(Locale.getDefault(), "%.2fx", speed)
+ }
+ vBinding.useGlobalCheckbox.setOnCheckedChangeListener { _: CompoundButton?, isChecked: Boolean ->
+ vBinding.seekBar.isEnabled = !isChecked
+ vBinding.seekBar.alpha = if (isChecked) 0.4f else 1f
+ vBinding.currentSpeedLabel.alpha = if (isChecked) 0.4f else 1f
+ }
+ vBinding.seekBar.updateSpeed(1.0f)
+ MaterialAlertDialogBuilder(activity)
+ .setTitle(R.string.playback_speed)
+ .setView(vBinding.root)
+ .setPositiveButton("OK") { _: DialogInterface?, _: Int ->
+ val newSpeed = if (vBinding.useGlobalCheckbox.isChecked) FeedPreferences.SPEED_USE_GLOBAL
+ else vBinding.seekBar.currentSpeed
+ saveFeedPreferences { it: FeedPreferences ->
+ it.playSpeed = newSpeed
}
- .setNegativeButton(R.string.cancel_label, null)
- .show()
- }, verticalAlignment = Alignment.CenterVertically
- ) {
+ }
+ .setNegativeButton(R.string.cancel_label, null)
+ .show()
+ }) {
Icon(imageVector = ImageVector.vectorResource(id = R.drawable.ic_playback_speed), "")
- Text(stringResource(id = R.string.playback_speed))
- } },
- { Row(modifier = Modifier.padding(horizontal = 16.dp)
- .clickable {
- isExpanded = false
- selectMode = false
- Logd(TAG, "ic_tag: ${selected.size}")
- TagSettingsDialog.newInstance(selected).show(activity.supportFragmentManager, Companion.TAG)
- }, verticalAlignment = Alignment.CenterVertically
- ) {
+ Text(stringResource(id = R.string.playback_speed)) } },
+ { Row(verticalAlignment = Alignment.CenterVertically, modifier = Modifier.padding(horizontal = 16.dp).clickable {
+ isExpanded = false
+ selectMode = false
+ Logd(TAG, "ic_tag: ${selected.size}")
+ TagSettingsDialog.newInstance(selected).show(activity.supportFragmentManager, Companion.TAG)
+ }) {
Icon(imageVector = ImageVector.vectorResource(id = R.drawable.ic_tag), "")
- Text(stringResource(id = R.string.edit_tags))
- } },
- { Row(modifier = Modifier.padding(horizontal = 16.dp)
- .clickable {
- showAssociateDialog = true
- isExpanded = false
- selectMode = false
- Logd(TAG, "ic_playlist_play: ${selected.size}")
+ Text(stringResource(id = R.string.edit_tags)) } },
+ { Row(verticalAlignment = Alignment.CenterVertically, modifier = Modifier.padding(horizontal = 16.dp).clickable {
+ showAssociateDialog = true
+ isExpanded = false
+ selectMode = false
+ Logd(TAG, "ic_playlist_play: ${selected.size}")
// associatedQueuePrefHandler()
- }, verticalAlignment = Alignment.CenterVertically
- ) {
+ }) {
Icon(imageVector = ImageVector.vectorResource(id = R.drawable.ic_playlist_play), "")
- Text(stringResource(id = R.string.pref_feed_associated_queue))
- } },
- { Row(modifier = Modifier.padding(horizontal = 16.dp)
- .clickable {
- selectMode = false
- Logd(TAG, "ic_star: ${selected.size}")
- showChooseRatingDialog = true
- isExpanded = false
- }, verticalAlignment = Alignment.CenterVertically) {
+ Text(stringResource(id = R.string.pref_feed_associated_queue)) } },
+ { Row(verticalAlignment = Alignment.CenterVertically, modifier = Modifier.padding(horizontal = 16.dp).clickable {
+ selectMode = false
+ Logd(TAG, "ic_star: ${selected.size}")
+ showChooseRatingDialog = true
+ isExpanded = false
+ }) {
Icon(imageVector = ImageVector.vectorResource(id = R.drawable.ic_star), "Set rating")
Text(stringResource(id = R.string.set_rating_label)) } },
- { Row(modifier = Modifier.padding(horizontal = 16.dp)
- .clickable {
- isExpanded = false
- selectMode = false
- Logd(TAG, "baseline_import_export_24: ${selected.size}")
- val exportType = Export.OPML_SELECTED
- val title = String.format(exportType.outputNameTemplate, SimpleDateFormat("yyyy-MM-dd", Locale.US).format(Date()))
- val intentPickAction = Intent(Intent.ACTION_CREATE_DOCUMENT)
- .addCategory(Intent.CATEGORY_OPENABLE)
- .setType(exportType.contentType)
- .putExtra(Intent.EXTRA_TITLE, title)
- try {
- registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result: ActivityResult ->
- if (result.resultCode != RESULT_OK || result.data == null) return@registerForActivityResult
- val uri = result.data!!.data
- exportOPML(uri, selected)
- }.launch(intentPickAction)
- return@clickable
- } catch (e: ActivityNotFoundException) { Log.e(Companion.TAG, "No activity found. Should never happen...") }
- // if on SDK lower than API 21 or the implicit intent failed, fallback to the legacy export process
- exportOPML(null, selected)
- }, verticalAlignment = Alignment.CenterVertically
- ) {
+ { Row(verticalAlignment = Alignment.CenterVertically, modifier = Modifier.padding(horizontal = 16.dp).clickable {
+ isExpanded = false
+ selectMode = false
+ Logd(TAG, "baseline_import_export_24: ${selected.size}")
+ val exportType = Export.OPML_SELECTED
+ val title = String.format(exportType.outputNameTemplate, SimpleDateFormat("yyyy-MM-dd", Locale.US).format(Date()))
+ val intentPickAction = Intent(Intent.ACTION_CREATE_DOCUMENT)
+ .addCategory(Intent.CATEGORY_OPENABLE)
+ .setType(exportType.contentType)
+ .putExtra(Intent.EXTRA_TITLE, title)
+ try {
+ registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result: ActivityResult ->
+ if (result.resultCode != RESULT_OK || result.data == null) return@registerForActivityResult
+ val uri = result.data!!.data
+ exportOPML(uri, selected)
+ }.launch(intentPickAction)
+ return@clickable
+ } catch (e: ActivityNotFoundException) { Log.e(Companion.TAG, "No activity found. Should never happen...") }
+ // if on SDK lower than API 21 or the implicit intent failed, fallback to the legacy export process
+ exportOPML(null, selected)
+ }) {
Icon(imageVector = ImageVector.vectorResource(id = R.drawable.baseline_import_export_24), "")
- Text(stringResource(id = R.string.opml_export_label))
- } },
+ Text(stringResource(id = R.string.opml_export_label)) } },
)
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(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") }
}
}
@@ -849,8 +753,7 @@ class SubscriptionsFragment : Fragment(), Toolbar.OnMenuItemClickListener {
val lazyGridState = rememberLazyGridState()
LazyVerticalGrid(state = lazyGridState, columns = GridCells.Adaptive(80.dp),
verticalArrangement = Arrangement.spacedBy(16.dp), horizontalArrangement = Arrangement.spacedBy(16.dp),
- contentPadding = PaddingValues(start = 12.dp, top = 16.dp, end = 12.dp, bottom = 16.dp)
- ) {
+ contentPadding = PaddingValues(start = 12.dp, top = 16.dp, end = 12.dp, bottom = 16.dp)) {
items(feedListFiltered.size, key = {index -> feedListFiltered[index].id}) { index ->
val feed by remember { mutableStateOf(feedListFiltered[index]) }
var isSelected by remember { mutableStateOf(false) }
@@ -891,12 +794,11 @@ class SubscriptionsFragment : Fragment(), Toolbar.OnMenuItemClickListener {
AsyncImage(model = ImageRequest.Builder(context).data(imgLoc)
.memoryCachePolicy(CachePolicy.ENABLED).placeholder(R.mipmap.ic_launcher).error(R.mipmap.ic_launcher).build(),
contentDescription = "coverImage",
- modifier = Modifier.fillMaxWidth().aspectRatio(1f)
- .constrainAs(coverImage) {
- top.linkTo(parent.top)
- bottom.linkTo(parent.bottom)
- start.linkTo(parent.start)
- })
+ modifier = Modifier.fillMaxWidth().aspectRatio(1f).constrainAs(coverImage) {
+ top.linkTo(parent.top)
+ bottom.linkTo(parent.bottom)
+ start.linkTo(parent.start)
+ })
Text(NumberFormat.getInstance().format(feed.episodes.size.toLong()), color = Color.Green,
modifier = Modifier.background(Color.Gray).constrainAs(episodeCount) {
end.linkTo(parent.end)
@@ -923,7 +825,7 @@ class SubscriptionsFragment : Fragment(), Toolbar.OnMenuItemClickListener {
val lazyListState = rememberLazyListState()
LazyColumn(state = lazyListState, modifier = Modifier.padding(start = 10.dp, end = 10.dp, top = 10.dp, bottom = 10.dp),
verticalArrangement = Arrangement.spacedBy(8.dp)) {
- itemsIndexed(feedListFiltered, key = {index, feed -> feed.id}) { index, feed ->
+ itemsIndexed(feedListFiltered, key = { _, feed -> feed.id}) { index, feed ->
var isSelected by remember { mutableStateOf(false) }
LaunchedEffect(key1 = selectMode, key2 = selectedSize) {
isSelected = selectMode && feed in selected
@@ -935,35 +837,20 @@ class SubscriptionsFragment : Fragment(), Toolbar.OnMenuItemClickListener {
Logd(TAG, "toggleSelected: selected: ${selected.size}")
}
Row(Modifier.background(if (isSelected) MaterialTheme.colorScheme.secondaryContainer else MaterialTheme.colorScheme.surface)) {
- ConstraintLayout {
- val (coverImage, 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(),
- contentDescription = "imgvCover",
- placeholder = painterResource(R.mipmap.ic_launcher),
- error = painterResource(R.mipmap.ic_launcher),
- modifier = Modifier.width(80.dp).height(80.dp)
- .constrainAs(coverImage) {
- top.linkTo(parent.top)
- bottom.linkTo(parent.bottom)
- start.linkTo(parent.start)
- }.clickable(onClick = {
- Logd(TAG, "icon clicked!")
- if (!feed.isBuilding) {
- if (selectMode) toggleSelected()
- else (activity as MainActivity).loadChildFragment(FeedInfoFragment.newInstance(feed))
- }
- })
- )
-// if (feed.rating != Rating.UNRATED.code)
-// Icon(imageVector = ImageVector.vectorResource(Rating.fromCode(feed.rating).res), tint = MaterialTheme.colorScheme.tertiary,
-// contentDescription = "rating",
-// modifier = Modifier.background(MaterialTheme.colorScheme.tertiaryContainer).constrainAs(rating) {
-// start.linkTo(parent.start)
-// centerVerticallyTo(coverImage)
-// })
- }
+ 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(),
+ contentDescription = "imgvCover",
+ placeholder = painterResource(R.mipmap.ic_launcher),
+ error = painterResource(R.mipmap.ic_launcher),
+ modifier = Modifier.width(80.dp).height(80.dp).clickable(onClick = {
+ Logd(TAG, "icon clicked!")
+ if (!feed.isBuilding) {
+ if (selectMode) toggleSelected()
+ else (activity as MainActivity).loadChildFragment(FeedInfoFragment.newInstance(feed))
+ }
+ })
+ )
val textColor = MaterialTheme.colorScheme.onSurface
Column(Modifier.weight(1f).padding(start = 10.dp).combinedClickable(onClick = {
Logd(TAG, "clicked: ${feed.title}")
@@ -1014,51 +901,48 @@ class SubscriptionsFragment : Fragment(), Toolbar.OnMenuItemClickListener {
Row(modifier = Modifier.align(Alignment.TopEnd).width(150.dp).height(45.dp).background(Color.LightGray),
horizontalArrangement = Arrangement.Center, verticalAlignment = Alignment.CenterVertically) {
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])
- }
- selectedSize = selected.size
- Logd(TAG, "selectedIds: ${selected.size}")
- }))
+ 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])
+ }
+ 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..
if (selectNone) selectedList[index].value = false
LaunchedEffect(Unit) {
- if (filter != null) {
- if (item.values[index].filterId in filter.properties) selectedList[index].value = true
- }
+ if (filter != null && item.values[index].filterId in filter.properties) selectedList[index].value = true
}
OutlinedButton(modifier = Modifier.padding(0.dp).heightIn(min = 20.dp).widthIn(min = 20.dp).wrapContentWidth(),
border = BorderStroke(2.dp, if (selectedList[index].value) Color.Green else textColor),
@@ -1230,15 +1110,9 @@ class SubscriptionsFragment : Fragment(), Toolbar.OnMenuItemClickListener {
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/res/drawable/baseline_dynamic_feed_24.xml b/app/src/main/res/drawable/baseline_dynamic_feed_24.xml
new file mode 100644
index 00000000..f33c5257
--- /dev/null
+++ b/app/src/main/res/drawable/baseline_dynamic_feed_24.xml
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/fragment_subscriptions.xml b/app/src/main/res/layout/fragment_subscriptions.xml
deleted file mode 100644
index c16725cd..00000000
--- a/app/src/main/res/layout/fragment_subscriptions.xml
+++ /dev/null
@@ -1,74 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/app/src/main/res/layout/queue_title_spinner.xml b/app/src/main/res/layout/queue_title_spinner.xml
deleted file mode 100644
index 89676cb1..00000000
--- a/app/src/main/res/layout/queue_title_spinner.xml
+++ /dev/null
@@ -1,17 +0,0 @@
-
-
-
-
-
-
diff --git a/app/src/main/res/menu/queue.xml b/app/src/main/res/menu/queue.xml
index 84491747..f2889447 100644
--- a/app/src/main/res/menu/queue.xml
+++ b/app/src/main/res/menu/queue.xml
@@ -5,24 +5,26 @@
+
+
-
-
+ android:icon="@drawable/arrows_sort"
+ android:title="@string/sort"
+ custom:showAsAction="ifRoom" />
+
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index fd86cc38..cc7a41cc 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -572,6 +572,7 @@
The speed to use when starting audio playback for episodes in this podcast
Auto skip
Skip introductions and ending credits.
+ Associated
Associated queue
The queue to which epiosdes in the feed will added by default
Skip last
diff --git a/changelog.md b/changelog.md
index afc9c50f..f520aa96 100644
--- a/changelog.md
+++ b/changelog.md
@@ -1,3 +1,11 @@
+# 6.13.4
+
+* in Queues view, reworked the spinner in Compose and added associated feeds toggle
+* added info bar and swipe actions to Search fragment
+* spinners in Subscriptions view are in Compose
+* removed the "add" floating button in Subscriptions view
+* removed some unused dependencies and references to their licenses
+
# 6.13.3
* on playing the next episode in queue, if its state is lower than InProgress, the state is set as such
diff --git a/fastlane/metadata/android/en-US/changelogs/3020291.txt b/fastlane/metadata/android/en-US/changelogs/3020291.txt
new file mode 100644
index 00000000..e95e04a1
--- /dev/null
+++ b/fastlane/metadata/android/en-US/changelogs/3020291.txt
@@ -0,0 +1,7 @@
+ Version 6.13.4
+
+* in Queues view, reworked the spinner in Compose and added associated feeds toggle
+* added info bar and swipe actions to Search fragment
+* spinners in Subscriptions view are in Compose
+* removed the "add" floating button in Subscriptions view
+* removed some unused dependencies and references to their licenses
diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml
index b82895d2..5494385d 100644
--- a/gradle/libs.versions.toml
+++ b/gradle/libs.versions.toml
@@ -3,7 +3,6 @@
activityCompose = "1.9.3"
annotation = "1.9.1"
appcompat = "1.7.0"
-#awaitility = "4.2.1"
balloon = "1.6.6"
coil = "2.7.0"
commonsLang3 = "3.15.0"
@@ -17,8 +16,6 @@ coreKtxVersion = "1.8.1"
coreSplashscreen = "1.0.1"
desugar_jdk_libs_nio = "2.1.2"
documentfile = "1.0.1"
-#espressoCore = "3.6.1"
-fontawesomeTypeface = "5.13.3.0-kotlin"
fyydlin = "v0.5.0"
googleMaterialTypeface = "4.0.0.3-kotlin"
googleMaterialTypefaceOutlined = "4.0.0.2-kotlin"
@@ -27,26 +24,18 @@ gridlayout = "1.0.0"
groovyXml = "3.0.19"
iconicsCore = "5.5.0-b01"
iconicsViews = "5.5.0-b01"
-#javaxInject = "1"
jsoup = "1.18.1"
-#junit = "1.2.1"
-#junitVersion = "4.13.2"
kotlin = "2.0.20"
kotlinxCoroutinesAndroid = "1.8.1"
libraryBase = "2.1.0"
lifecycleRuntimeKtx = "2.8.7"
-#material = "1.7.2"
material3 = "1.3.1"
-#material3Android = "1.3.0"
-#materialIconsExtended = "1.7.3"
materialVersion = "1.12.0"
media3Common = "1.4.1"
media3Session = "1.4.1"
media3Ui = "1.4.1"
media3Exoplayer = "1.4.1"
mediarouter = "1.7.0"
-#mockitoInline = "5.2.0"
-#nanohttpd = "2.1.1"
okhttp = "4.12.0"
okhttpUrlconnection = "4.12.0"
okio = "3.9.0"
@@ -56,16 +45,11 @@ playServicesCastFramework = "22.0.0"
preferenceKtx = "1.2.1"
readability4j = "1.0.8"
recyclerview = "1.3.2"
-recyclerviewswipedecorator = "1.3"
-#robolectric = "4.13"
-#rules = "1.6.1"
-#runner = "1.6.2"
rxandroid = "3.0.2"
rxjava = "2.2.21"
rxjavaVersion = "3.1.8"
-#speedDial = "3.3.0"
searchpreference = "v2.5.0"
-stream = "1.2.2"
+#stream = "1.2.2"
uiToolingPreview = "1.7.5"
uiTooling = "1.7.5"
viewpager2 = "1.1.0"
@@ -74,9 +58,7 @@ wearable = "2.9.0"
webkit = "1.12.1"
window = "1.3.0"
workRuntime = "2.10.0"
-#uiViewbinding = "1.7.2"
fragmentCompose = "1.8.5"
-#fragment-ktx = "1.8.4"
[libraries]
androidx-activity-compose = { module = "androidx.activity:activity-compose", version.ref = "activityCompose" }
androidx-annotation = { module = "androidx.annotation:annotation", version.ref = "annotation" }
@@ -84,19 +66,11 @@ androidx-appcompat = { module = "androidx.appcompat:appcompat", version.ref = "a
androidx-compose-bom = { module = "androidx.compose:compose-bom", version.ref = "composeBom" }
androidx-constraintlayout-compose = { module = "androidx.constraintlayout:constraintlayout-compose", version.ref = "constraintlayoutCompose" }
androidx-coordinatorlayout = { module = "androidx.coordinatorlayout:coordinatorlayout", version.ref = "coordinatorlayout" }
-#androidx-core = { module = "androidx.test:core", version.ref = "rules" }
androidx-core-ktx = { module = "androidx.core:core-ktx", version.ref = "coreKtx" }
androidx-documentfile = { module = "androidx.documentfile:documentfile", version.ref = "documentfile" }
androidx-core-splashscreen = { module = "androidx.core:core-splashscreen", version.ref = "coreSplashscreen" }
-#androidx-espresso-contrib = { module = "androidx.test.espresso:espresso-contrib", version.ref = "espressoCore" }
-#androidx-espresso-core = { module = "androidx.test.espresso:espresso-core", version.ref = "espressoCore" }
-#androidx-espresso-intents = { module = "androidx.test.espresso:espresso-intents", version.ref = "espressoCore" }
androidx-gridlayout = { module = "androidx.gridlayout:gridlayout", version.ref = "gridlayout" }
-#androidx-junit = { module = "androidx.test.ext:junit", version.ref = "junit" }
androidx-lifecycle-runtime-ktx = { module = "androidx.lifecycle:lifecycle-runtime-ktx", version.ref = "lifecycleRuntimeKtx" }
-#androidx-material = { module = "androidx.compose.material:material", version.ref = "material" }
-#androidx-material-icons-extended = { module = "androidx.compose.material:material-icons-extended", version.ref = "materialIconsExtended" }
-#androidx-material3 = { module = "androidx.compose.material3:material3", version.ref = "material3" }
androidx-material3-android = { group = "androidx.compose.material3", name = "material3", version.ref = "material3" }
androidx-media3-exoplayer = { module = "androidx.media3:media3-exoplayer", version.ref = "media3Exoplayer" }
androidx-media3-media3-datasource-okhttp = { module = "androidx.media3:media3-datasource-okhttp", version.ref = "media3Ui" }
@@ -105,15 +79,12 @@ androidx-mediarouter = { module = "androidx.mediarouter:mediarouter", version.re
androidx-palette-ktx = { module = "androidx.palette:palette-ktx", version.ref = "paletteKtx" }
androidx-recyclerview = { module = "androidx.recyclerview:recyclerview", version.ref = "recyclerview" }
androidx-preference-ktx = { module = "androidx.preference:preference-ktx", version.ref = "preferenceKtx" }
-#androidx-runner = { module = "androidx.test:runner", version.ref = "runner" }
-#androidx-rules = { module = "androidx.test:rules", version.ref = "rules" }
androidx-ui-tooling = { module = "androidx.compose.ui:ui-tooling", version.ref = "uiTooling" }
androidx-ui-tooling-preview = { module = "androidx.compose.ui:ui-tooling-preview", version.ref = "uiToolingPreview" }
androidx-viewpager2 = { module = "androidx.viewpager2:viewpager2", version.ref = "viewpager2" }
androidx-webkit = { module = "androidx.webkit:webkit", version.ref = "webkit" }
androidx-window = { module = "androidx.window:window", version.ref = "window" }
androidx-work-runtime = { module = "androidx.work:work-runtime", version.ref = "workRuntime" }
-#awaitility = { module = "org.awaitility:awaitility", version.ref = "awaitility" }
balloon = { module = "com.github.skydoves:balloon", version.ref = "balloon" }
coil = { module = "io.coil-kt:coil", version.ref = "coil" }
coil-compose = { module = "io.coil-kt:coil-compose", version.ref = "coil" }
@@ -121,7 +92,6 @@ commons-io = { module = "commons-io:commons-io", version.ref = "commonsIo" }
commons-lang3 = { module = "org.apache.commons:commons-lang3", version.ref = "commonsLang3" }
conscrypt-android = { module = "org.conscrypt:conscrypt-android", version.ref = "conscryptAndroid" }
core-ktx = { module = "com.google.android.play:core-ktx", version.ref = "coreKtxVersion" }
-fontawesome-typeface = { module = "com.mikepenz:fontawesome-typeface", version.ref = "fontawesomeTypeface" }
fyydlin = { module = "com.github.mfietz:fyydlin", version.ref = "fyydlin" }
google-material-typeface-outlined = { module = "com.mikepenz:google-material-typeface-outlined", version.ref = "googleMaterialTypefaceOutlined" }
google-material-typeface = { module = "com.mikepenz:google-material-typeface", version.ref = "googleMaterialTypeface" }
@@ -129,34 +99,26 @@ gradle = { module = "com.android.tools.build:gradle", version.ref = "gradle" }
groovy-xml = { module = "org.codehaus.groovy:groovy-xml", version.ref = "groovyXml" }
iconics-views = { module = "com.mikepenz:iconics-views", version.ref = "iconicsViews" }
iconics-core = { module = "com.mikepenz:iconics-core", version.ref = "iconicsCore" }
-#javax-inject = { module = "javax.inject:javax.inject", version.ref = "javaxInject" }
jsoup = { module = "org.jsoup:jsoup", version.ref = "jsoup" }
-#junit = { module = "junit:junit", version.ref = "junitVersion" }
kotlinx-coroutines-android = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-android", version.ref = "kotlinxCoroutinesAndroid" }
library-base = { module = "io.realm.kotlin:library-base", version.ref = "libraryBase" }
material = { module = "com.google.android.material:material", version.ref = "materialVersion" }
media3-common = { module = "androidx.media3:media3-common", version.ref = "media3Common" }
media3-session = { module = "androidx.media3:media3-session", version.ref = "media3Session" }
-#mockito-inline = { module = "org.mockito:mockito-inline", version.ref = "mockitoInline" }
-#nanohttpd = { module = "com.nanohttpd:nanohttpd", version.ref = "nanohttpd" }
okhttp = { module = "com.squareup.okhttp3:okhttp", version.ref = "okhttp" }
okio = { module = "com.squareup.okio:okio", version.ref = "okio" }
okhttp3-okhttp-urlconnection = { module = "com.squareup.okhttp3:okhttp-urlconnection", version.ref = "okhttpUrlconnection" }
play-services-base = { module = "com.google.android.gms:play-services-base", version.ref = "playServicesBase" }
play-services-cast-framework = { module = "com.google.android.gms:play-services-cast-framework", version.ref = "playServicesCastFramework" }
readability4j = { module = "net.dankito.readability4j:readability4j", version.ref = "readability4j" }
-recyclerviewswipedecorator = { module = "com.github.xabaras:RecyclerViewSwipeDecorator", version.ref = "recyclerviewswipedecorator" }
-#robolectric = { module = "org.robolectric:robolectric", version.ref = "robolectric" }
rxandroid = { module = "io.reactivex.rxjava3:rxandroid", version.ref = "rxandroid" }
rxjava3-rxjava = { module = "io.reactivex.rxjava3:rxjava", version.ref = "rxjavaVersion" }
rxjava = { module = "io.reactivex.rxjava2:rxjava", version.ref = "rxjava" }
searchpreference = { module = "com.github.ByteHamster:SearchPreference", version.ref = "searchpreference" }
-#speed-dial = { module = "com.leinardi.android:speed-dial", version.ref = "speedDial" }
-stream = { module = "com.annimon:stream", version.ref = "stream" }
+#stream = { module = "com.annimon:stream", version.ref = "stream" }
vistaguide = { module = "com.github.XilinJia.vistaguide:VistaGuide", version.ref = "vistaguide" }
desugar_jdk_libs_nio = { module = "com.android.tools:desugar_jdk_libs_nio", version.ref = "desugar_jdk_libs_nio" }
wearable = { module = "com.google.android.wearable:wearable", version.ref = "wearable" }
-#androidx-ui-viewbinding = { group = "androidx.compose.ui", name = "ui-viewbinding", version.ref = "uiViewbinding" }
androidx-fragment-compose = { group = "androidx.fragment", name = "fragment-compose", version.ref = "fragmentCompose" }
androidx-fragment-fragment-ktx = { module = "androidx.fragment:fragment-ktx", version = "1.8.5" }