update
This commit is contained in:
parent
2a151145a2
commit
99e5382a5b
@ -69,6 +69,9 @@
|
|||||||
<activity
|
<activity
|
||||||
android:name=".activity.MoOfflineSongsActivity"
|
android:name=".activity.MoOfflineSongsActivity"
|
||||||
android:screenOrientation="portrait" />
|
android:screenOrientation="portrait" />
|
||||||
|
<activity
|
||||||
|
android:name=".activity.MoLikedSongsActivity"
|
||||||
|
android:screenOrientation="portrait" />
|
||||||
|
|
||||||
<service
|
<service
|
||||||
android:name=".service.PlaybackService"
|
android:name=".service.PlaybackService"
|
||||||
|
|||||||
@ -19,6 +19,7 @@ import kotlinx.coroutines.CoroutineScope
|
|||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
|
import relax.offline.music.database.AppFavoriteDBManager
|
||||||
import java.io.BufferedReader
|
import java.io.BufferedReader
|
||||||
import java.io.InputStreamReader
|
import java.io.InputStreamReader
|
||||||
|
|
||||||
@ -27,6 +28,8 @@ class App : Application() {
|
|||||||
companion object {
|
companion object {
|
||||||
lateinit var app: App
|
lateinit var app: App
|
||||||
private set
|
private set
|
||||||
|
lateinit var appFavoriteDBManager: AppFavoriteDBManager
|
||||||
|
private set
|
||||||
lateinit var appOfflineDBManager: AppOfflineDBManager
|
lateinit var appOfflineDBManager: AppOfflineDBManager
|
||||||
private set
|
private set
|
||||||
lateinit var currentAudioManager: CurrentAudioManager
|
lateinit var currentAudioManager: CurrentAudioManager
|
||||||
@ -109,6 +112,7 @@ class App : Application() {
|
|||||||
MediaControllerManager.init(this)
|
MediaControllerManager.init(this)
|
||||||
LocalMediaControllerManager.init(this)
|
LocalMediaControllerManager.init(this)
|
||||||
appOfflineDBManager = AppOfflineDBManager.getInstance(this)
|
appOfflineDBManager = AppOfflineDBManager.getInstance(this)
|
||||||
|
appFavoriteDBManager = AppFavoriteDBManager.getInstance(this)
|
||||||
currentAudioManager = CurrentAudioManager.getInstance(this)
|
currentAudioManager = CurrentAudioManager.getInstance(this)
|
||||||
databaseManager = DatabaseManager.getInstance(this)
|
databaseManager = DatabaseManager.getInstance(this)
|
||||||
initCurrentPlayingAudio()
|
initCurrentPlayingAudio()
|
||||||
|
|||||||
@ -36,6 +36,8 @@ import kotlinx.coroutines.NonCancellable
|
|||||||
import kotlinx.coroutines.channels.Channel
|
import kotlinx.coroutines.channels.Channel
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
|
import relax.offline.music.App
|
||||||
|
import relax.offline.music.bean.FavoriteBean
|
||||||
import relax.offline.music.bean.OfflineBean
|
import relax.offline.music.bean.OfflineBean
|
||||||
import relax.offline.music.innertube.Innertube
|
import relax.offline.music.innertube.Innertube
|
||||||
import relax.offline.music.util.FileSizeConverter
|
import relax.offline.music.util.FileSizeConverter
|
||||||
@ -229,17 +231,27 @@ abstract class MoBaseActivity : AppCompatActivity(), CoroutineScope by MainScope
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
fun insertOfflineData(mediaItem: MediaItem) {
|
suspend fun insertOfflineData(mediaItem: MediaItem) {
|
||||||
CoroutineScope(Dispatchers.IO).launch {
|
val bean = OfflineBean(
|
||||||
val bean = OfflineBean(
|
videoId = mediaItem.mediaId,
|
||||||
videoId = mediaItem.mediaId,
|
title = mediaItem.mediaMetadata.title.toString(),
|
||||||
title = mediaItem.mediaMetadata.title.toString(),
|
name = mediaItem.mediaMetadata.artist.toString(),
|
||||||
name = mediaItem.mediaMetadata.artist.toString(),
|
thumbnail = mediaItem.mediaMetadata.artworkUri.toString(),
|
||||||
thumbnail = mediaItem.mediaMetadata.artworkUri.toString(),
|
isOffline = true
|
||||||
isOffline = true
|
)
|
||||||
)
|
LogTag.LogD(Innertube.TAG, "insertOfflineBean bean->${bean}")
|
||||||
LogTag.LogD(Innertube.TAG, "insertOfflineBean bean->${bean}")
|
relax.offline.music.App.appOfflineDBManager.insertOfflineBean(bean)
|
||||||
relax.offline.music.App.appOfflineDBManager.insertOfflineBean(bean)
|
}
|
||||||
}
|
|
||||||
|
suspend fun insertFavoriteData(mediaItem: MediaItem) {
|
||||||
|
val bean = FavoriteBean(
|
||||||
|
videoId = mediaItem.mediaId,
|
||||||
|
title = mediaItem.mediaMetadata.title.toString(),
|
||||||
|
name = mediaItem.mediaMetadata.artist.toString(),
|
||||||
|
thumbnail = mediaItem.mediaMetadata.artworkUri.toString(),
|
||||||
|
isFavorite = true
|
||||||
|
)
|
||||||
|
LogTag.LogD(Innertube.TAG, "insertFavoriteBean bean->${bean}")
|
||||||
|
App.appFavoriteDBManager.insertFavoriteBean(bean)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -0,0 +1,148 @@
|
|||||||
|
package relax.offline.music.activity
|
||||||
|
|
||||||
|
import android.annotation.SuppressLint
|
||||||
|
import android.view.View
|
||||||
|
import androidx.recyclerview.widget.LinearLayoutManager
|
||||||
|
import com.gyf.immersionbar.ktx.immersionBar
|
||||||
|
import kotlinx.coroutines.channels.Channel
|
||||||
|
import kotlinx.coroutines.isActive
|
||||||
|
import kotlinx.coroutines.selects.select
|
||||||
|
import relax.offline.music.App
|
||||||
|
import relax.offline.music.adapter.LikedSongsAdapter
|
||||||
|
import relax.offline.music.bean.FavoriteBean
|
||||||
|
import relax.offline.music.databinding.ActivityLikedSongsBinding
|
||||||
|
|
||||||
|
class MoLikedSongsActivity : MoBaseActivity(), LikedSongsAdapter.OnItemFavoritesClickListener {
|
||||||
|
|
||||||
|
private val requests: Channel<Request> = Channel(Channel.UNLIMITED)
|
||||||
|
|
||||||
|
sealed class Request {
|
||||||
|
data object TryAgain : Request()
|
||||||
|
data class UpdateFavorite(val bean: FavoriteBean) : Request()
|
||||||
|
}
|
||||||
|
|
||||||
|
private lateinit var binding: ActivityLikedSongsBinding
|
||||||
|
private var adapter: LikedSongsAdapter? = null
|
||||||
|
private var favoriteBeans: MutableList<FavoriteBean> = mutableListOf()
|
||||||
|
|
||||||
|
override suspend fun main() {
|
||||||
|
binding = ActivityLikedSongsBinding.inflate(layoutInflater)
|
||||||
|
setContentView(binding.root)
|
||||||
|
initImmersionBar()
|
||||||
|
initView()
|
||||||
|
initAdapter()
|
||||||
|
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) {
|
||||||
|
is Request.TryAgain -> {
|
||||||
|
initData()
|
||||||
|
}
|
||||||
|
|
||||||
|
is Request.UpdateFavorite -> {
|
||||||
|
it.bean.isFavorite = !it.bean.isFavorite
|
||||||
|
adapter?.notifyDataSetChanged()
|
||||||
|
|
||||||
|
val currentFavoriteBean =
|
||||||
|
App.appFavoriteDBManager.getFavoriteBeanByID(it.bean.videoId)
|
||||||
|
if (currentFavoriteBean != null) {
|
||||||
|
App.appFavoriteDBManager.updateFavoriteBean(it.bean)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
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()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun initView() {
|
||||||
|
binding.backBtn.setOnClickListener {
|
||||||
|
finish()
|
||||||
|
}
|
||||||
|
binding.tryAgainBtn.setOnClickListener {
|
||||||
|
requests.trySend(Request.TryAgain)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun initAdapter() {
|
||||||
|
adapter = LikedSongsAdapter(this, favoriteBeans)
|
||||||
|
adapter?.setOnFavoritesItemClickListener(this)
|
||||||
|
binding.rv.layoutManager =
|
||||||
|
LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false)
|
||||||
|
binding.rv.adapter = adapter
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onFavoritesItemClick(position: Int) {
|
||||||
|
requests.trySend(Request.UpdateFavorite(favoriteBeans[position]))
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressLint("NotifyDataSetChanged")
|
||||||
|
private suspend fun initData() {
|
||||||
|
showLoadingUi()
|
||||||
|
favoriteBeans.clear()
|
||||||
|
val beans = App.appFavoriteDBManager.getAllFavoriteBeans()
|
||||||
|
val filteredBeans = beans.filter { it.isFavorite }
|
||||||
|
favoriteBeans.addAll(filteredBeans)
|
||||||
|
if (favoriteBeans.size > 0) {
|
||||||
|
showDataUi()
|
||||||
|
} else {
|
||||||
|
showNoContentUi()
|
||||||
|
}
|
||||||
|
if (adapter != null) {
|
||||||
|
adapter?.notifyDataSetChanged()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -38,14 +38,25 @@ import relax.offline.music.util.PlayMode
|
|||||||
import relax.offline.music.util.asMediaItem
|
import relax.offline.music.util.asMediaItem
|
||||||
import relax.offline.music.util.convertMillisToMinutesAndSecondsString
|
import relax.offline.music.util.convertMillisToMinutesAndSecondsString
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.channels.Channel
|
||||||
import kotlinx.coroutines.isActive
|
import kotlinx.coroutines.isActive
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.coroutines.selects.select
|
import kotlinx.coroutines.selects.select
|
||||||
import relax.offline.music.App
|
import relax.offline.music.App
|
||||||
|
import relax.offline.music.bean.FavoriteBean
|
||||||
|
|
||||||
@OptIn(UnstableApi::class)
|
@OptIn(UnstableApi::class)
|
||||||
class MoPlayDetailsActivity : MoBaseActivity(), Player.Listener {
|
class MoPlayDetailsActivity : MoBaseActivity(), Player.Listener {
|
||||||
|
|
||||||
|
private val requests: Channel<Request> = Channel(Channel.UNLIMITED)
|
||||||
|
|
||||||
|
sealed class Request {
|
||||||
|
data object OnFavorites : Request()
|
||||||
|
data class OnDownload(val mediaItem: MediaItem) : Request()
|
||||||
|
data class UpdateFavorite(val id: String) : Request()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
const val PLAY_DETAILS_VIDEO_ID = "play_details_videoId"
|
const val PLAY_DETAILS_VIDEO_ID = "play_details_videoId"
|
||||||
const val PLAY_DETAILS_PLAY_LIST_ID = "play_details_playlistId"
|
const val PLAY_DETAILS_PLAY_LIST_ID = "play_details_playlistId"
|
||||||
@ -120,6 +131,32 @@ class MoPlayDetailsActivity : MoBaseActivity(), Player.Listener {
|
|||||||
} else {
|
} else {
|
||||||
binding.playbackErrorLayout.visibility = View.VISIBLE
|
binding.playbackErrorLayout.visibility = View.VISIBLE
|
||||||
}
|
}
|
||||||
|
} else if (comeFrom != null && comeFrom == MoLikedSongsActivity::class.java) {
|
||||||
|
LogD(TAG, "从liked songs 进入")
|
||||||
|
binding.nameTv.text = intent.getStringExtra(PLAY_DETAILS_NAME)
|
||||||
|
binding.descTv.text = intent.getStringExtra(PLAY_DETAILS_DESC)
|
||||||
|
val favoriteBeans = App.appFavoriteDBManager.getAllFavoriteBeans()
|
||||||
|
val allFilteredBeans = favoriteBeans.filter { it.isFavorite}//过滤只有为true的值
|
||||||
|
//找到当前点击进来的歌曲media
|
||||||
|
val findCurrentMedia = allFilteredBeans.find { it.videoId == videoId }?.asMediaItem
|
||||||
|
if (findCurrentMedia != null) {
|
||||||
|
binding.likeAndDownloadLayout.visibility = View.VISIBLE
|
||||||
|
updateInfoUi(findCurrentMedia)
|
||||||
|
binding.playbackErrorLayout.visibility = View.GONE
|
||||||
|
binding.totalDurationTv.visibility = View.GONE
|
||||||
|
meController?.let {
|
||||||
|
it.setMediaItem(findCurrentMedia, true)
|
||||||
|
it.prepare()
|
||||||
|
it.play()
|
||||||
|
val mediaItems = allFilteredBeans
|
||||||
|
.map { mapAll -> mapAll.asMediaItem }//转换成MediaItem
|
||||||
|
.filter { filter -> filter.mediaId != videoId }//过滤掉id相等的。
|
||||||
|
it.addMediaItems(mediaItems)
|
||||||
|
}
|
||||||
|
updatePlayListDataAndAdapter()
|
||||||
|
} else {
|
||||||
|
binding.playbackErrorLayout.visibility = View.VISIBLE
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
LogD(TAG, "从点击任意歌曲进入")
|
LogD(TAG, "从点击任意歌曲进入")
|
||||||
binding.nameTv.text = intent.getStringExtra(PLAY_DETAILS_NAME)
|
binding.nameTv.text = intent.getStringExtra(PLAY_DETAILS_NAME)
|
||||||
@ -157,13 +194,52 @@ class MoPlayDetailsActivity : MoBaseActivity(), Player.Listener {
|
|||||||
else -> {}
|
else -> {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
requests.onReceive {
|
||||||
|
when (it) {
|
||||||
|
is Request.OnFavorites -> {
|
||||||
|
if (meController != null && meController.currentMediaItem != null) {
|
||||||
|
val currentMediaItem = meController.currentMediaItem
|
||||||
|
val currentFavoriteBean =
|
||||||
|
App.appFavoriteDBManager.getFavoriteBeanByID(currentMediaItem?.mediaId!!)
|
||||||
|
if (currentFavoriteBean != null) {
|
||||||
|
currentFavoriteBean.isFavorite = !currentFavoriteBean.isFavorite
|
||||||
|
App.appFavoriteDBManager.updateFavoriteBean(currentFavoriteBean)
|
||||||
|
} else {
|
||||||
|
insertFavoriteData(currentMediaItem)
|
||||||
|
}
|
||||||
|
requests.trySend(Request.UpdateFavorite(currentMediaItem.mediaId))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
is Request.OnDownload -> {
|
||||||
|
insertOfflineData(it.mediaItem)
|
||||||
|
}
|
||||||
|
|
||||||
|
is Request.UpdateFavorite -> {
|
||||||
|
val currentFavoriteBean =
|
||||||
|
App.appFavoriteDBManager.getFavoriteBeanByID(it.id)
|
||||||
|
LogD(TAG, "UpdateFavorite->${currentFavoriteBean}")
|
||||||
|
if (currentFavoriteBean != null) {
|
||||||
|
updateFavoriteUi(currentFavoriteBean.isFavorite)
|
||||||
|
} else {
|
||||||
|
updateFavoriteUi(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun activityOnResume() {
|
private suspend fun activityOnResume() {
|
||||||
|
//更新收藏按钮状态
|
||||||
// if (meController != null && meController.currentMediaItem != null) {
|
// if (meController != null && meController.currentMediaItem != null) {
|
||||||
// updateInfoUi(meController.currentMediaItem)
|
// val currentMediaItem = meController.currentMediaItem
|
||||||
|
// val currentFavoriteBean =
|
||||||
|
// App.appFavoriteDBManager.getFavoriteBeanByID(currentMediaItem?.mediaId!!)
|
||||||
|
// if (currentFavoriteBean != null) {
|
||||||
|
// updateFavoriteUi(currentFavoriteBean.isFavorite)
|
||||||
|
// }
|
||||||
// }
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -236,6 +312,13 @@ class MoPlayDetailsActivity : MoBaseActivity(), Player.Listener {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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 initPlayerListener() {
|
private fun initPlayerListener() {
|
||||||
meController?.addListener(playerListener)
|
meController?.addListener(playerListener)
|
||||||
@ -401,7 +484,9 @@ class MoPlayDetailsActivity : MoBaseActivity(), Player.Listener {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
binding.favoritesBtn.setOnClickListener {
|
||||||
|
requests.trySend(Request.OnFavorites)
|
||||||
|
}
|
||||||
binding.downloadBtn.setOnClickListener {
|
binding.downloadBtn.setOnClickListener {
|
||||||
if (meController != null && meController.currentMediaItem != null) {
|
if (meController != null && meController.currentMediaItem != null) {
|
||||||
val currentMediaItem = meController.currentMediaItem
|
val currentMediaItem = meController.currentMediaItem
|
||||||
@ -411,8 +496,6 @@ class MoPlayDetailsActivity : MoBaseActivity(), Player.Listener {
|
|||||||
return@setOnClickListener
|
return@setOnClickListener
|
||||||
}
|
}
|
||||||
|
|
||||||
insertOfflineData(currentMediaItem!!)
|
|
||||||
|
|
||||||
val downloadRequest = DownloadRequest
|
val downloadRequest = DownloadRequest
|
||||||
.Builder(contentId, contentId.toUri())
|
.Builder(contentId, contentId.toUri())
|
||||||
.setCustomCacheKey(contentId)
|
.setCustomCacheKey(contentId)
|
||||||
@ -423,8 +506,9 @@ class MoPlayDetailsActivity : MoBaseActivity(), Player.Listener {
|
|||||||
downloadRequest,
|
downloadRequest,
|
||||||
false
|
false
|
||||||
)
|
)
|
||||||
}
|
|
||||||
|
|
||||||
|
requests.trySend(Request.OnDownload(currentMediaItem!!))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -610,6 +694,7 @@ class MoPlayDetailsActivity : MoBaseActivity(), Player.Listener {
|
|||||||
}
|
}
|
||||||
// currentVideoID = mediaItem.mediaId
|
// currentVideoID = mediaItem.mediaId
|
||||||
updateDownloadUi(mediaItem.mediaId)
|
updateDownloadUi(mediaItem.mediaId)
|
||||||
|
requests.trySend(Request.UpdateFavorite(mediaItem.mediaId))//更新喜欢状态
|
||||||
|
|
||||||
Glide.with(this)
|
Glide.with(this)
|
||||||
.asBitmap()
|
.asBitmap()
|
||||||
|
|||||||
@ -0,0 +1,110 @@
|
|||||||
|
package relax.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 relax.offline.music.R
|
||||||
|
import relax.offline.music.activity.MoLikedSongsActivity
|
||||||
|
import relax.offline.music.activity.MoPlayDetailsActivity
|
||||||
|
import relax.offline.music.bean.FavoriteBean
|
||||||
|
import relax.offline.music.databinding.LikedListItemBinding
|
||||||
|
import relax.offline.music.media.MediaControllerManager
|
||||||
|
|
||||||
|
class LikedSongsAdapter(
|
||||||
|
private val context: Context,
|
||||||
|
private val list: List<FavoriteBean>,
|
||||||
|
) :
|
||||||
|
RecyclerView.Adapter<LikedSongsAdapter.ViewHolder>() {
|
||||||
|
|
||||||
|
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
|
||||||
|
val binding = LikedListItemBinding.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 {
|
||||||
|
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,
|
||||||
|
MoLikedSongsActivity::class.java
|
||||||
|
)
|
||||||
|
context.startActivity(intent)
|
||||||
|
}
|
||||||
|
holder.binding.favoritesBtn.setOnClickListener {
|
||||||
|
if (itemFavoritesClickListener != null) {
|
||||||
|
itemFavoritesClickListener?.onFavoritesItemClick(position)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getItemCount(): Int = list.size
|
||||||
|
|
||||||
|
inner class ViewHolder(val binding: LikedListItemBinding) :
|
||||||
|
RecyclerView.ViewHolder(binding.root) {
|
||||||
|
|
||||||
|
@SuppressLint("SetTextI18n")
|
||||||
|
fun bind(bean: FavoriteBean) {
|
||||||
|
|
||||||
|
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.isFavorite) {
|
||||||
|
binding.favoritesImg.setImageResource(R.drawable.favorited_icon)
|
||||||
|
} else {
|
||||||
|
binding.favoritesImg.setImageResource(R.drawable.not_favorited_icon)
|
||||||
|
}
|
||||||
|
val meController = MediaControllerManager.getController()
|
||||||
|
if (meController != null && meController.currentMediaItem != null) {
|
||||||
|
if (meController.currentMediaItem?.mediaId == bean.videoId) {
|
||||||
|
binding.listPlayView.visibility = View.VISIBLE
|
||||||
|
binding.title.setTextColor(context.getColor(R.color.green))
|
||||||
|
binding.name.setTextColor(context.getColor(R.color.green_60))
|
||||||
|
} else {
|
||||||
|
binding.title.setTextColor(context.getColor(R.color.white))
|
||||||
|
binding.name.setTextColor(context.getColor(R.color.white_60))
|
||||||
|
binding.listPlayView.visibility = View.GONE
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private var itemClickListener: OnItemClickListener? = null
|
||||||
|
|
||||||
|
fun setOnItemClickListener(listener: OnItemClickListener) {
|
||||||
|
itemClickListener = listener
|
||||||
|
}
|
||||||
|
|
||||||
|
interface OnItemClickListener {
|
||||||
|
fun onItemClick(position: Int)
|
||||||
|
}
|
||||||
|
|
||||||
|
private var itemFavoritesClickListener: OnItemFavoritesClickListener? = null
|
||||||
|
|
||||||
|
fun setOnFavoritesItemClickListener(listener: OnItemFavoritesClickListener) {
|
||||||
|
itemFavoritesClickListener = listener
|
||||||
|
}
|
||||||
|
|
||||||
|
interface OnItemFavoritesClickListener {
|
||||||
|
fun onFavoritesItemClick(position: Int)
|
||||||
|
}
|
||||||
|
}
|
||||||
20
app/src/main/java/relax/offline/music/bean/FavoriteBean.kt
Normal file
20
app/src/main/java/relax/offline/music/bean/FavoriteBean.kt
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
package relax.offline.music.bean
|
||||||
|
|
||||||
|
import androidx.annotation.Keep
|
||||||
|
import androidx.room.ColumnInfo
|
||||||
|
import androidx.room.Entity
|
||||||
|
import androidx.room.PrimaryKey
|
||||||
|
import java.io.Serializable
|
||||||
|
|
||||||
|
@Keep
|
||||||
|
@Entity
|
||||||
|
data class FavoriteBean(
|
||||||
|
@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 = "isFavorite") var isFavorite: Boolean
|
||||||
|
) : Serializable {
|
||||||
|
@PrimaryKey(autoGenerate = true)
|
||||||
|
var id: Long = 0
|
||||||
|
}
|
||||||
@ -0,0 +1,81 @@
|
|||||||
|
package relax.offline.music.database
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import androidx.room.Room
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.withContext
|
||||||
|
import relax.offline.music.bean.FavoriteBean
|
||||||
|
import relax.offline.music.util.LogTag
|
||||||
|
|
||||||
|
class AppFavoriteDBManager private constructor(context: Context) {
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
@Volatile
|
||||||
|
private var instance: AppFavoriteDBManager? = null
|
||||||
|
|
||||||
|
fun getInstance(context: Context): AppFavoriteDBManager {
|
||||||
|
return instance ?: synchronized(this) {
|
||||||
|
instance ?: AppFavoriteDBManager(context).also { instance = it }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private val database = Room.databaseBuilder(
|
||||||
|
context.applicationContext,
|
||||||
|
AppFavoriteDatabase::class.java, "favorite_data_base"
|
||||||
|
).build()
|
||||||
|
|
||||||
|
private val dao = database.localFavoriteDao()
|
||||||
|
|
||||||
|
suspend fun insertFavoriteBean(bean: FavoriteBean) {
|
||||||
|
withContext(Dispatchers.IO) {
|
||||||
|
val offlineBean = getFavoriteBeanByID(bean.videoId)
|
||||||
|
if (offlineBean == null) {
|
||||||
|
dao.insertFavoriteBean(bean)
|
||||||
|
} else {
|
||||||
|
dao.updateFavoriteBean(bean)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun insertOfflineListBean(list: List<FavoriteBean>) {
|
||||||
|
withContext(Dispatchers.IO) {
|
||||||
|
for (bean in list) {
|
||||||
|
val offlineBean = getFavoriteBeanByID(bean.videoId)
|
||||||
|
if (offlineBean == null) {
|
||||||
|
dao.insertFavoriteBean(bean)
|
||||||
|
} else {
|
||||||
|
dao.updateFavoriteBean(bean)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun getAllFavoriteBeans(): List<FavoriteBean> {
|
||||||
|
return withContext(Dispatchers.IO) {
|
||||||
|
dao.getAllFavoriteBeans()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun deleteFavoriteBean(bean: FavoriteBean) {
|
||||||
|
withContext(Dispatchers.IO) {
|
||||||
|
dao.deleteFavoriteBean(bean)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun deleteAllFavoriteBean() {
|
||||||
|
withContext(Dispatchers.IO) {
|
||||||
|
dao.deleteAllFavoriteBean()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun updateFavoriteBean(bean: FavoriteBean) {
|
||||||
|
withContext(Dispatchers.IO) {
|
||||||
|
dao.updateFavoriteBean(bean)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun getFavoriteBeanByID(id: String): FavoriteBean? {
|
||||||
|
return dao.getFavoriteBeanByID(id)
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,10 @@
|
|||||||
|
package relax.offline.music.database
|
||||||
|
|
||||||
|
import androidx.room.Database
|
||||||
|
import androidx.room.RoomDatabase
|
||||||
|
import relax.offline.music.bean.FavoriteBean
|
||||||
|
|
||||||
|
@Database(entities = [FavoriteBean::class], version = 1, exportSchema = false)
|
||||||
|
abstract class AppFavoriteDatabase : RoomDatabase() {
|
||||||
|
abstract fun localFavoriteDao(): FavoriteDao
|
||||||
|
}
|
||||||
@ -0,0 +1,30 @@
|
|||||||
|
package relax.offline.music.database
|
||||||
|
|
||||||
|
import androidx.room.*
|
||||||
|
import relax.offline.music.bean.FavoriteBean
|
||||||
|
|
||||||
|
@Dao
|
||||||
|
interface FavoriteDao {
|
||||||
|
|
||||||
|
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||||
|
suspend fun insertFavoriteBean(bean: FavoriteBean)
|
||||||
|
|
||||||
|
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||||
|
suspend fun insertFavoriteBeans(beans: List<FavoriteBean>)
|
||||||
|
|
||||||
|
@Query("SELECT * FROM FavoriteBean")
|
||||||
|
suspend fun getAllFavoriteBeans(): List<FavoriteBean>
|
||||||
|
|
||||||
|
@Delete
|
||||||
|
suspend fun deleteFavoriteBean(bean: FavoriteBean)
|
||||||
|
|
||||||
|
@Query("DELETE FROM FavoriteBean")
|
||||||
|
suspend fun deleteAllFavoriteBean()
|
||||||
|
|
||||||
|
@Update
|
||||||
|
suspend fun updateFavoriteBean(bean: FavoriteBean)
|
||||||
|
|
||||||
|
@Query("SELECT * FROM FavoriteBean WHERE videoId = :id LIMIT 1")
|
||||||
|
suspend fun getFavoriteBeanByID(id: String): FavoriteBean?
|
||||||
|
|
||||||
|
}
|
||||||
@ -5,14 +5,14 @@ import android.view.LayoutInflater
|
|||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
import com.gyf.immersionbar.ktx.immersionBar
|
import com.gyf.immersionbar.ktx.immersionBar
|
||||||
import relax.offline.music.R
|
|
||||||
import relax.offline.music.activity.MoOfflineSongsActivity
|
|
||||||
import relax.offline.music.databinding.FragmentMoMeBinding
|
|
||||||
import relax.offline.music.util.DownloadUtil
|
|
||||||
import kotlinx.coroutines.channels.Channel
|
import kotlinx.coroutines.channels.Channel
|
||||||
import kotlinx.coroutines.isActive
|
import kotlinx.coroutines.isActive
|
||||||
import kotlinx.coroutines.selects.select
|
import kotlinx.coroutines.selects.select
|
||||||
import relax.offline.music.App
|
import relax.offline.music.App
|
||||||
|
import relax.offline.music.R
|
||||||
|
import relax.offline.music.activity.MoLikedSongsActivity
|
||||||
|
import relax.offline.music.activity.MoOfflineSongsActivity
|
||||||
|
import relax.offline.music.databinding.FragmentMoMeBinding
|
||||||
|
|
||||||
class MoMeFragment : MoBaseFragment<FragmentMoMeBinding>() {
|
class MoMeFragment : MoBaseFragment<FragmentMoMeBinding>() {
|
||||||
|
|
||||||
@ -57,7 +57,17 @@ class MoMeFragment : MoBaseFragment<FragmentMoMeBinding>() {
|
|||||||
|
|
||||||
private fun initView() {
|
private fun initView() {
|
||||||
binding.likedSongsBtn.setOnClickListener {
|
binding.likedSongsBtn.setOnClickListener {
|
||||||
|
val count = binding.likedSongsTv.text.toString().trim().toInt()
|
||||||
|
if (count > 0) {
|
||||||
|
val intent = Intent(context, MoLikedSongsActivity::class.java)
|
||||||
|
startActivity(intent)
|
||||||
|
} else {
|
||||||
|
Toast.makeText(
|
||||||
|
activity,
|
||||||
|
getString(R.string.liked_songs_no_data_prompt),
|
||||||
|
Toast.LENGTH_LONG
|
||||||
|
).show()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
binding.offlineSongsBtn.setOnClickListener {
|
binding.offlineSongsBtn.setOnClickListener {
|
||||||
val count = binding.offlineSongsTv.text.toString().trim().toInt()
|
val count = binding.offlineSongsTv.text.toString().trim().toInt()
|
||||||
@ -79,6 +89,11 @@ class MoMeFragment : MoBaseFragment<FragmentMoMeBinding>() {
|
|||||||
val offlineBeans = App.appOfflineDBManager.getAllOfflineBeans()
|
val offlineBeans = App.appOfflineDBManager.getAllOfflineBeans()
|
||||||
val count = offlineBeans.count { it.bytesDownloaded?.let { bytes -> bytes > 0 } == true }
|
val count = offlineBeans.count { it.bytesDownloaded?.let { bytes -> bytes > 0 } == true }
|
||||||
binding.offlineSongsTv.text = "$count"
|
binding.offlineSongsTv.text = "$count"
|
||||||
|
|
||||||
|
//过滤只有为true的才计数
|
||||||
|
val favoriteBeans = App.appFavoriteDBManager.getAllFavoriteBeans()
|
||||||
|
val favorites = favoriteBeans.count { it.isFavorite }
|
||||||
|
binding.likedSongsTv.text = "$favorites"
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onResume() {
|
override fun onResume() {
|
||||||
|
|||||||
@ -9,7 +9,6 @@ import com.google.common.util.concurrent.ListenableFuture
|
|||||||
import com.google.common.util.concurrent.MoreExecutors
|
import com.google.common.util.concurrent.MoreExecutors
|
||||||
import relax.offline.music.service.PlaybackService
|
import relax.offline.music.service.PlaybackService
|
||||||
|
|
||||||
@UnstableApi
|
|
||||||
object MediaControllerManager {
|
object MediaControllerManager {
|
||||||
private var mediaController: MediaController? = null
|
private var mediaController: MediaController? = null
|
||||||
private var controllerFuture: ListenableFuture<MediaController>? = null
|
private var controllerFuture: ListenableFuture<MediaController>? = null
|
||||||
|
|||||||
@ -3,6 +3,7 @@ package relax.offline.music.service
|
|||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import android.os.Handler
|
import android.os.Handler
|
||||||
|
import androidx.annotation.OptIn
|
||||||
import androidx.core.net.toUri
|
import androidx.core.net.toUri
|
||||||
import androidx.media3.common.AudioAttributes
|
import androidx.media3.common.AudioAttributes
|
||||||
import androidx.media3.common.C
|
import androidx.media3.common.C
|
||||||
@ -42,8 +43,7 @@ import kotlinx.coroutines.Dispatchers
|
|||||||
import kotlinx.coroutines.runBlocking
|
import kotlinx.coroutines.runBlocking
|
||||||
import relax.offline.music.util.RingBuffer
|
import relax.offline.music.util.RingBuffer
|
||||||
|
|
||||||
|
@OptIn(UnstableApi::class)
|
||||||
@UnstableApi
|
|
||||||
class PlaybackService : MediaSessionService(), Player.Listener {
|
class PlaybackService : MediaSessionService(), Player.Listener {
|
||||||
private val TAG = LogTag.VO_SERVICE_LOG
|
private val TAG = LogTag.VO_SERVICE_LOG
|
||||||
private var mediaSession: MediaSession? = null
|
private var mediaSession: MediaSession? = null
|
||||||
|
|||||||
@ -9,6 +9,7 @@ import androidx.core.os.bundleOf
|
|||||||
import androidx.media3.common.MediaItem
|
import androidx.media3.common.MediaItem
|
||||||
import androidx.media3.common.MediaMetadata
|
import androidx.media3.common.MediaMetadata
|
||||||
import androidx.media3.common.util.UnstableApi
|
import androidx.media3.common.util.UnstableApi
|
||||||
|
import relax.offline.music.bean.FavoriteBean
|
||||||
import relax.offline.music.bean.OfflineBean
|
import relax.offline.music.bean.OfflineBean
|
||||||
import relax.offline.music.innertube.Innertube
|
import relax.offline.music.innertube.Innertube
|
||||||
import relax.offline.music.innertube.models.bodies.ContinuationBody
|
import relax.offline.music.innertube.models.bodies.ContinuationBody
|
||||||
@ -30,6 +31,21 @@ val OfflineBean.asMediaItem: MediaItem
|
|||||||
)
|
)
|
||||||
.build()
|
.build()
|
||||||
|
|
||||||
|
val FavoriteBean.asMediaItem: MediaItem
|
||||||
|
@OptIn(UnstableApi::class)
|
||||||
|
get() = MediaItem.Builder()
|
||||||
|
.setMediaId(videoId)
|
||||||
|
.setUri(videoId)
|
||||||
|
.setCustomCacheKey(videoId)
|
||||||
|
.setMediaMetadata(
|
||||||
|
MediaMetadata.Builder()
|
||||||
|
.setTitle(title)
|
||||||
|
.setArtist(name)
|
||||||
|
.setArtworkUri(thumbnail?.toUri())
|
||||||
|
.build()
|
||||||
|
)
|
||||||
|
.build()
|
||||||
|
|
||||||
val Innertube.SongItem.asMediaItem: MediaItem
|
val Innertube.SongItem.asMediaItem: MediaItem
|
||||||
@OptIn(UnstableApi::class)
|
@OptIn(UnstableApi::class)
|
||||||
get() = MediaItem.Builder()
|
get() = MediaItem.Builder()
|
||||||
|
|||||||
145
app/src/main/res/layout/activity_liked_songs.xml
Normal file
145
app/src/main/res/layout/activity_liked_songs.xml
Normal file
@ -0,0 +1,145 @@
|
|||||||
|
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
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_margin="16dp"
|
||||||
|
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:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:layout_marginEnd="58dp"
|
||||||
|
android:fontFamily="@font/medium_font"
|
||||||
|
android:gravity="center"
|
||||||
|
android:text="@string/liked_songs"
|
||||||
|
android:textColor="@color/white"
|
||||||
|
android:textSize="18dp" />
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<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">
|
||||||
|
|
||||||
|
<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>
|
||||||
|
|
||||||
|
</RelativeLayout>
|
||||||
117
app/src/main/res/layout/liked_list_item.xml
Normal file
117
app/src/main/res/layout/liked_list_item.xml
Normal file
@ -0,0 +1,117 @@
|
|||||||
|
<?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="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:scaleType="centerCrop"
|
||||||
|
android:src="@mipmap/app_logo" />
|
||||||
|
|
||||||
|
<RelativeLayout
|
||||||
|
android:id="@+id/listPlayView"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:background="@color/black_60"
|
||||||
|
android:visibility="gone">
|
||||||
|
|
||||||
|
<relax.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="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:gravity="center_vertical"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<relax.offline.music.view.MarqueeTextView
|
||||||
|
android:id="@+id/title"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:fontFamily="@font/medium_font"
|
||||||
|
android:maxLines="1"
|
||||||
|
android:text="@string/app_name"
|
||||||
|
android:textColor="@color/white"
|
||||||
|
android:textSize="14dp" />
|
||||||
|
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="4dp"
|
||||||
|
android:gravity="center_vertical"
|
||||||
|
android:orientation="horizontal">
|
||||||
|
|
||||||
|
<relax.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
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="horizontal">
|
||||||
|
|
||||||
|
<RelativeLayout
|
||||||
|
android:id="@+id/favoritesBtn"
|
||||||
|
android:layout_width="40dp"
|
||||||
|
android:layout_height="40dp"
|
||||||
|
android:gravity="center">
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/favoritesImg"
|
||||||
|
android:layout_width="24dp"
|
||||||
|
android:layout_height="24dp"
|
||||||
|
android:src="@drawable/not_favorited_icon" />
|
||||||
|
</RelativeLayout>
|
||||||
|
</LinearLayout>
|
||||||
|
</LinearLayout>
|
||||||
|
</LinearLayout>
|
||||||
Loading…
Reference in New Issue
Block a user