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

View File

@ -2,24 +2,64 @@ package melody.offline.music.activity
import android.annotation.SuppressLint import android.annotation.SuppressLint
import android.content.Intent import android.content.Intent
import android.graphics.Color
import android.graphics.drawable.ColorDrawable
import android.view.LayoutInflater
import android.view.View 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 androidx.recyclerview.widget.LinearLayoutManager
import com.bumptech.glide.Glide import com.bumptech.glide.Glide
import com.gyf.immersionbar.ktx.immersionBar import com.gyf.immersionbar.ktx.immersionBar
import kotlinx.coroutines.channels.Channel import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.isActive import kotlinx.coroutines.isActive
import kotlinx.coroutines.selects.select 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.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.databinding.ActivityDetailsBinding
import melody.offline.music.innertube.Innertube import melody.offline.music.innertube.Innertube
import melody.offline.music.innertube.requests.moPlaylistPage 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 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) private val requests: Channel<Request> = Channel(Channel.UNLIMITED)
enum class Request { sealed class Request {
TryAgain, 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 { companion object {
@ -32,7 +72,8 @@ class MoListDetailsActivity : MoBaseActivity() {
private var adapter: DetailsListAdapter? = null private var adapter: DetailsListAdapter? = null
private var myList: MutableList<Innertube.MoPlaylistOrAlbumPage.MoPlaylistOrAlbumListBean> = private var myList: MutableList<Innertube.MoPlaylistOrAlbumPage.MoPlaylistOrAlbumListBean> =
mutableListOf() mutableListOf()
private var myBean: Innertube.MoPlaylistOrAlbumPage? = null
private var currentPosition = -1
override suspend fun main() { override suspend fun main() {
binding = ActivityDetailsBinding.inflate(layoutInflater) binding = ActivityDetailsBinding.inflate(layoutInflater)
setContentView(binding.root) setContentView(binding.root)
@ -44,6 +85,7 @@ class MoListDetailsActivity : MoBaseActivity() {
} }
initView() initView()
LogD(TAG, "browseId->${browseId}") LogD(TAG, "browseId->${browseId}")
initDownloadFlow()
initData(browseId!!) initData(browseId!!)
onReceive() onReceive()
} }
@ -64,6 +106,163 @@ class MoListDetailsActivity : MoBaseActivity() {
Request.TryAgain -> { Request.TryAgain -> {
initData(browseId!!) 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 { 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() { private fun initView() {
adapter = DetailsListAdapter(this, myList) adapter = DetailsListAdapter(this, myList)
adapter?.setOnItemDownloadClickListener(this)
adapter?.setOnItemMoreClickListener(this)
binding.rv.layoutManager = LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false) binding.rv.layoutManager = LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false)
binding.rv.adapter = adapter binding.rv.adapter = adapter
@ -122,6 +333,30 @@ class MoListDetailsActivity : MoBaseActivity() {
startActivity(intent) 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") @SuppressLint("NotifyDataSetChanged")
@ -131,12 +366,13 @@ class MoListDetailsActivity : MoBaseActivity() {
if (this.isDestroyed || this.isFinishing) { if (this.isDestroyed || this.isFinishing) {
return return
} }
myBean = it
LogD(TAG, "moPlaylistPage onSuccess->${it.moPlaylistOrAlbumListBean}") LogD(TAG, "moPlaylistPage onSuccess->${it.moPlaylistOrAlbumListBean}")
if (it.moPlaylistOrAlbumListBean.isNotEmpty()) { if (it.moPlaylistOrAlbumListBean.isNotEmpty()) {
showDataUi() showDataUi()
Glide.with(this).load(it.thumbnail).into(binding.imageView) 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.subtitle.text = it.subtitle
binding.secondSubtitle.text = it.secondSubtitle binding.secondSubtitle.text = it.secondSubtitle
@ -166,4 +402,141 @@ class MoListDetailsActivity : MoBaseActivity() {
binding.loadingLayout.visibility = View.GONE binding.loadingLayout.visibility = View.GONE
binding.noContentLayout.visibility = View.VISIBLE 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,6 +103,7 @@ class MoPlaylistSongsActivity : MoBaseActivity() {
adapter?.notifyDataSetChanged() adapter?.notifyDataSetChanged()
} }
try {
LolAdWrapper.shared.loadAdShowNativeAd( LolAdWrapper.shared.loadAdShowNativeAd(
this, this,
AdPlacement.NATIVE_ME_PAGE_LIST, AdPlacement.NATIVE_ME_PAGE_LIST,
@ -110,6 +111,10 @@ class MoPlaylistSongsActivity : MoBaseActivity() {
R.layout.ad_layout_admob_banner, R.layout.ad_layout_admob_banner,
R.layout.ad_layout_max_banner R.layout.ad_layout_max_banner
) )
} catch (e: Exception) {
e.printStackTrace()
}
} }
private fun initView() { private fun initView() {

View File

@ -45,11 +45,23 @@ class DetailsListAdapter(
intent.putExtra(MoPlayDetailsActivity.PLAY_DETAILS_DESC, bean.name) intent.putExtra(MoPlayDetailsActivity.PLAY_DETAILS_DESC, bean.name)
context.startActivity(intent) 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 override fun getItemCount(): Int = list.size
inner class ViewHolder(private val binding: DetailsListItemBinding) : inner class ViewHolder(val binding: DetailsListItemBinding) :
RecyclerView.ViewHolder(binding.root) { RecyclerView.ViewHolder(binding.root) {
@SuppressLint("SetTextI18n") @SuppressLint("SetTextI18n")
@ -110,4 +122,24 @@ class DetailsListAdapter(
interface OnItemClickListener { interface OnItemClickListener {
fun onItemClick(position: Int) 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( data class PlaylistItem(
@PrimaryKey(autoGenerate = true) val id: Int = 0, @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 = "videoId") var videoId: String,
@ColumnInfo(name = "title") var title: String, @ColumnInfo(name = "title") var title: String,
@ColumnInfo(name = "name") var name: String, @ColumnInfo(name = "name") var name: String,

View File

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

View File

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

View File

@ -102,7 +102,7 @@ suspend fun Innertube.moPlaylistPage(browseId: String): Result<Innertube.MoPlayl
val bean = Innertube.MoPlaylistOrAlbumPage.MoPlaylistOrAlbumListBean( val bean = Innertube.MoPlaylistOrAlbumPage.MoPlaylistOrAlbumListBean(
title = runs0?.text, title = runs0?.text,
name = runs1?.text ?: musicResponsiveHeaderRenderer name = runs1?.text ?: musicResponsiveHeaderRenderer
?.subtitle ?.straplineTextOne
?.runs ?.runs
?.map { it.text } ?.map { it.text }
?.joinToString(""), ?.joinToString(""),
@ -121,7 +121,15 @@ suspend fun Innertube.moPlaylistPage(browseId: String): Result<Innertube.MoPlayl
params = watchEndpoint?.params, params = watchEndpoint?.params,
musicVideoType = watchEndpoint?.type, musicVideoType = watchEndpoint?.type,
pageType = browseEndpoint?.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) list.add(bean)
@ -157,6 +165,11 @@ suspend fun Innertube.moPlaylistPage(browseId: String): Result<Innertube.MoPlayl
?.musicDescriptionShelfRenderer ?.musicDescriptionShelfRenderer
?.description ?.description
?.text, ?.text,
singerName = musicResponsiveHeaderRenderer
?.straplineTextOne
?.runs
?.map { it.text }
?.joinToString { "" },
moPlaylistOrAlbumListBean = list moPlaylistOrAlbumListBean = list
) )
} }

View File

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

View File

@ -94,14 +94,27 @@
<RelativeLayout <RelativeLayout
android:id="@+id/downloadBtn" android:id="@+id/downloadBtn"
android:layout_width="40dp" android:layout_width="40dp"
android:visibility="gone" android:visibility="visible"
android:layout_height="40dp"> android:layout_height="40dp">
<ImageView <ImageView
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_centerInParent="true" android:layout_centerInParent="true"
android: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> </RelativeLayout>
</LinearLayout> </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="download">Download</string>
<string name="downloads">Downloads</string> <string name="downloads">Downloads</string>
<string name="library">Library</string> <string name="library">Library</string>
<string name="like">Like</string>
<string name="liked_songs">Liked songs</string> <string name="liked_songs">Liked songs</string>
<string name="offline_songs">Offline songs</string> <string name="offline_songs">Offline songs</string>
<string name="songs">Songs</string> <string name="songs">Songs</string>