6.11.4 commit

This commit is contained in:
Xilin Jia 2024-10-17 14:42:23 +01:00
parent a38d7766fc
commit ac412c3906
25 changed files with 139 additions and 369 deletions

View File

@ -26,7 +26,7 @@ Apache License 2.0
[com.mikepenz:iconics-core](https://github.com/mikepenz/Android-Iconics/blob/develop/LICENSE) 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.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.ByteHamster](https://github.com/ByteHamster/SearchPreference/blob/master/LICENSE) MIT License

View File

@ -31,8 +31,8 @@ android {
testApplicationId "ac.mdiq.podcini.tests" testApplicationId "ac.mdiq.podcini.tests"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
versionCode 3020273 versionCode 3020274
versionName "6.11.3" versionName "6.11.4"
applicationId "ac.mdiq.podcini.R" applicationId "ac.mdiq.podcini.R"
def commit = "" def commit = ""
@ -240,7 +240,7 @@ dependencies {
implementation libs.google.material.typeface.outlined implementation libs.google.material.typeface.outlined
implementation libs.fontawesome.typeface implementation libs.fontawesome.typeface
implementation libs.speed.dial // implementation libs.speed.dial
implementation libs.searchpreference implementation libs.searchpreference
implementation libs.balloon implementation libs.balloon
implementation libs.recyclerviewswipedecorator implementation libs.recyclerviewswipedecorator

View File

@ -440,13 +440,11 @@ class TTSActionButton(item: Episode) : EpisodeActionButton(item) {
j++ j++
Logd(TAG, "onDone ${mediaFile.length()} $utteranceId") Logd(TAG, "onDone ${mediaFile.length()} $utteranceId")
} }
@Deprecated("Deprecated in Java") @Deprecated("Deprecated in Java")
override fun onError(utteranceId: String) { override fun onError(utteranceId: String) {
Log.e(TAG, "onError utterance error: $utteranceId") Log.e(TAG, "onError utterance error: $utteranceId")
Log.e(TAG, "onError $readerText") Log.e(TAG, "onError $readerText")
} }
override fun onError(utteranceId: String, errorCode: Int) { override fun onError(utteranceId: String, errorCode: Int) {
Log.e(TAG, "onError1 utterance error: $utteranceId $errorCode") Log.e(TAG, "onError1 utterance error: $utteranceId $errorCode")
Log.e(TAG, "onError1 $readerText") Log.e(TAG, "onError1 $readerText")
@ -504,11 +502,7 @@ class TTSActionButton(item: Episode) : EpisodeActionButton(item) {
f.delete() f.delete()
} }
FeedEpisodesFragment.ttsWorking = false FeedEpisodesFragment.ttsWorking = false
} else withContext(Dispatchers.Main) { } else withContext(Dispatchers.Main) { Toast.makeText(context, R.string.episode_has_no_content, Toast.LENGTH_LONG).show() }
Toast.makeText(context,
R.string.episode_has_no_content,
Toast.LENGTH_LONG).show()
}
item.setPlayed(false) item.setPlayed(false)
processing = 1f processing = 1f

View File

@ -600,8 +600,8 @@ open class SwipeActions(private val fragment: Fragment, private val tag: String)
} }
showPickerDialog = false showPickerDialog = false
}) { }) {
Icon(painter = painterResource(keys[index].getActionIcon()), tint = Color.Black, contentDescription = null, modifier = Modifier.width(35.dp).height(35.dp)) Icon(painter = painterResource(keys[index].getActionIcon()), tint = textColor, contentDescription = null, modifier = Modifier.width(35.dp).height(35.dp))
Text(keys[index].getTitle(context), textAlign = TextAlign.Center) Text(keys[index].getTitle(context), color = textColor, textAlign = TextAlign.Center)
} }
} }
} }

View File

@ -29,7 +29,6 @@ import ac.mdiq.podcini.ui.dialog.RatingDialog
import ac.mdiq.podcini.ui.fragment.* import ac.mdiq.podcini.ui.fragment.*
import ac.mdiq.podcini.ui.fragment.AudioPlayerFragment.Companion.media3Controller import ac.mdiq.podcini.ui.fragment.AudioPlayerFragment.Companion.media3Controller
import ac.mdiq.podcini.ui.statistics.StatisticsFragment import ac.mdiq.podcini.ui.statistics.StatisticsFragment
import ac.mdiq.podcini.ui.utils.LockableBottomSheetBehavior
import ac.mdiq.podcini.ui.utils.ThemeUtils.getDrawableFromAttr import ac.mdiq.podcini.ui.utils.ThemeUtils.getDrawableFromAttr
import ac.mdiq.podcini.ui.utils.TransitionEffect import ac.mdiq.podcini.ui.utils.TransitionEffect
import ac.mdiq.podcini.util.EventFlow import ac.mdiq.podcini.util.EventFlow
@ -57,7 +56,6 @@ import android.view.ViewGroup.MarginLayoutParams
import android.widget.EditText import android.widget.EditText
import androidx.activity.result.contract.ActivityResultContracts import androidx.activity.result.contract.ActivityResultContracts
import androidx.appcompat.app.ActionBarDrawerToggle import androidx.appcompat.app.ActionBarDrawerToggle
import androidx.compose.ui.platform.ComposeView
import androidx.core.graphics.Insets import androidx.core.graphics.Insets
import androidx.core.view.ViewCompat import androidx.core.view.ViewCompat
import androidx.core.view.WindowCompat import androidx.core.view.WindowCompat
@ -99,7 +97,7 @@ class MainActivity : CastEnabledActivity() {
private lateinit var navDrawer: View private lateinit var navDrawer: View
private lateinit var dummyView : View private lateinit var dummyView : View
private lateinit var controllerFuture: ListenableFuture<MediaController> private lateinit var controllerFuture: ListenableFuture<MediaController>
lateinit var bottomSheet: LockableBottomSheetBehavior<*> lateinit var bottomSheet: BottomSheetBehavior<*>
private set private set
private var drawerToggle: ActionBarDrawerToggle? = null private var drawerToggle: ActionBarDrawerToggle? = null
@ -239,10 +237,10 @@ class MainActivity : CastEnabledActivity() {
runOnIOScope { checkFirstLaunch() } runOnIOScope { checkFirstLaunch() }
this.bottomSheet = BottomSheetBehavior.from(audioPlayerView) as LockableBottomSheetBehavior<*> this.bottomSheet = BottomSheetBehavior.from(audioPlayerView)
this.bottomSheet.isHideable = false this.bottomSheet.isHideable = false
this.bottomSheet.isDraggable = false this.bottomSheet.isDraggable = false
this.bottomSheet.setBottomSheetCallback(bottomSheetCallback) this.bottomSheet.addBottomSheetCallback(bottomSheetCallback)
restartUpdateAlarm(this, false) restartUpdateAlarm(this, false)
runOnIOScope { SynchronizationQueueSink.syncNowIfNotSyncedRecently() } runOnIOScope { SynchronizationQueueSink.syncNowIfNotSyncedRecently() }
@ -362,6 +360,7 @@ class MainActivity : CastEnabledActivity() {
// WorkManager.getInstance(this).pruneWork() // WorkManager.getInstance(this).pruneWork()
_binding = null _binding = null
// realm.close() // realm.close()
bottomSheet.removeBottomSheetCallback(bottomSheetCallback)
drawerLayout?.removeDrawerListener(drawerToggle!!) drawerLayout?.removeDrawerListener(drawerToggle!!)
MediaController.releaseFuture(controllerFuture) MediaController.releaseFuture(controllerFuture)
super.onDestroy() super.onDestroy()
@ -380,6 +379,7 @@ class MainActivity : CastEnabledActivity() {
private fun updateInsets() { private fun updateInsets() {
setPlayerVisible(audioPlayerView.visibility == View.VISIBLE) setPlayerVisible(audioPlayerView.visibility == View.VISIBLE)
val playerHeight = resources.getDimension(R.dimen.external_player_height).toInt() val playerHeight = resources.getDimension(R.dimen.external_player_height).toInt()
Logd(TAG, "playerHeight: $playerHeight ${navigationBarInsets.bottom}")
bottomSheet.peekHeight = playerHeight + navigationBarInsets.bottom bottomSheet.peekHeight = playerHeight + navigationBarInsets.bottom
} }
@ -387,20 +387,16 @@ class MainActivity : CastEnabledActivity() {
Logd(TAG, "setPlayerVisible $visible_") Logd(TAG, "setPlayerVisible $visible_")
val visible = visible_ ?: (bottomSheet.state != BottomSheetBehavior.STATE_COLLAPSED) val visible = visible_ ?: (bottomSheet.state != BottomSheetBehavior.STATE_COLLAPSED)
bottomSheet.setLocked(!visible) // bottomSheet.setLocked(!visible)
if (visible) bottomSheetCallback.onStateChanged(dummyView, bottomSheet.state) // Update toolbar visibility if (visible) bottomSheetCallback.onStateChanged(dummyView, bottomSheet.state) // Update toolbar visibility
else bottomSheet.setState(BottomSheetBehavior.STATE_COLLAPSED) else bottomSheet.setState(BottomSheetBehavior.STATE_COLLAPSED)
val params = mainView.layoutParams as MarginLayoutParams val params = mainView.layoutParams as MarginLayoutParams
val externalPlayerHeight = resources.getDimension(R.dimen.external_player_height).toInt() val externalPlayerHeight = resources.getDimension(R.dimen.external_player_height).toInt()
Logd(TAG, "externalPlayerHeight: $externalPlayerHeight ${navigationBarInsets.bottom}")
params.setMargins(navigationBarInsets.left, 0, navigationBarInsets.right, params.setMargins(navigationBarInsets.left, 0, navigationBarInsets.right,
navigationBarInsets.bottom + (if (visible) externalPlayerHeight else 0)) navigationBarInsets.bottom + (if (visible) externalPlayerHeight else 0))
mainView.layoutParams = params mainView.layoutParams = params
// val playerView = findViewById<FragmentContainerView>(R.id.playerFragment1)
// val playerView = findViewById<ComposeView>(R.id.player1)
// val playerParams = playerView?.layoutParams as? MarginLayoutParams
// playerParams?.setMargins(navigationBarInsets.left, 0, navigationBarInsets.right, 0)
// playerView?.layoutParams = playerParams
audioPlayerView.visibility = if (visible) View.VISIBLE else View.GONE audioPlayerView.visibility = if (visible) View.VISIBLE else View.GONE
} }

View File

@ -81,6 +81,7 @@ import androidx.compose.ui.window.Dialog
import androidx.constraintlayout.compose.ConstraintLayout import androidx.constraintlayout.compose.ConstraintLayout
import androidx.documentfile.provider.DocumentFile import androidx.documentfile.provider.DocumentFile
import coil.compose.AsyncImage import coil.compose.AsyncImage
import coil.compose.rememberAsyncImagePainter
import io.realm.kotlin.notifications.SingleQueryChange import io.realm.kotlin.notifications.SingleQueryChange
import io.realm.kotlin.notifications.UpdatedObject import io.realm.kotlin.notifications.UpdatedObject
import kotlinx.coroutines.* import kotlinx.coroutines.*
@ -602,10 +603,10 @@ fun EpisodeLazyColumn(activity: MainActivity, vms: SnapshotStateList<EpisodeVM>,
ConstraintLayout(modifier = Modifier.width(56.dp).height(56.dp)) { ConstraintLayout(modifier = Modifier.width(56.dp).height(56.dp)) {
val (imgvCover, checkMark) = createRefs() val (imgvCover, checkMark) = createRefs()
val imgLoc = ImageResourceUtils.getEpisodeListImageLocation(vm.episode) val imgLoc = ImageResourceUtils.getEpisodeListImageLocation(vm.episode)
Logd(TAG, "imgLoc: $imgLoc") val painter = rememberAsyncImagePainter(model = imgLoc)
AsyncImage(model = imgLoc, contentDescription = "imgvCover", Image(
placeholder = painterResource(R.mipmap.ic_launcher), painter = painter,
error = painterResource(R.mipmap.ic_launcher), contentDescription = "imgvCover",
modifier = Modifier.width(56.dp).height(56.dp) modifier = Modifier.width(56.dp).height(56.dp)
.constrainAs(imgvCover) { .constrainAs(imgvCover) {
top.linkTo(parent.top) top.linkTo(parent.top)
@ -615,7 +616,8 @@ fun EpisodeLazyColumn(activity: MainActivity, vms: SnapshotStateList<EpisodeVM>,
Logd(TAG, "icon clicked!") Logd(TAG, "icon clicked!")
if (selectMode) toggleSelected() if (selectMode) toggleSelected()
else if (vm.episode.feed != null) activity.loadChildFragment(FeedInfoFragment.newInstance(vm.episode.feed!!)) else if (vm.episode.feed != null) activity.loadChildFragment(FeedInfoFragment.newInstance(vm.episode.feed!!))
})) })
)
val alpha = if (vm.playedState) 1.0f else 0f val alpha = if (vm.playedState) 1.0f else 0f
if (vm.playedState) Icon(painter = painterResource(R.drawable.ic_check), tint = textColor, contentDescription = "played_mark", if (vm.playedState) Icon(painter = painterResource(R.drawable.ic_check), tint = textColor, contentDescription = "played_mark",
modifier = Modifier.background(Color.Green).alpha(alpha).constrainAs(checkMark) { modifier = Modifier.background(Color.Green).alpha(alpha).constrainAs(checkMark) {
@ -645,7 +647,7 @@ fun EpisodeLazyColumn(activity: MainActivity, vms: SnapshotStateList<EpisodeVM>,
if (index>=vms.size) return@LaunchedEffect if (index>=vms.size) return@LaunchedEffect
vms[index].inQueueState = curQueue.contains(vms[index].episode) vms[index].inQueueState = curQueue.contains(vms[index].episode)
} }
val dur = vm.episode.media!!.getDuration() val dur = vm.episode.media?.getDuration() ?: 0
val durText = DurationConverter.getDurationStringLong(dur) val durText = DurationConverter.getDurationStringLong(dur)
Row { Row {
if (vm.episode.media?.getMediaType() == MediaType.VIDEO) if (vm.episode.media?.getMediaType() == MediaType.VIDEO)
@ -656,7 +658,7 @@ fun EpisodeLazyColumn(activity: MainActivity, vms: SnapshotStateList<EpisodeVM>,
if (vm.inQueueState) if (vm.inQueueState)
Icon(painter = painterResource(R.drawable.ic_playlist_play), tint = textColor, contentDescription = "ivInPlaylist", modifier = Modifier.width(14.dp).height(14.dp)) Icon(painter = painterResource(R.drawable.ic_playlist_play), tint = textColor, contentDescription = "ivInPlaylist", modifier = Modifier.width(14.dp).height(14.dp))
val curContext = LocalContext.current val curContext = LocalContext.current
val dateSizeText = " · " + formatAbbrev(curContext, vm.episode.getPubDate()) + " · " + durText + " · " + if((vm.episode.media?.size?:0) > 0) Formatter.formatShortFileSize(curContext, vm.episode.media!!.size) else "" 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)
} }
Text(vm.episode.title?:"", color = textColor, maxLines = 2, overflow = TextOverflow.Ellipsis) Text(vm.episode.title?:"", color = textColor, maxLines = 2, overflow = TextOverflow.Ellipsis)
@ -677,7 +679,7 @@ fun EpisodeLazyColumn(activity: MainActivity, vms: SnapshotStateList<EpisodeVM>,
if (actionButton_ == null) { if (actionButton_ == null) {
LaunchedEffect(vms[index].downloadState) { LaunchedEffect(vms[index].downloadState) {
if (index>=vms.size) return@LaunchedEffect if (index>=vms.size) return@LaunchedEffect
if (isDownloading()) vm.dlPercent = dls?.getProgress(vms[index].episode.media!!.downloadUrl!!) ?: 0 if (isDownloading()) vm.dlPercent = dls?.getProgress(vms[index].episode.media?.downloadUrl?:"") ?: 0
Logd(TAG, "LaunchedEffect $index downloadState: ${vms[index].downloadState} ${vm.episode.media?.downloaded} ${vm.dlPercent}") Logd(TAG, "LaunchedEffect $index downloadState: ${vms[index].downloadState} ${vm.episode.media?.downloaded} ${vm.dlPercent}")
vm.actionButton = EpisodeActionButton.forItem(vm.episode) vm.actionButton = EpisodeActionButton.forItem(vm.episode)
vm.actionRes = vm.actionButton!!.getDrawable() vm.actionRes = vm.actionButton!!.getDrawable()

View File

@ -7,6 +7,7 @@ import ac.mdiq.podcini.storage.database.Feeds
import ac.mdiq.podcini.storage.database.Feeds.deleteFeedSync import ac.mdiq.podcini.storage.database.Feeds.deleteFeedSync
import ac.mdiq.podcini.storage.database.RealmDB.upsert import ac.mdiq.podcini.storage.database.RealmDB.upsert
import ac.mdiq.podcini.storage.model.Feed import ac.mdiq.podcini.storage.model.Feed
import ac.mdiq.podcini.storage.model.Feed.Companion.MAX_SYNTHETIC_ID
import ac.mdiq.podcini.storage.model.Rating import ac.mdiq.podcini.storage.model.Rating
import ac.mdiq.podcini.storage.model.SubscriptionLog import ac.mdiq.podcini.storage.model.SubscriptionLog
import ac.mdiq.podcini.ui.activity.MainActivity import ac.mdiq.podcini.ui.activity.MainActivity
@ -88,12 +89,24 @@ fun RemoveFeedDialog(feeds: List<Feed>, onDismissRequest: () -> Unit, callback:
CoroutineScope(Dispatchers.IO).launch { CoroutineScope(Dispatchers.IO).launch {
try { try {
for (f in feeds) { for (f in feeds) {
val sLog = SubscriptionLog(f.id, f.title?:"", f.downloadUrl?:"", f.link?:"", SubscriptionLog.Type.Feed.name) if (f.id > MAX_SYNTHETIC_ID) {
upsert(sLog) { val sLog = SubscriptionLog(f.id, f.title ?: "", f.downloadUrl ?: "", f.link ?: "", SubscriptionLog.Type.Feed.name)
it.rating = f.rating upsert(sLog) {
it.comment = f.comment it.rating = f.rating
it.comment += "\nReason to remove:\n" + textState.text it.comment = f.comment
it.cancelDate = Date().time it.comment += "\nReason to remove:\n" + textState.text
it.cancelDate = Date().time
}
} else {
for (e in f.episodes) {
val sLog = SubscriptionLog(e.id, e.title ?: "", e.media?.downloadUrl ?: "", e.link ?: "", SubscriptionLog.Type.Media.name)
upsert(sLog) {
it.rating = e.rating
it.comment = e.comment
it.comment += "\nReason to remove:\n" + textState.text
it.cancelDate = Date().time
}
}
} }
deleteFeedSync(context, f.id, false) deleteFeedSync(context, f.id, false)
} }

View File

@ -6,17 +6,12 @@ import ac.mdiq.podcini.databinding.FilterDialogRowBinding
import ac.mdiq.podcini.storage.model.EpisodeFilter import ac.mdiq.podcini.storage.model.EpisodeFilter
import ac.mdiq.podcini.ui.fragment.SubscriptionsFragment.Companion.TAG import ac.mdiq.podcini.ui.fragment.SubscriptionsFragment.Companion.TAG
import ac.mdiq.podcini.util.Logd import ac.mdiq.podcini.util.Logd
import android.app.Dialog
import android.content.DialogInterface
import android.os.Bundle import android.os.Bundle
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import android.widget.Button import android.widget.Button
import android.widget.FrameLayout
import android.widget.LinearLayout import android.widget.LinearLayout
import com.google.android.material.bottomsheet.BottomSheetBehavior
import com.google.android.material.bottomsheet.BottomSheetDialog
import com.google.android.material.bottomsheet.BottomSheetDialogFragment import com.google.android.material.bottomsheet.BottomSheetDialogFragment
import com.google.android.material.button.MaterialButtonToggleGroup import com.google.android.material.button.MaterialButtonToggleGroup
@ -89,31 +84,12 @@ abstract class EpisodeFilterDialog : BottomSheetDialogFragment() {
return layout return layout
} }
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
val dialog = super.onCreateDialog(savedInstanceState)
dialog.setOnShowListener { dialogInterface: DialogInterface ->
val bottomSheetDialog = dialogInterface as BottomSheetDialog
setupFullHeight(bottomSheetDialog)
}
return dialog
}
override fun onDestroyView() { override fun onDestroyView() {
Logd(TAG, "onDestroyView") Logd(TAG, "onDestroyView")
_binding = null _binding = null
super.onDestroyView() super.onDestroyView()
} }
private fun setupFullHeight(bottomSheetDialog: BottomSheetDialog) {
val bottomSheet = bottomSheetDialog.findViewById<View>(com.leinardi.android.speeddial.R.id.design_bottom_sheet) as? FrameLayout
if (bottomSheet != null) {
val behavior = BottomSheetBehavior.from(bottomSheet)
val layoutParams = bottomSheet.layoutParams
bottomSheet.layoutParams = layoutParams
behavior.state = BottomSheetBehavior.STATE_EXPANDED
}
}
abstract fun onFilterChanged(newFilterValues: Set<String>) abstract fun onFilterChanged(newFilterValues: Set<String>)
enum class FeedItemFilterGroup(vararg values: ItemProperties) { enum class FeedItemFilterGroup(vararg values: ItemProperties) {

View File

@ -7,17 +7,12 @@ import ac.mdiq.podcini.databinding.SortDialogItemBinding
import ac.mdiq.podcini.storage.model.EpisodeSortOrder import ac.mdiq.podcini.storage.model.EpisodeSortOrder
import ac.mdiq.podcini.ui.fragment.SubscriptionsFragment.Companion.TAG import ac.mdiq.podcini.ui.fragment.SubscriptionsFragment.Companion.TAG
import ac.mdiq.podcini.util.Logd import ac.mdiq.podcini.util.Logd
import android.app.Dialog
import android.content.DialogInterface
import android.os.Bundle import android.os.Bundle
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import android.view.WindowManager import android.view.WindowManager
import android.widget.CompoundButton import android.widget.CompoundButton
import android.widget.FrameLayout
import com.google.android.material.bottomsheet.BottomSheetBehavior
import com.google.android.material.bottomsheet.BottomSheetDialog
import com.google.android.material.bottomsheet.BottomSheetDialogFragment import com.google.android.material.bottomsheet.BottomSheetDialogFragment
open class EpisodeSortDialog : BottomSheetDialogFragment() { open class EpisodeSortDialog : BottomSheetDialogFragment() {
@ -91,28 +86,9 @@ open class EpisodeSortDialog : BottomSheetDialogFragment() {
protected open fun onSelectionChanged() {} protected open fun onSelectionChanged() {}
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
val dialog = super.onCreateDialog(savedInstanceState)
dialog.setOnShowListener { dialogInterface: DialogInterface ->
val bottomSheetDialog = dialogInterface as BottomSheetDialog
setupFullHeight(bottomSheetDialog)
}
return dialog
}
override fun onDestroyView() { override fun onDestroyView() {
Logd(TAG, "onDestroyView") Logd(TAG, "onDestroyView")
_binding = null _binding = null
super.onDestroyView() super.onDestroyView()
} }
private fun setupFullHeight(bottomSheetDialog: BottomSheetDialog) {
val bottomSheet = bottomSheetDialog.findViewById<FrameLayout>(com.leinardi.android.speeddial.R.id.design_bottom_sheet)
if (bottomSheet != null) {
val behavior = BottomSheetBehavior.from(bottomSheet)
val layoutParams = bottomSheet.layoutParams
bottomSheet.layoutParams = layoutParams
behavior.state = BottomSheetBehavior.STATE_EXPANDED
}
}
} }

View File

@ -10,20 +10,15 @@ import ac.mdiq.podcini.storage.model.FeedSortOrder
import ac.mdiq.podcini.storage.model.FeedSortOrder.Companion.getSortOrder import ac.mdiq.podcini.storage.model.FeedSortOrder.Companion.getSortOrder
import ac.mdiq.podcini.ui.fragment.SubscriptionsFragment.Companion.feedOrderBy import ac.mdiq.podcini.ui.fragment.SubscriptionsFragment.Companion.feedOrderBy
import ac.mdiq.podcini.ui.fragment.SubscriptionsFragment.Companion.feedOrderDir import ac.mdiq.podcini.ui.fragment.SubscriptionsFragment.Companion.feedOrderDir
import ac.mdiq.podcini.util.Logd
import ac.mdiq.podcini.util.EventFlow import ac.mdiq.podcini.util.EventFlow
import ac.mdiq.podcini.util.FlowEvent import ac.mdiq.podcini.util.FlowEvent
import android.app.Dialog import ac.mdiq.podcini.util.Logd
import android.content.DialogInterface
import android.os.Bundle import android.os.Bundle
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import android.view.WindowManager import android.view.WindowManager
import android.widget.CompoundButton import android.widget.CompoundButton
import android.widget.FrameLayout
import com.google.android.material.bottomsheet.BottomSheetBehavior
import com.google.android.material.bottomsheet.BottomSheetDialog
import com.google.android.material.bottomsheet.BottomSheetDialogFragment import com.google.android.material.bottomsheet.BottomSheetDialogFragment
open class FeedSortDialog : BottomSheetDialogFragment() { open class FeedSortDialog : BottomSheetDialogFragment() {
@ -110,31 +105,12 @@ open class FeedSortDialog : BottomSheetDialogFragment() {
protected open fun onSelectionChanged() {} protected open fun onSelectionChanged() {}
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
val dialog = super.onCreateDialog(savedInstanceState)
dialog.setOnShowListener { dialogInterface: DialogInterface ->
val bottomSheetDialog = dialogInterface as BottomSheetDialog
setupFullHeight(bottomSheetDialog)
}
return dialog
}
override fun onDestroyView() { override fun onDestroyView() {
Logd(TAG, "onDestroyView") Logd(TAG, "onDestroyView")
_binding = null _binding = null
super.onDestroyView() super.onDestroyView()
} }
private fun setupFullHeight(bottomSheetDialog: BottomSheetDialog) {
val bottomSheet = bottomSheetDialog.findViewById<FrameLayout>(com.leinardi.android.speeddial.R.id.design_bottom_sheet)
if (bottomSheet != null) {
val behavior = BottomSheetBehavior.from(bottomSheet)
val layoutParams = bottomSheet.layoutParams
bottomSheet.layoutParams = layoutParams
behavior.state = BottomSheetBehavior.STATE_EXPANDED
}
}
private fun setFeedOrder(selected: String, dir: Int) { private fun setFeedOrder(selected: String, dir: Int) {
appPrefs.edit().putString(UserPreferences.Prefs.prefDrawerFeedOrder.name, selected).apply() appPrefs.edit().putString(UserPreferences.Prefs.prefDrawerFeedOrder.name, selected).apply()
appPrefs.edit().putInt(UserPreferences.Prefs.prefDrawerFeedOrderDir.name, dir).apply() appPrefs.edit().putInt(UserPreferences.Prefs.prefDrawerFeedOrderDir.name, dir).apply()

View File

@ -6,7 +6,6 @@ import ac.mdiq.podcini.playback.PlaybackServiceStarter
import ac.mdiq.podcini.playback.ServiceStatusHandler import ac.mdiq.podcini.playback.ServiceStatusHandler
import ac.mdiq.podcini.playback.base.InTheatre.curEpisode import ac.mdiq.podcini.playback.base.InTheatre.curEpisode
import ac.mdiq.podcini.playback.base.InTheatre.curMedia import ac.mdiq.podcini.playback.base.InTheatre.curMedia
import ac.mdiq.podcini.playback.base.MediaPlayerBase
import ac.mdiq.podcini.playback.base.MediaPlayerBase.Companion.status import ac.mdiq.podcini.playback.base.MediaPlayerBase.Companion.status
import ac.mdiq.podcini.playback.base.PlayerStatus import ac.mdiq.podcini.playback.base.PlayerStatus
import ac.mdiq.podcini.playback.base.VideoMode import ac.mdiq.podcini.playback.base.VideoMode
@ -149,19 +148,10 @@ class AudioPlayerFragment : Fragment() {
val composeView = ComposeView(requireContext()).apply { val composeView = ComposeView(requireContext()).apply {
setContent { setContent {
CustomTheme(requireContext()) { CustomTheme(requireContext()) {
// Column(modifier = Modifier.fillMaxSize().statusBarsPadding().navigationBarsPadding() ) { Box(modifier = Modifier.fillMaxWidth().then(if (isCollapsed) Modifier else Modifier.statusBarsPadding().navigationBarsPadding())) {
// if (isCollapsed) PlayerUI() PlayerUI(Modifier.align(if (isCollapsed) Alignment.TopCenter else Alignment.BottomCenter).zIndex(1f))
//// else Spacer(modifier = Modifier.size(0.dp))
// Toolbar()
// DetailUI(modifier = Modifier.weight(1f))
// if (!isCollapsed) PlayerUI()
//// else Spacer(modifier = Modifier.size(0.dp))
// }
Box(modifier = Modifier.fillMaxWidth().statusBarsPadding().navigationBarsPadding()) {
val aligm = if (isCollapsed) Alignment.TopCenter else Alignment.BottomCenter
PlayerUI(Modifier.align(aligm).zIndex(1f))
if (!isCollapsed) { if (!isCollapsed) {
Column(Modifier.padding(bottom = 90.dp)) { Column(Modifier.padding(bottom = 120.dp)) {
Toolbar() Toolbar()
DetailUI(modifier = Modifier) DetailUI(modifier = Modifier)
} }
@ -269,14 +259,14 @@ class AudioPlayerFragment : Fragment() {
if (curMedia != null) { if (curMedia != null) {
val media = curMedia!! val media = curMedia!!
setIsShowPlay(!isShowPlay) setIsShowPlay(!isShowPlay)
if (media.getMediaType() == MediaType.VIDEO && MediaPlayerBase.status != PlayerStatus.PLAYING && if (media.getMediaType() == MediaType.VIDEO && status != PlayerStatus.PLAYING &&
(media is EpisodeMedia && media.episode?.feed?.preferences?.videoModePolicy != VideoMode.AUDIO_ONLY)) { (media is EpisodeMedia && media.episode?.feed?.preferences?.videoModePolicy != VideoMode.AUDIO_ONLY)) {
playPause() playPause()
requireContext().startActivity(getPlayerActivityIntent(requireContext(), curMedia!!.getMediaType())) requireContext().startActivity(getPlayerActivityIntent(requireContext(), curMedia!!.getMediaType()))
} else playPause() } else playPause()
} }
}, onLongClick = { }, onLongClick = {
if (controller != null && MediaPlayerBase.status == PlayerStatus.PLAYING) { if (controller != null && status == PlayerStatus.PLAYING) {
val fallbackSpeed = UserPreferences.fallbackSpeed val fallbackSpeed = UserPreferences.fallbackSpeed
if (fallbackSpeed > 0.1f) toggleFallbackSpeed(fallbackSpeed) if (fallbackSpeed > 0.1f) toggleFallbackSpeed(fallbackSpeed)
} }
@ -307,7 +297,7 @@ class AudioPlayerFragment : Fragment() {
Icon(painter = painterResource(R.drawable.ic_skip_48dp), tint = textColor, Icon(painter = painterResource(R.drawable.ic_skip_48dp), tint = textColor,
contentDescription = "rewind", contentDescription = "rewind",
modifier = Modifier.width(43.dp).height(43.dp).combinedClickable(onClick = { modifier = Modifier.width(43.dp).height(43.dp).combinedClickable(onClick = {
if (controller != null && MediaPlayerBase.status == PlayerStatus.PLAYING) { if (controller != null && status == PlayerStatus.PLAYING) {
val speedForward = UserPreferences.speedforwardSpeed val speedForward = UserPreferences.speedforwardSpeed
if (speedForward > 0.1f) speedForward(speedForward) if (speedForward > 0.1f) speedForward(speedForward)
} }
@ -530,9 +520,10 @@ class AudioPlayerFragment : Fragment() {
onPositionUpdate(FlowEvent.PlaybackPositionEvent(media, media.getPosition(), media.getDuration())) onPositionUpdate(FlowEvent.PlaybackPositionEvent(media, media.getPosition(), media.getDuration()))
if (prevMedia?.getIdentifier() != media.getIdentifier()) imgLoc = ImageResourceUtils.getEpisodeListImageLocation(media) if (prevMedia?.getIdentifier() != media.getIdentifier()) imgLoc = ImageResourceUtils.getEpisodeListImageLocation(media)
if (isPlayingVideoLocally && (curMedia as? EpisodeMedia)?.episode?.feed?.preferences?.videoModePolicy != VideoMode.AUDIO_ONLY) { if (isPlayingVideoLocally && (curMedia as? EpisodeMedia)?.episode?.feed?.preferences?.videoModePolicy != VideoMode.AUDIO_ONLY) {
(activity as MainActivity).bottomSheet.setLocked(true) // (activity as MainActivity).bottomSheet.setLocked(true)
(activity as MainActivity).bottomSheet.setState(BottomSheetBehavior.STATE_COLLAPSED) (activity as MainActivity).bottomSheet.state = BottomSheetBehavior.STATE_COLLAPSED
} else (activity as MainActivity).bottomSheet.setLocked(false) }
// else (activity as MainActivity).bottomSheet.setLocked(false)
prevMedia = media prevMedia = media
} }

View File

@ -32,8 +32,6 @@ import ac.mdiq.podcini.storage.utils.DurationConverter
import ac.mdiq.podcini.storage.utils.ImageResourceUtils import ac.mdiq.podcini.storage.utils.ImageResourceUtils
import ac.mdiq.podcini.ui.actions.* import ac.mdiq.podcini.ui.actions.*
import ac.mdiq.podcini.ui.activity.MainActivity import ac.mdiq.podcini.ui.activity.MainActivity
import ac.mdiq.podcini.ui.activity.VideoplayerActivity
import ac.mdiq.podcini.ui.activity.VideoplayerActivity.Companion
import ac.mdiq.podcini.ui.compose.ChaptersDialog import ac.mdiq.podcini.ui.compose.ChaptersDialog
import ac.mdiq.podcini.ui.compose.ChooseRatingDialog import ac.mdiq.podcini.ui.compose.ChooseRatingDialog
import ac.mdiq.podcini.ui.compose.CustomTheme import ac.mdiq.podcini.ui.compose.CustomTheme
@ -193,8 +191,8 @@ class EpisodeInfoFragment : Fragment(), Toolbar.OnMenuItemClickListener {
val imgLoc = if (episode != null) ImageResourceUtils.getEpisodeListImageLocation(episode!!) else null val imgLoc = if (episode != null) ImageResourceUtils.getEpisodeListImageLocation(episode!!) else null
AsyncImage(model = imgLoc, contentDescription = "imgvCover", error = painterResource(R.mipmap.ic_launcher), modifier = Modifier.width(56.dp).height(56.dp).clickable(onClick = { openPodcast() })) AsyncImage(model = imgLoc, contentDescription = "imgvCover", error = painterResource(R.mipmap.ic_launcher), modifier = Modifier.width(56.dp).height(56.dp).clickable(onClick = { openPodcast() }))
Column(modifier = Modifier.padding(start = 10.dp)) { Column(modifier = Modifier.padding(start = 10.dp)) {
Text(txtvPodcast, color = textColor, style = MaterialTheme.typography.bodyLarge, modifier = Modifier.clickable { openPodcast() }) Text(txtvPodcast, color = textColor, style = MaterialTheme.typography.bodyLarge, modifier = Modifier.fillMaxWidth().clickable { openPodcast() })
Text(txtvTitle, color = textColor, style = MaterialTheme.typography.bodyLarge.copy(fontWeight = FontWeight.Bold), maxLines = 5, overflow = TextOverflow.Ellipsis) Text(txtvTitle, color = textColor, style = MaterialTheme.typography.bodyLarge.copy(fontWeight = FontWeight.Bold), modifier = Modifier.fillMaxWidth(), maxLines = 5, overflow = TextOverflow.Ellipsis)
Text("$txtvPublished · $txtvDuration · $txtvSize", color = textColor, style = MaterialTheme.typography.bodyMedium) Text("$txtvPublished · $txtvDuration · $txtvSize", color = textColor, style = MaterialTheme.typography.bodyMedium)
} }
} }

View File

@ -1,6 +1,5 @@
package ac.mdiq.podcini.ui.fragment package ac.mdiq.podcini.ui.fragment
//import ac.mdiq.podcini.databinding.MultiSelectSpeedDialBinding
import ac.mdiq.podcini.R import ac.mdiq.podcini.R
import ac.mdiq.podcini.databinding.FeedItemListFragmentBinding import ac.mdiq.podcini.databinding.FeedItemListFragmentBinding
import ac.mdiq.podcini.net.download.DownloadStatus import ac.mdiq.podcini.net.download.DownloadStatus
@ -286,8 +285,8 @@ import java.util.concurrent.Semaphore
Column(Modifier.fillMaxWidth().constrainAs(taColumn) { Column(Modifier.fillMaxWidth().constrainAs(taColumn) {
top.linkTo(imgvCover.top) top.linkTo(imgvCover.top)
start.linkTo(imgvCover.end) }) { start.linkTo(imgvCover.end) }) {
Text(feed?.title?:"", color = textColor, fontWeight = FontWeight.Bold, style = MaterialTheme.typography.bodyLarge, maxLines = 2, overflow = TextOverflow.Ellipsis) Text(feed?.title?:"", color = textColor, fontWeight = FontWeight.Bold, style = MaterialTheme.typography.bodyLarge, modifier = Modifier.fillMaxWidth(), maxLines = 2, overflow = TextOverflow.Ellipsis)
Text(feed?.author?:"", color = textColor, fontWeight = FontWeight.Bold, style = MaterialTheme.typography.bodyMedium, maxLines = 1, overflow = TextOverflow.Ellipsis) Text(feed?.author?:"", color = textColor, fontWeight = FontWeight.Bold, style = MaterialTheme.typography.bodyMedium, modifier = Modifier.fillMaxWidth(), maxLines = 1, overflow = TextOverflow.Ellipsis)
} }
} }
} }
@ -504,7 +503,7 @@ import java.util.concurrent.Semaphore
// if (!event.isRunning) nextPageLoader.root.visibility = View.GONE // if (!event.isRunning) nextPageLoader.root.visibility = View.GONE
infoTextUpdate = if (event.isRunning) getString(R.string.refreshing_label) else "" infoTextUpdate = if (event.isRunning) getString(R.string.refreshing_label) else ""
infoBarText.value = "$infoTextFiltered $infoTextUpdate" infoBarText.value = "$infoTextFiltered $infoTextUpdate"
if (event.isRunning == false) loadFeed() if (!event.isRunning) loadFeed()
// binding.swipeRefresh.isRefreshing = event.isRunning // binding.swipeRefresh.isRefreshing = event.isRunning
} }

View File

@ -13,6 +13,7 @@ import ac.mdiq.podcini.storage.database.RealmDB.realm
import ac.mdiq.podcini.storage.database.RealmDB.runOnIOScope import ac.mdiq.podcini.storage.database.RealmDB.runOnIOScope
import ac.mdiq.podcini.storage.database.RealmDB.upsert import ac.mdiq.podcini.storage.database.RealmDB.upsert
import ac.mdiq.podcini.storage.model.Feed import ac.mdiq.podcini.storage.model.Feed
import ac.mdiq.podcini.storage.model.Feed.Companion.MAX_SYNTHETIC_ID
import ac.mdiq.podcini.storage.model.FeedFunding import ac.mdiq.podcini.storage.model.FeedFunding
import ac.mdiq.podcini.storage.model.Rating import ac.mdiq.podcini.storage.model.Rating
import ac.mdiq.podcini.ui.activity.MainActivity import ac.mdiq.podcini.ui.activity.MainActivity
@ -210,8 +211,8 @@ class FeedInfoFragment : Fragment(), Toolbar.OnMenuItemClickListener {
Column(Modifier.constrainAs(taColumn) { Column(Modifier.constrainAs(taColumn) {
top.linkTo(imgvCover.top) top.linkTo(imgvCover.top)
start.linkTo(imgvCover.end) }) { start.linkTo(imgvCover.end) }) {
Text(feed.title ?:"", color = textColor, fontWeight = FontWeight.Bold, style = MaterialTheme.typography.bodyLarge, maxLines = 2, overflow = TextOverflow.Ellipsis) Text(feed.title ?:"", color = textColor, fontWeight = FontWeight.Bold, style = MaterialTheme.typography.bodyLarge, modifier = Modifier.fillMaxWidth(), maxLines = 2, overflow = TextOverflow.Ellipsis)
Text(txtvAuthor, color = textColor, fontWeight = FontWeight.Bold, style = MaterialTheme.typography.bodyMedium, maxLines = 1, overflow = TextOverflow.Ellipsis) Text(text = txtvAuthor, color = textColor, fontWeight = FontWeight.Bold, style = MaterialTheme.typography.bodyMedium, modifier = Modifier.fillMaxWidth(), maxLines = 1, overflow = TextOverflow.Ellipsis)
} }
} }
} }
@ -240,50 +241,52 @@ class FeedInfoFragment : Fragment(), Toolbar.OnMenuItemClickListener {
modifier = Modifier.padding(start = 15.dp, top = 10.dp, bottom = 5.dp).clickable { showEditComment = true }) modifier = Modifier.padding(start = 15.dp, top = 10.dp, bottom = 5.dp).clickable { showEditComment = true })
Text(commentTextState.text, color = textColor, style = MaterialTheme.typography.bodyMedium, modifier = Modifier.padding(start = 15.dp, bottom = 10.dp)) Text(commentTextState.text, color = textColor, style = MaterialTheme.typography.bodyMedium, modifier = Modifier.padding(start = 15.dp, bottom = 10.dp))
Text(stringResource(R.string.url_label), color = textColor, style = MaterialTheme.typography.bodyLarge, modifier = Modifier.padding(top = 16.dp, bottom = 4.dp)) if (feed.id > MAX_SYNTHETIC_ID) {
Text(text = txtvUrl?:"", color = textColor, modifier = Modifier.clickable { Text(stringResource(R.string.url_label), color = textColor, style = MaterialTheme.typography.bodyLarge, modifier = Modifier.padding(top = 16.dp, bottom = 4.dp))
if (feed.downloadUrl != null) { Text(text = txtvUrl ?: "", color = textColor, modifier = Modifier.clickable {
val url: String = feed.downloadUrl!! if (feed.downloadUrl != null) {
val clipData: ClipData = ClipData.newPlainText(url, url) val url: String = feed.downloadUrl!!
val cm = requireContext().getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager val clipData: ClipData = ClipData.newPlainText(url, url)
cm.setPrimaryClip(clipData) val cm = requireContext().getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
if (Build.VERSION.SDK_INT <= 32) (activity as MainActivity).showSnackbarAbovePlayer(R.string.copied_to_clipboard, Snackbar.LENGTH_SHORT) cm.setPrimaryClip(clipData)
} if (Build.VERSION.SDK_INT <= 32) (activity as MainActivity).showSnackbarAbovePlayer(R.string.copied_to_clipboard, Snackbar.LENGTH_SHORT)
}) }
if (feed.paymentLinks.isNotEmpty()) { })
Text(stringResource(R.string.support_funding_label), color = textColor, style = MaterialTheme.typography.bodyLarge, modifier = Modifier.padding(top = 16.dp, bottom = 4.dp)) if (feed.paymentLinks.isNotEmpty()) {
fun fundingText(): String { Text(stringResource(R.string.support_funding_label), color = textColor, style = MaterialTheme.typography.bodyLarge, modifier = Modifier.padding(top = 16.dp, bottom = 4.dp))
val fundingList: ArrayList<FeedFunding> = feed.paymentLinks fun fundingText(): String {
// Filter for duplicates, but keep items in the order that they have in the feed. val fundingList: ArrayList<FeedFunding> = feed.paymentLinks
val i: MutableIterator<FeedFunding> = fundingList.iterator() // Filter for duplicates, but keep items in the order that they have in the feed.
while (i.hasNext()) { val i: MutableIterator<FeedFunding> = fundingList.iterator()
val funding: FeedFunding = i.next() while (i.hasNext()) {
for (other in fundingList) { val funding: FeedFunding = i.next()
if (other.url == funding.url) { for (other in fundingList) {
if (other.content != null && funding.content != null && other.content!!.length > funding.content!!.length) { if (other.url == funding.url) {
i.remove() if (other.content != null && funding.content != null && other.content!!.length > funding.content!!.length) {
break i.remove()
break
}
} }
} }
} }
val str = StringBuilder()
for (funding in fundingList) {
str.append(if (funding.content == null || funding.content!!.isEmpty()) requireContext().resources.getString(
R.string.support_podcast)
else funding.content).append(" ").append(funding.url)
str.append("\n")
}
return StringBuilder(StringUtils.trim(str.toString())).toString()
} }
val str = StringBuilder() val fundText = remember { fundingText() }
for (funding in fundingList) { Text(fundText, color = textColor)
str.append(if (funding.content == null || funding.content!!.isEmpty()) requireContext().resources.getString( }
R.string.support_podcast) Button(modifier = Modifier.padding(top = 10.dp), onClick = {
else funding.content).append(" ").append(funding.url) val fragment = SearchResultsFragment.newInstance(CombinedSearcher::class.java, "$txtvAuthor podcasts")
str.append("\n") (activity as MainActivity).loadChildFragment(fragment, TransitionEffect.SLIDE)
} }) {
return StringBuilder(StringUtils.trim(str.toString())).toString() Text(stringResource(R.string.feeds_related_to_author))
} }
val fundText = remember { fundingText() }
Text(fundText, color = textColor)
}
Button(modifier = Modifier.padding(top = 10.dp), onClick = {
val fragment = SearchResultsFragment.newInstance(CombinedSearcher::class.java, "$txtvAuthor podcasts")
(activity as MainActivity).loadChildFragment(fragment, TransitionEffect.SLIDE)
}) {
Text(stringResource(R.string.feeds_related_to_author))
} }
Text(stringResource(R.string.statistics_label), color = textColor, style = MaterialTheme.typography.bodyLarge, modifier = Modifier.padding(top = 16.dp, bottom = 4.dp)) Text(stringResource(R.string.statistics_label), color = textColor, style = MaterialTheme.typography.bodyLarge, modifier = Modifier.padding(top = 16.dp, bottom = 4.dp))
val arguments = Bundle() val arguments = Bundle()

View File

@ -418,11 +418,11 @@ class OnlineFeedFragment : Fragment() {
Text(HtmlToPlainText.getPlainText(feed?.description ?: ""), color = textColor, style = MaterialTheme.typography.bodyMedium) Text(HtmlToPlainText.getPlainText(feed?.description ?: ""), color = textColor, style = MaterialTheme.typography.bodyMedium)
val sLog = remember {feedLogsMap_[feed?.downloadUrl?:""] } val sLog = remember {feedLogsMap_[feed?.downloadUrl?:""] }
if (sLog != null) { if (sLog != null) {
val commentTextState by remember { mutableStateOf(TextFieldValue(sLog?.comment ?: "")) } val commentTextState by remember { mutableStateOf(TextFieldValue(sLog.comment ?: "")) }
val context = LocalContext.current val context = LocalContext.current
val cancelDate = remember { formatAbbrev(context, Date(sLog.cancelDate)) } val cancelDate = remember { formatAbbrev(context, Date(sLog.cancelDate)) }
val ratingRes = remember { fromCode(sLog.rating).res } val ratingRes = remember { fromCode(sLog.rating).res }
if (!commentTextState.text.isEmpty()) { if (commentTextState.text.isNotEmpty()) {
Row(verticalAlignment = Alignment.CenterVertically, modifier = Modifier.padding(start = 15.dp, top = 10.dp, bottom = 5.dp)) { Row(verticalAlignment = Alignment.CenterVertically, modifier = Modifier.padding(start = 15.dp, top = 10.dp, bottom = 5.dp)) {
Text(stringResource(R.string.my_opinion_label), color = MaterialTheme.colorScheme.primary, style = MaterialTheme.typography.titleMedium) Text(stringResource(R.string.my_opinion_label), color = MaterialTheme.colorScheme.primary, style = MaterialTheme.typography.titleMedium)
Icon(painter = painterResource(ratingRes), tint = MaterialTheme.colorScheme.tertiary, contentDescription = null, modifier = Modifier.padding(start = 5.dp)) Icon(painter = painterResource(ratingRes), tint = MaterialTheme.colorScheme.tertiary, contentDescription = null, modifier = Modifier.padding(start = 5.dp))

View File

@ -181,7 +181,7 @@ import kotlin.math.max
swipeActions = SwipeActions(this, TAG) swipeActions = SwipeActions(this, TAG)
swipeActions.setFilter(EpisodeFilter(EpisodeFilter.States.queued.name)) swipeActions.setFilter(EpisodeFilter(EpisodeFilter.States.queued.name))
swipeActionsBin = SwipeActions(this, TAG+".Bin") swipeActionsBin = SwipeActions(this, "$TAG.Bin")
swipeActionsBin.setFilter(EpisodeFilter(EpisodeFilter.States.queued.name)) swipeActionsBin.setFilter(EpisodeFilter(EpisodeFilter.States.queued.name))
binding.lazyColumn.setContent { binding.lazyColumn.setContent {
@ -462,6 +462,7 @@ import kotlin.math.max
toolbar.addView(spinnerLayout) toolbar.addView(spinnerLayout)
} }
refreshMenuItems() refreshMenuItems()
refreshSwipeTelltale()
if (showBin) { if (showBin) {
item.setIcon(R.drawable.playlist_play) item.setIcon(R.drawable.playlist_play)
// speedDialView.addActionItem(addToQueueActionItem) // speedDialView.addActionItem(addToQueueActionItem)

View File

@ -1160,31 +1160,12 @@ class SubscriptionsFragment : Fragment(), Toolbar.OnMenuItemClickListener {
return layout return layout
} }
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
val dialog = super.onCreateDialog(savedInstanceState)
dialog.setOnShowListener { dialogInterface: DialogInterface ->
val bottomSheetDialog = dialogInterface as BottomSheetDialog
setupFullHeight(bottomSheetDialog)
}
return dialog
}
override fun onDestroyView() { override fun onDestroyView() {
Logd(TAG, "onDestroyView") Logd(TAG, "onDestroyView")
_binding = null _binding = null
super.onDestroyView() super.onDestroyView()
} }
private fun setupFullHeight(bottomSheetDialog: BottomSheetDialog) {
val bottomSheet = bottomSheetDialog.findViewById<View>(com.leinardi.android.speeddial.R.id.design_bottom_sheet) as? FrameLayout
if (bottomSheet != null) {
val behavior = BottomSheetBehavior.from(bottomSheet)
val layoutParams = bottomSheet.layoutParams
bottomSheet.layoutParams = layoutParams
behavior.state = BottomSheetBehavior.STATE_EXPANDED
}
}
private fun onFilterChanged(newFilterValues: Set<String>) { private fun onFilterChanged(newFilterValues: Set<String>) {
feedsFilter = StringUtils.join(newFilterValues, ",") feedsFilter = StringUtils.join(newFilterValues, ",")
Logd(TAG, "onFilterChanged: $feedsFilter") Logd(TAG, "onFilterChanged: $feedsFilter")

View File

@ -1,63 +0,0 @@
package ac.mdiq.podcini.ui.utils
import android.content.Context
import android.util.AttributeSet
import android.view.MotionEvent
import android.view.View
import androidx.coordinatorlayout.widget.CoordinatorLayout
import com.google.android.material.bottomsheet.ViewPagerBottomSheetBehavior
/**
* Based on https://stackoverflow.com/a/40798214
*/
class LockableBottomSheetBehavior<V : View?> : ViewPagerBottomSheetBehavior<V> {
private var isLocked = false
constructor()
constructor(context: Context, attrs: AttributeSet?) : super(context, attrs)
fun setLocked(locked: Boolean) {
isLocked = locked
}
override fun onInterceptTouchEvent(parent: CoordinatorLayout, child: V & Any, event: MotionEvent): Boolean {
var handled = false
if (!isLocked) handled = super.onInterceptTouchEvent(parent, child, event)
return handled
}
override fun onTouchEvent(parent: CoordinatorLayout, child: V & Any, event: MotionEvent): Boolean {
var handled = false
if (!isLocked) handled = super.onTouchEvent(parent, child, event)
return handled
}
override fun onStartNestedScroll(coordinatorLayout: CoordinatorLayout, child: V & Any, directTargetChild: View, target: View, axes: Int, type: Int): Boolean {
var handled = false
if (!isLocked) handled = super.onStartNestedScroll(coordinatorLayout, child, directTargetChild, target, axes, type)
return handled
}
override fun onNestedPreScroll(coordinatorLayout: CoordinatorLayout, child: V & Any, target: View, dx: Int, dy: Int, consumed: IntArray, type: Int) {
if (!isLocked) super.onNestedPreScroll(coordinatorLayout, child, target, dx, dy, consumed, type)
}
override fun onStopNestedScroll(coordinatorLayout: CoordinatorLayout, child: V & Any, target: View, type: Int) {
if (!isLocked) super.onStopNestedScroll(coordinatorLayout, child, target, type)
}
override fun onNestedPreFling(coordinatorLayout: CoordinatorLayout, child: V & Any, target: View, velocityX: Float, velocityY: Float): Boolean {
var handled = false
if (!isLocked) handled = super.onNestedPreFling(coordinatorLayout, child, target, velocityX, velocityY)
return handled
}
}

View File

@ -1,55 +0,0 @@
package com.google.android.material.bottomsheet
import android.content.Context
import android.util.AttributeSet
import android.view.View
import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView
import androidx.viewpager2.widget.ViewPager2
import java.lang.ref.WeakReference
/**
* Override [.findScrollingChild] to support [ViewPager]'s nested scrolling.
* In order to override package level method and field.
* This class put in the same package path where [BottomSheetBehavior] located.
* Source: https://medium.com/@hanru.yeh/funny-solution-that-makes-bottomsheetdialog-support-viewpager-with-nestedscrollingchilds-bfdca72235c3
*/
open class ViewPagerBottomSheetBehavior<V : View?> : BottomSheetBehavior<V> {
constructor() : super()
constructor(context: Context, attrs: AttributeSet?) : super(context, attrs)
public override fun findScrollingChild(view: View): View? {
if (view.isNestedScrollingEnabled) {
return view
}
when (view) {
is ViewPager2 -> {
val recycler = view.getChildAt(0) as RecyclerView
val currentViewPagerChild = recycler.getChildAt(view.currentItem)
if (currentViewPagerChild != null) {
return findScrollingChild(currentViewPagerChild)
}
}
is ViewGroup -> {
var i = 0
val count = view.childCount
while (i < count) {
val scrollingChild = findScrollingChild(view.getChildAt(i))
if (scrollingChild != null) {
return scrollingChild
}
i++
}
}
}
return null
}
fun updateScrollingChild() {
val childView = viewRef?.get() ?: return
val scrollingChild = findScrollingChild(childView)
nestedScrollingChildRef = WeakReference(scrollingChild)
}
}

View File

@ -30,7 +30,7 @@
android:background="?android:attr/colorBackground" android:background="?android:attr/colorBackground"
android:elevation="8dp" android:elevation="8dp"
android:visibility="gone" android:visibility="gone"
app:layout_behavior="ac.mdiq.podcini.ui.utils.LockableBottomSheetBehavior" /> app:layout_behavior="com.google.android.material.bottomsheet.BottomSheetBehavior" />
</androidx.coordinatorlayout.widget.CoordinatorLayout> </androidx.coordinatorlayout.widget.CoordinatorLayout>

View File

@ -1,41 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<merge
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<com.leinardi.android.speeddial.SpeedDialOverlayLayout
android:id="@+id/fabSDOverlay"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:importantForAccessibility="no" />
<!-- The FAB SpeedDial
1. MUST be placed at the bottom of the layout xml to ensure it is at the front,
clickable on Pre-Lollipop devices (that do not support elevation).
See: https://stackoverflow.com/a/2614402
2. ScrollView is needed to ensure the vertical list of speed dials are
accessible when screen height is small, eg., landscape mode on most phones.
-->
<ScrollView
android:id="@+id/fabSDScrollCtr"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|right"
android:layout_alignParentBottom="true"
android:layout_alignParentEnd="true"
android:layout_alignParentRight="true"
android:elevation="@dimen/sd_open_elevation">
<com.leinardi.android.speeddial.SpeedDialView
android:id="@+id/fabSD"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:accessibilityTraversalBefore="@android:id/list"
android:contentDescription="@string/apply_action"
android:visibility="gone"
app:sdMainFabClosedSrc="@drawable/ic_fab_edit"
app:sdOverlayLayout="@id/fabSDOverlay" />
</ScrollView>
</merge>

View File

@ -6,7 +6,7 @@
<dimen name="drawer_corner_size">16dp</dimen> <dimen name="drawer_corner_size">16dp</dimen>
<dimen name="widget_margin">0dp</dimen> <dimen name="widget_margin">0dp</dimen>
<dimen name="widget_inner_radius">4dp</dimen> <dimen name="widget_inner_radius">4dp</dimen>
<dimen name="external_player_height">135dp</dimen> <dimen name="external_player_height">110dp</dimen>
<dimen name="text_size_micro">12sp</dimen> <dimen name="text_size_micro">12sp</dimen>
<dimen name="text_size_small">14sp</dimen> <dimen name="text_size_small">14sp</dimen>
<dimen name="text_size_navdrawer">16sp</dimen> <dimen name="text_size_navdrawer">16sp</dimen>

View File

@ -1,3 +1,15 @@
# 6.11.4
* corrected color contrast in SwipeActions dialog
* removed the empty space on top of playerUI
* largely improved scroll performance of episodes list caused by image loading
* fixed title text out of screen issue in some headers
* fixed swipe actions not initialized in Queue Bin
* in FeedInfo details, removed inapplicable items for synthetic feeds
* when removing synthetic feed, record all episodes in the feed in SubscriptionLog
* updated some Compose dependencies
* speed-dial dependency removed and old bottomSheet multi-select codes cleaned up
# 6.11.3 # 6.11.3
* supports Youtube live episodes received from share * supports Youtube live episodes received from share

View File

@ -0,0 +1,11 @@
Version 6.11.4
* corrected color contrast in SwipeActions dialog
* removed the empty space on top of playerUI
* largely improved scroll performance of episodes list caused by image loading
* fixed title text out of screen issue in some headers
* fixed swipe actions not initialized in Queue Bin
* in FeedInfo details, removed inapplicable items for synthetic feeds
* when removing synthetic feed, record all episodes in the feed in SubscriptionLog
* updated some Compose dependencies
* speed-dial dependency removed and old bottomSheet multi-select codes cleaned up

View File

@ -1,14 +1,14 @@
[versions] [versions]
activityCompose = "1.9.2" activityCompose = "1.9.3"
annotation = "1.8.2" annotation = "1.9.0"
appcompat = "1.7.0" appcompat = "1.7.0"
awaitility = "4.2.1" awaitility = "4.2.1"
balloon = "1.6.6" balloon = "1.6.6"
coil = "2.7.0" coil = "2.7.0"
commonsLang3 = "3.15.0" commonsLang3 = "3.15.0"
commonsIo = "2.16.1" commonsIo = "2.16.1"
composeBom = "2024.09.03" composeBom = "2024.10.00"
conscryptAndroid = "2.5.2" conscryptAndroid = "2.5.2"
constraintlayoutCompose = "1.0.1" constraintlayoutCompose = "1.0.1"
coordinatorlayout = "1.2.0" coordinatorlayout = "1.2.0"
@ -63,11 +63,11 @@ runner = "1.6.2"
rxandroid = "3.0.2" rxandroid = "3.0.2"
rxjava = "2.2.21" rxjava = "2.2.21"
rxjavaVersion = "3.1.8" rxjavaVersion = "3.1.8"
speedDial = "3.3.0" #speedDial = "3.3.0"
searchpreference = "v2.5.0" searchpreference = "v2.5.0"
stream = "1.2.2" stream = "1.2.2"
uiToolingPreview = "1.7.3" uiToolingPreview = "1.7.4"
uiTooling = "1.7.3" uiTooling = "1.7.4"
viewpager2 = "1.1.0" viewpager2 = "1.1.0"
vistaguide = "lv0.24.2.6" vistaguide = "lv0.24.2.6"
wearable = "2.9.0" wearable = "2.9.0"
@ -151,7 +151,7 @@ rxandroid = { module = "io.reactivex.rxjava3:rxandroid", version.ref = "rxandroi
rxjava3-rxjava = { module = "io.reactivex.rxjava3:rxjava", version.ref = "rxjavaVersion" } rxjava3-rxjava = { module = "io.reactivex.rxjava3:rxjava", version.ref = "rxjavaVersion" }
rxjava = { module = "io.reactivex.rxjava2:rxjava", version.ref = "rxjava" } rxjava = { module = "io.reactivex.rxjava2:rxjava", version.ref = "rxjava" }
searchpreference = { module = "com.github.ByteHamster:SearchPreference", version.ref = "searchpreference" } searchpreference = { module = "com.github.ByteHamster:SearchPreference", version.ref = "searchpreference" }
speed-dial = { module = "com.leinardi.android:speed-dial", version.ref = "speedDial" } #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" } 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" } desugar_jdk_libs_nio = { module = "com.android.tools:desugar_jdk_libs_nio", version.ref = "desugar_jdk_libs_nio" }