diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 197d8db..5eda91a 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -1,7 +1,7 @@ + tools:ignore="LockedOrientationActivity,DiscouragedApi"> @@ -89,6 +89,9 @@ + { + showAddPlaylistBottomDialog( + FavoriteBean( + videoId = it.bean.videoId, + title = it.bean.title, + name = it.bean.name, + thumbnail = it.bean.thumbnail, + isFavorite = it.bean.isFavorite + ) + ) + } } } events.onReceive { @@ -190,7 +200,10 @@ class MoOfflineSongsActivity : MoBaseActivity() { } } binding.moreAddPlaylistBtn.setOnClickListener { - + if (currentPosition >= 0) { + requests.trySend(Request.OnAddPlaylist(offlineList[currentPosition])) + hideBottomLayout() + } } binding.favoritesBtn.setOnClickListener { if (currentPosition >= 0) { diff --git a/app/src/main/java/melody/offline/music/activity/MoPlayDetailsActivity.kt b/app/src/main/java/melody/offline/music/activity/MoPlayDetailsActivity.kt index 095c44c..85a823a 100644 --- a/app/src/main/java/melody/offline/music/activity/MoPlayDetailsActivity.kt +++ b/app/src/main/java/melody/offline/music/activity/MoPlayDetailsActivity.kt @@ -24,7 +24,7 @@ import com.bumptech.glide.request.target.CustomTarget import com.bumptech.glide.request.transition.Transition import com.gyf.immersionbar.ktx.immersionBar import melody.offline.music.R -import melody.offline.music.adapter.PlayListAdapter +import melody.offline.music.adapter.DetailsPlayListAdapter import melody.offline.music.databinding.ActivityMoPlayDetailsBinding import melody.offline.music.innertube.Innertube import melody.offline.music.media.MediaControllerManager @@ -75,7 +75,7 @@ class MoPlayDetailsActivity : MoBaseActivity(), Player.Listener { private var currentVideoID = "" private var comeFrom: Class<*>? = null - private var playListAdapter: PlayListAdapter? = null + private var detailsPlayListAdapter: DetailsPlayListAdapter? = null private var startPlayTime = 0L @@ -414,20 +414,20 @@ class MoPlayDetailsActivity : MoBaseActivity(), Player.Listener { } playList.clear() playList.addAll(allMediaItems) - playListAdapter?.notifyDataSetChanged() + detailsPlayListAdapter?.notifyDataSetChanged() } } private var playList: MutableList = mutableListOf() private fun initPlayListAdapter() { - playListAdapter = PlayListAdapter( + detailsPlayListAdapter = DetailsPlayListAdapter( this@MoPlayDetailsActivity, playList ) binding.playListRv.layoutManager = LinearLayoutManager( this@MoPlayDetailsActivity, LinearLayoutManager.VERTICAL, false ) - binding.playListRv.adapter = playListAdapter + binding.playListRv.adapter = detailsPlayListAdapter } @SuppressLint("NotifyDataSetChanged") @@ -663,8 +663,8 @@ class MoPlayDetailsActivity : MoBaseActivity(), Player.Listener { if (reason == Player.DISCONTINUITY_REASON_AUTO_TRANSITION || reason == Player.DISCONTINUITY_REASON_SEEK_ADJUSTMENT) { updateProgressUi() updateInfoUi(meController?.currentMediaItem) - if (playListAdapter != null) { - playListAdapter?.notifyDataSetChanged() + if (detailsPlayListAdapter != null) { + detailsPlayListAdapter?.notifyDataSetChanged() } } } diff --git a/app/src/main/java/melody/offline/music/activity/MoPlaylistSongsActivity.kt b/app/src/main/java/melody/offline/music/activity/MoPlaylistSongsActivity.kt new file mode 100644 index 0000000..0c95e40 --- /dev/null +++ b/app/src/main/java/melody/offline/music/activity/MoPlaylistSongsActivity.kt @@ -0,0 +1,241 @@ +package melody.offline.music.activity + +import android.annotation.SuppressLint +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 androidx.annotation.OptIn +import androidx.appcompat.app.AlertDialog +import androidx.media3.common.util.UnstableApi +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.PlaylistSongsAdapter +import melody.offline.music.ads.AdPlacement +import melody.offline.music.ads.LolAdWrapper +import melody.offline.music.bean.PlaylistItem +import melody.offline.music.databinding.ActivityPlaylistSongsBinding + +@OptIn(UnstableApi::class) +class MoPlaylistSongsActivity : MoBaseActivity() { + companion object { + const val PLAYLIST_ID = "playlist_id" + const val PLAYLIST_TITLE = "playlist_title" + } + + private val requests: Channel = Channel(Channel.UNLIMITED) + + sealed class Request { + data object TryAgain : Request() + } + + private lateinit var binding: ActivityPlaylistSongsBinding + private var adapter: PlaylistSongsAdapter? = null + private var playlistItems: MutableList = mutableListOf() + private var currentPosition = -1 + private var playlistId = -1 + private var playlistTitle = "" + + override suspend fun main() { + binding = ActivityPlaylistSongsBinding.inflate(layoutInflater) + setContentView(binding.root) + playlistId = intent.getIntExtra(PLAYLIST_ID, -1) + playlistTitle = intent.getStringExtra(PLAYLIST_TITLE) ?: "" + LolAdWrapper.shared.showAdTiming(this, AdPlacement.INST_ME_PAGE_LIST) + initImmersionBar() + initView() + initAdapter() + LolAdWrapper.shared.loadAdIfNotCached(this, AdPlacement.INST_ME_PAGE_LIST) + initData() + onReceive() + } + + private fun initImmersionBar() { + immersionBar { + statusBarDarkFont(false) + statusBarView(binding.view) + } + } + + @SuppressLint("NotifyDataSetChanged") + private suspend fun onReceive() { + while (isActive) { + select { + requests.onReceive { + when (it) { + Request.TryAgain -> { + initData() + } + } + } + events.onReceive { + when (it) { + Event.ActivityOnResume -> { + activityOnResume() + } + + Event.AutomaticallySwitchSongs -> { + if (adapter != null) { + adapter?.notifyDataSetChanged() + } + } + + else -> {} + } + } + } + } + } + + @SuppressLint("NotifyDataSetChanged") + private fun activityOnResume() { + addMusicPlayerViewToLayout(binding.playMusicLayout) + + if (adapter != null) { + 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 + ) + } + + private fun initView() { + binding.titleText.text = playlistTitle + binding.layoutInfo.setOnClickListener { } + binding.backBtn.setOnClickListener { + finish() + } + binding.tryAgainBtn.setOnClickListener { + requests.trySend(Request.TryAgain) + } + binding.bottomCloseBtn.setOnClickListener { + hideBottomLayout() + } + binding.bottomBlankLayout.setOnClickListener { + hideBottomLayout() + } + binding.moreDownloadBtn.setOnClickListener { + if (currentPosition >= 0) { + + } + } + binding.moreAddPlaylistBtn.setOnClickListener { + if (currentPosition >= 0) { + } + } + binding.favoritesBtn.setOnClickListener { + if (currentPosition >= 0) { + } + } + } + + private fun initAdapter() { + adapter = PlaylistSongsAdapter(this, playlistItems) + adapter?.setOnMoreClickListener(object : PlaylistSongsAdapter.OnMoreClickListener { + override fun onMoreClick(position: Int) { + currentPosition = position + toggleBottomLayout(position) + } + }) + binding.rv.layoutManager = LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false) + binding.rv.adapter = adapter + } + + @SuppressLint("NotifyDataSetChanged") + private suspend fun initData() { + showLoadingUi() + playlistItems.clear() + playlistItems.addAll(App.appPlaylistDBManager.getPlaylistItems(playlistId)) + if (playlistItems.size > 0) { + showDataUi() + } else { + showNoContentUi() + } + if (adapter != null) { + adapter?.notifyDataSetChanged() + } + } + + 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() + } + cancelBtn.setOnClickListener { + dialog.dismiss() + } + } + + 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 = playlistItems[position] + Glide.with(this).load(bean.thumbnail).into(binding.songImg) + binding.title.text = bean.title + if (bean.name.isEmpty()) { + binding.name.visibility = View.GONE + } else { + binding.name.visibility = View.VISIBLE + binding.name.text = bean.name + } + } + + private fun hideBottomLayout() { + val slideDownAnimation = AnimationUtils.loadAnimation(this, R.anim.slide_down) + binding.bottomLayout.startAnimation(slideDownAnimation) + binding.bottomLayout.visibility = View.GONE + } + + private fun showDataUi() { + binding.loadingLayout.visibility = View.GONE + binding.noContentLayout.visibility = View.GONE + } + + private fun showLoadingUi() { + binding.loadingLayout.visibility = View.VISIBLE + binding.noContentLayout.visibility = View.GONE + } + + private fun showNoContentUi() { + binding.loadingLayout.visibility = View.GONE + binding.noContentLayout.visibility = View.VISIBLE + } +} \ No newline at end of file diff --git a/app/src/main/java/melody/offline/music/adapter/PlayListAdapter.kt b/app/src/main/java/melody/offline/music/adapter/DetailsPlayListAdapter.kt similarity index 90% rename from app/src/main/java/melody/offline/music/adapter/PlayListAdapter.kt rename to app/src/main/java/melody/offline/music/adapter/DetailsPlayListAdapter.kt index 9795fa2..574ac73 100644 --- a/app/src/main/java/melody/offline/music/adapter/PlayListAdapter.kt +++ b/app/src/main/java/melody/offline/music/adapter/DetailsPlayListAdapter.kt @@ -10,17 +10,17 @@ import androidx.media3.common.MediaItem import androidx.recyclerview.widget.RecyclerView import com.bumptech.glide.Glide import melody.offline.music.R -import melody.offline.music.databinding.PlayListItemBinding +import melody.offline.music.databinding.DetailsPlayListItemBinding import melody.offline.music.media.MediaControllerManager -class PlayListAdapter( +class DetailsPlayListAdapter( private val context: Context, private val list: List, ) : - RecyclerView.Adapter() { + RecyclerView.Adapter() { override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { - val binding = PlayListItemBinding.inflate(LayoutInflater.from(context), parent, false) + val binding = DetailsPlayListItemBinding.inflate(LayoutInflater.from(context), parent, false) return ViewHolder(binding) } @@ -46,7 +46,7 @@ class PlayListAdapter( override fun getItemCount(): Int = list.size - inner class ViewHolder(private val binding: PlayListItemBinding) : + inner class ViewHolder(private val binding: DetailsPlayListItemBinding) : RecyclerView.ViewHolder(binding.root) { @SuppressLint("SetTextI18n") diff --git a/app/src/main/java/melody/offline/music/adapter/PlaylistSongsAdapter.kt b/app/src/main/java/melody/offline/music/adapter/PlaylistSongsAdapter.kt new file mode 100644 index 0000000..fed6488 --- /dev/null +++ b/app/src/main/java/melody/offline/music/adapter/PlaylistSongsAdapter.kt @@ -0,0 +1,121 @@ +package melody.offline.music.adapter + +import android.annotation.SuppressLint +import android.content.Context +import android.content.Intent +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.recyclerview.widget.RecyclerView +import com.bumptech.glide.Glide +import melody.offline.music.R +import melody.offline.music.activity.MoOfflineSongsActivity +import melody.offline.music.activity.MoPlayDetailsActivity +import melody.offline.music.bean.OfflineBean +import melody.offline.music.bean.PlaylistItem +import melody.offline.music.databinding.PlayListItemBinding +import melody.offline.music.media.MediaControllerManager + +class PlaylistSongsAdapter( + private val context: Context, + private val list: List, +) : RecyclerView.Adapter() { + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { + val binding = PlayListItemBinding.inflate(LayoutInflater.from(context), parent, false) + return ViewHolder(binding) + } + + override fun onBindViewHolder(holder: ViewHolder, position: Int) { + val bean = list[position] + holder.bind(bean) + + holder.binding.moreBtn.setOnClickListener { + if (moreClickListener != null) { + moreClickListener?.onMoreClick(position) + } + } + + holder.binding.downloadBtn.setOnClickListener { + if (itemDownloadClickListener != null) { + itemDownloadClickListener?.onDownloadItemClick(position) + } + } + + holder.itemView.setOnClickListener { +// val intent = Intent(context, MoPlayDetailsActivity::class.java) +// intent.putExtra(MoPlayDetailsActivity.PLAY_DETAILS_VIDEO_ID, bean.videoId) +// intent.putExtra(MoPlayDetailsActivity.PLAY_DETAILS_NAME, bean.title) +// intent.putExtra(MoPlayDetailsActivity.PLAY_DETAILS_DESC, bean.name) +// intent.putExtra( +// MoPlayDetailsActivity.PLAY_DETAILS_COME_FROM, MoOfflineSongsActivity::class.java +// ) +// context.startActivity(intent) + } + } + + override fun getItemCount(): Int = list.size + + inner class ViewHolder(val binding: PlayListItemBinding) : + RecyclerView.ViewHolder(binding.root) { + + @SuppressLint("SetTextI18n") + fun bind(bean: PlaylistItem) { + + binding.apply { + Glide.with(context).load(bean.thumbnail).into(image) + title.text = bean.title + if (bean.name.isEmpty()) { + name.visibility = View.GONE + } else { + name.visibility = View.VISIBLE + name.text = bean.name + } + if (bean.size?.isNotEmpty() == true) { + size.text = bean.size + size.visibility = View.VISIBLE + } else { + size.visibility = View.GONE + } + + if (bean.isOffline) { + downloadCoImg.visibility = View.VISIBLE + downloadImg.setImageResource(R.drawable.download_done_icon) + } else { + downloadCoImg.visibility = View.GONE + downloadImg.setImageResource(R.drawable.download_icon) + } + } + } + } + + private var itemDownloadClickListener: OnItemDownloadClickListener? = null + + fun setOnDownloadItemClickListener(listener: OnItemDownloadClickListener) { + itemDownloadClickListener = listener + } + + interface OnItemDownloadClickListener { + fun onDownloadItemClick(position: Int) + } + + private var itemClickListener: OnItemClickListener? = null + + fun setOnItemClickListener(listener: OnItemClickListener) { + itemClickListener = listener + } + + interface OnItemClickListener { + fun onItemClick(position: Int) + } + + private var moreClickListener: OnMoreClickListener? = null + + fun setOnMoreClickListener(listener: OnMoreClickListener) { + moreClickListener = listener + } + + interface OnMoreClickListener { + fun onMoreClick(position: Int) + } +} \ No newline at end of file diff --git a/app/src/main/java/melody/offline/music/fragment/MoMeFragment.kt b/app/src/main/java/melody/offline/music/fragment/MoMeFragment.kt index 0ff1c07..b391019 100644 --- a/app/src/main/java/melody/offline/music/fragment/MoMeFragment.kt +++ b/app/src/main/java/melody/offline/music/fragment/MoMeFragment.kt @@ -30,6 +30,7 @@ import melody.offline.music.App import melody.offline.music.R import melody.offline.music.activity.MoLikedSongsActivity import melody.offline.music.activity.MoOfflineSongsActivity +import melody.offline.music.activity.MoPlaylistSongsActivity import melody.offline.music.activity.SettingsActivity import melody.offline.music.adapter.LikedSongsAdapter import melody.offline.music.adapter.NewPlayListAdapter @@ -186,7 +187,9 @@ class MoMeFragment : MoBaseFragment(), NewPlayListAdapter.O } override fun onItemClick(position: Int) { - + val intent = Intent(requireActivity(), MoPlaylistSongsActivity::class.java) + intent.putExtra(MoPlaylistSongsActivity.PLAYLIST_ID, playlist[position].id) + startActivity(intent) } private var bottomSheetDialog: BottomSheetDialog? = null diff --git a/app/src/main/res/layout/activity_playlist_songs.xml b/app/src/main/res/layout/activity_playlist_songs.xml new file mode 100644 index 0000000..faf8f88 --- /dev/null +++ b/app/src/main/res/layout/activity_playlist_songs.xml @@ -0,0 +1,362 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/details_play_list_item.xml b/app/src/main/res/layout/details_play_list_item.xml new file mode 100644 index 0000000..36057cc --- /dev/null +++ b/app/src/main/res/layout/details_play_list_item.xml @@ -0,0 +1,90 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/play_list_item.xml b/app/src/main/res/layout/play_list_item.xml index 36057cc..cc974d2 100644 --- a/app/src/main/res/layout/play_list_item.xml +++ b/app/src/main/res/layout/play_list_item.xml @@ -32,17 +32,17 @@ + android:background="@color/black_60" + android:visibility="gone"> @@ -67,24 +68,84 @@ android:id="@+id/title" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:fontFamily="@font/regular_font" + android:fontFamily="@font/medium_font" android:maxLines="1" android:text="@string/app_name" android:textColor="@color/white" android:textSize="14dp" /> - + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/search_result_other_layout.xml b/app/src/main/res/layout/search_result_other_layout.xml index 695f325..dfac64d 100644 --- a/app/src/main/res/layout/search_result_other_layout.xml +++ b/app/src/main/res/layout/search_result_other_layout.xml @@ -53,5 +53,5 @@ android:scrollbars="none" android:overScrollMode="never" tools:itemCount="1" - tools:listitem="@layout/play_list_item" /> + tools:listitem="@layout/details_play_list_item" /> \ No newline at end of file