diff --git a/app/build.gradle.kts b/app/build.gradle.kts
index 62262c2..30f0e80 100644
--- a/app/build.gradle.kts
+++ b/app/build.gradle.kts
@@ -84,5 +84,6 @@ dependencies {
implementation("org.brotli:dec:0.1.2")
implementation("com.google.android.flexbox:flexbox:3.0.0")
-
+ implementation("io.github.scwang90:refresh-layout-kernel:2.1.0")
+ implementation("io.github.scwang90:refresh-footer-ball:2.1.0")
}
\ No newline at end of file
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index b0f3d92..0ac0368 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -59,6 +59,9 @@
+
= Channel(Channel.UNLIMITED)
+
+ enum class Request {
+ TryAgain,
+ MoreData,
+ }
+
+ companion object {
+ const val SEARCH_MORE_QUERY = "search_more_query"
+ const val SEARCH_MORE_PARAMS = "search_more_params"
+ }
+
+ private lateinit var binding: ActivitySearchMoreBinding
+ private var query: String? = null
+ private var params: String? = null
+ private var list: MutableList = mutableListOf()
+ private var currentContinuation: String? = null
+ private var adapter: SearchResultOtherAdapter? = null
+
+ override suspend fun main() {
+ binding = ActivitySearchMoreBinding.inflate(layoutInflater)
+ setContentView(binding.root)
+ query = intent.getStringExtra(SEARCH_MORE_QUERY)
+ params = intent.getStringExtra(SEARCH_MORE_PARAMS)
+ if (query.isNullOrEmpty() || params.isNullOrEmpty()) {
+ return
+ }
+ initImmersionBar()
+ initView()
+ initAdapter()
+ initData(query!!, params!!)
+ onReceive()
+ }
+
+ private fun initImmersionBar() {
+ immersionBar {
+ statusBarDarkFont(false)
+ statusBarView(binding.view)
+ }
+ }
+
+ private suspend fun onReceive() {
+ while (isActive) {
+ select {
+ requests.onReceive {
+ when (it) {
+ Request.TryAgain -> {
+ initData(query!!, params!!)
+ }
+
+ Request.MoreData -> {
+ LogD(TAG, "Request.MoreData")
+ currentContinuation?.let { it1 -> initDataMore(it1) }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ private fun initView() {
+ binding.backBtn.setOnClickListener {
+ finish()
+ }
+ binding.tryAgainBtn.setOnClickListener {
+ requests.trySend(Request.TryAgain)
+ }
+ binding.refreshLayout.setEnableRefresh(false)
+ binding.refreshLayout.setEnableLoadMore(true)
+ binding.refreshLayout.setOnLoadMoreListener {
+ requests.trySend(Request.MoreData)
+ }
+ }
+
+ private fun initAdapter() {
+ adapter = SearchResultOtherAdapter(this, list)
+ binding.rv.layoutManager = LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false)
+ binding.rv.adapter = adapter
+ }
+
+ private suspend fun initData(query: String, params: String) {
+ showLoadingUi()
+ Innertube.moSearchPage(SearchBody(query = query, params = params))?.onSuccess { result ->
+ if (result.isNotEmpty()) {
+ showDataUi()
+ val title = result[0].title
+ if (title.isNullOrEmpty()) {
+ binding.title.visibility = View.GONE
+ } else {
+ binding.title.visibility = View.VISIBLE
+ binding.title.text = title
+ }
+ currentContinuation = result[0].continuation
+ list.clear()
+ list.addAll(result[0].searchResultList)
+ } else {
+ showNoContentUi()
+ }
+ }?.onFailure {
+ showNoContentUi()
+ }
+ }
+
+ @SuppressLint("NotifyDataSetChanged")
+ private suspend fun initDataMore(continuation: String) {
+ Innertube.moSearchPage(ContinuationBody(continuation = continuation))?.onSuccess { result ->
+ LogD(TAG, "initDataMore result->$result")
+ currentContinuation = result.continuation
+ if (result.searchResultList.isNotEmpty()) {
+ list.addAll(result.searchResultList)
+ if (!isFinishing) {
+ adapter?.notifyDataSetChanged()
+ }
+ binding.refreshLayout.finishLoadMore(true)
+ } else {
+ binding.refreshLayout.finishLoadMore(2000, true, false)
+ }
+ }?.onFailure {
+ binding.refreshLayout.finishLoadMore(2000, false, false)
+ }
+ }
+
+
+ private fun showDataUi() {
+ binding.loadingLayout.visibility = View.GONE
+ binding.noContentLayout.visibility = View.GONE
+ }
+
+ private fun showLoadingUi() {
+ binding.loadingLayout.visibility = View.VISIBLE
+ binding.noContentLayout.visibility = View.GONE
+ }
+
+ private fun showNoContentUi() {
+ binding.loadingLayout.visibility = View.GONE
+ binding.noContentLayout.visibility = View.VISIBLE
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/player/musicoo/adapter/SearchResultOtherAdapter.kt b/app/src/main/java/com/player/musicoo/adapter/SearchResultOtherAdapter.kt
index 150bff6..13e1488 100644
--- a/app/src/main/java/com/player/musicoo/adapter/SearchResultOtherAdapter.kt
+++ b/app/src/main/java/com/player/musicoo/adapter/SearchResultOtherAdapter.kt
@@ -2,6 +2,7 @@ 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
@@ -11,11 +12,15 @@ import androidx.media3.common.Player
import androidx.recyclerview.widget.RecyclerView
import com.bumptech.glide.Glide
import com.player.musicoo.R
+import com.player.musicoo.activity.MoListDetailsActivity
+import com.player.musicoo.activity.MoPlayDetailsActivity
+import com.player.musicoo.activity.MoSingerDetailsActivity
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
+import com.player.musicoo.util.LogTag
class SearchResultOtherAdapter(
private val context: Context,
@@ -24,7 +29,8 @@ class SearchResultOtherAdapter(
RecyclerView.Adapter() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
- val binding = SearchResultOtherItemBinding.inflate(LayoutInflater.from(context), parent, false)
+ val binding =
+ SearchResultOtherItemBinding.inflate(LayoutInflater.from(context), parent, false)
return ViewHolder(binding)
}
@@ -33,7 +39,40 @@ class SearchResultOtherAdapter(
holder.bind(bean)
holder.itemView.setOnClickListener {
+ LogTag.LogD(
+ LogTag.VO_ACT_LOG,
+ "SearchResultOtherAdapter bean.pageType->${bean.pageType}"
+ )
+ if (!bean.videoId.isNullOrEmpty()) {
+ 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.desc)
+ context.startActivity(intent)
+ } else if (!bean.browseId.isNullOrEmpty()) {
+ when (bean.pageType) {
+ "MUSIC_PAGE_TYPE_ARTIST" -> {
+ val intent = Intent(context, MoSingerDetailsActivity::class.java)
+ intent.putExtra(
+ MoSingerDetailsActivity.SINGER_DETAILS_PAGE_BROWSE_ID,
+ bean.browseId
+ )
+ context.startActivity(intent)
+ }
+ else -> {
+ val intent = Intent(context, MoListDetailsActivity::class.java)
+ intent.putExtra(
+ MoListDetailsActivity.PLAY_LIST_PAGE_BROWSE_ID,
+ bean.browseId
+ )
+ context.startActivity(intent)
+ }
+ }
+ }
}
}
diff --git a/app/src/main/java/com/player/musicoo/fragment/SearchFragment.kt b/app/src/main/java/com/player/musicoo/fragment/SearchFragment.kt
index 9bd1bb6..e77cbcb 100644
--- a/app/src/main/java/com/player/musicoo/fragment/SearchFragment.kt
+++ b/app/src/main/java/com/player/musicoo/fragment/SearchFragment.kt
@@ -24,6 +24,7 @@ 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.SearchBody
import com.player.musicoo.innertube.models.bodies.SearchSuggestionsBody
import com.player.musicoo.innertube.requests.moSearchPage
import com.player.musicoo.innertube.requests.searchSuggestions
@@ -176,9 +177,10 @@ class SearchFragment : MoBaseFragment(), TextWatcher,
appStore.searchHistoryStore = searchHistorySet
updateHistoryUi()
- Innertube.moSearchPage(input)?.onSuccess { result ->
+ Innertube.moSearchPage(SearchBody(query = input))?.onSuccess { result ->
showResultContent()
for (dataPage: Innertube.SearchDataPage in result) {
+ LogTag.LogD(TAG,"moSearchPage dataPage->$dataPage")
if (dataPage.type == 1) {//type为1的是最佳结果。
binding.contentLayout.addView(
SearchResultOptimalView(requireActivity(), dataPage)
@@ -238,7 +240,7 @@ class SearchFragment : MoBaseFragment(), TextWatcher,
@SuppressLint("NotifyDataSetChanged")
override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
- if (s.isNullOrEmpty()) {
+ if (s.isNullOrEmpty()) {//没有输入内容,隐藏删除按钮,展示历史记录,重新获取焦点弹出键盘。
binding.deleteInputBtn.visibility = View.GONE
showSearchHistory()
updateHistoryUi()
diff --git a/app/src/main/java/com/player/musicoo/innertube/Innertube.kt b/app/src/main/java/com/player/musicoo/innertube/Innertube.kt
index 87a5768..999e40a 100644
--- a/app/src/main/java/com/player/musicoo/innertube/Innertube.kt
+++ b/app/src/main/java/com/player/musicoo/innertube/Innertube.kt
@@ -178,7 +178,7 @@ object Innertube {
data class SingerDetailsListPage(
val title: String?,
val contents: SingerDetailsContent?
- ){
+ ) {
data class SingerDetailsContent(
val musicShelfContentList: List?,
val musicCarouselShelfContentList: List?,
@@ -186,9 +186,12 @@ object Innertube {
}
data class SearchDataPage(
- val type: Int?,
- val title: String?,
- val searchResultList: List
+ val type: Int? = null,
+ val title: String? = null,
+ val searchResultList: List,
+ val query: String? = null,
+ val params: String? = null,
+ val continuation: String? = null
) {
data class SearchResult(
val title: String?,
@@ -196,6 +199,7 @@ object Innertube {
val thumbnail: String?,
val videoId: String?,
val browseId: String?,
+ val pageType: String?,
)
}
diff --git a/app/src/main/java/com/player/musicoo/innertube/requests/MoSearchPage.kt b/app/src/main/java/com/player/musicoo/innertube/requests/MoSearchPage.kt
index 74f761e..530e1ad 100644
--- a/app/src/main/java/com/player/musicoo/innertube/requests/MoSearchPage.kt
+++ b/app/src/main/java/com/player/musicoo/innertube/requests/MoSearchPage.kt
@@ -2,10 +2,12 @@ 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.ContinuationResponse
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.ContinuationBody
import com.player.musicoo.innertube.models.bodies.SearchBody
import com.player.musicoo.innertube.utils.runCatchingNonCancellable
import com.player.musicoo.util.LogTag
@@ -13,11 +15,11 @@ 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>? =
+suspend fun Innertube.moSearchPage(body: SearchBody): Result>? =
runCatchingNonCancellable {
val response = client.post(search) {
- setBody(SearchBody(query = query))
+ setBody(body)
}.body()
val searchDataPageList: MutableList = mutableListOf()
@@ -32,6 +34,9 @@ suspend fun Innertube.moSearchPage(query: String): Result {
+ //不add博客数据
+ }
+
+ "MUSIC_PAGE_TYPE_USER_CHANNEL" -> {
+ //不add个人资料数据
+ }
+
+ else -> {
+ searchResultItemList.add(item)
+ }
+ }
+
}
}
}
@@ -150,10 +196,105 @@ suspend fun Innertube.moSearchPage(query: String): Result? =
+ runCatchingNonCancellable {
+ val response = client.post(search) {
+ setBody(body)
+ }.body()
+
+ val searchResultItemList: MutableList =
+ mutableListOf()
+
+ val musicShelfContinuation = response.continuationContents
+ ?.musicShelfContinuation
+
+ val itemContents = musicShelfContinuation
+ ?.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 ?: "",
+
+ pageType = itemContent.musicResponsiveListItemRenderer
+ ?.navigationEndpoint
+ ?.browseEndpoint
+ ?.type,
+ )
+ //这两个ID必须有一个id是存在的,否则就不进入添加数据
+ if (!item.videoId.isNullOrBlank() || !item.browseId.isNullOrBlank()) {
+ when (item.pageType) {
+ "MUSIC_PAGE_TYPE_PODCAST_SHOW_DETAIL_PAGE" -> {
+ //不add博客数据
+ }
+
+ "MUSIC_PAGE_TYPE_USER_CHANNEL" -> {
+ //不add个人资料数据
+ }
+
+ else -> {
+ searchResultItemList.add(item)
+ }
+ }
+
+ }
+ }
+ }
+
+ Innertube.SearchDataPage(
+ searchResultList = searchResultItemList,
+ continuation = musicShelfContinuation
+ ?.continuations
+ ?.firstOrNull()
+ ?.nextContinuationData
+ ?.continuation
+ )
}
\ No newline at end of file
diff --git a/app/src/main/java/com/player/musicoo/innertube/requests/MoSingerlistPage.kt b/app/src/main/java/com/player/musicoo/innertube/requests/MoSingerlistPage.kt
index 0b7be87..2586c31 100644
--- a/app/src/main/java/com/player/musicoo/innertube/requests/MoSingerlistPage.kt
+++ b/app/src/main/java/com/player/musicoo/innertube/requests/MoSingerlistPage.kt
@@ -85,17 +85,17 @@ suspend fun Innertube.moSingerListPage(browseId: String): Result${singerDetails.title}")
- LogTag.LogD(
- TAG,
- "musicShelfContentList size->${singerDetails.contents?.musicShelfContentList?.size}"
- )
- LogTag.LogD(
- TAG,
- "musicCarouselShelfContentList size->${singerDetails.contents?.musicCarouselShelfContentList?.size}"
- )
-
- LogTag.LogD(TAG, "--------------------------------------------")
+// 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)
diff --git a/app/src/main/java/com/player/musicoo/view/SearchResultOptimalView.kt b/app/src/main/java/com/player/musicoo/view/SearchResultOptimalView.kt
index 4b88b12..61dcea2 100644
--- a/app/src/main/java/com/player/musicoo/view/SearchResultOptimalView.kt
+++ b/app/src/main/java/com/player/musicoo/view/SearchResultOptimalView.kt
@@ -8,6 +8,7 @@ import android.widget.LinearLayout
import android.widget.TextView
import com.bumptech.glide.Glide
import com.player.musicoo.R
+import com.player.musicoo.activity.MoListDetailsActivity
import com.player.musicoo.activity.MoPlayDetailsActivity
import com.player.musicoo.activity.MoSingerDetailsActivity
import com.player.musicoo.innertube.Innertube
@@ -47,12 +48,24 @@ class SearchResultOptimalView(context: Context, data: Innertube.SearchDataPage)
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)
+ when (optimalBean.pageType) {
+ "MUSIC_PAGE_TYPE_ALBUM" -> {
+ val intent = Intent(context, MoListDetailsActivity::class.java)
+ intent.putExtra(
+ MoListDetailsActivity.PLAY_LIST_PAGE_BROWSE_ID,
+ optimalBean.browseId
+ )
+ context.startActivity(intent)
+ }
+ else -> {
+ val intent = Intent(context, MoSingerDetailsActivity::class.java)
+ intent.putExtra(
+ MoSingerDetailsActivity.SINGER_DETAILS_PAGE_BROWSE_ID,
+ optimalBean.browseId
+ )
+ context.startActivity(intent)
+ }
+ }
}
}
}
diff --git a/app/src/main/java/com/player/musicoo/view/SearchResultOtherView.kt b/app/src/main/java/com/player/musicoo/view/SearchResultOtherView.kt
index 688588e..d5fc19d 100644
--- a/app/src/main/java/com/player/musicoo/view/SearchResultOtherView.kt
+++ b/app/src/main/java/com/player/musicoo/view/SearchResultOtherView.kt
@@ -2,25 +2,37 @@ package com.player.musicoo.view
import android.annotation.SuppressLint
import android.content.Context
+import android.content.Intent
+import android.widget.LinearLayout
import android.widget.TextView
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.player.musicoo.R
+import com.player.musicoo.activity.MoSearchMoreActivity
import com.player.musicoo.adapter.SearchResultOtherAdapter
import com.player.musicoo.innertube.Innertube
@SuppressLint("ViewConstructor")
-class SearchResultOtherView (context: Context, data: Innertube.SearchDataPage) :
+class SearchResultOtherView(context: Context, data: Innertube.SearchDataPage) :
ModuleView(context) {
init {
contentView = inflate(getContext(), R.layout.search_result_other_layout, this)
val title = contentView?.findViewById(R.id.title)
title?.text = data.title
+ val moreBtn = contentView?.findViewById(R.id.moreBtn)
+ moreBtn?.setOnClickListener {
+ val intent = Intent(context, MoSearchMoreActivity::class.java)
+ intent.putExtra(MoSearchMoreActivity.SEARCH_MORE_QUERY, data.query)
+ intent.putExtra(MoSearchMoreActivity.SEARCH_MORE_PARAMS, data.params)
+ context.startActivity(intent)
+ }
+
val rv = contentView?.findViewById(R.id.rv)
val adapter = SearchResultOtherAdapter(context, data.searchResultList)
rv?.layoutManager = LinearLayoutManager(context, LinearLayoutManager.VERTICAL, false)
rv?.adapter = adapter
+
}
}
\ No newline at end of file
diff --git a/app/src/main/res/layout/activity_search_more.xml b/app/src/main/res/layout/activity_search_more.xml
new file mode 100644
index 0000000..87ee8ee
--- /dev/null
+++ b/app/src/main/res/layout/activity_search_more.xml
@@ -0,0 +1,133 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/gradle.properties b/gradle.properties
index 3c5031e..c22da57 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -20,4 +20,5 @@ kotlin.code.style=official
# Enables namespacing of each library's R class so that its R class includes only the
# resources declared in the library itself and none from the library's dependencies,
# thereby reducing the size of the R class for that library
-android.nonTransitiveRClass=true
\ No newline at end of file
+android.nonTransitiveRClass=true
+android.enableJetifier=true
\ No newline at end of file