This commit is contained in:
ocean 2024-06-26 18:14:25 +08:00
parent ca30bb789a
commit 1156dc4c34
12 changed files with 864 additions and 30 deletions

View File

@ -12,7 +12,9 @@ import android.renderscript.ScriptIntrinsicBlur
import android.view.LayoutInflater
import android.view.View
import android.widget.EditText
import android.widget.ImageView
import android.widget.LinearLayout
import android.widget.ProgressBar
import android.widget.RelativeLayout
import android.widget.TextView
import android.widget.Toast
@ -20,39 +22,53 @@ import androidx.annotation.OptIn
import androidx.appcompat.app.AlertDialog
import androidx.appcompat.app.AppCompatActivity
import androidx.core.content.ContextCompat
import androidx.core.net.toUri
import androidx.lifecycle.LifecycleOwner
import androidx.media3.common.MediaItem
import androidx.media3.common.Player
import androidx.media3.common.util.UnstableApi
import androidx.media3.exoplayer.offline.Download
import androidx.media3.exoplayer.offline.DownloadRequest
import androidx.media3.exoplayer.offline.DownloadService
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.bumptech.glide.Glide
import com.google.android.material.bottomsheet.BottomSheetDialog
import com.ironsource.be
import com.ironsource.vi
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.MainScope
import kotlinx.coroutines.NonCancellable
import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.isActive
import kotlinx.coroutines.launch
import kotlinx.coroutines.selects.select
import kotlinx.coroutines.withContext
import melody.offline.music.App
import melody.offline.music.R
import melody.offline.music.adapter.NewPlayListAdapter
import melody.offline.music.ads.AdPlacement
import melody.offline.music.ads.LolAdWrapper
import melody.offline.music.bean.FavoriteBean
import melody.offline.music.bean.OfflineBean
import melody.offline.music.bean.Playlist
import melody.offline.music.bean.PlaylistItem
import melody.offline.music.fragment.MoMeFragment
import melody.offline.music.http.getAppVersionCode
import melody.offline.music.http.getCountryCode
import melody.offline.music.innertube.Innertube
import melody.offline.music.media.MediaControllerManager
import melody.offline.music.service.MyDownloadService
import melody.offline.music.service.ViewModelMain
import melody.offline.music.sp.AppStore
import melody.offline.music.util.AnalysisUtil
import melody.offline.music.util.DownloadUtil
import melody.offline.music.util.FileSizeConverter
import melody.offline.music.util.LogTag
import melody.offline.music.view.MusicPlayerView
import org.json.JSONObject
@OptIn(UnstableApi::class)
abstract class MoBaseActivity : AppCompatActivity(), CoroutineScope by MainScope(), LifecycleOwner {
private var playerListener: Player.Listener? = null
@ -69,7 +85,6 @@ abstract class MoBaseActivity : AppCompatActivity(), CoroutineScope by MainScope
private var defer: suspend () -> Unit = {}
private var deferRunning = false
private lateinit var musicPlayerView: MusicPlayerView
private lateinit var bottomAddPlaylistSheetDialog: BottomSheetDialog
fun defer(operation: suspend () -> Unit) {
this.defer = operation
@ -365,7 +380,7 @@ abstract class MoBaseActivity : AppCompatActivity(), CoroutineScope by MainScope
}
suspend fun showAddPlaylistBottomDialog(favoriteBean: FavoriteBean) {
bottomAddPlaylistSheetDialog = BottomSheetDialog(this)
val bottomAddPlaylistSheetDialog = BottomSheetDialog(this)
val view = layoutInflater.inflate(R.layout.add_playlist_layout, null)
bottomAddPlaylistSheetDialog.setContentView(view)
val newPlayListBtn = view.findViewById<LinearLayout>(R.id.newPlayListBtn)
@ -397,8 +412,10 @@ abstract class MoBaseActivity : AppCompatActivity(), CoroutineScope by MainScope
).show()
}
} else {
val isOffline = App.appOfflineDBManager.getOfflineBeanByID(favoriteBean.videoId) != null
val isFavorite = App.appFavoriteDBManager.getFavoriteBeanByID(favoriteBean.videoId) != null
val isOffline =
App.appOfflineDBManager.getOfflineBeanByID(favoriteBean.videoId) != null
val isFavorite =
App.appFavoriteDBManager.getFavoriteBeanByID(favoriteBean.videoId) != null
App.appPlaylistDBManager.insertOrUpdatePlaylistItem(
PlaylistItem(
playlistId = playlist[position].id,
@ -461,8 +478,10 @@ abstract class MoBaseActivity : AppCompatActivity(), CoroutineScope by MainScope
}
val currentPlaylist = App.appPlaylistDBManager.getPlaylistByTitle(text)
if (currentPlaylist != null) {
val isOffline = App.appOfflineDBManager.getOfflineBeanByID(favoriteBean.videoId) != null//返回非null则为true
val isFavorite = App.appFavoriteDBManager.getFavoriteBeanByID(favoriteBean.videoId) != null
val isOffline =
App.appOfflineDBManager.getOfflineBeanByID(favoriteBean.videoId) != null//返回非null则为true
val isFavorite =
App.appFavoriteDBManager.getFavoriteBeanByID(favoriteBean.videoId) != null
val playlistItem = PlaylistItem(
playlistId = currentPlaylist.id,
videoId = favoriteBean.videoId,
@ -484,5 +503,4 @@ abstract class MoBaseActivity : AppCompatActivity(), CoroutineScope by MainScope
ContextCompat.getColor(this, R.color.main_bg_color)
bottomSheetDialog?.show()
}
}

View File

@ -2,24 +2,64 @@ package melody.offline.music.activity
import android.annotation.SuppressLint
import android.content.Intent
import android.graphics.Color
import android.graphics.drawable.ColorDrawable
import android.view.LayoutInflater
import android.view.View
import android.view.animation.AnimationUtils
import android.widget.TextView
import android.widget.Toast
import androidx.annotation.OptIn
import androidx.appcompat.app.AlertDialog
import androidx.core.net.toUri
import androidx.media3.common.util.UnstableApi
import androidx.media3.exoplayer.offline.Download
import androidx.media3.exoplayer.offline.DownloadRequest
import androidx.media3.exoplayer.offline.DownloadService
import androidx.recyclerview.widget.LinearLayoutManager
import com.bumptech.glide.Glide
import com.gyf.immersionbar.ktx.immersionBar
import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.isActive
import kotlinx.coroutines.selects.select
import melody.offline.music.App
import melody.offline.music.R
import melody.offline.music.adapter.DetailsListAdapter
import melody.offline.music.ads.AdPlacement
import melody.offline.music.ads.LolAdWrapper
import melody.offline.music.bean.FavoriteBean
import melody.offline.music.bean.OfflineBean
import melody.offline.music.databinding.ActivityDetailsBinding
import melody.offline.music.innertube.Innertube
import melody.offline.music.innertube.requests.moPlaylistPage
import melody.offline.music.service.MyDownloadService
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.LogTag.LogD
import org.json.JSONObject
class MoListDetailsActivity : MoBaseActivity() {
@OptIn(UnstableApi::class)
class MoListDetailsActivity : MoBaseActivity(), DetailsListAdapter.OnItemMoreClickListener,
DetailsListAdapter.OnItemDownloadClickListener {
private val requests: Channel<Request> = Channel(Channel.UNLIMITED)
enum class Request {
TryAgain,
sealed class Request {
data object TryAgain : Request()
data class UpdateFavorite(val bean: Innertube.MoPlaylistOrAlbumPage.MoPlaylistOrAlbumListBean) :
Request()
data class OnFavorites(val bean: Innertube.MoPlaylistOrAlbumPage.MoPlaylistOrAlbumListBean) :
Request()
data class OnDownload(val bean: Innertube.MoPlaylistOrAlbumPage.MoPlaylistOrAlbumListBean) :
Request()
data class OnDownloadRemove(val id: String) : Request()
data class OnUpdateDownloadUi(val id: String) : Request()
data class OnAddPlaylist(val bean: Innertube.MoPlaylistOrAlbumPage.MoPlaylistOrAlbumListBean) :
Request()
}
companion object {
@ -32,7 +72,8 @@ class MoListDetailsActivity : MoBaseActivity() {
private var adapter: DetailsListAdapter? = null
private var myList: MutableList<Innertube.MoPlaylistOrAlbumPage.MoPlaylistOrAlbumListBean> =
mutableListOf()
private var myBean: Innertube.MoPlaylistOrAlbumPage? = null
private var currentPosition = -1
override suspend fun main() {
binding = ActivityDetailsBinding.inflate(layoutInflater)
setContentView(binding.root)
@ -44,6 +85,7 @@ class MoListDetailsActivity : MoBaseActivity() {
}
initView()
LogD(TAG, "browseId->${browseId}")
initDownloadFlow()
initData(browseId!!)
onReceive()
}
@ -64,6 +106,163 @@ class MoListDetailsActivity : MoBaseActivity() {
Request.TryAgain -> {
initData(browseId!!)
}
is Request.UpdateFavorite -> {
val currentFavoriteBean =
App.appFavoriteDBManager.getFavoriteBeanByID(it.bean.videoId!!)
if (currentFavoriteBean != null) {
updateFavoriteUi(currentFavoriteBean.isFavorite)
} else {
updateFavoriteUi(false)
}
}
is Request.OnFavorites -> {
val jsonObject = JSONObject()
jsonObject.put(
"song_title", it.bean.title
)
val songMap = mutableMapOf(
Pair(
AnalysisUtil.PARAM_VALUE, jsonObject.toString()
)
)
val currentFavoriteBean =
App.appFavoriteDBManager.getFavoriteBeanByID(it.bean.videoId!!)
if (currentFavoriteBean != null) {
currentFavoriteBean.isFavorite = !currentFavoriteBean.isFavorite
App.appFavoriteDBManager.updateFavoriteBean(currentFavoriteBean)
if (currentFavoriteBean.isFavorite) {
AnalysisUtil.logEvent(
AnalysisUtil.PLAYER_B_LOVE_CLICK, songMap
)
} else {
AnalysisUtil.logEvent(
AnalysisUtil.PLAYER_B_UN_LOVE_CLICK, songMap
)
}
} else {
val b = FavoriteBean(
videoId = it.bean.videoId,
title = it.bean.title.toString(),
name = it.bean.name.toString(),
thumbnail = it.bean.thumbnailUrl,
isFavorite = true
)
App.appFavoriteDBManager.insertFavoriteBean(b)
AnalysisUtil.logEvent(AnalysisUtil.PLAYER_B_LOVE_CLICK, songMap)
}
requests.trySend(Request.UpdateFavorite(it.bean))
}
is Request.OnDownload -> {
val id = it.bean.videoId
val currentOfflineBean =
App.appOfflineDBManager.getOfflineBeanByID(id!!)
if (currentOfflineBean != null) {//判断当前数据库是否有这条数据。
if (currentPosition >= 0) {
showRemoveDownloadDialogHint(id)
}
} else {
val isFavorite =
App.appFavoriteDBManager.getFavoriteBeanByID(it.bean.videoId.toString()) != null
//判断是否已经下载了这条数据,已经下载,就直接进行数据库数据存储,反之走下载流程。
if (DownloadUtil.downloadResourceExist(id)) {
val favoriteBean = FavoriteBean(
id,
it.bean.title.toString(),
it.bean.name.toString(),
it.bean.thumbnailUrl,
isFavorite
)
insertOfflineData(favoriteBean)
requests.trySend(Request.OnUpdateDownloadUi(id))
} else {
val downloadRequest = DownloadRequest.Builder(id, id.toUri())
.setCustomCacheKey(id).build()
val downloadCount = DownloadUtil.getCurrentDownloads()
if (downloadCount >= 3) {
Toast.makeText(
this@MoListDetailsActivity,
getString(R.string.download_tips),
Toast.LENGTH_LONG
).show()
} else {
DownloadService.sendAddDownload(
this@MoListDetailsActivity,
MyDownloadService::class.java,
downloadRequest,
false
)
LolAdWrapper.shared.showAdTiming(
this@MoListDetailsActivity, AdPlacement.INST_DOWNLOAD
)
val favoriteBean = FavoriteBean(
id,
it.bean.title.toString(),
it.bean.name.toString(),
it.bean.thumbnailUrl,
isFavorite
)
insertOfflineData(favoriteBean)
val jsonObject = JSONObject()
jsonObject.put(
"download_id", "${favoriteBean.videoId}"
)
val songMap = mutableMapOf(
Pair(
AnalysisUtil.PARAM_VALUE, jsonObject.toString()
)
)
AnalysisUtil.logEvent(
AnalysisUtil.PLAYER_B_DOWNLOAD_CLICK, songMap
)
LolAdWrapper.shared.loadAdIfNotCached(
this@MoListDetailsActivity, AdPlacement.INST_DOWNLOAD
)
}
}
}
}
is Request.OnDownloadRemove -> {
val currentOfflineBean =
App.appOfflineDBManager.getOfflineBeanByID(it.id)
if (currentOfflineBean != null) {
App.appOfflineDBManager.deleteOfflineBean(currentOfflineBean)
}
requests.trySend(Request.OnUpdateDownloadUi(it.id))
}
is Request.OnUpdateDownloadUi -> {
if (App.appOfflineDBManager.getOfflineBeanByID(it.id) != null) {
binding.downloadImg.setImageResource(R.drawable.more_downloaded_icon)
binding.downloadTv.text =
getString(R.string.download_remove_offline)
} else {
binding.downloadImg.setImageResource(R.drawable.more_download_icon)
binding.downloadTv.text = getString(R.string.download_save_offline)
}
}
is Request.OnAddPlaylist -> {
val isFavorite =
App.appFavoriteDBManager.getFavoriteBeanByID(it.bean.videoId.toString()) != null
showAddPlaylistBottomDialog(
FavoriteBean(
videoId = it.bean.videoId.toString(),
title = it.bean.title.toString(),
name = it.bean.name.toString(),
thumbnail = it.bean.thumbnailUrl.toString(),
isFavorite
)
)
}
}
}
events.onReceive {
@ -94,8 +293,20 @@ class MoListDetailsActivity : MoBaseActivity() {
}
}
override fun onItemDownloadClick(position: Int) {
}
override fun onItemMoreClick(position: Int) {
currentPosition = position
toggleBottomLayout(position)
}
private fun initView() {
adapter = DetailsListAdapter(this, myList)
adapter?.setOnItemDownloadClickListener(this)
adapter?.setOnItemMoreClickListener(this)
binding.rv.layoutManager = LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false)
binding.rv.adapter = adapter
@ -122,6 +333,30 @@ class MoListDetailsActivity : MoBaseActivity() {
startActivity(intent)
}
}
binding.layoutInfo.setOnClickListener { }
binding.bottomCloseBtn.setOnClickListener {
hideBottomLayout()
}
binding.bottomBlankLayout.setOnClickListener {
hideBottomLayout()
}
binding.favoritesBtn.setOnClickListener {
if (currentPosition >= 0) {
requests.trySend(Request.OnFavorites(myList[currentPosition]))
}
}
binding.moreDownloadBtn.setOnClickListener {
if (currentPosition >= 0) {
requests.trySend(Request.OnDownload(myList[currentPosition]))
}
}
binding.moreAddPlaylistBtn.setOnClickListener {
if (currentPosition >= 0) {
requests.trySend(Request.OnAddPlaylist(myList[currentPosition]))
hideBottomLayout()
}
}
}
@SuppressLint("NotifyDataSetChanged")
@ -131,12 +366,13 @@ class MoListDetailsActivity : MoBaseActivity() {
if (this.isDestroyed || this.isFinishing) {
return
}
myBean = it
LogD(TAG, "moPlaylistPage onSuccess->${it.moPlaylistOrAlbumListBean}")
if (it.moPlaylistOrAlbumListBean.isNotEmpty()) {
showDataUi()
Glide.with(this).load(it.thumbnail).into(binding.imageView)
binding.title.text = it.title
binding.infoTitle.text = it.title
binding.subtitle.text = it.subtitle
binding.secondSubtitle.text = it.secondSubtitle
@ -166,4 +402,141 @@ class MoListDetailsActivity : MoBaseActivity() {
binding.loadingLayout.visibility = View.GONE
binding.noContentLayout.visibility = View.VISIBLE
}
private fun initDownloadFlow() {
ViewModelMain.modelDownloadsFlow.observe(this) { downloads ->
if (currentPosition >= 0) {
val id = myList[currentPosition].videoId
val currentScreenDownloads = downloads[id]
if (currentScreenDownloads != null) {
updateDownloadUI(currentScreenDownloads)
}
}
}
}
private fun updateDownloadUI(download: Download) {
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
}
}
}
private fun updateFavoriteUi(b: Boolean) {
if (b) {
binding.favoritesImg.setImageResource(R.drawable.favorited_icon)
} else {
binding.favoritesImg.setImageResource(R.drawable.not_favorited_icon)
}
}
private fun toggleBottomLayout(position: Int) {
if (binding.bottomLayout.visibility == View.VISIBLE) {
hideBottomLayout()
} else {
showBottomLayout(position)
}
}
private fun showBottomLayout(position: Int) {
val slideUpAnimation = AnimationUtils.loadAnimation(this, R.anim.slide_up)
binding.bottomLayout.startAnimation(slideUpAnimation)
binding.bottomLayout.visibility = View.VISIBLE
val bean = myList[position]
Glide.with(this).load(bean.thumbnailUrl).into(binding.songImg)
binding.title.text = bean.title
if (bean.name?.isEmpty() == true) {
binding.name.visibility = View.GONE
} else {
binding.name.visibility = View.VISIBLE
binding.name.text = bean.name
}
requests.trySend(Request.UpdateFavorite(bean))
requests.trySend(Request.OnUpdateDownloadUi(bean.videoId!!))
val currentDownload = DownloadUtil.getCurrentIdDownload(bean.videoId)
if (currentDownload != null) {
updateDownloadUI(currentDownload)
}
}
private fun hideBottomLayout() {
val slideDownAnimation = AnimationUtils.loadAnimation(this, R.anim.slide_down)
binding.bottomLayout.startAnimation(slideDownAnimation)
binding.bottomLayout.visibility = View.GONE
}
private fun showRemoveDownloadDialogHint(id: String) {
val inflater = LayoutInflater.from(this)
val dialogView = inflater.inflate(R.layout.dialog_hint, null)
val okBtn = dialogView.findViewById<TextView>(R.id.dialog_ok_btn)
val cancelBtn = dialogView.findViewById<TextView>(R.id.dialog_cancel_btn)
val dialogBuilder = AlertDialog.Builder(this).setView(dialogView)
val dialog = dialogBuilder.create()
dialog.window?.setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT))
dialog.show()
okBtn.setOnClickListener {
dialog.dismiss()
requests.trySend(Request.OnDownloadRemove(id))
}
cancelBtn.setOnClickListener {
dialog.dismiss()
}
}
}

View File

@ -103,13 +103,18 @@ class MoPlaylistSongsActivity : MoBaseActivity() {
adapter?.notifyDataSetChanged()
}
LolAdWrapper.shared.loadAdShowNativeAd(
this,
AdPlacement.NATIVE_ME_PAGE_LIST,
binding.frameAd,
R.layout.ad_layout_admob_banner,
R.layout.ad_layout_max_banner
)
try {
LolAdWrapper.shared.loadAdShowNativeAd(
this,
AdPlacement.NATIVE_ME_PAGE_LIST,
binding.frameAd,
R.layout.ad_layout_admob_banner,
R.layout.ad_layout_max_banner
)
} catch (e: Exception) {
e.printStackTrace()
}
}
private fun initView() {

View File

@ -45,11 +45,23 @@ class DetailsListAdapter(
intent.putExtra(MoPlayDetailsActivity.PLAY_DETAILS_DESC, bean.name)
context.startActivity(intent)
}
holder.binding.downloadBtn.setOnClickListener {
if (itemDownloadClickListener != null) {
itemDownloadClickListener?.onItemDownloadClick(position)
}
}
holder.binding.moreBtn.setOnClickListener {
if (itemMoreClickListener != null) {
itemMoreClickListener?.onItemMoreClick(position)
}
}
}
override fun getItemCount(): Int = list.size
inner class ViewHolder(private val binding: DetailsListItemBinding) :
inner class ViewHolder(val binding: DetailsListItemBinding) :
RecyclerView.ViewHolder(binding.root) {
@SuppressLint("SetTextI18n")
@ -110,4 +122,24 @@ class DetailsListAdapter(
interface OnItemClickListener {
fun onItemClick(position: Int)
}
private var itemDownloadClickListener: OnItemDownloadClickListener? = null
fun setOnItemDownloadClickListener(listener: OnItemDownloadClickListener) {
itemDownloadClickListener = listener
}
interface OnItemDownloadClickListener {
fun onItemDownloadClick(position: Int)
}
private var itemMoreClickListener: OnItemMoreClickListener? = null
fun setOnItemMoreClickListener(listener: OnItemMoreClickListener) {
itemMoreClickListener = listener
}
interface OnItemMoreClickListener {
fun onItemMoreClick(position: Int)
}
}

View File

@ -26,7 +26,7 @@ data class Playlist(
)
data class PlaylistItem(
@PrimaryKey(autoGenerate = true) val id: Int = 0,
@ColumnInfo(name = "playlist_id") val playlistId: Int,
@ColumnInfo(name = "playlist_id") val playlistId: Int? = null,
@ColumnInfo(name = "videoId") var videoId: String,
@ColumnInfo(name = "title") var title: String,
@ColumnInfo(name = "name") var name: String,

View File

@ -223,6 +223,7 @@ object Innertube {
val thumbnail: String?,
val secondSubtitle: String?,
val description: String?,
val singerName: String?,
val moPlaylistOrAlbumListBean: List<MoPlaylistOrAlbumListBean>
) {
data class MoPlaylistOrAlbumListBean(

View File

@ -30,7 +30,8 @@ data class SectionListRenderer(
val subtitle: Runs?,
val secondSubtitle: Runs?,
val thumbnail: ThumbnailRenderer?,
val description: Description?
val description: Description?,
val straplineTextOne: Runs?
) {
@Serializable
data class Description(

View File

@ -102,7 +102,7 @@ suspend fun Innertube.moPlaylistPage(browseId: String): Result<Innertube.MoPlayl
val bean = Innertube.MoPlaylistOrAlbumPage.MoPlaylistOrAlbumListBean(
title = runs0?.text,
name = runs1?.text ?: musicResponsiveHeaderRenderer
?.subtitle
?.straplineTextOne
?.runs
?.map { it.text }
?.joinToString(""),
@ -121,7 +121,15 @@ suspend fun Innertube.moPlaylistPage(browseId: String): Result<Innertube.MoPlayl
params = watchEndpoint?.params,
musicVideoType = watchEndpoint?.type,
pageType = browseEndpoint?.type,
thumbnailUrl = thumbnailUrl
thumbnailUrl = thumbnailUrl ?: musicResponsiveHeaderRenderer
?.thumbnail
?.musicThumbnailRenderer
?.thumbnail
?.thumbnails
?.let {
it.getOrNull(3) ?: it.getOrNull(2) ?: it.getOrNull(1) ?: it.getOrNull(0)
}
?.url
)
list.add(bean)
@ -157,6 +165,11 @@ suspend fun Innertube.moPlaylistPage(browseId: String): Result<Innertube.MoPlayl
?.musicDescriptionShelfRenderer
?.description
?.text,
singerName = musicResponsiveHeaderRenderer
?.straplineTextOne
?.runs
?.map { it.text }
?.joinToString { "" },
moPlaylistOrAlbumListBean = list
)
}

View File

@ -142,7 +142,7 @@
android:orientation="vertical">
<TextView
android:id="@+id/title"
android:id="@+id/info_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:fontFamily="@font/medium_font"
@ -220,9 +220,202 @@
android:layout_alignParentBottom="true"
android:orientation="vertical" />
</RelativeLayout>
</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"
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>

View File

@ -94,14 +94,27 @@
<RelativeLayout
android:id="@+id/downloadBtn"
android:layout_width="40dp"
android:visibility="gone"
android:visibility="visible"
android:layout_height="40dp">
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:src="@drawable/download_gray_icon" />
android:src="@drawable/download_icon" />
</RelativeLayout>
<RelativeLayout
android:id="@+id/moreBtn"
android:layout_width="40dp"
android:visibility="visible"
android:layout_height="40dp">
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:src="@drawable/three_dots_icon" />
</RelativeLayout>
</LinearLayout>

View File

@ -0,0 +1,184 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
xmlns:app="http://schemas.android.com/apk/res-auto">
<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"
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>

View File

@ -32,6 +32,7 @@
<string name="download">Download</string>
<string name="downloads">Downloads</string>
<string name="library">Library</string>
<string name="like">Like</string>
<string name="liked_songs">Liked songs</string>
<string name="offline_songs">Offline songs</string>
<string name="songs">Songs</string>