This commit is contained in:
ocean 2024-05-11 11:39:32 +08:00
parent 195ec0f242
commit c90b8a6930
18 changed files with 685 additions and 140 deletions

View File

@ -1,5 +1,6 @@
package com.player.musicoo.activity
import android.view.View
import androidx.recyclerview.widget.LinearLayoutManager
import com.bumptech.glide.Glide
import com.gyf.immersionbar.ktx.immersionBar
@ -45,8 +46,10 @@ class MoListDetailsActivity : MoBaseActivity() {
}
private suspend fun initData(browseId: String) {
showLoadingUi()
Innertube.moPlaylistPage(browseId)
?.onSuccess {
showDataUi()
Glide.with(this)
.load(it.thumbnail)
.into(binding.imageView)
@ -61,7 +64,23 @@ class MoListDetailsActivity : MoBaseActivity() {
binding.rv.adapter = adapter
}?.onFailure {
showNoContentUi()
LogD(TAG, "moPlaylistPage onFailure->${it}")
}
}
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
}
}

View File

@ -1,28 +1,36 @@
package com.player.musicoo.activity
import android.annotation.SuppressLint
import android.graphics.Bitmap
import android.graphics.drawable.Drawable
import android.os.Handler
import android.os.Looper
import android.os.Message
import android.view.View
import android.view.animation.AnimationUtils
import androidx.annotation.OptIn
import androidx.media3.common.MediaItem
import androidx.media3.common.PlaybackException
import androidx.media3.common.Player
import androidx.media3.common.util.UnstableApi
import androidx.recyclerview.widget.LinearLayoutManager
import com.bumptech.glide.Glide
import com.bumptech.glide.request.target.CustomTarget
import com.bumptech.glide.request.transition.Transition
import com.gyf.immersionbar.ktx.immersionBar
import com.player.musicoo.R
import com.player.musicoo.adapter.PlayListAdapter
import com.player.musicoo.databinding.ActivityMoPlayDetailsBinding
import com.player.musicoo.innertube.Innertube
import com.player.musicoo.media.MediaControllerManager
import com.player.musicoo.media.SongRadio
import com.player.musicoo.util.LogTag.LogD
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.selects.select
import com.player.musicoo.util.LogTag.LogD
@OptIn(UnstableApi::class)
class MoPlayDetailsActivity : MoBaseActivity(), Player.Listener {
@ -42,6 +50,7 @@ class MoPlayDetailsActivity : MoBaseActivity(), Player.Listener {
private var currentVideoID = ""
private var comeFrom: Class<*>? = null
private var playListAdapter: PlayListAdapter? = null
private fun initImmersionBar() {
immersionBar {
@ -120,9 +129,8 @@ class MoPlayDetailsActivity : MoBaseActivity(), Player.Listener {
if (meController != null && meController.currentMediaItem != null) {
binding.playbackErrorLayout.visibility = View.GONE
binding.loadingView.visibility = View.GONE
val currentString = convertMillisToMinutesAndSecondsString(meController.currentPosition)
binding.disableClicksLayout.visibility = View.GONE
val currentString = convertMillisToMinutesAndSecondsString(MediaControllerManager.getCurrentPosition())
binding.progressDurationTv.text = currentString
if (MediaControllerManager.getDuration() > 0) {
binding.totalDurationTv.visibility = View.VISIBLE
@ -133,13 +141,31 @@ class MoPlayDetailsActivity : MoBaseActivity(), Player.Listener {
binding.totalDurationTv.text =
convertMillisToMinutesAndSecondsString(MediaControllerManager.getDuration())
binding.sbProgress.value = meController.currentPosition.toFloat()
binding.sbProgress.value = MediaControllerManager.getCurrentPosition().toFloat()
binding.sbProgress.valueTo = MediaControllerManager.getDuration().toFloat()
updateProgressState()
binding.progressBar.progress = meController.bufferedPosition.toInt()
binding.progressBar.progress = MediaControllerManager.getBufferedPosition().toInt()
binding.progressBar.max = MediaControllerManager.getDuration().toInt()
updateProgressBufferingState()
val mediaItemCount = meController.mediaItemCount
val allMediaItems: MutableList<MediaItem> = mutableListOf()
for (index in 0 until mediaItemCount) {
val mediaItemAt = meController.getMediaItemAt(index)
allMediaItems.add(mediaItemAt)
}
playListAdapter = PlayListAdapter(
this@MoPlayDetailsActivity,
allMediaItems
)
binding.playListRv.layoutManager =
LinearLayoutManager(
this@MoPlayDetailsActivity,
LinearLayoutManager.VERTICAL,
false
)
binding.playListRv.adapter = playListAdapter
}
}
@ -147,6 +173,16 @@ class MoPlayDetailsActivity : MoBaseActivity(), Player.Listener {
binding.backBtn.setOnClickListener {
finish()
}
binding.tryAgainBtn.setOnClickListener {
if (meController != null) {
updateInfoUi(meController.currentMediaItem)
updateProgressState()
if (!meController.isPlaying) {
meController.prepare()
meController.play()
}
}
}
binding.playLayoutBtn.setOnClickListener {
if (meController != null) {
if (meController.isPlaying) {
@ -184,7 +220,13 @@ class MoPlayDetailsActivity : MoBaseActivity(), Player.Listener {
}
}
binding.listLayoutBtn.setOnClickListener {
LogD(TAG, "meController?.mediaItemCount->${meController?.mediaItemCount}")
toggleBottomLayout()
}
binding.bottomCloseBtn.setOnClickListener {
toggleBottomLayout()
}
binding.bottomBlankLayout.setOnClickListener {
toggleBottomLayout()
}
binding.progressBar.progress = 0
binding.sbProgress.value = 0f
@ -218,7 +260,20 @@ class MoPlayDetailsActivity : MoBaseActivity(), Player.Listener {
if (songRadioList.isEmpty()) {//集合为空则展示错误提示
binding.loadingView.visibility = View.GONE
binding.disableClicksLayout.visibility = View.GONE
binding.playbackErrorLayout.visibility = View.VISIBLE
} else {
playListAdapter = PlayListAdapter(
this@MoPlayDetailsActivity,
songRadioList.map(Innertube.SongItem::asMediaItem)
)
binding.playListRv.layoutManager =
LinearLayoutManager(
this@MoPlayDetailsActivity,
LinearLayoutManager.VERTICAL,
false
)
binding.playListRv.adapter = playListAdapter
}
if (isFinishing) {
@ -269,6 +324,7 @@ class MoPlayDetailsActivity : MoBaseActivity(), Player.Listener {
}
private val playerListener = object : Player.Listener {
@SuppressLint("NotifyDataSetChanged")
override fun onPositionDiscontinuity(
oldPosition: Player.PositionInfo,
newPosition: Player.PositionInfo,
@ -278,7 +334,11 @@ class MoPlayDetailsActivity : MoBaseActivity(), Player.Listener {
if (reason == Player.DISCONTINUITY_REASON_AUTO_TRANSITION
|| reason == Player.DISCONTINUITY_REASON_SEEK_ADJUSTMENT
) {
updateProgressUi()
updateInfoUi(meController?.currentMediaItem)
if (playListAdapter != null) {
playListAdapter?.notifyDataSetChanged()
}
}
}
@ -301,11 +361,14 @@ class MoPlayDetailsActivity : MoBaseActivity(), Player.Listener {
when (playbackState) {
Player.STATE_BUFFERING -> {
binding.loadingView.visibility = View.VISIBLE
binding.disableClicksLayout.visibility = View.VISIBLE
binding.playbackErrorLayout.visibility = View.GONE
}
Player.STATE_READY -> {
binding.playbackErrorLayout.visibility = View.GONE
binding.loadingView.visibility = View.GONE
binding.disableClicksLayout.visibility = View.GONE
binding.totalDurationTv.visibility = View.VISIBLE
binding.totalDurationTv.text =
convertMillisToMinutesAndSecondsString(
@ -320,6 +383,7 @@ class MoPlayDetailsActivity : MoBaseActivity(), Player.Listener {
else -> {
binding.loadingView.visibility = View.GONE
binding.disableClicksLayout.visibility = View.GONE
}
}
updateProgressState()
@ -343,9 +407,24 @@ class MoPlayDetailsActivity : MoBaseActivity(), Player.Listener {
binding.playbackErrorLayout.visibility = View.VISIBLE
return
}
Glide.with(this@MoPlayDetailsActivity)
Glide.with(this)
.asBitmap()
.load(mediaItem.mediaMetadata.artworkUri)
.into(binding.thumbnail)
.placeholder(R.mipmap.musicoo_logo_img)
.into(object : CustomTarget<Bitmap>() {
override fun onResourceReady(resource: Bitmap, transition: Transition<in Bitmap>?) {
binding.thumbnail.setImageBitmap(resource)
val blurredBitmap = applyGaussianBlur(resource, 25f, this@MoPlayDetailsActivity)
binding.imageView.setImageBitmap(blurredBitmap)
}
override fun onLoadCleared(placeholder: Drawable?) {
if (placeholder != null) {
binding.thumbnail.setImageDrawable(placeholder)
}
}
})
binding.nameTv.text = mediaItem.mediaMetadata.title
binding.descTv.text = mediaItem.mediaMetadata.artist
@ -372,11 +451,11 @@ class MoPlayDetailsActivity : MoBaseActivity(), Player.Listener {
override fun handleMessage(msg: Message) {
//判断是否ready与播放中否则停止更新进度
if (meController != null && meController.playbackState == Player.STATE_READY && meController.isPlaying) {
val currentPosition = meController.currentPosition
val currentPosition = MediaControllerManager.getCurrentPosition()
val currentString = convertMillisToMinutesAndSecondsString(currentPosition)
binding.progressDurationTv.text = currentString
val currentBufferedPosition = meController.bufferedPosition
val currentBufferedPosition = MediaControllerManager.getBufferedPosition()
binding.progressBar.progress = currentBufferedPosition.toInt()
// 更新 SeekBar 的进度
@ -405,7 +484,7 @@ class MoPlayDetailsActivity : MoBaseActivity(), Player.Listener {
private val progressBufferingHandler = object : Handler(Looper.myLooper()!!) {
override fun handleMessage(msg: Message) {
if (meController != null && meController.isLoading) {
val currentBufferedPosition = meController.bufferedPosition
val currentBufferedPosition = MediaControllerManager.getBufferedPosition()
binding.progressBar.progress = currentBufferedPosition.toInt()
sendEmptyMessageDelayed(1, 50)
}
@ -419,4 +498,25 @@ class MoPlayDetailsActivity : MoBaseActivity(), Player.Listener {
binding.playImg.setImageResource(R.drawable.play_green_icon)
}
}
private fun toggleBottomLayout() {
if (binding.bottomLayout.visibility == View.VISIBLE) {
hideBottomLayout()
} else {
showBottomLayout()
}
}
private fun showBottomLayout() {
val slideUpAnimation = AnimationUtils.loadAnimation(this, R.anim.slide_up)
binding.bottomLayout.startAnimation(slideUpAnimation)
binding.bottomLayout.visibility = View.VISIBLE
}
private fun hideBottomLayout() {
val slideDownAnimation = AnimationUtils.loadAnimation(this, R.anim.slide_down)
binding.bottomLayout.startAnimation(slideDownAnimation)
binding.bottomLayout.visibility = View.GONE
}
}

View File

@ -10,6 +10,7 @@ import androidx.recyclerview.widget.RecyclerView
import com.bumptech.glide.Glide
import com.player.musicoo.App
import com.player.musicoo.R
import com.player.musicoo.activity.MoPlayDetailsActivity
import com.player.musicoo.activity.PlayDetailsActivity
import com.player.musicoo.bean.Audio
import com.player.musicoo.databinding.DetailsListItemBinding
@ -27,6 +28,10 @@ class DetailsListAdapter(
) :
RecyclerView.Adapter<DetailsListAdapter.ViewHolder>() {
companion object {
const val FROM_TAG = "list_details_activity_to_adapter"
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val binding = DetailsListItemBinding.inflate(LayoutInflater.from(context), parent, false)
return ViewHolder(binding)
@ -35,6 +40,23 @@ class DetailsListAdapter(
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val bean = list[position]
holder.bind(bean)
val videoId = bean.videoId
holder.itemView.setOnClickListener {
val intent = Intent(context, MoPlayDetailsActivity::class.java)
intent.putExtra(MoPlayDetailsActivity.PLAY_DETAILS_VIDEO_ID, videoId)
intent.putExtra(MoPlayDetailsActivity.PLAY_DETAILS_PLAY_LIST_ID, bean.playlistId)
intent.putExtra(
MoPlayDetailsActivity.PLAY_DETAILS_PLAY_LIST_SET_VIDEO_ID,
bean.playlistSetVideoId
)
intent.putExtra(MoPlayDetailsActivity.PLAY_DETAILS_PLAY_PARAMS, bean.params)
intent.putExtra(MoPlayDetailsActivity.PLAY_DETAILS_NAME, bean.name)
intent.putExtra(MoPlayDetailsActivity.PLAY_DETAILS_DESC, bean.title)
context.startActivity(intent)
}
}
override fun getItemCount(): Int = list.size

View File

@ -0,0 +1,94 @@
package com.player.musicoo.adapter
import android.annotation.SuppressLint
import android.content.Context
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.media3.common.C
import androidx.media3.common.MediaItem
import androidx.media3.common.Player
import androidx.recyclerview.widget.RecyclerView
import com.bumptech.glide.Glide
import com.player.musicoo.R
import com.player.musicoo.databinding.PlayListItemBinding
import com.player.musicoo.media.MediaControllerManager
class PlayListAdapter(
private val context: Context,
private val list: List<MediaItem>,
) :
RecyclerView.Adapter<PlayListAdapter.ViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val binding = PlayListItemBinding.inflate(LayoutInflater.from(context), parent, false)
return ViewHolder(binding)
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val bean = list[position]
holder.bind(bean)
holder.itemView.setOnClickListener {
val meController = MediaControllerManager.getController()
if (meController != null && meController.currentMediaItem != null) {
var index = holder.bindingAdapterPosition
if (index > meController.mediaItemCount) {
index = 1
}
meController.seekTo(index, C.TIME_UNSET)
if (!meController.isPlaying) {
meController.prepare()
meController.play()
}
}
}
}
override fun getItemCount(): Int = list.size
inner class ViewHolder(private val binding: PlayListItemBinding) :
RecyclerView.ViewHolder(binding.root) {
@SuppressLint("SetTextI18n")
fun bind(bean: MediaItem) {
binding.apply {
val meController = MediaControllerManager.getController()
if (meController != null && meController.currentMediaItem != null) {
if (meController.currentMediaItem?.mediaId == bean.mediaId) {
binding.currentPlayingLayout.visibility = View.VISIBLE
binding.title.setTextColor(context.getColor(R.color.green))
binding.name.setTextColor(context.getColor(R.color.green_60))
} else {
binding.currentPlayingLayout.visibility = View.GONE
binding.title.setTextColor(context.getColor(R.color.white))
binding.name.setTextColor(context.getColor(R.color.white_60))
}
}
Glide.with(context)
.load(bean.mediaMetadata.artworkUri)
.into(image)
title.text = bean.mediaMetadata.title
if (bean.mediaMetadata.artist.isNullOrEmpty()) {
name.visibility = View.GONE
} else {
name.visibility = View.VISIBLE
name.text = bean.mediaMetadata.artist
}
}
}
}
private var itemClickListener: OnItemClickListener? = null
fun setOnItemClickListener(listener: OnItemClickListener) {
itemClickListener = listener
}
interface OnItemClickListener {
fun onItemClick(position: Int)
}
}

View File

@ -7,6 +7,7 @@ import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView
import com.bumptech.glide.Glide
import com.player.musicoo.activity.MoListDetailsActivity
import com.player.musicoo.activity.MoPlayDetailsActivity
import com.player.musicoo.databinding.MusicTowRowItemBinding
import com.player.musicoo.innertube.models.MusicCarouselShelfRenderer
@ -34,12 +35,56 @@ class TowRowListAdapter(
val browseId = browseEndpoint?.browseId
holder.bind(bean)
val watchEndpoint = bean.musicTwoRowItemRenderer
?.navigationEndpoint
?.watchEndpoint
val videoId = watchEndpoint?.videoId
val playlistId = watchEndpoint?.playlistId
val playlistSetVideoId = watchEndpoint?.playlistSetVideoId
val params = watchEndpoint?.params
val url = bean.musicTwoRowItemRenderer
?.thumbnailRenderer
?.musicThumbnailRenderer
?.thumbnail
?.thumbnails
?.let { it.getOrNull(1) ?: it.getOrNull(0) }
?.url ?: ""
val name = bean.musicTwoRowItemRenderer
?.title
?.runs
?.firstOrNull()
?.text ?: ""
val desc = bean.musicTwoRowItemRenderer
?.subtitle
?.runs
?.map { it.text }
?.joinToString("") ?: ""
holder.bind(url = url, name = name, desc = desc)
holder.itemView.setOnClickListener {
val intent = Intent(context, MoListDetailsActivity::class.java)
intent.putExtra(MoListDetailsActivity.PLAY_LIST_PAGE_BROWSE_ID, browseId)
context.startActivity(intent)
if (browseId.isNullOrEmpty()) {
val intent = Intent(context, MoPlayDetailsActivity::class.java)
intent.putExtra(MoPlayDetailsActivity.PLAY_DETAILS_VIDEO_ID, videoId)
intent.putExtra(MoPlayDetailsActivity.PLAY_DETAILS_PLAY_LIST_ID, playlistId)
if (playlistSetVideoId != null) {
intent.putExtra(
MoPlayDetailsActivity.PLAY_DETAILS_PLAY_LIST_SET_VIDEO_ID,
playlistSetVideoId
)
}
intent.putExtra(MoPlayDetailsActivity.PLAY_DETAILS_PLAY_PARAMS, params)
intent.putExtra(MoPlayDetailsActivity.PLAY_DETAILS_NAME, name)
intent.putExtra(MoPlayDetailsActivity.PLAY_DETAILS_DESC, desc)
context.startActivity(intent)
} else {
val intent = Intent(context, MoListDetailsActivity::class.java)
intent.putExtra(MoListDetailsActivity.PLAY_LIST_PAGE_BROWSE_ID, browseId)
context.startActivity(intent)
}
}
}
@ -48,24 +93,8 @@ class TowRowListAdapter(
inner class ViewHolder(private val binding: MusicTowRowItemBinding) :
RecyclerView.ViewHolder(binding.root) {
fun bind(content: MusicCarouselShelfRenderer.Content) {
val url = content.musicTwoRowItemRenderer
?.thumbnailRenderer
?.musicThumbnailRenderer
?.thumbnail
?.thumbnails
?.let { it.getOrNull(1) ?: it.getOrNull(0) }
?.url
val name = content.musicTwoRowItemRenderer
?.title
?.runs
?.firstOrNull()
?.text
val desc = content.musicTwoRowItemRenderer
?.subtitle
?.runs
?.map { it.text }
?.joinToString("")
fun bind(url: String, name: String, desc: String) {
binding.apply {
Glide.with(context)
.load(url)

View File

@ -90,6 +90,7 @@ class MoHomeFragment : MoBaseFragment<FragmentMoHomeBinding>() {
initHomeDataMore(it)
} else {
LogD(TAG, "homePage size 0")
showNoContentUi()
}
}?.onFailure {
showNoContentUi()
@ -141,16 +142,17 @@ class MoHomeFragment : MoBaseFragment<FragmentMoHomeBinding>() {
}
}
private fun showDataUi(){
private fun showDataUi() {
binding.loadingLayout.visibility = View.GONE
binding.noContentLayout.visibility = View.GONE
}
private fun showLoadingUi(){
private fun showLoadingUi() {
binding.loadingLayout.visibility = View.VISIBLE
binding.noContentLayout.visibility = View.GONE
}
private fun showNoContentUi(){
private fun showNoContentUi() {
binding.loadingLayout.visibility = View.GONE
binding.noContentLayout.visibility = View.VISIBLE
}

View File

@ -193,7 +193,9 @@ object Innertube {
val timeText: String?,
val browseId: String,
val videoId: String?,
val playlistId: String?,
val playlistId: String? = null,
val playlistSetVideoId: String? = null,
val params: String? = null,
val musicVideoType: String?,
val pageType: String?,
val thumbnailUrl: String?

View File

@ -80,7 +80,11 @@ suspend fun Innertube.moPlaylistPage(browseId: String): Result<Innertube.MoPlayl
val bean = Innertube.MoPlaylistOrAlbumPage.MoPlaylistOrAlbumListBean(
title = runs0?.text,
name = runs1?.text,
name = runs1?.text ?: musicDetailHeaderRenderer
?.subtitle
?.runs
?.map { it.text }
?.joinToString(""),
desc = runs2?.text,
timeText = content.musicResponsiveListItemRenderer
?.fixedColumns?.firstOrNull()
@ -92,6 +96,8 @@ suspend fun Innertube.moPlaylistPage(browseId: String): Result<Innertube.MoPlayl
browseId = browseEndpoint?.browseId.toString(),
videoId = watchEndpoint?.videoId,
playlistId = watchEndpoint?.playlistId,
playlistSetVideoId = watchEndpoint?.playlistSetVideoId,
params = watchEndpoint?.params,
musicVideoType = watchEndpoint?.type,
pageType = browseEndpoint?.type,
thumbnailUrl = thumbnailUrl

View File

@ -177,4 +177,26 @@ object MediaControllerManager {
}
return 0
}
fun getCurrentPosition(): Long {
mediaController?.let {
if (it.currentPosition > 0 && it.currentPosition < it.duration) {
return it.currentPosition
} else {
return 0
}
}
return 0
}
fun getBufferedPosition(): Long {
mediaController?.let {
if (it.bufferedPosition > 0) {
return it.bufferedPosition
} else {
return 0
}
}
return 0
}
}

View File

@ -57,12 +57,10 @@ class PlaybackService : MediaSessionService(), Player.Listener {
override fun onCreate() {
super.onCreate()
val cacheEvictor = when (val size = ExoPlayerDiskCacheMaxSize.`2GB`){
val cacheEvictor = when (val size = ExoPlayerDiskCacheMaxSize.`2GB`) {
ExoPlayerDiskCacheMaxSize.Unlimited -> NoOpCacheEvictor()
else -> LeastRecentlyUsedCacheEvictor(size.bytes)
}
// TODO: Remove in a future release
val directory = cacheDir.resolve("exoplayer").also { directory ->
if (directory.exists()) return@also
@ -80,7 +78,6 @@ class PlaybackService : MediaSessionService(), Player.Listener {
}
cache = SimpleCache(directory, cacheEvictor, StandaloneDatabaseProvider(this))
player = ExoPlayer.Builder(this, createRendersFactory(), createMediaSourceFactory())
.setHandleAudioBecomingNoisy(true)
.setWakeMode(C.WAKE_MODE_LOCAL)
@ -181,7 +178,7 @@ class PlaybackService : MediaSessionService(), Player.Listener {
)
}
}
LogD(TAG,"service urlResult->$urlResult")
LogD(TAG, "service urlResult->$urlResult")
urlResult?.getOrThrow()?.let { url ->
ringBuffer.append(videoId to url.toUri())

View 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="#FF555555" />
<corners android:radius="20dp" />
</shape>

View File

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<solid android:color="@color/bottom_layout_bg_color" />
<corners
android:topLeftRadius="18dp"
android:topRightRadius="18dp" />
</shape>

View File

@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="14dp"
android:height="17dp"
android:viewportWidth="14"
android:viewportHeight="17">
<path
android:pathData="M7,16.8C6.629,16.8 6.273,16.653 6.01,16.39C5.747,16.127 5.6,15.771 5.6,15.4V1.4C5.6,1.029 5.747,0.673 6.01,0.41C6.273,0.147 6.629,0 7,0C7.371,0 7.727,0.147 7.99,0.41C8.252,0.673 8.4,1.029 8.4,1.4V15.4C8.4,15.771 8.252,16.127 7.99,16.39C7.727,16.653 7.371,16.8 7,16.8ZM1.4,14C1.029,14 0.673,13.852 0.41,13.59C0.147,13.327 0,12.971 0,12.6V5.6C0,5.229 0.148,4.873 0.41,4.61C0.673,4.348 1.029,4.2 1.4,4.2C1.771,4.2 2.127,4.348 2.39,4.61C2.652,4.873 2.8,5.229 2.8,5.6V12.6C2.8,12.784 2.764,12.966 2.693,13.136C2.623,13.306 2.52,13.46 2.39,13.59C2.26,13.72 2.106,13.823 1.936,13.893C1.766,13.964 1.584,14 1.4,14ZM12.6,14C12.229,14 11.873,13.852 11.61,13.59C11.347,13.327 11.2,12.971 11.2,12.6V5.6C11.2,5.229 11.348,4.873 11.61,4.61C11.873,4.348 12.229,4.2 12.6,4.2C12.971,4.2 13.327,4.348 13.59,4.61C13.852,4.873 14,5.229 14,5.6V12.6C14,12.971 13.852,13.327 13.59,13.59C13.327,13.852 12.971,14 12.6,14Z"
android:fillColor="#FF80F988"/>
</vector>

View File

@ -16,107 +16,170 @@
android:layout_width="match_parent"
android:layout_height="0dp" />
<LinearLayout
android:id="@+id/title_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@+id/view"
android:layout_margin="16dp"
android:orientation="horizontal">
<RelativeLayout
android:id="@+id/back_btn"
android:layout_width="42dp"
android:layout_height="42dp"
android:background="@drawable/drw_back_bg">
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:src="@drawable/back_icon" />
</RelativeLayout>
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_below="@+id/title_layout"
android:layout_below="@+id/view"
android:orientation="vertical">
<LinearLayout
android:id="@+id/title_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginEnd="16dp"
android:gravity="center_vertical"
android:layout_margin="16dp"
android:orientation="horizontal">
<androidx.cardview.widget.CardView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:elevation="0dp"
app:cardCornerRadius="10dp"
app:cardElevation="0dp">
<RelativeLayout
android:id="@+id/back_btn"
android:layout_width="42dp"
android:layout_height="42dp"
android:background="@drawable/drw_back_bg">
<ImageView
android:id="@+id/imageView"
android:layout_width="160dp"
android:layout_height="160dp"
android:src="@mipmap/musicoo_logo_img" />
</androidx.cardview.widget.CardView>
<LinearLayout
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_marginStart="16dp"
android:layout_weight="1"
android:gravity="center_vertical"
android:orientation="vertical">
<TextView
android:id="@+id/title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:fontFamily="@font/medium_font"
android:maxLines="2"
android:text="@string/app_name"
android:textColor="@color/white"
android:textSize="18dp" />
android:layout_centerInParent="true"
android:src="@drawable/back_icon" />
</RelativeLayout>
<TextView
android:id="@+id/subtitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:fontFamily="@font/medium_font"
android:maxLines="1"
android:text="@string/ready_to_sleep"
android:textColor="@color/white_60"
android:textSize="12dp" />
<TextView
android:id="@+id/secondSubtitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="2dp"
android:fontFamily="@font/regular_font"
android:maxLines="1"
android:text="@string/app_name"
android:textColor="@color/white_60"
android:textSize="12dp" />
</LinearLayout>
</LinearLayout>
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/rv"
<LinearLayout
android:id="@+id/loadingLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="16dp" />
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>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginEnd="16dp"
android:gravity="center_vertical"
android:orientation="horizontal">
<androidx.cardview.widget.CardView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:elevation="0dp"
app:cardCornerRadius="10dp"
app:cardElevation="0dp">
<ImageView
android:id="@+id/imageView"
android:layout_width="160dp"
android:layout_height="160dp"
android:src="@mipmap/musicoo_logo_img" />
</androidx.cardview.widget.CardView>
<LinearLayout
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_marginStart="16dp"
android:layout_weight="1"
android:gravity="center_vertical"
android:orientation="vertical">
<TextView
android:id="@+id/title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:fontFamily="@font/medium_font"
android:maxLines="2"
android:text="@string/app_name"
android:textColor="@color/white"
android:textSize="18dp" />
<TextView
android:id="@+id/subtitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:fontFamily="@font/medium_font"
android:maxLines="1"
android:text="@string/ready_to_sleep"
android:textColor="@color/white_60"
android:textSize="12dp" />
<TextView
android:id="@+id/secondSubtitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="2dp"
android:fontFamily="@font/regular_font"
android:maxLines="1"
android:text="@string/app_name"
android:textColor="@color/white_60"
android:textSize="12dp" />
</LinearLayout>
</LinearLayout>
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/rv"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="16dp" />
</LinearLayout>
</LinearLayout>
</RelativeLayout>

View File

@ -23,7 +23,7 @@
android:id="@+id/imageView"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="5"
android:layout_weight="8"
android:scaleType="centerCrop" />
<View
@ -40,15 +40,15 @@
<View
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="5"
android:layout_weight="8"
android:background="@drawable/drw_details_bg"
android:visibility="gone" />
android:visibility="visible" />
<View
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:background="@color/main_bg_color" />
android:background="@color/black" />
</LinearLayout>
@ -122,19 +122,42 @@
android:progressBackgroundTint="@color/green"
android:progressTint="@color/green" />
<TextView
<LinearLayout
android:id="@+id/playback_error_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_centerInParent="true"
android:background="@color/black_60"
android:padding="24dp"
android:fontFamily="@font/medium_font"
android:gravity="center"
android:text="@string/playback_error"
android:textColor="@color/white_80"
android:textSize="16dp"
android:visibility="gone" />
android:background="@color/black_60"
android:orientation="vertical"
android:visibility="gone">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="24dp"
android:layout_marginEnd="24dp"
android:fontFamily="@font/medium_font"
android:gravity="center"
android:text="@string/playback_error"
android:textColor="@color/white_80"
android:textSize="16dp" />
<TextView
android:id="@+id/tryAgainBtn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:background="@drawable/drw_btn_bg"
android:paddingStart="24dp"
android:paddingTop="8dp"
android:fontFamily="@font/medium_font"
android:paddingEnd="24dp"
android:paddingBottom="8dp"
android:text="@string/try_again"
android:textColor="@color/black"
android:textSize="16dp" />
</LinearLayout>
</RelativeLayout>
@ -346,4 +369,50 @@
android:orientation="horizontal" />
</FrameLayout>
<LinearLayout
android:id="@+id/bottom_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_alignParentBottom="true"
android:orientation="vertical"
android:visibility="gone">
<View
android:id="@+id/bottom_blank_layout"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_marginTop="68dp"
android:layout_weight="3" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="5"
android:background="@drawable/drw_bottom_layout_bg"
android:orientation="vertical"
android:visibility="visible">
<LinearLayout
android:id="@+id/bottomCloseBtn"
android:layout_width="match_parent"
android:layout_height="32dp"
android:gravity="center">
<TextView
android:layout_width="30dp"
android:layout_height="4dp"
android:background="@drawable/drw_bottom_close_btn_bg"
android:gravity="center" />
</LinearLayout>
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/playListRv"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_margin="16dp" />
</LinearLayout>
</LinearLayout>
</RelativeLayout>

View File

@ -78,6 +78,7 @@
</LinearLayout>
<androidx.core.widget.NestedScrollView
android:id="@+id/sv"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginTop="-2dp">

View File

@ -0,0 +1,90 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
tools:ignore="MissingDefaultResource">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:orientation="horizontal"
android:paddingBottom="12dp">
<RelativeLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="16dp">
<androidx.cardview.widget.CardView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:elevation="0dp"
android:visibility="visible"
app:cardCornerRadius="4dp"
app:cardElevation="0dp">
<RelativeLayout
android:layout_width="48dp"
android:layout_height="48dp">
<ImageView
android:id="@+id/image"
android:layout_width="48dp"
android:layout_height="48dp"
android:scaleType="centerCrop"
android:src="@mipmap/musicoo_logo_img" />
<RelativeLayout
android:id="@+id/currentPlayingLayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/black_60"
android:gravity="center"
android:visibility="gone">
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/playing_small_green_icon" />
</RelativeLayout>
</RelativeLayout>
</androidx.cardview.widget.CardView>
</RelativeLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:orientation="vertical">
<com.player.musicoo.view.MarqueeTextView
android:id="@+id/title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:fontFamily="@font/regular_font"
android:maxLines="1"
android:text="@string/app_name"
android:textColor="@color/white"
android:textSize="14dp" />
<com.player.musicoo.view.MarqueeTextView
android:id="@+id/name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:fontFamily="@font/regular_font"
android:maxLines="1"
android:text="@string/app_name"
android:textColor="@color/white_60"
android:textSize="12dp" />
</LinearLayout>
</LinearLayout>
</LinearLayout>

View File

@ -8,4 +8,6 @@
<color name="white_80">#CCFFFFFF</color>
<color name="main_bg_color">#151718</color>
<color name="green">#FF80F988</color>
<color name="green_60">#9980F988</color>
<color name="bottom_layout_bg_color">#1A1A1A</color>
</resources>