This commit is contained in:
ocean 2024-07-02 10:59:14 +08:00
parent c8124ad849
commit 207f2e32ae
3 changed files with 114 additions and 440 deletions

View File

@ -31,6 +31,7 @@ import melody.offline.music.adapter.LikedSongsAdapter
import melody.offline.music.ads.AdPlacement
import melody.offline.music.ads.LolAdWrapper
import melody.offline.music.bean.FavoriteBean
import melody.offline.music.bean.PlaylistItem
import melody.offline.music.databinding.ActivityLikedSongsBinding
import melody.offline.music.fragment.MoMeFragment
import melody.offline.music.service.MyDownloadService
@ -38,29 +39,31 @@ import melody.offline.music.service.ViewModelMain
import melody.offline.music.util.AnalysisUtil
import melody.offline.music.util.DownloadUtil
import melody.offline.music.util.LogTag
import melody.offline.music.util.asPlaylistItem
import melody.offline.music.view.ListMoreBottomSheetDialog
import org.json.JSONObject
@OptIn(UnstableApi::class)
class MoLikedSongsActivity : MoBaseActivity(), LikedSongsAdapter.OnItemFavoritesClickListener,
LikedSongsAdapter.OnMoreClickListener {
LikedSongsAdapter.OnMoreClickListener, ListMoreBottomSheetDialog.ListMoreViewListener,
ListMoreBottomSheetDialog.UpdateAdapterListener {
private val requests: Channel<Request> = Channel(Channel.UNLIMITED)
sealed class Request {
data object TryAgain : Request()
data class UpdateFavorite(val bean: FavoriteBean) : Request()
data class OnFavorites(val bean: FavoriteBean) : Request()
data class OnDownload(val favoriteBean: FavoriteBean) : Request()
data class OnDownloadRemove(val id: String) : Request()
data class OnUpdateDownloadUi(val id: String) : Request()
data class OnAddPlaylist(val bean: FavoriteBean) : Request()
data class OnFavorites(val bean: PlaylistItem) : Request()
data class OnDownload(val bean: PlaylistItem) : Request()
data class OnDownloadRemove(val bean: PlaylistItem) : Request()
data class OnUpdateDownloadUi(val bean: PlaylistItem) : Request()
data class OnAddPlaylist(val bean: PlaylistItem) : Request()
data class OnUpdateCurrentListItem(val bean: PlaylistItem): Request()
}
private lateinit var binding: ActivityLikedSongsBinding
private var adapter: LikedSongsAdapter? = null
private var favoriteBeans: MutableList<FavoriteBean> = mutableListOf()
private var currentPosition = -1
private var isAnimationRunning = false//动画是否正在进行
private var myList: MutableList<PlaylistItem> = mutableListOf()
private var moreDialog: ListMoreBottomSheetDialog? = null
override suspend fun main() {
binding = ActivityLikedSongsBinding.inflate(layoutInflater)
@ -70,7 +73,6 @@ class MoLikedSongsActivity : MoBaseActivity(), LikedSongsAdapter.OnItemFavorites
initView()
initAdapter()
LolAdWrapper.shared.loadAdIfNotCached(this, AdPlacement.INST_ME_PAGE_LIST)
initDownloadFlow()
initData()
onReceive()
}
@ -92,43 +94,38 @@ class MoLikedSongsActivity : MoBaseActivity(), LikedSongsAdapter.OnItemFavorites
initData()
}
is Request.UpdateFavorite -> {
val currentFavoriteBean =
App.appFavoriteDBManager.getFavoriteBeanByID(it.bean.videoId)
if (currentFavoriteBean != null) {
updateFavoriteUi(currentFavoriteBean.isFavorite)
} else {
updateFavoriteUi(false)
}
}
is Request.OnFavorites -> {
it.bean.isFavorite = !it.bean.isFavorite
adapter?.notifyDataSetChanged()
adapter?.notifyDataSetChanged()//更新list
updateFavoriteUi(it.bean.isFavorite)
updateFavoriteUi(it.bean.isFavorite)//更新对话框
val currentFavoriteBean =
App.appFavoriteDBManager.getFavoriteBeanByID(it.bean.videoId)
val currentFavoriteBean = App.appFavoriteDBManager.getFavoriteBeanByID(it.bean.videoId)
if (currentFavoriteBean != null) {
App.appFavoriteDBManager.updateFavoriteBean(it.bean)
currentFavoriteBean.isFavorite = it.bean.isFavorite
App.appFavoriteDBManager.updateFavoriteBean(currentFavoriteBean)//更新数据库
}
}
is Request.OnDownload -> {
val id = it.favoriteBean.videoId
val id = it.bean.videoId
LogTag.LogD(TAG, "OnDownload id->${id}")
val currentOfflineBean = App.appOfflineDBManager.getOfflineBeanByID(id)
if (currentOfflineBean != null && currentOfflineBean.bytesDownloaded?.let { bytes -> bytes > 0 } == true) {//判断当前数据库是否有这条数据。
if (currentPosition >= 0) {
val videoId = favoriteBeans[currentPosition].videoId
showRemoveDownloadDialogHint(videoId)
}
showRemoveDownloadDialogHint(it.bean)
} else {
//判断是否已经下载了这条数据,已经下载,就直接进行数据库数据存储,反之走下载流程。
if (DownloadUtil.downloadResourceExist(id)) {
insertOfflineData(it.favoriteBean)
requests.trySend(Request.OnUpdateDownloadUi(id))
insertOfflineData(FavoriteBean(
videoId = it.bean.videoId,
title = it.bean.title,
name = it.bean.name,
thumbnail = it.bean.thumbnail,
isFavorite = it.bean.isFavorite
))
it.bean.isOffline = true
requests.trySend(Request.OnUpdateDownloadUi(it.bean))
requests.trySend(Request.OnUpdateCurrentListItem(it.bean))
} else {
val downloadRequest = DownloadRequest.Builder(id, id.toUri())
.setCustomCacheKey(id).build()
@ -153,10 +150,16 @@ class MoLikedSongsActivity : MoBaseActivity(), LikedSongsAdapter.OnItemFavorites
this@MoLikedSongsActivity, AdPlacement.INST_DOWNLOAD
)
insertOfflineData(it.favoriteBean)
insertOfflineData(FavoriteBean(
videoId = it.bean.videoId,
title = it.bean.title,
name = it.bean.name,
thumbnail = it.bean.thumbnail,
isFavorite = it.bean.isFavorite
))
val jsonObject = JSONObject()
jsonObject.put(
"download_id", "${it.favoriteBean.videoId}"
"download_id", it.bean.videoId
)
val songMap = mutableMapOf(
Pair(
@ -176,36 +179,33 @@ class MoLikedSongsActivity : MoBaseActivity(), LikedSongsAdapter.OnItemFavorites
}
is Request.OnDownloadRemove -> {
LogTag.LogD(TAG, "OnDownloadRemove id->${it.id}")
val currentOfflineBean =
App.appOfflineDBManager.getOfflineBeanByID(it.id)
App.appOfflineDBManager.getOfflineBeanByID(it.bean.videoId)
if (currentOfflineBean != null) {
App.appOfflineDBManager.deleteOfflineBean(currentOfflineBean)
it.bean.isOffline = false
}
requests.trySend(Request.OnUpdateDownloadUi(it.id))
requests.trySend(Request.OnUpdateDownloadUi(it.bean))
requests.trySend(Request.OnUpdateCurrentListItem(it.bean))
}
is Request.OnUpdateDownloadUi -> {
val offBean = App.appOfflineDBManager.getOfflineBeanByID(it.id)
if (offBean != null && offBean.bytesDownloaded?.let { bytes -> bytes > 0 } == true) {
binding.downloadLoading.visibility = View.GONE
binding.downloadImg.visibility = View.VISIBLE
binding.downloadImg.setImageResource(R.drawable.more_downloaded_icon)
binding.downloadTv.text =
getString(R.string.download_remove_offline)
LogTag.LogD(TAG,"like OnUpdateDownloadUi 1111")
} else {
binding.downloadLoading.visibility = View.GONE
binding.downloadImg.visibility = View.VISIBLE
binding.downloadImg.setImageResource(R.drawable.more_download_icon)
binding.downloadTv.text = getString(R.string.download_save_offline)
LogTag.LogD(TAG,"like OnUpdateDownloadUi 2222")
}
moreDialog?.updateDownloadBtnUi(it.bean.isOffline)
}
is Request.OnAddPlaylist -> {
showAddPlaylistBottomDialog(it.bean)
showAddPlaylistBottomDialog(FavoriteBean(
videoId = it.bean.videoId,
title = it.bean.title,
name = it.bean.name,
thumbnail = it.bean.thumbnail,
isFavorite = it.bean.isFavorite
))
}
is Request.OnUpdateCurrentListItem -> {
//更新数据集合当前的item数据
updateCurrentItem(it.bean.videoId)
}
}
}
@ -246,38 +246,15 @@ class MoLikedSongsActivity : MoBaseActivity(), LikedSongsAdapter.OnItemFavorites
}
private fun initView() {
binding.layoutInfo.setOnClickListener { }
binding.backBtn.setOnClickListener {
finish()
}
binding.tryAgainBtn.setOnClickListener {
requests.trySend(Request.TryAgain)
}
binding.bottomCloseBtn.setOnClickListener {
hideBottomLayout()
}
binding.bottomBlankLayout.setOnClickListener {
hideBottomLayout()
}
binding.favoritesBtn.setOnClickListener {
if (currentPosition >= 0) {
requests.trySend(Request.OnFavorites(favoriteBeans[currentPosition]))
}
}
binding.moreDownloadBtn.setOnClickListener {
if (currentPosition >= 0) {
requests.trySend(Request.OnDownload(favoriteBeans[currentPosition]))
}
}
binding.moreAddPlaylistBtn.setOnClickListener {
if (currentPosition >= 0) {
requests.trySend(Request.OnAddPlaylist(favoriteBeans[currentPosition]))
hideBottomLayout()
}
}
}
private fun showRemoveDownloadDialogHint(id: String) {
private fun showRemoveDownloadDialogHint(bean: PlaylistItem) {
val inflater = LayoutInflater.from(this)
val dialogView = inflater.inflate(R.layout.dialog_hint, null)
val okBtn = dialogView.findViewById<TextView>(R.id.dialog_ok_btn)
@ -288,7 +265,7 @@ class MoLikedSongsActivity : MoBaseActivity(), LikedSongsAdapter.OnItemFavorites
dialog.show()
okBtn.setOnClickListener {
dialog.dismiss()
requests.trySend(Request.OnDownloadRemove(id))
requests.trySend(Request.OnDownloadRemove(bean))
}
cancelBtn.setOnClickListener {
dialog.dismiss()
@ -296,7 +273,7 @@ class MoLikedSongsActivity : MoBaseActivity(), LikedSongsAdapter.OnItemFavorites
}
private fun initAdapter() {
adapter = LikedSongsAdapter(this, favoriteBeans)
adapter = LikedSongsAdapter(this, myList)
adapter?.setOnFavoritesItemClickListener(this)
adapter?.setOnMoreClickListener(this)
binding.rv.layoutManager = LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false)
@ -304,182 +281,82 @@ class MoLikedSongsActivity : MoBaseActivity(), LikedSongsAdapter.OnItemFavorites
}
override fun onFavoritesItemClick(position: Int) {
requests.trySend(Request.OnFavorites(favoriteBeans[position]))
requests.trySend(Request.OnFavorites(myList[position]))
}
override fun onMoreClick(position: Int) {
currentPosition = position
if (!isAnimationRunning) {
toggleBottomLayout(position)
}
}
private fun initDownloadFlow() {
ViewModelMain.modelDownloadsFlow.observe(this) { downloads ->
if (currentPosition >= 0) {
val id = favoriteBeans[currentPosition].videoId
val currentScreenDownloads = downloads[id]
if (currentScreenDownloads != null) {
updateDownloadUI(currentScreenDownloads)
}
}
}
}
private fun updateDownloadUI(download: Download) {
LogTag.LogD(TAG,"like updateDownloadUI download-${download.state}")
when (download.state) {
Download.STATE_DOWNLOADING -> {
binding.downloadLoading.visibility = View.VISIBLE
binding.downloadImg.setImageResource(R.drawable.more_download_icon)
binding.downloadImg.visibility = View.GONE
binding.downloadTv.text = getString(R.string.download_saving)
binding.moreDownloadBtn.isClickable = false
binding.moreDownloadBtn.isEnabled = false
}
Download.STATE_COMPLETED -> {
binding.downloadLoading.visibility = View.GONE
binding.downloadImg.setImageResource(R.drawable.more_downloaded_icon)
binding.downloadImg.visibility = View.VISIBLE
binding.downloadTv.text = getString(R.string.download_remove_offline)
binding.moreDownloadBtn.isClickable = true
binding.moreDownloadBtn.isEnabled = true
val jsonObject = JSONObject()
jsonObject.put(
"download_id", download.request.id
)
val songMap = mutableMapOf(
Pair(
AnalysisUtil.PARAM_VALUE, jsonObject.toString()
)
)
AnalysisUtil.logEvent(AnalysisUtil.PLAYER_B_DOWNLOAD_SUCCESS_ACTION, songMap)
}
Download.STATE_FAILED -> {
binding.downloadLoading.visibility = View.GONE
binding.downloadImg.setImageResource(R.drawable.error)
binding.downloadImg.visibility = View.VISIBLE
binding.downloadTv.text = getString(R.string.download_save_offline)
binding.moreDownloadBtn.isClickable = true
binding.moreDownloadBtn.isEnabled = true
}
else -> {
binding.downloadLoading.visibility = View.GONE
binding.downloadImg.setImageResource(R.drawable.more_download_icon)
binding.downloadImg.visibility = View.VISIBLE
binding.downloadTv.text = getString(R.string.download_save_offline)
binding.moreDownloadBtn.isClickable = true
binding.moreDownloadBtn.isEnabled = true
}
}
moreDialog = ListMoreBottomSheetDialog(
this@MoLikedSongsActivity,
myList[position],
this@MoLikedSongsActivity,
this@MoLikedSongsActivity
)
moreDialog?.setListMoreViewListener(this@MoLikedSongsActivity)
moreDialog?.show()
}
private fun updateFavoriteUi(b: Boolean) {
if (b) {
binding.favoritesImg.setImageResource(R.drawable.favorited_icon)
} else {
binding.favoritesImg.setImageResource(R.drawable.not_favorited_icon)
if (moreDialog != null) {
moreDialog?.updateFavoriteUi(b)
}
}
private fun toggleBottomLayout(position: Int) {
if (binding.bottomLayout.visibility == View.VISIBLE) {
hideBottomLayout()
} else {
showBottomLayout(position)
private suspend fun updateCurrentItem(id: String) {
//更新当前界面的数据集合
myList.forEach { item ->
if (item.videoId == id) {
val favoriteBean = App.appFavoriteDBManager.getFavoriteBeanByID(item.videoId)
val offlineBean = App.appOfflineDBManager.getOfflineBeanByID(item.videoId)
item.isFavorite = favoriteBean?.isFavorite ?: false
item.isOffline = offlineBean?.isOffline ?: false
item.bytesDownloaded = offlineBean?.bytesDownloaded
item.size = offlineBean?.size
}
}
myList.forEach { item ->
if (item.videoId == id) {
LogTag.LogD(TAG, "updateCurrentItem -> $item")
}
}
}
private fun showBottomLayout(position: Int) {
val slideUpAnimation = AnimationUtils.loadAnimation(this, R.anim.slide_up)
slideUpAnimation.setAnimationListener(object : Animation.AnimationListener {
override fun onAnimationStart(animation: Animation) {
isAnimationRunning = true
// 动画开始时禁用按钮
animationLoadingNotClick(false)
}
override fun onAnimationEnd(animation: Animation) {
isAnimationRunning = false
// 动画结束时启用按钮
animationLoadingNotClick(true)
}
override fun onAnimationRepeat(animation: Animation) {
// 可选:处理动画重复的情况
}
})
binding.bottomLayout.startAnimation(slideUpAnimation)
binding.bottomLayout.visibility = View.VISIBLE
val bean = favoriteBeans[position]
Glide.with(this).load(bean.thumbnail).into(binding.songImg)
binding.title.text = bean.title
if (bean.name.isEmpty()) {
binding.name.visibility = View.GONE
} else {
binding.name.visibility = View.VISIBLE
binding.name.text = bean.name
}
requests.trySend(Request.UpdateFavorite(bean))
val currentDownload = DownloadUtil.getCurrentIdDownload(bean.videoId)
if (currentDownload != null) {
//是否下载过资源下载过资源就不走download的ui更新因为 它肯定状态为3
if(DownloadUtil.downloadResourceExist(bean.videoId)){
requests.trySend(Request.OnUpdateDownloadUi(bean.videoId))
}else{
updateDownloadUI(currentDownload)
}
} else {
requests.trySend(Request.OnUpdateDownloadUi(bean.videoId))
}
override fun onFavoritesClicked(playlistItem: PlaylistItem) {
requests.trySend(Request.OnFavorites(playlistItem))
}
private fun hideBottomLayout() {
val slideDownAnimation = AnimationUtils.loadAnimation(this, R.anim.slide_down)
slideDownAnimation.setAnimationListener(object : Animation.AnimationListener {
override fun onAnimationStart(animation: Animation?) {
isAnimationRunning = true
animationLoadingNotClick(false)
}
override fun onDownloadClicked(playlistItem: PlaylistItem) {
requests.trySend(Request.OnDownload(playlistItem))
}
override fun onAnimationEnd(animation: Animation?) {
isAnimationRunning = false
animationLoadingNotClick(true)
}
override fun onAddToPlaylistClicked(playlistItem: PlaylistItem) {
requests.trySend(Request.OnAddPlaylist(playlistItem))
}
override fun onAnimationRepeat(animation: Animation?) {
}
})
binding.bottomLayout.startAnimation(slideDownAnimation)
binding.bottomLayout.visibility = View.GONE
//隐藏布局后则,解除点击限制
binding.moreDownloadBtn.isClickable = true
binding.moreDownloadBtn.isEnabled = true
override fun onUpdateAdapterListener(download: Download, playlistItem: PlaylistItem) {
requests.trySend(Request.OnUpdateCurrentListItem(playlistItem))
}
@SuppressLint("NotifyDataSetChanged")
private suspend fun initData() {
showLoadingUi()
favoriteBeans.clear()
val beans = App.appFavoriteDBManager.getAllFavoriteBeans()
val filteredBeans = beans.filter { it.isFavorite }
favoriteBeans.addAll(filteredBeans)
if (favoriteBeans.size > 0) {
if (filteredBeans.isNotEmpty()) {
myList.clear()
filteredBeans.forEach { favoriteItem->
val offlineBean = App.appOfflineDBManager.getOfflineBeanByID(favoriteItem.videoId)
myList.add(PlaylistItem(
videoId = favoriteItem.videoId,
title = favoriteItem.title,
name = favoriteItem.name,
thumbnail = favoriteItem.thumbnail,
isOffline = offlineBean?.isOffline ?: false,
isFavorite = favoriteItem.isFavorite,
bytesDownloaded = offlineBean?.bytesDownloaded,
size = offlineBean?.size
))
}
showDataUi()
} else {
showNoContentUi()
@ -489,16 +366,6 @@ class MoLikedSongsActivity : MoBaseActivity(), LikedSongsAdapter.OnItemFavorites
}
}
private fun animationLoadingNotClick(b: Boolean) {
binding.layoutInfo.isClickable = b
binding.bottomCloseBtn.isClickable = b
binding.bottomBlankLayout.isClickable = b
binding.moreDownloadBtn.isClickable = b
binding.moreAddPlaylistBtn.isClickable = b
binding.favoritesBtn.isClickable = b
}
private fun showDataUi() {
binding.loadingLayout.visibility = View.GONE
binding.noContentLayout.visibility = View.GONE

View File

@ -12,12 +12,13 @@ import melody.offline.music.R
import melody.offline.music.activity.MoLikedSongsActivity
import melody.offline.music.activity.MoPlayDetailsActivity
import melody.offline.music.bean.FavoriteBean
import melody.offline.music.bean.PlaylistItem
import melody.offline.music.databinding.LikedListItemBinding
import melody.offline.music.media.MediaControllerManager
class LikedSongsAdapter(
private val context: Context,
private val list: List<FavoriteBean>,
private val list: List<PlaylistItem>,
) :
RecyclerView.Adapter<LikedSongsAdapter.ViewHolder>() {
@ -59,7 +60,7 @@ class LikedSongsAdapter(
RecyclerView.ViewHolder(binding.root) {
@SuppressLint("SetTextI18n")
fun bind(bean: FavoriteBean) {
fun bind(bean: PlaylistItem) {
binding.apply {
Glide.with(context)

View File

@ -154,198 +154,4 @@
</LinearLayout>
<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:id="@+id/layout_info"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@drawable/drw_bottom_layout_bg"
android:orientation="vertical"
android:paddingBottom="32dp"
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>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:paddingStart="18dp"
android:paddingEnd="18dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
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:cardBackgroundColor="@color/transparent"
app:cardCornerRadius="10dp"
app:cardElevation="0dp">
<ImageView
android:id="@+id/songImg"
android:layout_width="50dp"
android:layout_height="50dp"
android:src="@mipmap/app_logo" />
</androidx.cardview.widget.CardView>
<LinearLayout
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_marginStart="12dp"
android:layout_marginEnd="12dp"
android:layout_weight="1"
android:gravity="center_vertical"
android:orientation="vertical">
<melody.offline.music.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" />
<melody.offline.music.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
android:id="@+id/favoritesBtn"
android:layout_width="32dp"
android:layout_height="32dp"
android:background="@drawable/drw_round_48_bg"
android:gravity="center">
<ImageView
android:id="@+id/favoritesImg"
android:layout_width="16dp"
android:layout_height="16dp"
android:src="@drawable/not_favorited_icon" />
</LinearLayout>
</LinearLayout>
<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:layout_marginTop="16dp"
android:background="@color/liner_color" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<LinearLayout
android:id="@+id/moreDownloadBtn"
android:layout_width="match_parent"
android:layout_height="48dp"
android:layout_marginTop="4dp"
android:gravity="center_vertical"
android:orientation="horizontal">
<RelativeLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<ImageView
android:id="@+id/downloadImg"
android:layout_width="24dp"
android:layout_height="24dp"
android:src="@drawable/more_download_icon" />
<ProgressBar
android:id="@+id/downloadLoading"
android:layout_width="24dp"
android:layout_height="24dp"
android:indeterminateTint="@color/green"
android:progressBackgroundTint="@color/green"
android:progressTint="@color/green"
android:visibility="gone" />
</RelativeLayout>
<TextView
android:id="@+id/downloadTv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="12dp"
android:text="@string/download_save_offline"
android:textColor="@color/white"
android:textSize="14dp" />
</LinearLayout>
<LinearLayout
android:id="@+id/moreAddPlaylistBtn"
android:layout_width="match_parent"
android:layout_height="48dp"
android:layout_marginTop="4dp"
android:gravity="center_vertical"
android:orientation="horizontal">
<ImageView
android:layout_width="24dp"
android:layout_height="24dp"
android:src="@drawable/more_add_playlist_icon" />
<TextView
android:id="@+id/addPlaylistTv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="12dp"
android:text="@string/add_to_playlist"
android:textColor="@color/white"
android:textSize="14dp" />
</LinearLayout>
</LinearLayout>
</LinearLayout>
</LinearLayout>
</LinearLayout>
</RelativeLayout>