update
This commit is contained in:
parent
619a588559
commit
195ec0f242
@ -3,15 +3,17 @@ package com.player.musicoo.activity
|
|||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.os.CountDownTimer
|
import android.os.CountDownTimer
|
||||||
|
import android.util.Log
|
||||||
import com.gyf.immersionbar.ktx.immersionBar
|
import com.gyf.immersionbar.ktx.immersionBar
|
||||||
import com.player.musicoo.databinding.ActivityLaunchBinding
|
import com.player.musicoo.databinding.ActivityLaunchBinding
|
||||||
|
import com.player.musicoo.util.LogTag
|
||||||
|
|
||||||
class LaunchActivity : BaseActivity() {
|
class LaunchActivity : BaseActivity() {
|
||||||
private lateinit var binding: ActivityLaunchBinding
|
private lateinit var binding: ActivityLaunchBinding
|
||||||
private val totalTime = 3000 // 5秒
|
private val totalTime = 3000L // 5秒
|
||||||
private val interval = 50 // 更新间隔,毫秒
|
private val interval = 50L // 更新间隔,毫秒
|
||||||
private val steps = totalTime / interval
|
private val steps = totalTime / interval
|
||||||
private val progressPerStep = 100 / steps
|
private val progressPerStep = 100f / steps.toFloat()
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
binding = ActivityLaunchBinding.inflate(layoutInflater)
|
binding = ActivityLaunchBinding.inflate(layoutInflater)
|
||||||
@ -26,13 +28,13 @@ class LaunchActivity : BaseActivity() {
|
|||||||
|
|
||||||
private fun initTimer() {
|
private fun initTimer() {
|
||||||
val progressBar = binding.customProgressBar
|
val progressBar = binding.customProgressBar
|
||||||
val timer = object : CountDownTimer(totalTime.toLong(), interval.toLong()) {
|
val timer = object : CountDownTimer(totalTime, interval) {
|
||||||
override fun onTick(millisUntilFinished: Long) {
|
override fun onTick(millisUntilFinished: Long) {
|
||||||
progressBar.setProgress(progressBar.getProgress() + progressPerStep)
|
progressBar.setProgress(progressBar.getProgress() + progressPerStep)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onFinish() {
|
override fun onFinish() {
|
||||||
progressBar.setProgress(100)
|
progressBar.setProgress(100f)
|
||||||
toMainActivity()
|
toMainActivity()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -17,6 +17,7 @@ import com.bumptech.glide.Glide
|
|||||||
import com.bumptech.glide.request.target.CustomTarget
|
import com.bumptech.glide.request.target.CustomTarget
|
||||||
import com.bumptech.glide.request.transition.Transition
|
import com.bumptech.glide.request.transition.Transition
|
||||||
import com.player.musicoo.R
|
import com.player.musicoo.R
|
||||||
|
import com.player.musicoo.media.MediaControllerManager
|
||||||
import com.player.musicoo.sp.AppStore
|
import com.player.musicoo.sp.AppStore
|
||||||
import com.player.musicoo.util.LogTag
|
import com.player.musicoo.util.LogTag
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
@ -40,7 +41,7 @@ abstract class MoBaseActivity : AppCompatActivity(), CoroutineScope by MainScope
|
|||||||
protected val TAG = LogTag.VO_ACT_LOG
|
protected val TAG = LogTag.VO_ACT_LOG
|
||||||
protected val appStore by lazy { AppStore(this) }
|
protected val appStore by lazy { AppStore(this) }
|
||||||
protected val events = Channel<Event>(Channel.UNLIMITED)
|
protected val events = Channel<Event>(Channel.UNLIMITED)
|
||||||
|
protected val meController = MediaControllerManager.getController()
|
||||||
protected abstract suspend fun main()
|
protected abstract suspend fun main()
|
||||||
private var defer: suspend () -> Unit = {}
|
private var defer: suspend () -> Unit = {}
|
||||||
private var deferRunning = false
|
private var deferRunning = false
|
||||||
|
|||||||
@ -1,6 +1,5 @@
|
|||||||
package com.player.musicoo.activity
|
package com.player.musicoo.activity
|
||||||
|
|
||||||
import android.util.Log
|
|
||||||
import androidx.recyclerview.widget.LinearLayoutManager
|
import androidx.recyclerview.widget.LinearLayoutManager
|
||||||
import com.bumptech.glide.Glide
|
import com.bumptech.glide.Glide
|
||||||
import com.gyf.immersionbar.ktx.immersionBar
|
import com.gyf.immersionbar.ktx.immersionBar
|
||||||
@ -8,6 +7,7 @@ import com.player.musicoo.adapter.DetailsListAdapter
|
|||||||
import com.player.musicoo.databinding.ActivityDetailsBinding
|
import com.player.musicoo.databinding.ActivityDetailsBinding
|
||||||
import com.player.musicoo.innertube.Innertube
|
import com.player.musicoo.innertube.Innertube
|
||||||
import com.player.musicoo.innertube.requests.moPlaylistPage
|
import com.player.musicoo.innertube.requests.moPlaylistPage
|
||||||
|
import com.player.musicoo.util.LogTag.LogD
|
||||||
|
|
||||||
class MoListDetailsActivity : MoBaseActivity() {
|
class MoListDetailsActivity : MoBaseActivity() {
|
||||||
|
|
||||||
@ -27,7 +27,7 @@ class MoListDetailsActivity : MoBaseActivity() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
initView()
|
initView()
|
||||||
Log.d(TAG, "browseId->${browseId}")
|
LogD(TAG, "browseId->${browseId}")
|
||||||
initData(browseId)
|
initData(browseId)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -61,7 +61,7 @@ class MoListDetailsActivity : MoBaseActivity() {
|
|||||||
binding.rv.adapter = adapter
|
binding.rv.adapter = adapter
|
||||||
|
|
||||||
}?.onFailure {
|
}?.onFailure {
|
||||||
Log.d(TAG, "moPlaylistPage onFailure->${it}")
|
LogD(TAG, "moPlaylistPage onFailure->${it}")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -3,33 +3,26 @@ package com.player.musicoo.activity
|
|||||||
import android.os.Handler
|
import android.os.Handler
|
||||||
import android.os.Looper
|
import android.os.Looper
|
||||||
import android.os.Message
|
import android.os.Message
|
||||||
import android.util.Log
|
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import androidx.annotation.OptIn
|
import androidx.annotation.OptIn
|
||||||
import androidx.media3.common.MediaItem
|
import androidx.media3.common.MediaItem
|
||||||
import androidx.media3.common.MediaMetadata
|
|
||||||
import androidx.media3.common.PlaybackException
|
import androidx.media3.common.PlaybackException
|
||||||
import androidx.media3.common.Player
|
import androidx.media3.common.Player
|
||||||
import androidx.media3.common.util.UnstableApi
|
import androidx.media3.common.util.UnstableApi
|
||||||
import androidx.media3.session.MediaController
|
|
||||||
import com.bumptech.glide.Glide
|
import com.bumptech.glide.Glide
|
||||||
import com.gyf.immersionbar.ktx.immersionBar
|
import com.gyf.immersionbar.ktx.immersionBar
|
||||||
import com.player.musicoo.R
|
import com.player.musicoo.R
|
||||||
import com.player.musicoo.databinding.ActivityMoPlayDetailsBinding
|
import com.player.musicoo.databinding.ActivityMoPlayDetailsBinding
|
||||||
import com.player.musicoo.innertube.Innertube
|
import com.player.musicoo.innertube.Innertube
|
||||||
import com.player.musicoo.innertube.models.bodies.PlayerBody
|
|
||||||
import com.player.musicoo.innertube.requests.player
|
|
||||||
import com.player.musicoo.media.MediaControllerManager
|
import com.player.musicoo.media.MediaControllerManager
|
||||||
import com.player.musicoo.media.SongRadio
|
import com.player.musicoo.media.SongRadio
|
||||||
import com.player.musicoo.service.LoginRequiredException
|
|
||||||
import com.player.musicoo.service.PlayableFormatNotFoundException
|
|
||||||
import com.player.musicoo.service.UnplayableException
|
|
||||||
import com.player.musicoo.service.VideoIdMismatchException
|
|
||||||
import com.player.musicoo.util.asMediaItem
|
import com.player.musicoo.util.asMediaItem
|
||||||
import com.player.musicoo.util.convertMillisToMinutesAndSecondsString
|
import com.player.musicoo.util.convertMillisToMinutesAndSecondsString
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.isActive
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.coroutines.runBlocking
|
import kotlinx.coroutines.selects.select
|
||||||
|
import com.player.musicoo.util.LogTag.LogD
|
||||||
|
|
||||||
@OptIn(UnstableApi::class)
|
@OptIn(UnstableApi::class)
|
||||||
class MoPlayDetailsActivity : MoBaseActivity(), Player.Listener {
|
class MoPlayDetailsActivity : MoBaseActivity(), Player.Listener {
|
||||||
@ -41,12 +34,15 @@ class MoPlayDetailsActivity : MoBaseActivity(), Player.Listener {
|
|||||||
const val PLAY_DETAILS_PLAY_PARAMS = "play_details_play_params"
|
const val PLAY_DETAILS_PLAY_PARAMS = "play_details_play_params"
|
||||||
const val PLAY_DETAILS_NAME = "play_details_name"
|
const val PLAY_DETAILS_NAME = "play_details_name"
|
||||||
const val PLAY_DETAILS_DESC = "play_details_desc"
|
const val PLAY_DETAILS_DESC = "play_details_desc"
|
||||||
|
const val PLAY_DETAILS_COME_FROM = "PLAY_DETAILS_COME_FROM"
|
||||||
}
|
}
|
||||||
|
|
||||||
private lateinit var binding: ActivityMoPlayDetailsBinding
|
private lateinit var binding: ActivityMoPlayDetailsBinding
|
||||||
|
|
||||||
private var currentVideoID = ""
|
private var currentVideoID = ""
|
||||||
|
|
||||||
|
private var comeFrom: Class<*>? = null
|
||||||
|
|
||||||
private fun initImmersionBar() {
|
private fun initImmersionBar() {
|
||||||
immersionBar {
|
immersionBar {
|
||||||
statusBarDarkFont(false)
|
statusBarDarkFont(false)
|
||||||
@ -59,15 +55,18 @@ class MoPlayDetailsActivity : MoBaseActivity(), Player.Listener {
|
|||||||
setContentView(binding.root)
|
setContentView(binding.root)
|
||||||
initImmersionBar()
|
initImmersionBar()
|
||||||
initClick()
|
initClick()
|
||||||
|
initPlayerListener()
|
||||||
|
|
||||||
val videoId = intent.getStringExtra(PLAY_DETAILS_VIDEO_ID)
|
val videoId = intent.getStringExtra(PLAY_DETAILS_VIDEO_ID)
|
||||||
Log.d(TAG, "MoPlayDetailsActivity main videoId->$videoId")
|
|
||||||
val playlistId = intent.getStringExtra(PLAY_DETAILS_PLAY_LIST_ID)
|
val playlistId = intent.getStringExtra(PLAY_DETAILS_PLAY_LIST_ID)
|
||||||
Log.d(TAG, "MoPlayDetailsActivity main playlistId->$playlistId")
|
|
||||||
val playlistSetVideoId = intent.getStringExtra(PLAY_DETAILS_PLAY_LIST_SET_VIDEO_ID)
|
val playlistSetVideoId = intent.getStringExtra(PLAY_DETAILS_PLAY_LIST_SET_VIDEO_ID)
|
||||||
Log.d(TAG, "MoPlayDetailsActivity main playlistSetVideoId->$playlistSetVideoId")
|
|
||||||
val params = intent.getStringExtra(PLAY_DETAILS_PLAY_PARAMS)
|
val params = intent.getStringExtra(PLAY_DETAILS_PLAY_PARAMS)
|
||||||
|
|
||||||
|
comeFrom = intent.getSerializableExtra(PLAY_DETAILS_COME_FROM) as Class<*>?
|
||||||
|
if (comeFrom != null && comeFrom == PrimaryActivity::class.java) {
|
||||||
|
// 处理来自 PrimaryActivity 的情况
|
||||||
|
updateCurrentMediaItemInfo()
|
||||||
|
} else {
|
||||||
binding.nameTv.text = intent.getStringExtra(PLAY_DETAILS_NAME)
|
binding.nameTv.text = intent.getStringExtra(PLAY_DETAILS_NAME)
|
||||||
binding.descTv.text = intent.getStringExtra(PLAY_DETAILS_DESC)
|
binding.descTv.text = intent.getStringExtra(PLAY_DETAILS_DESC)
|
||||||
|
|
||||||
@ -86,50 +85,116 @@ class MoPlayDetailsActivity : MoBaseActivity(), Player.Listener {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onReceive()
|
||||||
|
}
|
||||||
|
|
||||||
|
private suspend fun onReceive() {
|
||||||
|
while (isActive) {
|
||||||
|
select<Unit> {
|
||||||
|
events.onReceive {
|
||||||
|
when (it) {
|
||||||
|
Event.ActivityOnResume -> {
|
||||||
|
activityOnResume()
|
||||||
|
}
|
||||||
|
|
||||||
|
else -> {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun activityOnResume() {
|
||||||
|
if (comeFrom != null && comeFrom == PrimaryActivity::class.java) {
|
||||||
|
if (meController != null && meController.currentMediaItem != null) {
|
||||||
|
updateInfoUi(meController.currentMediaItem)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun initPlayerListener() {
|
||||||
|
meController?.addListener(playerListener)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun updateCurrentMediaItemInfo() {
|
||||||
|
if (meController != null && meController.currentMediaItem != null) {
|
||||||
|
binding.playbackErrorLayout.visibility = View.GONE
|
||||||
|
binding.loadingView.visibility = View.GONE
|
||||||
|
|
||||||
|
|
||||||
|
val currentString = convertMillisToMinutesAndSecondsString(meController.currentPosition)
|
||||||
|
binding.progressDurationTv.text = currentString
|
||||||
|
if (MediaControllerManager.getDuration() > 0) {
|
||||||
|
binding.totalDurationTv.visibility = View.VISIBLE
|
||||||
|
} else {
|
||||||
|
binding.totalDurationTv.visibility = View.GONE
|
||||||
|
}
|
||||||
|
|
||||||
|
binding.totalDurationTv.text =
|
||||||
|
convertMillisToMinutesAndSecondsString(MediaControllerManager.getDuration())
|
||||||
|
|
||||||
|
binding.sbProgress.value = meController.currentPosition.toFloat()
|
||||||
|
binding.sbProgress.valueTo = MediaControllerManager.getDuration().toFloat()
|
||||||
|
updateProgressState()
|
||||||
|
|
||||||
|
binding.progressBar.progress = meController.bufferedPosition.toInt()
|
||||||
|
binding.progressBar.max = MediaControllerManager.getDuration().toInt()
|
||||||
|
updateProgressBufferingState()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun initClick() {
|
private fun initClick() {
|
||||||
val currentPlayer = MediaControllerManager.getController()
|
|
||||||
binding.disableClicksLayout.setOnClickListener { }
|
|
||||||
binding.backBtn.setOnClickListener {
|
binding.backBtn.setOnClickListener {
|
||||||
finish()
|
finish()
|
||||||
}
|
}
|
||||||
binding.playLayoutBtn.setOnClickListener {
|
binding.playLayoutBtn.setOnClickListener {
|
||||||
if (currentPlayer != null) {
|
if (meController != null) {
|
||||||
if (currentPlayer.isPlaying) {
|
if (meController.isPlaying) {
|
||||||
currentPlayer.pause()
|
meController.pause()
|
||||||
updatePlayState(false)
|
updatePlayState(false)
|
||||||
} else {
|
} else {
|
||||||
currentPlayer.play()
|
meController.play()
|
||||||
updatePlayState(true)
|
updatePlayState(true)
|
||||||
}
|
}
|
||||||
updateProgressState()
|
updateProgressState()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
binding.playSkipBackBtn.setOnClickListener {
|
binding.playSkipBackBtn.setOnClickListener {
|
||||||
if (currentPlayer != null) {
|
if (meController != null) {
|
||||||
currentPlayer.seekToPreviousMediaItem()
|
meController.seekToPreviousMediaItem()
|
||||||
updateProgressUi()
|
updateProgressUi()
|
||||||
updateInfoUi(currentPlayer.currentMediaItem)
|
updateInfoUi(meController.currentMediaItem)
|
||||||
|
updateProgressState()
|
||||||
|
if (!meController.isPlaying) {
|
||||||
|
meController.prepare()
|
||||||
|
meController.play()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
binding.playSkipForwardBtn.setOnClickListener {
|
binding.playSkipForwardBtn.setOnClickListener {
|
||||||
if (currentPlayer != null) {
|
if (meController != null) {
|
||||||
currentPlayer.seekToNextMediaItem()
|
meController.seekToNextMediaItem()
|
||||||
updateProgressUi()
|
updateProgressUi()
|
||||||
updateInfoUi(currentPlayer.currentMediaItem)
|
updateInfoUi(meController.currentMediaItem)
|
||||||
|
updateProgressState()
|
||||||
|
if (!meController.isPlaying) {
|
||||||
|
meController.prepare()
|
||||||
|
meController.play()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
binding.listLayoutBtn.setOnClickListener {
|
binding.listLayoutBtn.setOnClickListener {
|
||||||
Log.d(TAG, "currentPlayer?.mediaItemCount->${currentPlayer?.mediaItemCount}")
|
LogD(TAG, "meController?.mediaItemCount->${meController?.mediaItemCount}")
|
||||||
}
|
}
|
||||||
binding.progressBar.progress = 0
|
binding.progressBar.progress = 0
|
||||||
binding.sbProgress.value = 0f
|
binding.sbProgress.value = 0f
|
||||||
binding.sbProgress.addOnChangeListener { slider, value, fromUser ->
|
binding.sbProgress.addOnChangeListener { slider, value, fromUser ->
|
||||||
if (fromUser) {
|
if (fromUser) {
|
||||||
if (currentPlayer != null) {
|
if (meController != null) {
|
||||||
currentPlayer.seekTo(value.toLong())
|
meController.seekTo(value.toLong())
|
||||||
val ss = currentPlayer.isPlaying
|
val ss = meController.isPlaying
|
||||||
if (!ss) {
|
if (!ss) {
|
||||||
currentPlayer.play()
|
meController.play()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -149,12 +214,11 @@ class MoPlayDetailsActivity : MoBaseActivity(), Player.Listener {
|
|||||||
parameters
|
parameters
|
||||||
).let {
|
).let {
|
||||||
launch(Dispatchers.Main) {
|
launch(Dispatchers.Main) {
|
||||||
val ss = it.process()//获取到的资源集合
|
val songRadioList = it.process()//获取到的资源集合
|
||||||
|
|
||||||
if (ss.isEmpty()) {//集合为空则展示错误提示
|
if (songRadioList.isEmpty()) {//集合为空则展示错误提示
|
||||||
binding.loadingView.visibility = View.GONE
|
binding.loadingView.visibility = View.GONE
|
||||||
binding.playbackErrorLayout.visibility = View.VISIBLE
|
binding.playbackErrorLayout.visibility = View.VISIBLE
|
||||||
binding.disableClicksLayout.visibility = View.VISIBLE
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isFinishing) {
|
if (isFinishing) {
|
||||||
@ -162,9 +226,7 @@ class MoPlayDetailsActivity : MoBaseActivity(), Player.Listener {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var mediaItem: MediaItem? = null
|
var mediaItem: MediaItem? = null
|
||||||
Log.d(TAG, "size = ${ss.size}")
|
for (song: Innertube.SongItem in songRadioList) {
|
||||||
Log.d(TAG, "MoPlayDetailsActivity initData currentVideoID->$currentVideoID")
|
|
||||||
for (song: Innertube.SongItem in ss) {
|
|
||||||
if (song.key == currentVideoID) {//判断当前ID得到一个mediaItem
|
if (song.key == currentVideoID) {//判断当前ID得到一个mediaItem
|
||||||
mediaItem = song.asMediaItem
|
mediaItem = song.asMediaItem
|
||||||
break
|
break
|
||||||
@ -175,7 +237,6 @@ class MoPlayDetailsActivity : MoBaseActivity(), Player.Listener {
|
|||||||
updateInfoUi(mediaItem)
|
updateInfoUi(mediaItem)
|
||||||
|
|
||||||
binding.playbackErrorLayout.visibility = View.GONE
|
binding.playbackErrorLayout.visibility = View.GONE
|
||||||
binding.disableClicksLayout.visibility = View.GONE
|
|
||||||
|
|
||||||
binding.totalDurationTv.visibility = View.GONE
|
binding.totalDurationTv.visibility = View.GONE
|
||||||
|
|
||||||
@ -190,18 +251,18 @@ class MoPlayDetailsActivity : MoBaseActivity(), Player.Listener {
|
|||||||
)
|
)
|
||||||
.build()
|
.build()
|
||||||
|
|
||||||
val meController = MediaControllerManager.getController()
|
|
||||||
meController?.let {
|
meController?.let {
|
||||||
it.addListener(playerListener)
|
|
||||||
it.setMediaItem(newMediaItem, true)
|
it.setMediaItem(newMediaItem, true)
|
||||||
it.prepare()
|
it.prepare()
|
||||||
it.play()
|
it.play()
|
||||||
|
|
||||||
it.addMediaItems(ss.map(Innertube.SongItem::asMediaItem).drop(1))
|
//过滤掉进入页面的id得到集合addMediaItems
|
||||||
|
val mediaItems = songRadioList.map(Innertube.SongItem::asMediaItem)
|
||||||
|
.filter { filter -> filter.mediaId != videoId }
|
||||||
|
it.addMediaItems(mediaItems)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
binding.playbackErrorLayout.visibility = View.VISIBLE
|
binding.playbackErrorLayout.visibility = View.VISIBLE
|
||||||
binding.disableClicksLayout.visibility = View.VISIBLE
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -213,29 +274,30 @@ class MoPlayDetailsActivity : MoBaseActivity(), Player.Listener {
|
|||||||
newPosition: Player.PositionInfo,
|
newPosition: Player.PositionInfo,
|
||||||
reason: Int
|
reason: Int
|
||||||
) {
|
) {
|
||||||
if (reason == Player.DISCONTINUITY_REASON_AUTO_TRANSITION) {
|
//保证自动播放完毕当前歌曲与通知切换歌曲可以更新UI信息
|
||||||
val meController = MediaControllerManager.getController()
|
if (reason == Player.DISCONTINUITY_REASON_AUTO_TRANSITION
|
||||||
|
|| reason == Player.DISCONTINUITY_REASON_SEEK_ADJUSTMENT
|
||||||
|
) {
|
||||||
updateInfoUi(meController?.currentMediaItem)
|
updateInfoUi(meController?.currentMediaItem)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onPlayerError(error: PlaybackException) {
|
override fun onPlayerError(error: PlaybackException) {
|
||||||
Log.d(TAG, "onPlayerError error= $error")
|
LogD(TAG, "onPlayerError error= $error")
|
||||||
binding.playbackErrorLayout.visibility = View.VISIBLE//展示错误提示
|
binding.playbackErrorLayout.visibility = View.VISIBLE//展示错误提示
|
||||||
|
|
||||||
|
updatePlayState(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onPlayWhenReadyChanged(
|
override fun onPlayWhenReadyChanged(
|
||||||
playWhenReady: Boolean,
|
playWhenReady: Boolean,
|
||||||
reason: Int
|
reason: Int
|
||||||
) {
|
) {
|
||||||
Log.d(TAG, "onPlayWhenReadyChanged = $playWhenReady")
|
|
||||||
updatePlayState(playWhenReady)
|
updatePlayState(playWhenReady)
|
||||||
updateProgressState()
|
updateProgressState()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onPlaybackStateChanged(playbackState: Int) {
|
override fun onPlaybackStateChanged(playbackState: Int) {
|
||||||
Log.d(TAG, "onPlaybackStateChanged = $playbackState")
|
|
||||||
|
|
||||||
when (playbackState) {
|
when (playbackState) {
|
||||||
Player.STATE_BUFFERING -> {
|
Player.STATE_BUFFERING -> {
|
||||||
binding.loadingView.visibility = View.VISIBLE
|
binding.loadingView.visibility = View.VISIBLE
|
||||||
@ -266,13 +328,14 @@ class MoPlayDetailsActivity : MoBaseActivity(), Player.Listener {
|
|||||||
|
|
||||||
override fun onDestroy() {
|
override fun onDestroy() {
|
||||||
super.onDestroy()
|
super.onDestroy()
|
||||||
MediaControllerManager.getController()?.removeListener(playerListener)
|
meController?.removeListener(playerListener)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun updateProgressUi() {
|
private fun updateProgressUi() {
|
||||||
binding.sbProgress.value = 0f
|
binding.sbProgress.value = 0f
|
||||||
binding.progressBar.progress = 0
|
binding.progressBar.progress = 0
|
||||||
binding.progressDurationTv.text = convertMillisToMinutesAndSecondsString(0L)
|
binding.progressDurationTv.text = convertMillisToMinutesAndSecondsString(0L)
|
||||||
|
binding.totalDurationTv.visibility = View.GONE
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun updateInfoUi(mediaItem: MediaItem?) {
|
private fun updateInfoUi(mediaItem: MediaItem?) {
|
||||||
@ -292,10 +355,9 @@ class MoPlayDetailsActivity : MoBaseActivity(), Player.Listener {
|
|||||||
* 更新播放进度
|
* 更新播放进度
|
||||||
*/
|
*/
|
||||||
private fun updateProgressState() {
|
private fun updateProgressState() {
|
||||||
val currentPlayer = MediaControllerManager.getController()
|
|
||||||
//判断是否ready与播放中,否则停止更新进度
|
//判断是否ready与播放中,否则停止更新进度
|
||||||
if (currentPlayer != null && currentPlayer.playbackState == Player.STATE_READY && currentPlayer.isPlaying) {
|
if (meController != null && meController.playbackState == Player.STATE_READY && meController.isPlaying) {
|
||||||
updatePlayState(currentPlayer.isPlaying)
|
updatePlayState(meController.isPlaying)
|
||||||
progressHandler.removeCallbacksAndMessages(null)
|
progressHandler.removeCallbacksAndMessages(null)
|
||||||
progressHandler.sendEmptyMessage(1)
|
progressHandler.sendEmptyMessage(1)
|
||||||
} else {
|
} else {
|
||||||
@ -308,14 +370,13 @@ class MoPlayDetailsActivity : MoBaseActivity(), Player.Listener {
|
|||||||
*/
|
*/
|
||||||
private val progressHandler = object : Handler(Looper.myLooper()!!) {
|
private val progressHandler = object : Handler(Looper.myLooper()!!) {
|
||||||
override fun handleMessage(msg: Message) {
|
override fun handleMessage(msg: Message) {
|
||||||
val currentPlayer = MediaControllerManager.getController()
|
|
||||||
//判断是否ready与播放中,否则停止更新进度
|
//判断是否ready与播放中,否则停止更新进度
|
||||||
if (currentPlayer != null && currentPlayer.playbackState == Player.STATE_READY && currentPlayer.isPlaying) {
|
if (meController != null && meController.playbackState == Player.STATE_READY && meController.isPlaying) {
|
||||||
val currentPosition = currentPlayer.currentPosition
|
val currentPosition = meController.currentPosition
|
||||||
val currentString = convertMillisToMinutesAndSecondsString(currentPosition)
|
val currentString = convertMillisToMinutesAndSecondsString(currentPosition)
|
||||||
binding.progressDurationTv.text = currentString
|
binding.progressDurationTv.text = currentString
|
||||||
|
|
||||||
val currentBufferedPosition = currentPlayer.bufferedPosition
|
val currentBufferedPosition = meController.bufferedPosition
|
||||||
binding.progressBar.progress = currentBufferedPosition.toInt()
|
binding.progressBar.progress = currentBufferedPosition.toInt()
|
||||||
|
|
||||||
// 更新 SeekBar 的进度
|
// 更新 SeekBar 的进度
|
||||||
@ -330,8 +391,7 @@ class MoPlayDetailsActivity : MoBaseActivity(), Player.Listener {
|
|||||||
* 更新缓冲进度
|
* 更新缓冲进度
|
||||||
*/
|
*/
|
||||||
private fun updateProgressBufferingState() {
|
private fun updateProgressBufferingState() {
|
||||||
val currentPlayer = MediaControllerManager.getController()
|
if (meController != null && meController.isLoading) {
|
||||||
if (currentPlayer != null && currentPlayer.isLoading) {
|
|
||||||
progressBufferingHandler.removeCallbacksAndMessages(null)
|
progressBufferingHandler.removeCallbacksAndMessages(null)
|
||||||
progressBufferingHandler.sendEmptyMessage(1)
|
progressBufferingHandler.sendEmptyMessage(1)
|
||||||
} else {
|
} else {
|
||||||
@ -344,9 +404,8 @@ class MoPlayDetailsActivity : MoBaseActivity(), Player.Listener {
|
|||||||
*/
|
*/
|
||||||
private val progressBufferingHandler = object : Handler(Looper.myLooper()!!) {
|
private val progressBufferingHandler = object : Handler(Looper.myLooper()!!) {
|
||||||
override fun handleMessage(msg: Message) {
|
override fun handleMessage(msg: Message) {
|
||||||
val currentPlayer = MediaControllerManager.getController()
|
if (meController != null && meController.isLoading) {
|
||||||
if (currentPlayer != null && currentPlayer.isLoading) {
|
val currentBufferedPosition = meController.bufferedPosition
|
||||||
val currentBufferedPosition = currentPlayer.bufferedPosition
|
|
||||||
binding.progressBar.progress = currentBufferedPosition.toInt()
|
binding.progressBar.progress = currentBufferedPosition.toInt()
|
||||||
sendEmptyMessageDelayed(1, 50)
|
sendEmptyMessageDelayed(1, 50)
|
||||||
}
|
}
|
||||||
@ -360,44 +419,4 @@ class MoPlayDetailsActivity : MoBaseActivity(), Player.Listener {
|
|||||||
binding.playImg.setImageResource(R.drawable.play_green_icon)
|
binding.playImg.setImageResource(R.drawable.play_green_icon)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getSourceUrl(videoId: String): String {
|
|
||||||
return runBlocking(Dispatchers.IO) {
|
|
||||||
Innertube.player(PlayerBody(videoId = videoId))
|
|
||||||
}?.mapCatching { body ->
|
|
||||||
if (body.videoDetails?.videoId != videoId) {
|
|
||||||
throw VideoIdMismatchException()
|
|
||||||
}
|
|
||||||
when (val status = body.playabilityStatus?.status) {
|
|
||||||
"OK" -> body.streamingData?.highestQualityFormat?.let { format ->
|
|
||||||
format.url
|
|
||||||
} ?: throw PlayableFormatNotFoundException()
|
|
||||||
|
|
||||||
"UNPLAYABLE" -> throw UnplayableException()
|
|
||||||
"LOGIN_REQUIRED" -> throw LoginRequiredException()
|
|
||||||
else -> throw PlaybackException(
|
|
||||||
status,
|
|
||||||
null,
|
|
||||||
PlaybackException.ERROR_CODE_REMOTE_ERROR
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}?.getOrNull() ?: ""
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun retryPlayback(
|
|
||||||
player: MediaController,
|
|
||||||
mediaId: String,
|
|
||||||
url: String,
|
|
||||||
mediaMetadata: MediaMetadata
|
|
||||||
) {
|
|
||||||
val mediaItem =
|
|
||||||
MediaItem.Builder()
|
|
||||||
.setMediaId(mediaId)
|
|
||||||
.setUri(url)
|
|
||||||
.setMediaMetadata(mediaMetadata)
|
|
||||||
.build()
|
|
||||||
player.setMediaItem(mediaItem)
|
|
||||||
player.prepare()
|
|
||||||
player.play()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@ -1,11 +1,24 @@
|
|||||||
package com.player.musicoo.activity
|
package com.player.musicoo.activity
|
||||||
|
|
||||||
|
import android.content.Intent
|
||||||
|
import android.os.Handler
|
||||||
|
import android.os.Looper
|
||||||
|
import android.os.Message
|
||||||
|
import android.view.View
|
||||||
|
import android.widget.Toast
|
||||||
import androidx.fragment.app.Fragment
|
import androidx.fragment.app.Fragment
|
||||||
import androidx.fragment.app.FragmentTransaction
|
import androidx.fragment.app.FragmentTransaction
|
||||||
|
import androidx.media3.common.MediaItem
|
||||||
|
import androidx.media3.common.Player
|
||||||
|
import com.bumptech.glide.Glide
|
||||||
import com.player.musicoo.R
|
import com.player.musicoo.R
|
||||||
import com.player.musicoo.databinding.ActivityPrimaryBinding
|
import com.player.musicoo.databinding.ActivityPrimaryBinding
|
||||||
import com.player.musicoo.fragment.ImportFragment
|
import com.player.musicoo.fragment.ImportFragment
|
||||||
import com.player.musicoo.fragment.MoHomeFragment
|
import com.player.musicoo.fragment.MoHomeFragment
|
||||||
|
import com.player.musicoo.media.MediaControllerManager
|
||||||
|
import com.player.musicoo.util.LogTag.LogD
|
||||||
|
import kotlinx.coroutines.isActive
|
||||||
|
import kotlinx.coroutines.selects.select
|
||||||
|
|
||||||
class PrimaryActivity : MoBaseActivity() {
|
class PrimaryActivity : MoBaseActivity() {
|
||||||
/**
|
/**
|
||||||
@ -22,6 +35,9 @@ class PrimaryActivity : MoBaseActivity() {
|
|||||||
binding = ActivityPrimaryBinding.inflate(layoutInflater)
|
binding = ActivityPrimaryBinding.inflate(layoutInflater)
|
||||||
setContentView(binding.root)
|
setContentView(binding.root)
|
||||||
initView()
|
initView()
|
||||||
|
initPlayerListener()
|
||||||
|
|
||||||
|
onReceive()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun initView() {
|
private fun initView() {
|
||||||
@ -38,6 +54,28 @@ class PrimaryActivity : MoBaseActivity() {
|
|||||||
changeFragment(1)
|
changeFragment(1)
|
||||||
updateBtnState(1)
|
updateBtnState(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
binding.playBlackBtn.setOnClickListener {
|
||||||
|
if (meController != null) {
|
||||||
|
if (meController.isPlaying) {
|
||||||
|
meController.pause()
|
||||||
|
updatePlayState(false)
|
||||||
|
} else {
|
||||||
|
meController.play()
|
||||||
|
updatePlayState(true)
|
||||||
|
}
|
||||||
|
updateProgressState()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
binding.goDetailsBtn.setOnClickListener {
|
||||||
|
val intent = Intent(this, MoPlayDetailsActivity::class.java)
|
||||||
|
intent.putExtra(
|
||||||
|
MoPlayDetailsActivity.PLAY_DETAILS_COME_FROM,
|
||||||
|
PrimaryActivity::class.java
|
||||||
|
)
|
||||||
|
startActivity(intent)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun initFragment() {
|
private fun initFragment() {
|
||||||
@ -86,4 +124,141 @@ class PrimaryActivity : MoBaseActivity() {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private suspend fun onReceive() {
|
||||||
|
while (isActive) {
|
||||||
|
select<Unit> {
|
||||||
|
events.onReceive {
|
||||||
|
when (it) {
|
||||||
|
Event.ActivityOnResume -> {
|
||||||
|
activityOnResume()
|
||||||
|
}
|
||||||
|
|
||||||
|
else -> {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun activityOnResume() {
|
||||||
|
if (meController != null && meController.mediaItemCount > 0 && meController.duration > 0) {
|
||||||
|
binding.playingStatusLayout.visibility = View.VISIBLE
|
||||||
|
updateInfoUi(meController.currentMediaItem)
|
||||||
|
} else {
|
||||||
|
binding.playingStatusLayout.visibility = View.GONE
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun initPlayerListener() {
|
||||||
|
meController?.addListener(playerListener)
|
||||||
|
}
|
||||||
|
|
||||||
|
private val playerListener = object : Player.Listener {
|
||||||
|
override fun onPositionDiscontinuity(
|
||||||
|
oldPosition: Player.PositionInfo,
|
||||||
|
newPosition: Player.PositionInfo,
|
||||||
|
reason: Int
|
||||||
|
) {
|
||||||
|
if (reason == Player.DISCONTINUITY_REASON_AUTO_TRANSITION) {
|
||||||
|
updateInfoUi(meController?.currentMediaItem)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onPlaybackStateChanged(playbackState: Int) {
|
||||||
|
LogD(TAG, "playbackState->$playbackState")
|
||||||
|
updateProgressState()
|
||||||
|
when (playbackState) {
|
||||||
|
Player.STATE_READY -> {
|
||||||
|
if (meController != null) {
|
||||||
|
LogD(TAG, "meController.duration->${meController.duration}")
|
||||||
|
binding.progressBar.setMaxProgress(MediaControllerManager.getDuration())
|
||||||
|
val currentPosition = meController.currentPosition
|
||||||
|
binding.progressBar.setProgress(currentPosition)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
else -> {}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onPlayWhenReadyChanged(
|
||||||
|
playWhenReady: Boolean,
|
||||||
|
reason: Int
|
||||||
|
) {
|
||||||
|
updatePlayState(playWhenReady)
|
||||||
|
updateProgressState()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onDestroy() {
|
||||||
|
super.onDestroy()
|
||||||
|
meController?.removeListener(playerListener)
|
||||||
|
}
|
||||||
|
|
||||||
|
private var backPressedTime: Long = 0
|
||||||
|
private val backToast: Toast by lazy {
|
||||||
|
Toast.makeText(baseContext, getString(R.string.exit_main_text), Toast.LENGTH_SHORT)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onBackPressed() {
|
||||||
|
if (backPressedTime + 2000 > System.currentTimeMillis()) {
|
||||||
|
super.onBackPressed()
|
||||||
|
backToast.cancel()
|
||||||
|
return
|
||||||
|
} else {
|
||||||
|
backToast.show()
|
||||||
|
}
|
||||||
|
backPressedTime = System.currentTimeMillis()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新播放进度
|
||||||
|
*/
|
||||||
|
private fun updateProgressState() {
|
||||||
|
//判断是否ready与播放中,否则停止更新进度
|
||||||
|
if (meController != null && meController.playbackState == Player.STATE_READY && meController.isPlaying) {
|
||||||
|
updatePlayState(meController.isPlaying)
|
||||||
|
progressHandler.removeCallbacksAndMessages(null)
|
||||||
|
progressHandler.sendEmptyMessage(1)
|
||||||
|
} else {
|
||||||
|
progressHandler.removeCallbacksAndMessages(null)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 播放进度
|
||||||
|
*/
|
||||||
|
private val progressHandler = object : Handler(Looper.myLooper()!!) {
|
||||||
|
override fun handleMessage(msg: Message) {
|
||||||
|
//判断是否ready与播放中,否则停止更新进度
|
||||||
|
if (meController != null && meController.playbackState == Player.STATE_READY && meController.isPlaying) {
|
||||||
|
val currentPosition = meController.currentPosition
|
||||||
|
binding.progressBar.setProgress(currentPosition)
|
||||||
|
sendEmptyMessageDelayed(1, 50)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun updatePlayState(b: Boolean) {
|
||||||
|
if (b) {
|
||||||
|
binding.playStatusImg.setImageResource(R.drawable.playing_black_icon)
|
||||||
|
} else {
|
||||||
|
binding.playStatusImg.setImageResource(R.drawable.play_black_icon)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun updateInfoUi(mediaItem: MediaItem?) {
|
||||||
|
if (mediaItem == null) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
Glide.with(this@PrimaryActivity)
|
||||||
|
.load(mediaItem.mediaMetadata.artworkUri)
|
||||||
|
.into(binding.audioImg)
|
||||||
|
|
||||||
|
binding.name.text = mediaItem.mediaMetadata.title
|
||||||
|
binding.desc.text = mediaItem.mediaMetadata.artist
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@ -1,7 +1,7 @@
|
|||||||
package com.player.musicoo.fragment
|
package com.player.musicoo.fragment
|
||||||
|
|
||||||
import android.util.Log
|
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import com.gyf.immersionbar.ktx.immersionBar
|
import com.gyf.immersionbar.ktx.immersionBar
|
||||||
import com.player.musicoo.databinding.FragmentMoHomeBinding
|
import com.player.musicoo.databinding.FragmentMoHomeBinding
|
||||||
@ -9,16 +9,28 @@ import com.player.musicoo.innertube.Innertube
|
|||||||
import com.player.musicoo.innertube.models.MusicCarouselShelfRenderer
|
import com.player.musicoo.innertube.models.MusicCarouselShelfRenderer
|
||||||
import com.player.musicoo.innertube.requests.homePage
|
import com.player.musicoo.innertube.requests.homePage
|
||||||
import com.player.musicoo.innertube.requests.homePageMore
|
import com.player.musicoo.innertube.requests.homePageMore
|
||||||
|
import com.player.musicoo.util.LogTag.LogD
|
||||||
import com.player.musicoo.view.MusicResponsiveListView
|
import com.player.musicoo.view.MusicResponsiveListView
|
||||||
import com.player.musicoo.view.MusicTowRowListView
|
import com.player.musicoo.view.MusicTowRowListView
|
||||||
|
import kotlinx.coroutines.channels.Channel
|
||||||
|
import kotlinx.coroutines.isActive
|
||||||
|
import kotlinx.coroutines.selects.select
|
||||||
|
|
||||||
class MoHomeFragment : MoBaseFragment<FragmentMoHomeBinding>() {
|
class MoHomeFragment : MoBaseFragment<FragmentMoHomeBinding>() {
|
||||||
|
|
||||||
|
private val requests: Channel<Request> = Channel(Channel.UNLIMITED)
|
||||||
|
|
||||||
|
enum class Request {
|
||||||
|
TryAgain,
|
||||||
|
}
|
||||||
|
|
||||||
override val bindingInflater: (LayoutInflater, ViewGroup?, Boolean) -> FragmentMoHomeBinding
|
override val bindingInflater: (LayoutInflater, ViewGroup?, Boolean) -> FragmentMoHomeBinding
|
||||||
get() = FragmentMoHomeBinding::inflate
|
get() = FragmentMoHomeBinding::inflate
|
||||||
|
|
||||||
override suspend fun onViewCreated() {
|
override suspend fun onViewCreated() {
|
||||||
initView()
|
initView()
|
||||||
|
initData()
|
||||||
|
onReceive()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun initImmersionBar() {
|
private fun initImmersionBar() {
|
||||||
@ -28,8 +40,30 @@ class MoHomeFragment : MoBaseFragment<FragmentMoHomeBinding>() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun initView() {
|
private suspend fun onReceive() {
|
||||||
|
while (isActive) {
|
||||||
|
select<Unit> {
|
||||||
|
requests.onReceive {
|
||||||
|
when (it) {
|
||||||
|
Request.TryAgain -> {
|
||||||
|
initData()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun initView() {
|
||||||
|
binding.tryAgainBtn.setOnClickListener {
|
||||||
|
requests.trySend(Request.TryAgain)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private suspend fun initData() {
|
||||||
|
showLoadingUi()
|
||||||
Innertube.homePage()?.onSuccess {
|
Innertube.homePage()?.onSuccess {
|
||||||
|
showDataUi()
|
||||||
if (it.homePage.isNotEmpty()) {
|
if (it.homePage.isNotEmpty()) {
|
||||||
for (home: Innertube.HomePage in it.homePage) {
|
for (home: Innertube.HomePage in it.homePage) {
|
||||||
for (content: MusicCarouselShelfRenderer.Content in home.contents) {
|
for (content: MusicCarouselShelfRenderer.Content in home.contents) {
|
||||||
@ -55,9 +89,12 @@ class MoHomeFragment : MoBaseFragment<FragmentMoHomeBinding>() {
|
|||||||
}
|
}
|
||||||
initHomeDataMore(it)
|
initHomeDataMore(it)
|
||||||
} else {
|
} else {
|
||||||
Log.d(TAG, "homePage size 0")
|
LogD(TAG, "homePage size 0")
|
||||||
|
}
|
||||||
|
}?.onFailure {
|
||||||
|
showNoContentUi()
|
||||||
|
LogD(TAG, "homePage onFailure->${it}")
|
||||||
}
|
}
|
||||||
}?.onFailure { Log.d(TAG, "homePage onFailure->${it}") }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun initHomeDataMore(baseHomePage: Innertube.BaseHomePage) {
|
private suspend fun initHomeDataMore(baseHomePage: Innertube.BaseHomePage) {
|
||||||
@ -87,7 +124,7 @@ class MoHomeFragment : MoBaseFragment<FragmentMoHomeBinding>() {
|
|||||||
}
|
}
|
||||||
initHomeDataMore(it)
|
initHomeDataMore(it)
|
||||||
}?.onFailure {
|
}?.onFailure {
|
||||||
Log.d(TAG, "initHomeDataMore onFailure ->${it}")
|
LogD(TAG, "initHomeDataMore onFailure ->${it}")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -103,4 +140,19 @@ class MoHomeFragment : MoBaseFragment<FragmentMoHomeBinding>() {
|
|||||||
initImmersionBar()
|
initImmersionBar()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun showDataUi(){
|
||||||
|
binding.loadingLayout.visibility = View.GONE
|
||||||
|
binding.noContentLayout.visibility = View.GONE
|
||||||
|
}
|
||||||
|
private fun showLoadingUi(){
|
||||||
|
binding.loadingLayout.visibility = View.VISIBLE
|
||||||
|
binding.noContentLayout.visibility = View.GONE
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun showNoContentUi(){
|
||||||
|
binding.loadingLayout.visibility = View.GONE
|
||||||
|
binding.noContentLayout.visibility = View.VISIBLE
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -167,42 +167,13 @@ object MediaControllerManager {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
fun isPlaying(): Boolean {
|
|
||||||
mediaController?.let {
|
|
||||||
return it.isPlaying
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
fun play() {
|
|
||||||
mediaController?.play()
|
|
||||||
}
|
|
||||||
|
|
||||||
fun getMediaItemCount(): Int {
|
|
||||||
mediaController?.let {
|
|
||||||
return it.mediaItemCount
|
|
||||||
}
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
fun getCurrentMediaItem(): MediaItem? {
|
|
||||||
mediaController?.let {
|
|
||||||
return it.currentMediaItem
|
|
||||||
}
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
fun getDuration(): Long {
|
fun getDuration(): Long {
|
||||||
mediaController?.let {
|
mediaController?.let {
|
||||||
|
if (it.duration > 0) {
|
||||||
return it.duration
|
return it.duration
|
||||||
}
|
} else {
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getTotalBufferedDuration():Long{
|
|
||||||
mediaController?.let {
|
|
||||||
return it.totalBufferedDuration
|
|
||||||
}
|
}
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,12 +1,15 @@
|
|||||||
package com.player.musicoo.service
|
package com.player.musicoo.service
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.util.Log
|
|
||||||
import androidx.core.app.NotificationCompat
|
import androidx.core.app.NotificationCompat
|
||||||
import androidx.media3.common.util.UnstableApi
|
import androidx.media3.common.util.UnstableApi
|
||||||
import androidx.media3.session.*
|
import androidx.media3.session.CommandButton
|
||||||
|
import androidx.media3.session.DefaultMediaNotificationProvider
|
||||||
|
import androidx.media3.session.MediaNotification
|
||||||
|
import androidx.media3.session.MediaSession
|
||||||
import com.google.common.collect.ImmutableList
|
import com.google.common.collect.ImmutableList
|
||||||
import com.player.musicoo.util.LogTag
|
import com.player.musicoo.util.LogTag
|
||||||
|
import com.player.musicoo.util.LogTag.LogD
|
||||||
|
|
||||||
@UnstableApi
|
@UnstableApi
|
||||||
class CustomMediaNotificationProvider(context: Context) :
|
class CustomMediaNotificationProvider(context: Context) :
|
||||||
@ -19,12 +22,12 @@ class CustomMediaNotificationProvider(context: Context) :
|
|||||||
actionFactory: MediaNotification.ActionFactory
|
actionFactory: MediaNotification.ActionFactory
|
||||||
): IntArray {
|
): IntArray {
|
||||||
|
|
||||||
Log.d(LogTag.VO_API_LOG, "mediaButtons->$mediaButtons")
|
LogD(LogTag.VO_API_LOG, "mediaButtons->$mediaButtons")
|
||||||
|
|
||||||
for (com: CommandButton in mediaButtons) {
|
for (com: CommandButton in mediaButtons) {
|
||||||
Log.d(LogTag.VO_API_LOG, "displayName->${com.displayName}")
|
LogD(LogTag.VO_API_LOG, "displayName->${com.displayName}")
|
||||||
Log.d(LogTag.VO_API_LOG, "playerCommand->${com.playerCommand}")
|
LogD(LogTag.VO_API_LOG, "playerCommand->${com.playerCommand}")
|
||||||
Log.d(LogTag.VO_API_LOG, "------------------------------------------")
|
LogD(LogTag.VO_API_LOG, "------------------------------------------")
|
||||||
}
|
}
|
||||||
|
|
||||||
val notificationMediaButtons = ImmutableList.builder<CommandButton>().apply {
|
val notificationMediaButtons = ImmutableList.builder<CommandButton>().apply {
|
||||||
|
|||||||
@ -41,6 +41,7 @@ import com.player.musicoo.innertube.models.bodies.PlayerBody
|
|||||||
import com.player.musicoo.innertube.requests.player
|
import com.player.musicoo.innertube.requests.player
|
||||||
import com.player.musicoo.util.ExoPlayerDiskCacheMaxSize
|
import com.player.musicoo.util.ExoPlayerDiskCacheMaxSize
|
||||||
import com.player.musicoo.util.LogTag
|
import com.player.musicoo.util.LogTag
|
||||||
|
import com.player.musicoo.util.LogTag.LogD
|
||||||
import com.player.musicoo.util.RingBuffer
|
import com.player.musicoo.util.RingBuffer
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.runBlocking
|
import kotlinx.coroutines.runBlocking
|
||||||
@ -152,7 +153,6 @@ class PlaybackService : MediaSessionService(), Player.Listener {
|
|||||||
|
|
||||||
return ResolvingDataSource.Factory(createCacheDataSource()) { dataSpec ->
|
return ResolvingDataSource.Factory(createCacheDataSource()) { dataSpec ->
|
||||||
val videoId = dataSpec.key ?: error("A key must be set")
|
val videoId = dataSpec.key ?: error("A key must be set")
|
||||||
Log.d(TAG,"111111 videoId")
|
|
||||||
if (cache.isCached(videoId, dataSpec.position, chunkLength)) {
|
if (cache.isCached(videoId, dataSpec.position, chunkLength)) {
|
||||||
dataSpec
|
dataSpec
|
||||||
} else {
|
} else {
|
||||||
@ -181,7 +181,7 @@ class PlaybackService : MediaSessionService(), Player.Listener {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Log.d(TAG,"1111 urlResult->$urlResult")
|
LogD(TAG,"service urlResult->$urlResult")
|
||||||
|
|
||||||
urlResult?.getOrThrow()?.let { url ->
|
urlResult?.getOrThrow()?.let { url ->
|
||||||
ringBuffer.append(videoId to url.toUri())
|
ringBuffer.append(videoId to url.toUri())
|
||||||
|
|||||||
@ -1,8 +1,22 @@
|
|||||||
package com.player.musicoo.util
|
package com.player.musicoo.util
|
||||||
|
|
||||||
|
import android.util.Log
|
||||||
|
|
||||||
object LogTag {
|
object LogTag {
|
||||||
const val VO_ACT_LOG = "vo-act—log"
|
const val VO_ACT_LOG = "vo-act—log"
|
||||||
const val VO_FRAGMENT_LOG = "vo-fragment-log"
|
const val VO_FRAGMENT_LOG = "vo-fragment-log"
|
||||||
const val VO_API_LOG = "vo-api—log"
|
const val VO_API_LOG = "vo-api—log"
|
||||||
const val VO_SERVICE_LOG = "vo-service—log"
|
const val VO_SERVICE_LOG = "vo-service—log"
|
||||||
|
|
||||||
|
fun LogD(tag: String, message: String) {
|
||||||
|
Log.d(tag, message)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun LogE(tag: String, message: String) {
|
||||||
|
Log.e(tag, message)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun LogI(tag: String, message: String) {
|
||||||
|
Log.i(tag, message)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@ -11,8 +11,8 @@ import android.util.AttributeSet
|
|||||||
import android.view.View
|
import android.view.View
|
||||||
|
|
||||||
class CustomProgressBar(context: Context, attrs: AttributeSet?) : View(context, attrs) {
|
class CustomProgressBar(context: Context, attrs: AttributeSet?) : View(context, attrs) {
|
||||||
private var progress = 0 // 当前进度
|
private var progress = 0f // 当前进度
|
||||||
private val maxProgress = 100 // 最大进度
|
private val maxProgress = 100f // 最大进度
|
||||||
private val progressBarHeight = 20f // 进度条高度
|
private val progressBarHeight = 20f // 进度条高度
|
||||||
private val cornerRadius = 10f // 圆角半径
|
private val cornerRadius = 10f // 圆角半径
|
||||||
private val backgroundColor = Color.parseColor("#26FFFFFF")
|
private val backgroundColor = Color.parseColor("#26FFFFFF")
|
||||||
@ -39,7 +39,7 @@ class CustomProgressBar(context: Context, attrs: AttributeSet?) : View(context,
|
|||||||
|
|
||||||
|
|
||||||
// 计算进度条的宽度
|
// 计算进度条的宽度
|
||||||
val progressBarWidth = (width * progress.toFloat() / maxProgress).toInt()
|
val progressBarWidth = (width * progress / maxProgress)
|
||||||
|
|
||||||
// 创建颜色渐变对象
|
// 创建颜色渐变对象
|
||||||
val gradient = LinearGradient(0f, 0f, width.toFloat(), 0f, intArrayOf(startColor, middleColor, endColor), null, Shader.TileMode.CLAMP)
|
val gradient = LinearGradient(0f, 0f, width.toFloat(), 0f, intArrayOf(startColor, middleColor, endColor), null, Shader.TileMode.CLAMP)
|
||||||
@ -50,12 +50,12 @@ class CustomProgressBar(context: Context, attrs: AttributeSet?) : View(context,
|
|||||||
canvas.drawRoundRect(rect, cornerRadius, cornerRadius, paintTow)
|
canvas.drawRoundRect(rect, cornerRadius, cornerRadius, paintTow)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getProgress():Int{
|
fun getProgress():Float{
|
||||||
return progress
|
return progress
|
||||||
}
|
}
|
||||||
|
|
||||||
// 设置进度
|
// 设置进度
|
||||||
fun setProgress(progress: Int) {
|
fun setProgress(progress: Float) {
|
||||||
this.progress = progress
|
this.progress = progress
|
||||||
invalidate() // 请求重绘
|
invalidate() // 请求重绘
|
||||||
}
|
}
|
||||||
|
|||||||
8
app/src/main/res/drawable/drw_btn_bg.xml
Normal file
8
app/src/main/res/drawable/drw_btn_bg.xml
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:shape="rectangle">
|
||||||
|
|
||||||
|
<solid android:color="#FF80F988" />
|
||||||
|
<corners android:radius="24dp" />
|
||||||
|
|
||||||
|
</shape>
|
||||||
@ -117,7 +117,10 @@
|
|||||||
android:id="@+id/loading_view"
|
android:id="@+id/loading_view"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_centerInParent="true" />
|
android:layout_centerInParent="true"
|
||||||
|
android:indeterminateTint="@color/green"
|
||||||
|
android:progressBackgroundTint="@color/green"
|
||||||
|
android:progressTint="@color/green" />
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/playback_error_layout"
|
android:id="@+id/playback_error_layout"
|
||||||
|
|||||||
@ -10,12 +10,19 @@
|
|||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="0dp" />
|
android:layout_height="0dp" />
|
||||||
|
|
||||||
<FrameLayout
|
<LinearLayout
|
||||||
android:id="@+id/frame_layout"
|
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:layout_above="@+id/bottom_layout"
|
android:layout_above="@+id/bottom_layout"
|
||||||
android:layout_below="@+id/view" />
|
android:layout_below="@+id/view"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<FrameLayout
|
||||||
|
android:id="@+id/frame_layout"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:id="@+id/bottom_layout"
|
android:id="@+id/bottom_layout"
|
||||||
@ -29,8 +36,8 @@
|
|||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginStart="12dp"
|
android:layout_marginStart="12dp"
|
||||||
android:visibility="gone"
|
|
||||||
android:layout_marginEnd="12dp"
|
android:layout_marginEnd="12dp"
|
||||||
|
android:visibility="visible"
|
||||||
app:hl_cornerRadius="36dp"
|
app:hl_cornerRadius="36dp"
|
||||||
app:hl_layoutBackground="#FF80F988"
|
app:hl_layoutBackground="#FF80F988"
|
||||||
app:hl_shadowColor="#40040604"
|
app:hl_shadowColor="#40040604"
|
||||||
@ -39,12 +46,20 @@
|
|||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="78dp"
|
android:layout_height="68dp"
|
||||||
android:layout_alignParentBottom="true"
|
android:layout_alignParentBottom="true"
|
||||||
android:gravity="center_vertical"
|
android:gravity="center_vertical"
|
||||||
android:paddingStart="20dp"
|
android:paddingStart="20dp"
|
||||||
android:paddingEnd="20dp">
|
android:paddingEnd="20dp">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/goDetailsBtn"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:gravity="center_vertical"
|
||||||
|
android:orientation="horizontal">
|
||||||
|
|
||||||
<RelativeLayout
|
<RelativeLayout
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content">
|
android:layout_height="wrap_content">
|
||||||
@ -60,8 +75,9 @@
|
|||||||
<ImageView
|
<ImageView
|
||||||
android:id="@+id/audio_img"
|
android:id="@+id/audio_img"
|
||||||
android:layout_width="48dp"
|
android:layout_width="48dp"
|
||||||
|
android:scaleType="centerCrop"
|
||||||
android:layout_height="48dp"
|
android:layout_height="48dp"
|
||||||
android:src="@mipmap/breathe" />
|
android:src="@mipmap/musicoo_logo_img" />
|
||||||
|
|
||||||
</androidx.cardview.widget.CardView>
|
</androidx.cardview.widget.CardView>
|
||||||
|
|
||||||
@ -85,9 +101,9 @@
|
|||||||
android:id="@+id/name"
|
android:id="@+id/name"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:maxLines="1"
|
|
||||||
android:ellipsize="marquee"
|
android:ellipsize="marquee"
|
||||||
android:fontFamily="@font/medium_font"
|
android:fontFamily="@font/medium_font"
|
||||||
|
android:maxLines="1"
|
||||||
android:text="@string/app_name"
|
android:text="@string/app_name"
|
||||||
android:textColor="@color/black"
|
android:textColor="@color/black"
|
||||||
android:textSize="16dp" />
|
android:textSize="16dp" />
|
||||||
@ -102,13 +118,15 @@
|
|||||||
android:textSize="12dp" />
|
android:textSize="12dp" />
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:id="@+id/alarm_clock_btn"
|
android:id="@+id/alarm_clock_btn"
|
||||||
android:layout_width="42dp"
|
android:layout_width="42dp"
|
||||||
android:visibility="gone"
|
|
||||||
android:layout_height="42dp"
|
android:layout_height="42dp"
|
||||||
android:gravity="center">
|
android:gravity="center"
|
||||||
|
android:visibility="gone">
|
||||||
|
|
||||||
<ImageView
|
<ImageView
|
||||||
android:layout_width="34dp"
|
android:layout_width="34dp"
|
||||||
|
|||||||
@ -73,6 +73,9 @@
|
|||||||
|
|
||||||
<ProgressBar
|
<ProgressBar
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
|
android:indeterminateTint="@color/green"
|
||||||
|
android:progressBackgroundTint="@color/green"
|
||||||
|
android:progressTint="@color/green"
|
||||||
android:layout_height="wrap_content" />
|
android:layout_height="wrap_content" />
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|||||||
@ -16,10 +16,70 @@
|
|||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="0dp" />
|
android:layout_height="0dp" />
|
||||||
|
|
||||||
<androidx.core.widget.NestedScrollView
|
<LinearLayout
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:layout_below="@+id/view"
|
android:layout_below="@+id/view"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/loadingLayout"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:gravity="center"
|
||||||
|
android:visibility="gone">
|
||||||
|
|
||||||
|
<ProgressBar
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:indeterminateTint="@color/green"
|
||||||
|
android:progressBackgroundTint="@color/green"
|
||||||
|
android:progressTint="@color/green"
|
||||||
|
android:layout_height="wrap_content" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/no_content_layout"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:gravity="center"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:visibility="gone">
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/no_content_iv"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:src="@mipmap/no_content_img" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="32dp"
|
||||||
|
android:layout_marginEnd="32dp"
|
||||||
|
android:fontFamily="@font/medium_font"
|
||||||
|
android:gravity="center"
|
||||||
|
android:text="@string/content_loading_failed"
|
||||||
|
android:textSize="14dp" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/tryAgainBtn"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="48dp"
|
||||||
|
android:layout_marginTop="16dp"
|
||||||
|
android:background="@drawable/drw_btn_bg"
|
||||||
|
android:fontFamily="@font/medium_font"
|
||||||
|
android:gravity="center"
|
||||||
|
android:paddingStart="32dp"
|
||||||
|
android:paddingEnd="32dp"
|
||||||
|
android:text="@string/try_again"
|
||||||
|
android:textColor="@color/black"
|
||||||
|
android:textSize="16dp" />
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<androidx.core.widget.NestedScrollView
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
android:layout_marginTop="-2dp">
|
android:layout_marginTop="-2dp">
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
@ -47,11 +107,14 @@
|
|||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:id="@+id/content_layout"
|
android:id="@+id/content_layout"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_marginStart="16dp"
|
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="16dp"
|
||||||
android:orientation="vertical" />
|
android:orientation="vertical" />
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
</androidx.core.widget.NestedScrollView>
|
</androidx.core.widget.NestedScrollView>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
</RelativeLayout>
|
</RelativeLayout>
|
||||||
@ -19,4 +19,7 @@
|
|||||||
<string name="expand">EXPAND</string>
|
<string name="expand">EXPAND</string>
|
||||||
<string name="description">Description</string>
|
<string name="description">Description</string>
|
||||||
<string name="playback_error">An unknown playback error has occurred</string>
|
<string name="playback_error">An unknown playback error has occurred</string>
|
||||||
|
<string name="exit_main_text">Press again to exit</string>
|
||||||
|
<string name="content_loading_failed">Content loading failed, click to try again.</string>
|
||||||
|
<string name="try_again">Try Again</string>
|
||||||
</resources>
|
</resources>
|
||||||
@ -1,6 +1,6 @@
|
|||||||
<resources xmlns:tools="http://schemas.android.com/tools">
|
<resources xmlns:tools="http://schemas.android.com/tools">
|
||||||
<!-- Base application theme. -->
|
<!-- Base application theme. -->
|
||||||
<style name="Base.Theme.Musicoo" parent="Theme.Material3.DayNight.NoActionBar">
|
<style name="Base.Theme.Musicoo" parent="Theme.Material3.Dark.NoActionBar">
|
||||||
<!-- Customize your light theme here. -->
|
<!-- Customize your light theme here. -->
|
||||||
<!-- <item name="colorPrimary">@color/my_light_primary</item> -->
|
<!-- <item name="colorPrimary">@color/my_light_primary</item> -->
|
||||||
<item name="android:progressTint">@color/green</item>
|
<item name="android:progressTint">@color/green</item>
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user