6.12.2 commit

This commit is contained in:
Xilin Jia 2024-10-23 22:29:58 +01:00
parent 79c1bf43f9
commit d3ca132b7c
14 changed files with 203 additions and 223 deletions

View File

@ -31,8 +31,8 @@ android {
testApplicationId "ac.mdiq.podcini.tests"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
versionCode 3020279
versionName "6.12.1"
versionCode 3020280
versionName "6.12.2"
applicationId "ac.mdiq.podcini.R"
def commit = ""

View File

@ -45,7 +45,7 @@ abstract class ServiceStatusHandler(private val activity: FragmentActivity) {
Logd(TAG, "statusUpdate onReceive doing updates")
MediaPlayerBase.status = info.playerStatus
prevStatus = MediaPlayerBase.status
curMedia = info.playable
// curMedia = info.playable
handleStatus()
}
} else {
@ -177,7 +177,7 @@ abstract class ServiceStatusHandler(private val activity: FragmentActivity) {
Logd(TAG, "Querying service info")
if (playbackService != null && PlaybackService.mPlayerInfo != null) {
MediaPlayerBase.status = PlaybackService.mPlayerInfo!!.playerStatus
curMedia = PlaybackService.mPlayerInfo!!.playable
// curMedia = PlaybackService.mPlayerInfo!!.playable
// make sure that new media is loaded if it's available
mediaInfoLoaded = false
handleStatus()

View File

@ -271,11 +271,13 @@ abstract class MediaPlayerBase protected constructor(protected val context: Cont
Log.d(TAG, "${this.javaClass.simpleName}: Setting player status to $newStatus")
this.oldStatus = status
status = newStatus
if (newMedia != null) setPlayable(newMedia)
if (newMedia != null && newStatus != PlayerStatus.INDETERMINATE) {
when {
oldStatus == PlayerStatus.PLAYING && newStatus != PlayerStatus.PLAYING -> callback.onPlaybackPause(newMedia, position)
oldStatus != PlayerStatus.PLAYING && newStatus == PlayerStatus.PLAYING -> callback.onPlaybackStart(newMedia, position)
if (newMedia != null) {
setPlayable(newMedia)
if (newStatus != PlayerStatus.INDETERMINATE) {
when {
oldStatus == PlayerStatus.PLAYING && newStatus != PlayerStatus.PLAYING -> callback.onPlaybackPause(newMedia, position)
oldStatus != PlayerStatus.PLAYING && newStatus == PlayerStatus.PLAYING -> callback.onPlaybackStart(newMedia, position)
}
}
}
callback.statusChanged(MediaPlayerInfo(oldStatus, status, curMedia))

View File

@ -389,15 +389,23 @@ class PlaybackService : MediaLibraryService() {
}
}
if (item != null) {
// fun shouldSkipKeepEpisode(): Boolean {
// return appPrefs.getBoolean(UserPreferences.Prefs.prefSkipKeepsEpisode.name, true)
// }
// fun shouldFavoriteKeepEpisode(): Boolean {
// return appPrefs.getBoolean(UserPreferences.Prefs.prefFavoriteKeepsEpisode.name, true)
// }
runOnIOScope {
if (ended || smartMarkAsPlayed || autoSkipped || (skipped && !shouldSkipKeepEpisode())) {
val shouldSkipKeepEpisode = appPrefs.getBoolean(UserPreferences.Prefs.prefSkipKeepsEpisode.name, true)
val shouldFavoriteKeepEpisode = appPrefs.getBoolean(UserPreferences.Prefs.prefFavoriteKeepsEpisode.name, true)
if (ended || smartMarkAsPlayed || autoSkipped || (skipped && !shouldSkipKeepEpisode)) {
Logd(TAG, "onPostPlayback ended: $ended smartMarkAsPlayed: $smartMarkAsPlayed autoSkipped: $autoSkipped skipped: $skipped")
// only mark the item as played if we're not keeping it anyways
item = setPlayStateSync(PlayState.PLAYED.code, ended || (skipped && smartMarkAsPlayed), item!!)
val action = item?.feed?.preferences?.autoDeleteAction
val shouldAutoDelete = (action == AutoDeleteAction.ALWAYS ||
(action == AutoDeleteAction.GLOBAL && item?.feed != null && shouldAutoDeleteItem(item!!.feed!!)))
if (playable is EpisodeMedia && shouldAutoDelete && (item?.isFavorite != true || !shouldFavoriteKeepEpisode())) {
if (playable is EpisodeMedia && shouldAutoDelete && (item?.isFavorite != true || !shouldFavoriteKeepEpisode)) {
item = deleteMediaSync(this@PlaybackService, item!!)
if (shouldDeleteRemoveFromQueue()) removeFromQueueSync(null, item!!)
}
@ -407,14 +415,6 @@ class PlaybackService : MediaLibraryService() {
}
}
fun shouldSkipKeepEpisode(): Boolean {
return appPrefs.getBoolean(UserPreferences.Prefs.prefSkipKeepsEpisode.name, true)
}
fun shouldFavoriteKeepEpisode(): Boolean {
return appPrefs.getBoolean(UserPreferences.Prefs.prefFavoriteKeepsEpisode.name, true)
}
override fun onPlaybackStart(playable: Playable, position: Int) {
Logd(TAG, "onPlaybackStart position: $position")
taskManager.startWidgetUpdater()
@ -747,12 +747,10 @@ class PlaybackService : MediaLibraryService() {
override fun onDestroy() {
Logd(TAG, "Service is about to be destroyed")
playbackService = null
isRunning = false
currentMediaType = MediaType.UNKNOWN
castStateListener.destroy()
currentitem = null
LocalMediaPlayer.cleanup()
@ -1967,8 +1965,8 @@ class PlaybackService : MediaLibraryService() {
}
override fun onIsPlayingChanged(isPlaying: Boolean) {
// val stat = if (isPlaying) PlayerStatus.PLAYING else PlayerStatus.PAUSED
// TODO: test
val stat = if (isPlaying) PlayerStatus.PLAYING else PlayerStatus.INDETERMINATE
// TODO: test: changing PAUSED to STOPPED or INDETERMINATE makes resume not possible if interrupted
val stat = if (isPlaying) PlayerStatus.PLAYING else PlayerStatus.PAUSED
setPlayerStatus(stat, curMedia)
Logd(TAG, "onIsPlayingChanged $isPlaying")
}

View File

@ -20,6 +20,8 @@ class EpisodeFilter(vararg properties: String) : Serializable {
val showNotAutoDownloadable: Boolean = hasProperty(States.not_auto_downloadable.name)
val showHasMedia: Boolean = hasProperty(States.has_media.name)
val showNoMedia: Boolean = hasProperty(States.no_media.name)
val showHasComments: Boolean = hasProperty(States.has_comments.name)
val showNoComments: Boolean = hasProperty(States.no_comments.name)
val showIsFavorite: Boolean = hasProperty(States.is_favorite.name)
val showNotFavorite: Boolean = hasProperty(States.not_favorite.name)
@ -48,6 +50,8 @@ class EpisodeFilter(vararg properties: String) : Serializable {
showNotAutoDownloadable && item.isAutoDownloadEnabled -> return false
showHasMedia && item.media == null -> return false
showNoMedia && item.media != null -> return false
showHasComments && item.comment.isEmpty() -> return false
showNoComments && item.comment.isNotEmpty() -> return false
showIsFavorite && !item.isFavorite -> return false
showNotFavorite && item.isFavorite -> return false
showQueued && !inAnyQueue(item) -> return false
@ -69,7 +73,7 @@ class EpisodeFilter(vararg properties: String) : Serializable {
val statements: MutableList<String> = ArrayList()
when {
showPlayed -> statements.add("playState >= ${PlayState.PLAYED.code}")
showUnplayed -> statements.add(" playState < ${PlayState.PLAYED.code}> ") // Match "New" items (read = -1) as well
showUnplayed -> statements.add(" playState < ${PlayState.PLAYED.code} ") // Match "New" items (read = -1) as well
showNew -> statements.add("playState == -1 ")
}
when {
@ -93,8 +97,12 @@ class EpisodeFilter(vararg properties: String) : Serializable {
showNoMedia -> statements.add("media == nil ")
}
when {
showIsFavorite -> statements.add("isFavorite == true ")
showNotFavorite -> statements.add("isFavorite == false ")
showHasComments -> statements.add(" comment != '' ")
showNoComments -> statements.add(" comment == '' ")
}
when {
showIsFavorite -> statements.add("rating == ${Rating.FAVORITE.code} ")
showNotFavorite -> statements.add("rating != ${Rating.FAVORITE.code} ")
}
if (statements.isEmpty()) return "id > 0"
@ -120,6 +128,8 @@ class EpisodeFilter(vararg properties: String) : Serializable {
not_favorite,
has_media,
no_media,
has_comments,
no_comments,
queued,
not_queued,
downloaded,

View File

@ -11,6 +11,8 @@ class FeedFilter(vararg properties: String) : Serializable {
val showNotKeepUpdated: Boolean = hasProperty(States.not_keepUpdated.name)
val showGlobalPlaySpeed: Boolean = hasProperty(States.global_playSpeed.name)
val showCustomPlaySpeed: Boolean = hasProperty(States.custom_playSpeed.name)
val showHasComments: Boolean = hasProperty(States.has_comments.name)
val showNoComments: Boolean = hasProperty(States.no_comments.name)
val showHasSkips: Boolean = hasProperty(States.has_skips.name)
val showNoSkips: Boolean = hasProperty(States.no_skips.name)
val showAlwaysAutoDelete: Boolean = hasProperty(States.always_auto_delete.name)
@ -36,6 +38,8 @@ class FeedFilter(vararg properties: String) : Serializable {
showNotKeepUpdated && feed.preferences?.keepUpdated != false -> return false
showGlobalPlaySpeed && feed.preferences?.playSpeed != SPEED_USE_GLOBAL -> return false
showCustomPlaySpeed && feed.preferences?.playSpeed == SPEED_USE_GLOBAL -> return false
showHasComments && feed.comment.isEmpty() -> return false
showNoComments && feed.comment.isEmpty() -> return false
showHasSkips && feed.preferences?.introSkip == 0 && feed.preferences?.endingSkip == 0 -> return false
showNoSkips && (feed.preferences?.introSkip != 0 || feed.preferences?.endingSkip != 0) -> return false
showAlwaysAutoDelete && feed.preferences?.autoDeleteAction != FeedPreferences.AutoDeleteAction.ALWAYS -> return false
@ -60,6 +64,10 @@ class FeedFilter(vararg properties: String) : Serializable {
showHasSkips -> statements.add(" preferences.introSkip != 0 OR preferences.endingSkip != 0 ")
showNoSkips -> statements.add(" preferences.introSkip == 0 AND preferences.endingSkip == 0 ")
}
when {
showHasComments -> statements.add(" comment != '' ")
showNoComments -> statements.add(" comment == '' ")
}
when {
showAlwaysAutoDelete -> statements.add(" preferences.autoDelete == ${FeedPreferences.AutoDeleteAction.ALWAYS.code} ")
showNeverAutoDelete -> statements.add(" preferences.playSpeed == ${FeedPreferences.AutoDeleteAction.NEVER.code} ")
@ -89,6 +97,8 @@ class FeedFilter(vararg properties: String) : Serializable {
custom_playSpeed,
has_skips,
no_skips,
has_comments,
no_comments,
// global_auto_delete,
always_auto_delete,
never_auto_delete,

View File

@ -1,92 +1,99 @@
package ac.mdiq.podcini.ui.dialog
import ac.mdiq.podcini.R
import ac.mdiq.podcini.databinding.FilterDialogBinding
import ac.mdiq.podcini.databinding.FilterDialogRowBinding
import ac.mdiq.podcini.storage.model.EpisodeFilter
import ac.mdiq.podcini.storage.model.FeedFilter
import ac.mdiq.podcini.ui.compose.CustomTheme
import ac.mdiq.podcini.ui.fragment.SubscriptionsFragment.Companion.TAG
import ac.mdiq.podcini.ui.fragment.SubscriptionsFragment.FeedFilterDialog.FeedFilterGroup.ItemProperties
import ac.mdiq.podcini.util.Logd
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.Button
import android.widget.LinearLayout
import androidx.compose.foundation.BorderStroke
import androidx.compose.foundation.layout.*
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.OutlinedButton
import androidx.compose.material3.Text
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.ComposeView
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import com.google.android.material.bottomsheet.BottomSheetDialogFragment
import com.google.android.material.button.MaterialButtonToggleGroup
abstract class EpisodeFilterDialog : BottomSheetDialogFragment() {
private lateinit var rows: LinearLayout
private var _binding: FilterDialogBinding? = null
private val binding get() = _binding!!
var filter: EpisodeFilter? = null
private val buttonMap: MutableMap<String, Button> = mutableMapOf()
val filtersDisabled: MutableSet<FeedItemFilterGroup> = mutableSetOf()
private val newFilterValues: Set<String>
get() {
val newFilterValues: MutableSet<String> = HashSet()
for (i in 0 until rows.childCount) {
if (rows.getChildAt(i) !is MaterialButtonToggleGroup) continue
val group = rows.getChildAt(i) as MaterialButtonToggleGroup
if (group.checkedButtonId == View.NO_ID) continue
val tag = group.findViewById<View>(group.checkedButtonId).tag as? String ?: continue
newFilterValues.add(tag)
}
return newFilterValues
}
private val filterValues: MutableSet<String> = mutableSetOf()
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
val layout = inflater.inflate(R.layout.filter_dialog, container, false)
_binding = FilterDialogBinding.bind(layout)
rows = binding.filterRows
Logd("EpisodeFilterDialog", "fragment onCreateView")
//add filter rows
for (item in FeedItemFilterGroup.entries) {
// Logd("EpisodeFilterDialog", "FeedItemFilterGroup: ${item.values[0].filterId} ${item.values[1].filterId}")
if (item in filtersDisabled) continue
val rBinding = FilterDialogRowBinding.inflate(inflater)
rBinding.filterButton1.setOnClickListener { onFilterChanged(newFilterValues) }
rBinding.filterButton2.setOnClickListener { onFilterChanged(newFilterValues) }
rBinding.filterButton1.setText(item.values[0].displayName)
rBinding.filterButton1.tag = item.values[0].filterId
buttonMap[item.values[0].filterId] = rBinding.filterButton1
rBinding.filterButton2.setText(item.values[1].displayName)
rBinding.filterButton2.tag = item.values[1].filterId
buttonMap[item.values[1].filterId] = rBinding.filterButton2
rBinding.filterButton1.maxLines = 3
rBinding.filterButton1.isSingleLine = false
rBinding.filterButton2.maxLines = 3
rBinding.filterButton2.isSingleLine = false
rows.addView(rBinding.root, rows.childCount - 1)
}
binding.confirmFiltermenu.setOnClickListener { dismiss() }
binding.resetFiltermenu.setOnClickListener {
onFilterChanged(emptySet())
for (i in 0 until rows.childCount) {
if (rows.getChildAt(i) is MaterialButtonToggleGroup) (rows.getChildAt(i) as MaterialButtonToggleGroup).clearChecked()
}
}
if (filter != null) {
for (filterId in filter!!.values) {
if (filterId.isNotEmpty()) {
val button = buttonMap[filterId]
if (button != null) (button.parent as MaterialButtonToggleGroup).check(button.id)
val composeView = ComposeView(requireContext()).apply {
setContent {
CustomTheme(requireContext()) {
MainView()
}
}
}
return composeView
}
@Composable
fun MainView() {
val textColor = MaterialTheme.colorScheme.onSurface
Column {
for (item in FeedItemFilterGroup.entries) {
if (item in filtersDisabled) continue
Row(modifier = Modifier.padding(2.dp).fillMaxWidth(), horizontalArrangement = Arrangement.Center, verticalAlignment = Alignment.CenterVertically) {
var selectedIndex by remember { mutableStateOf(-1) }
LaunchedEffect(Unit) {
if (filter != null) {
if (item.values[0].filterId in filter!!.values) selectedIndex = 0
else if (item.values[1].filterId in filter!!.values) selectedIndex = 1
}
}
OutlinedButton(modifier = Modifier.padding(2.dp), border = BorderStroke(2.dp, if (selectedIndex != 0) textColor else Color.Green),
onClick = {
if (selectedIndex != 0) {
selectedIndex = 0
filterValues.add(item.values[0].filterId)
filterValues.remove(item.values[1].filterId)
} else {
selectedIndex = -1
filterValues.remove(item.values[0].filterId)
}
onFilterChanged(filterValues)
},
) {
Text(text = stringResource(item.values[0].displayName), color = textColor)
}
Spacer(Modifier.width(5.dp))
OutlinedButton(modifier = Modifier.padding(2.dp), border = BorderStroke(2.dp, if (selectedIndex != 1) textColor else Color.Green),
onClick = {
if (selectedIndex != 1) {
selectedIndex = 1
filterValues.add(item.values[1].filterId)
filterValues.remove(item.values[0].filterId)
} else {
selectedIndex = -1
filterValues.remove(item.values[1].filterId)
}
onFilterChanged(filterValues)
},
) {
Text(text = stringResource(item.values[1].displayName), color = textColor)
}
}
}
}
return layout
}
override fun onDestroyView() {
Logd(TAG, "onDestroyView")
_binding = null
super.onDestroyView()
}
@ -97,6 +104,7 @@ abstract class EpisodeFilterDialog : BottomSheetDialogFragment() {
PAUSED(ItemProperties(R.string.hide_paused_episodes_label, EpisodeFilter.States.paused.name), ItemProperties(R.string.not_paused, EpisodeFilter.States.not_paused.name)),
FAVORITE(ItemProperties(R.string.hide_is_favorite_label, EpisodeFilter.States.is_favorite.name), ItemProperties(R.string.not_favorite, EpisodeFilter.States.not_favorite.name)),
MEDIA(ItemProperties(R.string.has_media, EpisodeFilter.States.has_media.name), ItemProperties(R.string.no_media, EpisodeFilter.States.no_media.name)),
OPINION(ItemProperties(R.string.has_comments, EpisodeFilter.States.has_comments.name), ItemProperties(R.string.no_comments, EpisodeFilter.States.no_comments.name)),
QUEUED(ItemProperties(R.string.queued_label, EpisodeFilter.States.queued.name), ItemProperties(R.string.not_queued_label, EpisodeFilter.States.not_queued.name)),
DOWNLOADED(ItemProperties(R.string.downloaded_label, EpisodeFilter.States.downloaded.name), ItemProperties(R.string.not_downloaded_label, EpisodeFilter.States.not_downloaded.name)),
AUTO_DOWNLOADABLE(ItemProperties(R.string.auto_downloadable_label, EpisodeFilter.States.auto_downloadable.name), ItemProperties(R.string.not_auto_downloadable_label, EpisodeFilter.States.not_auto_downloadable.name));

View File

@ -6,6 +6,7 @@ import ac.mdiq.podcini.playback.PlaybackServiceStarter
import ac.mdiq.podcini.playback.ServiceStatusHandler
import ac.mdiq.podcini.playback.base.InTheatre.curEpisode
import ac.mdiq.podcini.playback.base.InTheatre.curMedia
import ac.mdiq.podcini.playback.base.InTheatre.isCurrentlyPlaying
import ac.mdiq.podcini.playback.base.MediaPlayerBase.Companion.status
import ac.mdiq.podcini.playback.base.PlayerStatus
import ac.mdiq.podcini.playback.base.VideoMode
@ -510,7 +511,7 @@ class AudioPlayerFragment : Fragment() {
fun onPositionUpdate(event: FlowEvent.PlaybackPositionEvent) {
Logd(TAG, "onPositionUpdate")
if (!playButInit && playButRes == R.drawable.ic_play_48dp && curMedia is EpisodeMedia) {
playButRes = R.drawable.ic_pause
if (isCurrentlyPlaying(curMedia as? EpisodeMedia)) playButRes = R.drawable.ic_pause
playButInit = true
}
@ -753,14 +754,15 @@ class AudioPlayerFragment : Fragment() {
Logd(TAG, "loadMediaInfo() curMedia: ${curMedia?.getIdentifier()}")
val actMain = (activity as MainActivity)
var i = 0
while (curMedia == null && i++ < 6) runBlocking { delay(500) }
// while (curMedia == null && i++ < 6) runBlocking { delay(500) }
if (curMedia == null) {
if (actMain.isPlayerVisible()) actMain.setPlayerVisible(false)
return
}
if (!actMain.isPlayerVisible()) actMain.setPlayerVisible(true)
if (!loadItemsRunning) {
loadItemsRunning = true
if (!actMain.isPlayerVisible()) actMain.setPlayerVisible(true)
// if (!actMain.isPlayerVisible()) actMain.setPlayerVisible(true)
val curMediaChanged = currentMedia == null || curMedia?.getIdentifier() != currentMedia?.getIdentifier()
if (curMedia?.getIdentifier() != currentMedia?.getIdentifier()) updateUi(curMedia!!)
if (!isCollapsed && curMediaChanged) {

View File

@ -72,6 +72,7 @@ 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.painterResource
import androidx.compose.ui.res.stringResource
@ -650,7 +651,7 @@ class SubscriptionsFragment : Fragment(), Toolbar.OnMenuItemClickListener {
Dialog(onDismissRequest = onDismissRequest) {
Surface(shape = RoundedCornerShape(16.dp)) {
Column(modifier = Modifier.padding(16.dp), verticalArrangement = Arrangement.spacedBy(16.dp)) {
for (rating in Rating.entries) {
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)
onDismissRequest()
@ -1105,77 +1106,72 @@ class SubscriptionsFragment : Fragment(), Toolbar.OnMenuItemClickListener {
}
class FeedFilterDialog : BottomSheetDialogFragment() {
private lateinit var rows: LinearLayout
private var _binding: FilterDialogBinding? = null
private val binding get() = _binding!!
var filter: FeedFilter? = null
private val buttonMap: MutableMap<String, Button> = mutableMapOf()
private val newFilterValues: Set<String>
get() {
val newFilterValues: MutableSet<String> = HashSet()
for (i in 0 until rows.childCount) {
if (rows.getChildAt(i) !is MaterialButtonToggleGroup) continue
val group = rows.getChildAt(i) as MaterialButtonToggleGroup
if (group.checkedButtonId == View.NO_ID) continue
val tag = group.findViewById<View>(group.checkedButtonId).tag as? String ?: continue
newFilterValues.add(tag)
}
return newFilterValues
}
private val filterValues: MutableSet<String> = mutableSetOf()
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
val layout = inflater.inflate(R.layout.filter_dialog, container, false)
_binding = FilterDialogBinding.bind(layout)
rows = binding.filterRows
Logd("FeedFilterDialog", "fragment onCreateView")
//add filter rows
for (item in FeedFilterGroup.entries) {
// Logd("EpisodeFilterDialog", "FeedItemFilterGroup: ${item.values[0].filterId} ${item.values[1].filterId}")
val rBinding = FilterDialogRowBinding.inflate(inflater)
// rowBinding.root.addOnButtonCheckedListener { _: MaterialButtonToggleGroup?, _: Int, _: Boolean ->
// onFilterChanged(newFilterValues)
// }
rBinding.filterButton1.setOnClickListener { onFilterChanged(newFilterValues) }
rBinding.filterButton2.setOnClickListener { onFilterChanged(newFilterValues) }
rBinding.filterButton1.setText(item.values[0].displayName)
rBinding.filterButton1.tag = item.values[0].filterId
buttonMap[item.values[0].filterId] = rBinding.filterButton1
rBinding.filterButton2.setText(item.values[1].displayName)
rBinding.filterButton2.tag = item.values[1].filterId
buttonMap[item.values[1].filterId] = rBinding.filterButton2
rBinding.filterButton1.maxLines = 3
rBinding.filterButton1.isSingleLine = false
rBinding.filterButton2.maxLines = 3
rBinding.filterButton2.isSingleLine = false
rows.addView(rBinding.root, rows.childCount - 1)
}
binding.confirmFiltermenu.setOnClickListener { dismiss() }
binding.resetFiltermenu.setOnClickListener {
onFilterChanged(emptySet())
for (i in 0 until rows.childCount) {
if (rows.getChildAt(i) is MaterialButtonToggleGroup) (rows.getChildAt(i) as MaterialButtonToggleGroup).clearChecked()
}
}
if (filter != null) {
for (filterId in filter!!.values) {
if (filterId.isNotEmpty()) {
val button = buttonMap[filterId]
if (button != null) (button.parent as MaterialButtonToggleGroup).check(button.id)
val composeView = ComposeView(requireContext()).apply {
setContent {
CustomTheme(requireContext()) {
MainView()
}
}
}
return composeView
}
@Composable
fun MainView() {
val textColor = MaterialTheme.colorScheme.onSurface
Column {
for (item in FeedFilterGroup.entries) {
Row(modifier = Modifier.padding(2.dp).fillMaxWidth(), horizontalArrangement = Arrangement.Center, verticalAlignment = Alignment.CenterVertically) {
var selectedIndex by remember { mutableStateOf(-1) }
LaunchedEffect(Unit) {
if (filter != null) {
if (item.values[0].filterId in filter!!.values) selectedIndex = 0
else if (item.values[1].filterId in filter!!.values) selectedIndex = 1
}
}
OutlinedButton(modifier = Modifier.padding(2.dp), border = BorderStroke(2.dp, if (selectedIndex != 0) textColor else Color.Green),
onClick = {
if (selectedIndex != 0) {
selectedIndex = 0
filterValues.add(item.values[0].filterId)
filterValues.remove(item.values[1].filterId)
} else {
selectedIndex = -1
filterValues.remove(item.values[0].filterId)
}
onFilterChanged(filterValues)
},
) {
Text(text = stringResource(item.values[0].displayName), color = textColor)
}
Spacer(Modifier.width(5.dp))
OutlinedButton(modifier = Modifier.padding(2.dp), border = BorderStroke(2.dp, if (selectedIndex != 1) textColor else Color.Green),
onClick = {
if (selectedIndex != 1) {
selectedIndex = 1
filterValues.add(item.values[1].filterId)
filterValues.remove(item.values[0].filterId)
} else {
selectedIndex = -1
filterValues.remove(item.values[1].filterId)
}
onFilterChanged(filterValues)
},
) {
Text(text = stringResource(item.values[1].displayName), color = textColor)
}
}
}
}
return layout
}
override fun onDestroyView() {
Logd(TAG, "onDestroyView")
_binding = null
// _binding = null
super.onDestroyView()
}
@ -1188,6 +1184,7 @@ class SubscriptionsFragment : Fragment(), Toolbar.OnMenuItemClickListener {
enum class FeedFilterGroup(vararg values: ItemProperties) {
KEEP_UPDATED(ItemProperties(R.string.keep_updated, FeedFilter.States.keepUpdated.name), ItemProperties(R.string.not_keep_updated, FeedFilter.States.not_keepUpdated.name)),
PLAY_SPEED(ItemProperties(R.string.global_speed, FeedFilter.States.global_playSpeed.name), ItemProperties(R.string.custom_speed, FeedFilter.States.custom_playSpeed.name)),
OPINION(ItemProperties(R.string.has_comments, FeedFilter.States.has_comments.name), ItemProperties(R.string.no_comments, FeedFilter.States.no_comments.name)),
SKIPS(ItemProperties(R.string.has_skips, FeedFilter.States.has_skips.name), ItemProperties(R.string.no_skips, FeedFilter.States.no_skips.name)),
AUTO_DELETE(ItemProperties(R.string.always_auto_delete, FeedFilter.States.always_auto_delete.name), ItemProperties(R.string.never_auto_delete, FeedFilter.States.never_auto_delete.name)),
AUTO_DOWNLOAD(ItemProperties(R.string.auto_download, FeedFilter.States.autoDownload.name), ItemProperties(R.string.not_auto_download, FeedFilter.States.not_autoDownload.name));

View File

@ -1,43 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<ScrollView
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/filter_dialog">
<LinearLayout
android:id="@+id/filter_rows"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:paddingLeft="24dp"
android:paddingTop="24dp"
android:paddingRight="24dp"
android:paddingBottom="8dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal">
<com.google.android.material.button.MaterialButton
android:id="@+id/resetFiltermenu"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="@string/reset"
style="@style/Widget.MaterialComponents.Button.TextButton" />
<com.google.android.material.button.MaterialButton
android:id="@+id/confirmFiltermenu"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="@string/confirm_label"
style="@style/Widget.MaterialComponents.Button.TextButton" />
</LinearLayout>
</LinearLayout>
</ScrollView>

View File

@ -1,25 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<com.google.android.material.button.MaterialButtonToggleGroup
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/buttonGroup"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:weightSum="2"
app:singleSelection="true">
<Button
android:id="@+id/filterButton1"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
style="@style/OutlinedButtonBetterContrast" />
<Button
android:id="@+id/filterButton2"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
style="@style/OutlinedButtonBetterContrast" />
</com.google.android.material.button.MaterialButtonToggleGroup>

View File

@ -903,6 +903,8 @@
<string name="custom_speed">Custom play speed</string>
<string name="has_skips">Skips set</string>
<string name="no_skips">No Skips set</string>
<string name="has_comments">Has commented</string>
<string name="no_comments">Not commented</string>
<string name="always_auto_delete">Always auto delete</string>
<string name="never_auto_delete">Never auto delete</string>
<string name="auto_download">Auto download enabled</string>

View File

@ -1,3 +1,13 @@
# 6.12.2
* fixed play not resuming after interruption (watch for any side effects)
* fixed incorrect initial play button on PlayerUI
* fixed startup delay when curMedia is null
* rating list in popup for Subscriptions is reversed (favorite on top)
* first migration of Episodes and Feeds filters to Jetpack Compose
* added has/no comments in the filters
* fixed some errors in Episodes filter
# 6.12.1
* fixed circular calling functions when PlayerDetailed view is open

View File

@ -0,0 +1,9 @@
Version 6.12.2
* fixed play not resuming after interruption (watch for any side effects)
* fixed incorrect initial play button on PlayerUI
* fixed startup delay when curMedia is null
* rating list in popup for Subscriptions is reversed (favorite on top)
* first migration of Episodes and Feeds filters to Jetpack Compose
* added has/no comments in the filters
* fixed some errors in Episodes filter