From 6b29cc3f5d98c8f8f8047090e337eae81b248fa3 Mon Sep 17 00:00:00 2001 From: Xilin Jia <6257601+XilinJia@users.noreply.github.com> Date: Sat, 26 Oct 2024 19:45:43 +0100 Subject: [PATCH] 6.12.6 commit --- app/build.gradle | 4 +- .../podcini/net/sync/wifi/WifiSyncService.kt | 2 +- .../ImportExportPreferencesFragment.kt | 4 +- .../mdiq/podcini/storage/database/Queues.kt | 12 +- .../podcini/storage/model/EpisodeFilter.kt | 34 ++-- .../mdiq/podcini/ui/actions/SwipeActions.kt | 17 +- .../ac/mdiq/podcini/ui/compose/EpisodesVM.kt | 149 +++++++++++++----- .../ui/fragment/BaseEpisodesFragment.kt | 4 +- .../podcini/ui/fragment/DownloadsFragment.kt | 4 +- .../ui/fragment/FeedEpisodesFragment.kt | 7 +- .../podcini/ui/fragment/FeedInfoFragment.kt | 2 +- .../podcini/ui/fragment/QueuesFragment.kt | 42 +++-- .../podcini/ui/fragment/SearchFragment.kt | 42 +++-- .../ui/fragment/SubscriptionsFragment.kt | 68 +++++++- .../kotlin/ac/mdiq/podcini/util/ShareUtils.kt | 4 + app/src/main/res/values/strings.xml | 1 + changelog.md | 13 ++ .../android/en-US/changelogs/3020284.txt | 12 ++ 18 files changed, 294 insertions(+), 127 deletions(-) create mode 100644 fastlane/metadata/android/en-US/changelogs/3020284.txt diff --git a/app/build.gradle b/app/build.gradle index 01ace6d8..b69d15e6 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -31,8 +31,8 @@ android { testApplicationId "ac.mdiq.podcini.tests" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" - versionCode 3020283 - versionName "6.12.5" + versionCode 3020284 + versionName "6.12.6" applicationId "ac.mdiq.podcini.R" def commit = "" diff --git a/app/src/main/kotlin/ac/mdiq/podcini/net/sync/wifi/WifiSyncService.kt b/app/src/main/kotlin/ac/mdiq/podcini/net/sync/wifi/WifiSyncService.kt index f077d4f2..b9805e7f 100644 --- a/app/src/main/kotlin/ac/mdiq/podcini/net/sync/wifi/WifiSyncService.kt +++ b/app/src/main/kotlin/ac/mdiq/podcini/net/sync/wifi/WifiSyncService.kt @@ -247,7 +247,7 @@ import kotlin.math.min // only push downloaded items val pausedItems = getEpisodes(0, Int.MAX_VALUE, EpisodeFilter(EpisodeFilter.States.paused.name), EpisodeSortOrder.DATE_NEW_OLD) val readItems = getEpisodes(0, Int.MAX_VALUE, EpisodeFilter(EpisodeFilter.States.played.name), EpisodeSortOrder.DATE_NEW_OLD) - val favoriteItems = getEpisodes(0, Int.MAX_VALUE, EpisodeFilter(EpisodeFilter.States.is_favorite.name), EpisodeSortOrder.DATE_NEW_OLD) + val favoriteItems = getEpisodes(0, Int.MAX_VALUE, EpisodeFilter(EpisodeFilter.States.favorite.name), EpisodeSortOrder.DATE_NEW_OLD) val comItems = mutableSetOf() comItems.addAll(pausedItems) comItems.addAll(readItems) diff --git a/app/src/main/kotlin/ac/mdiq/podcini/preferences/fragments/ImportExportPreferencesFragment.kt b/app/src/main/kotlin/ac/mdiq/podcini/preferences/fragments/ImportExportPreferencesFragment.kt index ec5f8d52..ceffe1d8 100644 --- a/app/src/main/kotlin/ac/mdiq/podcini/preferences/fragments/ImportExportPreferencesFragment.kt +++ b/app/src/main/kotlin/ac/mdiq/podcini/preferences/fragments/ImportExportPreferencesFragment.kt @@ -959,7 +959,7 @@ class ImportExportPreferencesFragment : PreferenceFragmentCompat() { val queuedEpisodeActions: MutableList = mutableListOf() val pausedItems = getEpisodes(0, Int.MAX_VALUE, EpisodeFilter(EpisodeFilter.States.paused.name), EpisodeSortOrder.DATE_NEW_OLD) val readItems = getEpisodes(0, Int.MAX_VALUE, EpisodeFilter(EpisodeFilter.States.played.name), EpisodeSortOrder.DATE_NEW_OLD) - val favoriteItems = getEpisodes(0, Int.MAX_VALUE, EpisodeFilter(EpisodeFilter.States.is_favorite.name), EpisodeSortOrder.DATE_NEW_OLD) + val favoriteItems = getEpisodes(0, Int.MAX_VALUE, EpisodeFilter(EpisodeFilter.States.favorite.name), EpisodeSortOrder.DATE_NEW_OLD) val comItems = mutableSetOf() comItems.addAll(pausedItems) comItems.addAll(readItems) @@ -1018,7 +1018,7 @@ class ImportExportPreferencesFragment : PreferenceFragmentCompat() { val favTemplate = IOUtils.toString(favTemplateStream, UTF_8) val feedTemplateStream = context.assets.open(FEED_TEMPLATE) val feedTemplate = IOUtils.toString(feedTemplateStream, UTF_8) - val allFavorites = getEpisodes(0, Int.MAX_VALUE, EpisodeFilter(EpisodeFilter.States.is_favorite.name), EpisodeSortOrder.DATE_NEW_OLD) + val allFavorites = getEpisodes(0, Int.MAX_VALUE, EpisodeFilter(EpisodeFilter.States.favorite.name), EpisodeSortOrder.DATE_NEW_OLD) val favoritesByFeed = buildFeedMap(allFavorites) writer!!.append(templateParts[0]) for (feedId in favoritesByFeed.keys) { diff --git a/app/src/main/kotlin/ac/mdiq/podcini/storage/database/Queues.kt b/app/src/main/kotlin/ac/mdiq/podcini/storage/database/Queues.kt index 52683d0a..5abdf758 100644 --- a/app/src/main/kotlin/ac/mdiq/podcini/storage/database/Queues.kt +++ b/app/src/main/kotlin/ac/mdiq/podcini/storage/database/Queues.kt @@ -19,6 +19,9 @@ import ac.mdiq.podcini.util.EventFlow import ac.mdiq.podcini.util.FlowEvent import android.util.Log import androidx.annotation.OptIn +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.setValue import androidx.media3.common.util.UnstableApi import kotlinx.coroutines.Job import java.util.* @@ -30,12 +33,6 @@ object Queues { BACK, FRONT, AFTER_CURRENTLY_PLAYING, RANDOM } - var isQueueLocked: Boolean - get() = appPrefs.getBoolean(UserPreferences.Prefs.prefQueueLocked.name, false) - set(locked) { - appPrefs.edit().putBoolean(UserPreferences.Prefs.prefQueueLocked.name, locked).apply() - } - /** * Returns if the queue is in keep sorted mode. * Enables/disables the keep sorted mode of the queue. @@ -66,8 +63,7 @@ object Queues { var enqueueLocation: EnqueueLocation get() { val valStr = appPrefs.getString(UserPreferences.Prefs.prefEnqueueLocation.name, EnqueueLocation.BACK.name) - try { - return EnqueueLocation.valueOf(valStr!!) + try { return EnqueueLocation.valueOf(valStr!!) } catch (t: Throwable) { // should never happen but just in case Log.e(TAG, "getEnqueueLocation: invalid value '$valStr' Use default.", t) diff --git a/app/src/main/kotlin/ac/mdiq/podcini/storage/model/EpisodeFilter.kt b/app/src/main/kotlin/ac/mdiq/podcini/storage/model/EpisodeFilter.kt index a85fae63..ca682005 100644 --- a/app/src/main/kotlin/ac/mdiq/podcini/storage/model/EpisodeFilter.kt +++ b/app/src/main/kotlin/ac/mdiq/podcini/storage/model/EpisodeFilter.kt @@ -2,20 +2,21 @@ package ac.mdiq.podcini.storage.model import ac.mdiq.podcini.R import ac.mdiq.podcini.storage.database.Queues.inAnyQueue +import ac.mdiq.podcini.util.Logd import java.io.Serializable class EpisodeFilter(vararg properties_: String) : Serializable { val properties: HashSet = setOf(*properties_).filter { it.isNotEmpty() }.map {it.trim()}.toHashSet() - val showPlayed: Boolean = properties.contains(States.played.name) - val showUnplayed: Boolean = properties.contains(States.unplayed.name) - val showNew: Boolean = properties.contains(States.new.name) +// val showPlayed: Boolean = properties.contains(States.played.name) +// val showUnplayed: Boolean = properties.contains(States.unplayed.name) +// val showNew: Boolean = properties.contains(States.new.name) val showQueued: Boolean = properties.contains(States.queued.name) val showNotQueued: Boolean = properties.contains(States.not_queued.name) val showDownloaded: Boolean = properties.contains(States.downloaded.name) val showNotDownloaded: Boolean = properties.contains(States.not_downloaded.name) - val showIsFavorite: Boolean = properties.contains(States.is_favorite.name) - val showNotFavorite: Boolean = properties.contains(States.not_favorite.name) +// val showIsFavorite: Boolean = properties.contains(States.is_favorite.name) +// val showNotFavorite: Boolean = properties.contains(States.not_favorite.name) constructor(properties: String) : this(*(properties.split(",").toTypedArray())) @@ -30,11 +31,11 @@ class EpisodeFilter(vararg properties_: String) : Serializable { fun queryString(): String { val statements: MutableList = mutableListOf() - when { - showPlayed -> statements.add("playState >= ${PlayState.PLAYED.code}") - showUnplayed -> statements.add(" playState < ${PlayState.PLAYED.code} ") // Match "New" items (read = -1) as well - showNew -> statements.add("playState == -1 ") - } +// when { +//// showPlayed -> statements.add("playState >= ${PlayState.PLAYED.code}") +//// showUnplayed -> statements.add(" playState < ${PlayState.PLAYED.code} ") // Match "New" items (read = -1) as well +//// showNew -> statements.add("playState == -1 ") +// } val mediaTypeQuerys = mutableListOf() if (properties.contains(States.unknown.name)) mediaTypeQuerys.add(" media == nil OR media.mimeType == nil OR media.mimeType == '' ") @@ -118,10 +119,10 @@ class EpisodeFilter(vararg properties_: String) : Serializable { properties.contains(States.has_comments.name) -> statements.add(" comment != '' ") properties.contains(States.no_comments.name) -> statements.add(" comment == '' ") } - when { - showIsFavorite -> statements.add("rating == ${Rating.FAVORITE.code} ") - showNotFavorite -> statements.add("rating != ${Rating.FAVORITE.code} ") - } +// when { +// showIsFavorite -> statements.add("rating == ${Rating.FAVORITE.code} ") +// showNotFavorite -> statements.add("rating != ${Rating.FAVORITE.code} ") +// } if (statements.isEmpty()) return "id > 0" val query = StringBuilder(" (" + statements[0]) @@ -130,6 +131,7 @@ class EpisodeFilter(vararg properties_: String) : Serializable { query.append(r) } query.append(") ") + Logd("EpisodeFilter", "queryString: $query") return query.toString() } @@ -154,8 +156,8 @@ class EpisodeFilter(vararg properties_: String) : Serializable { audio_app, paused, not_paused, - is_favorite, - not_favorite, +// is_favorite, +// not_favorite, has_media, no_media, has_comments, 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 7eee1fd7..dbbc242d 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 @@ -28,6 +28,7 @@ import ac.mdiq.podcini.ui.fragment.* import ac.mdiq.podcini.ui.utils.LocalDeleteModal.deleteEpisodesWarnLocal import ac.mdiq.podcini.util.EventFlow import ac.mdiq.podcini.util.FlowEvent +import ac.mdiq.podcini.util.Logd import android.content.Context import android.content.SharedPreferences import android.util.TypedValue @@ -92,6 +93,7 @@ open class SwipeActions(private val fragment: Fragment, private val tag: String) // EventFlow.postEvent(FlowEvent.SwipeActionsChangedEvent()) // } // }) + Logd("SwipeActions", "showDialog()") val composeView = ComposeView(fragment.requireContext()).apply { setContent { val showDialog = remember { mutableStateOf(true) } @@ -101,6 +103,7 @@ open class SwipeActions(private val fragment: Fragment, private val tag: String) (fragment.view as? ViewGroup)?.removeView(this@apply) }) { actions = getPrefs(this@SwipeActions.tag) + // TODO: remove the need of event EventFlow.postEvent(FlowEvent.SwipeActionsChangedEvent()) } } @@ -149,7 +152,8 @@ open class SwipeActions(private val fragment: Fragment, private val tag: String) } override fun willRemove(filter: EpisodeFilter, item: Episode): Boolean { - return filter.showQueued || filter.showNew + return false +// return filter.showQueued || filter.showNew } } @@ -208,7 +212,8 @@ open class SwipeActions(private val fragment: Fragment, private val tag: String) } override fun willRemove(filter: EpisodeFilter, item: Episode): Boolean { - return filter.showQueued || filter.showNew + return false +// return filter.showQueued || filter.showNew } } @@ -274,7 +279,8 @@ open class SwipeActions(private val fragment: Fragment, private val tag: String) } override fun willRemove(filter: EpisodeFilter, item: Episode): Boolean { - return filter.showIsFavorite || filter.showNotFavorite + return false +// return filter.showIsFavorite || filter.showNotFavorite } } @@ -479,8 +485,9 @@ open class SwipeActions(private val fragment: Fragment, private val tag: String) } override fun willRemove(filter: EpisodeFilter, item: Episode): Boolean { - return if (item.playState == PlayState.NEW.code) filter.showPlayed || filter.showNew - else filter.showUnplayed || filter.showPlayed || filter.showNew + return false +// return if (item.playState == PlayState.NEW.code) filter.showPlayed || filter.showNew +// else filter.showUnplayed || filter.showPlayed || filter.showNew } } diff --git a/app/src/main/kotlin/ac/mdiq/podcini/ui/compose/EpisodesVM.kt b/app/src/main/kotlin/ac/mdiq/podcini/ui/compose/EpisodesVM.kt index 19264d18..fff311d4 100644 --- a/app/src/main/kotlin/ac/mdiq/podcini/ui/compose/EpisodesVM.kt +++ b/app/src/main/kotlin/ac/mdiq/podcini/ui/compose/EpisodesVM.kt @@ -55,8 +55,7 @@ import android.view.Gravity import androidx.compose.animation.core.Animatable import androidx.compose.animation.core.tween import androidx.compose.foundation.* -import androidx.compose.foundation.gestures.detectHorizontalDragGestures -import androidx.compose.foundation.gestures.detectTapGestures +import androidx.compose.foundation.gestures.* import androidx.compose.foundation.layout.* import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.itemsIndexed @@ -77,6 +76,7 @@ import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.input.pointer.pointerInput import androidx.compose.ui.input.pointer.util.VelocityTracker import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.platform.LocalDensity import androidx.compose.ui.platform.LocalView import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.vectorResource @@ -410,10 +410,10 @@ fun ShelveDialog(selected: List, onDismissRequest: () -> Unit) { } } - @OptIn(ExperimentalFoundationApi::class, ExperimentalMaterial3Api::class) @Composable -fun EpisodeLazyColumn(activity: MainActivity, vms: List, feed: Feed? = null, +fun EpisodeLazyColumn(activity: MainActivity, vms: MutableList, feed: Feed? = null, + isDraggable: Boolean = false, dragCB: ((Int, Int)->Unit)? = null, refreshCB: (()->Unit)? = null, leftSwipeCB: ((Episode) -> Unit)? = null, rightSwipeCB: ((Episode) -> Unit)? = null, actionButton_: ((Episode)-> EpisodeActionButton)? = null) { val TAG = "EpisodeLazyColumn" @@ -457,10 +457,7 @@ fun EpisodeLazyColumn(activity: MainActivity, vms: List, feed: Feed? Text(stringResource(R.string.feed_delete_reason_msg)) BasicTextField(value = textState, onValueChange = { textState = it }, textStyle = TextStyle(fontSize = 16.sp, color = textColor), - modifier = Modifier - .fillMaxWidth() - .height(100.dp) - .padding(start = 10.dp, end = 10.dp, bottom = 10.dp) + modifier = Modifier.fillMaxWidth().height(100.dp).padding(start = 10.dp, end = 10.dp, bottom = 10.dp) .border(1.dp, MaterialTheme.colorScheme.primary, MaterialTheme.shapes.small) ) Button(onClick = { @@ -649,19 +646,26 @@ fun EpisodeLazyColumn(activity: MainActivity, vms: List, feed: Feed? } @Composable - fun MainRow(vm: EpisodeVM, index: Int) { + fun MainRow(vm: EpisodeVM, index: Int, isBeingDragged: Boolean, yOffset: Float, onDragStart: () -> Unit, onDrag: (Float) -> Unit, onDragEnd: () -> Unit) { val textColor = MaterialTheme.colorScheme.onSurface fun toggleSelected() { vm.isSelected = !vm.isSelected if (vm.isSelected) selected.add(vms[index].episode) else selected.remove(vms[index].episode) } - Row(Modifier.background(if (vm.isSelected) MaterialTheme.colorScheme.secondaryContainer else MaterialTheme.colorScheme.surface)) { - if (false) { + val density = LocalDensity.current + Row(Modifier.background(if (vm.isSelected) MaterialTheme.colorScheme.secondaryContainer else MaterialTheme.colorScheme.surface) + .offset(y = with(density) { yOffset.toDp() })) { + if (isDraggable) { val typedValue = TypedValue() - LocalContext.current.theme.resolveAttribute(R.attr.dragview_background, typedValue, true) + context.theme.resolveAttribute(R.attr.dragview_background, typedValue, true) Icon(imageVector = ImageVector.vectorResource(typedValue.resourceId), tint = textColor, contentDescription = "drag handle", - modifier = Modifier.width(16.dp).align(Alignment.CenterVertically)) + modifier = Modifier.width(50.dp).align(Alignment.CenterVertically).padding(start = 10.dp, end = 15.dp) + .draggable(orientation = Orientation.Vertical, + state = rememberDraggableState { delta -> onDrag(delta) }, + onDragStarted = { onDragStart() }, + onDragStopped = { onDragEnd() } + )) } ConstraintLayout(modifier = Modifier.width(56.dp).height(56.dp)) { val (imgvCover, checkMark) = createRefs() @@ -727,7 +731,7 @@ fun EpisodeLazyColumn(activity: MainActivity, vms: List, feed: Feed? val durText = remember { DurationConverter.getDurationStringLong(dur) } val dateSizeText = " · " + formatAbbrev(curContext, vm.episode.getPubDate()) + " · " + durText + " · " + if ((vm.episode.media?.size ?: 0) > 0) Formatter.formatShortFileSize(curContext, vm.episode.media?.size ?: 0) else "" - Text(dateSizeText, color = textColor, style = MaterialTheme.typography.bodyMedium) + Text(dateSizeText, color = textColor, style = MaterialTheme.typography.bodyMedium, maxLines = 1, overflow = TextOverflow.Ellipsis) } Text(vm.episode.title ?: "", color = textColor, maxLines = 2, overflow = TextOverflow.Ellipsis) } @@ -774,7 +778,7 @@ fun EpisodeLazyColumn(activity: MainActivity, vms: List, feed: Feed? val dur = remember { vm.episode.media?.getDuration() ?: 0 } val durText = remember { DurationConverter.getDurationStringLong(dur) } vm.prog = if (dur > 0 && pos >= 0 && dur >= pos) 1.0f * pos / dur else 0f - Logd(TAG, "$index vm.prog: ${vm.prog}") +// Logd(TAG, "$index vm.prog: ${vm.prog}") Row { Text(DurationConverter.getDurationStringLong(vm.positionState), color = textColor, style = MaterialTheme.typography.bodySmall) LinearProgressIndicator(progress = { vm.prog }, modifier = Modifier.weight(1f).height(4.dp).align(Alignment.CenterVertically)) @@ -789,6 +793,13 @@ fun EpisodeLazyColumn(activity: MainActivity, vms: List, feed: Feed? refreshCB?.invoke() refreshing = false }) { + fun MutableList.move(fromIndex: Int, toIndex: Int) { + if (fromIndex != toIndex && fromIndex in indices && toIndex in indices) { + val item = removeAt(fromIndex) + add(toIndex, item) + } + } + val rowHeightPx = with(LocalDensity.current) { 56.dp.toPx() } LazyColumn(state = lazyListState, modifier = Modifier.padding(start = 10.dp, end = 10.dp, top = 10.dp, bottom = 10.dp), verticalArrangement = Arrangement.spacedBy(8.dp)) { itemsIndexed(vms, key = { _, vm -> vm.episode.id}) { index, vm -> vm.startMonitoring() @@ -801,7 +812,6 @@ fun EpisodeLazyColumn(activity: MainActivity, vms: List, feed: Feed? val velocityTracker = remember { VelocityTracker() } val offsetX = remember { Animatable(0f) } Box(modifier = Modifier.fillMaxWidth().pointerInput(Unit) { - Logd(TAG, "top box") detectHorizontalDragGestures(onDragStart = { velocityTracker.resetTracking() }, onHorizontalDrag = { change, dragAmount -> Logd(TAG, "onHorizontalDrag $dragAmount") @@ -829,7 +839,25 @@ fun EpisodeLazyColumn(activity: MainActivity, vms: List, feed: Feed? Logd(TAG, "LaunchedEffect $index ${vm.isSelected} ${selected.size}") } Column { - MainRow(vm, index) + var yOffset by remember { mutableStateOf(0f) } + var draggedIndex by remember { mutableStateOf(null) } + MainRow(vm, index, isBeingDragged = draggedIndex == index, + yOffset = if (draggedIndex == index) yOffset else 0f, + onDragStart = { draggedIndex = index }, + onDrag = { delta -> yOffset += delta }, + onDragEnd = { + draggedIndex?.let { startIndex -> + val newIndex = (startIndex + (yOffset / rowHeightPx).toInt()).coerceIn(0, vms.lastIndex) + Logd(TAG, "onDragEnd draggedIndex: $draggedIndex newIndex: $newIndex") + if (newIndex != startIndex) { + dragCB?.invoke(startIndex, newIndex) + val item = vms.removeAt(startIndex) + vms.add(newIndex, item) + } + } + draggedIndex = null + yOffset = 0f + }) ProgressRow(vm, index) } } @@ -842,9 +870,7 @@ fun EpisodeLazyColumn(activity: MainActivity, vms: List, feed: Feed? modifier = Modifier.width(35.dp).height(35.dp).padding(end = 10.dp) .clickable(onClick = { selected.clear() - for (i in 0..longPressIndex) { - selected.add(vms[i].episode) - } + for (i in 0..longPressIndex) selected.add(vms[i].episode) selectedSize = selected.size Logd(TAG, "selectedIds: ${selected.size}") })) @@ -852,9 +878,7 @@ fun EpisodeLazyColumn(activity: MainActivity, vms: List, feed: Feed? modifier = Modifier.width(35.dp).height(35.dp).padding(end = 10.dp) .clickable(onClick = { selected.clear() - for (i in longPressIndex.., feed: Feed? .clickable(onClick = { if (selectedSize != vms.size) { selected.clear() - for (vm in vms) { - selected.add(vm.episode) - } + for (vm in vms) selected.add(vm.episode) selectAllRes = R.drawable.ic_select_none } else { selected.clear() @@ -924,8 +946,7 @@ fun ConfirmAddYoutubeEpisode(sharedUrls: List, showDialog: Boolean, onDi }) { Text("Confirm") } - } else CircularProgressIndicator(progress = { 0.6f }, strokeWidth = 4.dp, - modifier = Modifier.padding(start = 20.dp, end = 20.dp).width(30.dp).height(30.dp)) + } else CircularProgressIndicator(progress = { 0.6f }, strokeWidth = 4.dp, modifier = Modifier.padding(start = 20.dp, end = 20.dp).width(30.dp).height(30.dp)) } } } @@ -936,7 +957,6 @@ fun ConfirmAddYoutubeEpisode(sharedUrls: List, showDialog: Boolean, onDi fun EpisodesFilterDialog(filter: EpisodeFilter? = null, filtersDisabled: MutableSet = mutableSetOf(), onDismissRequest: () -> Unit, onFilterChanged: (Set) -> Unit) { val filterValues: MutableSet = mutableSetOf() - Dialog(properties = DialogProperties(usePlatformDefaultWidth = false), onDismissRequest = { onDismissRequest() }) { val dialogWindowProvider = LocalView.current.parent as? DialogWindowProvider dialogWindowProvider?.window?.let { window -> @@ -1001,21 +1021,74 @@ fun EpisodesFilterDialog(filter: EpisodeFilter? = null, filtersDisabled: Mutable } } else { Column(modifier = Modifier.padding(start = 5.dp, bottom = 2.dp).fillMaxWidth()) { - Text(stringResource(item.nameRes) + " :", fontWeight = FontWeight.Bold, style = MaterialTheme.typography.headlineSmall, color = textColor) - NonlazyGrid(columns = 3, itemCount = item.values.size) { index -> - var selected by remember { mutableStateOf(false) } - if (selectNone) selected = false + val selectedList = remember { MutableList(item.values.size) { mutableStateOf(false)} } + var expandRow by remember { mutableStateOf(false) } + Row { + Text(stringResource(item.nameRes) + ".. :", fontWeight = FontWeight.Bold, style = MaterialTheme.typography.headlineSmall, color = textColor, modifier = Modifier.clickable { + expandRow = !expandRow + }) + var lowerSelected by remember { mutableStateOf(false) } + var higherSelected by remember { mutableStateOf(false) } + Spacer(Modifier.weight(1f)) + if (expandRow) Text("<<<", color = if (lowerSelected) Color.Green else textColor, style = MaterialTheme.typography.headlineSmall, modifier = Modifier.clickable { + val hIndex = selectedList.indexOfLast { it.value } + if (hIndex < 0) return@clickable + if (!lowerSelected) { + for (i in 0..hIndex) selectedList[i].value = true + } else { + for (i in 0..hIndex) selectedList[i].value = false + selectedList[hIndex].value = true + } + lowerSelected = !lowerSelected + for (i in item.values.indices) { + if (selectedList[i].value) filterValues.add(item.values[i].filterId) + else filterValues.remove(item.values[i].filterId) + } + onFilterChanged(filterValues) + }) + Spacer(Modifier.weight(1f)) + if (expandRow) Text("X", color = textColor, style = MaterialTheme.typography.headlineSmall, modifier = Modifier.clickable { + lowerSelected = false + higherSelected = false + for (i in item.values.indices) { + selectedList[i].value = false + filterValues.remove(item.values[i].filterId) + } + onFilterChanged(filterValues) + }) + Spacer(Modifier.weight(1f)) + if (expandRow) Text(">>>", color = if (higherSelected) Color.Green else textColor, style = MaterialTheme.typography.headlineSmall, modifier = Modifier.clickable { + val lIndex = selectedList.indexOfFirst { it.value } + if (lIndex < 0) return@clickable + if (!higherSelected) { + for (i in lIndex..item.values.size - 1) selectedList[i].value = true + } else { + for (i in lIndex..item.values.size - 1) selectedList[i].value = false + selectedList[lIndex].value = true + } + higherSelected = !higherSelected + for (i in item.values.indices) { + if (selectedList[i].value) filterValues.add(item.values[i].filterId) + else filterValues.remove(item.values[i].filterId) + } + onFilterChanged(filterValues) + }) + Spacer(Modifier.weight(1f)) + } + if (expandRow) NonlazyGrid(columns = 3, itemCount = item.values.size) { index -> + if (selectNone) selectedList[index].value = false LaunchedEffect(Unit) { if (filter != null) { - if (item.values[index].filterId in filter.properties) selected = true + if (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 (selected) Color.Green else textColor), + 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), onClick = { selectNone = false - selected = !selected - if (selected) filterValues.add(item.values[index].filterId) + selectedList[index].value = !selectedList[index].value + if (selectedList[index].value) filterValues.add(item.values[index].filterId) else filterValues.remove(item.values[index].filterId) onFilterChanged(filterValues) }, 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 aa9cda95..85e88184 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 @@ -92,11 +92,11 @@ abstract class BaseEpisodesFragment : Fragment(), Toolbar.OnMenuItemClickListene EpisodeLazyColumn( activity as MainActivity, vms = vms, leftSwipeCB = { - if (leftActionState.value == NoActionSwipeAction()) swipeActions.showDialog() + if (leftActionState.value is NoActionSwipeAction) swipeActions.showDialog() else leftActionState.value.performAction(it, this@BaseEpisodesFragment, swipeActions.filter ?: EpisodeFilter()) }, rightSwipeCB = { - if (rightActionState.value == NoActionSwipeAction()) swipeActions.showDialog() + if (rightActionState.value is NoActionSwipeAction) swipeActions.showDialog() else rightActionState.value.performAction(it, this@BaseEpisodesFragment, swipeActions.filter ?: EpisodeFilter()) }, ) diff --git a/app/src/main/kotlin/ac/mdiq/podcini/ui/fragment/DownloadsFragment.kt b/app/src/main/kotlin/ac/mdiq/podcini/ui/fragment/DownloadsFragment.kt index 71056820..d12a4cf0 100644 --- a/app/src/main/kotlin/ac/mdiq/podcini/ui/fragment/DownloadsFragment.kt +++ b/app/src/main/kotlin/ac/mdiq/podcini/ui/fragment/DownloadsFragment.kt @@ -111,11 +111,11 @@ import java.util.* InforBar(infoBarText, leftAction = leftActionState, rightAction = rightActionState, actionConfig = {swipeActions.showDialog()}) EpisodeLazyColumn(activity as MainActivity, vms = vms, leftSwipeCB = { - if (leftActionState.value == NoActionSwipeAction()) swipeActions.showDialog() + if (leftActionState.value is NoActionSwipeAction) swipeActions.showDialog() else leftActionState.value.performAction(it, this@DownloadsFragment, swipeActions.filter ?: EpisodeFilter()) }, rightSwipeCB = { - if (rightActionState.value == NoActionSwipeAction()) swipeActions.showDialog() + if (rightActionState.value is NoActionSwipeAction) swipeActions.showDialog() else rightActionState.value.performAction(it, this@DownloadsFragment, swipeActions.filter ?: EpisodeFilter()) }, actionButton_ = { DeleteActionButton(it) }) diff --git a/app/src/main/kotlin/ac/mdiq/podcini/ui/fragment/FeedEpisodesFragment.kt b/app/src/main/kotlin/ac/mdiq/podcini/ui/fragment/FeedEpisodesFragment.kt index 706629e1..87259efa 100644 --- a/app/src/main/kotlin/ac/mdiq/podcini/ui/fragment/FeedEpisodesFragment.kt +++ b/app/src/main/kotlin/ac/mdiq/podcini/ui/fragment/FeedEpisodesFragment.kt @@ -190,11 +190,12 @@ import java.util.concurrent.Semaphore EpisodeLazyColumn(activity as MainActivity, vms = vms, feed = feed, refreshCB = { FeedUpdateManager.runOnceOrAsk(requireContext(), feed) }, leftSwipeCB = { - if (leftActionState.value == NoActionSwipeAction()) swipeActions.showDialog() + if (leftActionState.value is NoActionSwipeAction) swipeActions.showDialog() else leftActionState.value.performAction(it, this@FeedEpisodesFragment, swipeActions.filter ?: EpisodeFilter()) }, rightSwipeCB = { - if (rightActionState.value == NoActionSwipeAction()) swipeActions.showDialog() + Logd(TAG, "rightActionState: ${rightActionState.value.getId()}") + if (rightActionState.value is NoActionSwipeAction) swipeActions.showDialog() else rightActionState.value.performAction(it, this@FeedEpisodesFragment, swipeActions.filter ?: EpisodeFilter()) }, ) @@ -402,7 +403,7 @@ import java.util.concurrent.Semaphore } when (item.itemId) { R.id.visit_website_item -> if (feed!!.link != null) IntentUtils.openInBrowser(requireContext(), feed!!.link!!) - R.id.share_feed -> ShareUtils.shareFeedLink(requireContext(), feed!!) + R.id.share_feed -> ShareUtils.shareFeedLinkNew(requireContext(), feed!!) R.id.refresh_feed -> FeedUpdateManager.runOnceOrAsk(requireContext(), feed) R.id.refresh_complete_item -> { Thread { diff --git a/app/src/main/kotlin/ac/mdiq/podcini/ui/fragment/FeedInfoFragment.kt b/app/src/main/kotlin/ac/mdiq/podcini/ui/fragment/FeedInfoFragment.kt index 2327dfbd..e5d43c08 100644 --- a/app/src/main/kotlin/ac/mdiq/podcini/ui/fragment/FeedInfoFragment.kt +++ b/app/src/main/kotlin/ac/mdiq/podcini/ui/fragment/FeedInfoFragment.kt @@ -347,7 +347,7 @@ class FeedInfoFragment : Fragment(), Toolbar.OnMenuItemClickListener { override fun onMenuItemClick(item: MenuItem): Boolean { when (item.itemId) { R.id.visit_website_item -> if (feed.link != null) IntentUtils.openInBrowser(requireContext(), feed.link!!) - R.id.share_item -> ShareUtils.shareFeedLink(requireContext(), feed) + R.id.share_item -> ShareUtils.shareFeedLinkNew(requireContext(), feed) R.id.reconnect_local_folder -> { val alert = MaterialAlertDialogBuilder(requireContext()) alert.setMessage(R.string.reconnect_local_folder_warning) 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 a424e76d..e81a41eb 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 @@ -11,9 +11,10 @@ import ac.mdiq.podcini.playback.service.PlaybackService import ac.mdiq.podcini.playback.service.PlaybackService.Companion.mediaBrowser import ac.mdiq.podcini.playback.service.PlaybackService.Companion.playbackService import ac.mdiq.podcini.preferences.UserPreferences +import ac.mdiq.podcini.preferences.UserPreferences.appPrefs import ac.mdiq.podcini.storage.database.Queues.clearQueue import ac.mdiq.podcini.storage.database.Queues.isQueueKeepSorted -import ac.mdiq.podcini.storage.database.Queues.isQueueLocked +import ac.mdiq.podcini.storage.database.Queues.moveInQueue import ac.mdiq.podcini.storage.database.Queues.queueKeepSortedOrder import ac.mdiq.podcini.storage.database.RealmDB.realm import ac.mdiq.podcini.storage.database.RealmDB.runOnIOScope @@ -33,7 +34,6 @@ import ac.mdiq.podcini.ui.activity.MainActivity import ac.mdiq.podcini.ui.compose.* import ac.mdiq.podcini.ui.dialog.ConfirmationDialog import ac.mdiq.podcini.ui.dialog.EpisodeSortDialog -import ac.mdiq.podcini.ui.utils.EmptyViewHandler import ac.mdiq.podcini.util.EventFlow import ac.mdiq.podcini.util.FlowEvent import ac.mdiq.podcini.util.Logd @@ -88,7 +88,7 @@ import kotlin.math.max private var _binding: ComposeFragmentBinding? = null private val binding get() = _binding!! - private lateinit var emptyViewHandler: EmptyViewHandler +// private lateinit var emptyViewHandler: EmptyViewHandler private lateinit var toolbar: MaterialToolbar private lateinit var swipeActions: SwipeActions private lateinit var swipeActionsBin: SwipeActions @@ -102,6 +102,8 @@ import kotlin.math.max private var leftActionStateBin = mutableStateOf(NoActionSwipeAction()) private var rightActionStateBin = mutableStateOf(NoActionSwipeAction()) + var isQueueLocked = appPrefs.getBoolean(UserPreferences.Prefs.prefQueueLocked.name, true) + private lateinit var spinnerLayout: View private lateinit var queueNames: Array private lateinit var spinnerTexts: MutableList @@ -115,7 +117,7 @@ import kotlin.math.max private var showBin by mutableStateOf(false) -// private var dragDropEnabled: Boolean = !(isQueueKeepSorted || isQueueLocked) + private var dragDropEnabled by mutableStateOf(!(isQueueKeepSorted || isQueueLocked)) private lateinit var browserFuture: ListenableFuture @@ -190,11 +192,11 @@ import kotlin.math.max Column { InforBar(infoBarText, leftAction = leftActionStateBin, rightAction = rightActionStateBin, actionConfig = { swipeActionsBin.showDialog() }) val leftCB = { episode: Episode -> - if (leftActionStateBin.value == NoActionSwipeAction()) swipeActionsBin.showDialog() + if (leftActionStateBin.value is NoActionSwipeAction) swipeActionsBin.showDialog() else leftActionStateBin.value.performAction(episode, this@QueuesFragment, swipeActionsBin.filter ?: EpisodeFilter()) } val rightCB = { episode: Episode -> - if (rightActionStateBin.value == NoActionSwipeAction()) swipeActionsBin.showDialog() + if (rightActionStateBin.value is NoActionSwipeAction) swipeActionsBin.showDialog() else rightActionStateBin.value.performAction(episode, this@QueuesFragment, swipeActionsBin.filter ?: EpisodeFilter()) } EpisodeLazyColumn(activity as MainActivity, vms = vms, leftSwipeCB = { leftCB(it) }, rightSwipeCB = { rightCB(it) }) @@ -203,14 +205,16 @@ import kotlin.math.max Column { InforBar(infoBarText, leftAction = leftActionState, rightAction = rightActionState, actionConfig = { swipeActions.showDialog() }) val leftCB = { episode: Episode -> - if (leftActionState.value == NoActionSwipeAction()) swipeActions.showDialog() + if (leftActionState.value is NoActionSwipeAction) swipeActions.showDialog() else leftActionState.value.performAction(episode, this@QueuesFragment, swipeActions.filter ?: EpisodeFilter()) } val rightCB = { episode: Episode -> - if (rightActionState.value == NoActionSwipeAction()) swipeActions.showDialog() + if (rightActionState.value is NoActionSwipeAction) swipeActions.showDialog() else rightActionState.value.performAction(episode, this@QueuesFragment, swipeActions.filter ?: EpisodeFilter()) } - EpisodeLazyColumn(activity as MainActivity, vms = vms, leftSwipeCB = { leftCB(it) }, rightSwipeCB = { rightCB(it) }) + EpisodeLazyColumn(activity as MainActivity, vms = vms, + isDraggable = dragDropEnabled, dragCB = { iFrom, iTo -> moveInQueue(iFrom, iTo, true) }, + leftSwipeCB = { leftCB(it) }, rightSwipeCB = { rightCB(it) }) } } } @@ -219,13 +223,6 @@ import kotlin.math.max lifecycle.addObserver(swipeActions) refreshSwipeTelltale() - emptyViewHandler = EmptyViewHandler(requireContext()) -// emptyViewHandler.attachToRecyclerView(recyclerView) - emptyViewHandler.setIcon(R.drawable.ic_playlist_play) - emptyViewHandler.setTitle(R.string.no_items_header_label) - emptyViewHandler.setMessage(R.string.no_items_label) -// emptyViewHandler.updateAdapter(adapter) - return binding.root } @@ -612,10 +609,10 @@ import kotlin.math.max @UnstableApi private fun toggleQueueLock() { val isLocked: Boolean = isQueueLocked - if (isLocked) setQueueLocked(false) + if (isLocked) setQueueLock(false) else { val shouldShowLockWarning: Boolean = prefs!!.getBoolean(PREF_SHOW_LOCK_WARNING, true) - if (!shouldShowLockWarning) setQueueLocked(true) + if (!shouldShowLockWarning) setQueueLock(true) else { val builder = MaterialAlertDialogBuilder(requireContext()) builder.setTitle(R.string.lock_queue) @@ -628,7 +625,7 @@ import kotlin.math.max builder.setPositiveButton(R.string.lock_queue) { _: DialogInterface?, _: Int -> prefs!!.edit().putBoolean(PREF_SHOW_LOCK_WARNING, !checkDoNotShowAgain.isChecked).apply() - setQueueLocked(true) + setQueueLock(true) } builder.setNegativeButton(R.string.cancel_label, null) builder.show() @@ -636,8 +633,10 @@ import kotlin.math.max } } - @UnstableApi private fun setQueueLocked(locked: Boolean) { + @UnstableApi private fun setQueueLock(locked: Boolean) { isQueueLocked = locked + appPrefs.edit().putBoolean(UserPreferences.Prefs.prefQueueLocked.name, locked).apply() + dragDropEnabled = !(isQueueKeepSorted || isQueueLocked) refreshMenuItems() // adapter?.updateDragDropEnabled() @@ -677,7 +676,7 @@ import kotlin.math.max loadItemsRunning = true Logd(TAG, "loadCurQueue() called ${curQueue.name}") while (curQueue.name.isEmpty()) runBlocking { delay(100) } - if (queueItems.isNotEmpty()) emptyViewHandler.hide() +// if (queueItems.isNotEmpty()) emptyViewHandler.hide() queueItems.clear() vms.clear() if (showBin) queueItems.addAll(realm.query(Episode::class, "id IN $0", curQueue.idsBinList) @@ -748,7 +747,6 @@ import kotlin.math.max val TAG = QueuesFragment::class.simpleName ?: "Anonymous" private const val KEY_UP_ARROW = "up_arrow" - private const val PREFS = "QueueFragment" private const val PREF_SHOW_LOCK_WARNING = "show_lock_warning" 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 a4edfe1e..be975542 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 @@ -36,10 +36,7 @@ import androidx.compose.foundation.combinedClickable import androidx.compose.foundation.layout.* import androidx.compose.foundation.lazy.LazyRow import androidx.compose.foundation.lazy.rememberLazyListState -import androidx.compose.material3.Checkbox -import androidx.compose.material3.Icon -import androidx.compose.material3.MaterialTheme -import androidx.compose.material3.Text +import androidx.compose.material3.* import androidx.compose.runtime.* import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier @@ -272,19 +269,30 @@ class SearchFragment : Fragment() { @Composable fun CriteriaList() { val textColor = MaterialTheme.colorScheme.onSurface - NonlazyGrid(columns = 2, itemCount = SearchBy.entries.size) { index -> - val c = SearchBy.entries[index] - Row(verticalAlignment = Alignment.CenterVertically, modifier = Modifier.padding(start = 10.dp, end = 10.dp)) { - var isChecked by remember { mutableStateOf(true) } - Checkbox( - checked = isChecked, - onCheckedChange = { newValue -> - c.selected = newValue - isChecked = newValue - } - ) - Spacer(modifier = Modifier.width(2.dp)) - Text(stringResource(c.nameRes), color = textColor) + 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)) + } + } + if (showGrid) NonlazyGrid(columns = 2, itemCount = SearchBy.entries.size) { index -> + val c = SearchBy.entries[index] + Row(verticalAlignment = Alignment.CenterVertically, modifier = Modifier.padding(start = 10.dp, end = 10.dp)) { + var isChecked by remember { mutableStateOf(true) } + Checkbox( + checked = isChecked, + onCheckedChange = { newValue -> + c.selected = newValue + isChecked = newValue + } + ) + Spacer(modifier = Modifier.width(2.dp)) + Text(stringResource(c.nameRes), color = textColor) + } } } } 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 0824e530..b9a3da26 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 @@ -1155,21 +1155,73 @@ class SubscriptionsFragment : Fragment(), Toolbar.OnMenuItemClickListener { } } else { Column(modifier = Modifier.padding(start = 5.dp, bottom = 2.dp).fillMaxWidth()) { - Text(stringResource(item.nameRes) + " :", fontWeight = FontWeight.Bold, style = MaterialTheme.typography.headlineSmall, color = textColor) - NonlazyGrid(columns = 3, itemCount = item.values.size) { index -> - var selected by remember { mutableStateOf(false) } - if (selectNone) selected = false + val selectedList = remember { MutableList(item.values.size) { mutableStateOf(false)} } + var expandRow by remember { mutableStateOf(false) } + Row { + Text(stringResource(item.nameRes) + ".. :", fontWeight = FontWeight.Bold, style = MaterialTheme.typography.headlineSmall, color = textColor, modifier = Modifier.clickable { + expandRow = !expandRow + }) + var lowerSelected by remember { mutableStateOf(false) } + var higherSelected by remember { mutableStateOf(false) } + Spacer(Modifier.weight(1f)) + if (expandRow) Text("<<<", color = if (lowerSelected) Color.Green else textColor, style = MaterialTheme.typography.headlineSmall, modifier = Modifier.clickable { + val hIndex = selectedList.indexOfLast { it.value } + if (hIndex < 0) return@clickable + if (!lowerSelected) { + for (i in 0..hIndex) selectedList[i].value = true + } else { + for (i in 0..hIndex) selectedList[i].value = false + selectedList[hIndex].value = true + } + lowerSelected = !lowerSelected + for (i in item.values.indices) { + if (selectedList[i].value) filterValues.add(item.values[i].filterId) + else filterValues.remove(item.values[i].filterId) + } + onFilterChanged(filterValues) + }) + Spacer(Modifier.weight(1f)) + if (expandRow) Text("X", color = textColor, style = MaterialTheme.typography.headlineSmall, modifier = Modifier.clickable { + lowerSelected = false + higherSelected = false + for (i in item.values.indices) { + selectedList[i].value = false + filterValues.remove(item.values[i].filterId) + } + onFilterChanged(filterValues) + }) + Spacer(Modifier.weight(1f)) + if (expandRow) Text(">>>", color = if (higherSelected) Color.Green else textColor, style = MaterialTheme.typography.headlineSmall, modifier = Modifier.clickable { + val lIndex = selectedList.indexOfFirst { it.value } + if (lIndex < 0) return@clickable + if (!higherSelected) { + for (i in lIndex..item.values.size - 1) selectedList[i].value = true + } else { + for (i in lIndex..item.values.size - 1) selectedList[i].value = false + selectedList[lIndex].value = true + } + higherSelected = !higherSelected + for (i in item.values.indices) { + if (selectedList[i].value) filterValues.add(item.values[i].filterId) + else filterValues.remove(item.values[i].filterId) + } + onFilterChanged(filterValues) + }) + Spacer(Modifier.weight(1f)) + } + if (expandRow) NonlazyGrid(columns = 3, itemCount = item.values.size) { index -> + if (selectNone) selectedList[index].value = false LaunchedEffect(Unit) { if (filter != null) { - if (item.values[index].filterId in filter.properties) selected = true + if (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 (selected) Color.Green else textColor), + border = BorderStroke(2.dp, if (selectedList[index].value) Color.Green else textColor), onClick = { selectNone = false - selected = !selected - if (selected) filterValues.add(item.values[index].filterId) + selectedList[index].value = !selectedList[index].value + if (selectedList[index].value) filterValues.add(item.values[index].filterId) else filterValues.remove(item.values[index].filterId) onFilterChanged(filterValues) }, diff --git a/app/src/main/kotlin/ac/mdiq/podcini/util/ShareUtils.kt b/app/src/main/kotlin/ac/mdiq/podcini/util/ShareUtils.kt index e8562e47..9206c390 100644 --- a/app/src/main/kotlin/ac/mdiq/podcini/util/ShareUtils.kt +++ b/app/src/main/kotlin/ac/mdiq/podcini/util/ShareUtils.kt @@ -47,6 +47,10 @@ object ShareUtils { shareLink(context, text) } + fun shareFeedLinkNew(context: Context, feed: Feed) { + shareLink(context, feed.downloadUrl?:"") + } + @JvmStatic fun hasLinkToShare(item: Episode?): Boolean { return item?.getLinkWithFallback() != null diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index fd8130d0..56a1045e 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -419,6 +419,7 @@ Duration Episode title Podcast title + Show criteria Title Author Random diff --git a/changelog.md b/changelog.md index bebf4dd2..f3c8aa55 100644 --- a/changelog.md +++ b/changelog.md @@ -1,3 +1,16 @@ +# 6.12.6 + +* in SearchFragment, made search criteria options are togglable, and added back search online option +* further enhanced filtering routines + * categories with multi-selections are expandable/collapsable + * once expanded, added three icons: <<<, X, >>> + * tapping on <<< will select all lower items, >>> all higher items, X to clear selection in the category +* manual sorting of Queues is back. + * The way to enable/disable it, uncheck/check "Lock queue" in menu + * if "Lock queue" doesn't appear, top "Sort" in the menu and then, uncheck "Keep sorted" in the popup +* in episodes lists, swipe dialog shows up when the swipe has not been configured +* changed the info shared from FeedInfo and FeedEpisodes, now it shares simply the download url of the feed + # 6.12.5 * fixed a long-standing issue in the play apk where rewind/forward buttons don't work during cast diff --git a/fastlane/metadata/android/en-US/changelogs/3020284.txt b/fastlane/metadata/android/en-US/changelogs/3020284.txt new file mode 100644 index 00000000..33eb1565 --- /dev/null +++ b/fastlane/metadata/android/en-US/changelogs/3020284.txt @@ -0,0 +1,12 @@ + Version 6.12.6 + +* in SearchFragment, made search criteria options are togglable, and added back search online option +* further enhanced filtering routines + * categories with multi-selections are expandable/collapsable + * once expanded, added three icons: <<<, X, >>> + * tapping on <<< will select all lower items, >>> all higher items, X to clear selection in the category +* manual sorting of Queues is back. + * The way to enable/disable it, uncheck/check "Lock queue" in menu + * if "Lock queue" doesn't appear, top "Sort" in the menu and then, uncheck "Keep sorted" in the popup +* in episodes lists, swipe dialog shows up when the swipe has not been configured +* changed the info shared from FeedInfo and FeedEpisodes, now it shares simply the download url of the feed