add playlist
This commit is contained in:
parent
641e789e2c
commit
87f3fdd933
@ -24,6 +24,7 @@ import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import melody.offline.music.database.AppFavoriteDBManager
|
||||
import melody.offline.music.database.AppPlaylistDBManager
|
||||
import melody.offline.music.firebase.RemoteConfig
|
||||
import melody.offline.music.http.CommonIpInfoUtil
|
||||
import melody.offline.music.http.UploadEventName
|
||||
@ -40,6 +41,8 @@ class App : Application() {
|
||||
companion object {
|
||||
lateinit var app: App
|
||||
private set
|
||||
lateinit var appPlaylistDBManager: AppPlaylistDBManager
|
||||
private set
|
||||
lateinit var appFavoriteDBManager: AppFavoriteDBManager
|
||||
private set
|
||||
lateinit var appOfflineDBManager: AppOfflineDBManager
|
||||
@ -137,6 +140,7 @@ class App : Application() {
|
||||
appFavoriteDBManager = AppFavoriteDBManager.getInstance(this)
|
||||
currentAudioManager = CurrentAudioManager.getInstance(this)
|
||||
databaseManager = DatabaseManager.getInstance(this)
|
||||
appPlaylistDBManager = AppPlaylistDBManager.getInstance(this)
|
||||
initCurrentPlayingAudio()
|
||||
initImportAudio()
|
||||
CacheManager.initializeCaches(this)
|
||||
|
||||
@ -11,35 +11,47 @@ import android.renderscript.RenderScript
|
||||
import android.renderscript.ScriptIntrinsicBlur
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.widget.EditText
|
||||
import android.widget.LinearLayout
|
||||
import android.widget.RelativeLayout
|
||||
import android.widget.TextView
|
||||
import android.widget.Toast
|
||||
import androidx.annotation.OptIn
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.lifecycle.LifecycleOwner
|
||||
import androidx.media3.common.MediaItem
|
||||
import androidx.media3.common.Player
|
||||
import androidx.media3.common.util.UnstableApi
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.google.android.material.bottomsheet.BottomSheetDialog
|
||||
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.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import org.json.JSONObject
|
||||
import melody.offline.music.App
|
||||
import melody.offline.music.R
|
||||
import melody.offline.music.adapter.NewPlayListAdapter
|
||||
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.media.MediaControllerManager
|
||||
import melody.offline.music.sp.AppStore
|
||||
import melody.offline.music.util.LogTag
|
||||
import melody.offline.music.http.getCountryCode
|
||||
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 {
|
||||
@ -57,6 +69,7 @@ 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
|
||||
@ -350,4 +363,123 @@ abstract class MoBaseActivity : AppCompatActivity(), CoroutineScope by MainScope
|
||||
LogTag.LogD(TAG, "withPermission currentCountryCode->${currentCountryCode}")
|
||||
return currentCountryCode !in restrictedCountryCodes
|
||||
}
|
||||
|
||||
|
||||
suspend fun showAddPlaylistBottomDialog(favoriteBean: FavoriteBean) {
|
||||
bottomAddPlaylistSheetDialog = BottomSheetDialog(this)
|
||||
val view = layoutInflater.inflate(R.layout.add_playlist_layout, null)
|
||||
bottomAddPlaylistSheetDialog.setContentView(view)
|
||||
val newPlayListBtn = view.findViewById<LinearLayout>(R.id.newPlayListBtn)
|
||||
val rv = view.findViewById<RecyclerView>(R.id.newPlayListRv)
|
||||
newPlayListBtn.setOnClickListener {
|
||||
bottomAddPlaylistSheetDialog.dismiss()
|
||||
showNewPlaylistBottomDialog(favoriteBean)
|
||||
}
|
||||
// 设置对话框背景为透明以显示圆角
|
||||
bottomAddPlaylistSheetDialog.window?.setBackgroundDrawableResource(android.R.color.transparent)
|
||||
bottomAddPlaylistSheetDialog.window?.navigationBarColor =
|
||||
ContextCompat.getColor(this, R.color.main_bg_color)
|
||||
bottomAddPlaylistSheetDialog.show()
|
||||
|
||||
val playlist = (App.appPlaylistDBManager.getAllPlaylists())
|
||||
val adapter = NewPlayListAdapter(this, playlist)
|
||||
adapter.setOnItemClickListener(object : NewPlayListAdapter.OnItemClickListener {
|
||||
override fun onItemClick(position: Int) {
|
||||
launch {
|
||||
val playlistItem =
|
||||
App.appPlaylistDBManager.getPlaylistItems(playlist[position].id)
|
||||
val isAny = playlistItem.any { it.title == favoriteBean.title }
|
||||
if (isAny) {//如何这首歌曲已经存在歌单则不添加
|
||||
withContext(Dispatchers.Main) {
|
||||
Toast.makeText(
|
||||
this@MoBaseActivity,
|
||||
getString(R.string.song_exists_playlist_hint),
|
||||
Toast.LENGTH_LONG
|
||||
).show()
|
||||
}
|
||||
} else {
|
||||
App.appPlaylistDBManager.insertOrUpdatePlaylistItem(
|
||||
PlaylistItem(
|
||||
playlistId = playlist[position].id,
|
||||
videoId = favoriteBean.videoId,
|
||||
title = favoriteBean.title,
|
||||
name = favoriteBean.name,
|
||||
thumbnail = favoriteBean.thumbnail,
|
||||
isOffline = false,
|
||||
isFavorite = favoriteBean.isFavorite
|
||||
)
|
||||
)
|
||||
withContext(Dispatchers.Main) {
|
||||
bottomAddPlaylistSheetDialog.dismiss()
|
||||
Toast.makeText(
|
||||
this@MoBaseActivity,
|
||||
getString(R.string.added_playlist_success_Hint),
|
||||
Toast.LENGTH_LONG
|
||||
).show()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
rv.layoutManager = LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false)
|
||||
rv.adapter = adapter
|
||||
}
|
||||
|
||||
private var bottomSheetDialog: BottomSheetDialog? = null
|
||||
private fun showNewPlaylistBottomDialog(favoriteBean: FavoriteBean) {
|
||||
bottomSheetDialog = BottomSheetDialog(this)
|
||||
val view = layoutInflater.inflate(R.layout.new_playlist_layout, null)
|
||||
bottomSheetDialog?.setContentView(view)
|
||||
val edit = view.findViewById<EditText>(R.id.playlistEt)
|
||||
val confirmBtn = view.findViewById<TextView>(R.id.confirmBtn)
|
||||
confirmBtn.setOnClickListener {
|
||||
val text = edit.text.toString().trim()
|
||||
if (text.isNotEmpty()) {
|
||||
launch {
|
||||
val playlist = App.appPlaylistDBManager.getPlaylistByTitle(text)
|
||||
if (playlist != null) {
|
||||
withContext(Dispatchers.Main) {
|
||||
Toast.makeText(
|
||||
this@MoBaseActivity,
|
||||
getString(R.string.new_playlist_duplicate_name_hint),
|
||||
Toast.LENGTH_LONG
|
||||
).show()
|
||||
}
|
||||
} else {
|
||||
val newPlaylist = Playlist(title = text)
|
||||
App.appPlaylistDBManager.insertOrUpdatePlaylist(newPlaylist)
|
||||
withContext(Dispatchers.Main) {
|
||||
if (bottomSheetDialog != null) {
|
||||
bottomSheetDialog?.dismiss()
|
||||
}
|
||||
Toast.makeText(
|
||||
this@MoBaseActivity,
|
||||
getString(R.string.created_successfully),
|
||||
Toast.LENGTH_LONG
|
||||
).show()
|
||||
}
|
||||
val currentPlaylist = App.appPlaylistDBManager.getPlaylistByTitle(text)
|
||||
if (currentPlaylist != null) {
|
||||
val playlistItem = PlaylistItem(
|
||||
playlistId = currentPlaylist.id,
|
||||
videoId = favoriteBean.videoId,
|
||||
title = favoriteBean.title,
|
||||
name = favoriteBean.name,
|
||||
thumbnail = favoriteBean.thumbnail,
|
||||
isOffline = false,
|
||||
isFavorite = favoriteBean.isFavorite
|
||||
)
|
||||
App.appPlaylistDBManager.insertOrUpdatePlaylistItem(playlistItem)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// 设置对话框背景为透明以显示圆角
|
||||
bottomSheetDialog?.window?.setBackgroundDrawableResource(android.R.color.transparent)
|
||||
bottomSheetDialog?.window?.navigationBarColor =
|
||||
ContextCompat.getColor(this, R.color.main_bg_color)
|
||||
bottomSheetDialog?.show()
|
||||
}
|
||||
|
||||
}
|
||||
@ -6,10 +6,12 @@ import android.graphics.drawable.ColorDrawable
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.animation.AnimationUtils
|
||||
import android.widget.EditText
|
||||
import android.widget.TextView
|
||||
import android.widget.Toast
|
||||
import androidx.annotation.OptIn
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.core.net.toUri
|
||||
import androidx.media3.common.util.UnstableApi
|
||||
import androidx.media3.exoplayer.offline.Download
|
||||
@ -17,6 +19,7 @@ import androidx.media3.exoplayer.offline.DownloadRequest
|
||||
import androidx.media3.exoplayer.offline.DownloadService
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import com.bumptech.glide.Glide
|
||||
import com.google.android.material.bottomsheet.BottomSheetDialog
|
||||
import com.gyf.immersionbar.ktx.immersionBar
|
||||
import kotlinx.coroutines.channels.Channel
|
||||
import kotlinx.coroutines.isActive
|
||||
@ -28,6 +31,7 @@ import melody.offline.music.ads.AdPlacement
|
||||
import melody.offline.music.ads.LolAdWrapper
|
||||
import melody.offline.music.bean.FavoriteBean
|
||||
import melody.offline.music.databinding.ActivityLikedSongsBinding
|
||||
import melody.offline.music.fragment.MoMeFragment
|
||||
import melody.offline.music.service.MyDownloadService
|
||||
import melody.offline.music.service.ViewModelMain
|
||||
import melody.offline.music.util.AnalysisUtil
|
||||
@ -48,6 +52,7 @@ class MoLikedSongsActivity : MoBaseActivity(), LikedSongsAdapter.OnItemFavorites
|
||||
data class OnDownload(val favoriteBean: FavoriteBean) : Request()
|
||||
data class OnDownloadRemove(val id: String) : Request()
|
||||
data class OnUpdateDownloadUi(val id: String) : Request()
|
||||
data class OnAddPlaylist(val bean: FavoriteBean) : Request()
|
||||
}
|
||||
|
||||
private lateinit var binding: ActivityLikedSongsBinding
|
||||
@ -189,6 +194,10 @@ class MoLikedSongsActivity : MoBaseActivity(), LikedSongsAdapter.OnItemFavorites
|
||||
binding.downloadTv.text = getString(R.string.download_save_offline)
|
||||
}
|
||||
}
|
||||
|
||||
is Request.OnAddPlaylist -> {
|
||||
showAddPlaylistBottomDialog(it.bean)
|
||||
}
|
||||
}
|
||||
}
|
||||
events.onReceive {
|
||||
@ -251,6 +260,12 @@ class MoLikedSongsActivity : MoBaseActivity(), LikedSongsAdapter.OnItemFavorites
|
||||
requests.trySend(Request.OnDownload(favoriteBeans[currentPosition]))
|
||||
}
|
||||
}
|
||||
binding.moreAddPlaylistBtn.setOnClickListener {
|
||||
if (currentPosition >= 0) {
|
||||
requests.trySend(Request.OnAddPlaylist(favoriteBeans[currentPosition]))
|
||||
hideBottomLayout()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun showRemoveDownloadDialogHint(id: String) {
|
||||
@ -348,7 +363,7 @@ class MoLikedSongsActivity : MoBaseActivity(), LikedSongsAdapter.OnItemFavorites
|
||||
|
||||
else -> {
|
||||
binding.downloadLoading.visibility = View.GONE
|
||||
binding.downloadImg.setImageResource(R.drawable.download_icon)
|
||||
binding.downloadImg.setImageResource(R.drawable.more_download_icon)
|
||||
binding.downloadImg.visibility = View.VISIBLE
|
||||
|
||||
binding.downloadTv.text = getString(R.string.download_save_offline)
|
||||
|
||||
@ -0,0 +1,79 @@
|
||||
package melody.offline.music.adapter
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.Context
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.media3.common.C
|
||||
import androidx.media3.common.MediaItem
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.bumptech.glide.Glide
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.DelicateCoroutinesApi
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.GlobalScope
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import melody.offline.music.App
|
||||
import melody.offline.music.R
|
||||
import melody.offline.music.bean.Playlist
|
||||
import melody.offline.music.databinding.NewPlayListItemBinding
|
||||
import melody.offline.music.databinding.PlayListItemBinding
|
||||
import melody.offline.music.media.MediaControllerManager
|
||||
|
||||
class NewPlayListAdapter(
|
||||
private val context: Context,
|
||||
private val list: List<Playlist>,
|
||||
) : RecyclerView.Adapter<NewPlayListAdapter.ViewHolder>() {
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
|
||||
val binding = NewPlayListItemBinding.inflate(LayoutInflater.from(context), parent, false)
|
||||
return ViewHolder(binding)
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
|
||||
val bean = list[position]
|
||||
holder.bind(bean)
|
||||
|
||||
holder.itemView.setOnClickListener {
|
||||
if (itemClickListener != null) {
|
||||
itemClickListener?.onItemClick(position)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun getItemCount(): Int = list.size
|
||||
|
||||
inner class ViewHolder(private val binding: NewPlayListItemBinding) :
|
||||
RecyclerView.ViewHolder(binding.root) {
|
||||
|
||||
@OptIn(DelicateCoroutinesApi::class)
|
||||
@SuppressLint("SetTextI18n")
|
||||
fun bind(bean: Playlist) {
|
||||
|
||||
binding.apply {
|
||||
title.text = bean.title
|
||||
GlobalScope.launch {
|
||||
val items = App.appPlaylistDBManager.getPlaylistItems(bean.id)
|
||||
withContext(Dispatchers.Main) {
|
||||
name.text = "${items.size} " + context.getString(R.string.songs)
|
||||
if (items.isNotEmpty()) {
|
||||
Glide.with(context).load(items[0].thumbnail).into(image)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private var itemClickListener: OnItemClickListener? = null
|
||||
|
||||
fun setOnItemClickListener(listener: OnItemClickListener) {
|
||||
itemClickListener = listener
|
||||
}
|
||||
|
||||
interface OnItemClickListener {
|
||||
fun onItemClick(position: Int)
|
||||
}
|
||||
}
|
||||
38
app/src/main/java/melody/offline/music/bean/playlist.kt
Normal file
38
app/src/main/java/melody/offline/music/bean/playlist.kt
Normal file
@ -0,0 +1,38 @@
|
||||
package melody.offline.music.bean
|
||||
|
||||
import androidx.room.Entity
|
||||
import androidx.room.PrimaryKey
|
||||
import androidx.room.ForeignKey
|
||||
import androidx.room.ColumnInfo
|
||||
import androidx.room.Index
|
||||
|
||||
@Entity(tableName = "playlists")
|
||||
data class Playlist(
|
||||
@PrimaryKey(autoGenerate = true) val id: Int = 0,
|
||||
@ColumnInfo(name = "title") val title: String
|
||||
)
|
||||
|
||||
@Entity(
|
||||
tableName = "playlist_items",
|
||||
foreignKeys = [
|
||||
ForeignKey(
|
||||
entity = Playlist::class,
|
||||
parentColumns = ["id"],
|
||||
childColumns = ["playlist_id"],
|
||||
onDelete = ForeignKey.CASCADE
|
||||
)
|
||||
],
|
||||
indices = [Index(value = ["playlist_id"])] // 添加索引
|
||||
)
|
||||
data class PlaylistItem(
|
||||
@PrimaryKey(autoGenerate = true) val id: Int = 0,
|
||||
@ColumnInfo(name = "playlist_id") val playlistId: Int,
|
||||
@ColumnInfo(name = "videoId") var videoId: String,
|
||||
@ColumnInfo(name = "title") var title: String,
|
||||
@ColumnInfo(name = "name") var name: String,
|
||||
@ColumnInfo(name = "thumbnail") var thumbnail: String? = null,
|
||||
@ColumnInfo(name = "bytesDownloaded") var bytesDownloaded: Long? = null,
|
||||
@ColumnInfo(name = "size") var size: String? = null,
|
||||
@ColumnInfo(name = "isOffline") var isOffline: Boolean,
|
||||
@ColumnInfo(name = "isFavorite") var isFavorite: Boolean
|
||||
)
|
||||
@ -1,4 +1,3 @@
|
||||
|
||||
package melody.offline.music.database
|
||||
|
||||
import androidx.room.Database
|
||||
|
||||
@ -0,0 +1,83 @@
|
||||
package melody.offline.music.database
|
||||
|
||||
import android.content.Context
|
||||
import androidx.room.Room
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
import melody.offline.music.bean.Playlist
|
||||
import melody.offline.music.bean.PlaylistItem
|
||||
|
||||
class AppPlaylistDBManager private constructor(context: Context) {
|
||||
|
||||
companion object {
|
||||
@Volatile
|
||||
private var instance: AppPlaylistDBManager? = null
|
||||
|
||||
fun getInstance(context: Context): AppPlaylistDBManager {
|
||||
return instance ?: synchronized(this) {
|
||||
instance ?: AppPlaylistDBManager(context).also { instance = it }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private val database = Room.databaseBuilder(
|
||||
context.applicationContext, AppPlaylistDatabase::class.java, "app_playlist_data_base"
|
||||
).build()
|
||||
|
||||
private val playlistDao = database.playlistDao()
|
||||
|
||||
|
||||
suspend fun insertOrUpdatePlaylist(playlist: Playlist) {
|
||||
return withContext(Dispatchers.IO) {
|
||||
val existingPlaylist = playlistDao.getPlaylistById(playlist.id)
|
||||
if (existingPlaylist == null) {
|
||||
playlistDao.insertPlaylist(playlist)
|
||||
} else {
|
||||
playlistDao.updatePlaylist(playlist)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun insertOrUpdatePlaylistItem(playlistItem: PlaylistItem) {
|
||||
return withContext(Dispatchers.IO) {
|
||||
val existingItem = playlistDao.getPlaylistItemById(playlistItem.id)
|
||||
if (existingItem == null) {
|
||||
playlistDao.insertPlaylistItem(playlistItem)
|
||||
} else {
|
||||
playlistDao.updatePlaylistItem(playlistItem)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun getPlaylistByTitle(title: String): Playlist? {
|
||||
return withContext(Dispatchers.IO) {
|
||||
playlistDao.getPlaylistByTitle(title)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
suspend fun getAllPlaylists(): List<Playlist> {
|
||||
return withContext(Dispatchers.IO) {
|
||||
playlistDao.getAllPlaylists()
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun getPlaylistItems(playlistId: Int): List<PlaylistItem> {
|
||||
return withContext(Dispatchers.IO) {
|
||||
playlistDao.getPlaylistItems(playlistId)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
suspend fun deletePlaylist(playlist: Playlist) {
|
||||
withContext(Dispatchers.IO) {
|
||||
playlistDao.deletePlaylist(playlist)
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun deletePlaylistItem(playlistItem: PlaylistItem) {
|
||||
withContext(Dispatchers.IO) {
|
||||
playlistDao.deletePlaylistItem(playlistItem)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,12 @@
|
||||
|
||||
package melody.offline.music.database
|
||||
|
||||
import androidx.room.Database
|
||||
import androidx.room.RoomDatabase
|
||||
import melody.offline.music.bean.Playlist
|
||||
import melody.offline.music.bean.PlaylistItem
|
||||
|
||||
@Database(entities = [Playlist::class, PlaylistItem::class], version = 1, exportSchema = false)
|
||||
abstract class AppPlaylistDatabase : RoomDatabase() {
|
||||
abstract fun playlistDao(): PlaylistDao
|
||||
}
|
||||
@ -0,0 +1,41 @@
|
||||
package melody.offline.music.database
|
||||
|
||||
import androidx.room.*
|
||||
import melody.offline.music.bean.Playlist
|
||||
import melody.offline.music.bean.PlaylistItem
|
||||
|
||||
@Dao
|
||||
interface PlaylistDao {
|
||||
@Insert
|
||||
suspend fun insertPlaylist(playlist: Playlist): Long
|
||||
|
||||
@Insert
|
||||
suspend fun insertPlaylistItem(playlistItem: PlaylistItem): Long
|
||||
|
||||
@Query("SELECT * FROM playlists WHERE id = :id")
|
||||
suspend fun getPlaylistById(id: Int): Playlist?
|
||||
|
||||
@Query("SELECT * FROM playlists WHERE title = :title")
|
||||
suspend fun getPlaylistByTitle(title: String): Playlist?
|
||||
|
||||
@Query("SELECT * FROM playlist_items WHERE id = :id")
|
||||
suspend fun getPlaylistItemById(id: Int): PlaylistItem?
|
||||
|
||||
@Query("SELECT * FROM playlists")
|
||||
fun getAllPlaylists(): List<Playlist>
|
||||
|
||||
@Query("SELECT * FROM playlist_items WHERE playlist_id = :playlistId")
|
||||
fun getPlaylistItems(playlistId: Int): List<PlaylistItem>
|
||||
|
||||
@Update
|
||||
suspend fun updatePlaylist(playlist: Playlist)
|
||||
|
||||
@Update
|
||||
suspend fun updatePlaylistItem(playlistItem: PlaylistItem)
|
||||
|
||||
@Delete
|
||||
suspend fun deletePlaylist(playlist: Playlist)
|
||||
|
||||
@Delete
|
||||
suspend fun deletePlaylistItem(playlistItem: PlaylistItem)
|
||||
}
|
||||
@ -1,9 +1,27 @@
|
||||
package melody.offline.music.fragment
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.app.Dialog
|
||||
import android.content.Intent
|
||||
import android.graphics.Color
|
||||
import android.graphics.drawable.ColorDrawable
|
||||
import android.view.Gravity
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.view.Window
|
||||
import android.view.WindowManager
|
||||
import android.view.animation.Animation
|
||||
import android.view.animation.AnimationUtils
|
||||
import android.widget.EditText
|
||||
import android.widget.RelativeLayout
|
||||
import android.widget.TextView
|
||||
import android.widget.Toast
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import com.bumptech.glide.Glide
|
||||
import com.google.android.material.bottomsheet.BottomSheetDialog
|
||||
import com.gyf.immersionbar.ktx.immersionBar
|
||||
import kotlinx.coroutines.channels.Channel
|
||||
import kotlinx.coroutines.isActive
|
||||
@ -13,17 +31,23 @@ import melody.offline.music.R
|
||||
import melody.offline.music.activity.MoLikedSongsActivity
|
||||
import melody.offline.music.activity.MoOfflineSongsActivity
|
||||
import melody.offline.music.activity.SettingsActivity
|
||||
import melody.offline.music.adapter.LikedSongsAdapter
|
||||
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.Playlist
|
||||
import melody.offline.music.databinding.FragmentMoMeBinding
|
||||
import melody.offline.music.util.AnalysisUtil
|
||||
|
||||
class MoMeFragment : MoBaseFragment<FragmentMoMeBinding>() {
|
||||
class MoMeFragment : MoBaseFragment<FragmentMoMeBinding>(), NewPlayListAdapter.OnItemClickListener {
|
||||
|
||||
private val requests: Channel<Request> = Channel(Channel.UNLIMITED)
|
||||
private var adapter: NewPlayListAdapter? = null
|
||||
private var playlist: MutableList<Playlist> = mutableListOf()
|
||||
|
||||
enum class Request {
|
||||
|
||||
sealed class Request {
|
||||
data class AddPlaylist(val text: String) : Request()
|
||||
}
|
||||
|
||||
override val bindingInflater: (LayoutInflater, ViewGroup?, Boolean) -> FragmentMoMeBinding
|
||||
@ -32,6 +56,7 @@ class MoMeFragment : MoBaseFragment<FragmentMoMeBinding>() {
|
||||
override suspend fun onViewCreated() {
|
||||
LolAdWrapper.shared.loadAdIfNotCached(requireActivity(), AdPlacement.INST_ME_PAGE_LIST)
|
||||
initView()
|
||||
initAdapter()
|
||||
onReceive()
|
||||
}
|
||||
|
||||
@ -46,7 +71,25 @@ class MoMeFragment : MoBaseFragment<FragmentMoMeBinding>() {
|
||||
while (isActive) {
|
||||
select<Unit> {
|
||||
requests.onReceive {
|
||||
|
||||
when (it) {
|
||||
is Request.AddPlaylist -> {
|
||||
val playlist = App.appPlaylistDBManager.getPlaylistByTitle(it.text)
|
||||
if (playlist != null) {
|
||||
Toast.makeText(
|
||||
requireActivity(),
|
||||
getString(R.string.new_playlist_duplicate_name_hint),
|
||||
Toast.LENGTH_LONG
|
||||
).show()
|
||||
} else {
|
||||
val newPlaylist = Playlist(title = it.text)
|
||||
App.appPlaylistDBManager.insertOrUpdatePlaylist(newPlaylist)
|
||||
if (bottomSheetDialog != null) {
|
||||
bottomSheetDialog?.dismiss()
|
||||
}
|
||||
getPlaylistData()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
events.onReceive {
|
||||
@ -88,6 +131,9 @@ class MoMeFragment : MoBaseFragment<FragmentMoMeBinding>() {
|
||||
).show()
|
||||
}
|
||||
}
|
||||
binding.newPlayListBtn.setOnClickListener {
|
||||
showBottomDialog()
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun fragmentOnResume() {
|
||||
@ -100,6 +146,8 @@ class MoMeFragment : MoBaseFragment<FragmentMoMeBinding>() {
|
||||
val favoriteBeans = App.appFavoriteDBManager.getAllFavoriteBeans()
|
||||
val favorites = favoriteBeans.count { it.isFavorite }
|
||||
binding.likedSongsTv.text = "$favorites"
|
||||
|
||||
getPlaylistData()
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
@ -121,4 +169,44 @@ class MoMeFragment : MoBaseFragment<FragmentMoMeBinding>() {
|
||||
initImmersionBar()
|
||||
}
|
||||
}
|
||||
|
||||
private fun initAdapter() {
|
||||
adapter = NewPlayListAdapter(requireActivity(), playlist)
|
||||
adapter?.setOnItemClickListener(this)
|
||||
binding.newPlayListRv.layoutManager =
|
||||
LinearLayoutManager(requireActivity(), LinearLayoutManager.VERTICAL, false)
|
||||
binding.newPlayListRv.adapter = adapter
|
||||
}
|
||||
|
||||
@SuppressLint("NotifyDataSetChanged")
|
||||
private suspend fun getPlaylistData() {
|
||||
playlist.clear()
|
||||
playlist.addAll(App.appPlaylistDBManager.getAllPlaylists())
|
||||
adapter?.notifyDataSetChanged()
|
||||
}
|
||||
|
||||
override fun onItemClick(position: Int) {
|
||||
|
||||
}
|
||||
|
||||
private var bottomSheetDialog: BottomSheetDialog? = null
|
||||
private fun showBottomDialog() {
|
||||
bottomSheetDialog = BottomSheetDialog(requireActivity())
|
||||
val view = layoutInflater.inflate(R.layout.new_playlist_layout, null)
|
||||
bottomSheetDialog?.setContentView(view)
|
||||
val edit = view.findViewById<EditText>(R.id.playlistEt)
|
||||
val confirmBtn = view.findViewById<TextView>(R.id.confirmBtn)
|
||||
confirmBtn.setOnClickListener {
|
||||
val text = edit.text.toString().trim()
|
||||
if (text.isNotEmpty()) {
|
||||
requests.trySend(Request.AddPlaylist(text))
|
||||
}
|
||||
}
|
||||
// 设置对话框背景为透明以显示圆角
|
||||
bottomSheetDialog?.window?.setBackgroundDrawableResource(android.R.color.transparent)
|
||||
bottomSheetDialog?.window?.navigationBarColor =
|
||||
ContextCompat.getColor(requireActivity(), R.color.main_bg_color)
|
||||
bottomSheetDialog?.show()
|
||||
}
|
||||
|
||||
}
|
||||
8
app/src/main/res/anim/slide_in_bottom.xml
Normal file
8
app/src/main/res/anim/slide_in_bottom.xml
Normal file
@ -0,0 +1,8 @@
|
||||
<!-- res/anim/slide_in_bottom.xml -->
|
||||
<set xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:shareInterpolator="false">
|
||||
<translate
|
||||
android:fromYDelta="100%p"
|
||||
android:toYDelta="0%p"
|
||||
android:duration="300"/>
|
||||
</set>
|
||||
8
app/src/main/res/anim/slide_out_bottom.xml
Normal file
8
app/src/main/res/anim/slide_out_bottom.xml
Normal file
@ -0,0 +1,8 @@
|
||||
<!-- res/anim/slide_out_bottom.xml -->
|
||||
<set xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:shareInterpolator="false">
|
||||
<translate
|
||||
android:fromYDelta="0%p"
|
||||
android:toYDelta="100%p"
|
||||
android:duration="300"/>
|
||||
</set>
|
||||
13
app/src/main/res/drawable/add_white_icon.xml
Normal file
13
app/src/main/res/drawable/add_white_icon.xml
Normal file
@ -0,0 +1,13 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<group>
|
||||
<clip-path
|
||||
android:pathData="M0,0h24v24h-24z"/>
|
||||
<path
|
||||
android:pathData="M12.04,2.182C12.401,2.182 12.748,2.325 13.004,2.581C13.26,2.837 13.403,3.184 13.403,3.545L13.403,10.734H21C21.361,10.734 21.708,10.878 21.964,11.133C22.22,11.389 22.364,11.736 22.364,12.098C22.364,12.459 22.22,12.806 21.964,13.062C21.708,13.318 21.361,13.461 21,13.461H13.403V21C13.403,21.362 13.259,21.708 13.003,21.964C12.748,22.22 12.401,22.364 12.039,22.364C11.677,22.364 11.331,22.22 11.075,21.964C10.819,21.708 10.675,21.362 10.675,21V13.461H3.545C3.184,13.461 2.837,13.318 2.581,13.062C2.325,12.806 2.182,12.459 2.182,12.098C2.182,11.736 2.325,11.389 2.581,11.133C2.837,10.878 3.184,10.734 3.545,10.734H10.675V3.545C10.675,3.184 10.819,2.837 11.075,2.581C11.331,2.325 11.677,2.182 12.039,2.182H12.04Z"
|
||||
android:fillColor="#ffffff"/>
|
||||
</group>
|
||||
</vector>
|
||||
7
app/src/main/res/drawable/drw_round_16_bg.xml
Normal file
7
app/src/main/res/drawable/drw_round_16_bg.xml
Normal file
@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:shape="rectangle">
|
||||
|
||||
<solid android:color="#26FFFFFF" />
|
||||
<corners android:radius="16dp" />
|
||||
</shape>
|
||||
94
app/src/main/res/layout/add_playlist_layout.xml
Normal file
94
app/src/main/res/layout/add_playlist_layout.xml
Normal file
@ -0,0 +1,94 @@
|
||||
<?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="match_parent">
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/bottom_layout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="@drawable/drw_bottom_layout_bg"
|
||||
android:orientation="vertical"
|
||||
android:visibility="visible">
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/layout_info"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
android:layout_weight="9"
|
||||
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>
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="16dp"
|
||||
android:fontFamily="@font/medium_font"
|
||||
android:gravity="center_horizontal"
|
||||
android:text="@string/add_to_playlist"
|
||||
android:textColor="@color/white"
|
||||
android:textSize="18dp" />
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/newPlayListBtn"
|
||||
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="16dp"
|
||||
android:gravity="center_vertical"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<RelativeLayout
|
||||
android:layout_width="56dp"
|
||||
android:layout_height="56dp"
|
||||
android:background="@drawable/drw_round_48_bg">
|
||||
|
||||
<ImageView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_centerInParent="true"
|
||||
android:src="@drawable/add_white_icon" />
|
||||
</RelativeLayout>
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="16dp"
|
||||
android:fontFamily="@font/regular_font"
|
||||
android:text="@string/new_playlist"
|
||||
android:textColor="@color/white"
|
||||
android:textSize="16dp" />
|
||||
</LinearLayout>
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/newPlayListRv"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="16dp"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:layout_marginBottom="16dp" />
|
||||
</LinearLayout>
|
||||
|
||||
<View
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
android:layout_weight="1" />
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
@ -63,7 +63,10 @@
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="14dp"
|
||||
android:layout_marginStart="12dp"
|
||||
android:layout_marginTop="16dp"
|
||||
android:layout_marginEnd="12dp"
|
||||
android:layout_marginBottom="8dp"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<LinearLayout
|
||||
@ -201,10 +204,62 @@
|
||||
android:layout_marginStart="14dp"
|
||||
android:layout_marginEnd="14dp" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="16dp"
|
||||
android:layout_marginTop="8dp"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:fontFamily="@font/medium_font"
|
||||
android:text="@string/new_playlist"
|
||||
android:textColor="@color/white"
|
||||
android:textSize="20dp" />
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/newPlayListBtn"
|
||||
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="16dp"
|
||||
android:gravity="center_vertical"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<RelativeLayout
|
||||
android:layout_width="56dp"
|
||||
android:layout_height="56dp"
|
||||
android:background="@drawable/drw_round_48_bg">
|
||||
|
||||
<ImageView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_centerInParent="true"
|
||||
android:src="@drawable/add_white_icon" />
|
||||
</RelativeLayout>
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="16dp"
|
||||
android:fontFamily="@font/regular_font"
|
||||
android:text="@string/new_playlist"
|
||||
android:textColor="@color/white"
|
||||
android:textSize="16dp" />
|
||||
</LinearLayout>
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/newPlayListRv"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="16dp"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:layout_marginBottom="16dp"
|
||||
android:overScrollMode="never"
|
||||
android:scrollbars="none" />
|
||||
</LinearLayout>
|
||||
|
||||
</androidx.core.widget.NestedScrollView>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</RelativeLayout>
|
||||
71
app/src/main/res/layout/new_play_list_item.xml
Normal file
71
app/src/main/res/layout/new_play_list_item.xml
Normal file
@ -0,0 +1,71 @@
|
||||
<?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">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/image"
|
||||
android:layout_width="56dp"
|
||||
android:layout_height="56dp"
|
||||
android:scaleType="centerCrop"
|
||||
android:src="@mipmap/app_logo" />
|
||||
|
||||
</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:layout_marginTop="4dp"
|
||||
android:fontFamily="@font/regular_font"
|
||||
android:maxLines="1"
|
||||
android:text="@string/app_name"
|
||||
android:textColor="@color/white_60"
|
||||
android:textSize="12dp" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
88
app/src/main/res/layout/new_playlist_layout.xml
Normal file
88
app/src/main/res/layout/new_playlist_layout.xml
Normal file
@ -0,0 +1,88 @@
|
||||
<?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="match_parent">
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/bottom_layout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="@drawable/drw_bottom_layout_bg"
|
||||
android:orientation="vertical"
|
||||
android:visibility="visible">
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/layout_info"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
android:layout_weight="9"
|
||||
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>
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="16dp"
|
||||
android:layout_marginBottom="16dp"
|
||||
android:gravity="center"
|
||||
android:text="@string/new_playlist"
|
||||
android:textColor="@color/white"
|
||||
android:textSize="16dp" />
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="16dp"
|
||||
android:background="@drawable/drw_round_16_bg">
|
||||
|
||||
<EditText
|
||||
android:id="@+id/playlistEt"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="56dp"
|
||||
android:layout_marginStart="16dp"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:background="@null"
|
||||
android:gravity="center"
|
||||
android:text="Playlist 1"
|
||||
android:textSize="14dp" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/confirmBtn"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_horizontal"
|
||||
android:layout_marginTop="32dp"
|
||||
android:background="@drawable/drw_btn_bg"
|
||||
android:fontFamily="@font/medium_font"
|
||||
android:paddingStart="32dp"
|
||||
android:paddingTop="16dp"
|
||||
android:paddingEnd="32dp"
|
||||
android:paddingBottom="16dp"
|
||||
android:text="@string/confirm"
|
||||
android:textColor="@color/black"
|
||||
android:textSize="14dp" />
|
||||
</LinearLayout>
|
||||
|
||||
<View
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
android:layout_weight="1" />
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
@ -47,4 +47,9 @@
|
||||
<string name="download_save_offline">Save to Offline</string>
|
||||
<string name="download_remove_offline">Remove from offline</string>
|
||||
<string name="download_remove_hint">Are you sure you want to delete this downloaded music file?</string>
|
||||
<string name="confirm">Confirm</string>
|
||||
<string name="new_playlist_duplicate_name_hint">A playlist with the same name already exists.</string>
|
||||
<string name="created_successfully">Created successfully</string>
|
||||
<string name="song_exists_playlist_hint">This song already exists in this playlist.</string>
|
||||
<string name="added_playlist_success_Hint">Successfully added to the playlist</string>
|
||||
</resources>
|
||||
@ -7,4 +7,9 @@
|
||||
<item name="android:minHeight">4dp</item> <!-- 设置进度条高度 -->
|
||||
<item name="android:maxHeight">4dp</item> <!-- 设置进度条高度 -->
|
||||
</style>
|
||||
|
||||
<style name="DialogAnimation">
|
||||
<item name="android:windowEnterAnimation">@anim/slide_in_bottom</item>
|
||||
<item name="android:windowExitAnimation">@anim/slide_out_bottom</item>
|
||||
</style>
|
||||
</resources>
|
||||
Loading…
Reference in New Issue
Block a user