From 1156dc4c34ecceaedb0ee4714b3115a4c7c72039 Mon Sep 17 00:00:00 2001 From: ocean <503259349@qq.com> Date: Wed, 26 Jun 2024 18:14:25 +0800 Subject: [PATCH] update --- .../offline/music/activity/MoBaseActivity.kt | 36 +- .../music/activity/MoListDetailsActivity.kt | 383 +++++++++++++++++- .../music/activity/MoPlaylistSongsActivity.kt | 19 +- .../music/adapter/DetailsListAdapter.kt | 34 +- .../melody/offline/music/bean/playlist.kt | 2 +- .../offline/music/innertube/Innertube.kt | 1 + .../innertube/models/SectionListRenderer.kt | 3 +- .../innertube/requests/MoPlaylistPage.kt | 17 +- app/src/main/res/layout/activity_details.xml | 197 ++++++++- app/src/main/res/layout/details_list_item.xml | 17 +- app/src/main/res/layout/list_more_layout.xml | 184 +++++++++ app/src/main/res/values/strings.xml | 1 + 12 files changed, 864 insertions(+), 30 deletions(-) create mode 100644 app/src/main/res/layout/list_more_layout.xml diff --git a/app/src/main/java/melody/offline/music/activity/MoBaseActivity.kt b/app/src/main/java/melody/offline/music/activity/MoBaseActivity.kt index 98b25c1..cb15a57 100644 --- a/app/src/main/java/melody/offline/music/activity/MoBaseActivity.kt +++ b/app/src/main/java/melody/offline/music/activity/MoBaseActivity.kt @@ -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(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() } - } \ No newline at end of file diff --git a/app/src/main/java/melody/offline/music/activity/MoListDetailsActivity.kt b/app/src/main/java/melody/offline/music/activity/MoListDetailsActivity.kt index f82f149..afca611 100644 --- a/app/src/main/java/melody/offline/music/activity/MoListDetailsActivity.kt +++ b/app/src/main/java/melody/offline/music/activity/MoListDetailsActivity.kt @@ -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 = 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 = 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(R.id.dialog_ok_btn) + val cancelBtn = dialogView.findViewById(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() + } + } + } \ No newline at end of file diff --git a/app/src/main/java/melody/offline/music/activity/MoPlaylistSongsActivity.kt b/app/src/main/java/melody/offline/music/activity/MoPlaylistSongsActivity.kt index 0c95e40..554ba13 100644 --- a/app/src/main/java/melody/offline/music/activity/MoPlaylistSongsActivity.kt +++ b/app/src/main/java/melody/offline/music/activity/MoPlaylistSongsActivity.kt @@ -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() { diff --git a/app/src/main/java/melody/offline/music/adapter/DetailsListAdapter.kt b/app/src/main/java/melody/offline/music/adapter/DetailsListAdapter.kt index 523ad07..97a4274 100644 --- a/app/src/main/java/melody/offline/music/adapter/DetailsListAdapter.kt +++ b/app/src/main/java/melody/offline/music/adapter/DetailsListAdapter.kt @@ -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) + } } \ No newline at end of file diff --git a/app/src/main/java/melody/offline/music/bean/playlist.kt b/app/src/main/java/melody/offline/music/bean/playlist.kt index 47b1eb2..7cd4ee2 100644 --- a/app/src/main/java/melody/offline/music/bean/playlist.kt +++ b/app/src/main/java/melody/offline/music/bean/playlist.kt @@ -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, diff --git a/app/src/main/java/melody/offline/music/innertube/Innertube.kt b/app/src/main/java/melody/offline/music/innertube/Innertube.kt index 73e7c1d..6eb41e3 100644 --- a/app/src/main/java/melody/offline/music/innertube/Innertube.kt +++ b/app/src/main/java/melody/offline/music/innertube/Innertube.kt @@ -223,6 +223,7 @@ object Innertube { val thumbnail: String?, val secondSubtitle: String?, val description: String?, + val singerName: String?, val moPlaylistOrAlbumListBean: List ) { data class MoPlaylistOrAlbumListBean( diff --git a/app/src/main/java/melody/offline/music/innertube/models/SectionListRenderer.kt b/app/src/main/java/melody/offline/music/innertube/models/SectionListRenderer.kt index 0839f39..9f72bf4 100644 --- a/app/src/main/java/melody/offline/music/innertube/models/SectionListRenderer.kt +++ b/app/src/main/java/melody/offline/music/innertube/models/SectionListRenderer.kt @@ -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( diff --git a/app/src/main/java/melody/offline/music/innertube/requests/MoPlaylistPage.kt b/app/src/main/java/melody/offline/music/innertube/requests/MoPlaylistPage.kt index c0698fe..8d6d88c 100644 --- a/app/src/main/java/melody/offline/music/innertube/requests/MoPlaylistPage.kt +++ b/app/src/main/java/melody/offline/music/innertube/requests/MoPlaylistPage.kt @@ -102,7 +102,7 @@ suspend fun Innertube.moPlaylistPage(browseId: String): Result - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/details_list_item.xml b/app/src/main/res/layout/details_list_item.xml index 45100d8..406a22f 100644 --- a/app/src/main/res/layout/details_list_item.xml +++ b/app/src/main/res/layout/details_list_item.xml @@ -94,14 +94,27 @@ + android:src="@drawable/download_icon" /> + + + + + diff --git a/app/src/main/res/layout/list_more_layout.xml b/app/src/main/res/layout/list_more_layout.xml new file mode 100644 index 0000000..8135645 --- /dev/null +++ b/app/src/main/res/layout/list_more_layout.xml @@ -0,0 +1,184 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index b501f80..bf9cde2 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -32,6 +32,7 @@ Download Downloads Library + Like Liked songs Offline songs Songs