6.11.3 commit
This commit is contained in:
parent
61ce79f667
commit
a38d7766fc
|
@ -31,8 +31,8 @@ android {
|
|||
testApplicationId "ac.mdiq.podcini.tests"
|
||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||
|
||||
versionCode 3020272
|
||||
versionName "6.11.2"
|
||||
versionCode 3020273
|
||||
versionName "6.11.3"
|
||||
|
||||
applicationId "ac.mdiq.podcini.R"
|
||||
def commit = ""
|
||||
|
|
|
@ -54,7 +54,6 @@ class FeedBuilder(val context: Context, val showError: (String?, String)->Unit)
|
|||
val eList: MutableList<Episode> = mutableListOf()
|
||||
|
||||
val uURL = URL(url)
|
||||
// if (url.startsWith("https://youtube.com/playlist?") || url.startsWith("https://music.youtube.com/playlist?")) {
|
||||
if (uURL.path.startsWith("/playlist") || uURL.path.startsWith("/playlist")) {
|
||||
val playlistInfo = PlaylistInfo.getInfo(Vista.getService(0), url) ?: return@launch
|
||||
feed_.title = playlistInfo.name
|
||||
|
|
|
@ -1436,10 +1436,10 @@ class PlaybackService : MediaLibraryService() {
|
|||
Logd(TAG, "setDataSource1 audioStreamsList ${audioStreamsList.size}")
|
||||
val audioIndex = if (isNetworkRestricted && prefLowQualityMedia) 0 else audioStreamsList.size - 1
|
||||
val audioStream = audioStreamsList[audioIndex]
|
||||
Logd(TAG, "setDataSource1 use audio quality: ${audioStream.bitrate}")
|
||||
Logd(TAG, "setDataSource1 use audio quality: ${audioStream.bitrate} forceVideo: ${media.forceVideo}")
|
||||
val aSource = DefaultMediaSourceFactory(context).createMediaSource(
|
||||
MediaItem.Builder().setMediaMetadata(metadata).setTag(metadata).setUri(Uri.parse(audioStream.content)).build())
|
||||
if (media.episode?.feed?.preferences?.videoModePolicy != VideoMode.AUDIO_ONLY) {
|
||||
if (media.forceVideo || media.episode?.feed?.preferences?.videoModePolicy != VideoMode.AUDIO_ONLY) {
|
||||
Logd(TAG, "setDataSource1 result: $streamInfo")
|
||||
Logd(TAG, "setDataSource1 videoStreams: ${streamInfo.videoStreams.size} videoOnlyStreams: ${streamInfo.videoOnlyStreams.size} audioStreams: ${streamInfo.audioStreams.size}")
|
||||
val videoStreamsList = getSortedStreamVideosList(streamInfo.videoStreams, streamInfo.videoOnlyStreams, true, true)
|
||||
|
@ -2502,12 +2502,10 @@ class PlaybackService : MediaLibraryService() {
|
|||
|
||||
private fun setToFallbackSpeed(speed: Float) {
|
||||
if (playbackService?.mPlayer == null || playbackService!!.isSpeedForward) return
|
||||
|
||||
if (!playbackService!!.isFallbackSpeed) {
|
||||
playbackService!!.normalSpeed = playbackService!!.mPlayer!!.getPlaybackSpeed()
|
||||
playbackService!!.mPlayer!!.setPlaybackParams(speed, isSkipSilence)
|
||||
} else playbackService!!.mPlayer!!.setPlaybackParams(playbackService!!.normalSpeed, isSkipSilence)
|
||||
|
||||
playbackService!!.isFallbackSpeed = !playbackService!!.isFallbackSpeed
|
||||
}
|
||||
|
||||
|
@ -2526,6 +2524,7 @@ class PlaybackService : MediaLibraryService() {
|
|||
playbackService?.mPlayer?.pause(true, reinit = false)
|
||||
playbackService?.isSpeedForward = false
|
||||
playbackService?.isFallbackSpeed = false
|
||||
(curMedia as? EpisodeMedia)?.forceVideo = false
|
||||
}
|
||||
PlayerStatus.PAUSED, PlayerStatus.PREPARED -> {
|
||||
playbackService?.mPlayer?.resume()
|
||||
|
|
|
@ -9,6 +9,9 @@ import ac.mdiq.podcini.util.showStackTrace
|
|||
import android.content.Context
|
||||
import android.os.Parcel
|
||||
import android.os.Parcelable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.setValue
|
||||
import io.realm.kotlin.ext.isManaged
|
||||
import io.realm.kotlin.types.EmbeddedRealmObject
|
||||
import io.realm.kotlin.types.annotations.Ignore
|
||||
|
@ -68,6 +71,9 @@ class EpisodeMedia: EmbeddedRealmObject, Playable {
|
|||
// if null: unknown, will be checked
|
||||
var hasEmbeddedPicture: Boolean? = null
|
||||
|
||||
@Ignore
|
||||
var forceVideo by mutableStateOf(false)
|
||||
|
||||
/* Used for loading item when restoring from parcel. */
|
||||
// var episodeId: Long = 0
|
||||
// private set
|
||||
|
|
|
@ -82,6 +82,7 @@ abstract class EpisodeActionButton internal constructor(@JvmField var item: Epis
|
|||
Card(modifier = Modifier.wrapContentSize(align = Alignment.Center).padding(16.dp), shape = RoundedCornerShape(16.dp)) {
|
||||
Row(modifier = Modifier.padding(16.dp), horizontalArrangement = Arrangement.spacedBy(8.dp)) {
|
||||
val label = getLabel()
|
||||
Logd(TAG, "button label: $label")
|
||||
if (label != R.string.play_label && label != R.string.pause_label && label != R.string.download_label) {
|
||||
IconButton(onClick = {
|
||||
PlayActionButton(item).onClick(context)
|
||||
|
@ -140,9 +141,9 @@ abstract class EpisodeActionButton internal constructor(@JvmField var item: Epis
|
|||
|
||||
fun playVideoIfNeeded(context: Context, media: Playable) {
|
||||
val item = (media as? EpisodeMedia)?.episode
|
||||
if (item?.feed?.preferences?.videoModePolicy != VideoMode.AUDIO_ONLY
|
||||
if ((media as? EpisodeMedia)?.forceVideo == true || (item?.feed?.preferences?.videoModePolicy != VideoMode.AUDIO_ONLY
|
||||
&& videoPlayMode != VideoMode.AUDIO_ONLY.code && videoMode != VideoMode.AUDIO_ONLY
|
||||
&& media.getMediaType() == MediaType.VIDEO)
|
||||
&& media.getMediaType() == MediaType.VIDEO))
|
||||
context.startActivity(getPlayerActivityIntent(context, MediaType.VIDEO))
|
||||
}
|
||||
}
|
||||
|
@ -365,7 +366,6 @@ class StreamActionButton(item: Episode) : EpisodeActionButton(item) {
|
|||
}
|
||||
|
||||
companion object {
|
||||
|
||||
fun stream(context: Context, media: Playable) {
|
||||
if (media !is EpisodeMedia || !InTheatre.isCurMedia(media)) PlaybackService.clearCurTempSpeed()
|
||||
PlaybackServiceStarter(context, media).shouldStreamThisTime(true).callEvenIfRunning(true).start()
|
||||
|
|
|
@ -138,11 +138,11 @@ class MainActivity : CastEnabledActivity() {
|
|||
}
|
||||
}
|
||||
override fun onSlide(view: View, slideOffset: Float) {
|
||||
val audioPlayer = supportFragmentManager.findFragmentByTag(AudioPlayerFragment.TAG) as? AudioPlayerFragment ?: return
|
||||
// val audioPlayer = supportFragmentManager.findFragmentByTag(AudioPlayerFragment.TAG) as? AudioPlayerFragment ?: return
|
||||
// if (slideOffset == 0.0f) { //STATE_COLLAPSED
|
||||
// audioPlayer.scrollToTop()
|
||||
// }
|
||||
audioPlayer.fadePlayerToToolbar(slideOffset)
|
||||
// audioPlayer.fadePlayerToToolbar(slideOffset)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -397,10 +397,10 @@ class MainActivity : CastEnabledActivity() {
|
|||
navigationBarInsets.bottom + (if (visible) externalPlayerHeight else 0))
|
||||
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
|
||||
// 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
|
||||
}
|
||||
|
||||
|
|
|
@ -117,7 +117,7 @@ class ShareReceiverActivity : AppCompatActivity() {
|
|||
if (finish) activity.finish()
|
||||
}
|
||||
// Youtube media
|
||||
(isYoutubeURL(url) && url.path.startsWith("/watch")) || isYoutubeServiceURL(url) -> {
|
||||
(isYoutubeURL(url) && (url.path.startsWith("/watch") || url.path.startsWith("/live"))) || isYoutubeServiceURL(url) -> {
|
||||
if (log != null) upsertBlk(log) {it.type = "youtube media" }
|
||||
Logd(TAG, "got youtube media")
|
||||
mediaCB()
|
||||
|
|
|
@ -102,8 +102,11 @@ class VideoplayerActivity : CastEnabledActivity() {
|
|||
var vmCode = 0
|
||||
if (curMedia is EpisodeMedia) {
|
||||
val media_ = curMedia as EpisodeMedia
|
||||
val vPol = media_.episode?.feed?.preferences?.videoModePolicy
|
||||
if (vPol != null && vPol != VideoMode.NONE) vmCode = vPol.code
|
||||
var vPol = media_.episode?.feed?.preferences?.videoModePolicy
|
||||
if (vPol != null) {
|
||||
if (vPol == VideoMode.AUDIO_ONLY && media_.forceVideo) vPol = VideoMode.WINDOW_VIEW
|
||||
if (vPol != VideoMode.NONE) vmCode = vPol.code
|
||||
}
|
||||
}
|
||||
Logd(TAG, "onCreate vmCode: $vmCode")
|
||||
if (vmCode == 0) vmCode = videoPlayMode
|
||||
|
@ -290,6 +293,7 @@ class VideoplayerActivity : CastEnabledActivity() {
|
|||
when (item.itemId) {
|
||||
R.id.player_switch_to_audio_only -> {
|
||||
switchToAudioOnly = true
|
||||
(curMedia as? EpisodeMedia)?.forceVideo = false
|
||||
finish()
|
||||
return true
|
||||
}
|
||||
|
@ -703,23 +707,23 @@ class VideoplayerActivity : CastEnabledActivity() {
|
|||
lifecycleScope.launch {
|
||||
try {
|
||||
episode = withContext(Dispatchers.IO) {
|
||||
var feedItem = (curMedia as? EpisodeMedia)?.episodeOrFetch()
|
||||
if (feedItem != null) {
|
||||
val duration = feedItem.media?.getDuration() ?: Int.MAX_VALUE
|
||||
val url = feedItem.media?.downloadUrl
|
||||
var episode_ = (curMedia as? EpisodeMedia)?.episodeOrFetch()
|
||||
if (episode_ != null) {
|
||||
val duration = episode_.media?.getDuration() ?: Int.MAX_VALUE
|
||||
val url = episode_.media?.downloadUrl
|
||||
val shownotesCleaner = ShownotesCleaner(requireContext())
|
||||
if (url?.contains("youtube.com") == true && feedItem.description?.startsWith("Short:") == true) {
|
||||
Logd(TAG, "getting extended description: ${feedItem.title}")
|
||||
if (url?.contains("youtube.com") == true && episode_.description?.startsWith("Short:") == true) {
|
||||
Logd(TAG, "getting extended description: ${episode_.title}")
|
||||
try {
|
||||
val info = feedItem.streamInfo
|
||||
val info = episode_.streamInfo
|
||||
if (info?.description?.content != null) {
|
||||
feedItem = upsert(feedItem) { it.description = info.description?.content }
|
||||
episode_ = upsert(episode_) { it.description = info.description?.content }
|
||||
webviewData = shownotesCleaner.processShownotes(info.description!!.content, duration)
|
||||
} else webviewData = shownotesCleaner.processShownotes(episode!!.description ?: "", duration)
|
||||
} else webviewData = shownotesCleaner.processShownotes(episode_.description ?: "", duration)
|
||||
} catch (e: Exception) { Logd(TAG, "StreamInfo error: ${e.message}") }
|
||||
} else webviewData = shownotesCleaner.processShownotes(episode!!.description ?: "", duration)
|
||||
} else webviewData = shownotesCleaner.processShownotes(episode_.description ?: "", duration)
|
||||
}
|
||||
feedItem
|
||||
episode_
|
||||
}
|
||||
withContext(Dispatchers.Main) {
|
||||
Logd(TAG, "load() item ${episode?.id}")
|
||||
|
@ -798,6 +802,7 @@ class VideoplayerActivity : CastEnabledActivity() {
|
|||
binding.toggleViews.setOnClickListener { (activity as VideoplayerActivity).toggleViews() }
|
||||
binding.audioOnly.setOnClickListener {
|
||||
(activity as? VideoplayerActivity)?.switchToAudioOnly = true
|
||||
(curMedia as? EpisodeMedia)?.forceVideo = false
|
||||
(activity as? VideoplayerActivity)?.finish()
|
||||
}
|
||||
if (!itemsLoaded) webvDescription?.loadDataWithBaseURL("https://127.0.0.1", webviewData,
|
||||
|
|
|
@ -679,13 +679,14 @@ fun EpisodeLazyColumn(activity: MainActivity, vms: SnapshotStateList<EpisodeVM>,
|
|||
if (index>=vms.size) return@LaunchedEffect
|
||||
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}")
|
||||
vm.actionButton = EpisodeActionButton.forItem(vms[index].episode)
|
||||
vm.actionButton = EpisodeActionButton.forItem(vm.episode)
|
||||
vm.actionRes = vm.actionButton!!.getDrawable()
|
||||
}
|
||||
LaunchedEffect(key1 = status) {
|
||||
if (index>=vms.size) return@LaunchedEffect
|
||||
Logd(TAG, "LaunchedEffect $index isPlayingState: ${vms[index].isPlayingState} ${vms[index].episode.title}")
|
||||
vm.actionButton = EpisodeActionButton.forItem(vms[index].episode)
|
||||
vm.actionButton = EpisodeActionButton.forItem(vm.episode)
|
||||
Logd(TAG, "LaunchedEffect vm.actionButton: ${vm.actionButton?.getLabel()}")
|
||||
vm.actionRes = vm.actionButton!!.getDrawable()
|
||||
}
|
||||
// LaunchedEffect(vm.isPlayingState) {
|
||||
|
@ -696,7 +697,7 @@ fun EpisodeLazyColumn(activity: MainActivity, vms: SnapshotStateList<EpisodeVM>,
|
|||
}
|
||||
Box(modifier = Modifier.width(40.dp).height(40.dp).padding(end = 10.dp).align(Alignment.CenterVertically).pointerInput(Unit) {
|
||||
detectTapGestures(onLongPress = { vm.showAltActionsDialog = true }, onTap = {
|
||||
vm.actionButton?.onClick(activity)
|
||||
vms[index].actionButton?.onClick(activity)
|
||||
})
|
||||
}, contentAlignment = Alignment.Center) {
|
||||
// actionRes = actionButton.getDrawable()
|
||||
|
|
|
@ -294,8 +294,7 @@ class SleepTimerDialog : DialogFragment() {
|
|||
paintDial.strokeWidth = size * 0.01f
|
||||
val textPos = radToPoint(i / 24.0f * 360f, size / 2 - 2.5f * padding)
|
||||
paintText.textSize = 0.4f * padding
|
||||
canvas.drawText(i.toString(), textPos.x.toFloat(),
|
||||
textPos.y + (-paintText.descent() - paintText.ascent()) / 2, paintText)
|
||||
canvas.drawText(i.toString(), textPos.x.toFloat(), textPos.y + (-paintText.descent() - paintText.ascent()) / 2, paintText)
|
||||
}
|
||||
val outer = radToPoint(i / 24.0f * 360f, size / 2 - 1.7f * padding)
|
||||
val inner = radToPoint(i / 24.0f * 360f, size / 2 - 1.9f * padding)
|
||||
|
|
|
@ -1,16 +1,15 @@
|
|||
package ac.mdiq.podcini.ui.fragment
|
||||
|
||||
import ac.mdiq.podcini.R
|
||||
import ac.mdiq.podcini.databinding.AudioplayerFragmentBinding
|
||||
import ac.mdiq.podcini.net.utils.NetworkUtils.fetchHtmlSource
|
||||
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.MediaPlayerBase
|
||||
import ac.mdiq.podcini.playback.base.MediaPlayerBase.Companion.status
|
||||
import ac.mdiq.podcini.playback.base.PlayerStatus
|
||||
import ac.mdiq.podcini.playback.base.VideoMode
|
||||
import ac.mdiq.podcini.playback.cast.CastEnabledActivity
|
||||
import ac.mdiq.podcini.playback.service.PlaybackService.Companion.curDurationFB
|
||||
import ac.mdiq.podcini.playback.service.PlaybackService.Companion.curPositionFB
|
||||
import ac.mdiq.podcini.playback.service.PlaybackService.Companion.curSpeedFB
|
||||
|
@ -40,7 +39,6 @@ import ac.mdiq.podcini.ui.compose.ChaptersDialog
|
|||
import ac.mdiq.podcini.ui.compose.ChooseRatingDialog
|
||||
import ac.mdiq.podcini.ui.compose.CustomTheme
|
||||
import ac.mdiq.podcini.ui.dialog.*
|
||||
import ac.mdiq.podcini.ui.fragment.EpisodeInfoFragment.Companion
|
||||
import ac.mdiq.podcini.ui.utils.ShownotesCleaner
|
||||
import ac.mdiq.podcini.ui.view.ShownotesWebView
|
||||
import ac.mdiq.podcini.util.EventFlow
|
||||
|
@ -52,9 +50,11 @@ import android.content.*
|
|||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.util.Log
|
||||
import android.view.*
|
||||
import android.view.KeyEvent
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.Toast
|
||||
import androidx.appcompat.widget.Toolbar
|
||||
import androidx.compose.foundation.*
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.material3.Icon
|
||||
|
@ -64,11 +64,13 @@ import androidx.compose.material3.Text
|
|||
import androidx.compose.runtime.*
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.ComposeView
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.viewinterop.AndroidView
|
||||
import androidx.compose.ui.zIndex
|
||||
import androidx.core.app.ShareCompat
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.core.text.HtmlCompat
|
||||
|
@ -77,7 +79,6 @@ import androidx.lifecycle.lifecycleScope
|
|||
import androidx.media3.common.util.UnstableApi
|
||||
import androidx.media3.session.MediaController
|
||||
import coil.compose.AsyncImage
|
||||
import com.google.android.material.appbar.MaterialToolbar
|
||||
import com.google.android.material.bottomsheet.BottomSheetBehavior
|
||||
import com.google.android.material.snackbar.Snackbar
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
|
@ -90,15 +91,9 @@ import org.apache.commons.lang3.StringUtils
|
|||
import java.text.DecimalFormat
|
||||
import java.text.NumberFormat
|
||||
import kotlin.math.max
|
||||
import kotlin.math.min
|
||||
|
||||
@UnstableApi
|
||||
class AudioPlayerFragment : Fragment(), Toolbar.OnMenuItemClickListener {
|
||||
var _binding: AudioplayerFragmentBinding? = null
|
||||
private val binding get() = _binding!!
|
||||
|
||||
private lateinit var toolbar: MaterialToolbar
|
||||
private var showPlayer1 by mutableStateOf(true)
|
||||
class AudioPlayerFragment : Fragment() {
|
||||
private var isCollapsed by mutableStateOf(true)
|
||||
|
||||
// private lateinit var controllerFuture: ListenableFuture<MediaController>
|
||||
|
@ -122,6 +117,7 @@ class AudioPlayerFragment : Fragment(), Toolbar.OnMenuItemClickListener {
|
|||
private var duration by mutableIntStateOf(0)
|
||||
private var txtvLengtTexth by mutableStateOf("")
|
||||
private var sliderValue by mutableFloatStateOf(0f)
|
||||
private var sleepTimerActive by mutableStateOf(isSleepTimerActive())
|
||||
|
||||
private var shownotesCleaner: ShownotesCleaner? = null
|
||||
|
||||
|
@ -145,48 +141,41 @@ class AudioPlayerFragment : Fragment(), Toolbar.OnMenuItemClickListener {
|
|||
|
||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
|
||||
super.onCreateView(inflater, container, savedInstanceState)
|
||||
_binding = AudioplayerFragmentBinding.inflate(inflater)
|
||||
binding.root.setOnTouchListener { _: View?, _: MotionEvent? -> true } // Avoid clicks going through player to fragments below
|
||||
|
||||
Logd(TAG, "fragment onCreateView")
|
||||
toolbar = binding.toolbar
|
||||
toolbar.title = ""
|
||||
toolbar.setNavigationOnClickListener {
|
||||
val bottomSheet = (activity as MainActivity).bottomSheet
|
||||
bottomSheet.state = BottomSheetBehavior.STATE_COLLAPSED
|
||||
}
|
||||
toolbar.setOnMenuItemClickListener(this)
|
||||
controller = createHandler()
|
||||
controller!!.init()
|
||||
onCollaped()
|
||||
|
||||
binding.player1.setContent {
|
||||
CustomTheme(requireContext()) {
|
||||
if (showPlayer1) PlayerUI()
|
||||
else Spacer(modifier = Modifier.size(0.dp))
|
||||
val composeView = ComposeView(requireContext()).apply {
|
||||
setContent {
|
||||
CustomTheme(requireContext()) {
|
||||
// Column(modifier = Modifier.fillMaxSize().statusBarsPadding().navigationBarsPadding() ) {
|
||||
// if (isCollapsed) PlayerUI()
|
||||
//// 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) {
|
||||
Column(Modifier.padding(bottom = 90.dp)) {
|
||||
Toolbar()
|
||||
DetailUI(modifier = Modifier)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
binding.composeDetailView.setContent {
|
||||
CustomTheme(requireContext()) {
|
||||
DetailUI()
|
||||
// if (!isCollapsed) DetailUI()
|
||||
// else Spacer(modifier = Modifier.size(0.dp))
|
||||
}
|
||||
}
|
||||
binding.player2.setContent {
|
||||
CustomTheme(requireContext()) {
|
||||
if (!showPlayer1) PlayerUI()
|
||||
else Spacer(modifier = Modifier.size(0.dp))
|
||||
}
|
||||
}
|
||||
// cardViewSeek = binding.cardViewSeek
|
||||
(activity as MainActivity).setPlayerVisible(false)
|
||||
return binding.root
|
||||
return composeView
|
||||
}
|
||||
|
||||
override fun onDestroyView() {
|
||||
Logd(TAG, "Fragment destroyed")
|
||||
_binding = null
|
||||
controller?.release()
|
||||
controller = null
|
||||
// MediaController.releaseFuture(controllerFuture)
|
||||
|
@ -195,9 +184,9 @@ class AudioPlayerFragment : Fragment(), Toolbar.OnMenuItemClickListener {
|
|||
|
||||
@OptIn(ExperimentalFoundationApi::class)
|
||||
@Composable
|
||||
fun PlayerUI() {
|
||||
Column(modifier = Modifier.fillMaxWidth()) {
|
||||
val textColor = MaterialTheme.colorScheme.onSurface
|
||||
fun PlayerUI(modifier: Modifier) {
|
||||
val textColor = MaterialTheme.colorScheme.onSurface
|
||||
Column(modifier = modifier.fillMaxWidth().background(MaterialTheme.colorScheme.surface)) {
|
||||
Text(titleText, maxLines = 1, color = textColor, style = MaterialTheme.typography.bodyMedium)
|
||||
Slider(value = sliderValue, valueRange = 0f..duration.toFloat(),
|
||||
// colors = SliderDefaults.colors(
|
||||
|
@ -231,7 +220,7 @@ class AudioPlayerFragment : Fragment(), Toolbar.OnMenuItemClickListener {
|
|||
if (playbackService == null) PlaybackServiceStarter(requireContext(), curMedia!!).start()
|
||||
}
|
||||
AsyncImage(model = imgLoc, contentDescription = "imgvCover", placeholder = painterResource(R.mipmap.ic_launcher), error = painterResource(R.mipmap.ic_launcher),
|
||||
modifier = Modifier.width(70.dp).height(70.dp).padding(start = 5.dp)
|
||||
modifier = Modifier.width(65.dp).height(65.dp).padding(start = 5.dp)
|
||||
.clickable(onClick = {
|
||||
Logd(TAG, "playerUiFragment icon was clicked")
|
||||
if (isCollapsed) {
|
||||
|
@ -255,7 +244,7 @@ class AudioPlayerFragment : Fragment(), Toolbar.OnMenuItemClickListener {
|
|||
Column(horizontalAlignment = Alignment.CenterHorizontally) {
|
||||
Icon(painter = painterResource(R.drawable.ic_playback_speed), tint = textColor,
|
||||
contentDescription = "speed",
|
||||
modifier = Modifier.width(48.dp).height(48.dp).clickable(onClick = {
|
||||
modifier = Modifier.width(43.dp).height(43.dp).clickable(onClick = {
|
||||
VariableSpeedDialog.newInstance(booleanArrayOf(true, true, true), null)?.show(childFragmentManager, null)
|
||||
}))
|
||||
Text(txtvPlaybackSpeed, color = textColor, style = MaterialTheme.typography.bodySmall)
|
||||
|
@ -264,7 +253,7 @@ class AudioPlayerFragment : Fragment(), Toolbar.OnMenuItemClickListener {
|
|||
Column(horizontalAlignment = Alignment.CenterHorizontally) {
|
||||
Icon(painter = painterResource(R.drawable.ic_fast_rewind), tint = textColor,
|
||||
contentDescription = "rewind",
|
||||
modifier = Modifier.width(48.dp).height(48.dp).combinedClickable(onClick = {
|
||||
modifier = Modifier.width(43.dp).height(43.dp).combinedClickable(onClick = {
|
||||
if (controller != null && playbackService?.isServiceReady() == true) {
|
||||
playbackService?.mPlayer?.seekDelta(-UserPreferences.rewindSecs * 1000)
|
||||
}
|
||||
|
@ -296,7 +285,7 @@ class AudioPlayerFragment : Fragment(), Toolbar.OnMenuItemClickListener {
|
|||
Column(horizontalAlignment = Alignment.CenterHorizontally) {
|
||||
Icon(painter = painterResource(R.drawable.ic_fast_forward), tint = textColor,
|
||||
contentDescription = "forward",
|
||||
modifier = Modifier.width(48.dp).height(48.dp).combinedClickable(onClick = {
|
||||
modifier = Modifier.width(43.dp).height(43.dp).combinedClickable(onClick = {
|
||||
if (controller != null && playbackService?.isServiceReady() == true) {
|
||||
playbackService?.mPlayer?.seekDelta(UserPreferences.fastForwardSecs * 1000)
|
||||
}
|
||||
|
@ -317,7 +306,7 @@ class AudioPlayerFragment : Fragment(), Toolbar.OnMenuItemClickListener {
|
|||
}
|
||||
Icon(painter = painterResource(R.drawable.ic_skip_48dp), tint = textColor,
|
||||
contentDescription = "rewind",
|
||||
modifier = Modifier.width(48.dp).height(48.dp).combinedClickable(onClick = {
|
||||
modifier = Modifier.width(43.dp).height(43.dp).combinedClickable(onClick = {
|
||||
if (controller != null && MediaPlayerBase.status == PlayerStatus.PLAYING) {
|
||||
val speedForward = UserPreferences.speedforwardSpeed
|
||||
if (speedForward > 0.1f) speedForward(speedForward)
|
||||
|
@ -332,9 +321,72 @@ class AudioPlayerFragment : Fragment(), Toolbar.OnMenuItemClickListener {
|
|||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun Toolbar() {
|
||||
val media: Playable = curMedia ?: return
|
||||
val feedItem = if (media is EpisodeMedia) media.episodeOrFetch() else null
|
||||
val textColor = MaterialTheme.colorScheme.onSurface
|
||||
val mediaType = curMedia?.getMediaType()
|
||||
val notAudioOnly = (curMedia as? EpisodeMedia)?.episode?.feed?.preferences?.videoModePolicy != VideoMode.AUDIO_ONLY
|
||||
Row(modifier = Modifier.fillMaxWidth().padding(10.dp), horizontalArrangement = Arrangement.SpaceBetween) {
|
||||
Icon(painter = painterResource(R.drawable.ic_arrow_down), tint = textColor, contentDescription = "Collapse", modifier = Modifier.clickable {
|
||||
(activity as MainActivity).bottomSheet.setState(BottomSheetBehavior.STATE_COLLAPSED)
|
||||
})
|
||||
var homeIcon by remember { mutableIntStateOf(R.drawable.baseline_home_24)}
|
||||
Icon(painter = painterResource(homeIcon), tint = textColor, contentDescription = "Home", modifier = Modifier.clickable {
|
||||
homeIcon = if (showHomeText) R.drawable.ic_home else R.drawable.outline_home_24
|
||||
buildHomeReaderText()
|
||||
})
|
||||
if (mediaType == MediaType.VIDEO) Icon(painter = painterResource(R.drawable.baseline_fullscreen_24), tint = textColor, contentDescription = "Play video",
|
||||
modifier = Modifier.clickable {
|
||||
if (notAudioOnly || (curMedia as? EpisodeMedia)?.forceVideo == true) {
|
||||
// playPause()
|
||||
} else {
|
||||
(curMedia as? EpisodeMedia)?.forceVideo = true
|
||||
status = PlayerStatus.STOPPED
|
||||
playbackService?.mPlayer?.pause(true, reinit = true)
|
||||
playbackService?.recreateMediaPlayer()
|
||||
}
|
||||
VideoPlayerActivityStarter(requireContext()).start()
|
||||
})
|
||||
if (controller != null) {
|
||||
val sleepRes = if (sleepTimerActive) R.drawable.ic_sleep_off else R.drawable.ic_sleep
|
||||
Icon(painter = painterResource(sleepRes), tint = textColor, contentDescription = "Sleep timer", modifier = Modifier.clickable {
|
||||
SleepTimerDialog().show(childFragmentManager, "SleepTimerDialog")
|
||||
})
|
||||
}
|
||||
if (currentMedia is EpisodeMedia) Icon(painter = painterResource(R.drawable.ic_feed), tint = textColor, contentDescription = "Open podcast",
|
||||
modifier = Modifier.clickable {
|
||||
if (feedItem?.feedId != null) {
|
||||
val intent: Intent = MainActivity.getIntentToOpenFeed(requireContext(), feedItem.feedId!!)
|
||||
startActivity(intent)
|
||||
}
|
||||
})
|
||||
Icon(painter = painterResource(R.drawable.ic_share), tint = textColor, contentDescription = "Share", modifier = Modifier.clickable {
|
||||
if (currentItem != null) {
|
||||
val shareDialog: ShareDialog = ShareDialog.newInstance(currentItem!!)
|
||||
shareDialog.show((requireActivity().supportFragmentManager), "ShareEpisodeDialog")
|
||||
}
|
||||
})
|
||||
Icon(painter = painterResource(R.drawable.baseline_offline_share_24), tint = textColor, contentDescription = "Share Note", modifier = Modifier.clickable {
|
||||
val notes = if (showHomeText) readerhtml else feedItem?.description
|
||||
if (!notes.isNullOrEmpty()) {
|
||||
val shareText = HtmlCompat.fromHtml(notes, HtmlCompat.FROM_HTML_MODE_COMPACT).toString()
|
||||
val context = requireContext()
|
||||
val intent = ShareCompat.IntentBuilder(context)
|
||||
.setType("text/plain")
|
||||
.setText(shareText)
|
||||
.setChooserTitle(R.string.share_notes_label)
|
||||
.createChooserIntent()
|
||||
context.startActivity(intent)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalFoundationApi::class)
|
||||
@Composable
|
||||
fun DetailUI() {
|
||||
fun DetailUI(modifier: Modifier) {
|
||||
var showChooseRatingDialog by remember { mutableStateOf(false) }
|
||||
if (showChooseRatingDialog) ChooseRatingDialog(listOf(currentItem!!)) {
|
||||
showChooseRatingDialog = false
|
||||
|
@ -343,7 +395,7 @@ class AudioPlayerFragment : Fragment(), Toolbar.OnMenuItemClickListener {
|
|||
if (showChaptersDialog) ChaptersDialog(media = currentMedia!!, onDismissRequest = {showChaptersDialog = false})
|
||||
|
||||
val scrollState = rememberScrollState()
|
||||
Column(modifier = Modifier.fillMaxWidth().verticalScroll(scrollState)) {
|
||||
Column(modifier = modifier.fillMaxWidth().verticalScroll(scrollState)) {
|
||||
val textColor = MaterialTheme.colorScheme.onSurface
|
||||
fun copyText(text: String): Boolean {
|
||||
val clipboardManager: ClipboardManager? = ContextCompat.getSystemService(requireContext(), ClipboardManager::class.java)
|
||||
|
@ -647,7 +699,7 @@ class AudioPlayerFragment : Fragment(), Toolbar.OnMenuItemClickListener {
|
|||
// if (isCollapsed) {
|
||||
isCollapsed = false
|
||||
if (shownotesCleaner == null) shownotesCleaner = ShownotesCleaner(requireContext())
|
||||
showPlayer1 = false
|
||||
// showPlayer1 = false
|
||||
if (currentMedia != null) updateUi(currentMedia!!)
|
||||
setIsShowPlay(isShowPlay)
|
||||
updateDetails()
|
||||
|
@ -657,7 +709,7 @@ class AudioPlayerFragment : Fragment(), Toolbar.OnMenuItemClickListener {
|
|||
fun onCollaped() {
|
||||
Logd(TAG, "onCollaped()")
|
||||
isCollapsed = true
|
||||
showPlayer1 = true
|
||||
// showPlayer1 = true
|
||||
if (currentMedia != null) updateUi(currentMedia!!)
|
||||
setIsShowPlay(isShowPlay)
|
||||
}
|
||||
|
@ -703,7 +755,7 @@ class AudioPlayerFragment : Fragment(), Toolbar.OnMenuItemClickListener {
|
|||
val item = (currentMedia as? EpisodeMedia)?.episodeOrFetch()
|
||||
if (item != null) setItem(item)
|
||||
setChapterDividers()
|
||||
setupOptionsMenu()
|
||||
sleepTimerActive = isSleepTimerActive()
|
||||
if (currentMedia != null) updateUi(currentMedia!!)
|
||||
// TODO: disable for now
|
||||
// if (!includingChapters) loadMediaInfo(true)
|
||||
|
@ -850,7 +902,7 @@ class AudioPlayerFragment : Fragment(), Toolbar.OnMenuItemClickListener {
|
|||
is FlowEvent.RatingEvent -> onRatingEvent(event)
|
||||
is FlowEvent.PlayerErrorEvent -> MediaPlayerErrorDialog.show(activity as Activity, event)
|
||||
// is FlowEvent.SleepTimerUpdatedEvent -> if (event.isCancelled || event.wasJustEnabled()) loadMediaInfo(false)
|
||||
is FlowEvent.SleepTimerUpdatedEvent -> if (event.isCancelled || event.wasJustEnabled()) setupOptionsMenu()
|
||||
is FlowEvent.SleepTimerUpdatedEvent -> if (event.isCancelled || event.wasJustEnabled()) sleepTimerActive = isSleepTimerActive()
|
||||
is FlowEvent.PlaybackPositionEvent -> onPlaybackPositionEvent(event)
|
||||
is FlowEvent.SpeedChangedEvent -> updatePlaybackSpeedButton(event)
|
||||
else -> {}
|
||||
|
@ -866,87 +918,11 @@ class AudioPlayerFragment : Fragment(), Toolbar.OnMenuItemClickListener {
|
|||
}
|
||||
}
|
||||
|
||||
private fun setupOptionsMenu() {
|
||||
if (toolbar.menu.size() == 0) toolbar.inflateMenu(R.menu.mediaplayer)
|
||||
|
||||
val isEpisodeMedia = currentMedia is EpisodeMedia
|
||||
toolbar.menu?.findItem(R.id.open_feed_item)?.setVisible(isEpisodeMedia)
|
||||
// val item = if (isEpisodeMedia) (currentMedia as EpisodeMedia).episodeOrFetch() else null
|
||||
// EpisodeMenuHandler.onPrepareMenu(toolbar.menu, item)
|
||||
|
||||
val mediaType = curMedia?.getMediaType()
|
||||
val notAudioOnly = (curMedia as? EpisodeMedia)?.episode?.feed?.preferences?.videoModePolicy != VideoMode.AUDIO_ONLY
|
||||
toolbar.menu?.findItem(R.id.show_video)?.setVisible(mediaType == MediaType.VIDEO && notAudioOnly)
|
||||
|
||||
if (controller != null) {
|
||||
toolbar.menu.findItem(R.id.set_sleeptimer_item).setVisible(!isSleepTimerActive())
|
||||
toolbar.menu.findItem(R.id.disable_sleeptimer_item).setVisible(isSleepTimerActive())
|
||||
}
|
||||
(activity as? CastEnabledActivity)?.requestCastButton(toolbar.menu)
|
||||
}
|
||||
|
||||
override fun onMenuItemClick(menuItem: MenuItem): Boolean {
|
||||
val media: Playable = curMedia ?: return false
|
||||
val feedItem = if (media is EpisodeMedia) media.episodeOrFetch() else null
|
||||
// if (feedItem != null && EpisodeMenuHandler.onMenuItemClicked(this, menuItem.itemId, feedItem)) return true
|
||||
|
||||
val itemId = menuItem.itemId
|
||||
when (itemId) {
|
||||
R.id.show_home_reader_view -> {
|
||||
if (showHomeText) menuItem.setIcon(R.drawable.ic_home)
|
||||
else menuItem.setIcon(R.drawable.outline_home_24)
|
||||
buildHomeReaderText()
|
||||
}
|
||||
R.id.show_video -> {
|
||||
playPause()
|
||||
VideoPlayerActivityStarter(requireContext()).start()
|
||||
}
|
||||
R.id.disable_sleeptimer_item, R.id.set_sleeptimer_item -> SleepTimerDialog().show(childFragmentManager, "SleepTimerDialog")
|
||||
R.id.open_feed_item -> {
|
||||
if (feedItem?.feedId != null) {
|
||||
val intent: Intent = MainActivity.getIntentToOpenFeed(requireContext(), feedItem.feedId!!)
|
||||
startActivity(intent)
|
||||
}
|
||||
}
|
||||
R.id.share_notes -> {
|
||||
val notes = if (showHomeText) readerhtml else feedItem?.description
|
||||
if (!notes.isNullOrEmpty()) {
|
||||
val shareText = HtmlCompat.fromHtml(notes, HtmlCompat.FROM_HTML_MODE_COMPACT).toString()
|
||||
val context = requireContext()
|
||||
val intent = ShareCompat.IntentBuilder(context)
|
||||
.setType("text/plain")
|
||||
.setText(shareText)
|
||||
.setChooserTitle(R.string.share_notes_label)
|
||||
.createChooserIntent()
|
||||
context.startActivity(intent)
|
||||
}
|
||||
}
|
||||
R.id.share_item -> {
|
||||
if (currentItem != null) {
|
||||
val shareDialog: ShareDialog = ShareDialog.newInstance(currentItem!!)
|
||||
shareDialog.show((requireActivity().supportFragmentManager), "ShareEpisodeDialog")
|
||||
}
|
||||
}
|
||||
else -> return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// fun scrollToTop() {
|
||||
//// binding.itemDescriptionFragment.scrollTo(0, 0)
|
||||
// savePreference()
|
||||
// }
|
||||
|
||||
fun fadePlayerToToolbar(slideOffset: Float) {
|
||||
val playerFadeProgress = (max(0.0, min(0.2, (slideOffset - 0.2f).toDouble())) / 0.2f).toFloat()
|
||||
val player = binding.player1
|
||||
player.alpha = 1 - playerFadeProgress
|
||||
player.visibility = if (playerFadeProgress > 0.99f) View.GONE else View.VISIBLE
|
||||
val toolbarFadeProgress = (max(0.0, min(0.2, (slideOffset - 0.6f).toDouble())) / 0.2f).toFloat()
|
||||
toolbar.setAlpha(toolbarFadeProgress)
|
||||
toolbar.visibility = if (toolbarFadeProgress < 0.01f) View.GONE else View.VISIBLE
|
||||
}
|
||||
|
||||
companion object {
|
||||
val TAG = AudioPlayerFragment::class.simpleName ?: "Anonymous"
|
||||
var media3Controller: MediaController? = null
|
||||
|
|
|
@ -32,6 +32,8 @@ import ac.mdiq.podcini.storage.utils.DurationConverter
|
|||
import ac.mdiq.podcini.storage.utils.ImageResourceUtils
|
||||
import ac.mdiq.podcini.ui.actions.*
|
||||
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.ChooseRatingDialog
|
||||
import ac.mdiq.podcini.ui.compose.CustomTheme
|
||||
|
@ -456,26 +458,6 @@ class EpisodeInfoFragment : Fragment(), Toolbar.OnMenuItemClickListener {
|
|||
else -> txtvSize = ""
|
||||
}
|
||||
|
||||
// val imgLocFB = ImageResourceUtils.getFallbackImageLocation(episode!!)
|
||||
// val imageLoader = imgvCover.context.imageLoader
|
||||
// val imageRequest = ImageRequest.Builder(requireContext())
|
||||
// .data(episode!!.imageLocation)
|
||||
// .placeholder(R.color.light_gray)
|
||||
// .listener(object : ImageRequest.Listener {
|
||||
// override fun onError(request: ImageRequest, result: ErrorResult) {
|
||||
// val fallbackImageRequest = ImageRequest.Builder(requireContext())
|
||||
// .data(imgLocFB)
|
||||
// .setHeader("User-Agent", "Mozilla/5.0")
|
||||
// .error(R.mipmap.ic_launcher)
|
||||
// .target(imgvCover)
|
||||
// .build()
|
||||
// imageLoader.enqueue(fallbackImageRequest)
|
||||
// }
|
||||
// })
|
||||
// .target(imgvCover)
|
||||
// .build()
|
||||
// imageLoader.enqueue(imageRequest)
|
||||
|
||||
updateButtons()
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:autoMirrored="true" android:height="24dp" android:tint="#000000" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp">
|
||||
|
||||
<path android:fillColor="@android:color/white" android:pathData="M14.6,10.26v1.31L17,9.33 14.6,7.1v1.28c-2.33,0.32 -3.26,1.92 -3.6,3.52 0.83,-1.13 1.93,-1.64 3.6,-1.64zM16,23L6,23c-1.1,0 -2,-0.9 -2,-2L4,5h2v16h10v2zM18,1h-8c-1.1,0 -2,0.9 -2,2v14c0,1.1 0.9,2 2,2h8c1.1,0 2,-0.9 2,-2L20,3c0,-1.1 -0.9,-2 -2,-2zM18,16h-8L10,4h8v12z"/>
|
||||
|
||||
</vector>
|
|
@ -1,47 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:id="@+id/audioplayer_fragment"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical">
|
||||
|
||||
<androidx.compose.ui.platform.ComposeView
|
||||
android:id="@+id/player1"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="@dimen/external_player_height"
|
||||
android:elevation="8dp"
|
||||
android:outlineProvider="none"/>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:fitsSystemWindows="true"
|
||||
android:orientation="vertical">
|
||||
|
||||
<com.google.android.material.appbar.MaterialToolbar
|
||||
android:id="@+id/toolbar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:minHeight="?android:attr/actionBarSize"
|
||||
android:theme="?attr/actionBarTheme"
|
||||
app:navigationContentDescription="@string/toolbar_back_button_content_description"
|
||||
app:navigationIcon="@drawable/ic_arrow_down" />
|
||||
|
||||
<androidx.compose.ui.platform.ComposeView
|
||||
android:id="@+id/composeDetailView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
android:layout_weight="1"
|
||||
android:layout_marginBottom="12dp"/>
|
||||
|
||||
<androidx.compose.ui.platform.ComposeView
|
||||
android:id="@+id/player2"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="@dimen/external_player_height"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
|
@ -7,8 +7,8 @@ buildscript {
|
|||
ext.kotlin_version = "$libs.versions.kotlin"
|
||||
dependencies {
|
||||
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
|
||||
classpath 'com.android.tools.build:gradle:8.5.2'
|
||||
classpath 'org.codehaus.groovy:groovy-xml:3.0.19'
|
||||
classpath libs.gradle
|
||||
classpath libs.groovy.xml
|
||||
}
|
||||
}
|
||||
|
||||
|
|
11
changelog.md
11
changelog.md
|
@ -1,3 +1,14 @@
|
|||
# 6.11.3
|
||||
|
||||
* supports Youtube live episodes received from share
|
||||
* fixed info not showing when playing video in window mode
|
||||
* AudioPlayer is fully in Compose, fixed the issue of top menu sometimes not shown
|
||||
* if you have podcast set to AudioOnly, you can tap on the square icon on the top bar of PlayerDetailed to force play video
|
||||
* this will re-construct the media item for the current episode to include video and plays audio-video together
|
||||
* it continues this way even after you close the video view and only listen
|
||||
* during this mode, you can switch between video and audio and the play is uninterrupted
|
||||
* it will resume playing audio only when you switch episodes and comeback to it
|
||||
|
||||
# 6.11.2
|
||||
|
||||
* fixed PlayerDetailed view not showing full info on Youtube media
|
||||
|
|
|
@ -0,0 +1,10 @@
|
|||
Version 6.11.3
|
||||
|
||||
* supports Youtube live episodes received from share
|
||||
* fixed info not showing when playing video in window mode
|
||||
* AudioPlayer is fully in Compose, fixed the issue of top menu sometimes not shown
|
||||
* if you have podcast set to AudioOnly, you can tap on the square icon on the top bar of PlayerDetailed to force play video
|
||||
* this will re-construct the media item for the current episode to include video and plays audio-video together
|
||||
* it continues this way even after you close the video view and only listen
|
||||
* during this mode, you can switch between video and audio and the play is uninterrupted
|
||||
* it will resume playing audio only when you switch episodes and comeback to it
|
|
@ -22,7 +22,9 @@ fontawesomeTypeface = "5.13.3.0-kotlin"
|
|||
fyydlin = "v0.5.0"
|
||||
googleMaterialTypeface = "4.0.0.3-kotlin"
|
||||
googleMaterialTypefaceOutlined = "4.0.0.2-kotlin"
|
||||
gradle = "8.5.2"
|
||||
gridlayout = "1.0.0"
|
||||
groovyXml = "3.0.19"
|
||||
iconicsCore = "5.5.0-b01"
|
||||
iconicsViews = "5.5.0-b01"
|
||||
javaxInject = "1"
|
||||
|
@ -123,6 +125,8 @@ fontawesome-typeface = { module = "com.mikepenz:fontawesome-typeface", version.r
|
|||
fyydlin = { module = "com.github.mfietz:fyydlin", version.ref = "fyydlin" }
|
||||
google-material-typeface-outlined = { module = "com.mikepenz:google-material-typeface-outlined", version.ref = "googleMaterialTypefaceOutlined" }
|
||||
google-material-typeface = { module = "com.mikepenz:google-material-typeface", version.ref = "googleMaterialTypeface" }
|
||||
gradle = { module = "com.android.tools.build:gradle", version.ref = "gradle" }
|
||||
groovy-xml = { module = "org.codehaus.groovy:groovy-xml", version.ref = "groovyXml" }
|
||||
iconics-views = { module = "com.mikepenz:iconics-views", version.ref = "iconicsViews" }
|
||||
iconics-core = { module = "com.mikepenz:iconics-core", version.ref = "iconicsCore" }
|
||||
javax-inject = { module = "javax.inject:javax.inject", version.ref = "javaxInject" }
|
||||
|
|
Loading…
Reference in New Issue