添加add playlist

This commit is contained in:
ocean 2024-06-26 13:46:44 +08:00
parent 87f3fdd933
commit ca30bb789a
12 changed files with 936 additions and 39 deletions

View File

@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" <manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
tools:ignore="LockedOrientationActivity"> tools:ignore="LockedOrientationActivity,DiscouragedApi">
<uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" /> <uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
@ -89,6 +89,9 @@
<activity <activity
android:name=".activity.MoLikedSongsActivity" android:name=".activity.MoLikedSongsActivity"
android:screenOrientation="portrait" /> android:screenOrientation="portrait" />
<activity
android:name=".activity.MoPlaylistSongsActivity"
android:screenOrientation="portrait" />
<service <service
android:name=".service.PlaybackService" android:name=".service.PlaybackService"

View File

@ -364,7 +364,6 @@ abstract class MoBaseActivity : AppCompatActivity(), CoroutineScope by MainScope
return currentCountryCode !in restrictedCountryCodes return currentCountryCode !in restrictedCountryCodes
} }
suspend fun showAddPlaylistBottomDialog(favoriteBean: FavoriteBean) { suspend fun showAddPlaylistBottomDialog(favoriteBean: FavoriteBean) {
bottomAddPlaylistSheetDialog = BottomSheetDialog(this) bottomAddPlaylistSheetDialog = BottomSheetDialog(this)
val view = layoutInflater.inflate(R.layout.add_playlist_layout, null) val view = layoutInflater.inflate(R.layout.add_playlist_layout, null)
@ -398,6 +397,8 @@ abstract class MoBaseActivity : AppCompatActivity(), CoroutineScope by MainScope
).show() ).show()
} }
} else { } else {
val isOffline = 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,
@ -405,8 +406,8 @@ abstract class MoBaseActivity : AppCompatActivity(), CoroutineScope by MainScope
title = favoriteBean.title, title = favoriteBean.title,
name = favoriteBean.name, name = favoriteBean.name,
thumbnail = favoriteBean.thumbnail, thumbnail = favoriteBean.thumbnail,
isOffline = false, isOffline = isOffline,
isFavorite = favoriteBean.isFavorite isFavorite = isFavorite
) )
) )
withContext(Dispatchers.Main) { withContext(Dispatchers.Main) {
@ -460,14 +461,16 @@ 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 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,
title = favoriteBean.title, title = favoriteBean.title,
name = favoriteBean.name, name = favoriteBean.name,
thumbnail = favoriteBean.thumbnail, thumbnail = favoriteBean.thumbnail,
isOffline = false, isOffline = isOffline,
isFavorite = favoriteBean.isFavorite isFavorite = isFavorite
) )
App.appPlaylistDBManager.insertOrUpdatePlaylistItem(playlistItem) App.appPlaylistDBManager.insertOrUpdatePlaylistItem(playlistItem)
} }

View File

@ -6,7 +6,6 @@ import android.graphics.drawable.ColorDrawable
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.animation.AnimationUtils import android.view.animation.AnimationUtils
import android.widget.RelativeLayout
import android.widget.TextView import android.widget.TextView
import androidx.annotation.OptIn import androidx.annotation.OptIn
import androidx.appcompat.app.AlertDialog import androidx.appcompat.app.AlertDialog
@ -22,10 +21,10 @@ import melody.offline.music.R
import melody.offline.music.adapter.OfflineSongsAdapter import melody.offline.music.adapter.OfflineSongsAdapter
import melody.offline.music.ads.AdPlacement import melody.offline.music.ads.AdPlacement
import melody.offline.music.ads.LolAdWrapper import melody.offline.music.ads.LolAdWrapper
import melody.offline.music.bean.FavoriteBean
import melody.offline.music.bean.OfflineBean import melody.offline.music.bean.OfflineBean
import melody.offline.music.databinding.ActivityOfflineSongsBinding import melody.offline.music.databinding.ActivityOfflineSongsBinding
import melody.offline.music.util.AnalysisUtil import melody.offline.music.util.AnalysisUtil
import melody.offline.music.util.DownloadUtil
import melody.offline.music.util.LogTag import melody.offline.music.util.LogTag
import org.json.JSONObject import org.json.JSONObject
@ -38,6 +37,7 @@ class MoOfflineSongsActivity : MoBaseActivity() {
data class OnFavorites(val bean: OfflineBean) : Request() data class OnFavorites(val bean: OfflineBean) : Request()
data class OnDownloadRemove(val id: String) : Request() data class OnDownloadRemove(val id: String) : Request()
data class UpdateFavorite(val id: String) : Request() data class UpdateFavorite(val id: String) : Request()
data class OnAddPlaylist(val bean: OfflineBean) : Request()
} }
private lateinit var binding: ActivityOfflineSongsBinding private lateinit var binding: ActivityOfflineSongsBinding
@ -130,7 +130,17 @@ class MoOfflineSongsActivity : MoBaseActivity() {
} }
} }
is Request.OnAddPlaylist -> {
showAddPlaylistBottomDialog(
FavoriteBean(
videoId = it.bean.videoId,
title = it.bean.title,
name = it.bean.name,
thumbnail = it.bean.thumbnail,
isFavorite = it.bean.isFavorite
)
)
}
} }
} }
events.onReceive { events.onReceive {
@ -190,7 +200,10 @@ class MoOfflineSongsActivity : MoBaseActivity() {
} }
} }
binding.moreAddPlaylistBtn.setOnClickListener { binding.moreAddPlaylistBtn.setOnClickListener {
if (currentPosition >= 0) {
requests.trySend(Request.OnAddPlaylist(offlineList[currentPosition]))
hideBottomLayout()
}
} }
binding.favoritesBtn.setOnClickListener { binding.favoritesBtn.setOnClickListener {
if (currentPosition >= 0) { if (currentPosition >= 0) {

View File

@ -24,7 +24,7 @@ import com.bumptech.glide.request.target.CustomTarget
import com.bumptech.glide.request.transition.Transition import com.bumptech.glide.request.transition.Transition
import com.gyf.immersionbar.ktx.immersionBar import com.gyf.immersionbar.ktx.immersionBar
import melody.offline.music.R 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.databinding.ActivityMoPlayDetailsBinding
import melody.offline.music.innertube.Innertube import melody.offline.music.innertube.Innertube
import melody.offline.music.media.MediaControllerManager import melody.offline.music.media.MediaControllerManager
@ -75,7 +75,7 @@ class MoPlayDetailsActivity : MoBaseActivity(), Player.Listener {
private var currentVideoID = "" private var currentVideoID = ""
private var comeFrom: Class<*>? = null private var comeFrom: Class<*>? = null
private var playListAdapter: PlayListAdapter? = null private var detailsPlayListAdapter: DetailsPlayListAdapter? = null
private var startPlayTime = 0L private var startPlayTime = 0L
@ -414,20 +414,20 @@ class MoPlayDetailsActivity : MoBaseActivity(), Player.Listener {
} }
playList.clear() playList.clear()
playList.addAll(allMediaItems) playList.addAll(allMediaItems)
playListAdapter?.notifyDataSetChanged() detailsPlayListAdapter?.notifyDataSetChanged()
} }
} }
private var playList: MutableList<MediaItem> = mutableListOf() private var playList: MutableList<MediaItem> = mutableListOf()
private fun initPlayListAdapter() { private fun initPlayListAdapter() {
playListAdapter = PlayListAdapter( detailsPlayListAdapter = DetailsPlayListAdapter(
this@MoPlayDetailsActivity, playList this@MoPlayDetailsActivity, playList
) )
binding.playListRv.layoutManager = LinearLayoutManager( binding.playListRv.layoutManager = LinearLayoutManager(
this@MoPlayDetailsActivity, LinearLayoutManager.VERTICAL, false this@MoPlayDetailsActivity, LinearLayoutManager.VERTICAL, false
) )
binding.playListRv.adapter = playListAdapter binding.playListRv.adapter = detailsPlayListAdapter
} }
@SuppressLint("NotifyDataSetChanged") @SuppressLint("NotifyDataSetChanged")
@ -663,8 +663,8 @@ class MoPlayDetailsActivity : MoBaseActivity(), Player.Listener {
if (reason == Player.DISCONTINUITY_REASON_AUTO_TRANSITION || reason == Player.DISCONTINUITY_REASON_SEEK_ADJUSTMENT) { if (reason == Player.DISCONTINUITY_REASON_AUTO_TRANSITION || reason == Player.DISCONTINUITY_REASON_SEEK_ADJUSTMENT) {
updateProgressUi() updateProgressUi()
updateInfoUi(meController?.currentMediaItem) updateInfoUi(meController?.currentMediaItem)
if (playListAdapter != null) { if (detailsPlayListAdapter != null) {
playListAdapter?.notifyDataSetChanged() detailsPlayListAdapter?.notifyDataSetChanged()
} }
} }
} }

View File

@ -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<Request> = Channel(Channel.UNLIMITED)
sealed class Request {
data object TryAgain : Request()
}
private lateinit var binding: ActivityPlaylistSongsBinding
private var adapter: PlaylistSongsAdapter? = null
private var playlistItems: MutableList<PlaylistItem> = 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<Unit> {
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<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()
}
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
}
}

View File

@ -10,17 +10,17 @@ import androidx.media3.common.MediaItem
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import com.bumptech.glide.Glide import com.bumptech.glide.Glide
import melody.offline.music.R import melody.offline.music.R
import melody.offline.music.databinding.PlayListItemBinding import melody.offline.music.databinding.DetailsPlayListItemBinding
import melody.offline.music.media.MediaControllerManager import melody.offline.music.media.MediaControllerManager
class PlayListAdapter( class DetailsPlayListAdapter(
private val context: Context, private val context: Context,
private val list: List<MediaItem>, private val list: List<MediaItem>,
) : ) :
RecyclerView.Adapter<PlayListAdapter.ViewHolder>() { RecyclerView.Adapter<DetailsPlayListAdapter.ViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { 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) return ViewHolder(binding)
} }
@ -46,7 +46,7 @@ class PlayListAdapter(
override fun getItemCount(): Int = list.size override fun getItemCount(): Int = list.size
inner class ViewHolder(private val binding: PlayListItemBinding) : inner class ViewHolder(private val binding: DetailsPlayListItemBinding) :
RecyclerView.ViewHolder(binding.root) { RecyclerView.ViewHolder(binding.root) {
@SuppressLint("SetTextI18n") @SuppressLint("SetTextI18n")

View File

@ -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<PlaylistItem>,
) : RecyclerView.Adapter<PlaylistSongsAdapter.ViewHolder>() {
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)
}
}

View File

@ -30,6 +30,7 @@ import melody.offline.music.App
import melody.offline.music.R import melody.offline.music.R
import melody.offline.music.activity.MoLikedSongsActivity import melody.offline.music.activity.MoLikedSongsActivity
import melody.offline.music.activity.MoOfflineSongsActivity import melody.offline.music.activity.MoOfflineSongsActivity
import melody.offline.music.activity.MoPlaylistSongsActivity
import melody.offline.music.activity.SettingsActivity import melody.offline.music.activity.SettingsActivity
import melody.offline.music.adapter.LikedSongsAdapter import melody.offline.music.adapter.LikedSongsAdapter
import melody.offline.music.adapter.NewPlayListAdapter import melody.offline.music.adapter.NewPlayListAdapter
@ -186,7 +187,9 @@ class MoMeFragment : MoBaseFragment<FragmentMoMeBinding>(), NewPlayListAdapter.O
} }
override fun onItemClick(position: Int) { 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 private var bottomSheetDialog: BottomSheetDialog? = null

View File

@ -0,0 +1,362 @@
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/main_bg_color"
android:orientation="vertical">
<ImageView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="centerCrop"
android:src="@mipmap/settings_bg_img" />
<View
android:id="@+id/view"
android:layout_width="match_parent"
android:layout_height="0dp" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_below="@+id/view"
android:orientation="vertical">
<LinearLayout
android:id="@+id/title_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginTop="16dp"
android:layout_marginEnd="16dp"
android:layout_marginBottom="8dp"
android:orientation="horizontal">
<RelativeLayout
android:id="@+id/back_btn"
android:layout_width="42dp"
android:layout_height="42dp"
android:background="@drawable/drw_back_bg">
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:src="@drawable/back_icon" />
</RelativeLayout>
<TextView
android:id="@+id/title_text"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:fontFamily="@font/medium_font"
android:gravity="center"
android:text="@string/app_name"
android:textColor="@color/white"
android:textSize="18dp" />
<RelativeLayout
android:id="@+id/playlist_title_more_btn"
android:layout_width="42dp"
android:layout_height="42dp">
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:src="@drawable/three_dots_icon" />
</RelativeLayout>
</LinearLayout>
<FrameLayout
android:id="@+id/frame_ad"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginEnd="16dp" />
<LinearLayout
android:id="@+id/loadingLayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:visibility="gone">
<ProgressBar
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:indeterminateTint="@color/green"
android:progressBackgroundTint="@color/green"
android:progressTint="@color/green" />
</LinearLayout>
<LinearLayout
android:id="@+id/no_content_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:orientation="vertical"
android:visibility="gone">
<ImageView
android:id="@+id/no_content_iv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@mipmap/no_content_img" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="32dp"
android:layout_marginEnd="32dp"
android:fontFamily="@font/medium_font"
android:gravity="center"
android:text="@string/content_loading_failed"
android:textSize="14dp" />
<TextView
android:id="@+id/tryAgainBtn"
android:layout_width="wrap_content"
android:layout_height="48dp"
android:layout_marginTop="16dp"
android:background="@drawable/drw_btn_bg"
android:fontFamily="@font/medium_font"
android:gravity="center"
android:paddingStart="32dp"
android:paddingEnd="32dp"
android:text="@string/try_again"
android:textColor="@color/black"
android:textSize="16dp" />
</LinearLayout>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginTop="8dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_above="@+id/play_music_layout"
android:orientation="vertical">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/rv"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginStart="16dp"
android:layout_marginTop="4dp"
android:layout_marginEnd="16dp"
android:layout_marginBottom="16dp"
android:overScrollMode="never"
android:scrollbars="none" />
</LinearLayout>
<LinearLayout
android:id="@+id/play_music_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
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_downloaded_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_remove_offline"
android:textColor="@color/white"
android:textSize="14dp" />
</LinearLayout>
<LinearLayout
android:id="@+id/moreAddPlaylistBtn"
android:layout_width="match_parent"
android:layout_height="48dp"
android:layout_marginTop="4dp"
android:gravity="center_vertical"
android:orientation="horizontal">
<ImageView
android:layout_width="24dp"
android:layout_height="24dp"
android:src="@drawable/more_add_playlist_icon" />
<TextView
android:id="@+id/addPlaylistTv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="12dp"
android:text="@string/add_to_playlist"
android:textColor="@color/white"
android:textSize="14dp" />
</LinearLayout>
</LinearLayout>
</LinearLayout>
</LinearLayout>
</LinearLayout>
</RelativeLayout>

View File

@ -0,0 +1,90 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
tools:ignore="MissingDefaultResource">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:orientation="horizontal"
android:paddingBottom="12dp">
<RelativeLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="16dp">
<androidx.cardview.widget.CardView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:elevation="0dp"
android:visibility="visible"
app:cardCornerRadius="4dp"
app:cardElevation="0dp">
<RelativeLayout
android:layout_width="48dp"
android:layout_height="48dp">
<ImageView
android:id="@+id/image"
android:layout_width="48dp"
android:layout_height="48dp"
android:scaleType="centerCrop"
android:src="@mipmap/app_logo" />
<RelativeLayout
android:id="@+id/listPlayView"
android:visibility="gone"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/black_60">
<melody.offline.music.view.MusicBarsView
android:layout_width="wrap_content"
android:layout_height="16dp"
android:layout_centerInParent="true" />
</RelativeLayout>
</RelativeLayout>
</androidx.cardview.widget.CardView>
</RelativeLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
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>
</LinearLayout>

View File

@ -32,17 +32,17 @@
<ImageView <ImageView
android:id="@+id/image" android:id="@+id/image"
android:layout_width="48dp" android:layout_width="match_parent"
android:layout_height="48dp" android:layout_height="match_parent"
android:scaleType="centerCrop" android:scaleType="centerCrop"
android:src="@mipmap/app_logo" /> android:src="@mipmap/app_logo" />
<RelativeLayout <RelativeLayout
android:id="@+id/listPlayView" android:id="@+id/listPlayView"
android:visibility="gone"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:background="@color/black_60"> android:background="@color/black_60"
android:visibility="gone">
<melody.offline.music.view.MusicBarsView <melody.offline.music.view.MusicBarsView
android:layout_width="wrap_content" android:layout_width="wrap_content"
@ -58,8 +58,9 @@
</RelativeLayout> </RelativeLayout>
<LinearLayout <LinearLayout
android:layout_width="match_parent" android:layout_width="0dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_weight="1"
android:gravity="center_vertical" android:gravity="center_vertical"
android:orientation="vertical"> android:orientation="vertical">
@ -67,24 +68,84 @@
android:id="@+id/title" android:id="@+id/title"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:fontFamily="@font/regular_font" android:fontFamily="@font/medium_font"
android:maxLines="1" android:maxLines="1"
android:text="@string/app_name" android:text="@string/app_name"
android:textColor="@color/white" android:textColor="@color/white"
android:textSize="14dp" /> 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
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="4dp"
android:gravity="center_vertical"
android:orientation="horizontal">
<ImageView
android:id="@+id/downloadCoImg"
android:layout_width="16dp"
android:layout_height="16dp"
android:layout_marginEnd="4dp"
android:src="@drawable/download_green_done_icon" />
<TextView
android:id="@+id/size"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="4dp"
android:fontFamily="@font/regular_font"
android:text="@string/app_name"
android:textColor="@color/white_60"
android:textSize="12dp" />
<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> </LinearLayout>
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:gravity="center_vertical"
android:orientation="horizontal">
<LinearLayout
android:id="@+id/downloadBtn"
android:layout_width="40dp"
android:layout_height="40dp"
android:gravity="center">
<ImageView
android:id="@+id/downloadImg"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/download_icon" />
</LinearLayout>
<LinearLayout
android:id="@+id/moreBtn"
android:layout_width="40dp"
android:layout_height="40dp"
android:gravity="center">
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/three_dots_icon" />
</LinearLayout>
</LinearLayout>
</LinearLayout> </LinearLayout>
</LinearLayout> </LinearLayout>

View File

@ -53,5 +53,5 @@
android:scrollbars="none" android:scrollbars="none"
android:overScrollMode="never" android:overScrollMode="never"
tools:itemCount="1" tools:itemCount="1"
tools:listitem="@layout/play_list_item" /> tools:listitem="@layout/details_play_list_item" />
</LinearLayout> </LinearLayout>