From 195ec0f242d1347cfad88b2c9e1f6fecd79e8818 Mon Sep 17 00:00:00 2001 From: ocean <503259349@qq.com> Date: Fri, 10 May 2024 16:56:04 +0800 Subject: [PATCH] update --- .../player/musicoo/activity/LaunchActivity.kt | 12 +- .../player/musicoo/activity/MoBaseActivity.kt | 3 +- .../musicoo/activity/MoListDetailsActivity.kt | 6 +- .../musicoo/activity/MoPlayDetailsActivity.kt | 247 ++++++++++-------- .../musicoo/activity/PrimaryActivity.kt | 175 +++++++++++++ .../player/musicoo/fragment/MoHomeFragment.kt | 62 ++++- .../musicoo/media/MediaControllerManager.kt | 39 +-- .../CustomMediaNotificationProvider.kt | 15 +- .../player/musicoo/service/PlaybackService.kt | 4 +- .../java/com/player/musicoo/util/LogTag.kt | 14 + .../player/musicoo/view/CustomProgressBar.kt | 10 +- app/src/main/res/drawable/drw_btn_bg.xml | 8 + .../res/layout/activity_mo_play_details.xml | 5 +- app/src/main/res/layout/activity_primary.xml | 126 +++++---- app/src/main/res/layout/fragment_import.xml | 3 + app/src/main/res/layout/fragment_mo_home.xml | 119 +++++++-- app/src/main/res/values/strings.xml | 3 + app/src/main/res/values/themes.xml | 2 +- 18 files changed, 594 insertions(+), 259 deletions(-) create mode 100644 app/src/main/res/drawable/drw_btn_bg.xml diff --git a/app/src/main/java/com/player/musicoo/activity/LaunchActivity.kt b/app/src/main/java/com/player/musicoo/activity/LaunchActivity.kt index b02e33a..1c0200f 100644 --- a/app/src/main/java/com/player/musicoo/activity/LaunchActivity.kt +++ b/app/src/main/java/com/player/musicoo/activity/LaunchActivity.kt @@ -3,15 +3,17 @@ package com.player.musicoo.activity import android.content.Intent import android.os.Bundle import android.os.CountDownTimer +import android.util.Log import com.gyf.immersionbar.ktx.immersionBar import com.player.musicoo.databinding.ActivityLaunchBinding +import com.player.musicoo.util.LogTag class LaunchActivity : BaseActivity() { private lateinit var binding: ActivityLaunchBinding - private val totalTime = 3000 // 5秒 - private val interval = 50 // 更新间隔,毫秒 + private val totalTime = 3000L // 5秒 + private val interval = 50L // 更新间隔,毫秒 private val steps = totalTime / interval - private val progressPerStep = 100 / steps + private val progressPerStep = 100f / steps.toFloat() override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) binding = ActivityLaunchBinding.inflate(layoutInflater) @@ -26,13 +28,13 @@ class LaunchActivity : BaseActivity() { private fun initTimer() { val progressBar = binding.customProgressBar - val timer = object : CountDownTimer(totalTime.toLong(), interval.toLong()) { + val timer = object : CountDownTimer(totalTime, interval) { override fun onTick(millisUntilFinished: Long) { progressBar.setProgress(progressBar.getProgress() + progressPerStep) } override fun onFinish() { - progressBar.setProgress(100) + progressBar.setProgress(100f) toMainActivity() } } diff --git a/app/src/main/java/com/player/musicoo/activity/MoBaseActivity.kt b/app/src/main/java/com/player/musicoo/activity/MoBaseActivity.kt index 855a77d..f80e629 100644 --- a/app/src/main/java/com/player/musicoo/activity/MoBaseActivity.kt +++ b/app/src/main/java/com/player/musicoo/activity/MoBaseActivity.kt @@ -17,6 +17,7 @@ import com.bumptech.glide.Glide import com.bumptech.glide.request.target.CustomTarget import com.bumptech.glide.request.transition.Transition import com.player.musicoo.R +import com.player.musicoo.media.MediaControllerManager import com.player.musicoo.sp.AppStore import com.player.musicoo.util.LogTag import kotlinx.coroutines.CoroutineScope @@ -40,7 +41,7 @@ abstract class MoBaseActivity : AppCompatActivity(), CoroutineScope by MainScope protected val TAG = LogTag.VO_ACT_LOG protected val appStore by lazy { AppStore(this) } protected val events = Channel(Channel.UNLIMITED) - + protected val meController = MediaControllerManager.getController() protected abstract suspend fun main() private var defer: suspend () -> Unit = {} private var deferRunning = false diff --git a/app/src/main/java/com/player/musicoo/activity/MoListDetailsActivity.kt b/app/src/main/java/com/player/musicoo/activity/MoListDetailsActivity.kt index d11f295..ff7931f 100644 --- a/app/src/main/java/com/player/musicoo/activity/MoListDetailsActivity.kt +++ b/app/src/main/java/com/player/musicoo/activity/MoListDetailsActivity.kt @@ -1,6 +1,5 @@ package com.player.musicoo.activity -import android.util.Log import androidx.recyclerview.widget.LinearLayoutManager import com.bumptech.glide.Glide 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.innertube.Innertube import com.player.musicoo.innertube.requests.moPlaylistPage +import com.player.musicoo.util.LogTag.LogD class MoListDetailsActivity : MoBaseActivity() { @@ -27,7 +27,7 @@ class MoListDetailsActivity : MoBaseActivity() { return } initView() - Log.d(TAG, "browseId->${browseId}") + LogD(TAG, "browseId->${browseId}") initData(browseId) } @@ -61,7 +61,7 @@ class MoListDetailsActivity : MoBaseActivity() { binding.rv.adapter = adapter }?.onFailure { - Log.d(TAG, "moPlaylistPage onFailure->${it}") + LogD(TAG, "moPlaylistPage onFailure->${it}") } } } \ No newline at end of file diff --git a/app/src/main/java/com/player/musicoo/activity/MoPlayDetailsActivity.kt b/app/src/main/java/com/player/musicoo/activity/MoPlayDetailsActivity.kt index d28577b..7ba50b9 100644 --- a/app/src/main/java/com/player/musicoo/activity/MoPlayDetailsActivity.kt +++ b/app/src/main/java/com/player/musicoo/activity/MoPlayDetailsActivity.kt @@ -3,33 +3,26 @@ package com.player.musicoo.activity import android.os.Handler import android.os.Looper import android.os.Message -import android.util.Log import android.view.View import androidx.annotation.OptIn import androidx.media3.common.MediaItem -import androidx.media3.common.MediaMetadata import androidx.media3.common.PlaybackException import androidx.media3.common.Player import androidx.media3.common.util.UnstableApi -import androidx.media3.session.MediaController import com.bumptech.glide.Glide import com.gyf.immersionbar.ktx.immersionBar import com.player.musicoo.R import com.player.musicoo.databinding.ActivityMoPlayDetailsBinding 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.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.convertMillisToMinutesAndSecondsString import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.isActive import kotlinx.coroutines.launch -import kotlinx.coroutines.runBlocking +import kotlinx.coroutines.selects.select +import com.player.musicoo.util.LogTag.LogD @OptIn(UnstableApi::class) 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_NAME = "play_details_name" const val PLAY_DETAILS_DESC = "play_details_desc" + const val PLAY_DETAILS_COME_FROM = "PLAY_DETAILS_COME_FROM" } private lateinit var binding: ActivityMoPlayDetailsBinding private var currentVideoID = "" + private var comeFrom: Class<*>? = null + private fun initImmersionBar() { immersionBar { statusBarDarkFont(false) @@ -59,77 +55,146 @@ class MoPlayDetailsActivity : MoBaseActivity(), Player.Listener { setContentView(binding.root) initImmersionBar() initClick() + initPlayerListener() val videoId = intent.getStringExtra(PLAY_DETAILS_VIDEO_ID) - Log.d(TAG, "MoPlayDetailsActivity main videoId->$videoId") 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) - Log.d(TAG, "MoPlayDetailsActivity main playlistSetVideoId->$playlistSetVideoId") val params = intent.getStringExtra(PLAY_DETAILS_PLAY_PARAMS) - binding.nameTv.text = intent.getStringExtra(PLAY_DETAILS_NAME) - binding.descTv.text = intent.getStringExtra(PLAY_DETAILS_DESC) + 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.descTv.text = intent.getStringExtra(PLAY_DETAILS_DESC) - if (videoId.isNullOrEmpty()) { - finish() - return + if (videoId.isNullOrEmpty()) { + finish() + return + } + //传入进来的ID,就是进入此界面的当前ID + currentVideoID = videoId + //根据进来界面的当前ID来获取资源。 + initData( + videoId, + playlistId, + playlistSetVideoId, + params + ) + } + + onReceive() + } + + private suspend fun onReceive() { + while (isActive) { + select { + 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() } - //传入进来的ID,就是进入此界面的当前ID - currentVideoID = videoId - //根据进来界面的当前ID来获取资源。 - initData( - videoId, - playlistId, - playlistSetVideoId, - params - ) } private fun initClick() { - val currentPlayer = MediaControllerManager.getController() - binding.disableClicksLayout.setOnClickListener { } binding.backBtn.setOnClickListener { finish() } binding.playLayoutBtn.setOnClickListener { - if (currentPlayer != null) { - if (currentPlayer.isPlaying) { - currentPlayer.pause() + if (meController != null) { + if (meController.isPlaying) { + meController.pause() updatePlayState(false) } else { - currentPlayer.play() + meController.play() updatePlayState(true) } updateProgressState() } } binding.playSkipBackBtn.setOnClickListener { - if (currentPlayer != null) { - currentPlayer.seekToPreviousMediaItem() + if (meController != null) { + meController.seekToPreviousMediaItem() updateProgressUi() - updateInfoUi(currentPlayer.currentMediaItem) + updateInfoUi(meController.currentMediaItem) + updateProgressState() + if (!meController.isPlaying) { + meController.prepare() + meController.play() + } } } binding.playSkipForwardBtn.setOnClickListener { - if (currentPlayer != null) { - currentPlayer.seekToNextMediaItem() + if (meController != null) { + meController.seekToNextMediaItem() updateProgressUi() - updateInfoUi(currentPlayer.currentMediaItem) + updateInfoUi(meController.currentMediaItem) + updateProgressState() + if (!meController.isPlaying) { + meController.prepare() + meController.play() + } } } binding.listLayoutBtn.setOnClickListener { - Log.d(TAG, "currentPlayer?.mediaItemCount->${currentPlayer?.mediaItemCount}") + LogD(TAG, "meController?.mediaItemCount->${meController?.mediaItemCount}") } binding.progressBar.progress = 0 binding.sbProgress.value = 0f binding.sbProgress.addOnChangeListener { slider, value, fromUser -> if (fromUser) { - if (currentPlayer != null) { - currentPlayer.seekTo(value.toLong()) - val ss = currentPlayer.isPlaying + if (meController != null) { + meController.seekTo(value.toLong()) + val ss = meController.isPlaying if (!ss) { - currentPlayer.play() + meController.play() } } } @@ -149,12 +214,11 @@ class MoPlayDetailsActivity : MoBaseActivity(), Player.Listener { parameters ).let { launch(Dispatchers.Main) { - val ss = it.process()//获取到的资源集合 + val songRadioList = it.process()//获取到的资源集合 - if (ss.isEmpty()) {//集合为空则展示错误提示 + if (songRadioList.isEmpty()) {//集合为空则展示错误提示 binding.loadingView.visibility = View.GONE binding.playbackErrorLayout.visibility = View.VISIBLE - binding.disableClicksLayout.visibility = View.VISIBLE } if (isFinishing) { @@ -162,9 +226,7 @@ class MoPlayDetailsActivity : MoBaseActivity(), Player.Listener { } var mediaItem: MediaItem? = null - Log.d(TAG, "size = ${ss.size}") - Log.d(TAG, "MoPlayDetailsActivity initData currentVideoID->$currentVideoID") - for (song: Innertube.SongItem in ss) { + for (song: Innertube.SongItem in songRadioList) { if (song.key == currentVideoID) {//判断当前ID得到一个mediaItem mediaItem = song.asMediaItem break @@ -175,7 +237,6 @@ class MoPlayDetailsActivity : MoBaseActivity(), Player.Listener { updateInfoUi(mediaItem) binding.playbackErrorLayout.visibility = View.GONE - binding.disableClicksLayout.visibility = View.GONE binding.totalDurationTv.visibility = View.GONE @@ -190,18 +251,18 @@ class MoPlayDetailsActivity : MoBaseActivity(), Player.Listener { ) .build() - val meController = MediaControllerManager.getController() meController?.let { - it.addListener(playerListener) it.setMediaItem(newMediaItem, true) it.prepare() 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 { binding.playbackErrorLayout.visibility = View.VISIBLE - binding.disableClicksLayout.visibility = View.VISIBLE } } } @@ -213,29 +274,30 @@ class MoPlayDetailsActivity : MoBaseActivity(), Player.Listener { newPosition: Player.PositionInfo, reason: Int ) { - if (reason == Player.DISCONTINUITY_REASON_AUTO_TRANSITION) { - val meController = MediaControllerManager.getController() + //保证自动播放完毕当前歌曲与通知切换歌曲可以更新UI信息 + if (reason == Player.DISCONTINUITY_REASON_AUTO_TRANSITION + || reason == Player.DISCONTINUITY_REASON_SEEK_ADJUSTMENT + ) { updateInfoUi(meController?.currentMediaItem) } } override fun onPlayerError(error: PlaybackException) { - Log.d(TAG, "onPlayerError error= $error") + LogD(TAG, "onPlayerError error= $error") binding.playbackErrorLayout.visibility = View.VISIBLE//展示错误提示 + + updatePlayState(false) } override fun onPlayWhenReadyChanged( playWhenReady: Boolean, reason: Int ) { - Log.d(TAG, "onPlayWhenReadyChanged = $playWhenReady") updatePlayState(playWhenReady) updateProgressState() } override fun onPlaybackStateChanged(playbackState: Int) { - Log.d(TAG, "onPlaybackStateChanged = $playbackState") - when (playbackState) { Player.STATE_BUFFERING -> { binding.loadingView.visibility = View.VISIBLE @@ -266,13 +328,14 @@ class MoPlayDetailsActivity : MoBaseActivity(), Player.Listener { override fun onDestroy() { super.onDestroy() - MediaControllerManager.getController()?.removeListener(playerListener) + meController?.removeListener(playerListener) } private fun updateProgressUi() { binding.sbProgress.value = 0f binding.progressBar.progress = 0 binding.progressDurationTv.text = convertMillisToMinutesAndSecondsString(0L) + binding.totalDurationTv.visibility = View.GONE } private fun updateInfoUi(mediaItem: MediaItem?) { @@ -292,10 +355,9 @@ class MoPlayDetailsActivity : MoBaseActivity(), Player.Listener { * 更新播放进度 */ private fun updateProgressState() { - val currentPlayer = MediaControllerManager.getController() //判断是否ready与播放中,否则停止更新进度 - if (currentPlayer != null && currentPlayer.playbackState == Player.STATE_READY && currentPlayer.isPlaying) { - updatePlayState(currentPlayer.isPlaying) + if (meController != null && meController.playbackState == Player.STATE_READY && meController.isPlaying) { + updatePlayState(meController.isPlaying) progressHandler.removeCallbacksAndMessages(null) progressHandler.sendEmptyMessage(1) } else { @@ -308,14 +370,13 @@ class MoPlayDetailsActivity : MoBaseActivity(), Player.Listener { */ private val progressHandler = object : Handler(Looper.myLooper()!!) { override fun handleMessage(msg: Message) { - val currentPlayer = MediaControllerManager.getController() //判断是否ready与播放中,否则停止更新进度 - if (currentPlayer != null && currentPlayer.playbackState == Player.STATE_READY && currentPlayer.isPlaying) { - val currentPosition = currentPlayer.currentPosition + if (meController != null && meController.playbackState == Player.STATE_READY && meController.isPlaying) { + val currentPosition = meController.currentPosition val currentString = convertMillisToMinutesAndSecondsString(currentPosition) binding.progressDurationTv.text = currentString - val currentBufferedPosition = currentPlayer.bufferedPosition + val currentBufferedPosition = meController.bufferedPosition binding.progressBar.progress = currentBufferedPosition.toInt() // 更新 SeekBar 的进度 @@ -330,8 +391,7 @@ class MoPlayDetailsActivity : MoBaseActivity(), Player.Listener { * 更新缓冲进度 */ private fun updateProgressBufferingState() { - val currentPlayer = MediaControllerManager.getController() - if (currentPlayer != null && currentPlayer.isLoading) { + if (meController != null && meController.isLoading) { progressBufferingHandler.removeCallbacksAndMessages(null) progressBufferingHandler.sendEmptyMessage(1) } else { @@ -344,9 +404,8 @@ class MoPlayDetailsActivity : MoBaseActivity(), Player.Listener { */ private val progressBufferingHandler = object : Handler(Looper.myLooper()!!) { override fun handleMessage(msg: Message) { - val currentPlayer = MediaControllerManager.getController() - if (currentPlayer != null && currentPlayer.isLoading) { - val currentBufferedPosition = currentPlayer.bufferedPosition + if (meController != null && meController.isLoading) { + val currentBufferedPosition = meController.bufferedPosition binding.progressBar.progress = currentBufferedPosition.toInt() sendEmptyMessageDelayed(1, 50) } @@ -360,44 +419,4 @@ class MoPlayDetailsActivity : MoBaseActivity(), Player.Listener { 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() - } } \ No newline at end of file diff --git a/app/src/main/java/com/player/musicoo/activity/PrimaryActivity.kt b/app/src/main/java/com/player/musicoo/activity/PrimaryActivity.kt index 2cbd060..8658efc 100644 --- a/app/src/main/java/com/player/musicoo/activity/PrimaryActivity.kt +++ b/app/src/main/java/com/player/musicoo/activity/PrimaryActivity.kt @@ -1,11 +1,24 @@ 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.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.databinding.ActivityPrimaryBinding import com.player.musicoo.fragment.ImportFragment 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() { /** @@ -22,6 +35,9 @@ class PrimaryActivity : MoBaseActivity() { binding = ActivityPrimaryBinding.inflate(layoutInflater) setContentView(binding.root) initView() + initPlayerListener() + + onReceive() } private fun initView() { @@ -38,6 +54,28 @@ class PrimaryActivity : MoBaseActivity() { changeFragment(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() { @@ -86,4 +124,141 @@ class PrimaryActivity : MoBaseActivity() { ) } } + + + private suspend fun onReceive() { + while (isActive) { + select { + 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 + } } \ No newline at end of file diff --git a/app/src/main/java/com/player/musicoo/fragment/MoHomeFragment.kt b/app/src/main/java/com/player/musicoo/fragment/MoHomeFragment.kt index 3225707..a5ba814 100644 --- a/app/src/main/java/com/player/musicoo/fragment/MoHomeFragment.kt +++ b/app/src/main/java/com/player/musicoo/fragment/MoHomeFragment.kt @@ -1,7 +1,7 @@ package com.player.musicoo.fragment -import android.util.Log import android.view.LayoutInflater +import android.view.View import android.view.ViewGroup import com.gyf.immersionbar.ktx.immersionBar 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.requests.homePage 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.MusicTowRowListView +import kotlinx.coroutines.channels.Channel +import kotlinx.coroutines.isActive +import kotlinx.coroutines.selects.select class MoHomeFragment : MoBaseFragment() { + private val requests: Channel = Channel(Channel.UNLIMITED) + + enum class Request { + TryAgain, + } + override val bindingInflater: (LayoutInflater, ViewGroup?, Boolean) -> FragmentMoHomeBinding get() = FragmentMoHomeBinding::inflate override suspend fun onViewCreated() { initView() + initData() + onReceive() } private fun initImmersionBar() { @@ -28,8 +40,30 @@ class MoHomeFragment : MoBaseFragment() { } } - private suspend fun initView() { + private suspend fun onReceive() { + while (isActive) { + select { + 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 { + showDataUi() if (it.homePage.isNotEmpty()) { for (home: Innertube.HomePage in it.homePage) { for (content: MusicCarouselShelfRenderer.Content in home.contents) { @@ -55,9 +89,12 @@ class MoHomeFragment : MoBaseFragment() { } initHomeDataMore(it) } else { - Log.d(TAG, "homePage size 0") + LogD(TAG, "homePage size 0") } - }?.onFailure { Log.d(TAG, "homePage onFailure->${it}") } + }?.onFailure { + showNoContentUi() + LogD(TAG, "homePage onFailure->${it}") + } } private suspend fun initHomeDataMore(baseHomePage: Innertube.BaseHomePage) { @@ -87,7 +124,7 @@ class MoHomeFragment : MoBaseFragment() { } initHomeDataMore(it) }?.onFailure { - Log.d(TAG, "initHomeDataMore onFailure ->${it}") + LogD(TAG, "initHomeDataMore onFailure ->${it}") } } } @@ -103,4 +140,19 @@ class MoHomeFragment : MoBaseFragment() { 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 + } + } \ No newline at end of file diff --git a/app/src/main/java/com/player/musicoo/media/MediaControllerManager.kt b/app/src/main/java/com/player/musicoo/media/MediaControllerManager.kt index 85bcad9..1cc6ca4 100644 --- a/app/src/main/java/com/player/musicoo/media/MediaControllerManager.kt +++ b/app/src/main/java/com/player/musicoo/media/MediaControllerManager.kt @@ -167,42 +167,13 @@ object MediaControllerManager { 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 { mediaController?.let { - return it.duration - } - return 0 - } - - fun getTotalBufferedDuration():Long{ - mediaController?.let { - return it.totalBufferedDuration + if (it.duration > 0) { + return it.duration + } else { + return 0 + } } return 0 } diff --git a/app/src/main/java/com/player/musicoo/service/CustomMediaNotificationProvider.kt b/app/src/main/java/com/player/musicoo/service/CustomMediaNotificationProvider.kt index 838297d..acc956d 100644 --- a/app/src/main/java/com/player/musicoo/service/CustomMediaNotificationProvider.kt +++ b/app/src/main/java/com/player/musicoo/service/CustomMediaNotificationProvider.kt @@ -1,12 +1,15 @@ package com.player.musicoo.service import android.content.Context -import android.util.Log import androidx.core.app.NotificationCompat 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.player.musicoo.util.LogTag +import com.player.musicoo.util.LogTag.LogD @UnstableApi class CustomMediaNotificationProvider(context: Context) : @@ -19,12 +22,12 @@ class CustomMediaNotificationProvider(context: Context) : actionFactory: MediaNotification.ActionFactory ): IntArray { - Log.d(LogTag.VO_API_LOG, "mediaButtons->$mediaButtons") + LogD(LogTag.VO_API_LOG, "mediaButtons->$mediaButtons") for (com: CommandButton in mediaButtons) { - Log.d(LogTag.VO_API_LOG, "displayName->${com.displayName}") - Log.d(LogTag.VO_API_LOG, "playerCommand->${com.playerCommand}") - Log.d(LogTag.VO_API_LOG, "------------------------------------------") + LogD(LogTag.VO_API_LOG, "displayName->${com.displayName}") + LogD(LogTag.VO_API_LOG, "playerCommand->${com.playerCommand}") + LogD(LogTag.VO_API_LOG, "------------------------------------------") } val notificationMediaButtons = ImmutableList.builder().apply { diff --git a/app/src/main/java/com/player/musicoo/service/PlaybackService.kt b/app/src/main/java/com/player/musicoo/service/PlaybackService.kt index 183d750..a2fa9ab 100644 --- a/app/src/main/java/com/player/musicoo/service/PlaybackService.kt +++ b/app/src/main/java/com/player/musicoo/service/PlaybackService.kt @@ -41,6 +41,7 @@ import com.player.musicoo.innertube.models.bodies.PlayerBody import com.player.musicoo.innertube.requests.player import com.player.musicoo.util.ExoPlayerDiskCacheMaxSize import com.player.musicoo.util.LogTag +import com.player.musicoo.util.LogTag.LogD import com.player.musicoo.util.RingBuffer import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.runBlocking @@ -152,7 +153,6 @@ class PlaybackService : MediaSessionService(), Player.Listener { return ResolvingDataSource.Factory(createCacheDataSource()) { dataSpec -> val videoId = dataSpec.key ?: error("A key must be set") - Log.d(TAG,"111111 videoId") if (cache.isCached(videoId, dataSpec.position, chunkLength)) { dataSpec } 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 -> ringBuffer.append(videoId to url.toUri()) diff --git a/app/src/main/java/com/player/musicoo/util/LogTag.kt b/app/src/main/java/com/player/musicoo/util/LogTag.kt index fdf7e58..6b7a6e6 100644 --- a/app/src/main/java/com/player/musicoo/util/LogTag.kt +++ b/app/src/main/java/com/player/musicoo/util/LogTag.kt @@ -1,8 +1,22 @@ package com.player.musicoo.util +import android.util.Log + object LogTag { const val VO_ACT_LOG = "vo-act—log" const val VO_FRAGMENT_LOG = "vo-fragment-log" const val VO_API_LOG = "vo-api—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) + } } \ No newline at end of file diff --git a/app/src/main/java/com/player/musicoo/view/CustomProgressBar.kt b/app/src/main/java/com/player/musicoo/view/CustomProgressBar.kt index 529e6dc..f35f6dd 100644 --- a/app/src/main/java/com/player/musicoo/view/CustomProgressBar.kt +++ b/app/src/main/java/com/player/musicoo/view/CustomProgressBar.kt @@ -11,8 +11,8 @@ import android.util.AttributeSet import android.view.View class CustomProgressBar(context: Context, attrs: AttributeSet?) : View(context, attrs) { - private var progress = 0 // 当前进度 - private val maxProgress = 100 // 最大进度 + private var progress = 0f // 当前进度 + private val maxProgress = 100f // 最大进度 private val progressBarHeight = 20f // 进度条高度 private val cornerRadius = 10f // 圆角半径 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) @@ -50,12 +50,12 @@ class CustomProgressBar(context: Context, attrs: AttributeSet?) : View(context, canvas.drawRoundRect(rect, cornerRadius, cornerRadius, paintTow) } - fun getProgress():Int{ + fun getProgress():Float{ return progress } // 设置进度 - fun setProgress(progress: Int) { + fun setProgress(progress: Float) { this.progress = progress invalidate() // 请求重绘 } diff --git a/app/src/main/res/drawable/drw_btn_bg.xml b/app/src/main/res/drawable/drw_btn_bg.xml new file mode 100644 index 0000000..b422119 --- /dev/null +++ b/app/src/main/res/drawable/drw_btn_bg.xml @@ -0,0 +1,8 @@ + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_mo_play_details.xml b/app/src/main/res/layout/activity_mo_play_details.xml index 64a2a95..f85b1de 100644 --- a/app/src/main/res/layout/activity_mo_play_details.xml +++ b/app/src/main/res/layout/activity_mo_play_details.xml @@ -117,7 +117,10 @@ android:id="@+id/loading_view" android:layout_width="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" /> - + android:layout_below="@+id/view" + android:orientation="vertical"> + + + + - - - - - - - - - - - + android:orientation="horizontal"> - + android:layout_height="wrap_content"> - + + + + + + + + + + + + + + + + + android:gravity="center" + android:visibility="gone"> diff --git a/app/src/main/res/layout/fragment_mo_home.xml b/app/src/main/res/layout/fragment_mo_home.xml index 881a002..7b66bfa 100644 --- a/app/src/main/res/layout/fragment_mo_home.xml +++ b/app/src/main/res/layout/fragment_mo_home.xml @@ -16,42 +16,105 @@ android:layout_width="match_parent" android:layout_height="0dp" /> - + android:orientation="vertical"> + android:layout_height="match_parent" + android:gravity="center" + android:visibility="gone"> - - - - - - - - + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 0ce0f53..c42ca71 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -19,4 +19,7 @@ EXPAND Description An unknown playback error has occurred + Press again to exit + Content loading failed, click to try again. + Try Again \ No newline at end of file diff --git a/app/src/main/res/values/themes.xml b/app/src/main/res/values/themes.xml index a7eec50..5c07101 100644 --- a/app/src/main/res/values/themes.xml +++ b/app/src/main/res/values/themes.xml @@ -1,6 +1,6 @@ -