update
This commit is contained in:
parent
c90b8a6930
commit
1c741d22ff
@ -82,4 +82,7 @@ dependencies {
|
||||
implementation("io.ktor:ktor-client-serialization:2.1.2")
|
||||
implementation("io.ktor:ktor-serialization-kotlinx-json:2.3.8")
|
||||
implementation("org.brotli:dec:0.1.2")
|
||||
|
||||
implementation("com.google.android.flexbox:flexbox:3.0.0")
|
||||
|
||||
}
|
||||
@ -56,6 +56,9 @@
|
||||
<activity
|
||||
android:name=".activity.MoPlayDetailsActivity"
|
||||
android:screenOrientation="portrait" />
|
||||
<activity
|
||||
android:name=".activity.MoSingerDetailsActivity"
|
||||
android:screenOrientation="portrait" />
|
||||
|
||||
<service
|
||||
android:name=".service.PlaybackService"
|
||||
|
||||
@ -6,30 +6,41 @@ import com.bumptech.glide.Glide
|
||||
import com.gyf.immersionbar.ktx.immersionBar
|
||||
import com.player.musicoo.adapter.DetailsListAdapter
|
||||
import com.player.musicoo.databinding.ActivityDetailsBinding
|
||||
import com.player.musicoo.fragment.MoHomeFragment
|
||||
import com.player.musicoo.innertube.Innertube
|
||||
import com.player.musicoo.innertube.requests.moPlaylistPage
|
||||
import com.player.musicoo.util.LogTag.LogD
|
||||
import kotlinx.coroutines.channels.Channel
|
||||
import kotlinx.coroutines.isActive
|
||||
import kotlinx.coroutines.selects.select
|
||||
|
||||
class MoListDetailsActivity : MoBaseActivity() {
|
||||
private val requests: Channel<Request> = Channel(Channel.UNLIMITED)
|
||||
|
||||
enum class Request {
|
||||
TryAgain,
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val PLAY_LIST_PAGE_BROWSE_ID = "play_list_page_browse_id"
|
||||
}
|
||||
|
||||
private lateinit var binding: ActivityDetailsBinding
|
||||
private var browseId: String? = null
|
||||
|
||||
override suspend fun main() {
|
||||
binding = ActivityDetailsBinding.inflate(layoutInflater)
|
||||
setContentView(binding.root)
|
||||
initImmersionBar()
|
||||
val browseId = intent.getStringExtra(PLAY_LIST_PAGE_BROWSE_ID)
|
||||
browseId = intent.getStringExtra(PLAY_LIST_PAGE_BROWSE_ID)
|
||||
if (browseId.isNullOrEmpty() || browseId == "null") {
|
||||
finish()
|
||||
return
|
||||
}
|
||||
initView()
|
||||
LogD(TAG, "browseId->${browseId}")
|
||||
initData(browseId)
|
||||
initData(browseId!!)
|
||||
onReceive()
|
||||
}
|
||||
|
||||
private fun initImmersionBar() {
|
||||
@ -39,10 +50,27 @@ class MoListDetailsActivity : MoBaseActivity() {
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun onReceive() {
|
||||
while (isActive) {
|
||||
select<Unit> {
|
||||
requests.onReceive {
|
||||
when (it) {
|
||||
Request.TryAgain -> {
|
||||
initData(browseId!!)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun initView() {
|
||||
binding.backBtn.setOnClickListener {
|
||||
finish()
|
||||
}
|
||||
binding.tryAgainBtn.setOnClickListener {
|
||||
requests.trySend(Request.TryAgain)
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun initData(browseId: String) {
|
||||
|
||||
@ -0,0 +1,129 @@
|
||||
package com.player.musicoo.activity
|
||||
|
||||
import android.view.View
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import com.bumptech.glide.Glide
|
||||
import com.gyf.immersionbar.ktx.immersionBar
|
||||
import com.player.musicoo.adapter.DetailsListAdapter
|
||||
import com.player.musicoo.databinding.ActivityDetailsBinding
|
||||
import com.player.musicoo.databinding.ActivitySingerDetailsBinding
|
||||
import com.player.musicoo.fragment.MoHomeFragment
|
||||
import com.player.musicoo.innertube.Innertube
|
||||
import com.player.musicoo.innertube.requests.moPlaylistPage
|
||||
import com.player.musicoo.innertube.requests.moSingerListPage
|
||||
import com.player.musicoo.util.LogTag.LogD
|
||||
import com.player.musicoo.view.SingerDetailsOtherView
|
||||
import com.player.musicoo.view.SingerDetailsSongView
|
||||
import kotlinx.coroutines.channels.Channel
|
||||
import kotlinx.coroutines.isActive
|
||||
import kotlinx.coroutines.selects.select
|
||||
|
||||
class MoSingerDetailsActivity : MoBaseActivity() {
|
||||
private val requests: Channel<Request> = Channel(Channel.UNLIMITED)
|
||||
|
||||
enum class Request {
|
||||
TryAgain,
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val SINGER_DETAILS_PAGE_BROWSE_ID = "singer_details_page_browse_id"
|
||||
}
|
||||
|
||||
private lateinit var binding: ActivitySingerDetailsBinding
|
||||
private var browseId: String? = null
|
||||
|
||||
override suspend fun main() {
|
||||
binding = ActivitySingerDetailsBinding.inflate(layoutInflater)
|
||||
setContentView(binding.root)
|
||||
initImmersionBar()
|
||||
browseId = intent.getStringExtra(SINGER_DETAILS_PAGE_BROWSE_ID)
|
||||
if (browseId.isNullOrEmpty() || browseId == "null") {
|
||||
finish()
|
||||
return
|
||||
}
|
||||
initView()
|
||||
LogD(TAG, "browseId->${browseId}")
|
||||
initData(browseId!!)
|
||||
onReceive()
|
||||
}
|
||||
|
||||
private fun initImmersionBar() {
|
||||
immersionBar {
|
||||
statusBarDarkFont(false)
|
||||
statusBarView(binding.view)
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun onReceive() {
|
||||
while (isActive) {
|
||||
select<Unit> {
|
||||
requests.onReceive {
|
||||
when (it) {
|
||||
Request.TryAgain -> {
|
||||
initData(browseId!!)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun initView() {
|
||||
binding.backBtn.setOnClickListener {
|
||||
finish()
|
||||
}
|
||||
binding.tryAgainBtn.setOnClickListener {
|
||||
requests.trySend(Request.TryAgain)
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun initData(browseId: String) {
|
||||
showLoadingUi()
|
||||
Innertube.moSingerListPage(browseId)
|
||||
?.onSuccess {
|
||||
showDataUi()
|
||||
Glide.with(this)
|
||||
.load(it.thumbnail)
|
||||
.into(binding.singerImg)
|
||||
binding.singerName.text = it.title
|
||||
binding.singerDesc.text = it.description
|
||||
if (it.list != null) {
|
||||
for (bean: Innertube.SingerDetailsListPage in it.list) {
|
||||
if (bean.contents?.musicShelfContentList != null && bean.contents.musicShelfContentList.isNotEmpty()) {
|
||||
binding.contentLayout.addView(
|
||||
SingerDetailsSongView(
|
||||
this@MoSingerDetailsActivity,
|
||||
bean
|
||||
)
|
||||
)
|
||||
} else if (bean.contents?.musicCarouselShelfContentList != null && bean.contents.musicCarouselShelfContentList.isNotEmpty()) {
|
||||
binding.contentLayout.addView(
|
||||
SingerDetailsOtherView(
|
||||
this@MoSingerDetailsActivity,
|
||||
bean
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}?.onFailure {
|
||||
showNoContentUi()
|
||||
LogD(TAG, "moSingerListPage onFailure->${it}")
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
@ -15,12 +15,13 @@ import com.player.musicoo.R
|
||||
import com.player.musicoo.databinding.ActivityPrimaryBinding
|
||||
import com.player.musicoo.fragment.ImportFragment
|
||||
import com.player.musicoo.fragment.MoHomeFragment
|
||||
import com.player.musicoo.fragment.SearchFragment
|
||||
import com.player.musicoo.media.MediaControllerManager
|
||||
import com.player.musicoo.util.LogTag.LogD
|
||||
import kotlinx.coroutines.isActive
|
||||
import kotlinx.coroutines.selects.select
|
||||
|
||||
class PrimaryActivity : MoBaseActivity() {
|
||||
class PrimaryActivity : MoBaseActivity() ,SearchFragment.SearchFragmentCancelClickListener{
|
||||
/**
|
||||
* musicResponsiveListItemRenderer
|
||||
* musicTwoRowItemRenderer
|
||||
@ -47,12 +48,16 @@ class PrimaryActivity : MoBaseActivity() {
|
||||
|
||||
private fun initClick() {
|
||||
binding.homeBtn.setOnClickListener {
|
||||
changeFragment(0)
|
||||
updateBtnState(0)
|
||||
changeFragment(0)
|
||||
}
|
||||
binding.searchBtn.setOnClickListener {
|
||||
updateBtnState(1)
|
||||
changeFragment(1)
|
||||
}
|
||||
binding.importBtn.setOnClickListener {
|
||||
changeFragment(1)
|
||||
updateBtnState(1)
|
||||
updateBtnState(2)
|
||||
changeFragment(2)
|
||||
}
|
||||
|
||||
binding.playBlackBtn.setOnClickListener {
|
||||
@ -81,9 +86,12 @@ class PrimaryActivity : MoBaseActivity() {
|
||||
private fun initFragment() {
|
||||
mFragments.clear()
|
||||
mFragments.add(MoHomeFragment())
|
||||
val searchFragment = SearchFragment()
|
||||
searchFragment.setButtonClickListener(this)
|
||||
mFragments.add(searchFragment)
|
||||
mFragments.add(ImportFragment())
|
||||
changeFragment(0)
|
||||
updateBtnState(0)
|
||||
changeFragment(0)
|
||||
}
|
||||
|
||||
private fun changeFragment(index: Int) {
|
||||
@ -116,9 +124,22 @@ class PrimaryActivity : MoBaseActivity() {
|
||||
else -> R.drawable.home_unselect_icon
|
||||
}
|
||||
)
|
||||
searchImg.setImageResource(
|
||||
when (index) {
|
||||
1 -> {
|
||||
binding.tabLayout.visibility = View.GONE
|
||||
R.drawable.search_select_icon
|
||||
}
|
||||
|
||||
else -> {
|
||||
binding.tabLayout.visibility = View.VISIBLE
|
||||
R.drawable.search_unselect_icon
|
||||
}
|
||||
}
|
||||
)
|
||||
importImg.setImageResource(
|
||||
when (index) {
|
||||
1 -> R.drawable.import_select_icon
|
||||
2 -> R.drawable.import_select_icon
|
||||
else -> R.drawable.import_unselect_icon
|
||||
}
|
||||
)
|
||||
@ -204,14 +225,19 @@ class PrimaryActivity : MoBaseActivity() {
|
||||
}
|
||||
|
||||
override fun onBackPressed() {
|
||||
if (backPressedTime + 2000 > System.currentTimeMillis()) {
|
||||
super.onBackPressed()
|
||||
backToast.cancel()
|
||||
return
|
||||
if (currentIndex == 1) {//等于搜索返回响应先退回home
|
||||
changeFragment(0)
|
||||
updateBtnState(0)
|
||||
} else {
|
||||
backToast.show()
|
||||
if (backPressedTime + 2000 > System.currentTimeMillis()) {
|
||||
super.onBackPressed()
|
||||
backToast.cancel()
|
||||
return
|
||||
} else {
|
||||
backToast.show()
|
||||
}
|
||||
backPressedTime = System.currentTimeMillis()
|
||||
}
|
||||
backPressedTime = System.currentTimeMillis()
|
||||
}
|
||||
|
||||
/**
|
||||
@ -261,4 +287,8 @@ class PrimaryActivity : MoBaseActivity() {
|
||||
binding.name.text = mediaItem.mediaMetadata.title
|
||||
binding.desc.text = mediaItem.mediaMetadata.artist
|
||||
}
|
||||
|
||||
override fun onFragmentClick() {
|
||||
onBackPressed()
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,52 @@
|
||||
package com.player.musicoo.adapter
|
||||
|
||||
import android.content.Context
|
||||
import android.view.LayoutInflater
|
||||
import android.view.ViewGroup
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.player.musicoo.databinding.SearchHistoryAdapterItemBinding
|
||||
|
||||
class SearchHistoryAdapter(
|
||||
private val context: Context,
|
||||
private val list: List<String>,
|
||||
) :
|
||||
RecyclerView.Adapter<SearchHistoryAdapter.ViewHolder>() {
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
|
||||
val binding =
|
||||
SearchHistoryAdapterItemBinding.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?.onHistoryItemClick(position)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun getItemCount(): Int = list.size
|
||||
|
||||
inner class ViewHolder(private val binding: SearchHistoryAdapterItemBinding) :
|
||||
RecyclerView.ViewHolder(binding.root) {
|
||||
|
||||
fun bind(bean: String) {
|
||||
binding.apply {
|
||||
historyName.text = bean
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private var itemClickListener: HistoryOnItemClickListener? = null
|
||||
|
||||
fun setOnItemClickListener(listener: HistoryOnItemClickListener) {
|
||||
itemClickListener = listener
|
||||
}
|
||||
|
||||
interface HistoryOnItemClickListener {
|
||||
fun onHistoryItemClick(position: Int)
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,67 @@
|
||||
package com.player.musicoo.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.media3.common.Player
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.bumptech.glide.Glide
|
||||
import com.player.musicoo.R
|
||||
import com.player.musicoo.databinding.PlayListItemBinding
|
||||
import com.player.musicoo.databinding.SearchResultOtherItemBinding
|
||||
import com.player.musicoo.databinding.SearchResultOtherLayoutBinding
|
||||
import com.player.musicoo.innertube.Innertube
|
||||
import com.player.musicoo.media.MediaControllerManager
|
||||
|
||||
class SearchResultOtherAdapter(
|
||||
private val context: Context,
|
||||
private val list: List<Innertube.SearchDataPage.SearchResult>,
|
||||
) :
|
||||
RecyclerView.Adapter<SearchResultOtherAdapter.ViewHolder>() {
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
|
||||
val binding = SearchResultOtherItemBinding.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 {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
override fun getItemCount(): Int = list.size
|
||||
|
||||
inner class ViewHolder(private val binding: SearchResultOtherItemBinding) :
|
||||
RecyclerView.ViewHolder(binding.root) {
|
||||
|
||||
@SuppressLint("SetTextI18n")
|
||||
fun bind(bean: Innertube.SearchDataPage.SearchResult) {
|
||||
|
||||
binding.apply {
|
||||
Glide.with(context)
|
||||
.load(bean.thumbnail)
|
||||
.into(image)
|
||||
title.text = bean.title
|
||||
name.text = bean.desc
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private var itemClickListener: OnItemClickListener? = null
|
||||
|
||||
fun setOnItemClickListener(listener: OnItemClickListener) {
|
||||
itemClickListener = listener
|
||||
}
|
||||
|
||||
interface OnItemClickListener {
|
||||
fun onItemClick(position: Int)
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,45 @@
|
||||
package com.player.musicoo.adapter
|
||||
|
||||
import android.content.Context
|
||||
import android.view.LayoutInflater
|
||||
import android.view.ViewGroup
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.player.musicoo.databinding.SearchSuggestionsAdapterItemBinding
|
||||
|
||||
class SearchSuggestionsAdapter(
|
||||
private val context: Context,
|
||||
private val list: List<String>,
|
||||
) :
|
||||
RecyclerView.Adapter<SearchSuggestionsAdapter.ViewHolder>() {
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
|
||||
val binding =
|
||||
SearchSuggestionsAdapterItemBinding.inflate(LayoutInflater.from(context), parent, false)
|
||||
return ViewHolder(binding)
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
|
||||
val bean = list[position]
|
||||
holder.binding.text.text = bean
|
||||
holder.itemView.setOnClickListener {
|
||||
if (itemClickListener != null) {
|
||||
itemClickListener?.onItemClick(position)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun getItemCount(): Int = list.size
|
||||
|
||||
inner class ViewHolder(val binding: SearchSuggestionsAdapterItemBinding) :
|
||||
RecyclerView.ViewHolder(binding.root)
|
||||
|
||||
private var itemClickListener: OnItemClickListener? = null
|
||||
|
||||
fun setOnItemClickListener(listener: OnItemClickListener) {
|
||||
itemClickListener = listener
|
||||
}
|
||||
|
||||
interface OnItemClickListener {
|
||||
fun onItemClick(position: Int)
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,137 @@
|
||||
package com.player.musicoo.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 com.player.musicoo.App
|
||||
import com.player.musicoo.R
|
||||
import com.player.musicoo.activity.MoPlayDetailsActivity
|
||||
import com.player.musicoo.activity.PlayDetailsActivity
|
||||
import com.player.musicoo.bean.Audio
|
||||
import com.player.musicoo.databinding.DetailsListItemBinding
|
||||
import com.player.musicoo.databinding.MusicResponsiveItemBinding
|
||||
import com.player.musicoo.databinding.SoundsOfAppliancesLayoutBinding
|
||||
import com.player.musicoo.databinding.SoundsOfNatureLayoutBinding
|
||||
import com.player.musicoo.innertube.Innertube
|
||||
import com.player.musicoo.innertube.models.MusicCarouselShelfRenderer
|
||||
import com.player.musicoo.innertube.models.MusicShelfRenderer
|
||||
import com.player.musicoo.util.convertMillisToMinutesAndSecondsString
|
||||
import com.player.musicoo.util.getAudioDurationFromAssets
|
||||
|
||||
class SingerDetailsSongListAdapter(
|
||||
private val context: Context,
|
||||
private val list: List<MusicShelfRenderer.Content>,
|
||||
) :
|
||||
RecyclerView.Adapter<SingerDetailsSongListAdapter.ViewHolder>() {
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
|
||||
val binding = DetailsListItemBinding.inflate(LayoutInflater.from(context), parent, false)
|
||||
return ViewHolder(binding)
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
|
||||
val bean = list[position]
|
||||
holder.bind(bean)
|
||||
|
||||
val watchEndpoint = bean.musicResponsiveListItemRenderer
|
||||
?.flexColumns
|
||||
?.firstOrNull()
|
||||
?.musicResponsiveListItemFlexColumnRenderer
|
||||
?.text
|
||||
?.runs
|
||||
?.firstOrNull()
|
||||
?.navigationEndpoint
|
||||
?.watchEndpoint
|
||||
val videoId = watchEndpoint?.videoId
|
||||
val playlistId = watchEndpoint?.playlistId
|
||||
val playlistSetVideoId = watchEndpoint?.playlistSetVideoId
|
||||
val params = watchEndpoint?.params
|
||||
val flexColumns = bean.musicResponsiveListItemRenderer
|
||||
?.flexColumns
|
||||
val name = flexColumns?.get(0)?.musicResponsiveListItemFlexColumnRenderer?.text?.text
|
||||
val desc = flexColumns?.get(1)?.musicResponsiveListItemFlexColumnRenderer?.text?.text
|
||||
|
||||
holder.itemView.setOnClickListener {
|
||||
|
||||
val intent = Intent(context, MoPlayDetailsActivity::class.java)
|
||||
intent.putExtra(MoPlayDetailsActivity.PLAY_DETAILS_VIDEO_ID, videoId)
|
||||
intent.putExtra(MoPlayDetailsActivity.PLAY_DETAILS_PLAY_LIST_ID, playlistId)
|
||||
intent.putExtra(
|
||||
MoPlayDetailsActivity.PLAY_DETAILS_PLAY_LIST_SET_VIDEO_ID,
|
||||
playlistSetVideoId
|
||||
)
|
||||
intent.putExtra(MoPlayDetailsActivity.PLAY_DETAILS_PLAY_PARAMS, params)
|
||||
|
||||
intent.putExtra(MoPlayDetailsActivity.PLAY_DETAILS_NAME, name)
|
||||
intent.putExtra(MoPlayDetailsActivity.PLAY_DETAILS_DESC, desc)
|
||||
context.startActivity(intent)
|
||||
}
|
||||
}
|
||||
|
||||
override fun getItemCount(): Int = list.size
|
||||
|
||||
inner class ViewHolder(private val binding: DetailsListItemBinding) :
|
||||
RecyclerView.ViewHolder(binding.root) {
|
||||
|
||||
@SuppressLint("SetTextI18n")
|
||||
fun bind(bean: MusicShelfRenderer.Content) {
|
||||
|
||||
binding.apply {
|
||||
|
||||
val thumbnailUrl = bean.musicResponsiveListItemRenderer
|
||||
?.thumbnail
|
||||
?.musicThumbnailRenderer
|
||||
?.thumbnail
|
||||
?.thumbnails?.let {
|
||||
it.getOrNull(2) ?: it.getOrNull(1) ?: it.getOrNull(0)
|
||||
}?.url
|
||||
|
||||
if (!thumbnailUrl.isNullOrEmpty()) {
|
||||
image.visibility = View.VISIBLE
|
||||
Glide.with(context)
|
||||
.load(thumbnailUrl)
|
||||
.into(image)
|
||||
sortTv.visibility = View.GONE
|
||||
} else {
|
||||
image.visibility = View.GONE
|
||||
sortTv.visibility = View.VISIBLE
|
||||
sortTv.text = "${bindingAdapterPosition + 1}"
|
||||
}
|
||||
|
||||
title.text = bean.musicResponsiveListItemRenderer
|
||||
?.flexColumns
|
||||
?.firstOrNull()
|
||||
?.musicResponsiveListItemFlexColumnRenderer
|
||||
?.text
|
||||
?.text
|
||||
val desc = bean.musicResponsiveListItemRenderer
|
||||
?.flexColumns
|
||||
?.drop(1)
|
||||
?.map { it.musicResponsiveListItemFlexColumnRenderer?.text?.text }
|
||||
?.joinToString(" ")
|
||||
if (desc.isNullOrEmpty()) {
|
||||
name.visibility = View.GONE
|
||||
} else {
|
||||
name.visibility = View.VISIBLE
|
||||
name.text = desc
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private var itemClickListener: OnItemClickListener? = null
|
||||
|
||||
fun setOnItemClickListener(listener: OnItemClickListener) {
|
||||
itemClickListener = listener
|
||||
}
|
||||
|
||||
interface OnItemClickListener {
|
||||
fun onItemClick(position: Int)
|
||||
}
|
||||
}
|
||||
319
app/src/main/java/com/player/musicoo/fragment/SearchFragment.kt
Normal file
319
app/src/main/java/com/player/musicoo/fragment/SearchFragment.kt
Normal file
@ -0,0 +1,319 @@
|
||||
package com.player.musicoo.fragment
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.Context
|
||||
import android.os.Handler
|
||||
import android.os.Looper
|
||||
import android.text.Editable
|
||||
import android.text.TextWatcher
|
||||
import android.view.KeyEvent
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.view.inputmethod.EditorInfo
|
||||
import android.view.inputmethod.InputMethodManager
|
||||
import android.widget.TextView
|
||||
import android.widget.TextView.OnEditorActionListener
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import com.google.android.flexbox.AlignItems
|
||||
import com.google.android.flexbox.FlexWrap
|
||||
import com.google.android.flexbox.FlexboxLayoutManager
|
||||
import com.google.android.flexbox.JustifyContent
|
||||
import com.gyf.immersionbar.ktx.immersionBar
|
||||
import com.player.musicoo.adapter.SearchHistoryAdapter
|
||||
import com.player.musicoo.adapter.SearchSuggestionsAdapter
|
||||
import com.player.musicoo.databinding.FragmentSearchBinding
|
||||
import com.player.musicoo.innertube.Innertube
|
||||
import com.player.musicoo.innertube.models.bodies.SearchSuggestionsBody
|
||||
import com.player.musicoo.innertube.requests.moSearchPage
|
||||
import com.player.musicoo.innertube.requests.searchSuggestions
|
||||
import com.player.musicoo.util.LogTag
|
||||
import com.player.musicoo.view.SearchResultOptimalView
|
||||
import com.player.musicoo.view.SearchResultOtherView
|
||||
import kotlinx.coroutines.channels.Channel
|
||||
import kotlinx.coroutines.isActive
|
||||
import kotlinx.coroutines.selects.select
|
||||
|
||||
class SearchFragment : MoBaseFragment<FragmentSearchBinding>(), TextWatcher,
|
||||
View.OnFocusChangeListener, SearchSuggestionsAdapter.OnItemClickListener,
|
||||
OnEditorActionListener, SearchHistoryAdapter.HistoryOnItemClickListener {
|
||||
|
||||
interface SearchFragmentCancelClickListener {
|
||||
fun onFragmentClick()
|
||||
}
|
||||
|
||||
fun setButtonClickListener(listener: SearchFragmentCancelClickListener) {
|
||||
this.buttonClickListener = listener
|
||||
}
|
||||
|
||||
private var buttonClickListener: SearchFragmentCancelClickListener? = null
|
||||
|
||||
private val requests: Channel<Request> = Channel(Channel.UNLIMITED)
|
||||
|
||||
sealed class Request {
|
||||
data object SearchSuggestions : Request()
|
||||
data class SearchData(val input: String) : Request()
|
||||
}
|
||||
|
||||
private var searchSuggestionsAdapterAdapter: SearchSuggestionsAdapter? = null
|
||||
private var searchSuggestionsList: MutableList<String> = mutableListOf()
|
||||
private var searchHistorySet: MutableSet<String> = mutableSetOf()
|
||||
private var searchHistory: MutableList<String> = mutableListOf()
|
||||
private var searchHistoryAdapter: SearchHistoryAdapter? = null
|
||||
|
||||
override val bindingInflater: (LayoutInflater, ViewGroup?, Boolean) -> FragmentSearchBinding
|
||||
get() = FragmentSearchBinding::inflate
|
||||
|
||||
override suspend fun onViewCreated() {
|
||||
initView()
|
||||
initSearchSuggestionsAdapter()
|
||||
initSearchHistoryAdapter()
|
||||
initImmersionBar()
|
||||
onReceive()
|
||||
}
|
||||
|
||||
private fun initView() {
|
||||
binding.cancelBtn.setOnClickListener {
|
||||
buttonClickListener?.onFragmentClick()
|
||||
}
|
||||
binding.searchEdit.let {
|
||||
it.setOnEditorActionListener(this)
|
||||
it.addTextChangedListener(this)
|
||||
it.onFocusChangeListener = this
|
||||
it.requestFocus()
|
||||
}
|
||||
binding.deleteInputBtn.setOnClickListener {
|
||||
binding.searchEdit.text.clear()
|
||||
}
|
||||
binding.deleteHistoryBtn.setOnClickListener {
|
||||
appStore.searchHistoryStore = emptySet()
|
||||
updateHistoryUi()
|
||||
}
|
||||
}
|
||||
|
||||
private fun initSearchSuggestionsAdapter() {
|
||||
searchSuggestionsAdapterAdapter =
|
||||
SearchSuggestionsAdapter(requireActivity(), searchSuggestionsList)
|
||||
searchSuggestionsAdapterAdapter?.setOnItemClickListener(this)
|
||||
binding.searchSuggestionsRv.layoutManager = LinearLayoutManager(
|
||||
requireActivity(),
|
||||
LinearLayoutManager.VERTICAL,
|
||||
false
|
||||
)
|
||||
binding.searchSuggestionsRv.adapter = searchSuggestionsAdapterAdapter
|
||||
}
|
||||
|
||||
private fun initSearchHistoryAdapter() {
|
||||
searchHistoryAdapter =
|
||||
SearchHistoryAdapter(requireActivity(), searchHistory)
|
||||
searchHistoryAdapter?.setOnItemClickListener(this)
|
||||
val layoutManager = FlexboxLayoutManager(requireActivity())
|
||||
layoutManager.flexWrap = FlexWrap.WRAP // 设置换行方式为自动换行
|
||||
layoutManager.alignItems = AlignItems.STRETCH // 设置项目在副轴上的对齐方式为拉伸
|
||||
layoutManager.justifyContent = JustifyContent.FLEX_START // 设置主轴上的对齐方式为起始端对齐
|
||||
binding.historyRv.layoutManager = layoutManager
|
||||
|
||||
binding.historyRv.adapter = searchHistoryAdapter
|
||||
|
||||
updateHistoryUi()
|
||||
}
|
||||
|
||||
@SuppressLint("NotifyDataSetChanged")
|
||||
private fun updateHistoryUi() {
|
||||
searchHistory.clear()
|
||||
searchHistory.addAll(appStore.searchHistoryStore)
|
||||
searchHistoryAdapter?.notifyDataSetChanged()
|
||||
if (appStore.searchHistoryStore.isNotEmpty()) {
|
||||
binding.historyLayout.visibility = View.VISIBLE
|
||||
} else {
|
||||
binding.historyLayout.visibility = View.GONE
|
||||
}
|
||||
}
|
||||
|
||||
private fun initImmersionBar() {
|
||||
immersionBar {
|
||||
statusBarDarkFont(false)
|
||||
statusBarView(binding.view)
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressLint("NotifyDataSetChanged", "SetTextI18n")
|
||||
private suspend fun onReceive() {
|
||||
while (isActive) {
|
||||
select<Unit> {
|
||||
requests.onReceive {
|
||||
when (it) {
|
||||
is Request.SearchSuggestions -> {
|
||||
val input = binding.searchEdit.text.toString().trim()
|
||||
Innertube.searchSuggestions(SearchSuggestionsBody(input = input))
|
||||
?.onSuccess { suggestionsList ->
|
||||
LogTag.LogD(TAG, "suggestionsList->${suggestionsList?.size}")
|
||||
if (suggestionsList != null) {
|
||||
showSearchSuggestions()
|
||||
searchSuggestionsList.clear()
|
||||
searchSuggestionsList.addAll(suggestionsList)
|
||||
searchSuggestionsAdapterAdapter?.notifyDataSetChanged()
|
||||
} else {
|
||||
binding.searchSuggestionsLayout.visibility = View.GONE
|
||||
}
|
||||
}?.onFailure { error ->
|
||||
LogTag.LogD(TAG, "searchSuggestions onFailure->${error}")
|
||||
binding.searchSuggestionsLayout.visibility = View.GONE
|
||||
}
|
||||
}
|
||||
|
||||
is Request.SearchData -> {
|
||||
binding.contentLayout.removeAllViews()
|
||||
binding.searchEdit.clearFocus()
|
||||
showLoadingLayout()
|
||||
val input = it.input
|
||||
if (input.isNotEmpty()) {
|
||||
searchHistorySet.clear()
|
||||
searchHistorySet.addAll(appStore.searchHistoryStore)
|
||||
if (!appStore.searchHistoryStore.contains(input)) {//把不存在的记录保存到集合中
|
||||
searchHistorySet.add(input)
|
||||
}
|
||||
appStore.searchHistoryStore = searchHistorySet
|
||||
updateHistoryUi()
|
||||
|
||||
Innertube.moSearchPage(input)?.onSuccess { result ->
|
||||
showResultContent()
|
||||
for (dataPage: Innertube.SearchDataPage in result) {
|
||||
if (dataPage.type == 1) {//type为1的是最佳结果。
|
||||
binding.contentLayout.addView(
|
||||
SearchResultOptimalView(requireActivity(), dataPage)
|
||||
)
|
||||
} else if (dataPage.type == 2) {//type为2的是其他搜索结果。
|
||||
if (dataPage.searchResultList.isNotEmpty()) {//如何数据集合为空就不添加view
|
||||
binding.contentLayout.addView(
|
||||
SearchResultOtherView(
|
||||
requireActivity(),
|
||||
dataPage
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}?.onFailure {
|
||||
showNoContentLayout()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onHiddenChanged(hidden: Boolean) {
|
||||
super.onHiddenChanged(hidden)
|
||||
if (!hidden) {
|
||||
binding.searchEdit.requestFocus()
|
||||
} else {
|
||||
binding.searchEdit.clearFocus()
|
||||
binding.searchEdit.text.clear()
|
||||
binding.contentLayout.removeAllViews()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onHistoryItemClick(position: Int) {
|
||||
binding.searchEdit.setText(searchHistory[position])
|
||||
requests.trySend(Request.SearchData(searchHistory[position]))
|
||||
}
|
||||
|
||||
override fun onItemClick(position: Int) {
|
||||
binding.searchEdit.setText(searchSuggestionsList[position])
|
||||
requests.trySend(Request.SearchData(searchSuggestionsList[position]))
|
||||
}
|
||||
|
||||
override fun onEditorAction(v: TextView?, actionId: Int, event: KeyEvent?): Boolean {
|
||||
if (actionId == EditorInfo.IME_ACTION_SEARCH) {
|
||||
requests.trySend(Request.SearchData(binding.searchEdit.text.toString().trim()))
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {}
|
||||
|
||||
@SuppressLint("NotifyDataSetChanged")
|
||||
override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
|
||||
if (s.isNullOrEmpty()) {
|
||||
binding.deleteInputBtn.visibility = View.GONE
|
||||
showSearchHistory()
|
||||
updateHistoryUi()
|
||||
binding.searchEdit.requestFocus()
|
||||
} else {
|
||||
binding.deleteInputBtn.visibility = View.VISIBLE
|
||||
}
|
||||
}
|
||||
|
||||
override fun afterTextChanged(s: Editable?) {
|
||||
requests.trySend(Request.SearchSuggestions)
|
||||
}
|
||||
|
||||
override fun onFocusChange(v: View?, hasFocus: Boolean) {
|
||||
if (hasFocus) {
|
||||
Handler(Looper.getMainLooper()).postDelayed({
|
||||
showImm()
|
||||
}, 200)
|
||||
} else {
|
||||
Handler(Looper.getMainLooper()).postDelayed({
|
||||
hideImm()
|
||||
}, 200)
|
||||
}
|
||||
}
|
||||
|
||||
private fun showImm() {
|
||||
val imm =
|
||||
requireActivity().getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
|
||||
imm.showSoftInput(binding.searchEdit, InputMethodManager.SHOW_IMPLICIT)
|
||||
}
|
||||
|
||||
private fun hideImm() {
|
||||
val imm =
|
||||
requireActivity().getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
|
||||
imm.hideSoftInputFromWindow(binding.searchEdit.windowToken, 0)
|
||||
}
|
||||
|
||||
private fun showResultContent() {
|
||||
binding.contentScrollView.visibility = View.VISIBLE
|
||||
binding.searchSuggestionsLayout.visibility = View.GONE
|
||||
binding.historyLayout.visibility = View.GONE
|
||||
binding.loadingLayout.visibility = View.GONE
|
||||
binding.noContentLayout.visibility = View.GONE
|
||||
}
|
||||
|
||||
private fun showSearchSuggestions() {
|
||||
binding.contentScrollView.visibility = View.GONE
|
||||
binding.searchSuggestionsLayout.visibility = View.VISIBLE
|
||||
binding.historyLayout.visibility = View.GONE
|
||||
binding.loadingLayout.visibility = View.GONE
|
||||
binding.noContentLayout.visibility = View.GONE
|
||||
}
|
||||
|
||||
private fun showSearchHistory() {
|
||||
binding.contentScrollView.visibility = View.GONE
|
||||
binding.searchSuggestionsLayout.visibility = View.GONE
|
||||
binding.historyLayout.visibility = View.VISIBLE
|
||||
binding.loadingLayout.visibility = View.GONE
|
||||
binding.noContentLayout.visibility = View.GONE
|
||||
}
|
||||
|
||||
private fun showLoadingLayout() {
|
||||
binding.loadingLayout.visibility = View.VISIBLE
|
||||
binding.noContentLayout.visibility = View.GONE
|
||||
binding.contentScrollView.visibility = View.GONE
|
||||
binding.searchSuggestionsLayout.visibility = View.GONE
|
||||
binding.historyLayout.visibility = View.GONE
|
||||
}
|
||||
|
||||
private fun showNoContentLayout() {
|
||||
binding.loadingLayout.visibility = View.GONE
|
||||
binding.noContentLayout.visibility = View.VISIBLE
|
||||
binding.contentScrollView.visibility = View.GONE
|
||||
binding.searchSuggestionsLayout.visibility = View.GONE
|
||||
binding.historyLayout.visibility = View.GONE
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,6 +1,9 @@
|
||||
package com.player.musicoo.innertube
|
||||
|
||||
import com.player.musicoo.innertube.models.MusicCarouselShelfRenderer
|
||||
import com.player.musicoo.innertube.models.MusicResponsiveListItemRenderer
|
||||
import com.player.musicoo.innertube.models.MusicShelfRenderer
|
||||
import com.player.musicoo.innertube.models.MusicTwoRowItemRenderer
|
||||
import io.ktor.client.HttpClient
|
||||
import io.ktor.client.engine.okhttp.OkHttp
|
||||
import io.ktor.client.plugins.BrowserUserAgent
|
||||
@ -165,6 +168,37 @@ object Innertube {
|
||||
companion object
|
||||
}
|
||||
|
||||
data class SingerDetailsPage(
|
||||
val title: String?,
|
||||
val thumbnail: String?,
|
||||
val description: String?,
|
||||
val list: List<SingerDetailsListPage>?
|
||||
)
|
||||
|
||||
data class SingerDetailsListPage(
|
||||
val title: String?,
|
||||
val contents: SingerDetailsContent?
|
||||
){
|
||||
data class SingerDetailsContent(
|
||||
val musicShelfContentList: List<MusicShelfRenderer.Content>?,
|
||||
val musicCarouselShelfContentList: List<MusicCarouselShelfRenderer.Content>?,
|
||||
)
|
||||
}
|
||||
|
||||
data class SearchDataPage(
|
||||
val type: Int?,
|
||||
val title: String?,
|
||||
val searchResultList: List<SearchResult>
|
||||
) {
|
||||
data class SearchResult(
|
||||
val title: String?,
|
||||
val desc: String?,
|
||||
val thumbnail: String?,
|
||||
val videoId: String?,
|
||||
val browseId: String?,
|
||||
)
|
||||
}
|
||||
|
||||
data class BaseHomePage(
|
||||
val visitorData: String?,
|
||||
val cToken: String?,
|
||||
|
||||
@ -0,0 +1,36 @@
|
||||
package com.player.musicoo.innertube.models
|
||||
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
data class MusicCardShelfRenderer(
|
||||
val thumbnail: MusicThumbnailRenderer?,
|
||||
val title: Runs?,
|
||||
val subtitle: Runs?,
|
||||
val header: Header?,
|
||||
) {
|
||||
@Serializable
|
||||
data class MusicThumbnailRenderer(
|
||||
val musicThumbnailRenderer: MusicThumbnailRenderer?
|
||||
) {
|
||||
@Serializable
|
||||
data class MusicThumbnailRenderer(
|
||||
val thumbnail: Thumbnail?
|
||||
) {
|
||||
@Serializable
|
||||
data class Thumbnail(
|
||||
val thumbnails: List<com.player.musicoo.innertube.models.Thumbnail>?
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Serializable
|
||||
data class Header(
|
||||
val musicCardShelfHeaderBasicRenderer: MusicCardShelfHeaderBasicRenderer?
|
||||
) {
|
||||
@Serializable
|
||||
data class MusicCardShelfHeaderBasicRenderer(
|
||||
val title: Runs?,
|
||||
)
|
||||
}
|
||||
}
|
||||
@ -18,6 +18,8 @@ data class SectionListRenderer(
|
||||
val musicShelfRenderer: MusicShelfRenderer?,
|
||||
val gridRenderer: GridRenderer?,
|
||||
val musicDescriptionShelfRenderer: MusicDescriptionShelfRenderer?,
|
||||
@JsonNames("musicCardShelfRenderer")
|
||||
val musicCardShelfRenderer: MusicCardShelfRenderer?
|
||||
) {
|
||||
|
||||
@Serializable
|
||||
|
||||
@ -7,5 +7,5 @@ import kotlinx.serialization.Serializable
|
||||
data class SearchBody(
|
||||
val context: Context = Context.DefaultWeb,
|
||||
val query: String,
|
||||
val params: String
|
||||
val params: String? = null
|
||||
)
|
||||
|
||||
@ -0,0 +1,159 @@
|
||||
package com.player.musicoo.innertube.requests
|
||||
|
||||
import com.player.musicoo.innertube.Innertube
|
||||
import com.player.musicoo.innertube.models.BrowseResponse
|
||||
import com.player.musicoo.innertube.models.MusicShelfRenderer
|
||||
import com.player.musicoo.innertube.models.SearchResponse
|
||||
import com.player.musicoo.innertube.models.SectionListRenderer
|
||||
import com.player.musicoo.innertube.models.bodies.BrowseBody
|
||||
import com.player.musicoo.innertube.models.bodies.SearchBody
|
||||
import com.player.musicoo.innertube.utils.runCatchingNonCancellable
|
||||
import com.player.musicoo.util.LogTag
|
||||
import io.ktor.client.call.body
|
||||
import io.ktor.client.request.post
|
||||
import io.ktor.client.request.setBody
|
||||
|
||||
suspend fun Innertube.moSearchPage(query: String): Result<List<Innertube.SearchDataPage>>? =
|
||||
runCatchingNonCancellable {
|
||||
|
||||
val response = client.post(search) {
|
||||
setBody(SearchBody(query = query))
|
||||
}.body<SearchResponse>()
|
||||
|
||||
val searchDataPageList: MutableList<Innertube.SearchDataPage> = mutableListOf()
|
||||
|
||||
val contents = response.contents
|
||||
?.tabbedSearchResultsRenderer
|
||||
?.tabs
|
||||
?.firstOrNull()
|
||||
?.tabRenderer
|
||||
?.content
|
||||
?.sectionListRenderer
|
||||
?.contents
|
||||
var type = -1
|
||||
var title = ""
|
||||
if (contents != null) {
|
||||
for (content: SectionListRenderer.Content in contents) {
|
||||
//保证每次循环都是一个新的集合
|
||||
val searchResultItemList: MutableList<Innertube.SearchDataPage.SearchResult> =
|
||||
mutableListOf()
|
||||
|
||||
if (content.musicCardShelfRenderer != null) {
|
||||
type = 1
|
||||
title = content.musicCardShelfRenderer
|
||||
.header
|
||||
?.musicCardShelfHeaderBasicRenderer
|
||||
?.title
|
||||
?.text ?: ""
|
||||
|
||||
val searchResultTitle = content.musicCardShelfRenderer
|
||||
.title
|
||||
?.runs
|
||||
?.firstOrNull()
|
||||
?.text ?: ""
|
||||
|
||||
val searchResultDesc = content.musicCardShelfRenderer
|
||||
.subtitle?.text
|
||||
|
||||
val searchResultThumbnail = content.musicCardShelfRenderer
|
||||
.thumbnail
|
||||
?.musicThumbnailRenderer
|
||||
?.thumbnail
|
||||
?.thumbnails
|
||||
?.let {
|
||||
it.getOrNull(3) ?: it.getOrNull(2) ?: it.getOrNull(1)
|
||||
?: it.getOrNull(0)
|
||||
}
|
||||
?.url ?: ""
|
||||
|
||||
val searchResultVideoId = content.musicCardShelfRenderer
|
||||
.title
|
||||
?.runs
|
||||
?.firstOrNull()
|
||||
?.navigationEndpoint
|
||||
?.watchEndpoint
|
||||
?.videoId ?: ""
|
||||
|
||||
val searchResultBrowseId = content.musicCardShelfRenderer
|
||||
.title
|
||||
?.runs
|
||||
?.firstOrNull()
|
||||
?.navigationEndpoint
|
||||
?.browseEndpoint
|
||||
?.browseId
|
||||
|
||||
val result = Innertube.SearchDataPage.SearchResult(
|
||||
title = searchResultTitle,
|
||||
desc = searchResultDesc,
|
||||
thumbnail = searchResultThumbnail,
|
||||
videoId = searchResultVideoId,
|
||||
browseId = searchResultBrowseId
|
||||
)
|
||||
searchResultItemList.add(result)
|
||||
} else {
|
||||
if (content.musicShelfRenderer != null) {
|
||||
type = 2
|
||||
title = content.musicShelfRenderer.title
|
||||
?.text ?: ""
|
||||
val itemContents = content.musicShelfRenderer.contents
|
||||
if (itemContents != null) {
|
||||
for (itemContent: MusicShelfRenderer.Content in itemContents) {
|
||||
|
||||
val item = Innertube.SearchDataPage.SearchResult(
|
||||
title = itemContent
|
||||
.musicResponsiveListItemRenderer
|
||||
?.flexColumns
|
||||
?.firstOrNull()
|
||||
?.musicResponsiveListItemFlexColumnRenderer
|
||||
?.text
|
||||
?.runs
|
||||
?.firstOrNull()
|
||||
?.text,
|
||||
desc = itemContent.musicResponsiveListItemRenderer
|
||||
?.flexColumns
|
||||
?.get(1)
|
||||
?.musicResponsiveListItemFlexColumnRenderer
|
||||
?.text
|
||||
?.runs
|
||||
?.map { it.text }
|
||||
?.joinToString("") ?: "",
|
||||
thumbnail = itemContent.musicResponsiveListItemRenderer
|
||||
?.thumbnail
|
||||
?.musicThumbnailRenderer
|
||||
?.thumbnail
|
||||
?.thumbnails
|
||||
?.let { it.getOrNull(1) ?: it.getOrNull(0) }
|
||||
?.url,
|
||||
videoId = itemContent.musicResponsiveListItemRenderer
|
||||
?.flexColumns
|
||||
?.firstOrNull()
|
||||
?.musicResponsiveListItemFlexColumnRenderer
|
||||
?.text
|
||||
?.runs
|
||||
?.firstOrNull()
|
||||
?.navigationEndpoint
|
||||
?.watchEndpoint
|
||||
?.videoId,
|
||||
browseId = itemContent.musicResponsiveListItemRenderer
|
||||
?.navigationEndpoint
|
||||
?.browseEndpoint
|
||||
?.browseId ?: ""
|
||||
)
|
||||
if (!item.videoId.isNullOrBlank() || !item.browseId.isNullOrBlank()) {
|
||||
searchResultItemList.add(item)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
val dataPage =
|
||||
Innertube.SearchDataPage(
|
||||
type,
|
||||
title = title,
|
||||
searchResultList = searchResultItemList
|
||||
)
|
||||
searchDataPageList.add(dataPage)
|
||||
}
|
||||
}
|
||||
searchDataPageList
|
||||
}
|
||||
@ -0,0 +1,112 @@
|
||||
package com.player.musicoo.innertube.requests
|
||||
|
||||
import com.player.musicoo.innertube.Innertube
|
||||
import com.player.musicoo.innertube.models.BrowseResponse
|
||||
import com.player.musicoo.innertube.models.MusicCarouselShelfRenderer
|
||||
import com.player.musicoo.innertube.models.MusicShelfRenderer
|
||||
import com.player.musicoo.innertube.models.SectionListRenderer
|
||||
import com.player.musicoo.innertube.models.bodies.BrowseBody
|
||||
import com.player.musicoo.innertube.utils.runCatchingNonCancellable
|
||||
import com.player.musicoo.util.LogTag
|
||||
import io.ktor.client.call.body
|
||||
import io.ktor.client.request.post
|
||||
import io.ktor.client.request.setBody
|
||||
|
||||
suspend fun Innertube.moSingerListPage(browseId: String): Result<Innertube.SingerDetailsPage>? =
|
||||
runCatchingNonCancellable {
|
||||
val response = client.post(browse) {
|
||||
setBody(BrowseBody(browseId = browseId))
|
||||
}.body<BrowseResponse>()
|
||||
|
||||
val list: MutableList<Innertube.SingerDetailsListPage> = mutableListOf()
|
||||
|
||||
val musicImmersiveHeaderRenderer = response.header
|
||||
?.musicImmersiveHeaderRenderer
|
||||
|
||||
val title = musicImmersiveHeaderRenderer
|
||||
?.title
|
||||
?.text
|
||||
|
||||
val thumbnail = musicImmersiveHeaderRenderer
|
||||
?.thumbnail
|
||||
?.musicThumbnailRenderer
|
||||
?.thumbnail
|
||||
?.thumbnails
|
||||
?.let {
|
||||
it.getOrNull(2) ?: it.getOrNull(1) ?: it.getOrNull(0)
|
||||
}?.url
|
||||
|
||||
val description = musicImmersiveHeaderRenderer
|
||||
?.description
|
||||
?.text
|
||||
|
||||
val sectionListRendererContents = response
|
||||
.contents
|
||||
?.singleColumnBrowseResultsRenderer
|
||||
?.tabs
|
||||
?.firstOrNull()
|
||||
?.tabRenderer
|
||||
?.content
|
||||
?.sectionListRenderer
|
||||
?.contents
|
||||
|
||||
if (sectionListRendererContents != null) {
|
||||
for (content: SectionListRenderer.Content in sectionListRendererContents) {
|
||||
var title = ""
|
||||
val musicShelfRendererContents: MutableList<MusicShelfRenderer.Content> =
|
||||
mutableListOf()
|
||||
val musicCarouselShelfContents: MutableList<MusicCarouselShelfRenderer.Content> =
|
||||
mutableListOf()
|
||||
if (content.musicShelfRenderer != null) {
|
||||
title = content.musicShelfRenderer
|
||||
.title?.text ?: ""
|
||||
if (content.musicShelfRenderer.contents != null) {
|
||||
musicShelfRendererContents.clear()
|
||||
musicShelfRendererContents.addAll(content.musicShelfRenderer.contents)
|
||||
}
|
||||
}
|
||||
if (content.musicCarouselShelfRenderer != null) {
|
||||
title = content.musicCarouselShelfRenderer.header
|
||||
?.musicCarouselShelfBasicHeaderRenderer
|
||||
?.title
|
||||
?.text ?: ""
|
||||
|
||||
if (content.musicCarouselShelfRenderer.contents != null) {
|
||||
musicCarouselShelfContents.clear()
|
||||
musicCarouselShelfContents.addAll(content.musicCarouselShelfRenderer.contents)
|
||||
}
|
||||
}
|
||||
|
||||
val singerDetailsContent = Innertube.SingerDetailsListPage.SingerDetailsContent(
|
||||
musicShelfRendererContents,
|
||||
musicCarouselShelfContents
|
||||
)
|
||||
|
||||
val singerDetails =
|
||||
Innertube.SingerDetailsListPage(title = title, contents = singerDetailsContent)
|
||||
|
||||
LogTag.LogD(TAG, "title->${singerDetails.title}")
|
||||
LogTag.LogD(
|
||||
TAG,
|
||||
"musicShelfContentList size->${singerDetails.contents?.musicShelfContentList?.size}"
|
||||
)
|
||||
LogTag.LogD(
|
||||
TAG,
|
||||
"musicCarouselShelfContentList size->${singerDetails.contents?.musicCarouselShelfContentList?.size}"
|
||||
)
|
||||
|
||||
LogTag.LogD(TAG, "--------------------------------------------")
|
||||
|
||||
if (title.isNotEmpty()) {
|
||||
list.add(singerDetails)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Innertube.SingerDetailsPage(
|
||||
title = title,
|
||||
thumbnail = thumbnail,
|
||||
description = description,
|
||||
list
|
||||
)
|
||||
}
|
||||
@ -11,9 +11,13 @@ class AppStore(context: Context) {
|
||||
.asStoreProvider()
|
||||
)
|
||||
|
||||
|
||||
var searchHistoryStore: Set<String> by store.stringSet(
|
||||
key = SEARCH_HISTORY,
|
||||
defaultValue = emptySet()
|
||||
)
|
||||
|
||||
companion object {
|
||||
private const val FILE_NAME = "music_oo_app"
|
||||
const val SEARCH_HISTORY = "search_history"
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,60 @@
|
||||
package com.player.musicoo.view
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.widget.ImageView
|
||||
import android.widget.LinearLayout
|
||||
import android.widget.TextView
|
||||
import com.bumptech.glide.Glide
|
||||
import com.player.musicoo.R
|
||||
import com.player.musicoo.activity.MoPlayDetailsActivity
|
||||
import com.player.musicoo.activity.MoSingerDetailsActivity
|
||||
import com.player.musicoo.innertube.Innertube
|
||||
|
||||
@SuppressLint("ViewConstructor")
|
||||
class SearchResultOptimalView(context: Context, data: Innertube.SearchDataPage) :
|
||||
ModuleView(context) {
|
||||
|
||||
init {
|
||||
contentView = inflate(getContext(), R.layout.search_result_optimal_layout, this)
|
||||
val title = contentView?.findViewById<TextView>(R.id.title)
|
||||
title?.text = data.title
|
||||
|
||||
val name = contentView?.findViewById<TextView>(R.id.name)
|
||||
name?.text = data.searchResultList.firstOrNull()?.title
|
||||
|
||||
val desc = contentView?.findViewById<TextView>(R.id.desc)
|
||||
desc?.text = data.searchResultList.firstOrNull()?.desc
|
||||
|
||||
val image = contentView?.findViewById<ImageView>(R.id.image)
|
||||
val optimalBean = data.searchResultList.firstOrNull()
|
||||
val url = optimalBean?.thumbnail
|
||||
Glide.with(context)
|
||||
.load(url)
|
||||
.into(image!!)
|
||||
|
||||
val playBtn = contentView?.findViewById<LinearLayout>(R.id.playBtn)
|
||||
playBtn?.setOnClickListener {
|
||||
if (optimalBean != null) {
|
||||
if (!optimalBean.videoId.isNullOrEmpty()) {
|
||||
val intent = Intent(context, MoPlayDetailsActivity::class.java)
|
||||
intent.putExtra(
|
||||
MoPlayDetailsActivity.PLAY_DETAILS_VIDEO_ID,
|
||||
optimalBean.videoId
|
||||
)
|
||||
intent.putExtra(MoPlayDetailsActivity.PLAY_DETAILS_NAME, optimalBean.title)
|
||||
intent.putExtra(MoPlayDetailsActivity.PLAY_DETAILS_DESC, optimalBean.desc)
|
||||
context.startActivity(intent)
|
||||
} else if (!optimalBean.browseId.isNullOrEmpty()) {
|
||||
val intent = Intent(context, MoSingerDetailsActivity::class.java)
|
||||
intent.putExtra(
|
||||
MoSingerDetailsActivity.SINGER_DETAILS_PAGE_BROWSE_ID,
|
||||
optimalBean.browseId
|
||||
)
|
||||
context.startActivity(intent)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,26 @@
|
||||
package com.player.musicoo.view
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.Context
|
||||
import android.widget.TextView
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.player.musicoo.R
|
||||
import com.player.musicoo.adapter.SearchResultOtherAdapter
|
||||
import com.player.musicoo.innertube.Innertube
|
||||
|
||||
@SuppressLint("ViewConstructor")
|
||||
class SearchResultOtherView (context: Context, data: Innertube.SearchDataPage) :
|
||||
ModuleView(context) {
|
||||
init {
|
||||
contentView = inflate(getContext(), R.layout.search_result_other_layout, this)
|
||||
val title = contentView?.findViewById<TextView>(R.id.title)
|
||||
title?.text = data.title
|
||||
|
||||
val rv = contentView?.findViewById<RecyclerView>(R.id.rv)
|
||||
val adapter = SearchResultOtherAdapter(context, data.searchResultList)
|
||||
rv?.layoutManager = LinearLayoutManager(context, LinearLayoutManager.VERTICAL, false)
|
||||
rv?.adapter = adapter
|
||||
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,27 @@
|
||||
package com.player.musicoo.view
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.Context
|
||||
import android.widget.TextView
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.player.musicoo.R
|
||||
import com.player.musicoo.adapter.TowRowListAdapter
|
||||
import com.player.musicoo.innertube.Innertube
|
||||
|
||||
@SuppressLint("ViewConstructor")
|
||||
class SingerDetailsOtherView (context: Context, bean: Innertube.SingerDetailsListPage) :
|
||||
ModuleView(context) {
|
||||
init {
|
||||
contentView = inflate(getContext(), R.layout.singer_music_list_layout, this)
|
||||
val title = contentView?.findViewById<TextView>(R.id.title)
|
||||
title?.text = bean.title
|
||||
|
||||
val rv = contentView?.findViewById<RecyclerView>(R.id.rv)
|
||||
|
||||
val adapter = TowRowListAdapter(context, bean.contents?.musicCarouselShelfContentList!!)
|
||||
rv?.layoutManager = LinearLayoutManager(context, LinearLayoutManager.HORIZONTAL, false)
|
||||
rv?.adapter = adapter
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,28 @@
|
||||
package com.player.musicoo.view
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.Context
|
||||
import android.widget.TextView
|
||||
import androidx.recyclerview.widget.GridLayoutManager
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.player.musicoo.R
|
||||
import com.player.musicoo.adapter.SingerDetailsSongListAdapter
|
||||
import com.player.musicoo.innertube.Innertube
|
||||
|
||||
@SuppressLint("ViewConstructor")
|
||||
class SingerDetailsSongView(context: Context, bean: Innertube.SingerDetailsListPage) :
|
||||
ModuleView(context) {
|
||||
init {
|
||||
contentView = inflate(getContext(), R.layout.singer_music_list_layout, this)
|
||||
val title = contentView?.findViewById<TextView>(R.id.title)
|
||||
title?.text = bean.title
|
||||
|
||||
val rv = contentView?.findViewById<RecyclerView>(R.id.rv)
|
||||
|
||||
val adapter = SingerDetailsSongListAdapter(context, bean.contents?.musicShelfContentList!!)
|
||||
rv?.layoutManager = LinearLayoutManager(context, GridLayoutManager.VERTICAL, false)
|
||||
rv?.adapter = adapter
|
||||
}
|
||||
|
||||
}
|
||||
13
app/src/main/res/drawable/arrow_bottom_grey_icon.xml
Normal file
13
app/src/main/res/drawable/arrow_bottom_grey_icon.xml
Normal file
@ -0,0 +1,13 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="20dp"
|
||||
android:height="20dp"
|
||||
android:viewportWidth="20"
|
||||
android:viewportHeight="20">
|
||||
<path
|
||||
android:pathData="M15.833,7.083L10,12.917L4.167,7.083"
|
||||
android:strokeLineJoin="round"
|
||||
android:strokeWidth="1.5"
|
||||
android:fillColor="#00000000"
|
||||
android:strokeColor="@color/white_60"
|
||||
android:strokeLineCap="round"/>
|
||||
</vector>
|
||||
9
app/src/main/res/drawable/cha_icon.xml
Normal file
9
app/src/main/res/drawable/cha_icon.xml
Normal file
@ -0,0 +1,9 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="32dp"
|
||||
android:height="32dp"
|
||||
android:viewportWidth="1024"
|
||||
android:viewportHeight="1024">
|
||||
<path
|
||||
android:fillColor="@color/white"
|
||||
android:pathData="M512,471.7l207.4,-207.4a28.4,28.4 0,1 1,40.3 40.2L552.3,512l207.4,207.4a28.4,28.4 0,1 1,-40.2 40.3L512,552.3l-207.4,207.4a28.4,28.4 0,1 1,-40.3 -40.2L471.7,512l-207.4,-207.4a28.4,28.4 0,0 1,40.2 -40.3L512,471.7z" />
|
||||
</vector>
|
||||
28
app/src/main/res/drawable/delete_icon.xml
Normal file
28
app/src/main/res/drawable/delete_icon.xml
Normal file
@ -0,0 +1,28 @@
|
||||
<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="M7.5,7.033C7.5,6.503 7.616,5.979 7.843,5.489C8.069,5 8.4,4.556 8.818,4.181C9.236,3.807 9.732,3.51 10.278,3.307C10.824,3.104 11.409,3 12,3C12.591,3 13.176,3.104 13.722,3.307C14.268,3.51 14.764,3.807 15.182,4.181C15.6,4.556 15.931,5 16.157,5.489C16.384,5.979 16.5,6.503 16.5,7.033"
|
||||
android:strokeAlpha="0.6"
|
||||
android:strokeWidth="1.5"
|
||||
android:fillColor="#00000000"
|
||||
android:strokeColor="#ffffff"/>
|
||||
<path
|
||||
android:pathData="M2.25,7.033C2.25,7.447 2.586,7.783 3,7.783H4.25V12.176C4.25,13.821 4.481,15.458 4.938,17.038C5.618,19.396 7.574,21.159 9.993,21.568L10.151,21.595C11.375,21.802 12.625,21.802 13.849,21.595L14.007,21.568C16.42,21.16 18.384,19.39 19.062,17.038C19.519,15.458 19.75,13.821 19.75,12.176V7.783H21C21.414,7.783 21.75,7.447 21.75,7.033C21.75,6.618 21.414,6.283 21,6.283H3C2.586,6.283 2.25,6.618 2.25,7.033ZM5.75,12.176V7.783H18.25V12.176C18.25,13.68 18.038,15.177 17.621,16.622C17.102,18.42 15.602,19.777 13.757,20.089L13.599,20.116C12.54,20.295 11.46,20.295 10.401,20.116L10.243,20.089C8.392,19.776 6.899,18.426 6.379,16.622C5.962,15.177 5.75,13.68 5.75,12.176ZM5,6.283V7.033H4.25V6.283H5Z"
|
||||
android:fillColor="#ffffff"
|
||||
android:fillAlpha="0.6"
|
||||
android:fillType="evenOdd"/>
|
||||
<path
|
||||
android:pathData="M10,12V16M14,12V16"
|
||||
android:strokeAlpha="0.6"
|
||||
android:strokeWidth="1.5"
|
||||
android:fillColor="#00000000"
|
||||
android:strokeColor="#ffffff"
|
||||
android:strokeLineCap="round"/>
|
||||
</group>
|
||||
</vector>
|
||||
8
app/src/main/res/drawable/drw_search_history_tag_bg.xml
Normal file
8
app/src/main/res/drawable/drw_search_history_tag_bg.xml
Normal file
@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:shape="rectangle">
|
||||
|
||||
<solid android:color="#FF1F1F1F" />
|
||||
<corners android:radius="30dp" />
|
||||
|
||||
</shape>
|
||||
8
app/src/main/res/drawable/drw_search_layout_bg.xml
Normal file
8
app/src/main/res/drawable/drw_search_layout_bg.xml
Normal file
@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:shape="rectangle">
|
||||
|
||||
<solid android:color="#FF212121" />
|
||||
<corners android:radius="20dp" />
|
||||
|
||||
</shape>
|
||||
22
app/src/main/res/drawable/search_select_icon.xml
Normal file
22
app/src/main/res/drawable/search_select_icon.xml
Normal file
@ -0,0 +1,22 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="36dp"
|
||||
android:height="36dp"
|
||||
android:viewportWidth="36"
|
||||
android:viewportHeight="36">
|
||||
<path
|
||||
android:strokeWidth="1"
|
||||
android:pathData="M18,18m-17.5,0a17.5,17.5 0,1 1,35 0a17.5,17.5 0,1 1,-35 0"
|
||||
android:fillColor="#00000000"
|
||||
android:strokeColor="#80F988"/>
|
||||
<group>
|
||||
<clip-path
|
||||
android:pathData="M32,4l-28,0l-0,28l28,0z"/>
|
||||
<path
|
||||
android:pathData="M17.5,6C11.149,6 6,11.149 6,17.5C6,23.851 11.149,29 17.5,29C23.851,29 29,23.851 29,17.5C29,11.149 23.851,6 17.5,6ZM15.354,10.861C15.819,10.795 16.176,10.395 16.176,9.912C16.176,9.383 15.747,8.954 15.218,8.954C15.16,8.954 15.104,8.959 15.049,8.968C11.923,9.472 9.472,11.923 8.968,15.049C8.959,15.104 8.954,15.16 8.954,15.218C8.954,15.747 9.383,16.176 9.912,16.176C10.395,16.176 10.795,15.819 10.861,15.354C11.233,13.044 13.044,11.233 15.354,10.861Z"
|
||||
android:fillColor="#80F988"
|
||||
android:fillType="evenOdd"/>
|
||||
<path
|
||||
android:pathData="M29.431,29.434C29.745,29.107 29.745,28.594 29.431,28.279L27.264,26.111C26.938,25.796 26.425,25.796 26.111,26.111C25.796,26.437 25.796,26.95 26.111,27.265L28.278,29.434C28.429,29.585 28.639,29.667 28.848,29.667C29.058,29.667 29.268,29.585 29.431,29.434Z"
|
||||
android:fillColor="#80F988"/>
|
||||
</group>
|
||||
</vector>
|
||||
19
app/src/main/res/drawable/search_unselect_icon.xml
Normal file
19
app/src/main/res/drawable/search_unselect_icon.xml
Normal file
@ -0,0 +1,19 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="28dp"
|
||||
android:height="28dp"
|
||||
android:viewportWidth="28"
|
||||
android:viewportHeight="28">
|
||||
<group>
|
||||
<clip-path
|
||||
android:pathData="M28,0l-28,0l-0,28l28,0z"/>
|
||||
<path
|
||||
android:pathData="M13.5,2C7.149,2 2,7.149 2,13.5C2,19.851 7.149,25 13.5,25C19.851,25 25,19.851 25,13.5C25,7.149 19.851,2 13.5,2ZM11.354,6.861C11.819,6.795 12.176,6.395 12.176,5.912C12.176,5.383 11.747,4.954 11.218,4.954C11.16,4.954 11.104,4.959 11.049,4.968C7.923,5.472 5.472,7.923 4.968,11.049C4.959,11.104 4.954,11.16 4.954,11.218C4.954,11.747 5.383,12.176 5.912,12.176C6.395,12.176 6.795,11.819 6.861,11.354C7.233,9.044 9.044,7.233 11.354,6.861Z"
|
||||
android:fillColor="#ffffff"
|
||||
android:fillAlpha="0.6"
|
||||
android:fillType="evenOdd"/>
|
||||
<path
|
||||
android:pathData="M25.431,25.434C25.745,25.107 25.745,24.594 25.431,24.279L23.264,22.111C22.938,21.796 22.425,21.796 22.111,22.111C21.796,22.437 21.796,22.95 22.111,23.265L24.278,25.434C24.429,25.585 24.639,25.667 24.848,25.667C25.058,25.667 25.268,25.585 25.431,25.434Z"
|
||||
android:fillColor="#ffffff"
|
||||
android:fillAlpha="0.6"/>
|
||||
</group>
|
||||
</vector>
|
||||
@ -176,6 +176,8 @@
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/rv"
|
||||
android:layout_width="match_parent"
|
||||
android:scrollbars="none"
|
||||
android:overScrollMode="never"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="16dp" />
|
||||
</LinearLayout>
|
||||
|
||||
@ -409,6 +409,8 @@
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/playListRv"
|
||||
android:layout_width="match_parent"
|
||||
android:scrollbars="none"
|
||||
android:overScrollMode="never"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_margin="16dp" />
|
||||
|
||||
|
||||
@ -5,6 +5,13 @@
|
||||
android:layout_height="match_parent"
|
||||
android:background="@color/main_bg_color">
|
||||
|
||||
<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"
|
||||
@ -177,6 +184,21 @@
|
||||
android:src="@drawable/home_select_icon" />
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/search_btn"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_weight="1"
|
||||
android:gravity="center">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/search_img"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:src="@drawable/search_unselect_icon" />
|
||||
</LinearLayout>
|
||||
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/import_btn"
|
||||
android:layout_width="0dp"
|
||||
|
||||
205
app/src/main/res/layout/activity_singer_details.xml
Normal file
205
app/src/main/res/layout/activity_singer_details.xml
Normal file
@ -0,0 +1,205 @@
|
||||
<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">
|
||||
|
||||
<RelativeLayout
|
||||
android:id="@+id/relative"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/singerImg"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
android:layout_weight="2"
|
||||
android:scaleType="centerCrop" />
|
||||
|
||||
<View
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
android:layout_weight="3" />
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical">
|
||||
|
||||
<View
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
android:layout_weight="2"
|
||||
android:background="@drawable/drw_details_bg"
|
||||
android:visibility="visible" />
|
||||
|
||||
<View
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
android:layout_weight="3"
|
||||
android:background="@color/black" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</RelativeLayout>
|
||||
|
||||
<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>
|
||||
|
||||
</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>
|
||||
|
||||
<androidx.core.widget.NestedScrollView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="16dp"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:orientation="vertical">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/singerName"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="68dp"
|
||||
android:fontFamily="@font/medium_font"
|
||||
android:text="@string/app_name"
|
||||
android:textColor="@color/white"
|
||||
android:textSize="22dp" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/singerDesc"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="8dp"
|
||||
android:ellipsize="end"
|
||||
android:fontFamily="@font/regular_font"
|
||||
android:maxLines="2"
|
||||
android:text="@string/app_name"
|
||||
android:textColor="@color/white_60"
|
||||
android:textSize="14dp" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/singerDescExpand"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="2dp"
|
||||
android:ellipsize="end"
|
||||
android:fontFamily="@font/regular_font"
|
||||
android:text="@string/expand"
|
||||
android:textColor="@color/white_60"
|
||||
android:textSize="12dp" />
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/content_layout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="16dp"
|
||||
android:layout_marginTop="16dp"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:orientation="vertical" />
|
||||
</LinearLayout>
|
||||
|
||||
</androidx.core.widget.NestedScrollView>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</RelativeLayout>
|
||||
@ -2,14 +2,7 @@
|
||||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="@color/main_bg_color">
|
||||
|
||||
<ImageView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:scaleType="fitXY"
|
||||
android:src="@mipmap/main_bg_img" />
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<View
|
||||
android:id="@+id/view"
|
||||
|
||||
@ -1,20 +1,13 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="#FF000000">
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<View
|
||||
android:id="@+id/view"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp" />
|
||||
|
||||
<ImageView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:scaleType="fitXY"
|
||||
android:src="@mipmap/import_fragment_bg" />
|
||||
|
||||
<RelativeLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
|
||||
@ -2,14 +2,7 @@
|
||||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="@color/main_bg_color">
|
||||
|
||||
<ImageView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:scaleType="fitXY"
|
||||
android:src="@mipmap/main_bg_img" />
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<View
|
||||
android:id="@+id/view"
|
||||
|
||||
217
app/src/main/res/layout/fragment_search.xml
Normal file
217
app/src/main/res/layout/fragment_search.xml
Normal file
@ -0,0 +1,217 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<ImageView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:scaleType="centerCrop"
|
||||
android:src="@mipmap/settings_bg_img"
|
||||
android:visibility="gone" />
|
||||
|
||||
<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:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="18dp"
|
||||
android:layout_marginTop="32dp"
|
||||
android:layout_marginEnd="18dp"
|
||||
android:gravity="center_vertical"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="40dp"
|
||||
android:layout_weight="1"
|
||||
android:background="@drawable/drw_search_layout_bg"
|
||||
android:gravity="center_vertical"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<ImageView
|
||||
android:layout_width="16dp"
|
||||
android:layout_height="16dp"
|
||||
android:layout_marginStart="16dp"
|
||||
android:src="@drawable/search_unselect_icon" />
|
||||
|
||||
<EditText
|
||||
android:id="@+id/searchEdit"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_marginStart="8dp"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:layout_weight="1"
|
||||
android:background="@null"
|
||||
android:focusable="true"
|
||||
android:focusableInTouchMode="true"
|
||||
android:hint="@string/enter_keywords"
|
||||
android:imeOptions="actionSearch"
|
||||
android:singleLine="true"
|
||||
android:textSize="14dp" />
|
||||
|
||||
<RelativeLayout
|
||||
android:id="@+id/deleteInputBtn"
|
||||
android:layout_width="40dp"
|
||||
android:layout_height="40dp"
|
||||
android:visibility="gone">
|
||||
|
||||
<ImageView
|
||||
android:layout_width="16dp"
|
||||
android:layout_height="16dp"
|
||||
android:layout_centerInParent="true"
|
||||
android:src="@drawable/cha_icon" />
|
||||
</RelativeLayout>
|
||||
</LinearLayout>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/cancelBtn"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_marginStart="8dp"
|
||||
android:fontFamily="@font/regular_font"
|
||||
android:gravity="center"
|
||||
android:text="@string/cancel"
|
||||
android:textColor="@color/white_80"
|
||||
android:textSize="14dp" />
|
||||
</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/no_found"
|
||||
android:textSize="14dp" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<androidx.core.widget.NestedScrollView
|
||||
android:id="@+id/contentScrollView"
|
||||
android:visibility="gone"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/content_layout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_marginStart="18dp"
|
||||
android:layout_marginEnd="18dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical" />
|
||||
|
||||
</androidx.core.widget.NestedScrollView>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/searchSuggestionsLayout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_marginStart="18dp"
|
||||
android:layout_marginTop="16dp"
|
||||
android:layout_marginEnd="18dp"
|
||||
android:orientation="vertical"
|
||||
android:visibility="gone">
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/searchSuggestionsRv"
|
||||
android:scrollbars="none"
|
||||
android:overScrollMode="never"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
tools:itemCount="1"
|
||||
tools:listitem="@layout/search_suggestions_adapter_item" />
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/historyLayout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="18dp"
|
||||
android:layout_marginTop="16dp"
|
||||
android:layout_marginEnd="18dp"
|
||||
android:orientation="vertical"
|
||||
android:visibility="visible">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="40dp"
|
||||
android:gravity="center_vertical"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<TextView
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:fontFamily="@font/bold_font_italic"
|
||||
android:text="@string/history"
|
||||
android:textColor="@color/white_60"
|
||||
android:textSize="14dp" />
|
||||
|
||||
<RelativeLayout
|
||||
android:id="@+id/delete_history_btn"
|
||||
android:layout_width="40dp"
|
||||
android:layout_height="40dp">
|
||||
|
||||
<ImageView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_centerInParent="true"
|
||||
android:src="@drawable/delete_icon" />
|
||||
|
||||
</RelativeLayout>
|
||||
</LinearLayout>
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/historyRv"
|
||||
android:layout_width="match_parent"
|
||||
android:scrollbars="none"
|
||||
android:overScrollMode="never"
|
||||
android:layout_height="wrap_content" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</RelativeLayout>
|
||||
@ -19,6 +19,8 @@
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/rv"
|
||||
android:layout_width="match_parent"
|
||||
android:scrollbars="none"
|
||||
android:overScrollMode="never"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="12dp" />
|
||||
</LinearLayout>
|
||||
26
app/src/main/res/layout/search_history_adapter_item.xml
Normal file
26
app/src/main/res/layout/search_history_adapter_item.xml
Normal file
@ -0,0 +1,26 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:layout_marginTop="8dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@drawable/drw_search_history_tag_bg">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/historyName"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="12dp"
|
||||
android:layout_marginTop="4dp"
|
||||
android:layout_marginEnd="12dp"
|
||||
android:textSize="12dp"
|
||||
android:layout_marginBottom="4dp"
|
||||
android:text="@string/app_name"
|
||||
android:textColor="@color/white" />
|
||||
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
103
app/src/main/res/layout/search_result_optimal_layout.xml
Normal file
103
app/src/main/res/layout/search_result_optimal_layout.xml
Normal file
@ -0,0 +1,103 @@
|
||||
<?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"
|
||||
android:orientation="vertical"
|
||||
tools:ignore="MissingDefaultResource">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/title"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="16dp"
|
||||
android:fontFamily="@font/medium_font"
|
||||
android:text="@string/app_name"
|
||||
android:textColor="@color/white"
|
||||
android:textSize="18dp" />
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/playBtn"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="16dp"
|
||||
android:gravity="center_vertical"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<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="100dp"
|
||||
android:layout_height="56dp">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/image"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:scaleType="centerCrop"
|
||||
android:src="@mipmap/musicoo_logo_img" />
|
||||
|
||||
<RelativeLayout
|
||||
android:id="@+id/currentPlayingLayout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="@color/black_60"
|
||||
android:gravity="center"
|
||||
android:visibility="gone">
|
||||
|
||||
<ImageView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:src="@drawable/playing_small_green_icon" />
|
||||
</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">
|
||||
|
||||
<com.player.musicoo.view.MarqueeTextView
|
||||
android:id="@+id/name"
|
||||
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="16dp" />
|
||||
|
||||
<com.player.musicoo.view.MarqueeTextView
|
||||
android:id="@+id/desc"
|
||||
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="14dp" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
90
app/src/main/res/layout/search_result_other_item.xml
Normal file
90
app/src/main/res/layout/search_result_other_item.xml
Normal 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/musicoo_logo_img" />
|
||||
|
||||
<RelativeLayout
|
||||
android:id="@+id/currentPlayingLayout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="@color/black_60"
|
||||
android:gravity="center"
|
||||
android:visibility="gone">
|
||||
|
||||
<ImageView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:src="@drawable/playing_small_green_icon" />
|
||||
</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">
|
||||
|
||||
<com.player.musicoo.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" />
|
||||
|
||||
<com.player.musicoo.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>
|
||||
57
app/src/main/res/layout/search_result_other_layout.xml
Normal file
57
app/src/main/res/layout/search_result_other_layout.xml
Normal file
@ -0,0 +1,57 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
tools:ignore="MissingDefaultResource">
|
||||
|
||||
<RelativeLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="32dp"
|
||||
android:layout_marginTop="16dp"
|
||||
android:gravity="center_vertical"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/title"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:fontFamily="@font/medium_font"
|
||||
android:text="@string/app_name"
|
||||
android:textColor="@color/white"
|
||||
android:textSize="18dp" />
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/moreBtn"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_alignParentEnd="true"
|
||||
android:gravity="center_vertical">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:fontFamily="@font/regular_font"
|
||||
android:text="@string/more"
|
||||
android:textColor="@color/white_60"
|
||||
android:textSize="12dp" />
|
||||
|
||||
<ImageView
|
||||
android:layout_width="16dp"
|
||||
android:layout_height="16dp"
|
||||
android:rotation="270"
|
||||
android:src="@drawable/arrow_bottom_grey_icon" />
|
||||
</LinearLayout>
|
||||
</RelativeLayout>
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/rv"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="12dp"
|
||||
android:scrollbars="none"
|
||||
android:overScrollMode="never"
|
||||
tools:itemCount="1"
|
||||
tools:listitem="@layout/play_list_item" />
|
||||
</LinearLayout>
|
||||
27
app/src/main/res/layout/search_suggestions_adapter_item.xml
Normal file
27
app/src/main/res/layout/search_suggestions_adapter_item.xml
Normal file
@ -0,0 +1,27 @@
|
||||
<?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="wrap_content">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center_vertical"
|
||||
android:orientation="vertical">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/text"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="40dp"
|
||||
android:fontFamily="@font/regular_font"
|
||||
android:textSize="14dp"
|
||||
android:textColor="@color/white_80"
|
||||
android:gravity="center_vertical"
|
||||
android:text="@string/app_name" />
|
||||
|
||||
<View
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0.5dp"
|
||||
android:background="#33EEEEEE" />
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
26
app/src/main/res/layout/singer_music_list_layout.xml
Normal file
26
app/src/main/res/layout/singer_music_list_layout.xml
Normal file
@ -0,0 +1,26 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
tools:ignore="MissingDefaultResource">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/title"
|
||||
android:layout_marginTop="8dp"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:fontFamily="@font/medium_font"
|
||||
android:text="@string/app_name"
|
||||
android:textColor="@color/white"
|
||||
android:textSize="18dp" />
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/rv"
|
||||
android:layout_width="match_parent"
|
||||
android:scrollbars="none"
|
||||
android:overScrollMode="never"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="12dp" />
|
||||
</LinearLayout>
|
||||
@ -9,6 +9,7 @@
|
||||
<string name="ok">OK</string>
|
||||
<string name="cancel">Cancel</string>
|
||||
<string name="settings">Settings</string>
|
||||
<string name="search">Search</string>
|
||||
<string name="about">About</string>
|
||||
<string name="feedback">Feedback</string>
|
||||
<string name="share">Share</string>
|
||||
@ -22,4 +23,8 @@
|
||||
<string name="exit_main_text">Press again to exit</string>
|
||||
<string name="content_loading_failed">Content loading failed, click to try again.</string>
|
||||
<string name="try_again">Try Again</string>
|
||||
<string name="enter_keywords">Enter keywords</string>
|
||||
<string name="history">History</string>
|
||||
<string name="no_found">No Found</string>
|
||||
<string name="more">More</string>
|
||||
</resources>
|
||||
Loading…
Reference in New Issue
Block a user